Rule = TruthStatement / Clause
Clause = (ClauseFinding / ClauseObservable) [ ws ANDOP ws (ClauseFinding / ClauseObservable) ] ws
TruthStatement = ws 1*1("true" / "otherwise true") ws
;;; A Rule is either a truth statement or a clause
;;; A truth statement is either "true" or "otherwise true"
;;; A clause is either a clause with a finding or a clause with an observable
;;; and value optionally followed by the AND operator and a clause with a finding or observable and value.
ClauseObservable = "IFA" ws ( AttributeObservable ws NumericOperator ws Value )
;;; Observable clause has a mandatory value
ClauseFinding = "IFA" ws ( AttributeFinding )
AttributeObservable = ConceptObservable
;;; This could be removed and AttributeObservable changed to
;;; ConceptObservable without affecting the grammar
AttributeFinding = ConceptFinding
;;; This could be removed and AttributeFinding changed to
;;; ConceptFinding without affecting the grammar
NumericOperator = ("<" / ">=")
;;;Age at onset rules use greaterthanorequals for lower bounds and lessthan for upper bound
Value = ConceptAny / Numeric / OtherText
;;; Added in ConceptAny, which allows any Concept to be used as a Value,
;;; although clearly not all concepts are suitable
ConceptObservable = SctId ws pipe ws FullySpecifiedNameObservable ws pipe
ConceptFinding = SctId ws pipe ws FullySpecifiedNameFinding ws pipe
ConceptAny = SctId ws pipe ws FullySpecifiedName ws pipe
SctId = 6*18( digit )
;;; The SctId must be a valid SNOMED CT Concept.id value
FullySpecifiedNameObservable = 1*nonwsnonpipe *( ( 1*SP "(" *SP 1*nonwsnonparennonpipe *SP ")" !( ws pipe ) ) / ( 1*SP 1*nonwsnonparennonpipe !( ws pipe ) ) ) *SP 1*1( "(observable entity)" )
;;; The FSN of an observable must have a semantic tag = "observable entity"
;;; and may contain other embedded parenthesised strings. The ! (NOT)
;;; look-ahead operator serves to prevent the parser consuming the
;;; semantic tag when it's looking for words before the tag.