about summary refs log tree commit diff
path: root/pkg
diff options
context:
space:
mode:
authorMel <einebeere@gmail.com>2022-08-08 23:46:09 +0000
committerMel <einebeere@gmail.com>2022-08-08 23:46:09 +0000
commit7717384414926eaa5821f04a08ee0d198f7b786f (patch)
tree0b667518eba4c8d023d39a70fecf153a795a7f9c /pkg
parente0161c493867e788ad9db208247f3275e2d057dc (diff)
downloadjinx-7717384414926eaa5821f04a08ee0d198f7b786f.tar.zst
jinx-7717384414926eaa5821f04a08ee0d198f7b786f.zip
Produce (slightly) better parser errors
Diffstat (limited to 'pkg')
-rw-r--r--pkg/lang/parser/errors.go60
-rw-r--r--pkg/lang/parser/exprs.go8
-rw-r--r--pkg/lang/parser/parser.go7
-rw-r--r--pkg/lang/parser/stmts.go8
-rw-r--r--pkg/lang/scanner/token/token.go72
-rw-r--r--pkg/libs/source/loc.go6
6 files changed, 151 insertions, 10 deletions
diff --git a/pkg/lang/parser/errors.go b/pkg/lang/parser/errors.go
new file mode 100644
index 0000000..a020e01
--- /dev/null
+++ b/pkg/lang/parser/errors.go
@@ -0,0 +1,60 @@
+package parser
+
+import (
+	"fmt"
+	"jinx/pkg/lang/scanner/token"
+	"jinx/pkg/libs/source"
+)
+
+type ErrFunctionNoThisAllowed struct {
+	At source.Loc
+}
+
+func (e ErrFunctionNoThisAllowed) Error() string {
+	return fmt.Sprintf("non-method function cannot have 'this' as parameter at %v", e.At)
+}
+
+type ErrFunctionLiteralNoThisAllowed struct {
+	At source.Loc
+}
+
+func (e ErrFunctionLiteralNoThisAllowed) Error() string {
+	return fmt.Sprintf("function literal cannot have 'this' as parameter at %v", e.At)
+}
+
+type ErrExpectedToken struct {
+	At     source.Loc
+	Wanted token.TokenKind
+	Got    token.Token
+}
+
+func (e ErrExpectedToken) Error() string {
+	return fmt.Sprintf("unexpected token %v, wanted %v at %v", e.Got, e.Wanted, e.At)
+}
+
+type ErrExpectedStatementEnd struct {
+	At  source.Loc
+	Got token.Token
+}
+
+func (e ErrExpectedStatementEnd) Error() string {
+	return fmt.Sprintf("expected statement end, got %v at %v", e.Got, e.At)
+}
+
+type ErrExpectedUnitExpressionStart struct {
+	At  source.Loc
+	Got token.Token
+}
+
+func (e ErrExpectedUnitExpressionStart) Error() string {
+	return fmt.Sprintf("unexpected token %v, wanted unit expression start at %v", e.Got, e.At)
+}
+
+type ErrExpectedValueLiteral struct {
+	At  source.Loc
+	Got token.Token
+}
+
+func (e ErrExpectedValueLiteral) Error() string {
+	return fmt.Sprintf("unexpected token %v, wanted value literal %v", e.Got, e.At)
+}
diff --git a/pkg/lang/parser/exprs.go b/pkg/lang/parser/exprs.go
index 48d1a8c..1603838 100644
--- a/pkg/lang/parser/exprs.go
+++ b/pkg/lang/parser/exprs.go
@@ -1,7 +1,6 @@
 package parser
 
 import (
-	"fmt"
 	"jinx/pkg/lang/ast"
 	"jinx/pkg/lang/scanner/token"
 )
@@ -207,7 +206,8 @@ func (p *Parser) parseUnitExpr() (ast.Expr, error) {
 	case token.KwTrue, token.KwFalse, token.KwNull, token.KwThis:
 		return p.parseValueLitExpr()
 	default:
-		panic(fmt.Errorf("unexpected token '%v', wanted unit expression start", p.peek().Kind))
+		tok := p.peek()
+		return ast.Expr{}, ErrExpectedUnitExpressionStart{tok.At, tok}
 	}
 }
 
@@ -250,7 +250,7 @@ func (p *Parser) parseFnLitExpr() (ast.Expr, error) {
 	}
 
 	if hasThis {
-		return ast.Expr{}, fmt.Errorf("function literal cannot have 'this' parameter")
+		return ast.Expr{}, ErrFunctionLiteralNoThisAllowed{fnTok.At}
 	}
 
 	// TODO: Also parse just an expression
@@ -406,6 +406,6 @@ func (p *Parser) parseValueLitExpr() (ast.Expr, error) {
 			Value: ast.ExprThis{},
 		}, nil
 	default:
-		panic(fmt.Errorf("unexpected token '%v', wanted value literal", tok))
+		return ast.Expr{}, ErrExpectedValueLiteral{At: tok.At, Got: tok}
 	}
 }
diff --git a/pkg/lang/parser/parser.go b/pkg/lang/parser/parser.go
index 0ff4b09..d939e4b 100644
--- a/pkg/lang/parser/parser.go
+++ b/pkg/lang/parser/parser.go
@@ -1,7 +1,6 @@
 package parser
 
 import (
-	"fmt"
 	"jinx/pkg/lang/ast"
 	"jinx/pkg/lang/scanner/token"
 )
@@ -88,7 +87,11 @@ func (p *Parser) next() token.Token {
 func (p *Parser) expect(kind token.TokenKind) (token.Token, error) {
 	tok := p.next()
 	if tok.Kind != kind {
-		return token.Token{}, fmt.Errorf("expected '%v', got '%v'", kind, tok.Kind)
+		return token.Token{}, ErrExpectedToken{
+			At:     tok.At,
+			Wanted: kind,
+			Got:    tok,
+		}
 	}
 
 	return tok, nil
diff --git a/pkg/lang/parser/stmts.go b/pkg/lang/parser/stmts.go
index c20f7a0..fe5c65b 100644
--- a/pkg/lang/parser/stmts.go
+++ b/pkg/lang/parser/stmts.go
@@ -1,7 +1,6 @@
 package parser
 
 import (
-	"fmt"
 	"jinx/pkg/lang/ast"
 	"jinx/pkg/lang/scanner/token"
 )
@@ -154,7 +153,7 @@ func (p *Parser) parseFnDeclStmt() (ast.Stmt, error) {
 	}
 
 	if hasThis {
-		return ast.Stmt{}, fmt.Errorf("function cannot have 'this' as a parameter")
+		return ast.Stmt{}, ErrFunctionNoThisAllowed{At: fnTok.At}
 	}
 
 	block, err := p.parseBlock()
@@ -640,7 +639,10 @@ func (p *Parser) parseEmptyStmt() (ast.Stmt, error) {
 func (p *Parser) parseStmtEnd() error {
 	tok := p.peek()
 	if !tok.CanEndStmt() {
-		return fmt.Errorf("wanted statement end, received: '%v'", tok.Kind)
+		return ErrExpectedStatementEnd{
+			At:  tok.At,
+			Got: tok,
+		}
 	}
 	p.next()
 	return nil
diff --git a/pkg/lang/scanner/token/token.go b/pkg/lang/scanner/token/token.go
index a39df8a..9fee730 100644
--- a/pkg/lang/scanner/token/token.go
+++ b/pkg/lang/scanner/token/token.go
@@ -1,6 +1,9 @@
 package token
 
-import "jinx/pkg/libs/source"
+import (
+	"fmt"
+	"jinx/pkg/libs/source"
+)
 
 type Token struct {
 	Kind TokenKind
@@ -31,3 +34,70 @@ func (t Token) CanEndStmt() bool {
 		return false
 	}
 }
+
+func (t Token) String() string {
+	switch t.Kind {
+	case EOF, EOL:
+		return t.Kind.String()
+
+	case KwVar,
+		KwFn,
+		KwType,
+		KwIf,
+		KwElif,
+		KwElse,
+		KwFor,
+		KwTry,
+		KwCatch,
+		KwFinally,
+		KwReturn,
+		KwContinue,
+		KwBreak,
+		KwThrow,
+		KwIn,
+		KwNull,
+		KwTrue,
+		KwFalse,
+		KwThis,
+		KwUse,
+		KwFrom,
+		KwBy,
+		KwGlobal:
+		return fmt.Sprintf("keyword '%v'", t.Kind)
+
+	case Ident:
+		return fmt.Sprintf("identifier '%v'", t.Data)
+	case Int:
+		return fmt.Sprintf("integer '%v'", t.Data)
+	case Float:
+		return fmt.Sprintf("float '%v'", t.Data)
+	case String:
+		return fmt.Sprintf("string '%v'", t.Data)
+
+	case Assign,
+		Plus,
+		Minus,
+		Star,
+		Slash,
+		Percent,
+		Bang,
+		Eq,
+		Neq,
+		Lt,
+		Gt,
+		Lte,
+		Gte,
+		LParen,
+		RParen,
+		LBrace,
+		RBrace,
+		LBracket,
+		RBracket,
+		Comma,
+		Dot,
+		SemiColon:
+		return fmt.Sprintf("'%v'", t.Kind)
+	default:
+		return "unknown token"
+	}
+}
diff --git a/pkg/libs/source/loc.go b/pkg/libs/source/loc.go
index 3089d3b..f43b5c0 100644
--- a/pkg/libs/source/loc.go
+++ b/pkg/libs/source/loc.go
@@ -1,5 +1,7 @@
 package source
 
+import "fmt"
+
 type Loc struct {
 	Row int
 	Col int
@@ -8,3 +10,7 @@ type Loc struct {
 func NewLoc(row, col int) Loc {
 	return Loc{row, col}
 }
+
+func (l Loc) String() string {
+	return fmt.Sprintf("%d:%d", l.Row+1, l.Col)
+}