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,161 @@
|
|
1
|
+
module ActiveFacts
|
2
|
+
module CQL
|
3
|
+
class Compiler < ActiveFacts::CQL::Parser
|
4
|
+
|
5
|
+
# In a declaration, a Binding has one or more Reference's.
|
6
|
+
# A Binding is for a single ObjectType, normally related to just one Role,
|
7
|
+
# and the references (References) to it will normally be the object_type name
|
8
|
+
# with the same adjectives (modulo loose binding),
|
9
|
+
# or a role name or subscript reference.
|
10
|
+
#
|
11
|
+
# In some situations a Binding will have some References with the same adjectives,
|
12
|
+
# and one or more References with no adjectives - this is called "loose binding".
|
13
|
+
class Binding
|
14
|
+
attr_reader :player # The ObjectType (object type)
|
15
|
+
attr_reader :refs # an array of the References
|
16
|
+
attr_reader :role_name
|
17
|
+
attr_accessor :rebound_to # Loose binding may set this to another binding
|
18
|
+
attr_reader :variable
|
19
|
+
attr_accessor :instance # When binding fact instances, the instance goes here
|
20
|
+
|
21
|
+
def initialize player, role_name = nil
|
22
|
+
@player = player
|
23
|
+
@role_name = role_name
|
24
|
+
@refs = []
|
25
|
+
end
|
26
|
+
|
27
|
+
def inspect
|
28
|
+
"#{@player.name}#{@role_name and @role_name.is_a?(Integer) ? " (#{@role_name})" : " (as #{@role_name})"}"
|
29
|
+
end
|
30
|
+
|
31
|
+
def key
|
32
|
+
"#{@player.name}#{@role_name && " (as #{@role_name})"}"
|
33
|
+
end
|
34
|
+
|
35
|
+
def <=>(other)
|
36
|
+
key <=> other.key
|
37
|
+
end
|
38
|
+
|
39
|
+
def variable= v
|
40
|
+
@variable = v # A place for a breakpoint :)
|
41
|
+
end
|
42
|
+
|
43
|
+
def add_ref ref
|
44
|
+
@refs << ref
|
45
|
+
ref
|
46
|
+
end
|
47
|
+
|
48
|
+
def delete_ref ref
|
49
|
+
@refs.delete ref
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
class CompilationContext
|
54
|
+
attr_accessor :vocabulary
|
55
|
+
attr_accessor :allowed_forward_terms
|
56
|
+
attr_accessor :left_contraction_allowed
|
57
|
+
attr_accessor :left_contractable_clause
|
58
|
+
attr_accessor :left_contraction_conjunction
|
59
|
+
attr_reader :bindings # The Bindings in this declaration
|
60
|
+
attr_reader :player_by_role_name
|
61
|
+
|
62
|
+
def initialize vocabulary
|
63
|
+
@vocabulary = vocabulary
|
64
|
+
@vocabulary_identifier = @vocabulary.identifying_role_values
|
65
|
+
@allowed_forward_terms = []
|
66
|
+
@bindings = {}
|
67
|
+
@player_by_role_name = {}
|
68
|
+
@left_contraction_allowed = false
|
69
|
+
end
|
70
|
+
|
71
|
+
# Look up this object_type by its name
|
72
|
+
def object_type(name)
|
73
|
+
constellation = @vocabulary.constellation
|
74
|
+
player = constellation.ObjectType[[@vocabulary_identifier, name]]
|
75
|
+
|
76
|
+
# Bind to an existing role which has a role name (that's why we bind those first)
|
77
|
+
player ||= @player_by_role_name[name]
|
78
|
+
|
79
|
+
if !player && @allowed_forward_terms.include?(name)
|
80
|
+
@vocabulary.valid_entity_type_name(name) # No need for the result here, just no exceptional condition
|
81
|
+
player = constellation.EntityType(@vocabulary, name, :concept => :new)
|
82
|
+
end
|
83
|
+
|
84
|
+
player
|
85
|
+
end
|
86
|
+
|
87
|
+
# Pass in an array of clauses or References for player identification and binding (creating the Bindings)
|
88
|
+
# It's necessary to identify all players that define a role name first,
|
89
|
+
# so those names exist in the context for where they're used.
|
90
|
+
def bind *clauses
|
91
|
+
cl = clauses.flatten
|
92
|
+
cl.each { |clause| clause.identify_players_with_role_name(self) }
|
93
|
+
cl.each { |clause| clause.identify_other_players(self) }
|
94
|
+
cl.each { |clause| clause.bind(self) }
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
class Definition
|
99
|
+
attr_accessor :constellation, :vocabulary, :tree
|
100
|
+
def compile
|
101
|
+
raise "#{self.class} should implement the compile method"
|
102
|
+
end
|
103
|
+
|
104
|
+
def to_s
|
105
|
+
@vocabulary ? "#{vocabulary.to_s}::" : ''
|
106
|
+
end
|
107
|
+
|
108
|
+
def source
|
109
|
+
@tree.text_value
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
class Vocabulary < Definition
|
114
|
+
def initialize name
|
115
|
+
@name = name
|
116
|
+
end
|
117
|
+
|
118
|
+
def compile
|
119
|
+
if @constellation.Vocabulary.size > 0
|
120
|
+
@constellation.Topic @name
|
121
|
+
else
|
122
|
+
@constellation.Vocabulary @name
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
def to_s
|
127
|
+
@name
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
class Import < Definition
|
132
|
+
def initialize parser, name, alias_hash
|
133
|
+
@parser = parser
|
134
|
+
@name = name
|
135
|
+
@alias_hash = alias_hash
|
136
|
+
end
|
137
|
+
|
138
|
+
def to_s
|
139
|
+
"#{@vocabulary.to_s} imports #{@alias_hash.map{|k,v| "#{k} as #{v}" }*', '};"
|
140
|
+
end
|
141
|
+
|
142
|
+
def compile
|
143
|
+
@parser.compile_import(@name, @alias_hash)
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
class ObjectType < Definition
|
148
|
+
attr_reader :name
|
149
|
+
|
150
|
+
def initialize name
|
151
|
+
@name = name
|
152
|
+
end
|
153
|
+
|
154
|
+
def to_s
|
155
|
+
"#{super}#{@name}"
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|
@@ -0,0 +1,174 @@
|
|
1
|
+
module ActiveFacts
|
2
|
+
module CQL
|
3
|
+
class Compiler < ActiveFacts::CQL::Parser
|
4
|
+
|
5
|
+
class Unit < Definition
|
6
|
+
def initialize singular, plural, numerator, denominator, offset, base_units, approximately, ephemera_url
|
7
|
+
@singular = singular
|
8
|
+
@plural = plural
|
9
|
+
@numerator, @denominator = numerator, denominator
|
10
|
+
@offset = offset
|
11
|
+
@base_units = base_units # An array of pairs, each [unit_name, power]
|
12
|
+
@approximately = approximately
|
13
|
+
@ephemera_url = ephemera_url
|
14
|
+
end
|
15
|
+
|
16
|
+
def compile
|
17
|
+
if (@numerator.to_f / @denominator.to_i != 1.0)
|
18
|
+
coefficient = @constellation.Coefficient(
|
19
|
+
@numerator,
|
20
|
+
@denominator.to_i,
|
21
|
+
!@approximately
|
22
|
+
# REVISIT: activefacts-api is complaining at present. The following is better and should work:
|
23
|
+
# :numerator => @numerator,
|
24
|
+
# :denominator => @denominator.to_i,
|
25
|
+
# :is_precise => !@approximately
|
26
|
+
)
|
27
|
+
else
|
28
|
+
coefficient = nil
|
29
|
+
end
|
30
|
+
@offset = nil if @offset.to_f == 0
|
31
|
+
|
32
|
+
trace :units, "Defining new unit #{@singular}#{@plural ? "/"+@plural : ""}" do
|
33
|
+
trace :units, "Coefficient is #{coefficient.numerator}#{coefficient.denominator != 1 ? "/#{coefficient.denominator}" : ""} #{coefficient.is_precise ? "exactly" : "approximately"}" if coefficient
|
34
|
+
trace :units, "Offset is #{@offset}" if @offset
|
35
|
+
raise "Redefinition of unit #{@singular}" if @constellation.Unit.values.detect{|u| u.name == @singular}
|
36
|
+
raise "Redefinition of unit #{@plural}" if @constellation.Unit.values.detect{|u| u.name == @plural}
|
37
|
+
unit = @constellation.Unit(:new,
|
38
|
+
:name => @singular,
|
39
|
+
:plural_name => @plural,
|
40
|
+
:coefficient => coefficient,
|
41
|
+
:offset => @offset,
|
42
|
+
:is_fundamental => @base_units.empty?,
|
43
|
+
:ephemera_url => @ephemera_url,
|
44
|
+
:vocabulary => @vocabulary
|
45
|
+
)
|
46
|
+
@base_units.each do |base_unit, exponent|
|
47
|
+
base = @constellation.Unit.values.detect{|u| u.name == base_unit || u.plural_name == base_unit }
|
48
|
+
trace :units, "Base unit #{base_unit}^#{exponent} #{base ? "" : "(implicitly fundamental)"}"
|
49
|
+
base ||= @constellation.Unit(:new, :name => base_unit, :is_fundamental => true, :vocabulary => @vocabulary)
|
50
|
+
@constellation.Derivation(:derived_unit => unit, :base_unit => base, :exponent => exponent)
|
51
|
+
end
|
52
|
+
=begin
|
53
|
+
if @plural
|
54
|
+
plural_unit = @constellation.Unit(:new,
|
55
|
+
:name => @plural,
|
56
|
+
:is_fundamental => false,
|
57
|
+
:vocabulary => @vocabulary
|
58
|
+
)
|
59
|
+
@constellation.Derivation(:derived_unit => plural_unit, :base_unit => unit, :exponent => 1)
|
60
|
+
end
|
61
|
+
=end
|
62
|
+
unit
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def inspect
|
67
|
+
to_s
|
68
|
+
end
|
69
|
+
|
70
|
+
def to_s
|
71
|
+
super + "Unit(#{
|
72
|
+
@singular
|
73
|
+
}#{
|
74
|
+
@plural ? '/'+@plural : ''
|
75
|
+
}) is #{
|
76
|
+
@numerator
|
77
|
+
}/#{
|
78
|
+
@denominator
|
79
|
+
}+#{
|
80
|
+
@offset
|
81
|
+
} #{
|
82
|
+
@base_units.map{|b,e|
|
83
|
+
b+'^'+e.to_s
|
84
|
+
}*'*'
|
85
|
+
}"
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
class ValueType < ObjectType
|
90
|
+
def initialize name, base, parameters, unit, value_constraint, pragmas, context_note, auto_assigned_at
|
91
|
+
super name
|
92
|
+
@base_type_name = base
|
93
|
+
@parameters = parameters
|
94
|
+
@unit = unit
|
95
|
+
@value_constraint = value_constraint
|
96
|
+
@pragmas = pragmas
|
97
|
+
@context_note = context_note
|
98
|
+
@auto_assigned_at = auto_assigned_at
|
99
|
+
end
|
100
|
+
|
101
|
+
def compile
|
102
|
+
length, scale = *@parameters
|
103
|
+
|
104
|
+
# Create the base type unless it already exists:
|
105
|
+
base_type = nil
|
106
|
+
if (@base_type_name != @name)
|
107
|
+
unless base_type = @vocabulary.valid_value_type_name(@base_type_name)
|
108
|
+
base_type = @constellation.ValueType(@vocabulary, @base_type_name, :concept => :new)
|
109
|
+
return base_type if @base_type_name == @name
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
# Create and initialise the ValueType:
|
114
|
+
vt = @vocabulary.valid_value_type_name(@name) ||
|
115
|
+
@constellation.ValueType(@vocabulary, @name, :concept => :new)
|
116
|
+
vt.is_independent = true if @pragmas.delete('independent')
|
117
|
+
@pragmas.each do |p|
|
118
|
+
@constellation.ConceptAnnotation(:concept => vt.concept, :mapping_annotation => p)
|
119
|
+
end if @pragmas
|
120
|
+
vt.supertype = base_type if base_type
|
121
|
+
vt.length = length if length
|
122
|
+
vt.scale = scale if scale
|
123
|
+
vt.transaction_phase = @auto_assigned_at
|
124
|
+
|
125
|
+
unless @unit.empty?
|
126
|
+
unit_name, exponent = *@unit[0]
|
127
|
+
unit = @constellation.Name[unit_name].unit ||
|
128
|
+
@constellation.Name[unit_name].plural_named_unit
|
129
|
+
raise "Unit #{unit_name} for value type #{@name} is not defined" unless unit
|
130
|
+
if exponent != 1
|
131
|
+
base_unit = unit
|
132
|
+
unit_name = base_unit.name+"^#{exponent}"
|
133
|
+
unless unit = @constellation.Unit.detect{|k,v| v.name == unit_name }
|
134
|
+
# Define a derived unit (these are skipped on output)
|
135
|
+
unit = @constellation.Unit(:new,
|
136
|
+
:vocabulary => @vocabulary,
|
137
|
+
:name => unit_name,
|
138
|
+
:is_fundamental => false
|
139
|
+
)
|
140
|
+
@constellation.Derivation(unit, base_unit).exponent = exponent
|
141
|
+
end
|
142
|
+
end
|
143
|
+
vt.unit = unit
|
144
|
+
end
|
145
|
+
|
146
|
+
if @value_constraint
|
147
|
+
@value_constraint.constellation = @constellation
|
148
|
+
vt.value_constraint = @value_constraint.compile
|
149
|
+
end
|
150
|
+
|
151
|
+
if @context_note
|
152
|
+
@context_note.compile(@constellation, vt)
|
153
|
+
end
|
154
|
+
|
155
|
+
vt
|
156
|
+
end
|
157
|
+
|
158
|
+
def to_s
|
159
|
+
"ValueType: #{super} is written as #{
|
160
|
+
@base_type_name
|
161
|
+
}#{
|
162
|
+
@parameters.size > 0 ? "(#{ @parameters.map{|p|p.to_s}*', ' })" : ''
|
163
|
+
}#{
|
164
|
+
@unit && @unit.length > 0 ? " in #{@unit.inspect}" : ''
|
165
|
+
}#{
|
166
|
+
@value_constraint ? " "+@value_constraint.to_s : ''
|
167
|
+
}#{
|
168
|
+
@pragmas.size > 0 ? ", pragmas [#{@pragmas*','}]" : ''
|
169
|
+
};"
|
170
|
+
end
|
171
|
+
end
|
172
|
+
end
|
173
|
+
end
|
174
|
+
end
|
@@ -0,0 +1,234 @@
|
|
1
|
+
#
|
2
|
+
# ActiveFacts CQL Parser.
|
3
|
+
# The parser turns CQL strings into abstract syntax trees ready for semantic analysis.
|
4
|
+
#
|
5
|
+
# Copyright (c) 2009 Clifford Heath. Read the LICENSE file.
|
6
|
+
#
|
7
|
+
require 'rubygems'
|
8
|
+
require 'treetop'
|
9
|
+
|
10
|
+
# These are Treetop files, which Polyglot will compile on the fly if precompiled ones aren't found:
|
11
|
+
|
12
|
+
require 'activefacts/cql/parser/CQLParser'
|
13
|
+
|
14
|
+
module ActiveFacts
|
15
|
+
module CQL
|
16
|
+
class Parser < CQLParser
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
require 'activefacts/cql/parser/nodes'
|
21
|
+
|
22
|
+
class Treetop::Runtime::SyntaxNode
|
23
|
+
def node_type
|
24
|
+
terminal? ? :keyword : :composite
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
module ActiveFacts
|
29
|
+
module CQL
|
30
|
+
module Terms
|
31
|
+
class SavedContext < Treetop::Runtime::SyntaxNode
|
32
|
+
attr_accessor :context
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
# Extend the generated parser:
|
37
|
+
class Parser
|
38
|
+
include ActiveFacts
|
39
|
+
|
40
|
+
# The Context manages some key information revealed or needed during parsing
|
41
|
+
# These methods are semantic predicates; if they return false this parse rule will fail.
|
42
|
+
class Context
|
43
|
+
attr_reader :term, :global_term
|
44
|
+
attr_reader :terms
|
45
|
+
|
46
|
+
def initialize(parser)
|
47
|
+
@parser = parser
|
48
|
+
@terms = {}
|
49
|
+
@role_names = {}
|
50
|
+
@allowed_forward_terms = []
|
51
|
+
end
|
52
|
+
|
53
|
+
def object_type(name, kind)
|
54
|
+
index_name(@terms, name) && trace(:context, "new #{kind} '#{name}'")
|
55
|
+
true
|
56
|
+
end
|
57
|
+
|
58
|
+
def reset_role_names
|
59
|
+
trace :context, "\tresetting role names #{@role_names.keys.sort*", "}" if @role_names && @role_names.size > 0
|
60
|
+
@role_names = {}
|
61
|
+
end
|
62
|
+
|
63
|
+
def allowed_forward_terms(terms)
|
64
|
+
@allowed_forward_terms = terms
|
65
|
+
end
|
66
|
+
|
67
|
+
def new_leading_adjective_term(adj, term)
|
68
|
+
index_name(@role_names, "#{adj} #{term}", term) && trace(:context, "new compound term '#{adj}- #{term}'")
|
69
|
+
true
|
70
|
+
end
|
71
|
+
|
72
|
+
def new_trailing_adjective_term(adj, term)
|
73
|
+
index_name(@role_names, "#{term} #{adj}", term) && trace(:context, "new compound term '#{term} -#{adj}'")
|
74
|
+
true
|
75
|
+
end
|
76
|
+
|
77
|
+
def role_name(name)
|
78
|
+
index_name(@role_names, name) && trace(:context, "new role '#{name}'")
|
79
|
+
true
|
80
|
+
end
|
81
|
+
|
82
|
+
def term_starts?(s, context_saver)
|
83
|
+
@term = @global_term = nil
|
84
|
+
|
85
|
+
@term_part = s
|
86
|
+
@context_saver = context_saver
|
87
|
+
t = @terms[s] || @role_names[s] || system_term(s)
|
88
|
+
if t
|
89
|
+
# s is a prefix of the keys of t.
|
90
|
+
if t[s]
|
91
|
+
@global_term = @term = @term_part
|
92
|
+
@context_saver.context = {:term => @term, :global_term => @global_term }
|
93
|
+
end
|
94
|
+
trace :context, "Term #{t[s] ? "is" : "starts"} '#{@term_part}'"
|
95
|
+
elsif @allowed_forward_terms.include?(@term_part)
|
96
|
+
@term = @term_part
|
97
|
+
@context_saver.context = {:term => @term, :global_term => @term }
|
98
|
+
trace :context, "Term #{s} is an allowed forward"
|
99
|
+
return true
|
100
|
+
end
|
101
|
+
t
|
102
|
+
end
|
103
|
+
|
104
|
+
def term_continues?(s)
|
105
|
+
@term_part = "#{@term_part} #{s}"
|
106
|
+
t = @terms[@term_part]
|
107
|
+
r = @role_names[@term_part]
|
108
|
+
if t && (!r || !r[@term_part]) # Part of a term and not a complete role name
|
109
|
+
w = "term"
|
110
|
+
else
|
111
|
+
t = r
|
112
|
+
w = "role_name"
|
113
|
+
end
|
114
|
+
if t
|
115
|
+
trace :context, "Multi-word #{w} #{t[@term_part] ? 'ends at' : 'continues to'} #{@term_part.inspect}"
|
116
|
+
|
117
|
+
# Record the name of the full term and the underlying global term:
|
118
|
+
if t[@term_part]
|
119
|
+
@term = @term_part if t[@term_part]
|
120
|
+
@global_term = (t = t[@term_part]) == true ? @term_part : t
|
121
|
+
trace :context, "saving context #{@term}/#{@global_term}"
|
122
|
+
@context_saver.context = {:term => @term, :global_term => @global_term }
|
123
|
+
end
|
124
|
+
end
|
125
|
+
t
|
126
|
+
end
|
127
|
+
|
128
|
+
def term_complete?
|
129
|
+
return true if @allowed_forward_terms.include?(@term)
|
130
|
+
return true if system_term(@term)
|
131
|
+
(t = @terms[@term] and t[@term]) or
|
132
|
+
(t = @role_names[@term] and t[@term])
|
133
|
+
end
|
134
|
+
|
135
|
+
def system_term(s)
|
136
|
+
false
|
137
|
+
end
|
138
|
+
|
139
|
+
def unit? s
|
140
|
+
@parser.unit? s
|
141
|
+
end
|
142
|
+
|
143
|
+
private
|
144
|
+
# Index the name by all prefixes
|
145
|
+
def index_name(index, name, value = true)
|
146
|
+
added = false
|
147
|
+
words = name.split(/\s+/)
|
148
|
+
words.inject("") do |n, w|
|
149
|
+
# Index all prefixes up to the full term
|
150
|
+
n = n.empty? ? w : "#{n} #{w}"
|
151
|
+
index[n] ||= {}
|
152
|
+
added = true unless index[n][name]
|
153
|
+
index[n][name] = value # Save all possible completions of this prefix
|
154
|
+
n
|
155
|
+
end
|
156
|
+
added
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
class InputProxy
|
161
|
+
attr_reader :context, :parser
|
162
|
+
|
163
|
+
def initialize(input, context, parser)
|
164
|
+
@input = input
|
165
|
+
@context = context
|
166
|
+
@parser = parser
|
167
|
+
end
|
168
|
+
|
169
|
+
def length
|
170
|
+
@input.length
|
171
|
+
end
|
172
|
+
|
173
|
+
def size
|
174
|
+
length
|
175
|
+
end
|
176
|
+
|
177
|
+
def [](*a)
|
178
|
+
@input[*a]
|
179
|
+
end
|
180
|
+
|
181
|
+
def index(*a)
|
182
|
+
@input.index(*a)
|
183
|
+
end
|
184
|
+
|
185
|
+
def line_of(x)
|
186
|
+
@input.line_of(x)
|
187
|
+
end
|
188
|
+
|
189
|
+
def column_of(x)
|
190
|
+
@input.column_of(x)
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
def context
|
195
|
+
@context ||= Context.new(self)
|
196
|
+
end
|
197
|
+
|
198
|
+
def unit?(s)
|
199
|
+
# puts "Asking whether #{s.inspect} is a unit"
|
200
|
+
true
|
201
|
+
end
|
202
|
+
|
203
|
+
def parse(input, options = {})
|
204
|
+
input = InputProxy.new(input, context, self) unless input.respond_to?(:context)
|
205
|
+
super(input, options)
|
206
|
+
end
|
207
|
+
|
208
|
+
def parse_all(input, rule_name = nil, &block)
|
209
|
+
self.root = rule_name if rule_name
|
210
|
+
|
211
|
+
@index = 0 # Byte offset to start next parse
|
212
|
+
@block = block
|
213
|
+
self.consume_all_input = false
|
214
|
+
nodes = []
|
215
|
+
begin
|
216
|
+
node = parse(InputProxy.new(input, context, self), :index => @index)
|
217
|
+
unless node
|
218
|
+
raise failure_reason unless @index == input.size
|
219
|
+
return nil # No input, or no more input
|
220
|
+
end
|
221
|
+
if @block
|
222
|
+
@block.call(node)
|
223
|
+
else
|
224
|
+
nodes << node
|
225
|
+
end
|
226
|
+
end until self.index == @input_length
|
227
|
+
@block ? true : nodes
|
228
|
+
end
|
229
|
+
end
|
230
|
+
|
231
|
+
end
|
232
|
+
|
233
|
+
Polyglot.register('cql', CQL::Parser)
|
234
|
+
end
|