metal 0.0.1
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/License.txt +13 -0
- data/Manifest.txt +20 -0
- data/README.txt +29 -0
- data/Rakefile +4 -0
- data/bin/metal +38 -0
- data/bin/metal-run +52 -0
- data/config/hoe.rb +73 -0
- data/config/requirements.rb +15 -0
- data/lib/metal.rb +7 -0
- data/lib/metal/boot.metal +153 -0
- data/lib/metal/boot.rb +351 -0
- data/lib/metal/generator.rb +289 -0
- data/lib/metal/runtime.rb +413 -0
- data/lib/metal/version.rb +9 -0
- data/script/console +10 -0
- data/script/destroy +14 -0
- data/script/generate +14 -0
- data/setup.rb +1585 -0
- data/tasks/deployment.rake +34 -0
- data/tasks/environment.rake +7 -0
- data/test/test_helper.rb +2 -0
- data/test/test_metal.rb +11 -0
- metadata +80 -0
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
#
|
|
2
|
+
# Metal Generator
|
|
3
|
+
#
|
|
4
|
+
# Copyright (C) 2008 FURUHASHI Sadayuki
|
|
5
|
+
#
|
|
6
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
7
|
+
# you may not use this file except in compliance with the License.
|
|
8
|
+
# You may obtain a copy of the License at
|
|
9
|
+
#
|
|
10
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
11
|
+
#
|
|
12
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
13
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
14
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
15
|
+
# See the License for the specific language governing permissions and
|
|
16
|
+
# limitations under the License.
|
|
17
|
+
#
|
|
18
|
+
|
|
19
|
+
module Metal
|
|
20
|
+
module Generator
|
|
21
|
+
|
|
22
|
+
class Generator
|
|
23
|
+
def generate(lang, buf = '')
|
|
24
|
+
__send__("gen_#{lang}", buf)
|
|
25
|
+
buf
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
class Meta < Generator
|
|
30
|
+
def initialize(grammars)
|
|
31
|
+
@grammars = grammars
|
|
32
|
+
end
|
|
33
|
+
def gen_ruby(buf)
|
|
34
|
+
@grammars.each {|r|
|
|
35
|
+
r.gen_ruby(buf)
|
|
36
|
+
}
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def [](name)
|
|
40
|
+
name = name.to_sym
|
|
41
|
+
@grammars.find {|g| g.is_a?(Grammar) && g.name == name }
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
class Grammar < Generator
|
|
46
|
+
def initialize(name, rules, base, interface = false)
|
|
47
|
+
@name = name.to_sym
|
|
48
|
+
@rules = rules
|
|
49
|
+
@base = base
|
|
50
|
+
@interface = interface
|
|
51
|
+
end
|
|
52
|
+
attr_reader :name
|
|
53
|
+
def gen_ruby(buf)
|
|
54
|
+
if @interface
|
|
55
|
+
buf << %[module #{@name}\n]
|
|
56
|
+
else
|
|
57
|
+
buf << %[class #{@name} < #{@base || "Metal::ParserBase"}\n]
|
|
58
|
+
end
|
|
59
|
+
@rules.each {|r|
|
|
60
|
+
r.gen_ruby(buf)
|
|
61
|
+
}
|
|
62
|
+
buf << %[end\n]
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def [](name)
|
|
66
|
+
name = name.to_sym
|
|
67
|
+
@rules.find {|r| r.is_a?(Rule) && r.name == name }
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
class Precode < Generator
|
|
72
|
+
def initialize(code)
|
|
73
|
+
@code = code
|
|
74
|
+
end
|
|
75
|
+
def gen_ruby(buf)
|
|
76
|
+
buf << @code << "\n"
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
class Mixin < Generator
|
|
81
|
+
def initialize(name)
|
|
82
|
+
@name = name
|
|
83
|
+
end
|
|
84
|
+
def gen_ruby(buf)
|
|
85
|
+
buf << "include #{@name}\n"
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
class Rule < Generator
|
|
90
|
+
def initialize(name, or_set, args)
|
|
91
|
+
@name = name.to_sym
|
|
92
|
+
@or_set = or_set
|
|
93
|
+
@args = args
|
|
94
|
+
end
|
|
95
|
+
def gen_ruby(buf)
|
|
96
|
+
buf << %[def rule_#{@name}(#{@args.join(',')})\n]
|
|
97
|
+
@or_set.gen_ruby(buf)
|
|
98
|
+
buf << %[end\n]
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
class OrSet
|
|
103
|
+
def initialize(and_set)
|
|
104
|
+
@and_set = and_set
|
|
105
|
+
end
|
|
106
|
+
def gen_ruby(buf)
|
|
107
|
+
if @and_set.length == 1
|
|
108
|
+
# OPTIMIZE
|
|
109
|
+
@and_set[0].gen_ruby(buf)
|
|
110
|
+
elsif @and_set.length > 1
|
|
111
|
+
buf << "act_or(["
|
|
112
|
+
@and_set.each {|r|
|
|
113
|
+
buf << "lambda{"
|
|
114
|
+
r.gen_ruby(buf)
|
|
115
|
+
buf << "},\n"
|
|
116
|
+
}
|
|
117
|
+
buf << "])\n"
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
class AndSet
|
|
123
|
+
def initialize(exprs)
|
|
124
|
+
@exprs = exprs
|
|
125
|
+
end
|
|
126
|
+
def gen_ruby(buf)
|
|
127
|
+
@exprs.each {|r|
|
|
128
|
+
r.gen_ruby(buf)
|
|
129
|
+
buf << "\n"
|
|
130
|
+
}
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
class Expression
|
|
135
|
+
def initialize(pred, var)
|
|
136
|
+
@pred = pred
|
|
137
|
+
@var = var
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
def gen_ruby(buf)
|
|
141
|
+
buf << "#{@var} = " if @var
|
|
142
|
+
@pred.gen_ruby(buf)
|
|
143
|
+
end
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
class IterMany
|
|
147
|
+
def initialize(pred)
|
|
148
|
+
@pred = pred
|
|
149
|
+
end
|
|
150
|
+
def gen_ruby(buf)
|
|
151
|
+
buf << "act_many(lambda{"
|
|
152
|
+
@pred.gen_ruby(buf)
|
|
153
|
+
buf << "})"
|
|
154
|
+
end
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
class IterMany1
|
|
158
|
+
def initialize(pred)
|
|
159
|
+
@pred = pred
|
|
160
|
+
end
|
|
161
|
+
def gen_ruby(buf)
|
|
162
|
+
buf << "act_many1(lambda{"
|
|
163
|
+
@pred.gen_ruby(buf)
|
|
164
|
+
buf << "})"
|
|
165
|
+
end
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
class IterMay
|
|
169
|
+
def initialize(pred)
|
|
170
|
+
@pred = pred
|
|
171
|
+
end
|
|
172
|
+
def gen_ruby(buf)
|
|
173
|
+
buf << "act_may(lambda{"
|
|
174
|
+
@pred.gen_ruby(buf)
|
|
175
|
+
buf << "})"
|
|
176
|
+
end
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
class Action
|
|
180
|
+
def initialize(action)
|
|
181
|
+
@action = action
|
|
182
|
+
end
|
|
183
|
+
def gen_ruby(buf)
|
|
184
|
+
buf << @action.to_s
|
|
185
|
+
end
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
class Where
|
|
189
|
+
def initialize(action)
|
|
190
|
+
@action = action
|
|
191
|
+
end
|
|
192
|
+
def gen_ruby(buf)
|
|
193
|
+
buf << "act_where(lambda{#{@action.to_s}})"
|
|
194
|
+
end
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
class Apply
|
|
198
|
+
def initialize(name, vars)
|
|
199
|
+
@name = name
|
|
200
|
+
@vars = vars
|
|
201
|
+
end
|
|
202
|
+
def gen_ruby(buf)
|
|
203
|
+
buf << "apply(:#{@name}#{@vars.map{|v|",#{v}"}})"
|
|
204
|
+
end
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
class Super
|
|
208
|
+
def initialize(vars)
|
|
209
|
+
@vars = vars
|
|
210
|
+
end
|
|
211
|
+
def gen_ruby(buf)
|
|
212
|
+
if @vars == nil
|
|
213
|
+
buf << "super"
|
|
214
|
+
else
|
|
215
|
+
buf << "super(#{@vars.join(',')})"
|
|
216
|
+
end
|
|
217
|
+
end
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
class Other
|
|
221
|
+
def initialize(grammar, rule, args)
|
|
222
|
+
@grammar = grammar
|
|
223
|
+
@rule = rule
|
|
224
|
+
@args = args
|
|
225
|
+
end
|
|
226
|
+
def gen_ruby(buf)
|
|
227
|
+
buf << "#{@grammar}.new(:#{@rule}).parse(@input#{@args.map{|v|",#{v}"}})"
|
|
228
|
+
end
|
|
229
|
+
end
|
|
230
|
+
|
|
231
|
+
class LiteralQuotedToken
|
|
232
|
+
def initialize(str)
|
|
233
|
+
@str = str
|
|
234
|
+
end
|
|
235
|
+
def gen_ruby(buf)
|
|
236
|
+
buf << "act_token('#{@str}')"
|
|
237
|
+
end
|
|
238
|
+
end
|
|
239
|
+
|
|
240
|
+
class LiteralToken
|
|
241
|
+
def initialize(str)
|
|
242
|
+
@str = str
|
|
243
|
+
end
|
|
244
|
+
def gen_ruby(buf)
|
|
245
|
+
buf << "act_token(\"#{@str}\")"
|
|
246
|
+
end
|
|
247
|
+
end
|
|
248
|
+
|
|
249
|
+
class LiteralCharset
|
|
250
|
+
def initialize(set)
|
|
251
|
+
@set = set
|
|
252
|
+
end
|
|
253
|
+
def gen_ruby(buf)
|
|
254
|
+
buf << "rule_charset(%[#{@set}])"
|
|
255
|
+
end
|
|
256
|
+
end
|
|
257
|
+
|
|
258
|
+
class LiteralAny
|
|
259
|
+
def gen_ruby(buf)
|
|
260
|
+
buf << "rule_anything"
|
|
261
|
+
end
|
|
262
|
+
end
|
|
263
|
+
|
|
264
|
+
class PredNot
|
|
265
|
+
def initialize(expr)
|
|
266
|
+
@expr = expr
|
|
267
|
+
end
|
|
268
|
+
def gen_ruby(buf)
|
|
269
|
+
buf << "act_not(lambda{"
|
|
270
|
+
@expr.gen_ruby(buf)
|
|
271
|
+
buf << "})"
|
|
272
|
+
end
|
|
273
|
+
end
|
|
274
|
+
|
|
275
|
+
class PredLookahead
|
|
276
|
+
def initialize(expr)
|
|
277
|
+
@expr = expr
|
|
278
|
+
end
|
|
279
|
+
def gen_ruby(buf)
|
|
280
|
+
buf << "act_lookahead(lambda{"
|
|
281
|
+
@expr.gen_ruby(buf)
|
|
282
|
+
buf << "})"
|
|
283
|
+
end
|
|
284
|
+
end
|
|
285
|
+
|
|
286
|
+
end
|
|
287
|
+
end
|
|
288
|
+
|
|
289
|
+
|
|
@@ -0,0 +1,413 @@
|
|
|
1
|
+
#
|
|
2
|
+
# Metal Runtime
|
|
3
|
+
#
|
|
4
|
+
# Copyright (C) 2008 FURUHASHI Sadayuki
|
|
5
|
+
#
|
|
6
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
7
|
+
# you may not use this file except in compliance with the License.
|
|
8
|
+
# You may obtain a copy of the License at
|
|
9
|
+
#
|
|
10
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
11
|
+
#
|
|
12
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
13
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
14
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
15
|
+
# See the License for the specific language governing permissions and
|
|
16
|
+
# limitations under the License.
|
|
17
|
+
#
|
|
18
|
+
|
|
19
|
+
module Metal
|
|
20
|
+
|
|
21
|
+
class Input
|
|
22
|
+
def initialize
|
|
23
|
+
@marks = []
|
|
24
|
+
@pos = 0
|
|
25
|
+
@memo = {}
|
|
26
|
+
@error = []
|
|
27
|
+
end
|
|
28
|
+
attr_reader :pos
|
|
29
|
+
attr_reader :error
|
|
30
|
+
|
|
31
|
+
def get_memo(name)
|
|
32
|
+
@memo["#{@pos} #{name}"]
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def set_memo(pos, name, rec)
|
|
36
|
+
@memo["#{pos} #{name}"] = rec
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def next
|
|
40
|
+
val = self[@pos]
|
|
41
|
+
raise EOFError, "unexpected EOF." unless val
|
|
42
|
+
@pos += 1
|
|
43
|
+
val
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def prev
|
|
47
|
+
@pos -= 1
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def mark
|
|
51
|
+
@marks.push @pos
|
|
52
|
+
@marks.length-1
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def unmark(mark)
|
|
56
|
+
@marks[mark] = nil
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def rewind(mark)
|
|
60
|
+
@pos = @marks[mark]
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def seek(pos)
|
|
64
|
+
@pos = pos
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def leaked_marks # FIXME
|
|
68
|
+
@marks.compact.length
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def inspect_at(index)
|
|
72
|
+
"at #{index}"
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
class StringInput < Input
|
|
78
|
+
def initialize(source)
|
|
79
|
+
@source = source.scan(/./m) # FIXME Ruby 1.9
|
|
80
|
+
super()
|
|
81
|
+
end
|
|
82
|
+
def [](index)
|
|
83
|
+
@source[index]
|
|
84
|
+
end
|
|
85
|
+
def inspect_at(pos)
|
|
86
|
+
parsed = @source[0, pos].join.split("\n")
|
|
87
|
+
line = parsed.length
|
|
88
|
+
column = (line == 0 ? 0 : parsed.last.length)
|
|
89
|
+
"at line #{line}, col #{column}"
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
class ParseError < RuntimeError
|
|
95
|
+
def initialize(msg, input)
|
|
96
|
+
super(msg)
|
|
97
|
+
@pos = input.pos
|
|
98
|
+
@error = input.error
|
|
99
|
+
@input = input
|
|
100
|
+
end
|
|
101
|
+
attr_reader :pos
|
|
102
|
+
attr_reader :error
|
|
103
|
+
|
|
104
|
+
def message
|
|
105
|
+
msg = super
|
|
106
|
+
@error.each {|e|
|
|
107
|
+
msg << "\n\t\t" << e.to_s
|
|
108
|
+
}
|
|
109
|
+
msg << "\n"
|
|
110
|
+
end
|
|
111
|
+
def to_s
|
|
112
|
+
stdmsg(super)
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
private
|
|
116
|
+
def stdmsg(msg)
|
|
117
|
+
msg << " " << @input.inspect_at(@pos)
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
class LeftRecursion
|
|
122
|
+
def initialize
|
|
123
|
+
@detected = false
|
|
124
|
+
end
|
|
125
|
+
attr_accessor :detected
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
class Memo
|
|
129
|
+
def initialize(ret, pos)
|
|
130
|
+
@ret = ret
|
|
131
|
+
@pos = pos
|
|
132
|
+
end
|
|
133
|
+
attr_reader :ret
|
|
134
|
+
attr_reader :pos
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
class ParserBase
|
|
138
|
+
DEFAULT_ROOT = :main
|
|
139
|
+
|
|
140
|
+
def initialize(root = nil)
|
|
141
|
+
@root = root
|
|
142
|
+
@me = 0 # FIXME
|
|
143
|
+
end
|
|
144
|
+
attr_accessor :root
|
|
145
|
+
|
|
146
|
+
attr_accessor :me # FIXME
|
|
147
|
+
|
|
148
|
+
def parse(source, *args)
|
|
149
|
+
@input = case source
|
|
150
|
+
when Input
|
|
151
|
+
source
|
|
152
|
+
when IO
|
|
153
|
+
StringInput.new(source.read) #FIXME
|
|
154
|
+
when String
|
|
155
|
+
StringInput.new(source)
|
|
156
|
+
else
|
|
157
|
+
StringInput.new(source)
|
|
158
|
+
end
|
|
159
|
+
apply(@root || self.class::DEFAULT_ROOT, *args)
|
|
160
|
+
end
|
|
161
|
+
attr_reader :input
|
|
162
|
+
|
|
163
|
+
def apply(rule, *args)
|
|
164
|
+
rule_method = "rule_#{rule}"
|
|
165
|
+
unless args.empty?
|
|
166
|
+
return __send__(rule_method, *args)
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
memo_name = "#{self.class}.#{rule}"
|
|
170
|
+
if memorized = @input.get_memo(memo_name)
|
|
171
|
+
@me += 1 # FIXME
|
|
172
|
+
# memorized rule
|
|
173
|
+
if memorized.class == LeftRecursion
|
|
174
|
+
memorized.detected = true
|
|
175
|
+
raise ParseError.new("Left recursion detected", @input)
|
|
176
|
+
end
|
|
177
|
+
@input.seek(memorized.pos)
|
|
178
|
+
return memorized.ret
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
# non-memorized rule
|
|
182
|
+
m = @input.mark
|
|
183
|
+
begin
|
|
184
|
+
save_pos = @input.pos
|
|
185
|
+
lr = LeftRecursion.new
|
|
186
|
+
@input.set_memo(save_pos, memo_name, lr)
|
|
187
|
+
ret = __send__(rule_method)
|
|
188
|
+
@input.set_memo(save_pos, memo_name, Memo.new(ret, @input.pos))
|
|
189
|
+
rescue
|
|
190
|
+
@input.unmark(m)
|
|
191
|
+
raise $!
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
if lr.detected
|
|
195
|
+
sentinel = @input.pos
|
|
196
|
+
@input.rewind(m)
|
|
197
|
+
while true
|
|
198
|
+
n = @input.mark
|
|
199
|
+
begin
|
|
200
|
+
ret = __send__(rule_method)
|
|
201
|
+
break if @input.pos == sentinel
|
|
202
|
+
memorized = @input.set_memo(save_pos, memo_name, Memo.new(ret, @input.pos))
|
|
203
|
+
@input.rewind(n)
|
|
204
|
+
rescue ParseError
|
|
205
|
+
@input.unmark(n)
|
|
206
|
+
break
|
|
207
|
+
end
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
else
|
|
211
|
+
@input.unmark(m)
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
ret
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
def act_many(block, *rets)
|
|
218
|
+
@input.error.clear
|
|
219
|
+
while true
|
|
220
|
+
m = @input.mark
|
|
221
|
+
begin
|
|
222
|
+
rets.push block.call
|
|
223
|
+
rescue ParseError
|
|
224
|
+
@input.error.push $!
|
|
225
|
+
@input.rewind(m)
|
|
226
|
+
break
|
|
227
|
+
ensure
|
|
228
|
+
@input.unmark(m)
|
|
229
|
+
end
|
|
230
|
+
end
|
|
231
|
+
rets
|
|
232
|
+
end
|
|
233
|
+
|
|
234
|
+
def act_many1(block, *rets)
|
|
235
|
+
rets.push block.call
|
|
236
|
+
act_many(block, *rets)
|
|
237
|
+
end
|
|
238
|
+
|
|
239
|
+
def act_may(block)
|
|
240
|
+
m = @input.mark
|
|
241
|
+
rets = nil
|
|
242
|
+
@input.error.clear
|
|
243
|
+
begin
|
|
244
|
+
rets = block.call
|
|
245
|
+
rescue ParseError
|
|
246
|
+
@input.error.push $!
|
|
247
|
+
@input.rewind(m)
|
|
248
|
+
ensure
|
|
249
|
+
@input.unmark(m)
|
|
250
|
+
end
|
|
251
|
+
rets
|
|
252
|
+
end
|
|
253
|
+
|
|
254
|
+
def act_or(blocks)
|
|
255
|
+
ex = []
|
|
256
|
+
blocks.each {|block|
|
|
257
|
+
m = @input.mark
|
|
258
|
+
begin
|
|
259
|
+
ret = block.call
|
|
260
|
+
return ret
|
|
261
|
+
rescue ParseError
|
|
262
|
+
ex.push $!
|
|
263
|
+
@input.rewind(m)
|
|
264
|
+
ensure
|
|
265
|
+
@input.unmark(m)
|
|
266
|
+
end
|
|
267
|
+
}
|
|
268
|
+
msg = "All expression failed:"
|
|
269
|
+
ex.each {|e| msg << "\n\t\t\t" << e.to_s }
|
|
270
|
+
raise ParseError.new(msg, @input)
|
|
271
|
+
end
|
|
272
|
+
|
|
273
|
+
def act_not(block)
|
|
274
|
+
m = @input.mark
|
|
275
|
+
unless begin
|
|
276
|
+
block.call
|
|
277
|
+
nil
|
|
278
|
+
rescue ParseError
|
|
279
|
+
true
|
|
280
|
+
ensure
|
|
281
|
+
@input.rewind(m)
|
|
282
|
+
@input.unmark(m)
|
|
283
|
+
end
|
|
284
|
+
raise ParseError.new("unexpectedly succeeded", @input)
|
|
285
|
+
end
|
|
286
|
+
end
|
|
287
|
+
|
|
288
|
+
def act_lookahead(block)
|
|
289
|
+
m = @input.mark
|
|
290
|
+
begin
|
|
291
|
+
block.call # FIXME
|
|
292
|
+
ensure
|
|
293
|
+
@input.rewind(m)
|
|
294
|
+
@input.unmark(m)
|
|
295
|
+
end
|
|
296
|
+
end
|
|
297
|
+
|
|
298
|
+
def act_where(block)
|
|
299
|
+
unless block.call
|
|
300
|
+
raise ParseError.new("where block failed", @input)
|
|
301
|
+
end
|
|
302
|
+
end
|
|
303
|
+
|
|
304
|
+
def act_exactly(wanted)
|
|
305
|
+
m = @input.mark
|
|
306
|
+
begin
|
|
307
|
+
val = @input.next
|
|
308
|
+
unless val == wanted
|
|
309
|
+
@input.rewind(m)
|
|
310
|
+
raise ParseError.new("#{wanted.inspect} is expected", @input)
|
|
311
|
+
end
|
|
312
|
+
val
|
|
313
|
+
rescue EOFError
|
|
314
|
+
@input.rewind(m)
|
|
315
|
+
raise ParseError.new("#{wanted.inspect} is expected but EOF comes", @input)
|
|
316
|
+
ensure
|
|
317
|
+
@input.unmark(m)
|
|
318
|
+
end
|
|
319
|
+
end
|
|
320
|
+
|
|
321
|
+
def act_token(str)
|
|
322
|
+
return act_exactly(str) if str.length == 1
|
|
323
|
+
m = @input.mark
|
|
324
|
+
begin
|
|
325
|
+
str.scan(/./m) {|c| # FIXME Ruby 1.9
|
|
326
|
+
act_exactly(c)
|
|
327
|
+
}
|
|
328
|
+
str
|
|
329
|
+
rescue ParseError
|
|
330
|
+
@input.rewind(m)
|
|
331
|
+
raise ParseError.new("#{str.inspect} is expected", @input)
|
|
332
|
+
ensure
|
|
333
|
+
@input.unmark(m)
|
|
334
|
+
end
|
|
335
|
+
end
|
|
336
|
+
|
|
337
|
+
def rule_anything
|
|
338
|
+
@input.next
|
|
339
|
+
rescue EOFError
|
|
340
|
+
raise ParseError.new($!.to_s, @input)
|
|
341
|
+
end
|
|
342
|
+
|
|
343
|
+
def rule_end
|
|
344
|
+
m = @input.mark
|
|
345
|
+
begin
|
|
346
|
+
@input.next
|
|
347
|
+
raise ParseError.new("EOF expected", @input)
|
|
348
|
+
rescue EOFError
|
|
349
|
+
nil
|
|
350
|
+
ensure
|
|
351
|
+
@input.rewind(m)
|
|
352
|
+
@input.unmark(m)
|
|
353
|
+
end
|
|
354
|
+
end
|
|
355
|
+
|
|
356
|
+
def rule_charset(set)
|
|
357
|
+
x = @input.next
|
|
358
|
+
if x.count(set) == 0
|
|
359
|
+
@input.prev
|
|
360
|
+
raise ParseError.new("one of character in #{set.inspect} expected", @input)
|
|
361
|
+
end
|
|
362
|
+
x
|
|
363
|
+
rescue EOFError
|
|
364
|
+
raise ParseError.new($!.to_s, @input)
|
|
365
|
+
end
|
|
366
|
+
|
|
367
|
+
def rule_empty
|
|
368
|
+
nil
|
|
369
|
+
end
|
|
370
|
+
|
|
371
|
+
=begin
|
|
372
|
+
def rule_letter
|
|
373
|
+
rule_charset("a-zA-Z")
|
|
374
|
+
end
|
|
375
|
+
|
|
376
|
+
def rule_digit
|
|
377
|
+
rule_charset("0-9")
|
|
378
|
+
end
|
|
379
|
+
|
|
380
|
+
def rule_letter_or_digit
|
|
381
|
+
rule_charset("a-zA-Z0-9")
|
|
382
|
+
end
|
|
383
|
+
|
|
384
|
+
def rule_space
|
|
385
|
+
rule_charset(" \t\r\n")
|
|
386
|
+
end
|
|
387
|
+
|
|
388
|
+
def rule_spaces
|
|
389
|
+
while @input.next.count(" \t\r\n") != 0
|
|
390
|
+
true
|
|
391
|
+
end
|
|
392
|
+
end
|
|
393
|
+
=end
|
|
394
|
+
|
|
395
|
+
protected
|
|
396
|
+
def error(msg)
|
|
397
|
+
raise ParseError.new(msg, @input)
|
|
398
|
+
end
|
|
399
|
+
|
|
400
|
+
end
|
|
401
|
+
end
|
|
402
|
+
|
|
403
|
+
EXTEND_GRAMMER = <<EOF
|
|
404
|
+
MetaUtility {
|
|
405
|
+
-letter
|
|
406
|
+
<charset "a-zA-Z">
|
|
407
|
+
}
|
|
408
|
+
EOF
|
|
409
|
+
|
|
410
|
+
|
|
411
|
+
|
|
412
|
+
|
|
413
|
+
|