Fix handling of ::keywords using aliases

This patch introduces namespace tracking to Marginalia's parser: ns,
in-ns, require, use and alias forms are now evaluated in the proper
namespace at read-time. This is necessary to handle ::keywords in full
generality, since ::foo/bar is a valid token iff the symbol 'foo can
be resolved to a namespace at read time:

    (in-ns 'test)

    ::foo/bar
    ; => invalid token results in read-time error

    (require '[some.namespace :as foo])
    ::foo/bar
    ; => :some.namespace/foo

NB. only top-level #{ns in-ns require use alias} forms are recognized.
This commit is contained in:
Michał Marczyk 2011-09-14 12:04:59 +08:00 committed by Fogus
parent 142f09f75f
commit 3fff515edb
2 changed files with 16 additions and 2 deletions

View file

@ -15,6 +15,9 @@
(def top-level-comments (atom []))
(def sub-level-comments (atom []))
(def user-ns (the-ns 'user))
(def current-namespace (atom user-ns))
(def *comments* nil)
(defn read-comment [reader semicolon]
@ -49,6 +52,14 @@
(recur (.read rdr))
:else (.unread rdr c))))
(defn maybe-change-namespace [form]
(when (and (seq? form)
('#{ns in-ns require use alias} (first form)))
(binding [*ns* @current-namespace]
(eval form))
(when ('#{ns in-ns} (first form))
(reset! current-namespace (the-ns (second form))))))
(defn parse* [reader]
(take-while
:form
@ -58,12 +69,14 @@
(binding [*comments* top-level-comments]
(skip-spaces-and-comments reader))
(let [start (.getLineNumber reader)
form (binding [*comments* sub-level-comments]
form (binding [*comments* sub-level-comments
*ns* @current-namespace]
(. clojure.lang.LispReader
(read reader false nil false)))
end (.getLineNumber reader)
code {:form form :start start :end end}
comments @top-level-comments]
(maybe-change-namespace form)
(swap! top-level-comments (constantly []))
(if (empty? comments)
[code]

View file

@ -8,4 +8,5 @@
;(is (= (count (marginalia.parser/parse "(ns test)\n123")) 1)) still failing
(is (= (count (marginalia.parser/parse "(ns test)\n123\n")) 1))
(is (= (count (marginalia.parser/parse "(ns test)\n\"string\"")) 1))
(is (= (count (marginalia.parser/parse "(ns test)\n\"some string\"")) 1)))
(is (= (count (marginalia.parser/parse "(ns test)\n\"some string\"")) 1))
(is (= (count (marginalia.parser/parse "(ns test (:require [marginalia.parser :as parser]))\n(defn foo [] ::parser/foo)")) 1)))