3 fun read(input: String?): MalType = read_str(input)
5 fun eval(_ast: MalType, _env: Env): MalType {
10 ast = macroexpand(ast, env)
13 val first = ast.first()
15 if (first is MalSymbol && first.value == "def!") {
16 return env.set(ast.nth(1) as MalSymbol, eval(ast.nth(2), env))
17 } else if (first is MalSymbol && first.value == "let*") {
18 val childEnv = Env(env)
19 val bindings = ast.nth(1) as? ISeq ?: throw MalException("expected sequence as the first parameter to let*")
21 val it = bindings.seq().iterator()
22 while (it.hasNext()) {
24 if (!it.hasNext()) throw MalException("odd number of binding elements in let*")
25 childEnv.set(key as MalSymbol, eval(it.next(), childEnv))
30 } else if (first is MalSymbol && first.value == "fn*") {
31 val binds = ast.nth(1) as? ISeq ?: throw MalException("fn* requires a binding list as first parameter")
32 val params = binds.seq().filterIsInstance<MalSymbol>()
35 return MalFnFunction(body, params, env, { s: ISeq ->
36 eval(body, Env(env, params, s.seq()))
38 } else if (first is MalSymbol && first.value == "do") {
39 eval_ast(ast.slice(1, ast.seq().count() - 1), env)
40 ast = ast.seq().last()
41 } else if (first is MalSymbol && first.value == "if") {
42 val check = eval(ast.nth(1), env)
44 if (check != NIL && check != FALSE) {
46 } else if (ast.seq().asSequence().count() > 3) {
49 } else if (first is MalSymbol && first.value == "quote") {
51 } else if (first is MalSymbol && first.value == "quasiquote") {
52 ast = quasiquote(ast.nth(1))
53 } else if (first is MalSymbol && first.value == "defmacro!") {
54 val macro = eval(ast.nth(2), env) as MalFunction
56 return env.set(ast.nth(1) as MalSymbol, macro)
57 } else if (first is MalSymbol && first.value == "macroexpand") {
58 return macroexpand(ast.nth(1), env)
59 } else if (first is MalSymbol && first.value == "try*") {
62 return eval(body, env)
63 } catch (e: Exception) {
64 val thrown = if (e is MalException) e else MalException(e.message)
65 val symbol = (ast.nth(2) as MalList).nth(1) as MalSymbol
66 val catchBody = (ast.nth(2) as MalList).nth(2)
67 val catchEnv = Env(env)
68 catchEnv.set(symbol, thrown)
69 return eval(catchBody, catchEnv)
72 val evaluated = eval_ast(ast, env) as ISeq
73 val firstEval = evaluated.first()
75 if (firstEval is MalFnFunction) {
77 env = Env(firstEval.env, firstEval.params, evaluated.rest().seq())
78 } else if (firstEval is MalFunction) {
79 return firstEval.apply(evaluated.rest())
80 } else throw MalException("cannot execute non-function")
82 } else return eval_ast(ast, env)
86 fun eval_ast(ast: MalType, env: Env): MalType =
87 if (ast is MalSymbol) {
89 } else if (ast is MalList) {
90 ast.elements.fold(MalList(), { a, b -> a.conj_BANG(eval(b, env)); a })
91 } else if (ast is MalVector) {
92 ast.elements.fold(MalVector(), { a, b -> a.conj_BANG(eval(b, env)); a })
93 } else if (ast is MalHashMap) {
94 ast.elements.entries.fold(MalHashMap(), { a, b -> a.assoc_BANG(b.key, eval(b.value, env)); a })
97 private fun is_pair(ast: MalType): Boolean = ast is ISeq && ast.seq().any()
99 private fun quasiquote(ast: MalType): MalType {
101 val quoted = MalList()
102 quoted.conj_BANG(MalSymbol("quote"))
103 quoted.conj_BANG(ast)
107 val seq = ast as ISeq
108 var first = seq.first()
110 if ((first as? MalSymbol)?.value == "unquote") {
114 if (is_pair(first) && ((first as ISeq).first() as? MalSymbol)?.value == "splice-unquote") {
115 val spliced = MalList()
116 spliced.conj_BANG(MalSymbol("concat"))
117 spliced.conj_BANG((first as ISeq).nth(1))
118 spliced.conj_BANG(quasiquote(MalList(seq.seq().drop(1).toLinkedList())))
122 val consed = MalList()
123 consed.conj_BANG(MalSymbol("cons"))
124 consed.conj_BANG(quasiquote(ast.first()))
125 consed.conj_BANG(quasiquote(MalList(seq.seq().drop(1).toLinkedList())))
129 private fun is_macro_call(ast: MalType, env: Env): Boolean {
130 val symbol = (ast as? MalList)?.first() as? MalSymbol ?: return false
131 val function = env.find(symbol) as? MalFunction ?: return false
132 return function.is_macro
135 private fun macroexpand(_ast: MalType, env: Env): MalType {
137 while (is_macro_call(ast, env)) {
138 val symbol = (ast as MalList).first() as MalSymbol
139 val function = env.find(symbol) as MalFunction
140 ast = function.apply(ast.rest())
145 fun print(result: MalType) = pr_str(result, print_readably = true)
147 fun rep(input: String, env: Env): String =
148 print(eval(read(input), env))
150 fun main(args: Array<String>) {
152 ns.forEach({ it -> repl_env.set(it.key, it.value) })
154 // Need to cast the strings explicitly to MalType to get this to compile. Looks like a bug in kotlinc,
155 // and it results in a warning.
156 repl_env.set(MalSymbol("*ARGV*"), MalList(args.drop(1).map({ it -> MalString(it) as MalType }).toLinkedList()))
157 repl_env.set(MalSymbol("eval"), MalFunction({ a: ISeq -> eval(a.first(), repl_env) }))
159 rep("(def! not (fn* (a) (if a false true)))", repl_env)
160 rep("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))", repl_env)
161 rep("(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \"odd number of forms to cond\")) (cons 'cond (rest (rest xs)))))))", repl_env)
162 rep("(defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs) `(let* (or_FIXME ~(first xs)) (if or_FIXME or_FIXME (or ~@(rest xs))))))))", repl_env)
165 rep("(load-file \"${args[0]}\")", repl_env)
170 val input = readline("user> ")
173 println(rep(input, repl_env))
174 } catch (e: EofException) {
176 } catch (e: MalContinue) {
177 } catch (e: MalException) {
178 println("Error: " + e.message)
179 } catch (t: Throwable) {
180 println("Uncaught " + t + ": " + t.message)