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
@@ -25,12 +25,12 @@ module ActiveFacts
25
25
  @identification = identification
26
26
  @pragmas = pragmas
27
27
  @clauses = clauses || []
28
- @context_note = context_note
28
+ @context_note = context_note
29
29
  end
30
30
 
31
31
  def compile
32
32
  @entity_type = @vocabulary.valid_entity_type_name(@name) ||
33
- @constellation.EntityType(@vocabulary, @name, :concept => :new)
33
+ @constellation.EntityType(@vocabulary, @name, :concept => :new)
34
34
  @entity_type.is_independent = true if @pragmas.delete('independent')
35
35
 
36
36
  # REVISIT: CQL needs a way to indicate whether subtype migration can occur.
@@ -38,9 +38,9 @@ module ActiveFacts
38
38
  @supertypes.each_with_index do |supertype_name, i|
39
39
  add_supertype(supertype_name, @identification || i > 0)
40
40
  end
41
- @pragmas.each do |p|
42
- @constellation.ConceptAnnotation(:concept => @entity_type.concept, :mapping_annotation => p)
43
- end if @pragmas
41
+ @pragmas.each do |p|
42
+ @constellation.ConceptAnnotation(:concept => @entity_type.concept, :mapping_annotation => p)
43
+ end if @pragmas
44
44
 
45
45
  context = CompilationContext.new(@vocabulary)
46
46
 
@@ -61,9 +61,9 @@ module ActiveFacts
61
61
 
62
62
  make_preferred_identifier_over_roles identifying_roles
63
63
 
64
- if @context_note
65
- @context_note.compile(@constellation, @entity_type)
66
- end
64
+ if @context_note
65
+ @context_note.compile(@constellation, @entity_type)
66
+ end
67
67
 
68
68
  @clauses.each do |clause|
69
69
  next unless clause.context_note
@@ -138,7 +138,7 @@ module ActiveFacts
138
138
  #:is_mandatory => true,
139
139
  #:min_frequency => 1,
140
140
  )
141
- trace :constraint, "Made new preferred PC GUID=#{pc.concept.guid} min=nil max=1 over #{role_sequence.describe}"
141
+ trace :constraint, "Made new preferred PC GUID=#{pc.concept.guid} min=nil max=1 over #{role_sequence.describe}"
142
142
  end
143
143
  end
144
144
 
@@ -165,9 +165,9 @@ module ActiveFacts
165
165
 
166
166
  fact_type = create_identifying_fact_type(context, clauses)
167
167
  fact_types << fact_type if fact_type
168
- unless fact_type.all_role.detect{|r| r.object_type == @entity_type}
169
- objectify_existing_fact_type(fact_type)
170
- end
168
+ unless fact_type.all_role.detect{|r| r.object_type == @entity_type}
169
+ objectify_existing_fact_type(fact_type)
170
+ end
171
171
  end
172
172
  fact_types
173
173
  end
@@ -175,9 +175,9 @@ module ActiveFacts
175
175
  def create_identifying_fact_type context, clauses
176
176
  # See if any fact type already exists (this ET cannot be a player, but might objectify it)
177
177
  existing_clauses = clauses.select{ |clause| clause.match_existing_fact_type context }
178
- if negation = existing_clauses.detect{|c| c.certainty == false }
179
- raise "#{@name} cannot be identified by negated fact type #{negation.inspect}"
180
- end
178
+ if negation = existing_clauses.detect{|c| c.certainty == false }
179
+ raise "#{@name} cannot be identified by negated fact type #{negation.inspect}"
180
+ end
181
181
  any_matched = existing_clauses.size > 0
182
182
 
183
183
  operation = any_matched ? 'Objectifying' : 'Creating'
@@ -205,9 +205,9 @@ module ActiveFacts
205
205
 
206
206
  def objectify_existing_fact_type fact_type
207
207
  raise "#{@name} cannot objectify fact type '#{fact_type.entity_type.name}' that's already objectified" if fact_type.entity_type
208
- if @fact_type
209
- raise "#{@name} cannot objectify '#{fact_type.default_reading}', it already objectifies '#{@fact_type.default_reading}'"
210
- end
208
+ if @fact_type
209
+ raise "#{@name} cannot objectify '#{fact_type.default_reading}', it already objectifies '#{@fact_type.default_reading}'"
210
+ end
211
211
 
212
212
  if fact_type.internal_presence_constraints.select{|pc| pc.max_frequency == 1}.size == 0
213
213
  # If there's no existing uniqueness constraint over this fact type, make a spanning one.
@@ -219,7 +219,7 @@ module ActiveFacts
219
219
  :is_preferred_identifier => false, # We only get here when there is a reference mode on the entity type
220
220
  :max_frequency => 1
221
221
  )
222
- trace :constraint, "Made new objectification PC GUID=#{pc.concept.guid} min=nil max=1 over #{fact_type.preferred_reading.role_sequence.describe}"
222
+ trace :constraint, "Made new objectification PC GUID=#{pc.concept.guid} min=nil max=1 over #{fact_type.preferred_reading.role_sequence.describe}"
223
223
  end
224
224
 
225
225
  @fact_type = @entity_type.fact_type = fact_type
@@ -230,7 +230,7 @@ module ActiveFacts
230
230
  def add_supertype(supertype_name, not_identifying)
231
231
  trace :supertype, "Adding #{not_identifying ? '' : 'identifying '}supertype #{supertype_name} to #{@entity_type.name}" do
232
232
  supertype = @vocabulary.valid_entity_type_name(supertype_name) ||
233
- @constellation.EntityType(@vocabulary, supertype_name, :concept => :new) # Should always already exist
233
+ @constellation.EntityType(@vocabulary, supertype_name, :concept => :new) # Should always already exist
234
234
 
235
235
  # Did we already know about this supertyping?
236
236
  return if @entity_type.all_type_inheritance_as_subtype.detect{|ti| ti.supertype == supertype}
@@ -238,12 +238,12 @@ module ActiveFacts
238
238
  # By default, the first supertype identifies this entity type
239
239
  is_identifying_supertype = !not_identifying && @entity_type.all_type_inheritance_as_subtype.size == 0
240
240
 
241
- assimilation_pragmas = ['absorbed', 'separate', 'partitioned']
241
+ assimilation_pragmas = ['absorbed', 'separate', 'partitioned']
242
242
  assimilations = @pragmas.select { |p| assimilation_pragmas.include? p}
243
- @pragmas -= assimilation_pragmas
243
+ @pragmas -= assimilation_pragmas
244
244
  raise "Conflicting assimilation pragmas #{assimilations*', '}" if assimilations.size > 1
245
245
 
246
- @entity_type.add_supertype(supertype, is_identifying_supertype, assimilations[0])
246
+ @entity_type.add_supertype(supertype, is_identifying_supertype, assimilations[0])
247
247
  end
248
248
  end
249
249
 
@@ -252,11 +252,11 @@ module ActiveFacts
252
252
  vt = nil
253
253
  trace :entity, "Preparing value type #{vt_name} for reference mode" do
254
254
  # Find an existing ValueType called 'vt_name' or 'name vtname'
255
- # or find/create the supertype '#{mode}' and the subtype
255
+ # or find/create the supertype '#{mode}' and the subtype
256
256
  unless vt = @vocabulary.valid_object_type_name(vt_name) or
257
- vt = @vocabulary.valid_object_type_name(vt_name = "#{name} #{mode}")
257
+ vt = @vocabulary.valid_object_type_name(vt_name = "#{name} #{mode}")
258
258
  base_vt = @vocabulary.valid_value_type_name(mode) ||
259
- @constellation.ValueType(@vocabulary, mode, :concept => :new)
259
+ @constellation.ValueType(@vocabulary, mode, :concept => :new)
260
260
  vt = @constellation.ValueType(@vocabulary, vt_name, :supertype => base_vt, :concept => :new)
261
261
  if parameters
262
262
  length, scale = *parameters
@@ -358,7 +358,7 @@ module ActiveFacts
358
358
  :is_preferred_identifier => false,
359
359
  :is_mandatory => true
360
360
  )
361
- trace :constraint, "Made new refmode PC GUID=#{constraint.concept.guid} min=1 max=1 over #{rs0.describe}"
361
+ trace :constraint, "Made new refmode PC GUID=#{constraint.concept.guid} min=1 max=1 over #{rs0.describe}"
362
362
  else
363
363
  trace :mode, "Using existing EntityType PresenceConstraint"
364
364
  end
@@ -386,7 +386,7 @@ module ActiveFacts
386
386
  :is_preferred_identifier => true,
387
387
  :is_mandatory => false
388
388
  )
389
- trace :constraint, "Made new refmode ValueType PC GUID=#{constraint.concept.guid} min=0 max=1 over #{rs1.describe}"
389
+ trace :constraint, "Made new refmode ValueType PC GUID=#{constraint.concept.guid} min=0 max=1 over #{rs1.describe}"
390
390
  else
391
391
  trace :mode, "Marking existing ValueType PresenceConstraint as preferred"
392
392
  rs1.all_presence_constraint.single.is_preferred_identifier = true
@@ -28,7 +28,7 @@ module ActiveFacts
28
28
  def trailing_adjective; nil; end
29
29
  def value_constraint; nil; end
30
30
  def literal; nil; end
31
- def side_effects; nil; end
31
+ def side_effects; nil; end
32
32
  attr_accessor :player # What ObjectType does the Binding denote
33
33
  attr_accessor :binding # What Binding for that ObjectType
34
34
  attr_accessor :clause # What clause does the result participate in?
@@ -48,9 +48,9 @@ module ActiveFacts
48
48
  attr_reader :fact_type
49
49
  def objectified_as; self; end # The Reference which objectified this fact type
50
50
 
51
- def initialize
52
- @certainty = true # Assume it's definite
53
- end
51
+ def initialize
52
+ @certainty = true # Assume it's definite
53
+ end
54
54
 
55
55
  def operands context = nil
56
56
  raise "REVISIT: Implement operand enumeration in the operator subclass #{self.class.name}"
@@ -92,8 +92,8 @@ module ActiveFacts
92
92
  def result_value_type(context, name)
93
93
  vocabulary = context.vocabulary
94
94
  constellation = vocabulary.constellation
95
- vocabulary.valid_value_type_name(name) ||
96
- constellation.ValueType(vocabulary, name, :concept => :new)
95
+ vocabulary.valid_value_type_name(name) ||
96
+ constellation.ValueType(vocabulary, name, :concept => :new)
97
97
  end
98
98
 
99
99
  def is_naked_object_type
@@ -117,9 +117,9 @@ module ActiveFacts
117
117
  # REVISIT: We should auto-create steps from Entity Types to an identifying ValueType
118
118
  # REVISIT: We should traverse up the supertype of ValueTypes to find a DataType
119
119
  @fact_type = clause_ast.match_existing_fact_type(context, :exact_type => true)
120
- if clause.certainty == false
121
- raise "Negated fact types in expressions are not yet supported: #{clause.inspect}"
122
- end
120
+ if clause.certainty == false
121
+ raise "Negated fact types in expressions are not yet supported: #{clause.inspect}"
122
+ end
123
123
  return @fact_type if @fact_type
124
124
 
125
125
  @fact_type = clause_ast.make_fact_type context.vocabulary
@@ -130,9 +130,9 @@ module ActiveFacts
130
130
  opnds.each do |opnd|
131
131
  next unless opnd.is_a?(Operation)
132
132
  opnd.match_existing_fact_type context
133
- if opnd.certainty == false
134
- raise "Negated fact types in expressions are not yet supported: #{opnd.inspect}"
135
- end
133
+ if opnd.certainty == false
134
+ raise "Negated fact types in expressions are not yet supported: #{opnd.inspect}"
135
+ end
136
136
  end
137
137
  @fact_type
138
138
  end
@@ -189,8 +189,8 @@ module ActiveFacts
189
189
  end
190
190
  v = context.vocabulary
191
191
  @boolean ||=
192
- v.constellation.ValueType[[[v.name], 'Boolean']] ||
193
- v.constellation.ValueType(v, 'Boolean', :concept => :new)
192
+ v.constellation.ValueType[[[v.name], 'Boolean']] ||
193
+ v.constellation.ValueType(v, 'Boolean', :concept => :new)
194
194
  @player = @boolean
195
195
  end
196
196
  end
@@ -207,20 +207,20 @@ module ActiveFacts
207
207
 
208
208
  def to_s
209
209
  "COMPARE#{
210
- operator
211
- }(#{
212
- case @certainty
213
- when nil; 'maybe '
214
- when false; 'negated '
215
- # else 'definitely '
216
- end
210
+ operator
211
+ }(#{
212
+ case @certainty
213
+ when nil; 'maybe '
214
+ when false; 'negated '
215
+ # else 'definitely '
216
+ end
217
+ }#{
218
+ e1.to_s
219
+ } WITH #{
220
+ e2.to_s
217
221
  }#{
218
- e1.to_s
219
- } WITH #{
220
- e2.to_s
221
- }#{
222
- @qualifiers.empty? ? '' : ', ['+@qualifiers*', '+']'
223
- })"
222
+ @qualifiers.empty? ? '' : ', ['+@qualifiers*', '+']'
223
+ })"
224
224
  end
225
225
  end
226
226
 
@@ -10,17 +10,17 @@ module ActiveFacts
10
10
 
11
11
  def compile
12
12
  @population = @constellation.Population[[@vocabulary.identifying_role_values, @population_name]] ||
13
- @constellation.Population(@vocabulary, @population_name, :concept => :new)
13
+ @constellation.Population(@vocabulary, @population_name, :concept => :new)
14
14
 
15
15
  @context = CompilationContext.new(@vocabulary)
16
16
  @context.bind @clauses
17
17
  @context.left_contraction_allowed = true
18
18
  @clauses.each do |clause|
19
- ft = clause.match_existing_fact_type @context
20
- if clause.certainty == false
21
- raise "Negated fact #{clause.inspect} is not supported"
22
- end
23
- end
19
+ ft = clause.match_existing_fact_type @context
20
+ if clause.certainty == false
21
+ raise "Negated fact #{clause.inspect} is not supported"
22
+ end
23
+ end
24
24
 
25
25
  # Figure out the simple existential facts and find fact types:
26
26
  @bound_facts = []
@@ -84,16 +84,16 @@ module ActiveFacts
84
84
  # Find the roles of this clause that do not yet have an instance
85
85
  bare_roles = clause.refs.
86
86
  select do |ref|
87
- if !ref.binding.instance and ref.literal
87
+ if !ref.binding.instance and ref.literal
88
88
  ref.binding.instance = instance_identified_by_literal(ref.binding.player, ref.literal)
89
- end
89
+ end
90
90
 
91
91
  next false if ref.binding.instance
92
92
  true
93
93
  end
94
94
 
95
- trace :instance_detail, "Considering '#{clause.display}' with "+
96
- (bare_roles.empty? ? "no bare roles" : "bare roles: #{bare_roles.map{|ref| ref.player.name}*", "}") do
95
+ trace :instance_detail, "Considering '#{clause.display}' with "+
96
+ (bare_roles.empty? ? "no bare roles" : "bare roles: #{bare_roles.map{|ref| ref.player.name}*", "}") do
97
97
 
98
98
  # If all the roles are in place, we can bind the rest of this clause:
99
99
  return true if bare_roles.size == 0 && bind_complete_fact(clause)
@@ -149,7 +149,7 @@ module ActiveFacts
149
149
  fact = clause.refs[0].binding.instance.fact
150
150
  else
151
151
  # Check that this fact doesn't already exist
152
- trace :instance_detail, "Searching for existing fact instance"
152
+ trace :instance_detail, "Searching for existing fact instance"
153
153
 
154
154
  fact = clause.fact_type.all_fact.detect do |f|
155
155
 
@@ -207,7 +207,7 @@ module ActiveFacts
207
207
  return false unless identifying_binding # This happens when we have a bare objectification
208
208
  identifying_instance = identifying_binding.instance
209
209
  preferred_identifier = entity_type.preferred_identifier
210
- role_count = preferred_identifier.role_sequence.all_role_ref.size
210
+ role_count = preferred_identifier.role_sequence.all_role_ref.size
211
211
 
212
212
  trace :instance, "A #{binding.player.name} is #{role_count > 1 ? 'partly ':''}identified in #{clause.inspect}"
213
213
 
@@ -251,21 +251,21 @@ module ActiveFacts
251
251
  return false
252
252
  end
253
253
 
254
- instance = @constellation.Instance(:new, :object_type => entity_type, :population => @population)
255
- binding.instance = instance
256
- @bound_facts << instance
257
- identifiers.each do |rr, identifying_clause, identifying_binding, identifying_instance|
258
- # This clause provides the identifying literal for the entity_type
259
- id_fact =
260
- identifying_clause.fact =
261
- @constellation.Fact(:new, :fact_type => rr.role.fact_type, :population => @population)
262
- @bound_facts << id_fact
263
- role = (rr.role.fact_type.all_role.to_a-[rr.role])[0]
264
- @constellation.RoleValue(:instance => instance, :fact => id_fact, :population => @population, :role => role)
265
- @constellation.RoleValue(:instance => identifying_instance, :fact => id_fact, :role => rr.role, :population => @population)
266
- trace :instance, "Assert #{id_fact.verbalise.inspect} (existential) #{@population.name.size>0 ? " in "+@population.name.inspect : ''}"
267
- end
268
- trace :instance, "Assert #{instance.verbalise.inspect} #{@population.name.size>0 ? " in "+@population.name.inspect : ''}"
254
+ instance = @constellation.Instance(:new, :object_type => entity_type, :population => @population)
255
+ binding.instance = instance
256
+ @bound_facts << instance
257
+ identifiers.each do |rr, identifying_clause, identifying_binding, identifying_instance|
258
+ # This clause provides the identifying literal for the entity_type
259
+ id_fact =
260
+ identifying_clause.fact =
261
+ @constellation.Fact(:new, :fact_type => rr.role.fact_type, :population => @population)
262
+ @bound_facts << id_fact
263
+ role = (rr.role.fact_type.all_role.to_a-[rr.role])[0]
264
+ @constellation.RoleValue(:instance => instance, :fact => id_fact, :population => @population, :role => role)
265
+ @constellation.RoleValue(:instance => identifying_instance, :fact => id_fact, :role => rr.role, :population => @population)
266
+ trace :instance, "Assert #{id_fact.verbalise.inspect} (existential) #{@population.name.size>0 ? " in "+@population.name.inspect : ''}"
267
+ end
268
+ trace :instance, "Assert #{instance.verbalise.inspect} #{@population.name.size>0 ? " in "+@population.name.inspect : ''}"
269
269
 
270
270
  true # Done with this clause
271
271
  end
@@ -276,11 +276,11 @@ module ActiveFacts
276
276
  else
277
277
  trace :instance_detail, "Assert Value #{object_type.name} #{literal.inspect}" do
278
278
  is_literal_string = literal.literal.is_a?(String)
279
- # REVISIT: Check for subtypes and supertypes also, and promote type if necessary
280
- instance = object_type.all_instance.detect do |i|
279
+ # REVISIT: Check for subtypes and supertypes also, and promote type if necessary
280
+ instance = object_type.all_instance.detect do |i|
281
281
  #instance = @constellation.Instance.detect do |key, i|
282
282
  # REVISIT: And same unit
283
- trace :instance_detail2, "Comparing #{i.value.literal.inspect} to #{literal.literal.to_s.inspect}"
283
+ trace :instance_detail2, "Comparing #{i.value.literal.inspect} to #{literal.literal.to_s.inspect}"
284
284
  i.population == @population &&
285
285
  i.value &&
286
286
  i.value.literal.inspect == literal.literal.to_s.inspect &&
@@ -289,14 +289,14 @@ module ActiveFacts
289
289
  #instance = object_type.all_instance.detect { |instance|
290
290
  # instance.population == @population && instance.value == literal
291
291
  #}
292
- if instance
293
- trace :instance, "Instance already known: #{instance.verbalise.inspect}"
292
+ if instance
293
+ trace :instance, "Instance already known: #{instance.verbalise.inspect}"
294
294
  else
295
295
  instance = @constellation.Instance(:new)
296
296
  instance.object_type = object_type
297
297
  instance.population = @population
298
298
  instance.value = [literal.to_s, is_literal_string, nil]
299
- trace :instance, "Assert #{instance.verbalise.inspect} #{@population.name.size>0 ? " in "+@population.name.inspect : ''}"
299
+ trace :instance, "Assert #{instance.verbalise.inspect} #{@population.name.size>0 ? " in "+@population.name.inspect : ''}"
300
300
  @bound_facts << instance
301
301
  end
302
302
  instance
@@ -323,20 +323,20 @@ module ActiveFacts
323
323
  }
324
324
  if instance_rv
325
325
  instance = existing_instance
326
- trace :instance, "Already known: #{instance.verbalise.inspect}"
326
+ trace :instance, "Already known: #{instance.verbalise.inspect}"
327
327
  else
328
328
  # This fact has no clause.
329
- trace :instance_detail, "Creating implicit existential fact #{role.fact_type.default_reading}"
329
+ trace :instance_detail, "Creating implicit existential fact #{role.fact_type.default_reading}"
330
330
  fact = @constellation.Fact(:new, :fact_type => role.fact_type, :population => @population)
331
331
  @bound_facts << fact
332
332
  # This instance will be associated with its binding by our caller
333
333
  instance = @constellation.Instance(:new, :object_type => object_type, :population => @population)
334
- trace :instance_detail, "Creating Entity #{object_type.name} identified by '#{literal}' #{@population.name.size>0 ? " in "+@population.name.inspect : ''}"
334
+ trace :instance_detail, "Creating Entity #{object_type.name} identified by '#{literal}' #{@population.name.size>0 ? " in "+@population.name.inspect : ''}"
335
335
  @bound_facts << instance
336
336
  # The identifying fact type has two roles; create both role instances:
337
337
  @constellation.RoleValue(:instance => identifying_instance, :fact => fact, :population => @population, :role => role)
338
338
  @constellation.RoleValue(:instance => instance, :fact => fact, :population => @population, :role => (role.fact_type.all_role-[role])[0])
339
- trace :instance, "Assert #{instance.verbalise.inspect}"
339
+ trace :instance, "Assert #{instance.verbalise.inspect}"
340
340
  end
341
341
  instance
342
342
  end
@@ -12,17 +12,17 @@ module ActiveFacts
12
12
  @returning = returning || []
13
13
  end
14
14
 
15
- def to_s
16
- inspect
17
- end
15
+ def to_s
16
+ inspect
17
+ end
18
18
 
19
19
  def inspect
20
20
  "Query: " +
21
- if @conditions.empty?
22
- ''
23
- else
24
- 'where ' + @conditions.map{|c| ((j=c.conjunction) ? j+' ' : '') + c.inspect}*' '
25
- end
21
+ if @conditions.empty?
22
+ ''
23
+ else
24
+ 'where ' + @conditions.map{|c| ((j=c.conjunction) ? j+' ' : '') + c.inspect}*' '
25
+ end
26
26
  # REVISIT: @returning = returning
27
27
  end
28
28
 
@@ -55,10 +55,10 @@ module ActiveFacts
55
55
 
56
56
  def match_condition_fact_types
57
57
  @conditions.each do |condition|
58
- trace :projection, "matching condition fact_type #{condition.inspect}" do
59
- fact_type = condition.match_existing_fact_type @context
60
- raise "Unrecognised fact type #{condition.inspect} in #{self.class}" unless fact_type
61
- end
58
+ trace :projection, "matching condition fact_type #{condition.inspect}" do
59
+ fact_type = condition.match_existing_fact_type @context
60
+ raise "Unrecognised fact type #{condition.inspect} in #{self.class}" unless fact_type
61
+ end
62
62
  end
63
63
  end
64
64
 
@@ -105,12 +105,12 @@ module ActiveFacts
105
105
  # * Objectify the fact type if @name
106
106
  #
107
107
 
108
- # Prepare to objectify the fact type (so readings for link fact types can be created)
108
+ # Prepare to objectify the fact type (so readings for link fact types can be created)
109
109
  if @name
110
- entity_type = @vocabulary.valid_entity_type_name(@name)
110
+ entity_type = @vocabulary.valid_entity_type_name(@name)
111
111
  raise "You can't objectify #{@name}, it already exists" if entity_type
112
112
  @entity_type = @constellation.EntityType(@vocabulary, @name, :fact_type => @fact_type, :concept => :new)
113
- end
113
+ end
114
114
 
115
115
  prepare_roles @clauses
116
116
 
@@ -119,13 +119,13 @@ module ActiveFacts
119
119
 
120
120
  return true unless @clauses.size > 0 # Nothing interesting was said.
121
121
 
122
- if @entity_type
123
- # Extract readings for link fact types
124
- @link_fact_type_clauses, @clauses =
125
- @clauses.partition do |clause|
126
- clause.refs.size == 2 and clause.refs.detect{|ref| ref.player == @entity_type}
127
- end
128
- end
122
+ if @entity_type
123
+ # Extract readings for link fact types
124
+ @link_fact_type_clauses, @clauses =
125
+ @clauses.partition do |clause|
126
+ clause.refs.size == 2 and clause.refs.detect{|ref| ref.player == @entity_type}
127
+ end
128
+ end
129
129
 
130
130
  # See if any existing fact type is being invoked (presumably to objectify or extend it)
131
131
  @fact_type = check_compatibility_of_matched_clauses
@@ -139,7 +139,7 @@ module ActiveFacts
139
139
  first_clause.make_embedded_constraints vocabulary
140
140
  @existing_clauses = [first_clause]
141
141
  elsif (n = @clauses.size - @existing_clauses.size) > 0
142
- raise "Cannot extend a negated fact type" if @existing_clauses.detect {|clause| clause.certainty == false }
142
+ raise "Cannot extend a negated fact type" if @existing_clauses.detect {|clause| clause.certainty == false }
143
143
  trace :binding, "Extending existing fact type with #{n} new readings"
144
144
  end
145
145
 
@@ -158,20 +158,20 @@ module ActiveFacts
158
158
  end
159
159
 
160
160
  if @name
161
- # Objectify the fact type:
161
+ # Objectify the fact type:
162
162
  @entity_type.fact_type = @fact_type
163
163
  if @fact_type.entity_type and @name != @fact_type.entity_type.name
164
164
  raise "Cannot objectify fact type as #{@name} and as #{@fact_type.entity_type.name}"
165
165
  end
166
166
  ifts = @entity_type.create_link_fact_types
167
- create_link_fact_type_readings(ifts)
167
+ create_link_fact_type_readings(ifts)
168
168
  if @pragmas
169
169
  @entity_type.is_independent = true if @pragmas.delete('independent')
170
170
  end
171
171
  end
172
- @pragmas.each do |p|
173
- @constellation.ConceptAnnotation(:concept => (@entity_type||@fact_type).concept, :mapping_annotation => p)
174
- end if @pragmas
172
+ @pragmas.each do |p|
173
+ @constellation.ConceptAnnotation(:concept => (@entity_type||@fact_type).concept, :mapping_annotation => p)
174
+ end if @pragmas
175
175
 
176
176
  @clauses.each do |clause|
177
177
  next unless clause.context_note
@@ -182,36 +182,40 @@ module ActiveFacts
182
182
  make_default_identifier_for_fact_type if @conditions.empty?
183
183
 
184
184
  # Compile the conditions:
185
- super
185
+ query = super
186
186
  unless @conditions.empty?
187
187
  @clauses.each do |clause|
188
188
  project_clause_roles(clause)
189
189
  end
190
+
191
+ # Add the head to complete the derived fact type
192
+ query.derived_fact_type = @fact_type unless query.nil?
193
+
190
194
  end
191
195
 
192
196
  @fact_type
193
197
  end
194
198
 
195
- def create_link_fact_type_readings(ifts)
196
- @link_fact_type_clauses.each do |clause|
197
- #next false unless clause.refs.size == 2 and clause.refs.detect{|r| r.role.object_type == @entity_type }
198
- other_ref = clause.refs.detect{|ref| ref.player != @entity_type}
199
- ift = ifts.detect do |ift|
200
- other_ref.binding.refs.map{|r| r.role}.include?(ift.implying_role)
201
- end
202
- unless ift
203
- raise "Clause #{clause} in unmatched in definition of objectified fact type #{@name}"
204
- next
205
- end
206
- # This clause is a reading for the LinkFactType ift
207
- i = 0
208
- reading_text = clause.phrases.map do |phrase|
209
- next phrase if String === phrase
210
- "{#{(i += 1)-1}}"
211
- end*' '
212
- clause.make_reading(@vocabulary, ift)
213
- end
214
- end
199
+ def create_link_fact_type_readings(ifts)
200
+ @link_fact_type_clauses.each do |clause|
201
+ #next false unless clause.refs.size == 2 and clause.refs.detect{|r| r.role.object_type == @entity_type }
202
+ other_ref = clause.refs.detect{|ref| ref.player != @entity_type}
203
+ ift = ifts.detect do |ift|
204
+ other_ref.binding.refs.map{|r| r.role}.include?(ift.implying_role)
205
+ end
206
+ unless ift
207
+ raise "Clause #{clause} in unmatched in definition of objectified fact type #{@name}"
208
+ next
209
+ end
210
+ # This clause is a reading for the LinkFactType ift
211
+ i = 0
212
+ reading_text = clause.phrases.map do |phrase|
213
+ next phrase if String === phrase
214
+ "{#{(i += 1)-1}}"
215
+ end*' '
216
+ clause.make_reading(@vocabulary, ift)
217
+ end
218
+ end
215
219
 
216
220
  def project_clause_roles(clause)
217
221
  # Attach the clause's role references to the projected roles of the query
@@ -235,7 +239,7 @@ module ActiveFacts
235
239
  def check_compatibility_of_matched_clauses
236
240
  # REVISIT: If we have conditions, we must match all given clauses exactly (no side-effects)
237
241
  @existing_clauses = @clauses.
238
- select{ |clause| clause.match_existing_fact_type @context }.
242
+ select{ |clause| clause.match_existing_fact_type(@context, exact_type: true) }.
239
243
  # subtyping match is not allowed for fact type extension:
240
244
  reject{ |clause| clause.side_effects.role_side_effects.detect{|se| se.common_supertype } }.
241
245
  sort_by{ |clause| clause.side_effects.cost }
@@ -292,41 +296,43 @@ module ActiveFacts
292
296
 
293
297
  # We need to check uniqueness constraints after processing the whole vocabulary
294
298
  # raise "Fact type must be named as it has no identifying uniqueness constraint" unless @name || @fact_type.all_role.size == 1
295
- trace :constraint, "Need to check #{@fact_type.default_reading.inspect} for a uniqueness constraint"
296
- fact_type.check_and_add_spanning_uniqueness_constraint = proc do
297
- trace :constraint, "Checking #{@fact_type.default_reading.inspect} for a uniqueness constraint"
298
- existing_pc = nil
299
- found = @fact_type.all_role.
300
- detect do |role|
301
- role.all_role_ref.detect do |rr|
302
- # This RoleSequence, to be relevant, must only reference roles of this fact type
303
- rr.role_sequence.all_role_ref.all? {|rr2| rr2.role.fact_type == @fact_type} and
304
- # The RoleSequence must have at least one uniqueness constraint
305
- rr.role_sequence.all_presence_constraint.detect do |pc|
306
- if pc.max_frequency == 1
307
- existing_pc = pc
308
- end
309
- end
310
- end
311
- end
312
- true # A place for a breakpoint
313
-
314
- if !found
315
- # There's no existing uniqueness constraint over the roles of this fact type. Add one
316
- pc = @constellation.PresenceConstraint(
317
- :new,
318
- :vocabulary => @vocabulary,
319
- :name => @fact_type.entity_type ? @fact_type.entity_type.name+"PK" : '',
320
- :role_sequence => (rs = @fact_type.preferred_reading.role_sequence),
321
- :max_frequency => 1,
322
- :is_preferred_identifier => true # (prefer || !!@fact_type.entity_type)
323
- )
324
- pc.concept.topic = @fact_type.concept.topic
325
- trace :constraint, "Made new fact type implicit PC GUID=#{pc.concept.guid} #{pc.name} min=nil max=1 over #{rs.describe}"
326
- elsif pc
327
- trace :constraint, "Will rely on existing UC GUID=#{pc.concept.guid} #{pc.name} to be used as PI over #{rs.describe}"
299
+ unless @fact_type.all_role.size == 1
300
+ trace :constraint, "Need to check #{@fact_type.default_reading.inspect} for a uniqueness constraint"
301
+ fact_type.check_and_add_spanning_uniqueness_constraint = proc do
302
+ trace :constraint, "Checking #{@fact_type.default_reading.inspect} for a uniqueness constraint"
303
+ existing_pc = nil
304
+ found = @fact_type.all_role.
305
+ detect do |role|
306
+ role.all_role_ref.detect do |rr|
307
+ # This RoleSequence, to be relevant, must only reference roles of this fact type
308
+ rr.role_sequence.all_role_ref.all? {|rr2| rr2.role.fact_type == @fact_type} and
309
+ # The RoleSequence must have at least one uniqueness constraint
310
+ rr.role_sequence.all_presence_constraint.detect do |pc|
311
+ if pc.max_frequency == 1
312
+ existing_pc = pc
313
+ end
314
+ end
315
+ end
316
+ end
317
+ true # A place for a breakpoint
318
+
319
+ if !found
320
+ # There's no existing uniqueness constraint over the roles of this fact type. Add one
321
+ pc = @constellation.PresenceConstraint(
322
+ :concept => [:new, {:implication_rule => "spanning"}],
323
+ :vocabulary => @vocabulary,
324
+ :name => @fact_type.entity_type ? @fact_type.entity_type.name+"PK" : '',
325
+ :role_sequence => (rs = @fact_type.preferred_reading.role_sequence),
326
+ :max_frequency => 1,
327
+ :is_preferred_identifier => true # (prefer || !!@fact_type.entity_type)
328
+ )
329
+ pc.concept.topic = @fact_type.concept.topic
330
+ trace :constraint, "Made new fact type implicit PC GUID=#{pc.concept.guid} #{pc.name} min=nil max=1 over #{rs.describe}"
331
+ elsif pc
332
+ trace :constraint, "Will rely on existing UC GUID=#{pc.concept.guid} #{pc.name} to be used as PI over #{rs.describe}"
333
+ end
328
334
  end
329
- end
335
+ end
330
336
  end
331
337
 
332
338
  def has_more_adjectives(less, more)
@@ -407,7 +413,7 @@ module ActiveFacts
407
413
  end
408
414
 
409
415
  def inspect
410
- s = super
416
+ s = super
411
417
  "FactType: #{@conditions.size > 0 ? super+' ' : '' }#{@clauses.inspect}" +
412
418
  (@pragmas && @pragmas.size > 0 ? ", pragmas [#{@pragmas.flatten.sort*','}]" : '')
413
419