activefacts-cql 1.8.3 → 1.9.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 +4 -4
- data/.gitignore +1 -0
- data/activefacts-cql.gemspec +1 -1
- data/lib/activefacts/cql/compiler.rb +84 -29
- data/lib/activefacts/cql/compiler/clause.rb +51 -9
- data/lib/activefacts/cql/compiler/constraint.rb +1 -1
- data/lib/activefacts/cql/compiler/expression.rb +279 -34
- data/lib/activefacts/cql/compiler/fact_type.rb +4 -2
- data/lib/activefacts/cql/compiler/query.rb +13 -9
- data/lib/activefacts/cql/compiler/shared.rb +10 -6
- data/lib/activefacts/cql/compiler/transform_rule.rb +136 -0
- data/lib/activefacts/cql/parser.rb +45 -2
- data/lib/activefacts/cql/parser/CQLParser.treetop +59 -5
- data/lib/activefacts/cql/parser/Language/English.treetop +5 -1
- data/lib/activefacts/cql/parser/Language/French.treetop +6 -2
- data/lib/activefacts/cql/parser/Language/Mandarin.treetop +2 -1
- data/lib/activefacts/cql/parser/Terms.treetop +2 -2
- data/lib/activefacts/cql/parser/TransformRules.treetop +226 -0
- data/lib/activefacts/cql/verbaliser.rb +5 -7
- data/lib/activefacts/cql/version.rb +1 -1
- data/lib/activefacts/input/cql.rb +2 -1
- data/lib/rubygems_plugin.rb +15 -12
- metadata +6 -4
@@ -44,7 +44,7 @@ module ActiveFacts
|
|
44
44
|
unless @conditions.empty? and @returning.empty?
|
45
45
|
trace :query, "building query for derived fact type (returning #{@returning}) with #{@conditions.size} conditions: (#{@conditions.map{|c|c.inspect}*', '})" do
|
46
46
|
@query = build_variables(@conditions.flatten)
|
47
|
-
@roles_by_binding = build_all_steps(@conditions)
|
47
|
+
@roles_by_binding = build_all_steps(@query, @conditions)
|
48
48
|
@query.validate
|
49
49
|
@query
|
50
50
|
end
|
@@ -108,7 +108,9 @@ module ActiveFacts
|
|
108
108
|
# Prepare to objectify the fact type (so readings for link fact types can be created)
|
109
109
|
if @name
|
110
110
|
entity_type = @vocabulary.valid_entity_type_name(@name)
|
111
|
-
|
111
|
+
|
112
|
+
# REVISIT disable name check -- GSP 5 Jul 2017
|
113
|
+
raise "You can't objectify #{@name}, it already exists" if entity_type && false
|
112
114
|
@entity_type = @constellation.EntityType(@vocabulary, @name, :fact_type => @fact_type, :concept => :new)
|
113
115
|
end
|
114
116
|
|
@@ -8,8 +8,11 @@ module ActiveFacts
|
|
8
8
|
query = @constellation.Query(:new)
|
9
9
|
all_bindings_in_clauses(clauses_list).
|
10
10
|
each do |binding|
|
11
|
-
|
12
|
-
|
11
|
+
var_name = (r = binding.refs.select{|r| r.is_a?(Reference)}.first) ? r.var_name : nil
|
12
|
+
trace :query, "Creating variable #{query.all_variable.size} for #{binding.inspect} with role_name #{var_name}"
|
13
|
+
binding.variable = @constellation.Variable(
|
14
|
+
query, query.all_variable.size, :object_type => binding.player, role_name: var_name
|
15
|
+
)
|
13
16
|
if literal = binding.refs.detect{|r| r.literal}
|
14
17
|
if literal.kind_of?(ActiveFacts::CQL::Compiler::Reference)
|
15
18
|
# REVISIT: Fix this crappy ad-hoc polymorphism hack
|
@@ -23,21 +26,21 @@ module ActiveFacts
|
|
23
26
|
end
|
24
27
|
end
|
25
28
|
|
26
|
-
def build_all_steps(clauses_list)
|
29
|
+
def build_all_steps(query, clauses_list)
|
27
30
|
roles_by_binding = {}
|
28
31
|
trace :query, "Building steps" do
|
29
32
|
clauses_list.each do |clause|
|
30
|
-
build_step(clause, roles_by_binding)
|
33
|
+
build_step(query, clause, roles_by_binding)
|
31
34
|
end
|
32
35
|
end
|
33
36
|
roles_by_binding
|
34
37
|
end
|
35
38
|
|
36
|
-
def build_step clause, roles_by_binding = {}, parent_variable = nil
|
39
|
+
def build_step query, clause, roles_by_binding = {}, parent_variable = nil
|
37
40
|
return unless clause.refs.size > 0 # Empty clause... really?
|
38
41
|
|
39
42
|
step = @constellation.Step(
|
40
|
-
|
43
|
+
query, query.all_step.size,
|
41
44
|
:fact_type => clause.fact_type,
|
42
45
|
:alternative_set => nil,
|
43
46
|
:is_disallowed => clause.certainty == false,
|
@@ -56,7 +59,7 @@ module ActiveFacts
|
|
56
59
|
objectification_step = nil
|
57
60
|
if ref.nested_clauses
|
58
61
|
ref.nested_clauses.each do |nested_clause|
|
59
|
-
objectification_step = build_step nested_clause, roles_by_binding
|
62
|
+
objectification_step = build_step(query, nested_clause, roles_by_binding)
|
60
63
|
if ref.binding.player.is_a?(ActiveFacts::Metamodel::EntityType) and
|
61
64
|
ref.binding.player.fact_type == nested_clause.fact_type
|
62
65
|
objectification_step.objectification_variable = binding.variable
|
@@ -72,9 +75,10 @@ module ActiveFacts
|
|
72
75
|
if binding.variable.object_type.common_supertype(role.object_type)
|
73
76
|
# REVISIT: there's an implicit subtyping step here, create it; then always raise the error here.
|
74
77
|
# I don't want to do this for now because the verbaliser will always verbalise all steps.
|
75
|
-
raise "Disallowing implicit subtyping step from #{role.object_type.name} to #{binding.variable.object_type.name} in #{clause.fact_type.default_reading.inspect}"
|
78
|
+
# raise "Disallowing implicit subtyping step from #{role.object_type.name} to #{binding.variable.object_type.name} in #{clause.fact_type.default_reading.inspect}"
|
79
|
+
else
|
80
|
+
raise "A #{role.object_type.name} cannot satisfy #{binding.variable.object_type.name} in #{clause.fact_type.default_reading.inspect}"
|
76
81
|
end
|
77
|
-
raise "A #{role.object_type.name} cannot satisfy #{binding.variable.object_type.name} in #{clause.fact_type.default_reading.inspect}"
|
78
82
|
end
|
79
83
|
|
80
84
|
trace :query, "Creating Play for #{ref.inspect}"
|
@@ -13,7 +13,7 @@ module ActiveFacts
|
|
13
13
|
class Binding
|
14
14
|
attr_reader :player # The ObjectType (object type)
|
15
15
|
attr_reader :refs # an array of the References
|
16
|
-
|
16
|
+
attr_accessor :role_name
|
17
17
|
attr_accessor :rebound_to # Loose binding may set this to another binding
|
18
18
|
attr_reader :variable
|
19
19
|
attr_accessor :instance # When binding fact instances, the instance goes here
|
@@ -35,7 +35,7 @@ module ActiveFacts
|
|
35
35
|
def <=>(other)
|
36
36
|
key <=> other.key
|
37
37
|
end
|
38
|
-
|
38
|
+
|
39
39
|
def variable= v
|
40
40
|
@variable = v # A place for a breakpoint :)
|
41
41
|
end
|
@@ -111,15 +111,17 @@ module ActiveFacts
|
|
111
111
|
end
|
112
112
|
|
113
113
|
class Vocabulary < Definition
|
114
|
-
def initialize name
|
114
|
+
def initialize name, is_transform, version_number
|
115
115
|
@name = name
|
116
|
+
@is_transform = is_transform
|
117
|
+
@version_number = version_number
|
116
118
|
end
|
117
119
|
|
118
120
|
def compile
|
119
121
|
if @constellation.Vocabulary.size > 0
|
120
122
|
@constellation.Topic @name
|
121
123
|
else
|
122
|
-
@constellation.Vocabulary @
|
124
|
+
@constellation.Vocabulary(@name, is_transform: @is_transform, version_number: @version_number)
|
123
125
|
end
|
124
126
|
end
|
125
127
|
|
@@ -129,9 +131,11 @@ module ActiveFacts
|
|
129
131
|
end
|
130
132
|
|
131
133
|
class Import < Definition
|
132
|
-
def initialize parser, name, alias_hash
|
134
|
+
def initialize parser, name, import_role, version_pattern, alias_hash
|
133
135
|
@parser = parser
|
134
136
|
@name = name
|
137
|
+
@import_role = import_role
|
138
|
+
@version_pattern = version_pattern
|
135
139
|
@alias_hash = alias_hash
|
136
140
|
end
|
137
141
|
|
@@ -140,7 +144,7 @@ module ActiveFacts
|
|
140
144
|
end
|
141
145
|
|
142
146
|
def compile
|
143
|
-
@parser.compile_import(@name, @alias_hash)
|
147
|
+
@parser.compile_import(@name, @import_role, @alias_hash)
|
144
148
|
end
|
145
149
|
end
|
146
150
|
|
@@ -0,0 +1,136 @@
|
|
1
|
+
#
|
2
|
+
# ActiveFacts CQL Parser.
|
3
|
+
# Compiler classes relating to Transform Rules.
|
4
|
+
#
|
5
|
+
# Copyright (c) 2017 Factil Pty Ltd. Read the LICENSE file.
|
6
|
+
#
|
7
|
+
module ActiveFacts
|
8
|
+
module CQL
|
9
|
+
class Compiler < ActiveFacts::CQL::Parser
|
10
|
+
|
11
|
+
class TransformRule < Definition
|
12
|
+
attr_accessor :compound_matching
|
13
|
+
|
14
|
+
def initialize compound_matching
|
15
|
+
@compound_matching = compound_matching
|
16
|
+
end
|
17
|
+
|
18
|
+
def compile
|
19
|
+
context = CompilationContext.new(@vocabulary)
|
20
|
+
transform_matching = @compound_matching.compile(context)
|
21
|
+
@constellation.TransformRule(:new, :compound_matching => transform_matching)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.build_transform_target_refs context, targ_term_list, transform_rule
|
26
|
+
vocabulary_identifier = context.vocabulary.identifying_role_values
|
27
|
+
constellation = context.vocabulary.constellation
|
28
|
+
|
29
|
+
targ_term_list.flatten!
|
30
|
+
(0 ... targ_term_list.size).each do |idx|
|
31
|
+
ref = targ_term_list[idx]
|
32
|
+
if (target_ot = constellation.ObjectType[[vocabulary_identifier, ref.term]]).nil?
|
33
|
+
raise "Target object '#{ref.term}' of transformation must be a valid object type"
|
34
|
+
end
|
35
|
+
constellation.TransformTargetRef(
|
36
|
+
transform_rule, idx, :object_type => target_ot,
|
37
|
+
:leading_adjective => ref.leading_adjective, :trailing_adjective => ref.trailing_adjective
|
38
|
+
)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
class CompoundMatching
|
43
|
+
attr_accessor :targ_term_list, :transform_query, :transform_matchings
|
44
|
+
|
45
|
+
def initialize targ_term_list, transform_query, transform_matchings
|
46
|
+
@targ_term_list = targ_term_list
|
47
|
+
@transform_query = transform_query
|
48
|
+
@transform_matchings = transform_matchings
|
49
|
+
end
|
50
|
+
|
51
|
+
def compile(context)
|
52
|
+
compound_rule = nil
|
53
|
+
vocabulary_identifier = context.vocabulary.identifying_role_values
|
54
|
+
constellation = context.vocabulary.constellation
|
55
|
+
|
56
|
+
source_ot = nil
|
57
|
+
source_query = nil
|
58
|
+
if @transform_query.is_a?(ActiveFacts::CQL::Compiler::Reference)
|
59
|
+
if (source_ot = constellation.ObjectType[[vocabulary_identifier, @transform_query.term]]).nil?
|
60
|
+
raise "Invalid source object '#{@transform_query.term}' for '#{@targ_term.term}' transformation"
|
61
|
+
end
|
62
|
+
elsif @transform_query.is_a?(Array)
|
63
|
+
query = Query.new(nil, @transform_query.flatten)
|
64
|
+
query.constellation = constellation
|
65
|
+
query.vocabulary = context.vocabulary
|
66
|
+
if (source_query = query.compile).nil?
|
67
|
+
raise "Invalid source query for '#{@targ_term.term}' transformation"
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
compound_rule = constellation.CompoundMatching(
|
72
|
+
:new, :source_object_type => source_ot, :source_query => source_query
|
73
|
+
)
|
74
|
+
ActiveFacts::CQL::Compiler.build_transform_target_refs(context, @targ_term_list, compound_rule)
|
75
|
+
|
76
|
+
@transform_matchings.each do |tr|
|
77
|
+
trule = tr.compile(context)
|
78
|
+
trule.compound_matching = compound_rule
|
79
|
+
end
|
80
|
+
|
81
|
+
compound_rule
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
class SimpleMatching
|
86
|
+
attr_accessor :targ_term_list, :transform_expr
|
87
|
+
|
88
|
+
def initialize targ_term_list, transform_expr
|
89
|
+
@targ_term_list = targ_term_list
|
90
|
+
@transform_expr = transform_expr
|
91
|
+
end
|
92
|
+
|
93
|
+
def compile(context)
|
94
|
+
vocabulary_identifier = context.vocabulary.identifying_role_values
|
95
|
+
constellation = context.vocabulary.constellation
|
96
|
+
|
97
|
+
expr = transform_expr ? transform_expr.compile(context) : nil
|
98
|
+
simple_rule = constellation.SimpleMatching(:new, :expression => expr)
|
99
|
+
ActiveFacts::CQL::Compiler.build_transform_target_refs(context, @targ_term_list, simple_rule)
|
100
|
+
|
101
|
+
simple_rule
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
class ExpressionTermList
|
106
|
+
attr_accessor :term_list
|
107
|
+
|
108
|
+
def initialize term_list
|
109
|
+
@term_list = term_list
|
110
|
+
end
|
111
|
+
|
112
|
+
def compile(context)
|
113
|
+
vocabulary_identifier = context.vocabulary.identifying_role_values
|
114
|
+
constellation = context.vocabulary.constellation
|
115
|
+
|
116
|
+
expression = context.vocabulary.constellation.Expression(:new, :expression_type => 'Role')
|
117
|
+
|
118
|
+
@term_list.flatten!
|
119
|
+
(0 ... @term_list.size).each do |idx|
|
120
|
+
ref = term_list[idx]
|
121
|
+
if (object_type = constellation.ObjectType[[vocabulary_identifier, ref.term]]).nil?
|
122
|
+
raise "Object '#{ref.term}' of transformation must be a valid object type"
|
123
|
+
end
|
124
|
+
constellation.ExpressionObjectRef(
|
125
|
+
expression, idx, :object_type => object_type,
|
126
|
+
:leading_adjective => ref.leading_adjective, :trailing_adjective => ref.trailing_adjective
|
127
|
+
)
|
128
|
+
end
|
129
|
+
|
130
|
+
expression
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
@@ -119,6 +119,8 @@ module ActiveFacts
|
|
119
119
|
@term = @term_part if t[@term_part]
|
120
120
|
@global_term = (t = t[@term_part]) == true ? @term_part : t
|
121
121
|
trace :context, "saving context #{@term}/#{@global_term}"
|
122
|
+
# trace :context, "@terms =\n\t#{@terms.map{|k,v| "#{k} => #{v}"} * "\n\t"}"
|
123
|
+
# trace :context, "@role_names =\n\t#{@role_names.map{|k,v| "#{k} => #{v}"} * "\n\t"}"
|
122
124
|
@context_saver.context = {:term => @term, :global_term => @global_term }
|
123
125
|
end
|
124
126
|
end
|
@@ -128,8 +130,49 @@ module ActiveFacts
|
|
128
130
|
def term_complete?
|
129
131
|
return true if @allowed_forward_terms.include?(@term)
|
130
132
|
return true if system_term(@term)
|
131
|
-
(t = @terms[@term] and t[@term]) or
|
132
|
-
|
133
|
+
result = ((t = @terms[@term] and t[@term]) or (t = @role_names[@term] and t[@term]))
|
134
|
+
trace :context, "term #{@term} is #{result ? '' : 'in'}complete"
|
135
|
+
result
|
136
|
+
end
|
137
|
+
|
138
|
+
def global_term_starts?(s, context_saver)
|
139
|
+
@term = @global_term = nil
|
140
|
+
|
141
|
+
@term_part = s
|
142
|
+
@context_saver = context_saver
|
143
|
+
t = @terms[s] || system_term(s)
|
144
|
+
if t
|
145
|
+
# s is a prefix of the keys of t.
|
146
|
+
if t[s]
|
147
|
+
@global_term = @term = @term_part
|
148
|
+
@context_saver.context = {:term => @term, :global_term => @global_term }
|
149
|
+
end
|
150
|
+
trace :context, "Term #{t[s] ? "is" : "starts"} '#{@term_part}'"
|
151
|
+
elsif @allowed_forward_terms.include?(@term_part)
|
152
|
+
@term = @term_part
|
153
|
+
@context_saver.context = {:term => @term, :global_term => @term }
|
154
|
+
trace :context, "Term #{s} is an allowed forward"
|
155
|
+
return true
|
156
|
+
end
|
157
|
+
t
|
158
|
+
end
|
159
|
+
|
160
|
+
def global_term_continues?(s)
|
161
|
+
@term_part = "#{@term_part} #{s}"
|
162
|
+
t = @terms[@term_part]
|
163
|
+
if t
|
164
|
+
trace :context, "Multi-word term #{t[@term_part] ? 'ends at' : 'continues to'} #{@term_part.inspect}"
|
165
|
+
|
166
|
+
# Record the name of the full term and the underlying global term:
|
167
|
+
if t[@term_part]
|
168
|
+
@term = @term_part if t[@term_part]
|
169
|
+
@global_term = (t = t[@term_part]) == true ? @term_part : t
|
170
|
+
trace :context, "saving context #{@term}/#{@global_term}"
|
171
|
+
# trace :context, "@terms =\n\t#{@terms.map{|k,v| "#{k} => #{v}"} * "\n\t"}"
|
172
|
+
@context_saver.context = {:term => @term, :global_term => @global_term }
|
173
|
+
end
|
174
|
+
end
|
175
|
+
t
|
133
176
|
end
|
134
177
|
|
135
178
|
def system_term(s)
|
@@ -11,6 +11,7 @@ require 'activefacts/cql/parser/Terms'
|
|
11
11
|
require 'activefacts/cql/parser/ObjectTypes'
|
12
12
|
require 'activefacts/cql/parser/ValueTypes'
|
13
13
|
require 'activefacts/cql/parser/FactTypes'
|
14
|
+
require 'activefacts/cql/parser/TransformRules'
|
14
15
|
require 'activefacts/cql/parser/Context'
|
15
16
|
|
16
17
|
module ActiveFacts
|
@@ -22,6 +23,7 @@ module ActiveFacts
|
|
22
23
|
include ObjectTypes
|
23
24
|
include ValueTypes
|
24
25
|
include FactTypes
|
26
|
+
include TransformRules
|
25
27
|
include Context
|
26
28
|
|
27
29
|
rule cql_file
|
@@ -58,20 +60,35 @@ module ActiveFacts
|
|
58
60
|
/ object_type
|
59
61
|
/ informal_description
|
60
62
|
/ query
|
63
|
+
/ transform_rule
|
61
64
|
/ s ';' s { def ast; nil; end }
|
62
65
|
end
|
63
66
|
|
64
67
|
rule vocabulary_definition
|
65
|
-
|
68
|
+
schema_definition /
|
69
|
+
transform_definition
|
70
|
+
end
|
71
|
+
|
72
|
+
rule schema_definition
|
73
|
+
s ( schema / topic / vocabulary ) S vocabulary_name vn:version_number? s ';'
|
74
|
+
{
|
75
|
+
def ast
|
76
|
+
Compiler::Vocabulary.new(vocabulary_name.value, false, vn.empty? ? nil : vn.value)
|
77
|
+
end
|
78
|
+
}
|
79
|
+
end
|
80
|
+
|
81
|
+
rule transform_definition
|
82
|
+
s transform S vocabulary_name vn:version_number? s ';'
|
66
83
|
{
|
67
84
|
def ast
|
68
|
-
Compiler::Vocabulary.new(vocabulary_name.value)
|
85
|
+
Compiler::Vocabulary.new(vocabulary_name.value, true, vn.empty? ? nil : vn.value)
|
69
86
|
end
|
70
87
|
}
|
71
88
|
end
|
72
89
|
|
73
90
|
rule vocabulary_name
|
74
|
-
id tail:(S id)*
|
91
|
+
id tail:(S !version id)*
|
75
92
|
{
|
76
93
|
def node_type; :vocabulary; end
|
77
94
|
def value
|
@@ -81,10 +98,47 @@ module ActiveFacts
|
|
81
98
|
end
|
82
99
|
|
83
100
|
rule import_definition
|
84
|
-
s import S vocabulary_name alias_list ';'
|
101
|
+
s import i:import_role? S vocabulary_name vp:version_pattern? alias_list ';'
|
85
102
|
{
|
86
103
|
def ast
|
87
|
-
Compiler::Import.new(
|
104
|
+
Compiler::Import.new(
|
105
|
+
import.input.parser, vocabulary_name.value, i.empty? ? "topic" : i.value, vp.empty? ? nil : vp.value, alias_list.value
|
106
|
+
)
|
107
|
+
end
|
108
|
+
}
|
109
|
+
end
|
110
|
+
|
111
|
+
rule version_number
|
112
|
+
S version S version_number_string
|
113
|
+
{
|
114
|
+
def value
|
115
|
+
version_number_string.text_value
|
116
|
+
end
|
117
|
+
}
|
118
|
+
end
|
119
|
+
|
120
|
+
rule version_pattern
|
121
|
+
S version S version_pattern_string
|
122
|
+
{
|
123
|
+
def value
|
124
|
+
version_pattern_string.text_value
|
125
|
+
end
|
126
|
+
}
|
127
|
+
end
|
128
|
+
|
129
|
+
rule version_number_string
|
130
|
+
[0-9]+ '.' [0-9]+ '.' [0-9]+ ('-' [0-9A-Za-z-]+ ('.' [0-9A-Za-z-]+ )* )?
|
131
|
+
end
|
132
|
+
|
133
|
+
rule version_pattern_string
|
134
|
+
[0-9]+ ('.' [0-9]+ ('.' [0-9]+ ('-' [0-9A-Za-z-]+ ('.' [0-9A-Za-z-]+ )* )? )? )?
|
135
|
+
end
|
136
|
+
|
137
|
+
rule import_role
|
138
|
+
S id
|
139
|
+
{
|
140
|
+
def value
|
141
|
+
id.text_value
|
88
142
|
end
|
89
143
|
}
|
90
144
|
end
|
@@ -295,6 +295,7 @@ module ActiveFacts
|
|
295
295
|
rule radix_point '.' end
|
296
296
|
rule reflexive 'reflexive' !alphanumeric end
|
297
297
|
rule returning 'returning' !alphanumeric end
|
298
|
+
rule schema 'schema' !alphanumeric end
|
298
299
|
rule separate 'separate' !alphanumeric end
|
299
300
|
rule so_that 'so' S that end
|
300
301
|
rule static 'static' !alphanumeric end
|
@@ -304,10 +305,13 @@ module ActiveFacts
|
|
304
305
|
rule then 'then' !alphanumeric end
|
305
306
|
rule to 'to' !alphanumeric end
|
306
307
|
rule to_avoid to s 'avoid' !alphanumeric end
|
308
|
+
rule topic 'topic' !alphanumeric end
|
309
|
+
rule transform 'transform' !alphanumeric end
|
307
310
|
rule transient 'transient' !alphanumeric end
|
308
311
|
rule transitive 'transitive' !alphanumeric end
|
309
312
|
rule true 'true' !alphanumeric end
|
310
|
-
rule
|
313
|
+
rule version 'version' !alphanumeric end
|
314
|
+
rule vocabulary 'vocabulary' !alphanumeric end
|
311
315
|
rule when 'when' !alphanumeric end
|
312
316
|
rule where 'where' !alphanumeric end
|
313
317
|
rule which 'which' !alphanumeric end
|