dhaka 0.0.3 → 0.0.4
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/dhaka.rb +40 -39
- data/lib/grammar/grammar.rb +25 -2
- data/lib/grammar/grammar_symbol.rb +1 -1
- data/lib/grammar/precedence.rb +14 -0
- data/lib/grammar/production.rb +16 -2
- data/lib/parser/parser.rb +66 -12
- data/test/all_tests.rb +3 -1
- data/test/arithmetic_precedence_evaluator.rb +40 -0
- data/test/arithmetic_precedence_grammar.rb +22 -0
- data/test/arithmetic_precedence_grammar_test.rb +28 -0
- data/test/arithmetic_precedence_parser_test.rb +30 -0
- data/test/parser_test.rb +1 -6
- metadata +7 -2
data/lib/dhaka.rb
CHANGED
@@ -1,44 +1,45 @@
|
|
1
|
-
#--
|
2
|
-
# Copyright (c) 2006 Mushfeq Khan
|
3
|
-
#
|
4
|
-
# Permission is hereby granted, free of charge, to any person obtaining
|
5
|
-
# a copy of this software and associated documentation files (the
|
6
|
-
# "Software"), to deal in the Software without restriction, including
|
7
|
-
# without limitation the rights to use, copy, modify, merge, publish,
|
8
|
-
# distribute, sublicense, and/or sell copies of the Software, and to
|
9
|
-
# permit persons to whom the Software is furnished to do so, subject to
|
10
|
-
# the following conditions:
|
11
|
-
#
|
12
|
-
# The above copyright notice and this permission notice shall be
|
13
|
-
# included in all copies or substantial portions of the Software.
|
14
|
-
#
|
15
|
-
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
16
|
-
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
17
|
-
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
18
|
-
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
19
|
-
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
20
|
-
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
21
|
-
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
22
|
-
#++
|
1
|
+
#--
|
2
|
+
# Copyright (c) 2006 Mushfeq Khan
|
3
|
+
#
|
4
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
5
|
+
# a copy of this software and associated documentation files (the
|
6
|
+
# "Software"), to deal in the Software without restriction, including
|
7
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
8
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
9
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
10
|
+
# the following conditions:
|
11
|
+
#
|
12
|
+
# The above copyright notice and this permission notice shall be
|
13
|
+
# included in all copies or substantial portions of the Software.
|
14
|
+
#
|
15
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
16
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
17
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
18
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
19
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
20
|
+
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
21
|
+
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
22
|
+
#++
|
23
23
|
|
24
|
-
require 'grammar/grammar_symbol'
|
25
|
-
require 'grammar/production'
|
26
|
-
require 'grammar/closure_hash'
|
27
|
-
require 'grammar/grammar'
|
24
|
+
require File.dirname(__FILE__)+'/grammar/grammar_symbol'
|
25
|
+
require File.dirname(__FILE__)+'/grammar/production'
|
26
|
+
require File.dirname(__FILE__)+'/grammar/closure_hash'
|
27
|
+
require File.dirname(__FILE__)+'/grammar/grammar'
|
28
|
+
require File.dirname(__FILE__)+'/grammar/precedence'
|
28
29
|
|
29
|
-
require 'parser/parse_result'
|
30
|
-
require 'parser/item'
|
31
|
-
require 'parser/channel'
|
32
|
-
require 'parser/parser_methods'
|
33
|
-
require 'parser/parse_tree'
|
34
|
-
require 'parser/parser_state'
|
35
|
-
require 'parser/token'
|
36
|
-
require 'parser/action'
|
37
|
-
require 'parser/parser_run'
|
38
|
-
require 'parser/parser'
|
39
|
-
require 'parser/compiled_parser'
|
30
|
+
require File.dirname(__FILE__)+'/parser/parse_result'
|
31
|
+
require File.dirname(__FILE__)+'/parser/item'
|
32
|
+
require File.dirname(__FILE__)+'/parser/channel'
|
33
|
+
require File.dirname(__FILE__)+'/parser/parser_methods'
|
34
|
+
require File.dirname(__FILE__)+'/parser/parse_tree'
|
35
|
+
require File.dirname(__FILE__)+'/parser/parser_state'
|
36
|
+
require File.dirname(__FILE__)+'/parser/token'
|
37
|
+
require File.dirname(__FILE__)+'/parser/action'
|
38
|
+
require File.dirname(__FILE__)+'/parser/parser_run'
|
39
|
+
require File.dirname(__FILE__)+'/parser/parser'
|
40
|
+
require File.dirname(__FILE__)+'/parser/compiled_parser'
|
40
41
|
|
41
|
-
require 'tokenizer/tokenizer'
|
42
|
-
require 'evaluator/evaluator'
|
42
|
+
require File.dirname(__FILE__)+'/tokenizer/tokenizer'
|
43
|
+
require File.dirname(__FILE__)+'/evaluator/evaluator'
|
43
44
|
|
44
45
|
|
data/lib/grammar/grammar.rb
CHANGED
@@ -11,15 +11,34 @@ module Dhaka
|
|
11
11
|
@symbol = symbol
|
12
12
|
end
|
13
13
|
|
14
|
-
def method_missing(production_name, expansion)
|
14
|
+
def method_missing(production_name, expansion, options = {})
|
15
15
|
expansion_symbols = expansion.collect {|name| @grammar.symbols[name]}
|
16
|
-
|
16
|
+
if precedence_symbol_name = options[:prec]
|
17
|
+
production = Production.new(@symbol, expansion_symbols, production_name.to_s, @grammar.symbol_for_name(precedence_symbol_name).precedence)
|
18
|
+
else
|
19
|
+
production = Production.new(@symbol, expansion_symbols, production_name.to_s)
|
20
|
+
end
|
17
21
|
@symbol.nullable = true if expansion_symbols.empty?
|
18
22
|
@grammar.productions_by_symbol[production.symbol] << production
|
23
|
+
raise "Duplicate production named #{production.name}" if @grammar.productions_by_name[production.name]
|
19
24
|
@grammar.productions_by_name[production.name] = production
|
20
25
|
end
|
21
26
|
end
|
22
27
|
|
28
|
+
class PrecedenceBuilder
|
29
|
+
def initialize(grammar)
|
30
|
+
@grammar = grammar
|
31
|
+
@precedence_level = 0
|
32
|
+
end
|
33
|
+
def method_missing(associativity, symbol_names)
|
34
|
+
symbol_names.each do |symbol_name|
|
35
|
+
symbol = @grammar.symbols[symbol_name]
|
36
|
+
symbol.precedence = Precedence.new(@precedence_level, associativity)
|
37
|
+
end
|
38
|
+
@precedence_level += 1
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
23
42
|
class Grammar
|
24
43
|
|
25
44
|
def self.inherited(grammar)
|
@@ -83,6 +102,10 @@ module Dhaka
|
|
83
102
|
return channels, result
|
84
103
|
end
|
85
104
|
|
105
|
+
def self.precedences &blk
|
106
|
+
PrecedenceBuilder.new(self).instance_eval(&blk)
|
107
|
+
end
|
108
|
+
|
86
109
|
def self.first(given_symbol)
|
87
110
|
cached_result = self.__first_cache[given_symbol]
|
88
111
|
return cached_result if cached_result
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module Dhaka
|
2
|
+
class Precedence
|
3
|
+
include Comparable
|
4
|
+
attr_reader :precedence_level, :associativity
|
5
|
+
def initialize(precedence_level, associativity)
|
6
|
+
@precedence_level = precedence_level
|
7
|
+
@associativity = associativity
|
8
|
+
end
|
9
|
+
|
10
|
+
def <=> other
|
11
|
+
self.precedence_level <=> other.precedence_level
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
data/lib/grammar/production.rb
CHANGED
@@ -1,14 +1,28 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
module Dhaka
|
3
3
|
class Production
|
4
|
-
|
5
|
-
|
4
|
+
|
5
|
+
attr_reader :symbol, :expansion, :name, :precedence
|
6
|
+
|
7
|
+
def initialize(symbol, expansion, name, precedence = nil)
|
6
8
|
@symbol = symbol
|
7
9
|
@expansion = expansion
|
8
10
|
@name = name
|
11
|
+
if precedence
|
12
|
+
@precedence = precedence
|
13
|
+
else
|
14
|
+
@expansion.reverse_each do |symbol|
|
15
|
+
if symbol.terminal
|
16
|
+
@precedence = symbol.precedence
|
17
|
+
break
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
9
21
|
end
|
22
|
+
|
10
23
|
def to_s
|
11
24
|
"#{@name} #{@symbol} ::= #{@expansion.join(' ')}"
|
12
25
|
end
|
26
|
+
|
13
27
|
end
|
14
28
|
end
|
data/lib/parser/parser.rb
CHANGED
@@ -1,11 +1,19 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
require 'set'
|
3
|
+
require 'logger'
|
4
|
+
|
3
5
|
module Dhaka
|
4
6
|
class Parser
|
5
7
|
include ParserMethods
|
6
8
|
attr_reader :grammar, :start_state
|
7
9
|
|
8
|
-
def initialize(grammar)
|
10
|
+
def initialize(grammar, logger = nil)
|
11
|
+
if logger
|
12
|
+
@logger = logger
|
13
|
+
else
|
14
|
+
@logger = Logger.new(STDOUT)
|
15
|
+
@logger.level = Logger::WARN
|
16
|
+
end
|
9
17
|
@transitions = Hash.new {|hash, state| hash[state] = {}}
|
10
18
|
@grammar = grammar
|
11
19
|
@channels = []
|
@@ -14,6 +22,7 @@ module Dhaka
|
|
14
22
|
@channels += channels.to_a
|
15
23
|
new_state = ParserState.new(self, closure)
|
16
24
|
hash[kernel] = new_state
|
25
|
+
@logger.debug("Created #{new_state}.")
|
17
26
|
new_state.transition_items.each do |symbol, items|
|
18
27
|
destination_kernel = ItemSet.new(items.collect{|item| item.next_item})
|
19
28
|
destination_state = hash[destination_kernel]
|
@@ -31,8 +40,11 @@ module Dhaka
|
|
31
40
|
start_items = ItemSet.new(start_productions.collect {|production| Item.new(production, 0)})
|
32
41
|
start_items.each {|start_item| start_item.lookaheadset << @grammar.end_symbol}
|
33
42
|
@start_state = @states[start_items]
|
43
|
+
@logger.debug("Pumping #{@channels.size} channels...")
|
34
44
|
pump_channels
|
45
|
+
@logger.debug("Generating shift actions...")
|
35
46
|
generate_shift_actions
|
47
|
+
@logger.debug("Generating reduce actions...")
|
36
48
|
generate_reduce_actions
|
37
49
|
end
|
38
50
|
|
@@ -80,10 +92,13 @@ module Dhaka
|
|
80
92
|
|
81
93
|
def create_reduction_actions_for_item_and_state item, state
|
82
94
|
item.lookaheadset.each do |lookahead|
|
83
|
-
existing_action = state.actions[lookahead.name]
|
84
95
|
new_action = ReduceAction.new(item.production)
|
85
|
-
if existing_action
|
86
|
-
|
96
|
+
if existing_action = state.actions[lookahead.name]
|
97
|
+
if ReduceAction === existing_action
|
98
|
+
raise ParserReduceReduceConflictError.new(build_conflict_message(state, lookahead, new_action).join("\n"))
|
99
|
+
else
|
100
|
+
resolve_conflict state, lookahead, new_action
|
101
|
+
end
|
87
102
|
else
|
88
103
|
state.actions[lookahead.name] = new_action
|
89
104
|
end
|
@@ -91,25 +106,64 @@ module Dhaka
|
|
91
106
|
end
|
92
107
|
|
93
108
|
|
109
|
+
def resolve_conflict state, lookahead, new_action
|
110
|
+
message = build_conflict_message(state, lookahead, new_action)
|
111
|
+
shift_precedence = lookahead.precedence
|
112
|
+
reduce_precedence = new_action.production.precedence
|
113
|
+
if (shift_precedence && reduce_precedence)
|
114
|
+
if (shift_precedence > reduce_precedence)
|
115
|
+
message << "Resolving with precedence. Choosing shift over reduce."
|
116
|
+
elsif (shift_precedence < reduce_precedence)
|
117
|
+
message << "Resolving with precedence. Choosing reduce over shift."
|
118
|
+
state.actions[lookahead.name] = new_action
|
119
|
+
else
|
120
|
+
case shift_precedence.associativity
|
121
|
+
when :left
|
122
|
+
message << "Resolving with left associativity. Choosing reduce over shift."
|
123
|
+
state.actions[lookahead.name] = new_action
|
124
|
+
when :right
|
125
|
+
message << "Resolving with right associativity. Choosing shift over reduce."
|
126
|
+
when :nonassoc
|
127
|
+
message << "Resolving with non-associativity. Eliminating action."
|
128
|
+
state.actions[lookahead.name] = nil
|
129
|
+
end
|
130
|
+
end
|
131
|
+
else
|
132
|
+
message << "No precedence rule. Choosing shift over reduce."
|
133
|
+
end
|
134
|
+
@logger.warn(message.join("\n"))
|
135
|
+
end
|
136
|
+
|
137
|
+
def build_conflict_message state, lookahead, new_action
|
138
|
+
message = ["Parser Conflict at State:"] + state.items.values.collect{|it| it.to_s}
|
139
|
+
message << "Existing: #{state.actions[lookahead.name]}"
|
140
|
+
message << "New: #{new_action}"
|
141
|
+
message << "Lookahead: #{lookahead}"
|
142
|
+
message
|
143
|
+
end
|
144
|
+
|
94
145
|
def pump_channels
|
95
146
|
while true
|
96
|
-
|
97
|
-
|
147
|
+
unstable_count = 0
|
148
|
+
@channels.each do |channel|
|
149
|
+
if channel.pump
|
150
|
+
unstable_count += 1
|
151
|
+
end
|
98
152
|
end
|
153
|
+
break if unstable_count == 0
|
154
|
+
@logger.debug("#{unstable_count} unstable channels...")
|
99
155
|
end
|
100
156
|
end
|
101
157
|
|
102
158
|
end
|
103
159
|
|
104
160
|
|
105
|
-
class
|
106
|
-
def initialize(
|
107
|
-
@
|
108
|
-
@existing_action = existing_action
|
109
|
-
@new_action = new_action
|
161
|
+
class ParserReduceReduceConflictError < StandardError
|
162
|
+
def initialize(message)
|
163
|
+
@message = message
|
110
164
|
end
|
111
165
|
def to_s
|
112
|
-
|
166
|
+
@message
|
113
167
|
end
|
114
168
|
end
|
115
169
|
|
data/test/all_tests.rb
CHANGED
@@ -8,4 +8,6 @@ require 'compiled_parser_test'
|
|
8
8
|
require 'evaluator_test'
|
9
9
|
require 'arithmetic_tokenizer_test'
|
10
10
|
require 'malformed_grammar_test'
|
11
|
-
require 'brackets_test'
|
11
|
+
require 'brackets_test'
|
12
|
+
require 'arithmetic_precedence_grammar_test'
|
13
|
+
require 'arithmetic_precedence_parser_test'
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require File.dirname(__FILE__)+'/../lib/dhaka'
|
2
|
+
require 'arithmetic_precedence_grammar'
|
3
|
+
|
4
|
+
class ArithmeticPrecedenceEvaluator < Dhaka::Evaluator
|
5
|
+
|
6
|
+
self.grammar = ArithmeticPrecedenceGrammar
|
7
|
+
|
8
|
+
define_evaluation_rules do
|
9
|
+
|
10
|
+
for_subtraction do
|
11
|
+
child_nodes[0] - child_nodes[2]
|
12
|
+
end
|
13
|
+
|
14
|
+
for_addition do
|
15
|
+
child_nodes[0] + child_nodes[2]
|
16
|
+
end
|
17
|
+
|
18
|
+
for_division do
|
19
|
+
child_nodes[0].to_f/child_nodes[2]
|
20
|
+
end
|
21
|
+
|
22
|
+
for_multiplication do
|
23
|
+
child_nodes[0] * child_nodes[2]
|
24
|
+
end
|
25
|
+
|
26
|
+
for_literal do
|
27
|
+
child_nodes[0].token.value
|
28
|
+
end
|
29
|
+
|
30
|
+
for_parenthetized_expression do
|
31
|
+
child_nodes[1]
|
32
|
+
end
|
33
|
+
|
34
|
+
for_negated_expression do
|
35
|
+
-child_nodes[1]
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require File.dirname(__FILE__)+'/../lib/dhaka'
|
2
|
+
|
3
|
+
class ArithmeticPrecedenceGrammar < Dhaka::Grammar
|
4
|
+
precedences do
|
5
|
+
left ['+', '-']
|
6
|
+
left ['*', '/']
|
7
|
+
end
|
8
|
+
|
9
|
+
for_symbol(Dhaka::START_SYMBOL_NAME) do
|
10
|
+
expression ['E']
|
11
|
+
end
|
12
|
+
|
13
|
+
for_symbol('E') do
|
14
|
+
addition ['E', '+', 'E']
|
15
|
+
subtraction ['E', '-', 'E']
|
16
|
+
multiplication ['E', '*', 'E']
|
17
|
+
division ['E', '/', 'E']
|
18
|
+
literal ['n']
|
19
|
+
parenthetized_expression ['(', 'E', ')']
|
20
|
+
negated_expression ['-', 'E'], :prec => '*'
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require "test/unit"
|
2
|
+
require 'arithmetic_precedence_grammar'
|
3
|
+
|
4
|
+
class TestArithmeticPrecedenceGrammar < Test::Unit::TestCase
|
5
|
+
|
6
|
+
def setup
|
7
|
+
@addop = ArithmeticPrecedenceGrammar.symbol_for_name('+')
|
8
|
+
@subop = ArithmeticPrecedenceGrammar.symbol_for_name('-')
|
9
|
+
@mulop = ArithmeticPrecedenceGrammar.symbol_for_name('*')
|
10
|
+
@divop = ArithmeticPrecedenceGrammar.symbol_for_name('/')
|
11
|
+
end
|
12
|
+
|
13
|
+
def test_precedence_levels_and_associativity_of_terminals
|
14
|
+
assert_equal(0, @addop.precedence.precedence_level)
|
15
|
+
assert_equal(0, @subop.precedence.precedence_level)
|
16
|
+
assert_equal(1, @mulop.precedence.precedence_level)
|
17
|
+
assert_equal(1, @divop.precedence.precedence_level)
|
18
|
+
assert_equal(:left, @addop.precedence.associativity)
|
19
|
+
assert_equal(:left, @subop.precedence.associativity)
|
20
|
+
assert_equal(:left, @mulop.precedence.associativity)
|
21
|
+
assert_equal(:left, @divop.precedence.associativity)
|
22
|
+
end
|
23
|
+
def test_precedence_of_production
|
24
|
+
assert_equal(@addop.precedence, ArithmeticPrecedenceGrammar.production_named("addition").precedence)
|
25
|
+
assert_equal(@mulop.precedence, ArithmeticPrecedenceGrammar.production_named("multiplication").precedence)
|
26
|
+
assert_equal(@mulop.precedence, ArithmeticPrecedenceGrammar.production_named("negated_expression").precedence)
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require "test/unit"
|
2
|
+
require "arithmetic_precedence_grammar"
|
3
|
+
require "arithmetic_tokenizer"
|
4
|
+
require "arithmetic_precedence_evaluator"
|
5
|
+
|
6
|
+
class TestArithmeticPrecedenceParser < Test::Unit::TestCase
|
7
|
+
|
8
|
+
def test_parses_arithmetic_expressions
|
9
|
+
fake_logger = FakeLogger.new
|
10
|
+
parser = Dhaka::Parser.new(ArithmeticPrecedenceGrammar, fake_logger)
|
11
|
+
assert_equal(20, fake_logger.messages.size)
|
12
|
+
|
13
|
+
syntax_tree = parser.parse(ArithmeticTokenizer.tokenize("5 * -14/(2*7 - 7) + 2")).syntax_tree
|
14
|
+
# File.open('precedence.dot', 'w') {|file| file << syntax_tree.to_dot}
|
15
|
+
# File.open('precedence_parser.dot', 'w') {|file| file << parser.to_dot}
|
16
|
+
assert_equal(-8, ArithmeticPrecedenceEvaluator.new(syntax_tree).result)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
class FakeLogger
|
21
|
+
attr_reader :messages
|
22
|
+
def initialize
|
23
|
+
@messages = []
|
24
|
+
end
|
25
|
+
def debug message
|
26
|
+
end
|
27
|
+
def warn(message)
|
28
|
+
@messages << message
|
29
|
+
end
|
30
|
+
end
|
data/test/parser_test.rb
CHANGED
@@ -144,12 +144,7 @@ class ParserTest < Test::Unit::TestCase
|
|
144
144
|
|
145
145
|
def test_with_a_grammar_that_should_generate_an_RR_conflict
|
146
146
|
grammar = RRConflictGrammar
|
147
|
-
assert_raise(Dhaka::
|
148
|
-
end
|
149
|
-
|
150
|
-
def test_with_a_grammar_that_should_generate_an_SR_conflict
|
151
|
-
grammar = SRConflictGrammar
|
152
|
-
assert_raise(Dhaka::ParserConflictError) { Dhaka::Parser.new(grammar) }
|
147
|
+
assert_raise(Dhaka::ParserReduceReduceConflictError) { Dhaka::Parser.new(grammar) }
|
153
148
|
end
|
154
149
|
|
155
150
|
def set_finder(set1, set2)
|
metadata
CHANGED
@@ -3,8 +3,8 @@ rubygems_version: 0.9.0
|
|
3
3
|
specification_version: 1
|
4
4
|
name: dhaka
|
5
5
|
version: !ruby/object:Gem::Version
|
6
|
-
version: 0.0.
|
7
|
-
date: 2006-12-
|
6
|
+
version: 0.0.4
|
7
|
+
date: 2006-12-11 00:00:00 -05:00
|
8
8
|
summary: An LALR1 parser generator written in Ruby
|
9
9
|
require_paths:
|
10
10
|
- lib
|
@@ -34,6 +34,7 @@ files:
|
|
34
34
|
- lib/grammar/closure_hash.rb
|
35
35
|
- lib/grammar/grammar.rb
|
36
36
|
- lib/grammar/grammar_symbol.rb
|
37
|
+
- lib/grammar/precedence.rb
|
37
38
|
- lib/grammar/production.rb
|
38
39
|
- lib/parser/action.rb
|
39
40
|
- lib/parser/channel.rb
|
@@ -52,6 +53,10 @@ files:
|
|
52
53
|
- test/arithmetic_evaluator_test.rb
|
53
54
|
- test/arithmetic_grammar.rb
|
54
55
|
- test/arithmetic_grammar_test.rb
|
56
|
+
- test/arithmetic_precedence_evaluator.rb
|
57
|
+
- test/arithmetic_precedence_grammar.rb
|
58
|
+
- test/arithmetic_precedence_grammar_test.rb
|
59
|
+
- test/arithmetic_precedence_parser_test.rb
|
55
60
|
- test/arithmetic_test_methods.rb
|
56
61
|
- test/arithmetic_tokenizer.rb
|
57
62
|
- test/arithmetic_tokenizer_test.rb
|