diff options
| author | Joel Martin <github@martintribe.org> | 2015-01-02 23:20:00 -0600 |
|---|---|---|
| committer | Joel Martin <github@martintribe.org> | 2015-01-09 16:16:55 -0600 |
| commit | f522319598c701efde91a78b07110d7039a8c906 (patch) | |
| tree | c903469df13f81ffb7d706680c5eaafadbb90471 /docs | |
| parent | 5400d4bf5e7fe7f968a4553f55101de962a39ef7 (diff) | |
| download | mal-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.txt | 11 | ||||
| -rw-r--r-- | docs/steps/step1_read_print.txt | 14 | ||||
| -rw-r--r-- | docs/steps/step2_eval.txt | 25 | ||||
| -rw-r--r-- | docs/steps/step3_env.txt | 38 | ||||
| -rw-r--r-- | docs/steps/step4_if_fn_do.txt | 76 | ||||
| -rw-r--r-- | docs/steps/step5_tco.txt | 78 | ||||
| -rw-r--r-- | docs/steps/step6_file.txt | 85 | ||||
| -rw-r--r-- | docs/steps/step7_quote.txt | 92 | ||||
| -rw-r--r-- | docs/steps/step8_macros.txt | 102 | ||||
| -rw-r--r-- | docs/steps/step9_try.txt | 109 | ||||
| -rw-r--r-- | docs/steps/stepA_interop.txt | 112 | ||||
| -rw-r--r-- | docs/steps/stepA_interop2.txt | 131 |
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!} |
