Improved error handling
This commit is contained in:
parent
57f1da10fe
commit
4776b4c14c
@ -7,14 +7,16 @@ import (
|
||||
"git.akyoto.dev/cli/q/src/build/arch/x64"
|
||||
"git.akyoto.dev/cli/q/src/build/asm"
|
||||
"git.akyoto.dev/cli/q/src/build/config"
|
||||
"git.akyoto.dev/cli/q/src/build/fs"
|
||||
"git.akyoto.dev/cli/q/src/build/token"
|
||||
"git.akyoto.dev/cli/q/src/errors"
|
||||
"git.akyoto.dev/go/color/ansi"
|
||||
)
|
||||
|
||||
// Function represents a function.
|
||||
type Function struct {
|
||||
Name string
|
||||
File *File
|
||||
File *fs.File
|
||||
Head token.List
|
||||
Body token.List
|
||||
Variables map[string]*Variable
|
||||
@ -74,6 +76,12 @@ func (f *Function) Compile() {
|
||||
f.Assembler.Return()
|
||||
|
||||
if config.Verbose {
|
||||
f.PrintAsm()
|
||||
}
|
||||
}
|
||||
|
||||
// PrintAsm shows the assembly instructions.
|
||||
func (f *Function) PrintAsm() {
|
||||
fmt.Println()
|
||||
ansi.Bold.Println(f.Name + ".asm")
|
||||
|
||||
@ -88,7 +96,6 @@ func (f *Function) Compile() {
|
||||
fmt.Print("\n")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// CompileInstruction compiles a single instruction.
|
||||
func (f *Function) CompileInstruction(line token.List) error {
|
||||
@ -116,8 +123,7 @@ func (f *Function) CompileInstruction(line token.List) error {
|
||||
value := line[2:]
|
||||
|
||||
if len(value) == 0 {
|
||||
return fmt.Errorf("error to be implemented")
|
||||
// return errors.New(errors.MissingAssignmentValue, f.File.Path, f.File.Tokens, f.Cursor)
|
||||
return errors.New(errors.MissingAssignmentValue, f.File, line[1].After())
|
||||
}
|
||||
|
||||
if config.Verbose {
|
||||
|
@ -6,7 +6,7 @@ import (
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"git.akyoto.dev/cli/q/src/build/directory"
|
||||
"git.akyoto.dev/cli/q/src/build/fs"
|
||||
"git.akyoto.dev/cli/q/src/build/token"
|
||||
"git.akyoto.dev/cli/q/src/errors"
|
||||
)
|
||||
@ -38,7 +38,7 @@ func scan(files []string, functions chan<- *Function, errors chan<- error) {
|
||||
}
|
||||
|
||||
if stat.IsDir() {
|
||||
err = directory.Walk(file, func(name string) {
|
||||
err = fs.Walk(file, func(name string) {
|
||||
if !strings.HasSuffix(name, ".q") {
|
||||
return
|
||||
}
|
||||
@ -86,7 +86,7 @@ func scanFile(path string, functions chan<- *Function) error {
|
||||
|
||||
tokens := token.Tokenize(contents)
|
||||
|
||||
file := &File{
|
||||
file := &fs.File{
|
||||
Tokens: tokens,
|
||||
Path: path,
|
||||
}
|
||||
@ -118,7 +118,7 @@ func scanFile(path string, functions chan<- *Function) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
return errors.New(errors.ExpectedFunctionName, path, tokens, i)
|
||||
return errors.New(errors.ExpectedFunctionName, file, tokens[i].Position)
|
||||
}
|
||||
|
||||
// Function parameters
|
||||
@ -138,7 +138,7 @@ func scanFile(path string, functions chan<- *Function) error {
|
||||
groupLevel--
|
||||
|
||||
if groupLevel < 0 {
|
||||
return errors.New(errors.MissingGroupStart, path, tokens, i)
|
||||
return errors.New(errors.MissingGroupStart, file, tokens[i].Position)
|
||||
}
|
||||
|
||||
i++
|
||||
@ -152,11 +152,11 @@ func scanFile(path string, functions chan<- *Function) error {
|
||||
|
||||
if tokens[i].Kind == token.EOF {
|
||||
if groupLevel > 0 {
|
||||
return errors.New(errors.MissingGroupEnd, path, tokens, i)
|
||||
return errors.New(errors.MissingGroupEnd, file, tokens[i].Position)
|
||||
}
|
||||
|
||||
if paramsStart == -1 {
|
||||
return errors.New(errors.ExpectedFunctionParameters, path, tokens, i)
|
||||
return errors.New(errors.ExpectedFunctionParameters, file, tokens[i].Position)
|
||||
}
|
||||
|
||||
return nil
|
||||
@ -167,7 +167,7 @@ func scanFile(path string, functions chan<- *Function) error {
|
||||
continue
|
||||
}
|
||||
|
||||
return errors.New(errors.ExpectedFunctionParameters, path, tokens, i)
|
||||
return errors.New(errors.ExpectedFunctionParameters, file, tokens[i].Position)
|
||||
}
|
||||
|
||||
// Function definition
|
||||
@ -187,7 +187,7 @@ func scanFile(path string, functions chan<- *Function) error {
|
||||
blockLevel--
|
||||
|
||||
if blockLevel < 0 {
|
||||
return errors.New(errors.MissingBlockStart, path, tokens, i)
|
||||
return errors.New(errors.MissingBlockStart, file, tokens[i].Position)
|
||||
}
|
||||
|
||||
i++
|
||||
@ -201,11 +201,11 @@ func scanFile(path string, functions chan<- *Function) error {
|
||||
|
||||
if tokens[i].Kind == token.EOF {
|
||||
if blockLevel > 0 {
|
||||
return errors.New(errors.MissingBlockEnd, path, tokens, i)
|
||||
return errors.New(errors.MissingBlockEnd, file, tokens[i].Position)
|
||||
}
|
||||
|
||||
if bodyStart == -1 {
|
||||
return errors.New(errors.ExpectedFunctionDefinition, path, tokens, i)
|
||||
return errors.New(errors.ExpectedFunctionDefinition, file, tokens[i].Position)
|
||||
}
|
||||
|
||||
return nil
|
||||
@ -216,7 +216,7 @@ func scanFile(path string, functions chan<- *Function) error {
|
||||
continue
|
||||
}
|
||||
|
||||
return errors.New(errors.ExpectedFunctionDefinition, path, tokens, i)
|
||||
return errors.New(errors.ExpectedFunctionDefinition, file, tokens[i].Position)
|
||||
}
|
||||
|
||||
functions <- &Function{
|
||||
|
@ -1,4 +1,4 @@
|
||||
package build
|
||||
package fs
|
||||
|
||||
import "git.akyoto.dev/cli/q/src/build/token"
|
||||
|
@ -1,4 +1,4 @@
|
||||
package directory
|
||||
package fs
|
||||
|
||||
import (
|
||||
"syscall"
|
@ -1,16 +1,16 @@
|
||||
package directory_test
|
||||
package fs_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"git.akyoto.dev/cli/q/src/build/directory"
|
||||
"git.akyoto.dev/cli/q/src/build/fs"
|
||||
"git.akyoto.dev/go/assert"
|
||||
)
|
||||
|
||||
func TestWalk(t *testing.T) {
|
||||
var files []string
|
||||
|
||||
directory.Walk(".", func(file string) {
|
||||
fs.Walk(".", func(file string) {
|
||||
files = append(files, file)
|
||||
})
|
||||
|
||||
@ -19,6 +19,6 @@ func TestWalk(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestNonExisting(t *testing.T) {
|
||||
err := directory.Walk("does-not-exist", func(file string) {})
|
||||
err := fs.Walk("does-not-exist", func(file string) {})
|
||||
assert.NotNil(t, err)
|
||||
}
|
@ -11,6 +11,11 @@ type Token struct {
|
||||
Bytes []byte
|
||||
}
|
||||
|
||||
// After returns the position after the token.
|
||||
func (t Token) After() int {
|
||||
return t.Position + len(t.Bytes)
|
||||
}
|
||||
|
||||
// String creates a human readable representation for debugging purposes.
|
||||
func (t Token) String() string {
|
||||
return fmt.Sprintf("%s %s", t.Kind, t.Text())
|
||||
|
@ -5,60 +5,60 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"git.akyoto.dev/cli/q/src/build/fs"
|
||||
"git.akyoto.dev/cli/q/src/build/token"
|
||||
)
|
||||
|
||||
// Error is a compiler error at a given line and column.
|
||||
type Error struct {
|
||||
Path string
|
||||
Line int
|
||||
Column int
|
||||
Err error
|
||||
File *fs.File
|
||||
Position int
|
||||
Stack string
|
||||
}
|
||||
|
||||
// New generates an error message at the current token position.
|
||||
// The error message is clickable in popular editors and leads you
|
||||
// directly to the faulty file at the given line and position.
|
||||
func New(err error, path string, tokens []token.Token, cursor int) *Error {
|
||||
var (
|
||||
lineCount = 1
|
||||
lineStart = -1
|
||||
)
|
||||
|
||||
for i := range cursor {
|
||||
if tokens[i].Kind == token.NewLine {
|
||||
lineCount++
|
||||
lineStart = int(tokens[i].Position)
|
||||
func New(err error, file *fs.File, position int) *Error {
|
||||
return &Error{
|
||||
Err: err,
|
||||
File: file,
|
||||
Position: position,
|
||||
Stack: Stack(),
|
||||
}
|
||||
}
|
||||
|
||||
var column int
|
||||
|
||||
if cursor < len(tokens) {
|
||||
column = tokens[cursor].Position - lineStart
|
||||
} else {
|
||||
lastToken := tokens[len(tokens)-1]
|
||||
column = lastToken.Position - lineStart + len(lastToken.Text())
|
||||
}
|
||||
|
||||
return &Error{path, lineCount, column, err, Stack()}
|
||||
}
|
||||
|
||||
// Error generates the string representation.
|
||||
func (e *Error) Error() string {
|
||||
path := e.Path
|
||||
path := e.File.Path
|
||||
cwd, err := os.Getwd()
|
||||
|
||||
if err == nil {
|
||||
relativePath, err := filepath.Rel(cwd, e.Path)
|
||||
relativePath, err := filepath.Rel(cwd, e.File.Path)
|
||||
|
||||
if err == nil {
|
||||
path = relativePath
|
||||
}
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%s:%d:%d: %s\n\n%s", path, e.Line, e.Column, e.Err, e.Stack)
|
||||
line := 1
|
||||
column := 1
|
||||
lineStart := -1
|
||||
|
||||
for _, t := range e.File.Tokens {
|
||||
if t.Position >= e.Position {
|
||||
column = e.Position - lineStart
|
||||
break
|
||||
}
|
||||
|
||||
if t.Kind == token.NewLine {
|
||||
lineStart = t.Position
|
||||
line++
|
||||
}
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%s:%d:%d: %s\n\n%s", path, line, column, e.Err, e.Stack)
|
||||
}
|
||||
|
||||
// Unwrap returns the wrapped error.
|
||||
|
Loading…
Reference in New Issue
Block a user