Sha256: a02711aa91b506525e16d0df4250a9ef57e4333f5d9aa1a9bc4a0d4124b04446

Contents?: true

Size: 1.7 KB

Versions: 1

Compression:

Stored size: 1.7 KB

Contents

$:.unshift File.dirname(__FILE__)

require "lisp/version"
require "lisp/repl"

module Lisp
  def self.eval(string)
    execute(parse(tokenize(string)))
  end

  def self.tokenize(string)
    string.gsub("("," ( ").gsub(")"," ) ").split
  end

  def self.parse(tokens, tree = [])
    raise "unexpected: eof" if tokens.size.zero?

    case token = tokens.shift
    when "("
      while tokens[0] != ")" do
        tree.push parse(tokens)
      end
      tokens.shift
      tree
    when ")"
      raise "unexpected: )"
    else
      evaluator(token)
    end
  end

  def self.evaluator(token)
    case token
    when /\d/
      token.to_f
    else
      token.to_sym
    end
  end

  def self.execute(exp, scope = global)
    return scope[exp] if     exp.is_a? Symbol
    return exp        unless exp.is_a? Array

    case exp[0]
    when :define
      _, var, exp = exp
      scope[var] = execute(exp, scope)
    when :lambda
      _, params, exp = exp
      lambda { |*args| execute(exp, scope.merge(Hash[params.zip(args)])) }
    when :if
      _, test, conseq, alt = exp
      exp = execute(test, scope) ? conseq : alt
      execute(exp, scope)
    when :set!
      _, var, exp = exp
      if scope.has_key?(var) then scope[var] = execute(exp, scope) else raise "#{var} is undefined" end
    when :begin
      _, *exp = exp
      exp.map { |exp| execute(exp) }.last
    else
      func, *args = exp.map { |exp| execute(exp, scope) }
      func.call(*args)
    end
  end

  def self.global
    @scope ||= begin
      operators = [:==, :"!=", :"<", :"<=", :">", :">=", :+, :-, :*, :/]
      operators.inject({}) do |scope, operator|
        scope.merge(operator => lambda { |*args| args.inject(&operator) })
      end
    end
  end
end

Version data entries

1 entries across 1 versions & 1 rubygems

Version Path
lisp-1.3.0 lib/lisp.rb