improve compile error feedback

This commit is contained in:
palanqu 2025-03-30 21:37:17 +08:00
parent 3452677fbd
commit fb83b08269
4 changed files with 174 additions and 2 deletions

View File

@ -7,6 +7,7 @@
package compiler
import (
"fmt"
"math"
"math/big"
"math/rand"
@ -268,3 +269,82 @@ func main(a, b uint64) uint64 {
}
}
}
type parseErrorTestData struct {
code string
errorMessage string
}
var compileErrorTests = []parseErrorTestData{
{
code: `packag main`,
errorMessage: `Compile error [parse]: unexpected token 'packag': expected 'package', {data}:1:0
packag main
^`,
},
{
code: `package mai`,
errorMessage: `found packages mai and main`,
},
{
code: `
package main
func main1(a, b int4) int4 {
return Sum2(MinMax(a, b))
}
`,
errorMessage: `no main function defined`,
},
{
code: `
package main
fun main(a, b int4) int4 {
return Sum2(MinMax(a, b))
}`,
errorMessage: `Compile error [parse]: unexpected token 'identifier', {data}:3:1
fun main(a, b int4) int4 {
^`,
},
{
code: `package main
func main(a, b) int4 {
return Sum2(MinMax(a, b))
}`,
errorMessage: `Compile error [parse]: unexpected token ')' while parsing type, {data}:2:16
func main(a, b) int4 {
^`,
},
{
code: `package main
func main(a, b int4 int4 {
return Sum2(MinMax(a, b))
}`,
errorMessage: `Compile error [parse]: unexpected token 'int4': expected ',', {data}:2:22
func main(a, b int4 int4 {
^`,
},
{
code: `package main
func main(a, b int4) int4 {
a+b
}`,
errorMessage: `missing return at the end of function`,
},
}
func TestCompileErrorMessage(t *testing.T) {
for idx, testData := range compileErrorTests {
t.Run(fmt.Sprintf("test parse %d", idx), func(t *testing.T) {
_, _, err := New(utils.NewParams()).Compile(testData.code, nil)
if err == nil {
t.Fatalf("Parse test %d failed: expected error", idx)
}
if err.Error() != testData.errorMessage {
t.Fatalf("Parse test %d failed: expected error message %s, got %s", idx, testData.errorMessage, err.Error())
}
})
}
}

43
bedlam/compiler/errors.go Normal file
View File

@ -0,0 +1,43 @@
package compiler
import (
"fmt"
"source.quilibrium.com/quilibrium/monorepo/bedlam/compiler/utils"
)
type CompileError struct {
Stage CompileStage // "parse", "compile", "circuit"
SourcePos string
Location *utils.Point
Err error // origin error
}
type CompileStage int
const (
CompileStageParse CompileStage = iota
CompileStageCompile
CompileStageCircuit
)
var stateName = map[CompileStage]string{
CompileStageParse: "parse",
CompileStageCompile: "compile",
CompileStageCircuit: "circuit",
}
func (cs CompileStage) String() string {
return stateName[cs]
}
func (e *CompileError) Error() string {
msg := fmt.Sprintf("Compile error [%s]: %v", e.Stage, e.Err)
if e.Location != nil {
msg += fmt.Sprintf(", %s", e.Location)
}
if e.SourcePos != "" {
msg += fmt.Sprintf("\n%s", e.SourcePos)
}
return msg
}

View File

@ -0,0 +1,36 @@
package compiler
import (
"fmt"
"testing"
"source.quilibrium.com/quilibrium/monorepo/bedlam/compiler/utils"
)
func TestCompileError(t *testing.T) {
t.Run("test compile error message without location", func(t *testing.T) {
err := CompileError{
Stage: CompileStageParse,
SourcePos: "file:1:1",
Err: fmt.Errorf("error message"),
}
want := "Compile error [parse]: error message\nfile:1:1"
got := err.Error()
if got != want {
t.Errorf("got %q, want %q", got, want)
}
})
t.Run("test compile error message with location", func(t *testing.T) {
err := CompileError{
Stage: CompileStageParse,
SourcePos: "func main(a, b int4 int4",
Location: &utils.Point{Source: "file", Line: 1, Col: 1},
Err: fmt.Errorf("error message"),
}
want := "Compile error [parse]: error message, file:1:1\nfunc main(a, b int4 int4"
got := err.Error()
if got != want {
t.Errorf("got %q, want %q", got, want)
}
})
}

View File

@ -168,13 +168,26 @@ func (p *Parser) errf(loc utils.Point, format string, a ...interface{}) error {
indicator = append(indicator, r)
}
indicator = append(indicator, '^')
pos := fmt.Sprintf("%s\n%s", string(line), string(indicator))
compilerError := &CompileError{
Stage: CompileStageParse,
SourcePos: pos,
Location: &loc,
Err: errors.New(msg),
}
p.logger.Errorf(loc, "%s\n%s\n%s\n",
msg, string(line), string(indicator))
return errors.New(msg)
return compilerError
}
p.logger.Errorf(loc, "%s", msg)
return errors.New(msg)
compilerError := &CompileError{
Stage: CompileStageParse,
SourcePos: fmt.Sprintf("%s:%d:%d", p.lexer.Source(), loc.Line, loc.Col),
Location: &loc,
Err: errors.New(msg),
}
return compilerError
}
func (p *Parser) errUnexpected(offending *Token, expected TokenType) error {