Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Add end-line and end-column with IndexingReaders

- end-line and end-column are captured when forms are read
- required bug fix in pushback behavior of IndexingPushbackReader
- Add tests examining metadata for all forms read in simple string

Signed-off-by: Bronsa <brobronsa@gmail.com>
  • Loading branch information...
commit a7251e87c5530e04c3c7c4a983f30030f38730df 1 parent bf7811d
@aredington aredington authored Bronsa committed
View
47 src/main/clojure/clojure/tools/reader.clj
@@ -164,38 +164,53 @@
(defn- read-list
[rdr _]
- (let [[line column] (when (indexing-reader? rdr)
- [(get-line-number rdr) (int (dec (get-column-number rdr)))])
- the-list (read-delimited \) rdr true)]
+ (let [[start-line start-column] (when (indexing-reader? rdr)
+ [(get-line-number rdr) (int (dec (get-column-number rdr)))])
+ the-list (read-delimited \) rdr true)
+ [end-line end-column] (when (indexing-reader? rdr)
+ [(get-line-number rdr) (int (get-column-number rdr))])]
(if (empty? the-list)
'()
(with-meta (clojure.lang.PersistentList/create the-list)
- (when line
- {:line line :column column})))))
+ (when start-line
+ {:line start-line
+ :column start-column
+ :end-line end-line
+ :end-column end-column})))))
(defn- read-vector
[rdr _]
- (let [[line column] (when (indexing-reader? rdr)
+ (let [[start-line start-column] (when (indexing-reader? rdr)
[(get-line-number rdr) (int (dec (get-column-number rdr)))])
- the-vector (read-delimited \] rdr true)]
+ the-vector (read-delimited \] rdr true)
+ [end-line end-column] (when (indexing-reader? rdr)
+ [(get-line-number rdr) (int (get-column-number rdr))])]
(with-meta the-vector
- (when line
- {:line line :column column}))))
+ (when start-line
+ {:line start-line
+ :column start-column
+ :end-line end-line
+ :end-column end-column}))))
(defn- read-map
[rdr _]
- (let [[line column] (when (indexing-reader? rdr)
- [(get-line-number rdr) (int (dec (get-column-number rdr)))])
+ (let [[start-line start-column] (when (indexing-reader? rdr)
+ [(get-line-number rdr) (int (dec (get-column-number rdr)))])
the-map (read-delimited \} rdr true)
- map-count (count the-map)]
+ map-count (count the-map)
+ [end-line end-column] (when (indexing-reader? rdr)
+ [(get-line-number rdr) (int (dec (get-column-number rdr)))])]
(when (odd? map-count)
(reader-error rdr "Map literal must contain an even number of forms"))
(with-meta
(if (zero? map-count)
{}
(RT/map (to-array the-map)))
- (when line
- {:line line :column column}))))
+ (when start-line
+ {:line start-line
+ :column start-column
+ :end-line end-line
+ :end-column end-column}))))
(defn- read-number
[reader initch]
@@ -263,7 +278,9 @@
(or (when-let [p (parse-symbol token)]
(with-meta (symbol (p 0) (p 1))
(when line
- {:line line :column column})))
+ {:line line :column column
+ :end-line (get-line-number rdr)
+ :end-column (int (get-column-number rdr))})))
(reader-error rdr "Invalid token: " token))))))
(def ^:dynamic *alias-map*
View
12 src/main/clojure/clojure/tools/reader/reader_types.clj
@@ -106,7 +106,8 @@
(deftype IndexingPushbackReader
[rdr ^:unsynchronized-mutable line ^:unsynchronized-mutable column
- ^:unsynchronized-mutable line-start? ^:unsynchronized-mutable prev file-name]
+ ^:unsynchronized-mutable line-start? ^:unsynchronized-mutable prev
+ ^:unsynchronized-mutable prev-column file-name]
Reader
(read-char [reader]
(when-let [ch (read-char rdr)]
@@ -114,6 +115,7 @@
(set! prev line-start?)
(set! line-start? (newline? ch))
(when line-start?
+ (set! prev-column column)
(set! column 0)
(update! line inc))
(update! column inc)
@@ -124,9 +126,11 @@
IPushbackReader
(unread [reader ch]
- (when line-start? (update! line dec))
+ (if line-start?
+ (do (update! line dec)
+ (set! column prev-column))
+ (update! column dec))
(set! line-start? prev)
- (update! column dec)
(unread rdr ch))
IndexingReader
@@ -207,7 +211,7 @@
(indexing-push-back-reader s-or-rdr buf-len nil))
([s-or-rdr buf-len file-name]
(IndexingPushbackReader.
- (if (string? s-or-rdr) (string-push-back-reader s-or-rdr buf-len) s-or-rdr) 0 1 true nil file-name)))
+ (if (string? s-or-rdr) (string-push-back-reader s-or-rdr buf-len) s-or-rdr) 0 1 true nil 0 file-name)))
(defn read-line
"Reads a line from the reader or from *in* if no reader is specified"
View
66 src/test/clojure/clojure/tools/metadata_test.clj
@@ -0,0 +1,66 @@
+(ns clojure.tools.metadata-test
+ (:refer-clojure :exclude [read *default-data-reader-fn*])
+ (:use [clojure.tools.reader :only [read *default-data-reader-fn*]]
+ [clojure.test :only [deftest is]])
+ (:require [clojure.tools.reader.reader-types :as reader-types]
+ [clojure.walk :as walk])
+ (:import java.nio.charset.Charset
+ (java.io StringReader)
+ clojure.lang.LineNumberingPushbackReader))
+
+(def test-contents
+ "Contents of a file stream for testing."
+ "(ns clojure.tools.reader.haiku)\n\n(defn haiku
+ \"It will read the form
+ but will the form metadata be
+ or never become?\"
+ [first-five middle-seven last-five]
+ (- (apply +
+ ^{:last last-five} [1 2 3])
+ first-five middle-seven))")
+
+(defn test-reader
+ "Return a fresh byte array input stream reading off test-bytes"
+ []
+ (StringReader. test-contents))
+
+(def expected-haiku-ns
+ (with-meta '(^{:line 1 :column 2 :end-line 1 :end-column 4} ns
+ ^{:line 1 :column 5 :end-line 1 :end-column 31} clojure.tools.reader.haiku)
+ {:line 1 :column 1 :end-line 1 :end-column 32}))
+
+(def expected-haiku-defn
+ (with-meta (list
+ '^{:line 3 :column 2 :end-line 3 :end-column 6} defn
+ '^{:line 3 :column 7 :end-line 3 :end-column 12} haiku
+ "It will read the form\n but will the form metadata be\n or never become?"
+ (with-meta ['^{:line 7 :column 6 :end-line 7 :end-column 16} first-five
+ '^{:line 7 :column 17 :end-line 7 :end-column 29} middle-seven
+ '^{:line 7 :column 30 :end-line 7 :end-column 39} last-five]
+ {:line 7 :column 5 :end-line 7 :end-column 40})
+ (with-meta (list '^{:line 8 :column 6 :end-line 8, :end-column 7} -
+ (with-meta (list '^{:line 8 :column 9 :end-line 8 :end-column 14} apply
+ '^{:line 8 :column 15 :end-line 8 :end-column 16} +
+ ^{:last 'last-five :line 9 :column 34 :end-line 9 :end-column 41}
+ [1 2 3])
+ {:line 8 :column 8 :end-line 9 :end-column 42})
+ '^{:line 10 :column 8 :end-line 10 :end-column 18} first-five
+ '^{:line 10 :column 19 :end-line 10 :end-column 31} middle-seven)
+ {:line 8 :column 5 :end-line 10 :end-column 32}))
+ {:line 3 :column 1 :end-line 10 :end-column 33}))
+
+(deftest read-metadata
+ (let [reader (-> (test-reader)
+ (LineNumberingPushbackReader.)
+ (reader-types/indexing-push-back-reader 1 "haiku.clj"))
+ first-form (read reader)
+ second-form (read reader)]
+ (is (= {:line 1 :column 1 :end-line 1 :end-column 32} (meta first-form)))
+ (let [comparisons (map vector (tree-seq coll? identity expected-haiku-ns)
+ (tree-seq coll? identity first-form))]
+ (doseq [[expected actual] comparisons]
+ (is (= [expected (meta expected)] [actual (meta actual)]))))
+ (let [comparisons (map vector (tree-seq coll? identity expected-haiku-defn)
+ (tree-seq coll? identity second-form))]
+ (doseq [[expected actual] comparisons]
+ (is (= [expected (meta expected)] [actual (meta actual)]))))))
Please sign in to comment.
Something went wrong with that request. Please try again.