Made error messages display the source and some context.
This commit is contained in:
parent
27a5b51a02
commit
275f4782c8
1 changed files with 74 additions and 5 deletions
|
|
@ -2,6 +2,7 @@ package compiler
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"c65gm/internal/preproc"
|
"c65gm/internal/preproc"
|
||||||
|
|
@ -37,7 +38,7 @@ func (c *Compiler) Compile(lines []preproc.Line) ([]string, error) {
|
||||||
var lastKind = preproc.Source
|
var lastKind = preproc.Source
|
||||||
var scriptBuffer []string
|
var scriptBuffer []string
|
||||||
|
|
||||||
for _, line := range lines {
|
for i, line := range lines {
|
||||||
// Detect kind transitions and emit markers
|
// Detect kind transitions and emit markers
|
||||||
if line.Kind != lastKind {
|
if line.Kind != lastKind {
|
||||||
// Execute and close previous Script block
|
// 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:], '|')
|
end := strings.IndexByte(text[start+1:], '|')
|
||||||
if end == -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
|
end += start + 1
|
||||||
|
|
||||||
|
|
@ -102,18 +104,21 @@ func (c *Compiler) Compile(lines []preproc.Line) ([]string, error) {
|
||||||
// Find handler for this line
|
// Find handler for this line
|
||||||
cmd, found := c.registry.FindHandler(line)
|
cmd, found := c.registry.FindHandler(line)
|
||||||
if !found {
|
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
|
// Interpret the line
|
||||||
if err := cmd.Interpret(line, c.ctx); err != nil {
|
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
|
// Generate assembly
|
||||||
asmLines, err := cmd.Generate(c.ctx)
|
asmLines, err := cmd.Generate(c.ctx)
|
||||||
if err != nil {
|
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))
|
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
|
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
|
// checkAbsoluteOverlaps analyzes and warns about overlapping absolute addresses
|
||||||
func (c *Compiler) checkAbsoluteOverlaps() {
|
func (c *Compiler) checkAbsoluteOverlaps() {
|
||||||
c.ctx.FunctionHandler.ReportAbsoluteOverlaps()
|
c.ctx.FunctionHandler.ReportAbsoluteOverlaps()
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue