rum-goggles/v1/vendor/github.com/robertkrimen/otto/parser/error.go
2024-04-04 10:46:14 -04:00

188 lines
5.5 KiB
Go

package parser
import (
"fmt"
"sort"
"github.com/robertkrimen/otto/file"
"github.com/robertkrimen/otto/token"
)
const (
errUnexpectedToken = "Unexpected token %v"
errUnexpectedEndOfInput = "Unexpected end of input"
)
// UnexpectedNumber: 'Unexpected number',
// UnexpectedString: 'Unexpected string',
// UnexpectedIdentifier: 'Unexpected identifier',
// UnexpectedReserved: 'Unexpected reserved word',
// NewlineAfterThrow: 'Illegal newline after throw',
// InvalidRegExp: 'Invalid regular expression',
// UnterminatedRegExp: 'Invalid regular expression: missing /',
// InvalidLHSInAssignment: 'invalid left-hand side in assignment',
// InvalidLHSInForIn: 'Invalid left-hand side in for-in',
// MultipleDefaultsInSwitch: 'More than one default clause in switch statement',
// NoCatchOrFinally: 'Missing catch or finally after try',
// UnknownLabel: 'Undefined label \'%0\'',
// Redeclaration: '%0 \'%1\' has already been declared',
// IllegalContinue: 'Illegal continue statement',
// IllegalBreak: 'Illegal break statement',
// IllegalReturn: 'Illegal return statement',
// StrictModeWith: 'Strict mode code may not include a with statement',
// StrictCatchVariable: 'Catch variable may not be eval or arguments in strict mode',
// StrictVarName: 'Variable name may not be eval or arguments in strict mode',
// StrictParamName: 'Parameter name eval or arguments is not allowed in strict mode',
// StrictParamDupe: 'Strict mode function may not have duplicate parameter names',
// StrictFunctionName: 'Function name may not be eval or arguments in strict mode',
// StrictOctalLiteral: 'Octal literals are not allowed in strict mode.',
// StrictDelete: 'Delete of an unqualified identifier in strict mode.',
// StrictDuplicateProperty: 'Duplicate data property in object literal not allowed in strict mode',
// AccessorDataProperty: 'Object literal may not have data and accessor property with the same name',
// AccessorGetSet: 'Object literal may not have multiple get/set accessors with the same name',
// StrictLHSAssignment: 'Assignment to eval or arguments is not allowed in strict mode',
// StrictLHSPostfix: 'Postfix increment/decrement may not have eval or arguments operand in strict mode',
// StrictLHSPrefix: 'Prefix increment/decrement may not have eval or arguments operand in strict mode',
// StrictReservedWord: 'Use of future reserved word in strict mode'
// A SyntaxError is a description of an ECMAScript syntax error.
// An Error represents a parsing error. It includes the position where the error occurred and a message/description.
type Error struct {
Position file.Position
Message string
}
// FIXME Should this be "SyntaxError"?
func (e Error) Error() string {
filename := e.Position.Filename
if filename == "" {
filename = "(anonymous)"
}
return fmt.Sprintf("%s: Line %d:%d %s",
filename,
e.Position.Line,
e.Position.Column,
e.Message,
)
}
func (p *parser) error(place interface{}, msg string, msgValues ...interface{}) {
var idx file.Idx
switch place := place.(type) {
case int:
idx = p.idxOf(place)
case file.Idx:
if place == 0 {
idx = p.idxOf(p.chrOffset)
} else {
idx = place
}
default:
panic(fmt.Errorf("error(%T, ...)", place))
}
position := p.position(idx)
msg = fmt.Sprintf(msg, msgValues...)
p.errors.Add(position, msg)
}
func (p *parser) errorUnexpected(idx file.Idx, chr rune) {
if chr == -1 {
p.error(idx, errUnexpectedEndOfInput)
return
}
p.error(idx, errUnexpectedToken, token.ILLEGAL)
}
func (p *parser) errorUnexpectedToken(tkn token.Token) {
if tkn == token.EOF {
p.error(file.Idx(0), errUnexpectedEndOfInput)
return
}
value := tkn.String()
switch tkn {
case token.BOOLEAN, token.NULL:
p.error(p.idx, errUnexpectedToken, p.literal)
case token.IDENTIFIER:
p.error(p.idx, "Unexpected identifier")
case token.KEYWORD:
// TODO Might be a future reserved word
p.error(p.idx, "Unexpected reserved word")
case token.NUMBER:
p.error(p.idx, "Unexpected number")
case token.STRING:
p.error(p.idx, "Unexpected string")
default:
p.error(p.idx, errUnexpectedToken, value)
}
}
// ErrorList is a list of *Errors.
type ErrorList []*Error //nolint: errname
// Add adds an Error with given position and message to an ErrorList.
func (el *ErrorList) Add(position file.Position, msg string) {
*el = append(*el, &Error{position, msg})
}
// Reset resets an ErrorList to no errors.
func (el *ErrorList) Reset() {
*el = (*el)[0:0]
}
// Len implement sort.Interface.
func (el *ErrorList) Len() int {
return len(*el)
}
// Swap implement sort.Interface.
func (el *ErrorList) Swap(i, j int) {
(*el)[i], (*el)[j] = (*el)[j], (*el)[i]
}
// Less implement sort.Interface.
func (el *ErrorList) Less(i, j int) bool {
x := (*el)[i].Position
y := (*el)[j].Position
if x.Filename < y.Filename {
return true
}
if x.Filename == y.Filename {
if x.Line < y.Line {
return true
}
if x.Line == y.Line {
return x.Column < y.Column
}
}
return false
}
// Sort sorts el.
func (el *ErrorList) Sort() {
sort.Sort(el)
}
// Error implements the Error interface.
func (el *ErrorList) Error() string {
switch len(*el) {
case 0:
return "no errors"
case 1:
return (*el)[0].Error()
default:
return fmt.Sprintf("%s (and %d more errors)", (*el)[0].Error(), len(*el)-1)
}
}
// Err returns an error equivalent to this ErrorList.
// If the list is empty, Err returns nil.
func (el *ErrorList) Err() error {
if len(*el) == 0 {
return nil
}
return el
}