# # ActiveFacts CQL Parser. # Parse rules relating to FactType definitions. # # Copyright (c) 2009 Clifford Heath. Read the LICENSE file. # module ActiveFacts module CQL grammar FactTypes rule named_fact_type s term_definition_name ( written_as # REVISIT: What on earth was I thinking when I wrote this? / s is s mapping_pragmas where # REVISIT: Need a place to put mapping pragmas like [independent] ) &{|e| input.context.objectified_fact_type(e[1].value); true } anonymous_fact_type { def value f = anonymous_fact_type.value f[0] = term_definition_name.value f end } end rule anonymous_fact_type f0:fact_clause ftail:( (',' / and ) s f1:fact_clause s )* ctail:( (':' / where) s c:conditions s)? returning_clause? s ';' s { def value readings = [ f0.body, *ftail.elements.map{|e| e.f1.body } ] conditions = !ctail.empty? ? ctail.c.condition_list : [] [ nil, [ :fact_type, readings, conditions ] ] end } end rule returning_clause returning return (',' return)* end rule return by order 'REVISIT: return' end rule conditions head:condition s tail:( (',' s / and S) next:condition s )* { def condition_list [head.value] + tail.elements.map{|i| i.next.value} end } end rule condition head:clause s # tail:(or S alternate:clause s )* { def value # if tail.elements.size == 0 head.clause # else # [:"||", head.clause] + tail.elements.map{|i| i.alternate.clause} # end end } end rule clause # REVISIT: No context for comparisons, yet (comparison / fact_clause) { def clause self.body end } end rule fact_clause s q:qualifier? s reading s p:post_qualifiers? s c:context? { def body [ :fact_clause, (q.empty? ? [] : [ q.text_value ]) + (p.empty? ? [] : p.list), reading.value, c.empty? ? nil : c.value ] end } end rule qualifier maybe / definitely end rule post_qualifiers '[' s q0:post_qualifier tail:( s ',' s q1:post_qualifier )* s ']' s { def list [q0.text_value] + tail.elements.map{|e| e.q1.text_value} end } end rule post_qualifier static / transient / intransitive / transitive / acyclic / symmetric end rule reading role+ { def value elements.map{|r| r.value} end } end # This is the rule that causes most back-tracking. I think you can see why. # When we have an expression, we will come down here perhaps multiple times, # but find no way out as soon as we hit the trailing non_role. rule role q:quantifier? adj0:(a:role_word '-' s)? player:role_word !'-' s? adj1:( '-' a:(a:role_word s)? )? func:function_call? role_id:(role_name / subscript )? lr:( literal / restriction )? !non_role { def value r = {} quantifier = !q.empty? && q.value # "some" quantifier has nil value r[:quantifier] = quantifier if quantifier r[:leading_adjective] = adj0.a.value unless adj0.empty? r[:word] = player.value r[:trailing_adjective] = adj1.a.a.value unless adj1.empty? r[:function] = func.value if !func.empty? r[:role_name] = role_id.value unless role_id.empty? r[:restriction] = lr.ranges if !lr.empty? && lr.respond_to?(:ranges) r[:literal] = lr.value if !lr.empty? && lr.respond_to?(:value) r end } end rule role_name '(' s as S r:id s ')' s { def value; r.value; end } end rule subscript '(' i:([1-9] [0-9]*) ')' s { def value; i.text_value.to_i; end } end rule non_role # Any of these is illegal in or following a reading: comparator / add_op / mul_op end rule role_word !non_role_word id { def value; id.value; end } end rule non_role_word # These words are illegal in (but maybe ok following) a reading where a role word is expected: and / if / only / or / quantifier / restriction / but end end end end