activefacts 1.6.0 → 1.7.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (169) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +14 -0
  3. data/.rspec +2 -0
  4. data/.travis.yml +4 -0
  5. data/Gemfile +14 -0
  6. data/LICENSE.txt +21 -0
  7. data/README.md +60 -0
  8. data/Rakefile +3 -80
  9. data/activefacts.gemspec +36 -0
  10. data/bin/afgen +4 -2
  11. data/bin/cql +5 -1
  12. data/lib/activefacts.rb +3 -12
  13. data/lib/activefacts/{vocabulary/query_evaluator.rb → query/evaluator.rb} +0 -0
  14. data/lib/activefacts/version.rb +2 -2
  15. metadata +48 -296
  16. data/History.txt +0 -4
  17. data/LICENSE +0 -19
  18. data/Manifest.txt +0 -165
  19. data/README.rdoc +0 -81
  20. data/css/offline.css +0 -3
  21. data/css/orm2.css +0 -124
  22. data/css/print.css +0 -8
  23. data/css/style-print.css +0 -357
  24. data/css/style.css +0 -387
  25. data/download.html +0 -110
  26. data/examples/CQL/Address.cql +0 -44
  27. data/examples/CQL/Blog.cql +0 -54
  28. data/examples/CQL/CompanyDirectorEmployee.cql +0 -56
  29. data/examples/CQL/Death.cql +0 -17
  30. data/examples/CQL/Diplomacy.cql +0 -48
  31. data/examples/CQL/Genealogy.cql +0 -98
  32. data/examples/CQL/Insurance.cql +0 -320
  33. data/examples/CQL/Marriage.cql +0 -18
  34. data/examples/CQL/Metamodel.cql +0 -493
  35. data/examples/CQL/Monogamy.cql +0 -24
  36. data/examples/CQL/MultiInheritance.cql +0 -22
  37. data/examples/CQL/NonRoleId.cql +0 -14
  38. data/examples/CQL/OddIdentifier.cql +0 -18
  39. data/examples/CQL/OilSupply.cql +0 -53
  40. data/examples/CQL/OneToOnes.cql +0 -17
  41. data/examples/CQL/Orienteering.cql +0 -111
  42. data/examples/CQL/PersonPlaysGame.cql +0 -18
  43. data/examples/CQL/RedundantDependency.cql +0 -34
  44. data/examples/CQL/SchoolActivities.cql +0 -33
  45. data/examples/CQL/SeparateSubtype.cql +0 -30
  46. data/examples/CQL/ServiceDirector.cql +0 -276
  47. data/examples/CQL/SimplestUnary.cql +0 -12
  48. data/examples/CQL/Supervision.cql +0 -34
  49. data/examples/CQL/WaiterTips.cql +0 -33
  50. data/examples/CQL/Warehousing.cql +0 -101
  51. data/examples/CQL/WindowInRoomInBldg.cql +0 -28
  52. data/examples/CQL/unit.cql +0 -474
  53. data/examples/index.html +0 -420
  54. data/examples/intro.html +0 -327
  55. data/examples/local.css +0 -24
  56. data/index.html +0 -111
  57. data/lib/activefacts/cql.rb +0 -35
  58. data/lib/activefacts/cql/CQLParser.treetop +0 -158
  59. data/lib/activefacts/cql/Context.treetop +0 -48
  60. data/lib/activefacts/cql/Expressions.treetop +0 -67
  61. data/lib/activefacts/cql/FactTypes.treetop +0 -358
  62. data/lib/activefacts/cql/Language/English.treetop +0 -315
  63. data/lib/activefacts/cql/LexicalRules.treetop +0 -253
  64. data/lib/activefacts/cql/ObjectTypes.treetop +0 -210
  65. data/lib/activefacts/cql/Rakefile +0 -14
  66. data/lib/activefacts/cql/Terms.treetop +0 -183
  67. data/lib/activefacts/cql/ValueTypes.treetop +0 -202
  68. data/lib/activefacts/cql/compiler.rb +0 -156
  69. data/lib/activefacts/cql/compiler/clause.rb +0 -1137
  70. data/lib/activefacts/cql/compiler/constraint.rb +0 -581
  71. data/lib/activefacts/cql/compiler/entity_type.rb +0 -457
  72. data/lib/activefacts/cql/compiler/expression.rb +0 -443
  73. data/lib/activefacts/cql/compiler/fact.rb +0 -390
  74. data/lib/activefacts/cql/compiler/fact_type.rb +0 -421
  75. data/lib/activefacts/cql/compiler/query.rb +0 -106
  76. data/lib/activefacts/cql/compiler/shared.rb +0 -161
  77. data/lib/activefacts/cql/compiler/value_type.rb +0 -174
  78. data/lib/activefacts/cql/nodes.rb +0 -49
  79. data/lib/activefacts/cql/parser.rb +0 -241
  80. data/lib/activefacts/dependency_analyser.rb +0 -182
  81. data/lib/activefacts/generate/absorption.rb +0 -70
  82. data/lib/activefacts/generate/composition.rb +0 -118
  83. data/lib/activefacts/generate/cql.rb +0 -714
  84. data/lib/activefacts/generate/dm.rb +0 -279
  85. data/lib/activefacts/generate/help.rb +0 -64
  86. data/lib/activefacts/generate/helpers/inject.rb +0 -16
  87. data/lib/activefacts/generate/helpers/oo.rb +0 -162
  88. data/lib/activefacts/generate/helpers/ordered.rb +0 -605
  89. data/lib/activefacts/generate/helpers/rails.rb +0 -57
  90. data/lib/activefacts/generate/html/glossary.rb +0 -461
  91. data/lib/activefacts/generate/json.rb +0 -337
  92. data/lib/activefacts/generate/null.rb +0 -32
  93. data/lib/activefacts/generate/rails/models.rb +0 -246
  94. data/lib/activefacts/generate/rails/schema.rb +0 -216
  95. data/lib/activefacts/generate/records.rb +0 -46
  96. data/lib/activefacts/generate/ruby.rb +0 -133
  97. data/lib/activefacts/generate/sql/mysql.rb +0 -280
  98. data/lib/activefacts/generate/sql/server.rb +0 -273
  99. data/lib/activefacts/generate/stats.rb +0 -69
  100. data/lib/activefacts/generate/text.rb +0 -27
  101. data/lib/activefacts/generate/topics.rb +0 -265
  102. data/lib/activefacts/generate/traits/datavault.rb +0 -241
  103. data/lib/activefacts/generate/traits/oo.rb +0 -73
  104. data/lib/activefacts/generate/traits/ordered.rb +0 -33
  105. data/lib/activefacts/generate/traits/ruby.rb +0 -210
  106. data/lib/activefacts/generate/transform/datavault.rb +0 -266
  107. data/lib/activefacts/generate/transform/surrogate.rb +0 -214
  108. data/lib/activefacts/generate/version.rb +0 -26
  109. data/lib/activefacts/input/cql.rb +0 -43
  110. data/lib/activefacts/input/orm.rb +0 -1636
  111. data/lib/activefacts/mapping/rails.rb +0 -132
  112. data/lib/activefacts/persistence.rb +0 -15
  113. data/lib/activefacts/persistence/columns.rb +0 -446
  114. data/lib/activefacts/persistence/foreignkey.rb +0 -187
  115. data/lib/activefacts/persistence/index.rb +0 -240
  116. data/lib/activefacts/persistence/object_type.rb +0 -198
  117. data/lib/activefacts/persistence/reference.rb +0 -434
  118. data/lib/activefacts/persistence/tables.rb +0 -380
  119. data/lib/activefacts/registry.rb +0 -11
  120. data/lib/activefacts/support.rb +0 -132
  121. data/lib/activefacts/vocabulary.rb +0 -9
  122. data/lib/activefacts/vocabulary/extensions.rb +0 -1348
  123. data/lib/activefacts/vocabulary/metamodel.rb +0 -570
  124. data/lib/activefacts/vocabulary/verbaliser.rb +0 -804
  125. data/script/txt2html +0 -71
  126. data/spec/absorption_spec.rb +0 -95
  127. data/spec/cql/comparison_spec.rb +0 -89
  128. data/spec/cql/context_spec.rb +0 -94
  129. data/spec/cql/contractions_spec.rb +0 -224
  130. data/spec/cql/deontic_spec.rb +0 -88
  131. data/spec/cql/entity_type_spec.rb +0 -320
  132. data/spec/cql/expressions_spec.rb +0 -66
  133. data/spec/cql/fact_type_matching_spec.rb +0 -338
  134. data/spec/cql/french_spec.rb +0 -21
  135. data/spec/cql/parser/bad_literals_spec.rb +0 -86
  136. data/spec/cql/parser/constraints_spec.rb +0 -19
  137. data/spec/cql/parser/entity_types_spec.rb +0 -106
  138. data/spec/cql/parser/expressions_spec.rb +0 -199
  139. data/spec/cql/parser/fact_types_spec.rb +0 -44
  140. data/spec/cql/parser/literals_spec.rb +0 -312
  141. data/spec/cql/parser/pragmas_spec.rb +0 -89
  142. data/spec/cql/parser/value_types_spec.rb +0 -42
  143. data/spec/cql/role_matching_spec.rb +0 -148
  144. data/spec/cql/samples_spec.rb +0 -244
  145. data/spec/cql_cql_spec.rb +0 -73
  146. data/spec/cql_dm_spec.rb +0 -136
  147. data/spec/cql_mysql_spec.rb +0 -69
  148. data/spec/cql_parse_spec.rb +0 -34
  149. data/spec/cql_ruby_spec.rb +0 -73
  150. data/spec/cql_sql_spec.rb +0 -72
  151. data/spec/cql_symbol_tables_spec.rb +0 -261
  152. data/spec/cqldump_spec.rb +0 -170
  153. data/spec/helpers/array_matcher.rb +0 -23
  154. data/spec/helpers/ctrl_c_support.rb +0 -52
  155. data/spec/helpers/diff_matcher.rb +0 -39
  156. data/spec/helpers/file_matcher.rb +0 -34
  157. data/spec/helpers/parse_to_ast_matcher.rb +0 -80
  158. data/spec/helpers/string_matcher.rb +0 -30
  159. data/spec/helpers/test_parser.rb +0 -15
  160. data/spec/norma_cql_spec.rb +0 -66
  161. data/spec/norma_ruby_spec.rb +0 -62
  162. data/spec/norma_ruby_sql_spec.rb +0 -107
  163. data/spec/norma_sql_spec.rb +0 -57
  164. data/spec/norma_tables_spec.rb +0 -95
  165. data/spec/ruby_api_spec.rb +0 -23
  166. data/spec/spec_helper.rb +0 -35
  167. data/spec/transform_surrogate_spec.rb +0 -59
  168. data/status.html +0 -138
  169. data/why.html +0 -60
@@ -1,804 +0,0 @@
1
- #
2
- # ActiveFacts Vocabulary Metamodel.
3
- # Verbaliser for the ActiveFacts Vocabulary
4
- #
5
- # Copyright (c) 2009 Clifford Heath. Read the LICENSE file.
6
- #
7
- module ActiveFacts
8
- module Metamodel
9
- #
10
- # The Verbaliser fulfils two roles:
11
- # * Maintains verbalisation context to expand readings using subscripting where needed
12
- # * Verbalises Queries by iteratively choosing a Step and expanding readings
13
- #
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
- # names don't make such duplicates unambiguous, subscripts will be generated.
17
- # Thus, the verbalisation context must be completely populated before subscript
18
- # generation, which must be before any Player name gets verbalised.
19
- #
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
- #
26
- # When a Player occurs outside a Query, it's identified by a projected RoleRef.
27
- # REVISIT: This is untrue when a uniqueness constraint is imported from NORMA.
28
- # In this case no query will be constructed to project the roles of the constrained
29
- # object type (only the constrained roles will be projected) - this will be fixed.
30
- #
31
- # Each constraint (except Ring Constraints) has one or more RoleSequence containing
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
- #
35
- # The only type of query possible in a Ring Constraint is a subtyping query, which
36
- # is always implicit and unambiguous, so is never instantiated.
37
- #
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
- # are over only one ObjectType, by traversing a single FactType (and possibly,
41
- # multiple TypeInheritance FactTypes) for each RoleRef. Note however that when
42
- # the ObjectType is an objectified Fact Type, the FactType traversed might be a
43
- # phantom of the objectification. In the case of implicit queries, each Player is
44
- # identified by the projected RoleRef, except for the joined-over ObjectType whose
45
- # Player is... well, read the next paragraph!
46
- #
47
- # REVISIT: I believe that the foregoing paragraph is out of date, except with
48
- # respect to PresenceConstraints imported from NORMA (both external mandatory
49
- # and external uniqueness constraints). The joined-over Player in a UC is
50
- # identified by its RoleRefs in the RoleSequence of the Fact Type's preferred
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
- #
54
- # For a SetComparisonConstraint, there are two or more constrained RoleSequences.
55
- # The matching RoleRefs (by Ordinal position) are for joined players, that is,
56
- # one individual instance plays both roles. The RoleRefs must (now) be for the
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
- # can be projected.
60
- #
61
- # When expanding Reading text however, the RoleRefs in the reading's RoleSequence
62
- # may be expected not to be attached to the Players for that reading. Instead,
63
- # the set of one or more RoleRefs which caused that reading to be expanded must
64
- # be passed in, and the corresponding roles matched with Players to determine
65
- # the need to emit a subscript.
66
- #
67
- class Verbaliser
68
- # Verbalisation context:
69
- attr_reader :players
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
-
74
- # The projected role references over which we're verbalising
75
- attr_reader :role_refs
76
-
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
-
83
- def initialize role_refs = nil
84
- @role_refs = role_refs
85
-
86
- # Verbalisation context:
87
- @players = []
88
- @player_by_play = {}
89
- @player_by_role_ref = {}
90
- @player_joined_over = nil
91
-
92
- # Query Verbaliser context:
93
- @query = nil
94
- @variables = []
95
- @steps = []
96
- @steps_by_variable = {}
97
-
98
- add_role_refs role_refs if role_refs
99
- end
100
-
101
- class Player
102
- attr_accessor :object_type, :variables_by_query, :subscript, :plays, :role_refs
103
- def initialize object_type
104
- @object_type = object_type
105
- @variables_by_query = {}
106
- @subscript = nil
107
- @plays = []
108
- @role_refs = []
109
- end
110
-
111
- # What words are used (across all roles) for disambiguating the references to this player?
112
- # If more than one set of adjectives was used, this player must have been subject to loose binding.
113
- # This method is used to decide when subscripts aren't needed.
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 += [@variables_by_query.values.map{|jn| jn.role_name}.compact[0]].compact
126
- adjuncts.flatten*"_"
127
- end
128
-
129
- def describe
130
- @object_type.name + (@variables_by_query.size > 0 ? " (in #{@variables_by_query.size} variables)" : "")
131
- end
132
- end
133
-
134
- # Find or create a Player to which we can add this role_ref
135
- def player(ref)
136
- existing_player = if ref.is_a?(ActiveFacts::Metamodel::Play)
137
- @player_by_play[ref]
138
- else
139
- @player_by_role_ref[ref] or ref.play && @player_by_play[ref.play]
140
- end
141
- if existing_player
142
- trace :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
144
- else
145
- trace :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
149
- end
150
- end
151
-
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
- end
158
- player.variables_by_query[jn.query] = jn
159
- @player_by_play[play] = player
160
- player.plays << play
161
- end
162
-
163
- # Add a RoleRef to an existing Player
164
- def add_role_player player, role_ref
165
- #trace :subscript, "Adding role_ref #{role_ref.object_id} to player #{player.object_id}"
166
- if play = role_ref.play
167
- add_play(player, play)
168
- elsif !player.role_refs.include?(role_ref)
169
- trace :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
- player.role_refs.push(role_ref)
171
- @player_by_role_ref[role_ref] = player
172
- end
173
- end
174
-
175
- def add_role_ref role_ref
176
- add_role_player(player(role_ref), role_ref)
177
- end
178
-
179
- # Add RoleRefs to one or more Players, creating players where needed
180
- def add_role_refs role_refs
181
- role_refs.each{|rr| add_role_ref(rr) }
182
- end
183
-
184
- # Return an array of the names of these identifying_roles.
185
- def identifying_role_names identifying_role_refs
186
- identifying_role_refs.map do |role_ref|
187
- preferred_role_ref = role_ref.role.fact_type.preferred_reading.role_sequence.all_role_ref.detect{|reading_rr|
188
- reading_rr.role == role_ref.role
189
- }
190
-
191
- if (role_ref.role.fact_type.all_role.size == 1)
192
- role_ref.role.fact_type.default_reading # Need whole reading for a unary.
193
- elsif role_name = role_ref.role.role_name and role_name != ''
194
- role_name
195
- else
196
- role_name = preferred_role_ref.cql_name
197
- if p = player(preferred_role_ref) and p.subscript
198
- role_name += "(#{p.subscript})"
199
- end
200
- role_name
201
- end
202
- end
203
- end
204
-
205
- # All these readings are for the same fact type, and all will be emitted, so the roles cover the same players
206
- # This is used when verbalising fact types and entity types.
207
- def alternate_readings readings
208
- readings.map do |reading|
209
- reading.role_sequence.all_role_ref.sort_by{|rr| rr.role.ordinal}
210
- end.transpose.each do |role_refs|
211
- role_refs_have_same_player role_refs
212
- end
213
- end
214
-
215
- def plays_have_same_player plays
216
- return if plays.empty?
217
-
218
- # If any of these plays are for a known player, use that, else make a new player.
219
- existing_players = plays.map{|play| @player_by_play[play] }.compact.uniq
220
- if existing_players.size > 1
221
- raise "At most one existing player can play these roles: #{existing_players.map{|p|p.object_type.name}*', '}!"
222
- end
223
- p = existing_players[0] || player(plays[0])
224
- trace :subscript, "roles are playes of #{p.describe}" do
225
- plays.each do |play|
226
- trace :subscript, "#{play.describe}" do
227
- add_play p, play
228
- end
229
- end
230
- end
231
- end
232
-
233
- # These RoleRefs are all for the same player. Find whether any of them has a player already
234
- def role_refs_have_same_player role_refs
235
- role_refs = role_refs.is_a?(Array) ? role_refs : role_refs.all_role_ref.to_a
236
- return if role_refs.empty?
237
-
238
- # If any of these role_refs are for a known player, use that, else make a new player.
239
- existing_players =
240
- role_refs.map{|rr| @player_by_role_ref[rr] || @player_by_play[rr.play] }.compact.uniq
241
- if existing_players.size > 1
242
- raise "At most one existing player can play these roles: #{existing_players.map{|p|p.object_type.name}*', '}!"
243
- end
244
- p = existing_players[0] || player(role_refs[0])
245
-
246
- trace :subscript, "#{existing_players[0] ? 'Adding to existing' : 'Creating new'} player for #{role_refs.map{|rr| rr.role.object_type.name}.uniq*', '}" do
247
- role_refs.each do |rr|
248
- unless p.object_type == rr.role.object_type
249
- # This happens in SubtypePI because uniqueness constraint is built without its implicit subtyping step.
250
- # For now, explode only if there's no common supertype:
251
- if 0 == (p.object_type.supertypes_transitive & rr.role.object_type.supertypes_transitive).size
252
- raise "REVISIT: Internal error, trying to add role of #{rr.role.object_type.name} to player #{p.object_type.name}"
253
- end
254
- end
255
- add_role_player(p, rr)
256
- end
257
- end
258
- end
259
-
260
- # REVISIT: include_rolenames is a bit of a hack. Role names generally serve to disambiguate players,
261
- # so subscripts wouldn't be needed, but where a constraint refers to a fact type which is defined with
262
- # role names, those are considered. We should instead consider only the role names that are defined
263
- # within the constraint, not in the underlying fact types. For now, this parameter is passed as true
264
- # from all the object type verbalisations, and not from constraints.
265
- def create_subscripts(matching = :normal)
266
- # Create subscripts, where necessary
267
- @players.each { |p| p.subscript = nil } # Wipe subscripts
268
- @players.
269
- map{|p| [p, p.object_type] }.
270
- each do |player, object_type|
271
- next if player.subscript # Done previously
272
- dups = @players.select do |p|
273
- p.object_type == object_type &&
274
- p.role_adjuncts(matching) == player.role_adjuncts(matching)
275
- end
276
- if dups.size == 1
277
- trace :subscript, "No subscript needed for #{object_type.name}"
278
- next
279
- end
280
- trace :subscript, "Applying subscripts to #{dups.size} occurrences of #{object_type.name}" do
281
- s = 0
282
- dups.
283
- sort_by do |p| # Guarantee stable numbering
284
- p.role_adjuncts(:role_name) + ' ' +
285
- # Tie-breaker:
286
- p.role_refs.map{|rr| rr.role.fact_type.preferred_reading.text}.sort.to_s
287
- end.
288
- each do |player|
289
- jrname = player.plays.map{|play| play.role_ref && play.role_ref.role.role_name}.compact[0]
290
- rname = (rr = player.role_refs[0]) && rr.role.role_name
291
- if jrname and !rname
292
- # puts "Oops: rolename #{rname.inspect} != #{jrname.inspect}" if jrname != rname
293
- player.variables_by_query.values.each{|jn| jn.role_name = jrname }
294
- else
295
- player.subscript = s+1
296
- s += 1
297
- end
298
- end
299
- end
300
- end
301
- end
302
-
303
- # Expand a reading for an entity type or fact type definition. Unlike expansions in constraints,
304
- # these expansions include frequency constraints, role names and value constraints as passed-in,
305
- # and also define adjectives by using the hyphenated form (on at least the first occurrence).
306
- def expand_reading(reading, frequency_constraints = [], define_role_names = nil, value_constraints = [], &subscript_block)
307
- reading.expand(frequency_constraints, define_role_names, value_constraints) do |role_ref, *parts|
308
- parts + [
309
- (!(role_ref.role.role_name and define_role_names != nil) and p = player(role_ref) and p.subscript) ? "(#{p.subscript})" : nil
310
- ]
311
- end
312
- end
313
-
314
- # Where no explicit Query has been created, a query is still sometimes present (e.g. in a constraint from NORMA)
315
- # REVISIT: This probably doesn't produce the required result. Need to fix the NORMA importer to create the query.
316
- def role_refs_have_subtype_steps roles
317
- role_refs = roles.is_a?(Array) ? roles : roles.all_role_ref.to_a
318
- role_refs_by_object_type = role_refs.inject({}) { |h, r| (h[r.role.object_type] ||= []) << r; h }
319
- role_refs_by_object_type.values.each { |rrs| role_refs_have_same_player(rrs) }
320
- end
321
-
322
- # These roles are the players in an implicit counterpart join in a Presence Constraint.
323
- # REVISIT: It's not clear that we can safely use the preferred_reading's RoleRefs here.
324
- # Fix the CQL compiler to create proper queries for these presence constraints instead.
325
- def roles_have_same_player roles
326
- role_refs = roles.map do |role|
327
- role.fact_type.all_reading.map{|reading|
328
- reading.role_sequence.all_role_ref.detect{|rr| rr.role == role}
329
- } +
330
- role.all_role_ref.select{|rr| rr.role_sequence.all_reading.size == 0 }
331
- end.flatten.uniq
332
- role_refs_have_same_player(role_refs)
333
- end
334
-
335
- def prepare_role_sequence role_sequence, join_over = nil
336
- @role_refs = role_sequence.is_a?(Array) ? role_sequence : role_sequence.all_role_ref.to_a
337
-
338
- if jrr = @role_refs.detect{|rr| rr.play && rr.play.variable}
339
- return prepare_query_players(jrr.play.variable.query)
340
- end
341
-
342
- # Ensure that all the joined-over role_refs are indexed for subscript generation.
343
- role_refs_by_fact_type =
344
- @role_refs.inject({}) { |hash, rr| (hash[rr.role.fact_type] ||= []) << rr; hash }
345
- role_refs_by_fact_type.each do |fact_type, role_refs|
346
- role_refs.each { |rr| role_refs_have_same_player([rr]) }
347
-
348
- # Register the role_refs in the preferred reading which refer to roles not covered in the role sequence.
349
- prrs = fact_type.preferred_reading.role_sequence.all_role_ref
350
- residual_roles = fact_type.all_role.select{|r| !@role_refs.detect{|rr| rr.role == r} }
351
- residual_roles.each do |role|
352
- trace :subscript, "Adding residual role for #{role.object_type.name} (in #{fact_type.default_reading}) not covered in role sequence"
353
- preferred_role_ref = prrs.detect{|rr| rr.role == role}
354
- if p = @player_by_role_ref[preferred_role_ref] and !p.role_refs.include?(preferred_role_ref)
355
- raise "Adding DUPLICATE residual role for #{role.object_type.name}"
356
- end
357
- role_refs_have_same_player([prrs.detect{|rr| rr.role == role}])
358
- end
359
- end
360
- end
361
-
362
- def prepare_query_players query
363
- trace :subscript, "Indexing roles of fact types in #{query.all_step.size} steps" do
364
- steps = []
365
- # Register all references to each variable as being for the same player:
366
- query.all_variable.to_a.sort_by(&:ordinal).each do |variable|
367
- trace :subscript, "Adding Roles of #{variable.describe}" do
368
- plays_have_same_player(variable.all_play.to_a)
369
- steps = steps | variable.all_step
370
- end
371
- end
372
-
373
- =begin
374
- # For each fact type traversed, register a player for each role *not* linked to this query
375
- # REVISIT: Using the preferred_reading role_ref is wrong here; the same preferred_reading might occur twice,
376
- # so the respective object_type will need more than one Player and will be subscripted to keep them from being joined.
377
- # Accordingly, there must be a step for each such role, and to enforce that, I raise an exception here on duplication.
378
- # This isn't needed now all Variables have at least one Play
379
-
380
- steps.map do |js|
381
- if js.fact_type.is_a?(ActiveFacts::Metamodel::LinkFactType)
382
- js.fact_type.implying_role.fact_type
383
- else
384
- js.fact_type
385
- end
386
- end.uniq.each do |fact_type|
387
- #steps.map{|js|js.fact_type}.uniq.each do |fact_type|
388
- next if fact_type.is_a?(ActiveFacts::Metamodel::LinkFactType)
389
-
390
- trace :subscript, "Residual roles in '#{fact_type.default_reading}' are" do
391
- prrs = fact_type.preferred_reading.role_sequence.all_role_ref
392
- residual_roles = fact_type.all_role.select{|r| !r.all_role_ref.detect{|rr| rr.variable && rr.variable.query == query} }
393
- residual_roles.each do |r|
394
- trace :subscript, "Adding residual role for #{r.object_type.name} (in #{fact_type.default_reading}) not covered in query"
395
- preferred_role_ref = prrs.detect{|rr| rr.role == r}
396
- if p = @player_by_role_ref[preferred_role_ref] and !p.role_refs.include?(preferred_role_ref)
397
- raise "Adding DUPLICATE residual role for #{r.object_type.name} not covered in query"
398
- end
399
- role_refs_have_same_player([preferred_role_ref])
400
- end
401
- end
402
- end
403
- =end
404
- end
405
- end
406
-
407
- def verbalise_over_role_sequence role_sequence, conjunction = ' and ', role_proximity = :both
408
- @role_refs = role_sequence.is_a?(Array) ? role_sequence : role_sequence.all_role_ref.to_a
409
-
410
- if jrr = role_refs.detect{|rr| rr.play}
411
- return verbalise_query(jrr.play.variable.query)
412
- end
413
-
414
- # First, figure out whether there's a query:
415
- join_over, joined_roles = *Metamodel.plays_over(role_sequence.all_role_ref.map{|rr|rr.role}, role_proximity)
416
-
417
- role_by_fact_type = {}
418
- fact_types = @role_refs.map{|rr| ft = rr.role.fact_type; role_by_fact_type[ft] ||= rr.role; ft}.uniq
419
- readings = fact_types.map do |fact_type|
420
- name_substitutions = []
421
- # Choose a reading that start with the (first) role which caused us to emit this fact type:
422
- reading = fact_type.reading_preferably_starting_with_role(role_by_fact_type[fact_type])
423
- if join_over and # Find a reading preferably starting with the joined_over role:
424
- joined_role = fact_type.all_role.select{|r| join_over.subtypes_transitive.include?(r.object_type)}[0]
425
- reading = fact_type.reading_preferably_starting_with_role joined_role
426
-
427
- # Use the name of the joined_over object, not the role player, in case of a subtype step:
428
- rrrs = reading.role_sequence.all_role_ref_in_order
429
- role_index = (0..rrrs.size).detect{|i| rrrs[i].role == joined_role }
430
- name_substitutions[role_index] = [nil, join_over.name]
431
- end
432
- reading.role_sequence.all_role_ref.each do |rr|
433
- next unless player = @player_by_role_ref[rr]
434
- next unless subscript = player.subscript
435
- trace :subscript, "Need to apply subscript #{subscript} to #{rr.role.object_type.name}"
436
- end
437
- player_by_role = {}
438
- @player_by_role_ref.keys.each{|rr| player_by_role[rr.role] = @player_by_role_ref[rr] if rr.role.fact_type == fact_type }
439
- expand_reading_text(nil, reading.text, reading.role_sequence, player_by_role)
440
- end
441
- conjunction ? readings*conjunction : readings
442
- end
443
-
444
- # Expand this reading (or partial reading, during contraction)
445
- def expand_reading_text(step, text, role_sequence, player_by_role = {})
446
- if !player_by_role.empty? and !player_by_role.is_a?(Hash) || player_by_role.keys.detect{|k| !k.is_a?(ActiveFacts::Metamodel::Role)}
447
- raise "Need to change this call to expand_reading_text to pass a role->variable hash"
448
- end
449
- rrs = role_sequence.all_role_ref_in_order
450
- variable_by_role = {}
451
- if step
452
- plays = step.all_play
453
- variable_by_role = plays.inject({}) { |h, play| h[play.role] = play.variable; h }
454
- end
455
- trace :subscript, "expanding '#{text}' with #{role_sequence.describe}" do
456
- text.gsub(/\{(\d)\}/) do
457
- role_ref = rrs[$1.to_i]
458
- # REVISIT: We may need to use the step's role_refs to expand the role players here, not the reading's one (extra adjectives?)
459
- player = player_by_role[role_ref.role]
460
- variable = variable_by_role[role_ref.role]
461
-
462
- play_name = variable && variable.role_name
463
- raise hell if player && player.is_a?(ActiveFacts::Metamodel::EntityType) && player.fact_type && !variable
464
- subscripted_player(role_ref, player && player.subscript, play_name, variable && variable.value) +
465
- objectification_verbalisation(variable)
466
- end
467
- end
468
- end
469
-
470
- def subscripted_player role_ref, subscript = nil, play_name = nil, value = nil
471
- prr = @player_by_role_ref[role_ref]
472
- subscript ||= prr.subscript if prr
473
- trace :subscript, "Need to apply subscript #{subscript} to #{role_ref.role.object_type.name}" if subscript
474
- object_type = role_ref.role.object_type
475
- (play_name ||
476
- [
477
- role_ref.leading_adjective,
478
- object_type.name,
479
- role_ref.trailing_adjective
480
- ].compact*' '
481
- ) +
482
- (value ? ' '+value.inspect : '') +
483
- (subscript ? "(#{subscript})" : '')
484
- end
485
-
486
- def expand_contracted_text(step, reading, role_refs = [])
487
- ' that ' +
488
- expand_reading_text(step, reading.text.sub(/\A\{\d\} /,''), reading.role_sequence, role_refs)
489
- end
490
-
491
- # Each query we wish to verbalise must first have had its players prepared.
492
- # Then, this prepares the query for verbalising:
493
- def prepare_query query
494
- @query = query
495
- return unless query
496
-
497
- @variables = query.all_variable.to_a.sort_by(&:ordinal)
498
-
499
- @steps = @variables.map(&:all_step).flatten.uniq
500
- @steps_by_variable = @variables.
501
- inject({}) do |h, var|
502
- var.all_step.each{|step| (h[var] ||= []) << step}
503
- h
504
- end
505
- end
506
-
507
- # De-index this step now that we've processed it:
508
- def step_completed(step)
509
- @steps.delete(step)
510
-
511
- step.all_play.each do |play|
512
- var = play.variable
513
- steps = @steps_by_variable[var]
514
- steps.delete(step)
515
- @steps_by_variable.delete(var) if steps.empty?
516
- end
517
- end
518
-
519
- def choose_step(next_var)
520
- next_steps = @steps_by_variable[next_var]
521
-
522
- # We need to emit each objectification before mentioning an object that plays a role in one, if possible
523
- # so that we don't wind up with an objectification as the only way to mention its name.
524
-
525
- # If we don't have a next_var against which we can contract,
526
- # so just use any step involving this node, or just any step.
527
- if next_steps
528
- if next_step = next_steps.detect { |ns| !ns.is_objectification_step }
529
- trace :query, "Chose new non-objectification step: #{next_step.describe}"
530
- return next_step
531
- end
532
- end
533
-
534
- if next_step = @steps.detect { |ns| !ns.is_objectification_step }
535
- trace :query, "Chose random non-objectification step: #{next_step.describe}"
536
- return next_step
537
- end
538
-
539
- next_step = @steps[0]
540
- if next_step
541
- trace :query, "Chose new random step from #{steps.size}: #{next_step.describe}"
542
- if next_step.is_objectification_step
543
- # if this objectification plays any roles (other than its FT roles) in remaining steps, use one of those first:
544
- fact_type = next_step.fact_type.implying_role.fact_type
545
- jn = [next_step.input_play.variable, next_step.output_play.variable].detect{|jn| jn.object_type == fact_type.entity_type}
546
- sr = @steps_by_variable[jn].reject{|t| r = t.fact_type.implying_role and r.fact_type == fact_type}
547
- next_step = sr[0] if sr.size > 0
548
- end
549
- return next_step
550
- end
551
- raise "Internal error: There are more steps here, but we failed to choose one"
552
- end
553
-
554
- # The step we just emitted (using the reading given) is contractable iff
555
- # the reading has the next_var's role player as the final text
556
- def node_contractable_against_reading(next_var, reading)
557
- reading &&
558
- # Find whether last role has no following text, and its ordinal
559
- (reading.text =~ /\{([0-9])\}$/) &&
560
- # This reading's RoleRef for that role:
561
- (role_ref = reading.role_sequence.all_role_ref_in_order[$1.to_i]) &&
562
- # was that RoleRef for the upcoming node?
563
- role_ref.role.object_type == next_var.object_type
564
- end
565
-
566
- def reading_starts_with_node(reading, next_var)
567
- reading.text =~ /^\{([0-9])\}/ and
568
- role_ref = reading.role_sequence.all_role_ref.detect{|rr| rr.ordinal == $1.to_i} and
569
- role_ref.role.object_type == next_var.object_type
570
- end
571
-
572
- # The last reading we emitted ended with the object type name for next_var.
573
- # Choose a step and a reading that can be contracted against that name
574
- def contractable_step(next_steps, next_var)
575
- next_reading = nil
576
- next_step =
577
- next_steps.detect do |js|
578
- next false if js.is_objectification_step or js.is_disallowed
579
- # If we find a reading here, it can be contracted against the previous one
580
- next_reading =
581
- js.fact_type.all_reading_by_ordinal.detect do |reading|
582
- # This step is contractable iff the FactType has a reading that starts with the role of next_var (no preceding text)
583
- reading_starts_with_node(reading, next_var)
584
- end
585
- next_reading
586
- end
587
- trace :query, "#{next_reading ? "'"+next_reading.expand+"'" : "No reading"} contracts against last node '#{next_var.object_type.name}'"
588
- return [next_step, next_reading]
589
- end
590
-
591
- def objectification_verbalisation(variable)
592
- return '' unless variable
593
- raise "Not fully re-implemented, should pass the variable instead of #{variable.inspect}" unless variable.is_a?(ActiveFacts::Metamodel::Variable)
594
- objectified_node = nil
595
- object_type = variable.object_type
596
- return '' unless object_type.is_a?(Metamodel::EntityType) # Not a entity type
597
- return '' unless object_type.fact_type # Not objectified
598
-
599
- objectification_step = variable.step
600
- return '' unless objectification_step
601
-
602
- steps = [objectification_step]
603
- step_completed(objectification_step)
604
-
605
- =begin
606
- while other_step =
607
- @steps.
608
- detect{|step|
609
- step.is_objectification_step and
610
- step.input_play.variable.object_type == object_type || step.output_play.variable.object_type == object_type
611
- }
612
- steps << other_step
613
- trace :query, "Emitting objectification step allows deleting #{other_step.describe}"
614
- step_completed(other_step)
615
- end
616
- =end
617
-
618
- # Find all references to roles in this objectified fact type which are relevant to the variables of these steps:
619
- player_by_role = {}
620
- steps.each do |step|
621
- step.all_play.to_a.map do |play|
622
- player_by_role[play.role] = @player_by_play[play]
623
- end
624
- end
625
-
626
- # 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
627
-
628
- reading = object_type.fact_type.preferred_reading
629
- " (in which #{expand_reading_text(objectification_step, reading.text, reading.role_sequence, player_by_role)})"
630
- end
631
-
632
- def elided_objectification(next_step, fact_type, last_is_contractable, next_var)
633
- if last_is_contractable
634
- # Choose a reading that's contractable against the previous step, if possible
635
- reading = fact_type.all_reading_by_ordinal.
636
- detect do |reading|
637
- # Only contract a negative reading if we want one
638
- (!next_step.is_disallowed || !reading.is_negative == !next_step.is_disallowed) and
639
- reading_starts_with_node(reading, next_var)
640
- end
641
- end
642
- last_is_contractable = false unless reading
643
- reading ||= fact_type.preferred_reading(next_step.is_disallowed) || fact_type.preferred_reading
644
-
645
- # Find which role occurs last in the reading, and which Variable is attached
646
- reading.text =~ /\{(\d)\}[^{]*\Z/
647
- last_role_ref = reading.role_sequence.all_role_ref_in_order[$1.to_i]
648
- exit_node = @variables.detect{|jn| jn.all_play.detect{|play| play.role == last_role_ref.role}}
649
- exit_step = nil
650
-
651
- trace :query, "Stepping over an objectification to #{exit_node.object_type.name} requires eliding the other implied steps" do
652
- count = 0
653
- while other_step =
654
- @steps.
655
- detect{|js|
656
- trace :query, "Considering step '#{js.fact_type.default_reading}'"
657
- next unless js.is_objectification_step
658
-
659
- # REVISIT: This test is too weak: We need to ensure that the same variables are involved, not just the same object types:
660
- next unless js.input_play.variable.object_type == fact_type.entity_type || js.output_play.variable.object_type == fact_type.entity_type
661
- exit_step = js if js.output_play.variable == exit_node
662
- true
663
- }
664
- trace :query, "Emitting objectified FT allows deleting #{other_step.describe}"
665
- step_completed(other_step)
666
- # 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
667
- end
668
- end
669
-
670
- [ reading, exit_step ? exit_step.input_play.variable : exit_node, exit_step, last_is_contractable]
671
- end
672
-
673
- def verbalise_query query
674
- prepare_query query
675
- readings = ''
676
- next_var = @role_refs[0].play.variable # Choose a place to start
677
- last_is_contractable = false
678
-
679
- trace :query, "Verbalising query" do
680
- if trace(:query)
681
- trace :query, "variables:" do
682
- @variables.each do |var|
683
- trace :query, var.describe
684
- end
685
- end
686
- trace :query, "steps:" do
687
- @steps.each do |step|
688
- trace :query, step.describe
689
- end
690
- end
691
- end
692
-
693
- until @steps.empty?
694
- next_reading = nil
695
- # Choose amongst all remaining steps we can take from the next node, if any
696
- next_steps = @steps_by_variable[next_var]
697
- trace :query, "Next Steps from #{next_var.describe} are #{(next_steps||[]).map{|js| js.describe }.inspect}"
698
-
699
- # See if we can find a next step that contracts against the last (if any):
700
- next_step = nil
701
- if last_is_contractable && next_steps
702
- next_step, next_reading = *contractable_step(next_steps, next_var)
703
- end
704
-
705
- if next_step
706
- trace :query, "Chose #{next_step.describe} because it's contractable against last node #{next_var.object_type.name} using #{next_reading.expand}"
707
-
708
- player_by_role =
709
- next_step.all_play.inject({}) {|h, play| h[play.role] = @player_by_play[play]; h }
710
- raise "REVISIT: Needed a negated reading here" if !next_reading.is_negative != !next_step.is_disallowed
711
- raise "REVISIT: Need to emit 'maybe' here" if next_step.is_optional
712
- readings += expand_contracted_text(next_step, next_reading, player_by_role)
713
- step_completed(next_step)
714
- else
715
- next_step = choose_step(next_var) if !next_step
716
-
717
- player_by_role =
718
- next_step.all_play.inject({}) {|h, play| h[play.role] = @player_by_play[play]; h }
719
-
720
- if next_step.is_unary_step
721
- # Objectified unaries get emitted as unaries, not as objectifications:
722
- role = next_step.input_play.role
723
- role = role.fact_type.implying_role if role.fact_type.is_a?(LinkFactType)
724
- next_reading = role.fact_type.preferred_reading(next_step.is_disallowed) || role.fact_type.preferred_reading
725
- readings += " and " unless readings.empty?
726
- readings += "it is not the case that " if !next_step.is_disallowed != !next_reading.is_negative
727
- raise "REVISIT: Need to emit 'maybe' here" if next_step.is_optional
728
- readings += expand_reading_text(next_step, next_reading.text, next_reading.role_sequence, player_by_role)
729
- step_completed(next_step)
730
- elsif next_step.is_objectification_step
731
- fact_type = next_step.fact_type.implying_role.fact_type
732
-
733
- # This objectification step is over an implicit fact type, so player_by_role won't have all the players
734
- # Add the players of other roles associated with steps from this objectified player.
735
- objectified_node = next_step.input_play.variable
736
- raise "Assumption violated that the objectification is the input play" unless objectified_node.object_type.fact_type
737
- objectified_node.all_step.map do |other_step|
738
- other_step.all_play.map do |play|
739
- player_by_role[play.role] = @player_by_play[play]
740
- end
741
- end
742
-
743
- if last_is_contractable and next_var.object_type.is_a?(EntityType) and next_var.object_type.fact_type == fact_type
744
- # The last reading we emitted ended with the name of the objectification of this fact type, so we can contract the objectification
745
- # REVISIT: Do we need to use player_by_role here (if this objectification is traversed twice and so is subscripted)
746
- readings += objectification_verbalisation(fact_type.entity_type)
747
- else
748
- # This objectified fact type does not need to be made explicit.
749
- negation = next_step.is_disallowed
750
- next_reading, next_var, next_step, last_is_contractable =
751
- *elided_objectification(next_step, fact_type, last_is_contractable, next_var)
752
- if last_is_contractable
753
- raise "REVISIT: Need to emit 'maybe' here" if next_step and next_step.is_optional
754
- readings += expand_contracted_text(next_step, next_reading, player_by_role)
755
- else
756
- readings += " and " unless readings.empty?
757
- readings += "it is not the case that " if !!negation != !!next_reading.is_negative
758
- raise "REVISIT: Need to emit 'maybe' here" if next_step and next_step.is_optional
759
- readings += expand_reading_text(next_step, next_reading.text, next_reading.role_sequence, player_by_role)
760
- end
761
- # No need to continue if we just deleted the last step
762
- break if @steps.empty?
763
-
764
- end
765
- else
766
- fact_type = next_step.fact_type
767
- # Prefer a reading that starts with the player of next_var
768
- next_reading = fact_type.all_reading_by_ordinal.
769
- detect do |reading|
770
- (!next_step.is_disallowed || !reading.is_negative == !next_step.is_disallowed) and
771
- reading_starts_with_node(reading, next_var)
772
- end || fact_type.preferred_reading(next_step.is_disallowed)
773
- # REVISIT: If this step and reading has role references with adjectives, we need to expand using those
774
- readings += " and " unless readings.empty?
775
- readings += "it is not the case that " if !next_step.is_disallowed != !next_reading.is_negative
776
- raise "REVISIT: Need to emit 'maybe' here" if next_step and next_step.is_optional
777
- readings += expand_reading_text(next_step, next_reading.text, next_reading.role_sequence, player_by_role)
778
- step_completed(next_step)
779
- end
780
- end
781
-
782
- if next_step
783
- # Continue from this step with the node having the most steps remaining
784
- input_steps = @steps_by_variable[input_var = next_step.input_play.variable] || []
785
- output_play = next_step.output_plays.last
786
- output_steps = (output_play && (output_var = output_play.variable) && @steps_by_variable[output_var]) || []
787
-
788
- next_var = input_steps.size > output_steps.size ? input_var : output_var
789
- # Prepare for possible contraction following:
790
- last_is_contractable = next_reading && node_contractable_against_reading(next_var, next_reading)
791
- else
792
- # This shouldn't happen, but an elided objectification that had missing steps can cause it. Survive:
793
- next_var = (steps[0].input_play || steps[0].output_plays.last).variable
794
- last_is_contractable = false
795
- end
796
-
797
- end
798
- end
799
- readings
800
- end
801
- end
802
-
803
- end
804
- end