evanphx / rubinius

Rubinius, the Ruby VM

This URL has Read+Write access

rubinius / kernel / compiler / text.rb
100644 152 lines (120 sloc) 2.584 kb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
class Compiler
 
  ##
  # An alternate generator that prints out bytecode for display.
 
  class TextGenerator
 
    @@method_id = 0
 
    def initialize
      @text = ""
      @label = 0
      @other_methods = {}
      @ip = 0
      @file = "(unknown)"
      @line = 0
    end
 
    attr_reader :text, :ip, :file, :line
    attr_accessor :redo, :retry, :break, :next
 
    def advanced_since?(old)
      old < @ip
    end
 
    def advance(count=1)
      @ip += count
    end
 
    class Label
      def initialize(gen, idx)
        @gen = gen
        @index = idx
      end
 
      attr_reader :index
 
      def set!
        @gen.set_label(@index)
      end
 
      def inspect
        "l#{@index}"
      end
    end
 
    def new_label
      Label.new(self, @label += 1)
    end
 
    def set_label(idx)
      @text << "l#{idx}:\n"
    end
 
    def add_text(text)
      @text << text
      @text << "\n"
    end
 
    def run(node)
      node.bytecode(self)
    end
 
    def set_line(line, file)
      @file, @line = file, line
      @text << "#line #{line}\n"
    end
 
    def close
      return if @other_methods.empty?
      @other_methods.each_pair do |i,m|
        @text << "\n:==== Method #{i} ====\n"
        @text << m.generator.text
        @text << "\n"
      end
    end
 
    def method_missing(op, *args)
      if args.empty?
        @text << "#{op}\n"
        advance
      else
        @text << "#{op} #{args.map {|a| a.inspect}.join(' ')}\n"
        advance 1 + args.size
      end
    end
 
    def add(thing)
      method_missing(thing)
    end
 
    def push(what)
      @text << "push #{what}\n"
      advance
    end
 
    def send(meth, count, priv=false)
      @text << "send #{meth} #{count}"
      if priv
        @text << " true ; allow private\n"
      else
        @text << "\n"
      end
      advance
    end
 
    def dup
      method_missing :dup
    end
 
    def push_literal(lit)
      if lit.kind_of? MethodDescription
        @text << "push_literal #<Method #{@@method_id}>\n"
 
        @other_methods[@@method_id] = lit
        @@method_id += 1
        advance
      else
        method_missing :push_literal, lit
      end
    end
 
    def as_primitive(name)
      @text << "#primitive #{name}\n"
    end
 
    class EB
      @@ids = 0
 
      def initialize(gen)
        @gen = gen
        @idx = (@@ids += 1)
      end
 
      def start!
        @gen.add_text "; exc#{@idx} start"
      end
 
      def handle!
        @gen.add_text "; exc#{@idx} end"
      end
    end
 
    def exceptions
      ex = EB.new(self)
      ex.start!
      yield ex
    end
 
  end
end