activefacts 0.8.9 → 0.8.10
Sign up to get free protection for your applications and to get access to all the features.
- data/.gemtest +0 -0
- data/Manifest.txt +28 -33
- data/Rakefile +11 -12
- data/bin/cql +90 -46
- data/examples/CQL/Blog.cql +2 -1
- data/examples/CQL/CompanyDirectorEmployee.cql +2 -2
- data/examples/CQL/Death.cql +1 -1
- data/examples/CQL/Diplomacy.cql +9 -9
- data/examples/CQL/Genealogy.cql +3 -2
- data/examples/CQL/Insurance.cql +10 -7
- data/examples/CQL/JoinEquality.cql +2 -2
- data/examples/CQL/Marriage.cql +1 -1
- data/examples/CQL/Metamodel.cql +73 -53
- data/examples/CQL/MetamodelNext.cql +89 -67
- data/examples/CQL/OneToOnes.cql +2 -2
- data/examples/CQL/ServiceDirector.cql +10 -5
- data/examples/CQL/Supervision.cql +3 -3
- data/examples/CQL/Tests.Test5.Load.cql +1 -1
- data/examples/CQL/Warehousing.cql +4 -2
- data/lib/activefacts/cql/CQLParser.treetop +26 -60
- data/lib/activefacts/cql/Context.treetop +12 -2
- data/lib/activefacts/cql/Expressions.treetop +14 -30
- data/lib/activefacts/cql/FactTypes.treetop +165 -110
- data/lib/activefacts/cql/Language/English.treetop +167 -54
- data/lib/activefacts/cql/LexicalRules.treetop +16 -2
- data/lib/activefacts/cql/{Concepts.treetop → ObjectTypes.treetop} +36 -37
- data/lib/activefacts/cql/Terms.treetop +57 -27
- data/lib/activefacts/cql/ValueTypes.treetop +39 -13
- data/lib/activefacts/cql/compiler.rb +5 -3
- data/lib/activefacts/cql/compiler/{reading.rb → clause.rb} +407 -285
- data/lib/activefacts/cql/compiler/constraint.rb +178 -275
- data/lib/activefacts/cql/compiler/entity_type.rb +73 -64
- data/lib/activefacts/cql/compiler/expression.rb +418 -0
- data/lib/activefacts/cql/compiler/fact.rb +146 -145
- data/lib/activefacts/cql/compiler/fact_type.rb +197 -80
- data/lib/activefacts/cql/compiler/join.rb +159 -0
- data/lib/activefacts/cql/compiler/shared.rb +51 -23
- data/lib/activefacts/cql/compiler/value_type.rb +56 -2
- data/lib/activefacts/cql/parser.rb +15 -4
- data/lib/activefacts/generate/absorption.rb +7 -7
- data/lib/activefacts/generate/cql.rb +100 -37
- data/lib/activefacts/generate/oo.rb +28 -51
- data/lib/activefacts/generate/ordered.rb +60 -36
- data/lib/activefacts/generate/ruby.rb +6 -6
- data/lib/activefacts/generate/sql/server.rb +4 -4
- data/lib/activefacts/input/orm.rb +71 -53
- data/lib/activefacts/persistence.rb +1 -1
- data/lib/activefacts/persistence/columns.rb +27 -23
- data/lib/activefacts/persistence/foreignkey.rb +6 -6
- data/lib/activefacts/persistence/index.rb +17 -17
- data/lib/activefacts/persistence/{concept.rb → object_type.rb} +9 -9
- data/lib/activefacts/persistence/reference.rb +61 -36
- data/lib/activefacts/persistence/tables.rb +61 -59
- data/lib/activefacts/support.rb +54 -29
- data/lib/activefacts/version.rb +1 -1
- data/lib/activefacts/vocabulary/extensions.rb +99 -54
- data/lib/activefacts/vocabulary/metamodel.rb +43 -37
- data/lib/activefacts/vocabulary/verbaliser.rb +134 -109
- data/spec/absorption_spec.rb +8 -8
- data/spec/cql/comparison_spec.rb +91 -0
- data/spec/cql/contractions_spec.rb +251 -0
- data/spec/cql/entity_type_spec.rb +319 -0
- data/spec/cql/expressions_spec.rb +63 -0
- data/spec/cql/fact_type_matching_spec.rb +283 -0
- data/spec/cql/french_spec.rb +21 -0
- data/spec/cql/parser/bad_literals_spec.rb +86 -0
- data/spec/cql/parser/constraints_spec.rb +19 -0
- data/spec/cql/parser/entity_types_spec.rb +106 -0
- data/spec/cql/parser/expressions_spec.rb +179 -0
- data/spec/cql/parser/fact_types_spec.rb +41 -0
- data/spec/cql/parser/literals_spec.rb +312 -0
- data/spec/cql/parser/pragmas_spec.rb +89 -0
- data/spec/cql/parser/value_types_spec.rb +42 -0
- data/spec/cql/role_matching_spec.rb +147 -0
- data/spec/cql/samples_spec.rb +9 -9
- data/spec/cql_cql_spec.rb +1 -1
- data/spec/cql_dm_spec.rb +116 -0
- data/spec/cql_mysql_spec.rb +1 -1
- data/spec/cql_ruby_spec.rb +1 -1
- data/spec/cql_sql_spec.rb +3 -3
- data/spec/cql_symbol_tables_spec.rb +30 -30
- data/spec/cqldump_spec.rb +4 -4
- data/spec/helpers/array_matcher.rb +32 -27
- data/spec/helpers/diff_matcher.rb +6 -26
- data/spec/helpers/file_matcher.rb +41 -32
- data/spec/helpers/parse_to_ast_matcher.rb +76 -0
- data/spec/helpers/string_matcher.rb +32 -31
- data/spec/norma_cql_spec.rb +1 -1
- data/spec/norma_ruby_spec.rb +1 -1
- data/spec/norma_ruby_sql_spec.rb +1 -1
- data/spec/norma_sql_spec.rb +3 -1
- data/spec/norma_tables_spec.rb +1 -1
- data/spec/ruby_api_spec.rb +23 -0
- data/spec/spec_helper.rb +5 -4
- metadata +66 -66
- data/examples/CQL/OrienteeringER.cql +0 -58
- data/lib/activefacts/api.rb +0 -44
- data/lib/activefacts/api/concept.rb +0 -410
- data/lib/activefacts/api/constellation.rb +0 -128
- data/lib/activefacts/api/entity.rb +0 -256
- data/lib/activefacts/api/instance.rb +0 -60
- data/lib/activefacts/api/instance_index.rb +0 -80
- data/lib/activefacts/api/numeric.rb +0 -167
- data/lib/activefacts/api/role.rb +0 -80
- data/lib/activefacts/api/role_proxy.rb +0 -70
- data/lib/activefacts/api/role_values.rb +0 -117
- data/lib/activefacts/api/standard_types.rb +0 -87
- data/lib/activefacts/api/support.rb +0 -65
- data/lib/activefacts/api/value.rb +0 -135
- data/lib/activefacts/api/vocabulary.rb +0 -82
- data/spec/api/autocounter.rb +0 -82
- data/spec/api/constellation.rb +0 -130
- data/spec/api/entity_type.rb +0 -103
- data/spec/api/instance.rb +0 -461
- data/spec/api/roles.rb +0 -124
- data/spec/api/value_type.rb +0 -112
- data/spec/api_spec.rb +0 -13
- data/spec/cql/matching_spec.rb +0 -517
- data/spec/cql/unit_spec.rb +0 -394
- data/spec/spec.opts +0 -1
@@ -136,6 +136,11 @@ module ActiveFacts
|
|
136
136
|
value_type :length => 256
|
137
137
|
end
|
138
138
|
|
139
|
+
class TransactionTiming < String
|
140
|
+
value_type
|
141
|
+
restrict 'assert', 'commit'
|
142
|
+
end
|
143
|
+
|
139
144
|
class UnitId < AutoCounter
|
140
145
|
value_type
|
141
146
|
end
|
@@ -169,12 +174,12 @@ module ActiveFacts
|
|
169
174
|
|
170
175
|
class ContextNote
|
171
176
|
identified_by :context_note_id
|
172
|
-
has_one :concept # See Concept.all_context_note
|
173
177
|
has_one :constraint # See Constraint.all_context_note
|
174
178
|
one_to_one :context_note_id, :mandatory => true # See ContextNoteId.context_note
|
175
179
|
has_one :context_note_kind, :mandatory => true # See ContextNoteKind.all_context_note
|
176
180
|
has_one :discussion, :mandatory => true # See Discussion.all_context_note
|
177
181
|
has_one :fact_type # See FactType.all_context_note
|
182
|
+
has_one :object_type # See ObjectType.all_context_note
|
178
183
|
end
|
179
184
|
|
180
185
|
class Enforcement
|
@@ -201,9 +206,9 @@ module ActiveFacts
|
|
201
206
|
|
202
207
|
class Instance
|
203
208
|
identified_by :instance_id
|
204
|
-
has_one :concept, :mandatory => true # See Concept.all_instance
|
205
209
|
one_to_one :fact # See Fact.instance
|
206
210
|
one_to_one :instance_id, :mandatory => true # See InstanceId.instance
|
211
|
+
has_one :object_type, :mandatory => true # See ObjectType.all_instance
|
207
212
|
has_one :population, :mandatory => true # See Population.all_instance
|
208
213
|
has_one :value # See Value.all_instance
|
209
214
|
end
|
@@ -215,9 +220,10 @@ module ActiveFacts
|
|
215
220
|
|
216
221
|
class JoinNode
|
217
222
|
identified_by :join, :ordinal
|
218
|
-
has_one :concept, :mandatory => true # See Concept.all_join_node
|
219
223
|
has_one :join, :mandatory => true # See Join.all_join_node
|
224
|
+
has_one :object_type, :mandatory => true # See ObjectType.all_join_node
|
220
225
|
has_one :ordinal, :mandatory => true # See Ordinal.all_join_node
|
226
|
+
has_one :role_name, :class => Name # See Name.all_join_node_as_role_name
|
221
227
|
has_one :subscript # See Subscript.all_join_node
|
222
228
|
has_one :value # See Value.all_join_node
|
223
229
|
end
|
@@ -254,8 +260,8 @@ module ActiveFacts
|
|
254
260
|
identified_by :fact_type, :ordinal
|
255
261
|
has_one :fact_type, :mandatory => true # See FactType.all_role
|
256
262
|
has_one :ordinal, :mandatory => true # See Ordinal.all_role
|
257
|
-
|
258
|
-
|
263
|
+
one_to_one :implicit_fact_type, :counterpart => :implying_role # See ImplicitFactType.implying_role
|
264
|
+
has_one :object_type, :mandatory => true # See ObjectType.all_role
|
259
265
|
has_one :role_name, :class => Name # See Name.all_role_as_role_name
|
260
266
|
end
|
261
267
|
|
@@ -329,14 +335,6 @@ module ActiveFacts
|
|
329
335
|
has_one :value, :mandatory => true # See Value.all_bound
|
330
336
|
end
|
331
337
|
|
332
|
-
class Concept
|
333
|
-
identified_by :vocabulary, :name
|
334
|
-
maybe :is_independent
|
335
|
-
has_one :name, :mandatory => true # See Name.all_concept
|
336
|
-
has_one :pronoun # See Pronoun.all_concept
|
337
|
-
has_one :vocabulary, :mandatory => true # See Vocabulary.all_concept
|
338
|
-
end
|
339
|
-
|
340
338
|
class ConstraintShape < Shape
|
341
339
|
has_one :constraint, :mandatory => true # See Constraint.all_constraint_shape
|
342
340
|
end
|
@@ -367,10 +365,6 @@ module ActiveFacts
|
|
367
365
|
has_one :vocabulary, :mandatory => true # See Vocabulary.all_diagram
|
368
366
|
end
|
369
367
|
|
370
|
-
class EntityType < Concept
|
371
|
-
one_to_one :fact_type # See FactType.entity_type
|
372
|
-
end
|
373
|
-
|
374
368
|
class FactTypeShape < Shape
|
375
369
|
has_one :display_role_names_setting # See DisplayRoleNamesSetting.all_fact_type_shape
|
376
370
|
has_one :fact_type, :mandatory => true # See FactType.all_fact_type_shape
|
@@ -397,9 +391,17 @@ module ActiveFacts
|
|
397
391
|
has_one :context_note, :mandatory => true # See ContextNote.all_model_note_shape
|
398
392
|
end
|
399
393
|
|
394
|
+
class ObjectType
|
395
|
+
identified_by :vocabulary, :name
|
396
|
+
maybe :is_independent
|
397
|
+
has_one :name, :mandatory => true # See Name.all_object_type
|
398
|
+
has_one :pronoun # See Pronoun.all_object_type
|
399
|
+
has_one :vocabulary, :mandatory => true # See Vocabulary.all_object_type
|
400
|
+
end
|
401
|
+
|
400
402
|
class ObjectTypeShape < Shape
|
401
|
-
has_one :concept, :mandatory => true # See Concept.all_object_type_shape
|
402
403
|
maybe :has_expanded_reference_mode
|
404
|
+
has_one :object_type, :mandatory => true # See ObjectType.all_object_type_shape
|
403
405
|
end
|
404
406
|
|
405
407
|
class ObjectifiedFactTypeNameShape < Shape
|
@@ -461,14 +463,6 @@ module ActiveFacts
|
|
461
463
|
maybe :is_mandatory
|
462
464
|
end
|
463
465
|
|
464
|
-
class TypeInheritance < FactType
|
465
|
-
identified_by :subtype, :supertype
|
466
|
-
has_one :subtype, :class => EntityType, :mandatory => true # See EntityType.all_type_inheritance_as_subtype
|
467
|
-
has_one :supertype, :class => EntityType, :mandatory => true # See EntityType.all_type_inheritance_as_supertype
|
468
|
-
has_one :assimilation # See Assimilation.all_type_inheritance
|
469
|
-
maybe :provides_identification
|
470
|
-
end
|
471
|
-
|
472
466
|
class ValueConstraintShape < ConstraintShape
|
473
467
|
has_one :object_type_shape # See ObjectTypeShape.all_value_constraint_shape
|
474
468
|
one_to_one :role_display # See RoleDisplay.value_constraint_shape
|
@@ -480,8 +474,8 @@ module ActiveFacts
|
|
480
474
|
has_one :minimum_bound, :class => Bound # See Bound.all_value_range_as_minimum_bound
|
481
475
|
end
|
482
476
|
|
483
|
-
class ValueType <
|
484
|
-
|
477
|
+
class ValueType < ObjectType
|
478
|
+
has_one :auto_assigned_transaction_timing, :class => TransactionTiming # See TransactionTiming.all_value_type_as_auto_assigned_transaction_timing
|
485
479
|
has_one :length # See Length.all_value_type
|
486
480
|
has_one :scale # See Scale.all_value_type
|
487
481
|
has_one :supertype, :class => ValueType # See ValueType.all_value_type_as_supertype
|
@@ -495,20 +489,32 @@ module ActiveFacts
|
|
495
489
|
has_one :value_range, :mandatory => true # See ValueRange.all_allowed_range
|
496
490
|
end
|
497
491
|
|
492
|
+
class EntityType < ObjectType
|
493
|
+
one_to_one :fact_type # See FactType.entity_type
|
494
|
+
end
|
495
|
+
|
498
496
|
class ImplicitBooleanValueType < ValueType
|
499
497
|
end
|
500
498
|
|
501
|
-
class
|
502
|
-
identified_by :
|
503
|
-
has_one :name, :mandatory => true # See Name.
|
504
|
-
has_one :value_type, :mandatory => true # See ValueType.
|
499
|
+
class Facet
|
500
|
+
identified_by :value_type, :name
|
501
|
+
has_one :name, :mandatory => true # See Name.all_facet
|
502
|
+
has_one :value_type, :mandatory => true # See ValueType.all_facet
|
503
|
+
end
|
504
|
+
|
505
|
+
class TypeInheritance < FactType
|
506
|
+
identified_by :subtype, :supertype
|
507
|
+
has_one :subtype, :class => EntityType, :mandatory => true # See EntityType.all_type_inheritance_as_subtype
|
508
|
+
has_one :supertype, :class => EntityType, :mandatory => true # See EntityType.all_type_inheritance_as_supertype
|
509
|
+
has_one :assimilation # See Assimilation.all_type_inheritance
|
510
|
+
maybe :provides_identification
|
505
511
|
end
|
506
512
|
|
507
|
-
class
|
508
|
-
identified_by :
|
509
|
-
has_one :
|
510
|
-
has_one :value, :mandatory => true # See Value.
|
511
|
-
has_one :value_type, :mandatory => true # See ValueType.
|
513
|
+
class FacetValue
|
514
|
+
identified_by :value_type, :facet
|
515
|
+
has_one :facet, :mandatory => true # See Facet.all_facet_value
|
516
|
+
has_one :value, :mandatory => true # See Value.all_facet_value
|
517
|
+
has_one :value_type, :mandatory => true # See ValueType.all_facet_value
|
512
518
|
end
|
513
519
|
|
514
520
|
end
|
@@ -11,15 +11,15 @@ module ActiveFacts
|
|
11
11
|
# * Maintains verbalisation context to expand readings using subscripting where needed
|
12
12
|
# * Verbalises Joins by iteratively choosing a Join Step and expanding readings
|
13
13
|
#
|
14
|
-
# The verbalisation context consists of a set of Players, each for one
|
15
|
-
# There may be more than one Player for the same
|
14
|
+
# The verbalisation context consists of a set of Players, each for one ObjectType.
|
15
|
+
# There may be more than one Player for the same ObjectType. If adjectives or role
|
16
16
|
# names don't make such duplicates unambiguous, subscripts will be generated.
|
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
20
|
# When a Player occurs in a Join, it corresponds to one Join Node of that Join.
|
21
21
|
# Each such Player has one or more JoinRoles, which refer to roles played by
|
22
|
-
# that
|
22
|
+
# that ObjectType. Where a join traverses two roles of a ternary fact type, there
|
23
23
|
# will be a residual node that has only a single JoinRole with no other meaning.
|
24
24
|
# A JoinRole must be for exactly one Player, so is used to identify a Player.
|
25
25
|
#
|
@@ -37,11 +37,11 @@ module ActiveFacts
|
|
37
37
|
#
|
38
38
|
# A constrained RoleSequence that has no explicit Join may have an implicit join,
|
39
39
|
# as per ORM2, when the roles aren't in the same fact type. These implicit joins
|
40
|
-
# are over only one
|
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
|
-
# the
|
42
|
+
# the ObjectType is an objectified Fact Type, the FactType traversed might be a
|
43
43
|
# phantom of the objectification. In the case of implicit joins, each Player is
|
44
|
-
# identified by the projected RoleRef, except for the joined-over
|
44
|
+
# identified by the projected RoleRef, except for the joined-over ObjectType whose
|
45
45
|
# Player is... well, read the next paragraph!
|
46
46
|
#
|
47
47
|
# REVISIT: I believe that the foregoing paragraph is out of date, except with
|
@@ -54,7 +54,7 @@ module ActiveFacts
|
|
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
|
57
|
+
# same ObjectType (no implicit subtyping Join is allowed). Instead, the input modules
|
58
58
|
# find the closest common supertype and create explicit JoinSteps so its roles
|
59
59
|
# can be projected.
|
60
60
|
#
|
@@ -99,9 +99,9 @@ module ActiveFacts
|
|
99
99
|
end
|
100
100
|
|
101
101
|
class Player
|
102
|
-
attr_accessor :
|
103
|
-
def initialize
|
104
|
-
@
|
102
|
+
attr_accessor :object_type, :join_nodes_by_join, :subscript, :join_roles, :role_refs
|
103
|
+
def initialize object_type
|
104
|
+
@object_type = object_type
|
105
105
|
@join_nodes_by_join = {}
|
106
106
|
@subscript = nil
|
107
107
|
@join_roles = []
|
@@ -111,30 +111,41 @@ module ActiveFacts
|
|
111
111
|
# What words are used (across all roles) for disambiguating the references to this player?
|
112
112
|
# If more than one set of adjectives was used, this player must have been subject to loose binding.
|
113
113
|
# This method is used to decide when subscripts aren't needed.
|
114
|
-
def role_adjuncts
|
115
|
-
|
116
|
-
[
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
114
|
+
def role_adjuncts matching
|
115
|
+
if matching == :loose
|
116
|
+
adjuncts = []
|
117
|
+
else
|
118
|
+
adjuncts = @role_refs.map{|rr|
|
119
|
+
[
|
120
|
+
rr.leading_adjective,
|
121
|
+
matching == :rolenames ? rr.role.role_name : nil,
|
122
|
+
rr.trailing_adjective
|
123
|
+
].compact}.uniq.sort
|
124
|
+
end
|
125
|
+
adjuncts += [@join_nodes_by_join.values.map{|jn| jn.role_name}.compact[0]].compact
|
121
126
|
adjuncts.flatten*"_"
|
122
127
|
end
|
123
128
|
|
124
129
|
def describe
|
125
|
-
@
|
130
|
+
@object_type.name + (@join_nodes_by_join.size > 0 ? " (in #{@join_nodes_by_join.size} joins)" : "")
|
126
131
|
end
|
127
132
|
end
|
128
133
|
|
129
134
|
# Find or create a Player to which we can add this role_ref
|
130
135
|
def player(ref)
|
131
|
-
if ref.is_a?(ActiveFacts::Metamodel::JoinRole)
|
132
|
-
|
133
|
-
|
136
|
+
existing_player = if ref.is_a?(ActiveFacts::Metamodel::JoinRole)
|
137
|
+
@player_by_join_role[ref]
|
138
|
+
else
|
139
|
+
@player_by_role_ref[ref] or ref.join_role && @player_by_join_role[ref.join_role]
|
140
|
+
end
|
141
|
+
if existing_player
|
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}'"
|
143
|
+
return existing_player
|
134
144
|
else
|
135
|
-
|
136
|
-
|
137
|
-
|
145
|
+
debug :player, "Adding new 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}'"
|
146
|
+
p = Player.new(ref.role.object_type)
|
147
|
+
@players.push(p)
|
148
|
+
p
|
138
149
|
end
|
139
150
|
end
|
140
151
|
|
@@ -142,7 +153,7 @@ module ActiveFacts
|
|
142
153
|
return if player.join_roles.include?(join_role)
|
143
154
|
jn = join_role.join_node
|
144
155
|
if jn1 = player.join_nodes_by_join[jn.join] and jn1 != jn
|
145
|
-
raise "Player for #{player.
|
156
|
+
raise "Player for #{player.object_type.name} may only have one join node per join, not #{jn1.object_type.name} and #{jn.object_type.name}"
|
146
157
|
end
|
147
158
|
player.join_nodes_by_join[jn.join] = jn
|
148
159
|
@player_by_join_role[join_role] = player
|
@@ -155,7 +166,7 @@ module ActiveFacts
|
|
155
166
|
if jr = role_ref.join_role
|
156
167
|
add_join_role(player, jr)
|
157
168
|
elsif !player.role_refs.include?(role_ref)
|
158
|
-
debug :subscript, "Adding reference to player #{player.object_id} for #{role_ref.role.
|
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"
|
159
170
|
player.role_refs.push(role_ref)
|
160
171
|
@player_by_role_ref[role_ref] = player
|
161
172
|
end
|
@@ -182,11 +193,7 @@ module ActiveFacts
|
|
182
193
|
elsif role_name = role_ref.role.role_name and role_name != ''
|
183
194
|
role_name
|
184
195
|
else
|
185
|
-
|
186
|
-
role_words << preferred_role_ref.leading_adjective if preferred_role_ref.leading_adjective != ""
|
187
|
-
role_words << preferred_role_ref.role.concept.name
|
188
|
-
role_words << preferred_role_ref.trailing_adjective if preferred_role_ref.trailing_adjective != ""
|
189
|
-
role_name = role_words.compact*"-"
|
196
|
+
role_name = preferred_role_ref.cql_name
|
190
197
|
if p = player(preferred_role_ref) and p.subscript
|
191
198
|
role_name += "(#{p.subscript})"
|
192
199
|
end
|
@@ -211,10 +218,10 @@ module ActiveFacts
|
|
211
218
|
# If any of these join_roles are for a known player, use that, else make a new player.
|
212
219
|
existing_players = join_roles.map{|jr| @player_by_join_role[jr] }.compact.uniq
|
213
220
|
if existing_players.size > 1
|
214
|
-
raise "Can't join these roles to more than one existing player: #{existing_players.map{|p|p.
|
221
|
+
raise "Can't join these roles to more than one existing player: #{existing_players.map{|p|p.object_type.name}*', '}!"
|
215
222
|
end
|
216
223
|
p = existing_players[0] || player(join_roles[0])
|
217
|
-
debugger if join_roles.detect{|jr| jr.role.
|
224
|
+
debugger if join_roles.detect{|jr| jr.role.object_type != p.object_type }
|
218
225
|
debug :subscript, "Joining roles to #{p.describe}" do
|
219
226
|
join_roles.each do |jr|
|
220
227
|
debug :subscript, "#{jr.describe}" do
|
@@ -233,17 +240,17 @@ module ActiveFacts
|
|
233
240
|
existing_players =
|
234
241
|
role_refs.map{|rr| @player_by_role_ref[rr] || @player_by_join_role[rr.join_role] }.compact.uniq
|
235
242
|
if existing_players.size > 1
|
236
|
-
raise "Can't join these role_refs to more than one existing player: #{existing_players.map{|p|p.
|
243
|
+
raise "Can't join these role_refs to more than one existing player: #{existing_players.map{|p|p.object_type.name}*', '}!"
|
237
244
|
end
|
238
245
|
p = existing_players[0] || player(role_refs[0])
|
239
246
|
|
240
|
-
debug :subscript, "#{existing_players[0] ? 'Adding to existing' : 'Creating new'} player for #{role_refs.map{|rr| rr.role.
|
247
|
+
debug :subscript, "#{existing_players[0] ? 'Adding to existing' : 'Creating new'} player for #{role_refs.map{|rr| rr.role.object_type.name}.uniq*', '}" do
|
241
248
|
role_refs.each do |rr|
|
242
|
-
unless p.
|
249
|
+
unless p.object_type == rr.role.object_type
|
243
250
|
# This happens in SubtypePI because uniqueness constraint is built without its implicit subtyping join.
|
244
251
|
# For now, explode only if there's no common supertype:
|
245
|
-
if 0 == (p.
|
246
|
-
raise "REVISIT: Internal error, trying to add role of #{rr.role.
|
252
|
+
if 0 == (p.object_type.supertypes_transitive & rr.role.object_type.supertypes_transitive).size
|
253
|
+
raise "REVISIT: Internal error, trying to add role of #{rr.role.object_type.name} to player #{p.object_type.name}"
|
247
254
|
end
|
248
255
|
end
|
249
256
|
add_role_player(p, rr)
|
@@ -251,21 +258,44 @@ module ActiveFacts
|
|
251
258
|
end
|
252
259
|
end
|
253
260
|
|
254
|
-
|
261
|
+
# REVISIT: include_rolenames is a bit of a hack. Role names generally serve to disambiguate players,
|
262
|
+
# so subscripts wouldn't be needed, but where a constraint refers to a fact type which is defined with
|
263
|
+
# role names, those are considered. We should instead consider only the role names that are defined
|
264
|
+
# within the constraint, not in the underlying fact types. For now, this parameter is passed as true
|
265
|
+
# from all the object type verbalisations, and not from constraints.
|
266
|
+
def create_subscripts(matching = :normal)
|
255
267
|
# Create subscripts, where necessary
|
256
268
|
@players.each { |p| p.subscript = nil } # Wipe subscripts
|
257
269
|
@players.
|
258
|
-
map{|p| [p, p.
|
259
|
-
each do |player,
|
270
|
+
map{|p| [p, p.object_type] }.
|
271
|
+
each do |player, object_type|
|
260
272
|
next if player.subscript # Done previously
|
261
|
-
dups = @players.select
|
273
|
+
dups = @players.select do |p|
|
274
|
+
p.object_type == object_type &&
|
275
|
+
p.role_adjuncts(matching) == player.role_adjuncts(matching)
|
276
|
+
end
|
262
277
|
if dups.size == 1
|
263
|
-
debug :subscript, "No subscript needed for #{
|
278
|
+
debug :subscript, "No subscript needed for #{object_type.name}"
|
264
279
|
next
|
265
280
|
end
|
266
|
-
debug :subscript, "Applying subscripts to #{dups.size} occurrences of #{
|
267
|
-
|
268
|
-
|
281
|
+
debug :subscript, "Applying subscripts to #{dups.size} occurrences of #{object_type.name}" do
|
282
|
+
s = 0
|
283
|
+
dups.
|
284
|
+
sort_by{|p| # Guarantee stable numbering
|
285
|
+
p.role_adjuncts(:role_name) + ' ' +
|
286
|
+
# Tie-breaker:
|
287
|
+
p.role_refs.map{|rr| rr.role.fact_type.preferred_reading.text}.sort.to_s
|
288
|
+
}.
|
289
|
+
each do |player|
|
290
|
+
jrname = player.join_roles.map{|jr| jr.role_ref && jr.role_ref.role.role_name}.compact[0]
|
291
|
+
rname = (rr = player.role_refs[0]) && rr.role.role_name
|
292
|
+
if jrname and !rname
|
293
|
+
# puts "Oops: rolename #{rname.inspect} != #{jrname.inspect}" if jrname != rname
|
294
|
+
player.join_nodes_by_join.values.each{|jn| jn.role_name = jrname }
|
295
|
+
else
|
296
|
+
player.subscript = s+1
|
297
|
+
s += 1
|
298
|
+
end
|
269
299
|
end
|
270
300
|
end
|
271
301
|
end
|
@@ -276,7 +306,7 @@ module ActiveFacts
|
|
276
306
|
# and also define adjectives by using the hyphenated form (on at least the first occurrence).
|
277
307
|
def expand_reading(reading, frequency_constraints = [], define_role_names = nil, value_constraints = [], &subscript_block)
|
278
308
|
reading.expand(frequency_constraints, define_role_names, value_constraints) do |role_ref|
|
279
|
-
(!(role_ref.role.role_name and define_role_names) and p = player(role_ref) and p.subscript) ? "(#{p.subscript})" : ""
|
309
|
+
(!(role_ref.role.role_name and define_role_names != nil) and p = player(role_ref) and p.subscript) ? "(#{p.subscript})" : ""
|
280
310
|
end
|
281
311
|
end
|
282
312
|
|
@@ -284,8 +314,8 @@ module ActiveFacts
|
|
284
314
|
# REVISIT: This probably doesn't produce the required result. Need to fix the NORMA importer to create the join.
|
285
315
|
def role_refs_are_subtype_joined roles
|
286
316
|
role_refs = roles.is_a?(Array) ? roles : roles.all_role_ref.to_a
|
287
|
-
|
288
|
-
|
317
|
+
role_refs_by_object_type = role_refs.inject({}) { |h, r| (h[r.role.object_type] ||= []) << r; h }
|
318
|
+
role_refs_by_object_type.values.each { |rrs| role_refs_have_same_player(rrs) }
|
289
319
|
end
|
290
320
|
|
291
321
|
# These roles are the players in an implicit counterpart join in a Presence Constraint.
|
@@ -293,9 +323,11 @@ module ActiveFacts
|
|
293
323
|
# Fix the CQL compiler to create proper joins for these presence constraints instead.
|
294
324
|
def roles_have_same_player roles
|
295
325
|
role_refs = roles.map do |role|
|
296
|
-
|
297
|
-
|
298
|
-
|
326
|
+
role.fact_type.all_reading.map{|reading|
|
327
|
+
reading.role_sequence.all_role_ref.detect{|rr| rr.role == role}
|
328
|
+
} +
|
329
|
+
role.all_role_ref.select{|rr| rr.role_sequence.all_reading.size == 0 }
|
330
|
+
end.flatten.uniq
|
299
331
|
role_refs_have_same_player(role_refs)
|
300
332
|
end
|
301
333
|
|
@@ -316,10 +348,10 @@ module ActiveFacts
|
|
316
348
|
prrs = fact_type.preferred_reading.role_sequence.all_role_ref
|
317
349
|
residual_roles = fact_type.all_role.select{|r| !@role_refs.detect{|rr| rr.role == r} }
|
318
350
|
residual_roles.each do |role|
|
319
|
-
debug :subscript, "Adding residual role for #{role.
|
351
|
+
debug :subscript, "Adding residual role for #{role.object_type.name} (in #{fact_type.default_reading}) not covered in role sequence"
|
320
352
|
preferred_role_ref = prrs.detect{|rr| rr.role == role}
|
321
353
|
if p = @player_by_role_ref[preferred_role_ref] and !p.role_refs.include?(preferred_role_ref)
|
322
|
-
raise "Adding DUPLICATE residual role for #{role.
|
354
|
+
raise "Adding DUPLICATE residual role for #{role.object_type.name}"
|
323
355
|
end
|
324
356
|
role_refs_have_same_player([prrs.detect{|rr| rr.role == role}])
|
325
357
|
end
|
@@ -340,13 +372,13 @@ module ActiveFacts
|
|
340
372
|
=begin
|
341
373
|
# For each fact type traversed, register a player for each role *not* linked to this join
|
342
374
|
# REVISIT: Using the preferred_reading role_ref is wrong here; the same preferred_reading might occur twice,
|
343
|
-
# so the respective
|
375
|
+
# so the respective object_type will need more than one Player and will be subscripted to keep them from being joined.
|
344
376
|
# Accordingly, there must be a join step for each such role, and to enforce that, I raise an exception here on duplication.
|
345
377
|
# This isn't needed now all JoinNodes have at least one JoinRole
|
346
378
|
|
347
379
|
join_steps.map do |js|
|
348
380
|
if js.fact_type.is_a?(ActiveFacts::Metamodel::ImplicitFactType)
|
349
|
-
js.fact_type.
|
381
|
+
js.fact_type.implying_role.fact_type
|
350
382
|
else
|
351
383
|
js.fact_type
|
352
384
|
end
|
@@ -358,10 +390,10 @@ module ActiveFacts
|
|
358
390
|
prrs = fact_type.preferred_reading.role_sequence.all_role_ref
|
359
391
|
residual_roles = fact_type.all_role.select{|r| !r.all_role_ref.detect{|rr| rr.join_node && rr.join_node.join == join} }
|
360
392
|
residual_roles.each do |r|
|
361
|
-
debug :subscript, "Adding residual role for #{r.
|
393
|
+
debug :subscript, "Adding residual role for #{r.object_type.name} (in #{fact_type.default_reading}) not covered in join"
|
362
394
|
preferred_role_ref = prrs.detect{|rr| rr.role == r}
|
363
395
|
if p = @player_by_role_ref[preferred_role_ref] and !p.role_refs.include?(preferred_role_ref)
|
364
|
-
raise "Adding DUPLICATE residual role for #{r.
|
396
|
+
raise "Adding DUPLICATE residual role for #{r.object_type.name} not covered in join"
|
365
397
|
end
|
366
398
|
role_refs_have_same_player([preferred_role_ref])
|
367
399
|
end
|
@@ -388,7 +420,7 @@ module ActiveFacts
|
|
388
420
|
# Choose a reading that start with the (first) role which caused us to emit this fact type:
|
389
421
|
reading = fact_type.reading_preferably_starting_with_role(role_by_fact_type[fact_type])
|
390
422
|
if join_over and # Find a reading preferably starting with the joined_over role:
|
391
|
-
joined_role = fact_type.all_role.select{|r| join_over.subtypes_transitive.include?(r.
|
423
|
+
joined_role = fact_type.all_role.select{|r| join_over.subtypes_transitive.include?(r.object_type)}[0]
|
392
424
|
reading = fact_type.reading_preferably_starting_with_role joined_role
|
393
425
|
|
394
426
|
# Use the name of the joined_over object, not the role player, in case of a subtype join:
|
@@ -399,13 +431,11 @@ module ActiveFacts
|
|
399
431
|
reading.role_sequence.all_role_ref.each do |rr|
|
400
432
|
next unless player = @player_by_role_ref[rr]
|
401
433
|
next unless subscript = player.subscript
|
402
|
-
debug :subscript, "Need to apply subscript #{subscript} to #{rr.role.
|
434
|
+
debug :subscript, "Need to apply subscript #{subscript} to #{rr.role.object_type.name}"
|
403
435
|
end
|
404
436
|
player_by_role = {}
|
405
437
|
@player_by_role_ref.keys.each{|rr| player_by_role[rr.role] = @player_by_role_ref[rr] if rr.role.fact_type == fact_type }
|
406
|
-
#role_refs = @player_by_role_ref.keys.select{|rr| rr.role.fact_type == fact_type}
|
407
438
|
expand_reading_text(nil, reading.text, reading.role_sequence, player_by_role)
|
408
|
-
#reading.expand(name_substitutions)
|
409
439
|
end
|
410
440
|
joiner ? readings*joiner : readings
|
411
441
|
end
|
@@ -421,37 +451,29 @@ module ActiveFacts
|
|
421
451
|
text.gsub(/\{(\d)\}/) do
|
422
452
|
role_ref = rrs[$1.to_i]
|
423
453
|
# REVISIT: We may need to use the step's role_refs to expand the role players here, not the reading's one (extra adjectives?)
|
424
|
-
# REVISIT: There's no way to get literals to be emitted here (value join step?)
|
454
|
+
# REVISIT: There's no way to get literals to be emitted here (value join step or query result?)
|
425
455
|
|
426
456
|
player = player_by_role[role_ref.role]
|
427
457
|
|
428
|
-
=
|
429
|
-
|
430
|
-
|
431
|
-
player = @player_by_role_ref[rr] and subscript = player.subscript
|
432
|
-
if !subscript and
|
433
|
-
pp = @players.select{|p|p.concept == rr.role.concept} and
|
434
|
-
pp.detect{|p|p.subscript}
|
435
|
-
# raise "Internal error: Subscripted players (of the same concept #{pp[0].concept.name}) when this player isn't subscripted"
|
436
|
-
end
|
437
|
-
=end
|
438
|
-
|
439
|
-
subscripted_player(role_ref, player && player.subscript) +
|
440
|
-
objectification_verbalisation(role_ref.role.concept)
|
458
|
+
join_role_name = player && player.join_nodes_by_join.values.map{|jn| jn.role_name}.compact[0]
|
459
|
+
subscripted_player(role_ref, player && player.subscript, join_role_name) +
|
460
|
+
objectification_verbalisation(role_ref.role.object_type)
|
441
461
|
end
|
442
462
|
end
|
443
463
|
end
|
444
464
|
|
445
|
-
def subscripted_player role_ref, subscript = nil
|
446
|
-
|
447
|
-
|
448
|
-
|
449
|
-
|
450
|
-
|
451
|
-
|
452
|
-
|
453
|
-
|
454
|
-
|
465
|
+
def subscripted_player role_ref, subscript = nil, join_role_name = nil
|
466
|
+
prr = @player_by_role_ref[role_ref]
|
467
|
+
subscript ||= prr.subscript if prr
|
468
|
+
debug :subscript, "Need to apply subscript #{subscript} to #{role_ref.role.object_type.name}" if subscript
|
469
|
+
object_type = role_ref.role.object_type
|
470
|
+
(join_role_name ||
|
471
|
+
[
|
472
|
+
role_ref.leading_adjective,
|
473
|
+
object_type.name,
|
474
|
+
role_ref.trailing_adjective
|
475
|
+
].compact*' '
|
476
|
+
) +
|
455
477
|
(subscript ? "(#{subscript})" : '')
|
456
478
|
end
|
457
479
|
|
@@ -515,9 +537,9 @@ module ActiveFacts
|
|
515
537
|
debug :join, "Chose new random step from #{join_steps.size}: #{next_step.describe}"
|
516
538
|
if next_step.is_objectification_step
|
517
539
|
# if this objectification plays any roles (other than its FT roles) in remaining steps, use one of those first:
|
518
|
-
fact_type = next_step.fact_type.
|
519
|
-
jn = [next_step.input_join_role.join_node, next_step.output_join_role.join_node].detect{|jn| jn.
|
520
|
-
sr = @join_steps_by_join_node[jn].reject{|t| t.fact_type.
|
540
|
+
fact_type = next_step.fact_type.implying_role.fact_type
|
541
|
+
jn = [next_step.input_join_role.join_node, next_step.output_join_role.join_node].detect{|jn| jn.object_type == fact_type.entity_type}
|
542
|
+
sr = @join_steps_by_join_node[jn].reject{|t| r = t.fact_type.implying_role and r.fact_type == fact_type}
|
521
543
|
next_step = sr[0] if sr.size > 0
|
522
544
|
end
|
523
545
|
return next_step
|
@@ -534,13 +556,13 @@ module ActiveFacts
|
|
534
556
|
# This reading's RoleRef for that role:
|
535
557
|
(role_ref = reading.role_sequence.all_role_ref_in_order[$1.to_i]) &&
|
536
558
|
# was that RoleRef for the upcoming node?
|
537
|
-
role_ref.role.
|
559
|
+
role_ref.role.object_type == next_node.object_type
|
538
560
|
end
|
539
561
|
|
540
562
|
def reading_starts_with_node(reading, next_node)
|
541
563
|
reading.text =~ /^\{([0-9])\}/ and
|
542
564
|
role_ref = reading.role_sequence.all_role_ref.detect{|rr| rr.ordinal == $1.to_i} and
|
543
|
-
role_ref.role.
|
565
|
+
role_ref.role.object_type == next_node.object_type
|
544
566
|
end
|
545
567
|
|
546
568
|
# The last reading we emitted ended with the object type name for next_node.
|
@@ -558,21 +580,21 @@ module ActiveFacts
|
|
558
580
|
end
|
559
581
|
next_reading
|
560
582
|
end
|
561
|
-
debug :join, "#{next_reading ? "'"+next_reading.expand+"'" : "No reading"} contracts against last node '#{next_node.
|
583
|
+
debug :join, "#{next_reading ? "'"+next_reading.expand+"'" : "No reading"} contracts against last node '#{next_node.object_type.name}'"
|
562
584
|
return [next_step, next_reading]
|
563
585
|
end
|
564
586
|
|
565
|
-
# REVISIT: There might be more than one objectification_verbalisation for a given
|
566
|
-
def objectification_verbalisation(
|
587
|
+
# REVISIT: There might be more than one objectification_verbalisation for a given object_type. Need to get the Join Node here and emit an objectification step involving that node.
|
588
|
+
def objectification_verbalisation(object_type)
|
567
589
|
objectified_node = nil
|
568
|
-
unless
|
569
|
-
|
590
|
+
unless object_type.is_a?(Metamodel::EntityType) and
|
591
|
+
object_type.fact_type and # Not objectified
|
570
592
|
objectification_step = @join_steps.
|
571
593
|
detect do |js|
|
572
594
|
# The objectifying entity type should always be the input_join_node here, but be safe:
|
573
595
|
js.is_objectification_step and
|
574
|
-
(objectified_node = js.input_join_role.join_node).
|
575
|
-
(objectified_node = js.output_join_role.join_node).
|
596
|
+
(objectified_node = js.input_join_role.join_node).object_type == object_type ||
|
597
|
+
(objectified_node = js.output_join_role.join_node).object_type == object_type
|
576
598
|
end
|
577
599
|
return ''
|
578
600
|
end
|
@@ -586,7 +608,7 @@ module ActiveFacts
|
|
586
608
|
@join_steps.
|
587
609
|
detect{|js|
|
588
610
|
js.is_objectification_step and
|
589
|
-
js.input_join_role.join_node.
|
611
|
+
js.input_join_role.join_node.object_type == object_type || js.output_join_role.join_node.object_type == object_type
|
590
612
|
}
|
591
613
|
steps << other_step
|
592
614
|
debug :join, "Emitting objectification step allows deleting #{other_step.describe}"
|
@@ -601,9 +623,9 @@ module ActiveFacts
|
|
601
623
|
end
|
602
624
|
end
|
603
625
|
|
604
|
-
# role_refs = steps.map{|step| [step.input_join_role.join_node, step.output_join_role.join_node].map{|jn| jn.all_role_ref.detect{|rr| rr.role.fact_type ==
|
626
|
+
# role_refs = steps.map{|step| [step.input_join_role.join_node, step.output_join_role.join_node].map{|jn| jn.all_role_ref.detect{|rr| rr.role.fact_type == object_type.fact_type}}}.flatten.compact.uniq
|
605
627
|
|
606
|
-
reading =
|
628
|
+
reading = object_type.fact_type.preferred_reading
|
607
629
|
" (where #{expand_reading_text(objectification_step, reading.text, reading.role_sequence, player_by_role)})"
|
608
630
|
end
|
609
631
|
|
@@ -624,16 +646,19 @@ module ActiveFacts
|
|
624
646
|
exit_node = @join_nodes.detect{|jn| jn.all_join_role.detect{|jr| jr.role == last_role_ref.role}}
|
625
647
|
exit_step = nil
|
626
648
|
|
649
|
+
count = 0
|
627
650
|
while other_step =
|
628
651
|
@join_steps.
|
629
652
|
detect{|js|
|
630
653
|
next unless js.is_objectification_step
|
631
|
-
|
654
|
+
# REVISIT: This test is too weak: We need to ensure that the same join nodes are involved, not just the same object types:
|
655
|
+
next unless js.input_join_role.join_node.object_type == fact_type.entity_type || js.output_join_role.join_node.object_type == fact_type.entity_type
|
632
656
|
exit_step = js if js.output_join_role.join_node == exit_node
|
633
657
|
true
|
634
658
|
}
|
635
659
|
debug :join, "Emitting objectified FT allows deleting #{other_step.describe}"
|
636
660
|
step_completed(other_step)
|
661
|
+
# raise "The objectification of '#{fact_type.default_reading}' should not cause the deletion of more than #{fact_type.all_role.size} other join steps" if (count += 1) > fact_type.all_role.size
|
637
662
|
end
|
638
663
|
[ reading, exit_step ? exit_step.input_join_role.join_node : exit_node, exit_step, last_is_contractable]
|
639
664
|
end
|
@@ -657,7 +682,7 @@ module ActiveFacts
|
|
657
682
|
end
|
658
683
|
|
659
684
|
if next_step
|
660
|
-
debug :join, "Chose #{next_step.describe} because it's contractable against last node #{next_node.
|
685
|
+
debug :join, "Chose #{next_step.describe} because it's contractable against last node #{next_node.object_type.name} using #{next_reading.expand}"
|
661
686
|
|
662
687
|
player_by_role =
|
663
688
|
next_step.all_join_role.inject({}) {|h, jr| h[jr.role] = @player_by_join_role[jr]; h }
|
@@ -671,26 +696,26 @@ module ActiveFacts
|
|
671
696
|
|
672
697
|
if next_step.is_unary_step
|
673
698
|
# Objectified unaries get emitted as unaries, not as objectifications:
|
674
|
-
|
675
|
-
|
676
|
-
next_reading =
|
699
|
+
role = next_step.input_join_role.role
|
700
|
+
role = role.fact_type.implying_role if role.fact_type.is_a?(ImplicitFactType)
|
701
|
+
next_reading = role.fact_type.preferred_reading
|
677
702
|
readings += " and " unless readings.empty?
|
678
703
|
readings += expand_reading_text(next_step, next_reading.text, next_reading.role_sequence, player_by_role)
|
679
704
|
step_completed(next_step)
|
680
705
|
elsif next_step.is_objectification_step
|
681
|
-
fact_type = next_step.fact_type.
|
706
|
+
fact_type = next_step.fact_type.implying_role.fact_type
|
682
707
|
|
683
708
|
# This objectification step is over an implicit fact type, so player_by_role won't have all the players
|
684
709
|
# Add the players of other roles associated with steps from this objectified player.
|
685
710
|
objectified_node = next_step.input_join_role.join_node
|
686
|
-
raise "Assumption violated that the objectification is the input join role" unless objectified_node.
|
711
|
+
raise "Assumption violated that the objectification is the input join role" unless objectified_node.object_type.fact_type
|
687
712
|
objectified_node.all_join_step.map do |other_step|
|
688
713
|
(other_step.all_incidental_join_role.to_a + [other_step.output_join_role]).map do |jr|
|
689
714
|
player_by_role[jr.role] = @player_by_join_role[jr]
|
690
715
|
end
|
691
716
|
end
|
692
717
|
|
693
|
-
if last_is_contractable and next_node.
|
718
|
+
if last_is_contractable and next_node.object_type.is_a?(EntityType) and next_node.object_type.fact_type == fact_type
|
694
719
|
# The last reading we emitted ended with the name of the objectification of this fact type, so we can contract the objectification
|
695
720
|
# REVISIT: Do we need to use player_by_role here (if this objectification is traversed twice and so is subscripted)
|
696
721
|
readings += objectification_verbalisation(fact_type.entity_type)
|