metal 0.0.2 → 0.0.3

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.
@@ -19,296 +19,406 @@
19
19
  module Metal
20
20
  module Generator
21
21
 
22
- class Context
23
- def initialize(out)
24
- @out = out
25
- @rule = nil
26
- @grammar = nil
27
- end
28
- attr_reader :out
29
- attr_accessor :rule
30
- attr_accessor :grammar
22
+ def self.escape_quoated_string(str)
23
+ str = str.gsub("\\", "\\\\\\\\")
24
+ str.gsub!("\"", "\\\\\"")
25
+ str_len = string_length(str)
26
+ str.gsub!("\n", "\\\\n\"\n\"")
27
+ return str, str_len
31
28
  end
32
-
33
- class Generator
34
- def generate(lang, out = '')
35
- ctx = Context.new(out)
36
- __send__("gen_#{lang}", ctx)
37
- ctx.out
38
- end
29
+
30
+ def self.string_length(str)
31
+ #@str.bytesize # FIXME Ruby 1.9
32
+ str.size - str.scan(/\\/).length + str.scan(/\\\\/).length;
39
33
  end
40
-
41
- class Meta < Generator
42
- def initialize(grammars)
43
- @grammars = grammars
34
+
35
+ class Meta
36
+ def initialize(codes)
37
+ @grammars, @precodes = codes.partition {|i| i.is_a?(Grammar) }
38
+ @vars = []
39
+ @grammar = nil
40
+ @rule = nil
44
41
  end
45
- def gen_ruby(ctx)
46
- @grammars.each {|r|
47
- r.gen_ruby(ctx)
42
+ def generate(fname, source = '', header = '', rbcode = '')
43
+ @grammars.each {|grammar|
44
+ grammar.preprocess(self)
48
45
  }
49
- end
50
-
51
- def [](name)
52
- name = name.to_sym
53
- @grammars.find {|g| g.is_a?(Grammar) && g.name == name }
46
+
47
+ header << "#import \"metal/runtime.h\"\n"
48
+ source << "#import \"#{fname}.h\"\n"
49
+ source << "using namespace Metal;\n"
50
+
51
+ @vars.sort!
52
+ @vars.uniq!
53
+ @vars.each {|var|
54
+ source << "static param_t const PARAM_#{var} = param_lookup(\"#{var}\");\n"
55
+ }
56
+
57
+ rbcode << <<END
58
+ #import "ruby.h"
59
+ #import "metal/rbcode.h"
60
+ #import "#{fname}.h"
61
+ #ifdef __cplusplus
62
+ extern "C" {
63
+ #endif
64
+ void Init_#{fname}() {
65
+ VALUE mMetal = rb_define_module("Metal");
66
+ VALUE cBase = rb_define_class_under(mMetal, "ParserBase", rb_cObject);
67
+ VALUE code;
68
+ END
69
+
70
+ @precodes.each {|precode|
71
+ code, code_len = Generator.escape_quoated_string(precode.code)
72
+ rbcode << "rb_eval_string(\"#{code}\");\n"
73
+ }
74
+
75
+ @grammars.each {|grammar|
76
+ grammar.generate(self, source, header, rbcode)
77
+ }
78
+
79
+ rbcode << <<END
80
+ }
81
+ #ifdef __cplusplus
82
+ }
83
+ #endif
84
+ END
85
+
86
+ return [source, header, rbcode]
87
+ end
88
+
89
+ attr_accessor :meta, :grammar, :rule
90
+
91
+ def register_var(name)
92
+ @vars.push name
54
93
  end
55
94
  end
56
-
57
- class Grammar < Generator
58
- def initialize(name, rules, base, interface = false)
59
- @name = name.to_sym
60
- @rules = rules
95
+
96
+
97
+ class Grammar
98
+ def initialize(name, body, base = nil, category = nil)
99
+ @name = name
100
+ @rules = []; @tokens = [], @precodes = []
101
+ body.each {|b|
102
+ if b.is_a?(Rule)
103
+ @rules.push(b)
104
+ elsif b.is_a?(Token)
105
+ @tokens.push(b)
106
+ else
107
+ @precodes.push(b)
108
+ end
109
+ }
61
110
  @base = base
62
- @interface = interface
63
- end
64
- attr_reader :name
65
- def gen_ruby(ctx)
66
- ctx.grammar = @name
67
- if @interface
68
- ctx.out << %[module #{@name}\n]
111
+ @category = category
112
+ end
113
+ def preprocess(ctx)
114
+ ctx.grammar = self
115
+ @rules.each {|rules| rules.preprocess(ctx) }
116
+ # FIXME @tokens
117
+ end
118
+ def generate(ctx, source, header, rbcode)
119
+ ctx.grammar = self
120
+
121
+ if @category
122
+ header << "@interface #{@name} (#{@category})\n"
123
+ else
124
+ header << "@interface #{@name} : #{@base || 'MetalParserBase'}\n"
125
+ end
126
+ @rules.each {|rule|
127
+ header << "-(VALUE)rule_#{rule.name}:(Metal::Context*)ctx;\n"
128
+ }
129
+ # FIXME token
130
+ header << "@end\n"
131
+
132
+ if @category
133
+ source << "@implementation #{@name} (#{@category})\n"
69
134
  else
70
- ctx.out << %[class #{@name} < #{@base || "Metal::ParserBase"}\n]
135
+ source << "@implementation #{@name}\n"
71
136
  end
72
- @rules.each {|r|
73
- r.gen_ruby(ctx)
137
+ @rules.each {|rule|
138
+ source << "-(VALUE)rule_#{rule.name}:(Context*)ctx {\n"
139
+ source << "return ({\n"
140
+ rule.generate(ctx, source)
141
+ source << "});\n"
142
+ source << "}\n"
74
143
  }
75
- ctx.out << %[end\n]
144
+ # FIXME token
145
+ source << "@end\n"
146
+
147
+ rbcode << "{\n"
148
+ if @category
149
+ rbcode << "VALUE c#{@name} = rb_const_get(rb_cKernel,rb_intern(\"#{@name}\"));\n"
150
+ else
151
+ rbcode << "VALUE c#{@name} = rb_define_class(\"#{@name}\", #{@base ? "rb_const_get(rb_cKernel,rb_intern(\"#{@base}\"))" : "cBase"});\n";
152
+ rbcode << "rb_define_method(cMetalParser, \"parse\", (VALUE (*)(ANYARGS))(VALUE (*)(VALUE,VALUE))&Metal::parse_method_template<#{@name}>, 1);\n"
153
+ end
154
+ @precodes.each {|precode|
155
+ code, code_len = Generator.escape_quoated_string(precode.code)
156
+ rbcode << "code = rb_str_new(\"#{code}\",#{code_len});\n";
157
+ rbcode << "rb_mod_module_eval(1,&code,c#{@name});\n"
158
+ }
159
+ rbcode << "}\n"
76
160
  end
77
-
78
- def [](name)
79
- name = name.to_sym
80
- @rules.find {|r| r.is_a?(Rule) && r.name == name }
161
+ attr_reader :name
162
+ def category?
163
+ @category != nil
81
164
  end
82
165
  end
83
-
84
- class Precode < Generator
166
+
167
+
168
+ class Precode
85
169
  def initialize(code)
86
170
  @code = code
87
171
  end
88
- def gen_ruby(ctx)
89
- ctx.out << @code << "\n"
90
- end
172
+ attr_reader :code
91
173
  end
92
-
93
- class Mixin < Generator
94
- def initialize(name)
174
+
175
+
176
+ class Token
177
+ # FIXME
178
+ end
179
+
180
+ class Rule
181
+ def initialize(name, or_set)
95
182
  @name = name
183
+ @or_set = or_set
184
+
185
+ @serial = 0
96
186
  end
97
- def gen_ruby(ctx)
98
- ctx.out << "include #{@name}\n"
187
+ def preprocess(ctx)
188
+ ctx.rule = self
189
+ @or_set.preprocess(ctx)
99
190
  end
100
- end
101
-
102
- class Rule < Generator
103
- def initialize(name, or_set, args)
104
- @name = name.to_sym
105
- @or_set = or_set
106
- @args = args
191
+ def generate(ctx, source)
192
+ ctx.rule = self
193
+ source << "Env env;\n"
194
+ @or_set.generate(ctx, source)
107
195
  end
108
- def gen_ruby(ctx)
109
- ctx.rule = @name
110
- ctx.out << %[def rule_#{@name}(#{@args.join(',')})\n]
111
- @or_set.gen_ruby(ctx)
112
- ctx.out << %[end\n]
196
+ attr_reader :name
197
+
198
+ def with_lambda(source, &block)
199
+ obj = "rule_#{@name}_#{@serial+=1}"
200
+ source << "struct #{obj}_t : Lambda {\n"
201
+ source << "#{obj}_t(Context* _ctx, Env& _env) : Lambda(_ctx, _env) {}\n"
202
+ source << "VALUE call() {\n"
203
+ source << "return ({\n"
204
+ yield
205
+ source << "});\n"
206
+ source << "}\n"
207
+ source << "} #{obj}(ctx, env);\n"
208
+ obj
113
209
  end
114
210
  end
115
-
211
+
212
+
116
213
  class OrSet
117
- def initialize(and_set)
118
- @and_set = and_set
119
- end
120
- def gen_ruby(ctx)
121
- if @and_set.length == 1
122
- # OPTIMIZE
123
- @and_set[0].gen_ruby(ctx)
124
- elsif @and_set.length > 1
125
- ctx.out << "act_or(["
126
- @and_set.each {|r|
127
- ctx.out << "lambda{"
128
- r.gen_ruby(ctx)
129
- ctx.out << "},\n"
214
+ def initialize(and_sets)
215
+ @and_sets = and_sets
216
+ end
217
+ def preprocess(ctx)
218
+ @and_sets.each {|and_set| and_set.preprocess(ctx) }
219
+ end
220
+ def generate(ctx, source)
221
+ if @and_sets.length <= 1
222
+ # optimize
223
+ @and_sets.each {|and_set|
224
+ and_set.generate(ctx, source)
225
+ }
226
+ else
227
+ objs = []
228
+ @and_sets.each {|and_set|
229
+ obj = ctx.rule.with_lambda(source) {
230
+ and_set.generate(ctx, source)
231
+ }
232
+ objs.push obj
130
233
  }
131
- ctx.out << "])\n"
234
+ set_name = "#{objs.first}_or"
235
+ source << "Lambda* #{set_name}[] = {\n"
236
+ objs.each {|obj|
237
+ source << "&#{obj},\n"
238
+ }
239
+ source << "NULL\n"
240
+ source << "};\n"
241
+ source << "ctx->act_or(#{set_name});\n"
132
242
  end
133
243
  end
134
244
  end
135
-
245
+
136
246
  class AndSet
137
247
  def initialize(exprs)
138
248
  @exprs = exprs
139
249
  end
140
- def gen_ruby(ctx)
141
- @exprs.each {|r|
142
- r.gen_ruby(ctx)
143
- ctx.out << "\n"
250
+ def preprocess(ctx)
251
+ @exprs.each {|expr| expr.preprocess(ctx) }
252
+ end
253
+ def generate(ctx, source)
254
+ @exprs.each {|expr|
255
+ expr.generate(ctx, source)
144
256
  }
145
257
  end
146
258
  end
147
-
259
+
260
+
148
261
  class Expression
149
- def initialize(pred, var)
262
+ def initialize(pred, iter, var)
150
263
  @pred = pred
264
+ @iter = iter
151
265
  @var = var
152
266
  end
153
-
154
- def gen_ruby(ctx)
155
- ctx.out << "#{@var} = " if @var
156
- @pred.gen_ruby(ctx)
157
- end
158
- end
159
-
160
- class IterMany
161
- def initialize(pred)
162
- @pred = pred
163
- end
164
- def gen_ruby(ctx)
165
- ctx.out << "act_many(lambda{"
166
- @pred.gen_ruby(ctx)
167
- ctx.out << "})"
168
- end
169
- end
170
-
171
- class IterMany1
172
- def initialize(pred)
173
- @pred = pred
267
+ def preprocess(ctx)
268
+ ctx.register_var(@var) if @var
269
+ @pred.preprocess(ctx)
174
270
  end
175
- def gen_ruby(ctx)
176
- ctx.out << "act_many1(lambda{"
177
- @pred.gen_ruby(ctx)
178
- ctx.out << "})"
271
+ def generate(ctx, source)
272
+ source << "env[PARAM_#{@var}] = ({\n" if @var
273
+ if @iter
274
+ obj = ctx.rule.with_lambda(source) {
275
+ @pred.generate(ctx, source)
276
+ }
277
+ source << "ctx->act_#{@iter}(&#{obj});\n"
278
+ else
279
+ @pred.generate(ctx, source)
280
+ end
281
+ source << "});\n" if @var
179
282
  end
180
283
  end
181
-
182
- class IterMay
284
+
285
+
286
+ class PredNot
183
287
  def initialize(pred)
184
288
  @pred = pred
185
289
  end
186
- def gen_ruby(ctx)
187
- ctx.out << "act_may(lambda{"
188
- @pred.gen_ruby(ctx)
189
- ctx.out << "})"
190
- end
191
- end
192
-
193
- class Action
194
- def initialize(action)
195
- @action = action
290
+ def preprocess(ctx)
291
+ @pred.preprocess(ctx)
196
292
  end
197
- def gen_ruby(ctx)
198
- ctx.out << @action.to_s
293
+ def generate(ctx, source)
294
+ obj = ctx.rule.with_lambda(source) {
295
+ @pred.generate(ctx, source)
296
+ }
297
+ source << "ctx->act_not(&#{obj});\n"
199
298
  end
200
299
  end
201
-
202
- class Where
203
- def initialize(action)
204
- @action = action
205
- end
206
- def gen_ruby(ctx)
207
- ctx.out << "act_where(lambda{#{@action.to_s}})"
300
+
301
+ class PredLookahead
302
+ def initialize(literal)
303
+ @literal = literal
208
304
  end
209
- end
210
-
211
- class Apply
212
- def initialize(name, vars)
213
- @name = name
214
- @vars = vars
305
+ def preprocess(ctx)
306
+ @literal.preprocess(ctx)
215
307
  end
216
- def gen_ruby(ctx)
217
- ctx.out << "apply(:#{@name}#{@vars.map{|v|",#{v}"}})"
308
+ def generate(ctx, source)
309
+ obj = ctx.rule.with_lambda(source) {
310
+ @literal.generate(ctx, source)
311
+ }
312
+ source << "ctx->act_lookahead(&#{obj});\n"
218
313
  end
219
314
  end
220
-
221
- class Super
222
- def initialize(vars)
223
- @vars = vars
315
+
316
+
317
+ class LiteralSuper
318
+ def initialize(args)
319
+ @args = args
224
320
  end
225
- def gen_ruby(ctx)
226
- if @vars == nil
227
- ctx.out << "super"
228
- else
229
- ctx.out << "super(#{@vars.join(',')})"
230
- end
321
+ def preprocess(ctx) end
322
+ def generate(ctx, source)
323
+ source << "[super rule_#{ctx.rule.name}:ctx];\n"
231
324
  end
232
325
  end
233
-
234
- class Self
235
- def initialize(vars)
236
- @vars = vars
237
- end
238
- def gen_ruby(ctx)
239
- #name = "caller.find{|c|n = c.to_s.scan(/`rule_(.*)'/).flatten.first; break n if n}.to_sym"
240
- name = ctx.rule
241
- ctx.out << "apply(:#{name}#{@vars.map{|v|",#{v}"}})"
326
+
327
+ class LiteralEnd
328
+ def preprocess(ctx) end
329
+ def generate(ctx, source)
330
+ source << "ctx->act_end();\n";
242
331
  end
243
332
  end
244
-
245
- class Other
246
- def initialize(grammar, rule, args)
247
- @grammar = grammar
248
- @rule = rule
333
+
334
+ class LiteralApply
335
+ def initialize(name, args)
336
+ @name = name
249
337
  @args = args
250
338
  end
251
- def gen_ruby(ctx)
252
- ctx.out << "#{@grammar}.new(:#{@rule}).parse(@input#{@args.map{|v|",#{v}"}})"
339
+ def preprocess(ctx) end
340
+ def generate(ctx, source)
341
+ source << "ctx->apply(@selector(rule_#{@name}:));\n"
253
342
  end
254
343
  end
255
-
344
+
345
+
256
346
  class LiteralQuotedToken
257
347
  def initialize(str)
258
348
  @str = str
259
349
  end
260
- def gen_ruby(ctx)
261
- ctx.out << "act_token('#{@str}')"
350
+ def preprocess(ctx) end
351
+ def generate(ctx, source)
352
+ str, str_len = Generator.escape_quoated_string(@str)
353
+ if str_len == 1
354
+ # FIXME multibyte character optimize
355
+ source << "ctx->act_char(\"#{str}\",#{str_len});\n"
356
+ else
357
+ source << "ctx->act_token(\"#{str}\",#{str_len});\n"
358
+ end
262
359
  end
263
360
  end
264
-
361
+
265
362
  class LiteralToken
266
363
  def initialize(str)
267
364
  @str = str
268
365
  end
269
- def gen_ruby(ctx)
270
- ctx.out << "act_token(\"#{@str}\")"
366
+ def preprocess(ctx) end
367
+ def generate(ctx, source)
368
+ str = @str
369
+ str_len = Generator.string_length(str)
370
+ if str_len == 1
371
+ # FIXME multibyte character optimize
372
+ source << "ctx->act_char(\"#{str}\",#{str_len});\n"
373
+ else
374
+ source << "ctx->act_token(\"#{str}\",#{str_len});\n"
375
+ end
271
376
  end
272
377
  end
273
-
274
- class LiteralCharset
378
+
379
+ class LiteralCharclass
275
380
  def initialize(set)
276
381
  @set = set
277
382
  end
278
- def gen_ruby(ctx)
279
- ctx.out << "act_charset(%[#{@set}])"
383
+ def preprocess(ctx) end
384
+ def generate(ctx, source)
385
+ set, set_len = Generator.escape_quoated_string(@set)
386
+ reg = "[" << set << "]" # FIXME \\A
387
+ reg_len = set_len + 2
388
+ # FIXME regexp precompile
389
+ source << "ctx->act_charclass(\"#{reg}\",#{reg_len});\n"
280
390
  end
281
391
  end
282
-
392
+
283
393
  class LiteralAny
284
- def gen_ruby(ctx)
285
- ctx.out << "act_any"
394
+ def preprocess(ctx) end
395
+ def generate(ctx, source)
396
+ source << "ctx->act_any();\n"
286
397
  end
287
398
  end
288
-
289
- class PredNot
290
- def initialize(expr)
291
- @expr = expr
399
+
400
+ class Action
401
+ def initialize(code)
402
+ @code = code
292
403
  end
293
- def gen_ruby(ctx)
294
- ctx.out << "act_not(lambda{"
295
- @expr.gen_ruby(ctx)
296
- ctx.out << "})"
404
+ def preprocess(ctx) end
405
+ def generate(ctx, source)
406
+ code, code_len = Generator.escape_quoated_string(@code)
407
+ source << "ctx->act_run(env,\"#{code}\",#{code_len});\n"
297
408
  end
298
409
  end
299
-
300
- class PredLookahead
301
- def initialize(expr)
302
- @expr = expr
410
+
411
+ class Where
412
+ def initialize(code)
413
+ @code = code
303
414
  end
304
- def gen_ruby(ctx)
305
- ctx.out << "act_lookahead(lambda{"
306
- @expr.gen_ruby(ctx)
307
- ctx.out << "})"
415
+ def preprocess(ctx) end
416
+ def generate(ctx, source)
417
+ code, code_len = Generator.escape_quoated_string(@code)
418
+ source << "ctx->act_where(env,\"#{code}\",#{code_len});\n"
308
419
  end
309
420
  end
310
-
421
+
311
422
  end
312
423
  end
313
424
 
314
-