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,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
|