activefacts-cql 1.7.1
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 +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
|