Created
March 4, 2013 06:38
-
-
Save akoskovacs/5080432 to your computer and use it in GitHub Desktop.
A very simple and awesome Lisp interpreter in ruby
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 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