summaryrefslogtreecommitdiff
path: root/src/LispExecutionContext.kt
blob: 9c2d9f8c861f78b9cd6314a23fefd248baa7c38e (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
113
114
115
116
117
118
119
120
121
122
123
124
125
126
package moe.nea.lisp

class LispExecutionContext() {

    private val errorReporter = LispErrorReporter()
    val rootStackFrame = StackFrame(null)
    val unloadedModules = mutableMapOf<String, LispAst.Program>()
    val modules = mutableMapOf<String, Map<String, LispData>>()

    fun reportError(name: String, position: HasLispPosition): LispData.LispNil {
        println("Error: $name ${position.position}")
        return LispData.LispNil
    }


    fun genBindings(): StackFrame {
        return StackFrame(rootStackFrame)
    }

    fun setupStandardBindings() {
        CoreBindings.offerAllTo(rootStackFrame)
        registerModule("builtins", Builtins.builtinProgram)
        registerModule("test", Builtins.testProgram)
        modules["ntest"] = TestFramework.realizedTestModule
        importModule("builtins", rootStackFrame, object : HasLispPosition {
            override val position: LispPosition
                get() = error("Builtin import failed")
        })
    }

    fun runTests(
        program: LispAst.Program,
        name: String,
        stackFrame: StackFrame = genBindings(),
        testList: List<String> = emptyList(),
        isWhitelist: Boolean = false
    ): TestFramework.TestSuite {
        val testSuite = TestFramework.setup(stackFrame, name, testList, isWhitelist)
        executeProgram(stackFrame, program)
        testSuite.isTesting = false
        return testSuite
    }


    fun registerModule(moduleName: String, program: LispAst.Program) {
        if (moduleName in unloadedModules || moduleName in modules) {
            error("Cannot register already registered module $moduleName")
        }
        unloadedModules[moduleName] = program
    }

    fun importModule(moduleName: String, into: StackFrame, position: HasLispPosition) {
        var exports = modules[moduleName]
        if (exports == null) {
            val module = unloadedModules[moduleName]
            if (module == null) {
                reportError("Could not find module $moduleName", position)
                return
            }
            exports = realizeModule(moduleName)
        }
        into.variables.putAll(exports)
    }

    private fun realizeModule(moduleName: String): Map<String, LispData> {
        val map = mutableMapOf<String, LispData>()
        modules[moduleName] = map
        val module = unloadedModules.remove(moduleName) ?: error("Could not find module $moduleName")
        val stackFrame = genBindings()
        stackFrame.setValueLocal("export", LispData.externalRawCall("export") { context, callsite, stackFrame, args ->
            args.forEach { name ->
                if (name !is LispAst.Reference) {
                    context.reportError("Invalid export", name)
                    return@forEach
                }
                map[name.label] = context.resolveValue(stackFrame, name)
            }
            return@externalRawCall LispData.LispNil
        })
        executeProgram(stackFrame, module)
        return map
    }

    fun executeProgram(stackFrame: StackFrame, program: LispAst.Program): LispData? {
        var lastValue: LispData? = null
        for (node in program.nodes) {
            lastValue = executeLisp(stackFrame, node)
        }
        return lastValue
    }


    fun executeLisp(stackFrame: StackFrame, node: LispAst.LispNode): LispData {
        when (node) {
            is LispAst.Parenthesis -> {
                val first = node.items.firstOrNull()
                    ?: return reportError("Cannot execute empty parenthesis ()", node)

                val rest = node.items.drop(1)
                return when (val resolvedValue = resolveValue(stackFrame, first)) {
                    is LispData.LispExecutable -> {
                        resolvedValue.execute(this, node, stackFrame, rest)
                    }

                    else -> reportError("Cannot evaluate expression of type $resolvedValue", node)
                }

            }

            else -> return reportError("Expected invocation", node)
        }
    }

    fun resolveValue(stackFrame: StackFrame, node: LispAst.LispNode): LispData {
        return when (node) {
            is LispAst.Atom -> LispData.Atom(node.label)
            is LispAst.Parenthesis -> executeLisp(stackFrame, node)
            is LispAst.Reference -> stackFrame.resolveReference(node.label)
                ?: reportError("Could not resolve variable ${node.label}", node)

            is LispAst.NumberLiteral -> LispData.LispNumber(node.numberValue)
            is LispAst.StringLiteral -> LispData.LispString(node.parsedString)
        }
    }
}