class Rubic::Interpreter
Constants
- DEFAULT_GLOBAL_VARS
Public Class Methods
new()
click to toggle source
# File lib/rubic/interpreter.rb, line 16 def initialize @parser = Parser.new @global = Environment.new DEFAULT_GLOBAL_VARS.each {|k, v| @global[k] = v } builtins = Rubic::Builtin.constants.map {|c| Rubic::Builtin.const_get(c) } builtins.each {|ext| @global.extend(ext) } end
Public Instance Methods
evaluate(str)
click to toggle source
# File lib/rubic/interpreter.rb, line 24 def evaluate(str) seq = @parser.parse(str) execute_sequence(seq, @global) end
Private Instance Methods
execute(list_or_atom, env)
click to toggle source
# File lib/rubic/interpreter.rb, line 31 def execute(list_or_atom, env) # Atom case list_or_atom when Numeric, String atom = list_or_atom when Symbol atom = env[list_or_atom] else # fallthrough end return atom unless atom.nil? list = list_or_atom # Empty list return [] if list.empty? # Special Forms case list.first when :define if list[1].is_a?(Array) # procedure definition _, (name, *params), *body = list env[name] = -> (*args) do local = Environment.new(env) local.bind(params, args) execute_sequence(body, local) end return else # variable definition _, name, expr = list env[name] = execute(expr, env) return end when :cond _, *clauses = list clauses.each do |pred, expr| if pred == :else || execute(pred, env) return execute(expr, env) end end return when :if _, pred, cons, alt = list return execute(pred, env) ? execute(cons, env) : execute(alt, env) when :and _, *exprs = list exprs.each do |expr| return false unless execute(expr, env) end return true when :or _, *exprs = list exprs.each do |expr| return true if execute(expr, env) end return false when :lambda _, (*params), *body = list return -> (*args) do local = Environment.new(env) local.bind(params, args) execute_sequence(body, local) end when :let _, (*defs), *body = list local = Environment.new(env) defs.each {|name, expr| local[name] = execute(expr, env) } return execute_sequence(body, local) when :quote _, expr = list return quote(expr) when :set! _, name, expr = list env.assign(name, execute(expr, env)) return when :begin _, *seq = list return execute_sequence(seq, env) else # fallthrough end # Procedure call op, *args = list.map {|e| execute(e, env) } unless op.respond_to? :call raise Rubic::RuntimeError, "`#{op}' is not a procedure" end required = op.arity >= 0 ? op.arity : -op.arity - 1 if op.arity >= 0 ? required != args.size : required > args.size raise Rubic::ArgumentError, "wrong number of arguments (#{args.size} for #{required})" end op.call(*args) end
execute_sequence(seq, env)
click to toggle source
# File lib/rubic/interpreter.rb, line 135 def execute_sequence(seq, env) # execute expressions sequentially and returns the last result seq.reduce(nil) {|res, expr| res = execute(expr, env) } end
quote(expr)
click to toggle source
# File lib/rubic/interpreter.rb, line 140 def quote(expr) if expr.is_a? Array expr.map {|e| quote(e) }.reverse.reduce([]) {|res, e| [e, res] } else expr end end