package modules import ( "embed" "fmt" "path/filepath" "strings" "Moonshark/functions" luajit "git.sharkk.net/Sky/LuaJIT-to-Go" ) //go:embed *.lua var builtinModules embed.FS // ModuleRegistry manages built-in modules and Go functions type ModuleRegistry struct { modules map[string]string goFuncs map[string]luajit.GoFunction } // NewModuleRegistry creates a new module registry func NewModuleRegistry() *ModuleRegistry { mr := &ModuleRegistry{ modules: make(map[string]string), goFuncs: functions.GetAll(), } return mr } // RegisterModule adds a module by name and source code func (mr *ModuleRegistry) RegisterModule(name, source string) { mr.modules[name] = source } // RegisterGoFunction adds a Go function that modules can use func (mr *ModuleRegistry) RegisterGoFunction(name string, fn luajit.GoFunction) { mr.goFuncs[name] = fn } // LoadEmbeddedModules loads all modules from the embedded filesystem func (mr *ModuleRegistry) LoadEmbeddedModules() error { entries, err := builtinModules.ReadDir(".") if err != nil { return fmt.Errorf("failed to read modules directory: %w", err) } for _, entry := range entries { if entry.IsDir() || !strings.HasSuffix(entry.Name(), ".lua") { continue } moduleName := strings.TrimSuffix(entry.Name(), ".lua") source, err := builtinModules.ReadFile(filepath.Join(".", entry.Name())) if err != nil { return fmt.Errorf("failed to read module %s: %w", moduleName, err) } mr.RegisterModule(moduleName, string(source)) } return nil } // InstallModules sets up the module system in the Lua state func (mr *ModuleRegistry) InstallModules(state *luajit.State) error { // Create moonshark global table state.NewTable() state.SetGlobal("moonshark") // Install Go functions first if err := mr.installGoFunctions(state); err != nil { return fmt.Errorf("failed to install Go functions: %w", err) } // Register require function that checks our built-in modules first err := state.RegisterGoFunction("require", func(s *luajit.State) int { if err := s.CheckMinArgs(1); err != nil { return s.PushError("require: %v", err) } moduleName, err := s.SafeToString(1) if err != nil { return s.PushError("require: module name must be a string") } // Check if it's a built-in module if source, exists := mr.modules[moduleName]; exists { // Execute the module and return its result if err := s.LoadString(source); err != nil { return s.PushError("require: failed to load module '%s': %v", moduleName, err) } if err := s.Call(0, 1); err != nil { return s.PushError("require: failed to execute module '%s': %v", moduleName, err) } return 1 // Return the module's result } // Fall back to standard Lua require s.GetGlobal("_require_original") if s.IsFunction(-1) { s.PushString(moduleName) if err := s.Call(1, 1); err != nil { return s.PushError("require: %v", err) } return 1 } return s.PushError("require: module '%s' not found", moduleName) }) return err } // installGoFunctions installs all registered Go functions into the Lua state func (mr *ModuleRegistry) installGoFunctions(state *luajit.State) error { // Install functions in moonshark namespace state.GetGlobal("moonshark") for name, fn := range mr.goFuncs { if err := state.PushGoFunction(fn); err != nil { return fmt.Errorf("failed to register Go function '%s': %w", name, err) } state.SetField(-2, name) } state.Pop(1) // Remove moonshark table return nil } // BackupOriginalRequire saves the original require function func BackupOriginalRequire(state *luajit.State) { state.GetGlobal("require") state.SetGlobal("_require_original") }