activefacts 0.8.9 → 0.8.10

Sign up to get free protection for your applications and to get access to all the features.
Files changed (120) hide show
  1. data/.gemtest +0 -0
  2. data/Manifest.txt +28 -33
  3. data/Rakefile +11 -12
  4. data/bin/cql +90 -46
  5. data/examples/CQL/Blog.cql +2 -1
  6. data/examples/CQL/CompanyDirectorEmployee.cql +2 -2
  7. data/examples/CQL/Death.cql +1 -1
  8. data/examples/CQL/Diplomacy.cql +9 -9
  9. data/examples/CQL/Genealogy.cql +3 -2
  10. data/examples/CQL/Insurance.cql +10 -7
  11. data/examples/CQL/JoinEquality.cql +2 -2
  12. data/examples/CQL/Marriage.cql +1 -1
  13. data/examples/CQL/Metamodel.cql +73 -53
  14. data/examples/CQL/MetamodelNext.cql +89 -67
  15. data/examples/CQL/OneToOnes.cql +2 -2
  16. data/examples/CQL/ServiceDirector.cql +10 -5
  17. data/examples/CQL/Supervision.cql +3 -3
  18. data/examples/CQL/Tests.Test5.Load.cql +1 -1
  19. data/examples/CQL/Warehousing.cql +4 -2
  20. data/lib/activefacts/cql/CQLParser.treetop +26 -60
  21. data/lib/activefacts/cql/Context.treetop +12 -2
  22. data/lib/activefacts/cql/Expressions.treetop +14 -30
  23. data/lib/activefacts/cql/FactTypes.treetop +165 -110
  24. data/lib/activefacts/cql/Language/English.treetop +167 -54
  25. data/lib/activefacts/cql/LexicalRules.treetop +16 -2
  26. data/lib/activefacts/cql/{Concepts.treetop → ObjectTypes.treetop} +36 -37
  27. data/lib/activefacts/cql/Terms.treetop +57 -27
  28. data/lib/activefacts/cql/ValueTypes.treetop +39 -13
  29. data/lib/activefacts/cql/compiler.rb +5 -3
  30. data/lib/activefacts/cql/compiler/{reading.rb → clause.rb} +407 -285
  31. data/lib/activefacts/cql/compiler/constraint.rb +178 -275
  32. data/lib/activefacts/cql/compiler/entity_type.rb +73 -64
  33. data/lib/activefacts/cql/compiler/expression.rb +418 -0
  34. data/lib/activefacts/cql/compiler/fact.rb +146 -145
  35. data/lib/activefacts/cql/compiler/fact_type.rb +197 -80
  36. data/lib/activefacts/cql/compiler/join.rb +159 -0
  37. data/lib/activefacts/cql/compiler/shared.rb +51 -23
  38. data/lib/activefacts/cql/compiler/value_type.rb +56 -2
  39. data/lib/activefacts/cql/parser.rb +15 -4
  40. data/lib/activefacts/generate/absorption.rb +7 -7
  41. data/lib/activefacts/generate/cql.rb +100 -37
  42. data/lib/activefacts/generate/oo.rb +28 -51
  43. data/lib/activefacts/generate/ordered.rb +60 -36
  44. data/lib/activefacts/generate/ruby.rb +6 -6
  45. data/lib/activefacts/generate/sql/server.rb +4 -4
  46. data/lib/activefacts/input/orm.rb +71 -53
  47. data/lib/activefacts/persistence.rb +1 -1
  48. data/lib/activefacts/persistence/columns.rb +27 -23
  49. data/lib/activefacts/persistence/foreignkey.rb +6 -6
  50. data/lib/activefacts/persistence/index.rb +17 -17
  51. data/lib/activefacts/persistence/{concept.rb → object_type.rb} +9 -9
  52. data/lib/activefacts/persistence/reference.rb +61 -36
  53. data/lib/activefacts/persistence/tables.rb +61 -59
  54. data/lib/activefacts/support.rb +54 -29
  55. data/lib/activefacts/version.rb +1 -1
  56. data/lib/activefacts/vocabulary/extensions.rb +99 -54
  57. data/lib/activefacts/vocabulary/metamodel.rb +43 -37
  58. data/lib/activefacts/vocabulary/verbaliser.rb +134 -109
  59. data/spec/absorption_spec.rb +8 -8
  60. data/spec/cql/comparison_spec.rb +91 -0
  61. data/spec/cql/contractions_spec.rb +251 -0
  62. data/spec/cql/entity_type_spec.rb +319 -0
  63. data/spec/cql/expressions_spec.rb +63 -0
  64. data/spec/cql/fact_type_matching_spec.rb +283 -0
  65. data/spec/cql/french_spec.rb +21 -0
  66. data/spec/cql/parser/bad_literals_spec.rb +86 -0
  67. data/spec/cql/parser/constraints_spec.rb +19 -0
  68. data/spec/cql/parser/entity_types_spec.rb +106 -0
  69. data/spec/cql/parser/expressions_spec.rb +179 -0
  70. data/spec/cql/parser/fact_types_spec.rb +41 -0
  71. data/spec/cql/parser/literals_spec.rb +312 -0
  72. data/spec/cql/parser/pragmas_spec.rb +89 -0
  73. data/spec/cql/parser/value_types_spec.rb +42 -0
  74. data/spec/cql/role_matching_spec.rb +147 -0
  75. data/spec/cql/samples_spec.rb +9 -9
  76. data/spec/cql_cql_spec.rb +1 -1
  77. data/spec/cql_dm_spec.rb +116 -0
  78. data/spec/cql_mysql_spec.rb +1 -1
  79. data/spec/cql_ruby_spec.rb +1 -1
  80. data/spec/cql_sql_spec.rb +3 -3
  81. data/spec/cql_symbol_tables_spec.rb +30 -30
  82. data/spec/cqldump_spec.rb +4 -4
  83. data/spec/helpers/array_matcher.rb +32 -27
  84. data/spec/helpers/diff_matcher.rb +6 -26
  85. data/spec/helpers/file_matcher.rb +41 -32
  86. data/spec/helpers/parse_to_ast_matcher.rb +76 -0
  87. data/spec/helpers/string_matcher.rb +32 -31
  88. data/spec/norma_cql_spec.rb +1 -1
  89. data/spec/norma_ruby_spec.rb +1 -1
  90. data/spec/norma_ruby_sql_spec.rb +1 -1
  91. data/spec/norma_sql_spec.rb +3 -1
  92. data/spec/norma_tables_spec.rb +1 -1
  93. data/spec/ruby_api_spec.rb +23 -0
  94. data/spec/spec_helper.rb +5 -4
  95. metadata +66 -66
  96. data/examples/CQL/OrienteeringER.cql +0 -58
  97. data/lib/activefacts/api.rb +0 -44
  98. data/lib/activefacts/api/concept.rb +0 -410
  99. data/lib/activefacts/api/constellation.rb +0 -128
  100. data/lib/activefacts/api/entity.rb +0 -256
  101. data/lib/activefacts/api/instance.rb +0 -60
  102. data/lib/activefacts/api/instance_index.rb +0 -80
  103. data/lib/activefacts/api/numeric.rb +0 -167
  104. data/lib/activefacts/api/role.rb +0 -80
  105. data/lib/activefacts/api/role_proxy.rb +0 -70
  106. data/lib/activefacts/api/role_values.rb +0 -117
  107. data/lib/activefacts/api/standard_types.rb +0 -87
  108. data/lib/activefacts/api/support.rb +0 -65
  109. data/lib/activefacts/api/value.rb +0 -135
  110. data/lib/activefacts/api/vocabulary.rb +0 -82
  111. data/spec/api/autocounter.rb +0 -82
  112. data/spec/api/constellation.rb +0 -130
  113. data/spec/api/entity_type.rb +0 -103
  114. data/spec/api/instance.rb +0 -461
  115. data/spec/api/roles.rb +0 -124
  116. data/spec/api/value_type.rb +0 -112
  117. data/spec/api_spec.rb +0 -13
  118. data/spec/cql/matching_spec.rb +0 -517
  119. data/spec/cql/unit_spec.rb +0 -394
  120. data/spec/spec.opts +0 -1
@@ -29,8 +29,8 @@ module ActiveFacts
29
29
  :discussion => @discussion
30
30
  )
31
31
  case target
32
- when ActiveFacts::Metamodel::Concept
33
- context_note.concept = target
32
+ when ActiveFacts::Metamodel::ObjectType
33
+ context_note.object_type = target
34
34
  when ActiveFacts::Metamodel::Constraint
35
35
  context_note.constraint = target
36
36
  when ActiveFacts::Metamodel::FactType
@@ -52,10 +52,13 @@ module ActiveFacts
52
52
  end
53
53
 
54
54
  class Constraint < Definition
55
- def initialize context_note, enforcement, readings_lists = []
55
+ def initialize context_note, enforcement, clauses_lists = []
56
+ if context_note.is_a?(Treetop::Runtime::SyntaxNode)
57
+ context_note = context_note.empty? ? nil : context_note.ast
58
+ end
56
59
  @context_note = context_note
57
60
  @enforcement = enforcement
58
- @readings_lists = readings_lists
61
+ @clauses_lists = clauses_lists
59
62
  end
60
63
 
61
64
  def compile
@@ -67,38 +70,29 @@ module ActiveFacts
67
70
  # Override for constraint types that need loose binding (same role player matching with different adjectives)
68
71
  end
69
72
 
70
- # Return the unique array of all bindings in these readings, including in objectification joins
71
- def all_bindings_in_readings readings
72
- readings.map do |reading|
73
- reading.role_refs.map do |rr|
74
- [rr.binding] + (rr.objectification_join ? all_bindings_in_readings(rr.objectification_join) : [])
75
- end
76
- end.
77
- flatten.
78
- uniq
79
- end
80
-
81
- def bind_readings
73
+ def bind_clauses extra = []
82
74
  @context = CompilationContext.new(@vocabulary)
83
-
84
- @readings_lists.map do |readings_list|
85
- readings_list.each{ |reading| reading.identify_players_with_role_name(@context) }
86
- readings_list.each{ |reading| reading.identify_other_players(@context) }
87
- readings_list.each{ |reading| reading.bind_roles @context } # Create the Compiler::Bindings
88
- readings_list.each do |reading|
89
- fact_type = reading.match_existing_fact_type @context
90
- raise "Unrecognised fact type #{reading.inspect} in #{self.class}" unless fact_type
75
+ @context.left_contraction_allowed = true
76
+
77
+ @context.bind @clauses_lists, extra
78
+ @clauses_lists.map do |clauses_list|
79
+ @context.left_contractable_clause = nil # Don't contract outside this set of clauses
80
+ clauses_list.each do |clause|
81
+ fact_type = clause.match_existing_fact_type @context
82
+ raise "Unrecognised fact type #{clause.inspect} in #{self.class}" unless fact_type
91
83
  end
92
84
  end
93
85
 
86
+ # Any constrained roles will be first identified here.
87
+ # This means that they can't introduce role names.
94
88
  loose_binding
95
89
 
96
90
  # Ok, we have bound all players by subscript/role_name, by adjectives, and by loose binding,
97
91
  # and matched all the fact types that matter. Now assemble a join (with all join steps) for
98
- # each join list, and build an array of the bindings that are involved in the join steps.
99
- @bindings_by_list =
100
- @readings_lists.map do |readings_list|
101
- all_bindings_in_readings(readings_list)
92
+ # each join list, and build an array of the variables that are involved in the join steps.
93
+ @variables_by_list =
94
+ @clauses_lists.map do |clauses_list|
95
+ all_variables_in_clauses(clauses_list)
102
96
  end
103
97
 
104
98
  warn_ignored_joins
@@ -106,8 +100,8 @@ module ActiveFacts
106
100
 
107
101
  def warn_ignored_joins
108
102
  # Warn about ignored joins
109
- @readings_lists.each do |readings_list|
110
- fact_types = readings_list.map{|join| join.role_refs[0].role_ref.role.fact_type}.uniq
103
+ @clauses_lists.each do |clauses_list|
104
+ fact_types = clauses_list.map{|join| join.var_refs[0].role_ref.role.fact_type}.uniq
111
105
  if fact_types.size > 1
112
106
  puts "------->>>> Join ignored in #{self.class}: #{fact_types.map{|ft| ft.preferred_reading.expand}*' and '}"
113
107
  end
@@ -117,63 +111,61 @@ module ActiveFacts
117
111
  def loose_bind_wherever_possible
118
112
  # Apply loose binding over applicable roles:
119
113
  debug :binding, "Loose binding on #{self.class.name}" do
120
- @readings_lists.each do |readings_list|
121
- readings_list.each do |reading|
122
- reading.role_refs.each_with_index do |role_ref, i|
123
- next if role_ref.binding.refs.size > 1
124
- # if reading.side_effects && !reading.side_effects.role_side_effects[i].residual_adjectives
125
- # debug :binding, "Discounting #{role_ref.inspect} as needing loose binding because it has no residual_adjectives"
114
+ @clauses_lists.each do |clauses_list|
115
+ clauses_list.each do |clause|
116
+ clause.var_refs.each_with_index do |var_ref, i|
117
+ next if var_ref.variable.refs.size > 1
118
+ # if clause.side_effects && !clause.side_effects.role_side_effects[i].residual_adjectives
119
+ # debug :binding, "Discounting #{var_ref.inspect} as needing loose binding because it has no residual_adjectives"
126
120
  # next
127
121
  # end
128
- # This role_ref didn't match any other role_ref. Have a scout around for a suitable partner
129
- candidates = @context.bindings.
130
- select do |key, binding|
131
- binding.player == role_ref.binding.player and
132
- binding != role_ref.binding and
133
- binding.role_name == role_ref.binding.role_name and # Both will be nil if they match
134
- # REVISIT: Don't bind to a binding with a role occurrence in the same reading
135
- !binding.refs.detect{|rr|
136
- x = rr.reading == reading
137
- # puts "Discounting binding #{binding.inspect} as a match for #{role_ref.inspect} because it's already bound to a player in #{role_ref.reading.inspect}" if x
122
+ # This var_ref didn't match any other var_ref. Have a scout around for a suitable partner
123
+ candidates = @context.variables.
124
+ select do |key, variable|
125
+ variable.player == var_ref.variable.player and
126
+ variable != var_ref.variable and
127
+ variable.role_name == var_ref.variable.role_name and # Both will be nil if they match
128
+ # REVISIT: Don't bind to a variable with a role occurrence in the same clause
129
+ !variable.refs.detect{|vr|
130
+ x = vr.clause == clause
131
+ # puts "Discounting variable #{variable.inspect} as a match for #{var_ref.inspect} because it's already bound to a player in #{var_ref.clause.inspect}" if x
138
132
  x
139
133
  }
140
134
  end.map{|k,b| b}
141
135
  next if candidates.size != 1 # Fail
142
- debug :binding, "Loose binding #{role_ref.inspect} to #{candidates[0].inspect}"
143
- role_ref.rebind_to(@context, candidates[0].refs[0])
136
+ debug :binding, "Loose binding #{var_ref.inspect} to #{candidates[0].inspect}"
137
+ var_ref.rebind_to(@context, candidates[0].refs[0])
144
138
  end
145
139
  end
146
140
  end
147
141
  end
148
142
  end
149
143
 
150
- def loose_bind_roles
144
+ def loose_bind
151
145
  # Apply loose binding over applicable @roles:
152
146
  debug :binding, "Check for loose bindings on #{@roles.size} roles in #{self.class.name}" do
153
- @roles.each do |role_ref|
154
- role_ref.identify_player @context
155
- role_ref.bind @context
156
- if role_ref.binding.refs.size < @readings_lists.size+1
157
- debug :binding, "Insufficient bindings for #{role_ref.inspect} (#{role_ref.binding.refs.size}, expected #{@readings_lists.size+1}), attempting loose binding" do
158
- @readings_lists.each do |readings_list|
147
+ @roles.each do |var_ref|
148
+ if var_ref.variable.refs.size < @clauses_lists.size+1
149
+ debug :binding, "Insufficient bindings for #{var_ref.inspect} (#{var_ref.variable.refs.size}, expected #{@clauses_lists.size+1}), attempting loose binding" do
150
+ @clauses_lists.each do |clauses_list|
159
151
  candidates = []
160
- next if readings_list.
161
- detect do |reading|
162
- debug :binding, "Checking #{reading.inspect}"
163
- reading.role_refs.
164
- detect do |rr|
165
- already_bound = rr.binding == role_ref.binding
166
- if !already_bound && rr.player == role_ref.player
167
- candidates << rr
152
+ next if clauses_list.
153
+ detect do |clause|
154
+ debug :binding, "Checking #{clause.inspect}"
155
+ clause.var_refs.
156
+ detect do |vr|
157
+ already_bound = vr.variable == var_ref.variable
158
+ if !already_bound && vr.player == var_ref.player
159
+ candidates << vr
168
160
  end
169
161
  already_bound
170
162
  end
171
163
  end
172
- debug :binding, "Attempting loose binding for #{role_ref.inspect} in #{readings_list.inspect}, from the following candidates: #{candidates.inspect}"
164
+ debug :binding, "Attempting loose binding for #{var_ref.inspect} in #{clauses_list.inspect}, from the following candidates: #{candidates.inspect}"
173
165
 
174
166
  if candidates.size == 1
175
- debug :binding, "Rebinding #{candidates[0].inspect} to #{role_ref.inspect}"
176
- candidates[0].rebind_to(@context, role_ref)
167
+ debug :binding, "Rebinding #{candidates[0].inspect} to #{var_ref.inspect}"
168
+ candidates[0].rebind_to(@context, var_ref)
177
169
  end
178
170
  end
179
171
  end
@@ -182,41 +174,45 @@ module ActiveFacts
182
174
  end
183
175
  end
184
176
 
185
- def common_bindings
186
- @common_bindings ||= @bindings_by_list[1..-1].inject(@bindings_by_list[0]) { |r, b| r & b }
187
- raise "#{self.class} must cover some of the same roles, see #{@bindings_by_list.inspect}" unless @common_bindings.size > 0
188
- @common_bindings
177
+ def common_variables
178
+ @common_variables ||= @variables_by_list[1..-1].inject(@variables_by_list[0]) { |r, b| r & b }
179
+ raise "#{self.class} must cover some of the same roles, see #{@variables_by_list.inspect}" unless @common_variables.size > 0
180
+ @common_variables
181
+ end
182
+
183
+ def to_s
184
+ "#{self.class.name.sub(/.*::/,'')}" + (@clauses_lists.size > 0 ? " over #{@clauses_lists.inspect}" : '')
189
185
  end
190
186
  end
191
187
 
192
188
  class PresenceConstraint < Constraint
193
- def initialize context_note, enforcement, readings_lists, role_refs, quantifier
194
- super context_note, enforcement, readings_lists
195
- @role_refs = role_refs || []
189
+ def initialize context_note, enforcement, clauses_lists, var_refs, quantifier
190
+ super context_note, enforcement, clauses_lists
191
+ @var_refs = var_refs || []
196
192
  @quantifier = quantifier
197
193
  end
198
194
 
199
195
  def compile
200
- @readings = @readings_lists.map do |readings_list|
201
- raise "REVISIT: Join presence constraints not supported yet" if readings_list.size > 1 or
202
- readings_list.detect{|reading| reading.role_refs.detect{|rr| rr.objectification_join } }
203
- readings_list[0]
196
+ @clauses = @clauses_lists.map do |clauses_list|
197
+ raise "REVISIT: Join presence constraints not supported yet" if clauses_list.size > 1 or
198
+ clauses_list.detect{|clause| clause.var_refs.detect{|vr| vr.nested_clauses } }
199
+ clauses_list[0]
204
200
  end
205
201
 
206
- bind_readings
202
+ bind_clauses @var_refs
207
203
 
208
- if @role_refs.size > 0
209
- bind_roles
204
+ if @var_refs.size > 0
205
+ bind_constrained_roles
210
206
  else
211
- cb = common_bindings
207
+ cb = common_variables
212
208
  raise "Either/or must have only one duplicated role, not #{cb.inspect}" unless cb.size == 1
213
- @role_refs = cb[0].refs.reverse # REVISIT: Should have order these by reading, not like this
209
+ @var_refs = cb[0].refs.reverse # REVISIT: Should have order these by clause, not like this
214
210
  end
215
211
 
216
212
  role_sequence = @constellation.RoleSequence(:new)
217
- @role_refs.each do |role_ref|
218
- raise "The constrained role #{role_ref.inspect} was not found in the invoked fact types" if role_ref.binding.refs.size == 1
219
- (role_ref.binding.refs-[role_ref]).each do |ref|
213
+ @var_refs.each do |var_ref|
214
+ raise "The constrained role #{var_ref.inspect} was not found in the invoked fact types" if var_ref.variable.refs.size == 1
215
+ (var_ref.variable.refs-[var_ref]).each do |ref|
220
216
  role = (ref.role_ref && ref.role_ref.role) || ref.role
221
217
  raise "FactType role not found for #{ref.inspect}" unless role
222
218
  @constellation.RoleRef(role_sequence, role_sequence.all_role_ref.size, :role => role)
@@ -238,207 +234,87 @@ module ActiveFacts
238
234
  super
239
235
  end
240
236
 
241
- # In a PresenceConstraint, each role in "each XYZ" must occur in exactly one readings_list
237
+ # In a PresenceConstraint, each role in "each XYZ" must occur in exactly one clauses_list
242
238
  def loose_binding
243
239
  # loose_bind_wherever_possible
244
240
  end
245
241
 
246
- def bind_roles
247
- @role_refs.each do |role_ref|
248
- role_ref.identify_player @context
249
- role_ref.bind @context
250
- if role_ref.binding.refs.size == 1
242
+ def bind_constrained_roles
243
+ @var_refs.each do |var_ref|
244
+ if var_ref.variable.refs.size == 1
251
245
  # Apply loose binding over the constrained roles
252
246
  candidates =
253
- @readings.map do |reading|
254
- reading.role_refs.select{ |rr| rr.player == role_ref.player }
247
+ @clauses.map do |clause|
248
+ clause.var_refs.select{ |vr| vr.player == var_ref.player }
255
249
  end.flatten
256
250
  if candidates.size == 1
257
- debug :binding, "Rebinding #{role_ref.inspect} to #{candidates[0].inspect} in presence constraint"
258
- role_ref.rebind_to(@context, candidates[0])
251
+ debug :binding, "Rebinding #{var_ref.inspect} to #{candidates[0].inspect} in presence constraint"
252
+ var_ref.rebind_to(@context, candidates[0])
259
253
  end
260
254
  end
261
255
  end
262
256
  end
263
257
 
258
+ def to_s
259
+ "#{super} #{@quantifier.min}-#{@quantifier.max} over (#{@var_refs.map{|vr| vr.inspect}*', '})"
260
+ end
264
261
  end
265
262
 
266
263
  class SetConstraint < Constraint
267
- def initialize context_note, enforcement, readings_lists
268
- super context_note, enforcement, readings_lists
264
+ def initialize context_note, enforcement, clauses_lists
265
+ super context_note, enforcement, clauses_lists
269
266
  end
270
267
 
271
268
  def warn_ignored_joins
272
269
  # No warnings needed here any more
273
270
  end
274
271
 
275
- # Make a JoinNode for every binding present in these readings
276
- def build_join_nodes(readings_list)
277
- debug :join, "Building join nodes" do
278
- join = @constellation.Join(:new)
279
- all_bindings_in_readings(readings_list).
280
- each do |binding|
281
- debug :join, "Creating join node #{join.all_join_node.size} for #{binding.inspect}"
282
- binding.join_node = @constellation.JoinNode(join, join.all_join_node.size, :concept => binding.player)
283
- end
284
- join
285
- end
286
- end
287
-
288
- def build_join_steps reading, constrained_rs, objectification_node = nil
289
- join_roles = []
290
- incidental_roles = []
291
- debug :join, "Creating join Role Sequence for #{reading.inspect} with #{reading.role_refs.size} role refs" do
292
- objectification_step = nil
293
- reading.role_refs.each do |role_ref|
294
- # These role_refs are the Compiler::RoleRefs. These have associated Metamodel::RoleRefs,
295
- # but we need to create JoinRoles for those roles. # REVISIT: JoinRoles may need to save residual_adjectives
296
- binding = role_ref.binding
297
- role = role_ref.role || role_ref.role_ref.role
298
- join_role = nil
299
-
300
- if (reading.fact_type.entity_type)
301
- # This reading is of an objectified fact type.
302
- # We need a join step from this role to the phantom role, but not
303
- # for a role that has only one role_ref (this one) in their binding.
304
- # Create the JoinNode and JoinRole in any case though.
305
- refs_count = binding.refs.size
306
- objectification_ref_count = 0
307
- if role_ref.objectification_join
308
- role_ref.objectification_join.each do |r|
309
- objectification_ref_count += r.role_refs.select{|rr| rr.binding.refs.size > 1}.size
310
- end
311
- end
312
- refs_count += objectification_ref_count
313
-
314
- debug :join, "Creating Join Node #{role_ref.inspect} (counts #{refs_count}/#{objectification_ref_count}) and objectification Join Step for #{role_ref.inspect}" do
272
+ def role_sequences_for_common_variables ignore_trailing_joins = false
273
+ @clauses_lists.
274
+ zip(@variables_by_list).
275
+ map do |clauses_list, variables|
276
+ # Does this clauses_list involve a join?
277
+ if clauses_list.size > 1 or
278
+ clauses_list.detect{|clause| clause.var_refs.detect{|var_ref| var_ref.nested_clauses } }
315
279
 
316
- raise "Internal error: Trying to add role of #{role.concept.name} to join node for #{binding.join_node.concept.name}" unless binding.join_node.concept == role.concept
317
- join_role = @constellation.JoinRole(binding.join_node, role)
280
+ debug :join, "Building join for #{clauses_list.inspect}" do
281
+ debug :join, "Constrained variables are #{@common_variables.inspect}"
282
+ # Every Variable in these clauses becomes a Join Node,
283
+ # and every clause becomes a JoinStep (and a RoleSequence).
284
+ # The returned RoleSequences contains the RoleRefs for the common_variables.
318
285
 
319
- if (refs_count <= 1) # Our work here is done if there are no other refs
320
- if objectification_step
321
- join_role.join_step = objectification_step
322
- else
323
- incidental_roles << join_role
324
- end
325
- next
326
- end
327
-
328
- join_roles << join_role
329
- unless objectification_node
330
- # This is an implicit objectification, just the FT reading, not ET(where ...reading...)
331
- # We need to create a JoinNode for this object, even though it has no RoleRefs
332
- join = binding.join_node.join
333
- debug :join, "Creating JN#{join.all_join_node.size} for #{reading.fact_type.entity_type.name} in objectification"
334
- objectification_node = @constellation.JoinNode(join, join.all_join_node.size, :concept => reading.fact_type.entity_type)
335
- end
336
- raise "Internal error: Trying to add role of #{role.implicit_fact_type.all_role.single.concept.name} to join node for #{objectification_node.concept.name}" unless objectification_node.concept == role.implicit_fact_type.all_role.single.concept
337
-
338
- irole = role.implicit_fact_type.all_role.single
339
- raise "Internal error: Trying to add role of #{irole.concept.name} to join node for #{objectification_node.concept.name}" unless objectification_node.concept == irole.concept
340
- objectification_role = @constellation.JoinRole(objectification_node, role.implicit_fact_type.all_role.single)
341
- objectification_step = @constellation.JoinStep(objectification_role, join_role, :fact_type => role.implicit_fact_type)
342
- debug :join, "New #{objectification_step.describe}"
343
- debug :join, "Associating #{incidental_roles.map(&:describe)*', '} incidental roles with #{objectification_step.describe}" if incidental_roles.size > 0
344
- incidental_roles.each { |jr| jr.join_step = objectification_step }
345
- incidental_roles = []
346
- join_roles = []
347
- end
348
- else
349
- debug :join, "Creating Role Ref for #{role_ref.inspect}" do
350
- # REVISIT: If there's an implicit subtyping join here, create it; then always raise the error here.
351
- # I don't want to do this for now because the verbaliser will always verbalise all join steps.
352
- if binding.join_node.concept != role.concept and
353
- 0 == (binding.join_node.concept.supertypes_transitive & role.concept.supertypes_transitive).size
354
- raise "Internal error: Trying to add role of #{role.concept.name} to join node for #{binding.join_node.concept.name} in '#{reading.fact_type.default_reading}'"
355
- end
356
- raise "Internal error: Trying to add role of #{role.concept.name} to join node for #{binding.join_node.concept.name}" unless binding.join_node.concept == role.concept
357
- join_role = @constellation.JoinRole(binding.join_node, role)
358
- join_roles << join_role
359
- end
360
- end
361
-
362
- if role_ref.objectification_join
363
- # We are looking at a role whose player is an objectification of a fact type,
364
- # which will have ImplicitFactTypes for each role.
365
- # Each of these ImplicitFactTypes has a single phantom role played by the objectifying entity type
366
- # One of these phantom roles is likely to be the subject of an objectification join step.
367
- role_ref.objectification_join.each do |r|
368
- debug :join, "Building objectification join for #{role_ref.objectification_join.inspect}" do
369
- build_join_steps r, constrained_rs, binding.join_node
370
- end
371
- end
372
- end
373
- if (@common_bindings.include?(binding))
374
- debug :join, "#{binding.inspect} is a constrained binding, add the Role Ref for #{role.concept.name}"
375
- raise "Internal error: Trying to add role of #{role.concept.name} to join node for #{binding.join_node.concept.name}" unless binding.join_node.concept == role.concept
376
- @constellation.RoleRef(constrained_rs, constrained_rs.all_role_ref.size, :role => role, :join_role => join_role)
377
- end
378
- end
379
- end
380
-
381
- if join_roles.size > 0
382
- end_node = join_roles[-1].join_node
383
- if !reading.fact_type.entity_type and role = reading.fact_type.all_role.single
384
- # Don't give the ImplicitBoolean a join_node. We can live without one, for now.
385
- # The Join Step will have a duplicate node, and the fact type will tell us what's happening
386
- join_roles << join_roles[0]
387
- end
388
- # We aren't talking about objectification here, so there must be exactly two roles.
389
- raise "REVISIT: Internal error constructing join for #{reading.inspect}" if join_roles.size != 2
390
- js = @constellation.JoinStep(join_roles[0], join_roles[1], :fact_type => reading.fact_type)
391
- debug :join, "New Join Step #{js.describe}"
392
- debug :join, "Associating #{incidental_roles.map(&:describe)*', '} incidental roles with #{js.describe}" if incidental_roles.size > 0
393
- incidental_roles.each { |jr| jr.join_step = js }
394
- end
395
- end
396
-
397
- def role_sequences_for_common_bindings ignore_trailing_joins = false
398
- @readings_lists.
399
- zip(@bindings_by_list).
400
- map do |readings_list, bindings|
401
- # Does this readings_list involve a join?
402
- if readings_list.size > 1 or
403
- readings_list.detect{|reading| reading.role_refs.detect{|role_ref| role_ref.objectification_join } }
404
-
405
- debug :join, "Building join for #{readings_list.inspect}" do
406
- debug :join, "Constrained bindings are #{@common_bindings.inspect}"
407
- # Every Binding in these readings becomes a Join Node,
408
- # and every reading becomes a JoinStep (and a RoleSequence).
409
- # The returned RoleSequences contains the RoleRefs for the common_bindings.
410
-
411
- # Create a join with a join node for every binding:
412
- join = build_join_nodes(readings_list)
286
+ # Create a join with a join node for every variable and all join steps:
287
+ join = build_join_nodes(clauses_list)
288
+ roles_by_variable = build_all_join_steps(clauses_list)
289
+ join.validate
413
290
 
291
+ # Create the projected RoleSequence for the constraint:
414
292
  role_sequence = @constellation.RoleSequence(:new)
415
- debug :join, "Building join steps" do
416
- readings_list.each do |reading|
417
- build_join_steps(reading, role_sequence)
418
- end
293
+ @common_variables.each do |variable|
294
+ role, join_role = *roles_by_variable[variable]
295
+ @constellation.RoleRef(role_sequence, role_sequence.all_role_ref.size, :role => role, :join_role => join_role)
419
296
  end
420
- join.validate
421
297
 
422
298
  role_sequence
423
299
  end
424
300
  else
425
- # There's no join in this readings_list, just create a role_sequence
301
+ # There's no join in this clauses_list, just create a role_sequence
426
302
  role_sequence = @constellation.RoleSequence(:new)
427
- join_bindings = bindings-@common_bindings
428
- unless join_bindings.empty? or ignore_trailing_joins && join_bindings.size <= 1
429
- debug :constraint, "REVISIT: #{self.class}: Ignoring join from #{@common_bindings.inspect} to #{join_bindings.inspect} in #{readings_list.inspect}"
303
+ join_variables = variables-@common_variables
304
+ unless join_variables.empty? or ignore_trailing_joins && join_variables.size <= 1
305
+ debug :constraint, "REVISIT: #{self.class}: Ignoring join from #{@common_variables.inspect} to #{join_variables.inspect} in #{clauses_list.inspect}"
430
306
  end
431
- @common_bindings.each do |binding|
432
- roles = readings_list.
433
- map do |reading|
434
- reading.role_refs.detect{|rr| rr.binding == binding }
307
+ @common_variables.each do |variable|
308
+ roles = clauses_list.
309
+ map do |clause|
310
+ clause.var_refs.detect{|vr| vr.variable == variable }
435
311
  end.
436
- compact. # A join reading will probably not have the common binding
437
- map do |role_ref|
438
- role_ref.role_ref && role_ref.role_ref.role or role_ref.role
312
+ compact. # A join clause will probably not have the common variable
313
+ map do |var_ref|
314
+ var_ref.role_ref && var_ref.role_ref.role or var_ref.role
439
315
  end.
440
316
  compact
441
- # REVISIT: Should use reading side effects to preserve residual adjectives here.
317
+ # REVISIT: Should use clause side effects to preserve residual adjectives here.
442
318
  @constellation.RoleRef(role_sequence, role_sequence.all_role_ref.size, :role => roles[0])
443
319
  end
444
320
  role_sequence
@@ -448,18 +324,18 @@ module ActiveFacts
448
324
  end
449
325
 
450
326
  class SubsetConstraint < SetConstraint
451
- def initialize context_note, enforcement, readings_lists
452
- super context_note, enforcement, readings_lists
453
- @subset_readings = @readings_lists[0]
454
- @superset_readings = @readings_lists[1]
327
+ def initialize context_note, enforcement, clauses_lists
328
+ super context_note, enforcement, clauses_lists
329
+ @subset_clauses = @clauses_lists[0]
330
+ @superset_clauses = @clauses_lists[1]
455
331
  end
456
332
 
457
333
  def compile
458
- bind_readings
459
- common_bindings
334
+ bind_clauses
335
+ common_variables
460
336
 
461
337
  role_sequences =
462
- role_sequences_for_common_bindings
338
+ role_sequences_for_common_variables
463
339
 
464
340
  @constraint =
465
341
  @constellation.SubsetConstraint(
@@ -478,24 +354,24 @@ module ActiveFacts
478
354
  end
479
355
 
480
356
  class SetComparisonConstraint < SetConstraint
481
- def initialize context_note, enforcement, readings_lists
482
- super context_note, enforcement, readings_lists
357
+ def initialize context_note, enforcement, clauses_lists
358
+ super context_note, enforcement, clauses_lists
483
359
  end
484
360
  end
485
361
 
486
362
  class SetExclusionConstraint < SetComparisonConstraint
487
- def initialize context_note, enforcement, readings_lists, roles, quantifier
488
- super context_note, enforcement, readings_lists
363
+ def initialize context_note, enforcement, clauses_lists, roles, quantifier
364
+ super context_note, enforcement, clauses_lists
489
365
  @roles = roles || []
490
366
  @quantifier = quantifier
491
367
  end
492
368
 
493
369
  def compile
494
- bind_readings
495
- common_bindings
370
+ bind_clauses @roles
371
+ common_variables
496
372
 
497
373
  role_sequences =
498
- role_sequences_for_common_bindings
374
+ role_sequences_for_common_variables
499
375
 
500
376
  @constraint = @constellation.SetExclusionConstraint(
501
377
  :new,
@@ -509,28 +385,28 @@ module ActiveFacts
509
385
  super
510
386
  end
511
387
 
512
- # In a SetExclusionConstraint, each role in "for each XYZ" must occur in each readings_list
388
+ # In a SetExclusionConstraint, each role in "for each XYZ" must occur in each clauses_list
513
389
  def loose_binding
514
390
  if @roles.size == 0
515
391
  loose_bind_wherever_possible
516
392
  else
517
- loose_bind_roles
393
+ loose_bind
518
394
  end
519
395
  end
520
396
 
521
397
  end
522
398
 
523
399
  class SetEqualityConstraint < SetComparisonConstraint
524
- def initialize context_note, enforcement, readings_lists
525
- super context_note, enforcement, readings_lists
400
+ def initialize context_note, enforcement, clauses_lists
401
+ super context_note, enforcement, clauses_lists
526
402
  end
527
403
 
528
404
  def compile
529
- bind_readings
530
- common_bindings
405
+ bind_clauses
406
+ common_variables
531
407
 
532
408
  role_sequences =
533
- role_sequences_for_common_bindings
409
+ role_sequences_for_common_variables
534
410
 
535
411
  @constraint = @constellation.SetEqualityConstraint(
536
412
  :new,
@@ -546,7 +422,6 @@ module ActiveFacts
546
422
  def loose_binding
547
423
  loose_bind_wherever_possible
548
424
  end
549
-
550
425
  end
551
426
 
552
427
  class RingConstraint < Constraint
@@ -564,10 +439,10 @@ module ActiveFacts
564
439
  # Process the ring constraints:
565
440
  return if @rings.empty?
566
441
 
567
- role_refs = @role_sequence.all_role_ref.to_a
442
+ role_refs = @role_sequence.all_role_ref_in_order.to_a
568
443
  supertypes_by_position = role_refs.
569
444
  map do |role_ref|
570
- role_ref.role.concept.supertypes_transitive
445
+ role_ref.role.object_type.supertypes_transitive
571
446
  end
572
447
  role_pairs = []
573
448
  supertypes_by_position.each_with_index do |sts, i|
@@ -606,16 +481,22 @@ module ActiveFacts
606
481
  debug :constraint, "Added #{@constraint.verbalise} #{@constraint.class.roles.keys.map{|k|"#{k} => "+@constraint.send(k).verbalise}*", "}"
607
482
  super
608
483
  end
484
+
485
+ def to_s
486
+ "#{super} #{@rings*','} over #{@clauses_lists.inspect}"
487
+ end
609
488
  end
610
489
 
611
490
  class ValueConstraint < Constraint
612
- def initialize value_ranges, enforcement
491
+ def initialize value_ranges, units, enforcement
613
492
  super nil, enforcement
614
493
  @value_ranges = value_ranges
494
+ @units = units
615
495
  end
616
496
 
617
497
  def compile
618
498
  @constraint = @constellation.ValueConstraint(:new)
499
+ $stderr.puts "REVISIT: units on value constraints are not yet processed" if @units
619
500
  @value_ranges.each do |range|
620
501
  min, max = Array === range ? range : [range, range]
621
502
  v_range = @constellation.ValueRange(
@@ -627,6 +508,28 @@ module ActiveFacts
627
508
  @enforcement.compile(@constellation, @constraint) if @enforcement
628
509
  super
629
510
  end
511
+
512
+ def vrto_s vr
513
+ if Array === vr
514
+ min = vr[0]
515
+ max = vr[1]
516
+ if Numeric === min or Numeric === max
517
+ infinite = 1.0/0
518
+ min ||= -infinite
519
+ max ||= infinite
520
+ else
521
+ min ||= 'MIN'
522
+ max ||= 'MAX'
523
+ end
524
+ Range.new(min, max)
525
+ else
526
+ vr
527
+ end
528
+ end
529
+
530
+ def to_s
531
+ "#{super} to (#{@value_ranges.map{|vr| vrto_s(vr) }.inspect })#{ @units ? " in #{@units.inspect}" : ''}"
532
+ end
630
533
  end
631
534
 
632
535
  end