activefacts 0.8.10 → 0.8.12
Sign up to get free protection for your applications and to get access to all the features.
- data/Rakefile +3 -2
- data/bin/afgen +25 -23
- data/bin/cql +9 -8
- data/css/orm2.css +23 -3
- data/examples/CQL/CompanyDirectorEmployee.cql +1 -1
- data/examples/CQL/Diplomacy.cql +3 -3
- data/examples/CQL/Insurance.cql +27 -21
- data/examples/CQL/Metamodel.cql +12 -8
- data/examples/CQL/MetamodelNext.cql +172 -149
- data/examples/CQL/ServiceDirector.cql +17 -17
- data/examples/CQL/Supervision.cql +3 -5
- data/examples/CQL/WaiterTips.cql +1 -1
- data/examples/CQL/unit.cql +1 -1
- data/index.html +0 -0
- data/lib/activefacts/cql/FactTypes.treetop +41 -8
- data/lib/activefacts/cql/Language/English.treetop +10 -0
- data/lib/activefacts/cql/ObjectTypes.treetop +3 -1
- data/lib/activefacts/cql/Terms.treetop +34 -53
- data/lib/activefacts/cql/compiler.rb +1 -1
- data/lib/activefacts/cql/compiler/clause.rb +21 -8
- data/lib/activefacts/cql/compiler/constraint.rb +3 -1
- data/lib/activefacts/cql/compiler/entity_type.rb +1 -1
- data/lib/activefacts/cql/compiler/fact_type.rb +9 -3
- data/lib/activefacts/cql/compiler/join.rb +3 -0
- data/lib/activefacts/cql/compiler/value_type.rb +9 -4
- data/lib/activefacts/cql/parser.rb +11 -3
- data/lib/activefacts/generate/oo.rb +3 -3
- data/lib/activefacts/generate/ordered.rb +0 -4
- data/lib/activefacts/input/orm.rb +305 -250
- data/lib/activefacts/persistence/tables.rb +6 -0
- data/lib/activefacts/support.rb +18 -0
- data/lib/activefacts/version.rb +1 -1
- data/lib/activefacts/vocabulary/extensions.rb +59 -20
- data/lib/activefacts/vocabulary/metamodel.rb +23 -13
- data/lib/activefacts/vocabulary/verbaliser.rb +5 -3
- data/spec/absorption_spec.rb +3 -2
- data/spec/cql/comparison_spec.rb +1 -3
- data/spec/cql/context_spec.rb +1 -1
- data/spec/cql/entity_type_spec.rb +2 -2
- data/spec/cql/expressions_spec.rb +2 -4
- data/spec/cql/fact_type_matching_spec.rb +55 -3
- data/spec/cql/parser/fact_types_spec.rb +3 -0
- data/spec/cql/role_matching_spec.rb +8 -7
- data/spec/cql/samples_spec.rb +10 -2
- data/spec/cql_dm_spec.rb +2 -1
- data/spec/helpers/array_matcher.rb +18 -35
- data/spec/helpers/diff_matcher.rb +34 -13
- data/spec/helpers/file_matcher.rb +27 -43
- data/spec/helpers/string_matcher.rb +23 -33
- data/spec/norma_cql_spec.rb +1 -0
- data/spec/norma_tables_spec.rb +1 -2
- metadata +95 -102
@@ -58,14 +58,14 @@ Data Store is one Major-Version;
|
|
58
58
|
Data Store is one Minor-Version;
|
59
59
|
Data Store is one Revision-Version;
|
60
60
|
|
61
|
-
Date is identified by
|
62
|
-
Date has one
|
63
|
-
|
64
|
-
Credential has at most one Expiration-Date;
|
61
|
+
Date Time YMDHMS is identified by MDYHMS where
|
62
|
+
Date Time YMDHMS has one MDYHMS,
|
63
|
+
MDYHMS is of at most one Date Time YMDHMS;
|
65
64
|
|
66
|
-
|
67
|
-
|
68
|
-
|
65
|
+
DateYYMMDD is identified by DDMMYYYY where
|
66
|
+
DateYYMMDD has one DDMMYYYY,
|
67
|
+
DDMMYYYY is of at most one DateYYMMDD;
|
68
|
+
Credential has at most one Expiration-DateYYMMDD;
|
69
69
|
|
70
70
|
Duration is identified by Seconds where
|
71
71
|
Duration has one Seconds,
|
@@ -126,16 +126,16 @@ Recurring Schedule wednesday;
|
|
126
126
|
|
127
127
|
Satellite Message is identified by its Id;
|
128
128
|
Satellite Message is designated for one Data Store;
|
129
|
-
Satellite Message one Insertion-Date Time;
|
130
129
|
Satellite Message has at most one Message Data;
|
131
130
|
Satellite Message has at most one Message Header;
|
132
131
|
Satellite Message is of at most one Provider Type;
|
133
132
|
Satellite Message has one Serial Number;
|
133
|
+
Satellite Message has one insertion-Date Time YMDHMS;
|
134
134
|
|
135
135
|
Subscription is identified by its Nr;
|
136
136
|
Company has one Driver- Tech Subscription;
|
137
|
-
Subscription has one Beginning-
|
138
|
-
Subscription has at most one Ending-
|
137
|
+
Subscription has one Beginning-DateYYMMDD;
|
138
|
+
Subscription has at most one Ending-DateYYMMDD;
|
139
139
|
Subscription is enabled;
|
140
140
|
|
141
141
|
Switch is identified by its Id;
|
@@ -155,10 +155,10 @@ Switch is backup updates;
|
|
155
155
|
Switch is send disabled;
|
156
156
|
Switch is test vectors enabled;
|
157
157
|
|
158
|
-
|
159
|
-
|
160
|
-
HHMMSS is of at most one
|
161
|
-
Recurring Schedule has one Start-
|
158
|
+
TimeHMS is identified by HHMMSS where
|
159
|
+
TimeHMS has one HHMMSS,
|
160
|
+
HHMMSS is of at most one TimeHMS;
|
161
|
+
Recurring Schedule has one Start-TimeHMS;
|
162
162
|
|
163
163
|
Transaction is identified by its Nr;
|
164
164
|
Satellite Message has at most one Group-Transaction;
|
@@ -210,11 +210,11 @@ Data Store Service has one Subscription;
|
|
210
210
|
*/
|
211
211
|
either Company is client or Company is vendor but not both;
|
212
212
|
for each Credential exactly one of these holds:
|
213
|
-
Data Store(2) requires Credential,
|
213
|
+
Data Store (2) requires Credential,
|
214
214
|
Data Store Service requires Credential,
|
215
215
|
Vendor requires Credential,
|
216
216
|
Data Store File Host System has Internal-Credential,
|
217
|
-
Data Store(1) has Internal-Credential;
|
217
|
+
Data Store (1) has Internal-Credential;
|
218
218
|
for each Network exactly one of these holds:
|
219
219
|
Network is used by Host System,
|
220
220
|
Company has Origin-Network,
|
@@ -227,7 +227,7 @@ either Host System runs Switch or Data Store has Legacy-Switch but not both;
|
|
227
227
|
for each Network at most one of these holds:
|
228
228
|
Network is ip_single,
|
229
229
|
Network has Ending-IP;
|
230
|
-
Data Store Service (
|
230
|
+
Data Store Service (in which Service is from Data Store) belongs to Client
|
231
231
|
if and only if
|
232
232
|
Client has default Data Store;
|
233
233
|
Network has Ending IP
|
@@ -28,12 +28,10 @@ CEO runs Company,
|
|
28
28
|
/*
|
29
29
|
* Constraints:
|
30
30
|
*/
|
31
|
-
either Employee reports to Manager(
|
31
|
+
either Employee reports to Manager(1) or Employee is a Manager(2) that is a CEO that runs Company but not both;
|
32
32
|
Employee is a Manager that is a CEO that runs Company
|
33
33
|
if and only if
|
34
34
|
Employee works for Company;
|
35
|
-
|
36
|
-
// This constraint cannot be expressed in NORMA until it adds explicit join paths:
|
37
|
-
Employee(2) reports to Manager that is a kind of Employee(1) that works for Company
|
35
|
+
Employee(1) reports to Manager that is a kind of Employee(2) that works for Company
|
38
36
|
if and only if
|
39
|
-
Employee(
|
37
|
+
Employee(1) works for Company;
|
data/examples/CQL/WaiterTips.cql
CHANGED
@@ -28,6 +28,6 @@ Service earned a tip of at most one Amount;
|
|
28
28
|
/*
|
29
29
|
* Constraints:
|
30
30
|
*/
|
31
|
-
Service (
|
31
|
+
Service (in which Waiter served Meal) earned a tip of Amount
|
32
32
|
if and only if
|
33
33
|
Waiter for serving Meal reported a tip of Amount;
|
data/examples/CQL/unit.cql
CHANGED
data/index.html
CHANGED
File without changes
|
@@ -64,6 +64,8 @@ module ActiveFacts
|
|
64
64
|
|
65
65
|
rule joined_clauses
|
66
66
|
qualified_clauses
|
67
|
+
# REVISIT: This creates no precedence between and/or, which could cause confusion.
|
68
|
+
# Should disallow mixed conjuntions - using a sempred?
|
67
69
|
ftail:( conjunction:(',' / and / or ) s qualified_clauses s )*
|
68
70
|
{
|
69
71
|
def ast
|
@@ -192,13 +194,13 @@ module ActiveFacts
|
|
192
194
|
end
|
193
195
|
|
194
196
|
rule condition_contraction
|
195
|
-
role
|
197
|
+
role pq:post_qualifiers? q:qualifier? s comparator s e2:expression
|
196
198
|
!phrase # The contracted_clauses must not continue here!
|
197
199
|
{
|
198
200
|
def ast
|
199
201
|
c = Compiler::Comparison.new(comparator.text_value, role.ast, e2.ast, q.empty? ? [] : [q.text_value])
|
200
202
|
c.conjunction = comparator.text_value
|
201
|
-
[ role.ast,
|
203
|
+
[ role.ast, pq.empty? ? [] : pq.list, c ]
|
202
204
|
end
|
203
205
|
}
|
204
206
|
end
|
@@ -248,9 +250,36 @@ module ActiveFacts
|
|
248
250
|
}
|
249
251
|
end
|
250
252
|
|
251
|
-
# This is the rule that causes most back-tracking. I think you can see why.
|
252
253
|
rule role
|
253
|
-
|
254
|
+
aggregate
|
255
|
+
/
|
256
|
+
simple_role
|
257
|
+
end
|
258
|
+
|
259
|
+
rule aggregate
|
260
|
+
aggregate:id s of s term s # REVISIT: this term may need to pre-scanned in the qualified_clauses
|
261
|
+
in s '('
|
262
|
+
qualified_clauses # REVISIT: Need to test to verify this is the right level (not joined_clauses, etc)
|
263
|
+
s ')'
|
264
|
+
{
|
265
|
+
def ast
|
266
|
+
raise "Not implemented: AST for '#{aggregate.text_value} of #{term.text_value}'"
|
267
|
+
# This returns just the role with the nested clauses, which doesn;t even work:
|
268
|
+
term.ast(
|
269
|
+
nil, # No quantifier
|
270
|
+
nil, # No function call
|
271
|
+
nil, # No role_name
|
272
|
+
nil, # No value_constraint
|
273
|
+
nil, # No literal
|
274
|
+
qualified_clauses.ast
|
275
|
+
)
|
276
|
+
end
|
277
|
+
}
|
278
|
+
end
|
279
|
+
|
280
|
+
# This is the rule that causes most back-tracking. I think you can see why.
|
281
|
+
rule simple_role
|
282
|
+
q:(quantifier enforcement cn:context_note?)?
|
254
283
|
player:derived_variable
|
255
284
|
lr:(
|
256
285
|
literal u:unit?
|
@@ -261,12 +290,16 @@ module ActiveFacts
|
|
261
290
|
{
|
262
291
|
def ast
|
263
292
|
if !q.empty? && q.quantifier.value
|
264
|
-
quantifier = Compiler::Quantifier.new(
|
293
|
+
quantifier = Compiler::Quantifier.new(
|
294
|
+
q.quantifier.value[0],
|
295
|
+
q.quantifier.value[1],
|
296
|
+
q.enforcement.ast,
|
297
|
+
q.cn.empty? ? nil : q.cn.ast
|
298
|
+
)
|
265
299
|
end
|
266
300
|
if !lr.empty?
|
267
301
|
if lr.respond_to?(:literal)
|
268
|
-
literal = lr.literal.value
|
269
|
-
raise "Literals with units are not yet processed" unless lr.u.empty?
|
302
|
+
literal = Compiler::Literal.new(lr.literal.value, lr.u.empty? ? nil : lr.u.text_value)
|
270
303
|
end
|
271
304
|
value_constraint = Compiler::ValueConstraint.new(lr.value_constraint.ranges, lr.value_constraint.units, lr.enforcement.ast) if lr.respond_to?(:value_constraint)
|
272
305
|
raise "It is not permitted to provide both a literal value and a value constraint" if value_constraint and literal
|
@@ -286,7 +319,7 @@ module ActiveFacts
|
|
286
319
|
end
|
287
320
|
|
288
321
|
rule objectification_join
|
289
|
-
'(' s
|
322
|
+
'(' s in_which s facts:joined_clauses s ')' s
|
290
323
|
{
|
291
324
|
def ast
|
292
325
|
facts.ast
|
@@ -38,6 +38,12 @@ module ActiveFacts
|
|
38
38
|
{ def independent; !i.empty?; end }
|
39
39
|
end
|
40
40
|
|
41
|
+
rule in_which # Introduce an objectification join
|
42
|
+
where / # Old syntax
|
43
|
+
in s which # preferred syntax
|
44
|
+
{ def independent; !i.empty?; end }
|
45
|
+
end
|
46
|
+
|
41
47
|
# Units conversion keyword
|
42
48
|
rule conversion
|
43
49
|
converts s a:(approximately s)? to s
|
@@ -240,6 +246,7 @@ module ActiveFacts
|
|
240
246
|
rule feminine 'feminine' !alphanumeric end
|
241
247
|
rule identified ('known'/'identified') !alphanumeric end
|
242
248
|
rule if 'if' !alphanumeric end
|
249
|
+
rule in 'in' !alphanumeric end
|
243
250
|
rule import 'import' !alphanumeric end
|
244
251
|
rule independent 'independent' !alphanumeric end
|
245
252
|
rule intransitive 'intransitive' !alphanumeric end
|
@@ -250,6 +257,7 @@ module ActiveFacts
|
|
250
257
|
rule maybe 'maybe' !alphanumeric end
|
251
258
|
rule only 'only' !alphanumeric end
|
252
259
|
rule or 'or' !alphanumeric end
|
260
|
+
rule of 'of' !alphanumeric end
|
253
261
|
rule ordering_prefix by s (ascending/descending)? s end
|
254
262
|
rule otherwise 'otherwise' !alphanumeric end
|
255
263
|
rule partitioned 'partitioned' !alphanumeric end
|
@@ -271,6 +279,8 @@ module ActiveFacts
|
|
271
279
|
rule true 'true' !alphanumeric end
|
272
280
|
rule vocabulary 'vocabulary' !alphanumeric end
|
273
281
|
rule where 'where' !alphanumeric end
|
282
|
+
rule which 'which' !alphanumeric end
|
283
|
+
rule was 'was' !alphanumeric end
|
274
284
|
rule who 'who' !alphanumeric end
|
275
285
|
|
276
286
|
end
|
@@ -164,7 +164,9 @@ module ActiveFacts
|
|
164
164
|
end
|
165
165
|
|
166
166
|
rule mapping_pragma
|
167
|
-
(independent / separate / partitioned / personal / feminine / masculine
|
167
|
+
(independent / separate / partitioned / personal / feminine / masculine /
|
168
|
+
was s names:(id s)+ { def text_value; [ was.text_value, names.elements.map{|n|n.text_value} ]; end }
|
169
|
+
)
|
168
170
|
{ def value; text_value; end }
|
169
171
|
end
|
170
172
|
|
@@ -8,19 +8,8 @@ module ActiveFacts
|
|
8
8
|
module CQL
|
9
9
|
grammar Terms
|
10
10
|
rule term_definition_name
|
11
|
-
id s
|
12
|
-
|
13
|
-
{
|
14
|
-
def value
|
15
|
-
t.elements.inject([
|
16
|
-
id.value
|
17
|
-
]){|a, e| a << e.id.value}*' '
|
18
|
-
end
|
19
|
-
|
20
|
-
def node_type
|
21
|
-
:term
|
22
|
-
end
|
23
|
-
}
|
11
|
+
id s t:(!non_term_def id s)*
|
12
|
+
<Parser::TermDefinitionNameNode>
|
24
13
|
end
|
25
14
|
|
26
15
|
rule non_term_def
|
@@ -69,12 +58,15 @@ module ActiveFacts
|
|
69
58
|
&{|s| input.context.reset_role_names }
|
70
59
|
(
|
71
60
|
context_note # Context notes have different lexical conventions
|
72
|
-
/ '(' as S term_definition_name s ')' s #
|
61
|
+
/ '(' as S term_definition_name s ')' s # Prepare for a Role Name
|
73
62
|
&{|s| input.context.role_name(s[3].value) }
|
74
|
-
/ new_derived_value
|
75
|
-
/ new_adjective_term #
|
63
|
+
/ new_derived_value # Prepare for a derived term
|
64
|
+
/ new_adjective_term # Prepae for an existing term with new Adjectives
|
65
|
+
# The remaining rules exist to correctly eat up anything that doesn't match the above:
|
76
66
|
/ global_term # If we see A B - C D, don't recognise B as a new adjective for C D.
|
67
|
+
/ prescan_aggregate
|
77
68
|
/ id
|
69
|
+
# / literal # REVISIT: Literals might contain "(as Foo)" and mess things up
|
78
70
|
/ range # Covers all numbers and strings
|
79
71
|
/ comparator # handle two-character operators
|
80
72
|
/ S # White space and comments, must precede / and *
|
@@ -82,13 +74,9 @@ module ActiveFacts
|
|
82
74
|
)* [?;] s
|
83
75
|
end
|
84
76
|
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
def value
|
89
|
-
tail.elements.map{|e| e.id.text_value}
|
90
|
-
end
|
91
|
-
}
|
77
|
+
# Not sure this is even needed, but it doesn't seem to hurt:
|
78
|
+
rule prescan_aggregate
|
79
|
+
aggregate_type:id s of s global_term in s &'('
|
92
80
|
end
|
93
81
|
|
94
82
|
rule new_derived_value
|
@@ -105,12 +93,24 @@ module ActiveFacts
|
|
105
93
|
}
|
106
94
|
end
|
107
95
|
|
96
|
+
# Derived values are new terms introduced by an = sign before an expression
|
97
|
+
# This rule handles trailing words of a multi-word derived value
|
98
|
+
rule derived_value_continuation
|
99
|
+
s '-' tail:(s !global_term !(that/who) id)*
|
100
|
+
{
|
101
|
+
def value
|
102
|
+
tail.elements.map{|e| e.id.text_value}
|
103
|
+
end
|
104
|
+
}
|
105
|
+
end
|
106
|
+
|
107
|
+
# Used during the pre-scan, match a term with new adjective(s)
|
108
108
|
rule new_adjective_term
|
109
|
-
!global_term adj:id '-' lead_intervening s global_term # Definitely a new leading adjective for this term
|
110
|
-
&{|s|
|
109
|
+
!global_term adj:id '-' '-'? lead_intervening s global_term # Definitely a new leading adjective for this term
|
110
|
+
&{|s| adj = [s[1].text_value, s[4].value].compact*" "; input.context.new_leading_adjective_term(adj, s[6].text_value) }
|
111
111
|
/
|
112
|
-
global_term s trail_intervening '-' !global_term adj:id # Definitely a new trailing adjective for this term
|
113
|
-
&{|s|
|
112
|
+
global_term s trail_intervening '-' '-'? !global_term adj:id # Definitely a new trailing adjective for this term
|
113
|
+
&{|s| adj = [s[2].value, s[6].text_value].compact*" "; input.context.new_trailing_adjective_term(adj, s[0].text_value) }
|
114
114
|
end
|
115
115
|
|
116
116
|
rule lead_intervening # Words intervening between a new adjective and the term
|
@@ -133,33 +133,14 @@ module ActiveFacts
|
|
133
133
|
|
134
134
|
# This is the rule to use after the prescan; it only succeeds on a complete term or role reference
|
135
135
|
rule term
|
136
|
-
s head:id x
|
137
|
-
tail:(
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
t = x.context[:term]
|
142
|
-
gt = x.context[:global_term]
|
143
|
-
leading_adjective = t[0...-gt.size-1] if t.size > gt.size and t[-gt.size..-1] == gt
|
144
|
-
trailing_adjective = t[gt.size+1..-1] if t.size > gt.size and t[0...gt.size] == gt
|
145
|
-
Compiler::VarRef.new(gt, leading_adjective, trailing_adjective, quantifier, function_call, role_name, value_constraint, literal, nested_clauses)
|
146
|
-
end
|
147
|
-
|
148
|
-
def value # Sometimes we just want the full term name
|
149
|
-
x.context[:term]
|
150
|
-
end
|
151
|
-
def node_type; :term; end
|
152
|
-
}
|
136
|
+
s head:id x &{|s| w = s[1].text_value; input.context.term_starts?(w, s[2]) }
|
137
|
+
tail:(
|
138
|
+
s '-'? dbl:'-'? s w:id &{|s| w = s[4].text_value; input.context.term_continues?(w) }
|
139
|
+
)* &{|s| input.context.term_complete? }
|
140
|
+
<Parser::TermNode>
|
153
141
|
/
|
154
|
-
s head:id '-' s term &{|s| s[
|
155
|
-
|
156
|
-
def ast quantifier = nil, function_call = nil, role_name = nil, value_constraint = nil, literal = nil, nested_clauses = nil
|
157
|
-
ast = term.ast(quantifier, function_call, role_name, value_constraint, literal, nested_clauses)
|
158
|
-
ast.leading_adjective = head.text_value
|
159
|
-
ast
|
160
|
-
end
|
161
|
-
def node_type; :term; end
|
162
|
-
}
|
142
|
+
s head:id '-' '-'? s term &{|s| s[5].ast.leading_adjective == nil }
|
143
|
+
<Parser::TermLANode>
|
163
144
|
end
|
164
145
|
|
165
146
|
rule x
|
@@ -72,7 +72,7 @@ module ActiveFacts
|
|
72
72
|
|
73
73
|
def unit? s
|
74
74
|
name = @constellation.Name[s]
|
75
|
-
units = !name ? [] : name.
|
75
|
+
units = (!name ? [] : Array(name.unit) + Array(name.unit_as_plural_name)).uniq
|
76
76
|
debug :units, "Looking for unit #{s}, got #{units.map{|u|u.name}.inspect}"
|
77
77
|
units.size > 0
|
78
78
|
end
|
@@ -351,13 +351,15 @@ module ActiveFacts
|
|
351
351
|
if la = role_ref.leading_adjective and !la.empty?
|
352
352
|
# The leading adjectives must match, one way or another
|
353
353
|
la = la.split(/\s+/)
|
354
|
-
|
354
|
+
# We may have hyphenated adjectives. Break them up to check:
|
355
|
+
iw = intervening_words.map{|w| w.split(/-/)}.flatten
|
356
|
+
return nil unless la[0,iw.size] == iw
|
355
357
|
# Any intervening_words matched, see what remains
|
356
|
-
la.slice!(0,
|
358
|
+
la.slice!(0, iw.size)
|
357
359
|
|
358
360
|
# If there were intervening_words, the remaining reading adjectives must match the phrase's leading_adjective exactly.
|
359
361
|
phrase_la = (next_player_phrase.leading_adjective||'').split(/\s+/)
|
360
|
-
return nil if !
|
362
|
+
return nil if !iw.empty? && la != phrase_la
|
361
363
|
# If not, the phrase's leading_adjectives must *end* with the reading's
|
362
364
|
return nil if phrase_la[-la.size..-1] != la
|
363
365
|
role_has_residual_adjectives = true if phrase_la.size > la.size
|
@@ -468,7 +470,8 @@ module ActiveFacts
|
|
468
470
|
# the role_ref, those must be local, and we'll need to extract them.
|
469
471
|
|
470
472
|
if rra = side_effect.role_ref.trailing_adjective
|
471
|
-
debug :matching, "Deleting matched trailing adjective '#{rra}'#{side_effect.absorbed_followers>0 ? " in #{side_effect.absorbed_followers} followers" : ""}"
|
473
|
+
debug :matching, "Deleting matched trailing adjective '#{rra}'#{side_effect.absorbed_followers>0 ? " in #{side_effect.absorbed_followers} followers" : ""}, cost is #{side_effect.cost}"
|
474
|
+
side_effect.cancel_cost side_effect.absorbed_followers
|
472
475
|
|
473
476
|
# These adjective(s) matched either an adjective here, or a follower word, or both.
|
474
477
|
if a = phrase.trailing_adjective
|
@@ -487,7 +490,8 @@ module ActiveFacts
|
|
487
490
|
end
|
488
491
|
|
489
492
|
if rra = side_effect.role_ref.leading_adjective
|
490
|
-
debug :matching, "Deleting matched leading adjective '#{rra}'#{side_effect.absorbed_precursors>0 ? " in #{side_effect.absorbed_precursors} precursors" : ""}}"
|
493
|
+
debug :matching, "Deleting matched leading adjective '#{rra}'#{side_effect.absorbed_precursors>0 ? " in #{side_effect.absorbed_precursors} precursors" : ""}, cost is #{side_effect.cost}"
|
494
|
+
side_effect.cancel_cost side_effect.absorbed_precursors
|
491
495
|
|
492
496
|
# These adjective(s) matched either an adjective here, or a precursor word, or both.
|
493
497
|
if a = phrase.leading_adjective
|
@@ -694,11 +698,16 @@ module ActiveFacts
|
|
694
698
|
@absorbed_followers = absorbed_followers
|
695
699
|
@common_supertype = common_supertype
|
696
700
|
@residual_adjectives = residual_adjectives
|
701
|
+
@cancelled_cost = 0
|
697
702
|
debug :matching_fails, "Saving side effects for #{@phrase.term}, absorbs #{@absorbed_precursors}/#{@absorbed_followers}#{@common_supertype ? ', join over supertype '+ @common_supertype.name : ''}" if @absorbed_precursors+@absorbed_followers+(@common_supertype ? 1 : 0) > 0
|
698
703
|
end
|
699
704
|
|
700
705
|
def cost
|
701
|
-
absorbed_precursors + absorbed_followers + (common_supertype ? 1 : 0)
|
706
|
+
absorbed_precursors + absorbed_followers + (common_supertype ? 1 : 0) - @cancelled_cost
|
707
|
+
end
|
708
|
+
|
709
|
+
def cancel_cost c
|
710
|
+
@cancelled_cost += c
|
702
711
|
end
|
703
712
|
|
704
713
|
def to_s
|
@@ -963,16 +972,20 @@ module ActiveFacts
|
|
963
972
|
|
964
973
|
class Quantifier
|
965
974
|
attr_accessor :enforcement
|
975
|
+
attr_accessor :context_note
|
966
976
|
attr_reader :min, :max
|
967
977
|
|
968
|
-
def initialize min, max, enforcement = nil
|
978
|
+
def initialize min, max, enforcement = nil, context_note = nil
|
969
979
|
@min = min
|
970
980
|
@max = max
|
971
981
|
@enforcement = enforcement
|
982
|
+
@context_note = context_note
|
972
983
|
end
|
973
984
|
|
974
985
|
def inspect
|
975
|
-
"[#{@min}..#{@max}]
|
986
|
+
"[#{@min}..#{@max}]#{
|
987
|
+
@context_note && ' ' + @context_note.inspect
|
988
|
+
}"
|
976
989
|
end
|
977
990
|
end
|
978
991
|
|