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
@@ -10,15 +10,21 @@ module ActiveFacts
10
10
  @value_constraint = value_constraint
11
11
  @parameters = parameters
12
12
  end
13
+
14
+ def to_s
15
+ "identified by its #{name}" +
16
+ ((p = @parameters).size > 0 ? '('+p*', '+')' : '') +
17
+ ((v = @value_constraint) ? v.to_s : '')
18
+ end
13
19
  end
14
20
 
15
- class EntityType < Concept
16
- def initialize name, supertypes, identification, pragmas, readings
21
+ class EntityType < ObjectType
22
+ def initialize name, supertypes, identification, pragmas, clauses
17
23
  super name
18
24
  @supertypes = supertypes
19
25
  @identification = identification
20
26
  @pragmas = pragmas
21
- @readings = readings || []
27
+ @clauses = clauses || []
22
28
  end
23
29
 
24
30
  def compile
@@ -36,10 +42,12 @@ module ActiveFacts
36
42
  # Identification may be via a mode (create it) or by forward-referenced entity types (allow those):
37
43
  prepare_identifier context
38
44
 
45
+ context.bind @clauses, @identification.is_a?(Array) ? @identification : []
46
+
39
47
  # Create the fact types that define the identifying roles:
40
48
  fact_types = create_identifying_fact_types context
41
49
 
42
- # At this point, @identification is an array of RoleRefs and/or Readings (for unary fact types)
50
+ # At this point, @identification is an array of VarRefs and/or Clauses (for unary fact types)
43
51
  # Have to do this after creating the necessary fact types
44
52
  complete_reference_mode_fact_type fact_types
45
53
 
@@ -48,9 +56,9 @@ module ActiveFacts
48
56
 
49
57
  make_preferred_identifier_over_roles identifying_roles
50
58
 
51
- @readings.each do |reading|
52
- next unless reading.context_note
53
- reading.context_note.compile(@constellation, @entity_type)
59
+ @clauses.each do |clause|
60
+ next unless clause.context_note
61
+ clause.context_note.compile(@constellation, @entity_type)
54
62
  end
55
63
 
56
64
  @entity_type
@@ -62,7 +70,7 @@ module ActiveFacts
62
70
  if @identification.is_a? ReferenceMode
63
71
  make_entity_type_refmode_valuetypes(name, @identification.name, @identification.parameters)
64
72
  vt_name = @reference_mode_value_type.name
65
- @identification = [Compiler::RoleRef.new(vt_name, nil, nil, nil, nil, nil, @identification.value_constraint, nil)]
73
+ @identification = [Compiler::VarRef.new(vt_name, nil, nil, nil, nil, nil, @identification.value_constraint, nil)]
66
74
  else
67
75
  context.allowed_forward_terms = legal_forward_references(@identification)
68
76
  end
@@ -70,29 +78,28 @@ module ActiveFacts
70
78
  end
71
79
 
72
80
  # Names used in the identifying roles list may be forward referenced:
73
- def legal_forward_references(identification_roles)
74
- identification_roles.map do |phrase|
75
- phrase.is_a?(RoleRef) ? phrase.term : nil
81
+ def legal_forward_references(identification_phrases)
82
+ identification_phrases.map do |phrase|
83
+ phrase.is_a?(VarRef) ? phrase.term : nil
76
84
  end.compact.uniq
77
85
  end
78
86
 
79
87
  def bind_identifying_roles context
80
88
  return unless @identification
81
89
  @identification.map do |id|
82
- if id.is_a?(RoleRef)
83
- id.identify_player(context)
84
- binding = id.bind(context)
85
- roles = binding.refs.map{|r| r.role}.compact.uniq
90
+ if id.is_a?(VarRef)
91
+ variable = id.variable
92
+ roles = variable.refs.map{|r| r.role}.compact.uniq
86
93
  raise "Looking for an occurrence of identifying role #{id.inspect}, but found #{roles.size == 0 ? "none" : roles.size}" if roles.size != 1
87
94
  roles[0]
88
95
  else
89
- # id is a reading of a unary fact type.
96
+ # id is a clause of a unary fact type.
90
97
  id.identify_other_players context
91
- id.bind_roles context
92
- matching_reading =
93
- @readings.detect { |reading| reading.phrases_match id.phrases }
94
- raise "Unary identifying role 'id.inspect' is not found in the defined fact types" unless matching_reading
95
- matching_reading.fact_type.all_role.single
98
+ id.bind context
99
+ matching_clause =
100
+ @clauses.detect { |clause| clause.phrases_match id.phrases }
101
+ raise "Unary identifying role '#{id.inspect}' is not found in the defined fact types" unless matching_clause
102
+ matching_clause.fact_type.all_role.single
96
103
  end
97
104
  end
98
105
  end
@@ -138,51 +145,47 @@ module ActiveFacts
138
145
 
139
146
  def create_identifying_fact_types context
140
147
  fact_types = []
141
- # Categorise the readings into fact types according to the roles they play.
142
- @readings.each{ |reading| reading.identify_players_with_role_name(context) }
143
- @readings.each{ |reading| reading.identify_other_players(context) }
144
- @readings.inject({}) do |hash, reading|
145
- players_key = reading.role_refs.map{|rr| rr.key.compact}.sort
146
- (hash[players_key] ||= []) << reading
148
+ # Categorise the clauses into fact types according to the roles they play.
149
+ @clauses.inject({}) do |hash, clause|
150
+ players_key = clause.var_refs.map{|vr| vr.key.compact}.sort
151
+ (hash[players_key] ||= []) << clause
147
152
  hash
148
- end.each do |players_key, readings|
149
- readings.each{ |reading| reading.bind_roles context } # Create the Compiler::Bindings
150
-
153
+ end.each do |players_key, clauses|
151
154
  # REVISIT: Loose binding goes here; it might merge some Compiler#Roles
152
155
 
153
- fact_type = create_identifying_fact_type(context, readings)
156
+ fact_type = create_identifying_fact_type(context, clauses)
154
157
  fact_types << fact_type if fact_type
155
- objectify_existing_fact_type(fact_type) unless fact_type.all_role.detect{|r| r.concept == @entity_type}
158
+ objectify_existing_fact_type(fact_type) unless fact_type.all_role.detect{|r| r.object_type == @entity_type}
156
159
  end
157
160
  fact_types
158
161
  end
159
162
 
160
- def create_identifying_fact_type context, readings
163
+ def create_identifying_fact_type context, clauses
161
164
  # Remove uninteresting assertions:
162
- readings.reject!{|reading| reading.is_existential_type }
163
- return nil unless readings.size > 0 # Nothing interesting was said.
165
+ clauses.reject!{|clause| clause.is_existential_type }
166
+ return nil unless clauses.size > 0 # Nothing interesting was said.
164
167
 
165
168
  # See if any fact type already exists (this ET cannot be a player, but might objectify it)
166
- existing_readings = readings.select{ |reading| reading.match_existing_fact_type context }
167
- any_matched = existing_readings.size > 0
169
+ existing_clauses = clauses.select{ |clause| clause.match_existing_fact_type context }
170
+ any_matched = existing_clauses.size > 0
168
171
 
169
172
  operation = any_matched ? 'Objectifying' : 'Creating'
170
- player_names = readings[0].role_refs.map{|rr| rr.key.compact*'-'}
171
- debug :matching, "#{operation} fact type for #{readings.size} readings over (#{player_names*', '})" do
173
+ player_names = clauses[0].var_refs.map{|vr| vr.key.compact*'-'}
174
+ debug :matching, "#{operation} fact type for #{clauses.size} clauses over (#{player_names*', '})" do
172
175
  if any_matched # There's an existing fact type we must be objectifying
173
- fact_type = objectify_existing_fact_type(existing_readings[0].fact_type)
176
+ fact_type = objectify_existing_fact_type(existing_clauses[0].fact_type)
174
177
  end
175
178
 
176
179
  unless fact_type
177
- fact_type = readings[0].make_fact_type(@vocabulary)
178
- readings[0].make_reading(@vocabulary, fact_type)
179
- readings[0].make_embedded_constraints vocabulary
180
- existing_readings = [readings[0]]
180
+ fact_type = clauses[0].make_fact_type(@vocabulary)
181
+ clauses[0].make_reading(@vocabulary, fact_type)
182
+ clauses[0].make_embedded_constraints vocabulary
183
+ existing_clauses = [clauses[0]]
181
184
  end
182
185
 
183
- (readings - existing_readings).each do |reading|
184
- reading.make_reading(@vocabulary, fact_type)
185
- reading.make_embedded_constraints vocabulary
186
+ (clauses - existing_clauses).each do |clause|
187
+ clause.make_reading(@vocabulary, fact_type)
188
+ clause.make_embedded_constraints vocabulary
186
189
  end
187
190
 
188
191
  fact_type
@@ -226,24 +229,20 @@ module ActiveFacts
226
229
  inheritance_fact.assimilation = assimilations[0]
227
230
 
228
231
  # Create a reading:
229
- sub_role = @constellation.Role(inheritance_fact, 0, :concept => @entity_type)
230
- super_role = @constellation.Role(inheritance_fact, 1, :concept => supertype)
232
+ sub_role = @constellation.Role(inheritance_fact, 0, :object_type => @entity_type)
233
+ super_role = @constellation.Role(inheritance_fact, 1, :object_type => supertype)
231
234
 
232
235
  rs = @constellation.RoleSequence(:new)
233
236
  @constellation.RoleRef(rs, 0, :role => sub_role)
234
237
  @constellation.RoleRef(rs, 1, :role => super_role)
235
238
  @constellation.Reading(inheritance_fact, 0, :role_sequence => rs, :text => "{0} is a kind of {1}")
236
- # REVISIT: Can we get away with adding "{0} is a/n {1}" here?
237
- # REVISIT: Can we deprecate the "is a subtype of" reading?
238
- @constellation.Reading(inheritance_fact, 1, :role_sequence => rs, :text => "{0} is a subtype of {1}")
239
239
 
240
240
  rs2 = @constellation.RoleSequence(:new)
241
241
  @constellation.RoleRef(rs2, 0, :role => super_role)
242
242
  @constellation.RoleRef(rs2, 1, :role => sub_role)
243
243
  # Decide in which order to include is a/is an. Provide both, but in order.
244
- n = 'aeiouh'.include?(sub_role.concept.name.downcase[0]) ? 1 : 0
245
- @constellation.Reading(inheritance_fact, 2+n, :role_sequence => rs2, :text => "{0} is a {1}")
246
- @constellation.Reading(inheritance_fact, 3-n, :role_sequence => rs2, :text => "{0} is an {1}")
244
+ n = 'aeiouh'.include?(sub_role.object_type.name.downcase[0]) ? 'n' : ''
245
+ @constellation.Reading(inheritance_fact, 2, :role_sequence => rs2, :text => "{0} is a#{n} {1}")
247
246
 
248
247
  if is_identifying_supertype
249
248
  inheritance_fact.provides_identification = true
@@ -278,8 +277,8 @@ module ActiveFacts
278
277
  vt = nil
279
278
  debug :entity, "Preparing value type #{vt_name} for reference mode" do
280
279
  # Find or Create an appropriate ValueType called '#{vt_name}', of the supertype '#{mode}'
281
- unless vt = @constellation.Concept[[@vocabulary.identifying_role_values, vt_name]] or
282
- vt = @constellation.Concept[[@vocabulary.identifying_role_values, vt_name = "#{name} #{mode}"]]
280
+ unless vt = @constellation.ObjectType[[@vocabulary.identifying_role_values, vt_name]] or
281
+ vt = @constellation.ObjectType[[@vocabulary.identifying_role_values, vt_name = "#{name} #{mode}"]]
283
282
  base_vt = @constellation.ValueType(@vocabulary, mode)
284
283
  vt = @constellation.ValueType(@vocabulary, vt_name, :supertype => base_vt)
285
284
  if parameters
@@ -306,16 +305,16 @@ module ActiveFacts
306
305
  # Find an existing fact type, if any:
307
306
  entity_role = identifying_role = nil
308
307
  fact_type = fact_types.detect do |ft|
309
- identifying_role = ft.all_role.detect{|r| r.concept == identifying_type } and
310
- entity_role = ft.all_role.detect{|r| r.concept == @entity_type }
308
+ identifying_role = ft.all_role.detect{|r| r.object_type == identifying_type } and
309
+ entity_role = ft.all_role.detect{|r| r.object_type == @entity_type }
311
310
  end
312
311
 
313
312
  # Create an identifying fact type if needed:
314
313
  unless fact_type
315
314
  fact_type = @constellation.FactType(:new)
316
315
  fact_types << fact_type
317
- entity_role = @constellation.Role(fact_type, 0, :concept => @entity_type)
318
- identifying_role = @constellation.Role(fact_type, 1, :concept => identifying_type)
316
+ entity_role = @constellation.Role(fact_type, 0, :object_type => @entity_type)
317
+ identifying_role = @constellation.Role(fact_type, 1, :object_type => identifying_type)
319
318
  end
320
319
  @identification[0].role = identifying_role
321
320
 
@@ -342,7 +341,7 @@ module ActiveFacts
342
341
  end
343
342
  if rs01.all_reading.empty?
344
343
  @constellation.Reading(fact_type, fact_type.all_reading.size, :role_sequence => rs01, :text => "{0} has {1}")
345
- debug :mode, "Creating new forward reading '#{entity_role.concept.name} has #{identifying_type.name}'"
344
+ debug :mode, "Creating new forward reading '#{entity_role.object_type.name} has #{identifying_type.name}'"
346
345
  else
347
346
  debug :mode, "Using existing forward reading"
348
347
  end
@@ -356,7 +355,7 @@ module ActiveFacts
356
355
  end
357
356
  if rs10.all_reading.empty?
358
357
  @constellation.Reading(fact_type, fact_type.all_reading.size, :role_sequence => rs10, :text => "{0} is of {1}")
359
- debug :mode, "Creating new reverse reading '#{identifying_type.name} is of #{entity_role.concept.name}'"
358
+ debug :mode, "Creating new reverse reading '#{identifying_type.name} is of #{entity_role.object_type.name}'"
360
359
  else
361
360
  debug :mode, "Using existing reverse reading"
362
361
  end
@@ -417,9 +416,19 @@ module ActiveFacts
417
416
  end
418
417
  end
419
418
 
419
+ def to_s
420
+ "EntityType: #{super} #{
421
+ @supertypes.size > 0 ? "< #{@supertypes*','} " : ''
422
+ }#{
423
+ @identification.is_a?(ReferenceMode) ? @identification.to_s : @identification.inspect
424
+ }#{
425
+ @clauses.size > 0 ? " where #{@clauses.inspect}" : ''
426
+ }#{
427
+ @pragmas.size > 0 ? ", pragmas [#{@pragmas*','}]" : ''
428
+ };"
429
+ end
420
430
  end
421
431
 
422
432
  end
423
433
  end
424
434
  end
425
-
@@ -0,0 +1,418 @@
1
+ module ActiveFacts
2
+ module CQL
3
+ class Compiler
4
+
5
+ # An Operation is a binary or ternary fact type involving an operator,
6
+ # a result, and one or two operands.
7
+ # Viewed as a result, it behaves like a VarRef with a nested Clause.
8
+ # Viewed as a fact type, it behaves like a Clause.
9
+ #
10
+ # The only exception here is an equality comparison, where it may
11
+ # turn out that the equality is merely a projection. In this case
12
+ # the Operation is dropped from the clauses and is replaced by the
13
+ # projected operand.
14
+ #
15
+ # Each operand may be a Literal, a VarRef, or another Operation,
16
+ # so we need to recurse down the tree to build the join.
17
+ #
18
+ class Operation
19
+ # VarRef (in)compatibility:
20
+ [ :term, :leading_adjective, :trailing_adjective, :role_name, :quantifier,
21
+ :value_constraint, :embedded_presence_constraint, :literal
22
+ ].each do |s|
23
+ define_method(s) { raise "Unexpected call to Operation\##{s}" }
24
+ define_method(:"#{s}=") { raise "Unexpected call to Operation\##{s}=" }
25
+ end
26
+ def role_name; nil; end
27
+ def leading_adjective; nil; end
28
+ def trailing_adjective; nil; end
29
+ def value_constraint; nil; end
30
+ def literal; nil; end
31
+ attr_accessor :player # What ObjectType does the Variable denote
32
+ attr_accessor :variable # What Variable for that ObjectType
33
+ attr_accessor :clause # What clause does the result participate in?
34
+ attr_accessor :role # Which Role of this ObjectType
35
+ attr_accessor :role_ref # Which RoleRef to that Role
36
+ def nested_clauses; @nested_clauses ||= [self]; end
37
+ def clause; self; end
38
+ def objectification_of; @fact_type; end
39
+
40
+ # Clause (in)compatibility:
41
+ [ :phrases, :qualifiers, :context_note, :reading, :role_sequence, :fact
42
+ ].each do |s|
43
+ define_method(s) { raise "Unexpected call to Operation\##{s}" }
44
+ define_method(:"#{s}=") { raise "Unexpected call to Operation\##{s}=" }
45
+ end
46
+ def conjunction; nil; end
47
+ attr_reader :fact_type
48
+ def objectified_as; self; end # The VarRef which objectified this fact type
49
+
50
+ def operands context = nil
51
+ raise "REVISIT: Implement operand enumeration in the operator subclass #{self.class.name}"
52
+ end
53
+
54
+ def identify_players_with_role_name context
55
+ # Just recurse, there's no way (yet: REVISIT?) to add a role name to the result of an expression
56
+ var_refs.each { |o|
57
+ o.identify_players_with_role_name(context)
58
+ }
59
+ # As yet, an operation cannot have a role name:
60
+ # identify_player context if role_name
61
+ end
62
+
63
+ def identify_other_players context
64
+ # Just recurse, there's no way (yet: REVISIT?) to add a role name to the result of an expression
65
+ var_refs.each { |o|
66
+ o.identify_other_players(context)
67
+ }
68
+ identify_player context
69
+ end
70
+
71
+ def bind context
72
+ var_refs.each do |o|
73
+ o.bind context
74
+ end
75
+ name = result_type_name(context)
76
+ @player = result_value_type(context, name)
77
+ key = "#{name} #{object_id}" # Every Operation result is a unique Variable
78
+ @variable = (context.variables[key] ||= Variable.new(@player))
79
+ @variable.refs << self
80
+ @variable
81
+ end
82
+
83
+ def result_type_name(context)
84
+ raise "REVISIT: Implement result_type_name in the #{self.class.name} subclass"
85
+ end
86
+
87
+ def result_value_type(context, name)
88
+ vocabulary = context.vocabulary
89
+ constellation = vocabulary.constellation
90
+ constellation.ValueType(vocabulary, name)
91
+ end
92
+
93
+ def is_naked_object_type
94
+ false # All Operations are non-naked
95
+ end
96
+
97
+ def match_existing_fact_type context
98
+ opnds = var_refs
99
+ result_var_ref = VarRef.new(@variable.player.name)
100
+ result_var_ref.player = @variable.player
101
+ result_var_ref.variable = @variable
102
+ @variable.refs << result_var_ref
103
+ clause_ast = Clause.new(
104
+ [result_var_ref, '='] +
105
+ (opnds.size > 1 ? [opnds[0]] : []) +
106
+ [operator, opnds[-1]]
107
+ )
108
+
109
+ # REVISIT: All operands must be value-types or simply-identified Entity Types.
110
+
111
+ # REVISIT: We should auto-create joins from Entity Types to an identifying ValueType
112
+ # REVISIT: We should traverse up the supertype of ValueTypes to find a DataType
113
+ @fact_type = clause_ast.match_existing_fact_type(context, :exact_type => true)
114
+ return @fact_type if @fact_type
115
+
116
+ @fact_type = clause_ast.make_fact_type context.vocabulary
117
+ reading = clause_ast.make_reading context.vocabulary, @fact_type
118
+ rrs = reading.role_sequence.all_role_ref_in_order
119
+ opnds[0].role_ref = rrs[0]
120
+ opnds[-1].role_ref = rrs[-1]
121
+ opnds.each do |opnd|
122
+ next unless opnd.is_a?(Operation)
123
+ opnd.match_existing_fact_type context
124
+ end
125
+ @fact_type
126
+ end
127
+
128
+ def is_existential_type
129
+ false
130
+ end
131
+
132
+ def is_equality_comparison
133
+ false
134
+ end
135
+
136
+ def operator
137
+ raise "REVISIT: Implement operator access in the operator subclass #{self.class.name}"
138
+ end
139
+
140
+ end
141
+
142
+ class Comparison < Operation
143
+ attr_accessor :operator, :e1, :e2, :qualifiers, :conjunction
144
+
145
+ def initialize operator, e1, e2, qualifiers = []
146
+ @operator, @e1, @e2, @qualifiers = operator, e1, e2, qualifiers
147
+ end
148
+
149
+ def var_refs
150
+ [@e1, @e2]
151
+ end
152
+
153
+ def bind context
154
+ var_refs.each do |o|
155
+ o.bind context
156
+ end
157
+
158
+ # REVISIT: Return the projected binding instead:
159
+ return @result = nil if @projection
160
+
161
+ name = 'Boolean'
162
+ @player = result_value_type(context, name)
163
+ key = "#{name} #{object_id}" # Every Comparison result is a unique Variable
164
+ @variable = (context.variables[key] ||= Variable.new(@player))
165
+ @variable.refs << self
166
+ @variable
167
+ end
168
+
169
+ def result_type_name(context)
170
+ "compare#{operator}(#{[@e1,@e2].map{|e| e.player.name}*', '}))"
171
+ end
172
+
173
+ def is_equality_comparison
174
+ @operator == '='
175
+ end
176
+
177
+ def identify_player context
178
+ @player || begin
179
+ if @projection
180
+ raise "REVISIT: The player is the projected expression"
181
+ end
182
+ v = context.vocabulary
183
+ @player = v.constellation.ValueType(v, 'Boolean')
184
+ end
185
+ end
186
+
187
+ =begin
188
+ def project lr
189
+ @projection = lr
190
+ projected_rr = lr == :left ? @e2 : @e1
191
+ true
192
+ end
193
+ =end
194
+
195
+ def inspect; to_s; end
196
+
197
+ def to_s
198
+ "compare#{operator}(#{e1.to_s} #{e2.to_s}#{@qualifiers.empty? ? '' : ', ['+@qualifiers*', '+']'})"
199
+ end
200
+ end
201
+
202
+ class Sum < Operation
203
+ attr_accessor :terms
204
+ def initialize *terms
205
+ @terms = terms
206
+ end
207
+
208
+ def var_refs
209
+ @terms
210
+ end
211
+
212
+ def operator
213
+ '+'
214
+ end
215
+
216
+ def identify_player context
217
+ @player || begin
218
+ # The players in the @terms have already been identified
219
+ # REVISIT: Check compliance of all units in @terms, and apply conversions where necessary
220
+ # REVISIT: The type of this result should be derived from type promotion rules. Here, we take the left-most.
221
+ # REVISIT: We should define a subtype of the result type here, and apply the units to it.
222
+ v = context.vocabulary
223
+ @player = @terms[0].player
224
+ @player
225
+ end
226
+ end
227
+
228
+ def result_type_name(context)
229
+ "sum(#{ @terms.map{|f| f.player.name}*', ' })"
230
+ end
231
+
232
+ =begin
233
+ def result_value_type(context, name)
234
+ # REVISIT: If there are units involved, check compatibility
235
+ vt = super
236
+ vt
237
+ end
238
+ =end
239
+
240
+ def inspect; to_s; end
241
+
242
+ def to_s
243
+ 'sum(' + @terms.map{|term| "#{term.to_s}" } * ' ' + ')'
244
+ end
245
+ end
246
+
247
+ class Product < Operation
248
+ attr_accessor :factors
249
+ def initialize *factors
250
+ @factors = factors
251
+ end
252
+
253
+ def var_refs
254
+ @factors
255
+ end
256
+
257
+ def operator
258
+ '*'
259
+ end
260
+
261
+ def identify_player context
262
+ @player || begin
263
+ # The players in the @factors have already been identified
264
+ # REVISIT: Calculate the units of the result from the units in @factors
265
+ # REVISIT: The type of this result should be derived from type promotion rules. Here, we take the left-most.
266
+ # REVISIT: We should define a subtype of the result type here, and apply the units to it.
267
+ v = context.vocabulary
268
+ @player = @factors[0].player
269
+ end
270
+ end
271
+
272
+ def result_type_name(context)
273
+ "product(#{ @factors.map{|f| f.player.name}*', ' })"
274
+ end
275
+
276
+ =begin
277
+ def result_value_type(context, name)
278
+ vt = super
279
+ # REVISIT: If there are units involved, create the result units
280
+ vt
281
+ end
282
+ =end
283
+
284
+ def inspect; to_s; end
285
+
286
+ def to_s
287
+ 'product(' + @factors.map{|factor| "#{factor.to_s}" } * ' ' + ')'
288
+ end
289
+ end
290
+
291
+ class Reciprocal < Operation
292
+ attr_accessor :divisor
293
+ def initialize divisor
294
+ @divisor = divisor
295
+ end
296
+
297
+ def operator
298
+ '1/'
299
+ end
300
+
301
+ def var_refs
302
+ [@divisor]
303
+ end
304
+
305
+ def identify_player context
306
+ @player || begin
307
+ # The player in @divisor has already been identified
308
+ # REVISIT: Calculate the units of the result from the units in @divisor
309
+ # REVISIT: Do we want integer division?
310
+ v = context.vocabulary
311
+ @player = v.constellation.ValueType(v, 'Real')
312
+ end
313
+ end
314
+
315
+ =begin
316
+ def result_type_name(context)
317
+ raise hell
318
+ end
319
+ =end
320
+
321
+ def inspect; to_s; end
322
+
323
+ def to_s
324
+ "reciprocal(#{factor.to_s})"
325
+ end
326
+ end
327
+
328
+ class Negate
329
+ attr_accessor :term
330
+ def initialize term
331
+ @term = term
332
+ end
333
+
334
+ def operator
335
+ '0-'
336
+ end
337
+
338
+ def identify_player context
339
+ @player || begin
340
+ # The player in @term have already been identified
341
+ v = context.vocabulary
342
+ @player = @term.player
343
+ end
344
+ end
345
+
346
+ =begin
347
+ def result_type_name(context)
348
+ raise hell
349
+ end
350
+ =end
351
+
352
+ def inspect; to_s; end
353
+
354
+ def to_s
355
+ "negate(#{term.to_s})"
356
+ end
357
+ end
358
+
359
+ class Literal
360
+ attr_accessor :literal, :unit, :role, :role_ref, :clause
361
+ attr_reader :objectification_of, :leading_adjective, :trailing_adjective, :value_constraint
362
+
363
+ def initialize literal, unit
364
+ @literal, @unit = literal, unit
365
+ end
366
+
367
+ # Stubs:
368
+ def role_name; nil; end
369
+ def nested_clauses; nil; end
370
+
371
+ def inspect; to_s; end
372
+
373
+ def to_s
374
+ unit ? "(#{@literal.to_s} in #{unit.to_s})" : @literal.to_s
375
+ end
376
+
377
+ def player
378
+ @player
379
+ end
380
+
381
+ def identify_players_with_role_name(context)
382
+ # Nothing to do here, move along
383
+ end
384
+
385
+ def identify_other_players(context)
386
+ identify_player context
387
+ end
388
+
389
+ def identify_player context
390
+ @player || begin
391
+ player_name =
392
+ case @literal
393
+ when String; 'String'
394
+ when Float; 'Real'
395
+ when Numeric; 'Integer'
396
+ when TrueClass, FalseClass; 'Boolean'
397
+ end
398
+ v = context.vocabulary
399
+ @player = v.constellation.ValueType(v, player_name)
400
+ end
401
+ end
402
+
403
+ def bind context
404
+ @variable || begin
405
+ key = "#{@player.name} #{@literal}"
406
+ @variable = (context.variables[key] ||= Variable.new(@player))
407
+ @variable.refs << self
408
+ end
409
+ end
410
+
411
+ def variable
412
+ @variable
413
+ end
414
+ end
415
+
416
+ end
417
+ end
418
+ end