From: Bill Dolinar Date: 2006-11-07T15:20:11+09:00 Subject: Re: [QUIZ] Bytecode Compiler (#100) Changed my previous solution to use some of the things others have used that need to be pounded into my skull like \d in regular expression and pack/unpack. Also added unary + and cleaned up code. ############ require 'interp' module Compiler # compile expression into bytecode array def Compiler.compile(s) stack = [] eval(s.gsub(/(\d+)/, 'Value.new(stack, \1)')) stack end class Value attr_reader :value # constant value or nil for on stack ON_STACK = nil def initialize(stack, value) @stack = stack @value = value end # generate code for each binary operator {'+' => 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 <<-OPERATOR_CODE def #{operator}(rhs) push_if_value(@value) push_if_value(rhs.value) # swap stack items if necessary #{if operator != "+" "@stack << Interpreter::Ops::SWAP if rhs.value == nil && @value != nil" end} @stack << #{byte_code} Value.new(@stack, ON_STACK) end OPERATOR_CODE end def -@ if @value != ON_STACK push_if_value(-@value) else push_if_value(0) @stack << Interpreter::Ops::SWAP << Interpreter::Ops::SUB end Value.new(@stack, ON_STACK) end def +@ push_if_value(@value) Value.new(@stack, ON_STACK) end def push_if_value(value) if value != ON_STACK if (-32768..32767).include?(value) @stack << Interpreter::Ops::CONST @stack.concat([value].pack("n").unpack("C*")) else @stack << Interpreter::Ops::LCONST @stack.concat([value].pack("N").unpack("C*")) end end end end end