github.com/nuvolaris/goja@v0.0.0-20230825100449-967811910c6d/parser/error.go (about)

     1  package parser
     2  
     3  import (
     4  	"fmt"
     5  	"sort"
     6  
     7  	"github.com/nuvolaris/goja/file"
     8  	"github.com/nuvolaris/goja/token"
     9  )
    10  
    11  const (
    12  	err_UnexpectedToken      = "Unexpected token %v"
    13  	err_UnexpectedEndOfInput = "Unexpected end of input"
    14  	err_UnexpectedEscape     = "Unexpected escape"
    15  )
    16  
    17  //    UnexpectedNumber:  'Unexpected number',
    18  //    UnexpectedString:  'Unexpected string',
    19  //    UnexpectedIdentifier:  'Unexpected identifier',
    20  //    UnexpectedReserved:  'Unexpected reserved word',
    21  //    NewlineAfterThrow:  'Illegal newline after throw',
    22  //    InvalidRegExp: 'Invalid regular expression',
    23  //    UnterminatedRegExp:  'Invalid regular expression: missing /',
    24  //    InvalidLHSInAssignment:  'Invalid left-hand side in assignment',
    25  //    InvalidLHSInForIn:  'Invalid left-hand side in for-in',
    26  //    MultipleDefaultsInSwitch: 'More than one default clause in switch statement',
    27  //    NoCatchOrFinally:  'Missing catch or finally after try',
    28  //    UnknownLabel: 'Undefined label \'%0\'',
    29  //    Redeclaration: '%0 \'%1\' has already been declared',
    30  //    IllegalContinue: 'Illegal continue statement',
    31  //    IllegalBreak: 'Illegal break statement',
    32  //    IllegalReturn: 'Illegal return statement',
    33  //    StrictModeWith:  'Strict mode code may not include a with statement',
    34  //    StrictCatchVariable:  'Catch variable may not be eval or arguments in strict mode',
    35  //    StrictVarName:  'Variable name may not be eval or arguments in strict mode',
    36  //    StrictParamName:  'Parameter name eval or arguments is not allowed in strict mode',
    37  //    StrictParamDupe: 'Strict mode function may not have duplicate parameter names',
    38  //    StrictFunctionName:  'Function name may not be eval or arguments in strict mode',
    39  //    StrictOctalLiteral:  'Octal literals are not allowed in strict mode.',
    40  //    StrictDelete:  'Delete of an unqualified identifier in strict mode.',
    41  //    StrictDuplicateProperty:  'Duplicate data property in object literal not allowed in strict mode',
    42  //    AccessorDataProperty:  'Object literal may not have data and accessor property with the same name',
    43  //    AccessorGetSet:  'Object literal may not have multiple get/set accessors with the same name',
    44  //    StrictLHSAssignment:  'Assignment to eval or arguments is not allowed in strict mode',
    45  //    StrictLHSPostfix:  'Postfix increment/decrement may not have eval or arguments operand in strict mode',
    46  //    StrictLHSPrefix:  'Prefix increment/decrement may not have eval or arguments operand in strict mode',
    47  //    StrictReservedWord:  'Use of future reserved word in strict mode'
    48  
    49  // A SyntaxError is a description of an ECMAScript syntax error.
    50  
    51  // An Error represents a parsing error. It includes the position where the error occurred and a message/description.
    52  type Error struct {
    53  	Position file.Position
    54  	Message  string
    55  }
    56  
    57  // FIXME Should this be "SyntaxError"?
    58  
    59  func (self Error) Error() string {
    60  	filename := self.Position.Filename
    61  	if filename == "" {
    62  		filename = "(anonymous)"
    63  	}
    64  	return fmt.Sprintf("%s: Line %d:%d %s",
    65  		filename,
    66  		self.Position.Line,
    67  		self.Position.Column,
    68  		self.Message,
    69  	)
    70  }
    71  
    72  func (self *_parser) error(place interface{}, msg string, msgValues ...interface{}) *Error {
    73  	idx := file.Idx(0)
    74  	switch place := place.(type) {
    75  	case int:
    76  		idx = self.idxOf(place)
    77  	case file.Idx:
    78  		if place == 0 {
    79  			idx = self.idxOf(self.chrOffset)
    80  		} else {
    81  			idx = place
    82  		}
    83  	default:
    84  		panic(fmt.Errorf("error(%T, ...)", place))
    85  	}
    86  
    87  	position := self.position(idx)
    88  	msg = fmt.Sprintf(msg, msgValues...)
    89  	self.errors.Add(position, msg)
    90  	return self.errors[len(self.errors)-1]
    91  }
    92  
    93  func (self *_parser) errorUnexpected(idx file.Idx, chr rune) error {
    94  	if chr == -1 {
    95  		return self.error(idx, err_UnexpectedEndOfInput)
    96  	}
    97  	return self.error(idx, err_UnexpectedToken, token.ILLEGAL)
    98  }
    99  
   100  func (self *_parser) errorUnexpectedToken(tkn token.Token) error {
   101  	switch tkn {
   102  	case token.EOF:
   103  		return self.error(file.Idx(0), err_UnexpectedEndOfInput)
   104  	}
   105  	value := tkn.String()
   106  	switch tkn {
   107  	case token.BOOLEAN, token.NULL:
   108  		value = self.literal
   109  	case token.IDENTIFIER:
   110  		return self.error(self.idx, "Unexpected identifier")
   111  	case token.KEYWORD:
   112  		// TODO Might be a future reserved word
   113  		return self.error(self.idx, "Unexpected reserved word")
   114  	case token.ESCAPED_RESERVED_WORD:
   115  		return self.error(self.idx, "Keyword must not contain escaped characters")
   116  	case token.NUMBER:
   117  		return self.error(self.idx, "Unexpected number")
   118  	case token.STRING:
   119  		return self.error(self.idx, "Unexpected string")
   120  	}
   121  	return self.error(self.idx, err_UnexpectedToken, value)
   122  }
   123  
   124  // ErrorList is a list of *Errors.
   125  type ErrorList []*Error
   126  
   127  // Add adds an Error with given position and message to an ErrorList.
   128  func (self *ErrorList) Add(position file.Position, msg string) {
   129  	*self = append(*self, &Error{position, msg})
   130  }
   131  
   132  // Reset resets an ErrorList to no errors.
   133  func (self *ErrorList) Reset() { *self = (*self)[0:0] }
   134  
   135  func (self ErrorList) Len() int      { return len(self) }
   136  func (self ErrorList) Swap(i, j int) { self[i], self[j] = self[j], self[i] }
   137  func (self ErrorList) Less(i, j int) bool {
   138  	x := &self[i].Position
   139  	y := &self[j].Position
   140  	if x.Filename < y.Filename {
   141  		return true
   142  	}
   143  	if x.Filename == y.Filename {
   144  		if x.Line < y.Line {
   145  			return true
   146  		}
   147  		if x.Line == y.Line {
   148  			return x.Column < y.Column
   149  		}
   150  	}
   151  	return false
   152  }
   153  
   154  func (self ErrorList) Sort() {
   155  	sort.Sort(self)
   156  }
   157  
   158  // Error implements the Error interface.
   159  func (self ErrorList) Error() string {
   160  	switch len(self) {
   161  	case 0:
   162  		return "no errors"
   163  	case 1:
   164  		return self[0].Error()
   165  	}
   166  	return fmt.Sprintf("%s (and %d more errors)", self[0].Error(), len(self)-1)
   167  }
   168  
   169  // Err returns an error equivalent to this ErrorList.
   170  // If the list is empty, Err returns nil.
   171  func (self ErrorList) Err() error {
   172  	if len(self) == 0 {
   173  		return nil
   174  	}
   175  	return self
   176  }