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 }