activefacts 0.8.8 → 0.8.9
Sign up to get free protection for your applications and to get access to all the features.
- data/bin/cql +90 -31
- data/download.html +2 -1
- data/examples/CQL/Diplomacy.cql +8 -8
- data/examples/CQL/Insurance.cql +3 -3
- data/examples/CQL/Metamodel.cql +31 -16
- data/examples/CQL/MetamodelNext.cql +30 -18
- data/examples/CQL/Orienteering.cql +2 -2
- data/examples/CQL/OrienteeringER.cql +6 -6
- data/examples/CQL/SchoolActivities.cql +1 -1
- data/examples/CQL/ServiceDirector.cql +3 -3
- data/examples/CQL/Supervision.cql +3 -2
- data/examples/CQL/Tests.Test5.Load.cql +1 -1
- data/examples/CQL/WaiterTips.cql +1 -1
- data/lib/activefacts/cql/FactTypes.treetop +14 -2
- data/lib/activefacts/cql/compiler/constraint.rb +69 -36
- data/lib/activefacts/cql/compiler/fact.rb +142 -71
- data/lib/activefacts/cql/compiler/fact_type.rb +1 -1
- data/lib/activefacts/cql/compiler/reading.rb +35 -8
- data/lib/activefacts/cql/compiler/shared.rb +4 -3
- data/lib/activefacts/generate/cql.rb +16 -5
- data/lib/activefacts/generate/ordered.rb +1 -1
- data/lib/activefacts/input/orm.rb +136 -57
- data/lib/activefacts/support.rb +40 -16
- data/lib/activefacts/version.rb +1 -1
- data/lib/activefacts/vocabulary/extensions.rb +149 -58
- data/lib/activefacts/vocabulary/metamodel.rb +23 -11
- data/lib/activefacts/vocabulary/verbaliser.rb +223 -105
- data/spec/cql/samples_spec.rb +30 -6
- data/spec/cql_mysql_spec.rb +1 -2
- data/spec/norma_ruby_sql_spec.rb +0 -1
- metadata +4 -4
@@ -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
|
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
|
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
|
25
|
-
the club called Club Name has
|
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
|
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
|
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
|
55
|
+
each Name occurs one time in
|
56
56
|
map Name having Accessibility belongs to Club;
|
57
|
-
each Series Event occurs
|
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
|
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
|
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
|
239
|
-
|
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
|
-
|
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
|
23
|
+
Party is called one Party Name;
|
24
24
|
Party Moniker has one Accuracy;
|
25
25
|
|
26
26
|
Person is a kind of Party;
|
data/examples/CQL/WaiterTips.cql
CHANGED
@@ -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
|
227
|
+
'(' s where s facts:fact_clauses s ')' s
|
216
228
|
{
|
217
229
|
def ast
|
218
|
-
|
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
|
-
|
290
|
-
|
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
|
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.
|
300
|
-
# We
|
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
|
-
|
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
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
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
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
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
|
-
|
328
|
-
|
329
|
-
|
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
|
-
|
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
|
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
|
-
#
|
354
|
-
|
355
|
-
|
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
|
359
|
-
js = @constellation.JoinStep(
|
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
|
-
|
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,
|
417
|
+
build_join_steps(reading, role_sequence)
|
385
418
|
end
|
386
419
|
end
|
387
420
|
join.validate
|
388
421
|
|
389
|
-
|
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
|
-
@
|
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.
|
47
|
-
next
|
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
|
-
|
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
|
59
|
-
|
60
|
-
|
61
|
-
|
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
|
-
|
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
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
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
|
-
|
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
|
-
|
104
|
-
|
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
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
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
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
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
|
-
|
120
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
168
|
-
|
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 =
|
235
|
+
identifying_instance = identifying_binding.instance
|
171
236
|
|
172
|
-
[rr,
|
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
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
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
|
-
|
337
|
+
!rr.binding.instance
|
267
338
|
end.
|
268
339
|
map do |role_ref|
|
269
340
|
role_ref.binding
|