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
@@ -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