Really fix handling of ::keywords using aliases

With this patch, Marginalia installs a custom keyword reader during
parsing.

This reader reuses clojure.lang.LispReader's readToken and matchSymbol
methods to read in either the whole keyword (when faced with a
single-colon keyword) or the part following the first colon (a
single-colon keyword, if we get to this case). Single-colon keywords
may have arbitrary namespace parts, so no aliasing issues arise.

The object returned depends on the type of keyword being read in:

* :foo, :foo/bar => just the keyword

* ::foo/bar => (DoubleColonKeyword. :foo/bar)

DCK's print-method writes out a single colon followed by the string
representation of the DCK's contents, for the genuine double-colon
keyword look.

Note that readToken and matchSymbol are private; this patch uses
clojure.contrib.reflect/call-method to call them.
This commit is contained in:
Michał Marczyk 2011-09-17 08:42:16 +02:00
parent f7f63b1149
commit 0374c221d9
2 changed files with 34 additions and 2 deletions

View file

@ -4,7 +4,7 @@
(ns marginalia.parser
"Provides the parsing facilities for Marginalia."
(:refer-clojure :exclude [replace])
(:use [clojure.contrib [reflect :only (get-field)]]
(:use [clojure.contrib [reflect :only (get-field call-method)]]
[clojure [string :only (join replace)]]))
(defrecord Comment [content])
@ -39,6 +39,34 @@
(int \;)
reader))
(defrecord DoubleColonKeyword [content])
(defmethod print-method DoubleColonKeyword [dck ^java.io.Writer out]
(.write out (str \: (.content dck))))
(letfn [(read-token [reader c]
(call-method clojure.lang.LispReader :readToken
[java.io.PushbackReader Character/TYPE]
nil reader c))
(match-symbol [s]
(call-method clojure.lang.LispReader :matchSymbol
[String]
nil s))]
(defn read-keyword [reader colon]
(let [c (.read reader)]
(if (= \: c)
(-> (read-token reader c)
match-symbol
DoubleColonKeyword.)
(do (.unread reader c)
(-> (read-token reader colon)
match-symbol))))))
(defn set-keyword-reader [reader]
(aset (get-field clojure.lang.LispReader :macros nil)
(int \:)
reader))
(defn skip-spaces-and-comments [rdr]
(loop [c (.read rdr)]
(cond (= c -1) nil
@ -263,11 +291,14 @@
old-cmt-rdr (aget (get-field clojure.lang.LispReader :macros nil) (int \;))]
(try
(set-comment-reader read-comment)
(set-keyword-reader read-keyword)
(let [parsed-code (-> reader parse* doall)]
(set-comment-reader old-cmt-rdr)
(set-keyword-reader nil)
(arrange-in-sections parsed-code lines))
(catch Exception e
(set-comment-reader old-cmt-rdr)
(set-keyword-reader nil)
(throw e)))))
(defn parse-file [file]

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)))