diff --git a/bedlam/compiler/compiler_test.go b/bedlam/compiler/compiler_test.go index 0c73a79..40338fd 100644 --- a/bedlam/compiler/compiler_test.go +++ b/bedlam/compiler/compiler_test.go @@ -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()) + } + }) + } +} diff --git a/bedlam/compiler/errors.go b/bedlam/compiler/errors.go new file mode 100644 index 0000000..ff2c555 --- /dev/null +++ b/bedlam/compiler/errors.go @@ -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 +} diff --git a/bedlam/compiler/errors_test.go b/bedlam/compiler/errors_test.go new file mode 100644 index 0000000..d82cae7 --- /dev/null +++ b/bedlam/compiler/errors_test.go @@ -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) + } + }) +} diff --git a/bedlam/compiler/parser.go b/bedlam/compiler/parser.go index 272b309..9acf919 100644 --- a/bedlam/compiler/parser.go +++ b/bedlam/compiler/parser.go @@ -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 {