activefacts-cql 1.9.6 → 1.9.7

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.
@@ -1,197 +0,0 @@
1
- #
2
- # ActiveFacts CQL Parser.
3
- # Parse rules relating to Term names
4
- #
5
- # Copyright (c) 2009 Clifford Heath. Read the LICENSE file.
6
- #
7
- module ActiveFacts
8
- module CQL
9
- grammar Terms
10
- rule term_definition_name
11
- id s t:(!non_term_def id s)*
12
- <Parser::TermDefinitionNameNode>
13
- end
14
-
15
- rule non_term_def
16
- mapping_pragmas entity_prefix
17
- / mapping_pragmas written_as # Value type
18
- / mapping_pragmas is_where # Objectified type
19
- / non_phrase
20
- / identified_by # as in: "a kind of X identified by..."
21
- / in_units
22
- / auto_assignment
23
- / value_constraint
24
- end
25
-
26
- rule entity_prefix
27
- is s (independent s )? identified_by
28
- /
29
- subtype_prefix (independent s )? term_definition_name
30
- &{|e| input.context.object_type(e[2].value, "subtype") }
31
- end
32
-
33
- rule prescan
34
- informally_prescan
35
- /
36
- s each?
37
- s (
38
- term_definition_name mapping_pragmas entity_prefix
39
- &{|e| input.context.object_type(e[0].value, "entity type") }
40
- /
41
- t1:term_definition_name mapping_pragmas written_as any? s t2:term_definition_name
42
- &{|e|
43
- new_term = e[0].value
44
- input.context.object_type(new_term, "value type")
45
- base_term = e[5].value
46
- input.context.object_type(base_term, "value type")
47
- }
48
- /
49
- term_definition_name s mapping_pragmas is_where
50
- &{|e| input.context.object_type(e[0].value, "objectified_fact_type") }
51
- )?
52
- prescan_rest
53
- &{|s|
54
- # Wipe any terminal failures that were added:
55
- forget_failures_to_here
56
-
57
- # puts "========== prescan is complete on #{(s.map{|e|e.text_value}*" ").inspect} =========="
58
- false
59
- }
60
- end
61
-
62
- rule informally_prescan
63
- informally s ',' s
64
- informal_description_subject_prescan s
65
- informal_description_body
66
- informal_description_closer
67
- end
68
-
69
- rule informal_description_subject_prescan
70
- each S term_definition_name # Informal definition of an object type
71
- /
72
- when S phrase+ s ',' # or a fact type; we can't forward-reference these
73
- # This could memoize a bad parse for 'phrase'!!!
74
- end
75
-
76
- # Do a first-pass mainly lexical analysis, looking for role name definitions and adjectives,
77
- # for use in detecting terms later.
78
- rule prescan_rest
79
- &{|s| input.context.reset_role_names }
80
- (
81
- context_note # Context notes have different lexical conventions
82
- / '(' as S term_definition_name s ')' s # Prepare for a Role Name
83
- &{|s| input.context.role_name(s[3].value) }
84
- / new_derived_value # Prepare for a derived term
85
- / new_adjective_term # Prepare for an existing term with new Adjectives
86
- # The remaining rules exist to correctly eat up anything that doesn't match the above:
87
- / global_term # If we see A B - C D, don't recognise B as a new adjective for C D.
88
- / prescan_aggregate
89
- / id
90
- # / literal # REVISIT: Literals might contain "(as Foo)" and mess things up
91
- / range # Covers all numbers and strings
92
- / S # White space and comments, must precede / and *
93
- / [^;] # Skip anything else, we want the prescan to finish
94
- )* [?;] s
95
- end
96
-
97
- # Not sure this is even needed, but it doesn't seem to hurt:
98
- rule prescan_aggregate
99
- aggregate_type:id s agg_of s global_term agg_in s &'('
100
- end
101
-
102
- rule new_derived_value
103
- !global_term id derived_value_continuation? s '='
104
- &{|s|
105
- name = [s[1].text_value] + (s[2].empty? ? [] : s[2].value)
106
- input.context.object_type(name*' ', "derived value type")
107
- }
108
- /
109
- '=' s !global_term id derived_value_continuation? s (that/who)
110
- &{|s|
111
- name = [s[3].text_value] + (s[4].empty? ? [] : s[4].value)
112
- input.context.object_type(name*' ', "derived value type")
113
- }
114
- end
115
-
116
- # Derived values are new terms introduced by an = sign before an expression
117
- # This rule handles trailing words of a multi-word derived value
118
- rule derived_value_continuation
119
- s '-' tail:(s !global_term !(that/who) id)*
120
- {
121
- def value
122
- tail.elements.map{|e| e.id.text_value}
123
- end
124
- }
125
- end
126
-
127
- # Used during the pre-scan, match a term with new adjective(s)
128
- rule new_adjective_term
129
- !global_term adj:id '-' '-'? lead_intervening s global_term # Definitely a new leading adjective for this term
130
- &{|s| adj = [s[1].text_value, s[4].value].compact*" "; input.context.new_leading_adjective_term(adj, s[6].text_value) }
131
- /
132
- global_term s trail_intervening '-' '-'? !global_term adj:id # Definitely a new trailing adjective for this term
133
- &{|s| adj = [s[2].value, s[6].text_value].compact*" "; input.context.new_trailing_adjective_term(adj, s[0].text_value) }
134
- end
135
-
136
- rule lead_intervening # Words intervening between a new adjective and the term
137
- (S !global_term id)*
138
- {
139
- def value
140
- elements.size == 0 ? nil : elements.map{|e| e.id.text_value}*" "
141
- end
142
- }
143
- end
144
-
145
- rule trail_intervening # Words intervening between a new adjective and the term
146
- (!global_term id S)*
147
- {
148
- def value
149
- elements.size == 0 ? nil : elements.map{|e| e.id.text_value}*" "
150
- end
151
- }
152
- end
153
-
154
- # This is the rule to use after the prescan; it only succeeds on a complete term or role reference
155
- rule term
156
- s head:id x &{|s| w = s[1].text_value; input.context.term_starts?(w, s[2]) }
157
- tail:(
158
- s '-'? dbl:'-'? s w:id &{|s| w = s[4].text_value; input.context.term_continues?(w) }
159
- )* &{|s| input.context.term_complete? }
160
- <Parser::TermNode>
161
- /
162
- s head:id '-' '-'? s term &{|s| s[5].ast.leading_adjective == nil }
163
- <Parser::TermLANode>
164
- end
165
-
166
- rule x
167
- '' <SavedContext>
168
- end
169
-
170
- rule global_term
171
- # This rule shouldn't be used outside the prescan, it will memoize the wrong things.
172
- head:id x &{|s| input.context.global_term_starts?(s[0].text_value, s[1]) }
173
- tail:(s w:id &{|s| input.context.global_term_continues?(s[1].text_value) } )*
174
- { def value
175
- tail.elements.inject(head.value) { |t, e| "#{t} #{e.w.value}" }
176
- end
177
- }
178
- end
179
-
180
- rule non_phrase
181
- # These words are illegal in (but maybe ok following) a clause where a phrase is expected:
182
- and
183
- / but
184
- / if
185
- / role_list_constraint_followers
186
- / only_if
187
- / or
188
- / quantifier
189
- / returning
190
- / then
191
- / value_constraint
192
- / where
193
- end
194
-
195
- end
196
- end
197
- end
@@ -1,226 +0,0 @@
1
- #
2
- # ActiveFacts CQL Parser.
3
- # Parse rules relating to Transformation Rules.
4
- #
5
- # Copyright (c) 2017 Factil Pty Ltd. Read the LICENSE file.
6
- #
7
- module ActiveFacts
8
- module CQL
9
- grammar TransformRules
10
-
11
- rule transform_rule
12
- ctr:compound_matching s ';'
13
- {
14
- def ast
15
- Compiler::TransformRule.new(ctr.ast)
16
- end
17
- }
18
- end
19
-
20
- rule compound_matching
21
- s tl:term_list s '<==' s tq:transform_query? s '{' tr:transform_matchings s '}'
22
- {
23
- def ast
24
- Compiler::CompoundMatching.new(tl.ast, tq.empty? ? nil : tq.ast, tr.ast)
25
- end
26
- }
27
- end
28
-
29
- rule simple_matching
30
- s tl:term_list s '<--' s te:transform_expr?
31
- {
32
- def ast
33
- Compiler::SimpleMatching.new(tl.ast, te.empty? ? nil : te.ast)
34
- end
35
- }
36
- end
37
-
38
- rule transform_query
39
- cl:clauses_list { def ast; cl.ast; end; }
40
- / t:term { def ast; t.ast; end; }
41
- end
42
-
43
- rule transform_matchings
44
- s r0:transform_matching tail:(s ',' s r1:transform_matching)*
45
- {
46
- def ast
47
- [r0.ast, *tail.elements.map{|e| e.r1.ast }]
48
- end
49
- }
50
- end
51
-
52
- rule transform_matching
53
- str:simple_matching { def ast; str.ast; end; }
54
- / ctr:compound_matching { def ast; ctr.ast; end; }
55
- end
56
-
57
- rule term_list
58
- s t0:term tail:(s '.' s t1:term_list)*
59
- {
60
- def ast
61
- if tail.elements.empty?
62
- [t0.ast]
63
- else
64
- [t0.ast, *tail.elements.map{|t| t.t1.ast }]
65
- end
66
- end
67
- }
68
- end
69
-
70
- rule transform_expr
71
- s c:logical_texpr s '?' s t0:logical_texpr s ':' s t1:logical_texpr
72
- {
73
- def ast
74
- Compiler::Ternary.new(c.ast, t0.ast, t1.ast)
75
- end
76
- }
77
- / s o:aggregate_op S agg_of s t:logical_texpr
78
- {
79
- def ast
80
- Compiler::Aggregate.new(o.text_value, t.ast)
81
- end
82
- }
83
- / s t0:logical_texpr { def ast; t0.ast; end; }
84
- end
85
-
86
- rule aggregate_op
87
- 'sum' / 'average' / 'max' / 'min' / 'count'
88
- end
89
-
90
- rule logical_texpr
91
- s t0:logical_and_texpr tail:(s op:or s t1:logical_and_texpr)*
92
- {
93
- def ast
94
- if tail.elements.empty?
95
- t0.ast
96
- else
97
- Compiler::LogicalOr.new(t0.ast, *tail.elements.map{|e| e.t1.ast})
98
- end
99
- end
100
- }
101
- end
102
-
103
- rule logical_and_texpr
104
- s t0:equality_texpr tail:(s op:and s t1:equality_texpr)*
105
- {
106
- def ast
107
- if tail.elements.empty?
108
- t0.ast
109
- else
110
- Compiler::LogicalAnd.new(t0.ast, *tail.elements.map{|e| e.t1.ast})
111
- end
112
- end
113
- }
114
- end
115
-
116
- rule equality_texpr
117
- s t0:relational_texpr operation:(s op:equality_op s t1:relational_texpr)?
118
- {
119
- def ast
120
- if operation.empty?
121
- t0.ast
122
- else
123
- Compiler::Comparison.new(op.text_value, t0.ast, operation.t1.ast)
124
- end
125
- end
126
- }
127
- end
128
-
129
- rule equality_op
130
- '=' / '!='
131
- end
132
-
133
- rule relational_texpr
134
- s t0:additive_texpr operation:(s op:relational_op s t1:additive_texpr)?
135
- {
136
- def ast
137
- if operation.empty?
138
- t0.ast
139
- else
140
- Compiler::Comparison.new(op.text_value, t0.ast, operation.t1.ast)
141
- end
142
- end
143
- }
144
- end
145
-
146
- rule relational_op
147
- '<' / '>' / '>=' / '<='
148
- end
149
-
150
- rule additive_texpr
151
- s t0:multiplicative_texpr tail:(s op:additive_op s t1:multiplicative_texpr)*
152
- {
153
- def ast
154
- if tail.elements.empty?
155
- t0.ast
156
- else
157
- Compiler::Sum.new(
158
- t0.ast,
159
- *tail.elements.map{|e| e.op.text_value == '-' ? Compiler::Negate.new(e.t1.ast) : e.t1.ast}
160
- )
161
- end
162
- end
163
- }
164
- end
165
-
166
- rule additive_op
167
- '+' / '-'
168
- end
169
-
170
- rule multiplicative_texpr
171
- s t0:unary_texpr tail:(s op:multiplicative_op s t1:unary_texpr)*
172
- {
173
- def ast
174
- if tail.elements.empty?
175
- t0.ast
176
- else
177
- Compiler::Product.new(
178
- t0.ast,
179
- *tail.elements.map{|e| e.op.text_value == '/' ? Compiler::Reciprocal.new(e.t1.ast) : e.t1.ast}
180
- )
181
- end
182
- end
183
- }
184
- end
185
-
186
- rule multiplicative_op
187
- '*' / '/'
188
- end
189
-
190
- rule unary_texpr
191
- s u:unary_op? s t:primary_texpr
192
- {
193
- def ast
194
- if u.empty?
195
- t.ast
196
- else
197
- u.text_value == '-' ? Compiler::Negate.new(t.ast) : Compiler::Negation.new(t.ast)
198
- end
199
- end
200
- }
201
- end
202
-
203
- rule unary_op
204
- '-' / '!'
205
- end
206
-
207
- rule primary_texpr
208
- tl:term_list
209
- {
210
- def ast
211
- Compiler::ExpressionTermList.new(tl.ast)
212
- end
213
- }
214
- / l:literal
215
- {
216
- def ast
217
- Compiler::Literal.new(l.value, nil)
218
- end
219
- }
220
- # / '(' source_expr ')'
221
- # / id '(' source_expr (s ',' s source_expr) * ')'
222
- end
223
-
224
- end
225
- end
226
- end
@@ -1,244 +0,0 @@
1
- #
2
- # ActiveFacts CQL Parser.
3
- # Parse rules relating to ValueType definitions.
4
- #
5
- # Copyright (c) 2009 Clifford Heath. Read the LICENSE file.
6
- #
7
- module ActiveFacts
8
- module CQL
9
- grammar ValueTypes
10
- rule value_type
11
- s each?
12
- s term_definition_name
13
- m1:mapping_pragmas
14
- # REVISIT: ORM2 would allow (subtype_prefix term)?
15
- written_as
16
- any? s
17
- base:(term/implicit_value_type_name) s
18
- value_type_parameters
19
- u:in_units?
20
- a:auto_assignment?
21
- c:context_note?
22
- r:(value_constraint enforcement)?
23
- m2:mapping_pragmas
24
- c2:context_note?
25
- s ';' s
26
- {
27
- def ast
28
- name = term_definition_name.value
29
- params = value_type_parameters.values
30
- value_constraint = nil
31
- unless r.empty?
32
- value_constraint = Compiler::ValueConstraint.new(r.value_constraint.ast, r.enforcement.ast)
33
- end
34
- units = u.empty? ? [] : u.units.value
35
- auto_assigned_at = a.empty? ? nil : a.auto_assigned_at
36
- pragmas = m1.value+m2.value
37
- context_note = !c.empty? ? c.ast : (!c2.empty? ? c2.ast : nil)
38
- Compiler::ValueType.new name, base.value, params, units, value_constraint, pragmas, context_note, auto_assigned_at
39
- end
40
- }
41
- end
42
-
43
- rule in_units
44
- in S units
45
- end
46
-
47
- # A ValueType name that might not yet have been identified or registered as a Term
48
- # REVISIT: We'd like to support multi-word terms here (cannot include "where", "and", or anything that could follow in the value_type definition)
49
- # REVISIT: We'd like these to be forward-referenced ObjectTypes too, not immediately created as ValueTypes
50
- rule implicit_value_type_name
51
- id
52
- {
53
- def node_type; :term; end
54
- }
55
- end
56
-
57
- rule value_type_parameters
58
- '(' s tpl:type_parameter_list? ')' s
59
- { def values; tpl.empty? ? [] : tpl.values; end }
60
- / s
61
- { def values; []; end }
62
- end
63
-
64
- rule type_parameter_list
65
- head:parameter s tail:( ',' s parameter s)*
66
- {
67
- def values
68
- [head.value, *tail.elements.map{|i| i.parameter.value}]
69
- end
70
- }
71
- end
72
-
73
- rule parameter
74
- number / named_parameter
75
- end
76
-
77
- rule ordered_parameter
78
- number
79
- end
80
-
81
- rule named_parameter
82
- ( # Set the value for a parameter
83
- with s parameter_name as s literal:parameter_literal
84
- /
85
- parameter_name ':' s literal:parameter_literal
86
- ) <Parser::ValueTypeParameterSetting>
87
- / # Define a new parameter
88
- accepts s parameter_name as value_type:term s vr:(restricted s to s parameter_restriction)? <Parser::ValueTypeParameterDefinition>
89
- / # Restrict values for a parameter
90
- restricts s parameter_name to s parameter_restriction <Parser::ValueTypeParameterRestriction>
91
- end
92
-
93
- rule parameter_name
94
- !(with/accepts/restricts/as/term) id s
95
- { def value; id.text_value; end }
96
- end
97
-
98
- rule parameter_literal
99
- number
100
- / # We need the text_value of a string here, not the result of parsing it
101
- string { def value; text_value; end }
102
- end
103
-
104
- rule parameter_restriction
105
- range_list s
106
- { def values; range_list.ranges; end}
107
- /
108
- parameter_literal s direction:(min / max)? s
109
- { def values; [:value, parameter_literal.value, direction.text_value]; end}
110
- end
111
-
112
- rule unit_definition
113
- u:(
114
- s coeff:unit_coefficient? base:units? s o:unit_offset?
115
- conversion
116
- singular:unit_name s plural:('/' s p:unit_name s)?
117
- /
118
- s singular:unit_name s plural:('/' s p:unit_name s)?
119
- conversion
120
- coeff:unit_coefficient? base:units? s o:unit_offset?
121
- )
122
- q:(approximately '' / ephemera s url )? s
123
- ';'
124
- {
125
- def ast
126
- singular = u.singular.text_value
127
- plural = u.plural.text_value.empty? ? nil : u.plural.p.text_value
128
- if u.coeff.empty?
129
- raise "Unit definition requires either a coefficient or an ephemera URL" unless q.respond_to?(:ephemera)
130
- numerator,denominator = 1, 1
131
- else
132
- numerator, denominator = *u.coeff.ast
133
- end
134
- offset = u.o.text_value.empty? ? 0 : u.o.value
135
- bases = u.base.empty? ? [] : u.base.value
136
- approximately = q.respond_to?(:approximately) || u.conversion.approximate?
137
- ephemera = q.respond_to?(:ephemera) ? q.url.text_value : nil
138
- Compiler::Unit.new singular, plural, numerator, denominator, offset, bases, approximately, ephemera
139
- end
140
- }
141
- end
142
-
143
- rule unit_name
144
- id
145
- {
146
- def node_type; :unit; end
147
- }
148
- end
149
-
150
-
151
- rule unit_coefficient
152
- numerator:number denominator:(s '/' s number)? s
153
- {
154
- def ast
155
- [ numerator.text_value,
156
- (denominator.text_value.empty? ? "1" : denominator.number.text_value)
157
- ]
158
- end
159
- }
160
- end
161
-
162
- rule unit_offset
163
- sign:[-+] s number s
164
- { def value
165
- sign.text_value == '-' ? "-"+number.text_value : number.text_value
166
- end
167
- }
168
- end
169
-
170
- # In a unit definition, we may use undefined base units; this is the only way to get fundamental units
171
- rule units
172
- !non_unit maybe_unit s tail:(!non_unit maybe_unit s)* div:('/' s maybe_unit s tail:(!non_unit maybe_unit s)*)?
173
- { def value
174
- tail.elements.inject([maybe_unit.value]) { |a, e| a << e.maybe_unit.value } +
175
- (div.text_value.empty? ? [] : div.tail.elements.inject([div.maybe_unit.inverse]) { |a, e| a << e.maybe_unit.inverse })
176
- end
177
- }
178
- end
179
-
180
- rule non_unit
181
- restricted_to / conversion / approximately / ephemera / auto_assignment
182
- end
183
-
184
- rule unit
185
- maybe_unit &{|s| input.context.unit?(s[0].unit_name.text_value) }
186
- end
187
-
188
- rule maybe_unit
189
- unit_name pow:('^' '-'? [0-9])?
190
- { def value
191
- [unit_name.text_value, pow.text_value.empty? ? 1 : Integer(pow.text_value[1..-1])]
192
- end
193
- def inverse
194
- a = value
195
- a[1] = -a[1]
196
- a
197
- end
198
- }
199
- end
200
-
201
- rule value_constraint
202
- restricted_to restricted_values c:context_note?
203
- {
204
- def ast
205
- v = restricted_values.values
206
- c[:context_note] = c.ast unless c.empty?
207
- v
208
- end
209
- }
210
- # REVISIT: "where the possible value/s of that <Term> is/are value (, ...)"
211
- end
212
-
213
- rule restricted_values
214
- range_list s u:units?
215
- {
216
- def values
217
- { :ranges => range_list.ranges,
218
- :units => u.empty? ? nil : u.value
219
- }
220
- end
221
- }
222
- /
223
- regular_expression
224
- {
225
- def values
226
- { :regular_expression => contents }
227
- end
228
- }
229
- end
230
-
231
- rule range_list
232
- '{' s
233
- head:range s tail:( ',' s range )*
234
- '}' s
235
- {
236
- def ranges
237
- [head.value, *tail.elements.map{|e| e.range.value }]
238
- end
239
- }
240
- end
241
-
242
- end
243
- end
244
- end