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
@@ -11,9 +11,13 @@ module ActiveFacts
|
|
11
11
|
value_type
|
12
12
|
end
|
13
13
|
|
14
|
+
class AggregateCode < String
|
15
|
+
value_type :length => 32
|
16
|
+
end
|
17
|
+
|
14
18
|
class Assimilation < String
|
15
19
|
value_type
|
16
|
-
restrict 'partitioned', 'separate'
|
20
|
+
restrict 'absorbed', 'partitioned', 'separate'
|
17
21
|
end
|
18
22
|
|
19
23
|
class ContextNoteKind < String
|
@@ -58,6 +62,10 @@ module ActiveFacts
|
|
58
62
|
value_type
|
59
63
|
end
|
60
64
|
|
65
|
+
class ImplicationRuleName < String
|
66
|
+
value_type
|
67
|
+
end
|
68
|
+
|
61
69
|
class Length < UnsignedInteger
|
62
70
|
value_type :length => 32
|
63
71
|
end
|
@@ -87,6 +95,10 @@ module ActiveFacts
|
|
87
95
|
restrict 'feminine', 'masculine', 'neuter', 'personal'
|
88
96
|
end
|
89
97
|
|
98
|
+
class RegularExpression < String
|
99
|
+
value_type
|
100
|
+
end
|
101
|
+
|
90
102
|
class RingType < String
|
91
103
|
value_type
|
92
104
|
end
|
@@ -126,9 +138,15 @@ module ActiveFacts
|
|
126
138
|
one_to_one :agent_name, :mandatory => true # See AgentName.agent
|
127
139
|
end
|
128
140
|
|
141
|
+
class Aggregate
|
142
|
+
identified_by :aggregate_code
|
143
|
+
one_to_one :aggregate_code, :mandatory => true # See AggregateCode.aggregate
|
144
|
+
end
|
145
|
+
|
129
146
|
class AlternativeSet
|
130
147
|
identified_by :guid
|
131
148
|
one_to_one :guid, :mandatory => true # See Guid.alternative_set
|
149
|
+
maybe :members_are_exclusive
|
132
150
|
end
|
133
151
|
|
134
152
|
class Coefficient
|
@@ -141,6 +159,7 @@ module ActiveFacts
|
|
141
159
|
class Concept
|
142
160
|
identified_by :guid
|
143
161
|
one_to_one :guid, :mandatory => true # See Guid.concept
|
162
|
+
has_one :implication_rule # See ImplicationRule.all_concept
|
144
163
|
end
|
145
164
|
|
146
165
|
class Constraint < Concept
|
@@ -149,9 +168,9 @@ module ActiveFacts
|
|
149
168
|
end
|
150
169
|
|
151
170
|
class ContextNote < Concept
|
152
|
-
has_one :concept # See Concept.all_context_note
|
153
171
|
has_one :context_note_kind, :mandatory => true # See ContextNoteKind.all_context_note
|
154
172
|
has_one :discussion, :mandatory => true # See Discussion.all_context_note
|
173
|
+
has_one :relevant_concept, :class => Concept # See Concept.all_context_note_as_relevant_concept
|
155
174
|
end
|
156
175
|
|
157
176
|
class Enforcement
|
@@ -169,7 +188,9 @@ module ActiveFacts
|
|
169
188
|
class FactType < Concept
|
170
189
|
end
|
171
190
|
|
172
|
-
class
|
191
|
+
class ImplicationRule
|
192
|
+
identified_by :implication_rule_name
|
193
|
+
one_to_one :implication_rule_name, :mandatory => true # See ImplicationRuleName.implication_rule
|
173
194
|
end
|
174
195
|
|
175
196
|
class Instance < Concept
|
@@ -179,23 +200,13 @@ module ActiveFacts
|
|
179
200
|
has_one :value # See Value.all_instance
|
180
201
|
end
|
181
202
|
|
182
|
-
class
|
183
|
-
end
|
184
|
-
|
185
|
-
class JoinNode
|
186
|
-
identified_by :join, :ordinal
|
187
|
-
has_one :join, :mandatory => true # See Join.all_join_node
|
188
|
-
has_one :object_type, :mandatory => true # See ObjectType.all_join_node
|
189
|
-
has_one :ordinal, :mandatory => true # See Ordinal.all_join_node
|
190
|
-
has_one :role_name, :class => Name # See Name.all_join_node_as_role_name
|
191
|
-
has_one :subscript # See Subscript.all_join_node
|
192
|
-
has_one :value # See Value.all_join_node
|
203
|
+
class LinkFactType < FactType
|
193
204
|
end
|
194
205
|
|
195
|
-
class
|
206
|
+
class Location
|
196
207
|
identified_by :x, :y
|
197
|
-
has_one :x, :mandatory => true # See X.
|
198
|
-
has_one :y, :mandatory => true # See Y.
|
208
|
+
has_one :x, :mandatory => true # See X.all_location
|
209
|
+
has_one :y, :mandatory => true # See Y.all_location
|
199
210
|
end
|
200
211
|
|
201
212
|
class PresenceConstraint < Constraint
|
@@ -206,9 +217,13 @@ module ActiveFacts
|
|
206
217
|
has_one :role_sequence, :mandatory => true # See RoleSequence.all_presence_constraint
|
207
218
|
end
|
208
219
|
|
220
|
+
class Query < Concept
|
221
|
+
end
|
222
|
+
|
209
223
|
class Reading
|
210
224
|
identified_by :fact_type, :ordinal
|
211
225
|
has_one :fact_type, :mandatory => true # See FactType.all_reading
|
226
|
+
maybe :is_negative
|
212
227
|
has_one :ordinal, :mandatory => true # See Ordinal.all_reading
|
213
228
|
has_one :role_sequence, :mandatory => true # See RoleSequence.all_reading
|
214
229
|
has_one :text, :mandatory => true # See Text.all_reading
|
@@ -223,7 +238,7 @@ module ActiveFacts
|
|
223
238
|
class Role < Concept
|
224
239
|
identified_by :fact_type, :ordinal
|
225
240
|
has_one :fact_type, :mandatory => true # See FactType.all_role
|
226
|
-
one_to_one :
|
241
|
+
one_to_one :link_fact_type, :counterpart => :implying_role # See LinkFactType.implying_role
|
227
242
|
has_one :object_type, :mandatory => true # See ObjectType.all_role
|
228
243
|
has_one :ordinal, :mandatory => true # See Ordinal.all_role
|
229
244
|
has_one :role_name, :class => Name # See Name.all_role_as_role_name
|
@@ -236,7 +251,7 @@ module ActiveFacts
|
|
236
251
|
end
|
237
252
|
|
238
253
|
class RoleValue
|
239
|
-
identified_by :
|
254
|
+
identified_by :fact, :role
|
240
255
|
has_one :fact, :mandatory => true # See Fact.all_role_value
|
241
256
|
has_one :instance, :mandatory => true # See Instance.all_role_value
|
242
257
|
has_one :population, :mandatory => true # See Population.all_role_value
|
@@ -248,10 +263,10 @@ module ActiveFacts
|
|
248
263
|
|
249
264
|
class Shape
|
250
265
|
identified_by :guid
|
251
|
-
has_one :diagram, :mandatory => true # See Diagram.all_shape
|
252
266
|
one_to_one :guid, :mandatory => true # See Guid.shape
|
253
267
|
maybe :is_expanded
|
254
|
-
has_one :
|
268
|
+
has_one :location # See Location.all_shape
|
269
|
+
has_one :orm_diagram, :class => "ORMDiagram", :mandatory => true # See ORMDiagram.all_shape
|
255
270
|
end
|
256
271
|
|
257
272
|
class SubsetConstraint < SetConstraint
|
@@ -270,21 +285,41 @@ module ActiveFacts
|
|
270
285
|
end
|
271
286
|
|
272
287
|
class Value
|
273
|
-
identified_by :literal, :
|
274
|
-
maybe :
|
288
|
+
identified_by :literal, :is_literal_string, :unit
|
289
|
+
maybe :is_literal_string
|
275
290
|
has_one :literal, :mandatory => true # See Literal.all_value
|
276
291
|
has_one :unit # See Unit.all_value
|
292
|
+
has_one :value_type, :mandatory => true # See ValueType.all_value
|
277
293
|
end
|
278
294
|
|
279
295
|
class ValueConstraint < Constraint
|
296
|
+
has_one :regular_expression # See RegularExpression.all_value_constraint
|
280
297
|
one_to_one :role, :counterpart => :role_value_constraint # See Role.role_value_constraint
|
281
298
|
end
|
282
299
|
|
300
|
+
class Variable
|
301
|
+
identified_by :query, :ordinal
|
302
|
+
has_one :object_type, :mandatory => true # See ObjectType.all_variable
|
303
|
+
has_one :ordinal, :mandatory => true # See Ordinal.all_variable
|
304
|
+
one_to_one :projection, :class => Role # See Role.variable_as_projection
|
305
|
+
has_one :query, :mandatory => true # See Query.all_variable
|
306
|
+
has_one :role_name, :class => Name # See Name.all_variable_as_role_name
|
307
|
+
has_one :subscript # See Subscript.all_variable
|
308
|
+
has_one :value # See Value.all_variable
|
309
|
+
end
|
310
|
+
|
283
311
|
class Vocabulary
|
284
312
|
identified_by :name
|
285
313
|
one_to_one :name, :mandatory => true # See Name.vocabulary
|
286
314
|
end
|
287
315
|
|
316
|
+
class Aggregation
|
317
|
+
identified_by :aggregate, :aggregated_variable
|
318
|
+
has_one :aggregate, :mandatory => true # See Aggregate.all_aggregation
|
319
|
+
has_one :aggregated_variable, :class => Variable, :mandatory => true # See Variable.all_aggregation_as_aggregated_variable
|
320
|
+
has_one :variable, :mandatory => true # See Variable.all_aggregation
|
321
|
+
end
|
322
|
+
|
288
323
|
class Agreement
|
289
324
|
identified_by :context_note
|
290
325
|
one_to_one :context_note, :mandatory => true # See ContextNote.agreement
|
@@ -333,27 +368,13 @@ module ActiveFacts
|
|
333
368
|
has_one :rotation_setting # See RotationSetting.all_fact_type_shape
|
334
369
|
end
|
335
370
|
|
336
|
-
class JoinRole
|
337
|
-
identified_by :join_node, :role
|
338
|
-
has_one :join_node, :mandatory => true # See JoinNode.all_join_role
|
339
|
-
has_one :role, :mandatory => true # See Role.all_join_role
|
340
|
-
has_one :join_step, :counterpart => :incidental_join_role # See JoinStep.all_incidental_join_role
|
341
|
-
end
|
342
|
-
|
343
|
-
class JoinStep
|
344
|
-
identified_by :input_join_role, :output_join_role
|
345
|
-
has_one :alternative_set # See AlternativeSet.all_join_step
|
346
|
-
has_one :fact_type, :mandatory => true # See FactType.all_join_step
|
347
|
-
has_one :input_join_role, :class => JoinRole, :mandatory => true # See JoinRole.all_join_step_as_input_join_role
|
348
|
-
maybe :is_anti
|
349
|
-
maybe :is_outer
|
350
|
-
has_one :output_join_role, :class => JoinRole, :mandatory => true # See JoinRole.all_join_step_as_output_join_role
|
351
|
-
end
|
352
|
-
|
353
371
|
class ModelNoteShape < Shape
|
354
372
|
has_one :context_note, :mandatory => true # See ContextNote.all_model_note_shape
|
355
373
|
end
|
356
374
|
|
375
|
+
class ORMDiagram < Diagram
|
376
|
+
end
|
377
|
+
|
357
378
|
class ObjectType < Concept
|
358
379
|
identified_by :vocabulary, :name
|
359
380
|
maybe :is_independent
|
@@ -368,10 +389,16 @@ module ActiveFacts
|
|
368
389
|
end
|
369
390
|
|
370
391
|
class ObjectifiedFactTypeNameShape < Shape
|
371
|
-
identified_by :fact_type_shape
|
372
392
|
one_to_one :fact_type_shape, :mandatory => true # See FactTypeShape.objectified_fact_type_name_shape
|
373
393
|
end
|
374
394
|
|
395
|
+
class Play
|
396
|
+
identified_by :variable, :role
|
397
|
+
has_one :role, :mandatory => true # See Role.all_play
|
398
|
+
has_one :variable, :mandatory => true # See Variable.all_play
|
399
|
+
has_one :step, :counterpart => :incidental_play # See Step.all_incidental_play
|
400
|
+
end
|
401
|
+
|
375
402
|
class Population < Concept
|
376
403
|
identified_by :vocabulary, :name
|
377
404
|
has_one :name, :mandatory => true # See Name.all_population
|
@@ -379,7 +406,6 @@ module ActiveFacts
|
|
379
406
|
end
|
380
407
|
|
381
408
|
class ReadingShape < Shape
|
382
|
-
identified_by :fact_type_shape
|
383
409
|
one_to_one :fact_type_shape, :mandatory => true # See FactTypeShape.reading_shape
|
384
410
|
has_one :reading, :mandatory => true # See Reading.all_reading_shape
|
385
411
|
end
|
@@ -404,8 +430,8 @@ module ActiveFacts
|
|
404
430
|
has_one :ordinal, :mandatory => true # See Ordinal.all_role_ref
|
405
431
|
has_one :role, :mandatory => true # See Role.all_role_ref
|
406
432
|
has_one :role_sequence, :mandatory => true # See RoleSequence.all_role_ref
|
407
|
-
one_to_one :join_role # See JoinRole.role_ref
|
408
433
|
has_one :leading_adjective, :class => Adjective # See Adjective.all_role_ref_as_leading_adjective
|
434
|
+
one_to_one :play # See Play.role_ref
|
409
435
|
has_one :trailing_adjective, :class => Adjective # See Adjective.all_role_ref_as_trailing_adjective
|
410
436
|
end
|
411
437
|
|
@@ -426,6 +452,16 @@ module ActiveFacts
|
|
426
452
|
maybe :is_mandatory
|
427
453
|
end
|
428
454
|
|
455
|
+
class Step
|
456
|
+
identified_by :input_play, :output_play
|
457
|
+
has_one :alternative_set # See AlternativeSet.all_step
|
458
|
+
has_one :fact_type, :mandatory => true # See FactType.all_step
|
459
|
+
has_one :input_play, :class => Play, :mandatory => true # See Play.all_step_as_input_play
|
460
|
+
maybe :is_disallowed
|
461
|
+
maybe :is_optional
|
462
|
+
has_one :output_play, :class => Play # See Play.all_step_as_output_play
|
463
|
+
end
|
464
|
+
|
429
465
|
class ValueConstraintShape < ConstraintShape
|
430
466
|
has_one :object_type_shape # See ObjectTypeShape.all_value_constraint_shape
|
431
467
|
one_to_one :role_display # See RoleDisplay.value_constraint_shape
|
@@ -437,49 +473,53 @@ module ActiveFacts
|
|
437
473
|
has_one :minimum_bound, :class => Bound # See Bound.all_value_range_as_minimum_bound
|
438
474
|
end
|
439
475
|
|
440
|
-
class ValueType < ObjectType
|
441
|
-
has_one :auto_assigned_transaction_timing, :class => TransactionTiming # See TransactionTiming.all_value_type_as_auto_assigned_transaction_timing
|
442
|
-
has_one :length # See Length.all_value_type
|
443
|
-
has_one :scale # See Scale.all_value_type
|
444
|
-
has_one :supertype, :class => ValueType # See ValueType.all_value_type_as_supertype
|
445
|
-
has_one :unit # See Unit.all_value_type
|
446
|
-
one_to_one :value_constraint # See ValueConstraint.value_type
|
447
|
-
end
|
448
|
-
|
449
476
|
class AllowedRange
|
450
477
|
identified_by :value_constraint, :value_range
|
451
478
|
has_one :value_constraint, :mandatory => true # See ValueConstraint.all_allowed_range
|
452
479
|
has_one :value_range, :mandatory => true # See ValueRange.all_allowed_range
|
453
480
|
end
|
454
481
|
|
455
|
-
class
|
482
|
+
class DomainObjectType < ObjectType
|
483
|
+
end
|
484
|
+
|
485
|
+
class EntityType < DomainObjectType
|
456
486
|
one_to_one :fact_type # See FactType.entity_type
|
457
487
|
maybe :is_implied_by_objectification
|
458
488
|
end
|
459
489
|
|
490
|
+
class TypeInheritance < FactType
|
491
|
+
identified_by :subtype, :supertype
|
492
|
+
has_one :subtype, :class => EntityType, :mandatory => true # See EntityType.all_type_inheritance_as_subtype
|
493
|
+
has_one :supertype, :class => EntityType, :mandatory => true # See EntityType.all_type_inheritance_as_supertype
|
494
|
+
has_one :assimilation # See Assimilation.all_type_inheritance
|
495
|
+
maybe :provides_identification
|
496
|
+
end
|
497
|
+
|
498
|
+
class ValueType < DomainObjectType
|
499
|
+
has_one :length # See Length.all_value_type
|
500
|
+
has_one :scale # See Scale.all_value_type
|
501
|
+
has_one :supertype, :class => ValueType # See ValueType.all_value_type_as_supertype
|
502
|
+
has_one :transaction_timing # See TransactionTiming.all_value_type
|
503
|
+
has_one :unit # See Unit.all_value_type
|
504
|
+
one_to_one :value_constraint # See ValueConstraint.value_type
|
505
|
+
end
|
506
|
+
|
460
507
|
class Facet
|
461
508
|
identified_by :value_type, :name
|
462
509
|
has_one :name, :mandatory => true # See Name.all_facet
|
463
510
|
has_one :value_type, :mandatory => true # See ValueType.all_facet
|
511
|
+
has_one :facet_value_type, :class => ValueType, :mandatory => true # See ValueType.all_facet_as_facet_value_type
|
464
512
|
end
|
465
513
|
|
466
|
-
class
|
514
|
+
class FacetRestriction
|
467
515
|
identified_by :value_type, :facet
|
468
|
-
has_one :facet, :mandatory => true # See Facet.
|
469
|
-
has_one :
|
470
|
-
has_one :
|
516
|
+
has_one :facet, :mandatory => true # See Facet.all_facet_restriction
|
517
|
+
has_one :value_type, :mandatory => true # See ValueType.all_facet_restriction
|
518
|
+
has_one :value, :mandatory => true # See Value.all_facet_restriction
|
471
519
|
end
|
472
520
|
|
473
521
|
class ImplicitBooleanValueType < ValueType
|
474
522
|
end
|
475
523
|
|
476
|
-
class TypeInheritance < FactType
|
477
|
-
identified_by :subtype, :supertype
|
478
|
-
has_one :subtype, :class => EntityType, :mandatory => true # See EntityType.all_type_inheritance_as_subtype
|
479
|
-
has_one :supertype, :class => EntityType, :mandatory => true # See EntityType.all_type_inheritance_as_supertype
|
480
|
-
has_one :assimilation # See Assimilation.all_type_inheritance
|
481
|
-
maybe :provides_identification
|
482
|
-
end
|
483
|
-
|
484
524
|
end
|
485
525
|
end
|
@@ -9,7 +9,7 @@ module ActiveFacts
|
|
9
9
|
#
|
10
10
|
# The Verbaliser fulfils two roles:
|
11
11
|
# * Maintains verbalisation context to expand readings using subscripting where needed
|
12
|
-
# * Verbalises
|
12
|
+
# * Verbalises Queries by iteratively choosing a Step and expanding readings
|
13
13
|
#
|
14
14
|
# The verbalisation context consists of a set of Players, each for one ObjectType.
|
15
15
|
# There may be more than one Player for the same ObjectType. If adjectives or role
|
@@ -17,30 +17,30 @@ module ActiveFacts
|
|
17
17
|
# Thus, the verbalisation context must be completely populated before subscript
|
18
18
|
# generation, which must be before any Player name gets verbalised.
|
19
19
|
#
|
20
|
-
# When a Player occurs in a
|
21
|
-
# Each such Player has one or more
|
22
|
-
# that ObjectType. Where a
|
23
|
-
# will be a residual node that has only a single
|
24
|
-
# A
|
20
|
+
# When a Player occurs in a Query, it corresponds to one Variable of that Query.
|
21
|
+
# Each such Player has one or more Plays, which refer to roles played by
|
22
|
+
# that ObjectType. Where a query traverses two roles of a ternary fact type, there
|
23
|
+
# will be a residual node that has only a single Play with no other meaning.
|
24
|
+
# A Play must be for exactly one Player, so is used to identify a Player.
|
25
25
|
#
|
26
|
-
# When a Player occurs outside a
|
26
|
+
# When a Player occurs outside a Query, it's identified by a projected RoleRef.
|
27
27
|
# REVISIT: This is untrue when a uniqueness constraint is imported from NORMA.
|
28
|
-
# In this case no
|
28
|
+
# In this case no query will be constructed to project the roles of the constrained
|
29
29
|
# object type (only the constrained roles will be projected) - this will be fixed.
|
30
30
|
#
|
31
31
|
# Each constraint (except Ring Constraints) has one or more RoleSequence containing
|
32
|
-
# the projected RoleRefs. Each constrained RoleSequence may have an associated
|
33
|
-
# If it has a
|
32
|
+
# the projected RoleRefs. Each constrained RoleSequence may have an associated Query.
|
33
|
+
# If it has a Query, each RoleRef is projected from a Play, otherwise none are.
|
34
34
|
#
|
35
|
-
# The only type of
|
35
|
+
# The only type of query possible in a Ring Constraint is a subtyping query, which
|
36
36
|
# is always implicit and unambiguous, so is never instantiated.
|
37
37
|
#
|
38
|
-
# A constrained RoleSequence that has no explicit
|
39
|
-
# as per ORM2, when the roles aren't in the same fact type. These implicit
|
38
|
+
# A constrained RoleSequence that has no explicit Query may have an implicit query,
|
39
|
+
# as per ORM2, when the roles aren't in the same fact type. These implicit queries
|
40
40
|
# are over only one ObjectType, by traversing a single FactType (and possibly,
|
41
41
|
# multiple TypeInheritance FactTypes) for each RoleRef. Note however that when
|
42
42
|
# the ObjectType is an objectified Fact Type, the FactType traversed might be a
|
43
|
-
# phantom of the objectification. In the case of implicit
|
43
|
+
# phantom of the objectification. In the case of implicit queries, each Player is
|
44
44
|
# identified by the projected RoleRef, except for the joined-over ObjectType whose
|
45
45
|
# Player is... well, read the next paragraph!
|
46
46
|
#
|
@@ -48,14 +48,14 @@ module ActiveFacts
|
|
48
48
|
# respect to PresenceConstraints imported from NORMA (both external mandatory
|
49
49
|
# and external uniqueness constraints). The joined-over Player in a UC is
|
50
50
|
# identified by its RoleRefs in the RoleSequence of the Fact Type's preferred
|
51
|
-
# reading. Subtyping
|
52
|
-
# However, all other such
|
51
|
+
# reading. Subtyping steps in a mandatory constraint will probably malfunction.
|
52
|
+
# However, all other such queries are explicit, and these should be also.
|
53
53
|
#
|
54
54
|
# For a SetComparisonConstraint, there are two or more constrained RoleSequences.
|
55
55
|
# The matching RoleRefs (by Ordinal position) are for joined players, that is,
|
56
56
|
# one individual instance plays both roles. The RoleRefs must (now) be for the
|
57
|
-
# same ObjectType (no implicit subtyping
|
58
|
-
# find the closest common supertype and create explicit
|
57
|
+
# same ObjectType (no implicit subtyping step is allowed). Instead, the input modules
|
58
|
+
# find the closest common supertype and create explicit Steps so its roles
|
59
59
|
# can be projected.
|
60
60
|
#
|
61
61
|
# When expanding Reading text however, the RoleRefs in the reading's RoleSequence
|
@@ -67,44 +67,44 @@ module ActiveFacts
|
|
67
67
|
class Verbaliser
|
68
68
|
# Verbalisation context:
|
69
69
|
attr_reader :players
|
70
|
-
attr_reader :
|
71
|
-
attr_reader :player_joined_over # Used when there's an implicit
|
72
|
-
attr_reader :player_by_role_ref # Used when a constrained role sequence has no
|
70
|
+
attr_reader :player_by_play # Used for each query
|
71
|
+
attr_reader :player_joined_over # Used when there's an implicit query
|
72
|
+
attr_reader :player_by_role_ref # Used when a constrained role sequence has no query
|
73
73
|
|
74
74
|
# The projected role references over which we're verbalising
|
75
75
|
attr_reader :role_refs
|
76
76
|
|
77
|
-
#
|
78
|
-
attr_reader :
|
79
|
-
attr_reader :
|
80
|
-
attr_reader :
|
81
|
-
attr_reader :
|
77
|
+
# Query Verbaliser context:
|
78
|
+
attr_reader :query
|
79
|
+
attr_reader :variables # All Variables
|
80
|
+
attr_reader :steps # All remaining unemitted Steps
|
81
|
+
attr_reader :steps_by_variable # A Hash by Variable containing an array of remaining steps
|
82
82
|
|
83
83
|
def initialize role_refs = nil
|
84
84
|
@role_refs = role_refs
|
85
85
|
|
86
86
|
# Verbalisation context:
|
87
87
|
@players = []
|
88
|
-
@
|
88
|
+
@player_by_play = {}
|
89
89
|
@player_by_role_ref = {}
|
90
90
|
@player_joined_over = nil
|
91
91
|
|
92
|
-
#
|
93
|
-
@
|
94
|
-
@
|
95
|
-
@
|
96
|
-
@
|
92
|
+
# Query Verbaliser context:
|
93
|
+
@query = nil
|
94
|
+
@variables = []
|
95
|
+
@steps = []
|
96
|
+
@steps_by_variable = {}
|
97
97
|
|
98
98
|
add_role_refs role_refs if role_refs
|
99
99
|
end
|
100
100
|
|
101
101
|
class Player
|
102
|
-
attr_accessor :object_type, :
|
102
|
+
attr_accessor :object_type, :variables_by_query, :subscript, :plays, :role_refs
|
103
103
|
def initialize object_type
|
104
104
|
@object_type = object_type
|
105
|
-
@
|
105
|
+
@variables_by_query = {}
|
106
106
|
@subscript = nil
|
107
|
-
@
|
107
|
+
@plays = []
|
108
108
|
@role_refs = []
|
109
109
|
end
|
110
110
|
|
@@ -122,21 +122,21 @@ module ActiveFacts
|
|
122
122
|
rr.trailing_adjective
|
123
123
|
].compact}.uniq.sort
|
124
124
|
end
|
125
|
-
adjuncts += [@
|
125
|
+
adjuncts += [@variables_by_query.values.map{|jn| jn.role_name}.compact[0]].compact
|
126
126
|
adjuncts.flatten*"_"
|
127
127
|
end
|
128
128
|
|
129
129
|
def describe
|
130
|
-
@object_type.name + (@
|
130
|
+
@object_type.name + (@variables_by_query.size > 0 ? " (in #{@variables_by_query.size} variables)" : "")
|
131
131
|
end
|
132
132
|
end
|
133
133
|
|
134
134
|
# Find or create a Player to which we can add this role_ref
|
135
135
|
def player(ref)
|
136
|
-
existing_player = if ref.is_a?(ActiveFacts::Metamodel::
|
137
|
-
@
|
136
|
+
existing_player = if ref.is_a?(ActiveFacts::Metamodel::Play)
|
137
|
+
@player_by_play[ref]
|
138
138
|
else
|
139
|
-
@player_by_role_ref[ref] or ref.
|
139
|
+
@player_by_role_ref[ref] or ref.play && @player_by_play[ref.play]
|
140
140
|
end
|
141
141
|
if existing_player
|
142
142
|
debug :player, "Using existing player for #{ref.role.object_type.name} #{ref.respond_to?(:role_sequence) && ref.role_sequence.all_reading.size > 0 ? ' in reading' : ''}in '#{ref.role.fact_type.default_reading}'"
|
@@ -149,22 +149,22 @@ module ActiveFacts
|
|
149
149
|
end
|
150
150
|
end
|
151
151
|
|
152
|
-
def
|
153
|
-
return if player.
|
154
|
-
jn =
|
155
|
-
if jn1 = player.
|
156
|
-
raise "Player for #{player.object_type.name} may only have one
|
152
|
+
def add_play player, play
|
153
|
+
return if player.plays.include?(play)
|
154
|
+
jn = play.variable
|
155
|
+
if jn1 = player.variables_by_query[jn.query] and jn1 != jn
|
156
|
+
raise "Player for #{player.object_type.name} may only have one variable per query, not #{jn1.object_type.name} and #{jn.object_type.name}"
|
157
157
|
end
|
158
|
-
player.
|
159
|
-
@
|
160
|
-
player.
|
158
|
+
player.variables_by_query[jn.query] = jn
|
159
|
+
@player_by_play[play] = player
|
160
|
+
player.plays << play
|
161
161
|
end
|
162
162
|
|
163
163
|
# Add a RoleRef to an existing Player
|
164
164
|
def add_role_player player, role_ref
|
165
165
|
#debug :subscript, "Adding role_ref #{role_ref.object_id} to player #{player.object_id}"
|
166
|
-
if jr = role_ref.
|
167
|
-
|
166
|
+
if jr = role_ref.play
|
167
|
+
add_play(player, jr)
|
168
168
|
elsif !player.role_refs.include?(role_ref)
|
169
169
|
debug :subscript, "Adding reference to player #{player.object_id} for #{role_ref.role.object_type.name} in #{role_ref.role_sequence.describe} with #{role_ref.role_sequence.all_reading.size} readings"
|
170
170
|
player.role_refs.push(role_ref)
|
@@ -212,20 +212,20 @@ module ActiveFacts
|
|
212
212
|
end
|
213
213
|
end
|
214
214
|
|
215
|
-
def
|
216
|
-
return if
|
215
|
+
def plays_have_same_player plays
|
216
|
+
return if plays.empty?
|
217
217
|
|
218
|
-
# If any of these
|
219
|
-
existing_players =
|
218
|
+
# If any of these plays are for a known player, use that, else make a new player.
|
219
|
+
existing_players = plays.map{|jr| @player_by_play[jr] }.compact.uniq
|
220
220
|
if existing_players.size > 1
|
221
|
-
raise "
|
221
|
+
raise "At most one existing player can play these roles: #{existing_players.map{|p|p.object_type.name}*', '}!"
|
222
222
|
end
|
223
|
-
p = existing_players[0] || player(
|
224
|
-
debugger if
|
225
|
-
debug :subscript, "
|
226
|
-
|
227
|
-
debug :subscript, "#{
|
228
|
-
|
223
|
+
p = existing_players[0] || player(plays[0])
|
224
|
+
debugger if plays.detect{|jr| jr.role.object_type != p.object_type }
|
225
|
+
debug :subscript, "roles are playes of #{p.describe}" do
|
226
|
+
plays.each do |play|
|
227
|
+
debug :subscript, "#{play.describe}" do
|
228
|
+
add_play p, play
|
229
229
|
end
|
230
230
|
end
|
231
231
|
end
|
@@ -238,16 +238,16 @@ module ActiveFacts
|
|
238
238
|
|
239
239
|
# If any of these role_refs are for a known player, use that, else make a new player.
|
240
240
|
existing_players =
|
241
|
-
role_refs.map{|rr| @player_by_role_ref[rr] || @
|
241
|
+
role_refs.map{|rr| @player_by_role_ref[rr] || @player_by_play[rr.play] }.compact.uniq
|
242
242
|
if existing_players.size > 1
|
243
|
-
raise "
|
243
|
+
raise "At most one existing player can play these roles: #{existing_players.map{|p|p.object_type.name}*', '}!"
|
244
244
|
end
|
245
245
|
p = existing_players[0] || player(role_refs[0])
|
246
246
|
|
247
247
|
debug :subscript, "#{existing_players[0] ? 'Adding to existing' : 'Creating new'} player for #{role_refs.map{|rr| rr.role.object_type.name}.uniq*', '}" do
|
248
248
|
role_refs.each do |rr|
|
249
249
|
unless p.object_type == rr.role.object_type
|
250
|
-
# This happens in SubtypePI because uniqueness constraint is built without its implicit subtyping
|
250
|
+
# This happens in SubtypePI because uniqueness constraint is built without its implicit subtyping step.
|
251
251
|
# For now, explode only if there's no common supertype:
|
252
252
|
if 0 == (p.object_type.supertypes_transitive & rr.role.object_type.supertypes_transitive).size
|
253
253
|
raise "REVISIT: Internal error, trying to add role of #{rr.role.object_type.name} to player #{p.object_type.name}"
|
@@ -287,11 +287,11 @@ module ActiveFacts
|
|
287
287
|
p.role_refs.map{|rr| rr.role.fact_type.preferred_reading.text}.sort.to_s
|
288
288
|
}.
|
289
289
|
each do |player|
|
290
|
-
jrname = player.
|
290
|
+
jrname = player.plays.map{|jr| jr.role_ref && jr.role_ref.role.role_name}.compact[0]
|
291
291
|
rname = (rr = player.role_refs[0]) && rr.role.role_name
|
292
292
|
if jrname and !rname
|
293
293
|
# puts "Oops: rolename #{rname.inspect} != #{jrname.inspect}" if jrname != rname
|
294
|
-
player.
|
294
|
+
player.variables_by_query.values.each{|jn| jn.role_name = jrname }
|
295
295
|
else
|
296
296
|
player.subscript = s+1
|
297
297
|
s += 1
|
@@ -312,9 +312,9 @@ module ActiveFacts
|
|
312
312
|
end
|
313
313
|
end
|
314
314
|
|
315
|
-
# Where no explicit
|
316
|
-
# REVISIT: This probably doesn't produce the required result. Need to fix the NORMA importer to create the
|
317
|
-
def
|
315
|
+
# Where no explicit Query has been created, a query is still sometimes present (e.g. in a constraint from NORMA)
|
316
|
+
# REVISIT: This probably doesn't produce the required result. Need to fix the NORMA importer to create the query.
|
317
|
+
def role_refs_have_subtype_steps roles
|
318
318
|
role_refs = roles.is_a?(Array) ? roles : roles.all_role_ref.to_a
|
319
319
|
role_refs_by_object_type = role_refs.inject({}) { |h, r| (h[r.role.object_type] ||= []) << r; h }
|
320
320
|
role_refs_by_object_type.values.each { |rrs| role_refs_have_same_player(rrs) }
|
@@ -322,7 +322,7 @@ module ActiveFacts
|
|
322
322
|
|
323
323
|
# These roles are the players in an implicit counterpart join in a Presence Constraint.
|
324
324
|
# REVISIT: It's not clear that we can safely use the preferred_reading's RoleRefs here.
|
325
|
-
# Fix the CQL compiler to create proper
|
325
|
+
# Fix the CQL compiler to create proper queries for these presence constraints instead.
|
326
326
|
def roles_have_same_player roles
|
327
327
|
role_refs = roles.map do |role|
|
328
328
|
role.fact_type.all_reading.map{|reading|
|
@@ -336,8 +336,8 @@ module ActiveFacts
|
|
336
336
|
def prepare_role_sequence role_sequence, join_over = nil
|
337
337
|
@role_refs = role_sequence.is_a?(Array) ? role_sequence : role_sequence.all_role_ref.to_a
|
338
338
|
|
339
|
-
if jrr = @role_refs.detect{|rr| rr.
|
340
|
-
return
|
339
|
+
if jrr = @role_refs.detect{|rr| rr.play && rr.play.variable}
|
340
|
+
return prepare_query_players(jrr.play.variable.query)
|
341
341
|
end
|
342
342
|
|
343
343
|
# Ensure that all the joined-over role_refs are indexed for subscript generation.
|
@@ -360,42 +360,42 @@ module ActiveFacts
|
|
360
360
|
end
|
361
361
|
end
|
362
362
|
|
363
|
-
def
|
364
|
-
debug :subscript, "Indexing roles of fact types in #{
|
365
|
-
|
366
|
-
# Register all references to each
|
367
|
-
|
368
|
-
debug :subscript, "Adding Roles of #{
|
369
|
-
|
370
|
-
|
363
|
+
def prepare_query_players query
|
364
|
+
debug :subscript, "Indexing roles of fact types in #{query.all_step.size} steps" do
|
365
|
+
steps = []
|
366
|
+
# Register all references to each variable as being for the same player:
|
367
|
+
query.all_variable.sort_by{|jn| jn.ordinal}.each do |variable|
|
368
|
+
debug :subscript, "Adding Roles of #{variable.describe}" do
|
369
|
+
plays_have_same_player(variable.all_play.to_a)
|
370
|
+
steps = steps | variable.all_step
|
371
371
|
end
|
372
372
|
end
|
373
373
|
|
374
374
|
=begin
|
375
|
-
# For each fact type traversed, register a player for each role *not* linked to this
|
375
|
+
# For each fact type traversed, register a player for each role *not* linked to this query
|
376
376
|
# REVISIT: Using the preferred_reading role_ref is wrong here; the same preferred_reading might occur twice,
|
377
377
|
# so the respective object_type will need more than one Player and will be subscripted to keep them from being joined.
|
378
|
-
# Accordingly, there must be a
|
379
|
-
# This isn't needed now all
|
378
|
+
# Accordingly, there must be a step for each such role, and to enforce that, I raise an exception here on duplication.
|
379
|
+
# This isn't needed now all Variables have at least one Play
|
380
380
|
|
381
|
-
|
382
|
-
if js.fact_type.is_a?(ActiveFacts::Metamodel::
|
381
|
+
steps.map do |js|
|
382
|
+
if js.fact_type.is_a?(ActiveFacts::Metamodel::LinkFactType)
|
383
383
|
js.fact_type.implying_role.fact_type
|
384
384
|
else
|
385
385
|
js.fact_type
|
386
386
|
end
|
387
387
|
end.uniq.each do |fact_type|
|
388
|
-
#
|
389
|
-
next if fact_type.is_a?(ActiveFacts::Metamodel::
|
388
|
+
#steps.map{|js|js.fact_type}.uniq.each do |fact_type|
|
389
|
+
next if fact_type.is_a?(ActiveFacts::Metamodel::LinkFactType)
|
390
390
|
|
391
391
|
debug :subscript, "Residual roles in '#{fact_type.default_reading}' are" do
|
392
392
|
prrs = fact_type.preferred_reading.role_sequence.all_role_ref
|
393
|
-
residual_roles = fact_type.all_role.select{|r| !r.all_role_ref.detect{|rr| rr.
|
393
|
+
residual_roles = fact_type.all_role.select{|r| !r.all_role_ref.detect{|rr| rr.variable && rr.variable.query == query} }
|
394
394
|
residual_roles.each do |r|
|
395
|
-
debug :subscript, "Adding residual role for #{r.object_type.name} (in #{fact_type.default_reading}) not covered in
|
395
|
+
debug :subscript, "Adding residual role for #{r.object_type.name} (in #{fact_type.default_reading}) not covered in query"
|
396
396
|
preferred_role_ref = prrs.detect{|rr| rr.role == r}
|
397
397
|
if p = @player_by_role_ref[preferred_role_ref] and !p.role_refs.include?(preferred_role_ref)
|
398
|
-
raise "Adding DUPLICATE residual role for #{r.object_type.name} not covered in
|
398
|
+
raise "Adding DUPLICATE residual role for #{r.object_type.name} not covered in query"
|
399
399
|
end
|
400
400
|
role_refs_have_same_player([preferred_role_ref])
|
401
401
|
end
|
@@ -405,15 +405,15 @@ module ActiveFacts
|
|
405
405
|
end
|
406
406
|
end
|
407
407
|
|
408
|
-
def verbalise_over_role_sequence role_sequence,
|
408
|
+
def verbalise_over_role_sequence role_sequence, conjunction = ' and ', role_proximity = :both
|
409
409
|
@role_refs = role_sequence.is_a?(Array) ? role_sequence : role_sequence.all_role_ref.to_a
|
410
410
|
|
411
|
-
if jrr = role_refs.detect{|rr| rr.
|
412
|
-
return
|
411
|
+
if jrr = role_refs.detect{|rr| rr.play}
|
412
|
+
return verbalise_query(jrr.play.variable.query)
|
413
413
|
end
|
414
414
|
|
415
|
-
# First, figure out whether there's a
|
416
|
-
join_over, joined_roles = *Metamodel.
|
415
|
+
# First, figure out whether there's a query:
|
416
|
+
join_over, joined_roles = *Metamodel.plays_over(role_sequence.all_role_ref.map{|rr|rr.role}, role_proximity)
|
417
417
|
|
418
418
|
role_by_fact_type = {}
|
419
419
|
fact_types = @role_refs.map{|rr| ft = rr.role.fact_type; role_by_fact_type[ft] ||= rr.role; ft}.uniq
|
@@ -425,7 +425,7 @@ module ActiveFacts
|
|
425
425
|
joined_role = fact_type.all_role.select{|r| join_over.subtypes_transitive.include?(r.object_type)}[0]
|
426
426
|
reading = fact_type.reading_preferably_starting_with_role joined_role
|
427
427
|
|
428
|
-
# Use the name of the joined_over object, not the role player, in case of a subtype
|
428
|
+
# Use the name of the joined_over object, not the role player, in case of a subtype step:
|
429
429
|
rrrs = reading.role_sequence.all_role_ref_in_order
|
430
430
|
role_index = (0..rrrs.size).detect{|i| rrrs[i].role == joined_role }
|
431
431
|
name_substitutions[role_index] = [nil, join_over.name]
|
@@ -439,43 +439,48 @@ module ActiveFacts
|
|
439
439
|
@player_by_role_ref.keys.each{|rr| player_by_role[rr.role] = @player_by_role_ref[rr] if rr.role.fact_type == fact_type }
|
440
440
|
expand_reading_text(nil, reading.text, reading.role_sequence, player_by_role)
|
441
441
|
end
|
442
|
-
|
442
|
+
conjunction ? readings*conjunction : readings
|
443
443
|
end
|
444
444
|
|
445
445
|
# Expand this reading (or partial reading, during contraction)
|
446
446
|
def expand_reading_text(step, text, role_sequence, player_by_role = {})
|
447
447
|
if !player_by_role.empty? and !player_by_role.is_a?(Hash) || player_by_role.keys.detect{|k| !k.is_a?(ActiveFacts::Metamodel::Role)}
|
448
448
|
debugger
|
449
|
-
raise "Need to change this call to expand_reading_text to pass a role->
|
449
|
+
raise "Need to change this call to expand_reading_text to pass a role->variable hash"
|
450
450
|
end
|
451
451
|
rrs = role_sequence.all_role_ref_in_order
|
452
|
+
variable_by_role = {}
|
453
|
+
if step
|
454
|
+
plays = ([step.input_play, step.output_play]+step.all_incidental_play.to_a).compact
|
455
|
+
variable_by_role = plays.inject({}) { |h, play| h[play.role] = play.variable; h }
|
456
|
+
end
|
452
457
|
debug :subscript, "expanding '#{text}' with #{role_sequence.describe}" do
|
453
458
|
text.gsub(/\{(\d)\}/) do
|
454
459
|
role_ref = rrs[$1.to_i]
|
455
460
|
# REVISIT: We may need to use the step's role_refs to expand the role players here, not the reading's one (extra adjectives?)
|
456
|
-
# REVISIT: There's no way to get literals to be emitted here (value join step or query result?)
|
457
|
-
|
458
461
|
player = player_by_role[role_ref.role]
|
462
|
+
variable = variable_by_role[role_ref.role]
|
459
463
|
|
460
|
-
|
461
|
-
subscripted_player(role_ref, player && player.subscript,
|
464
|
+
play_name = variable && variable.role_name
|
465
|
+
subscripted_player(role_ref, player && player.subscript, play_name, variable && variable.value) +
|
462
466
|
objectification_verbalisation(role_ref.role.object_type)
|
463
467
|
end
|
464
468
|
end
|
465
469
|
end
|
466
470
|
|
467
|
-
def subscripted_player role_ref, subscript = nil,
|
471
|
+
def subscripted_player role_ref, subscript = nil, play_name = nil, value = nil
|
468
472
|
prr = @player_by_role_ref[role_ref]
|
469
473
|
subscript ||= prr.subscript if prr
|
470
474
|
debug :subscript, "Need to apply subscript #{subscript} to #{role_ref.role.object_type.name}" if subscript
|
471
475
|
object_type = role_ref.role.object_type
|
472
|
-
(
|
476
|
+
(play_name ||
|
473
477
|
[
|
474
478
|
role_ref.leading_adjective,
|
475
479
|
object_type.name,
|
476
480
|
role_ref.trailing_adjective
|
477
481
|
].compact*' '
|
478
482
|
) +
|
483
|
+
(value ? ' '+value.inspect : '') +
|
479
484
|
(subscript ? "(#{subscript})" : '')
|
480
485
|
end
|
481
486
|
|
@@ -484,72 +489,75 @@ module ActiveFacts
|
|
484
489
|
expand_reading_text(step, reading.text.sub(/\A\{\d\} /,''), reading.role_sequence, role_refs)
|
485
490
|
end
|
486
491
|
|
487
|
-
# Each
|
488
|
-
# Then, this prepares the
|
489
|
-
def
|
490
|
-
@
|
491
|
-
return unless
|
492
|
+
# Each query we wish to verbalise must first have had its players prepared.
|
493
|
+
# Then, this prepares the query for verbalising:
|
494
|
+
def prepare_query query
|
495
|
+
@query = query
|
496
|
+
return unless query
|
492
497
|
|
493
|
-
@
|
498
|
+
@variables = query.all_variable.sort_by{|jn| jn.ordinal}
|
494
499
|
|
495
|
-
@
|
496
|
-
@
|
500
|
+
@steps = @variables.map{|jn| jn.all_step }.flatten.uniq
|
501
|
+
@steps_by_variable = @variables.
|
497
502
|
inject({}) do |h, jn|
|
498
|
-
jn.
|
503
|
+
jn.all_step.each{|js| (h[jn] ||= []) << js}
|
499
504
|
h
|
500
505
|
end
|
501
506
|
end
|
502
507
|
|
503
508
|
# Remove this step now that we've processed it:
|
504
509
|
def step_completed(step)
|
505
|
-
@
|
510
|
+
@steps.delete(step)
|
506
511
|
|
507
|
-
input_node = step.
|
508
|
-
steps = @
|
512
|
+
input_node = step.input_play.variable
|
513
|
+
steps = @steps_by_variable[input_node]
|
509
514
|
steps.delete(step)
|
510
|
-
@
|
515
|
+
@steps_by_variable.delete(input_node) if steps.empty?
|
511
516
|
|
512
|
-
output_node = step.
|
517
|
+
output_node = step.output_play.variable
|
513
518
|
if (input_node != output_node)
|
514
|
-
steps = @
|
519
|
+
steps = @steps_by_variable[output_node]
|
515
520
|
steps.delete(step)
|
516
|
-
@
|
521
|
+
@steps_by_variable.delete(output_node) if steps.empty?
|
517
522
|
end
|
518
523
|
end
|
519
524
|
|
520
525
|
def choose_step(next_node)
|
521
|
-
next_steps = @
|
526
|
+
next_steps = @steps_by_variable[next_node]
|
527
|
+
|
528
|
+
# We need to emit each objectification before mentioning an object that plays a role in one, if possible
|
529
|
+
# so that we don't wind up with an objectification as the only way to mention its name.
|
522
530
|
|
523
531
|
# If we don't have a next_node against which we can contract,
|
524
|
-
# so just use any
|
532
|
+
# so just use any step involving this node, or just any step.
|
525
533
|
if next_steps
|
526
534
|
if next_step = next_steps.detect { |ns| !ns.is_objectification_step }
|
527
|
-
debug :
|
535
|
+
debug :query, "Chose new non-objectification step: #{next_step.describe}"
|
528
536
|
return next_step
|
529
537
|
end
|
530
538
|
end
|
531
539
|
|
532
|
-
if next_step = @
|
533
|
-
debug :
|
540
|
+
if next_step = @steps.detect { |ns| !ns.is_objectification_step }
|
541
|
+
debug :query, "Chose random non-objectification step: #{next_step.describe}"
|
534
542
|
return next_step
|
535
543
|
end
|
536
544
|
|
537
|
-
next_step = @
|
545
|
+
next_step = @steps[0]
|
538
546
|
if next_step
|
539
|
-
debug :
|
547
|
+
debug :query, "Chose new random step from #{steps.size}: #{next_step.describe}"
|
540
548
|
if next_step.is_objectification_step
|
541
549
|
# if this objectification plays any roles (other than its FT roles) in remaining steps, use one of those first:
|
542
550
|
fact_type = next_step.fact_type.implying_role.fact_type
|
543
|
-
jn = [next_step.
|
544
|
-
sr = @
|
551
|
+
jn = [next_step.input_play.variable, next_step.output_play.variable].detect{|jn| jn.object_type == fact_type.entity_type}
|
552
|
+
sr = @steps_by_variable[jn].reject{|t| r = t.fact_type.implying_role and r.fact_type == fact_type}
|
545
553
|
next_step = sr[0] if sr.size > 0
|
546
554
|
end
|
547
555
|
return next_step
|
548
556
|
end
|
549
|
-
raise "Internal error: There are more
|
557
|
+
raise "Internal error: There are more steps here, but we failed to choose one"
|
550
558
|
end
|
551
559
|
|
552
|
-
# The
|
560
|
+
# The step we just emitted (using the reading given) is contractable iff
|
553
561
|
# the reading has the next_node's role player as the final text
|
554
562
|
def node_contractable_against_reading(next_node, reading)
|
555
563
|
reading &&
|
@@ -573,7 +581,7 @@ module ActiveFacts
|
|
573
581
|
next_reading = nil
|
574
582
|
next_step =
|
575
583
|
next_steps.detect do |js|
|
576
|
-
next false if js.is_objectification_step
|
584
|
+
next false if js.is_objectification_step or js.is_disallowed
|
577
585
|
# If we find a reading here, it can be contracted against the previous one
|
578
586
|
next_reading =
|
579
587
|
js.fact_type.all_reading_by_ordinal.detect do |reading|
|
@@ -582,50 +590,50 @@ module ActiveFacts
|
|
582
590
|
end
|
583
591
|
next_reading
|
584
592
|
end
|
585
|
-
debug :
|
593
|
+
debug :query, "#{next_reading ? "'"+next_reading.expand+"'" : "No reading"} contracts against last node '#{next_node.object_type.name}'"
|
586
594
|
return [next_step, next_reading]
|
587
595
|
end
|
588
596
|
|
589
|
-
# REVISIT: There might be more than one objectification_verbalisation for a given object_type. Need to get the
|
597
|
+
# REVISIT: There might be more than one objectification_verbalisation for a given object_type. Need to get the Variable here and emit an objectification step involving that node.
|
590
598
|
def objectification_verbalisation(object_type)
|
591
599
|
objectified_node = nil
|
592
600
|
unless object_type.is_a?(Metamodel::EntityType) and
|
593
601
|
object_type.fact_type and # Not objectified
|
594
|
-
objectification_step = @
|
602
|
+
objectification_step = @steps.
|
595
603
|
detect do |js|
|
596
|
-
# The objectifying entity type should always be the
|
604
|
+
# The objectifying entity type should always be the input_variable here, but be safe:
|
597
605
|
js.is_objectification_step and
|
598
|
-
(objectified_node = js.
|
599
|
-
(objectified_node = js.
|
606
|
+
(objectified_node = js.input_play.variable).object_type == object_type ||
|
607
|
+
(objectified_node = js.output_play.variable).object_type == object_type
|
600
608
|
end
|
601
609
|
return ''
|
602
610
|
end
|
603
611
|
|
604
612
|
# REVISIT: We need to be working from the role_ref here - pass it in
|
605
|
-
# if objectification_step.
|
613
|
+
# if objectification_step.variable != role_ref.variable
|
606
614
|
|
607
615
|
steps = [objectification_step]
|
608
616
|
step_completed(objectification_step)
|
609
617
|
while other_step =
|
610
|
-
@
|
618
|
+
@steps.
|
611
619
|
detect{|js|
|
612
620
|
js.is_objectification_step and
|
613
|
-
js.
|
621
|
+
js.input_play.variable.object_type == object_type || js.output_play.variable.object_type == object_type
|
614
622
|
}
|
615
623
|
steps << other_step
|
616
|
-
debug :
|
624
|
+
debug :query, "Emitting objectification step allows deleting #{other_step.describe}"
|
617
625
|
step_completed(other_step)
|
618
626
|
end
|
619
627
|
|
620
|
-
# Find all references to roles in this objectified fact type which are relevant to the
|
628
|
+
# Find all references to roles in this objectified fact type which are relevant to the variables of these steps:
|
621
629
|
player_by_role = {}
|
622
|
-
steps.each do |
|
623
|
-
|
624
|
-
player_by_role[jr.role] = @
|
630
|
+
steps.each do |step|
|
631
|
+
step.all_play.to_a.map do |jr|
|
632
|
+
player_by_role[jr.role] = @player_by_play[jr]
|
625
633
|
end
|
626
634
|
end
|
627
635
|
|
628
|
-
# role_refs = steps.map{|step| [step.
|
636
|
+
# role_refs = steps.map{|step| [step.input_play.variable, step.output_play.variable].map{|jn| jn.all_role_ref.detect{|rr| rr.role.fact_type == object_type.fact_type}}}.flatten.compact.uniq
|
629
637
|
|
630
638
|
reading = object_type.fact_type.preferred_reading
|
631
639
|
" (in which #{expand_reading_text(objectification_step, reading.text, reading.role_sequence, player_by_role)})"
|
@@ -636,46 +644,53 @@ module ActiveFacts
|
|
636
644
|
# Choose a reading that's contractable against the previous step, if possible
|
637
645
|
reading = fact_type.all_reading_by_ordinal.
|
638
646
|
detect do |reading|
|
639
|
-
|
647
|
+
# Only contract a negative reading if we want one
|
648
|
+
(!next_step.is_disallowed || !reading.is_negative == !next_step.is_disallowed) and
|
649
|
+
reading_starts_with_node(reading, next_node)
|
640
650
|
end
|
641
651
|
end
|
642
652
|
last_is_contractable = false unless reading
|
643
|
-
reading ||= fact_type.preferred_reading
|
653
|
+
reading ||= fact_type.preferred_reading(next_step.is_disallowed) || fact_type.preferred_reading
|
644
654
|
|
645
|
-
# Find which role occurs last in the reading, and which
|
655
|
+
# Find which role occurs last in the reading, and which Variable is attached
|
646
656
|
reading.text =~ /\{(\d)\}[^{]*\Z/
|
647
657
|
last_role_ref = reading.role_sequence.all_role_ref_in_order[$1.to_i]
|
648
|
-
exit_node = @
|
658
|
+
exit_node = @variables.detect{|jn| jn.all_play.detect{|jr| jr.role == last_role_ref.role}}
|
649
659
|
exit_step = nil
|
650
660
|
|
651
|
-
|
652
|
-
|
653
|
-
|
654
|
-
|
655
|
-
|
656
|
-
|
657
|
-
|
658
|
-
|
659
|
-
|
660
|
-
|
661
|
-
|
662
|
-
|
663
|
-
|
664
|
-
|
665
|
-
|
661
|
+
debug :query, "Stepping over an objectification to #{exit_node.object_type.name} requires eliding the other implied steps" do
|
662
|
+
count = 0
|
663
|
+
while other_step =
|
664
|
+
@steps.
|
665
|
+
detect{|js|
|
666
|
+
debug :query, "Considering step '#{js.fact_type.default_reading}'"
|
667
|
+
next unless js.is_objectification_step
|
668
|
+
|
669
|
+
# REVISIT: This test is too weak: We need to ensure that the same variables are involved, not just the same object types:
|
670
|
+
next unless js.input_play.variable.object_type == fact_type.entity_type || js.output_play.variable.object_type == fact_type.entity_type
|
671
|
+
exit_step = js if js.output_play.variable == exit_node
|
672
|
+
true
|
673
|
+
}
|
674
|
+
debug :query, "Emitting objectified FT allows deleting #{other_step.describe}"
|
675
|
+
step_completed(other_step)
|
676
|
+
# raise "The objectification of '#{fact_type.default_reading}' should not cause the deletion of more than #{fact_type.all_role.size} other steps" if (count += 1) > fact_type.all_role.size
|
677
|
+
end
|
678
|
+
end
|
679
|
+
|
680
|
+
[ reading, exit_step ? exit_step.input_play.variable : exit_node, exit_step, last_is_contractable]
|
666
681
|
end
|
667
682
|
|
668
|
-
def
|
669
|
-
|
683
|
+
def verbalise_query query
|
684
|
+
prepare_query query
|
670
685
|
readings = ''
|
671
|
-
next_node = @role_refs[0].
|
686
|
+
next_node = @role_refs[0].play.variable # Choose a place to start
|
672
687
|
last_is_contractable = false
|
673
|
-
debug :
|
674
|
-
until @
|
688
|
+
debug :query, "Variables are #{@variables.map{|jn| jn.describe }.inspect}, Steps are #{@steps.map{|js| js.describe }.inspect}" do
|
689
|
+
until @steps.empty?
|
675
690
|
next_reading = nil
|
676
691
|
# Choose amonst all remaining steps we can take from the next node, if any
|
677
|
-
next_steps = @
|
678
|
-
debug :
|
692
|
+
next_steps = @steps_by_variable[next_node]
|
693
|
+
debug :query, "Next Steps from #{next_node.describe} are #{(next_steps||[]).map{|js| js.describe }.inspect}"
|
679
694
|
|
680
695
|
# See if we can find a next step that contracts against the last (if any):
|
681
696
|
next_step = nil
|
@@ -684,24 +699,28 @@ module ActiveFacts
|
|
684
699
|
end
|
685
700
|
|
686
701
|
if next_step
|
687
|
-
debug :
|
702
|
+
debug :query, "Chose #{next_step.describe} because it's contractable against last node #{next_node.object_type.name} using #{next_reading.expand}"
|
688
703
|
|
689
704
|
player_by_role =
|
690
|
-
next_step.
|
705
|
+
next_step.all_play.inject({}) {|h, jr| h[jr.role] = @player_by_play[jr]; h }
|
706
|
+
raise "REVISIT: Needed a negated reading here" if !next_reading.is_negative != !next_step.is_disallowed
|
707
|
+
raise "REVISIT: Need to emit 'maybe' here" if next_step.is_optional
|
691
708
|
readings += expand_contracted_text(next_step, next_reading, player_by_role)
|
692
709
|
step_completed(next_step)
|
693
710
|
else
|
694
711
|
next_step = choose_step(next_node) if !next_step
|
695
712
|
|
696
713
|
player_by_role =
|
697
|
-
next_step.
|
714
|
+
next_step.all_play.inject({}) {|h, jr| h[jr.role] = @player_by_play[jr]; h }
|
698
715
|
|
699
716
|
if next_step.is_unary_step
|
700
717
|
# Objectified unaries get emitted as unaries, not as objectifications:
|
701
|
-
role = next_step.
|
702
|
-
role = role.fact_type.implying_role if role.fact_type.is_a?(
|
703
|
-
|
718
|
+
role = next_step.input_play.role
|
719
|
+
role = role.fact_type.implying_role if role.fact_type.is_a?(LinkFactType)
|
720
|
+
next_reading = role.fact_type.preferred_reading(next_step.is_disallowed) || role.fact_type.preferred_reading
|
704
721
|
readings += " and " unless readings.empty?
|
722
|
+
readings += "it is not the case that " if !next_step.is_disallowed != !next_reading.is_negative
|
723
|
+
raise "REVISIT: Need to emit 'maybe' here" if next_step.is_optional
|
705
724
|
readings += expand_reading_text(next_step, next_reading.text, next_reading.role_sequence, player_by_role)
|
706
725
|
step_completed(next_step)
|
707
726
|
elsif next_step.is_objectification_step
|
@@ -709,11 +728,11 @@ module ActiveFacts
|
|
709
728
|
|
710
729
|
# This objectification step is over an implicit fact type, so player_by_role won't have all the players
|
711
730
|
# Add the players of other roles associated with steps from this objectified player.
|
712
|
-
objectified_node = next_step.
|
713
|
-
raise "Assumption violated that the objectification is the input
|
714
|
-
objectified_node.
|
715
|
-
(other_step.
|
716
|
-
player_by_role[jr.role] = @
|
731
|
+
objectified_node = next_step.input_play.variable
|
732
|
+
raise "Assumption violated that the objectification is the input play" unless objectified_node.object_type.fact_type
|
733
|
+
objectified_node.all_step.map do |other_step|
|
734
|
+
(other_step.all_incidental_play.to_a + [other_step.output_play]).map do |jr|
|
735
|
+
player_by_role[jr.role] = @player_by_play[jr]
|
717
736
|
end
|
718
737
|
end
|
719
738
|
|
@@ -723,16 +742,20 @@ module ActiveFacts
|
|
723
742
|
readings += objectification_verbalisation(fact_type.entity_type)
|
724
743
|
else
|
725
744
|
# This objectified fact type does not need to be made explicit.
|
745
|
+
negation = next_step.is_disallowed
|
726
746
|
next_reading, next_node, next_step, last_is_contractable =
|
727
747
|
*elided_objectification(next_step, fact_type, last_is_contractable, next_node)
|
728
748
|
if last_is_contractable
|
749
|
+
raise "REVISIT: Need to emit 'maybe' here" if next_step and next_step.is_optional
|
729
750
|
readings += expand_contracted_text(next_step, next_reading, player_by_role)
|
730
751
|
else
|
731
752
|
readings += " and " unless readings.empty?
|
753
|
+
readings += "it is not the case that " if !!negation != !!next_reading.is_negative
|
754
|
+
raise "REVISIT: Need to emit 'maybe' here" if next_step and next_step.is_optional
|
732
755
|
readings += expand_reading_text(next_step, next_reading.text, next_reading.role_sequence, player_by_role)
|
733
756
|
end
|
734
757
|
# No need to continue if we just deleted the last step
|
735
|
-
break if @
|
758
|
+
break if @steps.empty?
|
736
759
|
|
737
760
|
end
|
738
761
|
else
|
@@ -740,21 +763,30 @@ module ActiveFacts
|
|
740
763
|
# Prefer a reading that starts with the player of next_node
|
741
764
|
next_reading = fact_type.all_reading_by_ordinal.
|
742
765
|
detect do |reading|
|
743
|
-
|
744
|
-
|
745
|
-
|
766
|
+
(!next_step.is_disallowed || !reading.is_negative == !next_step.is_disallowed) and
|
767
|
+
reading_starts_with_node(reading, next_node)
|
768
|
+
end || fact_type.preferred_reading(next_step.is_disallowed)
|
769
|
+
# REVISIT: If this step and reading has role references with adjectives, we need to expand using those
|
746
770
|
readings += " and " unless readings.empty?
|
771
|
+
readings += "it is not the case that " if !next_step.is_disallowed != !next_reading.is_negative
|
772
|
+
raise "REVISIT: Need to emit 'maybe' here" if next_step and next_step.is_optional
|
747
773
|
readings += expand_reading_text(next_step, next_reading.text, next_reading.role_sequence, player_by_role)
|
748
774
|
step_completed(next_step)
|
749
775
|
end
|
750
776
|
end
|
751
777
|
|
752
|
-
|
753
|
-
|
754
|
-
|
755
|
-
|
756
|
-
|
757
|
-
|
778
|
+
if next_step
|
779
|
+
# Continue from this step with the node having the most steps remaining
|
780
|
+
input_steps = @steps_by_variable[input_node = next_step.input_play.variable] || []
|
781
|
+
output_steps = @steps_by_variable[output_node = next_step.output_play.variable] || []
|
782
|
+
next_node = input_steps.size > output_steps.size ? input_node : output_node
|
783
|
+
# Prepare for possible contraction following:
|
784
|
+
last_is_contractable = next_reading && node_contractable_against_reading(next_node, next_reading)
|
785
|
+
else
|
786
|
+
# This shouldn't happen, but an elided objectification that had missing steps can cause it. Survive:
|
787
|
+
next_node = (steps[0].input_play || steps[0].output_play).variable
|
788
|
+
last_is_contractable = false
|
789
|
+
end
|
758
790
|
|
759
791
|
end
|
760
792
|
end
|