aboutsummaryrefslogtreecommitdiff
path: root/docs
diff options
context:
space:
mode:
authorJoel Martin <github@martintribe.org>2015-01-02 23:20:00 -0600
committerJoel Martin <github@martintribe.org>2015-01-09 16:16:55 -0600
commitf522319598c701efde91a78b07110d7039a8c906 (patch)
treec903469df13f81ffb7d706680c5eaafadbb90471 /docs
parent5400d4bf5e7fe7f968a4553f55101de962a39ef7 (diff)
downloadmal-f522319598c701efde91a78b07110d7039a8c906.tar.gz
mal-f522319598c701efde91a78b07110d7039a8c906.zip
Racket: add steps0-A. Self-hosting.
- Some additioanl tests. - Split step9 tests into optional but self-hosting requirements (metadata on functions) and other optional (conj, metadata on collections).
Diffstat (limited to 'docs')
-rw-r--r--docs/steps/step0_repl.txt11
-rw-r--r--docs/steps/step1_read_print.txt14
-rw-r--r--docs/steps/step2_eval.txt25
-rw-r--r--docs/steps/step3_env.txt38
-rw-r--r--docs/steps/step4_if_fn_do.txt76
-rw-r--r--docs/steps/step5_tco.txt78
-rw-r--r--docs/steps/step6_file.txt85
-rw-r--r--docs/steps/step7_quote.txt92
-rw-r--r--docs/steps/step8_macros.txt102
-rw-r--r--docs/steps/step9_try.txt109
-rw-r--r--docs/steps/stepA_interop.txt112
-rw-r--r--docs/steps/stepA_interop2.txt131
12 files changed, 873 insertions, 0 deletions
diff --git a/docs/steps/step0_repl.txt b/docs/steps/step0_repl.txt
new file mode 100644
index 0000000..db31e58
--- /dev/null
+++ b/docs/steps/step0_repl.txt
@@ -0,0 +1,11 @@
+--- step0_repl ----------------------------------
+READ(str): return str
+
+EVAL(ast,any): return ast
+
+PRINT(exp): return exp
+
+rep(str): return PRINT(EVAL(READ(str),""))
+
+main loop: println(rep(readline("user> ")))
+
diff --git a/docs/steps/step1_read_print.txt b/docs/steps/step1_read_print.txt
new file mode 100644
index 0000000..3a0fd7c
--- /dev/null
+++ b/docs/steps/step1_read_print.txt
@@ -0,0 +1,14 @@
+--- step1_read_print ----------------------------
+import reader, printer
+
+READ(str): return reader.read_str(str)
+
+EVAL(ast,env): return ast
+
+PRINT(exp): return printer.pr_str(exp)
+
+rep(str): return PRINT(EVAL(READ(str),""))
+
+main loop:
+ try: println(rep(readline("user> ")))
+ catch e: println("Error: ", e)
diff --git a/docs/steps/step2_eval.txt b/docs/steps/step2_eval.txt
new file mode 100644
index 0000000..beb5500
--- /dev/null
+++ b/docs/steps/step2_eval.txt
@@ -0,0 +1,25 @@
+--- step2_eval ----------------------------------
+import types, reader, printer
+
+READ(str): return reader.read_str(str)
+
+eval_ast(ast,env):
+ switch type(ast):
+ symbol: return lookup(env, ast) OR raise "'" + ast + "' not found"
+ list,vector: return ast.map((x) -> EVAL(x,env))
+ hash: return ast.map((k,v) -> list(k, EVAL(v,env)))
+ _default_: return ast
+
+EVAL(ast,env):
+ if not list?(ast): return eval_ast(ast, env)
+ f, args = eval_ast(ast, env)
+ return apply(f, args)
+
+PRINT(exp): return printer.pr_str(exp)
+
+repl_env = {'+: add_fn, ...}
+rep(str): return PRINT(EVAL(READ(str),repl_env))
+
+main loop:
+ try: println(rep(readline("user> ")))
+ catch e: println("Error: ", e)
diff --git a/docs/steps/step3_env.txt b/docs/steps/step3_env.txt
new file mode 100644
index 0000000..a1de572
--- /dev/null
+++ b/docs/steps/step3_env.txt
@@ -0,0 +1,38 @@
+--- step3_env -----------------------------------
+import types, reader, printer, env
+
+READ(str): return reader.read_str(str)
+
+eval_ast(ast,env):
+ switch type(ast):
+ symbol: return env.get(ast)
+ list,vector: return ast.map((x) -> EVAL(x,env))
+ hash: return ast.map((k,v) -> list(k, EVAL(v,env)))
+ _default_: return ast
+
+EVAL(ast,env):
+ if not list?(ast): return eval_ast(ast, env)
+ switch ast[0]:
+ 'def!: return env.set(ast[1], EVAL(ast[2], env))
+ 'let*: return EVAL(ast[2], ...)
+ _default_: f, args = eval_ast(ast, env)
+ return apply(f, args)
+
+PRINT(exp): return printer.pr_str(exp)
+
+repl_env = new Env()
+rep(str): return PRINT(EVAL(READ(str),repl_env))
+
+repl_env.set('+, add_fn)
+ ...
+
+main loop:
+ try: println(rep(readline("user> ")))
+ catch e: println("Error: ", e)
+
+--- env module ----------------------------------
+class Env (outer=null)
+ data = hash_map()
+ set(k,v): return data.set(k,v)
+ find(k): return data.has(k) ? this : (if outer ? find(outer) : null)
+ get(k): return data.find(k).get(k) OR raise "'" + k + "' not found"
diff --git a/docs/steps/step4_if_fn_do.txt b/docs/steps/step4_if_fn_do.txt
new file mode 100644
index 0000000..805d9b1
--- /dev/null
+++ b/docs/steps/step4_if_fn_do.txt
@@ -0,0 +1,76 @@
+--- step4_if_fn_do ------------------------------
+import types, reader, printer, env, core
+
+READ(str): return reader.read_str(str)
+
+eval_ast(ast,env):
+ switch type(ast):
+ symbol: return env.get(ast)
+ list,vector: return ast.map((x) -> EVAL(x,env))
+ hash: return ast.map((k,v) -> list(k, EVAL(v,env)))
+ _default_: return ast
+
+EVAL(ast,env):
+ if not list?(ast): return eval_ast(ast, env)
+ switch ast[0]:
+ 'def!: return env.set(ast[1], EVAL(ast[2], env))
+ 'let*: return EVAL(ast[2], ...)
+ 'do: return eval_ast(rest(ast), env)[-1]
+ 'if: return EVAL(EVAL(ast[1], env) ? ast[2] : ast[3], env)
+ 'fn*: return (...a) -> EVAL(ast[2], new Env(env, ast[1], a))
+ _default_: f, args = eval_ast(ast, env)
+ return apply(f, args)
+
+PRINT(exp): return printer.pr_str(exp)
+
+repl_env = new Env()
+rep(str): return PRINT(EVAL(READ(str),repl_env))
+
+;; core.EXT: defined using Racket
+core.ns.map((k,v) -> (repl_env.set(k, v)))
+
+;; core.mal: defined using the language itself
+rep("(def! not (fn* (a) (if a false true)))")
+
+main loop:
+ try: println(rep(readline("user> ")))
+ catch e: println("Error: ", e)
+
+--- env module ----------------------------------
+class Env (outer=null,binds=[],exprs=[])
+ data = hash_map()
+ foreach b, i in binds:
+ if binds[i] == '&: data[binds[i+1]] = exprs.drop(i); break
+ else: data[binds[i]] = exprs[i]
+ set(k,v): return data.set(k,v)
+ find(k): return data.has(k) ? this : (if outer ? find(outer) : null)
+ get(k): return data.find(k).get(k) OR raise "'" + k + "' not found"
+
+--- core module ---------------------------------
+ns = {'=: equal?,
+ 'nil?: nil?,
+ 'true?: true?,
+ 'false?: false?,
+ 'symbol?: symbol?,
+
+ 'pr-str: (a) -> a.map(|s| pr_str(e,true)).join(" ")),
+ 'str: (a) -> a.map(|s| pr_str(e,false)).join("")),
+ 'prn: (a) -> println(a.map(|s| pr_str(e,true)).join(" ")),
+ 'println: (a) -> println(a.map(|s| pr_str(e,false)).join(" ")),
+
+ '<: lt,
+ '<=: lte,
+ '>: gt,
+ '>=: gte,
+ '+: add,
+ '-: sub,
+ '*: mult,
+ '/: div,
+
+ 'list: list,
+ 'list?: list?,
+ 'hash-map: hash_map,
+ 'map?: hash_map?,
+
+ 'empty?: empty?,
+ 'count: count}
diff --git a/docs/steps/step5_tco.txt b/docs/steps/step5_tco.txt
new file mode 100644
index 0000000..30e2a46
--- /dev/null
+++ b/docs/steps/step5_tco.txt
@@ -0,0 +1,78 @@
+--- step5_tco -----------------------------------
+import types, reader, printer, env, core
+
+READ(str): return reader.read_str(str)
+
+eval_ast(ast,env):
+ switch type(ast):
+ symbol: return env.get(ast)
+ list,vector: return ast.map((x) -> EVAL(x,env))
+ hash: return ast.map((k,v) -> list(k, EVAL(v,env)))
+ _default_: return ast
+
+EVAL(ast,env):
+ while true:
+ if not list?(ast): return eval_ast(ast, env)
+ switch ast[0]:
+ 'def!: return env.set(ast[1], EVAL(ast[2], env))
+ 'let*: env = ...; ast = ast[2] // TCO
+ 'do: ast = eval_ast(ast[1..-1], env)[-1] // TCO
+ 'if: EVAL(ast[1], env) ? ast = ast[2] : ast = ast[3] // TCO
+ 'fn*: return new MalFunc(...)
+ _default_: f, args = eval_ast(ast, env)
+ if malfunc?(ast[0]): ast = ast[0].fn; env = ... // TCO
+ else: return apply(f, args)
+
+PRINT(exp): return printer.pr_str(exp)
+
+repl_env = new Env()
+rep(str): return PRINT(EVAL(READ(str),repl_env))
+
+;; core.EXT: defined using Racket
+core.ns.map((k,v) -> (repl_env.set(k, v)))
+
+;; core.mal: defined using the language itself
+rep("(def! not (fn* (a) (if a false true)))")
+
+main loop:
+ try: println(rep(readline("user> ")))
+ catch e: println("Error: ", e)
+
+--- env module ----------------------------------
+class Env (outer=null,binds=[],exprs=[])
+ data = hash_map()
+ foreach b, i in binds:
+ if binds[i] == '&: data[binds[i+1]] = exprs.drop(i); break
+ else: data[binds[i]] = exprs[i]
+ set(k,v): return data.set(k,v)
+ find(k): return data.has(k) ? this : (if outer ? find(outer) : null)
+ get(k): return data.find(k).get(k) OR raise "'" + k + "' not found"
+
+--- core module ---------------------------------
+ns = {'=: equal?,
+ 'nil?: nil?,
+ 'true?: true?,
+ 'false?: false?,
+ 'symbol?: symbol?,
+
+ 'pr-str: (a) -> a.map(|s| pr_str(e,true)).join(" ")),
+ 'str: (a) -> a.map(|s| pr_str(e,false)).join("")),
+ 'prn: (a) -> println(a.map(|s| pr_str(e,true)).join(" ")),
+ 'println: (a) -> println(a.map(|s| pr_str(e,false)).join(" ")),
+
+ '<: lt,
+ '<=: lte,
+ '>: gt,
+ '>=: gte,
+ '+: add,
+ '-: sub,
+ '*: mult,
+ '/: div,
+
+ 'list: list,
+ 'list?: list?,
+ 'hash-map: hash_map,
+ 'map?: hash_map?,
+
+ 'empty?: empty?,
+ 'count: count}
diff --git a/docs/steps/step6_file.txt b/docs/steps/step6_file.txt
new file mode 100644
index 0000000..bccf8fa
--- /dev/null
+++ b/docs/steps/step6_file.txt
@@ -0,0 +1,85 @@
+--- step6_file ----------------------------------
+import types, reader, printer, env, core
+
+READ(str): return reader.read_str(str)
+
+eval_ast(ast,env):
+ switch type(ast):
+ symbol: return env.get(ast)
+ list,vector: return ast.map((x) -> EVAL(x,env))
+ hash: return ast.map((k,v) -> list(k, EVAL(v,env)))
+ _default_: return ast
+
+EVAL(ast,env):
+ while true:
+ if not list?(ast): return eval_ast(ast, env)
+ switch ast[0]:
+ 'def!: return env.set(ast[1], EVAL(ast[2], env))
+ 'let*: env = ...; ast = ast[2] // TCO
+ 'do: ast = eval_ast(ast[1..-1], env)[-1] // TCO
+ 'if: EVAL(ast[1], env) ? ast = ast[2] : ast = ast[3] // TCO
+ 'fn*: return new MalFunc(...)
+ _default_: f, args = eval_ast(ast, env)
+ if malfunc?(ast[0]): ast = ast[0].fn; env = ... // TCO
+ else: return apply(f, args)
+
+PRINT(exp): return printer.pr_str(exp)
+
+repl_env = new Env()
+rep(str): return PRINT(EVAL(READ(str),repl_env))
+
+;; core.EXT: defined using Racket
+core.ns.map((k,v) -> (repl_env.set(k, v)))
+repl_env.set('eval, (ast) -> EVAL(ast, repl-env))
+repl_env.set('*ARGV*, cmdline_args[1..])
+
+;; core.mal: defined using the language itself
+rep("(def! not (fn* (a) (if a false true)))")
+rep("(def! load-file (fn* (f) ...))")
+
+if cmdline_args: rep("(load-file \"" + args[0] + "\")"); exit 0
+
+main loop:
+ try: println(rep(readline("user> ")))
+ catch e: println("Error: ", e)
+
+--- env module ----------------------------------
+class Env (outer=null,binds=[],exprs=[])
+ data = hash_map()
+ foreach b, i in binds:
+ if binds[i] == '&: data[binds[i+1]] = exprs.drop(i); break
+ else: data[binds[i]] = exprs[i]
+ set(k,v): return data.set(k,v)
+ find(k): return data.has(k) ? this : (if outer ? find(outer) : null)
+ get(k): return data.find(k).get(k) OR raise "'" + k + "' not found"
+
+--- core module ---------------------------------
+ns = {'=: equal?,
+ 'nil?: nil?,
+ 'true?: true?,
+ 'false?: false?,
+ 'symbol?: symbol?,
+
+ 'pr-str: (a) -> a.map(|s| pr_str(e,true)).join(" ")),
+ 'str: (a) -> a.map(|s| pr_str(e,false)).join("")),
+ 'prn: (a) -> println(a.map(|s| pr_str(e,true)).join(" ")),
+ 'println: (a) -> println(a.map(|s| pr_str(e,false)).join(" ")),
+ 'read-string: read_str,
+ 'slurp read-file,
+
+ '<: lt,
+ '<=: lte,
+ '>: gt,
+ '>=: gte,
+ '+: add,
+ '-: sub,
+ '*: mult,
+ '/: div,
+
+ 'list: list,
+ 'list?: list?,
+ 'hash-map: hash_map,
+ 'map?: hash_map?,
+
+ 'empty?: empty?,
+ 'count: count}
diff --git a/docs/steps/step7_quote.txt b/docs/steps/step7_quote.txt
new file mode 100644
index 0000000..c166257
--- /dev/null
+++ b/docs/steps/step7_quote.txt
@@ -0,0 +1,92 @@
+--- step7_quote ---------------------------------
+import types, reader, printer, env, core
+
+READ(str): return reader.read_str(str)
+
+pair?(ast): return ... // true if non-empty sequence
+quasiquote(ast): return ... // quasiquote
+
+eval_ast(ast,env):
+ switch type(ast):
+ symbol: return env.get(ast)
+ list,vector: return ast.map((x) -> EVAL(x,env))
+ hash: return ast.map((k,v) -> list(k, EVAL(v,env)))
+ _default_: return ast
+
+EVAL(ast,env):
+ while true:
+ if not list?(ast): return eval_ast(ast, env)
+ switch ast[0]:
+ 'def!: return env.set(ast[1], EVAL(ast[2], env))
+ 'let*: env = ...; ast = ast[2] // TCO
+ 'quote: return ast[1]
+ 'quasiquote: ast = quasiquote(ast[1]) // TCO
+ 'do: ast = eval_ast(ast[1..-1], env)[-1] // TCO
+ 'if: EVAL(ast[1], env) ? ast = ast[2] : ast = ast[3] // TCO
+ 'fn*: return new MalFunc(...)
+ _default_: f, args = eval_ast(ast, env)
+ if malfunc?(ast[0]): ast = ast[0].fn; env = ... // TCO
+ else: return apply(f, args)
+
+PRINT(exp): return printer.pr_str(exp)
+
+repl_env = new Env()
+rep(str): return PRINT(EVAL(READ(str),repl_env))
+
+;; core.EXT: defined using Racket
+core.ns.map((k,v) -> (repl_env.set(k, v)))
+repl_env.set('eval, (ast) -> EVAL(ast, repl-env))
+repl_env.set('*ARGV*, cmdline_args[1..])
+
+;; core.mal: defined using the language itself
+rep("(def! not (fn* (a) (if a false true)))")
+rep("(def! load-file (fn* (f) ...))")
+
+if cmdline_args: rep("(load-file \"" + args[0] + "\")"); exit 0
+
+main loop:
+ try: println(rep(readline("user> ")))
+ catch e: println("Error: ", e)
+
+--- env module ----------------------------------
+class Env (outer=null,binds=[],exprs=[])
+ data = hash_map()
+ foreach b, i in binds:
+ if binds[i] == '&: data[binds[i+1]] = exprs.drop(i); break
+ else: data[binds[i]] = exprs[i]
+ set(k,v): return data.set(k,v)
+ find(k): return data.has(k) ? this : (if outer ? find(outer) : null)
+ get(k): return data.find(k).get(k) OR raise "'" + k + "' not found"
+
+--- core module ---------------------------------
+ns = {'=: equal?,
+ 'nil?: nil?,
+ 'true?: true?,
+ 'false?: false?,
+ 'symbol?: symbol?,
+
+ 'pr-str: (a) -> a.map(|s| pr_str(e,true)).join(" ")),
+ 'str: (a) -> a.map(|s| pr_str(e,false)).join("")),
+ 'prn: (a) -> println(a.map(|s| pr_str(e,true)).join(" ")),
+ 'println: (a) -> println(a.map(|s| pr_str(e,false)).join(" ")),
+ 'read-string: read_str,
+ 'slurp read-file,
+
+ '<: lt,
+ '<=: lte,
+ '>: gt,
+ '>=: gte,
+ '+: add,
+ '-: sub,
+ '*: mult,
+ '/: div,
+
+ 'list: list,
+ 'list?: list?,
+ 'hash-map: hash_map,
+ 'map?: hash_map?,
+
+ 'cons: (a) -> concat([a[0]], a[1]),
+ 'concat: (a) -> reduce(concat, [], a),
+ 'empty?: empty?,
+ 'count: count}
diff --git a/docs/steps/step8_macros.txt b/docs/steps/step8_macros.txt
new file mode 100644
index 0000000..da68191
--- /dev/null
+++ b/docs/steps/step8_macros.txt
@@ -0,0 +1,102 @@
+--- step8_macros --------------------------------
+import types, reader, printer, env, core
+
+READ(str): return reader.read_str(str)
+
+pair?(ast): return ... // true if non-empty sequence
+quasiquote(ast): return ... // quasiquote
+
+macro?(ast, env): return ... // true if macro function
+macroexpand(ast, env): return ... // recursive macro expansion
+
+eval_ast(ast,env):
+ switch type(ast):
+ symbol: return env.get(ast)
+ list,vector: return ast.map((x) -> EVAL(x,env))
+ hash: return ast.map((k,v) -> list(k, EVAL(v,env)))
+ _default_: return ast
+
+EVAL(ast,env):
+ while true:
+ if not list?(ast): return eval_ast(ast, env)
+ switch ast[0]:
+ 'def!: return env.set(ast[1], EVAL(ast[2], env))
+ 'let*: env = ...; ast = ast[2] // TCO
+ 'quote: return ast[1]
+ 'quasiquote: ast = quasiquote(ast[1]) // TCO
+ 'defmacro!: return ... // like def!, but set macro property
+ 'macroexpand: return macroexpand(ast[1], env)
+ 'do: ast = eval_ast(ast[1..-1], env)[-1] // TCO
+ 'if: EVAL(ast[1], env) ? ast = ast[2] : ast = ast[3] // TCO
+ 'fn*: return new MalFunc(...)
+ _default_: f, args = eval_ast(ast, env)
+ if malfunc?(ast[0]): ast = ast[0].fn; env = ... // TCO
+ else: return apply(f, args)
+
+PRINT(exp): return printer.pr_str(exp)
+
+repl_env = new Env()
+rep(str): return PRINT(EVAL(READ(str),repl_env))
+
+;; core.EXT: defined using Racket
+core.ns.map((k,v) -> (repl_env.set(k, v)))
+repl_env.set('eval, (ast) -> EVAL(ast, repl-env))
+repl_env.set('*ARGV*, cmdline_args[1..])
+
+;; core.mal: defined using the language itself
+rep("(def! not (fn* (a) (if a false true)))")
+rep("(def! load-file (fn* (f) ...))")
+rep("(defmacro! cond (fn* (& xs) ...))")
+rep("(defmacro! or (fn* (& xs) ...))")
+
+if cmdline_args: rep("(load-file \"" + args[0] + "\")"); exit 0
+
+main loop:
+ try: println(rep(readline("user> ")))
+ catch e: println("Error: ", e)
+
+--- env module ----------------------------------
+class Env (outer=null,binds=[],exprs=[])
+ data = hash_map()
+ foreach b, i in binds:
+ if binds[i] == '&: data[binds[i+1]] = exprs.drop(i); break
+ else: data[binds[i]] = exprs[i]
+ set(k,v): return data.set(k,v)
+ find(k): return data.has(k) ? this : (if outer ? find(outer) : null)
+ get(k): return data.find(k).get(k) OR raise "'" + k + "' not found"
+
+--- core module ---------------------------------
+ns = {'=: equal?,
+ 'nil?: nil?,
+ 'true?: true?,
+ 'false?: false?,
+ 'symbol?: symbol?,
+
+ 'pr-str: (a) -> a.map(|s| pr_str(e,true)).join(" ")),
+ 'str: (a) -> a.map(|s| pr_str(e,false)).join("")),
+ 'prn: (a) -> println(a.map(|s| pr_str(e,true)).join(" ")),
+ 'println: (a) -> println(a.map(|s| pr_str(e,false)).join(" ")),
+ 'read-string: read_str,
+ 'slurp read-file,
+
+ '<: lt,
+ '<=: lte,
+ '>: gt,
+ '>=: gte,
+ '+: add,
+ '-: sub,
+ '*: mult,
+ '/: div,
+
+ 'list: list,
+ 'list?: list?,
+ 'hash-map: hash_map,
+ 'map?: hash_map?,
+
+ 'cons: (a) -> concat([a[0]], a[1]),
+ 'concat: (a) -> reduce(concat, [], a),
+ 'nth: (a) -> a[0][a[1]] OR raise "nth: index out of range",
+ 'first: (a) -> a[0][0] OR nil,
+ 'rest: (a) -> a[0][1..] OR list(),
+ 'empty?: empty?,
+ 'count: count}
diff --git a/docs/steps/step9_try.txt b/docs/steps/step9_try.txt
new file mode 100644
index 0000000..f0bb5c9
--- /dev/null
+++ b/docs/steps/step9_try.txt
@@ -0,0 +1,109 @@
+--- step9_try -----------------------------------
+import types, reader, printer, env, core
+
+READ(str): return reader.read_str(str)
+
+pair?(ast): return ... // true if non-empty sequence
+quasiquote(ast): return ... // quasiquote
+
+macro?(ast, env): return ... // true if macro function
+macroexpand(ast, env): return ... // recursive macro expansion
+
+eval_ast(ast,env):
+ switch type(ast):
+ symbol: return env.get(ast)
+ list,vector: return ast.map((x) -> EVAL(x,env))
+ hash: return ast.map((k,v) -> list(k, EVAL(v,env)))
+ _default_: return ast
+
+EVAL(ast,env):
+ while true:
+ if not list?(ast): return eval_ast(ast, env)
+ switch ast[0]:
+ 'def!: return env.set(ast[1], EVAL(ast[2], env))
+ 'let*: env = ...; ast = ast[2] // TCO
+ 'quote: return ast[1]
+ 'quasiquote: ast = quasiquote(ast[1]) // TCO
+ 'defmacro!: return ... // like def!, but set macro property
+ 'macroexpand: return macroexpand(ast[1], env)
+ 'try*: return ... // try/catch native and malval exceptions
+ 'do: ast = eval_ast(ast[1..-1], env)[-1] // TCO
+ 'if: EVAL(ast[1], env) ? ast = ast[2] : ast = ast[3] // TCO
+ 'fn*: return new MalFunc(...)
+ _default_: f, args = eval_ast(ast, env)
+ if malfunc?(ast[0]): ast = ast[0].fn; env = ... // TCO
+ else: return apply(f, args)
+
+PRINT(exp): return printer.pr_str(exp)
+
+repl_env = new Env()
+rep(str): return PRINT(EVAL(READ(str),repl_env))
+
+;; core.EXT: defined using Racket
+core.ns.map((k,v) -> (repl_env.set(k, v)))
+repl_env.set('eval, (ast) -> EVAL(ast, repl-env))
+repl_env.set('*ARGV*, cmdline_args[1..])
+
+;; core.mal: defined using the language itself
+rep("(def! not (fn* (a) (if a false true)))")
+rep("(def! load-file (fn* (f) ...))")
+rep("(defmacro! cond (fn* (& xs) ...))")
+rep("(defmacro! or (fn* (& xs) ...))")
+
+if cmdline_args: rep("(load-file \"" + args[0] + "\")"); exit 0
+
+main loop:
+ try: println(rep(readline("user> ")))
+ catch e: println("Error: ", e)
+
+--- env module ----------------------------------
+class Env (outer=null,binds=[],exprs=[])
+ data = hash_map()
+ foreach b, i in binds:
+ if binds[i] == '&: data[binds[i+1]] = exprs.drop(i); break
+ else: data[binds[i]] = exprs[i]
+ set(k,v): return data.set(k,v)
+ find(k): return data.has(k) ? this : (if outer ? find(outer) : null)
+ get(k): return data.find(k).get(k) OR raise "'" + k + "' not found"
+
+--- core module ---------------------------------
+ns = {'=: equal?,
+ 'throw: throw,
+
+ 'nil?: nil?,
+ 'true?: true?,
+ 'false?: false?,
+ 'symbol: symbol,
+ 'symbol?: symbol?,
+
+ 'pr-str: (a) -> a.map(|s| pr_str(e,true)).join(" ")),
+ 'str: (a) -> a.map(|s| pr_str(e,false)).join("")),
+ 'prn: (a) -> println(a.map(|s| pr_str(e,true)).join(" ")),
+ 'println: (a) -> println(a.map(|s| pr_str(e,false)).join(" ")),
+ 'read-string: read_str,
+ 'readline: readline,
+ 'slurp read-file,
+
+ '<: lt,
+ '<=: lte,
+ '>: gt,
+ '>=: gte,
+ '+: add,
+ '-: sub,
+ '*: mult,
+ '/: div,
+
+ 'list: list,
+ 'list?: list?,
+ 'hash-map: hash_map,
+ 'map?: hash_map?,
+
+ 'cons: (a) -> concat([a[0]], a[1]),
+ 'concat: (a) -> reduce(concat, [], a),
+ 'nth: (a) -> a[0][a[1]] OR raise "nth: index out of range",
+ 'first: (a) -> a[0][0] OR nil,
+ 'rest: (a) -> a[0][1..] OR list(),
+ 'empty?: empty?,
+ 'count: count,
+ 'apply: apply,
+ 'map: map}
diff --git a/docs/steps/stepA_interop.txt b/docs/steps/stepA_interop.txt
new file mode 100644
index 0000000..9657468
--- /dev/null
+++ b/docs/steps/stepA_interop.txt
@@ -0,0 +1,112 @@
+--- step9_try -----------------------------------
+import types, reader, printer, env, core
+
+READ(str): return reader.read_str(str)
+
+pair?(ast): return ... // true if non-empty sequence
+quasiquote(ast): return ... // quasiquote
+
+macro?(ast, env): return ... // true if macro function
+macroexpand(ast, env): return ... // recursive macro expansion
+
+eval_ast(ast,env):
+ switch type(ast):
+ symbol: return env.get(ast)
+ list,vector: return ast.map((x) -> EVAL(x,env))
+ hash: return ast.map((k,v) -> list(k, EVAL(v,env)))
+ _default_: return ast
+
+EVAL(ast,env):
+ while true:
+ if not list?(ast): return eval_ast(ast, env)
+ switch ast[0]:
+ 'def!: return env.set(ast[1], EVAL(ast[2], env))
+ 'let*: env = ...; ast = ast[2] // TCO
+ 'quote: return ast[1]
+ 'quasiquote: ast = quasiquote(ast[1]) // TCO
+ 'defmacro!: return ... // like def!, but set macro property
+ 'macroexpand: return macroexpand(ast[1], env)
+ 'try*: return ... // try/catch native and malval exceptions
+ 'do: ast = eval_ast(ast[1..-1], env)[-1] // TCO
+ 'if: EVAL(ast[1], env) ? ast = ast[2] : ast = ast[3] // TCO
+ 'fn*: return new MalFunc(...)
+ _default_: f, args = eval_ast(ast, env)
+ if malfunc?(ast[0]): ast = ast[0].fn; env = ... // TCO
+ else: return apply(f, args)
+
+PRINT(exp): return printer.pr_str(exp)
+
+repl_env = new Env()
+rep(str): return PRINT(EVAL(READ(str),repl_env))
+
+;; core.EXT: defined using Racket
+core.ns.map((k,v) -> (repl_env.set(k, v)))
+repl_env.set('eval, (ast) -> EVAL(ast, repl-env))
+repl_env.set('*ARGV*, cmdline_args[1..])
+
+;; core.mal: defined using the language itself
+rep("(def! *host-language* \"racket\")")
+rep("(def! not (fn* (a) (if a false true)))")
+rep("(def! load-file (fn* (f) ...))")
+rep("(defmacro! cond (fn* (& xs) ...))")
+rep("(defmacro! or (fn* (& xs) ...))")
+
+if cmdline_args: rep("(load-file \"" + args[0] + "\")"); exit 0
+
+rep("(println (str \"Mal [\" *host-language* \"]\"))")
+main loop:
+ try: println(rep(readline("user> ")))
+ catch e: println("Error: ", e)
+
+--- env module ----------------------------------
+class Env (outer=null,binds=[],exprs=[])
+ data = hash_map()
+ foreach b, i in binds:
+ if binds[i] == '&: data[binds[i+1]] = exprs.drop(i); break
+ else: data[binds[i]] = exprs[i]
+ set(k,v): return data.set(k,v)
+ find(k): return data.has(k) ? this : (if outer ? find(outer) : null)
+ get(k): return data.find(k).get(k) OR raise "'" + k + "' not found"
+
+--- core module ---------------------------------
+ns = {'=: equal?,
+ 'throw: throw,
+
+ 'nil?: nil?,
+ 'true?: true?,
+ 'false?: false?,
+ 'symbol: symbol,
+ 'symbol?: symbol?,
+
+ 'pr-str: (a) -> a.map(|s| pr_str(e,true)).join(" ")),
+ 'str: (a) -> a.map(|s| pr_str(e,false)).join("")),
+ 'prn: (a) -> println(a.map(|s| pr_str(e,true)).join(" ")),
+ 'println: (a) -> println(a.map(|s| pr_str(e,false)).join(" ")),
+ 'read-string: read_str,
+ 'readline: readline,
+ 'slurp read-file,
+
+ '<: lt,
+ '<=: lte,
+ '>: gt,
+ '>=: gte,
+ '+: add,
+ '-: sub,
+ '*: mult,
+ '/: div,
+ 'time-ms cur-epoch-millis,
+
+ 'list: list,
+ 'list?: list?,
+ 'hash-map: hash_map,
+ 'map?: hash_map?,
+
+ 'cons: (a) -> concat([a[0]], a[1]),
+ 'concat: (a) -> reduce(concat, [], a),
+ 'nth: (a) -> a[0][a[1]] OR raise "nth: index out of range",
+ 'first: (a) -> a[0][0] OR nil,
+ 'rest: (a) -> a[0][1..] OR list(),
+ 'empty?: empty?,
+ 'count: count,
+ 'apply: apply,
+ 'map: map}
diff --git a/docs/steps/stepA_interop2.txt b/docs/steps/stepA_interop2.txt
new file mode 100644
index 0000000..1273411
--- /dev/null
+++ b/docs/steps/stepA_interop2.txt
@@ -0,0 +1,131 @@
+--- step9_try -----------------------------------
+import types, reader, printer, env, core
+
+READ(str): return reader.read_str(str)
+
+pair?(ast): return ... // true if non-empty sequence
+quasiquote(ast): return ... // quasiquote
+
+macro?(ast, env): return ... // true if macro function
+macroexpand(ast, env): return ... // recursive macro expansion
+
+eval_ast(ast,env):
+ switch type(ast):
+ symbol: return env.get(ast)
+ list,vector: return ast.map((x) -> EVAL(x,env))
+ hash: return ast.map((k,v) -> list(k, EVAL(v,env)))
+ _default_: return ast
+
+EVAL(ast,env):
+ while true:
+ if not list?(ast): return eval_ast(ast, env)
+ switch ast[0]:
+ 'def!: return env.set(ast[1], EVAL(ast[2], env))
+ 'let*: env = ...; ast = ast[2] // TCO
+ 'quote: return ast[1]
+ 'quasiquote: ast = quasiquote(ast[1]) // TCO
+ 'defmacro!: return ... // like def!, but set macro property
+ 'macroexpand: return macroexpand(ast[1], env)
+ 'try*: return ... // try/catch native and malval exceptions
+ 'do: ast = eval_ast(ast[1..-1], env)[-1] // TCO
+ 'if: EVAL(ast[1], env) ? ast = ast[2] : ast = ast[3] // TCO
+ 'fn*: return new MalFunc(...)
+ _default_: f, args = eval_ast(ast, env)
+ if malfunc?(ast[0]): ast = ast[0].fn; env = ... // TCO
+ else: return apply(f, args)
+
+PRINT(exp): return printer.pr_str(exp)
+
+repl_env = new Env()
+rep(str): return PRINT(EVAL(READ(str),repl_env))
+
+;; core.EXT: defined using Racket
+core.ns.map((k,v) -> (repl_env.set(k, v)))
+repl_env.set('eval, (ast) -> EVAL(ast, repl-env))
+repl_env.set('*ARGV*, cmdline_args[1..])
+
+;; core.mal: defined using the language itself
+rep("(def! *host-language* \"racket\")")
+rep("(def! not (fn* (a) (if a false true)))")
+rep("(def! load-file (fn* (f) ...))")
+rep("(defmacro! cond (fn* (& xs) ...))")
+rep("(defmacro! or (fn* (& xs) ...))")
+
+if cmdline_args: rep("(load-file \"" + args[0] + "\")"); exit 0
+
+rep("(println (str \"Mal [\" *host-language* \"]\"))")
+main loop:
+ try: println(rep(readline("user> ")))
+ catch e: println("Error: ", e)
+
+--- env module ----------------------------------
+class Env (outer=null,binds=[],exprs=[])
+ data = hash_map()
+ foreach b, i in binds:
+ if binds[i] == '&: data[binds[i+1]] = exprs.drop(i); break
+ else: data[binds[i]] = exprs[i]
+ set(k,v): return data.set(k,v)
+ find(k): return data.has(k) ? this : (if outer ? find(outer) : null)
+ get(k): return data.find(k).get(k) OR raise "'" + k + "' not found"
+
+--- core module ---------------------------------
+ns = {'=: equal?,
+ 'throw: throw,
+
+ 'nil?: nil?,
+ 'true?: true?,
+ 'false?: false?,
+ 'symbol: symbol,
+ 'symbol?: symbol?,
+ 'keyword: keyword,
+ 'keyword?: keyword?,
+
+ 'pr-str: (a) -> a.map(|s| pr_str(e,true)).join(" ")),
+ 'str: (a) -> a.map(|s| pr_str(e,false)).join("")),
+ 'prn: (a) -> println(a.map(|s| pr_str(e,true)).join(" ")),
+ 'println: (a) -> println(a.map(|s| pr_str(e,false)).join(" ")),
+ 'read-string: read_str,
+ 'readline: readline,
+ 'slurp read-file,
+
+ '<: lt,
+ '<=: lte,
+ '>: gt,
+ '>=: gte,
+ '+: add,
+ '-: sub,
+ '*: mult,
+ '/: div,
+
+ 'list: list,
+ 'list?: list?,
+ 'vector: vector,
+ 'vector?: vector?,
+ 'hash-map: hash_map,
+ 'map?: hash_map?,
+ 'assoc: assoc,
+ 'dissoc: dissoc,
+ 'get: get,
+ 'contains?: contains?,
+ 'keys: keys,
+ 'vals: vals,
+
+ 'sequential? sequential?,
+ 'cons: (a) -> concat([a[0]], a[1]),
+ 'concat: (a) -> reduce(concat, [], a),
+ 'nth: (a) -> a[0][a[1]] OR raise "nth: index out of range",
+ 'first: (a) -> a[0][0] OR nil,
+ 'rest: (a) -> a[0][1..] OR list(),
+ 'empty?: empty?,
+ 'count: count,
+ 'apply: apply,
+ 'map: map,
+ 'conj: conj,
+
+ 'meta: (a) -> a[0].meta,
+ 'with-meta: (a) -> a[0].with_meta(a[1]),
+ 'atom: (a) -> new Atom(a[0]),
+ 'atom?: (a) -> type(a[0]) == "atom",
+ 'deref: (a) -> a[0].val,
+ 'reset!: (a) -> a[0].val = a[1],
+ 'swap!: swap!}