mirror of
https://github.com/google/go-jsonnet.git
synced 2025-09-28 17:01:02 +02:00
Add ParseParameter. Also fell down the rabbithole improving content and consistency of parse errors
This commit is contained in:
parent
ce07c7fb8b
commit
5476fefb25
@ -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()
|
||||
}
|
||||
|
||||
|
@ -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}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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 {
|
||||
|
@ -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")`},
|
||||
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
2
testdata/extvar_static_error.golden
vendored
2
testdata/extvar_static_error.golden
vendored
@ -1,4 +1,4 @@
|
||||
<extvar:staticErrorVar>:1:1-2 Unexpected: (")", ")") while parsing terminal
|
||||
<extvar:staticErrorVar>:1:1-2 Unexpected: ")" while parsing terminal
|
||||
|
||||
)
|
||||
|
||||
|
2
testdata/object_comp_assert.golden
vendored
2
testdata/object_comp_assert.golden
vendored
@ -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] }
|
||||
|
||||
|
2
testdata/object_comp_bad_field.golden
vendored
2
testdata/object_comp_bad_field.golden
vendored
@ -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] }
|
||||
|
||||
|
2
testdata/object_comp_bad_field2.golden
vendored
2
testdata/object_comp_bad_field2.golden
vendored
@ -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] }
|
||||
|
||||
|
2
testdata/object_comp_illegal.golden
vendored
2
testdata/object_comp_illegal.golden
vendored
@ -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] }
|
||||
|
||||
|
2
testdata/unfinished_args.golden
vendored
2
testdata/unfinished_args.golden
vendored
@ -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
|
||||
|
||||
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user