Implemented Kotlin MAL up to step 4
[jackhill/mal.git] / kotlin / src / mal / step2_eval.kt
1 package mal
2
3 fun read(input: String?): MalType = read_str(input)
4
5 fun eval(ast: MalType, env: Map<String, MalType>): MalType =
6 if (ast is MalList) {
7 val evaluated = eval_ast(ast, env) as ISeq
8 if (evaluated.first() !is MalFunction) throw MalException("cannot execute non-function")
9 (evaluated.first() as MalFunction).apply(evaluated.rest())
10 } else eval_ast(ast, env)
11
12 fun eval_ast(ast: MalType, env: Map<String, MalType>): MalType =
13 if (ast is MalSymbol) {
14 env.get(ast.value) ?: throw MalException("'${ast.value}' not found")
15 } else if (ast is MalList) {
16 ast.elements.fold(MalList(), { a, b -> a.conj_BANG(eval(b, env)); a })
17 } else if (ast is MalVector) {
18 ast.elements.fold(MalVector(), { a, b -> a.conj_BANG(eval(b, env)); a })
19 } else if (ast is MalHashMap) {
20 ast.elements.entries.fold(MalHashMap(), { a, b -> a.assoc_BANG(b.key, eval(b.value, env)); a })
21 } else ast
22
23 fun print(result: MalType) = pr_str(result, print_readably = true)
24
25 fun main(args: Array<String>) {
26 val env = hashMapOf(
27 Pair("+", MalFunction({ a: ISeq -> a.seq().reduce({ x, y -> x as MalInteger + y as MalInteger }) })),
28 Pair("-", MalFunction({ a: ISeq -> a.seq().reduce({ x, y -> x as MalInteger - y as MalInteger }) })),
29 Pair("*", MalFunction({ a: ISeq -> a.seq().reduce({ x, y -> x as MalInteger * y as MalInteger }) })),
30 Pair("/", MalFunction({ a: ISeq -> a.seq().reduce({ x, y -> x as MalInteger / y as MalInteger }) }))
31 )
32
33 while (true) {
34 val input = readline("user> ") ?: break
35
36 try {
37 println(print(eval(read(input), env)))
38 } catch (e: MalContinue) {
39 } catch (e: MalException) {
40 println("Error: " + e.message)
41 } catch (t: Throwable) {
42 println("Uncaught " + t + ": " + t.message)
43 }
44 }
45 }