activefacts 0.8.9 → 0.8.10
Sign up to get free protection for your applications and to get access to all the features.
- 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
|