error.go 5.6 KB

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