summaryrefslogtreecommitdiff
path: root/src/LispData.kt
blob: 8e858f3fa82eea57e940090c4869d8d566cacb65 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
package moe.nea.lisp

sealed class LispData {

	object LispNil : LispData()
	data class Atom(val label: String) : LispData()
	data class LispString(val string: String) : LispData()
	data class LispNumber(val value: Double) : LispData()
	data class LispNode(val node: LispAst.LispNode) : LispData()
	data class LispHash(val map: Map<String, LispData>) : LispData()
	data class ForeignObject<T : Any?>(val obj: T) : LispData()
	data class LispList(val elements: List<LispData>) : LispData()
	sealed class LispExecutable() : LispData() {
		abstract fun execute(
			executionContext: LispExecutionContext,
			callsite: LispAst.LispNode,
			stackFrame: StackFrame,
			args: List<LispAst.LispNode>
		): LispData
	}

	abstract class JavaExecutable(val name: String) : LispExecutable()

	data class LispInterpretedCallable(
		val declarationStackFrame: StackFrame,
		val argNames: List<String>,
		val body: LispAst.Parenthesis,
		val name: String?,
	) : LispExecutable() {
		override fun execute(
			executionContext: LispExecutionContext,
			callsite: LispAst.LispNode,
			stackFrame: StackFrame,
			args: List<LispAst.LispNode>
		): LispData {

			val invocationFrame = declarationStackFrame.fork()
			if (argNames.lastOrNull() == "...") {
				for ((name, value) in argNames.dropLast(1).zip(args)) {
					invocationFrame.setValueLocal(name, executionContext.resolveValue(stackFrame, value))
				}
				invocationFrame.setValueLocal(
					"...",
					LispList(
						args.drop(argNames.size - 1).map { executionContext.resolveValue(stackFrame, it) })
				)
			} else if (argNames.size != args.size) {
				return stackFrame.reportError(
					"Expected ${argNames.size} arguments, got ${args.size} instead",
					callsite
				)
			} else
				for ((name, value) in argNames.zip(args)) {
					invocationFrame.setValueLocal(name, executionContext.resolveValue(stackFrame, value))
				}
			return executionContext.executeLisp(invocationFrame, body)
		}
	}

	companion object {
		fun externalRawCall(
			name: String,
			callable: (context: LispExecutionContext, callsite: LispAst.LispNode, stackFrame: StackFrame, args: List<LispAst.LispNode>) -> LispData
		): LispExecutable {
			return object : JavaExecutable(name) {
				override fun execute(
					executionContext: LispExecutionContext,
					callsite: LispAst.LispNode,
					stackFrame: StackFrame,
					args: List<LispAst.LispNode>
				): LispData {
					return callable.invoke(executionContext, callsite, stackFrame, args)
				}
			}
		}

		fun externalCall(
			name: String,
			callable: (args: List<LispData>, reportError: (String) -> LispData) -> LispData
		): LispExecutable {
			return object : JavaExecutable(name) {
				override fun execute(
					executionContext: LispExecutionContext,
					callsite: LispAst.LispNode,
					stackFrame: StackFrame,
					args: List<LispAst.LispNode>
				): LispData {
					val mappedArgs = args.map { executionContext.resolveValue(stackFrame, it) }
					return callable.invoke(mappedArgs) { stackFrame.reportError(it, callsite) }
				}
			}
		}


		fun createLambda(
			declarationStackFrame: StackFrame,
			args: List<String>,
			body: LispAst.Parenthesis,
			nameHint: String? = null,
		): LispExecutable {
			return LispInterpretedCallable(declarationStackFrame, args, body, nameHint)
		}

		fun boolean(b: Boolean): Atom {
			return if (b) {
				CoreBindings.trueValue
			} else {
				CoreBindings.falseValue
			}
		}
	}
}