rexical 1.0.3
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of rexical might be problematic. Click here for more details.
- data/CHANGELOG.rdoc +13 -0
- data/DOCUMENTATION.en.rdoc +215 -0
- data/DOCUMENTATION.ja.rdoc +205 -0
- data/Manifest.txt +37 -0
- data/README.ja +73 -0
- data/README.rdoc +52 -0
- data/Rakefile +28 -0
- data/bin/rex +18 -0
- data/lib/rexical.rb +3 -0
- data/lib/rexical/generator.rb +523 -0
- data/lib/rexical/info.rb +16 -0
- data/lib/rexical/rexcmd.rb +136 -0
- data/sample/a.cmd +1 -0
- data/sample/b.cmd +1 -0
- data/sample/c.cmd +4 -0
- data/sample/calc3.racc +47 -0
- data/sample/calc3.rex +15 -0
- data/sample/calc3.rex.rb +94 -0
- data/sample/calc3.tab.rb +188 -0
- data/sample/error1.rex +15 -0
- data/sample/error2.rex +15 -0
- data/sample/sample.html +32 -0
- data/sample/sample.rex +15 -0
- data/sample/sample.rex.rb +100 -0
- data/sample/sample.xhtml +32 -0
- data/sample/sample1.c +9 -0
- data/sample/sample1.rex +43 -0
- data/sample/sample2.bas +4 -0
- data/sample/sample2.rex +33 -0
- data/sample/simple.html +7 -0
- data/sample/simple.xhtml +10 -0
- data/sample/xhtmlparser.racc +66 -0
- data/sample/xhtmlparser.rex +72 -0
- data/test/assets/test.rex +12 -0
- data/test/rex-20060125.rb +152 -0
- data/test/rex-20060511.rb +143 -0
- data/test/test_generator.rb +184 -0
- metadata +109 -0
data/README.ja
ADDED
@@ -0,0 +1,73 @@
|
|
1
|
+
Rexical README
|
2
|
+
===========
|
3
|
+
|
4
|
+
Rexical �� Ruby �Τ���Υ�����ʥ����ͥ졼���Ǥ���
|
5
|
+
lex �� Ruby �Ǥ��������ޤ���
|
6
|
+
Racc �ȤȤ�˻Ȥ��褦���߷פ���Ƥ��ޤ���
|
7
|
+
|
8
|
+
|
9
|
+
ɬ�״Ķ�
|
10
|
+
--------
|
11
|
+
|
12
|
+
* ruby 1.8 �ʹ�
|
13
|
+
|
14
|
+
|
15
|
+
���ȡ���
|
16
|
+
------------
|
17
|
+
|
18
|
+
�ѥå������Υȥåץǥ��쥯�ȥ�Ǽ��Τ褦�����Ϥ��Ƥ���������
|
19
|
+
($ ���̾�桼����# �ϥ롼�ȤΥץ���ץȤǤ�)
|
20
|
+
|
21
|
+
$ ruby setup.rb config
|
22
|
+
$ ruby setup.rb setup
|
23
|
+
($ su)
|
24
|
+
# ruby setup.rb install
|
25
|
+
|
26
|
+
������̾�Υѥ��� Racc �����ȡ��뤵��ޤ�����ʬ�ι���
|
27
|
+
�ʥǥ��쥯�ȥ�˥��ȡ��뤷�����Ȥ��ϡ�setup.rb config ��
|
28
|
+
�Ƽ索�ץ�����Ĥ��Ƽ¹Ԥ��Ƥ������������ץ����Υꥹ�Ȥ�
|
29
|
+
|
30
|
+
$ ruby setup.rb --help
|
31
|
+
|
32
|
+
�Ǹ����ޤ���
|
33
|
+
|
34
|
+
|
35
|
+
�ƥ���
|
36
|
+
------
|
37
|
+
|
38
|
+
sample/ �ʲ��ˤ����Ĥ� Rexical ��ʸˡ�ե�����Υ���ץ뤬�Ѱ�
|
39
|
+
���Ƥ���ޤ����ʲ���¹Ԥ��Ƥ���������
|
40
|
+
|
41
|
+
$ rex sample1.rex --stub
|
42
|
+
$ ruby sample1.rex.rb sample1.c
|
43
|
+
|
44
|
+
$ rex sample2.rex --stub
|
45
|
+
$ ruby sample2.rex.rb sample2.bas
|
46
|
+
|
47
|
+
$ racc calc3.racc
|
48
|
+
$ rex calc3.rex
|
49
|
+
$ ruby calc3.tab.rb
|
50
|
+
|
51
|
+
Rexical �ξܤ���ʸˡ�� doc/ �ǥ��쥯�ȥ�ʲ��Ƥ���������
|
52
|
+
�ޤ�������� sample/ �ǥ��쥯�ȥ�ʲ��Ƥ���������
|
53
|
+
|
54
|
+
|
55
|
+
�饤����
|
56
|
+
----------
|
57
|
+
|
58
|
+
�饤���� GNU Lesser General Public License (LGPL) version 2
|
59
|
+
�Ǥ����������桼��������§�ե�����䡢Racc ������������������
|
60
|
+
Ruby ������ץȤϤ����оݳ��Ǥ��������ʥ饤�������ۤ��Ƥ���������
|
61
|
+
|
62
|
+
|
63
|
+
�Х��ʤ�
|
64
|
+
--------
|
65
|
+
|
66
|
+
Rexical ��ȤäƤ��ƥХ��餷�����ݤ����������顢�����Υ��ɥ쥹�ޤ�
|
67
|
+
����������
|
68
|
+
���ΤȤ��ϤǤ�������Х���Ƹ��Ǥ���ʸˡ�ե�������դ��Ƥ���������
|
69
|
+
|
70
|
+
|
71
|
+
ARIMA Yasuhiro
|
72
|
+
arima.yasuhiro@nifty.com
|
73
|
+
http://raa.ruby-lang.org/project/rex/
|
data/README.rdoc
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
= Rexical
|
2
|
+
|
3
|
+
* http://github.com/tenderlove/rexical/tree/master
|
4
|
+
|
5
|
+
== DESCRIPTION
|
6
|
+
|
7
|
+
Rexical is a lexical scanner generator.
|
8
|
+
It is written in Ruby itself, and generates Ruby program.
|
9
|
+
It is designed for use with Racc.
|
10
|
+
|
11
|
+
|
12
|
+
== SYNOPSIS
|
13
|
+
|
14
|
+
Here is a sample lexical definition:
|
15
|
+
|
16
|
+
class Sample
|
17
|
+
macro
|
18
|
+
BLANK [\ \t]+
|
19
|
+
rule
|
20
|
+
BLANK # no action
|
21
|
+
\d+ { [:digit, text.to_i] }
|
22
|
+
\w+ { [:word, text] }
|
23
|
+
\n
|
24
|
+
. { [text, text] }
|
25
|
+
end
|
26
|
+
|
27
|
+
Here is the command line usage:
|
28
|
+
|
29
|
+
$ rex sample1.rex --stub
|
30
|
+
$ ruby sample1.rex.rb sample1.c
|
31
|
+
|
32
|
+
$ rex sample2.rex --stub
|
33
|
+
$ ruby sample2.rex.rb sample2.bas
|
34
|
+
|
35
|
+
$ racc calc3.racc
|
36
|
+
$ rex calc3.rex
|
37
|
+
$ ruby calc3.tab.rb
|
38
|
+
|
39
|
+
== REQUIREMENTS
|
40
|
+
|
41
|
+
* ruby version 1.8.x or later.
|
42
|
+
|
43
|
+
== INSTALL
|
44
|
+
|
45
|
+
* sudo gem install rexical
|
46
|
+
|
47
|
+
== LICENSE
|
48
|
+
|
49
|
+
Rexical is distributed under the terms of the GNU Lesser General
|
50
|
+
Public License version 2. Note that you do NOT need to follow
|
51
|
+
LGPL for your own parser (Rexical outputs). You can provide those
|
52
|
+
files under any licenses you want.
|
data/Rakefile
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
# -*- ruby -*-
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'hoe'
|
5
|
+
|
6
|
+
$LOAD_PATH << File.join(File.dirname(__FILE__), 'lib')
|
7
|
+
require 'rexical'
|
8
|
+
|
9
|
+
HOE = Hoe.new('rexical', Rexical::VERSION) do |p|
|
10
|
+
p.readme_file = 'README.rdoc'
|
11
|
+
p.history_file = 'CHANGELOG.rdoc'
|
12
|
+
p.developer('Aaron Patterson', 'aaronp@rubyforge.org')
|
13
|
+
p.rubyforge_name = 'ruby-rex'
|
14
|
+
p.extra_rdoc_files = FileList['*.rdoc']
|
15
|
+
end
|
16
|
+
|
17
|
+
namespace :gem do
|
18
|
+
namespace :spec do
|
19
|
+
task :dev do
|
20
|
+
File.open("#{HOE.name}.gemspec", 'w') do |f|
|
21
|
+
HOE.spec.version = "#{HOE.version}.#{Time.now.strftime("%Y%m%d%H%M%S")}"
|
22
|
+
f.write(HOE.spec.to_ruby)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
# vim: syntax=Ruby
|
data/bin/rex
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
#
|
3
|
+
# rex
|
4
|
+
#
|
5
|
+
# Copyright (c) 2005-2006 ARIMA Yasuhiro <arima.yasuhiro@nifty.com>
|
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 License version 2.1.
|
10
|
+
# For details of LGPL, see the file "COPYING".
|
11
|
+
#
|
12
|
+
|
13
|
+
## ---------------------------------------------------------------------
|
14
|
+
|
15
|
+
require 'rubygems'
|
16
|
+
require 'rexical'
|
17
|
+
|
18
|
+
Rexical::Cmd.new.run
|
data/lib/rexical.rb
ADDED
@@ -0,0 +1,523 @@
|
|
1
|
+
#
|
2
|
+
# generator.rb
|
3
|
+
#
|
4
|
+
# Copyright (c) 2005-2006 ARIMA Yasuhiro <arima.yasuhiro@nifty.com>
|
5
|
+
#
|
6
|
+
# This program is free software.
|
7
|
+
# You can distribute/modify this program under the terms of
|
8
|
+
# the GNU Lesser General Public License version 2 or later.
|
9
|
+
#
|
10
|
+
|
11
|
+
require 'strscan'
|
12
|
+
module Rexical
|
13
|
+
|
14
|
+
## ---------------------------------------------------------------------
|
15
|
+
class ParseError < StandardError ; end
|
16
|
+
|
17
|
+
## ---------------------------------------------------------------------
|
18
|
+
class Generator
|
19
|
+
|
20
|
+
## ---------------------------------------------------------------------
|
21
|
+
attr_accessor :grammar_file
|
22
|
+
attr_accessor :grammar_lines
|
23
|
+
attr_accessor :scanner_file
|
24
|
+
attr_accessor :module_name
|
25
|
+
attr_accessor :class_name
|
26
|
+
attr_accessor :lineno
|
27
|
+
attr_accessor :rules
|
28
|
+
attr_accessor :exclusive_states
|
29
|
+
attr_accessor :ignorecase
|
30
|
+
attr_accessor :independent
|
31
|
+
attr_accessor :debug
|
32
|
+
|
33
|
+
## ---------------------------------------------------------------------
|
34
|
+
def initialize(opts)
|
35
|
+
@lineno = 0
|
36
|
+
@macro = {}
|
37
|
+
@rules = []
|
38
|
+
@exclusive_states = [nil]
|
39
|
+
@grammar_lines = nil
|
40
|
+
@scanner_header = ""
|
41
|
+
@scanner_footer = ""
|
42
|
+
@scanner_inner = ""
|
43
|
+
@opt = opts
|
44
|
+
end
|
45
|
+
|
46
|
+
## ---------------------------------------------------------------------
|
47
|
+
def add_header( st )
|
48
|
+
@scanner_header += "#{st}\n"
|
49
|
+
end
|
50
|
+
|
51
|
+
## ---------------------------------------------------------------------
|
52
|
+
def add_footer( st )
|
53
|
+
@scanner_footer += "#{st}\n"
|
54
|
+
end
|
55
|
+
|
56
|
+
## ---------------------------------------------------------------------
|
57
|
+
def add_inner( st )
|
58
|
+
@scanner_inner += "#{st}\n"
|
59
|
+
end
|
60
|
+
|
61
|
+
## ---------------------------------------------------------------------
|
62
|
+
def add_option( st )
|
63
|
+
opts = st.split
|
64
|
+
opts.each do |opt|
|
65
|
+
case opt
|
66
|
+
when /ignorecase/i
|
67
|
+
@opt['--ignorecase'] = true
|
68
|
+
when /stub/i
|
69
|
+
@opt['--stub'] = true
|
70
|
+
when /independent/i
|
71
|
+
@opt['--independent'] = true
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
## ---------------------------------------------------------------------
|
77
|
+
def add_macro( st )
|
78
|
+
ss = StringScanner.new(st)
|
79
|
+
ss.scan(/\s+/)
|
80
|
+
key = ss.scan(/\S+/)
|
81
|
+
ss.scan(/\s+/)
|
82
|
+
st = ss.post_match
|
83
|
+
len = st.size
|
84
|
+
ndx = 0
|
85
|
+
while ndx <= len
|
86
|
+
c = st[ndx,1]
|
87
|
+
ndx += 1
|
88
|
+
case c
|
89
|
+
when '\\'
|
90
|
+
ndx += 1
|
91
|
+
next
|
92
|
+
when '#', ' '
|
93
|
+
ndx -= 1
|
94
|
+
break
|
95
|
+
end
|
96
|
+
end
|
97
|
+
expr = st[0,ndx]
|
98
|
+
key = '{' + key + '}'
|
99
|
+
@macro.each_pair do |k, e|
|
100
|
+
expr.gsub!(k) { |m| e }
|
101
|
+
end
|
102
|
+
@macro[key] = expr
|
103
|
+
rescue
|
104
|
+
raise ParseError, "parse error in add_macro:'#{st}'"
|
105
|
+
end
|
106
|
+
|
107
|
+
## ---------------------------------------------------------------------
|
108
|
+
def add_rule( rule_state, rule_expr, rule_action=nil )
|
109
|
+
st = rule_expr.dup
|
110
|
+
@macro.each_pair do |k, e|
|
111
|
+
rule_expr.gsub!(k) { |m| e }
|
112
|
+
end
|
113
|
+
if rule_state.to_s[1,1] =~ /[A-Z]/
|
114
|
+
@exclusive_states << rule_state unless @exclusive_states.include?(rule_state)
|
115
|
+
exclusive_state = rule_state
|
116
|
+
start_state = nil
|
117
|
+
else
|
118
|
+
exclusive_state = nil
|
119
|
+
start_state = rule_state
|
120
|
+
end
|
121
|
+
rule = [exclusive_state, start_state, rule_expr, rule_action]
|
122
|
+
@rules << rule
|
123
|
+
rescue
|
124
|
+
raise ParseError, "parse error in add_rule:'#{st}'"
|
125
|
+
end
|
126
|
+
|
127
|
+
def read_grammar
|
128
|
+
@grammar_lines = StringScanner.new File.read(grammar_file)
|
129
|
+
end
|
130
|
+
|
131
|
+
def next_line
|
132
|
+
@lineno += 1
|
133
|
+
@grammar_lines.scan_until(/\n/).chomp
|
134
|
+
rescue
|
135
|
+
nil
|
136
|
+
end
|
137
|
+
|
138
|
+
def parse
|
139
|
+
state1 = :HEAD
|
140
|
+
state2 = nil
|
141
|
+
state3 = nil
|
142
|
+
lastmodes = []
|
143
|
+
while st = next_line
|
144
|
+
case state1
|
145
|
+
when :FOOT
|
146
|
+
add_footer st
|
147
|
+
|
148
|
+
when :HEAD
|
149
|
+
ss = StringScanner.new(st)
|
150
|
+
if ss.scan(/class/)
|
151
|
+
state1 = :CLASS
|
152
|
+
st = ss.post_match.strip
|
153
|
+
if st =~ /^(\S+)::(\S+)/
|
154
|
+
@module_name = $1
|
155
|
+
@class_name = $2
|
156
|
+
else
|
157
|
+
@module_name = nil
|
158
|
+
@class_name = st
|
159
|
+
end
|
160
|
+
else
|
161
|
+
add_header st
|
162
|
+
end
|
163
|
+
|
164
|
+
when :CLASS
|
165
|
+
s = st.strip
|
166
|
+
next if s.size == 0 or s[0,1] == '#'
|
167
|
+
|
168
|
+
ss = StringScanner.new(st)
|
169
|
+
if ss.scan(/option.*$/)
|
170
|
+
state2 = :OPTION
|
171
|
+
next
|
172
|
+
end
|
173
|
+
if ss.scan(/inner.*$/)
|
174
|
+
state2 = :INNER
|
175
|
+
next
|
176
|
+
end
|
177
|
+
if ss.scan(/macro.*$/)
|
178
|
+
state2 = :MACRO
|
179
|
+
next
|
180
|
+
end
|
181
|
+
if ss.scan(/rule.*$/)
|
182
|
+
state2 = :RULE
|
183
|
+
next
|
184
|
+
end
|
185
|
+
if ss.scan(/end.*$/)
|
186
|
+
state1 = :FOOT
|
187
|
+
next
|
188
|
+
end
|
189
|
+
|
190
|
+
case state2
|
191
|
+
when :OPTION
|
192
|
+
add_option st
|
193
|
+
|
194
|
+
when :INNER
|
195
|
+
add_inner st
|
196
|
+
|
197
|
+
when :MACRO
|
198
|
+
add_macro st
|
199
|
+
|
200
|
+
when :RULE
|
201
|
+
case state3
|
202
|
+
when nil
|
203
|
+
rule_state, rule_expr, rule_action = parse_rule(st)
|
204
|
+
if rule_action =~ /\s*\{/
|
205
|
+
lastmodes = parse_action(rule_action, lastmodes)
|
206
|
+
if lastmodes.empty?
|
207
|
+
add_rule rule_state, rule_expr, rule_action
|
208
|
+
else
|
209
|
+
state3 = :CONT
|
210
|
+
rule_action += "\n"
|
211
|
+
end
|
212
|
+
else
|
213
|
+
add_rule rule_state, rule_expr
|
214
|
+
end
|
215
|
+
|
216
|
+
when :CONT
|
217
|
+
rule_action += "#{st}\n"
|
218
|
+
lastmodes = parse_action(st, lastmodes)
|
219
|
+
if lastmodes.empty?
|
220
|
+
state3 = nil
|
221
|
+
add_rule rule_state, rule_expr, rule_action
|
222
|
+
else
|
223
|
+
end
|
224
|
+
|
225
|
+
end # case state3
|
226
|
+
|
227
|
+
end # case state2
|
228
|
+
|
229
|
+
end # case state1
|
230
|
+
|
231
|
+
end # while
|
232
|
+
|
233
|
+
end
|
234
|
+
|
235
|
+
## ---------------------------------------------------------------------
|
236
|
+
def parse_rule(st)
|
237
|
+
st.strip!
|
238
|
+
return if st.size == 0 or st[0,1] == '#'
|
239
|
+
ss = StringScanner.new(st)
|
240
|
+
ss.scan(/\s+/)
|
241
|
+
rule_state = ss.scan(/\:\S+/)
|
242
|
+
ss.scan(/\s+/)
|
243
|
+
rule_expr = ss.scan(/\S+/)
|
244
|
+
ss.scan(/\s+/)
|
245
|
+
[rule_state, rule_expr, ss.post_match]
|
246
|
+
end
|
247
|
+
|
248
|
+
## ---------------------------------------------------------------------
|
249
|
+
def parse_action(st, lastmodes=[])
|
250
|
+
modes = lastmodes
|
251
|
+
mode = lastmodes[-1]
|
252
|
+
ss = StringScanner.new(st)
|
253
|
+
until ss.eos?
|
254
|
+
c = ss.scan(/./)
|
255
|
+
case c
|
256
|
+
when '#'
|
257
|
+
if (mode == :brace) or (mode == nil)
|
258
|
+
#p [c, mode, modes]
|
259
|
+
return modes
|
260
|
+
end
|
261
|
+
when '{'
|
262
|
+
if (mode == :brace) or (mode == nil)
|
263
|
+
mode = :brace
|
264
|
+
modes.push mode
|
265
|
+
end
|
266
|
+
when '}'
|
267
|
+
if (mode == :brace)
|
268
|
+
modes.pop
|
269
|
+
mode = modes[0]
|
270
|
+
end
|
271
|
+
when "'"
|
272
|
+
if (mode == :brace)
|
273
|
+
mode = :quote
|
274
|
+
modes.push mode
|
275
|
+
elsif (mode == :quote)
|
276
|
+
modes.pop
|
277
|
+
mode = modes[0]
|
278
|
+
end
|
279
|
+
when '"'
|
280
|
+
if (mode == :brace)
|
281
|
+
mode = :doublequote
|
282
|
+
modes.push mode
|
283
|
+
elsif (mode == :doublequote)
|
284
|
+
modes.pop
|
285
|
+
mode = modes[0]
|
286
|
+
end
|
287
|
+
when '`'
|
288
|
+
if (mode == :brace)
|
289
|
+
mode = :backquote
|
290
|
+
modes.push mode
|
291
|
+
elsif (mode == :backquote)
|
292
|
+
modes.pop
|
293
|
+
mode = modes[0]
|
294
|
+
end
|
295
|
+
end
|
296
|
+
end
|
297
|
+
#p [c, mode, modes]
|
298
|
+
return modes
|
299
|
+
end
|
300
|
+
|
301
|
+
## ---------------------------------------------------------------------
|
302
|
+
|
303
|
+
REX_HEADER = <<-REX_EOT
|
304
|
+
#--
|
305
|
+
# DO NOT MODIFY!!!!
|
306
|
+
# This file is automatically generated by rex %s
|
307
|
+
# from lexical definition file "%s".
|
308
|
+
#++
|
309
|
+
|
310
|
+
REX_EOT
|
311
|
+
|
312
|
+
REX_UTIL = <<-REX_EOT
|
313
|
+
require 'strscan'
|
314
|
+
|
315
|
+
class ScanError < StandardError ; end
|
316
|
+
|
317
|
+
attr_reader :lineno
|
318
|
+
attr_reader :filename
|
319
|
+
|
320
|
+
def scan_setup ; end
|
321
|
+
|
322
|
+
def action &block
|
323
|
+
yield
|
324
|
+
end
|
325
|
+
|
326
|
+
def scan_str( str )
|
327
|
+
scan_evaluate str
|
328
|
+
do_parse
|
329
|
+
end
|
330
|
+
|
331
|
+
def load_file( filename )
|
332
|
+
@filename = filename
|
333
|
+
open(filename, "r") do |f|
|
334
|
+
scan_evaluate f.read
|
335
|
+
end
|
336
|
+
end
|
337
|
+
|
338
|
+
def scan_file( filename )
|
339
|
+
load_file filename
|
340
|
+
do_parse
|
341
|
+
end
|
342
|
+
|
343
|
+
REX_EOT
|
344
|
+
|
345
|
+
REX_NEXTTOKEN_DEBUG = <<-REX_EOT
|
346
|
+
def next_token
|
347
|
+
p token = @rex_tokens.shift
|
348
|
+
token
|
349
|
+
end
|
350
|
+
REX_EOT
|
351
|
+
|
352
|
+
REX_NEXTTOKEN = <<-REX_EOT
|
353
|
+
def next_token
|
354
|
+
@rex_tokens.shift
|
355
|
+
end
|
356
|
+
REX_EOT
|
357
|
+
|
358
|
+
REX_STUB = <<-REX_EOT
|
359
|
+
|
360
|
+
if __FILE__ == $0
|
361
|
+
exit if ARGV.size != 1
|
362
|
+
filename = ARGV.shift
|
363
|
+
rex = %s.new
|
364
|
+
begin
|
365
|
+
rex.load_file filename
|
366
|
+
while token = rex.next_token
|
367
|
+
p token
|
368
|
+
end
|
369
|
+
rescue
|
370
|
+
$stderr.printf %s, rex.filename, rex.lineno, $!.message
|
371
|
+
end
|
372
|
+
end
|
373
|
+
REX_EOT
|
374
|
+
|
375
|
+
## ---------------------------------------------------------------------
|
376
|
+
|
377
|
+
|
378
|
+
def scanner_io
|
379
|
+
unless scanner_file = @opt['--output-file']
|
380
|
+
scanner_file = grammar_file + ".rb"
|
381
|
+
end
|
382
|
+
f = File.open(scanner_file, 'w')
|
383
|
+
end
|
384
|
+
private :scanner_io
|
385
|
+
|
386
|
+
def write_scanner f = scanner_io
|
387
|
+
## scan flag
|
388
|
+
flag = ""
|
389
|
+
flag += "i" if @opt['--ignorecase']
|
390
|
+
## header
|
391
|
+
f.printf REX_HEADER, Rexical::VERSION, grammar_file
|
392
|
+
|
393
|
+
unless @opt['--independent']
|
394
|
+
f.printf "require 'racc/parser'\n"
|
395
|
+
end
|
396
|
+
|
397
|
+
@scanner_header.each_line do |s|
|
398
|
+
f.print s
|
399
|
+
end
|
400
|
+
if @module_name
|
401
|
+
f.puts "module #{@module_name}"
|
402
|
+
end
|
403
|
+
if @opt['--independent']
|
404
|
+
f.puts "class #{@class_name}"
|
405
|
+
else
|
406
|
+
f.puts "class #{@class_name} < Racc::Parser"
|
407
|
+
end
|
408
|
+
|
409
|
+
## utility method
|
410
|
+
f.print REX_UTIL
|
411
|
+
|
412
|
+
if @opt['--debug']
|
413
|
+
f.print REX_NEXTTOKEN_DEBUG
|
414
|
+
else
|
415
|
+
f.print REX_NEXTTOKEN
|
416
|
+
end
|
417
|
+
|
418
|
+
## scanner method
|
419
|
+
|
420
|
+
f.print <<-REX_EOT
|
421
|
+
|
422
|
+
def scan_evaluate( str )
|
423
|
+
scan_setup
|
424
|
+
@rex_tokens = []
|
425
|
+
@lineno = 1
|
426
|
+
ss = StringScanner.new(str)
|
427
|
+
state = nil
|
428
|
+
until ss.eos?
|
429
|
+
text = ss.peek(1)
|
430
|
+
@lineno += 1 if text == "\\n"
|
431
|
+
case state
|
432
|
+
REX_EOT
|
433
|
+
|
434
|
+
exclusive_states.each do |es|
|
435
|
+
f.printf <<-REX_EOT
|
436
|
+
when #{es ? es.to_s : "nil"}
|
437
|
+
case
|
438
|
+
REX_EOT
|
439
|
+
rules.each do |rule|
|
440
|
+
exclusive_state, start_state, rule_expr, rule_action = *rule
|
441
|
+
if es == exclusive_state
|
442
|
+
|
443
|
+
if rule_action
|
444
|
+
if start_state
|
445
|
+
f.print <<-REX_EOT
|
446
|
+
when (state == #{start_state}) and (text = ss.scan(/#{rule_expr}/#{flag}))
|
447
|
+
@rex_tokens.push action #{rule_action}
|
448
|
+
|
449
|
+
REX_EOT
|
450
|
+
else
|
451
|
+
f.print <<-REX_EOT
|
452
|
+
when (text = ss.scan(/#{rule_expr}/#{flag}))
|
453
|
+
@rex_tokens.push action #{rule_action}
|
454
|
+
|
455
|
+
REX_EOT
|
456
|
+
end
|
457
|
+
else
|
458
|
+
if start_state
|
459
|
+
f.print <<-REX_EOT
|
460
|
+
when (state == #{start_state}) and (text = ss.scan(/#{rule_expr}/#{flag}))
|
461
|
+
;
|
462
|
+
|
463
|
+
REX_EOT
|
464
|
+
else
|
465
|
+
f.print <<-REX_EOT
|
466
|
+
when (text = ss.scan(/#{rule_expr}/#{flag}))
|
467
|
+
;
|
468
|
+
|
469
|
+
REX_EOT
|
470
|
+
end
|
471
|
+
end
|
472
|
+
|
473
|
+
end
|
474
|
+
end
|
475
|
+
f.print <<-REX_EOT
|
476
|
+
else
|
477
|
+
text = ss.string[ss.pos .. -1]
|
478
|
+
raise ScanError, "can not match: '" + text + "'"
|
479
|
+
end # if
|
480
|
+
|
481
|
+
REX_EOT
|
482
|
+
end
|
483
|
+
f.print <<-REX_EOT
|
484
|
+
else
|
485
|
+
raise ScanError, "undefined state: '" + state.to_s + "'"
|
486
|
+
end # case state
|
487
|
+
end # until ss
|
488
|
+
end # def scan_evaluate
|
489
|
+
|
490
|
+
REX_EOT
|
491
|
+
|
492
|
+
## inner method
|
493
|
+
@scanner_inner.each_line do |s|
|
494
|
+
f.print s
|
495
|
+
end
|
496
|
+
f.puts "end # class"
|
497
|
+
f.puts "end # module" if @module_name
|
498
|
+
|
499
|
+
## footer
|
500
|
+
@scanner_footer.each_line do |s|
|
501
|
+
f.print s
|
502
|
+
end # case
|
503
|
+
|
504
|
+
## stub main
|
505
|
+
f.printf REX_STUB, @class_name, '"%s:%d:%s\n"' if @opt['--stub']
|
506
|
+
f.close
|
507
|
+
|
508
|
+
end ## def
|
509
|
+
end ## class
|
510
|
+
end ## module
|
511
|
+
|
512
|
+
|
513
|
+
## ---------------------------------------------------------------------
|
514
|
+
## test
|
515
|
+
|
516
|
+
if __FILE__ == $0
|
517
|
+
rex = Rexical::Generator.new(nil)
|
518
|
+
rex.grammar_file = "sample.rex"
|
519
|
+
rex.read_grammar
|
520
|
+
rex.parse
|
521
|
+
rex.write_scanner
|
522
|
+
end
|
523
|
+
|