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.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/Gemfile +6 -5
- data/activefacts-cql.gemspec +4 -3
- data/lib/activefacts/cql/compiler.rb +29 -22
- data/lib/activefacts/cql/compiler/clause.rb +86 -84
- data/lib/activefacts/cql/compiler/constraint.rb +53 -53
- data/lib/activefacts/cql/compiler/entity_type.rb +28 -28
- data/lib/activefacts/cql/compiler/expression.rb +27 -27
- data/lib/activefacts/cql/compiler/fact.rb +37 -37
- data/lib/activefacts/cql/compiler/fact_type.rb +91 -85
- data/lib/activefacts/cql/compiler/informal.rb +48 -0
- data/lib/activefacts/cql/compiler/query.rb +52 -52
- data/lib/activefacts/cql/compiler/shared.rb +17 -17
- data/lib/activefacts/cql/compiler/value_type.rb +11 -11
- data/lib/activefacts/cql/parser/CQLParser.treetop +76 -56
- data/lib/activefacts/cql/parser/Context.treetop +15 -17
- data/lib/activefacts/cql/parser/Expressions.treetop +21 -21
- data/lib/activefacts/cql/parser/FactTypes.treetop +216 -216
- data/lib/activefacts/cql/parser/Language/English.treetop +136 -131
- data/lib/activefacts/cql/parser/Language/French.treetop +118 -118
- data/lib/activefacts/cql/parser/Language/Mandarin.treetop +111 -111
- data/lib/activefacts/cql/parser/LexicalRules.treetop +45 -45
- data/lib/activefacts/cql/parser/ObjectTypes.treetop +120 -120
- data/lib/activefacts/cql/parser/Terms.treetop +63 -63
- data/lib/activefacts/cql/parser/ValueTypes.treetop +71 -71
- data/lib/activefacts/cql/require.rb +8 -8
- data/lib/activefacts/cql/verbaliser.rb +88 -88
- data/lib/activefacts/cql/version.rb +1 -1
- data/lib/activefacts/input/cql.rb +7 -7
- metadata +12 -16
@@ -25,12 +25,12 @@ module ActiveFacts
|
|
25
25
|
@identification = identification
|
26
26
|
@pragmas = pragmas
|
27
27
|
@clauses = clauses || []
|
28
|
-
|
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
|
-
|
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
|
-
|
42
|
-
|
43
|
-
|
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
|
-
|
65
|
-
|
66
|
-
|
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
|
-
|
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
|
-
|
169
|
-
|
170
|
-
|
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
|
-
|
179
|
-
|
180
|
-
|
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
|
-
|
209
|
-
|
210
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
241
|
+
assimilation_pragmas = ['absorbed', 'separate', 'partitioned']
|
242
242
|
assimilations = @pragmas.select { |p| assimilation_pragmas.include? p}
|
243
|
-
|
243
|
+
@pragmas -= assimilation_pragmas
|
244
244
|
raise "Conflicting assimilation pragmas #{assimilations*', '}" if assimilations.size > 1
|
245
245
|
|
246
|
-
|
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
|
-
|
255
|
+
# or find/create the supertype '#{mode}' and the subtype
|
256
256
|
unless vt = @vocabulary.valid_object_type_name(vt_name) or
|
257
|
-
|
257
|
+
vt = @vocabulary.valid_object_type_name(vt_name = "#{name} #{mode}")
|
258
258
|
base_vt = @vocabulary.valid_value_type_name(mode) ||
|
259
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
52
|
-
|
53
|
-
|
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
|
-
|
96
|
-
|
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
|
-
|
121
|
-
|
122
|
-
|
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
|
-
|
134
|
-
|
135
|
-
|
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
|
-
|
193
|
-
|
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
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
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
|
-
|
219
|
-
|
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
|
-
|
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
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
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
|
-
|
87
|
+
if !ref.binding.instance and ref.literal
|
88
88
|
ref.binding.instance = instance_identified_by_literal(ref.binding.player, ref.literal)
|
89
|
-
|
89
|
+
end
|
90
90
|
|
91
91
|
next false if ref.binding.instance
|
92
92
|
true
|
93
93
|
end
|
94
94
|
|
95
|
-
|
96
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
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
|
-
|
280
|
-
|
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
|
-
|
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
|
-
|
293
|
-
|
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
|
-
|
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
|
-
|
326
|
+
trace :instance, "Already known: #{instance.verbalise.inspect}"
|
327
327
|
else
|
328
328
|
# This fact has no clause.
|
329
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
16
|
-
|
17
|
-
|
15
|
+
def to_s
|
16
|
+
inspect
|
17
|
+
end
|
18
18
|
|
19
19
|
def inspect
|
20
20
|
"Query: " +
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
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
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
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
|
-
|
108
|
+
# Prepare to objectify the fact type (so readings for link fact types can be created)
|
109
109
|
if @name
|
110
|
-
|
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
|
-
|
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
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
173
|
-
|
174
|
-
|
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
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
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
|
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
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
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
|
-
|
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
|
-
|
416
|
+
s = super
|
411
417
|
"FactType: #{@conditions.size > 0 ? super+' ' : '' }#{@clauses.inspect}" +
|
412
418
|
(@pragmas && @pragmas.size > 0 ? ", pragmas [#{@pragmas.flatten.sort*','}]" : '')
|
413
419
|
|