From 5476fefb25f4f0d2561aa929645a19ee62406548 Mon Sep 17 00:00:00 2001 From: Dave Cunningham Date: Thu, 5 Mar 2020 02:10:14 +0000 Subject: [PATCH] Add ParseParameter. Also fell down the rabbithole improving content and consistency of parse errors --- error_formatter.go | 6 +- internal/errors/static_error.go | 49 ++++++--- internal/parser/lexer.go | 21 +++- internal/parser/parser.go | 147 ++++++++++++++----------- internal/parser/parser_test.go | 54 ++++----- main_test.go | 6 +- testdata/extvar_static_error.golden | 2 +- testdata/object_comp_assert.golden | 2 +- testdata/object_comp_bad_field.golden | 2 +- testdata/object_comp_bad_field2.golden | 2 +- testdata/object_comp_illegal.golden | 2 +- testdata/unfinished_args.golden | 2 +- 12 files changed, 174 insertions(+), 121 deletions(-) diff --git a/error_formatter.go b/error_formatter.go index 89c9d67..042e2c0 100644 --- a/error_formatter.go +++ b/error_formatter.go @@ -69,7 +69,7 @@ func (ef *termErrorFormatter) Format(err error) string { case RuntimeError: return ef.formatRuntime(&err) case errors.StaticError: - return ef.formatStatic(&err) + return ef.formatStatic(err) default: return ef.formatInternal(err) } @@ -79,10 +79,10 @@ func (ef *termErrorFormatter) formatRuntime(err *RuntimeError) string { return err.Error() + "\n" + ef.buildStackTrace(err.StackTrace) } -func (ef *termErrorFormatter) formatStatic(err *errors.StaticError) string { +func (ef *termErrorFormatter) formatStatic(err errors.StaticError) string { var buf bytes.Buffer buf.WriteString(err.Error() + "\n") - ef.showCode(&buf, err.Loc) + ef.showCode(&buf, err.Loc()) return buf.String() } diff --git a/internal/errors/static_error.go b/internal/errors/static_error.go index 62c526d..fdb8f0c 100644 --- a/internal/errors/static_error.go +++ b/internal/errors/static_error.go @@ -27,26 +27,45 @@ import ( // StaticError represents an error during parsing/lexing or static analysis. // TODO(sbarzowski) Make it possible to have multiple static errors and warnings -type StaticError struct { - Loc ast.LocationRange - Msg string +type StaticError interface { + // WithContext returns a new StaticError with additional context before the error message. + WithContext(string) StaticError + // Error returns the string representation of a StaticError. + Error() string + // Loc returns the place in the source code that triggerred the error. + Loc() ast.LocationRange } -// MakeStaticErrorMsg returns a StaticError with a message. +type staticError struct { + loc ast.LocationRange + msg string +} + +func (err staticError) WithContext(context string) StaticError { + return staticError{ + loc: err.loc, + msg: fmt.Sprintf("%v while %s", err.msg, context), + } +} + +func (err staticError) Error() string { + loc := "" + if err.loc.IsSet() { + loc = err.loc.String() + } + return fmt.Sprintf("%v %v", loc, err.msg) +} + +func (err staticError) Loc() ast.LocationRange { + return err.loc +} + +// MakeStaticErrorMsg returns a staticError with a message. func MakeStaticErrorMsg(msg string) StaticError { - return StaticError{Msg: msg} + return staticError{msg: msg} } // MakeStaticError returns a StaticError with a message and a LocationRange. func MakeStaticError(msg string, lr ast.LocationRange) StaticError { - return StaticError{Msg: msg, Loc: lr} -} - -// Error returns the string representation of a StaticError. -func (err StaticError) Error() string { - loc := "" - if err.Loc.IsSet() { - loc = err.Loc.String() - } - return fmt.Sprintf("%v %v", loc, err.Msg) + return staticError{msg: msg, loc: lr} } diff --git a/internal/parser/lexer.go b/internal/parser/lexer.go index 872d56e..f309d23 100644 --- a/internal/parser/lexer.go +++ b/internal/parser/lexer.go @@ -126,6 +126,17 @@ var tokenKindStrings = []string{ tokenEndOfFile: "end of file", } +var tokenHasContent = map[tokenKind]bool{ + tokenIdentifier: true, + tokenNumber: true, + tokenOperator: true, + tokenStringBlock: true, + tokenStringDouble: true, + tokenStringSingle: true, + tokenVerbatimStringDouble: true, + tokenVerbatimStringSingle: true, +} + func (tk tokenKind) String() string { if tk < 0 || int(tk) >= len(tokenKindStrings) { panic(fmt.Sprintf("INTERNAL ERROR: Unknown token kind:: %d", tk)) @@ -151,10 +162,10 @@ type Tokens []token func (t *token) String() string { if t.data == "" { return t.kind.String() - } else if t.kind == tokenOperator { - return fmt.Sprintf("\"%v\"", t.data) - } else { + } else if tokenHasContent[t.kind] { return fmt.Sprintf("(%v, \"%v\")", t.kind, t.data) + } else { + return fmt.Sprintf("\"%v\"", t.data) } } @@ -371,7 +382,7 @@ func (l *lexer) emitFullToken(kind tokenKind, data, stringBlockIndent, stringBlo data: data, stringBlockIndent: stringBlockIndent, stringBlockTermIndent: stringBlockTermIndent, - loc: ast.MakeLocationRange(l.fileName, l.source, l.tokenStartLoc, l.location()), + loc: ast.MakeLocationRange(l.fileName, l.source, l.tokenStartLoc, l.location()), }) l.fodder = ast.Fodder{} } @@ -387,7 +398,7 @@ func (l *lexer) addFodder(kind ast.FodderKind, blanks int, indent int, comment [ } func (l *lexer) makeStaticErrorPoint(msg string, loc ast.Location) errors.StaticError { - return errors.StaticError{Msg: msg, Loc: ast.MakeLocationRange(l.fileName, l.source, loc, loc)} + return errors.MakeStaticError(msg, ast.MakeLocationRange(l.fileName, l.source, loc, loc)) } // lexWhitespace consumes all whitespace and returns the number of \n and number of diff --git a/internal/parser/parser.go b/internal/parser/parser.go index 0d44a6b..2207f31 100644 --- a/internal/parser/parser.go +++ b/internal/parser/parser.go @@ -56,9 +56,9 @@ var bopPrecedence = map[ast.BinaryOp]precedence{ // --------------------------------------------------------------------------- -func makeUnexpectedError(t *token, while string) error { +func makeUnexpectedError(t *token, while string) errors.StaticError { return errors.MakeStaticError( - fmt.Sprintf("Unexpected: %v while %v", t, while), t.loc) + fmt.Sprintf("Unexpected: %v", t), t.loc).WithContext(while) } func locFromTokens(begin, end *token) ast.LocationRange { @@ -88,14 +88,14 @@ func (p *parser) pop() *token { return t } -func (p *parser) unexpectedTokenError(tk tokenKind, t *token) error { +func (p *parser) unexpectedTokenError(tk tokenKind, t *token) errors.StaticError { if tk == t.kind { - panic("Unexpectedly expected token kind.") + panic("Unexpectedly expected token kind") } return errors.MakeStaticError(fmt.Sprintf("Expected token %v but got %v", tk, t), t.loc) } -func (p *parser) popExpect(tk tokenKind) (*token, error) { +func (p *parser) popExpect(tk tokenKind) (*token, errors.StaticError) { t := p.pop() if t.kind != tk { return nil, p.unexpectedTokenError(tk, t) @@ -103,7 +103,7 @@ func (p *parser) popExpect(tk tokenKind) (*token, error) { return t, nil } -func (p *parser) popExpectOp(op string) (*token, error) { +func (p *parser) popExpectOp(op string) (*token, errors.StaticError) { t := p.pop() if t.kind != tokenOperator || t.data != op { return nil, errors.MakeStaticError( @@ -133,7 +133,7 @@ func astVarToIdentifier(node ast.Node) (ast.Fodder, *ast.Identifier, bool) { // parseArgument parses either id = expr or just expr. // It returns either (, id, , expr) or (nil, nil, nil, expr) // respectively. -func (p *parser) parseArgument() (ast.Fodder, *ast.Identifier, ast.Fodder, ast.Node, error) { +func (p *parser) parseArgument() (ast.Fodder, *ast.Identifier, ast.Fodder, ast.Node, errors.StaticError) { var idFodder ast.Fodder var id *ast.Identifier var eqFodder ast.Fodder @@ -153,7 +153,7 @@ func (p *parser) parseArgument() (ast.Fodder, *ast.Identifier, ast.Fodder, ast.N } // TODO(sbarzowski) - this returned bool is weird -func (p *parser) parseArguments(elementKind string) (*token, *ast.Arguments, bool, error) { +func (p *parser) parseArguments(elementKind string) (*token, *ast.Arguments, bool, errors.StaticError) { args := &ast.Arguments{} gotComma := false commaFodder := ast.Fodder{} @@ -168,7 +168,7 @@ func (p *parser) parseArguments(elementKind string) (*token, *ast.Arguments, boo } if !first && !gotComma { - return nil, nil, false, errors.MakeStaticError(fmt.Sprintf("Expected a comma before next %s, got %s.", elementKind, next), next.loc) + return nil, nil, false, errors.MakeStaticError(fmt.Sprintf("Expected a comma before next %s, got %s", elementKind, next), next.loc) } idFodder, id, eqFodder, expr, err := p.parseArgument() @@ -208,8 +208,34 @@ func (p *parser) parseArguments(elementKind string) (*token, *ast.Arguments, boo } } +// parseParameter parses either id = expr or just id. +// It returns either (, id, , expr) or (, id, nil, nil) +// respectively. +func (p *parser) parseParameter() (ast.Fodder, *ast.Identifier, ast.Fodder, ast.Node, errors.StaticError) { + var idFodder ast.Fodder + var id *ast.Identifier + var eqFodder ast.Fodder + ident, err := p.popExpect(tokenIdentifier) + if err != nil { + return nil, nil, nil, nil, err.WithContext("parsing parameter") + } + var tmpID = ast.Identifier(ident.data) + id = &tmpID + idFodder = ident.fodder + if p.peek().kind == tokenOperator && p.peek().data == "=" { + eq := p.pop() + eqFodder = eq.fodder + expr, err := p.parse(maxPrecedence) + if err != nil { + return nil, nil, nil, nil, err + } + return idFodder, id, eqFodder, expr, nil + } + return idFodder, id, nil, nil, nil +} + // TODO(sbarzowski) - this returned bool is weird -func (p *parser) parseParameters(elementKind string) (*token, []ast.Parameter, bool, error) { +func (p *parser) parseParameters(elementKind string) (*token, []ast.Parameter, bool, errors.StaticError) { var parenR *token var params []ast.Parameter @@ -226,10 +252,10 @@ func (p *parser) parseParameters(elementKind string) (*token, []ast.Parameter, b } if !first && !gotComma { - return nil, nil, false, errors.MakeStaticError(fmt.Sprintf("Expected a comma before next %s, got %s.", elementKind, next), next.loc) + return nil, nil, false, errors.MakeStaticError(fmt.Sprintf("Expected a comma before next %s, got %s", elementKind, next), next.loc) } - idFodder, id, eqFodder, expr, err := p.parseArgument() + idFodder, id, eqFodder, expr, err := p.parseParameter() if err != nil { return nil, nil, false, err } @@ -242,15 +268,11 @@ func (p *parser) parseParameters(elementKind string) (*token, []ast.Parameter, b gotComma = false } - if id == nil { - // Param has no default, is just 'id2' - idFodder2, id2, ok := astVarToIdentifier(expr) - if !ok { - return nil, nil, false, errors.MakeStaticError(fmt.Sprintf("Expected simple identifier but got a complex expression."), *expr.Loc()) - } + if expr == nil { + // Param has no default param := ast.Parameter{ - NameFodder: idFodder2, - Name: *id2, + NameFodder: idFodder, + Name: *id, } if gotComma { param.CommaFodder = commaFodder @@ -274,10 +296,10 @@ func (p *parser) parseParameters(elementKind string) (*token, []ast.Parameter, b } // TODO(sbarzowski) add location to all individual binds -func (p *parser) parseBind(binds *ast.LocalBinds) (*token, error) { - varID, err := p.popExpect(tokenIdentifier) - if err != nil { - return nil, err +func (p *parser) parseBind(binds *ast.LocalBinds) (*token, errors.StaticError) { + varID, popErr := p.popExpect(tokenIdentifier) + if popErr != nil { + return nil, popErr } for _, b := range *binds { if b.Variable == ast.Identifier(varID.data) { @@ -301,9 +323,9 @@ func (p *parser) parseBind(binds *ast.LocalBinds) (*token, error) { } } - eqToken, err := p.popExpectOp("=") - if err != nil { - return nil, err + eqToken, popErr := p.popExpectOp("=") + if popErr != nil { + return nil, popErr } body, err := p.parse(maxPrecedence) if err != nil { @@ -339,7 +361,7 @@ func (p *parser) parseBind(binds *ast.LocalBinds) (*token, error) { return delim, nil } -func (p *parser) parseObjectAssignmentOp() (opFodder ast.Fodder, plusSugar bool, hide ast.ObjectFieldHide, err error) { +func (p *parser) parseObjectAssignmentOp() (opFodder ast.Fodder, plusSugar bool, hide ast.ObjectFieldHide, err errors.StaticError) { op, err := p.popExpect(tokenOperator) if err != nil { return @@ -382,7 +404,7 @@ func (p *parser) parseObjectAssignmentOp() (opFodder ast.Fodder, plusSugar bool, // +gen set type LiteralField string -func (p *parser) parseObjectRemainderComp(fields ast.ObjectFields, gotComma bool, tok *token, next *token) (ast.Node, *token, error) { +func (p *parser) parseObjectRemainderComp(fields ast.ObjectFields, gotComma bool, tok *token, next *token) (ast.Node, *token, errors.StaticError) { numFields := 0 numAsserts := 0 var field ast.ObjectField @@ -399,16 +421,16 @@ func (p *parser) parseObjectRemainderComp(fields ast.ObjectFields, gotComma bool } if numAsserts > 0 { - return nil, nil, errors.MakeStaticError("Object comprehension cannot have asserts.", next.loc) + return nil, nil, errors.MakeStaticError("Object comprehension cannot have asserts", next.loc) } if numFields != 1 { - return nil, nil, errors.MakeStaticError("Object comprehension can only have one field.", next.loc) + return nil, nil, errors.MakeStaticError("Object comprehension can only have one field", next.loc) } if field.Hide != ast.ObjectFieldInherit { - return nil, nil, errors.MakeStaticError("Object comprehensions cannot have hidden fields.", next.loc) + return nil, nil, errors.MakeStaticError("Object comprehensions cannot have hidden fields", next.loc) } if field.Kind != ast.ObjectFieldExpr { - return nil, nil, errors.MakeStaticError("Object comprehensions can only have [e] fields.", next.loc) + return nil, nil, errors.MakeStaticError("Object comprehensions can only have [e] fields", next.loc) } spec, last, err := p.parseComprehensionSpecs(next, tokenBraceR) if err != nil { @@ -423,7 +445,7 @@ func (p *parser) parseObjectRemainderComp(fields ast.ObjectFields, gotComma bool }, last, nil } -func (p *parser) parseObjectRemainderField(literalFields *LiteralFieldSet, tok *token, next *token) (*ast.ObjectField, error) { +func (p *parser) parseObjectRemainderField(literalFields *LiteralFieldSet, tok *token, next *token) (*ast.ObjectField, errors.StaticError) { var kind ast.ObjectFieldKind var fodder1 ast.Fodder var expr1 ast.Node @@ -441,7 +463,7 @@ func (p *parser) parseObjectRemainderField(literalFields *LiteralFieldSet, tok * default: fodder1 = next.fodder kind = ast.ObjectFieldExpr - var err error + var err errors.StaticError expr1, err = p.parse(maxPrecedence) if err != nil { return nil, err @@ -460,7 +482,7 @@ func (p *parser) parseObjectRemainderField(literalFields *LiteralFieldSet, tok * var params []ast.Parameter if p.peek().kind == tokenParenL { parenL = p.pop() - var err error + var err errors.StaticError parenR, params, methComma, err = p.parseParameters("method parameter") if err != nil { return nil, err @@ -521,10 +543,10 @@ func (p *parser) parseObjectRemainderField(literalFields *LiteralFieldSet, tok * }, nil } -func (p *parser) parseObjectRemainderLocal(binds *ast.IdentifierSet, tok *token, next *token) (*ast.ObjectField, error) { - varID, err := p.popExpect(tokenIdentifier) - if err != nil { - return nil, err +func (p *parser) parseObjectRemainderLocal(binds *ast.IdentifierSet, tok *token, next *token) (*ast.ObjectField, errors.StaticError) { + varID, popErr := p.popExpect(tokenIdentifier) + if popErr != nil { + return nil, popErr } id := ast.Identifier(varID.data) @@ -543,14 +565,15 @@ func (p *parser) parseObjectRemainderLocal(binds *ast.IdentifierSet, tok *token, if p.peek().kind == tokenParenL { parenL = p.pop() isMethod = true + var err errors.StaticError parenR, params, funcComma, err = p.parseParameters("function parameter") if err != nil { return nil, err } } - opToken, err := p.popExpectOp("=") - if err != nil { - return nil, err + opToken, popErr := p.popExpectOp("=") + if popErr != nil { + return nil, popErr } body, err := p.parse(maxPrecedence) @@ -590,7 +613,7 @@ func (p *parser) parseObjectRemainderLocal(binds *ast.IdentifierSet, tok *token, }, nil } -func (p *parser) parseObjectRemainderAssert(tok *token, next *token) (*ast.ObjectField, error) { +func (p *parser) parseObjectRemainderAssert(tok *token, next *token) (*ast.ObjectField, errors.StaticError) { cond, err := p.parse(maxPrecedence) if err != nil { return nil, err @@ -623,7 +646,7 @@ func (p *parser) parseObjectRemainderAssert(tok *token, next *token) (*ast.Objec } // Parse object or object comprehension without leading brace -func (p *parser) parseObjectRemainder(tok *token) (ast.Node, *token, error) { +func (p *parser) parseObjectRemainder(tok *token) (ast.Node, *token, errors.StaticError) { var fields ast.ObjectFields literalFields := make(LiteralFieldSet) binds := make(ast.IdentifierSet) @@ -650,11 +673,11 @@ func (p *parser) parseObjectRemainder(tok *token) (ast.Node, *token, error) { } if !gotComma && !first { - return nil, nil, errors.MakeStaticError("Expected a comma before next field.", next.loc) + return nil, nil, errors.MakeStaticError("Expected a comma before next field", next.loc) } var field *ast.ObjectField - var err error + var err errors.StaticError switch next.kind { case tokenBracketL, tokenIdentifier, tokenStringDouble, tokenStringSingle, tokenStringBlock, tokenVerbatimStringDouble, tokenVerbatimStringSingle: @@ -693,14 +716,14 @@ func (p *parser) parseObjectRemainder(tok *token) (ast.Node, *token, error) { } /* parses for x in expr for y in expr if expr for z in expr ... */ -func (p *parser) parseComprehensionSpecs(forToken *token, end tokenKind) (*ast.ForSpec, *token, error) { - var parseComprehensionSpecsHelper func(forToken *token, outer *ast.ForSpec) (*ast.ForSpec, *token, error) - parseComprehensionSpecsHelper = func(forToken *token, outer *ast.ForSpec) (*ast.ForSpec, *token, error) { +func (p *parser) parseComprehensionSpecs(forToken *token, end tokenKind) (*ast.ForSpec, *token, errors.StaticError) { + var parseComprehensionSpecsHelper func(forToken *token, outer *ast.ForSpec) (*ast.ForSpec, *token, errors.StaticError) + parseComprehensionSpecsHelper = func(forToken *token, outer *ast.ForSpec) (*ast.ForSpec, *token, errors.StaticError) { var ifSpecs []ast.IfSpec - varID, err := p.popExpect(tokenIdentifier) - if err != nil { - return nil, nil, err + varID, popErr := p.popExpect(tokenIdentifier) + if popErr != nil { + return nil, nil, popErr } id := ast.Identifier(varID.data) inToken, err := p.popExpect(tokenIn) @@ -748,7 +771,7 @@ func (p *parser) parseComprehensionSpecs(forToken *token, end tokenKind) (*ast.F // Assumes that the leading '[' has already been consumed and passed as tok. // Should read up to and consume the trailing ']' -func (p *parser) parseArray(tok *token) (ast.Node, error) { +func (p *parser) parseArray(tok *token) (ast.Node, errors.StaticError) { if p.peek().kind == tokenBracketR { bracketR := p.pop() return &ast.Array{ @@ -801,7 +824,7 @@ func (p *parser) parseArray(tok *token) (ast.Node, error) { break } if !gotComma { - return nil, errors.MakeStaticError("Expected a comma before next array element.", next.loc) + return nil, errors.MakeStaticError("Expected a comma before next array element", next.loc) } nextElem, err := p.parse(maxPrecedence) if err != nil { @@ -868,7 +891,7 @@ func tokenStringToAst(tok *token) *ast.LiteralString { } } -func (p *parser) parseTerminal() (ast.Node, error) { +func (p *parser) parseTerminal() (ast.Node, errors.StaticError) { tok := p.pop() switch tok.kind { case tokenAssert, tokenBraceR, tokenBracketR, tokenComma, tokenDot, tokenElse, @@ -877,7 +900,7 @@ func (p *parser) parseTerminal() (ast.Node, error) { return nil, makeUnexpectedError(tok, "parsing terminal") case tokenEndOfFile: - return nil, errors.MakeStaticError("Unexpected end of file.", tok.loc) + return nil, errors.MakeStaticError("Unexpected end of file", tok.loc) case tokenBraceL: obj, _, err := p.parseObjectRemainder(tok) @@ -953,7 +976,7 @@ func (p *parser) parseTerminal() (ast.Node, error) { idFodder = fieldID.fodder id = (*ast.Identifier)(&fieldID.data) case tokenBracketL: - var err error + var err errors.StaticError index, err = p.parse(maxPrecedence) if err != nil { return nil, err @@ -964,7 +987,7 @@ func (p *parser) parseTerminal() (ast.Node, error) { } idFodder = bracketR.fodder default: - return nil, errors.MakeStaticError("Expected . or [ after super.", tok.loc) + return nil, errors.MakeStaticError("Expected . or [ after super", tok.loc) } return &ast.SuperIndex{ NodeBase: ast.NewNodeBaseLoc(tok.loc, tok.fodder), @@ -978,11 +1001,11 @@ func (p *parser) parseTerminal() (ast.Node, error) { return nil, errors.MakeStaticError(fmt.Sprintf("INTERNAL ERROR: Unknown tok kind: %v", tok.kind), tok.loc) } -func (p *parser) parsingFailure(msg string, tok *token) (ast.Node, error) { +func (p *parser) parsingFailure(msg string, tok *token) (ast.Node, errors.StaticError) { return nil, errors.MakeStaticError(msg, tok.loc) } -func (p *parser) parse(prec precedence) (ast.Node, error) { +func (p *parser) parse(prec precedence) (ast.Node, errors.StaticError) { begin := p.peek() switch begin.kind { @@ -1365,7 +1388,7 @@ func (p *parser) parse(prec precedence) (ast.Node, error) { // --------------------------------------------------------------------------- // Parse parses a slice of tokens into a parse tree. -func Parse(t Tokens) (ast.Node, error) { +func Parse(t Tokens) (ast.Node, errors.StaticError) { p := makeParser(t) expr, err := p.parse(maxPrecedence) if err != nil { diff --git a/internal/parser/parser_test.go b/internal/parser/parser_test.go index aa34d6e..a510880 100644 --- a/internal/parser/parser_test.go +++ b/internal/parser/parser_test.go @@ -146,74 +146,74 @@ type testError struct { } var errorTests = []testError{ - {`,`, `test:1:1-2 Unexpected: (",", ",") while parsing terminal`}, - {`function(a, b c)`, `test:1:15-16 Expected a comma before next function parameter, got (IDENTIFIER, "c").`}, - {`function(a, 1)`, `test:1:13-14 Expected simple identifier but got a complex expression.`}, - {`function(,)`, `test:1:10-11 Unexpected: (",", ",") while parsing terminal`}, - {`function(a=)`, `test:1:12-13 Unexpected: (")", ")") while parsing terminal`}, - {`function(a=,)`, `test:1:12-13 Unexpected: (",", ",") while parsing terminal`}, + {`,`, `test:1:1-2 Unexpected: "," while parsing terminal`}, + {`function(a, b c)`, `test:1:15-16 Expected a comma before next function parameter, got (IDENTIFIER, "c")`}, + {`function(a, 1)`, `test:1:13-14 Expected token IDENTIFIER but got (NUMBER, "1") while parsing parameter`}, + {`function(,)`, `test:1:10-11 Expected token IDENTIFIER but got "," while parsing parameter`}, + {`function(a=)`, `test:1:12-13 Unexpected: ")" while parsing terminal`}, + {`function(a=,)`, `test:1:12-13 Unexpected: "," while parsing terminal`}, {`a b`, `test:1:3-4 Did not expect: (IDENTIFIER, "b")`}, - {`foo(a, bar(a b))`, `test:1:14-15 Expected a comma before next function argument, got (IDENTIFIER, "b").`}, + {`foo(a, bar(a b))`, `test:1:14-15 Expected a comma before next function argument, got (IDENTIFIER, "b")`}, {`local`, `test:1:6 Expected token IDENTIFIER but got end of file`}, {`local foo = 1, foo = 2; true`, `test:1:16-19 Duplicate local var: foo`}, - {`local foo(a b) = a; true`, `test:1:13-14 Expected a comma before next function parameter, got (IDENTIFIER, "b").`}, - {`local foo(a): a; true`, `test:1:13-14 Expected operator = but got ":"`}, - {`local foo(a) = bar(a b); true`, `test:1:22-23 Expected a comma before next function argument, got (IDENTIFIER, "b").`}, - {`local foo: 1; true`, `test:1:10-11 Expected operator = but got ":"`}, - {`local foo = bar(a b); true`, `test:1:19-20 Expected a comma before next function argument, got (IDENTIFIER, "b").`}, + {`local foo(a b) = a; true`, `test:1:13-14 Expected a comma before next function parameter, got (IDENTIFIER, "b")`}, + {`local foo(a): a; true`, `test:1:13-14 Expected operator = but got (OPERATOR, ":")`}, + {`local foo(a) = bar(a b); true`, `test:1:22-23 Expected a comma before next function argument, got (IDENTIFIER, "b")`}, + {`local foo: 1; true`, `test:1:10-11 Expected operator = but got (OPERATOR, ":")`}, + {`local foo = bar(a b); true`, `test:1:19-20 Expected a comma before next function argument, got (IDENTIFIER, "b")`}, {`{a b}`, `test:1:4-5 Expected token OPERATOR but got (IDENTIFIER, "b")`}, {`{a = b}`, `test:1:4-5 Expected one of :, ::, :::, +:, +::, +:::, got: =`}, {`{a :::: b}`, `test:1:4-8 Expected one of :, ::, :::, +:, +::, +:::, got: ::::`}, - {`{assert x for x in [1, 2, 3]}`, `test:1:11-14 Object comprehension cannot have asserts.`}, - {`{['foo' + x]: true, [x]: x for x in [1, 2, 3]}`, `test:1:28-31 Object comprehension can only have one field.`}, - {`{foo: x for x in [1, 2, 3]}`, `test:1:9-12 Object comprehensions can only have [e] fields.`}, - {`{[x]:: true for x in [1, 2, 3]}`, `test:1:13-16 Object comprehensions cannot have hidden fields.`}, + {`{assert x for x in [1, 2, 3]}`, `test:1:11-14 Object comprehension cannot have asserts`}, + {`{['foo' + x]: true, [x]: x for x in [1, 2, 3]}`, `test:1:28-31 Object comprehension can only have one field`}, + {`{foo: x for x in [1, 2, 3]}`, `test:1:9-12 Object comprehensions can only have [e] fields`}, + {`{[x]:: true for x in [1, 2, 3]}`, `test:1:13-16 Object comprehensions cannot have hidden fields`}, {`{[x]: true for 1 in [1, 2, 3]}`, `test:1:16-17 Expected token IDENTIFIER but got (NUMBER, "1")`}, {`{[x]: true for x at [1, 2, 3]}`, `test:1:18-20 Expected token in but got (IDENTIFIER, "at")`}, - {`{[x]: true for x in [1, 2 3]}`, `test:1:27-28 Expected a comma before next array element.`}, + {`{[x]: true for x in [1, 2 3]}`, `test:1:27-28 Expected a comma before next array element`}, {`{[x]: true for x in [1, 2, 3] if (a b)}`, `test:1:37-38 Expected token ")" but got (IDENTIFIER, "b")`}, {`{[x]: true for x in [1, 2, 3] if a b}`, `test:1:36-37 Expected for, if or "}" after for clause, got: (IDENTIFIER, "b")`}, - {`{a: b c:d}`, `test:1:7-8 Expected a comma before next field.`}, + {`{a: b c:d}`, `test:1:7-8 Expected a comma before next field`}, {`{[(x y)]: z}`, `test:1:6-7 Expected token ")" but got (IDENTIFIER, "y")`}, {`{[x y]: z}`, `test:1:5-6 Expected token "]" but got (IDENTIFIER, "y")`}, - {`{foo(x y): z}`, `test:1:8-9 Expected a comma before next method parameter, got (IDENTIFIER, "y").`}, + {`{foo(x y): z}`, `test:1:8-9 Expected a comma before next method parameter, got (IDENTIFIER, "y")`}, {`{foo(x)+: z}`, `test:1:2-5 Cannot use +: syntax sugar in a method: foo`}, {`{foo: 1, foo: 2}`, `test:1:10-13 Duplicate field: foo`}, {`{foo: (1 2)}`, `test:1:10-11 Expected token ")" but got (NUMBER, "2")`}, {`{local 1 = 3, true}`, `test:1:8-9 Expected token IDENTIFIER but got (NUMBER, "1")`}, {`{local foo = 1, local foo = 2, true}`, `test:1:23-26 Duplicate local var: foo`}, - {`{local foo(a b) = 1, a: true}`, `test:1:14-15 Expected a comma before next function parameter, got (IDENTIFIER, "b").`}, - {`{local foo(a): 1, a: true}`, `test:1:14-15 Expected operator = but got ":"`}, + {`{local foo(a b) = 1, a: true}`, `test:1:14-15 Expected a comma before next function parameter, got (IDENTIFIER, "b")`}, + {`{local foo(a): 1, a: true}`, `test:1:14-15 Expected operator = but got (OPERATOR, ":")`}, {`{local foo(a) = (a b), a: true}`, `test:1:20-21 Expected token ")" but got (IDENTIFIER, "b")`}, {`{assert (a b), a: true}`, `test:1:12-13 Expected token ")" but got (IDENTIFIER, "b")`}, {`{assert a: (a b), a: true}`, `test:1:15-16 Expected token ")" but got (IDENTIFIER, "b")`}, - {`{function(a, b) a+b: true}`, `test:1:2-10 Unexpected: (function, "function") while parsing field definition`}, + {`{function(a, b) a+b: true}`, `test:1:2-10 Unexpected: "function" while parsing field definition`}, {`[(a b), 2, 3]`, `test:1:5-6 Expected token ")" but got (IDENTIFIER, "b")`}, {`[1, (a b), 2, 3]`, `test:1:8-9 Expected token ")" but got (IDENTIFIER, "b")`}, - {`[a for b in [1 2 3]]`, `test:1:16-17 Expected a comma before next array element.`}, + {`[a for b in [1 2 3]]`, `test:1:16-17 Expected a comma before next array element`}, - {`for`, `test:1:1-4 Unexpected: (for, "for") while parsing terminal`}, - {``, `test:1:1 Unexpected end of file.`}, + {`for`, `test:1:1-4 Unexpected: "for" while parsing terminal`}, + {``, `test:1:1 Unexpected end of file`}, {`((a b))`, `test:1:5-6 Expected token ")" but got (IDENTIFIER, "b")`}, {`a.1`, `test:1:3-4 Expected token IDENTIFIER but got (NUMBER, "1")`}, {`super.1`, `test:1:7-8 Expected token IDENTIFIER but got (NUMBER, "1")`}, {`super[(a b)]`, `test:1:10-11 Expected token ")" but got (IDENTIFIER, "b")`}, {`super[a b]`, `test:1:9-10 Expected token "]" but got (IDENTIFIER, "b")`}, - {`super`, `test:1:1-6 Expected . or [ after super.`}, + {`super`, `test:1:1-6 Expected . or [ after super`}, {`assert (a b); true`, `test:1:11-12 Expected token ")" but got (IDENTIFIER, "b")`}, {`assert a: (a b); true`, `test:1:14-15 Expected token ")" but got (IDENTIFIER, "b")`}, - {`assert a: 'foo', true`, `test:1:16-17 Expected token ";" but got (",", ",")`}, + {`assert a: 'foo', true`, `test:1:16-17 Expected token ";" but got ","`}, {`assert a: 'foo'; (a b)`, `test:1:21-22 Expected token ")" but got (IDENTIFIER, "b")`}, {`error (a b)`, `test:1:10-11 Expected token ")" but got (IDENTIFIER, "b")`}, diff --git a/main_test.go b/main_test.go index 7ac1904..966ee55 100644 --- a/main_test.go +++ b/main_test.go @@ -137,10 +137,10 @@ func runInternalJsonnet(i jsonnetInput) jsonnetResult { vm.NativeFunction(jsonToString) vm.NativeFunction(nativeError) - rawAST, err := parser.SnippetToRawAST(i.name, string(i.input)) - if err != nil { + rawAST, staticErr := parser.SnippetToRawAST(i.name, string(i.input)) + if staticErr != nil { return jsonnetResult{ - output: errFormatter.Format(err) + "\n", + output: errFormatter.Format(staticErr) + "\n", isError: true, } } diff --git a/testdata/extvar_static_error.golden b/testdata/extvar_static_error.golden index d27c0ac..509a87c 100644 --- a/testdata/extvar_static_error.golden +++ b/testdata/extvar_static_error.golden @@ -1,4 +1,4 @@ -:1:1-2 Unexpected: (")", ")") while parsing terminal +:1:1-2 Unexpected: ")" while parsing terminal ) diff --git a/testdata/object_comp_assert.golden b/testdata/object_comp_assert.golden index 4ab3acf..6938b4c 100644 --- a/testdata/object_comp_assert.golden +++ b/testdata/object_comp_assert.golden @@ -1,4 +1,4 @@ -testdata/object_comp_assert:1:32-35 Object comprehension cannot have asserts. +testdata/object_comp_assert:1:32-35 Object comprehension cannot have asserts { assert self.x == 5, ["x"]: 5 for i in [42] } diff --git a/testdata/object_comp_bad_field.golden b/testdata/object_comp_bad_field.golden index 7a16c9f..609511c 100644 --- a/testdata/object_comp_bad_field.golden +++ b/testdata/object_comp_bad_field.golden @@ -1,4 +1,4 @@ -testdata/object_comp_bad_field:1:9-12 Object comprehensions can only have [e] fields. +testdata/object_comp_bad_field:1:9-12 Object comprehensions can only have [e] fields { x: 42 for _ in [1] } diff --git a/testdata/object_comp_bad_field2.golden b/testdata/object_comp_bad_field2.golden index a4205a8..ba6d8fa 100644 --- a/testdata/object_comp_bad_field2.golden +++ b/testdata/object_comp_bad_field2.golden @@ -1,4 +1,4 @@ -testdata/object_comp_bad_field2:1:11-14 Object comprehensions can only have [e] fields. +testdata/object_comp_bad_field2:1:11-14 Object comprehensions can only have [e] fields { "x": 42 for _ in [1] } diff --git a/testdata/object_comp_illegal.golden b/testdata/object_comp_illegal.golden index 651b782..9adeba4 100644 --- a/testdata/object_comp_illegal.golden +++ b/testdata/object_comp_illegal.golden @@ -1,4 +1,4 @@ -testdata/object_comp_illegal:1:15-18 Object comprehension can only have one field. +testdata/object_comp_illegal:1:15-18 Object comprehension can only have one field { local x = 5 for y in [1, 2, 3] } diff --git a/testdata/unfinished_args.golden b/testdata/unfinished_args.golden index e52ff88..3a80fa7 100644 --- a/testdata/unfinished_args.golden +++ b/testdata/unfinished_args.golden @@ -1,4 +1,4 @@ -testdata/unfinished_args:2:1 Expected a comma before next function argument, got end of file. +testdata/unfinished_args:2:1 Expected a comma before next function argument, got end of file