From 79c256af68185ef3ffe63b5f86d6fc0c9c70a621 Mon Sep 17 00:00:00 2001 From: DualFroz Date: Wed, 26 Aug 2026 09:00:00 +0200 Subject: [PATCH] feat(module): add module loader and import resolution --- src/main/kotlin/lumen/module/ModuleLoader.kt | 60 ++++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 src/main/kotlin/lumen/module/ModuleLoader.kt diff --git a/src/main/kotlin/lumen/module/ModuleLoader.kt b/src/main/kotlin/lumen/module/ModuleLoader.kt new file mode 100644 index 0000000..1e89327 --- /dev/null +++ b/src/main/kotlin/lumen/module/ModuleLoader.kt @@ -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 = mutableMapOf() +) { + private val loaded = HashSet() + private val loading = HashSet() + + 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) { + 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) + } +}