From: Bill Dolinar Date: 2006-11-06T00:43:29+09:00 Subject: Re: [QUIZ] Bytecode Compiler (#100) require 'interp' module Compiler # use eval and Value class below to compile # expression into bytecode def Compiler.compile(s) s.gsub!(/([0-9]+)/, 'Value.new(stack, \1)') stack = [] eval(s) stack end class Value attr_reader :number # constant value or nil for on stack ON_STACK = nil def initialize(stack, number) @number = number @stack = stack end # generate code for each binary operator (except -@) # algorithm: # push constants (or don't if already on stack) # swap if necessary # push bytecode # create stack item {'+' => Interpreter::Ops::ADD, '-' => Interpreter::Ops::SUB, '*' => Interpreter::Ops::MUL, '**'=> Interpreter::Ops::POW, '/' => Interpreter::Ops::DIV, '%' => Interpreter::Ops::MOD}.each do |operator, byte_code| Value.module_eval <<-FUNC def #{operator}(rhs) push_const(@number) push_const(rhs.number) # may need to swap integers on stack for all but plus #{ if operator != "+" "@stack << Interpreter::Ops::SWAP if rhs.number == nil && @number != nil" end } @stack << #{byte_code} Value.new(@stack, ON_STACK) end FUNC end def -@ if @number != ON_STACK @number = -@number push_const(@number) else push_const(@number) push_const(0) @stack << Interpreter::Ops::SWAP @stack << Interpreter::Ops::SUB end Value.new(@stack, ON_STACK) end def push_const(number) if number != ON_STACK if (-32768..32767).include?(number) @stack << Interpreter::Ops::CONST else @stack << Interpreter::Ops::LCONST @stack << ((number >> 24) & 0xff) @stack << ((number >> 16) & 0xff) end @stack << ((number >> 8) & 0xff) @stack << (number & 0xff) end end end end