3 fun read(input: String?): MalType = read_str(input)
5 fun eval(_ast: MalType, _env: Env): MalType {
11 val first = ast.first()
13 if (first is MalSymbol && first.value == "def!") {
14 return env.set(ast.nth(1) as MalSymbol, eval(ast.nth(2), env))
15 } else if (first is MalSymbol && first.value == "let*") {
16 val childEnv = Env(env)
17 val bindings = ast.nth(1) as? ISeq ?: throw MalException("expected sequence as the first parameter to let*")
19 val it = bindings.seq().iterator()
20 while (it.hasNext()) {
22 if (!it.hasNext()) throw MalException("odd number of binding elements in let*")
23 childEnv.set(key as MalSymbol, eval(it.next(), childEnv))
28 } else if (first is MalSymbol && first.value == "fn*") {
29 val binds = ast.nth(1) as? ISeq ?: throw MalException("fn* requires a binding list as first parameter")
30 val params = binds.seq().filterIsInstance<MalSymbol>()
33 return MalFnFunction(body, params, env, { s: ISeq ->
34 eval(body, Env(env, params, s.seq()))
36 } else if (first is MalSymbol && first.value == "do") {
37 eval_ast(ast.slice(1, ast.seq().count() - 1), env)
38 ast = ast.seq().last()
39 } else if (first is MalSymbol && first.value == "if") {
40 val check = eval(ast.nth(1), env)
42 if (check != NIL && check != FALSE) {
44 } else if (ast.seq().asSequence().count() > 3) {
47 } else if (first is MalSymbol && first.value == "quote") {
49 } else if (first is MalSymbol && first.value == "quasiquote") {
50 ast = quasiquote(ast.nth(1))
52 val evaluated = eval_ast(ast, env) as ISeq
53 val firstEval = evaluated.first()
55 if (firstEval is MalFnFunction) {
57 env = Env(firstEval.env, firstEval.params, evaluated.rest().seq())
58 } else if (firstEval is MalFunction) {
59 return firstEval.apply(evaluated.rest())
60 } else throw MalException("cannot execute non-function")
62 } else return eval_ast(ast, env)
66 private fun is_pair(ast: MalType): Boolean = ast is ISeq && ast.seq().any()
68 private fun quasiquote(ast: MalType): MalType {
70 val quoted = MalList()
71 quoted.conj_BANG(MalSymbol("quote"))
77 var first = seq.first()
79 if ((first as? MalSymbol)?.value == "unquote") {
83 if (is_pair(first) && ((first as ISeq).first() as? MalSymbol)?.value == "splice-unquote") {
84 val spliced = MalList()
85 spliced.conj_BANG(MalSymbol("concat"))
86 spliced.conj_BANG((first as ISeq).nth(1))
87 spliced.conj_BANG(quasiquote(MalList(seq.seq().drop(1).toLinkedList())))
91 val consed = MalList()
92 consed.conj_BANG(MalSymbol("cons"))
93 consed.conj_BANG(quasiquote(ast.first()))
94 consed.conj_BANG(quasiquote(MalList(seq.seq().drop(1).toLinkedList())))
98 fun eval_ast(ast: MalType, env: Env): MalType =
99 if (ast is MalSymbol) {
101 } else if (ast is MalList) {
102 ast.elements.fold(MalList(), { a, b -> a.conj_BANG(eval(b, env)); a })
103 } else if (ast is MalVector) {
104 ast.elements.fold(MalVector(), { a, b -> a.conj_BANG(eval(b, env)); a })
105 } else if (ast is MalHashMap) {
106 ast.elements.entries.fold(MalHashMap(), { a, b -> a.assoc_BANG(b.key, eval(b.value, env)); a })
109 fun print(result: MalType) = pr_str(result, print_readably = true)
111 fun rep(input: String, env: Env): String =
112 print(eval(read(input), env))
114 fun main(args: Array<String>) {
116 ns.forEach({ it -> repl_env.set(it.key, it.value) })
118 // Need to cast the strings explicitly to MalType to get this to compile. Looks like a bug in kotlinc,
119 // and it results in a warning.
120 repl_env.set(MalSymbol("*ARGV*"), MalList(args.drop(1).map({ it -> MalString(it) as MalType }).toLinkedList()))
121 repl_env.set(MalSymbol("eval"), MalFunction({ a: ISeq -> eval(a.first(), repl_env) }))
123 rep("(def! not (fn* (a) (if a false true)))", repl_env)
124 rep("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))", repl_env)
127 rep("(load-file \"${args[0]}\")", repl_env)
132 val input = readline("user> ")
135 println(rep(input, repl_env))
136 } catch (e: EofException) {
138 } catch (e: MalContinue) {
139 } catch (e: MalException) {
140 println("Error: " + e.message)
141 } catch (t: Throwable) {
142 println("Uncaught " + t + ": " + t.message)