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