better error messages
This commit is contained in:
parent
f2986c7a79
commit
ec817b58cd
84
expr.go
84
expr.go
@ -9,7 +9,9 @@ import (
|
|||||||
"github.com/mediocregopher/ginger/lexer"
|
"github.com/mediocregopher/ginger/lexer"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TODO error type which incorporates token
|
// TODO doc strings
|
||||||
|
// TODO empty blocks
|
||||||
|
// TODO empty parenthesis
|
||||||
|
|
||||||
type tok lexer.Token
|
type tok lexer.Token
|
||||||
|
|
||||||
@ -217,13 +219,39 @@ func (b Block) Equal(e Expr) bool {
|
|||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
type exprErr struct {
|
||||||
|
reason string
|
||||||
|
tok lexer.Token
|
||||||
|
tokCtx string // e.g. "block starting at" or "open paren at"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e exprErr) Error() string {
|
||||||
|
msg := e.reason
|
||||||
|
if err := e.tok.Err(); err != nil {
|
||||||
|
msg += " - token error: " + err.Error()
|
||||||
|
} else if (e.tok != lexer.Token{}) {
|
||||||
|
msg += " - "
|
||||||
|
if e.tokCtx != "" {
|
||||||
|
msg += e.tokCtx + ": "
|
||||||
|
}
|
||||||
|
msg = fmt.Sprintf("%s [line:%d col:%d]", msg, e.tok.Row, e.tok.Col)
|
||||||
|
}
|
||||||
|
return msg
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
// toks[0] must be start
|
// toks[0] must be start
|
||||||
func sliceEnclosedToks(toks []lexer.Token, start, end lexer.Token) ([]lexer.Token, []lexer.Token, error) {
|
func sliceEnclosedToks(toks []lexer.Token, start, end lexer.Token) ([]lexer.Token, []lexer.Token, error) {
|
||||||
c := 1
|
c := 1
|
||||||
ret := []lexer.Token{}
|
ret := []lexer.Token{}
|
||||||
|
first := toks[0]
|
||||||
for i, tok := range toks[1:] {
|
for i, tok := range toks[1:] {
|
||||||
if err := tok.Err(); err != nil {
|
if tok.Err() != nil {
|
||||||
return nil, nil, fmt.Errorf("missing closing %v, hit error:% s", end, err)
|
return nil, nil, exprErr{
|
||||||
|
reason: fmt.Sprintf("missing closing %v", end),
|
||||||
|
tok: tok,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if tok.Equal(start) {
|
if tok.Equal(start) {
|
||||||
@ -237,7 +265,11 @@ func sliceEnclosedToks(toks []lexer.Token, start, end lexer.Token) ([]lexer.Toke
|
|||||||
ret = append(ret, tok)
|
ret = append(ret, tok)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil, nil, fmt.Errorf("missing closing %v", end)
|
return nil, nil, exprErr{
|
||||||
|
reason: fmt.Sprintf("missing closing %v", end),
|
||||||
|
tok: first,
|
||||||
|
tokCtx: "starting at",
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func readAllToks(r io.Reader) []lexer.Token {
|
func readAllToks(r io.Reader) []lexer.Token {
|
||||||
@ -278,8 +310,11 @@ func parseSingle(toks []lexer.Token) (Expr, []lexer.Token, error) {
|
|||||||
var expr Expr
|
var expr Expr
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
if err := toks[0].Err(); err != nil {
|
if toks[0].Err() != nil {
|
||||||
return nil, nil, err
|
return nil, nil, exprErr{
|
||||||
|
reason: "could not parse token",
|
||||||
|
tok: toks[0],
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if toks[0].Equal(openParen) {
|
if toks[0].Equal(openParen) {
|
||||||
@ -293,7 +328,11 @@ func parseSingle(toks []lexer.Token) (Expr, []lexer.Token, error) {
|
|||||||
if expr, ptoks, err = parse(ptoks); err != nil {
|
if expr, ptoks, err = parse(ptoks); err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
} else if len(ptoks) > 0 {
|
} else if len(ptoks) > 0 {
|
||||||
return nil, nil, fmt.Errorf("multiple expressions inside parenthesis; %v", starter)
|
return nil, nil, exprErr{
|
||||||
|
reason: "multiple expressions inside parenthesis",
|
||||||
|
tok: starter,
|
||||||
|
tokCtx: "starting at",
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return expr, toks, nil
|
return expr, toks, nil
|
||||||
|
|
||||||
@ -323,13 +362,23 @@ func parseNonPunct(tok lexer.Token) (Expr, error) {
|
|||||||
return parseString(tok)
|
return parseString(tok)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil, fmt.Errorf("unexpected non-punctuation token: %v", tok)
|
return nil, exprErr{
|
||||||
|
reason: "unexpected non-punctuation token",
|
||||||
|
tok: tok,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseIdentifier(t lexer.Token) (Expr, error) {
|
func parseIdentifier(t lexer.Token) (Expr, error) {
|
||||||
if t.Val[0] == '-' || (t.Val[0] >= '0' && t.Val[0] <= '9') {
|
if t.Val[0] == '-' || (t.Val[0] >= '0' && t.Val[0] <= '9') {
|
||||||
n, err := strconv.ParseInt(t.Val, 10, 64)
|
n, err := strconv.ParseInt(t.Val, 10, 64)
|
||||||
return Int{tok: tok(t), val: n}, err
|
if err != nil {
|
||||||
|
return nil, exprErr{
|
||||||
|
reason: "error parsing number",
|
||||||
|
// TODO err: err,
|
||||||
|
tok: t,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Int{tok: tok(t), val: n}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if t.Val == "true" {
|
if t.Val == "true" {
|
||||||
@ -343,7 +392,14 @@ func parseIdentifier(t lexer.Token) (Expr, error) {
|
|||||||
|
|
||||||
func parseString(t lexer.Token) (Expr, error) {
|
func parseString(t lexer.Token) (Expr, error) {
|
||||||
str, err := strconv.Unquote(t.Val)
|
str, err := strconv.Unquote(t.Val)
|
||||||
return String{tok: tok(t), str: str}, err
|
if err != nil {
|
||||||
|
return nil, exprErr{
|
||||||
|
reason: "error parsing string",
|
||||||
|
// TODO err: err,
|
||||||
|
tok: t,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return String{tok: tok(t), str: str}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseConnectingPunct(toks []lexer.Token, root Expr) (Expr, []lexer.Token, error) {
|
func parseConnectingPunct(toks []lexer.Token, root Expr) (Expr, []lexer.Token, error) {
|
||||||
@ -418,8 +474,6 @@ func parsePipe(toks []lexer.Token, root Expr) (Expr, []lexer.Token, error) {
|
|||||||
func parseBlock(toks []lexer.Token) (Expr, error) {
|
func parseBlock(toks []lexer.Token) (Expr, error) {
|
||||||
b := Block{}
|
b := Block{}
|
||||||
|
|
||||||
// TODO figure out what we want to do about empty blocks
|
|
||||||
|
|
||||||
var expr Expr
|
var expr Expr
|
||||||
var err error
|
var err error
|
||||||
for {
|
for {
|
||||||
@ -432,7 +486,11 @@ func parseBlock(toks []lexer.Token) (Expr, error) {
|
|||||||
}
|
}
|
||||||
stmt, ok := expr.(Statement)
|
stmt, ok := expr.(Statement)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, fmt.Errorf("blocks may only contain full statements: %v", expr)
|
return nil, exprErr{
|
||||||
|
reason: "blocks may only contain full statements",
|
||||||
|
tok: expr.Token(),
|
||||||
|
tokCtx: "non-statement here",
|
||||||
|
}
|
||||||
}
|
}
|
||||||
b.stmts = append(b.stmts, stmt)
|
b.stmts = append(b.stmts, stmt)
|
||||||
}
|
}
|
||||||
|
@ -36,10 +36,10 @@ func (tok Token) Equal(tok2 Token) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Err returns the error contained by the token, if any. Only returns non-nil if
|
// Err returns the error contained by the token, if any. Only returns non-nil if
|
||||||
// TokenType is Err or EOR
|
// TokenType is Err or EOF
|
||||||
func (tok Token) Err() error {
|
func (tok Token) Err() error {
|
||||||
if tok.TokenType == Err || tok.TokenType == EOF {
|
if tok.TokenType == Err || tok.TokenType == EOF {
|
||||||
return errors.New(tok.Val)
|
return fmt.Errorf("[line:%d col:%d] %s", tok.Row, tok.Col, tok.Val)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user