fix module registry, test failures
This commit is contained in:
parent
0012a7089d
commit
abf3aaba35
@ -204,10 +204,16 @@ func random_string(s *luajit.State) int {
|
|||||||
}
|
}
|
||||||
|
|
||||||
charset := "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
|
charset := "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
|
||||||
if s.GetTop() >= 2 {
|
if s.GetTop() >= 2 && !s.IsNil(2) {
|
||||||
charset = s.ToString(2)
|
charset = s.ToString(2)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if len(charset) == 0 {
|
||||||
|
s.PushNil()
|
||||||
|
s.PushString("empty charset")
|
||||||
|
return 2
|
||||||
|
}
|
||||||
|
|
||||||
result := make([]byte, length)
|
result := make([]byte, length)
|
||||||
charsetLen := big.NewInt(int64(len(charset)))
|
charsetLen := big.NewInt(int64(len(charset)))
|
||||||
for i := range result {
|
for i := range result {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
package registry
|
package modules
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"embed"
|
"embed"
|
||||||
@ -8,7 +8,11 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"Moonshark/functions"
|
"Moonshark/modules/crypto"
|
||||||
|
"Moonshark/modules/fs"
|
||||||
|
"Moonshark/modules/json"
|
||||||
|
"Moonshark/modules/math"
|
||||||
|
lua_string "Moonshark/modules/string"
|
||||||
|
|
||||||
luajit "git.sharkk.net/Sky/LuaJIT-to-Go"
|
luajit "git.sharkk.net/Sky/LuaJIT-to-Go"
|
||||||
)
|
)
|
||||||
@ -16,7 +20,7 @@ import (
|
|||||||
// Global registry instance
|
// Global registry instance
|
||||||
var Global *Registry
|
var Global *Registry
|
||||||
|
|
||||||
//go:embed modules/*.lua
|
//go:embed crypto/*.lua fs/*.lua json/*.lua math/*.lua string/*.lua
|
||||||
var embeddedModules embed.FS
|
var embeddedModules embed.FS
|
||||||
|
|
||||||
// Registry manages all Lua modules and Go functions
|
// Registry manages all Lua modules and Go functions
|
||||||
@ -33,33 +37,56 @@ func New() *Registry {
|
|||||||
goFuncs: make(map[string]luajit.GoFunction),
|
goFuncs: make(map[string]luajit.GoFunction),
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load all Go functions
|
// Load all Go functions from each module
|
||||||
maps.Copy(r.goFuncs, functions.GetJSONFunctions())
|
maps.Copy(r.goFuncs, json.GetFunctionList())
|
||||||
maps.Copy(r.goFuncs, functions.GetStringFunctions())
|
maps.Copy(r.goFuncs, lua_string.GetFunctionList())
|
||||||
maps.Copy(r.goFuncs, functions.GetMathFunctions())
|
maps.Copy(r.goFuncs, math.GetFunctionList())
|
||||||
maps.Copy(r.goFuncs, functions.GetFSFunctions())
|
maps.Copy(r.goFuncs, fs.GetFunctionList())
|
||||||
maps.Copy(r.goFuncs, functions.GetCryptoFunctions())
|
maps.Copy(r.goFuncs, crypto.GetFunctionList())
|
||||||
|
|
||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
|
|
||||||
// LoadEmbeddedModules loads all .lua files from embedded filesystem
|
// LoadEmbeddedModules loads all .lua files from embedded filesystem
|
||||||
func (r *Registry) LoadEmbeddedModules() error {
|
func (r *Registry) LoadEmbeddedModules() error {
|
||||||
entries, err := embeddedModules.ReadDir("modules")
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to read modules directory: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
r.mutex.Lock()
|
r.mutex.Lock()
|
||||||
defer r.mutex.Unlock()
|
defer r.mutex.Unlock()
|
||||||
|
|
||||||
|
// Load modules from subdirectories
|
||||||
|
subdirs := []string{"crypto", "fs", "json", "math", "string"}
|
||||||
|
for _, subdir := range subdirs {
|
||||||
|
if err := r.loadModulesFromDir(subdir); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// loadModulesFromDir loads all .lua files from a specific directory
|
||||||
|
func (r *Registry) loadModulesFromDir(dir string) error {
|
||||||
|
entries, err := embeddedModules.ReadDir(dir)
|
||||||
|
if err != nil {
|
||||||
|
return nil // Skip missing directories
|
||||||
|
}
|
||||||
|
|
||||||
for _, entry := range entries {
|
for _, entry := range entries {
|
||||||
if entry.IsDir() || !strings.HasSuffix(entry.Name(), ".lua") {
|
if entry.IsDir() || !strings.HasSuffix(entry.Name(), ".lua") {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
moduleName := strings.TrimSuffix(entry.Name(), ".lua")
|
fileName := strings.TrimSuffix(entry.Name(), ".lua")
|
||||||
source, err := embeddedModules.ReadFile(filepath.Join("modules", entry.Name()))
|
|
||||||
|
// If filename matches directory name (e.g., math/math.lua),
|
||||||
|
// register as just the directory name for require("math")
|
||||||
|
var moduleName string
|
||||||
|
if fileName == dir {
|
||||||
|
moduleName = dir
|
||||||
|
} else {
|
||||||
|
moduleName = dir + "/" + fileName
|
||||||
|
}
|
||||||
|
|
||||||
|
source, err := embeddedModules.ReadFile(filepath.Join(dir, entry.Name()))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to read module %s: %w", moduleName, err)
|
return fmt.Errorf("failed to read module %s: %w", moduleName, err)
|
||||||
}
|
}
|
||||||
@ -138,15 +165,3 @@ func Initialize() error {
|
|||||||
Global = New()
|
Global = New()
|
||||||
return Global.LoadEmbeddedModules()
|
return Global.LoadEmbeddedModules()
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetGoFunctions returns all Go functions
|
|
||||||
func (r *Registry) GetGoFunctions() map[string]luajit.GoFunction {
|
|
||||||
r.mutex.RLock()
|
|
||||||
defer r.mutex.RUnlock()
|
|
||||||
|
|
||||||
result := make(map[string]luajit.GoFunction, len(r.goFuncs))
|
|
||||||
for k, v := range r.goFuncs {
|
|
||||||
result[k] = v
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
@ -80,6 +80,15 @@ func string_join(s *luajit.State) int {
|
|||||||
parts[i] = s.ToString(-1) // Convert via Lua
|
parts[i] = s.ToString(-1) // Convert via Lua
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
case map[string]any:
|
||||||
|
// Empty table {} from Lua becomes map[string]any{}
|
||||||
|
if len(v) == 0 {
|
||||||
|
parts = []string{} // Empty array
|
||||||
|
} else {
|
||||||
|
s.PushNil()
|
||||||
|
s.PushString("not an array")
|
||||||
|
return 2
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
s.PushNil()
|
s.PushNil()
|
||||||
s.PushString("not an array")
|
s.PushString("not an array")
|
||||||
|
@ -163,6 +163,8 @@ end
|
|||||||
|
|
||||||
function str.lines(s)
|
function str.lines(s)
|
||||||
if type(s) ~= "string" then error("str.lines: argument must be a string", 2) end
|
if type(s) ~= "string" then error("str.lines: argument must be a string", 2) end
|
||||||
|
if s == "" then return {""} end
|
||||||
|
|
||||||
s = s:gsub("\r\n", "\n"):gsub("\r", "\n")
|
s = s:gsub("\r\n", "\n"):gsub("\r", "\n")
|
||||||
local lines = {}
|
local lines = {}
|
||||||
for line in (s .. "\n"):gmatch("([^\n]*)\n") do
|
for line in (s .. "\n"):gmatch("([^\n]*)\n") do
|
||||||
@ -485,7 +487,7 @@ end
|
|||||||
function str.slug(s)
|
function str.slug(s)
|
||||||
if type(s) ~= "string" then error("str.slug: argument must be a string", 2) end
|
if type(s) ~= "string" then error("str.slug: argument must be a string", 2) end
|
||||||
|
|
||||||
local result = s:lower()
|
local result = str.remove_accents(s):lower()
|
||||||
result = result:gsub("[^%w%s]", "")
|
result = result:gsub("[^%w%s]", "")
|
||||||
result = result:gsub("%s+", "-")
|
result = result:gsub("%s+", "-")
|
||||||
result = result:gsub("^%-+", ""):gsub("%-+$", "")
|
result = result:gsub("^%-+", ""):gsub("%-+$", "")
|
||||||
@ -493,4 +495,155 @@ function str.slug(s)
|
|||||||
return result
|
return result
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- Add these functions to the end of string.lua, before the return statement
|
||||||
|
|
||||||
|
function str.screaming_snake_case(s)
|
||||||
|
if type(s) ~= "string" then error("str.screaming_snake_case: argument must be a string", 2) end
|
||||||
|
return str.snake_case(s):upper()
|
||||||
|
end
|
||||||
|
|
||||||
|
function str.wrap(s, width)
|
||||||
|
if type(s) ~= "string" then error("str.wrap: first argument must be a string", 2) end
|
||||||
|
if type(width) ~= "number" or width <= 0 then error("str.wrap: width must be positive number", 2) end
|
||||||
|
|
||||||
|
local words = str.words(s)
|
||||||
|
local lines = {}
|
||||||
|
local current_line = ""
|
||||||
|
|
||||||
|
for _, word in ipairs(words) do
|
||||||
|
if current_line == "" then
|
||||||
|
current_line = word
|
||||||
|
elseif str.length(current_line .. " " .. word) <= width then
|
||||||
|
current_line = current_line .. " " .. word
|
||||||
|
else
|
||||||
|
table.insert(lines, current_line)
|
||||||
|
current_line = word
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if current_line ~= "" then
|
||||||
|
table.insert(lines, current_line)
|
||||||
|
end
|
||||||
|
|
||||||
|
return lines
|
||||||
|
end
|
||||||
|
|
||||||
|
function str.dedent(s)
|
||||||
|
if type(s) ~= "string" then error("str.dedent: argument must be a string", 2) end
|
||||||
|
|
||||||
|
local lines = str.lines(s)
|
||||||
|
if #lines == 0 then return "" end
|
||||||
|
|
||||||
|
-- Find minimum indentation
|
||||||
|
local min_indent = math.huge
|
||||||
|
for _, line in ipairs(lines) do
|
||||||
|
if line:match("%S") then -- Non-empty line
|
||||||
|
local indent = line:match("^(%s*)")
|
||||||
|
min_indent = math.min(min_indent, #indent)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if min_indent == math.huge then min_indent = 0 end
|
||||||
|
|
||||||
|
-- Remove common indentation
|
||||||
|
local result = {}
|
||||||
|
for _, line in ipairs(lines) do
|
||||||
|
table.insert(result, line:sub(min_indent + 1))
|
||||||
|
end
|
||||||
|
|
||||||
|
return table.concat(result, "\n")
|
||||||
|
end
|
||||||
|
|
||||||
|
function str.shell_quote(s)
|
||||||
|
if type(s) ~= "string" then error("str.shell_quote: argument must be a string", 2) end
|
||||||
|
|
||||||
|
if s:match("^[%w%-%./]+$") then
|
||||||
|
return s -- No quoting needed
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Replace single quotes with '"'"'
|
||||||
|
local quoted = s:gsub("'", "'\"'\"'")
|
||||||
|
return "'" .. quoted .. "'"
|
||||||
|
end
|
||||||
|
|
||||||
|
function str.iequals(a, b)
|
||||||
|
if type(a) ~= "string" then error("str.iequals: first argument must be a string", 2) end
|
||||||
|
if type(b) ~= "string" then error("str.iequals: second argument must be a string", 2) end
|
||||||
|
return str.lower(a) == str.lower(b)
|
||||||
|
end
|
||||||
|
|
||||||
|
function str.template_advanced(template, context)
|
||||||
|
if type(template) ~= "string" then error("str.template_advanced: first argument must be a string", 2) end
|
||||||
|
context = context or {}
|
||||||
|
if type(context) ~= "table" then error("str.template_advanced: second argument must be a table", 2) end
|
||||||
|
|
||||||
|
return template:gsub("%${([%w_.]+)}", function(path)
|
||||||
|
local keys = str.split(path, ".")
|
||||||
|
local value = context
|
||||||
|
|
||||||
|
for _, key in ipairs(keys) do
|
||||||
|
if type(value) == "table" and value[key] ~= nil then
|
||||||
|
value = value[key]
|
||||||
|
else
|
||||||
|
return ""
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return tostring(value)
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
function str.is_whitespace(s)
|
||||||
|
if type(s) ~= "string" then error("str.is_whitespace: argument must be a string", 2) end
|
||||||
|
return s:match("^%s*$") ~= nil
|
||||||
|
end
|
||||||
|
|
||||||
|
function str.strip_whitespace(s)
|
||||||
|
if type(s) ~= "string" then error("str.strip_whitespace: argument must be a string", 2) end
|
||||||
|
return s:gsub("%s", "")
|
||||||
|
end
|
||||||
|
|
||||||
|
function str.normalize_whitespace(s)
|
||||||
|
if type(s) ~= "string" then error("str.normalize_whitespace: argument must be a string", 2) end
|
||||||
|
return str.trim(s:gsub("%s+", " "))
|
||||||
|
end
|
||||||
|
|
||||||
|
function str.extract_numbers(s)
|
||||||
|
if type(s) ~= "string" then error("str.extract_numbers: argument must be a string", 2) end
|
||||||
|
|
||||||
|
local numbers = {}
|
||||||
|
for match in s:gmatch("%-?%d+%.?%d*") do
|
||||||
|
local num = tonumber(match)
|
||||||
|
if num then
|
||||||
|
table.insert(numbers, num)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return numbers
|
||||||
|
end
|
||||||
|
|
||||||
|
function str.remove_accents(s)
|
||||||
|
if type(s) ~= "string" then error("str.remove_accents: argument must be a string", 2) end
|
||||||
|
|
||||||
|
local accents = {
|
||||||
|
["à"] = "a", ["á"] = "a", ["â"] = "a", ["ã"] = "a", ["ä"] = "a", ["å"] = "a",
|
||||||
|
["è"] = "e", ["é"] = "e", ["ê"] = "e", ["ë"] = "e",
|
||||||
|
["ì"] = "i", ["í"] = "i", ["î"] = "i", ["ï"] = "i",
|
||||||
|
["ò"] = "o", ["ó"] = "o", ["ô"] = "o", ["õ"] = "o", ["ö"] = "o",
|
||||||
|
["ù"] = "u", ["ú"] = "u", ["û"] = "u", ["ü"] = "u",
|
||||||
|
["ñ"] = "n", ["ç"] = "c", ["ÿ"] = "y",
|
||||||
|
["À"] = "A", ["Á"] = "A", ["Â"] = "A", ["Ã"] = "A", ["Ä"] = "A", ["Å"] = "A",
|
||||||
|
["È"] = "E", ["É"] = "E", ["Ê"] = "E", ["Ë"] = "E",
|
||||||
|
["Ì"] = "I", ["Í"] = "I", ["Î"] = "I", ["Ï"] = "I",
|
||||||
|
["Ò"] = "O", ["Ó"] = "O", ["Ô"] = "O", ["Õ"] = "O", ["Ö"] = "O",
|
||||||
|
["Ù"] = "U", ["Ú"] = "U", ["Û"] = "U", ["Ü"] = "U",
|
||||||
|
["Ñ"] = "N", ["Ç"] = "C", ["Ÿ"] = "Y"
|
||||||
|
}
|
||||||
|
|
||||||
|
local result = s
|
||||||
|
for accented, plain in pairs(accents) do
|
||||||
|
result = result:gsub(accented, plain)
|
||||||
|
end
|
||||||
|
return result
|
||||||
|
end
|
||||||
|
|
||||||
return str
|
return str
|
@ -5,7 +5,7 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
"Moonshark/registry"
|
"Moonshark/modules"
|
||||||
|
|
||||||
luajit "git.sharkk.net/Sky/LuaJIT-to-Go"
|
luajit "git.sharkk.net/Sky/LuaJIT-to-Go"
|
||||||
)
|
)
|
||||||
@ -25,7 +25,7 @@ func main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Initialize global registry
|
// Initialize global registry
|
||||||
if err := registry.Initialize(); err != nil {
|
if err := modules.Initialize(); err != nil {
|
||||||
fmt.Fprintf(os.Stderr, "Error: failed to initialize registry: %v\n", err)
|
fmt.Fprintf(os.Stderr, "Error: failed to initialize registry: %v\n", err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
@ -39,7 +39,7 @@ func main() {
|
|||||||
defer state.Close()
|
defer state.Close()
|
||||||
|
|
||||||
// Install module system in main state
|
// Install module system in main state
|
||||||
if err := registry.Global.InstallInState(state); err != nil {
|
if err := modules.Global.InstallInState(state); err != nil {
|
||||||
fmt.Fprintf(os.Stderr, "Error: failed to install module system: %v\n", err)
|
fmt.Fprintf(os.Stderr, "Error: failed to install module system: %v\n", err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user