module Lisp

Constants

VERSION

Public Class Methods

eval(string) click to toggle source
# File lib/lisp/interpreter.rb, line 3
def eval string
  execute parse tokenize string
end
execute(expression, scope = global) click to toggle source
# File lib/lisp/interpreter.rb, line 28
def execute expression, scope = global
  return scope.fetch(expression) { |var| raise "#{var} is undefined" } if expression.is_a? Symbol
  return expression unless expression.is_a? Array

  case expression[0]
  when :define
    _, var, expression = expression
    scope[var] = execute expression, scope
  when :lambda
    _, params, expression = expression
    lambda { |*args| execute expression, scope.merge(Hash[params.zip(args)]) }
  when :if
    _, test, consequent, alternative = expression
    expression = if execute test, scope then consequent else alternative end
    execute expression, scope
  when :set!
    _, var, expression = expression
    if scope.has_key?(var) then scope[var] = execute expression, scope else raise "#{var} is undefined" end
  when :begin
    _, *expression = expression
    expression.map { |expression| execute expression, scope }.last
  else
    function, *args = expression.map { |expression| execute expression, scope }
    function.call *args
  end
end
parse(tokens, tree = []) click to toggle source
# File lib/lisp/interpreter.rb, line 11
def 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
    atom token
  end
end
tokenize(string) click to toggle source
# File lib/lisp/interpreter.rb, line 7
def tokenize string
  string.gsub("(", " ( ").gsub(")", " ) ").split
end

Private Class Methods

atom(token) click to toggle source
# File lib/lisp/interpreter.rb, line 57
def atom token
  case token
  when /^[\p{N}\.]+$/
    token.to_f % 1 > 0 ? token.to_f : token.to_i
  else
    token.to_sym
  end
end
global() click to toggle source
# File lib/lisp/interpreter.rb, line 66
def global
  operators = [:==, :"!=", :"<", :"<=", :">", :">=", :+, :-, :*, :/]

  operators.inject({}) do |scope, operator|
    scope.merge operator => lambda { |*args| args.inject &operator }
  end
end