activefacts 0.8.10 → 0.8.12
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/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
|
|