activefacts-cql 1.8.1 → 1.8.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (31) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/Gemfile +6 -5
  4. data/activefacts-cql.gemspec +4 -3
  5. data/lib/activefacts/cql/compiler.rb +29 -22
  6. data/lib/activefacts/cql/compiler/clause.rb +86 -84
  7. data/lib/activefacts/cql/compiler/constraint.rb +53 -53
  8. data/lib/activefacts/cql/compiler/entity_type.rb +28 -28
  9. data/lib/activefacts/cql/compiler/expression.rb +27 -27
  10. data/lib/activefacts/cql/compiler/fact.rb +37 -37
  11. data/lib/activefacts/cql/compiler/fact_type.rb +91 -85
  12. data/lib/activefacts/cql/compiler/informal.rb +48 -0
  13. data/lib/activefacts/cql/compiler/query.rb +52 -52
  14. data/lib/activefacts/cql/compiler/shared.rb +17 -17
  15. data/lib/activefacts/cql/compiler/value_type.rb +11 -11
  16. data/lib/activefacts/cql/parser/CQLParser.treetop +76 -56
  17. data/lib/activefacts/cql/parser/Context.treetop +15 -17
  18. data/lib/activefacts/cql/parser/Expressions.treetop +21 -21
  19. data/lib/activefacts/cql/parser/FactTypes.treetop +216 -216
  20. data/lib/activefacts/cql/parser/Language/English.treetop +136 -131
  21. data/lib/activefacts/cql/parser/Language/French.treetop +118 -118
  22. data/lib/activefacts/cql/parser/Language/Mandarin.treetop +111 -111
  23. data/lib/activefacts/cql/parser/LexicalRules.treetop +45 -45
  24. data/lib/activefacts/cql/parser/ObjectTypes.treetop +120 -120
  25. data/lib/activefacts/cql/parser/Terms.treetop +63 -63
  26. data/lib/activefacts/cql/parser/ValueTypes.treetop +71 -71
  27. data/lib/activefacts/cql/require.rb +8 -8
  28. data/lib/activefacts/cql/verbaliser.rb +88 -88
  29. data/lib/activefacts/cql/version.rb +1 -1
  30. data/lib/activefacts/input/cql.rb +7 -7
  31. metadata +12 -16
@@ -0,0 +1,48 @@
1
+ module ActiveFacts
2
+ module CQL
3
+ class Compiler < ActiveFacts::CQL::Parser
4
+ class InformalDefinition < Definition
5
+ def initialize kind, subject, phrases, text
6
+ @kind = kind
7
+ @subject = subject
8
+ @phrases = phrases
9
+ @text = text
10
+ end
11
+
12
+ def compile
13
+ @context = CompilationContext.new(@vocabulary)
14
+ case @kind
15
+ when :each
16
+ compile_object_description
17
+ when :when
18
+ compile_fact_type_description
19
+ end
20
+ end
21
+
22
+ def apply_description concept_type
23
+ concept_type.concept.informal_description =
24
+ [ concept_type.concept.informal_description,
25
+ @text
26
+ ].compact*".\n"
27
+ end
28
+
29
+ def compile_object_description
30
+ object_type = @context.object_type(@subject)
31
+ raise "Cannot add informal description of undefined object #{@subject.inspect}" unless object_type
32
+ apply_description object_type
33
+ false
34
+ end
35
+
36
+ def compile_fact_type_description
37
+ @clause = Compiler::Clause.new(@phrases) # Make the phrases into a clause
38
+ @context.bind [@clause] # Bind player names in the claise
39
+ fact_type = @clause.match_existing_fact_type @context
40
+ apply_description fact_type if fact_type
41
+ false
42
+ end
43
+
44
+ end
45
+ end
46
+ end
47
+ end
48
+
@@ -10,11 +10,11 @@ module ActiveFacts
10
10
  each do |binding|
11
11
  trace :query, "Creating variable #{query.all_variable.size} for #{binding.inspect}"
12
12
  binding.variable = @constellation.Variable(query, query.all_variable.size, :object_type => binding.player)
13
- if literal = binding.refs.detect{|r| r.literal}
14
- if literal.kind_of?(ActiveFacts::CQL::Compiler::Reference)
15
- # REVISIT: Fix this crappy ad-hoc polymorphism hack
16
- literal = literal.literal
17
- end
13
+ if literal = binding.refs.detect{|r| r.literal}
14
+ if literal.kind_of?(ActiveFacts::CQL::Compiler::Reference)
15
+ # REVISIT: Fix this crappy ad-hoc polymorphism hack
16
+ literal = literal.literal
17
+ end
18
18
  unit = @constellation.Unit.detect{|k, v| [v.name, v.plural_name].include? literal.unit} if literal.unit
19
19
  binding.variable.value = [literal.literal.to_s, literal.literal.is_a?(String), unit]
20
20
  end
@@ -34,60 +34,60 @@ module ActiveFacts
34
34
  end
35
35
 
36
36
  def build_step clause, roles_by_binding = {}, parent_variable = nil
37
- return unless clause.refs.size > 0 # Empty clause... really?
37
+ return unless clause.refs.size > 0 # Empty clause... really?
38
38
 
39
- step = @constellation.Step(
40
- :guid => :new,
41
- :fact_type => clause.fact_type,
42
- :alternative_set => nil,
43
- :is_disallowed => clause.certainty == false,
44
- :is_optional => clause.certainty == nil
45
- )
39
+ step = @constellation.Step(
40
+ :guid => :new,
41
+ :fact_type => clause.fact_type,
42
+ :alternative_set => nil,
43
+ :is_disallowed => clause.certainty == false,
44
+ :is_optional => clause.certainty == nil
45
+ )
46
46
 
47
- trace :query, "Creating Plays for #{clause.inspect} with #{clause.refs.size} refs" do
48
- is_input = true
49
- clause.refs.each do |ref|
50
- # These refs are the Compiler::References, which have associated Metamodel::RoleRefs,
51
- # but we need to create Plays for those roles.
52
- # REVISIT: Plays may need to save residual_adjectives
53
- binding = ref.binding
54
- role = (ref && ref.role) || (ref.role_ref && ref.role_ref.role)
47
+ trace :query, "Creating Plays for #{clause.inspect} with #{clause.refs.size} refs" do
48
+ is_input = true
49
+ clause.refs.each do |ref|
50
+ # These refs are the Compiler::References, which have associated Metamodel::RoleRefs,
51
+ # but we need to create Plays for those roles.
52
+ # REVISIT: Plays may need to save residual_adjectives
53
+ binding = ref.binding
54
+ role = (ref && ref.role) || (ref.role_ref && ref.role_ref.role)
55
55
 
56
- objectification_step = nil
57
- if ref.nested_clauses
58
- ref.nested_clauses.each do |nested_clause|
59
- objectification_step = build_step nested_clause, roles_by_binding
60
- if ref.binding.player.is_a?(ActiveFacts::Metamodel::EntityType) and
61
- ref.binding.player.fact_type == nested_clause.fact_type
62
- objectification_step.objectification_variable = binding.variable
63
- end
64
- end
65
- end
66
- if clause.is_naked_object_type
67
- raise "#{self} lacks a proper objectification" if clause.refs[0].nested_clauses and !objectification_step
68
- return objectification_step
69
- end
56
+ objectification_step = nil
57
+ if ref.nested_clauses
58
+ ref.nested_clauses.each do |nested_clause|
59
+ objectification_step = build_step nested_clause, roles_by_binding
60
+ if ref.binding.player.is_a?(ActiveFacts::Metamodel::EntityType) and
61
+ ref.binding.player.fact_type == nested_clause.fact_type
62
+ objectification_step.objectification_variable = binding.variable
63
+ end
64
+ end
65
+ end
66
+ if clause.is_naked_object_type
67
+ raise "#{self} lacks a proper objectification" if clause.refs[0].nested_clauses and !objectification_step
68
+ return objectification_step
69
+ end
70
70
 
71
- if binding.variable.object_type != role.object_type # Type mismatch
72
- if binding.variable.object_type.common_supertype(role.object_type)
73
- # REVISIT: there's an implicit subtyping step here, create it; then always raise the error here.
74
- # I don't want to do this for now because the verbaliser will always verbalise all steps.
75
- raise "Disallowing implicit subtyping step from #{role.object_type.name} to #{binding.variable.object_type.name} in #{clause.fact_type.default_reading.inspect}"
76
- end
77
- raise "A #{role.object_type.name} cannot satisfy #{binding.variable.object_type.name} in #{clause.fact_type.default_reading.inspect}"
78
- end
71
+ if binding.variable.object_type != role.object_type # Type mismatch
72
+ if binding.variable.object_type.common_supertype(role.object_type)
73
+ # REVISIT: there's an implicit subtyping step here, create it; then always raise the error here.
74
+ # I don't want to do this for now because the verbaliser will always verbalise all steps.
75
+ raise "Disallowing implicit subtyping step from #{role.object_type.name} to #{binding.variable.object_type.name} in #{clause.fact_type.default_reading.inspect}"
76
+ end
77
+ raise "A #{role.object_type.name} cannot satisfy #{binding.variable.object_type.name} in #{clause.fact_type.default_reading.inspect}"
78
+ end
79
79
 
80
- trace :query, "Creating Play for #{ref.inspect}"
81
- play = @constellation.Play(:step => step, :role => role, :variable => binding.variable)
82
- play.is_input = is_input
83
- is_input = false
80
+ trace :query, "Creating Play for #{ref.inspect}"
81
+ play = @constellation.Play(:step => step, :role => role, :variable => binding.variable)
82
+ play.is_input = is_input
83
+ is_input = false
84
84
 
85
- roles_by_binding[binding] = [role, play]
86
- end
87
- end
85
+ roles_by_binding[binding] = [role, play]
86
+ end
87
+ end
88
88
 
89
- step
90
- end
89
+ step
90
+ end
91
91
 
92
92
  # Return the unique array of all bindings in these clauses, including in objectification steps
93
93
  def all_bindings_in_clauses clauses
@@ -36,18 +36,18 @@ module ActiveFacts
36
36
  key <=> other.key
37
37
  end
38
38
 
39
- def variable= v
40
- @variable = v # A place for a breakpoint :)
41
- end
42
-
43
- def add_ref ref
44
- @refs << ref
45
- ref
46
- end
47
-
48
- def delete_ref ref
49
- @refs.delete ref
50
- end
39
+ def variable= v
40
+ @variable = v # A place for a breakpoint :)
41
+ end
42
+
43
+ def add_ref ref
44
+ @refs << ref
45
+ ref
46
+ end
47
+
48
+ def delete_ref ref
49
+ @refs.delete ref
50
+ end
51
51
  end
52
52
 
53
53
  class CompilationContext
@@ -77,7 +77,7 @@ module ActiveFacts
77
77
  player ||= @player_by_role_name[name]
78
78
 
79
79
  if !player && @allowed_forward_terms.include?(name)
80
- @vocabulary.valid_entity_type_name(name) # No need for the result here, just no exceptional condition
80
+ @vocabulary.valid_entity_type_name(name) # No need for the result here, just no exceptional condition
81
81
  player = constellation.EntityType(@vocabulary, name, :concept => :new)
82
82
  end
83
83
 
@@ -117,10 +117,10 @@ module ActiveFacts
117
117
 
118
118
  def compile
119
119
  if @constellation.Vocabulary.size > 0
120
- @constellation.Topic @name
121
- else
122
- @constellation.Vocabulary @name
123
- end
120
+ @constellation.Topic @name
121
+ else
122
+ @constellation.Vocabulary @name
123
+ end
124
124
  end
125
125
 
126
126
  def to_s
@@ -94,8 +94,8 @@ module ActiveFacts
94
94
  @unit = unit
95
95
  @value_constraint = value_constraint
96
96
  @pragmas = pragmas
97
- @context_note = context_note
98
- @auto_assigned_at = auto_assigned_at
97
+ @context_note = context_note
98
+ @auto_assigned_at = auto_assigned_at
99
99
  end
100
100
 
101
101
  def compile
@@ -104,19 +104,19 @@ module ActiveFacts
104
104
  # Create the base type unless it already exists:
105
105
  base_type = nil
106
106
  if (@base_type_name != @name)
107
- unless base_type = @vocabulary.valid_value_type_name(@base_type_name)
107
+ unless base_type = @vocabulary.valid_value_type_name(@base_type_name)
108
108
  base_type = @constellation.ValueType(@vocabulary, @base_type_name, :concept => :new)
109
109
  return base_type if @base_type_name == @name
110
110
  end
111
111
  end
112
112
 
113
113
  # Create and initialise the ValueType:
114
- vt = @vocabulary.valid_value_type_name(@name) ||
115
- @constellation.ValueType(@vocabulary, @name, :concept => :new)
114
+ vt = @vocabulary.valid_value_type_name(@name) ||
115
+ @constellation.ValueType(@vocabulary, @name, :concept => :new)
116
116
  vt.is_independent = true if @pragmas.delete('independent')
117
- @pragmas.each do |p|
118
- @constellation.ConceptAnnotation(:concept => vt.concept, :mapping_annotation => p)
119
- end if @pragmas
117
+ @pragmas.each do |p|
118
+ @constellation.ConceptAnnotation(:concept => vt.concept, :mapping_annotation => p)
119
+ end if @pragmas
120
120
  vt.supertype = base_type if base_type
121
121
  vt.length = length if length
122
122
  vt.scale = scale if scale
@@ -148,9 +148,9 @@ module ActiveFacts
148
148
  vt.value_constraint = @value_constraint.compile
149
149
  end
150
150
 
151
- if @context_note
152
- @context_note.compile(@constellation, vt)
153
- end
151
+ if @context_note
152
+ @context_note.compile(@constellation, vt)
153
+ end
154
154
 
155
155
  vt
156
156
  end
@@ -37,16 +37,16 @@ module ActiveFacts
37
37
 
38
38
  # Each definition has an ast() method that returns an instance of a subclass of Compiler::Definition
39
39
  rule definition
40
- definition_body s
41
- {
42
- def ast
43
- definition_body.ast
44
- end
45
-
46
- def body
47
- definition_body.text_value
48
- end
49
- }
40
+ definition_body s
41
+ {
42
+ def ast
43
+ definition_body.ast
44
+ end
45
+
46
+ def body
47
+ definition_body.text_value
48
+ end
49
+ }
50
50
  end
51
51
 
52
52
  rule definition_body
@@ -56,30 +56,31 @@ module ActiveFacts
56
56
  / constraint
57
57
  / unit_definition # REVISIT: Move this above the prescan?
58
58
  / object_type
59
- / query
60
- / s ';' s { def ast; nil; end }
59
+ / informal_description
60
+ / query
61
+ / s ';' s { def ast; nil; end }
61
62
  end
62
63
 
63
64
  rule vocabulary_definition
64
65
  s vocabulary S vocabulary_name s ';'
65
66
  {
66
- def ast
67
- Compiler::Vocabulary.new(vocabulary_name.value)
68
- end
67
+ def ast
68
+ Compiler::Vocabulary.new(vocabulary_name.value)
69
+ end
69
70
  }
70
71
  end
71
72
 
72
73
  rule vocabulary_name
73
- id
74
- { def node_type; :vocabulary; end }
74
+ id
75
+ { def node_type; :vocabulary; end }
75
76
  end
76
77
 
77
78
  rule import_definition
78
79
  s import S vocabulary_name alias_list ';'
79
80
  {
80
- def ast
81
- Compiler::Import.new(import.input.parser, vocabulary_name.value, alias_list.value)
82
- end
81
+ def ast
82
+ Compiler::Import.new(import.input.parser, vocabulary_name.value, alias_list.value)
83
+ end
83
84
  }
84
85
  end
85
86
 
@@ -94,8 +95,27 @@ module ActiveFacts
94
95
  end
95
96
 
96
97
  rule alias_term
97
- id
98
- { def node_type; :term; end }
98
+ id
99
+ { def node_type; :term; end }
100
+ end
101
+
102
+ rule informal_description
103
+ informally s ',' s
104
+ subject:(
105
+ signifier:each S term # Informal definition of an object type
106
+ /
107
+ signifier:when S reading:phrase+ s ',' # or a fact type
108
+ ) s
109
+ text:(!(!. / '.' [ \t\r]* "\n") (string / .))* # Allow . inside a string
110
+ (!. / '.' [ \t\r]* "\n") # The description terminates in a fullstop at the end of a line, or EOF
111
+ {
112
+ def ast
113
+ kind = subject.signifier.text_value.to_sym
114
+ subject_name = (kind == :each ? subject.term.text_value : subject.reading.text_value)
115
+ phrases = subject.reading.elements.map(&:ast) if kind == :when
116
+ Compiler::InformalDefinition.new(kind, subject_name, phrases, text.text_value)
117
+ end
118
+ }
99
119
  end
100
120
 
101
121
  rule constraint
@@ -106,60 +126,60 @@ module ActiveFacts
106
126
  # REVISIT: / value_constraint
107
127
  end
108
128
 
129
+ # Adding enforcement to a constraint makes it deontic
109
130
  rule enforcement
110
- s '(' s otherwise s action s a:agent? s ')' s
111
- {
112
- def ast; Compiler::Enforcement.new(action.text_value, a.empty? ? nil : a.text_value); end
113
- }
114
- /
115
- ''
116
- {
117
- def ast; nil; end
118
- }
119
- end
120
-
121
- # An enforcement action, like SMS, email, log, alarm, etc.
122
- rule action
123
- id
131
+ s '(' s
132
+ otherwise s
133
+ action:id s # An enforcement action, like SMS, email, log, alarm, etc.
134
+ a:agent? s
135
+ ')' s
136
+ {
137
+ def ast; Compiler::Enforcement.new(action.text_value, a.empty? ? nil : a.text_value); end
138
+ }
139
+ /
140
+ ''
141
+ {
142
+ def ast; nil; end
143
+ }
124
144
  end
125
145
 
126
146
  # presence constraint:
127
147
  rule presence_constraint
128
- (each_occurs_in_clauses / either_or)
148
+ (each_occurs_in_clauses / either_or)
129
149
  {
130
- def ast
131
- Compiler::PresenceConstraint.new c, enforcement.ast, clauses_ast, role_list_ast, quantifier_ast
132
- end
133
- }
150
+ def ast
151
+ Compiler::PresenceConstraint.new c, enforcement.ast, clauses_ast, role_list_ast, quantifier_ast
152
+ end
153
+ }
134
154
  end
135
155
 
136
156
  # set (exclusion, mandatory exclusion, complex equality) constraint
137
157
  rule set_constraint
138
- (for_each_how_many / either_or_not_both)
158
+ (for_each_how_many / either_or_not_both)
139
159
  {
140
- def ast
141
- Compiler::SetExclusionConstraint.new c, enforcement.ast, clauses_ast, role_list_ast, quantifier_ast
142
- end
143
- }
160
+ def ast
161
+ Compiler::SetExclusionConstraint.new c, enforcement.ast, clauses_ast, role_list_ast, quantifier_ast
162
+ end
163
+ }
144
164
  end
145
165
 
146
166
  rule subset_constraint
147
167
  (a_only_if_b / if_b_then_a)
148
168
  {
149
- def ast
150
- Compiler::SubsetConstraint.new c, enforcement.ast, [clauses.ast, r2.ast]
151
- end
152
- }
169
+ def ast
170
+ Compiler::SubsetConstraint.new c, enforcement.ast, [clauses.ast, r2.ast]
171
+ end
172
+ }
153
173
  end
154
174
 
155
175
  rule equality_constraint
156
- if_and_only_if
176
+ if_and_only_if
157
177
  {
158
- def ast
159
- all_clauses = [clauses.ast, *tail.elements.map{|e| e.clauses.ast }]
160
- Compiler::SetEqualityConstraint.new c, enforcement.ast, all_clauses
161
- end
162
- }
178
+ def ast
179
+ all_clauses = [clauses.ast, *tail.elements.map{|e| e.clauses.ast }]
180
+ Compiler::SetEqualityConstraint.new c, enforcement.ast, all_clauses
181
+ end
182
+ }
163
183
  end
164
184
 
165
185
  end