activefacts-cql 1.9.6 → 1.9.7
Sign up to get free protection for your applications and to get access to all the features.
- 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
|