foreverman-dhaka 2.2.1
Sign up to get free protection for your applications and to get access to all the features.
- data/Rakefile +64 -0
- data/lib/dhaka.rb +62 -0
- data/lib/dhaka/dot/dot.rb +29 -0
- data/lib/dhaka/evaluator/evaluator.rb +133 -0
- data/lib/dhaka/grammar/closure_hash.rb +15 -0
- data/lib/dhaka/grammar/grammar.rb +236 -0
- data/lib/dhaka/grammar/grammar_symbol.rb +27 -0
- data/lib/dhaka/grammar/precedence.rb +19 -0
- data/lib/dhaka/grammar/production.rb +36 -0
- data/lib/dhaka/lexer/accept_actions.rb +36 -0
- data/lib/dhaka/lexer/alphabet.rb +21 -0
- data/lib/dhaka/lexer/compiled_lexer.rb +46 -0
- data/lib/dhaka/lexer/dfa.rb +121 -0
- data/lib/dhaka/lexer/lexeme.rb +32 -0
- data/lib/dhaka/lexer/lexer.rb +70 -0
- data/lib/dhaka/lexer/lexer_run.rb +78 -0
- data/lib/dhaka/lexer/regex_grammar.rb +393 -0
- data/lib/dhaka/lexer/regex_parser.rb +2010 -0
- data/lib/dhaka/lexer/regex_tokenizer.rb +14 -0
- data/lib/dhaka/lexer/specification.rb +96 -0
- data/lib/dhaka/lexer/state.rb +68 -0
- data/lib/dhaka/lexer/state_machine.rb +37 -0
- data/lib/dhaka/parser/action.rb +55 -0
- data/lib/dhaka/parser/channel.rb +58 -0
- data/lib/dhaka/parser/compiled_parser.rb +51 -0
- data/lib/dhaka/parser/conflict.rb +54 -0
- data/lib/dhaka/parser/item.rb +43 -0
- data/lib/dhaka/parser/parse_result.rb +50 -0
- data/lib/dhaka/parser/parse_tree.rb +66 -0
- data/lib/dhaka/parser/parser.rb +165 -0
- data/lib/dhaka/parser/parser_methods.rb +11 -0
- data/lib/dhaka/parser/parser_run.rb +39 -0
- data/lib/dhaka/parser/parser_state.rb +74 -0
- data/lib/dhaka/parser/token.rb +22 -0
- data/lib/dhaka/runtime.rb +51 -0
- data/lib/dhaka/tokenizer/tokenizer.rb +190 -0
- data/test/all_tests.rb +5 -0
- data/test/arithmetic/arithmetic_evaluator.rb +64 -0
- data/test/arithmetic/arithmetic_evaluator_test.rb +43 -0
- data/test/arithmetic/arithmetic_grammar.rb +41 -0
- data/test/arithmetic/arithmetic_grammar_test.rb +9 -0
- data/test/arithmetic/arithmetic_test_methods.rb +9 -0
- data/test/arithmetic/arithmetic_tokenizer.rb +39 -0
- data/test/arithmetic/arithmetic_tokenizer_test.rb +38 -0
- data/test/arithmetic_precedence/arithmetic_precedence_evaluator.rb +43 -0
- data/test/arithmetic_precedence/arithmetic_precedence_grammar.rb +24 -0
- data/test/arithmetic_precedence/arithmetic_precedence_grammar_test.rb +30 -0
- data/test/arithmetic_precedence/arithmetic_precedence_lexer_specification.rb +23 -0
- data/test/arithmetic_precedence/arithmetic_precedence_parser_test.rb +33 -0
- data/test/brackets/bracket_grammar.rb +23 -0
- data/test/brackets/bracket_tokenizer.rb +22 -0
- data/test/brackets/brackets_test.rb +28 -0
- data/test/chittagong/chittagong_driver.rb +46 -0
- data/test/chittagong/chittagong_driver_test.rb +276 -0
- data/test/chittagong/chittagong_evaluator.rb +284 -0
- data/test/chittagong/chittagong_evaluator_test.rb +38 -0
- data/test/chittagong/chittagong_grammar.rb +104 -0
- data/test/chittagong/chittagong_lexer.rb +109 -0
- data/test/chittagong/chittagong_lexer_specification.rb +37 -0
- data/test/chittagong/chittagong_lexer_test.rb +58 -0
- data/test/chittagong/chittagong_parser.rb +879 -0
- data/test/chittagong/chittagong_parser_test.rb +55 -0
- data/test/chittagong/chittagong_test.rb +170 -0
- data/test/core/another_lalr_but_not_slr_grammar.rb +20 -0
- data/test/core/compiled_parser_test.rb +44 -0
- data/test/core/dfa_test.rb +170 -0
- data/test/core/evaluator_test.rb +22 -0
- data/test/core/grammar_test.rb +83 -0
- data/test/core/lalr_but_not_slr_grammar.rb +19 -0
- data/test/core/lexer_test.rb +139 -0
- data/test/core/malformed_grammar.rb +7 -0
- data/test/core/malformed_grammar_test.rb +8 -0
- data/test/core/nullable_grammar.rb +21 -0
- data/test/core/parse_result_test.rb +44 -0
- data/test/core/parser_state_test.rb +24 -0
- data/test/core/parser_test.rb +131 -0
- data/test/core/precedence_grammar.rb +17 -0
- data/test/core/precedence_grammar_test.rb +9 -0
- data/test/core/rr_conflict_grammar.rb +21 -0
- data/test/core/simple_grammar.rb +22 -0
- data/test/core/sr_conflict_grammar.rb +16 -0
- data/test/dhaka_test_helper.rb +18 -0
- data/test/fake_logger.rb +17 -0
- metadata +137 -0
@@ -0,0 +1,50 @@
|
|
1
|
+
module Dhaka
|
2
|
+
# Returned on successful parsing of the input token stream.
|
3
|
+
class ParseSuccessResult < DelegateClass(ParseTreeCompositeNode)
|
4
|
+
# Contains the parse result.
|
5
|
+
attr_accessor :parse_tree
|
6
|
+
|
7
|
+
def initialize(parse_tree) #:nodoc:
|
8
|
+
super
|
9
|
+
@parse_tree = parse_tree
|
10
|
+
end
|
11
|
+
|
12
|
+
# This is false.
|
13
|
+
def has_error?
|
14
|
+
false
|
15
|
+
end
|
16
|
+
|
17
|
+
# Returns the dot representation of the parse tree
|
18
|
+
def to_dot
|
19
|
+
Dot::Digraph.new(:fontsize => 10, :shape => :box, :size => 5) do |g|
|
20
|
+
parse_tree.to_dot(g)
|
21
|
+
end.to_dot
|
22
|
+
end
|
23
|
+
|
24
|
+
# Deprecated. Use the +parse_tree+ accessor.
|
25
|
+
alias syntax_tree parse_tree
|
26
|
+
end
|
27
|
+
|
28
|
+
# Returned on unsuccessful parsing of the input token stream.
|
29
|
+
class ParseErrorResult
|
30
|
+
attr_reader :unexpected_token, :parser_state
|
31
|
+
|
32
|
+
def initialize(unexpected_token, parser_state) #:nodoc:
|
33
|
+
@unexpected_token = unexpected_token
|
34
|
+
@parser_state = parser_state
|
35
|
+
end
|
36
|
+
|
37
|
+
# This is true.
|
38
|
+
def has_error?
|
39
|
+
true
|
40
|
+
end
|
41
|
+
|
42
|
+
def inspect #:nodoc:
|
43
|
+
"<Dhaka::ParseErrorResult unexpected_token=#{unexpected_token.inspect}>"
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
|
49
|
+
|
50
|
+
|
@@ -0,0 +1,66 @@
|
|
1
|
+
module Dhaka
|
2
|
+
# These are composite nodes of the syntax tree returned by the successful parsing of a token stream.
|
3
|
+
class ParseTreeCompositeNode
|
4
|
+
attr_reader :production, :child_nodes
|
5
|
+
|
6
|
+
def initialize(production) #:nodoc:
|
7
|
+
@production = production
|
8
|
+
@child_nodes = []
|
9
|
+
end
|
10
|
+
def linearize #:nodoc:
|
11
|
+
child_nodes.collect {|child_node| child_node.linearize}.flatten + [self]
|
12
|
+
end
|
13
|
+
|
14
|
+
def tokens
|
15
|
+
child_nodes.collect{|child_node| child_node.tokens}.flatten
|
16
|
+
end
|
17
|
+
|
18
|
+
def to_s #:nodoc:
|
19
|
+
"CompositeNode: #{production.symbol} --> [#{child_nodes.join(", ")}]"
|
20
|
+
end
|
21
|
+
|
22
|
+
# Returns the dot representation of this node.
|
23
|
+
def to_dot graph
|
24
|
+
graph.node(self, :label => production)
|
25
|
+
child_nodes.each do |child|
|
26
|
+
graph.edge(self, child)
|
27
|
+
child.to_dot(graph)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def head_node? #:nodoc:
|
32
|
+
production.symbol.name == START_SYMBOL_NAME
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
|
37
|
+
# These are leaf nodes of syntax trees. They contain tokens.
|
38
|
+
class ParseTreeLeafNode
|
39
|
+
attr_reader :token
|
40
|
+
|
41
|
+
def initialize(token) #:nodoc:
|
42
|
+
@token = token
|
43
|
+
end
|
44
|
+
|
45
|
+
def linearize #:nodoc:
|
46
|
+
[]
|
47
|
+
end
|
48
|
+
|
49
|
+
def tokens
|
50
|
+
[token]
|
51
|
+
end
|
52
|
+
|
53
|
+
def to_s #:nodoc:
|
54
|
+
"LeafNode: #{token}"
|
55
|
+
end
|
56
|
+
|
57
|
+
# Returns the dot representation of this node.
|
58
|
+
def to_dot(graph)
|
59
|
+
graph.node(self, :label => token)
|
60
|
+
end
|
61
|
+
|
62
|
+
def head_node? #:nodoc:
|
63
|
+
false
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,165 @@
|
|
1
|
+
module Dhaka
|
2
|
+
# The parser generator. To generate a parser from a grammar specification +ArithmeticPrecedenceGrammar+, one would
|
3
|
+
# write:
|
4
|
+
# parser = Dhaka::Parser.new(ArithmeticPrecedenceGrammar)
|
5
|
+
#
|
6
|
+
# To compile this parser to Ruby source as +ArithmeticPrecedenceParser+:
|
7
|
+
# parser.compile_to_ruby_source_as(:ArithmeticPrecedenceParser)
|
8
|
+
# which returns a string of Ruby code.
|
9
|
+
class Parser
|
10
|
+
include ParserMethods
|
11
|
+
attr_reader :grammar
|
12
|
+
|
13
|
+
# Creates a new parser from the given grammar. Messages are logged by default to STDOUT
|
14
|
+
# and the log level is WARN. Shift-reduce conflicts are reported at WARN and reduce-reduce conflicts
|
15
|
+
# at ERROR. You may pass in your own logger. Logging at DEBUG shows a lot of progress output.
|
16
|
+
def initialize(grammar, logger = nil)
|
17
|
+
@shift_actions = Hash.new {|hash, state| hash[state] = ShiftAction.new(state)}
|
18
|
+
@reduce_actions = Hash.new {|hash, production| hash[production] = ReduceAction.new(production)}
|
19
|
+
@logger = logger || default_logger
|
20
|
+
@transitions = Hash.new {|hash, state| hash[state] = {}}
|
21
|
+
@grammar = grammar
|
22
|
+
@channels = Hash.new {|hash, start_item| hash[start_item] = []}
|
23
|
+
@states = Hash.new do |hash, kernel|
|
24
|
+
closure, channels = grammar.closure(kernel)
|
25
|
+
channels.each do |start_item, channel_set|
|
26
|
+
@channels[start_item].concat channel_set.to_a
|
27
|
+
end
|
28
|
+
new_state = ParserState.new(self, closure)
|
29
|
+
hash[kernel] = new_state
|
30
|
+
@logger.debug("Created #{new_state.unique_name}.")
|
31
|
+
new_state.transition_items.each do |symbol, items|
|
32
|
+
destination_kernel = ItemSet.new(items.collect{|item| item.next_item})
|
33
|
+
destination_state = hash[destination_kernel]
|
34
|
+
items.each {|item| @channels[item] << grammar.passive_channel(item, destination_state.items[item.next_item])}
|
35
|
+
@transitions[new_state][symbol] = destination_state
|
36
|
+
end
|
37
|
+
new_state
|
38
|
+
end
|
39
|
+
initialize_states
|
40
|
+
end
|
41
|
+
|
42
|
+
# Returns the Ruby source of the generated parser compiled as +parser_class_name+. This can be written out to a file.
|
43
|
+
def compile_to_ruby_source_as parser_class_name
|
44
|
+
result = "class #{parser_class_name} < Dhaka::CompiledParser\n\n"
|
45
|
+
result << " self.grammar = #{grammar.name}\n\n"
|
46
|
+
result << " start_with #{start_state.id}\n\n"
|
47
|
+
states.each do |state|
|
48
|
+
result << "#{state.compile_to_ruby_source}\n\n"
|
49
|
+
end
|
50
|
+
result << "end"
|
51
|
+
result
|
52
|
+
end
|
53
|
+
|
54
|
+
# Returns the dot representation of the parser. If <tt>:hide_lookaheads</tt> is set to true in the
|
55
|
+
# options hash, lookaheads are not written out to the parser states, which is helpful when there are dozens
|
56
|
+
# of lookahead symbols for every item in every state.
|
57
|
+
def to_dot(options = {})
|
58
|
+
Dot::Digraph.new(:fontsize => 10, :shape => :box, :size => 5) do |g|
|
59
|
+
states.each do |state|
|
60
|
+
g.node(state, :label => state.items.values.collect{|item| item.to_s(options)}.join("\n"))
|
61
|
+
@transitions[state].each do |symbol, dest_state|
|
62
|
+
g.edge(state, dest_state, :label => symbol.name)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end.to_dot
|
66
|
+
end
|
67
|
+
|
68
|
+
def inspect
|
69
|
+
"<Dhaka::Parser grammar : #{grammar}>"
|
70
|
+
end
|
71
|
+
|
72
|
+
private
|
73
|
+
attr_reader :start_state
|
74
|
+
|
75
|
+
def states
|
76
|
+
@states.values
|
77
|
+
end
|
78
|
+
|
79
|
+
def default_logger
|
80
|
+
logger = Logger.new(STDOUT)
|
81
|
+
logger.level = Logger::WARN
|
82
|
+
logger.formatter = ParserLogOutputFormatter.new
|
83
|
+
logger
|
84
|
+
end
|
85
|
+
|
86
|
+
def initialize_states
|
87
|
+
start_productions = grammar.productions_for_symbol(grammar.start_symbol)
|
88
|
+
raise NoStartProductionsError.new(grammar) if start_productions.empty?
|
89
|
+
start_items = ItemSet.new(start_productions.collect {|production| Item.new(production, 0)})
|
90
|
+
start_items.each {|start_item| start_item.lookaheadset << grammar.end_symbol}
|
91
|
+
@start_state = @states[start_items]
|
92
|
+
@logger.debug("Pumping #{@channels.keys.size} dirty items...")
|
93
|
+
pump_channels @channels.keys
|
94
|
+
@logger.debug("Generating shift actions...")
|
95
|
+
generate_shift_actions
|
96
|
+
@logger.debug("Generating reduce actions...")
|
97
|
+
generate_reduce_actions
|
98
|
+
end
|
99
|
+
|
100
|
+
def generate_shift_actions
|
101
|
+
@states.values.each do |state|
|
102
|
+
@transitions[state].keys.each do |symbol|
|
103
|
+
state.actions[symbol.name] = @shift_actions[@transitions[state][symbol]]
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
def generate_reduce_actions
|
109
|
+
@states.values.each do |state|
|
110
|
+
state.items.values.select{ |item| !item.next_symbol }.each do |item|
|
111
|
+
create_reduction_actions_for_item_and_state item, state
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
def create_reduction_actions_for_item_and_state item, state
|
117
|
+
item.lookaheadset.each do |lookahead|
|
118
|
+
new_action = @reduce_actions[item.production]
|
119
|
+
if existing_action = state.actions[lookahead.name]
|
120
|
+
if ReduceAction === existing_action
|
121
|
+
message = ReduceReduceConflict.new(state, lookahead, new_action).resolve
|
122
|
+
@logger.error(message)
|
123
|
+
else
|
124
|
+
message = ShiftReduceConflict.new(state, lookahead, new_action).resolve
|
125
|
+
@logger.warn(message)
|
126
|
+
end
|
127
|
+
else
|
128
|
+
state.actions[lookahead.name] = new_action
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
def pump_channels dirty_items
|
134
|
+
loop do
|
135
|
+
new_dirty_items = Set.new
|
136
|
+
dirty_items.each do |dirty_item|
|
137
|
+
@channels[dirty_item].each do |channel|
|
138
|
+
new_dirty_items << channel.end_item if channel.pump
|
139
|
+
end
|
140
|
+
end
|
141
|
+
break if new_dirty_items.empty?
|
142
|
+
@logger.debug("#{new_dirty_items.size} dirty items...")
|
143
|
+
dirty_items = new_dirty_items
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
# Raised when trying to create a Parser for a grammar that has no productions for the start symbol
|
149
|
+
class NoStartProductionsError < StandardError
|
150
|
+
def initialize(grammar) #:nodoc:
|
151
|
+
@grammar = grammar
|
152
|
+
end
|
153
|
+
def to_s #:nodoc:
|
154
|
+
"No start productions defined for #{@grammar.name}"
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
class ParserLogOutputFormatter < Logger::Formatter #:nodoc:
|
159
|
+
def call(severity, time, progname, msg)
|
160
|
+
"\n%s -- %s: %s\n" % [ severity, progname, msg2str(msg)]
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
end
|
165
|
+
|
@@ -0,0 +1,11 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
module Dhaka
|
3
|
+
# This module is included both in Parser and CompiledParser.
|
4
|
+
module ParserMethods
|
5
|
+
# +token_stream+ is an Enumerable of Token-s. Returns either a ParseSuccessResult or a ParseErrorResult.
|
6
|
+
def parse token_stream
|
7
|
+
parser_run = ParserRun.new(grammar, start_state, token_stream)
|
8
|
+
parser_run.run
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module Dhaka
|
2
|
+
class ParserRun #:nodoc:
|
3
|
+
|
4
|
+
def initialize(grammar, start_state, token_stream)
|
5
|
+
@grammar = grammar
|
6
|
+
@node_stack = []
|
7
|
+
@state_stack = [start_state]
|
8
|
+
@token_stream = token_stream
|
9
|
+
@symbol_queue = []
|
10
|
+
end
|
11
|
+
|
12
|
+
def run
|
13
|
+
tokenize_result = token_stream.each do |token|
|
14
|
+
@current_token = token
|
15
|
+
@symbol_queue << @current_token.symbol_name
|
16
|
+
error = execute_actions
|
17
|
+
return error if error
|
18
|
+
node_stack << ParseTreeLeafNode.new(@current_token)
|
19
|
+
state_stack.last
|
20
|
+
end
|
21
|
+
return tokenize_result if TokenizerErrorResult === tokenize_result
|
22
|
+
ParseSuccessResult.new(node_stack.first) if node_stack.first.head_node?
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
attr_reader :state_stack, :token_stream, :node_stack
|
28
|
+
|
29
|
+
def execute_actions
|
30
|
+
while symbol_name = @symbol_queue.pop
|
31
|
+
action = state_stack.last.actions[symbol_name]
|
32
|
+
return ParseErrorResult.new(@current_token, state_stack.last) unless action
|
33
|
+
instance_eval(&action.action_code)
|
34
|
+
end
|
35
|
+
nil
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
module Dhaka
|
2
|
+
class ParserState #:nodoc:
|
3
|
+
attr_accessor :items, :actions, :id
|
4
|
+
|
5
|
+
@@state_id = 0
|
6
|
+
|
7
|
+
def self.next_state_id
|
8
|
+
result = @@state_id
|
9
|
+
@@state_id += 1
|
10
|
+
result
|
11
|
+
end
|
12
|
+
|
13
|
+
def initialize(parser, items, id=nil)
|
14
|
+
@parser = parser
|
15
|
+
@items = items
|
16
|
+
@actions = {}
|
17
|
+
@id = id || ParserState.next_state_id
|
18
|
+
end
|
19
|
+
|
20
|
+
def transition_items
|
21
|
+
result = Hash.new {|h, k| h[k] = ItemSet.new()}
|
22
|
+
items.values.each do |item|
|
23
|
+
result[item.next_symbol] << item if item.next_symbol
|
24
|
+
end
|
25
|
+
result
|
26
|
+
end
|
27
|
+
|
28
|
+
def unique_name
|
29
|
+
"State#{id}"
|
30
|
+
end
|
31
|
+
|
32
|
+
def compile_to_ruby_source
|
33
|
+
result = " at_state(#{id}) {\n"
|
34
|
+
|
35
|
+
symbol_names_by_action = Hash.new {|hash, key| hash[key] = []}
|
36
|
+
actions.each do |symbol_name, action|
|
37
|
+
symbol_names_by_action[action] << symbol_name
|
38
|
+
end
|
39
|
+
|
40
|
+
symbol_names_by_action.keys.each do |action|
|
41
|
+
symbol_names = symbol_names_by_action[action].collect {|symbol_name| "#{symbol_name.inspect}"}.join(', ')
|
42
|
+
result << " for_symbols(#{symbol_names}) { #{action.compile_to_ruby_source} }\n"
|
43
|
+
end
|
44
|
+
|
45
|
+
result << " }"
|
46
|
+
result
|
47
|
+
end
|
48
|
+
|
49
|
+
def for_symbols *symbol_names, &blk
|
50
|
+
symbol_names.each do |symbol_name|
|
51
|
+
actions[symbol_name] = @parser.instance_eval(&blk)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
alias :for_symbol :for_symbols
|
56
|
+
|
57
|
+
def to_s(options = {})
|
58
|
+
items.values.collect{|item| item.to_s(options)}.join("\n")
|
59
|
+
end
|
60
|
+
|
61
|
+
end
|
62
|
+
|
63
|
+
class ItemSet < Set #:nodoc:
|
64
|
+
def hash
|
65
|
+
result = 5381
|
66
|
+
each { |item| result ^= item.hash }
|
67
|
+
result
|
68
|
+
end
|
69
|
+
|
70
|
+
def eql? other
|
71
|
+
self == other
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Dhaka
|
2
|
+
# Represents a portion of the input character stream that is mapped by the tokenizer
|
3
|
+
# to a symbol in the grammar. The attribute +input_position+ contains the start index position of the original
|
4
|
+
# string input that this token came from. It can be used to report errors by indicating the specific portion
|
5
|
+
# of the input where the error occurred.
|
6
|
+
class Token
|
7
|
+
attr_accessor :symbol_name, :value, :input_position
|
8
|
+
def initialize(symbol_name, value, input_position)
|
9
|
+
@symbol_name = symbol_name
|
10
|
+
@value = value
|
11
|
+
@input_position = input_position
|
12
|
+
end
|
13
|
+
|
14
|
+
def to_s #:nodoc:
|
15
|
+
value ? "#{symbol_name} : #{value}" : "#{symbol_name}"
|
16
|
+
end
|
17
|
+
|
18
|
+
def == other
|
19
|
+
symbol_name == other.symbol_name && value == other.value
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright (c) 2006, 2007 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
|
+
|
24
|
+
require 'set'
|
25
|
+
require 'logger'
|
26
|
+
require 'delegate'
|
27
|
+
|
28
|
+
%w[
|
29
|
+
grammar/grammar_symbol
|
30
|
+
grammar/production
|
31
|
+
grammar/grammar
|
32
|
+
grammar/precedence
|
33
|
+
parser/parse_tree
|
34
|
+
parser/parse_result
|
35
|
+
parser/parser_methods
|
36
|
+
parser/parser_state
|
37
|
+
parser/token
|
38
|
+
parser/action
|
39
|
+
parser/parser_run
|
40
|
+
parser/compiled_parser
|
41
|
+
tokenizer/tokenizer
|
42
|
+
evaluator/evaluator
|
43
|
+
lexer/accept_actions
|
44
|
+
lexer/alphabet
|
45
|
+
lexer/state_machine
|
46
|
+
lexer/state
|
47
|
+
lexer/specification
|
48
|
+
lexer/lexeme
|
49
|
+
lexer/lexer_run
|
50
|
+
lexer/compiled_lexer
|
51
|
+
].each {|path| require File.join(File.dirname(__FILE__), path)}
|