Skip to content

Commit 7f0be4f

Browse files
committed
Add parser support for relative import.
1 parent c1be87d commit 7f0be4f

File tree

4 files changed

+33
-2
lines changed

4 files changed

+33
-2
lines changed

compiler/compiler.lisp

+4
Original file line numberDiff line numberDiff line change
@@ -1365,6 +1365,10 @@ LOCALS shares share tail structure with input arg locals."
13651365
(defvar *inside-import-from-stmt* nil) ;; hack
13661366

13671367
(defmacro [import-from-stmt] (mod-name-as-list items)
1368+
(when (eq (car mod-name-as-list) '[.])
1369+
(error "Relative imports are not yet supported by the compiler: ~A"
1370+
(with-output-to-string (s)
1371+
(py-pprint `([import-from-stmt] ,mod-name-as-list ,items) s))))
13681372
`(let* ((*module-namespace* nil) ;; hack
13691373
(args (list :within-mod-path ,*compile-file-truename*
13701374
:within-mod-name ',*current-module-name*))

parser/grammar.lisp

+6-1
Original file line numberDiff line numberDiff line change
@@ -348,12 +348,17 @@ Value should be a (weak) EQ hash table: (make-weak-key-hash-table :test 'eq).")
348348
(gp comma--dotted-as-name+)
349349
(p comma--dotted-as-name ([,] dotted-as-name) $2)
350350

351-
(p import-from ([from] dotted-name [import] import-from-2) `([import-from-stmt] ,$2 ,$4))
351+
(p import-from ([from] dotted-name [import] import-from-2) `([import-from-stmt] ,$2 ,$4))
352+
(p import-from ([from] dots dotted-name? [import] import-from-2) `([import-from-stmt] ,(nconc $2 $3) ,$5))
353+
352354
(p import-from-2 :or
353355
(([*]) $1)
354356
((import-as-name comma--import-as-name*) (cons $1 $2))
355357
(([(] import-as-name comma--import-as-name* comma? [)]) (cons $2 $3)))
356358

359+
(p dots ([.]) (list $1))
360+
(p dots (dots [.]) (nconc $1 (list $2)))
361+
357362
(gp comma--import-as-name+)
358363
(p comma--import-as-name ([,] import-as-name) $2)
359364

parser/lexer.lisp

+15-1
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,8 @@ 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 :unknown))
103+
(last-newline-in-source :reader ls-last-newline-in-source :initform :unknown)
104+
(last-returned-value :accessor ls-last-returned-value :initform nil))
104105
(:metaclass closer-mop:funcallable-standard-class))
105106

106107
(defmethod print-object ((lexer lexer) stream)
@@ -124,6 +125,7 @@ where TOKEN-KIND is a symbol like '[identifier]"
124125
(define-symbol-macro %lex-curr-line-no% (ls-curr-line-no *lex-state*))
125126
(define-symbol-macro %lex-string% (ls-string *lex-state*))
126127
(define-symbol-macro %lex-tab-width% (ls-tab-width *lex-state*))
128+
(define-symbol-macro %lex-last-returned-value% (ls-last-returned-value *lex-state*))
127129

128130
(defgeneric call-lexer (yacc-version lexer op)
129131
(:documentation "Returns either the eof-token, or two values: TOKEN-KIND, TOKEN-VALUE"))
@@ -162,6 +164,7 @@ On EOF returns: eof-token, eof-token."
162164
(flet ((lex-return (token value source-loc &optional msg)
163165
(when *lex-debug* (format t "Lexer returns: ~S ~S ~S~@[ ~A~]~%" token value source-loc msg))
164166
(incf return-count)
167+
(setf %lex-last-returned-value% value)
165168
(return-from call-lexer (values token value source-loc)))
166169
(lex-todo (token value)
167170
(when *lex-debug* (format t "Lexer todo: ~S ~S~%" token value))
@@ -234,6 +237,12 @@ On EOF returns: eof-token, eof-token."
234237
(read-kind :string c)
235238
(lex-return '[literal-expr] (list '[literal-expr] :string val) source-loc)))
236239

240+
((and (char= c #\.)
241+
(member %lex-last-returned-value% '([from] [.])))
242+
(multiple-value-bind (val source-loc)
243+
(read-kind :dot c)
244+
(lex-return val val source-loc)))
245+
237246
((or (punct-char1-p c)
238247
(punct-char-not-punct-char1-p c))
239248
(multiple-value-bind (token source-loc)
@@ -422,6 +431,11 @@ Used by compiler to generate 'forbidden' identfiers.")
422431
(or (find-symbol str (load-time-value (find-package :clpython.ast.reserved)))
423432
(intern str (load-time-value (find-package :clpython.user))))))
424433

434+
(defmethod read-kind ((kind (eql :dot)) c1 &rest args)
435+
(declare (ignorable kind))
436+
(assert (char= c1 #\.))
437+
'[.])
438+
425439
;; String
426440

427441
(defmethod read-kind ((kind (eql :string)) ch1 &key raw unicode)

test/parser-test.lisp

+8
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,8 @@
8787
`([literal-expr] :number ,(expt 10 n-expt)))))
8888

8989
;; import
90+
(test-equal '([import-from-stmt] ({a} {b}) (({c} nil))) (ps "from a.b import c" t))
91+
9092
(test-equal '([import-from-stmt] ({sys}) (({path} nil))) (ps "from sys import path" t))
9193
(test-equal '([import-from-stmt] ({sys}) (({path} nil))) (ps "from sys import (path)" t))
9294
(test-equal '([import-from-stmt] ({sys}) (({path} nil))) (ps "from sys import (path,)" t))
@@ -103,6 +105,12 @@
103105
(test-equal '([import-from-stmt] ({sys}) (({path} nil) ({exit} {e}))) (ps "from sys import (path, exit as e,)" t))
104106
(test-error (ps "from sys import (path exit)" t) :condition-type '{SyntaxError})
105107

108+
(test-equal '([import-from-stmt] ([.] ) (({b} nil))) (ps "from . import b" t))
109+
(test-equal '([import-from-stmt] ([.] {a} ) (({b} nil))) (ps "from .a import b" t))
110+
(test-equal '([import-from-stmt] ([.] [.] {a} ) (({b} nil))) (ps "from ..a import b" t))
111+
(test-equal '([import-from-stmt] ([.] [.] [.] {a} ) (({b} nil))) (ps "from ...a import b" t))
112+
(test-equal '([import-from-stmt] ([.] [.] [.] {a} {b}) (({c} nil))) (ps "from ...a.b import c" t))
113+
106114
;; suffix operations
107115
(test-equal '([attributeref-expr]
108116
([call-expr]

0 commit comments

Comments
 (0)