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.
- checksums.yaml +4 -4
- data/Rakefile +19 -0
- data/activefacts-cql.gemspec +5 -3
- data/lib/activefacts/cql/parser.rb +4 -0
- data/lib/activefacts/cql/version.rb +1 -1
- metadata +19 -21
- data/lib/activefacts/cql/Rakefile +0 -14
- data/lib/activefacts/cql/parser/CQLParser.treetop +0 -258
- data/lib/activefacts/cql/parser/Context.treetop +0 -46
- data/lib/activefacts/cql/parser/Expressions.treetop +0 -67
- data/lib/activefacts/cql/parser/FactTypes.treetop +0 -371
- data/lib/activefacts/cql/parser/Language/English.treetop +0 -329
- data/lib/activefacts/cql/parser/Language/French.treetop +0 -325
- data/lib/activefacts/cql/parser/Language/Mandarin.treetop +0 -305
- data/lib/activefacts/cql/parser/LexicalRules.treetop +0 -253
- data/lib/activefacts/cql/parser/ObjectTypes.treetop +0 -209
- data/lib/activefacts/cql/parser/Terms.treetop +0 -197
- data/lib/activefacts/cql/parser/TransformRules.treetop +0 -226
- data/lib/activefacts/cql/parser/ValueTypes.treetop +0 -244
@@ -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
|