activefacts-cql 1.8.1 → 1.8.2

Sign up to get free protection for your applications and to get access to all the features.
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