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.
@@ -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
+