racc 1.4.6
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.
- data/.gitattributes +2 -0
- data/.gitignore +7 -0
- data/COPYING +515 -0
- data/ChangeLog +846 -0
- data/DEPENDS +4 -0
- data/README.en.rdoc +86 -0
- data/README.ja.rdoc +96 -0
- data/Rakefile +15 -0
- data/TODO +5 -0
- data/bin/racc +308 -0
- data/bin/racc2y +195 -0
- data/bin/y2racc +339 -0
- data/doc/en/NEWS.en.rdoc +282 -0
- data/doc/en/command.en.html +78 -0
- data/doc/en/debug.en.rdoc +20 -0
- data/doc/en/grammar.en.rdoc +230 -0
- data/doc/en/index.en.html +10 -0
- data/doc/en/parser.en.rdoc +74 -0
- data/doc/en/usage.en.html +92 -0
- data/doc/ja/NEWS.ja.rdoc +307 -0
- data/doc/ja/command.ja.html +94 -0
- data/doc/ja/debug.ja.rdoc +36 -0
- data/doc/ja/grammar.ja.rdoc +348 -0
- data/doc/ja/index.ja.html +10 -0
- data/doc/ja/parser.ja.rdoc +125 -0
- data/doc/ja/usage.ja.html +414 -0
- data/ext/racc/cparse/MANIFEST +4 -0
- data/ext/racc/cparse/cparse.c +824 -0
- data/ext/racc/cparse/depend +1 -0
- data/ext/racc/cparse/extconf.rb +7 -0
- data/fastcache/extconf.rb +2 -0
- data/fastcache/fastcache.c +185 -0
- data/lib/racc.rb +6 -0
- data/lib/racc/compat.rb +40 -0
- data/lib/racc/debugflags.rb +59 -0
- data/lib/racc/exception.rb +15 -0
- data/lib/racc/grammar.rb +1115 -0
- data/lib/racc/grammarfileparser.rb +559 -0
- data/lib/racc/info.rb +16 -0
- data/lib/racc/iset.rb +91 -0
- data/lib/racc/logfilegenerator.rb +214 -0
- data/lib/racc/parser.rb +439 -0
- data/lib/racc/parserfilegenerator.rb +511 -0
- data/lib/racc/pre-setup +13 -0
- data/lib/racc/sourcetext.rb +34 -0
- data/lib/racc/state.rb +971 -0
- data/lib/racc/statetransitiontable.rb +316 -0
- data/lib/racc/static.rb +5 -0
- data/misc/dist.sh +31 -0
- data/sample/array.y +67 -0
- data/sample/array2.y +59 -0
- data/sample/calc-ja.y +66 -0
- data/sample/calc.y +65 -0
- data/sample/conflict.y +15 -0
- data/sample/hash.y +60 -0
- data/sample/lalr.y +17 -0
- data/sample/lists.y +57 -0
- data/sample/syntax.y +46 -0
- data/sample/yyerr.y +46 -0
- data/setup.rb +1587 -0
- data/tasks/doc.rb +12 -0
- data/tasks/email.rb +55 -0
- data/tasks/file.rb +37 -0
- data/tasks/gem.rb +37 -0
- data/tasks/test.rb +16 -0
- data/test/assets/chk.y +126 -0
- data/test/assets/conf.y +16 -0
- data/test/assets/digraph.y +29 -0
- data/test/assets/echk.y +118 -0
- data/test/assets/err.y +60 -0
- data/test/assets/expect.y +7 -0
- data/test/assets/firstline.y +4 -0
- data/test/assets/ichk.y +102 -0
- data/test/assets/intp.y +546 -0
- data/test/assets/mailp.y +437 -0
- data/test/assets/newsyn.y +25 -0
- data/test/assets/noend.y +4 -0
- data/test/assets/nonass.y +41 -0
- data/test/assets/normal.y +27 -0
- data/test/assets/norule.y +4 -0
- data/test/assets/nullbug1.y +25 -0
- data/test/assets/nullbug2.y +15 -0
- data/test/assets/opt.y +123 -0
- data/test/assets/percent.y +35 -0
- data/test/assets/recv.y +97 -0
- data/test/assets/rrconf.y +14 -0
- data/test/assets/scan.y +72 -0
- data/test/assets/syntax.y +50 -0
- data/test/assets/unterm.y +5 -0
- data/test/assets/useless.y +12 -0
- data/test/assets/yyerr.y +46 -0
- data/test/bench.y +36 -0
- data/test/helper.rb +88 -0
- data/test/infini.y +8 -0
- data/test/scandata/brace +7 -0
- data/test/scandata/gvar +1 -0
- data/test/scandata/normal +4 -0
- data/test/scandata/percent +18 -0
- data/test/scandata/slash +10 -0
- data/test/src.intp +34 -0
- data/test/start.y +20 -0
- data/test/test_chk_y.rb +51 -0
- data/test/test_grammar_file_parser.rb +15 -0
- data/test/test_racc_command.rb +155 -0
- data/test/test_scan_y.rb +51 -0
- data/test/testscanner.rb +51 -0
- data/web/racc.en.rhtml +42 -0
- data/web/racc.ja.rhtml +51 -0
- metadata +166 -0
data/bin/racc2y
ADDED
@@ -0,0 +1,195 @@
|
|
1
|
+
#!/usr/local/bin/ruby
|
2
|
+
#
|
3
|
+
# $Id$
|
4
|
+
#
|
5
|
+
# Copyright (c) 1999-2006 Minero Aoki
|
6
|
+
#
|
7
|
+
# This program is feee software.
|
8
|
+
# You can distribute/modify this program under the terms of
|
9
|
+
# the GNU LGPL, Lesser General Public License version 2.1.
|
10
|
+
# For details of the LGPL, see the file "COPYING".
|
11
|
+
#
|
12
|
+
|
13
|
+
require 'racc/grammarfileparser'
|
14
|
+
require 'racc/info'
|
15
|
+
require 'optparse'
|
16
|
+
|
17
|
+
def main
|
18
|
+
@with_action = true
|
19
|
+
with_header = false
|
20
|
+
with_inner = false
|
21
|
+
with_footer = false
|
22
|
+
output = nil
|
23
|
+
parser = OptionParser.new
|
24
|
+
parser.banner = "Usage: #{File.basename($0)} [-AHIF] [-oFILENAME] GRAMMARFILE"
|
25
|
+
parser.on('-o', '--output=FILENAME', 'output file name [<input>.yacc]') {|name|
|
26
|
+
output = name
|
27
|
+
}
|
28
|
+
parser.on('-A', '--without-action', 'Does not include actions.') {
|
29
|
+
@with_action = false
|
30
|
+
}
|
31
|
+
parser.on('-H', '--with-header', 'Includes header part.') {
|
32
|
+
with_header = true
|
33
|
+
}
|
34
|
+
parser.on('-I', '--with-inner', 'Includes inner part.') {
|
35
|
+
with_inner = true
|
36
|
+
}
|
37
|
+
parser.on('-F', '--with-footer', 'Includes footer part.') {
|
38
|
+
with_footer = true
|
39
|
+
}
|
40
|
+
parser.on('--version', 'Prints version and quit.') {
|
41
|
+
puts "racc2y version #{Racc::Version}"
|
42
|
+
exit 0
|
43
|
+
}
|
44
|
+
parser.on('--copyright', 'Prints copyright and quit.') {
|
45
|
+
puts Racc::Copyright
|
46
|
+
exit 0
|
47
|
+
}
|
48
|
+
parser.on('--help', 'Prints this message and quit.') {
|
49
|
+
puts parser.help
|
50
|
+
exit 1
|
51
|
+
}
|
52
|
+
begin
|
53
|
+
parser.parse!
|
54
|
+
rescue OptionParser::ParseError => err
|
55
|
+
$stderr.puts err.message
|
56
|
+
$stderr.puts parser.help
|
57
|
+
exit 1
|
58
|
+
end
|
59
|
+
if ARGV.empty?
|
60
|
+
$stderr.puts "no input file"
|
61
|
+
exit 1
|
62
|
+
end
|
63
|
+
unless ARGV.size == 1
|
64
|
+
$stderr.puts "too many inputs"
|
65
|
+
exit 1
|
66
|
+
end
|
67
|
+
input = ARGV[0]
|
68
|
+
|
69
|
+
begin
|
70
|
+
result = Racc::GrammarFileParser.parse_file(input)
|
71
|
+
result.grammar.init
|
72
|
+
File.open(output || "#{input}.yacc", 'w') {|f|
|
73
|
+
f.puts "/* generated from #{input} */"
|
74
|
+
if with_header
|
75
|
+
f.puts
|
76
|
+
f.puts '%{'
|
77
|
+
print_user_codes f, result.params.header
|
78
|
+
f.puts '%}'
|
79
|
+
end
|
80
|
+
f.puts
|
81
|
+
print_terminals f, result.grammar
|
82
|
+
f.puts
|
83
|
+
print_precedence_table f, precedence_table(result.grammar)
|
84
|
+
f.puts
|
85
|
+
f.puts '%%'
|
86
|
+
print_grammar f, result.grammar
|
87
|
+
f.puts '%%'
|
88
|
+
if with_inner
|
89
|
+
f.puts '/*---- inner ----*/'
|
90
|
+
print_user_codes f, result.params.inner
|
91
|
+
end
|
92
|
+
if with_footer
|
93
|
+
f.puts '/*---- footer ----*/'
|
94
|
+
print_user_codes f, result.params.footer
|
95
|
+
end
|
96
|
+
}
|
97
|
+
rescue SystemCallError => err
|
98
|
+
$stderr.puts err.message
|
99
|
+
exit 1
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
def print_terminals(f, grammar)
|
104
|
+
init_indent = '%token'.size
|
105
|
+
f.print '%token'
|
106
|
+
columns = init_indent
|
107
|
+
grammar.symboltable.each_terminal do |t|
|
108
|
+
next unless t.terminal?
|
109
|
+
next if t.dummy?
|
110
|
+
next if t == grammar.symboltable.anchor
|
111
|
+
next if t == grammar.symboltable.error
|
112
|
+
unless t.value.kind_of?(String)
|
113
|
+
if columns > 60
|
114
|
+
f.puts
|
115
|
+
f.print ' ' * init_indent
|
116
|
+
columns = init_indent
|
117
|
+
end
|
118
|
+
columns += f.write(" #{yacc_symbol(t)}")
|
119
|
+
end
|
120
|
+
end
|
121
|
+
f.puts
|
122
|
+
end
|
123
|
+
|
124
|
+
def precedence_table(grammar)
|
125
|
+
table = []
|
126
|
+
grammar.symboltable.select {|sym| sym.precedence }.each do |sym|
|
127
|
+
(table[sym.prec] ||= [sym.assoc]).push sym
|
128
|
+
end
|
129
|
+
table.compact
|
130
|
+
end
|
131
|
+
|
132
|
+
def print_precedence_table(f, table)
|
133
|
+
return if table.empty?
|
134
|
+
f.puts '/* precedance table */'
|
135
|
+
table.each do |syms|
|
136
|
+
assoc = syms.shift
|
137
|
+
f.printf '%%%-8s ', assoc.to_s.downcase
|
138
|
+
f.puts syms.map {|s| yacc_symbol(s) }.join(' ')
|
139
|
+
end
|
140
|
+
f.puts
|
141
|
+
end
|
142
|
+
|
143
|
+
def print_grammar(f, grammar)
|
144
|
+
prev_target = nil
|
145
|
+
indent = 10
|
146
|
+
embactions = []
|
147
|
+
grammar.each do |rule|
|
148
|
+
if rule.target.dummy?
|
149
|
+
embactions.push rule.action unless rule.action.empty?
|
150
|
+
next
|
151
|
+
end
|
152
|
+
if rule.target == prev_target
|
153
|
+
f.print ' ' * indent, '|'
|
154
|
+
else
|
155
|
+
prev_target = rule.target
|
156
|
+
f.printf "\n%-10s:", yacc_symbol(prev_target)
|
157
|
+
end
|
158
|
+
rule.symbols.each do |s|
|
159
|
+
if s.dummy? # target of dummy rule for embedded action
|
160
|
+
f.puts
|
161
|
+
print_action f, embactions.shift, indent
|
162
|
+
f.print ' ' * (indent + 1)
|
163
|
+
else
|
164
|
+
f.print ' ', yacc_symbol(s)
|
165
|
+
end
|
166
|
+
end
|
167
|
+
if rule.specified_prec
|
168
|
+
f.print ' %prec ', yacc_symbol(rule.specified_prec)
|
169
|
+
end
|
170
|
+
f.puts
|
171
|
+
unless rule.action.empty?
|
172
|
+
print_action f, rule.action, indent
|
173
|
+
end
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
def print_action(f, action, indent)
|
178
|
+
return unless @with_action
|
179
|
+
f.print ' ' * (indent + 4), "{\n"
|
180
|
+
f.print ' ' * (indent + 6), action.source.text.strip, "\n"
|
181
|
+
f.print ' ' * (indent + 4) , "}\n"
|
182
|
+
end
|
183
|
+
|
184
|
+
def print_user_codes(f, srcs)
|
185
|
+
return if srcs.empty?
|
186
|
+
srcs.each do |src|
|
187
|
+
f.puts src.text
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
def yacc_symbol(s)
|
192
|
+
s.to_s.gsub('"', "'")
|
193
|
+
end
|
194
|
+
|
195
|
+
main
|
data/bin/y2racc
ADDED
@@ -0,0 +1,339 @@
|
|
1
|
+
#!/usr/local/bin/ruby
|
2
|
+
#
|
3
|
+
# $Id$
|
4
|
+
#
|
5
|
+
# Copyright (c) 1999-2006 Minero Aoki
|
6
|
+
#
|
7
|
+
# This program is free software.
|
8
|
+
# You can distribute/modify this program under the terms of
|
9
|
+
# the GNU LGPL, Lesser General Public Lisence version 2.1.
|
10
|
+
# For details of the GNU LGPL, see the file "COPYING".
|
11
|
+
#
|
12
|
+
|
13
|
+
require 'racc/info'
|
14
|
+
require 'strscan'
|
15
|
+
require 'forwardable'
|
16
|
+
require 'optparse'
|
17
|
+
|
18
|
+
def main
|
19
|
+
@with_action = true
|
20
|
+
@with_header = false
|
21
|
+
@with_usercode = false
|
22
|
+
cname = 'MyParser'
|
23
|
+
input = nil
|
24
|
+
output = nil
|
25
|
+
parser = OptionParser.new
|
26
|
+
parser.banner = "Usage: #{File.basename($0)} [-Ahu] [-c <classname>] [-o <filename>] <input>"
|
27
|
+
parser.on('-o', '--output=FILENAME', 'output file name [<input>.racc]') {|name|
|
28
|
+
output = name
|
29
|
+
}
|
30
|
+
parser.on('-c', '--classname=NAME', "Name of the parser class. [#{cname}]") {|name|
|
31
|
+
cname = name
|
32
|
+
}
|
33
|
+
parser.on('-A', '--without-action', 'Does not include actions.') {
|
34
|
+
@with_action = false
|
35
|
+
}
|
36
|
+
parser.on('-h', '--with-header', 'Includes header (%{...%}).') {
|
37
|
+
@with_header = true
|
38
|
+
}
|
39
|
+
parser.on('-u', '--with-user-code', 'Includes user code.') {
|
40
|
+
@with_usercode = true
|
41
|
+
}
|
42
|
+
parser.on('--version', 'Prints version and quit.') {
|
43
|
+
puts "y2racc version #{Racc::Version}"
|
44
|
+
exit 0
|
45
|
+
}
|
46
|
+
parser.on('--copyright', 'Prints copyright and quit.') {
|
47
|
+
puts Racc::Copyright
|
48
|
+
exit 0
|
49
|
+
}
|
50
|
+
parser.on('--help', 'Prints this message and quit.') {
|
51
|
+
puts parser.help
|
52
|
+
exit 1
|
53
|
+
}
|
54
|
+
begin
|
55
|
+
parser.parse!
|
56
|
+
rescue OptionParser::ParseError => err
|
57
|
+
$stderr.puts err.message
|
58
|
+
$stderr.puts parser.help
|
59
|
+
exit 1
|
60
|
+
end
|
61
|
+
if ARGV.empty?
|
62
|
+
$stderr.puts 'no input'
|
63
|
+
exit 1
|
64
|
+
end
|
65
|
+
if ARGV.size > 1
|
66
|
+
$stderr.puts 'too many input'
|
67
|
+
exit 1
|
68
|
+
end
|
69
|
+
input = ARGV[0]
|
70
|
+
|
71
|
+
begin
|
72
|
+
result = YaccFileParser.parse_file(input)
|
73
|
+
File.open(output || "#{input}.racc", 'w') {|f|
|
74
|
+
convert cname, result, f
|
75
|
+
}
|
76
|
+
rescue SystemCallError => err
|
77
|
+
$stderr.puts err.message
|
78
|
+
exit 1
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def convert(classname, result, f)
|
83
|
+
init_indent = 'token'.size
|
84
|
+
f.puts %<# Converted from "#{result.filename}" by y2racc version #{Racc::Version}>
|
85
|
+
f.puts
|
86
|
+
f.puts "class #{classname}"
|
87
|
+
unless result.terminals.empty?
|
88
|
+
f.puts
|
89
|
+
f.print 'token'
|
90
|
+
columns = init_indent
|
91
|
+
result.terminals.each do |t|
|
92
|
+
if columns > 60
|
93
|
+
f.puts
|
94
|
+
f.print ' ' * init_indent
|
95
|
+
columns = init_indent
|
96
|
+
end
|
97
|
+
columns += f.write(" #{t}")
|
98
|
+
end
|
99
|
+
f.puts
|
100
|
+
end
|
101
|
+
unless result.precedence_table.empty?
|
102
|
+
f.puts
|
103
|
+
f.puts 'preclow'
|
104
|
+
result.precedence_table.each do |assoc, toks|
|
105
|
+
f.printf " %-8s %s\n", assoc, toks.join(' ') unless toks.empty?
|
106
|
+
end
|
107
|
+
f.puts 'prechigh'
|
108
|
+
end
|
109
|
+
if result.start
|
110
|
+
f.puts
|
111
|
+
f.puts "start #{@start}"
|
112
|
+
end
|
113
|
+
|
114
|
+
f.puts
|
115
|
+
f.puts 'rule'
|
116
|
+
texts = @with_action ? result.grammar : result.grammar_without_actions
|
117
|
+
texts.each do |text|
|
118
|
+
f.print text
|
119
|
+
end
|
120
|
+
|
121
|
+
if @with_header and result.header
|
122
|
+
f.puts
|
123
|
+
f.puts '---- header'
|
124
|
+
f.puts result.header
|
125
|
+
end
|
126
|
+
if @with_usercode and result.usercode
|
127
|
+
f.puts
|
128
|
+
f.puts '---- footer'
|
129
|
+
f.puts result.usercode
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
class ParseError < StandardError; end
|
134
|
+
|
135
|
+
class StringScanner_withlineno
|
136
|
+
def initialize(src)
|
137
|
+
@s = StringScanner.new(src)
|
138
|
+
@lineno = 1
|
139
|
+
end
|
140
|
+
|
141
|
+
extend Forwardable
|
142
|
+
def_delegator "@s", :eos?
|
143
|
+
def_delegator "@s", :rest
|
144
|
+
|
145
|
+
attr_reader :lineno
|
146
|
+
|
147
|
+
def scan(re)
|
148
|
+
advance_lineno(@s.scan(re))
|
149
|
+
end
|
150
|
+
|
151
|
+
def scan_until(re)
|
152
|
+
advance_lineno(@s.scan_until(re))
|
153
|
+
end
|
154
|
+
|
155
|
+
def skip(re)
|
156
|
+
str = advance_lineno(@s.scan(re))
|
157
|
+
str ? str.size : nil
|
158
|
+
end
|
159
|
+
|
160
|
+
def getch
|
161
|
+
advance_lineno(@s.getch)
|
162
|
+
end
|
163
|
+
|
164
|
+
private
|
165
|
+
|
166
|
+
def advance_lineno(str)
|
167
|
+
@lineno += str.count("\n") if str
|
168
|
+
str
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
class YaccFileParser
|
173
|
+
|
174
|
+
Result = Struct.new(:terminals, :precedence_table, :start,
|
175
|
+
:header, :grammar, :usercode, :filename)
|
176
|
+
class Result # reopen
|
177
|
+
def initialize
|
178
|
+
super
|
179
|
+
self.terminals = []
|
180
|
+
self.precedence_table = []
|
181
|
+
self.start = nil
|
182
|
+
self.grammar = []
|
183
|
+
self.header = nil
|
184
|
+
self.usercode = nil
|
185
|
+
self.filename = nil
|
186
|
+
end
|
187
|
+
|
188
|
+
def grammar_without_actions
|
189
|
+
grammar().map {|text| text[0,1] == '{' ? '{}' : text }
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
def YaccFileParser.parse_file(filename)
|
194
|
+
new().parse(File.read(filename), filename)
|
195
|
+
end
|
196
|
+
|
197
|
+
def parse(src, filename = '-')
|
198
|
+
@result = Result.new
|
199
|
+
@filename = filename
|
200
|
+
@result.filename = filename
|
201
|
+
s = StringScanner_withlineno.new(src)
|
202
|
+
parse_header s
|
203
|
+
parse_grammar s
|
204
|
+
@result
|
205
|
+
end
|
206
|
+
|
207
|
+
private
|
208
|
+
|
209
|
+
COMMENT = %r</\*[^*]*\*+(?:[^/*][^*]*\*+)*/>
|
210
|
+
CHAR = /'((?:[^'\\]+|\\.)*)'/
|
211
|
+
STRING = /"((?:[^"\\]+|\\.)*)"/
|
212
|
+
|
213
|
+
def parse_header(s)
|
214
|
+
skip_until_percent s
|
215
|
+
until s.eos?
|
216
|
+
case
|
217
|
+
when t = s.scan(/left/)
|
218
|
+
@result.precedence_table.push ['left', scan_symbols(s)]
|
219
|
+
when t = s.scan(/right/)
|
220
|
+
@result.precedence_table.push ['right', scan_symbols(s)]
|
221
|
+
when t = s.scan(/nonassoc/)
|
222
|
+
@result.precedence_table.push ['nonassoc', scan_symbols(s)]
|
223
|
+
when t = s.scan(/token/)
|
224
|
+
list = scan_symbols(s)
|
225
|
+
list.shift if /\A<(.*)>\z/ =~ list[0]
|
226
|
+
@result.terminals.concat list
|
227
|
+
when t = s.scan(/start/)
|
228
|
+
@result.start = scan_symbols(s)[0]
|
229
|
+
when s.skip(%r<(?:
|
230
|
+
type | union | expect | thong | binary |
|
231
|
+
semantic_parser | pure_parser | no_lines |
|
232
|
+
raw | token_table
|
233
|
+
)\b>x)
|
234
|
+
skip_until_percent s
|
235
|
+
when s.skip(/\{/) # header (%{...%})
|
236
|
+
str = s.scan_until(/\%\}/)
|
237
|
+
str.chop!
|
238
|
+
str.chop!
|
239
|
+
@result.header = str
|
240
|
+
skip_until_percent s
|
241
|
+
when s.skip(/\%/) # grammar (%%...)
|
242
|
+
return
|
243
|
+
else
|
244
|
+
raise ParseError, "#{@filename}:#{s.lineno}: scan error"
|
245
|
+
end
|
246
|
+
end
|
247
|
+
end
|
248
|
+
|
249
|
+
def skip_until_percent(s)
|
250
|
+
until s.eos?
|
251
|
+
s.skip /[^\%\/]+/
|
252
|
+
next if s.skip(COMMENT)
|
253
|
+
return if s.getch == '%'
|
254
|
+
end
|
255
|
+
end
|
256
|
+
|
257
|
+
def scan_symbols(s)
|
258
|
+
list = []
|
259
|
+
until s.eos?
|
260
|
+
s.skip /\s+/
|
261
|
+
if s.skip(COMMENT)
|
262
|
+
;
|
263
|
+
elsif t = s.scan(CHAR)
|
264
|
+
list.push t
|
265
|
+
elsif t = s.scan(STRING)
|
266
|
+
list.push t
|
267
|
+
elsif s.skip(/\%/)
|
268
|
+
break
|
269
|
+
elsif t = s.scan(/\S+/)
|
270
|
+
list.push t
|
271
|
+
else
|
272
|
+
raise ParseError, "#{@filename}:#{@lineno}: scan error"
|
273
|
+
end
|
274
|
+
end
|
275
|
+
list
|
276
|
+
end
|
277
|
+
|
278
|
+
def parse_grammar(s)
|
279
|
+
buf = []
|
280
|
+
until s.eos?
|
281
|
+
if t = s.scan(/[^%'"{\/]+/)
|
282
|
+
buf.push t
|
283
|
+
break if s.eos?
|
284
|
+
end
|
285
|
+
if s.skip(/\{/)
|
286
|
+
buf.push scan_action(s)
|
287
|
+
elsif t = s.scan(/'(?:[^'\\]+|\\.)*'/) then buf.push t
|
288
|
+
elsif t = s.scan(/"(?:[^"\\]+|\\.)*"/) then buf.push t
|
289
|
+
elsif t = s.scan(COMMENT) then buf.push t
|
290
|
+
elsif s.skip(/%prec\b/) then buf.push '='
|
291
|
+
elsif s.skip(/%%/)
|
292
|
+
@result.usercode = s.rest
|
293
|
+
break
|
294
|
+
else
|
295
|
+
buf.push s.getch
|
296
|
+
end
|
297
|
+
end
|
298
|
+
@result.grammar = buf
|
299
|
+
end
|
300
|
+
|
301
|
+
def scan_action(s)
|
302
|
+
buf = '{'
|
303
|
+
nest = 1
|
304
|
+
until s.eos?
|
305
|
+
if t = s.scan(%r<[^/{}'"]+>)
|
306
|
+
buf << t
|
307
|
+
break if s.eos?
|
308
|
+
elsif t = s.scan(COMMENT)
|
309
|
+
buf << t
|
310
|
+
elsif t = s.scan(CHAR)
|
311
|
+
buf << t
|
312
|
+
elsif t = s.scan(STRING)
|
313
|
+
buf << t
|
314
|
+
else
|
315
|
+
c = s.getch
|
316
|
+
buf << c
|
317
|
+
case c
|
318
|
+
when '{'
|
319
|
+
nest += 1
|
320
|
+
when '}'
|
321
|
+
nest -= 1
|
322
|
+
return buf if nest == 0
|
323
|
+
end
|
324
|
+
end
|
325
|
+
end
|
326
|
+
$stderr.puts "warning: unterminated action in #{@filename}"
|
327
|
+
buf
|
328
|
+
end
|
329
|
+
|
330
|
+
end
|
331
|
+
|
332
|
+
unless Object.method_defined?(:funcall)
|
333
|
+
class Object
|
334
|
+
alias funcall __send__
|
335
|
+
end
|
336
|
+
end
|
337
|
+
|
338
|
+
|
339
|
+
main
|