activefacts 0.8.9 → 0.8.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gemtest +0 -0
- data/Manifest.txt +28 -33
- data/Rakefile +11 -12
- data/bin/cql +90 -46
- data/examples/CQL/Blog.cql +2 -1
- data/examples/CQL/CompanyDirectorEmployee.cql +2 -2
- data/examples/CQL/Death.cql +1 -1
- data/examples/CQL/Diplomacy.cql +9 -9
- data/examples/CQL/Genealogy.cql +3 -2
- data/examples/CQL/Insurance.cql +10 -7
- data/examples/CQL/JoinEquality.cql +2 -2
- data/examples/CQL/Marriage.cql +1 -1
- data/examples/CQL/Metamodel.cql +73 -53
- data/examples/CQL/MetamodelNext.cql +89 -67
- data/examples/CQL/OneToOnes.cql +2 -2
- data/examples/CQL/ServiceDirector.cql +10 -5
- data/examples/CQL/Supervision.cql +3 -3
- data/examples/CQL/Tests.Test5.Load.cql +1 -1
- data/examples/CQL/Warehousing.cql +4 -2
- data/lib/activefacts/cql/CQLParser.treetop +26 -60
- data/lib/activefacts/cql/Context.treetop +12 -2
- data/lib/activefacts/cql/Expressions.treetop +14 -30
- data/lib/activefacts/cql/FactTypes.treetop +165 -110
- data/lib/activefacts/cql/Language/English.treetop +167 -54
- data/lib/activefacts/cql/LexicalRules.treetop +16 -2
- data/lib/activefacts/cql/{Concepts.treetop → ObjectTypes.treetop} +36 -37
- data/lib/activefacts/cql/Terms.treetop +57 -27
- data/lib/activefacts/cql/ValueTypes.treetop +39 -13
- data/lib/activefacts/cql/compiler.rb +5 -3
- data/lib/activefacts/cql/compiler/{reading.rb → clause.rb} +407 -285
- data/lib/activefacts/cql/compiler/constraint.rb +178 -275
- data/lib/activefacts/cql/compiler/entity_type.rb +73 -64
- data/lib/activefacts/cql/compiler/expression.rb +418 -0
- data/lib/activefacts/cql/compiler/fact.rb +146 -145
- data/lib/activefacts/cql/compiler/fact_type.rb +197 -80
- data/lib/activefacts/cql/compiler/join.rb +159 -0
- data/lib/activefacts/cql/compiler/shared.rb +51 -23
- data/lib/activefacts/cql/compiler/value_type.rb +56 -2
- data/lib/activefacts/cql/parser.rb +15 -4
- data/lib/activefacts/generate/absorption.rb +7 -7
- data/lib/activefacts/generate/cql.rb +100 -37
- data/lib/activefacts/generate/oo.rb +28 -51
- data/lib/activefacts/generate/ordered.rb +60 -36
- data/lib/activefacts/generate/ruby.rb +6 -6
- data/lib/activefacts/generate/sql/server.rb +4 -4
- data/lib/activefacts/input/orm.rb +71 -53
- data/lib/activefacts/persistence.rb +1 -1
- data/lib/activefacts/persistence/columns.rb +27 -23
- data/lib/activefacts/persistence/foreignkey.rb +6 -6
- data/lib/activefacts/persistence/index.rb +17 -17
- data/lib/activefacts/persistence/{concept.rb → object_type.rb} +9 -9
- data/lib/activefacts/persistence/reference.rb +61 -36
- data/lib/activefacts/persistence/tables.rb +61 -59
- data/lib/activefacts/support.rb +54 -29
- data/lib/activefacts/version.rb +1 -1
- data/lib/activefacts/vocabulary/extensions.rb +99 -54
- data/lib/activefacts/vocabulary/metamodel.rb +43 -37
- data/lib/activefacts/vocabulary/verbaliser.rb +134 -109
- data/spec/absorption_spec.rb +8 -8
- data/spec/cql/comparison_spec.rb +91 -0
- data/spec/cql/contractions_spec.rb +251 -0
- data/spec/cql/entity_type_spec.rb +319 -0
- data/spec/cql/expressions_spec.rb +63 -0
- data/spec/cql/fact_type_matching_spec.rb +283 -0
- data/spec/cql/french_spec.rb +21 -0
- data/spec/cql/parser/bad_literals_spec.rb +86 -0
- data/spec/cql/parser/constraints_spec.rb +19 -0
- data/spec/cql/parser/entity_types_spec.rb +106 -0
- data/spec/cql/parser/expressions_spec.rb +179 -0
- data/spec/cql/parser/fact_types_spec.rb +41 -0
- data/spec/cql/parser/literals_spec.rb +312 -0
- data/spec/cql/parser/pragmas_spec.rb +89 -0
- data/spec/cql/parser/value_types_spec.rb +42 -0
- data/spec/cql/role_matching_spec.rb +147 -0
- data/spec/cql/samples_spec.rb +9 -9
- data/spec/cql_cql_spec.rb +1 -1
- data/spec/cql_dm_spec.rb +116 -0
- data/spec/cql_mysql_spec.rb +1 -1
- data/spec/cql_ruby_spec.rb +1 -1
- data/spec/cql_sql_spec.rb +3 -3
- data/spec/cql_symbol_tables_spec.rb +30 -30
- data/spec/cqldump_spec.rb +4 -4
- data/spec/helpers/array_matcher.rb +32 -27
- data/spec/helpers/diff_matcher.rb +6 -26
- data/spec/helpers/file_matcher.rb +41 -32
- data/spec/helpers/parse_to_ast_matcher.rb +76 -0
- data/spec/helpers/string_matcher.rb +32 -31
- data/spec/norma_cql_spec.rb +1 -1
- data/spec/norma_ruby_spec.rb +1 -1
- data/spec/norma_ruby_sql_spec.rb +1 -1
- data/spec/norma_sql_spec.rb +3 -1
- data/spec/norma_tables_spec.rb +1 -1
- data/spec/ruby_api_spec.rb +23 -0
- data/spec/spec_helper.rb +5 -4
- metadata +66 -66
- data/examples/CQL/OrienteeringER.cql +0 -58
- data/lib/activefacts/api.rb +0 -44
- data/lib/activefacts/api/concept.rb +0 -410
- data/lib/activefacts/api/constellation.rb +0 -128
- data/lib/activefacts/api/entity.rb +0 -256
- data/lib/activefacts/api/instance.rb +0 -60
- data/lib/activefacts/api/instance_index.rb +0 -80
- data/lib/activefacts/api/numeric.rb +0 -167
- data/lib/activefacts/api/role.rb +0 -80
- data/lib/activefacts/api/role_proxy.rb +0 -70
- data/lib/activefacts/api/role_values.rb +0 -117
- data/lib/activefacts/api/standard_types.rb +0 -87
- data/lib/activefacts/api/support.rb +0 -65
- data/lib/activefacts/api/value.rb +0 -135
- data/lib/activefacts/api/vocabulary.rb +0 -82
- data/spec/api/autocounter.rb +0 -82
- data/spec/api/constellation.rb +0 -130
- data/spec/api/entity_type.rb +0 -103
- data/spec/api/instance.rb +0 -461
- data/spec/api/roles.rb +0 -124
- data/spec/api/value_type.rb +0 -112
- data/spec/api_spec.rb +0 -13
- data/spec/cql/matching_spec.rb +0 -517
- data/spec/cql/unit_spec.rb +0 -394
- data/spec/spec.opts +0 -1
|
@@ -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 <
|
|
16
|
-
def initialize name, supertypes, identification, pragmas,
|
|
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
|
-
@
|
|
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
|
|
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
|
-
@
|
|
52
|
-
next unless
|
|
53
|
-
|
|
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::
|
|
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(
|
|
74
|
-
|
|
75
|
-
phrase.is_a?(
|
|
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?(
|
|
83
|
-
id.
|
|
84
|
-
|
|
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
|
|
96
|
+
# id is a clause of a unary fact type.
|
|
90
97
|
id.identify_other_players context
|
|
91
|
-
id.
|
|
92
|
-
|
|
93
|
-
@
|
|
94
|
-
raise "Unary identifying role 'id.inspect' is not found in the defined fact types" unless
|
|
95
|
-
|
|
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
|
|
142
|
-
@
|
|
143
|
-
|
|
144
|
-
|
|
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,
|
|
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,
|
|
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.
|
|
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,
|
|
163
|
+
def create_identifying_fact_type context, clauses
|
|
161
164
|
# Remove uninteresting assertions:
|
|
162
|
-
|
|
163
|
-
return nil unless
|
|
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
|
-
|
|
167
|
-
any_matched =
|
|
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 =
|
|
171
|
-
debug :matching, "#{operation} fact type for #{
|
|
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(
|
|
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 =
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
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
|
-
(
|
|
184
|
-
|
|
185
|
-
|
|
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, :
|
|
230
|
-
super_role = @constellation.Role(inheritance_fact, 1, :
|
|
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.
|
|
245
|
-
@constellation.Reading(inheritance_fact, 2
|
|
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.
|
|
282
|
-
vt = @constellation.
|
|
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.
|
|
310
|
-
entity_role = ft.all_role.detect{|r| r.
|
|
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, :
|
|
318
|
-
identifying_role = @constellation.Role(fact_type, 1, :
|
|
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.
|
|
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.
|
|
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
|