forked from robertkrimen/otto
-
Notifications
You must be signed in to change notification settings - Fork 0
/
error.go
187 lines (166 loc) · 5.47 KB
/
error.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
package parser
import (
"fmt"
"sort"
"github.com/kavanahuang/otto/file"
"github.com/kavanahuang/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
}