activefacts-cql 1.7.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +11 -0
- data/.rspec +1 -0
- data/.travis.yml +4 -0
- data/Gemfile +10 -0
- data/LICENSE.txt +21 -0
- data/README.md +19 -0
- data/Rakefile +6 -0
- data/activefacts-cql.gemspec +29 -0
- data/bin/setup +7 -0
- data/lib/activefacts/cql.rb +7 -0
- data/lib/activefacts/cql/.gitignore +0 -0
- data/lib/activefacts/cql/Rakefile +14 -0
- data/lib/activefacts/cql/compiler.rb +156 -0
- data/lib/activefacts/cql/compiler/clause.rb +1137 -0
- data/lib/activefacts/cql/compiler/constraint.rb +581 -0
- data/lib/activefacts/cql/compiler/entity_type.rb +457 -0
- data/lib/activefacts/cql/compiler/expression.rb +443 -0
- data/lib/activefacts/cql/compiler/fact.rb +390 -0
- data/lib/activefacts/cql/compiler/fact_type.rb +421 -0
- data/lib/activefacts/cql/compiler/query.rb +106 -0
- data/lib/activefacts/cql/compiler/shared.rb +161 -0
- data/lib/activefacts/cql/compiler/value_type.rb +174 -0
- data/lib/activefacts/cql/parser.rb +234 -0
- data/lib/activefacts/cql/parser/CQLParser.treetop +167 -0
- data/lib/activefacts/cql/parser/Context.treetop +48 -0
- data/lib/activefacts/cql/parser/Expressions.treetop +67 -0
- data/lib/activefacts/cql/parser/FactTypes.treetop +358 -0
- data/lib/activefacts/cql/parser/Language/English.treetop +315 -0
- data/lib/activefacts/cql/parser/Language/French.treetop +315 -0
- data/lib/activefacts/cql/parser/Language/Mandarin.treetop +304 -0
- data/lib/activefacts/cql/parser/LexicalRules.treetop +253 -0
- data/lib/activefacts/cql/parser/ObjectTypes.treetop +210 -0
- data/lib/activefacts/cql/parser/Terms.treetop +183 -0
- data/lib/activefacts/cql/parser/ValueTypes.treetop +202 -0
- data/lib/activefacts/cql/parser/nodes.rb +49 -0
- data/lib/activefacts/cql/require.rb +36 -0
- data/lib/activefacts/cql/verbaliser.rb +804 -0
- data/lib/activefacts/cql/version.rb +5 -0
- data/lib/activefacts/input/cql.rb +43 -0
- data/lib/rubygems_plugin.rb +12 -0
- metadata +167 -0
@@ -0,0 +1,167 @@
|
|
1
|
+
#
|
2
|
+
# ActiveFacts CQL Parser.
|
3
|
+
# Parse rules relating to high-level CQL definitions and constraints.
|
4
|
+
#
|
5
|
+
# Copyright (c) 2009 Clifford Heath. Read the LICENSE file.
|
6
|
+
#
|
7
|
+
require 'activefacts/cql/parser/LexicalRules'
|
8
|
+
require 'activefacts/cql/parser/Language/English'
|
9
|
+
require 'activefacts/cql/parser/Expressions'
|
10
|
+
require 'activefacts/cql/parser/Terms'
|
11
|
+
require 'activefacts/cql/parser/ObjectTypes'
|
12
|
+
require 'activefacts/cql/parser/ValueTypes'
|
13
|
+
require 'activefacts/cql/parser/FactTypes'
|
14
|
+
require 'activefacts/cql/parser/Context'
|
15
|
+
|
16
|
+
module ActiveFacts
|
17
|
+
module CQL
|
18
|
+
grammar CQL
|
19
|
+
include LexicalRules
|
20
|
+
include Expressions
|
21
|
+
include Terms
|
22
|
+
include ObjectTypes
|
23
|
+
include ValueTypes
|
24
|
+
include FactTypes
|
25
|
+
include Context
|
26
|
+
|
27
|
+
rule cql_file
|
28
|
+
s seq:definition*
|
29
|
+
{
|
30
|
+
def definitions
|
31
|
+
seq.elements.map{|e|
|
32
|
+
e.value rescue $stderr.puts "Can't call value() on #{e.inspect}"
|
33
|
+
}
|
34
|
+
end
|
35
|
+
}
|
36
|
+
end
|
37
|
+
|
38
|
+
# Each definition has an ast() method that returns an instance of a subclass of Compiler::Definition
|
39
|
+
rule definition
|
40
|
+
definition_body s
|
41
|
+
{
|
42
|
+
def ast
|
43
|
+
definition_body.ast
|
44
|
+
end
|
45
|
+
|
46
|
+
def body
|
47
|
+
definition_body.text_value
|
48
|
+
end
|
49
|
+
}
|
50
|
+
end
|
51
|
+
|
52
|
+
rule definition_body
|
53
|
+
vocabulary_definition
|
54
|
+
/ import_definition
|
55
|
+
/ prescan # Always fails, but its side-effects are needed in the following
|
56
|
+
/ constraint
|
57
|
+
/ unit_definition # REVISIT: Move this above the prescan?
|
58
|
+
/ object_type
|
59
|
+
/ query
|
60
|
+
/ s ';' s { def ast; nil; end }
|
61
|
+
end
|
62
|
+
|
63
|
+
rule vocabulary_definition
|
64
|
+
s vocabulary S vocabulary_name s ';'
|
65
|
+
{
|
66
|
+
def ast
|
67
|
+
Compiler::Vocabulary.new(vocabulary_name.value)
|
68
|
+
end
|
69
|
+
}
|
70
|
+
end
|
71
|
+
|
72
|
+
rule vocabulary_name
|
73
|
+
id
|
74
|
+
{ def node_type; :vocabulary; end }
|
75
|
+
end
|
76
|
+
|
77
|
+
rule import_definition
|
78
|
+
s import S vocabulary_name alias_list ';'
|
79
|
+
{
|
80
|
+
def ast
|
81
|
+
Compiler::Import.new(import.input.parser, vocabulary_name.value, alias_list.value)
|
82
|
+
end
|
83
|
+
}
|
84
|
+
end
|
85
|
+
|
86
|
+
# REVISIT: Need a way to define equivalent readings for fact types here (and in the metamodel)
|
87
|
+
rule alias_list
|
88
|
+
( s ',' s alias S aliased_from:alias_term S as S alias_to:alias_term s )*
|
89
|
+
{
|
90
|
+
def value
|
91
|
+
elements.inject({}){|h, e| h[e.aliased_from.value] = e.alias_to; h }
|
92
|
+
end
|
93
|
+
}
|
94
|
+
end
|
95
|
+
|
96
|
+
rule alias_term
|
97
|
+
id
|
98
|
+
{ def node_type; :term; end }
|
99
|
+
end
|
100
|
+
|
101
|
+
rule constraint
|
102
|
+
subset_constraint /
|
103
|
+
equality_constraint /
|
104
|
+
set_constraint /
|
105
|
+
presence_constraint
|
106
|
+
# REVISIT: / value_constraint
|
107
|
+
end
|
108
|
+
|
109
|
+
rule enforcement
|
110
|
+
s '(' s otherwise s action s a:agent? s ')' s
|
111
|
+
{
|
112
|
+
def ast; Compiler::Enforcement.new(action.text_value, a.empty? ? nil : a.text_value); end
|
113
|
+
}
|
114
|
+
/
|
115
|
+
''
|
116
|
+
{
|
117
|
+
def ast; nil; end
|
118
|
+
}
|
119
|
+
end
|
120
|
+
|
121
|
+
# An enforcement action, like SMS, email, log, alarm, etc.
|
122
|
+
rule action
|
123
|
+
id
|
124
|
+
end
|
125
|
+
|
126
|
+
# presence constraint:
|
127
|
+
rule presence_constraint
|
128
|
+
(each_occurs_in_clauses / either_or)
|
129
|
+
{
|
130
|
+
def ast
|
131
|
+
Compiler::PresenceConstraint.new c, enforcement.ast, clauses_ast, role_list_ast, quantifier_ast
|
132
|
+
end
|
133
|
+
}
|
134
|
+
end
|
135
|
+
|
136
|
+
# set (exclusion, mandatory exclusion, complex equality) constraint
|
137
|
+
rule set_constraint
|
138
|
+
(for_each_how_many / either_or_not_both)
|
139
|
+
{
|
140
|
+
def ast
|
141
|
+
Compiler::SetExclusionConstraint.new c, enforcement.ast, clauses_ast, role_list_ast, quantifier_ast
|
142
|
+
end
|
143
|
+
}
|
144
|
+
end
|
145
|
+
|
146
|
+
rule subset_constraint
|
147
|
+
(a_only_if_b / if_b_then_a)
|
148
|
+
{
|
149
|
+
def ast
|
150
|
+
Compiler::SubsetConstraint.new c, enforcement.ast, [clauses.ast, r2.ast]
|
151
|
+
end
|
152
|
+
}
|
153
|
+
end
|
154
|
+
|
155
|
+
rule equality_constraint
|
156
|
+
if_and_only_if
|
157
|
+
{
|
158
|
+
def ast
|
159
|
+
all_clauses = [clauses.ast, *tail.elements.map{|e| e.clauses.ast }]
|
160
|
+
Compiler::SetEqualityConstraint.new c, enforcement.ast, all_clauses
|
161
|
+
end
|
162
|
+
}
|
163
|
+
end
|
164
|
+
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
#
|
2
|
+
# ActiveFacts CQL Parser.
|
3
|
+
# Parse rules relating to definition context.
|
4
|
+
#
|
5
|
+
# Copyright (c) 2009 Clifford Heath. Read the LICENSE file.
|
6
|
+
#
|
7
|
+
module ActiveFacts
|
8
|
+
module CQL
|
9
|
+
grammar Context
|
10
|
+
rule context_note
|
11
|
+
'('
|
12
|
+
s w:who_says? s context_type discussion agreed:(',' a:as_agreed_by)? s
|
13
|
+
')' s
|
14
|
+
{
|
15
|
+
def value
|
16
|
+
[ w.empty? ? nil : w.value, context_type.value, discussion.text_value, agreed.empty? ? [] : agreed.a.value]
|
17
|
+
end
|
18
|
+
def ast
|
19
|
+
who = w.empty? ? nil : w.value
|
20
|
+
ag = agreed.empty? ? [] : agreed.a.value
|
21
|
+
Compiler::ContextNote.new context_type.value, discussion.text_value, who, ag
|
22
|
+
end
|
23
|
+
}
|
24
|
+
end
|
25
|
+
|
26
|
+
rule who_says
|
27
|
+
according_to agents s ','
|
28
|
+
{ def value; agents.value; end }
|
29
|
+
end
|
30
|
+
|
31
|
+
rule context_type
|
32
|
+
because s { def value; 'because'; end } /
|
33
|
+
as_opposed_to { def value; 'as_opposed_to'; end } /
|
34
|
+
so_that { def value; 'so_that'; end } /
|
35
|
+
to_avoid { def value; 'to_avoid'; end }
|
36
|
+
end
|
37
|
+
|
38
|
+
rule discussion
|
39
|
+
(
|
40
|
+
'(' discussion ')' / (!( [()] / ',' as_agreed_by) .)*
|
41
|
+
)
|
42
|
+
{
|
43
|
+
def node_type; :linking; end
|
44
|
+
}
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
#
|
2
|
+
# ActiveFacts CQL Parser.
|
3
|
+
# Parse rules relating to Expressions
|
4
|
+
#
|
5
|
+
# Copyright (c) 2009 Clifford Heath. Read the LICENSE file.
|
6
|
+
#
|
7
|
+
module ActiveFacts
|
8
|
+
module CQL
|
9
|
+
grammar Expressions
|
10
|
+
rule expression
|
11
|
+
sum
|
12
|
+
end
|
13
|
+
|
14
|
+
rule sum
|
15
|
+
t0:product s tail:( op:add_op s t1:product s )*
|
16
|
+
{
|
17
|
+
def ast
|
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
|
23
|
+
end
|
24
|
+
}
|
25
|
+
end
|
26
|
+
|
27
|
+
rule add_op
|
28
|
+
'+' / '-'
|
29
|
+
end
|
30
|
+
|
31
|
+
rule product
|
32
|
+
f0:factor s tail:( op:mul_op s f1:factor s )*
|
33
|
+
{
|
34
|
+
def ast
|
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
|
40
|
+
end
|
41
|
+
}
|
42
|
+
end
|
43
|
+
|
44
|
+
rule factor
|
45
|
+
literal u:unit? s
|
46
|
+
{
|
47
|
+
def ast
|
48
|
+
Compiler::Literal.new(literal.value, u.empty? ? nil : u.text_value)
|
49
|
+
end
|
50
|
+
}
|
51
|
+
/ derived_variable
|
52
|
+
/ !context_note '(' s sum s ')' s { def ast; sum.ast; end }
|
53
|
+
end
|
54
|
+
|
55
|
+
rule derived_variable
|
56
|
+
derived:term s role_id:(role_name / subscript )?
|
57
|
+
{
|
58
|
+
def ast quantifier = nil, value_constraint = nil, literal = nil, nested_clauses = nil
|
59
|
+
role_name = role_id.empty? ? nil : role_id.value
|
60
|
+
derived.ast(quantifier, nil, role_name, value_constraint, literal, nested_clauses)
|
61
|
+
end
|
62
|
+
}
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,358 @@
|
|
1
|
+
#
|
2
|
+
# ActiveFacts CQL Parser.
|
3
|
+
# Parse rules relating to FactType definitions.
|
4
|
+
#
|
5
|
+
# Copyright (c) 2009 Clifford Heath. Read the LICENSE file.
|
6
|
+
#
|
7
|
+
module ActiveFacts
|
8
|
+
module CQL
|
9
|
+
grammar FactTypes
|
10
|
+
rule query
|
11
|
+
s query_clauses r:returning_clause? '?'
|
12
|
+
{
|
13
|
+
def ast
|
14
|
+
Compiler::FactType.new nil, [], query_clauses.ast, (r.empty? ? nil : r)
|
15
|
+
end
|
16
|
+
}
|
17
|
+
end
|
18
|
+
|
19
|
+
rule named_fact_type
|
20
|
+
s each?
|
21
|
+
s term_definition_name
|
22
|
+
mapping_pragmas is_where
|
23
|
+
anonymous_fact_type
|
24
|
+
{
|
25
|
+
def ast
|
26
|
+
ft = anonymous_fact_type.ast
|
27
|
+
ft.name = term_definition_name.value
|
28
|
+
pragmas = mapping_pragmas.value
|
29
|
+
pragmas << 'independent' if is_where.independent
|
30
|
+
ft.pragmas = pragmas
|
31
|
+
ft
|
32
|
+
end
|
33
|
+
}
|
34
|
+
end
|
35
|
+
|
36
|
+
rule anonymous_fact_type
|
37
|
+
query_clauses
|
38
|
+
ctail:( (':' / where) s a:query_clauses s)?
|
39
|
+
returning_clause?
|
40
|
+
s ';'
|
41
|
+
{
|
42
|
+
def ast
|
43
|
+
clauses_ast = query_clauses.ast
|
44
|
+
conditions = !ctail.empty? ? ctail.a.ast : []
|
45
|
+
returning = respond_to?(:returning_clause) ? returning_clause.ast : nil
|
46
|
+
value_derivation = clauses_ast.detect{|r| r.is_equality_comparison}
|
47
|
+
if !value_derivation and
|
48
|
+
conditions.empty? and
|
49
|
+
clauses_ast.detect{|r| r.includes_literals}
|
50
|
+
raise "Fact instances may not contain conditions" unless conditions.empty? && !returning
|
51
|
+
Compiler::Fact.new clauses_ast
|
52
|
+
elsif (clauses_ast.size == 1 &&
|
53
|
+
clauses_ast[0].phrases.size == 1 &&
|
54
|
+
(popname = clauses_ast[0].phrases[0]) &&
|
55
|
+
!popname.is_a?(Compiler::Reference) &&
|
56
|
+
conditions.detect{|r| r.includes_literals}
|
57
|
+
)
|
58
|
+
Compiler::Fact.new conditions, popname
|
59
|
+
else
|
60
|
+
Compiler::FactType.new nil, clauses_ast, conditions, returning
|
61
|
+
end
|
62
|
+
end
|
63
|
+
}
|
64
|
+
end
|
65
|
+
|
66
|
+
rule query_clauses
|
67
|
+
qualified_clauses
|
68
|
+
# REVISIT: This creates no precedence between and/or, which could cause confusion.
|
69
|
+
# Should disallow mixed conjuntions - using a sempred?
|
70
|
+
ftail:( conjunction:(',' / and / or ) s qualified_clauses s )*
|
71
|
+
{
|
72
|
+
def ast
|
73
|
+
clauses_ast = qualified_clauses.ast
|
74
|
+
ftail.elements.each{|e|
|
75
|
+
conjunction = e.conjunction.text_value
|
76
|
+
# conjunction = 'and' if conjunction == ',' # ',' means AND, but disallows left-contractions
|
77
|
+
clauses_ast += e.qualified_clauses.ast(conjunction)
|
78
|
+
}
|
79
|
+
clauses_ast
|
80
|
+
end
|
81
|
+
}
|
82
|
+
end
|
83
|
+
|
84
|
+
rule returning_clause
|
85
|
+
returning s return (s ',' s return)*
|
86
|
+
end
|
87
|
+
|
88
|
+
rule return
|
89
|
+
ordering_prefix? phrase+
|
90
|
+
end
|
91
|
+
|
92
|
+
rule qualified_clauses
|
93
|
+
s certainty s contracted_clauses s p:post_qualifiers? s c:context_note?
|
94
|
+
{
|
95
|
+
def ast(conjunction = nil)
|
96
|
+
r = contracted_clauses.ast # An array of clause asts
|
97
|
+
r[0].conjunction = conjunction
|
98
|
+
# pre-qualifiers apply to the first clause, post_qualifiers and context_note to the last
|
99
|
+
# REVISIT: This may be incorrect where the last is a nested clause
|
100
|
+
r[0].certainty = certainty.value
|
101
|
+
r[-1].qualifiers += p.list unless p.empty?
|
102
|
+
r[-1].context_note = c.ast unless c.empty?
|
103
|
+
r
|
104
|
+
end
|
105
|
+
}
|
106
|
+
end
|
107
|
+
|
108
|
+
rule certainty
|
109
|
+
negative_prefix { def value; false; end }
|
110
|
+
/
|
111
|
+
maybe { def value; nil; end }
|
112
|
+
/
|
113
|
+
definitely { def value; true; end }
|
114
|
+
/
|
115
|
+
'' { def value; true; end }
|
116
|
+
end
|
117
|
+
|
118
|
+
rule post_qualifiers
|
119
|
+
'[' s q0:post_qualifier tail:( s ',' s q1:post_qualifier )* s ']' s
|
120
|
+
{
|
121
|
+
def list
|
122
|
+
[q0.text_value, *tail.elements.map{|e| e.q1.text_value}]
|
123
|
+
end
|
124
|
+
}
|
125
|
+
end
|
126
|
+
|
127
|
+
rule post_qualifier
|
128
|
+
static / transient /
|
129
|
+
intransitive / stronglyintransitive / transitive / acyclic / symmetric / asymmetric / antisymmetric / reflexive / irreflexive
|
130
|
+
end
|
131
|
+
|
132
|
+
rule clauses_list
|
133
|
+
clauses tail:( ',' s clauses )*
|
134
|
+
{
|
135
|
+
def ast
|
136
|
+
[clauses.ast, *tail.elements.map{|e| e.clauses.ast }]
|
137
|
+
end
|
138
|
+
}
|
139
|
+
end
|
140
|
+
|
141
|
+
rule clauses
|
142
|
+
contracted_clauses s tail:( and s contracted_clauses s )*
|
143
|
+
{
|
144
|
+
def ast
|
145
|
+
clauses = contracted_clauses.ast
|
146
|
+
tail.elements.map{|e| clauses += e.contracted_clauses.ast }
|
147
|
+
clauses
|
148
|
+
end
|
149
|
+
}
|
150
|
+
end
|
151
|
+
|
152
|
+
rule contracted_clauses
|
153
|
+
comparison
|
154
|
+
/
|
155
|
+
(
|
156
|
+
contraction # A contraction will terminate this repetition by eating to the end
|
157
|
+
/
|
158
|
+
phrase
|
159
|
+
)+
|
160
|
+
{
|
161
|
+
def ast
|
162
|
+
asts = elements.map{ |r| r.ast }
|
163
|
+
contracted_clauses = []
|
164
|
+
qualifiers = []
|
165
|
+
if asts[-1].is_a?(Array) # A contraction (Array of [role, qualifiers, *clauses])
|
166
|
+
contracted_clauses = asts.pop # Pull off the contracted_clauses
|
167
|
+
contracted_role = contracted_clauses.shift
|
168
|
+
qualifiers = contracted_clauses.shift
|
169
|
+
asts.push(contracted_role) # And replace it by the role removed from the contracted_clauses
|
170
|
+
end
|
171
|
+
clause_ast = Compiler::Clause.new(asts, qualifiers)
|
172
|
+
[clause_ast] + contracted_clauses
|
173
|
+
end
|
174
|
+
}
|
175
|
+
end
|
176
|
+
|
177
|
+
rule contraction
|
178
|
+
reading_contraction /
|
179
|
+
condition_contraction
|
180
|
+
end
|
181
|
+
|
182
|
+
rule reading_contraction
|
183
|
+
role p:post_qualifiers? conjunction:(that/who) s certainty s contracted_clauses s
|
184
|
+
{
|
185
|
+
def ast
|
186
|
+
# contracted_clauses.ast will return an array of Clauses, but the first clause is special. We must:
|
187
|
+
# * prepend a new role (we get the Role to build *two* ast nodes)
|
188
|
+
# * attach the qualifiers
|
189
|
+
clauses_ast = contracted_clauses.ast
|
190
|
+
clauses_ast[0].conjunction = conjunction.text_value
|
191
|
+
clauses_ast[0].phrases.unshift(role.ast)
|
192
|
+
clauses_ast[0].certainty = certainty.value
|
193
|
+
|
194
|
+
# A contraction returns an array containing:
|
195
|
+
# * a role AST
|
196
|
+
# * a qualifiers array
|
197
|
+
# * an array of Clauses
|
198
|
+
[role.ast, p.empty? ? [] : p.list] + clauses_ast
|
199
|
+
end
|
200
|
+
}
|
201
|
+
end
|
202
|
+
|
203
|
+
rule condition_contraction
|
204
|
+
role pq:post_qualifiers? certainty s comparator s e2:expression
|
205
|
+
!phrase # The contracted_clauses must not continue here!
|
206
|
+
{
|
207
|
+
def ast
|
208
|
+
c = Compiler::Comparison.new(comparator.text_value, role.ast, e2.ast, certainty.value)
|
209
|
+
c.conjunction = comparator.text_value
|
210
|
+
[ role.ast, pq.empty? ? [] : pq.list, c ]
|
211
|
+
end
|
212
|
+
}
|
213
|
+
end
|
214
|
+
|
215
|
+
rule comparison
|
216
|
+
e1:expression s certainty s comparator s contraction p:post_qualifiers?
|
217
|
+
{
|
218
|
+
def ast
|
219
|
+
role, qualifiers, *clauses_ast = *contraction.ast
|
220
|
+
clauses_ast[0].qualifiers += p.list unless p.empty? # apply post_qualifiers to the contracted clause
|
221
|
+
# clauses_ast[0].conjunction = 'and' # AND is implicit for a contraction
|
222
|
+
c = Compiler::Comparison.new(comparator.text_value, e1.ast, role, certainty.value)
|
223
|
+
[c] + clauses_ast
|
224
|
+
end
|
225
|
+
}
|
226
|
+
/
|
227
|
+
certainty e1:expression s comparator s e2:expression # comparisons have no post-qualifiers: p:post_qualifiers?
|
228
|
+
{
|
229
|
+
def ast
|
230
|
+
c = Compiler::Comparison.new(comparator.text_value, e1.ast, e2.ast, certainty.value)
|
231
|
+
[c]
|
232
|
+
end
|
233
|
+
}
|
234
|
+
end
|
235
|
+
|
236
|
+
rule comparator
|
237
|
+
'<=' / '<>' / '<' / '=' / '>=' / '>' / '!='
|
238
|
+
end
|
239
|
+
|
240
|
+
rule phrase
|
241
|
+
role # A role reference containing a term, perhaps with attached paraphernalia
|
242
|
+
/ # A hyphenated non-term. Important: no embedded spaces
|
243
|
+
id tail:('-' !term id)+ s
|
244
|
+
{
|
245
|
+
def ast
|
246
|
+
[id.value, *tail.elements.map{|e| e.id.value}]*"-"
|
247
|
+
end
|
248
|
+
def node_type; :linking; end
|
249
|
+
}
|
250
|
+
/ # A normal non-term
|
251
|
+
!non_phrase id s
|
252
|
+
{
|
253
|
+
def ast
|
254
|
+
id.value
|
255
|
+
end
|
256
|
+
def node_type; :linking; end
|
257
|
+
}
|
258
|
+
end
|
259
|
+
|
260
|
+
rule role
|
261
|
+
aggregate
|
262
|
+
/
|
263
|
+
simple_role
|
264
|
+
end
|
265
|
+
|
266
|
+
rule aggregate
|
267
|
+
aggregate:id s
|
268
|
+
agg_of s term_or_unary s agg_in s # REVISIT: this term may need to pre-scanned in the qualified_clauses
|
269
|
+
'(' qualified_clauses s ')' # REVISIT: Need to test to verify this is the right level (not query_clauses, etc)
|
270
|
+
{
|
271
|
+
def ast
|
272
|
+
raise "Not implemented: AST for '#{aggregate.text_value} of #{term_or_unary.text_value}'"
|
273
|
+
# This returns just the role with the nested clauses, which doesn't even work:
|
274
|
+
term.ast(
|
275
|
+
nil, # No quantifier
|
276
|
+
nil, # No function call
|
277
|
+
nil, # No role_name
|
278
|
+
nil, # No value_constraint
|
279
|
+
nil, # No literal
|
280
|
+
qualified_clauses.ast
|
281
|
+
)
|
282
|
+
end
|
283
|
+
}
|
284
|
+
end
|
285
|
+
|
286
|
+
rule role_quantifier
|
287
|
+
quantifier mapping_pragmas enforcement cn:context_note?
|
288
|
+
{
|
289
|
+
def ast
|
290
|
+
Compiler::Quantifier.new(
|
291
|
+
quantifier.value[0],
|
292
|
+
quantifier.value[1],
|
293
|
+
enforcement.ast,
|
294
|
+
cn.empty? ? nil : cn.ast,
|
295
|
+
mapping_pragmas.value
|
296
|
+
)
|
297
|
+
end
|
298
|
+
}
|
299
|
+
end
|
300
|
+
|
301
|
+
# This is the rule that causes most back-tracking. I think you can see why.
|
302
|
+
rule simple_role
|
303
|
+
q:role_quantifier?
|
304
|
+
player:derived_variable
|
305
|
+
lr:(
|
306
|
+
literal u:unit?
|
307
|
+
/
|
308
|
+
value_constraint enforcement
|
309
|
+
)?
|
310
|
+
oj:objectification_step?
|
311
|
+
{
|
312
|
+
def ast
|
313
|
+
if !q.empty? && q.quantifier.value
|
314
|
+
quantifier = q.ast
|
315
|
+
end
|
316
|
+
if !lr.empty?
|
317
|
+
if lr.respond_to?(:literal)
|
318
|
+
literal = Compiler::Literal.new(lr.literal.value, lr.u.empty? ? nil : lr.u.text_value)
|
319
|
+
end
|
320
|
+
value_constraint = Compiler::ValueConstraint.new(lr.value_constraint.ast, lr.enforcement.ast) if lr.respond_to?(:value_constraint)
|
321
|
+
raise "It is not permitted to provide both a literal value and a value constraint" if value_constraint and literal
|
322
|
+
end
|
323
|
+
|
324
|
+
nested_clauses =
|
325
|
+
if oj.empty?
|
326
|
+
nil
|
327
|
+
else
|
328
|
+
ast = oj.ast
|
329
|
+
ast[0].conjunction = 'where'
|
330
|
+
ast
|
331
|
+
end
|
332
|
+
player.ast(quantifier, value_constraint, literal, nested_clauses)
|
333
|
+
end
|
334
|
+
}
|
335
|
+
end
|
336
|
+
|
337
|
+
rule objectification_step
|
338
|
+
'(' s in_which s facts:query_clauses s ')' s
|
339
|
+
{
|
340
|
+
def ast
|
341
|
+
facts.ast
|
342
|
+
end
|
343
|
+
}
|
344
|
+
end
|
345
|
+
|
346
|
+
rule role_name
|
347
|
+
'(' s as S r:term s ')' s
|
348
|
+
{ def value; r.value; end }
|
349
|
+
end
|
350
|
+
|
351
|
+
rule subscript
|
352
|
+
'(' s i:([1-9] [0-9]*) s ')' s
|
353
|
+
{ def value; i.text_value.to_i; end }
|
354
|
+
end
|
355
|
+
|
356
|
+
end
|
357
|
+
end
|
358
|
+
end
|