Add ParseParameter. Also fell down the rabbithole improving content and consistency of parse errors

This commit is contained in:
Dave Cunningham 2020-03-05 02:10:14 +00:00 committed by Stanisław Barzowski
parent ce07c7fb8b
commit 5476fefb25
12 changed files with 174 additions and 121 deletions

View File

@ -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()
}

View File

@ -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}
}

View File

@ -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)
}
}
@ -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

View File

@ -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 <f1> id <f2> = expr or just expr.
// It returns either (<f1>, id, <f2>, 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 <f1> id <f2> = expr or just <f1> id.
// It returns either (<f1>, id, <f2>, expr) or (<f1>, 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 {

View File

@ -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")`},

View File

@ -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,
}
}

View File

@ -1,4 +1,4 @@
<extvar:staticErrorVar>:1:1-2 Unexpected: (")", ")") while parsing terminal
<extvar:staticErrorVar>:1:1-2 Unexpected: ")" while parsing terminal
)

View File

@ -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] }

View File

@ -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] }

View File

@ -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] }

View File

@ -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] }

View File

@ -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