Moonshark/modules.go
2025-07-14 20:45:26 -05:00

109 lines
2.7 KiB
Go

package main
import (
"embed"
"fmt"
"path/filepath"
"strings"
luajit "git.sharkk.net/Sky/LuaJIT-to-Go"
)
//go:embed modules/*.lua
var builtinModules embed.FS
// ModuleRegistry manages built-in modules
type ModuleRegistry struct {
modules map[string]string
}
// NewModuleRegistry creates a new module registry
func NewModuleRegistry() *ModuleRegistry {
return &ModuleRegistry{
modules: make(map[string]string),
}
}
// RegisterModule adds a module by name and source code
func (mr *ModuleRegistry) RegisterModule(name, source string) {
mr.modules[name] = source
}
// LoadEmbeddedModules loads all modules from the embedded filesystem
func (mr *ModuleRegistry) LoadEmbeddedModules() error {
entries, err := builtinModules.ReadDir("modules")
if err != nil {
fmt.Printf("Failed to read modules directory: %v\n", err)
return 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("modules", 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")
// 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
}
// BackupOriginalRequire saves the original require function
func BackupOriginalRequire(state *luajit.State) {
state.GetGlobal("require")
state.SetGlobal("_require_original")
}