summaryrefslogtreecommitdiff
path: root/src/LispData.kt
blob: 5af3dd443c2d9ee7f0a72d313381ef94ac90bc56 (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
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()
    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 executionContext.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) { executionContext.reportError(it, callsite) }
                }
            }
        }


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