activefacts 0.8.16 → 0.8.18
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +15 -0
- data/Manifest.txt +10 -4
- data/bin/afgen +26 -20
- data/bin/cql +1 -1
- data/css/orm2.css +89 -9
- data/examples/CQL/CompanyDirectorEmployee.cql +4 -4
- data/examples/CQL/Genealogy.cql +5 -5
- data/examples/CQL/Metamodel.cql +121 -91
- data/examples/CQL/MonthInSeason.cql +2 -6
- data/examples/CQL/SeparateSubtype.cql +11 -9
- data/examples/CQL/ServiceDirector.cql +21 -33
- data/examples/CQL/Supervision.cql +0 -3
- data/examples/CQL/WindowInRoomInBldg.cql +10 -4
- data/examples/CQL/unit.cql +1 -1
- data/lib/activefacts.rb +1 -0
- data/lib/activefacts/cql/CQLParser.treetop +5 -1
- data/lib/activefacts/cql/Context.treetop +2 -7
- data/lib/activefacts/cql/Expressions.treetop +2 -2
- data/lib/activefacts/cql/FactTypes.treetop +37 -31
- data/lib/activefacts/cql/Language/English.treetop +21 -4
- data/lib/activefacts/cql/LexicalRules.treetop +59 -1
- data/lib/activefacts/cql/ObjectTypes.treetop +22 -12
- data/lib/activefacts/cql/Terms.treetop +13 -9
- data/lib/activefacts/cql/ValueTypes.treetop +30 -11
- data/lib/activefacts/cql/compiler.rb +34 -5
- data/lib/activefacts/cql/compiler/clause.rb +207 -116
- data/lib/activefacts/cql/compiler/constraint.rb +129 -105
- data/lib/activefacts/cql/compiler/entity_type.rb +49 -27
- data/lib/activefacts/cql/compiler/expression.rb +71 -42
- data/lib/activefacts/cql/compiler/fact.rb +70 -64
- data/lib/activefacts/cql/compiler/fact_type.rb +108 -57
- data/lib/activefacts/cql/compiler/query.rb +178 -0
- data/lib/activefacts/cql/compiler/shared.rb +13 -12
- data/lib/activefacts/cql/compiler/value_type.rb +10 -4
- data/lib/activefacts/cql/nodes.rb +1 -1
- data/lib/activefacts/cql/parser.rb +6 -2
- data/lib/activefacts/generate/absorption.rb +6 -3
- data/lib/activefacts/generate/cql.rb +140 -84
- data/lib/activefacts/generate/dm.rb +12 -6
- data/lib/activefacts/generate/help.rb +25 -6
- data/lib/activefacts/generate/helpers/oo.rb +195 -0
- data/lib/activefacts/generate/helpers/ordered.rb +589 -0
- data/lib/activefacts/generate/helpers/rails.rb +57 -0
- data/lib/activefacts/generate/html/glossary.rb +274 -54
- data/lib/activefacts/generate/json.rb +25 -22
- data/lib/activefacts/generate/null.rb +1 -0
- data/lib/activefacts/generate/rails/models.rb +244 -0
- data/lib/activefacts/generate/rails/schema.rb +185 -0
- data/lib/activefacts/generate/records.rb +1 -0
- data/lib/activefacts/generate/ruby.rb +51 -30
- data/lib/activefacts/generate/sql/mysql.rb +5 -3
- data/lib/activefacts/generate/sql/server.rb +8 -4
- data/lib/activefacts/generate/text.rb +1 -0
- data/lib/activefacts/generate/transform/surrogate.rb +209 -0
- data/lib/activefacts/generate/version.rb +1 -0
- data/lib/activefacts/input/orm.rb +234 -181
- data/lib/activefacts/mapping/rails.rb +122 -0
- data/lib/activefacts/persistence/columns.rb +34 -18
- data/lib/activefacts/persistence/foreignkey.rb +129 -71
- data/lib/activefacts/persistence/index.rb +42 -12
- data/lib/activefacts/persistence/reference.rb +37 -23
- data/lib/activefacts/persistence/tables.rb +53 -19
- data/lib/activefacts/registry.rb +11 -0
- data/lib/activefacts/support.rb +28 -10
- data/lib/activefacts/version.rb +1 -1
- data/lib/activefacts/vocabulary/extensions.rb +246 -117
- data/lib/activefacts/vocabulary/metamodel.rb +105 -65
- data/lib/activefacts/vocabulary/verbaliser.rb +226 -194
- data/spec/absorption_spec.rb +1 -0
- data/spec/cql/comparison_spec.rb +8 -8
- data/spec/cql/contractions_spec.rb +16 -43
- data/spec/cql/entity_type_spec.rb +2 -1
- data/spec/cql/expressions_spec.rb +2 -2
- data/spec/cql/fact_type_matching_spec.rb +4 -1
- data/spec/cql/parser/bad_literals_spec.rb +30 -30
- data/spec/cql/parser/entity_types_spec.rb +6 -6
- data/spec/cql/parser/expressions_spec.rb +25 -19
- data/spec/cql/samples_spec.rb +5 -4
- data/spec/cql_cql_spec.rb +2 -1
- data/spec/cql_dm_spec.rb +4 -0
- data/spec/cql_mysql_spec.rb +4 -0
- data/spec/cql_parse_spec.rb +2 -0
- data/spec/cql_ruby_spec.rb +4 -0
- data/spec/cql_sql_spec.rb +4 -0
- data/spec/cqldump_spec.rb +7 -4
- data/spec/helpers/parse_to_ast_matcher.rb +7 -3
- data/spec/helpers/test_parser.rb +2 -0
- data/spec/norma_cql_spec.rb +5 -2
- data/spec/norma_ruby_spec.rb +4 -1
- data/spec/norma_ruby_sql_spec.rb +4 -1
- data/spec/norma_sql_spec.rb +4 -1
- data/spec/norma_tables_spec.rb +2 -2
- data/spec/ruby_api_spec.rb +1 -1
- data/spec/spec_helper.rb +2 -0
- data/spec/transform_surrogate_spec.rb +59 -0
- metadata +70 -60
- data/TODO +0 -308
- data/lib/activefacts/cql/compiler/join.rb +0 -162
- data/lib/activefacts/generate/oo.rb +0 -176
- data/lib/activefacts/generate/ordered.rb +0 -602
@@ -26,23 +26,23 @@ module ActiveFacts
|
|
26
26
|
@context.left_contraction_allowed = true
|
27
27
|
match_condition_fact_types
|
28
28
|
|
29
|
-
# Build the
|
29
|
+
# Build the query:
|
30
30
|
unless @conditions.empty? and !@returning
|
31
|
-
debug :
|
32
|
-
@
|
33
|
-
@
|
34
|
-
@
|
35
|
-
@
|
31
|
+
debug :query, "building query for derived fact type" do
|
32
|
+
@query = build_variables(@conditions.flatten)
|
33
|
+
@roles_by_binding = build_all_steps(@conditions)
|
34
|
+
@query.validate
|
35
|
+
@query
|
36
36
|
end
|
37
37
|
end
|
38
38
|
@context.left_contraction_allowed = false
|
39
|
-
@
|
39
|
+
@query
|
40
40
|
end
|
41
41
|
|
42
42
|
def match_condition_fact_types
|
43
43
|
@conditions.each do |condition|
|
44
44
|
next if condition.is_naked_object_type
|
45
|
-
# REVISIT: Many conditions will imply a number of different
|
45
|
+
# REVISIT: Many conditions will imply a number of different steps, which need to be handled (similar to nested_clauses).
|
46
46
|
debug :projection, "matching condition fact_type #{condition.inspect}" do
|
47
47
|
fact_type = condition.match_existing_fact_type @context
|
48
48
|
raise "Unrecognised fact type #{condition.inspect} in #{self.class}" unless fact_type
|
@@ -93,6 +93,13 @@ module ActiveFacts
|
|
93
93
|
# * Objectify the fact type if @name
|
94
94
|
#
|
95
95
|
|
96
|
+
# Prepare to objectify the fact type (so readings for implicit fact types can be created)
|
97
|
+
if @name
|
98
|
+
entity_type = @vocabulary.valid_entity_type_name(@name)
|
99
|
+
raise "You can't objectify #{@name}, it already exists" if entity_type
|
100
|
+
@entity_type = @constellation.EntityType(@vocabulary, @name, :fact_type => @fact_type, :guid => :new)
|
101
|
+
end
|
102
|
+
|
96
103
|
prepare_roles @clauses
|
97
104
|
|
98
105
|
# REVISIT: Compiling the conditions here make it impossible to define a self-referential (transitive) query.
|
@@ -102,9 +109,16 @@ module ActiveFacts
|
|
102
109
|
@clauses.reject!{|clause| clause.is_existential_type }
|
103
110
|
return true unless @clauses.size > 0 # Nothing interesting was said.
|
104
111
|
|
112
|
+
if @entity_type
|
113
|
+
# Extract readings for implicit fact types
|
114
|
+
@implicit_readings, @clauses =
|
115
|
+
@clauses.partition do |clause|
|
116
|
+
clause.refs.size == 2 and clause.refs.detect{|ref| ref.player == @entity_type}
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
105
120
|
# See if any existing fact type is being invoked (presumably to objectify or extend it)
|
106
121
|
@fact_type = check_compatibility_of_matched_clauses
|
107
|
-
|
108
122
|
verify_matching_roles # All clauses of a fact type must have the same roles
|
109
123
|
|
110
124
|
if !@fact_type
|
@@ -116,6 +130,7 @@ module ActiveFacts
|
|
116
130
|
@fact_type.create_implicit_fact_type_for_unary if @fact_type.all_role.size == 1 && !@name
|
117
131
|
@existing_clauses = [first_clause]
|
118
132
|
elsif (n = @clauses.size - @existing_clauses.size) > 0
|
133
|
+
raise "Cannot extend a negated fact type" if @existing_clauses.detect {|clause| clause.certainty == false }
|
119
134
|
debug :binding, "Extending existing fact type with #{n} new readings"
|
120
135
|
end
|
121
136
|
|
@@ -133,17 +148,16 @@ module ActiveFacts
|
|
133
148
|
clause.make_embedded_constraints(vocabulary)
|
134
149
|
end
|
135
150
|
|
136
|
-
# Objectify the fact type if necessary:
|
137
151
|
if @name
|
152
|
+
# Objectify the fact type:
|
153
|
+
@entity_type.fact_type = @fact_type
|
138
154
|
if @fact_type.entity_type and @name != @fact_type.entity_type.name
|
139
155
|
raise "Cannot objectify fact type as #{@name} and as #{@fact_type.entity_type.name}"
|
140
156
|
end
|
141
|
-
|
142
|
-
|
143
|
-
e = @constellation.EntityType(@vocabulary, @name, :fact_type => @fact_type, :guid => :new)
|
144
|
-
e.create_implicit_fact_types
|
157
|
+
ifts = @entity_type.create_implicit_fact_types
|
158
|
+
create_implicit_readings(ifts)
|
145
159
|
if @pragmas
|
146
|
-
|
160
|
+
@entity_type.is_independent = true if @pragmas.delete('independent')
|
147
161
|
end
|
148
162
|
if @pragmas && @pragmas.size > 0
|
149
163
|
$stderr.puts "Mapping pragmas #{@pragmas.inspect} are ignored for objectified fact type #{@name}"
|
@@ -169,13 +183,35 @@ module ActiveFacts
|
|
169
183
|
@fact_type
|
170
184
|
end
|
171
185
|
|
186
|
+
def create_implicit_readings(ifts)
|
187
|
+
@implicit_readings.each do |clause|
|
188
|
+
#next false unless clause.refs.size == 2 and clause.refs.detect{|r| r.role.object_type == @entity_type }
|
189
|
+
other_ref = clause.refs.detect{|ref| ref.player != @entity_type}
|
190
|
+
ift = ifts.detect do |ift|
|
191
|
+
other_ref.binding.refs.map{|r| r.role}.include?(ift.implying_role)
|
192
|
+
end
|
193
|
+
next unless ift
|
194
|
+
# This clause is a reading for the implicit LinkFactType ift
|
195
|
+
i = 0
|
196
|
+
reading_text = clause.phrases.map do |phrase|
|
197
|
+
next phrase if String === phrase
|
198
|
+
"{#{(i += 1)-1}}"
|
199
|
+
end*' '
|
200
|
+
ir = ActiveFacts::Metamodel::LinkFactType::ImplicitReading
|
201
|
+
irrs = ir::ImplicitReadingRoleSequence
|
202
|
+
irrf = irrs::ImplicitReadingRoleRef
|
203
|
+
reading = ir.new(ift, reading_text)
|
204
|
+
ift.add_reading reading
|
205
|
+
end
|
206
|
+
end
|
207
|
+
|
172
208
|
def project_clause_roles(clause)
|
173
|
-
# Attach the clause's role references to the projected roles of the
|
174
|
-
clause.
|
175
|
-
role,
|
176
|
-
raise "#{
|
177
|
-
raise "#{
|
178
|
-
|
209
|
+
# Attach the clause's role references to the projected roles of the query
|
210
|
+
clause.refs.each_with_index do |ref, i|
|
211
|
+
role, play = @roles_by_binding[ref.binding]
|
212
|
+
raise "#{ref} must be a role projected from the conditions" unless role
|
213
|
+
raise "#{ref} has already-projected play!" if play.role_ref
|
214
|
+
ref.role_ref.play = play
|
179
215
|
end
|
180
216
|
end
|
181
217
|
|
@@ -183,7 +219,7 @@ module ActiveFacts
|
|
183
219
|
def is_projected_role(rr)
|
184
220
|
# rr is a RoleRef on one side of the comparison.
|
185
221
|
# If its binding contains a reference from our readings, it's projected.
|
186
|
-
rr.
|
222
|
+
rr.binding.refs.detect do |ref|
|
187
223
|
@readings.include?(ref.reading)
|
188
224
|
end
|
189
225
|
end
|
@@ -237,25 +273,40 @@ module ActiveFacts
|
|
237
273
|
|
238
274
|
# If there's an existing presence constraint that can be converted into a PC, do that:
|
239
275
|
@clauses.each do |clause|
|
240
|
-
|
241
|
-
epc =
|
276
|
+
ref = clause.refs[-1] or next
|
277
|
+
epc = ref.embedded_presence_constraint or next
|
242
278
|
epc.max_frequency == 1 or next
|
243
279
|
next if epc.enforcement
|
244
280
|
epc.is_preferred_identifier = true
|
245
281
|
return
|
246
282
|
end
|
247
283
|
|
248
|
-
#
|
284
|
+
# We need to check uniqueness constraints after processing the whole vocabulary
|
249
285
|
# raise "Fact type must be named as it has no identifying uniqueness constraint" unless @name || @fact_type.all_role.size == 1
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
286
|
+
debug :constraint, "Need to check #{@fact_type.default_reading.inspect} for a uniqueness constraint"
|
287
|
+
fact_type.check_and_add_spanning_uniqueness_constraint = proc do
|
288
|
+
debug :constraint, "Checking #{@fact_type.default_reading.inspect} for a uniqueness constraint"
|
289
|
+
if !@fact_type.all_role.
|
290
|
+
detect do |role|
|
291
|
+
role.all_role_ref.detect do |rr|
|
292
|
+
# This RoleSequence, to be relevant, must only reference roles of this fact type
|
293
|
+
rr.role_sequence.all_role_ref.all? {|rr2| rr2.role.fact_type == @fact_type} and
|
294
|
+
# The RoleSequence must have at least one uniqueness constraint
|
295
|
+
rr.role_sequence.all_presence_constraint.detect{|pc| pc.max_frequency == 1}
|
296
|
+
end
|
297
|
+
end
|
298
|
+
# There's no existing uniqueness constraint over the roles of this fact type. Add one
|
299
|
+
pc = @constellation.PresenceConstraint(
|
300
|
+
:new,
|
301
|
+
:vocabulary => @vocabulary,
|
302
|
+
:name => @fact_type.entity_type ? @fact_type.entity_type.name+"PK" : '',
|
303
|
+
:role_sequence => (rs = @fact_type.preferred_reading.role_sequence),
|
304
|
+
:max_frequency => 1,
|
305
|
+
:is_preferred_identifier => true # (prefer || !!@fact_type.entity_type)
|
306
|
+
)
|
307
|
+
debug :constraint, "Made new fact type implicit PC GUID=#{pc.guid} #{pc.name} min=nil max=1 over #{rs.describe}"
|
308
|
+
end
|
309
|
+
end
|
259
310
|
end
|
260
311
|
|
261
312
|
def has_more_adjectives(less, more)
|
@@ -265,12 +316,12 @@ module ActiveFacts
|
|
265
316
|
end
|
266
317
|
|
267
318
|
def verify_matching_roles
|
268
|
-
|
269
|
-
|
319
|
+
refs_by_clause_and_key = {}
|
320
|
+
clauses_by_refs =
|
270
321
|
@clauses.inject({}) do |hash, clause|
|
271
|
-
keys = clause.
|
272
|
-
key =
|
273
|
-
|
322
|
+
keys = clause.refs.map do |ref|
|
323
|
+
key = ref.key.compact
|
324
|
+
refs_by_clause_and_key[[clause, key]] = ref
|
274
325
|
key
|
275
326
|
end.sort_by{|a| a.map{|k|k.to_s}}
|
276
327
|
raise "Fact types may not have duplicate roles" if keys.uniq.size < keys.size
|
@@ -278,14 +329,14 @@ module ActiveFacts
|
|
278
329
|
hash
|
279
330
|
end
|
280
331
|
|
281
|
-
if
|
282
|
-
# Attempt loose binding here; it might merge some Compiler::
|
283
|
-
variants =
|
284
|
-
(
|
332
|
+
if clauses_by_refs.size != 1 and @conditions.empty?
|
333
|
+
# Attempt loose binding here; it might merge some Compiler::References to share the same Variables
|
334
|
+
variants = clauses_by_refs.keys
|
335
|
+
(clauses_by_refs.size-1).downto(1) do |m| # Start with the last one
|
285
336
|
0.upto(m-1) do |l| # Try to rebind onto any lower one
|
286
337
|
common = variants[m]&variants[l]
|
287
|
-
clauses_l =
|
288
|
-
clauses_m =
|
338
|
+
clauses_l = clauses_by_refs[variants[l]]
|
339
|
+
clauses_m = clauses_by_refs[variants[m]]
|
289
340
|
l_keys = variants[l]-common
|
290
341
|
m_keys = variants[m]-common
|
291
342
|
debug :binding, "Try to collapse variant #{m} onto #{l}; diffs are #{l_keys.inspect} -> #{m_keys.inspect}"
|
@@ -295,16 +346,16 @@ module ActiveFacts
|
|
295
346
|
candidates = []
|
296
347
|
(0...m_keys.size).each do |j|
|
297
348
|
m_key = m_keys[j]
|
298
|
-
|
299
|
-
|
300
|
-
debug :binding, "Can we match #{
|
301
|
-
next if
|
302
|
-
if has_more_adjectives(
|
303
|
-
debug :binding, "can rebind #{
|
304
|
-
candidates << [
|
305
|
-
elsif has_more_adjectives(
|
306
|
-
debug :binding, "can rebind #{
|
307
|
-
candidates << [
|
349
|
+
l_ref = refs_by_clause_and_key[[clauses_l[0], l_key]]
|
350
|
+
m_ref = refs_by_clause_and_key[[clauses_m[0], m_key]]
|
351
|
+
debug :binding, "Can we match #{l_ref.inspect} (#{i}) with #{m_ref.inspect} (#{j})?"
|
352
|
+
next if m_ref.player != l_ref.player
|
353
|
+
if has_more_adjectives(m_ref, l_ref)
|
354
|
+
debug :binding, "can rebind #{m_ref.inspect} to #{l_ref.inspect}"
|
355
|
+
candidates << [m_ref, l_ref]
|
356
|
+
elsif has_more_adjectives(l_ref, m_ref)
|
357
|
+
debug :binding, "can rebind #{l_ref.inspect} to #{m_ref.inspect}"
|
358
|
+
candidates << [l_ref, m_ref]
|
308
359
|
end
|
309
360
|
end
|
310
361
|
|
@@ -323,7 +374,7 @@ module ActiveFacts
|
|
323
374
|
else
|
324
375
|
# No point continuing, we failed on this one.
|
325
376
|
raise "All readings in a fact type definition must have matching role players, compare (#{
|
326
|
-
|
377
|
+
clauses_by_refs.keys.map do |keys|
|
327
378
|
keys.map{|key| key*'-' }*", "
|
328
379
|
end*") with ("
|
329
380
|
})"
|
@@ -345,7 +396,7 @@ module ActiveFacts
|
|
345
396
|
else
|
346
397
|
''
|
347
398
|
end +
|
348
|
-
(@pragmas && @pragmas.size > 0 ? ", pragmas [#{@pragmas.sort*','}]" : '')
|
399
|
+
(@pragmas && @pragmas.size > 0 ? ", pragmas [#{@pragmas.flatten.sort*','}]" : '')
|
349
400
|
|
350
401
|
# REVISIT: @returning = returning
|
351
402
|
end
|
@@ -0,0 +1,178 @@
|
|
1
|
+
module ActiveFacts
|
2
|
+
module CQL
|
3
|
+
class Compiler < ActiveFacts::CQL::Parser
|
4
|
+
class Definition
|
5
|
+
# Make a Variable for every binding present in these clauses
|
6
|
+
def build_variables(clauses_list)
|
7
|
+
debug :query, "Building variables" do
|
8
|
+
query = @constellation.Query(:new)
|
9
|
+
all_bindings_in_clauses(clauses_list).
|
10
|
+
each do |binding|
|
11
|
+
debug :query, "Creating variable #{query.all_variable.size} for #{binding.inspect}"
|
12
|
+
binding.variable = @constellation.Variable(query, query.all_variable.size, :object_type => binding.player)
|
13
|
+
if literal = binding.refs.detect{|r| r.literal}
|
14
|
+
if literal.kind_of?(ActiveFacts::CQL::Compiler::Reference)
|
15
|
+
# REVISIT: Fix this crappy ad-hoc polymorphism hack
|
16
|
+
literal = literal.literal
|
17
|
+
end
|
18
|
+
unit = @constellation.Unit.detect{|k, v| [v.name, v.plural_name].include? literal.unit} if literal.unit
|
19
|
+
binding.variable.value = [literal.literal.to_s, literal.literal.is_a?(String), unit]
|
20
|
+
end
|
21
|
+
end
|
22
|
+
query
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def build_all_steps(clauses_list)
|
27
|
+
roles_by_binding = {}
|
28
|
+
debug :query, "Building steps" do
|
29
|
+
clauses_list.each do |clause|
|
30
|
+
next if clause.is_naked_object_type
|
31
|
+
build_steps(clause, roles_by_binding)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
roles_by_binding
|
35
|
+
end
|
36
|
+
|
37
|
+
def build_steps clause, roles_by_binding = {}, objectification_node = nil
|
38
|
+
plays = []
|
39
|
+
incidental_roles = []
|
40
|
+
debug :query, "Creating Role Sequence for #{clause.inspect} with #{clause.refs.size} role refs" do
|
41
|
+
objectification_step = nil
|
42
|
+
clause.refs.each do |ref|
|
43
|
+
# These refs are the Compiler::References, which have associated Metamodel::RoleRefs,
|
44
|
+
# but we need to create Plays for those roles.
|
45
|
+
# REVISIT: Plays may need to save residual_adjectives
|
46
|
+
binding = ref.binding
|
47
|
+
role = (ref && ref.role) || (ref.role_ref && ref.role_ref.role)
|
48
|
+
play = nil
|
49
|
+
|
50
|
+
debugger unless clause.fact_type
|
51
|
+
if (clause.fact_type.entity_type)
|
52
|
+
# This clause is of an objectified fact type.
|
53
|
+
# We need a step from this role to the phantom role, but not
|
54
|
+
# for a role that has only one ref (this one) in their binding.
|
55
|
+
# Create the Variable and Play in any case though.
|
56
|
+
refs_count = binding.refs.size
|
57
|
+
objectification_ref_count = 0
|
58
|
+
if ref.nested_clauses
|
59
|
+
ref.nested_clauses.each do |ojc|
|
60
|
+
objectification_ref_count += ojc.refs.select{|ref| ref.binding.refs.size > 1}.size
|
61
|
+
end
|
62
|
+
end
|
63
|
+
refs_count += objectification_ref_count
|
64
|
+
|
65
|
+
debug :query, "Creating Variable #{ref.inspect} (counts #{refs_count}/#{objectification_ref_count}) and objectification Step for #{ref.inspect}" do
|
66
|
+
|
67
|
+
raise "Internal error: Trying to add role of #{role.object_type.name} to variable for #{binding.variable.object_type.name}" unless binding.variable.object_type == role.object_type
|
68
|
+
play = @constellation.Play(binding.variable, role)
|
69
|
+
|
70
|
+
if (refs_count <= 1) # Our work here is done if there are no other refs
|
71
|
+
if objectification_step
|
72
|
+
play.step = objectification_step
|
73
|
+
else
|
74
|
+
incidental_roles << play
|
75
|
+
end
|
76
|
+
next
|
77
|
+
end
|
78
|
+
|
79
|
+
plays << play
|
80
|
+
unless objectification_node
|
81
|
+
# This is an implicit objectification, just the FT clause, not ET(where ...clause...)
|
82
|
+
# We need to create a Variable for this object, even though it has no References
|
83
|
+
query = binding.variable.query
|
84
|
+
debug :query, "Creating JN#{query.all_variable.size} for #{clause.fact_type.entity_type.name} in objectification"
|
85
|
+
objectification_node = @constellation.Variable(query, query.all_variable.size, :object_type => clause.fact_type.entity_type)
|
86
|
+
end
|
87
|
+
raise "Internal error: Trying to add role of #{role.link_fact_type.all_role.single.object_type.name} to variable for #{objectification_node.object_type.name}" unless objectification_node.object_type == role.link_fact_type.all_role.single.object_type
|
88
|
+
|
89
|
+
irole = role.link_fact_type.all_role.single
|
90
|
+
raise "Internal error: Trying to add role of #{irole.object_type.name} to variable for #{objectification_node.object_type.name}" unless objectification_node.object_type == irole.object_type
|
91
|
+
objectification_role = @constellation.Play(objectification_node, role.link_fact_type.all_role.single)
|
92
|
+
objectification_step = @constellation.Step(
|
93
|
+
objectification_role,
|
94
|
+
play,
|
95
|
+
:fact_type => role.link_fact_type,
|
96
|
+
:is_optional => false,
|
97
|
+
:is_disallowed => clause.certainty == false
|
98
|
+
)
|
99
|
+
if clause.certainty == nil
|
100
|
+
objectification_step.is_optional = true
|
101
|
+
end
|
102
|
+
debug :query, "New #{objectification_step.describe}"
|
103
|
+
debug :query, "Associating #{incidental_roles.map(&:describe)*', '} incidental roles with #{objectification_step.describe}" if incidental_roles.size > 0
|
104
|
+
incidental_roles.each { |jr| jr.step = objectification_step }
|
105
|
+
incidental_roles = []
|
106
|
+
plays = []
|
107
|
+
end
|
108
|
+
else
|
109
|
+
debug :query, "Creating Reference for #{ref.inspect}" do
|
110
|
+
# REVISIT: If there's an implicit subtyping step here, create it; then always raise the error here.
|
111
|
+
# I don't want to do this for now because the verbaliser will always verbalise all steps.
|
112
|
+
if binding.variable.object_type != role.object_type and
|
113
|
+
0 == (binding.variable.object_type.supertypes_transitive & role.object_type.supertypes_transitive).size
|
114
|
+
raise "Internal error: Trying to add role of #{role.object_type.name} to variable #{binding.variable.ordinal} for #{binding.variable.object_type.name} in '#{clause.fact_type.default_reading}'"
|
115
|
+
end
|
116
|
+
raise "Internal error: Trying to add role of #{role.object_type.name} to variable #{binding.variable.ordinal} for #{binding.variable.object_type.name}" unless binding.variable.object_type == role.object_type
|
117
|
+
begin
|
118
|
+
play = @constellation.Play(binding.variable, role)
|
119
|
+
rescue ArgumentError => e
|
120
|
+
play = @constellation.Play(binding.variable, role)
|
121
|
+
end
|
122
|
+
plays << play
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
if ref.nested_clauses
|
127
|
+
# We are looking at a role whose player is an objectification of a fact type,
|
128
|
+
# which will have ImplicitFactTypes for each role.
|
129
|
+
# Each of these ImplicitFactTypes has a single phantom role played by the objectifying entity type
|
130
|
+
# One of these phantom roles is likely to be the subject of an objectification step.
|
131
|
+
ref.nested_clauses.each do |r|
|
132
|
+
debug :query, "Building objectification step for #{ref.nested_clauses.inspect}" do
|
133
|
+
build_steps r, roles_by_binding, binding.variable
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
roles_by_binding[binding] = [role, play]
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
if plays.size > 0
|
142
|
+
end_node = plays[-1].variable
|
143
|
+
if !clause.fact_type.entity_type and role = clause.fact_type.all_role.single
|
144
|
+
# Don't give the ImplicitBoolean a variable. We can live without one, for now.
|
145
|
+
# The Step will have a duplicate node, and the fact type will tell us what's happening
|
146
|
+
plays << plays[0]
|
147
|
+
end
|
148
|
+
# We aren't talking about objectification here, so there must be exactly two roles.
|
149
|
+
raise "REVISIT: Internal error constructing step for #{clause.inspect}" if plays.size != 2
|
150
|
+
js = @constellation.Step(
|
151
|
+
plays[0],
|
152
|
+
plays[1],
|
153
|
+
:fact_type => clause.fact_type,
|
154
|
+
:is_disallowed => clause.certainty == false,
|
155
|
+
:is_optional => clause.certainty == nil
|
156
|
+
)
|
157
|
+
debug :query, "New #{js.describe}"
|
158
|
+
debug :query, "Associating #{incidental_roles.map(&:describe)*', '} incidental roles with #{js.describe}" if incidental_roles.size > 0
|
159
|
+
incidental_roles.each { |jr| jr.step = js }
|
160
|
+
end
|
161
|
+
roles_by_binding
|
162
|
+
end
|
163
|
+
|
164
|
+
# Return the unique array of all bindings in these clauses, including in objectification steps
|
165
|
+
def all_bindings_in_clauses clauses
|
166
|
+
clauses.map do |clause|
|
167
|
+
clause.refs.map do |ref|
|
168
|
+
raise "Binding reference #{ref.inspect} is not bound to a binding" unless ref.binding
|
169
|
+
[ref.binding] + (ref.nested_clauses ? all_bindings_in_clauses(ref.nested_clauses) : [])
|
170
|
+
end
|
171
|
+
end.
|
172
|
+
flatten.
|
173
|
+
uniq
|
174
|
+
end
|
175
|
+
end
|
176
|
+
end
|
177
|
+
end
|
178
|
+
end
|