# # ActiveFacts CQL Parser. # Parse rules relating to high-level CQL definitions and constraints. # # Copyright (c) 2009 Clifford Heath. Read the LICENSE file. # module ActiveFacts module CQL grammar CQL include LexicalRules include Language # One of the language modules provides this module include Expressions include Terms include Concepts include ValueTypes include FactTypes include Context rule cql_file s seq:definition* { def definitions seq.elements.map{|e| e.value rescue $stderr.puts "Can't call value() on #{e.inspect}" } end } end # Each definition has an ast() method that returns an instance of a subclass of Compiler::Definition rule definition definition_body s { def ast definition_body.ast end def body definition_body.text_value end } end rule definition_body vocabulary_definition / import_definition / prescan # Always fails, but its side-effects are needed in the following / constraint / unit_definition # REVISIT: Move this above the prescan? / concept / s ';' s { def ast; nil; end } end rule vocabulary_definition s vocabulary S id s ';' { def ast Compiler::Vocabulary.new(id.value) end } end rule import_definition s import S id alias_list ';' { def ast Compiler::Import.new(id.value, alias_list.value) end } end # REVISIT: Need a way to define equivalent readings for fact types here (and in the metamodel) rule alias_list ( s ',' s alias S aliased_from:id S as S alias_to:id s )* { def value elements.inject({}){|h, e| h[e.aliased_from.value] = e.alias_to; h } end } end rule constraint subset_constraint / equality_constraint / set_constraint / presence_constraint # REVISIT: / value_constraint end rule enforcement s '(' s otherwise s action s a:agent? s ')' s { def ast; Compiler::Enforcement.new(action.text_value, a.empty? ? nil : a.text_value); end } / '' { def ast; nil; end } end # presence constraint: rule presence_constraint s 'each' s ('combination' S)? role_list s 'occurs' s quantifier s 'time' 's'? s enforcement 'in' s readings_list s c:context_note? ';' { def ast context_note = c.empty? ? nil : c.ast Compiler::PresenceConstraint.new context_note, enforcement.ast, readings_list.ast, role_list.ast, quantifier.ast end } end # set (exclusion, mandatory exclusion, complex equality) constraint rule set_constraint s 'for' s 'each' s role_list s quantifier s 'of' s 'these' s 'holds' s enforcement ':' s readings_list s c:context_note? ';' { def ast context_note = c.empty? ? nil : c.ast Compiler::SetExclusionConstraint.new context_note, enforcement.ast, readings_list.ast, role_list.ast, quantifier.ast end } / s either? s r1:readings s or s r2:readings exclusion:(but s not s both s)? c:context_note? enforcement ';' { def ast context_note = c.empty? ? nil : c.ast if exclusion.text_value.empty? Compiler::PresenceConstraint.new context_note, enforcement.ast, [r1.ast, r2.ast], nil, Compiler::Quantifier.new(1, nil) else quantifier = Compiler::Quantifier.new(*(exclusion.text_value.empty? ? [1, nil] : [1, 1])) #quantifier = Compiler::Quantifier.new(1, 1) Compiler::SetExclusionConstraint.new context_note, enforcement.ast, [r1.ast, r2.ast], nil, quantifier end end } end rule subset_constraint s readings s only s if s r2:readings s c:context_note? enforcement ';' { def ast context_note = c.empty? ? nil : c.ast Compiler::SubsetConstraint.new context_note, enforcement.ast, [readings.ast, r2.ast] end } end rule equality_constraint s readings s tail:( if s and s only s if s readings s)+ c:context_note? enforcement ';' { def ast context_note = c.empty? ? nil : c.ast all_readings = [readings.ast, *tail.elements.map{|e| e.readings.ast }] Compiler::SetEqualityConstraint.new context_note, enforcement.ast, all_readings end } end rule readings_list readings tail:( ',' s readings )* { def ast [readings.ast, *tail.elements.map{|e| e.readings.ast }] end } end rule readings reading s tail:( and s reading s )* { def ast readings = reading.ast tail.elements.map{|e| readings += e.reading.ast } readings end } end end end end