Skip to content

Commit adf4c40

Browse files
committed
Improve error messages for eof
1 parent 8648be9 commit adf4c40

File tree

5 files changed

+23
-27
lines changed

5 files changed

+23
-27
lines changed

expr_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -108,9 +108,9 @@ func ExampleEval_matches() {
108108

109109
fmt.Printf("%v", output)
110110

111-
// Output: error parsing regexp: missing closing ): `a(` (1:17)
111+
// Output: error parsing regexp: missing closing ): `a(` (1:16)
112112
// | "a" matches "a("
113-
// | ................^
113+
// | ...............^
114114
}
115115

116116
func ExampleCompile() {

parser/lexer/lexer.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,14 @@ func (l *lexer) emitValue(t Kind, value string) {
6969
l.start = l.end
7070
}
7171

72+
func (l *lexer) emitEOF() {
73+
l.tokens = append(l.tokens, Token{
74+
Location: l.loc(l.start - 1), // Point to previous position for better error messages.
75+
Kind: EOF,
76+
})
77+
l.start = l.end
78+
}
79+
7280
func (l *lexer) word() string {
7381
return l.input[l.start:l.end]
7482
}

parser/lexer/state.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ type stateFn func(*lexer) stateFn
99
func root(l *lexer) stateFn {
1010
switch r := l.next(); {
1111
case r == eof:
12-
l.emit(EOF)
12+
l.emitEOF()
1313
return nil
1414
case isSpace(r):
1515
l.ignore()

parser/parser.go

Lines changed: 6 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,7 @@ func (p *parser) parseExpression(precedence int) Node {
145145
nodeLeft := p.parsePrimary()
146146

147147
token := p.current
148-
for token.Is(Operator) {
148+
for token.Is(Operator) && p.err == nil {
149149
if op, ok := binaryOperators[token.Value]; ok {
150150
if op.precedence >= precedence {
151151
p.next()
@@ -230,7 +230,7 @@ func (p *parser) parsePrimary() Node {
230230

231231
func (p *parser) parseConditionalExpression(node Node) Node {
232232
var expr1, expr2 Node
233-
for p.current.Is(Operator, "?") {
233+
for p.current.Is(Operator, "?") && p.err == nil {
234234
p.next()
235235

236236
if !p.current.Is(Operator, ":") {
@@ -371,7 +371,7 @@ func (p *parser) parseMapExpression(token Token) Node {
371371
p.expect(Bracket, "{")
372372

373373
nodes := make([]Node, 0)
374-
for !p.current.Is(Bracket, "}") {
374+
for !p.current.Is(Bracket, "}") && p.err == nil {
375375
if len(nodes) > 0 {
376376
p.expect(Operator, ",")
377377
}
@@ -404,27 +404,15 @@ func (p *parser) parseMapExpression(token Token) Node {
404404

405405
func (p *parser) parsePostfixExpression(node Node) Node {
406406
token := p.current
407-
for token.Is(Operator) || token.Is(Bracket) {
407+
for (token.Is(Operator) || token.Is(Bracket)) && p.err == nil {
408408
if token.Value == "." {
409409
p.next()
410410

411411
token = p.current
412412
p.next()
413413

414414
if token.Kind != Identifier &&
415-
// Operators like "not" and "matches" are valid method or property names,
416-
//
417-
// In other words, besides name token kind, operator kind could also be parsed as a property or method.
418-
// This is because operators are processed by the lexer prior to names. So "not" in "foo.not()"
419-
// or "matches" in "foo.matches" will be recognized as an operator first. But in fact, "not"
420-
// and "matches" in such expressions shall be parsed as method or property names.
421-
//
422-
// And this ONLY works if the operator consists of valid characters for a property or method name.
423-
//
424-
// Other types, such as text kind and number kind, can't be parsed as property nor method names.
425-
//
426-
// As a result, if token is NOT an operator OR token.value is NOT a valid property or method name,
427-
// an error shall be returned.
415+
// Operators like "not" and "matches" are valid methods or property names.
428416
(token.Kind != Operator || !isValidIdentifier(token.Value)) {
429417
p.error("expected name")
430418
}
@@ -535,7 +523,7 @@ func (p *parser) parseList(start, end string) []Node {
535523
p.expect(Bracket, start)
536524

537525
nodes := make([]Node, 0)
538-
for !p.current.Is(Bracket, end) {
526+
for !p.current.Is(Bracket, end) && p.err == nil {
539527
if len(nodes) > 0 {
540528
p.expect(Operator, ",")
541529
}

parser/parser_test.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -227,14 +227,14 @@ func TestParse(t *testing.T) {
227227

228228
const errorTests = `
229229
foo.
230-
unexpected end of expression (1:5)
230+
unexpected end of expression (1:4)
231231
| foo.
232-
| ....^
232+
| ...^
233233
234234
a+
235-
unexpected token EOF (1:3)
235+
unexpected token EOF (1:2)
236236
| a+
237-
| ..^
237+
| .^
238238
239239
a ? (1+2) c
240240
unexpected token Identifier("c") (1:11)
@@ -257,9 +257,9 @@ a map key must be a quoted string, a number, a identifier, or an expression encl
257257
| .^
258258
259259
a matches 'a:)b'
260-
error parsing regexp: unexpected ): ` + "`a:)b`" + ` (1:17)
260+
error parsing regexp: unexpected ): ` + "`a:)b`" + ` (1:16)
261261
| a matches 'a:)b'
262-
| ................^
262+
| ...............^
263263
264264
foo({.bar})
265265
a map key must be a quoted string, a number, a identifier, or an expression enclosed in parentheses (unexpected token Operator(".")) (1:6)

0 commit comments

Comments
 (0)