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
@@ -0,0 +1,152 @@
|
|
1
|
+
#!C:/Program Files/ruby-1.8/bin/ruby
|
2
|
+
#
|
3
|
+
# rex
|
4
|
+
#
|
5
|
+
# Copyright (c) 2005 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
|
+
REX_OPTIONS = <<-EOT
|
16
|
+
|
17
|
+
o -o --output-file <outfile> file name of output [<filename>.rb]
|
18
|
+
o -s --stub - append stub code for debug
|
19
|
+
o -i --ignorecase - ignore char case
|
20
|
+
o -C --check-only - syntax check only
|
21
|
+
o - --independent - independent mode
|
22
|
+
o -d --debug - print debug information
|
23
|
+
o -h --help - print this message and quit
|
24
|
+
o - --version - print version and quit
|
25
|
+
o - --copyright - print copyright and quit
|
26
|
+
|
27
|
+
EOT
|
28
|
+
|
29
|
+
## ---------------------------------------------------------------------
|
30
|
+
|
31
|
+
require 'getoptlong'
|
32
|
+
require 'rex/generator'
|
33
|
+
require 'rex/info'
|
34
|
+
|
35
|
+
## ---------------------------------------------------------------------
|
36
|
+
|
37
|
+
=begin
|
38
|
+
class Rex
|
39
|
+
def initialize
|
40
|
+
end
|
41
|
+
end
|
42
|
+
=end
|
43
|
+
|
44
|
+
def main
|
45
|
+
$cmd = File.basename($0, ".rb")
|
46
|
+
opt = get_options
|
47
|
+
filename = ARGV[0]
|
48
|
+
|
49
|
+
rex = Rex::Generator.new(opt)
|
50
|
+
begin
|
51
|
+
rex.grammar_file = filename
|
52
|
+
rex.read_grammar
|
53
|
+
rex.parse
|
54
|
+
if opt['--check-only']
|
55
|
+
$stderr.puts "syntax ok"
|
56
|
+
return 0
|
57
|
+
end
|
58
|
+
rex.write_scanner
|
59
|
+
|
60
|
+
rescue Rex::ParseError, Errno::ENOENT
|
61
|
+
msg = $!.to_s
|
62
|
+
unless /\A\d/ === msg
|
63
|
+
msg[0,0] = ' '
|
64
|
+
end
|
65
|
+
$stderr.puts "#{$cmd}:#{rex.grammar_file}:#{rex.lineno}:#{msg}"
|
66
|
+
return 1
|
67
|
+
|
68
|
+
end
|
69
|
+
return 0
|
70
|
+
end
|
71
|
+
|
72
|
+
## ---------------------------------------------------------------------
|
73
|
+
def get_options
|
74
|
+
tmp = REX_OPTIONS.collect do |line|
|
75
|
+
next if /\A\s*\z/ === line
|
76
|
+
disp, sopt, lopt, takearg, doc = line.strip.split(/\s+/, 5)
|
77
|
+
a = []
|
78
|
+
a.push lopt unless lopt == '-'
|
79
|
+
a.push sopt unless sopt == '-'
|
80
|
+
a.push takearg == '-' ?
|
81
|
+
GetoptLong::NO_ARGUMENT : GetoptLong::REQUIRED_ARGUMENT
|
82
|
+
a
|
83
|
+
end
|
84
|
+
getopt = GetoptLong.new(*tmp.compact)
|
85
|
+
getopt.quiet = true
|
86
|
+
|
87
|
+
opt = {}
|
88
|
+
begin
|
89
|
+
getopt.each do |name, arg|
|
90
|
+
raise GetoptLong::InvalidOption,
|
91
|
+
"#{$cmd}: #{name} given twice" if opt.key? name
|
92
|
+
opt[name] = arg.empty? ? true : arg
|
93
|
+
end
|
94
|
+
rescue GetoptLong::AmbigousOption, GetoptLong::InvalidOption,
|
95
|
+
GetoptLong::MissingArgument, GetoptLong::NeedlessArgument
|
96
|
+
usage 1, $!.message
|
97
|
+
end
|
98
|
+
|
99
|
+
usage if opt['--help']
|
100
|
+
|
101
|
+
if opt['--version']
|
102
|
+
puts "#{$cmd} version #{Rex::Version}"
|
103
|
+
exit 0
|
104
|
+
end
|
105
|
+
if opt['--copyright']
|
106
|
+
puts "#{$cmd} version #{Rex::Version}"
|
107
|
+
puts "#{Rex::Copyright} <#{Rex::Mailto}>"
|
108
|
+
exit 0
|
109
|
+
end
|
110
|
+
|
111
|
+
usage(1, 'no grammar file given') if ARGV.empty?
|
112
|
+
usage(1, 'too many grammar files given') if ARGV.size > 1
|
113
|
+
|
114
|
+
opt
|
115
|
+
end
|
116
|
+
|
117
|
+
## ---------------------------------------------------------------------
|
118
|
+
|
119
|
+
def usage(status=0, msg=nil )
|
120
|
+
f = (status == 0 ? $stdout : $stderr)
|
121
|
+
f.puts "#{$cmd}: #{msg}" if msg
|
122
|
+
f.print <<-EOT
|
123
|
+
Usage: #{$cmd} [options] <grammar file>
|
124
|
+
Options:
|
125
|
+
EOT
|
126
|
+
|
127
|
+
REX_OPTIONS.each do |line|
|
128
|
+
next if line.strip.empty?
|
129
|
+
if /\A\s*\z/ === line
|
130
|
+
f.puts
|
131
|
+
next
|
132
|
+
end
|
133
|
+
|
134
|
+
disp, sopt, lopt, takearg, doc = line.strip.split(/\s+/, 5)
|
135
|
+
if disp == 'o'
|
136
|
+
sopt = nil if sopt == '-'
|
137
|
+
lopt = nil if lopt == '-'
|
138
|
+
opt = [sopt, lopt].compact.join(',')
|
139
|
+
|
140
|
+
takearg = nil if takearg == '-'
|
141
|
+
opt = [opt, takearg].compact.join(' ')
|
142
|
+
|
143
|
+
f.printf "%-27s %s\n", opt, doc
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
exit status
|
148
|
+
end
|
149
|
+
|
150
|
+
## ---------------------------------------------------------------------
|
151
|
+
|
152
|
+
main
|
@@ -0,0 +1,143 @@
|
|
1
|
+
#!/usr/local/bin/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
|
+
REX_OPTIONS = <<-EOT
|
16
|
+
|
17
|
+
o -o --output-file <outfile> file name of output [<filename>.rb]
|
18
|
+
o -s --stub - append stub code for debug
|
19
|
+
o -i --ignorecase - ignore char case
|
20
|
+
o -C --check-only - syntax check only
|
21
|
+
o - --independent - independent mode
|
22
|
+
o -d --debug - print debug information
|
23
|
+
o -h --help - print this message and quit
|
24
|
+
o - --version - print version and quit
|
25
|
+
o - --copyright - print copyright and quit
|
26
|
+
|
27
|
+
EOT
|
28
|
+
|
29
|
+
## ---------------------------------------------------------------------
|
30
|
+
|
31
|
+
require 'getoptlong'
|
32
|
+
require 'rex/generator'
|
33
|
+
require 'rex/info'
|
34
|
+
|
35
|
+
## ---------------------------------------------------------------------
|
36
|
+
|
37
|
+
class RexRunner
|
38
|
+
def run
|
39
|
+
@status = 1
|
40
|
+
usage 'no grammar file given' if ARGV.empty?
|
41
|
+
usage 'too many grammar files given' if ARGV.size > 1
|
42
|
+
filename = ARGV[0]
|
43
|
+
|
44
|
+
rex = Rex::Generator.new(@opt)
|
45
|
+
begin
|
46
|
+
rex.grammar_file = filename
|
47
|
+
rex.read_grammar
|
48
|
+
rex.parse
|
49
|
+
if @opt['--check-only']
|
50
|
+
$stderr.puts "syntax ok"
|
51
|
+
return 0
|
52
|
+
end
|
53
|
+
rex.write_scanner
|
54
|
+
@status = 0
|
55
|
+
|
56
|
+
rescue Rex::ParseError, Errno::ENOENT
|
57
|
+
msg = $!.to_s
|
58
|
+
unless /\A\d/ === msg
|
59
|
+
msg[0,0] = ' '
|
60
|
+
end
|
61
|
+
$stderr.puts "#{@cmd}:#{rex.grammar_file}:#{rex.lineno}:#{msg}"
|
62
|
+
|
63
|
+
ensure
|
64
|
+
exit @status
|
65
|
+
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def initialize
|
70
|
+
@status = 2
|
71
|
+
@cmd = File.basename($0, ".rb")
|
72
|
+
tmp = REX_OPTIONS.collect do |line|
|
73
|
+
next if /\A\s*\z/ === line
|
74
|
+
disp, sopt, lopt, takearg, doc = line.strip.split(/\s+/, 5)
|
75
|
+
a = []
|
76
|
+
a.push lopt unless lopt == '-'
|
77
|
+
a.push sopt unless sopt == '-'
|
78
|
+
a.push takearg == '-' ?
|
79
|
+
GetoptLong::NO_ARGUMENT : GetoptLong::REQUIRED_ARGUMENT
|
80
|
+
a
|
81
|
+
end
|
82
|
+
getopt = GetoptLong.new(*tmp.compact)
|
83
|
+
getopt.quiet = true
|
84
|
+
|
85
|
+
@opt = {}
|
86
|
+
begin
|
87
|
+
getopt.each do |name, arg|
|
88
|
+
raise GetoptLong::InvalidOption,
|
89
|
+
"#{@cmd}: #{name} given twice" if @opt.key? name
|
90
|
+
@opt[name] = arg.empty? ? true : arg
|
91
|
+
end
|
92
|
+
rescue GetoptLong::AmbigousOption, GetoptLong::InvalidOption,
|
93
|
+
GetoptLong::MissingArgument, GetoptLong::NeedlessArgument
|
94
|
+
usage $!.message
|
95
|
+
end
|
96
|
+
|
97
|
+
usage if @opt['--help']
|
98
|
+
|
99
|
+
if @opt['--version']
|
100
|
+
puts "#{@cmd} version #{Rex::Version}"
|
101
|
+
exit 0
|
102
|
+
end
|
103
|
+
if @opt['--copyright']
|
104
|
+
puts "#{@cmd} version #{Rex::Version}"
|
105
|
+
puts "#{Rex::Copyright} <#{Rex::Mailto}>"
|
106
|
+
exit 0
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
def usage( msg=nil )
|
111
|
+
f = $stderr
|
112
|
+
f.puts "#{@cmd}: #{msg}" if msg
|
113
|
+
f.print <<-EOT
|
114
|
+
Usage: #{@cmd} [options] <grammar file>
|
115
|
+
Options:
|
116
|
+
EOT
|
117
|
+
|
118
|
+
REX_OPTIONS.each do |line|
|
119
|
+
next if line.strip.empty?
|
120
|
+
if /\A\s*\z/ === line
|
121
|
+
f.puts
|
122
|
+
next
|
123
|
+
end
|
124
|
+
|
125
|
+
disp, sopt, lopt, takearg, doc = line.strip.split(/\s+/, 5)
|
126
|
+
if disp == 'o'
|
127
|
+
sopt = nil if sopt == '-'
|
128
|
+
lopt = nil if lopt == '-'
|
129
|
+
opt = [sopt, lopt].compact.join(',')
|
130
|
+
|
131
|
+
takearg = nil if takearg == '-'
|
132
|
+
opt = [opt, takearg].compact.join(' ')
|
133
|
+
|
134
|
+
f.printf "%-27s %s\n", opt, doc
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
exit @status
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
RexRunner.new.run
|
143
|
+
|
@@ -0,0 +1,184 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require 'tempfile'
|
3
|
+
require 'rexical'
|
4
|
+
require 'stringio'
|
5
|
+
|
6
|
+
class TestGenerator < Test::Unit::TestCase
|
7
|
+
def test_header_is_written_after_module
|
8
|
+
rex = Rexical::Generator.new(
|
9
|
+
"--independent" => true
|
10
|
+
)
|
11
|
+
rex.grammar_file = File.join File.dirname(__FILE__), 'assets', 'test.rex'
|
12
|
+
rex.read_grammar
|
13
|
+
rex.parse
|
14
|
+
|
15
|
+
output = StringIO.new
|
16
|
+
rex.write_scanner output
|
17
|
+
|
18
|
+
comments = []
|
19
|
+
output.string.split(/[\n]/).each do |line|
|
20
|
+
comments << line.chomp if line =~ /^#/
|
21
|
+
end
|
22
|
+
|
23
|
+
assert_match 'DO NOT MODIFY', comments.join
|
24
|
+
assert_equal '#--', comments.first
|
25
|
+
assert_equal '#++', comments.last
|
26
|
+
end
|
27
|
+
|
28
|
+
def test_read_non_existent_file
|
29
|
+
rex = Rexical::Generator.new(nil)
|
30
|
+
rex.grammar_file = 'non_existent_file'
|
31
|
+
assert_raises Errno::ENOENT do
|
32
|
+
rex.read_grammar
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def test_scanner_inherits
|
37
|
+
rex = Rexical::Generator.new(
|
38
|
+
"--independent" => true
|
39
|
+
)
|
40
|
+
rex.grammar_lines = StringScanner.new %q{
|
41
|
+
class Calculator < Bar
|
42
|
+
rule
|
43
|
+
\d+ { [:NUMBER, text.to_i] }
|
44
|
+
\s+ { [:S, text] }
|
45
|
+
end
|
46
|
+
}
|
47
|
+
|
48
|
+
rex.parse
|
49
|
+
|
50
|
+
output = StringIO.new
|
51
|
+
rex.write_scanner output
|
52
|
+
assert_match 'Calculator < Bar', output.string
|
53
|
+
end
|
54
|
+
|
55
|
+
def test_scanner_inherits_many_levels
|
56
|
+
rex = Rexical::Generator.new(
|
57
|
+
"--independent" => true
|
58
|
+
)
|
59
|
+
rex.grammar_lines = StringScanner.new %q{
|
60
|
+
class Calculator < Foo::Bar
|
61
|
+
rule
|
62
|
+
\d+ { [:NUMBER, text.to_i] }
|
63
|
+
\s+ { [:S, text] }
|
64
|
+
end
|
65
|
+
}
|
66
|
+
|
67
|
+
rex.parse
|
68
|
+
|
69
|
+
output = StringIO.new
|
70
|
+
rex.write_scanner output
|
71
|
+
assert_match 'Calculator < Foo::Bar', output.string
|
72
|
+
end
|
73
|
+
|
74
|
+
def test_simple_scanner
|
75
|
+
rex = Rexical::Generator.new(
|
76
|
+
"--independent" => true
|
77
|
+
)
|
78
|
+
rex.grammar_lines = StringScanner.new %q{
|
79
|
+
class Calculator
|
80
|
+
rule
|
81
|
+
\d+ { [:NUMBER, text.to_i] }
|
82
|
+
\s+ { [:S, text] }
|
83
|
+
end
|
84
|
+
}
|
85
|
+
|
86
|
+
rex.parse
|
87
|
+
|
88
|
+
output = StringIO.new
|
89
|
+
rex.write_scanner output
|
90
|
+
|
91
|
+
m = Module.new
|
92
|
+
m.module_eval output.string
|
93
|
+
calc = m::Calculator.new
|
94
|
+
calc.scan_evaluate('1 2 10')
|
95
|
+
|
96
|
+
assert_tokens [[:NUMBER, 1],
|
97
|
+
[:S, ' '],
|
98
|
+
[:NUMBER, 2],
|
99
|
+
[:S, ' '],
|
100
|
+
[:NUMBER, 10]], calc
|
101
|
+
end
|
102
|
+
|
103
|
+
def test_simple_scanner_with_macros
|
104
|
+
rex = Rexical::Generator.new(
|
105
|
+
"--independent" => true
|
106
|
+
)
|
107
|
+
rex.grammar_lines = StringScanner.new %q{
|
108
|
+
class Calculator
|
109
|
+
macro
|
110
|
+
digit \d+
|
111
|
+
rule
|
112
|
+
{digit} { [:NUMBER, text.to_i] }
|
113
|
+
\s+ { [:S, text] }
|
114
|
+
end
|
115
|
+
}
|
116
|
+
|
117
|
+
rex.parse
|
118
|
+
|
119
|
+
output = StringIO.new
|
120
|
+
rex.write_scanner output
|
121
|
+
|
122
|
+
m = Module.new
|
123
|
+
m.module_eval output.string
|
124
|
+
calc = m::Calculator.new
|
125
|
+
calc.scan_evaluate('1 2 10')
|
126
|
+
|
127
|
+
assert_tokens [[:NUMBER, 1],
|
128
|
+
[:S, ' '],
|
129
|
+
[:NUMBER, 2],
|
130
|
+
[:S, ' '],
|
131
|
+
[:NUMBER, 10]], calc
|
132
|
+
end
|
133
|
+
|
134
|
+
def test_nested_macros
|
135
|
+
rex = Rexical::Generator.new(
|
136
|
+
"--independent" => true
|
137
|
+
)
|
138
|
+
rex.grammar_lines = StringScanner.new %q{
|
139
|
+
class Calculator
|
140
|
+
macro
|
141
|
+
nonascii [^\0-\177]
|
142
|
+
string "{nonascii}*"
|
143
|
+
rule
|
144
|
+
{string} { [:STRING, text] }
|
145
|
+
end
|
146
|
+
}
|
147
|
+
|
148
|
+
rex.parse
|
149
|
+
|
150
|
+
output = StringIO.new
|
151
|
+
rex.write_scanner output
|
152
|
+
assert_match '"[^\0-\177]*"', output.string
|
153
|
+
end
|
154
|
+
|
155
|
+
def test_more_nested_macros
|
156
|
+
rex = Rexical::Generator.new(
|
157
|
+
"--independent" => true
|
158
|
+
)
|
159
|
+
rex.grammar_lines = StringScanner.new %q{
|
160
|
+
class Calculator
|
161
|
+
macro
|
162
|
+
nonascii [^\0-\177]
|
163
|
+
sing {nonascii}*
|
164
|
+
string "{sing}"
|
165
|
+
rule
|
166
|
+
{string} { [:STRING, text] }
|
167
|
+
end
|
168
|
+
}
|
169
|
+
|
170
|
+
rex.parse
|
171
|
+
|
172
|
+
output = StringIO.new
|
173
|
+
rex.write_scanner output
|
174
|
+
assert_match '"[^\0-\177]*"', output.string
|
175
|
+
end
|
176
|
+
|
177
|
+
def assert_tokens expected, scanner
|
178
|
+
tokens = []
|
179
|
+
while token = scanner.next_token
|
180
|
+
tokens << token
|
181
|
+
end
|
182
|
+
assert_equal expected, tokens
|
183
|
+
end
|
184
|
+
end
|