LuaJIT-to-Go/stack.go

124 lines
2.6 KiB
Go

package luajit
/*
#include <lua.h>
#include <lauxlib.h>
*/
import "C"
import (
"fmt"
"strings"
)
// LuaError represents an enhanced error from the Lua state
type LuaError struct {
Code int
Message string
File string
Line int
StackTrace string
Context string
}
func (e *LuaError) Error() string {
var parts []string
if e.File != "" && e.Line > 0 {
parts = append(parts, fmt.Sprintf("%s:%d", e.File, e.Line))
}
if e.Context != "" {
parts = append(parts, fmt.Sprintf("[%s]", e.Context))
}
parts = append(parts, e.Message)
if e.Code != 0 {
parts = append(parts, fmt.Sprintf("(code=%d)", e.Code))
}
result := strings.Join(parts, " ")
if e.StackTrace != "" {
result += "\n" + e.StackTrace
}
return result
}
// Stack management constants from lua.h
const (
LUA_MINSTACK = 20 // Minimum Lua stack size
LUA_MAXSTACK = 1000000 // Maximum Lua stack size
LUA_REGISTRYINDEX = -10000 // Pseudo-index for the Lua registry
LUA_GLOBALSINDEX = -10002 // Pseudo-index for globals table
)
// GetStackTrace returns the current Lua stack trace
func (s *State) GetStackTrace() string {
s.GetGlobal("debug")
if !s.IsTable(-1) {
s.Pop(1)
return "debug table not available"
}
s.GetField(-1, "traceback")
if !s.IsFunction(-1) {
s.Pop(2) // Remove debug table and non-function
return "debug.traceback not available"
}
s.Call(0, 1)
trace := s.ToString(-1)
s.Pop(1) // Remove the trace
return trace
}
// GetErrorInfo extracts detailed error information from the Lua stack
func (s *State) GetErrorInfo(context string) *LuaError {
if s.GetTop() == 0 {
return &LuaError{
Message: "unknown error (empty stack)",
Context: context,
}
}
message := s.ToString(-1)
// Parse file:line from common Lua error format
var file string
var line int
if colonPos := strings.Index(message, ":"); colonPos > 0 {
beforeColon := message[:colonPos]
afterColon := message[colonPos+1:]
if secondColonPos := strings.Index(afterColon, ":"); secondColonPos > 0 {
file = beforeColon
if n, err := fmt.Sscanf(afterColon[:secondColonPos], "%d", &line); n == 1 && err == nil {
// Strip the file:line part from message for cleaner display
message = strings.TrimSpace(afterColon[secondColonPos+1:])
}
}
}
// Get stack trace
stackTrace := s.GetStackTrace()
return &LuaError{
Message: message,
File: file,
Line: line,
StackTrace: stackTrace,
Context: context,
}
}
// CreateLuaError creates a LuaError with full context information
func (s *State) CreateLuaError(code int, context string) *LuaError {
err := s.GetErrorInfo(context)
err.Code = code
return err
}