Skip to content

Instantly share code, notes, and snippets.

@BlackPrincess
Last active August 29, 2015 14:07
Show Gist options
  • Save BlackPrincess/58cae619eec83d82b7a6 to your computer and use it in GitHub Desktop.
Save BlackPrincess/58cae619eec83d82b7a6 to your computer and use it in GitHub Desktop.
module Lisby
class Env < Hash
def self.add_operators(env)
env.update({
:'+' => ->(a,b) { a + b },
:'-' => ->(a,b) { a - b },
:'*' => ->(a,b) { a * b },
:'/' => ->(a,b) { a / b },
:'not' => ->(a,b) { a != b },
:'>' => ->(a,b) { a > b },
:'<' => ->(a,b) { a < b },
:'>=' => ->(a,b) { a >= b },
:'<=' => ->(a,b) { a <= b },
:'=' => ->(a,b) { a == b },
:'equal?' => ->(a,b) { a.equal?(b) },
:'eq?' => ->(a,b){ a.ia_a?(b) },
:'length' => ->(a) { a.length },
:'cons' => ->(a,b) { [a] + b },
:'car' => ->(a) { a.first },
:'cdr' => ->(a) { a.drop(1) },
:'list' => ->(*a) { Array(a) },
:'list?' => ->(a) { a.instance_of?(Array) },
:'null?' => ->(a) { a.nil? },
:'symbol?' => ->(a) { a.instance_of?(Symbol) }
})
end
def initialize(params = [], args = [], outer = nil)
update(Hash[params.zip(args)])
@outer = outer
end
def find(var)
if include?(var)
self
elsif [email protected]?
@outer.find(var)
else
raise "SyntaxError! #{var} is undefined"
end
end
end
module Core
@@global_env = Lisby::Env.add_operators(Env.new)
def self.eval(x, env=@@global_env)
if x.instance_of?(Symbol)
env.find(x)[x]
elsif !x.instance_of?(Array)
x
elsif x.first == :'quote'
_, exp = x
exp
elsif x.first == :'if'
_, test, conseq, alt = x
eval(
eval(test, env) ? conseq : alt,
env
)
elsif x.first == :'set!'
_, var, exp = x
env.find(var)[var] = eval(exp, env)
nil
elsif x.first == :'define'
_, var, exp = x
env[var] = eval(exp, env)
nil
elsif x.first == :'lambda'
_, vars, exp = x
->(*args) { eval(exp, Lisby::Env.new(vars, args, env))}
elsif x.first == :'begin'
x.drop(1).map{ |exp| eval(exp, env)}.last
else
exps = x.map {|exp| eval(exp, env)}
procs = exps.shift()
procs.call(*exps)
end
end
end
module Parser
def self.read(s)
read_from(tokenize(s))
end
def self.parse(s)
read(s)
end
def self.tokenize(s)
s.gsub('(',' ( ').gsub(')',' ) ').split().map { |s| s.to_sym}
end
def self.read_from(tokens)
raise 'SyntaxError! unexpected EOF while reading' if tokens.length == 0
token = tokens.shift()
case token.to_s
when '('
l = []
until tokens.first.to_s == ')'
l.push(read_from(tokens))
end
tokens.shift()
l
when ')'
raise 'SyntaxError! unexpected )'
else
atom(token)
end
end
def self.atom(token)
begin
Integer(token.to_s)
rescue ArgumentError
begin
Float(token.to_s)
rescue ArgumentError
token.to_sym
end
end
end
end
module REPL
def self.repl(prompt= 'lisby.rb >')
while true
print prompt
input = gets
break if input == ":q\n"
begin
p Lisby::Core.eval(Lisby::Parser.parse(input))
rescue Exception => e
p e
end
end
end
end
end
def prelude(s)
Lisby::Core.eval(Lisby::Parser.parse(s))
end
prelude("(define % (lambda (a b)
(- a (* (/ a b) b))))")
prelude("(define range (lambda (a b)
(if (> a b)
(list)
(cons
a
(range (+ a 1) b)))))")
prelude("(define map (lambda (f xs)
(if (= (length xs) 0)
(list)
(cons
(f (car xs))
(map f (cdr xs))))))")
prelude("(define filter (lambda (f xs)
(if (= (length xs) 0)
(list)
(if (f (car xs))
(cons
(car xs)
(filter f (cdr xs)))
(filter f (cdr xs))))))")
prelude("(define sieve (lambda (xs)
(if (= (length xs) 0)
(list)
(cons
(car xs)
(sieve
(filter
(lambda (x)
(not (% x (car xs)) 0))
(cdr xs)))))))")
prelude("(define primes (lambda (x)
(sieve (range 2 x))))")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment