Step 1 | Step 6 |
reader.EXT:
Reader(tokens) object: position, next(), peek()
tokenize: /[\s,]*(~@|[\[\]{}()'`~^@]|"(?:\\.|[^\\"])*"|;.*|[^\s\[\]{}('"`,;)]*)/
read_atom: int, float, string (escaped), keyword, nil, true, false, symbol
read_list: repeatedly read_form until end token (EOF is error)
read_form: expand reader macros, read_list (vector/maps too), or read_atom
read_str: tokenize, error if no tokens, call read_form(Reader(tokens))
printer.EXT:
pr_str(ast, print_readably):
- map pr_str across collections
- unescape strings if print_readably
step1_read_print.EXT:
main(args): loop: writeline PRINT(EVAL(READ(readline()), ""))
|
core.EXT:
read-string: call reader.read_str
slurp: return file content as a string
atom, atom?, deref, reset!, swap!: atom functions
step6_file.EXT:
main(args):
- add eval and *ARGV* to repl_env
- define load-file using rep
- if args, set *ARGV* to rest(args) and call load-file with args[0]
|
| |
Step 2 | Step 7 |
step2_eval.EXT:
eval_ast(ast, env): lookup symbols in env, map EVAL across collections
EVAL(ast, env):
- if not list?(ast), return eval_ast(ast, env)
- otherwise apply (ast is a list):
el = eval_ast(ast, env)
return el[0](rest(el))
main(args): loop: writeline PRINT(EVAL(READ(readline()), {+: add, ...}))
|
core.EXT:
cons, concat: sequence functions
step7_quote.EXT:
quasiquote(ast):
- ast is empty or not a list -> (quote ast)
- (unquote FOO) -> FOO
- ((splice-unquote FOO) BAR..) -> (concat FOO quasiquote(BAR...))
- (FOO BAR...) -> (cons FOO quasiquote(BAR...))
EVAL(ast, env):
- quote -> return ast[1]
- quasiquote -> set ast to quasiquote(ast[1]), loop
|
| |
Step 3 | Step 8 |
env.EXT:
Env(outer) object: data, set(k, v), find(k), get(k)
step3_env.EXT:
eval_ast(ast, env): switch to env.get for symbol lookup
EVAL(ast, env):
- def! -> return env.set(ast[1], EVAL(ast[2], env))
- let* -> create new env let_env
for each ODD/EVEN pair in ast[1]:
let_env.set(ODD, EVAL(EVEN, let_env))
return EVAL(ast[2], let_env)
main(args): populate repl_env with numeric functions using repl_env.set
|
core.EXT:
nth, first, rest: sequence functions
step8_macros.EXT:
macroexpand(ast, env):
- while env.get(ast[0]) is a macro: ast = env.get(ast[0])(rest(ast))
EVAL(ast, env):
- before apply section, add ast = macroexpand(ast, env)
- defmacro! -> same as def!, but set mal function macro flag
- macroexpand -> return macroexpand(ast[1], env)
|
| |
Step 4 | Step 9 |
env.EXT:
Env(outer, binds, exprs) object: map binds to exprs, handle "&" as variadic
core.EXT:
=: recursive compare of collections
pr-str, str: return pr_str(arg, true) join " ", pr_str(arg, false) join ""
prn, println: print pr_str(arg, true) join "", pr_str(arg, false) join ""
<, <=, >, >=, +, -, *, /: numeric comparison and numeric operations
list, list?, empty?, count: sequence functions
step4_do_if_fn.EXT:
EVAL(ast, env):
- do -> return last element of eval_ast(ast, env)
- if -> if EVAL(ast[1], env): return EVAL(ast[2], env)
else : return EVAL(ast[3], env)
- fn* -> return closure:
(args) -> EVAL(ast[2], new Env(env, ast[1], args))
main(args): populate repl_env with core functions, define not using rep()
|
core.EXT:
throw: raise mal value as exception (maybe wrap in native exception)
vector, vector?: sequence functions
hash-map, get, contains?, keys, vals: hash-map functions
assoc, dissoc: immutable hash-map transform functions
apply(f, args..., last): return f(concat(args, last))
map(f, args): return list of mapping f on each args
step9_try.EXT:
EVAL(ast, env):
- try* -> try EVAL(ast[1], env)
catch exception exc (unwrap if necessary):
new err_env with ast[2][1] symbol bound to exc
EVAL(ast[2][2], err_env)
|
| |
Step 5 | Step A |
step5_tco.EXT:
EVAL(ast, env):
- top level loop in EVAL
- let* -> set env to let_env, set ast to ast[2], loop
- do -> eval_ast of middle elements, sets ast to last element, loop
- if -> set ast to ast[2] or ast[3] (or nil) depending condition, loop
- fn* -> return new mal function type f with:
f.ast=ast[2], f.params=ast[1], f.env=env
- apply -> el = eval_ast(ast, env)
f = el[0]
if f is a mal function: ast = f.ast and env = f.env, loop
else : return el[0](rest(el))
|
core.EXT:
string?: true if string
readline: prompt and read a line of input (synchronous)
time-ms: return milliseconds since epoch (1970-1-1)
conj, seq: type specific sequence functions
meta, with-meta: metadata functions
step9_try.EXT:
EVAL(ast, env):
- set *host-language* in repl_env to host language name
- *gensym-count*: define (using rep()) an atom type containing an integer
- gensym: define using rep(), increment *gensym-count*, return unique symbol
- or: use gensym to fix or macro
main(args): rep("(println (str \"Mal [\" *host-language* \"]\"))")
|
| |