feat(module): add module loader and import resolution
This commit is contained in:
1 parent
345915cd92
commit
79c256af68
1 file changed
+60
@@ -0,0 +1,60 @@
|
||||
package lumen.module
|
||||
|
||||
import lumen.LumenError
|
||||
import lumen.RuntimeError
|
||||
import lumen.ast.ImportStmt
|
||||
import lumen.ast.Stmt
|
||||
import lumen.compiler.Compiler
|
||||
import lumen.lexer.Lexer
|
||||
import lumen.parser.Parser
|
||||
import lumen.runtime.NilValue
|
||||
import lumen.runtime.Value
|
||||
import lumen.vm.Vm
|
||||
import java.io.File
|
||||
|
||||
class ModuleError(val path: String, val source: String, val error: LumenError) :
|
||||
RuntimeException(error.message)
|
||||
|
||||
class ModuleLoader(
|
||||
private val vm: Vm,
|
||||
private val globalMutability: MutableMap<String, Boolean> = mutableMapOf()
|
||||
) {
|
||||
private val loaded = HashSet<String>()
|
||||
private val loading = HashSet<String>()
|
||||
|
||||
fun load(file: File): Value {
|
||||
val key = file.canonicalPath
|
||||
if (key in loaded || key in loading) return NilValue
|
||||
loading.add(key)
|
||||
val source = file.readText()
|
||||
try {
|
||||
val program = Parser(Lexer(source).scan()).parseProgram()
|
||||
loadImports(file, program)
|
||||
val script = Compiler(globalMutability).compile(program)
|
||||
val result = vm.interpret(script)
|
||||
loading.remove(key)
|
||||
loaded.add(key)
|
||||
return result
|
||||
} catch (error: LumenError) {
|
||||
throw ModuleError(file.path, source, error)
|
||||
}
|
||||
}
|
||||
|
||||
fun loadImports(origin: File, program: List<Stmt>) {
|
||||
for (stmt in program) {
|
||||
if (stmt !is ImportStmt) continue
|
||||
val target = resolve(origin, stmt.path)
|
||||
if (!target.exists()) {
|
||||
throw RuntimeError("cannot import '${stmt.path}': file not found", stmt.line, stmt.column)
|
||||
}
|
||||
load(target)
|
||||
}
|
||||
}
|
||||
|
||||
private fun resolve(origin: File, path: String): File {
|
||||
val direct = File(path)
|
||||
if (direct.isAbsolute) return direct
|
||||
val base = origin.absoluteFile.parentFile ?: File(".")
|
||||
return File(base, path)
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user