dhaka 2.0.0 → 2.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/dhaka.rb +24 -22
- data/lib/evaluator/evaluator.rb +42 -44
- data/lib/grammar/closure_hash.rb +4 -3
- data/lib/grammar/grammar.rb +113 -110
- data/lib/grammar/grammar_symbol.rb +6 -3
- data/lib/grammar/precedence.rb +3 -2
- data/lib/grammar/production.rb +5 -6
- data/lib/parser/action.rb +16 -11
- data/lib/parser/channel.rb +22 -16
- data/lib/parser/compiled_parser.rb +28 -22
- data/lib/parser/conflict.rb +54 -0
- data/lib/parser/item.rb +19 -19
- data/lib/parser/parse_result.rb +16 -1
- data/lib/parser/parse_tree.rb +15 -9
- data/lib/parser/parser.rb +51 -80
- data/lib/parser/parser_run.rb +6 -6
- data/lib/parser/parser_state.rb +16 -18
- data/lib/parser/token.rb +6 -4
- data/lib/tokenizer/tokenizer.rb +34 -31
- data/test/all_tests.rb +4 -18
- data/test/another_lalr_but_not_slr_grammar.rb +9 -5
- data/test/{arithmetic_evaluator.rb → arithmetic/arithmetic_evaluator.rb} +1 -2
- data/test/{arithmetic_evaluator_test.rb → arithmetic/arithmetic_evaluator_test.rb} +9 -20
- data/test/arithmetic/arithmetic_grammar.rb +41 -0
- data/test/{arithmetic_grammar_test.rb → arithmetic/arithmetic_grammar_test.rb} +2 -4
- data/test/{arithmetic_test_methods.rb → arithmetic/arithmetic_test_methods.rb} +1 -3
- data/test/{arithmetic_tokenizer.rb → arithmetic/arithmetic_tokenizer.rb} +8 -10
- data/test/{arithmetic_tokenizer_test.rb → arithmetic/arithmetic_tokenizer_test.rb} +4 -2
- data/test/{arithmetic_precedence_evaluator.rb → arithmetic_precedence/arithmetic_precedence_evaluator.rb} +1 -2
- data/test/arithmetic_precedence/arithmetic_precedence_grammar.rb +24 -0
- data/test/{arithmetic_precedence_grammar_test.rb → arithmetic_precedence/arithmetic_precedence_grammar_test.rb} +2 -3
- data/test/arithmetic_precedence/arithmetic_precedence_parser_test.rb +31 -0
- data/test/{arithmetic_precedence_tokenizer.rb → arithmetic_precedence/arithmetic_precedence_tokenizer.rb} +8 -10
- data/test/brackets/bracket_grammar.rb +23 -0
- data/test/{bracket_tokenizer.rb → brackets/bracket_tokenizer.rb} +2 -4
- data/test/{brackets_test.rb → brackets/brackets_test.rb} +3 -4
- data/test/chittagong/chittagong_driver.rb +47 -0
- data/test/{chittagong_driver_test.rb → chittagong/chittagong_driver_test.rb} +66 -58
- data/test/{chittagong_evaluator.rb → chittagong/chittagong_evaluator.rb} +28 -13
- data/test/{chittagong_evaluator_test.rb → chittagong/chittagong_evaluator_test.rb} +6 -10
- data/test/chittagong/chittagong_grammar.rb +110 -0
- data/test/{chittagong_parser_test.rb → chittagong/chittagong_parser_test.rb} +5 -7
- data/test/{chittagong_test.rb → chittagong/chittagong_test.rb} +27 -36
- data/test/{chittagong_tokenizer.rb → chittagong/chittagong_tokenizer.rb} +17 -17
- data/test/{chittagong_tokenizer_test.rb → chittagong/chittagong_tokenizer_test.rb} +2 -3
- data/test/compiled_parser_test.rb +9 -42
- data/test/dhaka_test_helper.rb +17 -0
- data/test/evaluator_test.rb +18 -3
- data/test/grammar_test.rb +10 -15
- data/test/lalr_but_not_slr_grammar.rb +10 -8
- data/test/malformed_grammar.rb +2 -4
- data/test/malformed_grammar_test.rb +2 -3
- data/test/nullable_grammar.rb +11 -8
- data/test/parse_result_test.rb +44 -0
- data/test/parser_state_test.rb +36 -0
- data/test/parser_test.rb +53 -103
- data/test/precedence_grammar.rb +6 -6
- data/test/precedence_grammar_test.rb +2 -3
- data/test/rr_conflict_grammar.rb +5 -7
- data/test/simple_grammar.rb +6 -8
- data/test/sr_conflict_grammar.rb +6 -6
- metadata +30 -26
- data/test/arithmetic_grammar.rb +0 -35
- data/test/arithmetic_precedence_grammar.rb +0 -24
- data/test/arithmetic_precedence_parser_test.rb +0 -33
- data/test/bracket_grammar.rb +0 -25
- data/test/chittagong_grammar.rb +0 -104
- data/test/incomplete_arithmetic_evaluator.rb +0 -60
data/lib/parser/parser.rb
CHANGED
@@ -1,7 +1,3 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
require 'set'
|
3
|
-
require 'logger'
|
4
|
-
|
5
1
|
module Dhaka
|
6
2
|
# The parser generator. To generate a parser from a grammar specification +ArithmeticPrecedenceGrammar+, one would
|
7
3
|
# write:
|
@@ -12,31 +8,26 @@ module Dhaka
|
|
12
8
|
# which returns a string of Ruby code.
|
13
9
|
class Parser
|
14
10
|
include ParserMethods
|
15
|
-
attr_reader :grammar
|
11
|
+
attr_reader :grammar
|
16
12
|
|
17
13
|
# Creates a new parser from the given grammar. Messages are logged by default to STDOUT
|
18
14
|
# and the log level is WARN. Shift-reduce conflicts are reported at WARN and reduce-reduce conflicts
|
19
15
|
# at ERROR. You may pass in your own logger. Logging at DEBUG shows a lot of progress output.
|
20
16
|
def initialize(grammar, logger = nil)
|
21
|
-
|
22
|
-
@logger = logger
|
23
|
-
else
|
24
|
-
@logger = Logger.new(STDOUT)
|
25
|
-
@logger.level = Logger::WARN
|
26
|
-
end
|
17
|
+
@logger = logger || default_logger
|
27
18
|
@transitions = Hash.new {|hash, state| hash[state] = {}}
|
28
|
-
@grammar
|
29
|
-
@channels
|
19
|
+
@grammar = grammar
|
20
|
+
@channels = []
|
30
21
|
@states = Hash.new do |hash, kernel|
|
31
|
-
channels, closure =
|
32
|
-
@channels
|
33
|
-
new_state
|
22
|
+
channels, closure = grammar.closure(kernel)
|
23
|
+
@channels.concat channels.to_a
|
24
|
+
new_state = ParserState.new(self, closure)
|
34
25
|
hash[kernel] = new_state
|
35
|
-
@logger.debug("Created #{new_state}.")
|
26
|
+
@logger.debug("Created #{new_state.unique_name}.")
|
36
27
|
new_state.transition_items.each do |symbol, items|
|
37
28
|
destination_kernel = ItemSet.new(items.collect{|item| item.next_item})
|
38
|
-
destination_state
|
39
|
-
items.each { |item| @channels <<
|
29
|
+
destination_state = hash[destination_kernel]
|
30
|
+
items.each { |item| @channels << grammar.passive_channel(item, destination_state.items[item.next_item]) }
|
40
31
|
@transitions[new_state][symbol] = destination_state
|
41
32
|
end
|
42
33
|
new_state
|
@@ -47,7 +38,7 @@ module Dhaka
|
|
47
38
|
# Returns the Ruby source of the generated parser compiled as +parser_class_name+. This can be written out to a file.
|
48
39
|
def compile_to_ruby_source_as parser_class_name
|
49
40
|
result = "class #{parser_class_name} < Dhaka::CompiledParser\n\n"
|
50
|
-
result << " self.grammar = #{
|
41
|
+
result << " self.grammar = #{grammar.name}\n\n"
|
51
42
|
result << " start_with #{start_state.id}\n\n"
|
52
43
|
states.each do |state|
|
53
44
|
result << "#{state.compile_to_ruby_source}\n\n"
|
@@ -60,29 +51,40 @@ module Dhaka
|
|
60
51
|
# options hash, lookaheads are not written out to the parser states, which is helpful when there are dozens
|
61
52
|
# of lookahead symbols for every item in every state.
|
62
53
|
def to_dot(options = {})
|
63
|
-
result = ["digraph x {",
|
64
|
-
result
|
65
|
-
states.each
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
54
|
+
result = ["digraph x {", %(node [fontsize="10" shape=box size="5"])]
|
55
|
+
result.concat states.collect { |state| state.to_dot(options) }
|
56
|
+
states.each do |state|
|
57
|
+
@transitions[state].each do |symbol, dest_state|
|
58
|
+
result << %(#{state.unique_name} -> #{dest_state.unique_name} [label="#{symbol.name}"])
|
59
|
+
end
|
60
|
+
end
|
70
61
|
result << ['}']
|
71
62
|
result.join("\n")
|
72
63
|
end
|
64
|
+
|
65
|
+
def inspect
|
66
|
+
"<Dhaka::Parser grammar : #{grammar}>"
|
67
|
+
end
|
73
68
|
|
74
|
-
private :start_state
|
75
69
|
private
|
76
|
-
|
70
|
+
attr_reader :start_state
|
71
|
+
|
77
72
|
def states
|
78
73
|
@states.values
|
79
74
|
end
|
75
|
+
|
76
|
+
def default_logger
|
77
|
+
logger = Logger.new(STDOUT)
|
78
|
+
logger.level = Logger::WARN
|
79
|
+
logger.formatter = ParserLogOutputFormatter.new
|
80
|
+
logger
|
81
|
+
end
|
80
82
|
|
81
83
|
def initialize_states
|
82
|
-
start_productions =
|
83
|
-
raise NoStartProductionsError.new(
|
84
|
+
start_productions = grammar.productions_for_symbol(grammar.start_symbol)
|
85
|
+
raise NoStartProductionsError.new(grammar) if start_productions.empty?
|
84
86
|
start_items = ItemSet.new(start_productions.collect {|production| Item.new(production, 0)})
|
85
|
-
start_items.each {|start_item| start_item.lookaheadset <<
|
87
|
+
start_items.each {|start_item| start_item.lookaheadset << grammar.end_symbol}
|
86
88
|
@start_state = @states[start_items]
|
87
89
|
@logger.debug("Pumping #{@channels.size} channels...")
|
88
90
|
pump_channels
|
@@ -94,9 +96,9 @@ module Dhaka
|
|
94
96
|
|
95
97
|
def generate_shift_actions
|
96
98
|
@states.values.each do |state|
|
97
|
-
@transitions[state].keys.each
|
98
|
-
|
99
|
-
|
99
|
+
@transitions[state].keys.each do |symbol|
|
100
|
+
state.actions[symbol.name] = ShiftAction.new(@transitions[state][symbol])
|
101
|
+
end
|
100
102
|
end
|
101
103
|
end
|
102
104
|
|
@@ -113,9 +115,11 @@ module Dhaka
|
|
113
115
|
new_action = ReduceAction.new(item.production)
|
114
116
|
if existing_action = state.actions[lookahead.name]
|
115
117
|
if ReduceAction === existing_action
|
116
|
-
|
118
|
+
message = ReduceReduceConflict.new(state, lookahead, new_action).resolve
|
119
|
+
@logger.error(message)
|
117
120
|
else
|
118
|
-
|
121
|
+
message = ShiftReduceConflict.new(state, lookahead, new_action).resolve
|
122
|
+
@logger.warn(message)
|
119
123
|
end
|
120
124
|
else
|
121
125
|
state.actions[lookahead.name] = new_action
|
@@ -123,56 +127,16 @@ module Dhaka
|
|
123
127
|
end
|
124
128
|
end
|
125
129
|
|
126
|
-
|
127
|
-
def resolve_conflict state, lookahead, new_action
|
128
|
-
message = build_conflict_message(state, lookahead, new_action)
|
129
|
-
shift_precedence = lookahead.precedence
|
130
|
-
reduce_precedence = new_action.production.precedence
|
131
|
-
if (shift_precedence && reduce_precedence)
|
132
|
-
if (shift_precedence > reduce_precedence)
|
133
|
-
message << "Resolving with precedence. Choosing shift over reduce."
|
134
|
-
elsif (shift_precedence < reduce_precedence)
|
135
|
-
message << "Resolving with precedence. Choosing reduce over shift."
|
136
|
-
state.actions[lookahead.name] = new_action
|
137
|
-
else
|
138
|
-
case shift_precedence.associativity
|
139
|
-
when :left
|
140
|
-
message << "Resolving with left associativity. Choosing reduce over shift."
|
141
|
-
state.actions[lookahead.name] = new_action
|
142
|
-
when :right
|
143
|
-
message << "Resolving with right associativity. Choosing shift over reduce."
|
144
|
-
when :nonassoc
|
145
|
-
message << "Resolving with non-associativity. Eliminating action."
|
146
|
-
state.actions.delete(lookahead.name)
|
147
|
-
end
|
148
|
-
end
|
149
|
-
else
|
150
|
-
message << "No precedence rule. Choosing shift over reduce."
|
151
|
-
end
|
152
|
-
@logger.warn(message.join("\n"))
|
153
|
-
end
|
154
|
-
|
155
|
-
def build_conflict_message state, lookahead, new_action
|
156
|
-
message = ["Parser Conflict at State:"] + state.items.values.collect{|it| it.to_s(:hide_lookaheads => true)}
|
157
|
-
message << "Existing: #{state.actions[lookahead.name]}"
|
158
|
-
message << "New: #{new_action}"
|
159
|
-
message << "Lookahead: #{lookahead}"
|
160
|
-
message
|
161
|
-
end
|
162
|
-
|
163
130
|
def pump_channels
|
164
|
-
|
131
|
+
loop do
|
165
132
|
unstable_count = 0
|
166
133
|
@channels.each do |channel|
|
167
|
-
if channel.pump
|
168
|
-
unstable_count += 1
|
169
|
-
end
|
134
|
+
unstable_count += 1 if channel.pump
|
170
135
|
end
|
171
|
-
break if unstable_count
|
136
|
+
break if unstable_count.zero?
|
172
137
|
@logger.debug("#{unstable_count} unstable channels...")
|
173
138
|
end
|
174
139
|
end
|
175
|
-
|
176
140
|
end
|
177
141
|
|
178
142
|
# Raised when trying to create a Parser for a grammar that has no productions for the start symbol
|
@@ -184,5 +148,12 @@ module Dhaka
|
|
184
148
|
"No start productions defined for #{@grammar.name}"
|
185
149
|
end
|
186
150
|
end
|
151
|
+
|
152
|
+
class ParserLogOutputFormatter < Logger::Formatter #:nodoc:
|
153
|
+
def call(severity, time, progname, msg)
|
154
|
+
"\n%s -- %s: %s\n" % [ severity, progname, msg2str(msg)]
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
187
158
|
end
|
188
159
|
|
data/lib/parser/parser_run.rb
CHANGED
@@ -2,9 +2,9 @@ module Dhaka
|
|
2
2
|
class ParserRun #:nodoc:
|
3
3
|
|
4
4
|
def initialize(grammar, start_state, token_stream)
|
5
|
-
@grammar
|
6
|
-
@node_stack
|
7
|
-
@state_stack
|
5
|
+
@grammar = grammar
|
6
|
+
@node_stack = []
|
7
|
+
@state_stack = [start_state]
|
8
8
|
@token_stream = token_stream
|
9
9
|
@symbol_queue = []
|
10
10
|
end
|
@@ -17,7 +17,7 @@ module Dhaka
|
|
17
17
|
return error if error
|
18
18
|
node_stack << ParseTreeLeafNode.new(@current_token)
|
19
19
|
end
|
20
|
-
ParseSuccessResult.new(node_stack
|
20
|
+
ParseSuccessResult.new(node_stack.first)
|
21
21
|
end
|
22
22
|
|
23
23
|
private
|
@@ -26,9 +26,9 @@ module Dhaka
|
|
26
26
|
|
27
27
|
def execute_actions
|
28
28
|
while symbol_name = @symbol_queue.pop
|
29
|
-
action = state_stack
|
29
|
+
action = state_stack.last.actions[symbol_name]
|
30
30
|
return ParseErrorResult.new(@current_token) unless action
|
31
|
-
|
31
|
+
instance_eval(&action.action_code)
|
32
32
|
end
|
33
33
|
nil
|
34
34
|
end
|
data/lib/parser/parser_state.rb
CHANGED
@@ -1,43 +1,40 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
require 'set'
|
3
1
|
module Dhaka
|
4
2
|
class ParserState #:nodoc:
|
5
|
-
|
6
3
|
attr_accessor :items, :actions, :id
|
7
4
|
|
8
5
|
@@state_id = 0
|
9
6
|
|
10
7
|
def self.next_state_id
|
11
|
-
result
|
8
|
+
result = @@state_id
|
12
9
|
@@state_id += 1
|
13
10
|
result
|
14
11
|
end
|
15
12
|
|
16
13
|
def initialize(parser, items, id=nil)
|
17
|
-
@parser
|
18
|
-
@items
|
14
|
+
@parser = parser
|
15
|
+
@items = items
|
19
16
|
@actions = {}
|
20
|
-
@id
|
17
|
+
@id = id || ParserState.next_state_id
|
21
18
|
end
|
22
19
|
|
23
20
|
def transition_items
|
24
21
|
result = Hash.new {|h, k| h[k] = ItemSet.new()}
|
25
|
-
|
26
|
-
|
22
|
+
items.values.each do |item|
|
23
|
+
result[item.next_symbol] << item if item.next_symbol
|
27
24
|
end
|
28
25
|
result
|
29
26
|
end
|
30
27
|
|
31
|
-
def
|
32
|
-
|
28
|
+
def unique_name
|
29
|
+
"State#{id}"
|
33
30
|
end
|
34
31
|
|
35
32
|
def to_dot(options = {})
|
36
|
-
label
|
37
|
-
"#{dot_name} [label=\"#{label}\"]"
|
33
|
+
%(#{unique_name} [label="#{items.values.collect{|item| item.to_s(options)}.join('\n')}"])
|
38
34
|
end
|
35
|
+
|
39
36
|
def compile_to_ruby_source
|
40
|
-
result = " at_state(#{
|
37
|
+
result = " at_state(#{id}) {\n"
|
41
38
|
actions.each do |symbol_name, action|
|
42
39
|
result << " for_symbol('#{symbol_name}') { #{action.compile_to_ruby_source} }\n"
|
43
40
|
end
|
@@ -49,18 +46,19 @@ module Dhaka
|
|
49
46
|
actions[symbol_name] = @parser.instance_eval(&blk)
|
50
47
|
end
|
51
48
|
|
52
|
-
def to_s
|
53
|
-
|
49
|
+
def to_s(options = {})
|
50
|
+
items.values.collect{|item| item.to_s(options)}.join("\n")
|
54
51
|
end
|
55
52
|
|
56
53
|
end
|
57
54
|
|
58
55
|
class ItemSet < Set #:nodoc:
|
59
56
|
def hash
|
60
|
-
|
57
|
+
collect{|item| item.hash}.inject{|result, hashcode| result ^ hashcode}
|
61
58
|
end
|
59
|
+
|
62
60
|
def eql? other
|
63
61
|
self == other
|
64
62
|
end
|
65
63
|
end
|
66
|
-
end
|
64
|
+
end
|
data/lib/parser/token.rb
CHANGED
@@ -6,15 +6,17 @@ module Dhaka
|
|
6
6
|
class Token
|
7
7
|
attr_accessor :symbol_name, :value, :input_position
|
8
8
|
def initialize(symbol_name, value, input_position)
|
9
|
-
@symbol_name
|
10
|
-
@value
|
9
|
+
@symbol_name = symbol_name
|
10
|
+
@value = value
|
11
11
|
@input_position = input_position
|
12
12
|
end
|
13
|
+
|
13
14
|
def to_s #:nodoc:
|
14
|
-
"#{symbol_name}"
|
15
|
+
value ? "#{symbol_name} : #{value}" : "#{symbol_name}"
|
15
16
|
end
|
17
|
+
|
16
18
|
def == other
|
17
|
-
|
19
|
+
symbol_name == other.symbol_name && value == other.value
|
18
20
|
end
|
19
21
|
end
|
20
22
|
end
|
data/lib/tokenizer/tokenizer.rb
CHANGED
@@ -1,5 +1,4 @@
|
|
1
1
|
module Dhaka
|
2
|
-
|
3
2
|
# Reserved constant used to identify the idle state of the tokenizer.
|
4
3
|
TOKENIZER_IDLE_STATE = :idle_state
|
5
4
|
|
@@ -7,17 +6,18 @@ module Dhaka
|
|
7
6
|
# be passed in directly to the parser.
|
8
7
|
class TokenizerSuccessResult
|
9
8
|
include Enumerable
|
9
|
+
|
10
10
|
def initialize(tokens)
|
11
11
|
@tokens = tokens
|
12
12
|
end
|
13
|
+
|
13
14
|
# Returns false.
|
14
15
|
def has_error?
|
15
16
|
false
|
16
17
|
end
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
end
|
18
|
+
|
19
|
+
def each(&block)
|
20
|
+
@tokens.each(&block)
|
21
21
|
end
|
22
22
|
end
|
23
23
|
|
@@ -25,9 +25,11 @@ module Dhaka
|
|
25
25
|
class TokenizerErrorResult
|
26
26
|
# The index of the character that caused the error.
|
27
27
|
attr_reader :unexpected_char_index
|
28
|
+
|
28
29
|
def initialize(unexpected_char_index)
|
29
30
|
@unexpected_char_index = unexpected_char_index
|
30
31
|
end
|
32
|
+
|
31
33
|
# Returns true.
|
32
34
|
def has_error?
|
33
35
|
true
|
@@ -101,34 +103,41 @@ module Dhaka
|
|
101
103
|
# switch_to Dhaka::TOKENIZER_IDLE_STATE
|
102
104
|
# end
|
103
105
|
# for_characters digits do
|
104
|
-
# curr_token.value
|
106
|
+
# curr_token.value << curr_char
|
105
107
|
# advance
|
106
108
|
# end
|
107
109
|
# end
|
108
110
|
#
|
109
111
|
# end
|
110
|
-
|
111
|
-
|
112
112
|
class Tokenizer
|
113
|
+
class << self
|
114
|
+
# Define the action for the state named +state_name+.
|
115
|
+
def for_state(state_name, &blk)
|
116
|
+
states[state_name].instance_eval(&blk)
|
117
|
+
end
|
113
118
|
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
119
|
+
# Tokenizes a string +input+ and returns a TokenizerErrorResult on failure or a TokenizerSuccessResult on sucess.
|
120
|
+
def tokenize(input)
|
121
|
+
new(input).run
|
122
|
+
end
|
123
|
+
|
124
|
+
private
|
125
|
+
def inherited(tokenizer)
|
126
|
+
class << tokenizer
|
127
|
+
attr_accessor :states, :grammar
|
128
|
+
end
|
129
|
+
tokenizer.states = Hash.new {|hash, key| hash[key] = TokenizerState.new}
|
130
|
+
end
|
122
131
|
end
|
123
132
|
|
124
133
|
# The tokens shifted so far.
|
125
134
|
attr_reader :tokens
|
126
135
|
|
127
136
|
def initialize(input) #:nodoc:
|
128
|
-
@input
|
129
|
-
@current_state
|
137
|
+
@input = input
|
138
|
+
@current_state = self.class.states[TOKENIZER_IDLE_STATE]
|
130
139
|
@curr_char_index = 0
|
131
|
-
@tokens
|
140
|
+
@tokens = []
|
132
141
|
end
|
133
142
|
|
134
143
|
# The character currently being processed.
|
@@ -140,10 +149,14 @@ module Dhaka
|
|
140
149
|
def advance
|
141
150
|
@curr_char_index += 1
|
142
151
|
end
|
152
|
+
|
153
|
+
def inspect
|
154
|
+
"<Dhaka::Tokenizer grammar : #{grammar}>"
|
155
|
+
end
|
143
156
|
|
144
157
|
# The token currently on top of the stack.
|
145
158
|
def curr_token
|
146
|
-
tokens
|
159
|
+
tokens.last
|
147
160
|
end
|
148
161
|
|
149
162
|
# Push a new token on to the stack with symbol corresponding to +symbol_name+ and a value of +value+.
|
@@ -164,17 +177,7 @@ module Dhaka
|
|
164
177
|
instance_eval(&blk)
|
165
178
|
end
|
166
179
|
tokens << Dhaka::Token.new(Dhaka::END_SYMBOL_NAME, nil, nil)
|
167
|
-
|
180
|
+
TokenizerSuccessResult.new(tokens)
|
168
181
|
end
|
169
|
-
|
170
|
-
private
|
171
|
-
def self.inherited(tokenizer)
|
172
|
-
class << tokenizer
|
173
|
-
attr_accessor :states, :grammar
|
174
|
-
end
|
175
|
-
tokenizer.states = Hash.new {|hash, key| hash[key] = TokenizerState.new}
|
176
|
-
end
|
177
|
-
|
178
182
|
end
|
179
|
-
|
180
183
|
end
|