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,421 +0,0 @@
1
- module ActiveFacts
2
- module CQL
3
- class Compiler < ActiveFacts::CQL::Parser
4
-
5
- class Query < ObjectType # A fact type which is objectified (whether derived or not) is also an ObjectType
6
- attr_reader :context # Exposed for testing purposes
7
- attr_reader :conditions
8
-
9
- def initialize name, conditions = nil, returning = nil
10
- super name
11
- @conditions = conditions
12
- @returning = returning || []
13
- end
14
-
15
- def to_s
16
- inspect
17
- end
18
-
19
- def inspect
20
- "Query: " +
21
- if @conditions.empty?
22
- ''
23
- else
24
- 'where ' + @conditions.map{|c| ((j=c.conjunction) ? j+' ' : '') + c.inspect}*' '
25
- end
26
- # REVISIT: @returning = returning
27
- end
28
-
29
- def prepare_roles clauses = nil
30
- trace :binding, "preparing roles" do
31
- @context ||= CompilationContext.new(@vocabulary)
32
- @context.bind clauses||[], @conditions, @returning
33
- end
34
- end
35
-
36
- def compile
37
- # Match roles with players, and match clauses with existing fact types
38
- prepare_roles unless @context
39
-
40
- @context.left_contraction_allowed = true
41
- match_condition_fact_types
42
-
43
- # Build the query:
44
- unless @conditions.empty? and @returning.empty?
45
- trace :query, "building query for derived fact type (returning #{@returning}) with #{@conditions.size} conditions: (#{@conditions.map{|c|c.inspect}*', '})" do
46
- @query = build_variables(@conditions.flatten)
47
- @roles_by_binding = build_all_steps(@conditions)
48
- @query.validate
49
- @query
50
- end
51
- end
52
- @context.left_contraction_allowed = false
53
- @query
54
- end
55
-
56
- def match_condition_fact_types
57
- @conditions.each do |condition|
58
- trace :projection, "matching condition fact_type #{condition.inspect}" do
59
- fact_type = condition.match_existing_fact_type @context
60
- raise "Unrecognised fact type #{condition.inspect} in #{self.class}" unless fact_type
61
- end
62
- end
63
- end
64
-
65
- def detect_projection_by_equality condition
66
- return false unless condition.is_a?(Comparison)
67
- if is_projected_role(condition.e1)
68
- condition.project :left
69
- elsif is_projected_role(condition.e2)
70
- condition.project :right
71
- end
72
- end
73
-
74
- def is_projected_role(rr)
75
- false
76
- end
77
- end
78
-
79
- class FactType < ActiveFacts::CQL::Compiler::Query
80
- attr_reader :fact_type
81
- attr_reader :clauses
82
- attr_writer :name
83
- attr_writer :pragmas
84
-
85
- def initialize name, clauses, conditions = nil, returning = nil
86
- super name, conditions, returning
87
- @clauses = clauses
88
- if ec = @clauses.detect{|r| r.is_equality_comparison}
89
- @clauses.delete(ec)
90
- @conditions.unshift(ec)
91
- end
92
- end
93
-
94
- def compile
95
- # Process:
96
- # * Identify all role players (must be done for both clauses and conditions BEFORE matching clauses)
97
- # * Match up the players in all @clauses
98
- # - Be aware of multiple roles with the same player, and bind tight/loose using subscripts/role_names/adjectives
99
- # - Reject the fact type unless all @clauses match
100
- # * Find any existing fact type that matches any clause, or make a new one
101
- # * Add each clause that doesn't already exist in the fact type
102
- # * Create any ring constraint(s)
103
- # * Create embedded presence constraints
104
- # * If fact type has no identifier, arrange to create the implicit one (before first use?)
105
- # * Objectify the fact type if @name
106
- #
107
-
108
- # Prepare to objectify the fact type (so readings for implicit fact types can be created)
109
- if @name
110
- entity_type = @vocabulary.valid_entity_type_name(@name)
111
- raise "You can't objectify #{@name}, it already exists" if entity_type
112
- @entity_type = @constellation.EntityType(@vocabulary, @name, :fact_type => @fact_type, :concept => :new)
113
- end
114
-
115
- prepare_roles @clauses
116
-
117
- # REVISIT: Compiling the conditions here make it impossible to define a self-referential (transitive) query.
118
- return super if @clauses.empty? # It's a query
119
-
120
- return true unless @clauses.size > 0 # Nothing interesting was said.
121
-
122
- if @entity_type
123
- # Extract readings for implicit fact types
124
- @implicit_readings, @clauses =
125
- @clauses.partition do |clause|
126
- clause.refs.size == 2 and clause.refs.detect{|ref| ref.player == @entity_type}
127
- end
128
- end
129
-
130
- # See if any existing fact type is being invoked (presumably to objectify or extend it)
131
- @fact_type = check_compatibility_of_matched_clauses
132
- verify_matching_roles # All clauses of a fact type must have the same roles
133
-
134
- if !@fact_type
135
- # Make a new fact type:
136
- first_clause = @clauses[0]
137
- @fact_type = first_clause.make_fact_type(@vocabulary)
138
- first_clause.make_reading(@vocabulary, @fact_type)
139
- first_clause.make_embedded_constraints vocabulary
140
- @fact_type.create_implicit_fact_type_for_unary if @fact_type.all_role.size == 1 && !@name
141
- @existing_clauses = [first_clause]
142
- elsif (n = @clauses.size - @existing_clauses.size) > 0
143
- raise "Cannot extend a negated fact type" if @existing_clauses.detect {|clause| clause.certainty == false }
144
- trace :binding, "Extending existing fact type with #{n} new readings"
145
- end
146
-
147
- # Now make any new readings:
148
- new_clauses = @clauses - @existing_clauses
149
- new_clauses.each do |clause|
150
- clause.make_reading(@vocabulary, @fact_type)
151
- clause.make_embedded_constraints vocabulary
152
- end
153
-
154
- # If a clause matched but the match left extra adjectives, we need to make a new RoleSequence for them:
155
- @existing_clauses.each do |clause|
156
- clause.adjust_for_match
157
- # Add any new constraints that we found in the match (presence, ring, etc)
158
- clause.make_embedded_constraints(vocabulary)
159
- end
160
-
161
- if @name
162
- # Objectify the fact type:
163
- @entity_type.fact_type = @fact_type
164
- if @fact_type.entity_type and @name != @fact_type.entity_type.name
165
- raise "Cannot objectify fact type as #{@name} and as #{@fact_type.entity_type.name}"
166
- end
167
- ifts = @entity_type.create_implicit_fact_types
168
- create_implicit_readings(ifts)
169
- if @pragmas
170
- @entity_type.is_independent = true if @pragmas.delete('independent')
171
- end
172
- end
173
- @pragmas.each do |p|
174
- @constellation.ConceptAnnotation(:concept => (@entity_type||@fact_type).concept, :mapping_annotation => p)
175
- end if @pragmas
176
-
177
- @clauses.each do |clause|
178
- next unless clause.context_note
179
- clause.context_note.compile(@constellation, @fact_type)
180
- end
181
-
182
- # REVISIT: This isn't the thing to do long term; it needs to be added later only if we find no other constraint
183
- make_default_identifier_for_fact_type if @conditions.empty?
184
-
185
- # Compile the conditions:
186
- super
187
- unless @conditions.empty?
188
- @clauses.each do |clause|
189
- project_clause_roles(clause)
190
- end
191
- end
192
-
193
- @fact_type
194
- end
195
-
196
- def create_implicit_readings(ifts)
197
- @implicit_readings.each do |clause|
198
- #next false unless clause.refs.size == 2 and clause.refs.detect{|r| r.role.object_type == @entity_type }
199
- other_ref = clause.refs.detect{|ref| ref.player != @entity_type}
200
- ift = ifts.detect do |ift|
201
- other_ref.binding.refs.map{|r| r.role}.include?(ift.implying_role)
202
- end
203
- next unless ift
204
- # This clause is a reading for the implicit LinkFactType ift
205
- i = 0
206
- reading_text = clause.phrases.map do |phrase|
207
- next phrase if String === phrase
208
- "{#{(i += 1)-1}}"
209
- end*' '
210
- ir = ActiveFacts::Metamodel::LinkFactType::ImplicitReading
211
- irrs = ir::ImplicitReadingRoleSequence
212
- irrf = irrs::ImplicitReadingRoleRef
213
- reading = ir.new(ift, reading_text)
214
- ift.add_reading reading
215
- end
216
- end
217
-
218
- def project_clause_roles(clause)
219
- # Attach the clause's role references to the projected roles of the query
220
- clause.refs.each_with_index do |ref, i|
221
- role, play = @roles_by_binding[ref.binding]
222
- raise "#{ref} must be a role projected from the conditions" unless role
223
- raise "#{ref} has already-projected play!" if play.role_ref
224
- ref.role_ref.play = play
225
- end
226
- end
227
-
228
- # A Comparison in the conditions which projects a role is not treated as a comparison, just as projection
229
- def is_projected_role(rr)
230
- # rr is a RoleRef on one side of the comparison.
231
- # If its binding contains a reference from our readings, it's projected.
232
- rr.binding.refs.detect do |ref|
233
- @readings.include?(ref.reading)
234
- end
235
- end
236
-
237
- def check_compatibility_of_matched_clauses
238
- # REVISIT: If we have conditions, we must match all given clauses exactly (no side-effects)
239
- @existing_clauses = @clauses.
240
- select{ |clause| clause.match_existing_fact_type @context }.
241
- # subtyping match is not allowed for fact type extension:
242
- reject{ |clause| clause.side_effects.role_side_effects.detect{|se| se.common_supertype } }.
243
- sort_by{ |clause| clause.side_effects.cost }
244
- fact_types = @existing_clauses.map{ |clause| clause.fact_type }.uniq.compact
245
-
246
- return nil if fact_types.empty? # There are no matched fact types
247
-
248
- if @clauses.size == 1 && @existing_clauses[0].side_effects.cost != 0
249
- trace :matching, "There's only a single clause, but it's not an exact match"
250
- return nil
251
- end
252
-
253
- if (fact_types.size > 1)
254
- # There must be only one fact type with exact matches:
255
- if @existing_clauses[0].side_effects.cost != 0 or
256
- @existing_clauses.detect{|r| r.fact_type != fact_types[0] && r.side_effects.cost == 0 }
257
- raise "Clauses match different existing fact types '#{fact_types.map{|ft| ft.preferred_reading.expand}*"', '"}'"
258
- end
259
- # Try to make false-matched clauses match the chosen one instead
260
- @existing_clauses.reject!{|r| r.fact_type != fact_types[0] }
261
- end
262
- fact_types[0]
263
- end
264
-
265
- def make_default_identifier_for_fact_type(prefer = true)
266
- # Non-objectified unaries don't need a PI:
267
- return if @fact_type.all_role.size == 1 && !@fact_type.entity_type
268
-
269
- # It's possible that this fact type is objectified and inherits identification through a supertype.
270
- return if @fact_type.entity_type and @fact_type.entity_type.all_type_inheritance_as_subtype.detect{|ti| ti.provides_identification}
271
-
272
- # If it's a non-objectified binary and there's an alethic uniqueness constraint over the fact type already, we're done
273
- return if !@fact_type.entity_type &&
274
- @fact_type.all_role.size == 2 &&
275
- @fact_type.all_role.
276
- detect do |r|
277
- r.all_role_ref.detect do |rr|
278
- rr.role_sequence.all_presence_constraint.detect do |pc|
279
- pc.max_frequency == 1 && !pc.enforcement
280
- end
281
- end
282
- end
283
-
284
- # If there's an existing presence constraint that can be converted into a PC, do that:
285
- @clauses.each do |clause|
286
- ref = clause.refs[-1] or next
287
- epc = ref.embedded_presence_constraint or next
288
- epc.max_frequency == 1 or next
289
- next if epc.enforcement
290
- trace :constraint, "Converting UC into PI for #{@fact_type.entity_type.name}"
291
- epc.is_preferred_identifier = true
292
- return
293
- end
294
-
295
- # We need to check uniqueness constraints after processing the whole vocabulary
296
- # raise "Fact type must be named as it has no identifying uniqueness constraint" unless @name || @fact_type.all_role.size == 1
297
- trace :constraint, "Need to check #{@fact_type.default_reading.inspect} for a uniqueness constraint"
298
- fact_type.check_and_add_spanning_uniqueness_constraint = proc do
299
- trace :constraint, "Checking #{@fact_type.default_reading.inspect} for a uniqueness constraint"
300
- existing_pc = nil
301
- found = @fact_type.all_role.
302
- detect do |role|
303
- role.all_role_ref.detect do |rr|
304
- # This RoleSequence, to be relevant, must only reference roles of this fact type
305
- rr.role_sequence.all_role_ref.all? {|rr2| rr2.role.fact_type == @fact_type} and
306
- # The RoleSequence must have at least one uniqueness constraint
307
- rr.role_sequence.all_presence_constraint.detect do |pc|
308
- if pc.max_frequency == 1
309
- existing_pc = pc
310
- end
311
- end
312
- end
313
- end
314
- true # A place for a breakpoint
315
-
316
- if !found
317
- # There's no existing uniqueness constraint over the roles of this fact type. Add one
318
- pc = @constellation.PresenceConstraint(
319
- :new,
320
- :vocabulary => @vocabulary,
321
- :name => @fact_type.entity_type ? @fact_type.entity_type.name+"PK" : '',
322
- :role_sequence => (rs = @fact_type.preferred_reading.role_sequence),
323
- :max_frequency => 1,
324
- :is_preferred_identifier => true # (prefer || !!@fact_type.entity_type)
325
- )
326
- pc.concept.topic = @fact_type.concept.topic
327
- trace :constraint, "Made new fact type implicit PC GUID=#{pc.concept.guid} #{pc.name} min=nil max=1 over #{rs.describe}"
328
- elsif pc
329
- trace :constraint, "Will rely on existing UC GUID=#{pc.concept.guid} #{pc.name} to be used as PI over #{rs.describe}"
330
- end
331
- end
332
- end
333
-
334
- def has_more_adjectives(less, more)
335
- return false if less.leading_adjective && less.leading_adjective != more.leading_adjective
336
- return false if less.trailing_adjective && less.trailing_adjective != more.trailing_adjective
337
- return true
338
- end
339
-
340
- def verify_matching_roles
341
- refs_by_clause_and_key = {}
342
- clauses_by_refs =
343
- @clauses.inject({}) do |hash, clause|
344
- keys = clause.refs.map do |ref|
345
- key = ref.key.compact
346
- refs_by_clause_and_key[[clause, key]] = ref
347
- key
348
- end.sort_by{|a| a.map{|k|k.to_s}}
349
- raise "Fact types may not have duplicate roles" if keys.uniq.size < keys.size
350
- (hash[keys] ||= []) << clause
351
- hash
352
- end
353
-
354
- if clauses_by_refs.size != 1 and @conditions.empty?
355
- # Attempt loose binding here; it might merge some Compiler::References to share the same Variables
356
- variants = clauses_by_refs.keys
357
- (clauses_by_refs.size-1).downto(1) do |m| # Start with the last one
358
- 0.upto(m-1) do |l| # Try to rebind onto any lower one
359
- common = variants[m]&variants[l]
360
- clauses_l = clauses_by_refs[variants[l]]
361
- clauses_m = clauses_by_refs[variants[m]]
362
- l_keys = variants[l]-common
363
- m_keys = variants[m]-common
364
- trace :binding, "Try to collapse variant #{m} onto #{l}; diffs are #{l_keys.inspect} -> #{m_keys.inspect}"
365
- rebindings = 0
366
- l_keys.each_with_index do |l_key, i|
367
- # Find possible rebinding candidates; there must be exactly one.
368
- candidates = []
369
- (0...m_keys.size).each do |j|
370
- m_key = m_keys[j]
371
- l_ref = refs_by_clause_and_key[[clauses_l[0], l_key]]
372
- m_ref = refs_by_clause_and_key[[clauses_m[0], m_key]]
373
- trace :binding, "Can we match #{l_ref.inspect} (#{i}) with #{m_ref.inspect} (#{j})?"
374
- next if m_ref.player != l_ref.player
375
- if has_more_adjectives(m_ref, l_ref)
376
- trace :binding, "can rebind #{m_ref.inspect} to #{l_ref.inspect}"
377
- candidates << [m_ref, l_ref]
378
- elsif has_more_adjectives(l_ref, m_ref)
379
- trace :binding, "can rebind #{l_ref.inspect} to #{m_ref.inspect}"
380
- candidates << [l_ref, m_ref]
381
- end
382
- end
383
-
384
- # trace :binding, "found #{candidates.size} rebinding candidates for this role"
385
- trace :binding, "rebinding is ambiguous so not attempted" if candidates.size > 1
386
- if (candidates.size == 1)
387
- candidates[0][0].rebind_to(@context, candidates[0][1])
388
- rebindings += 1
389
- end
390
-
391
- end
392
- if (rebindings == l_keys.size)
393
- # Successfully rebound this fact type
394
- trace :binding, "Successfully rebound clauses #{clauses_l.map{|r|r.inspect}*'; '} on to #{clauses_m.map{|r|r.inspect}*'; '}"
395
- break
396
- else
397
- # No point continuing, we failed on this one.
398
- raise "All readings in a fact type definition must have matching role players, compare (#{
399
- clauses_by_refs.keys.map do |keys|
400
- keys.map{|key| key*'-' }*", "
401
- end*") with ("
402
- })"
403
- end
404
-
405
- end
406
- end
407
- # else all clauses already matched
408
- end
409
- end
410
-
411
- def inspect
412
- s = super
413
- "FactType: #{@conditions.size > 0 ? super+' ' : '' }#{@clauses.inspect}" +
414
- (@pragmas && @pragmas.size > 0 ? ", pragmas [#{@pragmas.flatten.sort*','}]" : '')
415
-
416
- # REVISIT: @returning = returning
417
- end
418
- end
419
- end
420
- end
421
- end
@@ -1,106 +0,0 @@
1
- module ActiveFacts
2
- module CQL
3
- class Compiler < ActiveFacts::CQL::Parser
4
- class Definition
5
- # Make a Variable for every binding present in these clauses
6
- def build_variables(clauses_list)
7
- trace :query, "Building variables" do
8
- query = @constellation.Query(:new)
9
- all_bindings_in_clauses(clauses_list).
10
- each do |binding|
11
- trace :query, "Creating variable #{query.all_variable.size} for #{binding.inspect}"
12
- binding.variable = @constellation.Variable(query, query.all_variable.size, :object_type => binding.player)
13
- if literal = binding.refs.detect{|r| r.literal}
14
- if literal.kind_of?(ActiveFacts::CQL::Compiler::Reference)
15
- # REVISIT: Fix this crappy ad-hoc polymorphism hack
16
- literal = literal.literal
17
- end
18
- unit = @constellation.Unit.detect{|k, v| [v.name, v.plural_name].include? literal.unit} if literal.unit
19
- binding.variable.value = [literal.literal.to_s, literal.literal.is_a?(String), unit]
20
- end
21
- end
22
- query
23
- end
24
- end
25
-
26
- def build_all_steps(clauses_list)
27
- roles_by_binding = {}
28
- trace :query, "Building steps" do
29
- clauses_list.each do |clause|
30
- build_step(clause, roles_by_binding)
31
- end
32
- end
33
- roles_by_binding
34
- end
35
-
36
- def build_step clause, roles_by_binding = {}, parent_variable = nil
37
- return unless clause.refs.size > 0 # Empty clause... really?
38
-
39
- step = @constellation.Step(
40
- :guid => :new,
41
- :fact_type => clause.fact_type,
42
- :alternative_set => nil,
43
- :is_disallowed => clause.certainty == false,
44
- :is_optional => clause.certainty == nil
45
- )
46
-
47
- trace :query, "Creating Plays for #{clause.inspect} with #{clause.refs.size} refs" do
48
- is_input = true
49
- clause.refs.each do |ref|
50
- # These refs are the Compiler::References, which have associated Metamodel::RoleRefs,
51
- # but we need to create Plays for those roles.
52
- # REVISIT: Plays may need to save residual_adjectives
53
- binding = ref.binding
54
- role = (ref && ref.role) || (ref.role_ref && ref.role_ref.role)
55
-
56
- objectification_step = nil
57
- if ref.nested_clauses
58
- ref.nested_clauses.each do |nested_clause|
59
- objectification_step = build_step nested_clause, roles_by_binding
60
- if ref.binding.player.is_a?(ActiveFacts::Metamodel::EntityType) and
61
- ref.binding.player.fact_type == nested_clause.fact_type
62
- objectification_step.objectification_variable = binding.variable
63
- end
64
- end
65
- end
66
- if clause.is_naked_object_type
67
- raise "#{self} lacks a proper objectification" if clause.refs[0].nested_clauses and !objectification_step
68
- return objectification_step
69
- end
70
-
71
- if binding.variable.object_type != role.object_type # Type mismatch
72
- if binding.variable.object_type.common_supertype(role.object_type)
73
- # REVISIT: there's an implicit subtyping step here, create it; then always raise the error here.
74
- # I don't want to do this for now because the verbaliser will always verbalise all steps.
75
- raise "Disallowing implicit subtyping step from #{role.object_type.name} to #{binding.variable.object_type.name} in #{clause.fact_type.default_reading.inspect}"
76
- end
77
- raise "A #{role.object_type.name} cannot satisfy #{binding.variable.object_type.name} in #{clause.fact_type.default_reading.inspect}"
78
- end
79
-
80
- trace :query, "Creating Play for #{ref.inspect}"
81
- play = @constellation.Play(:step => step, :role => role, :variable => binding.variable)
82
- play.is_input = is_input
83
- is_input = false
84
-
85
- roles_by_binding[binding] = [role, play]
86
- end
87
- end
88
-
89
- step
90
- end
91
-
92
- # Return the unique array of all bindings in these clauses, including in objectification steps
93
- def all_bindings_in_clauses clauses
94
- clauses.map do |clause|
95
- clause.refs.map do |ref|
96
- raise "Binding reference #{ref.inspect} is not bound to a binding" unless ref.binding
97
- [ref.binding] + (ref.nested_clauses ? all_bindings_in_clauses(ref.nested_clauses) : [])
98
- end
99
- end.
100
- flatten.
101
- uniq
102
- end
103
- end
104
- end
105
- end
106
- end