racc 1.4.6
Sign up to get free protection for your applications and to get access to all the features.
- 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
@@ -0,0 +1,559 @@
|
|
1
|
+
#
|
2
|
+
# $Id$
|
3
|
+
#
|
4
|
+
# Copyright (c) 1999-2006 Minero Aoki
|
5
|
+
#
|
6
|
+
# This program is free software.
|
7
|
+
# You can distribute/modify this program under the terms of
|
8
|
+
# the GNU LGPL, Lesser General Public License version 2.1.
|
9
|
+
# For details of the GNU LGPL, see the file "COPYING".
|
10
|
+
#
|
11
|
+
|
12
|
+
require 'racc'
|
13
|
+
require 'racc/compat'
|
14
|
+
require 'racc/grammar'
|
15
|
+
require 'racc/parserfilegenerator'
|
16
|
+
require 'racc/sourcetext'
|
17
|
+
require 'stringio'
|
18
|
+
|
19
|
+
module Racc
|
20
|
+
|
21
|
+
grammar = Grammar.define {
|
22
|
+
g = self
|
23
|
+
|
24
|
+
g.class = seq(:CLASS, :cname, many(:param), :RULE, :rules, option(:END))
|
25
|
+
|
26
|
+
g.cname = seq(:rubyconst) {|name|
|
27
|
+
@result.params.classname = name
|
28
|
+
}\
|
29
|
+
| seq(:rubyconst, "<", :rubyconst) {|c, _, s|
|
30
|
+
@result.params.classname = c
|
31
|
+
@result.params.superclass = s
|
32
|
+
}
|
33
|
+
|
34
|
+
g.rubyconst = separated_by1(:colon2, :SYMBOL) {|syms|
|
35
|
+
syms.map {|s| s.to_s }.join('::')
|
36
|
+
}
|
37
|
+
|
38
|
+
g.colon2 = seq(':', ':')
|
39
|
+
|
40
|
+
g.param = seq(:CONV, many1(:convdef), :END) {|*|
|
41
|
+
#@grammar.end_convert_block # FIXME
|
42
|
+
}\
|
43
|
+
| seq(:PRECHIGH, many1(:precdef), :PRECLOW) {|*|
|
44
|
+
@grammar.end_precedence_declaration true
|
45
|
+
}\
|
46
|
+
| seq(:PRECLOW, many1(:precdef), :PRECHIGH) {|*|
|
47
|
+
@grammar.end_precedence_declaration false
|
48
|
+
}\
|
49
|
+
| seq(:START, :symbol) {|_, sym|
|
50
|
+
@grammar.start_symbol = sym
|
51
|
+
}\
|
52
|
+
| seq(:TOKEN, :symbols) {|_, syms|
|
53
|
+
syms.each do |s|
|
54
|
+
s.should_terminal
|
55
|
+
end
|
56
|
+
}\
|
57
|
+
| seq(:OPTION, :options) {|_, syms|
|
58
|
+
syms.each do |opt|
|
59
|
+
case opt
|
60
|
+
when 'result_var'
|
61
|
+
@result.params.result_var = true
|
62
|
+
when 'no_result_var'
|
63
|
+
@result.params.result_var = false
|
64
|
+
when 'omit_action_call'
|
65
|
+
@result.params.omit_action_call = true
|
66
|
+
when 'no_omit_action_call'
|
67
|
+
@result.params.omit_action_call = false
|
68
|
+
else
|
69
|
+
raise CompileError, "unknown option: #{opt}"
|
70
|
+
end
|
71
|
+
end
|
72
|
+
}\
|
73
|
+
| seq(:EXPECT, :DIGIT) {|_, num|
|
74
|
+
if @grammar.n_expected_srconflicts
|
75
|
+
raise CompileError, "`expect' seen twice"
|
76
|
+
end
|
77
|
+
@grammar.n_expected_srconflicts = num
|
78
|
+
}
|
79
|
+
|
80
|
+
g.convdef = seq(:symbol, :STRING) {|sym, code|
|
81
|
+
sym.serialized = code
|
82
|
+
}
|
83
|
+
|
84
|
+
g.precdef = seq(:LEFT, :symbols) {|_, syms|
|
85
|
+
@grammar.declare_precedence :Left, syms
|
86
|
+
}\
|
87
|
+
| seq(:RIGHT, :symbols) {|_, syms|
|
88
|
+
@grammar.declare_precedence :Right, syms
|
89
|
+
}\
|
90
|
+
| seq(:NONASSOC, :symbols) {|_, syms|
|
91
|
+
@grammar.declare_precedence :Nonassoc, syms
|
92
|
+
}
|
93
|
+
|
94
|
+
g.symbols = seq(:symbol) {|sym|
|
95
|
+
[sym]
|
96
|
+
}\
|
97
|
+
| seq(:symbols, :symbol) {|list, sym|
|
98
|
+
list.push sym
|
99
|
+
list
|
100
|
+
}\
|
101
|
+
| seq(:symbols, "|")
|
102
|
+
|
103
|
+
g.symbol = seq(:SYMBOL) {|sym| @grammar.intern(sym) }\
|
104
|
+
| seq(:STRING) {|str| @grammar.intern(str) }
|
105
|
+
|
106
|
+
g.options = many(:SYMBOL) {|syms| syms.map {|s| s.to_s } }
|
107
|
+
|
108
|
+
g.rules = option(:rules_core) {|list|
|
109
|
+
add_rule_block list unless list.empty?
|
110
|
+
nil
|
111
|
+
}
|
112
|
+
|
113
|
+
g.rules_core = seq(:symbol) {|sym|
|
114
|
+
[sym]
|
115
|
+
}\
|
116
|
+
| seq(:rules_core, :rule_item) {|list, i|
|
117
|
+
list.push i
|
118
|
+
list
|
119
|
+
}\
|
120
|
+
| seq(:rules_core, ';') {|list, *|
|
121
|
+
add_rule_block list unless list.empty?
|
122
|
+
list.clear
|
123
|
+
list
|
124
|
+
}\
|
125
|
+
| seq(:rules_core, ':') {|list, *|
|
126
|
+
next_target = list.pop
|
127
|
+
add_rule_block list unless list.empty?
|
128
|
+
[next_target]
|
129
|
+
}
|
130
|
+
|
131
|
+
g.rule_item = seq(:symbol)\
|
132
|
+
| seq("|") {|*|
|
133
|
+
OrMark.new(@scanner.lineno)
|
134
|
+
}\
|
135
|
+
| seq("=", :symbol) {|_, sym|
|
136
|
+
Prec.new(sym, @scanner.lineno)
|
137
|
+
}\
|
138
|
+
| seq(:ACTION) {|src|
|
139
|
+
UserAction.source_text(src)
|
140
|
+
}
|
141
|
+
}
|
142
|
+
|
143
|
+
GrammarFileParser = grammar.parser_class
|
144
|
+
|
145
|
+
if grammar.states.srconflict_exist?
|
146
|
+
raise 'Racc boot script fatal: S/R conflict in build'
|
147
|
+
end
|
148
|
+
if grammar.states.rrconflict_exist?
|
149
|
+
raise 'Racc boot script fatal: R/R conflict in build'
|
150
|
+
end
|
151
|
+
|
152
|
+
class GrammarFileParser # reopen
|
153
|
+
|
154
|
+
class Result
|
155
|
+
def initialize(grammar)
|
156
|
+
@grammar = grammar
|
157
|
+
@params = ParserFileGenerator::Params.new
|
158
|
+
end
|
159
|
+
|
160
|
+
attr_reader :grammar
|
161
|
+
attr_reader :params
|
162
|
+
end
|
163
|
+
|
164
|
+
def GrammarFileParser.parse_file(filename)
|
165
|
+
parse(File.read(filename), filename, 1)
|
166
|
+
end
|
167
|
+
|
168
|
+
def GrammarFileParser.parse(src, filename = '-', lineno = 1)
|
169
|
+
new().parse(src, filename, lineno)
|
170
|
+
end
|
171
|
+
|
172
|
+
def initialize(debug_flags = DebugFlags.new)
|
173
|
+
@yydebug = debug_flags.parse
|
174
|
+
end
|
175
|
+
|
176
|
+
def parse(src, filename = '-', lineno = 1)
|
177
|
+
@filename = filename
|
178
|
+
@lineno = lineno
|
179
|
+
@scanner = GrammarFileScanner.new(src, @filename)
|
180
|
+
@scanner.debug = @yydebug
|
181
|
+
@grammar = Grammar.new
|
182
|
+
@result = Result.new(@grammar)
|
183
|
+
@embedded_action_seq = 0
|
184
|
+
yyparse @scanner, :yylex
|
185
|
+
parse_user_code
|
186
|
+
@result.grammar.init
|
187
|
+
@result
|
188
|
+
end
|
189
|
+
|
190
|
+
private
|
191
|
+
|
192
|
+
def next_token
|
193
|
+
@scanner.scan
|
194
|
+
end
|
195
|
+
|
196
|
+
def on_error(tok, val, _values)
|
197
|
+
if val.respond_to?(:id2name)
|
198
|
+
v = val.id2name
|
199
|
+
elsif val.kind_of?(String)
|
200
|
+
v = val
|
201
|
+
else
|
202
|
+
v = val.inspect
|
203
|
+
end
|
204
|
+
raise CompileError, "#{location()}: unexpected token '#{v}'"
|
205
|
+
end
|
206
|
+
|
207
|
+
def location
|
208
|
+
"#{@filename}:#{@lineno - 1 + @scanner.lineno}"
|
209
|
+
end
|
210
|
+
|
211
|
+
def add_rule_block(list)
|
212
|
+
sprec = nil
|
213
|
+
target = list.shift
|
214
|
+
case target
|
215
|
+
when OrMark, UserAction, Prec
|
216
|
+
raise CompileError, "#{target.lineno}: unexpected symbol #{target.name}"
|
217
|
+
end
|
218
|
+
curr = []
|
219
|
+
list.each do |i|
|
220
|
+
case i
|
221
|
+
when OrMark
|
222
|
+
add_rule target, curr, sprec
|
223
|
+
curr = []
|
224
|
+
sprec = nil
|
225
|
+
when Prec
|
226
|
+
raise CompileError, "'=<prec>' used twice in one rule" if sprec
|
227
|
+
sprec = i.symbol
|
228
|
+
else
|
229
|
+
curr.push i
|
230
|
+
end
|
231
|
+
end
|
232
|
+
add_rule target, curr, sprec
|
233
|
+
end
|
234
|
+
|
235
|
+
def add_rule(target, list, sprec)
|
236
|
+
if list.last.kind_of?(UserAction)
|
237
|
+
act = list.pop
|
238
|
+
else
|
239
|
+
act = UserAction.empty
|
240
|
+
end
|
241
|
+
list.map! {|s| s.kind_of?(UserAction) ? embedded_action(s) : s }
|
242
|
+
rule = Rule.new(target, list, act)
|
243
|
+
rule.specified_prec = sprec
|
244
|
+
@grammar.add rule
|
245
|
+
end
|
246
|
+
|
247
|
+
def embedded_action(act)
|
248
|
+
sym = @grammar.intern("@#{@embedded_action_seq += 1}".intern, true)
|
249
|
+
@grammar.add Rule.new(sym, [], act)
|
250
|
+
sym
|
251
|
+
end
|
252
|
+
|
253
|
+
#
|
254
|
+
# User Code Block
|
255
|
+
#
|
256
|
+
|
257
|
+
def parse_user_code
|
258
|
+
line = @scanner.lineno
|
259
|
+
_, *blocks = *@scanner.epilogue.split(/^----/)
|
260
|
+
blocks.each do |block|
|
261
|
+
header, *body = block.to_a
|
262
|
+
label0, pathes = *header.sub(/\A-+/, '').split('=', 2)
|
263
|
+
label = canonical_label(label0)
|
264
|
+
(pathes ? pathes.strip.split(' ') : []).each do |path|
|
265
|
+
add_user_code label, SourceText.new(File.read(path), path, 1)
|
266
|
+
end
|
267
|
+
add_user_code label, SourceText.new(body.join(''), @filename, line + 1)
|
268
|
+
line += (1 + body.size)
|
269
|
+
end
|
270
|
+
end
|
271
|
+
|
272
|
+
USER_CODE_LABELS = {
|
273
|
+
'header' => :header,
|
274
|
+
'prepare' => :header, # obsolete
|
275
|
+
'inner' => :inner,
|
276
|
+
'footer' => :footer,
|
277
|
+
'driver' => :footer # obsolete
|
278
|
+
}
|
279
|
+
|
280
|
+
def canonical_label(src)
|
281
|
+
label = src.to_s.strip.downcase.slice(/\w+/)
|
282
|
+
unless USER_CODE_LABELS.key?(label)
|
283
|
+
raise CompileError, "unknown user code type: #{label.inspect}"
|
284
|
+
end
|
285
|
+
label
|
286
|
+
end
|
287
|
+
|
288
|
+
def add_user_code(label, src)
|
289
|
+
@result.params.send(USER_CODE_LABELS[label]).push src
|
290
|
+
end
|
291
|
+
|
292
|
+
end
|
293
|
+
|
294
|
+
|
295
|
+
class GrammarFileScanner
|
296
|
+
|
297
|
+
def initialize(str, filename = '-')
|
298
|
+
@lines = str.split(/\n|\r\n|\r/)
|
299
|
+
@filename = filename
|
300
|
+
@lineno = -1
|
301
|
+
@line_head = true
|
302
|
+
@in_rule_blk = false
|
303
|
+
@in_conv_blk = false
|
304
|
+
@in_block = nil
|
305
|
+
@epilogue = ''
|
306
|
+
@debug = false
|
307
|
+
next_line
|
308
|
+
end
|
309
|
+
|
310
|
+
attr_reader :epilogue
|
311
|
+
|
312
|
+
def lineno
|
313
|
+
@lineno + 1
|
314
|
+
end
|
315
|
+
|
316
|
+
attr_accessor :debug
|
317
|
+
|
318
|
+
def yylex(&block)
|
319
|
+
unless @debug
|
320
|
+
yylex0(&block)
|
321
|
+
else
|
322
|
+
yylex0 do |sym, tok|
|
323
|
+
$stderr.printf "%7d %-10s %s\n", lineno(), sym.inspect, tok.inspect
|
324
|
+
yield [sym, tok]
|
325
|
+
end
|
326
|
+
end
|
327
|
+
end
|
328
|
+
|
329
|
+
private
|
330
|
+
|
331
|
+
def yylex0
|
332
|
+
begin
|
333
|
+
until @line.empty?
|
334
|
+
@line.sub!(/\A\s+/, '')
|
335
|
+
if /\A\#/ =~ @line
|
336
|
+
break
|
337
|
+
elsif /\A\/\*/ =~ @line
|
338
|
+
skip_comment
|
339
|
+
elsif s = reads(/\A[a-zA-Z_]\w*/)
|
340
|
+
yield [atom_symbol(s), s.intern]
|
341
|
+
elsif s = reads(/\A\d+/)
|
342
|
+
yield [:DIGIT, s.to_i]
|
343
|
+
elsif ch = reads(/\A./)
|
344
|
+
case ch
|
345
|
+
when '"', "'"
|
346
|
+
yield [:STRING, eval(scan_quoted(ch))]
|
347
|
+
when '{'
|
348
|
+
lineno = lineno()
|
349
|
+
yield [:ACTION, SourceText.new(scan_action(), @filename, lineno)]
|
350
|
+
else
|
351
|
+
if ch == '|'
|
352
|
+
@line_head = false
|
353
|
+
end
|
354
|
+
yield [ch, ch]
|
355
|
+
end
|
356
|
+
else
|
357
|
+
end
|
358
|
+
end
|
359
|
+
end while next_line()
|
360
|
+
yield nil
|
361
|
+
end
|
362
|
+
|
363
|
+
def next_line
|
364
|
+
@lineno += 1
|
365
|
+
@line = @lines[@lineno]
|
366
|
+
if not @line or /\A----/ =~ @line
|
367
|
+
@epilogue = @lines.join("\n")
|
368
|
+
@lines.clear
|
369
|
+
@line = nil
|
370
|
+
if @in_block
|
371
|
+
@lineno -= 1
|
372
|
+
scan_error! sprintf('unterminated %s', @in_block)
|
373
|
+
end
|
374
|
+
false
|
375
|
+
else
|
376
|
+
@line.sub!(/(?:\n|\r\n|\r)\z/, '')
|
377
|
+
@line_head = true
|
378
|
+
true
|
379
|
+
end
|
380
|
+
end
|
381
|
+
|
382
|
+
ReservedWord = {
|
383
|
+
'right' => :RIGHT,
|
384
|
+
'left' => :LEFT,
|
385
|
+
'nonassoc' => :NONASSOC,
|
386
|
+
'preclow' => :PRECLOW,
|
387
|
+
'prechigh' => :PRECHIGH,
|
388
|
+
'token' => :TOKEN,
|
389
|
+
'convert' => :CONV,
|
390
|
+
'options' => :OPTION,
|
391
|
+
'start' => :START,
|
392
|
+
'expect' => :EXPECT,
|
393
|
+
'class' => :CLASS,
|
394
|
+
'rule' => :RULE,
|
395
|
+
'end' => :END
|
396
|
+
}
|
397
|
+
|
398
|
+
def atom_symbol(token)
|
399
|
+
if token == 'end'
|
400
|
+
symbol = :END
|
401
|
+
@in_conv_blk = false
|
402
|
+
@in_rule_blk = false
|
403
|
+
else
|
404
|
+
if @line_head and not @in_conv_blk and not @in_rule_blk
|
405
|
+
symbol = ReservedWord[token] || :SYMBOL
|
406
|
+
else
|
407
|
+
symbol = :SYMBOL
|
408
|
+
end
|
409
|
+
case symbol
|
410
|
+
when :RULE then @in_rule_blk = true
|
411
|
+
when :CONV then @in_conv_blk = true
|
412
|
+
end
|
413
|
+
end
|
414
|
+
@line_head = false
|
415
|
+
symbol
|
416
|
+
end
|
417
|
+
|
418
|
+
def skip_comment
|
419
|
+
@in_block = 'comment'
|
420
|
+
until m = /\*\//.match(@line)
|
421
|
+
next_line
|
422
|
+
end
|
423
|
+
@line = m.post_match
|
424
|
+
@in_block = nil
|
425
|
+
end
|
426
|
+
|
427
|
+
$raccs_print_type = false
|
428
|
+
|
429
|
+
def scan_action
|
430
|
+
buf = ''
|
431
|
+
nest = 1
|
432
|
+
pre = nil
|
433
|
+
@in_block = 'action'
|
434
|
+
begin
|
435
|
+
pre = nil
|
436
|
+
if s = reads(/\A\s+/)
|
437
|
+
# does not set 'pre'
|
438
|
+
buf << s
|
439
|
+
end
|
440
|
+
until @line.empty?
|
441
|
+
if s = reads(/\A[^'"`{}%#\/\$]+/)
|
442
|
+
buf << (pre = s)
|
443
|
+
next
|
444
|
+
end
|
445
|
+
case ch = read(1)
|
446
|
+
when '{'
|
447
|
+
nest += 1
|
448
|
+
buf << (pre = ch)
|
449
|
+
when '}'
|
450
|
+
nest -= 1
|
451
|
+
if nest == 0
|
452
|
+
@in_block = nil
|
453
|
+
return buf
|
454
|
+
end
|
455
|
+
buf << (pre = ch)
|
456
|
+
when '#' # comment
|
457
|
+
buf << ch << @line
|
458
|
+
break
|
459
|
+
when "'", '"', '`'
|
460
|
+
buf << (pre = scan_quoted(ch))
|
461
|
+
when '%'
|
462
|
+
if literal_head? pre, @line
|
463
|
+
# % string, regexp, array
|
464
|
+
buf << ch
|
465
|
+
case ch = read(1)
|
466
|
+
when /[qQx]/n
|
467
|
+
buf << ch << (pre = scan_quoted(read(1), '%string'))
|
468
|
+
when /wW/n
|
469
|
+
buf << ch << (pre = scan_quoted(read(1), '%array'))
|
470
|
+
when /s/n
|
471
|
+
buf << ch << (pre = scan_quoted(read(1), '%symbol'))
|
472
|
+
when /r/n
|
473
|
+
buf << ch << (pre = scan_quoted(read(1), '%regexp'))
|
474
|
+
when /[a-zA-Z0-9= ]/n # does not include "_"
|
475
|
+
scan_error! "unknown type of % literal '%#{ch}'"
|
476
|
+
else
|
477
|
+
buf << (pre = scan_quoted(ch, '%string'))
|
478
|
+
end
|
479
|
+
else
|
480
|
+
# operator
|
481
|
+
buf << '||op->' if $raccs_print_type
|
482
|
+
buf << (pre = ch)
|
483
|
+
end
|
484
|
+
when '/'
|
485
|
+
if literal_head? pre, @line
|
486
|
+
# regexp
|
487
|
+
buf << (pre = scan_quoted(ch, 'regexp'))
|
488
|
+
else
|
489
|
+
# operator
|
490
|
+
buf << '||op->' if $raccs_print_type
|
491
|
+
buf << (pre = ch)
|
492
|
+
end
|
493
|
+
when '$' # gvar
|
494
|
+
buf << ch << (pre = read(1))
|
495
|
+
else
|
496
|
+
raise 'racc: fatal: must not happen'
|
497
|
+
end
|
498
|
+
end
|
499
|
+
buf << "\n"
|
500
|
+
end while next_line()
|
501
|
+
raise 'racc: fatal: scan finished before parser finished'
|
502
|
+
end
|
503
|
+
|
504
|
+
def literal_head?(pre, post)
|
505
|
+
(!pre || /[a-zA-Z_0-9]/n !~ pre[-1,1]) &&
|
506
|
+
!post.empty? && /\A[\s\=]/n !~ post
|
507
|
+
end
|
508
|
+
|
509
|
+
def read(len)
|
510
|
+
s = @line[0, len]
|
511
|
+
@line = @line[len .. -1]
|
512
|
+
s
|
513
|
+
end
|
514
|
+
|
515
|
+
def reads(re)
|
516
|
+
m = re.match(@line) or return nil
|
517
|
+
@line = m.post_match
|
518
|
+
m[0]
|
519
|
+
end
|
520
|
+
|
521
|
+
def scan_quoted(left, tag = 'string')
|
522
|
+
buf = left.dup
|
523
|
+
buf = "||#{tag}->" + buf if $raccs_print_type
|
524
|
+
re = get_quoted_re(left)
|
525
|
+
sv, @in_block = @in_block, tag
|
526
|
+
begin
|
527
|
+
if s = reads(re)
|
528
|
+
buf << s
|
529
|
+
break
|
530
|
+
else
|
531
|
+
buf << @line
|
532
|
+
end
|
533
|
+
end while next_line()
|
534
|
+
@in_block = sv
|
535
|
+
buf << "<-#{tag}||" if $raccs_print_type
|
536
|
+
buf
|
537
|
+
end
|
538
|
+
|
539
|
+
LEFT_TO_RIGHT = {
|
540
|
+
'(' => ')',
|
541
|
+
'{' => '}',
|
542
|
+
'[' => ']',
|
543
|
+
'<' => '>'
|
544
|
+
}
|
545
|
+
|
546
|
+
CACHE = {}
|
547
|
+
|
548
|
+
def get_quoted_re(left)
|
549
|
+
term = Regexp.quote(LEFT_TO_RIGHT[left] || left)
|
550
|
+
CACHE[left] ||= /\A[^#{term}\\]*(?:\\.[^\\#{term}]*)*#{term}/
|
551
|
+
end
|
552
|
+
|
553
|
+
def scan_error!(msg)
|
554
|
+
raise CompileError, "#{lineno()}: #{msg}"
|
555
|
+
end
|
556
|
+
|
557
|
+
end
|
558
|
+
|
559
|
+
end # module Racc
|