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
@@ -28,7 +28,7 @@ module ActiveFacts
|
|
28
28
|
:context_note_kind => @context_kind,
|
29
29
|
:discussion => @discussion
|
30
30
|
)
|
31
|
-
context_note.
|
31
|
+
context_note.relevant_concept = target
|
32
32
|
if @agreed_date || @agreed_agents
|
33
33
|
agreement = constellation.Agreement(context_note)
|
34
34
|
agreement.date = @agreed_date if @agreed_date
|
@@ -74,6 +74,7 @@ module ActiveFacts
|
|
74
74
|
clauses_list.each do |clause|
|
75
75
|
fact_type = clause.match_existing_fact_type @context
|
76
76
|
raise "Unrecognised fact type #{clause.inspect} in #{self.class}" unless fact_type
|
77
|
+
raise "Negated fact type #{clause.inspect} in #{self.class} is not yet supported" if clause.certainty == false
|
77
78
|
end
|
78
79
|
end
|
79
80
|
|
@@ -82,22 +83,22 @@ module ActiveFacts
|
|
82
83
|
loose_binding
|
83
84
|
|
84
85
|
# Ok, we have bound all players by subscript/role_name, by adjectives, and by loose binding,
|
85
|
-
# and matched all the fact types that matter. Now assemble a
|
86
|
-
# each
|
87
|
-
@
|
86
|
+
# and matched all the fact types that matter. Now assemble a query (with all steps) for
|
87
|
+
# each query list, and build an array of the bindings that are involved in the steps.
|
88
|
+
@bindings_by_list =
|
88
89
|
@clauses_lists.map do |clauses_list|
|
89
|
-
|
90
|
+
all_bindings_in_clauses(clauses_list)
|
90
91
|
end
|
91
92
|
|
92
|
-
|
93
|
+
warn_ignored_queries
|
93
94
|
end
|
94
95
|
|
95
|
-
def
|
96
|
-
# Warn about ignored
|
96
|
+
def warn_ignored_queries
|
97
|
+
# Warn about ignored queries
|
97
98
|
@clauses_lists.each do |clauses_list|
|
98
|
-
fact_types = clauses_list.map{|
|
99
|
+
fact_types = clauses_list.map{|clauses| (rr = clauses.refs[0].role_ref) && rr.role.fact_type}.compact.uniq
|
99
100
|
if fact_types.size > 1
|
100
|
-
|
101
|
+
raise "------->>>> join ignored in #{self.class}: #{fact_types.map{|ft| ft.preferred_reading.expand}*' and '}"
|
101
102
|
end
|
102
103
|
end
|
103
104
|
end
|
@@ -107,28 +108,28 @@ module ActiveFacts
|
|
107
108
|
debug :binding, "Loose binding on #{self.class.name}" do
|
108
109
|
@clauses_lists.each do |clauses_list|
|
109
110
|
clauses_list.each do |clause|
|
110
|
-
clause.
|
111
|
-
next if
|
111
|
+
clause.refs.each_with_index do |ref, i|
|
112
|
+
next if ref.binding.refs.size > 1
|
112
113
|
# if clause.side_effects && !clause.side_effects.role_side_effects[i].residual_adjectives
|
113
|
-
# debug :binding, "Discounting #{
|
114
|
+
# debug :binding, "Discounting #{ref.inspect} as needing loose binding because it has no residual_adjectives"
|
114
115
|
# next
|
115
116
|
# end
|
116
|
-
# This
|
117
|
-
candidates = @context.
|
118
|
-
select do |key,
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
# REVISIT: Don't bind to a
|
123
|
-
!
|
117
|
+
# This ref didn't match any other ref. Have a scout around for a suitable partner
|
118
|
+
candidates = @context.bindings.
|
119
|
+
select do |key, binding|
|
120
|
+
binding.player == ref.binding.player and
|
121
|
+
binding != ref.binding and
|
122
|
+
binding.role_name == ref.binding.role_name and # Both will be nil if they match
|
123
|
+
# REVISIT: Don't bind to a binding with a role occurrence in the same clause
|
124
|
+
!binding.refs.detect{|vr|
|
124
125
|
x = vr.clause == clause
|
125
|
-
# puts "Discounting
|
126
|
+
# puts "Discounting binding #{binding.inspect} as a match for #{ref.inspect} because it's already bound to a player in #{ref.clause.inspect}" if x
|
126
127
|
x
|
127
128
|
}
|
128
129
|
end.map{|k,b| b}
|
129
130
|
next if candidates.size != 1 # Fail
|
130
|
-
debug :binding, "Loose binding #{
|
131
|
-
|
131
|
+
debug :binding, "Loose binding #{ref.inspect} to #{candidates[0].inspect}"
|
132
|
+
ref.rebind_to(@context, candidates[0].refs[0])
|
132
133
|
end
|
133
134
|
end
|
134
135
|
end
|
@@ -138,28 +139,28 @@ module ActiveFacts
|
|
138
139
|
def loose_bind
|
139
140
|
# Apply loose binding over applicable @roles:
|
140
141
|
debug :binding, "Check for loose bindings on #{@roles.size} roles in #{self.class.name}" do
|
141
|
-
@roles.each do |
|
142
|
-
if
|
143
|
-
debug :binding, "Insufficient bindings for #{
|
142
|
+
@roles.each do |ref|
|
143
|
+
if ref.binding.refs.size < @clauses_lists.size+1
|
144
|
+
debug :binding, "Insufficient bindings for #{ref.inspect} (#{ref.binding.refs.size}, expected #{@clauses_lists.size+1}), attempting loose binding" do
|
144
145
|
@clauses_lists.each do |clauses_list|
|
145
146
|
candidates = []
|
146
147
|
next if clauses_list.
|
147
148
|
detect do |clause|
|
148
149
|
debug :binding, "Checking #{clause.inspect}"
|
149
|
-
clause.
|
150
|
+
clause.refs.
|
150
151
|
detect do |vr|
|
151
|
-
already_bound = vr.
|
152
|
-
if !already_bound && vr.player ==
|
152
|
+
already_bound = vr.binding == ref.binding
|
153
|
+
if !already_bound && vr.player == ref.player
|
153
154
|
candidates << vr
|
154
155
|
end
|
155
156
|
already_bound
|
156
157
|
end
|
157
158
|
end
|
158
|
-
debug :binding, "Attempting loose binding for #{
|
159
|
+
debug :binding, "Attempting loose binding for #{ref.inspect} in #{clauses_list.inspect}, from the following candidates: #{candidates.inspect}"
|
159
160
|
|
160
161
|
if candidates.size == 1
|
161
|
-
debug :binding, "Rebinding #{candidates[0].inspect} to #{
|
162
|
-
candidates[0].rebind_to(@context,
|
162
|
+
debug :binding, "Rebinding #{candidates[0].inspect} to #{ref.inspect}"
|
163
|
+
candidates[0].rebind_to(@context, ref)
|
163
164
|
end
|
164
165
|
end
|
165
166
|
end
|
@@ -168,10 +169,10 @@ module ActiveFacts
|
|
168
169
|
end
|
169
170
|
end
|
170
171
|
|
171
|
-
def
|
172
|
-
@
|
173
|
-
raise "#{self.class} must cover some of the same roles, see #{@
|
174
|
-
@
|
172
|
+
def common_bindings
|
173
|
+
@common_bindings ||= @bindings_by_list[1..-1].inject(@bindings_by_list[0]) { |r, b| r & b }
|
174
|
+
raise "#{self.class} must cover some of the same roles, see #{@bindings_by_list.inspect}" unless @common_bindings.size > 0
|
175
|
+
@common_bindings
|
175
176
|
end
|
176
177
|
|
177
178
|
def to_s
|
@@ -180,33 +181,33 @@ module ActiveFacts
|
|
180
181
|
end
|
181
182
|
|
182
183
|
class PresenceConstraint < Constraint
|
183
|
-
def initialize context_note, enforcement, clauses_lists,
|
184
|
+
def initialize context_note, enforcement, clauses_lists, refs, quantifier
|
184
185
|
super context_note, enforcement, clauses_lists
|
185
|
-
@
|
186
|
+
@refs = refs || []
|
186
187
|
@quantifier = quantifier
|
187
188
|
end
|
188
189
|
|
189
190
|
def compile
|
190
191
|
@clauses = @clauses_lists.map do |clauses_list|
|
191
|
-
raise "REVISIT:
|
192
|
-
clauses_list.detect{|clause| clause.
|
192
|
+
raise "REVISIT: join presence constraints not supported yet" if clauses_list.size > 1 or
|
193
|
+
clauses_list.detect{|clause| clause.refs.detect{|vr| vr.nested_clauses } }
|
193
194
|
clauses_list[0]
|
194
195
|
end
|
195
196
|
|
196
|
-
bind_clauses @
|
197
|
+
bind_clauses @refs
|
197
198
|
|
198
|
-
if @
|
199
|
+
if @refs.size > 0
|
199
200
|
bind_constrained_roles
|
200
201
|
else
|
201
|
-
cb =
|
202
|
+
cb = common_bindings
|
202
203
|
raise "Either/or must have only one duplicated role, not #{cb.inspect}" unless cb.size == 1
|
203
|
-
@
|
204
|
+
@refs = cb[0].refs.reverse # REVISIT: Should have order these by clause, not like this
|
204
205
|
end
|
205
206
|
|
206
207
|
role_sequence = @constellation.RoleSequence(:new)
|
207
|
-
@
|
208
|
-
raise "The constrained role #{
|
209
|
-
(
|
208
|
+
@refs.each do |ref|
|
209
|
+
raise "The constrained role #{ref.inspect} was not found in the invoked fact types" if ref.binding.refs.size == 1
|
210
|
+
(ref.binding.refs-[ref]).each do |ref|
|
210
211
|
role = (ref.role_ref && ref.role_ref.role) || ref.role
|
211
212
|
raise "FactType role not found for #{ref.inspect}" unless role
|
212
213
|
@constellation.RoleRef(role_sequence, role_sequence.all_role_ref.size, :role => role)
|
@@ -225,6 +226,7 @@ module ActiveFacts
|
|
225
226
|
:is_mandatory => @quantifier.min && @quantifier.min > 0
|
226
227
|
)
|
227
228
|
@enforcement.compile(@constellation, @constraint) if @enforcement
|
229
|
+
debug :constraint, "Made new PC GUID=#{@constraint.guid} min=#{@quantifier.min.inspect} max=#{@quantifier.max.inspect} over #{role_sequence.describe}"
|
228
230
|
super
|
229
231
|
end
|
230
232
|
|
@@ -234,23 +236,23 @@ module ActiveFacts
|
|
234
236
|
end
|
235
237
|
|
236
238
|
def bind_constrained_roles
|
237
|
-
@
|
238
|
-
if
|
239
|
+
@refs.each do |ref|
|
240
|
+
if ref.binding.refs.size == 1
|
239
241
|
# Apply loose binding over the constrained roles
|
240
242
|
candidates =
|
241
243
|
@clauses.map do |clause|
|
242
|
-
clause.
|
244
|
+
clause.refs.select{ |vr| vr.player == ref.player }
|
243
245
|
end.flatten
|
244
246
|
if candidates.size == 1
|
245
|
-
debug :binding, "Rebinding #{
|
246
|
-
|
247
|
+
debug :binding, "Rebinding #{ref.inspect} to #{candidates[0].inspect} in presence constraint"
|
248
|
+
ref.rebind_to(@context, candidates[0])
|
247
249
|
end
|
248
250
|
end
|
249
251
|
end
|
250
252
|
end
|
251
253
|
|
252
254
|
def to_s
|
253
|
-
"#{super} #{@quantifier.min}-#{@quantifier.max} over (#{@
|
255
|
+
"#{super} #{@quantifier.min}-#{@quantifier.max} over (#{@refs.map{|vr| vr.inspect}*', '})"
|
254
256
|
end
|
255
257
|
end
|
256
258
|
|
@@ -259,53 +261,56 @@ module ActiveFacts
|
|
259
261
|
super context_note, enforcement, clauses_lists
|
260
262
|
end
|
261
263
|
|
262
|
-
def
|
264
|
+
def warn_ignored_queries
|
263
265
|
# No warnings needed here any more
|
264
266
|
end
|
265
267
|
|
266
|
-
def
|
268
|
+
def role_sequences_for_common_bindings ignore_trailing_steps = false
|
267
269
|
@clauses_lists.
|
268
|
-
zip(@
|
269
|
-
map do |clauses_list,
|
270
|
-
# Does this clauses_list involve a
|
270
|
+
zip(@bindings_by_list).
|
271
|
+
map do |clauses_list, bindings|
|
272
|
+
# Does this clauses_list involve a query?
|
271
273
|
if clauses_list.size > 1 or
|
272
|
-
clauses_list.detect
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
#
|
281
|
-
|
282
|
-
|
283
|
-
|
274
|
+
clauses_list.detect do |clause|
|
275
|
+
clause.refs.detect{|ref| ref.nested_clauses } or
|
276
|
+
clause.includes_literals
|
277
|
+
end
|
278
|
+
|
279
|
+
debug :query, "Building query for #{clauses_list.inspect}" do
|
280
|
+
debug :query, "Constrained bindings are #{@common_bindings.inspect}"
|
281
|
+
# Every Binding in these clauses becomes a Variable,
|
282
|
+
# and every clause becomes a Step (and a RoleSequence).
|
283
|
+
# The returned RoleSequences contains the RoleRefs for the common_bindings.
|
284
|
+
|
285
|
+
# Create a query with a variable for every binding and all steps:
|
286
|
+
query = build_variables(clauses_list)
|
287
|
+
roles_by_binding = build_all_steps(clauses_list)
|
288
|
+
query.validate
|
284
289
|
|
285
290
|
# Create the projected RoleSequence for the constraint:
|
286
291
|
role_sequence = @constellation.RoleSequence(:new)
|
287
|
-
@
|
288
|
-
role,
|
289
|
-
@constellation.RoleRef(role_sequence, role_sequence.all_role_ref.size, :role => role, :
|
292
|
+
@common_bindings.each do |binding|
|
293
|
+
role, play = *roles_by_binding[binding]
|
294
|
+
@constellation.RoleRef(role_sequence, role_sequence.all_role_ref.size, :role => role, :play => play)
|
290
295
|
end
|
291
296
|
|
292
297
|
role_sequence
|
293
298
|
end
|
294
299
|
else
|
295
|
-
# There's no
|
300
|
+
# There's no query in this clauses_list, just create a role_sequence
|
296
301
|
role_sequence = @constellation.RoleSequence(:new)
|
297
|
-
|
298
|
-
unless
|
299
|
-
debug :constraint, "REVISIT: #{self.class}: Ignoring
|
302
|
+
query_bindings = bindings-@common_bindings
|
303
|
+
unless query_bindings.empty? or ignore_trailing_steps && query_bindings.size <= 1
|
304
|
+
debug :constraint, "REVISIT: #{self.class}: Ignoring query from #{@common_bindings.inspect} to #{query_bindings.inspect} in #{clauses_list.inspect}"
|
300
305
|
end
|
301
|
-
@
|
306
|
+
@common_bindings.each do |binding|
|
302
307
|
roles = clauses_list.
|
303
308
|
map do |clause|
|
304
|
-
clause.
|
309
|
+
clause.refs.detect{|vr| vr.binding == binding }
|
305
310
|
end.
|
306
|
-
compact. # A
|
307
|
-
map do |
|
308
|
-
|
311
|
+
compact. # A query clause will probably not have the common binding
|
312
|
+
map do |ref|
|
313
|
+
ref.role_ref && ref.role_ref.role or ref.role
|
309
314
|
end.
|
310
315
|
compact
|
311
316
|
# REVISIT: Should use clause side effects to preserve residual adjectives here.
|
@@ -326,10 +331,10 @@ module ActiveFacts
|
|
326
331
|
|
327
332
|
def compile
|
328
333
|
bind_clauses
|
329
|
-
|
334
|
+
common_bindings
|
330
335
|
|
331
336
|
role_sequences =
|
332
|
-
|
337
|
+
role_sequences_for_common_bindings
|
333
338
|
|
334
339
|
@constraint =
|
335
340
|
@constellation.SubsetConstraint(
|
@@ -362,10 +367,10 @@ module ActiveFacts
|
|
362
367
|
|
363
368
|
def compile
|
364
369
|
bind_clauses @roles
|
365
|
-
|
370
|
+
common_bindings
|
366
371
|
|
367
372
|
role_sequences =
|
368
|
-
|
373
|
+
role_sequences_for_common_bindings
|
369
374
|
|
370
375
|
@constraint = @constellation.SetExclusionConstraint(
|
371
376
|
:new,
|
@@ -397,10 +402,10 @@ module ActiveFacts
|
|
397
402
|
|
398
403
|
def compile
|
399
404
|
bind_clauses
|
400
|
-
|
405
|
+
common_bindings
|
401
406
|
|
402
407
|
role_sequences =
|
403
|
-
|
408
|
+
role_sequences_for_common_bindings
|
404
409
|
|
405
410
|
@constraint = @constellation.SetEqualityConstraint(
|
406
411
|
:new,
|
@@ -419,8 +424,9 @@ module ActiveFacts
|
|
419
424
|
end
|
420
425
|
|
421
426
|
class RingConstraint < Constraint
|
422
|
-
Types = %w{acyclic intransitive symmetric asymmetric transitive antisymmetric irreflexive reflexive}
|
427
|
+
Types = %w{acyclic intransitive stronglyintransitive symmetric asymmetric transitive antisymmetric irreflexive reflexive}
|
423
428
|
Pairs = {
|
429
|
+
:stronglyintransitive => [:acyclic, :asymmetric, :symmetric],
|
424
430
|
:intransitive => [:acyclic, :asymmetric, :symmetric],
|
425
431
|
:transitive => [:acyclic],
|
426
432
|
:acyclic => [:transitive],
|
@@ -487,28 +493,42 @@ module ActiveFacts
|
|
487
493
|
end
|
488
494
|
|
489
495
|
class ValueConstraint < Constraint
|
490
|
-
def initialize
|
496
|
+
def initialize ast, enforcement
|
491
497
|
super nil, enforcement
|
492
|
-
@value_ranges =
|
493
|
-
@units = units
|
498
|
+
@value_ranges = ast[:ranges]
|
499
|
+
@units = ast[:units]
|
500
|
+
@regular_expression = ast[:regular_expression]
|
494
501
|
end
|
495
502
|
|
503
|
+
def assert_value(val)
|
504
|
+
if val.is_a?(String)
|
505
|
+
@constellation.Value(eval(val), true, nil)
|
506
|
+
elsif val
|
507
|
+
@constellation.Value(val.to_s, false , nil)
|
508
|
+
else
|
509
|
+
nil
|
510
|
+
end
|
511
|
+
end
|
512
|
+
|
496
513
|
def compile
|
497
514
|
@constraint = @constellation.ValueConstraint(:new)
|
498
515
|
raise "Units on value constraints are not yet processed (at line #{'REVISIT'})" if @units
|
499
516
|
# @string.line_of(node.interval.first)
|
500
517
|
|
501
|
-
|
502
|
-
|
503
|
-
|
504
|
-
|
505
|
-
|
506
|
-
|
507
|
-
|
508
|
-
|
509
|
-
|
510
|
-
|
511
|
-
|
518
|
+
if @value_ranges
|
519
|
+
@value_ranges.each do |range|
|
520
|
+
min, max = Array === range ? range : [range, range]
|
521
|
+
v_range = @constellation.ValueRange(
|
522
|
+
min && @constellation.Bound(:value => assert_value(min), :is_inclusive => true),
|
523
|
+
max && @constellation.Bound(:value => assert_value(max), :is_inclusive => true))
|
524
|
+
ar = @constellation.AllowedRange(@constraint, v_range)
|
525
|
+
end
|
526
|
+
else
|
527
|
+
@constraint.regular_expression = @regular_expression
|
528
|
+
end
|
529
|
+
@enforcement.compile(@constellation, @constraint) if @enforcement
|
530
|
+
super
|
531
|
+
end
|
512
532
|
|
513
533
|
def vrto_s vr
|
514
534
|
if Array === vr
|
@@ -529,7 +549,11 @@ module ActiveFacts
|
|
529
549
|
end
|
530
550
|
|
531
551
|
def to_s
|
532
|
-
"#{super} to
|
552
|
+
"#{super} to " +
|
553
|
+
(@value_ranges ?
|
554
|
+
"(#{@value_ranges.map{|vr| vrto_s(vr) }.inspect })#{ @units ? " in #{@units.inspect}" : ''}" :
|
555
|
+
@regular_expression
|
556
|
+
)
|
533
557
|
end
|
534
558
|
end
|
535
559
|
|
@@ -19,16 +19,18 @@ module ActiveFacts
|
|
19
19
|
end
|
20
20
|
|
21
21
|
class EntityType < ObjectType
|
22
|
-
def initialize name, supertypes, identification, pragmas, clauses
|
22
|
+
def initialize name, supertypes, identification, pragmas, clauses, context_note
|
23
23
|
super name
|
24
24
|
@supertypes = supertypes
|
25
25
|
@identification = identification
|
26
26
|
@pragmas = pragmas
|
27
27
|
@clauses = clauses || []
|
28
|
+
@context_note = context_note
|
28
29
|
end
|
29
30
|
|
30
31
|
def compile
|
31
|
-
@entity_type = @
|
32
|
+
@entity_type = @vocabulary.valid_entity_type_name(@name) ||
|
33
|
+
@constellation.EntityType(@vocabulary, @name, :guid => :new)
|
32
34
|
@entity_type.is_independent = true if (@pragmas.include? 'independent')
|
33
35
|
|
34
36
|
# REVISIT: CQL needs a way to indicate whether subtype migration can occur.
|
@@ -47,7 +49,7 @@ module ActiveFacts
|
|
47
49
|
# Create the fact types that define the identifying roles:
|
48
50
|
fact_types = create_identifying_fact_types context
|
49
51
|
|
50
|
-
# At this point, @identification is an array of
|
52
|
+
# At this point, @identification is an array of References and/or Clauses (for unary fact types)
|
51
53
|
# Have to do this after creating the necessary fact types
|
52
54
|
complete_reference_mode_fact_type fact_types
|
53
55
|
|
@@ -56,6 +58,10 @@ module ActiveFacts
|
|
56
58
|
|
57
59
|
make_preferred_identifier_over_roles identifying_roles
|
58
60
|
|
61
|
+
if @context_note
|
62
|
+
@context_note.compile(@constellation, @entity_type)
|
63
|
+
end
|
64
|
+
|
59
65
|
@clauses.each do |clause|
|
60
66
|
next unless clause.context_note
|
61
67
|
clause.context_note.compile(@constellation, @entity_type)
|
@@ -70,7 +76,7 @@ module ActiveFacts
|
|
70
76
|
if @identification.is_a? ReferenceMode
|
71
77
|
make_entity_type_refmode_valuetypes(name, @identification.name, @identification.parameters)
|
72
78
|
vt_name = @reference_mode_value_type.name
|
73
|
-
@identification = [Compiler::
|
79
|
+
@identification = [Compiler::Reference.new(vt_name, nil, nil, nil, nil, nil, @identification.value_constraint, nil)]
|
74
80
|
else
|
75
81
|
context.allowed_forward_terms = legal_forward_references(@identification)
|
76
82
|
end
|
@@ -80,16 +86,16 @@ module ActiveFacts
|
|
80
86
|
# Names used in the identifying roles list may be forward referenced:
|
81
87
|
def legal_forward_references(identification_phrases)
|
82
88
|
identification_phrases.map do |phrase|
|
83
|
-
phrase.is_a?(
|
89
|
+
phrase.is_a?(Reference) ? phrase.term : nil
|
84
90
|
end.compact.uniq
|
85
91
|
end
|
86
92
|
|
87
93
|
def bind_identifying_roles context
|
88
94
|
return unless @identification
|
89
95
|
@identification.map do |id|
|
90
|
-
if id.is_a?(
|
91
|
-
|
92
|
-
roles =
|
96
|
+
if id.is_a?(Reference)
|
97
|
+
binding = id.binding
|
98
|
+
roles = binding.refs.map{|r|r.role || (rr=r.role_ref and rr.role)}.compact.uniq
|
93
99
|
raise "Looking for an occurrence of identifying role #{id.inspect}, but found #{roles.size == 0 ? "none" : roles.size}" if roles.size != 1
|
94
100
|
roles[0]
|
95
101
|
else
|
@@ -129,6 +135,7 @@ module ActiveFacts
|
|
129
135
|
#:is_mandatory => true,
|
130
136
|
#:min_frequency => 1,
|
131
137
|
)
|
138
|
+
debug :constraint, "Made new preferred PC GUID=#{pc.guid} min=nil max=1 over #{role_sequence.describe}"
|
132
139
|
end
|
133
140
|
end
|
134
141
|
|
@@ -147,7 +154,7 @@ module ActiveFacts
|
|
147
154
|
fact_types = []
|
148
155
|
# Categorise the clauses into fact types according to the roles they play.
|
149
156
|
@clauses.inject({}) do |hash, clause|
|
150
|
-
players_key = clause.
|
157
|
+
players_key = clause.refs.map{|vr| vr.key.compact}.sort
|
151
158
|
(hash[players_key] ||= []) << clause
|
152
159
|
hash
|
153
160
|
end.each do |players_key, clauses|
|
@@ -155,7 +162,9 @@ module ActiveFacts
|
|
155
162
|
|
156
163
|
fact_type = create_identifying_fact_type(context, clauses)
|
157
164
|
fact_types << fact_type if fact_type
|
158
|
-
|
165
|
+
unless fact_type.all_role.detect{|r| r.object_type == @entity_type}
|
166
|
+
objectify_existing_fact_type(fact_type)
|
167
|
+
end
|
159
168
|
end
|
160
169
|
fact_types
|
161
170
|
end
|
@@ -167,10 +176,13 @@ module ActiveFacts
|
|
167
176
|
|
168
177
|
# See if any fact type already exists (this ET cannot be a player, but might objectify it)
|
169
178
|
existing_clauses = clauses.select{ |clause| clause.match_existing_fact_type context }
|
179
|
+
if negation = existing_clauses.detect{|c| c.certainty == false }
|
180
|
+
raise "#{@name} cannot be identified by negated fact type #{negation.inspect}"
|
181
|
+
end
|
170
182
|
any_matched = existing_clauses.size > 0
|
171
183
|
|
172
184
|
operation = any_matched ? 'Objectifying' : 'Creating'
|
173
|
-
player_names = clauses[0].
|
185
|
+
player_names = clauses[0].refs.map{|vr| vr.key.compact*'-'}
|
174
186
|
debug :matching, "#{operation} fact type for #{clauses.size} clauses over (#{player_names*', '})" do
|
175
187
|
if any_matched # There's an existing fact type we must be objectifying
|
176
188
|
fact_type = objectify_existing_fact_type(existing_clauses[0].fact_type)
|
@@ -194,10 +206,13 @@ module ActiveFacts
|
|
194
206
|
|
195
207
|
def objectify_existing_fact_type fact_type
|
196
208
|
raise "#{@name} cannot objectify fact type '#{fact_type.entity_type.name}' that's already objectified" if fact_type.entity_type
|
197
|
-
|
209
|
+
if @fact_type
|
210
|
+
raise "#{@name} cannot objectify '#{fact_type.default_reading}', it already objectifies '#{@fact_type.default_reading}'"
|
211
|
+
end
|
212
|
+
|
198
213
|
if fact_type.internal_presence_constraints.select{|pc| pc.max_frequency == 1}.size == 0
|
199
214
|
# If there's no existing uniqueness constraint over this fact type, make a spanning one.
|
200
|
-
@constellation.PresenceConstraint(
|
215
|
+
pc = @constellation.PresenceConstraint(
|
201
216
|
:new,
|
202
217
|
:vocabulary => @vocabulary,
|
203
218
|
:name => @entity_type.name+"UQ",
|
@@ -205,18 +220,20 @@ module ActiveFacts
|
|
205
220
|
:is_preferred_identifier => false, # We only get here when there is a reference mode on the entity type
|
206
221
|
:max_frequency => 1
|
207
222
|
)
|
223
|
+
debug :constraint, "Made new objectification PC GUID=#{pc.guid} min=nil max=1 over #{fact_type.preferred_reading.role_sequence.describe}"
|
208
224
|
end
|
209
225
|
|
210
226
|
@fact_type = @entity_type.fact_type = fact_type
|
211
|
-
@entity_type.create_implicit_fact_types
|
227
|
+
@entity_type.create_implicit_fact_types # REVISIT: Could there be readings for the implicit fact types here?
|
212
228
|
@fact_type
|
213
229
|
end
|
214
230
|
|
215
231
|
def add_supertype(supertype_name, not_identifying)
|
216
|
-
debug :supertype, "Adding supertype #{supertype_name}" do
|
217
|
-
supertype = @
|
232
|
+
debug :supertype, "Adding #{not_identifying ? '' : 'identifying '}supertype #{supertype_name} to #{@entity_type.name}" do
|
233
|
+
supertype = @vocabulary.valid_entity_type_name(supertype_name) ||
|
234
|
+
@constellation.EntityType(@vocabulary, supertype_name, :guid => :new) # Should always already exist
|
218
235
|
|
219
|
-
# Did we already know about this
|
236
|
+
# Did we already know about this supertyping?
|
220
237
|
return if @entity_type.all_type_inheritance_as_subtype.detect{|ti| ti.supertype == supertype}
|
221
238
|
|
222
239
|
# By default, the first supertype identifies this entity type
|
@@ -235,14 +252,14 @@ module ActiveFacts
|
|
235
252
|
rs = @constellation.RoleSequence(:new)
|
236
253
|
@constellation.RoleRef(rs, 0, :role => sub_role)
|
237
254
|
@constellation.RoleRef(rs, 1, :role => super_role)
|
238
|
-
@constellation.Reading(inheritance_fact, 0, :role_sequence => rs, :text => "{0} is a kind of {1}")
|
255
|
+
@constellation.Reading(inheritance_fact, 0, :role_sequence => rs, :text => "{0} is a kind of {1}", :is_negative => false)
|
239
256
|
|
240
257
|
rs2 = @constellation.RoleSequence(:new)
|
241
258
|
@constellation.RoleRef(rs2, 0, :role => super_role)
|
242
259
|
@constellation.RoleRef(rs2, 1, :role => sub_role)
|
243
260
|
# Decide in which order to include is a/is an. Provide both, but in order.
|
244
261
|
n = 'aeiouh'.include?(sub_role.object_type.name.downcase[0]) ? 'n' : ''
|
245
|
-
@constellation.Reading(inheritance_fact, 2, :role_sequence => rs2, :text => "{0} is a#{n} {1}")
|
262
|
+
@constellation.Reading(inheritance_fact, 2, :role_sequence => rs2, :text => "{0} is a#{n} {1}", :is_negative => false)
|
246
263
|
|
247
264
|
if is_identifying_supertype
|
248
265
|
inheritance_fact.provides_identification = true
|
@@ -258,6 +275,7 @@ module ActiveFacts
|
|
258
275
|
pc1.min_frequency = 1
|
259
276
|
pc1.max_frequency = 1
|
260
277
|
pc1.is_preferred_identifier = false
|
278
|
+
debug :constraint, "Made new subtype PC GUID=#{pc1.guid} min=1 max=1 over #{p1rs.describe}"
|
261
279
|
|
262
280
|
p2rs = @constellation.RoleSequence(:new)
|
263
281
|
constellation.RoleRef(p2rs, 0).role = super_role
|
@@ -269,6 +287,8 @@ module ActiveFacts
|
|
269
287
|
pc2.max_frequency = 1
|
270
288
|
# The supertype role often identifies the subtype:
|
271
289
|
pc2.is_preferred_identifier = inheritance_fact.provides_identification
|
290
|
+
debug :supertype, "identification of #{@entity_type.name} via supertype #{supertype.name} was #{inheritance_fact.provides_identification ? '' : 'not '}added"
|
291
|
+
debug :constraint, "Made new supertype PC GUID=#{pc2.guid} min=1 max=1 over #{p2rs.describe}"
|
272
292
|
end
|
273
293
|
end
|
274
294
|
|
@@ -276,10 +296,12 @@ module ActiveFacts
|
|
276
296
|
vt_name = "#{name}#{mode}"
|
277
297
|
vt = nil
|
278
298
|
debug :entity, "Preparing value type #{vt_name} for reference mode" do
|
279
|
-
# Find
|
280
|
-
|
281
|
-
|
282
|
-
|
299
|
+
# Find an existing ValueType called 'vt_name' or 'name vtname'
|
300
|
+
# or find/create the supertype '#{mode}' and the subtype
|
301
|
+
unless vt = @vocabulary.valid_object_type_name(vt_name) or
|
302
|
+
vt = @vocabulary.valid_object_type_name(vt_name = "#{name} #{mode}")
|
303
|
+
base_vt = @vocabulary.valid_value_type_name(mode) ||
|
304
|
+
@constellation.ValueType(@vocabulary, mode, :guid => :new)
|
283
305
|
vt = @constellation.ValueType(@vocabulary, vt_name, :supertype => base_vt, :guid => :new)
|
284
306
|
if parameters
|
285
307
|
length, scale = *parameters
|
@@ -340,7 +362,7 @@ module ActiveFacts
|
|
340
362
|
@constellation.RoleRef(rs01, 1, :role => identifying_role)
|
341
363
|
end
|
342
364
|
if rs01.all_reading.empty?
|
343
|
-
@constellation.Reading(fact_type, fact_type.all_reading.size, :role_sequence => rs01, :text => "{0} has {1}")
|
365
|
+
@constellation.Reading(fact_type, fact_type.all_reading.size, :role_sequence => rs01, :text => "{0} has {1}", :is_negative => false)
|
344
366
|
debug :mode, "Creating new forward reading '#{entity_role.object_type.name} has #{identifying_type.name}'"
|
345
367
|
else
|
346
368
|
debug :mode, "Using existing forward reading"
|
@@ -354,7 +376,7 @@ module ActiveFacts
|
|
354
376
|
@constellation.RoleRef(rs10, 1, :role => entity_role)
|
355
377
|
end
|
356
378
|
if rs10.all_reading.empty?
|
357
|
-
@constellation.Reading(fact_type, fact_type.all_reading.size, :role_sequence => rs10, :text => "{0} is of {1}")
|
379
|
+
@constellation.Reading(fact_type, fact_type.all_reading.size, :role_sequence => rs10, :text => "{0} is of {1}", :is_negative => false)
|
358
380
|
debug :mode, "Creating new reverse reading '#{identifying_type.name} is of #{entity_role.object_type.name}'"
|
359
381
|
else
|
360
382
|
debug :mode, "Using existing reverse reading"
|
@@ -381,7 +403,7 @@ module ActiveFacts
|
|
381
403
|
:is_preferred_identifier => false,
|
382
404
|
:is_mandatory => true
|
383
405
|
)
|
384
|
-
|
406
|
+
debug :constraint, "Made new refmode PC GUID=#{constraint.guid} min=1 max=1 over #{rs0.describe}"
|
385
407
|
else
|
386
408
|
debug :mode, "Using existing EntityType PresenceConstraint"
|
387
409
|
end
|
@@ -409,7 +431,7 @@ module ActiveFacts
|
|
409
431
|
:is_preferred_identifier => true,
|
410
432
|
:is_mandatory => false
|
411
433
|
)
|
412
|
-
|
434
|
+
debug :constraint, "Made new refmode ValueType PC GUID=#{constraint.guid} min=0 max=1 over #{rs1.describe}"
|
413
435
|
else
|
414
436
|
debug :mode, "Marking existing ValueType PresenceConstraint as preferred"
|
415
437
|
rs1.all_presence_constraint.single.is_preferred_identifier = true
|