Skip to content

Instantly share code, notes, and snippets.

@akoskovacs
Created March 4, 2013 06:38
Show Gist options
  • Save akoskovacs/5080432 to your computer and use it in GitHub Desktop.
Save akoskovacs/5080432 to your computer and use it in GitHub Desktop.
A very simple and awesome Lisp interpreter in ruby
# A very simple and awesome Lisp interpreter in ruby
# Usage:
# require './rb_lisp.rb'
# RBLisp::Interpreter.new("(print (+ 5 (* 99 5)))") # => will print 500
module RBLisp
class Value
attr_accessor :type, :value
def initialize(args)
@type = args[:type] || :nil
@value = args[:value]
end
end
class Interpreter
NIL_VALUE = Value.new type: :nil
TRUE_VALUE = Value.new type: :true
def initialize(str)
@stack = []
define_common_functions
@values = { :pi => (Value.new type: :number, value: 3.141592654) }
interpret(str)
end
def define_common_functions
myprint = lambda do |s,argc|
argc.times do
v = s.pop
puts v.value
end
return NIL_VALUE
end
plus = lambda do |s,argc|
a = 0
argc.times do
v = s.pop
a = a + v.value if v.type == :number
end
return Value.new type: :number, value: a
end
mul = lambda do |s,argc|
a = 1
argc.times do
v = s.pop
a = a * v.value if v.type == :number
end
return Value.new type: :number, value: a
end
@functions = { :print => (Value.new type: :proc, value: myprint),
:+ => (Value.new type: :proc, value: plus),
:* => (Value.new type: :proc, value: mul)}
end
def interpret(str)
l = str.scan /\w+|\)|\(|:|'|\+|\*/
if l.shift == '('
eval_list(l)
end
end
def eval_list(l)
fn = l.shift
argc=0
args = []
while e = l.shift and e != ')' and e != nil
case e[0]
when '('
eval_list(l)
argc = argc+1
when ')' then break
when '0'..'9'
@stack << (Value.new type: :number, value: e.to_i)
argc = argc+1
when 'a'..'z'
@stack.push @values[e.to_sym]
argc = argc+1
when '\"'
@stack.push (Value.new type: :string, value: e[1..(e.length-2)])
argc = argc+1
when nil then return
end
end
call_function(fn.to_sym, argc)
end
def call_function(fn, argc)
f = @functions[fn]
if f == nil
puts "ERROR method #{fn.to_s} not exist"
elsif f.type == :proc
@stack.push(f.value.call(@stack, argc))
else
body = f.value
body.shift
@stack.push(eval_list(body))
end
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment