Skip to content

Commit d8969d3

Browse files
committed
Implement reader support for #?@
This changeset adds "sharp" reader support for the #?@ unquote reader macro, added in Clojure 1.7.0. Unfortunately the parser is somewhat complicated because there are three #? cases: 1. #? ... 2. #?@ ... 3. #?foo ... The first case was already correctly handled. The second case requires extending the parser to recognize \?\@, which requires consuming an additional character from the reader before delegating to reading subsequent form. Unfortunately to handle the third case correctly, we need to push the \? back onto the reader, before simply reading a pair of tokens. Fixes #46
1 parent 72a7095 commit d8969d3

File tree

3 files changed

+27
-1
lines changed

3 files changed

+27
-1
lines changed

src/rewrite_clj/parser/core.clj

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,24 @@
119119
\' (node/var-node (parse-printables reader :var 1 true))
120120
\= (node/eval-node (parse-printables reader :eval 1 true))
121121
\_ (node/uneval-node (parse-printables reader :uneval 1 true))
122+
\? (do
123+
;; we need to examine the next character, so consume one (known \?)
124+
(reader/next reader)
125+
;; we will always have a reader-macro-node as the result
126+
(node/reader-macro-node
127+
(let [read1 (fn [] (parse-printables reader :reader-macro 1))]
128+
(cons (case (reader/peek reader)
129+
;; the easy case, just emit a token
130+
\( (node/token-node (symbol "?"))
131+
132+
;; the harder case, match \@, consume it and emit the token
133+
\@ (do (reader/next reader)
134+
(node/token-node (symbol "?@")))
135+
136+
;; otherwise no idea what we're reading but its \? prefixed
137+
(do (reader/unread reader \?)
138+
(first (read1))))
139+
(read1)))))
122140
(node/reader-macro-node (parse-printables reader :reader-macro 2))))
123141

124142
(defmethod parse-next* :deref

src/rewrite_clj/reader.clj

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,11 @@
109109
[reader]
110110
(r/read-char reader))
111111

112+
(defn unread
113+
"Unreads a char. Puts the char back on the reader."
114+
[reader ch]
115+
(r/unread reader ch))
116+
112117
(defn peek
113118
"Peek next char."
114119
[reader]

test/rewrite_clj/parser_test.clj

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@
8686
"#=sym" :eval [] '(eval 'sym)
8787
"#= sym" :eval [:whitespace] '(eval 'sym)
8888
"#'sym" :var [] '(var sym)
89-
"#'\nsym" :var [:newline]) '(var sym)
89+
"#'\nsym" :var [:newline])
9090

9191
(fact "about eval."
9292
(let [n (p/parse-string "#=(+ 1 2)")]
@@ -189,6 +189,9 @@
189189
"#=(+ 1 2)" :eval [:list]
190190
"#macro 1" :reader-macro [:token :whitespace :token]
191191
"#macro (* 2 3)" :reader-macro [:token :whitespace :list]
192+
"#?(:clj bar)" :reader-macro [:token :list]
193+
"#?@(:clj bar)" :reader-macro [:token :list]
194+
"#?foo baz" :reader-macro [:token :whitespace :token]
192195
"#_abc" :uneval [:token]
193196
"#_(+ 1 2)" :uneval [:list]
194197
"#(+ % 1)" :fn [:token :whitespace

0 commit comments

Comments
 (0)