Made error messages display the source and some context.

This commit is contained in:
Mattias Hansson 2026-01-03 12:05:02 +01:00
parent 27a5b51a02
commit 275f4782c8

View file

@ -2,6 +2,7 @@ package compiler
import (
"fmt"
"os"
"strings"
"c65gm/internal/preproc"
@ -37,7 +38,7 @@ func (c *Compiler) Compile(lines []preproc.Line) ([]string, error) {
var lastKind = preproc.Source
var scriptBuffer []string
for _, line := range lines {
for i, line := range lines {
// Detect kind transitions and emit markers
if line.Kind != lastKind {
// Execute and close previous Script block
@ -78,7 +79,8 @@ func (c *Compiler) Compile(lines []preproc.Line) ([]string, error) {
}
end := strings.IndexByte(text[start+1:], '|')
if end == -1 {
return nil, fmt.Errorf("%s:%d: unclosed | in assembler line", line.Filename, line.LineNo)
c.printErrorWithContext(lines, i, fmt.Errorf("unclosed | in assembler line"))
return nil, fmt.Errorf("compilation failed")
}
end += start + 1
@ -102,18 +104,21 @@ func (c *Compiler) Compile(lines []preproc.Line) ([]string, error) {
// Find handler for this line
cmd, found := c.registry.FindHandler(line)
if !found {
return nil, &UnhandledLineError{Line: line}
c.printErrorWithContext(lines, i, fmt.Errorf("unhandled line (no matching command)"))
return nil, fmt.Errorf("compilation failed")
}
// Interpret the line
if err := cmd.Interpret(line, c.ctx); err != nil {
return nil, fmt.Errorf("%s:%d: %w", line.Filename, line.LineNo, err)
c.printErrorWithContext(lines, i, err)
return nil, fmt.Errorf("compilation failed")
}
// Generate assembly
asmLines, err := cmd.Generate(c.ctx)
if err != nil {
return nil, fmt.Errorf("%s:%d: %w", line.Filename, line.LineNo, err)
c.printErrorWithContext(lines, i, err)
return nil, fmt.Errorf("compilation failed")
}
codeOutput = append(codeOutput, fmt.Sprintf("; %s", line.Text))
@ -136,6 +141,70 @@ func (c *Compiler) Compile(lines []preproc.Line) ([]string, error) {
return c.assembleOutput(codeOutput), nil
}
// printErrorWithContext prints an error with source code context to stderr
func (c *Compiler) printErrorWithContext(lines []preproc.Line, lineIndex int, err error) {
if lineIndex < 0 || lineIndex >= len(lines) {
// Shouldn't happen, but be safe
_, _ = fmt.Fprintf(os.Stderr, "Error: %v\n", err)
return
}
line := lines[lineIndex]
const contextLines = 3
// Print error header
_, _ = fmt.Fprintf(os.Stderr, "\nError: %v\n", err)
_, _ = fmt.Fprintf(os.Stderr, " --> %s:%d\n\n", line.Filename, line.LineNo)
// Find all lines from the same file for context
// Group consecutive lines from the same file
fileLines := make(map[string][]int)
for i, l := range lines {
if l.Filename == line.Filename {
fileLines[l.Filename] = append(fileLines[l.Filename], i)
}
}
// Get context range (lineIndex within our lines array)
startIdx := lineIndex - contextLines
if startIdx < 0 {
startIdx = 0
}
endIdx := lineIndex + contextLines
if endIdx >= len(lines) {
endIdx = len(lines) - 1
}
// Calculate width for line numbers based on actual source line numbers
maxLineNo := 0
for i := startIdx; i <= endIdx; i++ {
if lines[i].LineNo > maxLineNo {
maxLineNo = lines[i].LineNo
}
}
maxLineNumWidth := len(fmt.Sprintf("%d", maxLineNo))
// Print context
for i := startIdx; i <= endIdx; i++ {
l := lines[i]
// Skip lines from different files
if l.Filename != line.Filename {
continue
}
if i == lineIndex {
// Error line - highlight it
_, _ = fmt.Fprintf(os.Stderr, ">> %*d | %s\n", maxLineNumWidth, l.LineNo, l.Text)
} else {
// Context line
_, _ = fmt.Fprintf(os.Stderr, " %*d | %s\n", maxLineNumWidth, l.LineNo, l.Text)
}
}
_, _ = fmt.Fprintf(os.Stderr, "\n")
}
// checkAbsoluteOverlaps analyzes and warns about overlapping absolute addresses
func (c *Compiler) checkAbsoluteOverlaps() {
c.ctx.FunctionHandler.ReportAbsoluteOverlaps()