aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJoel Martin <github@martintribe.org>2014-04-10 22:24:58 -0500
committerJoel Martin <github@martintribe.org>2014-04-10 22:24:58 -0500
commit8bf53bec72d27d7d895b39812ffae1e990425158 (patch)
treea658f2900b9ceff44522780b3d5c28afb1abf0e2
parentb5f469de182a3e95b2d968ddccf2e2f9b77a3dc2 (diff)
downloadmal-8bf53bec72d27d7d895b39812ffae1e990425158.tar.gz
mal-8bf53bec72d27d7d895b39812ffae1e990425158.zip
Ruby: step4_if_fn_do with core functions.
-rw-r--r--ruby/core.rb20
-rw-r--r--ruby/env.rb9
-rw-r--r--ruby/printer.rb4
-rw-r--r--ruby/reader.rb2
-rw-r--r--ruby/step3_env.rb7
-rw-r--r--ruby/step4_if_fn_do.rb89
-rw-r--r--ruby/types.rb1
7 files changed, 125 insertions, 7 deletions
diff --git a/ruby/core.rb b/ruby/core.rb
new file mode 100644
index 0000000..268ebad
--- /dev/null
+++ b/ruby/core.rb
@@ -0,0 +1,20 @@
+$core_ns = {
+ :"=" => lambda {|a,b| a == b},
+ :"pr-str" => lambda {|*a| a.map {|e| _pr_str(e, true)}.join(" ")},
+ :"str" => lambda {|*a| a.map {|e| _pr_str(e, false)}.join("")},
+ :"prn" => lambda {|*a| puts(a.map {|e| _pr_str(e, true)}.join(" "))},
+ :"println" => lambda {|*a| puts(a.map {|e| _pr_str(e, false)}.join(" "))},
+ :< => lambda {|a,b| a < b},
+ :<= => lambda {|a,b| a <= b},
+ :> => lambda {|a,b| a > b},
+ :>= => lambda {|a,b| a >= b},
+ :+ => lambda {|a,b| a + b},
+ :- => lambda {|a,b| a - b},
+ :* => lambda {|a,b| a * b},
+ :/ => lambda {|a,b| a / b},
+ :list => lambda {|*a| List.new a},
+ :list? => lambda {|*a| a[0].is_a? List},
+ :empty? => lambda {|a| a.size == 0},
+ :count => lambda {|a| a.size},
+}
+
diff --git a/ruby/env.rb b/ruby/env.rb
index dfd5ec9..97dfa13 100644
--- a/ruby/env.rb
+++ b/ruby/env.rb
@@ -3,6 +3,15 @@ class Env
def initialize(outer=nil, binds=[], exprs=[])
@data = {}
@outer = outer
+ binds.each_index do |i|
+ if binds[i] == :"&"
+ data[binds[i+1]] = exprs.drop(i)
+ break
+ else
+ data[binds[i]] = exprs[i]
+ end
+ end
+ return self
end
def find(key)
diff --git a/ruby/printer.rb b/ruby/printer.rb
index 2d46e07..424ca23 100644
--- a/ruby/printer.rb
+++ b/ruby/printer.rb
@@ -9,9 +9,7 @@ def _pr_str(obj, print_readably=true)
"[" + obj.map{|x| _pr_str(x, _r)}.join(" ") + "]"
when String
if _r
- "\"" + obj.gsub(/\\/, "\\\\") \
- .gsub(/"/, "\\\\\"") \
- .gsub(/\n/, "\\\\n") + "\""
+ obj.inspect # escape special characters
else
obj
end
diff --git a/ruby/reader.rb b/ruby/reader.rb
index d224e9c..0c113e3 100644
--- a/ruby/reader.rb
+++ b/ruby/reader.rb
@@ -21,7 +21,7 @@ def tokenize(str)
end
def parse_str(t)
- return t[1..-2].gsub(/\\"/, "\"").gsub(/\\n/, "\n")
+ return t[1..-2].gsub(/\\"/, '"').gsub(/\\n/, "\n") # unescape
end
def read_atom(rdr)
diff --git a/ruby/step3_env.rb b/ruby/step3_env.rb
index 97be81d..9d1c373 100644
--- a/ruby/step3_env.rb
+++ b/ruby/step3_env.rb
@@ -31,12 +31,13 @@ def EVAL(ast, env)
# apply list
a0,a1,a2,a3 = ast
case a0
- when :"def!"
+ when :def!
return env.set(a1, EVAL(a2, env))
when :"let*"
let_env = Env.new(env)
-
- a1.each_slice(2) {|a,e| let_env.set(a, EVAL(e, let_env))}
+ a1.each_slice(2) do |a,e|
+ let_env.set(a, EVAL(e, let_env))
+ end
return EVAL(a2, let_env)
else
el = eval_ast(ast, env)
diff --git a/ruby/step4_if_fn_do.rb b/ruby/step4_if_fn_do.rb
new file mode 100644
index 0000000..e2e3e29
--- /dev/null
+++ b/ruby/step4_if_fn_do.rb
@@ -0,0 +1,89 @@
+require "readline"
+require "types"
+require "reader"
+require "printer"
+require "env"
+require "core"
+
+# read
+def READ(str)
+ return read_str(str)
+end
+
+# eval
+def eval_ast(ast, env)
+ return case ast
+ when Symbol
+ env.get(ast)
+ when List
+ List.new ast.map{|a| EVAL(a, env)}
+ when Vector
+ Vector.new ast.map{|a| EVAL(a, env)}
+ else
+ ast
+ end
+end
+
+def EVAL(ast, env)
+ if not ast.is_a? List
+ return eval_ast(ast, env)
+ end
+
+ # apply list
+ a0,a1,a2,a3 = ast
+ case a0
+ when :def!
+ return env.set(a1, EVAL(a2, env))
+ when :"let*"
+ let_env = Env.new(env)
+ a1.each_slice(2) do |a,e|
+ let_env.set(a, EVAL(e, let_env))
+ end
+ return EVAL(a2, let_env)
+ when :do
+ el = eval_ast(ast.drop(1), env)
+ return el.last
+ when :if
+ cond = EVAL(a1, env)
+ if not cond
+ return nil if a3 == nil
+ return EVAL(a3, env)
+ else
+ return EVAL(a2, env)
+ end
+ when :"fn*"
+ return lambda {|*args|
+ EVAL(a2, Env.new(env, a1, args))
+ }
+ else
+ el = eval_ast(ast, env)
+ f = el[0]
+ return f[*el.drop(1)]
+ end
+end
+
+# print
+def PRINT(exp)
+ return _pr_str(exp, true)
+end
+
+# repl
+repl_env = Env.new
+RE = lambda {|str| EVAL(READ(str), repl_env) }
+REP = lambda {|str| PRINT(EVAL(READ(str), repl_env)) }
+_ref = lambda {|k,v| repl_env.set(k, v) }
+
+# Import core functions
+$core_ns.each &_ref
+
+# Defined using the language itself
+RE["(def! not (fn* (a) (if a false true)))"]
+
+while line = Readline.readline("user> ", true)
+ begin
+ puts REP[line]
+ rescue Exception => e
+ puts "Error: #{e}"
+ puts "\t#{e.backtrace.join("\n\t")}"
+ end
+end
diff --git a/ruby/types.rb b/ruby/types.rb
index a7ce285..02ca7a6 100644
--- a/ruby/types.rb
+++ b/ruby/types.rb
@@ -3,3 +3,4 @@ end
class Vector < Array
end
+