Skip to content

Commit 7d0864e

Browse files
committed
Fix error message: unexpected NEWLINE sometimes reported as unexpected EOF
1 parent bf7ce47 commit 7d0864e

File tree

6 files changed

+33
-17
lines changed

6 files changed

+33
-17
lines changed

parser/grammar-aclyacc.lisp

+5-5
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ Therefore need to convert TOKEN-KIND to the corresponding TOKEN-CODE before pass
8181
(last-newline-in-source (second (assoc :last-newline-in-source pos)))
8282
(token (maybe-unwrap-literal-value (excl.yacc:grammar-parse-error-token c)))
8383
(encl-error (excl.yacc::grammar-parse-error-enclosed-error c)))
84+
8485
(cond (encl-error ;; Error in one of our grammar rules
8586
(when clpython:*exceptions-loaded*
8687
(assert (not (typep encl-error '{SyntaxError}))
@@ -91,10 +92,9 @@ Therefore need to convert TOKEN-KIND to the corresponding TOKEN-CODE before pass
9192
(format nil "Parse error at line ~A~@[, at token `~S'~].~%[Internal error: ~A~_(caught due to ~S)]"
9293
line token encl-error '*catch-yacc-conditions*)))
9394

94-
((and token
95-
(not (eq token (lexer-eof-token yacc-version)))
96-
(not (and (eq token '[newline]) (not last-newline-in-source))))
97-
(raise-syntax-error (format nil "At line ~A, parser got unexpected token: ~S" line token)))
95+
((or (eq token (lexer-eof-token yacc-version))
96+
(and (eq token '[newline]) (not last-newline-in-source)))
97+
(raise-unexpected-eof))
9898

9999
(t
100-
(raise-unexpected-eof))))))))
100+
(raise-syntax-error (format nil "At line ~A, parser got unexpected token: ~S" line token)))))))))

parser/grammar-clyacc.lisp

+6-5
Original file line numberDiff line numberDiff line change
@@ -60,11 +60,12 @@
6060
(value (maybe-unwrap-literal-value (yacc:yacc-parse-error-value c)))
6161
(expected-tokens (yacc:yacc-parse-error-expected-terminals c)))
6262

63-
(cond ((and (not (eq token (lexer-eof-token yacc-version)))
64-
(not (and (eq token '[newline]) (not last-newline-in-source))))
63+
(cond ((or (eq token (lexer-eof-token yacc-version))
64+
(and (eq token '[newline]) (not last-newline-in-source)))
65+
(raise-unexpected-eof))
66+
67+
(t
6568
(raise-syntax-error
6669
(format nil "At line ~A, parser got unexpected token: ~S. ~
6770
~_Expected one of: ~{`~A'~^ ~}."
68-
line value expected-tokens)))
69-
(t
70-
(raise-unexpected-eof)))))))
71+
line value expected-tokens))))))))

parser/lexer.lisp

+7-5
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ where TOKEN-KIND is a symbol like '[identifier]"
100100
(bracket-level :accessor ls-bracket-level :initform 0 :type fixnum)
101101
(open-deco :initform nil)
102102
(return-count :initform 0)
103-
(last-newline-in-source :reader ls-last-newline-in-source :initform nil))
103+
(last-newline-in-source :reader ls-last-newline-in-source :initform :unknown))
104104
(:metaclass closer-mop:funcallable-standard-class))
105105

106106
(defmethod print-object ((lexer lexer) stream)
@@ -142,8 +142,8 @@ On EOF returns: eof-token, eof-token."
142142

143143
(defmethod call-lexer (yacc-version (lexer lexer) (op (eql nil)))
144144
(declare (ignorable yacc-version op))
145-
(with-slots (last-read-char-ix curr-line-no yacc-version tokens-todo
146-
indent-stack bracket-level open-deco return-count last-newline-in-source) lexer
145+
(with-slots (last-read-char-ix curr-line-no yacc-version tokens-todo indent-stack
146+
bracket-level open-deco return-count last-newline-in-source eof-returned-already) lexer
147147
(when (= last-read-char-ix -1)
148148
;; Check leading whitespace. This will go unnoticed by the lexer otherwise.
149149
(destructuring-bind (newline-p new-indent eof-p)
@@ -166,6 +166,8 @@ On EOF returns: eof-token, eof-token."
166166
(loop
167167
(let ((c (lex-read-char :eof-error nil)))
168168
(cond ((not c)
169+
(when (eq last-newline-in-source :unknown)
170+
(setf last-newline-in-source nil))
169171
(when (zerop return-count) ;; grammar does not like empty files, so dummy content
170172
(lex-return '[identifier] '{None} nil))
171173
(when *lex-fake-eof-after-toplevel-form*
@@ -243,8 +245,8 @@ On EOF returns: eof-token, eof-token."
243245
(lex-unread-char c)
244246
(destructuring-bind (newline-p new-indent eof-p)
245247
(read-kind :whitespace c)
246-
(when (and eof-p newline-p)
247-
(setf last-newline-in-source t))
248+
(when eof-p
249+
(setf last-newline-in-source newline-p))
248250
(when (and (not eof-p) newline-p (zerop bracket-level))
249251
;; Queue eof before dedents as todo.
250252
(when (and (zerop new-indent)

parser/parser.lisp

+1-1
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ Most important options:
2929
(:method ((x string) &rest options &key (yacc-version *default-yacc-version*) one-expr record-source-location tab-width)
3030
(declare (ignore one-expr record-source-location tab-width))
3131
(let ((lexer (apply #'make-lexer yacc-version x (sans options :one-expr :record-source-location))))
32-
(apply #'parse-module-with-yacc yacc-version lexer (sans options :tab-width))))
32+
(apply #'parse-module-with-yacc yacc-version lexer (sans options :tab-width :yacc-version))))
3333

3434
(:method ((x pathname) &rest options)
3535
(apply #'parse (slurp-file x) options))

test/parser-test.lisp

+6
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,12 @@ def f(): pass" t))
220220
(test :unexp-eof-error (try-parse "'") :fail-info "(EOF in Lexer)")
221221
(test :unexp-eof-error (try-parse "def")
222222
:fail-info "Unexpected EOF in grammar")
223+
(test :syntax-error (try-parse "def f(x):
224+
if x > 1
225+
return 3
226+
else:
227+
return 1")) ;; missing colon after "x > 1"
228+
223229
(test :unexp-eof-error (try-parse "def f():") :fail-info "(EOF in grammar)")
224230
(test :syntax-error (try-parse "def def") :fail-info "(Incorrect grammar)")
225231
(test :syntax-error (try-parse " 42") :fail-info "(Leading whitespace)"))

test/tsetup.lisp

+8-1
Original file line numberDiff line numberDiff line change
@@ -119,10 +119,17 @@ seems to give implementations some freedom here. (In practice: Allegro=NIL, LisW
119119
(test-false (seq-member 'a #(b)))
120120
(test-false (seq-member 'a '(b)))))
121121

122+
(defmacro with-all-parser-versions (&body body)
123+
`(dolist (.yacc-version (append (when (asdf:find-system :yacc nil) (list :cl-yacc))
124+
#+allegro '(:allegro-yacc)))
125+
(let ((clpython.parser::*default-yacc-version* .yacc-version))
126+
,@body)))
127+
122128
(defun run-tests ()
123129
(with-subtest (:name "CLPython")
124130
(test-comp-testfunc)
125-
(run-parser-test)
131+
(with-all-parser-versions
132+
(run-parser-test))
126133
#+(or)(run-lispy-test)
127134
(run-compiler-test)
128135
(run-code-walker-test)

0 commit comments

Comments
 (0)