-
Notifications
You must be signed in to change notification settings - Fork 25
Description
In ECL, the rule for memberFilter
is ambiguous. It basically takes the form of key = value
where key
may be one of a few keywords (moduleId
, effectiveTime
, or active
; where Hermes supports just the last one of these) or any string, in which case it's interpreted as the name of a refset field. Unfortunately, the way Instaparse is implemented, and unlike the behaviour of recommended parsers for processing ECL, it seems to preferentially parse the last option of alternatives for a non-terminal, in this case the catch-all memberFieldFilter
.
I have opened ECL #8 about this but it seems like the grammar is not about to be updated to remove this ambiguity. Instead, I found the following workaround to work to make also an Instaparser prefer the other expansions for memberFilter
to the catch-all option:
(ns ...
(:require
[instaparse.combinators :as i-com]
[instaparse.core :as insta :refer (defparser)]
[shadow.resource :as rc]
))
(defparser ^:private insta-parser
(-> (rc/inline "ecl-brief-2-2.abnf")
i-com/abnf
;; The catch-all `memberFieldFilter` non-terminal is
;; already the last one in the disjunction. To prevent
;; it from triggering when another option is applicable,
;; cast the disjunction as an ordered choice.
(update
:memberFilter
#(apply i-com/ord (:parsers %))
;; Just reversing the order works, too, but is
;; technically not correct.
; #(apply i-com/alt (reverse (:parsers %)))
))
:output-format :enlive
:start :expressionConstraint
)
I noticed a comment in the Hermes codebase implying that you have been aware of this issue:
(defn- parse-member-filter
"memberFilter = memberFieldFilter / moduleFilter / effectiveTimeFilter / activeFilter"
[ctx refset-id loc]
;; note, must try to parse memberFieldFilter last as least specific
(or (zx/xml1-> loc :activeFilter #(parse-member-filter--active-filter ctx refset-id %))
(zx/xml1-> loc :moduleFilter #(parse-member-filter--module-filter ctx refset-id %))
(zx/xml1-> loc :effectiveTimeFilter #(parse-member-effective-time-filter ctx refset-id %))
(zx/xml1-> loc :memberFieldFilter #(parse-member-field-filter ctx refset-id %))))
but this doesn't appear to result in the preferred parse, I guess because by the time this code executes, the parser has already made its mind about the single parse for the input string, so only one of the possible child non-terminals is already present in loc
.