activefacts-cql 1.8.3 → 1.9.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/activefacts-cql.gemspec +1 -1
- data/lib/activefacts/cql/compiler.rb +84 -29
- data/lib/activefacts/cql/compiler/clause.rb +51 -9
- data/lib/activefacts/cql/compiler/constraint.rb +1 -1
- data/lib/activefacts/cql/compiler/expression.rb +279 -34
- data/lib/activefacts/cql/compiler/fact_type.rb +4 -2
- data/lib/activefacts/cql/compiler/query.rb +13 -9
- data/lib/activefacts/cql/compiler/shared.rb +10 -6
- data/lib/activefacts/cql/compiler/transform_rule.rb +136 -0
- data/lib/activefacts/cql/parser.rb +45 -2
- data/lib/activefacts/cql/parser/CQLParser.treetop +59 -5
- data/lib/activefacts/cql/parser/Language/English.treetop +5 -1
- data/lib/activefacts/cql/parser/Language/French.treetop +6 -2
- data/lib/activefacts/cql/parser/Language/Mandarin.treetop +2 -1
- data/lib/activefacts/cql/parser/Terms.treetop +2 -2
- data/lib/activefacts/cql/parser/TransformRules.treetop +226 -0
- data/lib/activefacts/cql/verbaliser.rb +5 -7
- data/lib/activefacts/cql/version.rb +1 -1
- data/lib/activefacts/input/cql.rb +2 -1
- data/lib/rubygems_plugin.rb +15 -12
- metadata +6 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9c904dd901055b4ee41174b3d60a3d16d02c4af0
|
4
|
+
data.tar.gz: 8321e5b02ed21d4132878e5d43e556ecd27460fa
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 699900788c5a3e09206a553b0751cce243a2b17edd3b7c7bb6ea59b00fb3177dd458674247631bb90af1a282840b64892862dbd8902f8513a1ad324852a6e6bb
|
7
|
+
data.tar.gz: d29b4f20f3fc97a4dfb04366bb2f035f9ae837fcae73d617ce5917903429327be0c1bc2eee0a6b052226cb29949c68b501f8d2412b3bee4bc4488c6ae8c32851
|
data/.gitignore
CHANGED
data/activefacts-cql.gemspec
CHANGED
@@ -24,7 +24,7 @@ Gem::Specification.new do |spec|
|
|
24
24
|
spec.add_development_dependency "rake", "~> 10.0"
|
25
25
|
spec.add_development_dependency "rspec", "~> 3.3"
|
26
26
|
|
27
|
-
spec.add_runtime_dependency "activefacts-metamodel", "~> 1", ">= 1.9.
|
27
|
+
spec.add_runtime_dependency "activefacts-metamodel", "~> 1", ">= 1.9.14"
|
28
28
|
spec.add_runtime_dependency "treetop", [">= 1.4.14", "~> 1.4"]
|
29
29
|
end
|
30
30
|
|
@@ -14,6 +14,7 @@ require 'activefacts/cql/compiler/fact'
|
|
14
14
|
require 'activefacts/cql/compiler/constraint'
|
15
15
|
require 'activefacts/cql/compiler/query'
|
16
16
|
require 'activefacts/cql/compiler/informal'
|
17
|
+
require 'activefacts/cql/compiler/transform_rule'
|
17
18
|
|
18
19
|
module ActiveFacts
|
19
20
|
module CQL
|
@@ -23,30 +24,35 @@ module ActiveFacts
|
|
23
24
|
'fr' => 'French',
|
24
25
|
'cn' => 'Mandarin'
|
25
26
|
}
|
27
|
+
EXTENSIONS = ['fiml', 'fidl', 'fiql', 'cql']
|
28
|
+
|
26
29
|
attr_reader :vocabulary
|
27
30
|
|
28
|
-
def initialize *a
|
29
|
-
@
|
31
|
+
def initialize filepath, *a
|
32
|
+
@filepath = filepath
|
30
33
|
super *a
|
31
34
|
@constellation = ActiveFacts::API::Constellation.new(ActiveFacts::Metamodel)
|
32
35
|
@constellation.loggers << proc{|*k| trace :apilog, k.inspect} if trace(:apilog)
|
33
36
|
@language = nil
|
37
|
+
@pending_import_topic = nil
|
38
|
+
@pending_import_role = ''
|
39
|
+
@pending_import_file_name = ''
|
34
40
|
trace :file, "Parsing '#{@filename}'"
|
35
41
|
end
|
36
42
|
|
37
|
-
def compile_file
|
38
|
-
|
39
|
-
@
|
40
|
-
File.open(
|
43
|
+
def compile_file filepath
|
44
|
+
old_filepath = @filepath
|
45
|
+
@filepath = filename
|
46
|
+
File.open(filepath) do |f|
|
41
47
|
compile(f.read)
|
42
48
|
end
|
43
|
-
@
|
49
|
+
@filepath = old_filepath
|
44
50
|
@vocabulary
|
45
51
|
end
|
46
52
|
|
47
53
|
# Load the appropriate natural language module
|
48
54
|
def detect_language
|
49
|
-
@
|
55
|
+
@filepath =~ /.*\.(..)\.cql$/i
|
50
56
|
language_code = $1
|
51
57
|
@language = LANGUAGES[language_code] || 'English'
|
52
58
|
end
|
@@ -67,6 +73,23 @@ module ActiveFacts
|
|
67
73
|
end
|
68
74
|
end
|
69
75
|
|
76
|
+
def create_import_if_pending new_topic
|
77
|
+
if @pending_import_topic
|
78
|
+
trace :import, "Topic #{@pending_import_topic.topic_name} imports #{new_topic.topic_name} as #{@pending_import_role} from file #{@pending_import_file_name}"
|
79
|
+
|
80
|
+
@constellation.Import(
|
81
|
+
topic: @pending_import_topic,
|
82
|
+
precursor_topic: new_topic,
|
83
|
+
import_role: @pending_import_role,
|
84
|
+
file_name: @pending_import_file_name
|
85
|
+
)
|
86
|
+
|
87
|
+
@pending_import_topic = nil
|
88
|
+
@pending_import_role = ''
|
89
|
+
@pending_import_file_name = ''
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
70
93
|
def compile input
|
71
94
|
include_language
|
72
95
|
|
@@ -87,10 +110,11 @@ module ActiveFacts
|
|
87
110
|
value = compile_definition ast
|
88
111
|
trace :definition, "Compiled to #{value.is_a?(Array) ? value.map{|v| v.verbalise}*', ' : value.verbalise}" if value
|
89
112
|
if value.is_a?(ActiveFacts::Metamodel::Topic)
|
90
|
-
topic_flood if @topic
|
113
|
+
topic_flood() if @topic
|
114
|
+
create_import_if_pending(value)
|
91
115
|
@topic = value
|
92
116
|
elsif ast.is_a?(Compiler::Vocabulary)
|
93
|
-
topic_flood if @topic
|
117
|
+
topic_flood() if @topic
|
94
118
|
@vocabulary = value
|
95
119
|
@topic = @constellation.Topic(@vocabulary.name)
|
96
120
|
end
|
@@ -104,33 +128,25 @@ module ActiveFacts
|
|
104
128
|
raise ne
|
105
129
|
end
|
106
130
|
end
|
107
|
-
topic_flood if @topic
|
131
|
+
topic_flood() if @topic
|
108
132
|
end
|
109
133
|
raise failure_reason unless ok
|
110
134
|
vocabulary
|
111
135
|
end
|
112
136
|
|
113
|
-
def compile_import file, aliases
|
137
|
+
def compile_import file, import_role, aliases
|
114
138
|
saved_index = @index
|
115
139
|
saved_block = @block
|
116
140
|
saved_string = @string
|
117
141
|
saved_input_length = @input_length
|
118
|
-
|
119
|
-
|
120
|
-
@
|
142
|
+
old_filepath = @filepath
|
143
|
+
@file = file
|
144
|
+
@filepath = import_filepath(old_filepath, file)
|
121
145
|
|
122
|
-
|
123
|
-
File.open(@filename) do |f|
|
124
|
-
topic_flood if @topic
|
125
|
-
@topic = @constellation.Topic(File.basename(@filename, '.cql'))
|
126
|
-
trace :import, "Importing #{@filename} as #{@topic.topic_name}" do
|
127
|
-
ok = parse_all(f.read, nil, &@block)
|
128
|
-
end
|
129
|
-
@topic = saved_topic
|
130
|
-
end
|
146
|
+
compile_import_file(@filepath, import_role)
|
131
147
|
|
132
148
|
rescue => e
|
133
|
-
ne = StandardError.new("In #{@
|
149
|
+
ne = StandardError.new("In #{@filepath} #{e.message.strip}")
|
134
150
|
ne.set_backtrace(e.backtrace)
|
135
151
|
raise ne
|
136
152
|
ensure
|
@@ -138,13 +154,52 @@ module ActiveFacts
|
|
138
154
|
@index = saved_index
|
139
155
|
@input_length = saved_input_length
|
140
156
|
@string = saved_string
|
141
|
-
@
|
157
|
+
@filepath = old_filepath
|
142
158
|
nil
|
143
159
|
end
|
144
160
|
|
145
|
-
#
|
146
|
-
def
|
147
|
-
|
161
|
+
# redefine in subsclass for different behaviour
|
162
|
+
def import_filepath(old_filepath, file)
|
163
|
+
filepath = ''
|
164
|
+
EXTENSIONS.each do |extension|
|
165
|
+
filepath = File.dirname(old_filepath)+'/'+file+".#{extension}"
|
166
|
+
break if File.exist?(filepath)
|
167
|
+
end
|
168
|
+
filepath
|
169
|
+
end
|
170
|
+
|
171
|
+
# redefine in subsclass for different behaviour
|
172
|
+
def compile_import_file filepath, import_role
|
173
|
+
# REVISIT: Save and use another @vocabulary for this file?
|
174
|
+
File.open(filepath) do |f|
|
175
|
+
compile_import_input(f.read, import_role)
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
def compile_import_input input, import_role
|
180
|
+
topic_external_name = @file
|
181
|
+
|
182
|
+
if existing_topic = @constellation.Topic[[topic_external_name]]
|
183
|
+
# topic has already been loaded, just build import
|
184
|
+
trace :import, "Topic #{@topic.topic_name} has already been loaded, skip reload"
|
185
|
+
import = @constellation.Import(
|
186
|
+
topic: @topic, precursor_topic: existing_topic,
|
187
|
+
import_role: import_role, file_name: topic_external_name
|
188
|
+
)
|
189
|
+
else
|
190
|
+
# topic has not been loaded previously, import topic
|
191
|
+
saved_topic = @topic
|
192
|
+
topic_flood() if @topic
|
193
|
+
|
194
|
+
@pending_import_topic = saved_topic
|
195
|
+
@pending_import_role = import_role
|
196
|
+
@pending_import_file_name = topic_external_name
|
197
|
+
|
198
|
+
trace :import, "Importing #{@filepath} into #{@topic.topic_name}" do
|
199
|
+
ok = parse_all(input, nil, &@block)
|
200
|
+
end
|
201
|
+
@topic = saved_topic
|
202
|
+
end
|
148
203
|
end
|
149
204
|
|
150
205
|
def compile_definition ast
|
@@ -22,7 +22,7 @@ module ActiveFacts
|
|
22
22
|
end
|
23
23
|
|
24
24
|
def refs
|
25
|
-
@phrases.select{|r| r.
|
25
|
+
@phrases.select{|r| r.is_a?(ActiveFacts::CQL::Compiler::Reference)}
|
26
26
|
end
|
27
27
|
|
28
28
|
# A clause that contains only the name of a ObjectType and no literal or reading text
|
@@ -142,7 +142,7 @@ module ActiveFacts
|
|
142
142
|
# no change is made to this Clause object - those will be done later.
|
143
143
|
#
|
144
144
|
def match_existing_fact_type context, options = {}
|
145
|
-
raise "Cannot match a clause that contains no object types" if refs.size == 0
|
145
|
+
raise "Cannot match a clause that contains no object types for #{self.inspect}" if refs.size == 0
|
146
146
|
raise "Internal error, clause already matched, should not match again" if @fact_type
|
147
147
|
|
148
148
|
if is_naked_object_type
|
@@ -184,7 +184,12 @@ module ActiveFacts
|
|
184
184
|
contract_left = proc do
|
185
185
|
contracted_from = left_contract_this_onto.refs[0]
|
186
186
|
contraction_player = contracted_from.player
|
187
|
-
contracted_role = Reference.new(
|
187
|
+
contracted_role = Reference.new(
|
188
|
+
term: contraction_player.name,
|
189
|
+
leading_adjective: contracted_from.leading_adjective,
|
190
|
+
trailing_adjective: contracted_from.trailing_adjective,
|
191
|
+
role_name: contracted_from.role_name
|
192
|
+
)
|
188
193
|
supposed_roles << contracted_role
|
189
194
|
left_insertion = contracted_role.inspect+' '
|
190
195
|
contracted_role.player = contracted_from.player
|
@@ -199,7 +204,12 @@ module ActiveFacts
|
|
199
204
|
contract_right = proc do
|
200
205
|
contracted_from = left_contract_this_onto.refs[-1]
|
201
206
|
contraction_player = contracted_from.player
|
202
|
-
contracted_role = Reference.new(
|
207
|
+
contracted_role = Reference.new(
|
208
|
+
term: contraction_player.name,
|
209
|
+
leading_adjective: contracted_from.leading_adjective,
|
210
|
+
trailing_adjective: contracted_from.trailing_adjective,
|
211
|
+
role_name: contracted_from.role_name
|
212
|
+
)
|
203
213
|
supposed_roles << contracted_role
|
204
214
|
right_insertion = ' '+contracted_role.inspect
|
205
215
|
contracted_role.player = contracted_from.player
|
@@ -306,7 +316,7 @@ module ActiveFacts
|
|
306
316
|
|
307
317
|
if equal_best.size > 1 and equal_best.detect{|k,m| !m.fact_type.is_a?(Metamodel::TypeInheritance)}
|
308
318
|
# Complain if there's more than one equivalent cost match (unless all are TypeInheritance):
|
309
|
-
|
319
|
+
trace :matching_fails, "#{@phrases.inspect} could match any of the following:\n\t"+
|
310
320
|
best_matches.map { |reading| reading.expand + " with " + matches[reading].describe } * "\n\t"
|
311
321
|
end
|
312
322
|
end
|
@@ -317,7 +327,14 @@ module ActiveFacts
|
|
317
327
|
@fact_type = @side_effects.fact_type
|
318
328
|
trace :matching, "Matched '#{@fact_type.default_reading}'"
|
319
329
|
@phrases = phrases
|
320
|
-
|
330
|
+
|
331
|
+
# REVISIT: Drop handling side-effects because roles are still required for processing conditions in transformation logic
|
332
|
+
# apply_side_effects(context, @side_effects)
|
333
|
+
@side_effects.role_side_effects.each do |side_effect|
|
334
|
+
phrase = side_effect.phrase
|
335
|
+
phrase.role_ref = side_effect.role_ref
|
336
|
+
end
|
337
|
+
|
321
338
|
return @fact_type
|
322
339
|
end
|
323
340
|
|
@@ -757,7 +774,7 @@ module ActiveFacts
|
|
757
774
|
|
758
775
|
if @qualifiers && @qualifiers.size > 0
|
759
776
|
# We shouldn't make a new ring constraint if there's already one over this ring.
|
760
|
-
existing_rcs =
|
777
|
+
existing_rcs =
|
761
778
|
@role_sequence.all_role_ref.map{|rr| rr.role.all_ring_constraint.to_a }.flatten.uniq
|
762
779
|
unless existing_rcs[0]
|
763
780
|
rc = RingConstraint.new(@role_sequence, @qualifiers)
|
@@ -904,6 +921,22 @@ module ActiveFacts
|
|
904
921
|
}}"
|
905
922
|
end
|
906
923
|
|
924
|
+
def var_name
|
925
|
+
if @role_name && !@role_name.is_a?(Integer)
|
926
|
+
@role_name
|
927
|
+
else
|
928
|
+
"#{
|
929
|
+
@leading_adjective && @leading_adjective.split.map(&:capitalize).join(' ') + ' '
|
930
|
+
}#{
|
931
|
+
@term
|
932
|
+
}#{
|
933
|
+
@trailing_adjective && ' ' + @trailing_adjective.split.map(&:capitalize).join(' ')
|
934
|
+
}#{
|
935
|
+
@role_name && "(#{@role_name})"
|
936
|
+
}"
|
937
|
+
end
|
938
|
+
end
|
939
|
+
|
907
940
|
def <=>(other)
|
908
941
|
( 4*(@term <=> other.term) +
|
909
942
|
2*((@leading_adjective||'') <=> (other.leading_adjective||'')) +
|
@@ -1038,7 +1071,7 @@ module ActiveFacts
|
|
1038
1071
|
end
|
1039
1072
|
|
1040
1073
|
def find_pc_over_roles(roles)
|
1041
|
-
|
1074
|
+
raise "No Role for embedded_presence_constraint" if roles.size == 0 # Safeguard; this would chuck an exception otherwise
|
1042
1075
|
roles[0].all_role_ref.each do |role_ref|
|
1043
1076
|
next if role_ref.role_sequence.all_role_ref.map(&:role) != roles
|
1044
1077
|
pc = role_ref.role_sequence.all_presence_constraint.single # Will return nil if there's more than one.
|
@@ -1049,7 +1082,7 @@ module ActiveFacts
|
|
1049
1082
|
end
|
1050
1083
|
|
1051
1084
|
def make_embedded_presence_constraint vocabulary
|
1052
|
-
|
1085
|
+
return unless @role_ref
|
1053
1086
|
fact_type = @role_ref.role.fact_type
|
1054
1087
|
constellation = vocabulary.constellation
|
1055
1088
|
|
@@ -1098,6 +1131,15 @@ module ActiveFacts
|
|
1098
1131
|
def result(context = nil)
|
1099
1132
|
self
|
1100
1133
|
end
|
1134
|
+
|
1135
|
+
def compile(context)
|
1136
|
+
identify_player(context)
|
1137
|
+
constellation = context.vocabulary.constellation
|
1138
|
+
constellation.Expression(
|
1139
|
+
:new, :expression_type => 'Role', :object_type => @player,
|
1140
|
+
:leading_adjective => leading_adjective, :trailing_adjective => trailing_adjective
|
1141
|
+
)
|
1142
|
+
end
|
1101
1143
|
end
|
1102
1144
|
|
1103
1145
|
# REVISIT: This needs to handle annotations for some/that/which, etc.
|
@@ -297,7 +297,7 @@ module ActiveFacts
|
|
297
297
|
|
298
298
|
# Create a query with a variable for every binding and all steps:
|
299
299
|
query = build_variables(clauses_list)
|
300
|
-
roles_by_binding = build_all_steps(clauses_list)
|
300
|
+
roles_by_binding = build_all_steps(query, clauses_list)
|
301
301
|
query.validate
|
302
302
|
|
303
303
|
# Create the projected RoleSequence for the constraint:
|
@@ -195,14 +195,6 @@ module ActiveFacts
|
|
195
195
|
end
|
196
196
|
end
|
197
197
|
|
198
|
-
=begin
|
199
|
-
def project lr
|
200
|
-
@projection = lr
|
201
|
-
projected_rr = lr == :left ? @e2 : @e1
|
202
|
-
true
|
203
|
-
end
|
204
|
-
=end
|
205
|
-
|
206
198
|
def inspect; to_s; end
|
207
199
|
|
208
200
|
def to_s
|
@@ -222,6 +214,15 @@ module ActiveFacts
|
|
222
214
|
@qualifiers.empty? ? '' : ', ['+@qualifiers*', '+']'
|
223
215
|
})"
|
224
216
|
end
|
217
|
+
|
218
|
+
def compile(context)
|
219
|
+
op1 = e1.compile(context)
|
220
|
+
op2 = e2.compile(context)
|
221
|
+
context.vocabulary.constellation.Expression(
|
222
|
+
:new, :expression_type => 'Binary', :operator => operator,
|
223
|
+
:first_operand_expression => op1, :second_operand_expression => op2
|
224
|
+
)
|
225
|
+
end
|
225
226
|
end
|
226
227
|
|
227
228
|
class Sum < Operation
|
@@ -254,19 +255,29 @@ module ActiveFacts
|
|
254
255
|
"SUM_OF<#{ @terms.map{|f| f.player.name}*', ' }>"
|
255
256
|
end
|
256
257
|
|
257
|
-
=begin
|
258
|
-
def result_value_type(context, name)
|
259
|
-
# REVISIT: If there are units involved, check compatibility
|
260
|
-
vt = super
|
261
|
-
vt
|
262
|
-
end
|
263
|
-
=end
|
264
|
-
|
265
258
|
def inspect; to_s; end
|
266
259
|
|
267
260
|
def to_s
|
268
261
|
'SUM(' + @terms.map{|term| "#{term.to_s}" } * ' PLUS ' + ')'
|
269
262
|
end
|
263
|
+
|
264
|
+
def compile(context)
|
265
|
+
compile_terms(context, @terms)
|
266
|
+
end
|
267
|
+
|
268
|
+
def compile_terms(context, terms)
|
269
|
+
if terms.size == 1
|
270
|
+
terms[0].compile(context)
|
271
|
+
else
|
272
|
+
lhs = terms.shift
|
273
|
+
lhs_expr = lhs.compile(context)
|
274
|
+
rhs_expr = compile_terms(context, terms)
|
275
|
+
context.vocabulary.constellation.Expression(
|
276
|
+
:new, :expression_type => 'Binary', :operator => operator,
|
277
|
+
:first_operand_expression => lhs_expr, :second_operand_expression => rhs_expr
|
278
|
+
)
|
279
|
+
end
|
280
|
+
end
|
270
281
|
end
|
271
282
|
|
272
283
|
class Product < Operation
|
@@ -298,19 +309,29 @@ module ActiveFacts
|
|
298
309
|
"PRODUCT_OF<#{ @factors.map{|f| f.player.name}*' ' }>"
|
299
310
|
end
|
300
311
|
|
301
|
-
=begin
|
302
|
-
def result_value_type(context, name)
|
303
|
-
vt = super
|
304
|
-
# REVISIT: If there are units involved, create the result units
|
305
|
-
vt
|
306
|
-
end
|
307
|
-
=end
|
308
|
-
|
309
312
|
def inspect; to_s; end
|
310
313
|
|
311
314
|
def to_s
|
312
315
|
'PRODUCT(' + @factors.map{|factor| "#{factor.to_s}" } * ' TIMES ' + ')'
|
313
316
|
end
|
317
|
+
|
318
|
+
def compile(context)
|
319
|
+
compile_factors(context, @factors)
|
320
|
+
end
|
321
|
+
|
322
|
+
def compile_factors(context, factors)
|
323
|
+
if factors.size == 1
|
324
|
+
factors[0].compile(context)
|
325
|
+
else
|
326
|
+
lhs = factors.shift
|
327
|
+
lhs_expr = lhs.compile(context)
|
328
|
+
rhs_expr = compile_factors(context, factors)
|
329
|
+
context.vocabulary.constellation.Expression(
|
330
|
+
:new, :expression_type => 'Binary', :operator => operator,
|
331
|
+
:first_operand_expression => lhs_expr, :second_operand_expression => rhs_expr
|
332
|
+
)
|
333
|
+
end
|
334
|
+
end
|
314
335
|
end
|
315
336
|
|
316
337
|
class Reciprocal < Operation
|
@@ -337,17 +358,19 @@ module ActiveFacts
|
|
337
358
|
end
|
338
359
|
end
|
339
360
|
|
340
|
-
=begin
|
341
|
-
def result_type_name(context)
|
342
|
-
raise hell
|
343
|
-
end
|
344
|
-
=end
|
345
|
-
|
346
361
|
def inspect; to_s; end
|
347
362
|
|
348
363
|
def to_s
|
349
364
|
"RECIPROCAL(#{factor.to_s})"
|
350
365
|
end
|
366
|
+
|
367
|
+
def compile(context)
|
368
|
+
op1 = @divisor.compile(context)
|
369
|
+
context.vocabulary.constellation.Expression(
|
370
|
+
:new, :expression_type => 'Unary', :operator => operator,
|
371
|
+
:first_operand_expression => op1
|
372
|
+
)
|
373
|
+
end
|
351
374
|
end
|
352
375
|
|
353
376
|
class Negate
|
@@ -368,16 +391,227 @@ module ActiveFacts
|
|
368
391
|
end
|
369
392
|
end
|
370
393
|
|
371
|
-
|
394
|
+
def inspect; to_s; end
|
395
|
+
|
396
|
+
def to_s
|
397
|
+
"NEGATIVE(#{term.to_s})"
|
398
|
+
end
|
399
|
+
|
400
|
+
def compile(context)
|
401
|
+
op1 = @term.compile(context)
|
402
|
+
context.vocabulary.constellation.Expression(
|
403
|
+
:new, :expression_type => 'Unary', :operator => operator,
|
404
|
+
:first_operand_expression => op1
|
405
|
+
)
|
406
|
+
end
|
407
|
+
end
|
408
|
+
|
409
|
+
class Negation
|
410
|
+
attr_accessor :term
|
411
|
+
def initialize term
|
412
|
+
@term = term
|
413
|
+
end
|
414
|
+
|
415
|
+
def operator
|
416
|
+
'not'
|
417
|
+
end
|
418
|
+
|
419
|
+
def identify_player context
|
420
|
+
@player || begin
|
421
|
+
# The player in @term have already been identified
|
422
|
+
v = context.vocabulary
|
423
|
+
@player = @term.player
|
424
|
+
end
|
425
|
+
end
|
426
|
+
|
427
|
+
def inspect; to_s; end
|
428
|
+
|
429
|
+
def to_s
|
430
|
+
"NEGATION(#{term.to_s})"
|
431
|
+
end
|
432
|
+
|
433
|
+
def compile(context)
|
434
|
+
op1 = @term.compile(context)
|
435
|
+
context.vocabulary.constellation.Expression(
|
436
|
+
:new, :expression_type => 'Unary', :operator => operator,
|
437
|
+
:first_operand_expression => op1
|
438
|
+
)
|
439
|
+
end
|
440
|
+
end
|
441
|
+
|
442
|
+
class LogicalAnd < Operation
|
443
|
+
attr_accessor :factors
|
444
|
+
def initialize *factors
|
445
|
+
@factors = factors
|
446
|
+
end
|
447
|
+
|
448
|
+
def refs
|
449
|
+
@factors
|
450
|
+
end
|
451
|
+
|
452
|
+
def operator
|
453
|
+
'and'
|
454
|
+
end
|
455
|
+
|
456
|
+
def identify_player context
|
457
|
+
@player || begin
|
458
|
+
v = context.vocabulary
|
459
|
+
@player = @factors[0].player
|
460
|
+
end
|
461
|
+
end
|
462
|
+
|
372
463
|
def result_type_name(context)
|
373
|
-
|
464
|
+
"CONJUNCTION_OF<#{ @factors.map{|f| f.player.name}*' ' }>"
|
374
465
|
end
|
375
|
-
=end
|
376
466
|
|
377
467
|
def inspect; to_s; end
|
378
468
|
|
379
469
|
def to_s
|
380
|
-
"
|
470
|
+
'CONJUNCTION(' + @factors.map{|factor| "#{factor.to_s}" } * ' AND ' + ')'
|
471
|
+
end
|
472
|
+
|
473
|
+
def compile(context)
|
474
|
+
compile_factors(context, @factors)
|
475
|
+
end
|
476
|
+
|
477
|
+
def compile_factors(context, factors)
|
478
|
+
if factors.size == 1
|
479
|
+
factors[0].compile(context)
|
480
|
+
else
|
481
|
+
lhs = factors.shift
|
482
|
+
lhs_expr = lhs.compile(context)
|
483
|
+
rhs_expr = compile_factors(context, factors)
|
484
|
+
context.vocabulary.constellation.Expression(
|
485
|
+
:new, :expression_type => 'Binary', :operator => operator,
|
486
|
+
:first_operand_expression => lhs_expr, :second_operand_expression => rhs_expr
|
487
|
+
)
|
488
|
+
end
|
489
|
+
end
|
490
|
+
end
|
491
|
+
|
492
|
+
class LogicalOr < Operation
|
493
|
+
attr_accessor :factors
|
494
|
+
def initialize *factors
|
495
|
+
@factors = factors
|
496
|
+
end
|
497
|
+
|
498
|
+
def refs
|
499
|
+
@factors
|
500
|
+
end
|
501
|
+
|
502
|
+
def operator
|
503
|
+
'or'
|
504
|
+
end
|
505
|
+
|
506
|
+
def identify_player context
|
507
|
+
@player || begin
|
508
|
+
v = context.vocabulary
|
509
|
+
@player = @factors[0].player
|
510
|
+
end
|
511
|
+
end
|
512
|
+
|
513
|
+
def result_type_name(context)
|
514
|
+
"DISJUNCTION_OF<#{ @factors.map{|f| f.player.name}*' ' }>"
|
515
|
+
end
|
516
|
+
|
517
|
+
def inspect; to_s; end
|
518
|
+
|
519
|
+
def to_s
|
520
|
+
'DISJUNCTION(' + @factors.map{|factor| "#{factor.to_s}" } * ' OR ' + ')'
|
521
|
+
end
|
522
|
+
|
523
|
+
def compile(context)
|
524
|
+
compile_factors(context, @factors)
|
525
|
+
end
|
526
|
+
|
527
|
+
def compile_factors(context, factors)
|
528
|
+
if factors.size == 1
|
529
|
+
factors[0].compile(context)
|
530
|
+
else
|
531
|
+
lhs = factors.shift
|
532
|
+
lhs_expr = lhs.compile(context)
|
533
|
+
rhs_expr = compile_factors(context, factors)
|
534
|
+
context.vocabulary.constellation.Expression(
|
535
|
+
:new, :expression_type => 'Binary', :operator => operator,
|
536
|
+
:first_operand_expression => lhs_expr, :second_operand_expression => rhs_expr
|
537
|
+
)
|
538
|
+
end
|
539
|
+
end
|
540
|
+
end
|
541
|
+
|
542
|
+
class Ternary < Operation
|
543
|
+
attr_accessor :condition, :true_value, :false_value
|
544
|
+
def initialize condition, true_value, false_value
|
545
|
+
@condition = condition
|
546
|
+
@true_value = true_value
|
547
|
+
@false_value = false_value
|
548
|
+
end
|
549
|
+
|
550
|
+
def refs
|
551
|
+
[@condition, @true_value, @false_value]
|
552
|
+
end
|
553
|
+
|
554
|
+
def operator
|
555
|
+
'?'
|
556
|
+
end
|
557
|
+
|
558
|
+
def identify_player context
|
559
|
+
@player || begin
|
560
|
+
v = context.vocabulary
|
561
|
+
@player = @true_value.player
|
562
|
+
end
|
563
|
+
end
|
564
|
+
|
565
|
+
def inspect; to_s; end
|
566
|
+
|
567
|
+
def to_s
|
568
|
+
"TERNARY(#{@condition.to_s}, #{@true_value.to_s}, #{@false_value.to_s})"
|
569
|
+
end
|
570
|
+
|
571
|
+
def compile(context)
|
572
|
+
op1 = @condition.compile(context)
|
573
|
+
op2 = @true_value.compile(context)
|
574
|
+
op3 = @false_value.compile(context)
|
575
|
+
context.vocabulary.constellation.Expression(
|
576
|
+
:new, :expression_type => 'Ternary', :operator => operator,
|
577
|
+
:first_operand_expression => op1, :second_operand_expression => op2, :third_operand_expression => op3
|
578
|
+
)
|
579
|
+
end
|
580
|
+
end
|
581
|
+
|
582
|
+
class Aggregate < Operation
|
583
|
+
attr_accessor :operation, :aggregand
|
584
|
+
def initialize operation, aggregand
|
585
|
+
@operation = operation
|
586
|
+
@aggregand = aggregand
|
587
|
+
end
|
588
|
+
|
589
|
+
def refs
|
590
|
+
[@operation, @aggregand]
|
591
|
+
end
|
592
|
+
|
593
|
+
def operator
|
594
|
+
operation
|
595
|
+
end
|
596
|
+
|
597
|
+
def identify_player context
|
598
|
+
@player || begin
|
599
|
+
@player = @aggregand.player
|
600
|
+
end
|
601
|
+
end
|
602
|
+
|
603
|
+
def inspect; to_s; end
|
604
|
+
|
605
|
+
def to_s
|
606
|
+
"AGGREGATE(#{@operation.to_s}, #{@aggregand.to_s})"
|
607
|
+
end
|
608
|
+
|
609
|
+
def compile(context)
|
610
|
+
op1 = @aggregand.compile(context)
|
611
|
+
context.vocabulary.constellation.Expression(
|
612
|
+
:new, :expression_type => 'Unary', :operator => operator,
|
613
|
+
:first_operand_expression => op1
|
614
|
+
)
|
381
615
|
end
|
382
616
|
end
|
383
617
|
|
@@ -436,8 +670,19 @@ module ActiveFacts
|
|
436
670
|
def binding
|
437
671
|
@binding
|
438
672
|
end
|
673
|
+
|
674
|
+
def compile(context)
|
675
|
+
literal_string = case @literal
|
676
|
+
when String; "'#{@literal.to_s}'"
|
677
|
+
else @literal.to_s
|
678
|
+
end
|
679
|
+
context.vocabulary.constellation.Expression(
|
680
|
+
:new, :expression_type => 'Literal', :literal_string => literal_string
|
681
|
+
)
|
682
|
+
end
|
439
683
|
end
|
440
684
|
|
441
685
|
end
|
686
|
+
|
442
687
|
end
|
443
688
|
end
|