activefacts 0.8.8 → 0.8.9

Sign up to get free protection for your applications and to get access to all the features.
@@ -54,7 +54,7 @@ Event Control has at most one Point Value;
54
54
 
55
55
  Event Scoring Method is where
56
56
  Scoring Method is used for Course of Event,
57
- Course of Event uses at most one Scoring Method;
57
+ Course of Event uses one Scoring Method;
58
58
 
59
59
  Map is identified by its ID;
60
60
  Club (as Owner) owns Map,
@@ -87,7 +87,7 @@ Series has one Series Name (as Name),
87
87
 
88
88
  Entry is identified by its ID where
89
89
  Person entered Course of Event,
90
- Person entered Event in at most one Course;
90
+ Person entered Event in one Course;
91
91
  Entry received at most one Score;
92
92
  Entry finished in at most one finish-Placing;
93
93
 
@@ -21,8 +21,8 @@ Series Name is written as String;
21
21
  * Entity Types
22
22
  */
23
23
  Club is where
24
- Code is of the club called at most one Club Name,
25
- the club called Club Name has at most one Code;
24
+ Code is of the club called one Club Name,
25
+ the club called Club Name has one Code;
26
26
 
27
27
  Map is where
28
28
  map-Name having Accessibility belongs to Club;
@@ -35,7 +35,7 @@ Event is where
35
35
  event-ID is Series Event called Event Name run by Club using Map on Date at Location;
36
36
 
37
37
  Event Control is where
38
- Event includes Control which is worth at most one Point Value;
38
+ Event includes Control which is worth one Point Value;
39
39
 
40
40
  Event Course is where
41
41
  Course is available at Event,
@@ -46,13 +46,13 @@ Event Course is where
46
46
  */
47
47
  each Club occurs at least one time in
48
48
  event ID is Series Event called Event Name run by Club using Map on Date at Location;
49
- each Event Name occurs at most one time in
49
+ each Event Name occurs one time in
50
50
  event ID is Series Event called Event Name run by Club using Map on Date at Location;
51
51
  each Event occurs at least one time in
52
52
  Event includes Control which is worth Point Value;
53
53
  each ID occurs one time in
54
54
  event ID is Series Event called Event Name run by Club using Map on Date at Location;
55
- each Name occurs at most one time in
55
+ each Name occurs one time in
56
56
  map Name having Accessibility belongs to Club;
57
- each Series Event occurs at most one time in
57
+ each Series Event occurs one time in
58
58
  event ID is Series Event called Event Name run by Club using Map on Date at Location;
@@ -22,7 +22,7 @@ Student is enrolled in one School;
22
22
 
23
23
  Student Participation is where
24
24
  Student represents School in Activity,
25
- Student participates in Activity which is sanctioned by at most one School;
25
+ Student participates in Activity which is sanctioned by one School;
26
26
 
27
27
  /*
28
28
  * Constraints:
@@ -193,7 +193,7 @@ Service is identified by Vendor and Service Type where
193
193
  Service is of one Service Type;
194
194
 
195
195
  Data Store File Host System is where
196
- Data Store has at most one File Host System;
196
+ Data Store has one File Host System;
197
197
  Data Store File Host System has one Internal-Credential;
198
198
 
199
199
  Data Store Service is where
@@ -235,8 +235,8 @@ Switch is on private Network
235
235
  if and only if
236
236
  Data Store has Legacy Switch;
237
237
  Client has default Data Store
238
- only if Data Store belongs to Client;
239
- Data Store has Tcp Proxy Network
238
+ only if Client uses Data Store;
239
+ Tcp Proxy Network is in Data Store
240
240
  only if Network is ip_single;
241
241
  Network uses Domain Name
242
242
  only if Network is ip_single;
@@ -29,9 +29,10 @@ CEO runs Company,
29
29
  * Constraints:
30
30
  */
31
31
  either Employee reports to Manager(1) or Employee is a Manager(2) that is a CEO that runs Company but not both;
32
- CEO runs Company
32
+ Employee is a Manager that is a CEO that runs Company
33
33
  if and only if
34
- CEO is a kind of Manager that works for Company;
34
+ Employee works for Company;
35
+
35
36
  // This constraint cannot be expressed in NORMA until it adds explicit join paths:
36
37
  Employee(1) reports to Manager that is a kind of Employee(2) that works for Company
37
38
  if and only if
@@ -20,7 +20,7 @@ Event Date is identified by ymd where
20
20
  Party is identified by its Id [independent];
21
21
 
22
22
  Party Moniker is where
23
- Party is called at most one Party Name;
23
+ Party is called one Party Name;
24
24
  Party Moniker has one Accuracy;
25
25
 
26
26
  Person is a kind of Party;
@@ -19,7 +19,7 @@ Meal is identified by its Id;
19
19
  Waiter is identified by its Nr;
20
20
 
21
21
  WaiterTip is where
22
- Waiter for serving Meal reported a tip of at most one Amount;
22
+ Waiter for serving Meal reported a tip of one Amount;
23
23
 
24
24
  Service is where
25
25
  Waiter served Meal;
@@ -49,6 +49,18 @@ module ActiveFacts
49
49
  }
50
50
  end
51
51
 
52
+ rule fact_clauses
53
+ fact_clause
54
+ ftail:( (',' / and ) s fact_clause s )*
55
+ {
56
+ def ast
57
+ readings = fact_clause.ast
58
+ ftail.elements.each{|e| readings += e.fact_clause.ast }
59
+ readings
60
+ end
61
+ }
62
+ end
63
+
52
64
  rule returning_clause
53
65
  returning return (',' return)*
54
66
  end
@@ -212,10 +224,10 @@ module ActiveFacts
212
224
  end
213
225
 
214
226
  rule objectification_join
215
- '(' s where s fact_clause s ')' s
227
+ '(' s where s facts:fact_clauses s ')' s
216
228
  {
217
229
  def ast
218
- fact_clause.ast
230
+ facts.ast
219
231
  end
220
232
  }
221
233
  end
@@ -286,47 +286,76 @@ module ActiveFacts
286
286
  end
287
287
 
288
288
  def build_join_steps reading, constrained_rs, objectification_node = nil
289
- role_sequence = nil
290
- role_refs = []
289
+ join_roles = []
290
+ incidental_roles = []
291
291
  debug :join, "Creating join Role Sequence for #{reading.inspect} with #{reading.role_refs.size} role refs" do
292
+ objectification_step = nil
292
293
  reading.role_refs.each do |role_ref|
293
294
  # These role_refs are the Compiler::RoleRefs. These have associated Metamodel::RoleRefs,
294
- # but we need new RoleRefs to attach to the join (and save any residual_adjectives)
295
+ # but we need to create JoinRoles for those roles. # REVISIT: JoinRoles may need to save residual_adjectives
295
296
  binding = role_ref.binding
296
297
  role = role_ref.role || role_ref.role_ref.role
298
+ join_role = nil
297
299
 
298
300
  if (reading.fact_type.entity_type)
299
- # This reading is of an objectified fact type. The second role ref is to a phantom role.
300
- # We don't need join steps for roles that have only one role_ref (this one) in their binding
301
+ # This reading is of an objectified fact type.
302
+ # We need a join step from this role to the phantom role, but not
303
+ # for a role that has only one role_ref (this one) in their binding.
304
+ # Create the JoinNode and JoinRole in any case though.
301
305
  refs_count = binding.refs.size
302
306
  objectification_ref_count = 0
303
- role_ref.objectification_join.each{|r| objectification_ref_count += r.role_refs.select{|rr| rr.binding.refs.size > 1}.size} if role_ref.objectification_join
307
+ if role_ref.objectification_join
308
+ role_ref.objectification_join.each do |r|
309
+ objectification_ref_count += r.role_refs.select{|rr| rr.binding.refs.size > 1}.size
310
+ end
311
+ end
304
312
  refs_count += objectification_ref_count
305
- debug :join, "#{refs_count > 1 ? 'Creating' : 'Skipping'} Role Ref #{role_ref.inspect} (counts #{refs_count}/#{objectification_ref_count}) and objectification Join Step for #{role_ref.inspect}" do
306
-
307
- if (refs_count > 1)
308
- role_sequence ||= @constellation.RoleSequence(:new)
309
- role_refs <<
310
- @constellation.RoleRef(role_sequence, role_sequence.all_role_ref.size, :role => role, :join_node => binding.join_node)
311
- unless objectification_node
312
- # We need to create a JoinNode for this object, even though it has no RoleRefs
313
- # REVISIT: Or just complain and make the user show the objectification explicitly. Except that doesn't work, see Warehouse...
314
- raise "Can't join over fact type '#{reading.fact_type.default_reading}' because it's not objectified" unless reading.fact_type.entity_type
315
- join = binding.join_node.join
316
- debug :join, "Creating join node #{join.all_join_node.size} for #{reading.fact_type.entity_type.name} in objectification"
317
- objectification_node = @constellation.JoinNode(join, join.all_join_node.size, :concept => reading.fact_type.entity_type)
313
+
314
+ debug :join, "Creating Join Node #{role_ref.inspect} (counts #{refs_count}/#{objectification_ref_count}) and objectification Join Step for #{role_ref.inspect}" do
315
+
316
+ raise "Internal error: Trying to add role of #{role.concept.name} to join node for #{binding.join_node.concept.name}" unless binding.join_node.concept == role.concept
317
+ join_role = @constellation.JoinRole(binding.join_node, role)
318
+
319
+ if (refs_count <= 1) # Our work here is done if there are no other refs
320
+ if objectification_step
321
+ join_role.join_step = objectification_step
322
+ else
323
+ incidental_roles << join_role
318
324
  end
319
- @constellation.RoleRef(role_sequence, 1, :role => role.implicit_fact_type.all_role.single, :join_node => objectification_node)
320
- js = @constellation.JoinStep(objectification_node, binding.join_node, :fact_type => role.implicit_fact_type)
321
- debug :join, "New Join Step #{js.describe}"
322
- role_sequence = nil # Make a new role sequence for the next role, if any
325
+ next
326
+ end
327
+
328
+ join_roles << join_role
329
+ unless objectification_node
330
+ # This is an implicit objectification, just the FT reading, not ET(where ...reading...)
331
+ # We need to create a JoinNode for this object, even though it has no RoleRefs
332
+ join = binding.join_node.join
333
+ debug :join, "Creating JN#{join.all_join_node.size} for #{reading.fact_type.entity_type.name} in objectification"
334
+ objectification_node = @constellation.JoinNode(join, join.all_join_node.size, :concept => reading.fact_type.entity_type)
323
335
  end
336
+ raise "Internal error: Trying to add role of #{role.implicit_fact_type.all_role.single.concept.name} to join node for #{objectification_node.concept.name}" unless objectification_node.concept == role.implicit_fact_type.all_role.single.concept
337
+
338
+ irole = role.implicit_fact_type.all_role.single
339
+ raise "Internal error: Trying to add role of #{irole.concept.name} to join node for #{objectification_node.concept.name}" unless objectification_node.concept == irole.concept
340
+ objectification_role = @constellation.JoinRole(objectification_node, role.implicit_fact_type.all_role.single)
341
+ objectification_step = @constellation.JoinStep(objectification_role, join_role, :fact_type => role.implicit_fact_type)
342
+ debug :join, "New #{objectification_step.describe}"
343
+ debug :join, "Associating #{incidental_roles.map(&:describe)*', '} incidental roles with #{objectification_step.describe}" if incidental_roles.size > 0
344
+ incidental_roles.each { |jr| jr.join_step = objectification_step }
345
+ incidental_roles = []
346
+ join_roles = []
324
347
  end
325
348
  else
326
349
  debug :join, "Creating Role Ref for #{role_ref.inspect}" do
327
- role_sequence ||= @constellation.RoleSequence(:new)
328
- role_refs <<
329
- @constellation.RoleRef(role_sequence, role_sequence.all_role_ref.size, :role => role, :join_node => binding.join_node)
350
+ # REVISIT: If there's an implicit subtyping join here, create it; then always raise the error here.
351
+ # I don't want to do this for now because the verbaliser will always verbalise all join steps.
352
+ if binding.join_node.concept != role.concept and
353
+ 0 == (binding.join_node.concept.supertypes_transitive & role.concept.supertypes_transitive).size
354
+ raise "Internal error: Trying to add role of #{role.concept.name} to join node for #{binding.join_node.concept.name} in '#{reading.fact_type.default_reading}'"
355
+ end
356
+ raise "Internal error: Trying to add role of #{role.concept.name} to join node for #{binding.join_node.concept.name}" unless binding.join_node.concept == role.concept
357
+ join_role = @constellation.JoinRole(binding.join_node, role)
358
+ join_roles << join_role
330
359
  end
331
360
  end
332
361
 
@@ -343,21 +372,25 @@ module ActiveFacts
343
372
  end
344
373
  if (@common_bindings.include?(binding))
345
374
  debug :join, "#{binding.inspect} is a constrained binding, add the Role Ref for #{role.concept.name}"
346
- @constellation.RoleRef(constrained_rs, constrained_rs.all_role_ref.size, :role => role, :join_node => binding.join_node)
375
+ raise "Internal error: Trying to add role of #{role.concept.name} to join node for #{binding.join_node.concept.name}" unless binding.join_node.concept == role.concept
376
+ @constellation.RoleRef(constrained_rs, constrained_rs.all_role_ref.size, :role => role, :join_role => join_role)
347
377
  end
348
378
  end
349
379
  end
350
380
 
351
- if role_sequence
381
+ if join_roles.size > 0
382
+ end_node = join_roles[-1].join_node
352
383
  if !reading.fact_type.entity_type and role = reading.fact_type.all_role.single
353
- # REVISIT: It might prove to be evil to use the same JoinNode twice for the same JoinStep here... but I don't have a real
354
- role_refs <<
355
- @constellation.RoleRef(role_sequence, role_sequence.all_role_ref.size, :role => role.implicit_fact_type.all_role.single, :join_node => role_sequence.all_role_ref.single.join_node)
384
+ # Don't give the ImplicitBoolean a join_node. We can live without one, for now.
385
+ # The Join Step will have a duplicate node, and the fact type will tell us what's happening
386
+ join_roles << join_roles[0]
356
387
  end
357
388
  # We aren't talking about objectification here, so there must be exactly two roles.
358
- raise "REVISIT: Internal error constructing join for #{reading.inspect}" if role_sequence.all_role_ref.size != 2 && role_refs.size == 2
359
- js = @constellation.JoinStep(role_refs[0].join_node, role_refs[1].join_node, :fact_type => reading.fact_type)
389
+ raise "REVISIT: Internal error constructing join for #{reading.inspect}" if join_roles.size != 2
390
+ js = @constellation.JoinStep(join_roles[0], join_roles[1], :fact_type => reading.fact_type)
360
391
  debug :join, "New Join Step #{js.describe}"
392
+ debug :join, "Associating #{incidental_roles.map(&:describe)*', '} incidental roles with #{js.describe}" if incidental_roles.size > 0
393
+ incidental_roles.each { |jr| jr.join_step = js }
361
394
  end
362
395
  end
363
396
 
@@ -378,15 +411,15 @@ module ActiveFacts
378
411
  # Create a join with a join node for every binding:
379
412
  join = build_join_nodes(readings_list)
380
413
 
381
- join.role_sequence = @constellation.RoleSequence(:new)
414
+ role_sequence = @constellation.RoleSequence(:new)
382
415
  debug :join, "Building join steps" do
383
416
  readings_list.each do |reading|
384
- build_join_steps(reading, join.role_sequence)
417
+ build_join_steps(reading, role_sequence)
385
418
  end
386
419
  end
387
420
  join.validate
388
421
 
389
- join.role_sequence
422
+ role_sequence
390
423
  end
391
424
  else
392
425
  # There's no join in this readings_list, just create a role_sequence
@@ -15,12 +15,12 @@ module ActiveFacts
15
15
  @readings.each{ |reading| reading.identify_players_with_role_name(@context) }
16
16
  @readings.each{ |reading| reading.identify_other_players(@context) }
17
17
  @readings.each{ |reading| reading.bind_roles @context }
18
+ @readings.each{ |reading| reading.match_existing_fact_type @context }
18
19
 
19
20
  # Figure out the simple existential facts and find fact types:
20
- @bound_instances = {} # Instances indexed by binding
21
- @bound_fact_types = []
22
21
  @bound_facts = []
23
- @unbound_readings = @readings.
22
+ @ojr_by_role_ref = {}
23
+ @unbound_readings = all_readings.
24
24
  map do |reading|
25
25
  bind_literal_or_fact_type reading
26
26
  end.
@@ -43,90 +43,149 @@ module ActiveFacts
43
43
 
44
44
  # Any clause that has one binding and no other word is
45
45
  # either a value instance or a simply-identified entity.
46
- reading.role_refs.map do |role_ref|
47
- next role_ref unless l = role_ref.literal
46
+ reading.role_refs.each do |role_ref|
47
+ next unless l = role_ref.literal # No literal
48
+ next if role_ref.binding.instance # Already bound
48
49
  player = role_ref.binding.player
49
50
  # raise "A literal may not be an objectification" if role_ref.role_ref.objectification_join
50
51
  # raise "Not processing facts involving objectification joins yet" if role_ref.role_ref
51
52
  debug :instance, "Making #{player.class.basename} #{player.name} using #{l.inspect}" do
52
- @bound_instances[role_ref.binding] =
53
- instance_identified_by_literal player, l
53
+ role_ref.binding.instance = instance_identified_by_literal(player, l)
54
54
  end
55
55
  role_ref
56
56
  end
57
57
 
58
- if reading.phrases.size == 1 && (role_ref = reading.phrases[0]).is_a?(Compiler::RoleRef)
59
- # This is an existential fact (like "Name 'foo'", or "Company 'Microsoft'")
60
- # @bound_instances[role_ref.binding]
61
- nil # Nothing to see here, move along
58
+ if reading.phrases.size == 1 and (role_ref = reading.phrases[0]).is_a?(Compiler::RoleRef)
59
+ if role_ref.objectification_join
60
+ # Assign the objectified fact type as this reading's fact type?
61
+ reading.fact_type = role_ref.player.fact_type
62
+ reading
63
+ else
64
+ # This is an existential fact (like "Name 'foo'", or "Company 'Microsoft'")
65
+ nil # Nothing to see here, move along
66
+ end
62
67
  else
63
- @bound_fact_types << reading.match_existing_fact_type(@context)
68
+ raise "Fact Type not found: '#{reading.display}'" unless reading.fact_type
69
+ # This instance will be associated with its binding by our caller
64
70
  reading
65
71
  end
66
72
  end
67
73
 
74
+ #
75
+ # Try to bind this reading, and return true if it can be completed
76
+ #
77
+ def bind_reading reading
78
+ return true if reading.fact
79
+
80
+ # Find the roles of this reading that do not yet have an instance
81
+ bare_roles = reading.role_refs.
82
+ select do |role_ref|
83
+ next false if role_ref.binding.instance
84
+ next false if role_ref.literal and
85
+ role_ref.binding.instance = instance_identified_by_literal(role_ref.binding.player, role_ref.literal)
86
+ true
87
+ end
88
+
89
+ debug :instance, "Considering '#{reading.display}' with "+
90
+ (bare_roles.empty? ? "no bare roles" : "bare roles: #{bare_roles.map{|role_ref| role_ref.player.name}*", "}") do
91
+
92
+ # If all the roles are in place, we can bind the rest of this reading:
93
+ return true if bare_roles.size == 0 && bind_complete_fact(reading)
94
+
95
+ progress = false
96
+ if bare_roles.size == 1 &&
97
+ (binding = bare_roles[0].binding) &&
98
+ (et = binding.player).is_a?(ActiveFacts::Metamodel::EntityType)
99
+ if et.preferred_identifier.role_sequence.all_role_ref.detect{|rr| rr.role.fact_type == reading.fact_type} &&
100
+ bind_entity_if_identifier_ready(reading, et, binding)
101
+ progress = true
102
+ end
103
+ end
104
+
105
+ return true if progress
106
+ debug :instance, "Can't make progress on '#{reading.display}'"
107
+ nil
108
+ end
109
+ end
110
+
68
111
  # Take one pass through the @unbound_readings, processing (and removing) any that have all pre-requisites
69
112
  def bind_more_facts
113
+ return false unless @unbound_readings.size > 0
70
114
  @pass += 1
71
115
 
72
116
  progress = false
73
117
  debug :instance, "Pass #{@pass} with #{@unbound_readings.size} readings to consider" do
74
- @unbound_readings.map! do |reading|
75
- # See if we can create the fact instance for this reading yet
76
-
77
- # Find the roles of this reading that do not yet have an entry in @bound_instances:
78
- bare_roles = reading.role_refs.
79
- select do |role_ref|
80
- !role_ref.literal && !@bound_instances[role_ref.binding]
81
- end
82
-
83
- debug :instance, "Considering '#{reading.fact_type.preferred_reading.expand}' with bare roles: #{bare_roles.map{|role_ref| role_ref.player.name}*", "} "
84
-
85
- if bare_roles.size == 0
86
- reading = nil if bind_complete_fact reading
87
- elsif bare_roles.size == 1 &&
88
- (binding = bare_roles[0].binding) &&
89
- (et = binding.player).is_a?(ActiveFacts::Metamodel::EntityType) &&
90
- et.preferred_identifier.role_sequence.all_role_ref.detect{|rr| rr.role.fact_type == reading.fact_type}
91
-
92
- reading = nil if bind_entity_if_identifier_ready reading, et, binding
118
+ @unbound_readings =
119
+ @unbound_readings.select do |reading|
120
+ action = bind_reading(reading)
121
+ progress = true if action
122
+ !action
93
123
  end
94
- progress = true unless reading
95
- reading
96
- end
97
- @unbound_readings.compact!
124
+ debug :instance, "end of pass, unbound readings are #{@unbound_readings.map(&:display)*', '}"
98
125
  end # debug
99
126
  progress
100
127
  end
101
128
 
129
+ # Occasionally we need to search through all the readings:
130
+ def all_readings
131
+ @readings.map do |reading|
132
+ [reading] + reading.role_refs.map{|rr| rr.objectification_join}
133
+ end.flatten.compact
134
+ end
135
+
102
136
  def bind_complete_fact reading
103
- debug :instance, "All bindings in '#{reading.fact_type.preferred_reading.expand}' contain instances; create the fact type"
104
- instances = reading.role_refs.map{|rr| @bound_instances[rr.binding]}
137
+ return true unless reading.fact_type # An bare objectification
138
+ debug :instance, "All bindings in '#{reading.display}' contain instances; create the fact type"
139
+ instances = reading.role_refs.map{|rr| rr.binding.instance}
105
140
  debug :instance, "Instances are #{instances.map{|i| "#{i.concept.name} #{i.value.inspect}"}*", "}"
106
141
 
107
- # Check that this fact doesn't already exist
108
- fact = reading.fact_type.all_fact.detect{|f|
109
- # Get the role values of this fact in the order of the reading we just bound
110
- role_values_in_reading_order = f.all_role_value.sort_by do |rv|
111
- reading.reading.role_sequence.all_role_ref.detect{|rr| rr.role == rv.role}.ordinal
142
+ if e = reading.fact_type.entity_type and
143
+ reading.role_refs[0].binding.instance.concept == e
144
+ fact = reading.role_refs[0].binding.instance.fact
145
+ else
146
+ # Check that this fact doesn't already exist
147
+ fact = reading.fact_type.all_fact.detect do |f|
148
+ # Get the role values of this fact in the order of the reading we just bound
149
+ role_values_in_reading_order = f.all_role_value.sort_by do |rv|
150
+ debugger unless reading.reading
151
+ reading.reading.role_sequence.all_role_ref.detect{|rr| rr.role == rv.role}.ordinal
152
+ end
153
+ # If all this fact's role values are played by the bound instances, it's the same fact
154
+ !role_values_in_reading_order.zip(instances).detect{|rv, i| rv.instance != i }
112
155
  end
113
- # If all this fact's role values are played by the bound instances, it's the same fact
114
- !role_values_in_reading_order.zip(instances).detect{|rv, i| rv.instance != i }
115
- }
116
- unless fact
117
- fact = @constellation.Fact(:new, :fact_type => reading.fact_type, :population => @population)
156
+ end
157
+ if fact
158
+ reading.fact = fact
159
+ debug :instance, "Found existing fact type instance"
160
+ else
161
+ fact =
162
+ reading.fact =
163
+ @constellation.Fact(:new, :fact_type => reading.fact_type, :population => @population)
118
164
  @bound_facts << fact
119
- instance =
120
- @constellation.Instance(:new, :concept => reading.fact_type.entity_type, :fact => fact, :population => @population)
121
- @bound_facts << instance
122
- reading.reading.role_sequence.all_role_ref.zip(instances).each do |rr, instance|
165
+
166
+ reading.reading.role_sequence.all_role_ref_in_order.zip(instances).each do |rr, instance|
123
167
  debug :instance, "New fact has #{instance.concept.name} role #{instance.value.inspect}"
124
168
  # REVISIT: Any residual adjectives after the fact type matching are lost here.
125
169
  @constellation.RoleValue(:fact => fact, :instance => instance, :role => rr.role, :population => @population)
126
170
  end
127
- else
128
- debug :instance, "Found existing fact type instance"
129
171
  end
172
+
173
+ if !fact.instance && reading.fact_type.entity_type
174
+ # Objectified fact type; create the instance
175
+ # Create the instance that objectifies this fact. We don't have the binding to assign it to though; that'll happen in our caller
176
+ debug :instance, "Objectifying fact as #{reading.fact_type.entity_type.name}"
177
+ instance =
178
+ @constellation.Instance(:new, :concept => reading.fact_type.entity_type, :fact => fact, :population => @population)
179
+ @bound_facts << instance
180
+ end
181
+
182
+ if reading.fact and
183
+ reading.objectified_as and
184
+ instance = reading.fact.instance and
185
+ instance.concept == reading.objectified_as.binding.player
186
+ reading.objectified_as.binding.instance = instance
187
+ end
188
+
130
189
  true
131
190
  end
132
191
 
@@ -136,7 +195,8 @@ module ActiveFacts
136
195
  def bind_entity_if_identifier_ready reading, entity_type, binding
137
196
  # Check this instance doesn't already exist already:
138
197
  identifying_binding = (reading.role_refs.map{|rr| rr.binding}-[binding])[0]
139
- identifying_instance = @bound_instances[identifying_binding]
198
+ return false unless identifying_binding # This happens when we have a bare objectification
199
+ identifying_instance = identifying_binding.instance
140
200
  preferred_identifier = entity_type.preferred_identifier
141
201
 
142
202
  debug :instance, "This clause associates a new #{binding.player.name} with a #{identifying_binding.player.name}#{identifying_instance ? " which exists" : ""}"
@@ -155,7 +215,7 @@ module ActiveFacts
155
215
  if role_value
156
216
  instance = (role_value.fact.all_role_value.to_a-[role_value])[0].instance
157
217
  debug :instance, "Found an existing instance (of #{instance.concept.name}) from a previous definition"
158
- @bound_instances[binding] = instance
218
+ binding.instance = instance
159
219
  return true # Done with this reading
160
220
  end
161
221
 
@@ -164,29 +224,37 @@ module ActiveFacts
164
224
  # Then we have to create an instance of each fact
165
225
  identifiers =
166
226
  pi_role_refs.map do |rr|
167
- fact_a = @readings.detect{|reading| rr.role.fact_type == reading.fact_type}
168
- identifying_role_ref = fact_a.role_refs.select{|role_ref| role_ref.binding != binding}[0]
227
+ # Find a reading that provides the identifying_role_ref for this player:
228
+ identifying_reading = all_readings.detect do |reading|
229
+ rr.role.fact_type == reading.fact_type &&
230
+ reading.role_refs.detect{|r1| r1.binding == binding}
231
+ end
232
+ return false unless identifying_reading
233
+ identifying_role_ref = identifying_reading.role_refs.select{|role_ref| role_ref.binding != binding}[0]
169
234
  identifying_binding = identifying_role_ref ? identifying_role_ref.binding : nil
170
- identifying_instance = @bound_instances[identifying_binding]
235
+ identifying_instance = identifying_binding.instance
171
236
 
172
- [rr, fact_a, identifying_binding, identifying_instance]
237
+ [rr, identifying_reading, identifying_binding, identifying_instance]
173
238
  end
174
239
  if identifiers.detect{ |i| !i[3] } # Not all required facts are bound yet
175
240
  debug :instance, "Can't go through with creating #{binding.player.name}; not all the identifying facts are in"
176
241
  return false
177
242
  end
178
243
 
179
- debug :instance, "Going ahead with creating #{binding.player.name} using #{identifiers.size} roles"
180
- instance = @constellation.Instance(:new, :concept => entity_type, :population => @population)
181
- @bound_instances[binding] = instance
182
- @bound_facts << instance
183
- identifiers.each do |rr, fact_a, identifying_binding, identifying_instance|
184
- # This reading provides the identifying literal for the entity_type
185
- id_fact = @constellation.Fact(:new, :fact_type => rr.role.fact_type, :population => @population)
186
- @bound_facts << id_fact
187
- role = (rr.role.fact_type.all_role.to_a-[rr.role])[0]
188
- @constellation.RoleValue(:instance => instance, :fact => id_fact, :population => @population, :role => role)
189
- @constellation.RoleValue(:instance => identifying_instance, :fact => id_fact, :role => rr.role, :population => @population)
244
+ debug :instance, "Going ahead with creating #{binding.player.name} using #{identifiers.size} roles" do
245
+ instance = @constellation.Instance(:new, :concept => entity_type, :population => @population)
246
+ binding.instance = instance
247
+ @bound_facts << instance
248
+ identifiers.each do |rr, identifying_reading, identifying_binding, identifying_instance|
249
+ # This reading provides the identifying literal for the entity_type
250
+ id_fact =
251
+ identifying_reading.fact =
252
+ @constellation.Fact(:new, :fact_type => rr.role.fact_type, :population => @population)
253
+ @bound_facts << id_fact
254
+ role = (rr.role.fact_type.all_role.to_a-[rr.role])[0]
255
+ @constellation.RoleValue(:instance => instance, :fact => id_fact, :population => @population, :role => role)
256
+ @constellation.RoleValue(:instance => identifying_instance, :fact => id_fact, :role => rr.role, :population => @population)
257
+ end
190
258
  end
191
259
 
192
260
  true # Done with this reading
@@ -231,6 +299,7 @@ module ActiveFacts
231
299
  identifying_role_refs = concept.preferred_identifier.role_sequence.all_role_ref
232
300
  raise "Single literal cannot satisfy multiple identifying roles for #{concept.name}" if identifying_role_refs.size > 1
233
301
  role = identifying_role_refs.single.role
302
+ # This instance has no binding; the binding is of the entity type not the identifying value type
234
303
  identifying_instance = instance_identified_by_literal role.concept, literal
235
304
  existing_instance = nil
236
305
  instance_rv = identifying_instance.all_role_value.detect { |rv|
@@ -244,8 +313,10 @@ module ActiveFacts
244
313
  instance = existing_instance
245
314
  debug :instance, "This #{concept.name} entity already exists"
246
315
  else
316
+ # This fact has no reading.
247
317
  fact = @constellation.Fact(:new, :fact_type => role.fact_type, :population => @population)
248
318
  @bound_facts << fact
319
+ # This instance will be associated with its binding by our caller
249
320
  instance = @constellation.Instance(:new, :concept => concept, :population => @population)
250
321
  @bound_facts << instance
251
322
  # The identifying fact type has two roles; create both role instances:
@@ -263,7 +334,7 @@ module ActiveFacts
263
334
  map do |reading|
264
335
  reading.role_refs.
265
336
  select do |rr|
266
- !@bound_instances[rr.binding]
337
+ !rr.binding.instance
267
338
  end.
268
339
  map do |role_ref|
269
340
  role_ref.binding