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
data/examples/CQL/OneToOnes.cql
CHANGED
@@ -9,9 +9,9 @@ Girl ID is written as Auto Counter;
|
|
9
9
|
/*
|
10
10
|
* Entity Types
|
11
11
|
*/
|
12
|
-
Boy is identified by its ID
|
12
|
+
Boy is independent identified by its ID;
|
13
13
|
|
14
|
-
Girl is identified by its ID
|
14
|
+
Girl is independent identified by its ID;
|
15
15
|
Girl is going out with at most one Boy,
|
16
16
|
Boy is going out with at most one Girl;
|
17
17
|
|
@@ -29,7 +29,7 @@ Recurring Schedule Id is written as Auto Counter;
|
|
29
29
|
Satellite Message Id is written as Unsigned Integer(32);
|
30
30
|
Seconds is written as Unsigned Integer(32);
|
31
31
|
Serial Number is written as String(20);
|
32
|
-
Service Type is written as String(15)
|
32
|
+
Service Type [independent] is written as String(15);
|
33
33
|
Subscription Nr is written as Signed Integer(32);
|
34
34
|
Switch Id is written as Auto Counter;
|
35
35
|
Transaction Nr is written as Unsigned Integer(32);
|
@@ -84,7 +84,7 @@ Monitor is identified by its Id;
|
|
84
84
|
Monitor monitors one Data Store;
|
85
85
|
Monitor is disabled;
|
86
86
|
|
87
|
-
Monitoring Application is identified by its Name
|
87
|
+
Monitoring Application is independent identified by its Name;
|
88
88
|
Monitor is owned by one Monitoring Application;
|
89
89
|
|
90
90
|
Network is identified by its Nr;
|
@@ -105,13 +105,14 @@ Notification Level is identified by its Nr;
|
|
105
105
|
Notification Level has one Initial- Delay Duration;
|
106
106
|
Notification Level has one Repeat-Duration;
|
107
107
|
|
108
|
-
Notification Type is identified by its Name
|
108
|
+
Notification Type is independent identified by its Name;
|
109
109
|
|
110
110
|
Provider Type is identified by its Id;
|
111
111
|
|
112
112
|
Recurring Schedule is identified by its Id;
|
113
113
|
Monitor has All- Exclusion Recurring Schedule,
|
114
114
|
All- Exclusion Recurring Schedule applies to at most one Monitor;
|
115
|
+
Monitor (as IntegratingMonitor) has Integration- Exclusion Recurring Schedule; // Avoid ambiguity; this is a new fact type
|
115
116
|
Monitor (as IntegratingMonitor) has Integration- Exclusion Recurring Schedule,
|
116
117
|
Integration- Exclusion Recurring Schedule applies to at most one IntegratingMonitor;
|
117
118
|
Recurring Schedule has one Duration;
|
@@ -146,6 +147,7 @@ Switch is one Revision-Version;
|
|
146
147
|
Switch has one monitoring-Port;
|
147
148
|
Switch (as Private Interface Switch) is on private-Network,
|
148
149
|
private-Network connects to at most one Private Interface Switch;
|
150
|
+
Switch (as Public Interface Switch) is on public-Network; // Avoid ambiguity; this is a new fact type
|
149
151
|
Switch (as Public Interface Switch) is on at least one public-Network,
|
150
152
|
Network connects to at most one Public Interface Switch;
|
151
153
|
Switch is backup messages;
|
@@ -208,11 +210,11 @@ Data Store Service has one Subscription;
|
|
208
210
|
*/
|
209
211
|
either Company is client or Company is vendor but not both;
|
210
212
|
for each Credential exactly one of these holds:
|
211
|
-
Data Store(
|
213
|
+
Data Store(2) requires Credential,
|
212
214
|
Data Store Service requires Credential,
|
213
215
|
Vendor requires Credential,
|
214
216
|
Data Store File Host System has Internal-Credential,
|
215
|
-
Data Store(
|
217
|
+
Data Store(1) has Internal-Credential;
|
216
218
|
for each Network exactly one of these holds:
|
217
219
|
Network is used by Host System,
|
218
220
|
Company has Origin-Network,
|
@@ -225,6 +227,9 @@ either Host System runs Switch or Data Store has Legacy-Switch but not both;
|
|
225
227
|
for each Network at most one of these holds:
|
226
228
|
Network is ip_single,
|
227
229
|
Network has Ending-IP;
|
230
|
+
Data Store Service (where Service is from Data Store) belongs to Client
|
231
|
+
if and only if
|
232
|
+
Client has default Data Store;
|
228
233
|
Network has Ending IP
|
229
234
|
if and only if
|
230
235
|
Network is ip_range;
|
@@ -28,12 +28,12 @@ CEO runs Company,
|
|
28
28
|
/*
|
29
29
|
* Constraints:
|
30
30
|
*/
|
31
|
-
either Employee reports to Manager(
|
31
|
+
either Employee reports to Manager(2) or Employee is a Manager(1) 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
35
|
|
36
36
|
// This constraint cannot be expressed in NORMA until it adds explicit join paths:
|
37
|
-
Employee(
|
37
|
+
Employee(2) reports to Manager that is a kind of Employee(1) that works for Company
|
38
38
|
if and only if
|
39
|
-
Employee(
|
39
|
+
Employee(2) works for Company;
|
@@ -17,7 +17,7 @@ Event Date is identified by ymd where
|
|
17
17
|
Event Date has one ymd,
|
18
18
|
ymd is of at most one Event Date;
|
19
19
|
|
20
|
-
Party is identified by its Id
|
20
|
+
Party is independent identified by its Id;
|
21
21
|
|
22
22
|
Party Moniker is where
|
23
23
|
Party is called one Party Name;
|
@@ -72,12 +72,14 @@ Purchase Order is to one Supplier,
|
|
72
72
|
Transfer Request is identified by its ID;
|
73
73
|
Dispatch Item is for at most one Transfer Request;
|
74
74
|
Received Item is for at most one Transfer Request;
|
75
|
+
Transfer Request is for one Product;
|
76
|
+
Transfer Request is for one Quantity;
|
75
77
|
|
76
78
|
Warehouse is identified by its ID;
|
77
79
|
Purchase Order is to one Warehouse;
|
78
80
|
Sales Order is from one Warehouse;
|
79
|
-
Transfer Request is
|
80
|
-
Transfer Request is
|
81
|
+
Transfer Request is from one Warehouse (as From Warehouse);
|
82
|
+
Transfer Request is to one Warehouse (as To Warehouse);
|
81
83
|
Warehouse contains at least one Bin;
|
82
84
|
|
83
85
|
Customer is a kind of Party;
|
@@ -11,7 +11,7 @@ module ActiveFacts
|
|
11
11
|
include Language # One of the language modules provides this module
|
12
12
|
include Expressions
|
13
13
|
include Terms
|
14
|
-
include
|
14
|
+
include ObjectTypes
|
15
15
|
include ValueTypes
|
16
16
|
include FactTypes
|
17
17
|
include Context
|
@@ -47,21 +47,27 @@ module ActiveFacts
|
|
47
47
|
/ prescan # Always fails, but its side-effects are needed in the following
|
48
48
|
/ constraint
|
49
49
|
/ unit_definition # REVISIT: Move this above the prescan?
|
50
|
-
/
|
50
|
+
/ object_type
|
51
|
+
/ query
|
51
52
|
/ s ';' s { def ast; nil; end }
|
52
53
|
end
|
53
54
|
|
54
55
|
rule vocabulary_definition
|
55
|
-
s vocabulary S
|
56
|
+
s vocabulary S vocabulary_name s ';'
|
56
57
|
{
|
57
58
|
def ast
|
58
|
-
Compiler::Vocabulary.new(
|
59
|
+
Compiler::Vocabulary.new(vocabulary_name.value)
|
59
60
|
end
|
60
61
|
}
|
61
62
|
end
|
62
63
|
|
64
|
+
rule vocabulary_name
|
65
|
+
id
|
66
|
+
{ def node_type; :vocabulary; end }
|
67
|
+
end
|
68
|
+
|
63
69
|
rule import_definition
|
64
|
-
s import S
|
70
|
+
s import S vocabulary_name alias_list ';'
|
65
71
|
{
|
66
72
|
def ast
|
67
73
|
Compiler::Import.new(id.value, alias_list.value)
|
@@ -71,7 +77,7 @@ module ActiveFacts
|
|
71
77
|
|
72
78
|
# REVISIT: Need a way to define equivalent readings for fact types here (and in the metamodel)
|
73
79
|
rule alias_list
|
74
|
-
( s ',' s alias S aliased_from:
|
80
|
+
( s ',' s alias S aliased_from:alias_term S as S alias_to:alias_term s )*
|
75
81
|
{
|
76
82
|
def value
|
77
83
|
elements.inject({}){|h, e| h[e.aliased_from.value] = e.alias_to; h }
|
@@ -79,6 +85,11 @@ module ActiveFacts
|
|
79
85
|
}
|
80
86
|
end
|
81
87
|
|
88
|
+
rule alias_term
|
89
|
+
id
|
90
|
+
{ def node_type; :term; end }
|
91
|
+
end
|
92
|
+
|
82
93
|
rule constraint
|
83
94
|
subset_constraint /
|
84
95
|
equality_constraint /
|
@@ -101,84 +112,39 @@ module ActiveFacts
|
|
101
112
|
|
102
113
|
# presence constraint:
|
103
114
|
rule presence_constraint
|
104
|
-
|
105
|
-
readings_list s
|
106
|
-
c:context_note? ';'
|
115
|
+
(each_occurs_in_clauses / either_or)
|
107
116
|
{
|
108
117
|
def ast
|
109
|
-
|
110
|
-
Compiler::PresenceConstraint.new context_note, enforcement.ast, readings_list.ast, role_list.ast, quantifier.ast
|
118
|
+
Compiler::PresenceConstraint.new c, enforcement.ast, clauses_ast, role_list_ast, quantifier_ast
|
111
119
|
end
|
112
120
|
}
|
113
121
|
end
|
114
122
|
|
115
123
|
# set (exclusion, mandatory exclusion, complex equality) constraint
|
116
124
|
rule set_constraint
|
117
|
-
|
118
|
-
readings_list s
|
119
|
-
c:context_note? ';'
|
125
|
+
(for_each_how_many / either_or_not_both)
|
120
126
|
{
|
121
127
|
def ast
|
122
|
-
|
123
|
-
Compiler::SetExclusionConstraint.new context_note, enforcement.ast, readings_list.ast, role_list.ast, quantifier.ast
|
128
|
+
Compiler::SetExclusionConstraint.new c, enforcement.ast, clauses_ast, role_list_ast, quantifier_ast
|
124
129
|
end
|
125
130
|
}
|
126
|
-
/
|
127
|
-
s either? s r1:readings s or s r2:readings exclusion:(but s not s both s)? c:context_note? enforcement ';'
|
128
|
-
{
|
129
|
-
def ast
|
130
|
-
context_note = c.empty? ? nil : c.ast
|
131
|
-
|
132
|
-
if exclusion.text_value.empty?
|
133
|
-
Compiler::PresenceConstraint.new context_note, enforcement.ast, [r1.ast, r2.ast], nil, Compiler::Quantifier.new(1, nil)
|
134
|
-
else
|
135
|
-
quantifier = Compiler::Quantifier.new(*(exclusion.text_value.empty? ? [1, nil] : [1, 1]))
|
136
|
-
#quantifier = Compiler::Quantifier.new(1, 1)
|
137
|
-
Compiler::SetExclusionConstraint.new context_note, enforcement.ast, [r1.ast, r2.ast], nil, quantifier
|
138
|
-
end
|
139
|
-
end
|
140
|
-
}
|
141
131
|
end
|
142
132
|
|
143
133
|
rule subset_constraint
|
144
|
-
|
145
|
-
c:context_note? enforcement ';'
|
134
|
+
(a_only_if_b / if_b_then_a)
|
146
135
|
{
|
147
136
|
def ast
|
148
|
-
|
149
|
-
Compiler::SubsetConstraint.new context_note, enforcement.ast, [readings.ast, r2.ast]
|
137
|
+
Compiler::SubsetConstraint.new c, enforcement.ast, [clauses.ast, r2.ast]
|
150
138
|
end
|
151
139
|
}
|
152
140
|
end
|
153
141
|
|
154
142
|
rule equality_constraint
|
155
|
-
|
156
|
-
c:context_note? enforcement ';'
|
157
|
-
{
|
158
|
-
def ast
|
159
|
-
context_note = c.empty? ? nil : c.ast
|
160
|
-
all_readings = [readings.ast, *tail.elements.map{|e| e.readings.ast }]
|
161
|
-
Compiler::SetEqualityConstraint.new context_note, enforcement.ast, all_readings
|
162
|
-
end
|
163
|
-
}
|
164
|
-
end
|
165
|
-
|
166
|
-
rule readings_list
|
167
|
-
readings tail:( ',' s readings )*
|
168
|
-
{
|
169
|
-
def ast
|
170
|
-
[readings.ast, *tail.elements.map{|e| e.readings.ast }]
|
171
|
-
end
|
172
|
-
}
|
173
|
-
end
|
174
|
-
|
175
|
-
rule readings
|
176
|
-
reading s tail:( and s reading s )*
|
143
|
+
if_and_only_if
|
177
144
|
{
|
178
145
|
def ast
|
179
|
-
|
180
|
-
|
181
|
-
readings
|
146
|
+
all_clauses = [clauses.ast, *tail.elements.map{|e| e.clauses.ast }]
|
147
|
+
Compiler::SetEqualityConstraint.new c, enforcement.ast, all_clauses
|
182
148
|
end
|
183
149
|
}
|
184
150
|
end
|
@@ -29,14 +29,24 @@ module ActiveFacts
|
|
29
29
|
end
|
30
30
|
|
31
31
|
rule context_type
|
32
|
-
because
|
32
|
+
because s { def value; 'because'; end } /
|
33
33
|
as_opposed_to { def value; 'as_opposed_to'; end } /
|
34
34
|
so_that { def value; 'so_that'; end } /
|
35
35
|
to_avoid { def value; 'to_avoid'; end }
|
36
36
|
end
|
37
37
|
|
38
|
+
# An enforcement action, like SMS, email, log, alarm, etc.
|
39
|
+
rule action
|
40
|
+
id
|
41
|
+
end
|
42
|
+
|
38
43
|
rule discussion
|
39
|
-
|
44
|
+
(
|
45
|
+
'(' discussion ')' / (!( [()] / ',' as_agreed_by) .)*
|
46
|
+
)
|
47
|
+
{
|
48
|
+
def node_type; :linking; end
|
49
|
+
}
|
40
50
|
end
|
41
51
|
end
|
42
52
|
end
|
@@ -7,22 +7,6 @@
|
|
7
7
|
module ActiveFacts
|
8
8
|
module CQL
|
9
9
|
grammar Expressions
|
10
|
-
rule comparison
|
11
|
-
e1:expression s
|
12
|
-
comparator s e2:expression
|
13
|
-
{
|
14
|
-
def ast
|
15
|
-
Compiler::Comparison.new comparator.text_value, e1.ast, e2.ast
|
16
|
-
end
|
17
|
-
}
|
18
|
-
end
|
19
|
-
|
20
|
-
rule comparator
|
21
|
-
'<=' / '<' / '=' / '>=' / '>'
|
22
|
-
# REVISIT: These words occur in readings, find alternates:
|
23
|
-
# / matches / includes
|
24
|
-
end
|
25
|
-
|
26
10
|
rule expression
|
27
11
|
sum
|
28
12
|
end
|
@@ -31,7 +15,11 @@ module ActiveFacts
|
|
31
15
|
t0:product s tail:( op:add_op s t1:product s )*
|
32
16
|
{
|
33
17
|
def ast
|
34
|
-
|
18
|
+
if tail.elements.empty?
|
19
|
+
t0.ast
|
20
|
+
else
|
21
|
+
Compiler::Sum.new(t0.ast, *tail.elements.map{|e| e.op.text_value == '-' ? Compiler::Negate.new(e.t1.ast) : e.t1.ast})
|
22
|
+
end
|
35
23
|
end
|
36
24
|
}
|
37
25
|
end
|
@@ -44,7 +32,11 @@ module ActiveFacts
|
|
44
32
|
f0:factor s tail:( op:mul_op s f1:factor s )*
|
45
33
|
{
|
46
34
|
def ast
|
47
|
-
|
35
|
+
if tail.elements.empty?
|
36
|
+
f0.ast
|
37
|
+
else
|
38
|
+
Compiler::Product.new(f0.ast, *tail.elements.map{|e| e.op.text_value != '*' ? Compiler::Reciprocal.new(e.op.text_value, e.f1.ast) : e.f1.ast})
|
39
|
+
end
|
48
40
|
end
|
49
41
|
}
|
50
42
|
end
|
@@ -61,19 +53,11 @@ module ActiveFacts
|
|
61
53
|
end
|
62
54
|
|
63
55
|
rule derived_variable
|
64
|
-
variable:term s
|
56
|
+
variable:term s role_id:(role_name / subscript )?
|
65
57
|
{
|
66
|
-
def ast
|
67
|
-
|
68
|
-
|
69
|
-
}
|
70
|
-
end
|
71
|
-
|
72
|
-
rule function_call
|
73
|
-
'.' s func:id s param_list:( '(' s params:( p0:expression s tail:( ',' s p1:expression s )* )? ')' s )?
|
74
|
-
{
|
75
|
-
def ast
|
76
|
-
Compiler::FunctionCall.new(func.value, *param_list.elements.map{|param| param.ast })
|
58
|
+
def ast quantifier = nil, value_constraint = nil, literal = nil, nested_clauses = nil
|
59
|
+
role_name = role_id.empty? ? nil : role_id.value
|
60
|
+
variable.ast(quantifier, nil, role_name, value_constraint, literal, nested_clauses)
|
77
61
|
end
|
78
62
|
}
|
79
63
|
end
|
@@ -7,100 +7,93 @@
|
|
7
7
|
module ActiveFacts
|
8
8
|
module CQL
|
9
9
|
grammar FactTypes
|
10
|
+
rule query
|
11
|
+
s joined_clauses r:returning_clause? '?'
|
12
|
+
{
|
13
|
+
def ast
|
14
|
+
Compiler::FactType.new nil, [], joined_clauses.ast, (r.empty? ? nil : r)
|
15
|
+
end
|
16
|
+
}
|
17
|
+
end
|
18
|
+
|
10
19
|
rule named_fact_type
|
11
20
|
s term_definition_name
|
12
|
-
|
21
|
+
mapping_pragmas is_where
|
13
22
|
anonymous_fact_type
|
14
23
|
{
|
15
24
|
def ast
|
16
25
|
ft = anonymous_fact_type.ast
|
17
26
|
ft.name = term_definition_name.value
|
27
|
+
pragmas = mapping_pragmas.value
|
28
|
+
pragmas << 'independent' if is_where.independent
|
29
|
+
ft.pragmas = pragmas
|
18
30
|
ft
|
19
31
|
end
|
20
32
|
}
|
21
33
|
end
|
22
34
|
|
23
35
|
rule anonymous_fact_type
|
24
|
-
|
25
|
-
|
26
|
-
ctail:( (':' / where) s c:conditions s)?
|
36
|
+
joined_clauses
|
37
|
+
ctail:( (':' / where) s a:joined_clauses s)?
|
27
38
|
returning_clause?
|
28
39
|
s ';'
|
29
40
|
{
|
30
41
|
def ast
|
31
|
-
|
32
|
-
|
33
|
-
conditions = !ctail.empty? ? ctail.c.ast : []
|
42
|
+
clauses_ast = joined_clauses.ast
|
43
|
+
conditions = !ctail.empty? ? ctail.a.ast : []
|
34
44
|
returning = respond_to?(:returning_clause) ? returning_clause.ast : nil
|
35
|
-
|
45
|
+
value_derivation = clauses_ast.detect{|r| r.is_equality_comparison}
|
46
|
+
if !value_derivation and
|
47
|
+
conditions.empty? and
|
48
|
+
clauses_ast.detect{|r| r.includes_literals}
|
36
49
|
raise "Fact instances may not contain conditions" unless conditions.empty? && !returning
|
37
|
-
Compiler::Fact.new
|
38
|
-
elsif (
|
39
|
-
|
40
|
-
(popname =
|
41
|
-
!popname.is_a?(Compiler::
|
50
|
+
Compiler::Fact.new clauses_ast
|
51
|
+
elsif (clauses_ast.size == 1 &&
|
52
|
+
clauses_ast[0].phrases.size == 1 &&
|
53
|
+
(popname = clauses_ast[0].phrases[0]) &&
|
54
|
+
!popname.is_a?(Compiler::VarRef) &&
|
42
55
|
conditions.detect{|r| r.includes_literals}
|
43
56
|
)
|
44
57
|
Compiler::Fact.new conditions, popname
|
45
58
|
else
|
46
|
-
Compiler::FactType.new nil,
|
59
|
+
Compiler::FactType.new nil, clauses_ast, conditions, returning
|
47
60
|
end
|
48
61
|
end
|
49
62
|
}
|
50
63
|
end
|
51
64
|
|
52
|
-
rule
|
53
|
-
|
54
|
-
ftail:( (',' / and ) s
|
65
|
+
rule joined_clauses
|
66
|
+
qualified_clauses
|
67
|
+
ftail:( conjunction:(',' / and / or ) s qualified_clauses s )*
|
55
68
|
{
|
56
69
|
def ast
|
57
|
-
|
58
|
-
ftail.elements.each{|e|
|
59
|
-
|
70
|
+
clauses_ast = qualified_clauses.ast
|
71
|
+
ftail.elements.each{|e|
|
72
|
+
conjunction = e.conjunction.text_value
|
73
|
+
# conjunction = 'and' if conjunction == ',' # ',' means AND, but disallows left-contractions
|
74
|
+
clauses_ast += e.qualified_clauses.ast(conjunction)
|
75
|
+
}
|
76
|
+
clauses_ast
|
60
77
|
end
|
61
78
|
}
|
62
79
|
end
|
63
80
|
|
64
81
|
rule returning_clause
|
65
|
-
returning return (',' return)*
|
82
|
+
returning s return (s ',' s return)*
|
66
83
|
end
|
67
84
|
|
68
85
|
rule return
|
69
|
-
|
86
|
+
ordering_prefix? phrase+
|
70
87
|
end
|
71
88
|
|
72
|
-
rule
|
73
|
-
|
89
|
+
rule qualified_clauses
|
90
|
+
s q:qualifier? s contracted_clauses s p:post_qualifiers? s c:context_note?
|
74
91
|
{
|
75
|
-
def ast
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
}
|
81
|
-
end
|
82
|
-
|
83
|
-
rule condition
|
84
|
-
head:clause s # tail:(or S alternate:clause s )*
|
85
|
-
{
|
86
|
-
def ast
|
87
|
-
head.ast
|
88
|
-
# Compiler::Alternates.new(head.ast, *tail.elements.map{|i| i.alternate.ast})
|
89
|
-
# REVISIT: alternate conditions are not yet implemented
|
90
|
-
end
|
91
|
-
}
|
92
|
-
end
|
93
|
-
|
94
|
-
rule clause
|
95
|
-
# REVISIT: No context for comparisons, yet
|
96
|
-
comparison / fact_clause
|
97
|
-
end
|
98
|
-
|
99
|
-
rule fact_clause
|
100
|
-
s q:qualifier? s reading s p:post_qualifiers? s c:context_note?
|
101
|
-
{
|
102
|
-
def ast
|
103
|
-
r = reading.ast # An array of readings
|
92
|
+
def ast(conjunction = nil)
|
93
|
+
r = contracted_clauses.ast # An array of clause asts
|
94
|
+
r[0].conjunction = conjunction
|
95
|
+
# pre-qualifiers apply to the first clause, post_qualifiers and context_note to the last
|
96
|
+
# REVISIT: This may be incorrect where the last is a nested clause
|
104
97
|
r[0].qualifiers << q.text_value unless q.empty?
|
105
98
|
r[-1].qualifiers += p.list unless p.empty?
|
106
99
|
r[-1].context_note = c.ast unless c.empty?
|
@@ -127,104 +120,173 @@ module ActiveFacts
|
|
127
120
|
intransitive / transitive / acyclic / symmetric / asymmetric / antisymmetric / reflexive / irreflexive
|
128
121
|
end
|
129
122
|
|
130
|
-
rule
|
123
|
+
rule clauses_list
|
124
|
+
clauses tail:( ',' s clauses )*
|
125
|
+
{
|
126
|
+
def ast
|
127
|
+
[clauses.ast, *tail.elements.map{|e| e.clauses.ast }]
|
128
|
+
end
|
129
|
+
}
|
130
|
+
end
|
131
|
+
|
132
|
+
rule clauses
|
133
|
+
contracted_clauses s tail:( and s contracted_clauses s )*
|
134
|
+
{
|
135
|
+
def ast
|
136
|
+
clauses = contracted_clauses.ast
|
137
|
+
tail.elements.map{|e| clauses += e.contracted_clauses.ast }
|
138
|
+
clauses
|
139
|
+
end
|
140
|
+
}
|
141
|
+
end
|
142
|
+
|
143
|
+
rule contracted_clauses
|
144
|
+
comparison
|
145
|
+
/
|
131
146
|
(
|
132
|
-
|
147
|
+
contraction # A contraction will terminate this repetition by eating to the end
|
133
148
|
/
|
134
|
-
|
135
|
-
/ # A hyphenated non-term. Important: no embedded spaces
|
136
|
-
id tail:('-' !term id)+ s
|
137
|
-
{
|
138
|
-
def ast
|
139
|
-
[id.value, *tail.elements.map{|e| e.id.value}]*"-"
|
140
|
-
end
|
141
|
-
}
|
142
|
-
/ # A normal non-term
|
143
|
-
!non_role_word id s
|
144
|
-
{
|
145
|
-
def ast
|
146
|
-
id.value
|
147
|
-
end
|
148
|
-
}
|
149
|
+
phrase
|
149
150
|
)+
|
150
151
|
{
|
151
152
|
def ast
|
152
153
|
asts = elements.map{ |r| r.ast }
|
153
|
-
|
154
|
+
contracted_clauses = []
|
154
155
|
qualifiers = []
|
155
|
-
if asts[-1].is_a?(Array) # A
|
156
|
-
|
157
|
-
contracted_role =
|
158
|
-
qualifiers =
|
159
|
-
asts.push(contracted_role) # And replace it by the role removed from the
|
156
|
+
if asts[-1].is_a?(Array) # A contraction (Array of [role, qualifiers, *clauses])
|
157
|
+
contracted_clauses = asts.pop # Pull off the contracted_clauses
|
158
|
+
contracted_role = contracted_clauses.shift
|
159
|
+
qualifiers = contracted_clauses.shift
|
160
|
+
asts.push(contracted_role) # And replace it by the role removed from the contracted_clauses
|
160
161
|
end
|
161
|
-
|
162
|
-
|
163
|
-
[reading] + contraction
|
162
|
+
clause_ast = Compiler::Clause.new(asts, qualifiers)
|
163
|
+
[clause_ast] + contracted_clauses
|
164
164
|
end
|
165
165
|
}
|
166
166
|
end
|
167
167
|
|
168
|
-
|
169
|
-
|
170
|
-
|
168
|
+
rule contraction
|
169
|
+
reading_contraction /
|
170
|
+
condition_contraction
|
171
|
+
end
|
172
|
+
|
173
|
+
rule reading_contraction
|
174
|
+
role p:post_qualifiers? conjunction:(that/who) s q:qualifier? s contracted_clauses s
|
171
175
|
{
|
172
176
|
def ast
|
173
|
-
#
|
174
|
-
# * prepend a new role
|
177
|
+
# contracted_clauses.ast will return an array of Clauses, but the first clause is special. We must:
|
178
|
+
# * prepend a new role (we get the Role to build *two* ast nodes)
|
175
179
|
# * attach the qualifiers
|
176
|
-
|
177
|
-
|
178
|
-
|
180
|
+
clauses_ast = contracted_clauses.ast
|
181
|
+
clauses_ast[0].conjunction = conjunction.text_value
|
182
|
+
clauses_ast[0].phrases.unshift(role.ast)
|
183
|
+
clauses_ast[0].qualifiers << q.text_value unless q.empty? # Add maybe/definitely
|
179
184
|
|
180
|
-
# A
|
185
|
+
# A contraction returns an array containing:
|
181
186
|
# * a role AST
|
182
187
|
# * a qualifiers array
|
183
|
-
# * an array of
|
184
|
-
[role.ast, p.empty? ? [] : p.list] +
|
188
|
+
# * an array of Clauses
|
189
|
+
[role.ast, p.empty? ? [] : p.list] + clauses_ast
|
190
|
+
end
|
191
|
+
}
|
192
|
+
end
|
193
|
+
|
194
|
+
rule condition_contraction
|
195
|
+
role p:post_qualifiers? q:qualifier? s comparator s e2:expression
|
196
|
+
!phrase # The contracted_clauses must not continue here!
|
197
|
+
{
|
198
|
+
def ast
|
199
|
+
c = Compiler::Comparison.new(comparator.text_value, role.ast, e2.ast, q.empty? ? [] : [q.text_value])
|
200
|
+
c.conjunction = comparator.text_value
|
201
|
+
[ role.ast, p.empty? ? [] : p.list, c ]
|
202
|
+
end
|
203
|
+
}
|
204
|
+
end
|
205
|
+
|
206
|
+
rule comparison
|
207
|
+
e1:expression s q:qualifier? s comparator s contraction p:post_qualifiers?
|
208
|
+
{
|
209
|
+
def ast
|
210
|
+
role, qualifiers, *clauses_ast = *contraction.ast
|
211
|
+
clauses_ast[0].qualifiers += p.list unless p.empty? # apply post_qualifiers to the contracted clause
|
212
|
+
# clauses_ast[0].conjunction = 'and' # AND is implicit for a contraction
|
213
|
+
c = Compiler::Comparison.new(comparator.text_value, e1.ast, role)
|
214
|
+
c.qualifiers << q.text_value unless q.empty?
|
215
|
+
[c] + clauses_ast
|
185
216
|
end
|
186
217
|
}
|
218
|
+
/
|
219
|
+
q:qualifier? e1:expression s comparator s e2:expression # comparisons have no post-qualifiers: p:post_qualifiers?
|
220
|
+
{
|
221
|
+
def ast
|
222
|
+
[Compiler::Comparison.new(comparator.text_value, e1.ast, e2.ast, q.empty? ? [] : [q.text_value])]
|
223
|
+
end
|
224
|
+
}
|
225
|
+
end
|
226
|
+
|
227
|
+
rule comparator
|
228
|
+
'<=' / '<>' / '<' / '=' / '>=' / '>' / '!='
|
229
|
+
end
|
230
|
+
|
231
|
+
rule phrase
|
232
|
+
role # A role reference containing a term, perhaps with attached paraphernalia
|
233
|
+
/ # A hyphenated non-term. Important: no embedded spaces
|
234
|
+
id tail:('-' !term id)+ s
|
235
|
+
{
|
236
|
+
def ast
|
237
|
+
[id.value, *tail.elements.map{|e| e.id.value}]*"-"
|
238
|
+
end
|
239
|
+
def node_type; :linking; end
|
240
|
+
}
|
241
|
+
/ # A normal non-term
|
242
|
+
!non_phrase id s
|
243
|
+
{
|
244
|
+
def ast
|
245
|
+
id.value
|
246
|
+
end
|
247
|
+
def node_type; :linking; end
|
248
|
+
}
|
187
249
|
end
|
188
250
|
|
189
251
|
# This is the rule that causes most back-tracking. I think you can see why.
|
190
|
-
# When we have an expression, we will come down here perhaps multiple times,
|
191
|
-
# but find no way out as soon as we hit the trailing non_role.
|
192
252
|
rule role
|
193
253
|
q:(quantifier enforcement)?
|
194
|
-
player:
|
195
|
-
func:function_call? s
|
196
|
-
role_id:(role_name / subscript )?
|
254
|
+
player:derived_variable
|
197
255
|
lr:(
|
198
256
|
literal u:unit?
|
199
257
|
/
|
200
258
|
value_constraint enforcement
|
201
259
|
)?
|
202
260
|
oj:objectification_join?
|
203
|
-
!non_role # If we integrate fact_clauses with comparisons, this can go.
|
204
261
|
{
|
205
262
|
def ast
|
206
263
|
if !q.empty? && q.quantifier.value
|
207
264
|
quantifier = Compiler::Quantifier.new(q.quantifier.value[0], q.quantifier.value[1], q.enforcement.ast)
|
208
265
|
end
|
209
|
-
role_name = role_id.empty? ? nil : role_id.value
|
210
|
-
function_call = nil
|
211
266
|
if !lr.empty?
|
212
267
|
if lr.respond_to?(:literal)
|
213
268
|
literal = lr.literal.value
|
214
269
|
raise "Literals with units are not yet processed" unless lr.u.empty?
|
215
270
|
end
|
216
|
-
value_constraint = Compiler::ValueConstraint.new(lr.value_constraint.ranges, lr.enforcement.ast) if lr.respond_to?(:value_constraint)
|
217
|
-
raise "It is not permitted to provide both a literal value and a value
|
271
|
+
value_constraint = Compiler::ValueConstraint.new(lr.value_constraint.ranges, lr.value_constraint.units, lr.enforcement.ast) if lr.respond_to?(:value_constraint)
|
272
|
+
raise "It is not permitted to provide both a literal value and a value constraint" if value_constraint and literal
|
218
273
|
end
|
219
274
|
|
220
|
-
|
221
|
-
|
275
|
+
nested_clauses =
|
276
|
+
if oj.empty?
|
277
|
+
nil
|
278
|
+
else
|
279
|
+
ast = oj.ast
|
280
|
+
ast[0].conjunction = 'where'
|
281
|
+
ast
|
282
|
+
end
|
283
|
+
player.ast(quantifier, value_constraint, literal, nested_clauses)
|
222
284
|
end
|
223
285
|
}
|
224
286
|
end
|
225
287
|
|
226
288
|
rule objectification_join
|
227
|
-
'(' s where s facts:
|
289
|
+
'(' s where s facts:joined_clauses s ')' s
|
228
290
|
{
|
229
291
|
def ast
|
230
292
|
facts.ast
|
@@ -242,13 +304,6 @@ module ActiveFacts
|
|
242
304
|
{ def value; i.text_value.to_i; end }
|
243
305
|
end
|
244
306
|
|
245
|
-
rule non_role
|
246
|
-
# Any of these is illegal in or following a reading (they indicate a comparison is coming). Later, this will change:
|
247
|
-
comparator
|
248
|
-
/ add_op
|
249
|
-
/ mul_op
|
250
|
-
end
|
251
|
-
|
252
307
|
end
|
253
308
|
end
|
254
309
|
end
|