~ubuntu-branches/ubuntu/trusty/ruby1.9/trusty

« back to all changes in this revision

Viewing changes to tool/instruction.rb

  • Committer: Bazaar Package Importer
  • Author(s): Stephan Hermann
  • Date: 2008-01-24 11:42:29 UTC
  • mfrom: (1.1.9 upstream)
  • Revision ID: james.westby@ubuntu.com-20080124114229-jw2f87rdxlq6gp11
Tags: 1.9.0.0-2ubuntu1
* Merge from debian unstable, remaining changes:
  - Robustify check for target_os, fixing build failure on lpia.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#
 
2
#
 
3
#
 
4
 
 
5
require 'erb'
 
6
 
 
7
module RubyVM
 
8
  class Instruction
 
9
    def initialize name, opes, pops, rets, comm, body, tvars, sp_inc,
 
10
                   orig = self, defopes = [], type = nil,
 
11
                   nsc = [], psc = [[], []]
 
12
 
 
13
      @name = name
 
14
      @opes = opes # [[type, name], ...]
 
15
      @pops = pops # [[type, name], ...]
 
16
      @rets = rets # [[type, name], ...]
 
17
      @comm = comm # {:c => category, :e => en desc, :j => ja desc}
 
18
      @body = body # '...'
 
19
 
 
20
      @orig    = orig
 
21
      @defopes = defopes
 
22
      @type    = type
 
23
      @tvars   = tvars
 
24
 
 
25
      @nextsc = nsc
 
26
      @pushsc = psc
 
27
      @sc     = []
 
28
      @unifs  = []
 
29
      @optimized = []
 
30
      @is_sc  = false
 
31
      @sp_inc = sp_inc
 
32
    end
 
33
 
 
34
    def add_sc sci
 
35
      @sc << sci
 
36
      sci.set_sc
 
37
    end
 
38
    
 
39
    attr_reader :name, :opes, :pops, :rets
 
40
    attr_reader :body, :comm
 
41
    attr_reader :nextsc, :pushsc
 
42
    attr_reader :orig, :defopes, :type
 
43
    attr_reader :sc
 
44
    attr_reader :unifs, :optimized
 
45
    attr_reader :is_sc
 
46
    attr_reader :tvars
 
47
    attr_reader :sp_inc
 
48
    
 
49
    def set_sc
 
50
      @is_sc = true
 
51
    end
 
52
    
 
53
    def add_unif insns
 
54
      @unifs << insns
 
55
    end
 
56
 
 
57
    def add_optimized insn
 
58
      @optimized << insn
 
59
    end
 
60
 
 
61
    def sp_increase_c_expr
 
62
      if(pops.any?{|t, v| v == '...'} ||
 
63
         rets.any?{|t, v| v == '...'})
 
64
        # user definision
 
65
        raise "no sp increase definition" if @sp_inc.nil?
 
66
        ret = "int inc = 0;\n"
 
67
 
 
68
        @opes.each_with_index{|(t, v), i|
 
69
          if t == 'rb_num_t'
 
70
            ret << "        unsigned long #{v} = FIX2INT(opes[#{i}]);\n"
 
71
          end
 
72
        }
 
73
        @defopes.each_with_index{|((t, var), val), i|
 
74
          if t == 'rb_num_t' && val != '*'
 
75
            ret << "        unsigned long #{var} = #{val};\n"
 
76
          end
 
77
        }
 
78
 
 
79
        ret << "        #{@sp_inc};\n"
 
80
        ret << "        return depth + inc;"
 
81
        ret
 
82
      else
 
83
        "return depth + #{rets.size - pops.size};"
 
84
      end
 
85
    end
 
86
    
 
87
    def inspect
 
88
      "#<Instruction:#{@name}>"
 
89
    end
 
90
  end
 
91
 
 
92
  class InstructionsLoader
 
93
    def initialize opts = {}
 
94
      @insns    = []
 
95
      @insn_map = {}
 
96
 
 
97
      @vpath = opts[:VPATH] || File
 
98
      @use_const = opts[:use_const]
 
99
      @verbose   = opts[:verbose]
 
100
      @destdir   = opts[:destdir]
 
101
 
 
102
      (@vm_opts = load_vm_opts).each {|k, v|
 
103
        @vm_opts[k] = opts[k] if opts.key?(k)
 
104
      }
 
105
 
 
106
      load_insns_def opts[:"insns.def"] || 'insns.def'
 
107
 
 
108
      load_opt_operand_def opts[:"opope.def"] || 'opt_operand.def'
 
109
      load_insn_unification_def opts[:"unif.def"] || 'opt_insn_unif.def'
 
110
      make_stackcaching_insns if vm_opt?('STACK_CACHING')
 
111
    end
 
112
 
 
113
    attr_reader :vpath
 
114
    attr_reader :destdir
 
115
  
 
116
    %w[use_const verbose].each do |attr|
 
117
      attr_reader attr
 
118
      alias_method "#{attr}?", attr
 
119
      remove_method attr
 
120
    end
 
121
 
 
122
    def [](s)
 
123
      @insn_map[s.to_s]
 
124
    end
 
125
 
 
126
    def each
 
127
      @insns.each{|insn|
 
128
        yield insn
 
129
      }
 
130
    end
 
131
 
 
132
    def size
 
133
      @insns.size
 
134
    end
 
135
 
 
136
    ###
 
137
    private
 
138
 
 
139
    def vm_opt? name
 
140
      @vm_opts[name]
 
141
    end
 
142
 
 
143
    def load_vm_opts file = nil
 
144
      file ||= 'vm_opts.h'
 
145
      opts = {}
 
146
      vpath.open(file) do |f|
 
147
        f.grep(/^\#define\s+OPT_([A-Z_]+)\s+(\d+)/) do
 
148
          opts[$1] = !$2.to_i.zero?
 
149
        end
 
150
      end
 
151
      opts
 
152
    end
 
153
 
 
154
    SKIP_COMMENT_PATTERN = Regexp.compile(Regexp.escape('/** ##skip'))
 
155
 
 
156
    include Enumerable
 
157
 
 
158
    def add_insn insn
 
159
      @insns << insn
 
160
      @insn_map[insn.name] = insn
 
161
    end
 
162
 
 
163
    def make_insn name, opes, pops, rets, comm, body, sp_inc
 
164
      add_insn Instruction.new(name, opes, pops, rets, comm, body, [], sp_inc)
 
165
    end
 
166
 
 
167
    # str -> [[type, var], ...]
 
168
    def parse_vars line
 
169
      raise unless /\((.*?)\)/ =~ line
 
170
      vars = $1.split(',')
 
171
      vars.map!{|v|
 
172
        if /\s*(\S+)\s+(\S+)\s*/ =~ v
 
173
          type = $1
 
174
          var  = $2
 
175
        elsif /\s*\.\.\.\s*/ =~ v
 
176
          type = var  = '...'
 
177
        else
 
178
          raise
 
179
        end
 
180
        [type, var]
 
181
      }
 
182
      vars
 
183
    end
 
184
 
 
185
    def parse_comment comm
 
186
      c = 'others'
 
187
      j = ''
 
188
      e = ''
 
189
      comm.each_line{|line|
 
190
        case line
 
191
        when /@c (.+)/
 
192
          c = $1
 
193
        when /@e (.+)/
 
194
          e = $1
 
195
        when /@e\s*$/
 
196
          e = ''
 
197
        when /@j (.+)$/
 
198
          j = $1
 
199
        when /@j\s*$/
 
200
          j = ''
 
201
        end
 
202
      }
 
203
      { :c => c,
 
204
        :e => e,
 
205
        :j => j,
 
206
      }
 
207
    end
 
208
 
 
209
    def load_insns_def file
 
210
      body = insn = opes = pops = rets = nil
 
211
      comment = ''
 
212
 
 
213
      vpath.open(file) {|f|
 
214
        f.instance_variable_set(:@line_no, 0)
 
215
        class << f
 
216
          def line_no
 
217
            @line_no
 
218
          end
 
219
          def gets
 
220
            @line_no += 1
 
221
            super
 
222
          end
 
223
        end
 
224
 
 
225
        while line = f.gets
 
226
          line.chomp!
 
227
          case line
 
228
 
 
229
          when SKIP_COMMENT_PATTERN
 
230
            while line = f.gets.chomp
 
231
              if /\s+\*\/$/ =~ line
 
232
                break
 
233
              end
 
234
            end
 
235
 
 
236
            # collect instruction comment
 
237
          when /^\/\*\*$/
 
238
            while line = f.gets
 
239
              if /\s+\*\/\s*$/ =~ line
 
240
                break
 
241
              else
 
242
                comment << line
 
243
              end
 
244
            end
 
245
 
 
246
            # start instruction body
 
247
          when /^DEFINE_INSN$/
 
248
            insn = f.gets.chomp
 
249
            opes = parse_vars(f.gets.chomp)
 
250
            pops = parse_vars(f.gets.chomp).reverse
 
251
            rets_str = f.gets.chomp
 
252
            rets = parse_vars(rets_str).reverse
 
253
            comment = parse_comment(comment)
 
254
            insn_in = true
 
255
            body    = ''
 
256
 
 
257
            sp_inc = rets_str[%r"//\s*(.+)", 1]
 
258
 
 
259
            raise unless /^\{$/ =~ f.gets.chomp
 
260
            line_no = f.line_no
 
261
 
 
262
          # end instruction body
 
263
          when /^\}/
 
264
            if insn_in
 
265
              body.instance_variable_set(:@line_no, line_no)
 
266
              body.instance_variable_set(:@file, f.path)
 
267
              insn = make_insn(insn, opes, pops, rets, comment, body, sp_inc)
 
268
              insn_in = false
 
269
              comment = ''
 
270
            end
 
271
 
 
272
          else
 
273
            if insn_in
 
274
              body << line + "\n"
 
275
            end
 
276
          end
 
277
        end
 
278
      }
 
279
    end
 
280
 
 
281
    ## opt op
 
282
    def load_opt_operand_def file
 
283
      vpath.foreach(file) {|line|
 
284
        line = line.gsub(/\#.*/, '').strip
 
285
        next  if line.length == 0
 
286
        break if /__END__/ =~ line
 
287
        /(\S+)\s+(.+)/ =~ line
 
288
        insn = $1
 
289
        opts = $2
 
290
        add_opt_operand insn, opts.split(/,/).map{|e| e.strip}
 
291
      } if file
 
292
    end
 
293
 
 
294
    def label_escape label
 
295
      label.gsub(/\(/, '_O_').
 
296
      gsub(/\)/, '_C_').
 
297
      gsub(/\*/, '_WC_')
 
298
    end
 
299
 
 
300
    def add_opt_operand insn_name, opts
 
301
      insn = @insn_map[insn_name]
 
302
      opes = insn.opes
 
303
 
 
304
      if opes.size != opts.size
 
305
        raise "operand size mismatcvh for #{insn.name} (opes: #{opes.size}, opts: #{opts.size})"
 
306
      end
 
307
 
 
308
      ninsn = insn.name + '_OP_' + opts.map{|e| label_escape(e)}.join('_')
 
309
      nopes = []
 
310
      defv  = []
 
311
 
 
312
      opts.each_with_index{|e, i|
 
313
        if e == '*'
 
314
          nopes << opes[i]
 
315
        end
 
316
        defv  << [opes[i], e]
 
317
      }
 
318
 
 
319
      make_insn_operand_optimiized(insn, ninsn, nopes, defv)
 
320
    end
 
321
 
 
322
    def make_insn_operand_optimiized orig_insn, name, opes, defopes
 
323
      comm = orig_insn.comm.dup
 
324
      comm[:c] = 'optimize'
 
325
      add_insn insn = Instruction.new(
 
326
        name, opes, orig_insn.pops, orig_insn.rets, comm,
 
327
        orig_insn.body, orig_insn.tvars, orig_insn.sp_inc,
 
328
        orig_insn, defopes)
 
329
      orig_insn.add_optimized insn
 
330
    end
 
331
 
 
332
    ## insn unif
 
333
    def load_insn_unification_def file
 
334
      vpath.foreach(file) {|line|
 
335
        line = line.gsub(/\#.*/, '').strip
 
336
        next  if line.length == 0
 
337
        break if /__END__/ =~ line
 
338
        make_unified_insns line.split.map{|e|
 
339
          raise "unknown insn: #{e}" unless @insn_map[e]
 
340
          @insn_map[e]
 
341
        }
 
342
      } if file
 
343
    end
 
344
 
 
345
    def all_combination sets
 
346
      ret = sets.shift.map{|e| [e]}
 
347
 
 
348
      sets.each{|set|
 
349
        prev = ret
 
350
        ret  = []
 
351
        prev.each{|ary|
 
352
          set.each{|e|
 
353
            eary = ary.dup
 
354
            eary << e
 
355
            ret  << eary
 
356
          }
 
357
        }
 
358
      }
 
359
      ret
 
360
    end
 
361
 
 
362
    def make_unified_insns insns
 
363
      if vm_opt?('UNIFY_ALL_COMBINATION')
 
364
        insn_sets = insns.map{|insn|
 
365
          [insn] + insn.optimized
 
366
        }
 
367
 
 
368
        all_combination(insn_sets).each{|insns_set|
 
369
          make_unified_insn_each insns_set
 
370
        }
 
371
      else
 
372
        make_unified_insn_each insns
 
373
      end
 
374
    end
 
375
 
 
376
    def mk_private_val vals, i, redef
 
377
      vals.dup.map{|v|
 
378
        # v[0] : type
 
379
        # v[1] : var name
 
380
 
 
381
        v = v.dup
 
382
        if v[0] != '...'
 
383
          redef[v[1]] = v[0]
 
384
          v[1] = "#{v[1]}_#{i}"
 
385
        end
 
386
        v
 
387
      }
 
388
    end
 
389
 
 
390
    def mk_private_val2 vals, i, redef
 
391
      vals.dup.map{|v|
 
392
        # v[0][0] : type
 
393
        # v[0][1] : var name
 
394
        # v[1] : default val
 
395
 
 
396
        pv = v.dup
 
397
        v = pv[0] = pv[0].dup
 
398
        if v[0] != '...'
 
399
          redef[v[1]] = v[0]
 
400
          v[1] = "#{v[1]}_#{i}"
 
401
        end
 
402
        pv
 
403
      }
 
404
    end
 
405
 
 
406
    def make_unified_insn_each insns
 
407
      names = []
 
408
      opes = []
 
409
      pops = []
 
410
      rets = []
 
411
      comm = {
 
412
        :c => 'optimize',
 
413
        :e => 'unified insn',
 
414
        :j => 'unified insn',
 
415
      }
 
416
      body = ''
 
417
      passed = []
 
418
      tvars = []
 
419
      defopes = []
 
420
      sp_inc = ''
 
421
 
 
422
      insns.each_with_index{|insn, i|
 
423
        names << insn.name
 
424
        redef_vars = {}
 
425
 
 
426
        e_opes = mk_private_val(insn.opes, i, redef_vars)
 
427
        e_pops = mk_private_val(insn.pops, i, redef_vars)
 
428
        e_rets = mk_private_val(insn.rets, i, redef_vars)
 
429
        # ToDo: fix it
 
430
        e_defs = mk_private_val2(insn.defopes, i, redef_vars)
 
431
 
 
432
        passed_vars = []
 
433
        while pvar = e_pops.pop
 
434
          rvar = rets.pop
 
435
          if rvar
 
436
            raise "unsupported unif insn: #{insns.inspect}" if rvar[0] == '...'
 
437
            passed_vars << [pvar, rvar]
 
438
            tvars << rvar
 
439
          else
 
440
            e_pops.push pvar
 
441
            break
 
442
          end
 
443
        end
 
444
 
 
445
        opes.concat e_opes
 
446
        pops.concat e_pops
 
447
        rets.concat e_rets
 
448
        defopes.concat e_defs
 
449
        sp_inc += "#{insn.sp_inc}"
 
450
 
 
451
        body += "{ /* unif: #{i} */\n" +
 
452
                passed_vars.map{|rpvars|
 
453
                  pv = rpvars[0]
 
454
                  rv = rpvars[1]
 
455
                  "#define #{pv[1]} #{rv[1]}"
 
456
                }.join("\n") +
 
457
                "\n" + 
 
458
                redef_vars.map{|v, type|
 
459
                  "#define #{v} #{v}_#{i}"
 
460
                }.join("\n") + "\n" +
 
461
                insn.body +
 
462
                passed_vars.map{|rpvars|
 
463
                  "#undef #{rpvars[0][1]}"
 
464
                }.join("\n") +
 
465
                "\n" +
 
466
                redef_vars.keys.map{|v|
 
467
                  "#undef  #{v}"
 
468
                }.join("\n") +
 
469
                "\n}\n"
 
470
      }
 
471
 
 
472
      tvars_ary = []
 
473
      tvars.each{|tvar|
 
474
        unless opes.any?{|var|
 
475
          var[1] == tvar[1]
 
476
        } || defopes.any?{|pvar|
 
477
          pvar[0][1] == tvar[1]
 
478
        }
 
479
          tvars_ary << tvar
 
480
        end
 
481
      }
 
482
      add_insn insn = Instruction.new("UNIFIED_" + names.join('_'),
 
483
                                   opes, pops, rets.reverse, comm, body,
 
484
                                   tvars_ary, sp_inc)
 
485
      insn.defopes.replace defopes
 
486
      insns[0].add_unif [insn, insns]
 
487
    end
 
488
 
 
489
    ## sc
 
490
    SPECIAL_INSN_FOR_SC_AFTER = {
 
491
      /\Asend/      => [:a],
 
492
      /\Aend/       => [:a],
 
493
      /\Ayield/     => [:a],
 
494
      /\Aclassdef/  => [:a],
 
495
      /\Amoduledef/ => [:a],
 
496
    }
 
497
    FROM_SC = [[], [:a], [:b], [:a, :b], [:b, :a]]
 
498
 
 
499
    def make_stackcaching_insns
 
500
      pops = rets = nil
 
501
 
 
502
      @insns.dup.each{|insn|
 
503
        opops = insn.pops
 
504
        orets = insn.rets
 
505
        oopes = insn.opes
 
506
        ocomm = insn.comm
 
507
 
 
508
        after = nil
 
509
        SPECIAL_INSN_FOR_SC_AFTER.any?{|k, v|
 
510
          if k =~ insn.name
 
511
            after = v
 
512
            break
 
513
          end
 
514
        }
 
515
 
 
516
        insns = []
 
517
        FROM_SC.each{|from|
 
518
          name, pops, rets, pushs1, pushs2, nextsc =
 
519
          *calc_stack(insn, from, after, opops, orets)
 
520
 
 
521
          make_insn_sc(insn, name, oopes, pops, rets, [pushs1, pushs2], nextsc)
 
522
        }
 
523
      }
 
524
    end
 
525
 
 
526
    def make_insn_sc orig_insn, name, opes, pops, rets, pushs, nextsc
 
527
      comm = orig_insn.comm.dup
 
528
      comm[:c] = 'optimize(sc)'
 
529
 
 
530
      scinsn = Instruction.new(
 
531
        name, opes, pops, rets, comm,
 
532
        orig_insn.body, orig_insn.tvars, orig_insn.sp_inc,
 
533
        orig_insn, orig_insn.defopes, :sc, nextsc, pushs)
 
534
 
 
535
      add_insn scinsn
 
536
      orig_insn.add_sc scinsn
 
537
    end
 
538
 
 
539
    def self.complement_name st
 
540
      "#{st[0] ? st[0] : 'x'}#{st[1] ? st[1] : 'x'}"
 
541
    end
 
542
 
 
543
    def add_stack_value st
 
544
      len = st.length
 
545
      if len == 0
 
546
        st[0] = :a
 
547
        [nil, :a]
 
548
      elsif len == 1
 
549
        if st[0] == :a
 
550
          st[1] = :b
 
551
        else
 
552
          st[1] = :a
 
553
        end
 
554
        [nil, st[1]]
 
555
      else
 
556
        st[0], st[1] = st[1], st[0]
 
557
        [st[1], st[1]]
 
558
      end
 
559
    end
 
560
 
 
561
    def calc_stack insn, ofrom, oafter, opops, orets
 
562
      from = ofrom.dup
 
563
      pops = opops.dup
 
564
      rets = orets.dup
 
565
      rest_scr = ofrom.dup
 
566
 
 
567
      pushs_before = []
 
568
      pushs= []
 
569
 
 
570
      pops.each_with_index{|e, i|
 
571
        if e[0] == '...'
 
572
          pushs_before = from
 
573
          from = []
 
574
        end
 
575
        r = from.pop
 
576
        break unless r
 
577
        pops[i] = pops[i].dup << r
 
578
      }
 
579
 
 
580
      if oafter
 
581
        from = oafter
 
582
        from.each_with_index{|r, i|
 
583
          rets[i] = rets[i].dup << r if rets[i]
 
584
        }
 
585
      else
 
586
        rets = rets.reverse
 
587
        rets.each_with_index{|e, i|
 
588
          break if e[0] == '...'
 
589
          pushed, r = add_stack_value from
 
590
          rets[i] = rets[i].dup << r
 
591
          if pushed
 
592
            if rest_scr.pop
 
593
              pushs << pushed
 
594
            end
 
595
 
 
596
            if i - 2 >= 0
 
597
              rets[i-2].pop
 
598
            end
 
599
          end
 
600
        }
 
601
      end
 
602
 
 
603
      if false #|| insn.name =~ /test3/
 
604
        p ofrom
 
605
        p pops
 
606
        p rets
 
607
        p pushs_before
 
608
        p pushs
 
609
        p from
 
610
        exit
 
611
      end
 
612
 
 
613
      ret = ["#{insn.name}_SC_#{InstructionsLoader.complement_name(ofrom)}_#{complement_name(from)}",
 
614
            pops, rets, pushs_before, pushs, from]
 
615
    end
 
616
  end
 
617
 
 
618
  class SourceCodeGenerator
 
619
    def initialize insns
 
620
      @insns = insns
 
621
    end
 
622
 
 
623
    attr_reader :insns
 
624
 
 
625
    def generate
 
626
      raise "should not reach here"
 
627
    end
 
628
 
 
629
    def vpath
 
630
      @insns.vpath
 
631
    end
 
632
 
 
633
    def verbose?
 
634
      @insns.verbose?
 
635
    end
 
636
 
 
637
    def use_const?
 
638
      @insns.use_const?
 
639
    end
 
640
 
 
641
    def build_string
 
642
      @lines = []
 
643
      yield
 
644
      @lines.join("\n")
 
645
    end
 
646
 
 
647
    EMPTY_STRING = ''.freeze
 
648
 
 
649
    def commit str = EMPTY_STRING
 
650
      @lines << str
 
651
    end
 
652
 
 
653
    def comment str
 
654
      @lines << str if verbose?
 
655
    end
 
656
 
 
657
    def output_path(fn)
 
658
      d = @insns.destdir
 
659
      fn = File.join(d, fn) if d
 
660
      fn
 
661
    end
 
662
  end
 
663
 
 
664
  ###################################################################
 
665
  # vm.inc
 
666
  class VmBodyGenerator < SourceCodeGenerator
 
667
    # vm.inc
 
668
    def generate
 
669
      vm_body = ''
 
670
      @insns.each{|insn|
 
671
        vm_body << "\n"
 
672
        vm_body << make_insn_def(insn)
 
673
      }
 
674
      src = vpath.read('template/vm.inc.tmpl')
 
675
      ERB.new(src).result(binding)
 
676
    end
 
677
 
 
678
    def generate_from_insnname insnname
 
679
      make_insn_def @insns[insnname.to_s]
 
680
    end
 
681
 
 
682
    #######
 
683
    private
 
684
 
 
685
    def make_header_prepare_stack insn
 
686
      comment "  /* prepare stack status */"
 
687
 
 
688
      push_ba = insn.pushsc
 
689
      raise "unsupport" if push_ba[0].size > 0 && push_ba[1].size > 0
 
690
 
 
691
      push_ba.each{|pushs|
 
692
        pushs.each{|r|
 
693
          commit "  PUSH(SCREG(#{r}));"
 
694
        }
 
695
      }
 
696
    end
 
697
  
 
698
    def make_header_operands insn
 
699
      comment "  /* declare and get from iseq */"
 
700
 
 
701
      vars = insn.opes
 
702
      n = 0
 
703
      ops = []
 
704
 
 
705
      vars.each_with_index{|(type, var), i|
 
706
        if type == '...'
 
707
          break
 
708
        end
 
709
 
 
710
        ops << "  #{type} #{var} = (#{type})GET_OPERAND(#{i+1});"
 
711
        n   += 1
 
712
      }
 
713
      @opn = n
 
714
 
 
715
      # reverse or not?
 
716
      # ops.join
 
717
      commit ops.reverse
 
718
    end
 
719
  
 
720
    def make_header_default_operands insn
 
721
      vars = insn.defopes
 
722
 
 
723
      vars.each{|e|
 
724
        next if e[1] == '*'
 
725
        if use_const?
 
726
          commit "  const #{e[0][0]} #{e[0][1]} = #{e[1]};"
 
727
        else
 
728
          commit "  #define #{e[0][1]} #{e[1]}"
 
729
        end
 
730
      }
 
731
    end
 
732
 
 
733
    def make_footer_default_operands insn
 
734
      comment " /* declare and initialize default opes */"
 
735
      if use_const?
 
736
        commit
 
737
      else
 
738
        vars = insn.defopes
 
739
 
 
740
        vars.each{|e|
 
741
          next if e[1] == '*'
 
742
          commit "#undef #{e[0][1]}"
 
743
        }
 
744
      end
 
745
    end
 
746
 
 
747
    def make_header_stack_pops insn
 
748
      comment "  /* declare and pop from stack */"
 
749
 
 
750
      n = 0
 
751
      pops = []
 
752
      vars = insn.pops
 
753
      vars.each_with_index{|iter, i|
 
754
        type, var, r = *iter
 
755
        if type == '...'
 
756
          break
 
757
        end
 
758
        if r
 
759
          pops << "  #{type} #{var} = SCREG(#{r});"
 
760
        else
 
761
          pops << "  #{type} #{var} = TOPN(#{n});"
 
762
          n += 1
 
763
        end
 
764
      }
 
765
      @popn = n
 
766
 
 
767
      # reverse or not?
 
768
      commit pops.reverse
 
769
    end
 
770
 
 
771
    def make_header_temporary_vars insn
 
772
      comment "  /* declare temporary vars */"
 
773
 
 
774
      insn.tvars.each{|var|
 
775
        commit "  #{var[0]} #{var[1]};"
 
776
      }
 
777
    end
 
778
 
 
779
    def make_header_stack_val insn
 
780
      comment "/* declare stack push val */"
 
781
 
 
782
      vars = insn.opes + insn.pops + insn.defopes.map{|e| e[0]}
 
783
 
 
784
      insn.rets.each{|var|
 
785
        if vars.all?{|e| e[1] != var[1]} && var[1] != '...'
 
786
          commit "  #{var[0]} #{var[1]};"
 
787
        end
 
788
      }
 
789
    end
 
790
 
 
791
    def make_header_analysis insn
 
792
      commit "  USAGE_ANALYSIS_INSN(BIN(#{insn.name}));"
 
793
      insn.opes.each_with_index{|op, i|
 
794
        commit "  USAGE_ANALYSIS_OPERAND(BIN(#{insn.name}), #{i}, #{op[1]});"
 
795
      }
 
796
    end
 
797
 
 
798
    def make_header_pc insn
 
799
      commit  "  ADD_PC(1+#{@opn});"
 
800
      commit  "  PREFETCH(GET_PC());"
 
801
    end
 
802
 
 
803
    def make_header_popn insn
 
804
      comment "  /* management */"
 
805
      commit  "  POPN(#{@popn});" if @popn > 0
 
806
    end
 
807
 
 
808
    def make_hader_debug insn
 
809
      comment "  /* for debug */"
 
810
      commit  "  DEBUG_ENTER_INSN(\"#{insn.name}\");"
 
811
    end
 
812
 
 
813
    def make_header_defines insn
 
814
      commit  "  #define CURRENT_INSN_#{insn.name} 1"
 
815
      commit  "  #define INSN_IS_SC()     #{insn.sc ? 0 : 1}"
 
816
      commit  "  #define INSN_LABEL(lab)  LABEL_#{insn.name}_##lab"
 
817
      commit  "  #define LABEL_IS_SC(lab) LABEL_##lab##_###{insn.sc.size == 0 ? 't' : 'f'}"
 
818
    end
 
819
 
 
820
    def make_footer_stack_val insn
 
821
      comment "  /* push stack val */"
 
822
 
 
823
      insn.rets.reverse_each{|v|
 
824
        if v[1] == '...'
 
825
          break
 
826
        end
 
827
        if v[2]
 
828
          commit "  SCREG(#{v[2]}) = #{v[1]};"
 
829
        else
 
830
          commit "  PUSH(#{v[1]});"
 
831
        end
 
832
      }
 
833
    end
 
834
 
 
835
    def make_footer_undefs insn
 
836
      commit "#undef CURRENT_INSN_#{insn.name}"
 
837
      commit "#undef INSN_IS_SC"
 
838
      commit "#undef INSN_LABEL"
 
839
      commit "#undef LABEL_IS_SC"
 
840
    end
 
841
 
 
842
    def make_header insn
 
843
      commit "INSN_ENTRY(#{insn.name}){"
 
844
      make_header_prepare_stack insn
 
845
      commit "{"
 
846
      make_header_stack_val  insn
 
847
      make_header_default_operands insn
 
848
      make_header_operands   insn
 
849
      make_header_stack_pops insn
 
850
      make_header_temporary_vars insn
 
851
      #
 
852
      make_hader_debug insn
 
853
      make_header_pc insn
 
854
      make_header_popn insn
 
855
      make_header_defines insn
 
856
      make_header_analysis insn
 
857
      commit  "{"
 
858
    end
 
859
 
 
860
    def make_footer insn
 
861
      make_footer_stack_val insn
 
862
      make_footer_default_operands insn
 
863
      make_footer_undefs insn
 
864
      commit "  END_INSN(#{insn.name});}}}"
 
865
    end
 
866
 
 
867
    def make_insn_def insn
 
868
      build_string do
 
869
        make_header insn
 
870
        if line = insn.body.instance_variable_get(:@line_no)
 
871
          file = insn.body.instance_variable_get(:@file)
 
872
          commit "#line #{line+1} \"#{file}\""
 
873
          commit insn.body
 
874
          commit '#line __CURRENT_LINE__ "__CURRENT_FILE__"'
 
875
        else
 
876
          insn.body
 
877
        end
 
878
        make_footer(insn)
 
879
      end
 
880
    end
 
881
  end
 
882
 
 
883
  ###################################################################
 
884
  # vmtc.inc
 
885
  class VmTCIncGenerator < SourceCodeGenerator
 
886
    def generate
 
887
 
 
888
      insns_table = build_string do
 
889
        @insns.each{|insn|
 
890
          commit "  LABEL_PTR(#{insn.name}),"
 
891
        }
 
892
      end
 
893
 
 
894
      insn_end_table = build_string do
 
895
        @insns.each{|insn|
 
896
          commit "  ELABEL_PTR(#{insn.name}),\n"
 
897
        }
 
898
      end
 
899
    
 
900
      ERB.new(vpath.read('template/vmtc.inc.tmpl')).result(binding)
 
901
    end
 
902
  end
 
903
 
 
904
  ###################################################################
 
905
  # insns_info.inc
 
906
  class InsnsInfoIncGenerator < SourceCodeGenerator
 
907
    def generate
 
908
      insns_info_inc
 
909
    end
 
910
 
 
911
    ###
 
912
    private
 
913
 
 
914
    def op2typesig op
 
915
      case op
 
916
      when /^OFFSET/
 
917
        "TS_OFFSET"
 
918
      when /^rb_num_t/
 
919
        "TS_NUM"
 
920
      when /^lindex_t/
 
921
        "TS_LINDEX"
 
922
      when /^dindex_t/
 
923
        "TS_DINDEX"
 
924
      when /^VALUE/
 
925
        "TS_VALUE"
 
926
      when /^ID/
 
927
        "TS_ID"
 
928
      when /GENTRY/
 
929
        "TS_GENTRY"
 
930
      when /^IC/
 
931
        "TS_IC"
 
932
      when /^\.\.\./
 
933
        "TS_VARIABLE"
 
934
      when /^CDHASH/
 
935
        "TS_CDHASH"
 
936
      when /^ISEQ/
 
937
        "TS_ISEQ"
 
938
      when /rb_insn_func_t/
 
939
        "TS_FUNCPTR"
 
940
      else
 
941
        raise "unknown op type: #{op}"
 
942
      end
 
943
    end
 
944
 
 
945
    TYPE_CHARS = {
 
946
      'TS_OFFSET'    => 'O',
 
947
      'TS_NUM'       => 'N',
 
948
      'TS_LINDEX'    => 'L',
 
949
      'TS_DINDEX'    => 'D',
 
950
      'TS_VALUE'     => 'V',
 
951
      'TS_ID'        => 'I',
 
952
      'TS_GENTRY'    => 'G',
 
953
      'TS_IC'        => 'C',
 
954
      'TS_CDHASH'    => 'H',
 
955
      'TS_ISEQ'      => 'S',
 
956
      'TS_VARIABLE'  => '.',
 
957
      'TS_FUNCPTR'   => 'F',
 
958
    }
 
959
 
 
960
    # insns_info.inc
 
961
    def insns_info_inc
 
962
      # insn_type_chars
 
963
      insn_type_chars = TYPE_CHARS.map{|t, c|
 
964
        "#define #{t} '#{c}'"
 
965
      }.join("\n")
 
966
 
 
967
      # insn_names
 
968
      insn_names = ''
 
969
      @insns.each{|insn|
 
970
        insn_names << "  \"#{insn.name}\",\n"
 
971
      }
 
972
 
 
973
      # operands info
 
974
      operands_info = ''
 
975
      operands_num_info = ''
 
976
      @insns.each{|insn|
 
977
        opes = insn.opes
 
978
        operands_info << '  '
 
979
        ot = opes.map{|type, var|
 
980
          TYPE_CHARS.fetch(op2typesig(type))
 
981
        }
 
982
        operands_info << "\"#{ot.join}\"" << ", \n"
 
983
 
 
984
        num = opes.size + 1
 
985
        operands_num_info << "  #{num},\n"
 
986
      }
 
987
 
 
988
      # stack num
 
989
      stack_num_info = ''
 
990
      @insns.each{|insn|
 
991
        num = insn.rets.size
 
992
        stack_num_info << "  #{num},\n"
 
993
      }
 
994
 
 
995
      # stack increase
 
996
      stack_increase = ''
 
997
      @insns.each{|insn|
 
998
        stack_increase << <<-EOS
 
999
        case BIN(#{insn.name}):{
 
1000
          #{insn.sp_increase_c_expr}
 
1001
        }
 
1002
        EOS
 
1003
      }
 
1004
      ERB.new(vpath.read('template/insns_info.inc.tmpl')).result(binding)
 
1005
    end
 
1006
  end
 
1007
 
 
1008
  ###################################################################
 
1009
  # insns.inc
 
1010
  class InsnsIncGenerator < SourceCodeGenerator
 
1011
    def generate
 
1012
      i=0
 
1013
      insns = build_string do
 
1014
        @insns.each{|insn|
 
1015
          commit "  %-30s = %d,\n" % ["BIN(#{insn.name})", i]
 
1016
          i+=1
 
1017
        }
 
1018
      end
 
1019
 
 
1020
      ERB.new(vpath.read('template/insns.inc.tmpl')).result(binding)
 
1021
    end
 
1022
  end
 
1023
 
 
1024
  ###################################################################
 
1025
  # minsns.inc
 
1026
  class MInsnsIncGenerator < SourceCodeGenerator
 
1027
    def generate
 
1028
      i=0
 
1029
      defs = build_string do
 
1030
        @insns.each{|insn|
 
1031
          commit "  rb_define_const(mYarvInsns, %-30s, INT2FIX(%d));\n" %
 
1032
                 ["\"I#{insn.name}\"", i]
 
1033
          i+=1
 
1034
        }
 
1035
      end
 
1036
      ERB.new(vpath.read('template/minsns.inc.tmpl')).result(binding)
 
1037
    end
 
1038
  end
 
1039
 
 
1040
  ###################################################################
 
1041
  # optinsn.inc
 
1042
  class OptInsnIncGenerator < SourceCodeGenerator
 
1043
    def generate
 
1044
      optinsn_inc
 
1045
    end
 
1046
 
 
1047
    ###
 
1048
    private
 
1049
 
 
1050
    def val_as_type op
 
1051
      type = op[0][0]
 
1052
      val  = op[1]
 
1053
 
 
1054
      case type
 
1055
      when /^long/, /^rb_num_t/, /^lindex_t/, /^dindex_t/
 
1056
        "INT2FIX(#{val})"
 
1057
      when /^VALUE/
 
1058
        val
 
1059
      when /^ID/
 
1060
        "INT2FIX(#{val})"
 
1061
      when /^ISEQ/, /^rb_insn_func_t/
 
1062
        val
 
1063
      when /GENTRY/
 
1064
        raise
 
1065
      when /^\.\.\./
 
1066
        raise
 
1067
      else
 
1068
        raise "type: #{type}"
 
1069
      end
 
1070
    end
 
1071
 
 
1072
    # optinsn.inc
 
1073
    def optinsn_inc
 
1074
      rule = ''
 
1075
      opt_insns_map = Hash.new{|h, k| h[k] = []}
 
1076
 
 
1077
      @insns.each{|insn|
 
1078
        next if insn.defopes.size == 0
 
1079
        next if insn.type         == :sc
 
1080
        next if /^UNIFIED/ =~ insn.name.to_s
 
1081
 
 
1082
        originsn = insn.orig
 
1083
        opt_insns_map[originsn] << insn
 
1084
      }
 
1085
 
 
1086
      rule = build_string do
 
1087
        opt_insns_map.each{|originsn, optinsns|
 
1088
          commit "case BIN(#{originsn.name}):"
 
1089
 
 
1090
          optinsns.sort_by{|opti|
 
1091
            opti.defopes.find_all{|e| e[1] == '*'}.size
 
1092
          }.each{|opti|
 
1093
            commit "  if("
 
1094
            i = 0
 
1095
            commit "    " + opti.defopes.map{|opinfo|
 
1096
              i += 1
 
1097
              next if opinfo[1] == '*'
 
1098
              "insnobj->operands[#{i-1}] == #{val_as_type(opinfo)}"
 
1099
            }.compact.join('&&  ')
 
1100
            commit "  ){"
 
1101
            idx = 0
 
1102
            n = 0
 
1103
            opti.defopes.each{|opinfo|
 
1104
              if opinfo[1] == '*'
 
1105
                if idx != n
 
1106
                  commit "    insnobj->operands[#{idx}] = insnobj->operands[#{n}];"
 
1107
                end
 
1108
                idx += 1
 
1109
              else
 
1110
                # skip
 
1111
              end
 
1112
              n += 1
 
1113
            }
 
1114
            commit "    insnobj->insn_id = BIN(#{opti.name});"
 
1115
            commit "    insnobj->operand_size = #{idx};"
 
1116
            commit "    break;\n  }\n"
 
1117
          }
 
1118
          commit "  break;";
 
1119
        }
 
1120
      end
 
1121
 
 
1122
      ERB.new(vpath.read('template/optinsn.inc.tmpl')).result(binding)
 
1123
    end
 
1124
  end
 
1125
 
 
1126
  ###################################################################
 
1127
  # optunifs.inc
 
1128
  class OptUnifsIncGenerator < SourceCodeGenerator
 
1129
    def generate
 
1130
      unif_insns_each = ''
 
1131
      unif_insns      = ''
 
1132
      unif_insns_data = []
 
1133
 
 
1134
      insns = @insns.find_all{|insn| !insn.is_sc}
 
1135
      insns.each{|insn|
 
1136
        size = insn.unifs.size
 
1137
        if size > 0
 
1138
          insn.unifs.sort_by{|unif| -unif[1].size}.each_with_index{|unif, i|
 
1139
 
 
1140
          uni_insn, uni_insns = *unif
 
1141
          uni_insns = uni_insns[1..-1]
 
1142
          unif_insns_each << "static int UNIFIED_#{insn.name}_#{i}[] = {" +
 
1143
                             "  BIN(#{uni_insn.name}), #{uni_insns.size + 2}, \n  " +
 
1144
                             uni_insns.map{|e| "BIN(#{e.name})"}.join(", ") + "};\n"
 
1145
          }
 
1146
        else
 
1147
          
 
1148
        end
 
1149
        if size > 0
 
1150
          unif_insns << "static int *UNIFIED_#{insn.name}[] = {(int *)#{size+1}, \n"
 
1151
          unif_insns << (0...size).map{|e| "  UNIFIED_#{insn.name}_#{e}"}.join(",\n") + "};\n"
 
1152
          unif_insns_data << "  UNIFIED_#{insn.name}"
 
1153
        else
 
1154
          unif_insns_data << "  0"
 
1155
        end
 
1156
      }
 
1157
      unif_insns_data = "static int **unified_insns_data[] = {\n" +
 
1158
                        unif_insns_data.join(",\n") + "};\n"
 
1159
      ERB.new(vpath.read('template/optunifs.inc.tmpl')).result(binding)
 
1160
    end
 
1161
  end
 
1162
 
 
1163
  ###################################################################
 
1164
  # opt_sc.inc
 
1165
  class OptSCIncGenerator < SourceCodeGenerator
 
1166
    def generate
 
1167
      sc_insn_info = []
 
1168
      @insns.each{|insn|
 
1169
        insns = insn.sc
 
1170
        if insns.size > 0
 
1171
          insns = ['SC_ERROR'] + insns.map{|e| "    BIN(#{e.name})"}
 
1172
        else
 
1173
          insns = Array.new(6){'SC_ERROR'}
 
1174
        end
 
1175
        sc_insn_info << "  {\n#{insns.join(",\n")}}"
 
1176
      }
 
1177
      sc_insn_info = sc_insn_info.join(",\n")
 
1178
 
 
1179
      sc_insn_next = @insns.map{|insn|
 
1180
        "  SCS_#{InstructionsLoader.complement_name(insn.nextsc).upcase}" +
 
1181
        (verbose? ? " /* #{insn.name} */" : '')
 
1182
      }.join(",\n")
 
1183
      ERB.new(vpath.read('template/opt_sc.inc.tmpl')).result(binding)
 
1184
    end
 
1185
  end
 
1186
 
 
1187
  ###################################################################
 
1188
  # yasmdata.rb
 
1189
  class YASMDataRbGenerator < SourceCodeGenerator
 
1190
    def generate
 
1191
      insn_id2no = ''
 
1192
      @insns.each_with_index{|insn, i|
 
1193
        insn_id2no << "        :#{insn.name} => #{i},\n"
 
1194
      }
 
1195
      ERB.new(vpath.read('template/yasmdata.rb.tmpl')).result(binding)
 
1196
    end
 
1197
  end
 
1198
 
 
1199
  ###################################################################
 
1200
  # yarvarch.*
 
1201
  class YARVDocGenerator < SourceCodeGenerator
 
1202
    def generate
 
1203
 
 
1204
    end
 
1205
 
 
1206
    def desc lang
 
1207
      d = ''
 
1208
      i = 0
 
1209
      cat = nil
 
1210
      @insns.each{|insn|
 
1211
        seq    = insn.opes.map{|t,v| v}.join(' ')
 
1212
        before = insn.pops.reverse.map{|t,v| v}.join(' ')
 
1213
        after  = insn.rets.reverse.map{|t,v| v}.join(' ')
 
1214
 
 
1215
        if cat != insn.comm[:c]
 
1216
          d << "** #{insn.comm[:c]}\n\n"
 
1217
          cat = insn.comm[:c]
 
1218
        end
 
1219
 
 
1220
        d << "*** #{insn.name}\n"
 
1221
        d << "\n"
 
1222
        d << insn.comm[lang] + "\n\n"
 
1223
        d << ":instruction sequence: 0x%02x #{seq}\n" % i
 
1224
        d << ":stack: #{before} => #{after}\n\n"
 
1225
        i+=1
 
1226
      }
 
1227
      d
 
1228
    end
 
1229
 
 
1230
    def desc_ja
 
1231
      d = desc :j
 
1232
      ERB.new(vpath.read('template/yarvarch.ja')).result(binding)
 
1233
    end
 
1234
 
 
1235
    def desc_en
 
1236
      d = desc :e
 
1237
      ERB.new(vpath.read('template/yarvarch.en')).result(binding)
 
1238
    end
 
1239
  end
 
1240
 
 
1241
  module VPATH
 
1242
    def search(meth, base, *rest)
 
1243
      begin
 
1244
        meth.call(base, *rest)
 
1245
      rescue Errno::ENOENT => error
 
1246
        each do |dir|
 
1247
          return meth.call(File.join(dir, base), *rest) rescue nil
 
1248
        end
 
1249
        raise error
 
1250
      end
 
1251
    end
 
1252
 
 
1253
    def process(*args, &block)
 
1254
      search(File.method(__callee__), *args, &block)
 
1255
    end
 
1256
 
 
1257
    alias stat process
 
1258
    alias lstat process
 
1259
 
 
1260
    def open(*args)
 
1261
      f = search(File.method(:open), *args)
 
1262
      if block_given?
 
1263
        begin
 
1264
          yield f
 
1265
        ensure
 
1266
          f.close unless f.closed?
 
1267
        end
 
1268
      else
 
1269
        f
 
1270
      end
 
1271
    end
 
1272
 
 
1273
    def read(*args)
 
1274
      open(*args) {|f| f.read}
 
1275
    end
 
1276
 
 
1277
    def foreach(file, *args, &block)
 
1278
      open(file) {|f| f.each(*args, &block)}
 
1279
    end
 
1280
 
 
1281
    def self.def_options(opt)
 
1282
      vpath = []
 
1283
      path_sep = ':'
 
1284
 
 
1285
      opt.on("-I", "--srcdir=DIR", "add a directory to search path") {|dir|
 
1286
        vpath |= [dir]
 
1287
      }
 
1288
      opt.on("-L", "--vpath=PATH LIST", "add directories to search path") {|dirs|
 
1289
        vpath |= dirs.split(path_sep)
 
1290
      }
 
1291
      opt.on("--path-separator=SEP", /\A\W\z/, "separator for vpath") {|sep|
 
1292
        path_sep = sep
 
1293
      }
 
1294
 
 
1295
      proc {
 
1296
        vpath.extend(self) unless vpath.empty?
 
1297
      }
 
1298
    end
 
1299
  end
 
1300
 
 
1301
  class SourceCodeGenerator
 
1302
    Files = { # codes
 
1303
      'vm.inc'         => VmBodyGenerator,
 
1304
      'vmtc.inc'       => VmTCIncGenerator,
 
1305
      'insns.inc'      => InsnsIncGenerator,
 
1306
      'insns_info.inc' => InsnsInfoIncGenerator,
 
1307
    # 'minsns.inc'     => MInsnsIncGenerator,
 
1308
      'optinsn.inc'    => OptInsnIncGenerator,
 
1309
      'optunifs.inc'   => OptUnifsIncGenerator,
 
1310
      'opt_sc.inc'     => OptSCIncGenerator,
 
1311
      'yasmdata.rb'    => YASMDataRbGenerator,
 
1312
    }
 
1313
 
 
1314
    def generate args = []
 
1315
      args = Files.keys if args.empty?
 
1316
      args.each{|fn|
 
1317
        s = Files[fn].new(@insns).generate
 
1318
        open(output_path(fn), 'w') {|f| f.puts(s)}
 
1319
      }
 
1320
    end
 
1321
 
 
1322
    def self.def_options(opt)
 
1323
      opts = {
 
1324
        :"insns.def" => 'insns.def',
 
1325
        :"opope.def" => 'opt_operand.def',
 
1326
        :"unif.def"  => 'opt_insn_unif.def',
 
1327
      }
 
1328
 
 
1329
      opt.on("-Dname", /\AOPT_(\w+)\z/, "enable VM option") {|s, v|
 
1330
        opts[v] = true
 
1331
      }
 
1332
      opt.on("--enable=name[,name...]", Array,
 
1333
        "enable VM options (without OPT_ prefix)") {|*a|
 
1334
        a.each {|v| opts[v] = true}
 
1335
      }
 
1336
      opt.on("-Uname", /\AOPT_(\w+)\z/, "disable VM option") {|s, v|
 
1337
        opts[v] = false
 
1338
      }
 
1339
      opt.on("--disable=name[,name...]", Array,
 
1340
        "disable VM options (without OPT_ prefix)") {|*a|
 
1341
          a.each {|v| opts[v] = false}
 
1342
      }
 
1343
      opt.on("-i", "--insnsdef=FILE", "--instructions-def",
 
1344
        "instructions definition file") {|n|
 
1345
        opts[:insns_def] = n
 
1346
      }
 
1347
      opt.on("-o", "--opt-operanddef=FILE", "--opt-operand-def",
 
1348
        "vm option: operand definition file") {|n|
 
1349
        opts[:opope_def] = n
 
1350
      }
 
1351
      opt.on("-u", "--opt-insnunifdef=FILE", "--opt-insn-unif-def",
 
1352
        "vm option: instruction unification file") {|n|
 
1353
        opts[:unif_def] = n
 
1354
      }
 
1355
      opt.on("-C", "--[no-]use-const",
 
1356
        "use consts for default operands instead of macros") {|v|
 
1357
        opts[:use_const] = v
 
1358
      }
 
1359
      opt.on("-d", "--destdir", "--output-directory=DIR",
 
1360
        "make output file underneath DIR") {|v|
 
1361
        opts[:destdir] = v
 
1362
      }
 
1363
      opt.on("-V", "--[no-]verbose") {|v|
 
1364
        opts[:verbose] = v
 
1365
      }
 
1366
 
 
1367
      vpath = VPATH.def_options(opt)
 
1368
 
 
1369
      proc {
 
1370
        opts[:VPATH] = vpath.call
 
1371
        build opts
 
1372
      }
 
1373
    end
 
1374
 
 
1375
    def self.build opts, vpath = ['./']
 
1376
      opts[:VPATH] = vpath.extend(VPATH) unless opts[:VPATH]
 
1377
      self.new InstructionsLoader.new(opts)
 
1378
    end
 
1379
  end
 
1380
end
 
1381