Moonshark/modules/modules.go

140 lines
3.6 KiB
Go

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")
}