# # ActiveFacts CQL Parser. # Parse rules relating to Concept definitions. # # Copyright (c) 2009 Clifford Heath. Read the LICENSE file. # module ActiveFacts module CQL grammar Concepts rule concept value_type / entity_type / named_fact_type / anonymous_fact_type end rule entity_type s term_definition_name sup:(basetype / subtype) &{|s| # There's an implicit type when we use an identification mode, register it: mode = s[2].identification_mode if mode input.context.object_type(s[1].value+mode, "identification mode type") input.context.object_type(s[1].value+' '+mode, "identification mode type") end true } mapping_pragmas ec:entity_conditions? ';' { def ast name = term_definition_name.value readings = ec.empty? ? [] : ec.ast Compiler::EntityType.new name, sup.supers, sup.ast, mapping_pragmas.value, readings end } end rule basetype is s # independency? identification { def ast; identification.ast; end def supers; []; end def identification_mode; identification.mode; end } end rule subtype subtype_prefix # independency? supertype_list ident:identification? { def ast; ident.empty? ? nil : ident.ast; end def supers; supertype_list.value; end def identification_mode; ident.empty? ? nil : ident.mode; end } end # REVISIT: This doesn't work, and I don't know why. rule independency ('independent' S / 'separate' S / 'partitioned' S) end rule supertype_list primary:term s alternate_supertypes:( ',' s !identified_by name:term s )* { def value [primary.value, *alternate_supertypes.elements.map { |sup| sup.name.value } ] end } end rule identification # REVISIT: Consider distinguishing "-Id" from just "Id", and not prepending the entity type name if no "-" identified_by its s i:(term/id) value_type_parameters r:(value_constraint enforcement)? # Reference Mode; value_constraint may be needed for the ValueType { def ast if r.empty? value_constraint = nil else value_constraint = Compiler::ValueConstraint.new(r.value_constraint.ranges, r.enforcement.ast) end Compiler::ReferenceMode.new(i.value, value_constraint, value_type_parameters.values) end def mode i.value end } / identified_by role_list &{|s| role_list = s[-1] forwards = role_list.ast. map do |role_ref| next nil if role_ref.is_a?(Compiler::Reading) # Can't forward-reference unaries next nil if role_ref.leading_adjective or role_ref.trailing_adjective role_ref.term end. compact input.context.allowed_forward_terms(forwards) true } { def ast role_list.ast end def mode nil end } end # Identified by roles... also used for constraints, beware rule role_list head:term_or_unary s tail:( ( and S / ',' s ) term_or_unary s )* { def ast [head.ast, *tail.elements.map{|e| e.term_or_unary.ast}] end } end rule term_or_unary pre_text:(s !non_role_word !term id)* s term post_text:(s !non_role_word !term id)* s ss:subscript? { def ast t = term.ast t.role_name = ss.value if !ss.empty? if pre_text.elements.size == 0 && post_text.elements.size == 0 t else pre_words = pre_text.elements.map{|w| w.id.text_value} post_words = post_text.elements.map{|w| w.id.text_value} Compiler::Reading.new(pre_words + [t] + post_words, [], nil) end end } / s !non_role_word id s &non_role_word s ss:subscript { # A forward-referenced entity type # REVISIT: A change in this rule might allow forward-referencing a multi-word term def ast Compiler::RoleRef.new(id.text_value, nil, nil, nil, ss.empty? ? nil : ss.value) end } end rule mapping_pragmas '[' s h:mapping_pragma t:(s ',' s mapping_pragma)* s ']' s { def value; t.elements.inject([h.value]) { |a, e| a << e.mapping_pragma.value }; end } / '' { def value; []; end } end rule mapping_pragma (independent / separate / partitioned / personal / feminine / masculine) { def value; text_value; end } end rule entity_conditions (':' / where) s c:conditions? { def ast c.empty? ? [] : c.ast end def conditions c.empty? ? [] : c.condition_list end } end end end end