Last active
August 29, 2015 13:58
-
-
Save jesg/10146611 to your computer and use it in GitHub Desktop.
This file contains hidden or 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
| module Lisp | |
| def self.lexer(str) | |
| tokens = Array.new | |
| str | |
| .gsub(/\(/, ' ( ') | |
| .gsub(/\)/, ' ) ') | |
| .split(/\s/) | |
| .each do |token| | |
| next if token.empty? | |
| tokens << case | |
| when token =~ /^\d+$/ | |
| token.to_i | |
| when token =~ /^\d+\.\d+$/ | |
| token.to_f | |
| else | |
| token | |
| end | |
| end | |
| tokens | |
| end | |
| def self.parse(tokens) | |
| first = tokens.shift | |
| if first =~ /\(/ | |
| ast = Array.new | |
| until tokens.first =~ /\)/ | |
| ast << parse(tokens) | |
| end | |
| tokens.shift | |
| return ast | |
| else | |
| return first | |
| end | |
| end | |
| class Env < Hash | |
| def initialize(params=[], args=[], outer=nil) | |
| merge params.zip(args).to_h | |
| @outer = outer | |
| end | |
| def find(var) | |
| key?(var) ? self : @outer.find(var) | |
| end | |
| end | |
| def self.eval(x, env) | |
| if x.instance_of? String | |
| return env.find(x)[x] | |
| elsif not x.instance_of? Array | |
| return x | |
| elsif x.empty? | |
| return x | |
| elsif 'quote' == x.first | |
| (_, exp) = x | |
| return exp | |
| elsif 'if' == x.first | |
| (_, test, conseq, alt) = x | |
| return Lisp.eval((Lisp.eval(test, env) ? conseq : alt), env) | |
| elsif 'set!' == x.first | |
| (_, var, exp) = x | |
| env.find(var)[var] = Lisp.eval(exp, env) | |
| elsif 'define' == x.first | |
| (_, var, exp) = x | |
| env[var] = Lisp.eval(exp, env) | |
| elsif 'lambda' == x.first # (lambda (arg1 arg2...) exp1 exp2...) | |
| (_, vars) = x | |
| return proc do |args| | |
| result = [] | |
| local_env = Lisp.Env.new(vars, args, env) | |
| x.slice(2..-1).each do |exp| | |
| result = Lisp.eval(exp, local_env) | |
| end | |
| result | |
| end | |
| else | |
| exps = x.collect { |exp| Lisp.eval(exp, env) } | |
| proc = exps.shift | |
| return proc.call(*exps) | |
| end | |
| end | |
| def self.repl(env, cursor='> ') | |
| loop do | |
| print cursor | |
| result = Lisp.eval(Lisp.parse(Lisp.lexer(gets())), env) | |
| puts(result.to_s) | |
| end | |
| end | |
| end | |
| global_env = Lisp::Env.new | |
| global_env['car'] = proc {|x| x.last} | |
| global_env['cdr'] = proc {|x| x.slice(0..-2)} | |
| global_env['cons'] = proc {|head, tail| tail.push(head) } | |
| global_env['eq'] = proc {|x, y| x == y } | |
| ['+', '-', '*', '/', '%'].each do |msg| | |
| global_env[msg] = proc {|x, *y| x.send msg, *y} | |
| end | |
| Lisp.repl(global_env) | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment