Class: Verneuil::Compiler::Visitor
- Inherits:
-
Object
- Object
- Verneuil::Compiler::Visitor
- Defined in:
- lib/verneuil/compiler.rb
Overview
Compiler visitor visits sexps and transforms them into executable code by calling back on its code generator.
Constant Summary collapse
- NOPOP =
[:return, :defn, :class]
Instance Method Summary collapse
-
#accept_arglist(*args) ⇒ Object
s(:arglist, ARGUMENT, ARGUMENT) - Argument lists.
-
#accept_args(*arg_names) ⇒ Object
s(:args, ARGUMENT_NAMES).
-
#accept_array(*elements) ⇒ Object
s(:array, ELEMENTS) - array literal.
-
#accept_block(*statements) ⇒ Object
s(:block, STATEMENT, STATEMENT, …) - Blocks of code.
-
#accept_call(receiver, method_name, args) ⇒ Object
s(:call, RECEIVER, NAME, ARGUMENTS) - Method calls with or without receiver.
-
#accept_class(name, _, scope) ⇒ Object
s(:class, :Fixnum, SUPER, s(:scope, s(:defn, :foo, s(:args), DEF))) - a method definition for a class.
-
#accept_colon2(left, right) ⇒ Object
s(:colon2, s(:const, :Verneuil), :Process) - access something inside a namespace.
-
#accept_const(const) ⇒ Object
s(:const, :Verneuil) - Resolve a constant globally and return it.
-
#accept_defined(exp) ⇒ Object
s(:defined, s(:lvar, :a)) - test if a local variable is defined.
-
#accept_defn(name, args, body) ⇒ Object
s(:defn, NAME, ARG_NAMES, BODY) - a method definition.
-
#accept_false ⇒ Object
false.
-
#accept_if(cond, _then, _else) ⇒ Object
s(:if, COND, THEN, ELSE) - an if statement.
-
#accept_iter(call, assigns, block = nil) ⇒ Object
s(:iter, s(:call, RECEIVER, METHOD, ARGUMENTS), ASSIGNS, BLOCK) - call a method with a block.
-
#accept_lasgn(*args) ⇒ Object
s(:lasgn, VARIABLE, VALUE) - assignment of local variables.
-
#accept_lit(value) ⇒ Object
s(:lit, LITERAL) - A literal value.
-
#accept_lvar(name) ⇒ Object
s(:lvar, VARIABLE) - local variable access.
-
#accept_nil ⇒ Object
nil.
-
#accept_op_asgn_or(variable, assign) ⇒ Object
s(:op_asgn_or, s(:lvar, :a), s(:lasgn, :a, s(:lit, 1))) - var ||= value.
-
#accept_return(val) ⇒ Object
s(:return, RETVAL) - return from the current method.
-
#accept_scope(body) ⇒ Object
s(:scope, BODY) - a new scope.
-
#accept_self ⇒ Object
self.
-
#accept_true ⇒ Object
true.
-
#accept_while(cond, body, t) ⇒ Object
s(:while, test, body, true) - a while statement.
-
#initialize(generator, compiler) ⇒ Visitor
constructor
A new instance of Visitor.
- #visit(sexp) ⇒ Object
Constructor Details
#initialize(generator, compiler) ⇒ Visitor
Returns a new instance of Visitor.
42 43 44 45 46 47 |
# File 'lib/verneuil/compiler.rb', line 42 def initialize(generator, compiler) @generator = generator @compiler = compiler @class_context = [] end |
Instance Method Details
#accept_arglist(*args) ⇒ Object
s(:arglist, ARGUMENT, ARGUMENT) - Argument lists. Needs to return the actual number of arguments that we’ve compiled.
104 105 106 107 108 109 |
# File 'lib/verneuil/compiler.rb', line 104 def accept_arglist(*args) args.each do |arg| visit(arg) end return args.size end |
#accept_args(*arg_names) ⇒ Object
s(:args, ARGUMENT_NAMES)
268 269 270 271 272 273 274 275 276 277 278 |
# File 'lib/verneuil/compiler.rb', line 268 def accept_args(*arg_names) arg_names.each do |name| if name.to_s.start_with?('&') stripped_name = name.to_s[1..-1].to_sym @generator.load_block @generator.lvar_set stripped_name else @generator.lvar_set name end end end |
#accept_array(*elements) ⇒ Object
s(:array, ELEMENTS) - array literal.
134 135 136 137 138 |
# File 'lib/verneuil/compiler.rb', line 134 def accept_array(*elements) elements.each { |el| visit(el) } @generator.load Array @generator.ruby_call :"[]", elements.size end |
#accept_block(*statements) ⇒ Object
s(:block, STATEMENT, STATEMENT, …) - Blocks of code.
113 114 115 116 117 118 119 120 121 122 123 124 |
# File 'lib/verneuil/compiler.rb', line 113 def accept_block(*statements) statements.each_with_index do |statement, idx| type, *rest = statement visit(statement) unless idx+1 == statements.size || NOPOP.include?(type) @generator.pop 1 end end end |
#accept_call(receiver, method_name, args) ⇒ Object
s(:call, RECEIVER, NAME, ARGUMENTS) - Method calls with or without receiver.
90 91 92 93 94 95 96 97 98 99 |
# File 'lib/verneuil/compiler.rb', line 90 def accept_call(receiver, method_name, args) argc = visit(args) if receiver visit(receiver) @generator.ruby_call method_name, argc else @generator.ruby_call_implicit method_name, argc end end |
#accept_class(name, _, scope) ⇒ Object
s(:class, :Fixnum, SUPER, s(:scope, s(:defn, :foo, s(:args), DEF))) - a method definition for a class.
NOTE that verneuils classes don’t work like Rubies classes at all. This is more or less just a method for masking methods on Ruby classes, not a way to define classes.
258 259 260 261 262 263 264 |
# File 'lib/verneuil/compiler.rb', line 258 def accept_class(name, _, scope) @class_context.push name visit(scope) ensure @class_context.pop end |
#accept_colon2(left, right) ⇒ Object
s(:colon2, s(:const, :Verneuil), :Process) - access something inside a namespace. Constants are resolved at compile time!
75 76 77 78 79 |
# File 'lib/verneuil/compiler.rb', line 75 def accept_colon2(left, right) left_const = visit(left) const = left_const.const_get(right) @generator.load const end |
#accept_const(const) ⇒ Object
s(:const, :Verneuil) - Resolve a constant globally and return it.
83 84 85 |
# File 'lib/verneuil/compiler.rb', line 83 def accept_const(const) eval(const.to_s) end |
#accept_defined(exp) ⇒ Object
s(:defined, s(:lvar, :a)) - test if a local variable is defined.
192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 |
# File 'lib/verneuil/compiler.rb', line 192 def accept_defined(exp) type, * = exp case type when :call # s(:call, nil, :a, s(:arglist)) @generator.test_defined exp[2] when :lvar # s(:lvar, :a) @generator.test_defined exp.last when :lit @generator.load 'expression' else fail "Don't know how to implement defined? with #{variable.inspect}." end end |
#accept_defn(name, args, body) ⇒ Object
s(:defn, NAME, ARG_NAMES, BODY) - a method definition.
231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 |
# File 'lib/verneuil/compiler.rb', line 231 def accept_defn(name, args, body) # Jumping over functions so that definitions don't get executed. adr_end = @generator.fwd_adr @generator.jump adr_end method = Verneuil::Method.new( @class_context.last, name, @generator.current_adr) @generator.program.symbols.add(method) # Enters a new local scope and defines arguments visit(args) visit(body) @generator.return adr_end.resolve end |
#accept_false ⇒ Object
false.
331 332 333 |
# File 'lib/verneuil/compiler.rb', line 331 def accept_false @generator.load false end |
#accept_if(cond, _then, _else) ⇒ Object
s(:if, COND, THEN, ELSE) - an if statement
142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 |
# File 'lib/verneuil/compiler.rb', line 142 def accept_if(cond, _then, _else) adr_else = @generator.fwd_adr adr_end = @generator.fwd_adr visit(cond) @generator.jump_if_false adr_else visit(_then) @generator.jump adr_end adr_else.resolve if _else visit(_else) else @generator.load nil end adr_end.resolve end |
#accept_iter(call, assigns, block = nil) ⇒ Object
s(:iter, s(:call, RECEIVER, METHOD, ARGUMENTS), ASSIGNS, BLOCK) - call a method with a block.
298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 |
# File 'lib/verneuil/compiler.rb', line 298 def accept_iter(call, assigns, block=nil) # Jump over the block code adr_end_of_block = @generator.fwd_adr @generator.jump adr_end_of_block adr_start_of_block = @generator.current_adr if assigns type, *names = assigns fail "BUG: Unsupported type of block arguments: #{type}" \ unless type == :lasgn accept_args(*names) end visit(block) if block @generator.return adr_end_of_block.resolve # Compile the call as we would normally, adding a push_block/pop_block # around it. @generator.push_block adr_start_of_block visit(call) @generator.pop_block end |
#accept_lasgn(*args) ⇒ Object
s(:lasgn, VARIABLE, VALUE) - assignment of local variables. s(:lasgn, VARIABLE) - implicit assignment of local vars.
211 212 213 214 215 216 217 218 219 220 |
# File 'lib/verneuil/compiler.rb', line 211 def accept_lasgn(*args) if args.size == 2 val = args.last visit(val) end name = args.first @generator.dup 0 @generator.lvar_set name end |
#accept_lit(value) ⇒ Object
s(:lit, LITERAL) - A literal value.
128 129 130 |
# File 'lib/verneuil/compiler.rb', line 128 def accept_lit(value) @generator.load value end |
#accept_lvar(name) ⇒ Object
s(:lvar, VARIABLE) - local variable access.
224 225 226 227 |
# File 'lib/verneuil/compiler.rb', line 224 def accept_lvar(name) visit( s(:call, nil, name, s(:arglist))) end |
#accept_nil ⇒ Object
nil
343 344 345 |
# File 'lib/verneuil/compiler.rb', line 343 def accept_nil @generator.load nil end |
#accept_op_asgn_or(variable, assign) ⇒ Object
s(:op_asgn_or, s(:lvar, :a), s(:lasgn, :a, s(:lit, 1))) - var ||= value.
180 181 182 183 184 185 186 187 188 |
# File 'lib/verneuil/compiler.rb', line 180 def accept_op_asgn_or(variable, assign) (*), var, val = assign visit( s(:lasgn, var, s(:if, s(:defined, variable), variable, val))) end |
#accept_return(val) ⇒ Object
s(:return, RETVAL) - return from the current method.
290 291 292 293 |
# File 'lib/verneuil/compiler.rb', line 290 def accept_return(val) visit(val) @generator.return end |
#accept_scope(body) ⇒ Object
s(:scope, BODY) - a new scope.
282 283 284 285 286 |
# File 'lib/verneuil/compiler.rb', line 282 def accept_scope(body) # For now, we don't know what we'll eventually do with scopes. So here # is this very basic idea... visit(body) end |
#accept_self ⇒ Object
self
337 338 339 |
# File 'lib/verneuil/compiler.rb', line 337 def accept_self @generator.load_self end |
#accept_true ⇒ Object
true.
325 326 327 |
# File 'lib/verneuil/compiler.rb', line 325 def accept_true @generator.load true end |
#accept_while(cond, body, t) ⇒ Object
s(:while, test, body, true) - a while statement.
163 164 165 166 167 168 169 170 171 172 173 174 175 176 |
# File 'lib/verneuil/compiler.rb', line 163 def accept_while(cond, body, t) fail "What is t for?" unless t adr_end = @generator.fwd_adr adr_test = @generator.current_adr visit(cond) @generator.jump_if_false adr_end visit(body) @generator.jump adr_test adr_end.resolve end |
#visit(sexp) ⇒ Object
49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 |
# File 'lib/verneuil/compiler.rb', line 49 def visit(sexp) type, *args = sexp sym = "accept_#{type}".to_sym raise ArgumentError, "No sexp given?" unless sexp && type raise NotImplementedError, "No acceptor for #{sexp}." \ unless respond_to? sym begin self.send(sym, *args) rescue ArgumentError => ex if md=ex..match(/wrong number of arguments \(([^)]+)\)/) sexp_skeleton = s(sexp[0], *sexp[1..-1].map { |e| e && s(e.first, '...') }) raise ArgumentError, "wrong number of elements in #{sexp_skeleton} (#{md[1]})." end raise end end |