Pi66

Home | Repos | About | Blog | Gaza

merodi
A markdown-based static site generator.
git clone https://git.pi66.xyz/merodi

About | Files | Commits | Refs | License


feat: add readable errors for Lua load failures

Author: pi66
Date: 2026-09-10 21:27
commit: a8d47ec1

Add describeLuaError and helpers to show source context for Lua parse
and runtime errors instead of gopher-lua's bare one-line messages.

Include line-snippet extraction, EOF tail views, and heuristics for
unclosed blocks, functions, and strings.

Stat

A src/core/lua/error.go 69 +69 -0

Diff

diff --git a/src/core/lua/error.go b/src/core/lua/error.go
new file mode 100644
index 0000000..3454631
--- /dev/null
+++ b/src/core/lua/error.go
@@ -0,0 +1,69 @@
+package lua
+
+import (
+	"fmt"
+	"os"
+	"strconv"
+	"strings"
+)
+
+func describeLuaError(path string, err error) error {
+	src, readErr := os.ReadFile(path)
+	if readErr != nil {
+		return fmt.Errorf("%s: %w", path, err)
+	}

...
Show all
diff --git a/src/core/lua/error.go b/src/core/lua/error.go
new file mode 100644
index 0000000..3454631
--- /dev/null
+++ b/src/core/lua/error.go
@@ -0,0 +1,69 @@
+package lua
+
+import (
+	"fmt"
+	"os"
+	"strconv"
+	"strings"
+)
+
+func describeLuaError(path string, err error) error {
+	src, readErr := os.ReadFile(path)
+	if readErr != nil {
+		return fmt.Errorf("%s: %w", path, err)
+	}
+
+	lines := strings.Split(string(src), "\n")
+
+	var b strings.Builder
+	fmt.Fprintf(&b, "%s\n\n", err.Error())
+
+	if line, ok := extractLine(err.Error()); ok && line > 0 && line <= len(lines) {
+		writeSnippet(&b, lines, line)
+	} else {
+		writeTail(&b, lines)
+		writeHints(&b, string(src))
+	}
+
+	return fmt.Errorf("%s", b.String())
+}
+
+func extractLine(msg string) (int, bool) {
+	for p := range strings.SplitSeq(msg, ":") {
+		if n, err := strconv.Atoi(strings.TrimSpace(p)); err == nil {
+			return n, true
+		}
+	}
+	return 0, false
+}
+
+func writeSnippet(b *strings.Builder, lines []string, line int) {
+	start, end := max(0, line-3), min(len(lines), line+2)
+	for i := start; i < end; i++ {
+		marker := "  "
+		if i == line-1 {
+			marker = "> "
+		}
+		fmt.Fprintf(b, "%s%4d | %s\n", marker, i+1, lines[i])
+	}
+}
+
+func writeTail(b *strings.Builder, lines []string) {
+	start := max(0, len(lines)-6)
+	fmt.Fprintln(b, "  (error at EOF, showing end of file)")
+	for i := start; i < len(lines); i++ {
+		fmt.Fprintf(b, "  %4d | %s\n", i+1, lines[i])
+	}
+}
+
+func writeHints(b *strings.Builder, src string) {
+	if o, c := strings.Count(src, "{"), strings.Count(src, "}"); o != c {
+		fmt.Fprintf(b, "\n  hint: %d '{' vs %d '}' — likely an unclosed block\n", o, c)
+	}
+	if f, e := strings.Count(src, "function"), strings.Count(src, "end"); f > e {
+		fmt.Fprintf(b, "  hint: %d 'function' vs %d 'end' — likely a missing 'end'\n", f, e)
+	}
+	if strings.Count(src, `"`)%2 != 0 {
+		fmt.Fprintln(b, "  hint: odd number of '\"' — likely an unterminated string")
+	}
+}

// Created by Pi66

// Copyright ©Pi66 2026