├── .gitignore ├── README.md ├── doc └── intro.md ├── project.clj ├── src └── codn │ ├── parser │ ├── ExceptionInfo.clj │ ├── commons.clj │ ├── core.clj │ ├── reader_types.clj │ └── utils.clj │ └── reader │ └── core.clj ├── target ├── classes │ └── codn │ │ ├── parse │ │ ├── ExceptionInfo$_getData.class │ │ ├── ExceptionInfo$_init.class │ │ ├── ExceptionInfo$_toString.class │ │ ├── ExceptionInfo$fn__14.class │ │ ├── ExceptionInfo$loading__4916__auto__.class │ │ ├── ExceptionInfo.class │ │ └── ExceptionInfo__init.class │ │ └── parser │ │ ├── ExceptionInfo$_getData.class │ │ ├── ExceptionInfo$_init.class │ │ ├── ExceptionInfo$_toString.class │ │ ├── ExceptionInfo$fn__92.class │ │ ├── ExceptionInfo$loading__4916__auto__.class │ │ ├── ExceptionInfo.class │ │ └── ExceptionInfo__init.class ├── repl-port └── stale │ └── extract-native.dependencies └── test └── codn ├── parser ├── common_tests.clj ├── reader_edn_test.clj └── reader_test.clj └── reader ├── common_tests.clj ├── reader_edn_test.clj └── reader_test.clj /.gitignore: -------------------------------------------------------------------------------- 1 | pom.xml 2 | *jar 3 | /lib/ 4 | /classes/ 5 | /targets/ 6 | .lein-deps-sum 7 | .lein-failures 8 | .lein-repl-history 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | codn 2 | ==== 3 | 4 | clojure source code as EDN data 5 | 6 | notes: 7 | - this is WIP in both design and implementation, but passes all the tests inherited from tools.reader 8 | - the current implementation is frankenstein-ed off of clojure.tools.reader (Thanks!) and neither elegant or consistent 9 | - look in the test/codn/parser directory for more examples 10 | 11 | ## Rationale 12 | 13 | Clojure's built-in readers both parse source code, and interpret reader macros such as ` (syntax-quote). 14 | 15 | This means the result of read is a) a derivative of the source code, rather than a literal representation, and b) tied to the environment in which the read is performed. 16 | 17 | These two characteristics of clojure's readers make them unsuitable for tasks such as source code analysis, and dynamic code distribution in distributed systems. 18 | 19 | Codn (COde as eDN) transliterates clojure source code into a strict EDN subset, allowing the source code to be treated as pure data. 20 | 21 | ### Objectives 22 | 23 | The objectives of codn are: 24 | 25 | - represent clojure source code as pure EDN data 26 | - provide a basis for source code analysis 27 | - provide a basis for implementing sub-codeqs in codeq 28 | - provide a basis for multipass analysis and compilation 29 | - provide a safe, analyzable basis for code exchange in distributed systems 30 | - cleanly interoperate with term rewriting / tree transformation libraries 31 | 32 | It is a non-objective of codn to provide isomorphic representation of textual documents containing source code. While codn is potentially useful for IDEs and editors, the goal is not to represent the document containing the program, but the program itself. 33 | 34 | ## Usage 35 | 36 | Codn provides facilities for parsing source code into the codn format, and for reading codn data into their corresponding runtime structures. 37 | 38 | Add codn to your project dependencies: 39 | 40 | [codn/codn "0.1.0-SNAPSHOT"] 41 | 42 | ### Parsing 43 | 44 | load parser namespace: 45 | 46 | (require '[codn.parser.core :as p]) 47 | 48 | Primitives return a form {:head primitive-type-keyword :value primitive-value} 49 | 50 | (p/parse-string "a") 51 | => {:head :symbol, :value a} 52 | 53 | Compound structures return a form {:head compound-type-keyword :body [components..]} 54 | 55 | (p/parse-string "[a]") 56 | => {:head :vector, :body [{:head :symbol, :value a}]} 57 | 58 | (p/parse-string "(deref a)") 59 | => {:head :list, :body [{:head :symbol, :value deref} {:head :symbol, :value a}]} 60 | 61 | Reader macros and other non-EDN syntaxes are parsed into compound structures, differentiated by their :head : 62 | 63 | (p/parse-string "`a") 64 | => {:head :syntax-quote, :body [{:head :symbol, :value a}]} 65 | 66 | (p/parse-string "@a") 67 | => {:head :deref, :body [{:head :symbol, :value a}]} 68 | 69 | (p/parse-string "#foo.bar[1]") 70 | => {:head :constructor, :body [{:head :symbol, :value foo.bar} {:head :vector, :body [{:head :integer, :value 1}]}]} 71 | 72 | ### Reading 73 | 74 | load reader namespace: 75 | 76 | (require '[codn.reader.core :as r]) 77 | 78 | (r/read-codn '{:head :vector, :body [{:head :symbol, :value a}]}) 79 | => [a] 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | -------------------------------------------------------------------------------- /doc/intro.md: -------------------------------------------------------------------------------- 1 | # Introduction to codn 2 | 3 | TODO: write [great documentation](http://jacobian.org/writing/great-documentation/what-to-write/) 4 | -------------------------------------------------------------------------------- /project.clj: -------------------------------------------------------------------------------- 1 | (defproject codn "0.1.0-SNAPSHOT" 2 | :description "Clojure source code as edn data" 3 | :url "https://github.com/kovasb/codn" 4 | :license {:name "Eclipse Public License" 5 | :url "http://www.eclipse.org/legal/epl-v10.html"} 6 | :source-paths ["src"] 7 | :test-paths ["test"] 8 | :aot [codn.parser.ExceptionInfo] 9 | :repositories {"sonatype-oss-public" "https://oss.sonatype.org/content/groups/public/"} 10 | :dependencies [[org.clojure/clojure "1.6.0-master-SNAPSHOT"]] 11 | :profiles {:1.3 {:dependencies [[org.clojure/clojure "1.3.0"]]} 12 | :1.4 {:dependencies [[org.clojure/clojure "1.4.0"]]} 13 | :1.5 {:dependencies [[org.clojure/clojure "1.5.1"]]} 14 | :1.6 {:dependencies [[org.clojure/clojure "1.6.0-master-SNAPSHOT"]]}} 15 | :aliases {"test-all" ["with-profile" "test,1.3:test,1.4:test,1.5:test,1.6" "test"] 16 | "check-all" ["with-profile" "1.3:1.4:1.5:1.6" "check"]} 17 | :min-lein-version "2.0.0") 18 | -------------------------------------------------------------------------------- /src/codn/parser/ExceptionInfo.clj: -------------------------------------------------------------------------------- 1 | (ns codn.parser.ExceptionInfo 2 | (:gen-class :extends java.lang.Exception 3 | :init init 4 | :constructors {[String clojure.lang.IPersistentMap] [String] 5 | [String clojure.lang.IPersistentMap Throwable] [String Throwable]} 6 | :state data 7 | :methods [[getData [] clojure.lang.IPersistentMap]])) 8 | 9 | (defn -init 10 | ([s data] 11 | [[s] data]) 12 | ([s data throwable] 13 | [[s throwable] data])) 14 | 15 | (defn -getData [^codn.parse.ExceptionInfo this] 16 | (.data this)) 17 | 18 | (defn -toString [^codn.parse.ExceptionInfo this] 19 | (str "codn.parse.ExceptionInfo: " (.getMessage this) " " (str (.data this)))) 20 | -------------------------------------------------------------------------------- /src/codn/parser/commons.clj: -------------------------------------------------------------------------------- 1 | (ns codn.parser.commons 2 | (:refer-clojure :exclude [char read-line]) 3 | (:use codn.parser.reader-types 4 | codn.parser.utils) 5 | (:import (clojure.lang BigInt Numbers) 6 | (java.util regex.Pattern regex.Matcher) 7 | java.lang.reflect.Constructor)) 8 | 9 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 10 | ;; helpers 11 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 12 | 13 | (defn number-literal? 14 | "Checks whether the reader is at the start of a number literal" 15 | [reader initch] 16 | (or (numeric? initch) 17 | (and (or (identical? \+ initch) (identical? \- initch)) 18 | (numeric? (peek-char reader))))) 19 | 20 | (defn read-past 21 | "Read until first character that doesn't match pred, returning 22 | char." 23 | [pred rdr] 24 | (loop [ch (read-char rdr)] 25 | (if (pred ch) 26 | (recur (read-char rdr)) 27 | ch))) 28 | 29 | (defn skip-line 30 | "Advances the reader to the end of a line. Returns the reader" 31 | [reader _] 32 | (loop [] 33 | (when-not (newline? (read-char reader)) 34 | (recur))) 35 | reader) 36 | 37 | (def ^Pattern int-pattern #"([-+]?)(?:(0)|([1-9][0-9]*)|0[xX]([0-9A-Fa-f]+)|0([0-7]+)|([1-9][0-9]?)[rR]([0-9A-Za-z]+)|0[0-9]+)(N)?") 38 | (def ^Pattern ratio-pattern #"([-+]?[0-9]+)/([0-9]+)") 39 | (def ^Pattern float-pattern #"([-+]?[0-9]+(\.[0-9]*)?([eE][-+]?[0-9]+)?)(M)?") 40 | 41 | (defn- match-int 42 | [^Matcher m] 43 | (if (.group m 2) 44 | (if (.group m 8) 0N 0) 45 | (let [negate? (= "-" (.group m 1)) 46 | a (cond 47 | (.group m 3) [(.group m 3) 10] 48 | (.group m 4) [(.group m 4) 16] 49 | (.group m 5) [(.group m 5) 8] 50 | (.group m 7) [(.group m 7) (Integer/parseInt (.group m 6))] 51 | :else [nil nil]) 52 | ^String n (a 0) 53 | radix (int (a 1))] 54 | (when n 55 | (let [bn (BigInteger. n radix) 56 | bn (if negate? (.negate bn) bn)] 57 | (if (.group m 8) 58 | (BigInt/fromBigInteger bn) 59 | (if (< (.bitLength bn) 64) 60 | (.longValue bn) 61 | (BigInt/fromBigInteger bn)))))))) 62 | 63 | (defn- match-ratio 64 | [^Matcher m] 65 | (let [^String numerator (.group m 1) 66 | ^String denominator (.group m 2) 67 | numerator (if (.startsWith numerator "+") 68 | (subs numerator 1) 69 | numerator)] 70 | (/ (-> numerator BigInteger. BigInt/fromBigInteger Numbers/reduceBigInt) 71 | (-> denominator BigInteger. BigInt/fromBigInteger Numbers/reduceBigInt)))) 72 | 73 | (defn- match-float 74 | [^String s ^Matcher m] 75 | (if (.group m 4) 76 | (BigDecimal. ^String (.group m 1)) 77 | (Double/parseDouble s))) 78 | 79 | (defn match-number [^String s] 80 | (let [int-matcher (.matcher int-pattern s)] 81 | (if (.matches int-matcher) 82 | (match-int int-matcher) 83 | (let [float-matcher (.matcher float-pattern s)] 84 | (if (.matches float-matcher) 85 | (match-float s float-matcher) 86 | (let [ratio-matcher (.matcher ratio-pattern s)] 87 | (when (.matches ratio-matcher) 88 | (match-ratio ratio-matcher)))))))) 89 | 90 | (defn parse-symbol [^String token] 91 | (when-not (or (identical? "" token) 92 | (not= -1 (.indexOf token "::"))) 93 | (let [ns-idx (.indexOf token "/")] 94 | (if-let [ns (and (pos? ns-idx) 95 | (subs token 0 ns-idx))] 96 | (let [ns-idx (inc ns-idx)] 97 | (when-not (== ns-idx (count token)) 98 | (let [sym (subs token ns-idx)] 99 | (when (and (not (numeric? (nth sym 0))) 100 | (not (identical? "" sym)) 101 | (or (= sym "/") 102 | (== -1 (.indexOf sym "/")))) 103 | [ns sym])))) 104 | (when (or (= token "/") 105 | (== -1 (.indexOf token "/"))) 106 | [nil token]))))) 107 | 108 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 109 | ;; readers 110 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 111 | 112 | (defn read-comment 113 | [rdr & _] 114 | (skip-line rdr _)) 115 | 116 | (defn throwing-reader 117 | [msg] 118 | (fn [rdr & _] 119 | (reader-error rdr msg))) 120 | 121 | (defn read-regex 122 | [rdr ch] 123 | (let [sb (StringBuilder.)] 124 | (loop [ch (read-char rdr)] 125 | (if (identical? \" ch) 126 | (Pattern/compile (str sb)) 127 | (if (nil? ch) 128 | (reader-error rdr "EOF while reading regex") 129 | (do 130 | (.append sb ch ) 131 | (when (identical? \\ ch) 132 | (let [ch (read-char rdr)] 133 | (if (nil? ch) 134 | (reader-error rdr "EOF while reading regex")) 135 | (.append sb ch))) 136 | (recur (read-char rdr)))))))) 137 | -------------------------------------------------------------------------------- /src/codn/parser/core.clj: -------------------------------------------------------------------------------- 1 | (ns ^{:doc "A clojure reader in clojure" 2 | :author "Bronsa"} 3 | codn.parser.core 4 | (:refer-clojure :exclude [read read-line read-string char 5 | default-data-readers *default-data-reader-fn* 6 | *read-eval* *data-readers*]) 7 | (:use codn.parser.reader-types 8 | [codn.parser utils commons]) 9 | (:import (clojure.lang PersistentHashSet IMeta 10 | RT Symbol Reflector Var IObj 11 | PersistentVector IRecord Namespace) 12 | java.lang.reflect.Constructor)) 13 | 14 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 15 | ;; helpers 16 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 17 | 18 | (declare parse macros dispatch-macros 19 | ^:dynamic *read-eval* 20 | ^:dynamic *data-readers* 21 | ^:dynamic *default-data-reader-fn* 22 | default-data-readers) 23 | 24 | (defn- wrapping-reader2 25 | [sym] 26 | (fn [rdr _] 27 | {:head sym :body [(parse rdr true nil true)]})) 28 | 29 | 30 | (defn- macro-terminating? [ch] 31 | (case ch 32 | (\" \; \@ \^ \` \~ \( \) \[ \] \{ \} \\) true 33 | false)) 34 | 35 | (defn- ^String read-token 36 | [rdr initch] 37 | (if-not initch 38 | (reader-error rdr "EOF while reading") 39 | (loop [sb (doto (StringBuilder.) (.append initch)) 40 | ch (read-char rdr)] 41 | (if (or (whitespace? ch) 42 | (macro-terminating? ch) 43 | (nil? ch)) 44 | (do (unread rdr ch) 45 | (str sb)) 46 | (recur (.append sb ch) (read-char rdr)))))) 47 | 48 | (declare read-tagged) 49 | 50 | (defn- read-dispatch 51 | [rdr _] 52 | (if-let [ch (read-char rdr)] 53 | (if-let [dm (dispatch-macros ch)] 54 | (dm rdr ch) 55 | (if-let [obj (read-tagged (doto rdr (unread ch)) ch)] ;; ctor reader is implemented as a taggged literal 56 | obj 57 | (reader-error rdr "No dispatch macro for " ch))) 58 | (reader-error rdr "EOF while reading character"))) 59 | 60 | (defn- read-unmatched-delimiter 61 | [rdr ch] 62 | (reader-error rdr "Unmatched delimiter " ch)) 63 | 64 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 65 | ;; readers 66 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 67 | 68 | (defn- read-unicode-char 69 | ([^String token offset length base] 70 | (let [l (+ offset length)] 71 | (when-not (== (count token) l) 72 | (throw (IllegalArgumentException. (str "Invalid unicode character: \\" token)))) 73 | (loop [i offset uc 0] 74 | (if (== i l) 75 | (char uc) 76 | (let [d (Character/digit (int (nth token i)) (int base))] 77 | (if (== d -1) 78 | (throw (IllegalArgumentException. (str "Invalid digit: " (nth token i)))) 79 | (recur (inc i) (long (+ d (* uc base)))))))))) 80 | 81 | ([rdr initch base length exact?] 82 | (loop [i 1 uc (Character/digit (int initch) (int base))] 83 | (if (== uc -1) 84 | (throw (IllegalArgumentException. (str "Invalid digit: " initch))) 85 | (if-not (== i length) 86 | (let [ch (peek-char rdr)] 87 | (if (or (whitespace? ch) 88 | (macros ch) 89 | (nil? ch)) 90 | (if exact? 91 | (throw (IllegalArgumentException. 92 | (str "Invalid character length: " i ", should be: " length))) 93 | (char uc)) 94 | (let [d (Character/digit (int ch) (int base))] 95 | (read-char rdr) 96 | (if (== d -1) 97 | (throw (IllegalArgumentException. (str "Invalid digit: " ch))) 98 | (recur (inc i) (long (+ d (* uc base)))))))) 99 | (char uc)))))) 100 | 101 | (def ^:private ^:const upper-limit (int \uD7ff)) 102 | (def ^:private ^:const lower-limit (int \uE000)) 103 | 104 | (defn- read-char* 105 | [rdr backslash] 106 | (let [ch (read-char rdr)] 107 | (if-not (nil? ch) 108 | (let [token (read-token rdr ch) 109 | token-len (count token)] 110 | (let [result 111 | (cond 112 | 113 | (== 1 token-len) (Character/valueOf (nth token 0)) 114 | 115 | (= token "newline") \newline 116 | (= token "space") \space 117 | (= token "tab") \tab 118 | (= token "backspace") \backspace 119 | (= token "formfeed") \formfeed 120 | (= token "return") \return 121 | 122 | (.startsWith token "u") 123 | (let [c (read-unicode-char token 1 4 16) 124 | ic (int c)] 125 | (if (and (> ic upper-limit) 126 | (< ic lower-limit)) 127 | (reader-error rdr "Invalid character constant: \\u" (Integer/toString ic 16)) 128 | c)) 129 | 130 | (.startsWith token "x") 131 | (read-unicode-char token 1 2 16) 132 | 133 | (.startsWith token "o") 134 | (let [len (dec token-len)] 135 | (if (> len 3) 136 | (reader-error rdr "Invalid octal escape sequence length: " len) 137 | (let [uc (read-unicode-char token 1 len 8)] 138 | (if (> (int uc) 0377) 139 | (reader-error rdr "Octal escape sequence must be in range [0, 377]") 140 | uc)))) 141 | 142 | :else (reader-error rdr "Unsupported character: \\" token))] 143 | {:head :character :value result} 144 | )) 145 | (reader-error rdr "EOF while reading character")))) 146 | 147 | (defn- ^PersistentVector read-delimited 148 | [delim rdr recursive?] 149 | (let [first-line (when (indexing-reader? rdr) 150 | (get-line-number rdr)) 151 | delim (char delim)] 152 | (loop [a (transient [])] 153 | (if-let [ch (read-past whitespace? rdr)] 154 | (if (identical? delim (char ch)) 155 | (persistent! a) 156 | (if-let [macrofn (macros ch)] 157 | (let [mret (macrofn rdr ch)] 158 | (recur (if-not (identical? mret rdr) (conj! a mret) a))) 159 | (let [o (parse (doto rdr (unread ch)) true nil recursive?)] 160 | (recur (if-not (identical? o rdr) (conj! a o) a))))) 161 | (reader-error rdr "EOF while reading" 162 | (when first-line 163 | (str ", starting at line" first-line))))))) 164 | 165 | (defn- read-list 166 | [rdr _] 167 | (let [[line column] (when (indexing-reader? rdr) 168 | [(get-line-number rdr) (int (dec (get-column-number rdr)))]) 169 | the-list (read-delimited \) rdr true)] 170 | (if (empty? the-list) 171 | {:head :list :body []} ;; changed 172 | (with-meta 173 | {:head :list :body (vec the-list)} ;; changed 174 | (when line 175 | {:line line :column column}))))) 176 | 177 | (defn- read-vector 178 | [rdr _] 179 | (let [[line column] (when (indexing-reader? rdr) 180 | [(get-line-number rdr) (int (dec (get-column-number rdr)))]) 181 | the-vector (read-delimited \] rdr true)] 182 | (with-meta {:head :vector :body the-vector} ;; changed 183 | (when line 184 | {:line line :column column})))) 185 | 186 | (defn- read-map 187 | [rdr _] 188 | (let [[line column] (when (indexing-reader? rdr) 189 | [(get-line-number rdr) (int (dec (get-column-number rdr)))]) 190 | the-map (read-delimited \} rdr true) 191 | map-count (count the-map)] 192 | (when (odd? map-count) 193 | (reader-error rdr "Map literal must contain an even number of forms")) 194 | (with-meta 195 | (if (zero? map-count) 196 | {:head :map :body []} 197 | {:head :map :body (vec the-map)}) ;; changed 198 | (when line 199 | {:line line :column column})))) 200 | 201 | (defn- read-number 202 | [reader initch] 203 | (loop [sb (doto (StringBuilder.) (.append initch)) 204 | ch (read-char reader)] 205 | (if (or (whitespace? ch) (macros ch) (nil? ch)) 206 | (let [s (str sb)] 207 | (unread reader ch) 208 | (let [n (match-number s)] 209 | (if n 210 | (cond 211 | ;;(decimal? n) {:head :decimal :value n} 212 | (or (decimal? n) (float? n)) {:head :float :value n} 213 | (integer? n) {:head :integer :value n} 214 | (ratio? n) {:head :ratio :body [{:head :integer :value (numerator n)} {:head :integer :value (denominator n)}]} 215 | ) 216 | (reader-error reader "Invalid number format [" s "]")))) 217 | (recur (doto sb (.append ch)) (read-char reader))))) 218 | 219 | (defn- escape-char [sb rdr] 220 | (let [ch (read-char rdr)] 221 | (case ch 222 | \t "\t" 223 | \r "\r" 224 | \n "\n" 225 | \\ "\\" 226 | \" "\"" 227 | \b "\b" 228 | \f "\f" 229 | \u (let [ch (read-char rdr)] 230 | (if (== -1 (Character/digit (int ch) 16)) 231 | (reader-error rdr "Invalid unicode escape: \\u" ch) 232 | (read-unicode-char rdr ch 16 4 true))) 233 | \x (let [ch (read-char rdr)] 234 | (if (== -1 (Character/digit (int ch) 16)) 235 | (reader-error rdr "Invalid unicode escape: \\x" ch) 236 | (read-unicode-char rdr ch 16 2 true))) 237 | (if (numeric? ch) 238 | (let [ch (read-unicode-char rdr ch 8 3 false)] 239 | (if (> (int ch) 0337) 240 | (reader-error rdr "Octal escape sequence must be in range [0, 377]") 241 | ch)) 242 | (reader-error rdr "Unsupported escape character: \\" ch))))) 243 | 244 | (defn- read-string* 245 | [reader _] 246 | (loop [sb (StringBuilder.) 247 | ch (read-char reader)] 248 | (case ch 249 | nil (reader-error reader "EOF while reading string") 250 | \\ (recur (doto sb (.append (escape-char sb reader))) 251 | (read-char reader)) 252 | \" {:head :string :value (str sb)} 253 | (recur (doto sb (.append ch)) (read-char reader))))) 254 | 255 | (defn- read-symbol 256 | [rdr initch] 257 | (when-let [token (read-token rdr initch)] 258 | (let [[line column] (when (indexing-reader? rdr) 259 | [(get-line-number rdr) (int (dec (get-column-number rdr)))])] 260 | (case token 261 | 262 | ;; special symbols 263 | "nil" {:head :nil :value nil} 264 | "true" {:head :boolean :value true} 265 | "false" {:head :boolean :value false} 266 | "/" {:head :symbol :value '/} 267 | "NaN" {:head :NaN :value Double/NaN} 268 | "-Infinity" {:head :negative-infinity :value Double/NEGATIVE_INFINITY} 269 | ("Infinity" "+Infinity") {:head :positive-infinity :value Double/POSITIVE_INFINITY} 270 | 271 | (or (when-let [p (parse-symbol token)] 272 | (with-meta {:head :symbol :value (symbol (p 0) (p 1))} 273 | (when line 274 | {:line line :column column}))) 275 | (reader-error rdr "Invalid token: " token)))))) 276 | 277 | (defn- resolve-ns [sym] 278 | (or ((ns-aliases *ns*) sym) 279 | (find-ns sym))) 280 | 281 | 282 | (defn- read-keyword 283 | [reader initch] 284 | (let [ch (read-char reader)] 285 | (if-not (whitespace? ch) 286 | (let [token (read-token reader ch) 287 | s (parse-symbol token)] 288 | (if s 289 | (let [^String ns (s 0) 290 | ^String name (s 1)] 291 | (if (identical? \: (nth token 0)) 292 | (if ns 293 | {:head :autoresolved-keyword :body [{:head :keyword :value (keyword (subs ns 1) name)}] } 294 | {:head :autoresolved-keyword :body [{:head :keyword :value (keyword (subs name 1))}]}) 295 | {:head :keyword :value (keyword ns name)})) 296 | (reader-error reader "Invalid token: :" token))) 297 | (reader-error reader "Invalid token: :")))) 298 | 299 | (defn- wrapping-reader 300 | [sym] 301 | (fn [rdr _] 302 | (list sym (parse rdr true nil true)))) 303 | 304 | (defn- read-meta 305 | [rdr _] 306 | (let [[line column] (when (indexing-reader? rdr) 307 | [(get-line-number rdr) (int (dec (get-column-number rdr)))]) 308 | m (desugar-meta (parse rdr true nil true))] 309 | (when-not (map? m) 310 | (reader-error rdr "Metadata must be Symbol, Keyword, String or Map")) 311 | (let [o (parse rdr true nil true)] 312 | (if (instance? IMeta o) 313 | (let [m (if (and line 314 | (seq? o)) 315 | (assoc m :line line 316 | :column column) 317 | m)] 318 | (if (instance? IObj o) 319 | {:head :meta :body [m o]} 320 | {:head :meta :body [m o]})) 321 | {:head :meta :body [m o]})))) 322 | 323 | 324 | (defn- read-set 325 | [rdr _] 326 | {:head :set :body (vec (read-delimited \} rdr true))} 327 | ) 328 | 329 | (defn- read-discard 330 | [rdr _] 331 | (parse rdr true nil true) 332 | rdr) 333 | 334 | (def ^:private ^:dynamic arg-env) 335 | 336 | 337 | (defn- read-fn 338 | [rdr _] 339 | (if (thread-bound? #'arg-env) 340 | (throw (IllegalStateException. "Nested #()s are not allowed"))) 341 | (binding [arg-env (sorted-map)] 342 | (let [form (parse (doto rdr (unread \()) true nil true)] 343 | {:head :fn :body [form]}))) 344 | 345 | 346 | ;; should never hit this 347 | 348 | (declare read-symbol) 349 | 350 | 351 | (defn- read-unquote 352 | [rdr comma] 353 | (if-let [ch (peek-char rdr)] 354 | (if (identical? \@ ch) 355 | ((wrapping-reader2 :unquote-splicing) (doto rdr read-char) \@) 356 | ((wrapping-reader2 :unquote) rdr \~)))) 357 | 358 | 359 | (defn- macros [ch] 360 | (case ch 361 | \" read-string* 362 | \: read-keyword 363 | \; read-comment 364 | \' (wrapping-reader2 :quote) 365 | \@ (wrapping-reader2 :deref) 366 | \^ read-meta 367 | \` (wrapping-reader2 :syntax-quote) 368 | \~ read-unquote 369 | \( read-list 370 | \) read-unmatched-delimiter 371 | \[ read-vector 372 | \] read-unmatched-delimiter 373 | \{ read-map 374 | \} read-unmatched-delimiter 375 | \\ read-char* 376 | \# read-dispatch 377 | nil)) 378 | 379 | 380 | (defn read-regex2 381 | [rdr ch] 382 | (let [sb (StringBuilder.)] 383 | (loop [ch (read-char rdr)] 384 | (if (identical? \" ch) 385 | {:head :regex :body [(str sb)]} 386 | (if (nil? ch) 387 | (reader-error rdr "EOF while reading regex") 388 | (do 389 | (.append sb ch ) 390 | (when (identical? \\ ch) 391 | (let [ch (read-char rdr)] 392 | (if (nil? ch) 393 | (reader-error rdr "EOF while reading regex")) 394 | (.append sb ch))) 395 | (recur (read-char rdr)))))))) 396 | 397 | (defn- dispatch-macros [ch] 398 | (case ch 399 | \^ read-meta ;deprecated 400 | \' (wrapping-reader2 :var-quote) 401 | \( read-fn 402 | \= (wrapping-reader2 :read-eval) ;; read-eval 403 | \{ read-set 404 | \< (throwing-reader "Unreadable form") 405 | \" read-regex2 406 | \! read-comment 407 | \_ read-discard 408 | nil)) 409 | 410 | 411 | (defn- read-tagged [rdr initch] 412 | (let [tag (parse rdr true nil false)] 413 | (if-not (symbol? (:value tag)) 414 | (reader-error rdr "Reader tag must be a symbol")) 415 | (if (.contains (name (:value tag)) ".") 416 | {:head :constructor :body [tag (parse rdr true nil true)]} 417 | {:head :tagged-literal :body [tag (parse rdr true nil true)]}))) 418 | 419 | 420 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 421 | ;; Public API 422 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 423 | 424 | 425 | (defn parse 426 | "Parses the first object from an IPushbackReader or a java.io.PushbackReader." 427 | ([] (parse *in*)) 428 | ([reader] (parse reader true nil)) 429 | ([reader eof-error? sentinel] (parse reader eof-error? sentinel false)) 430 | ([reader eof-error? sentinel recursive?] 431 | (try 432 | (let [ch (read-char reader)] 433 | (cond 434 | (whitespace? ch) (parse reader eof-error? sentinel recursive?) 435 | (nil? ch) (if eof-error? (reader-error reader "EOF") sentinel) 436 | (number-literal? reader ch) (read-number reader ch) 437 | (comment-prefix? ch) (parse (read-comment reader ch) eof-error? sentinel recursive?) 438 | :else (let [f (macros ch)] 439 | (if f 440 | (let [res (f reader ch)] 441 | (if (identical? res reader) 442 | (parse reader eof-error? sentinel recursive?) 443 | res)) 444 | (read-symbol reader ch))))) 445 | (catch Exception e 446 | (if (ex-info? e) 447 | (throw e) 448 | (throw (ex-info (.getMessage e) 449 | (merge {:type :reader-exception} 450 | (if (indexing-reader? reader) 451 | {:line (get-line-number reader) 452 | :column (get-column-number reader)})) 453 | e))))))) 454 | 455 | (defn parse-string 456 | "Parses one object from the string s." 457 | [s] 458 | (when (and s (not (identical? s ""))) 459 | (parse (string-push-back-reader s) true nil false))) 460 | -------------------------------------------------------------------------------- /src/codn/parser/reader_types.clj: -------------------------------------------------------------------------------- 1 | (ns 2 | codn.parser.reader-types 3 | (:refer-clojure :exclude [char read-line]) 4 | (:use codn.parser.utils) 5 | (:import clojure.lang.LineNumberingPushbackReader 6 | (java.io InputStream BufferedReader))) 7 | 8 | (defmacro ^:private update! [what f] 9 | (list 'set! what (list f what))) 10 | 11 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 12 | ;; reader protocols 13 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 14 | 15 | (defprotocol Reader 16 | (read-char [reader] 17 | "Returns the next char from the Reader, nil if the end of stream has been reached") 18 | (peek-char [reader] 19 | "Returns the next char from the Reader without removing it from the reader stream")) 20 | 21 | (defprotocol IPushbackReader 22 | (unread [reader ch] 23 | "Pushes back a single character on to the stream")) 24 | 25 | (defprotocol IndexingReader 26 | (get-line-number [reader] 27 | "Returns the line number of the next character to be read from the stream") 28 | (get-column-number [reader] 29 | "Returns the line number of the next character to be read from the stream")) 30 | 31 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 32 | ;; reader deftypes 33 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 34 | 35 | (deftype StringReader 36 | [^String s s-len ^:unsynchronized-mutable s-pos] 37 | Reader 38 | (read-char [reader] 39 | (when (> s-len s-pos) 40 | (let [r (nth s s-pos)] 41 | (update! s-pos inc) 42 | r))) 43 | (peek-char [reader] 44 | (when (> s-len s-pos) 45 | (nth s s-pos)))) 46 | 47 | (deftype InputStreamReader [^InputStream is ^:unsynchronized-mutable ^"[B" buf] 48 | Reader 49 | (read-char [reader] 50 | (if buf 51 | (let [c (aget buf 0)] 52 | (set! buf nil) 53 | (char c)) 54 | (let [c (.read is)] 55 | (when (>= c 0) 56 | (char c))))) 57 | (peek-char [reader] 58 | (when-not buf 59 | (set! buf (byte-array 1)) 60 | (when (== -1 (.read is buf)) 61 | (set! buf nil))) 62 | (when buf 63 | (char (aget buf 0))))) 64 | 65 | (deftype PushbackReader 66 | [rdr ^"[Ljava.lang.Object;" buf buf-len ^:unsynchronized-mutable buf-pos] 67 | Reader 68 | (read-char [reader] 69 | (char 70 | (if (< buf-pos buf-len) 71 | (let [r (aget buf buf-pos)] 72 | (update! buf-pos inc) 73 | r) 74 | (read-char rdr)))) 75 | (peek-char [reader] 76 | (char 77 | (if (< buf-pos buf-len) 78 | (aget buf buf-pos) 79 | (peek-char rdr)))) 80 | IPushbackReader 81 | (unread [reader ch] 82 | (when ch 83 | (if (zero? buf-pos) (throw (RuntimeException. "Pushback buffer is full"))) 84 | (update! buf-pos dec) 85 | (aset buf buf-pos ch)))) 86 | 87 | (defn- normalize-newline [rdr ch] 88 | (if (identical? \return ch) 89 | (let [c (peek-char rdr)] 90 | (when (identical? \formfeed c) 91 | (read-char rdr)) 92 | \newline) 93 | ch)) 94 | 95 | (deftype IndexingPushbackReader 96 | [rdr ^:unsynchronized-mutable line ^:unsynchronized-mutable column 97 | ^:unsynchronized-mutable line-start? ^:unsynchronized-mutable prev] 98 | Reader 99 | (read-char [reader] 100 | (when-let [ch (read-char rdr)] 101 | (let [ch (normalize-newline rdr ch)] 102 | (set! prev line-start?) 103 | (set! line-start? (newline? ch)) 104 | (when line-start? 105 | (set! column 0) 106 | (update! line inc)) 107 | (update! column inc) 108 | ch))) 109 | 110 | (peek-char [reader] 111 | (peek-char rdr)) 112 | 113 | IPushbackReader 114 | (unread [reader ch] 115 | (when line-start? (update! line dec)) 116 | (set! line-start? prev) 117 | (update! column dec) 118 | (unread rdr ch)) 119 | 120 | IndexingReader 121 | (get-line-number [reader] (int (inc line))) 122 | (get-column-number [reader] (int column))) 123 | 124 | (extend-type java.io.PushbackReader 125 | Reader 126 | (read-char [rdr] 127 | (let [c (.read ^java.io.PushbackReader rdr)] 128 | (when (>= c 0) 129 | (normalize-newline rdr (char c))))) 130 | 131 | (peek-char [rdr] 132 | (when-let [c (read-char rdr)] 133 | (unread rdr c) 134 | c)) 135 | 136 | IPushbackReader 137 | (unread [rdr c] 138 | (when c 139 | (.unread ^java.io.PushbackReader rdr (int c))))) 140 | 141 | (extend LineNumberingPushbackReader 142 | IndexingReader 143 | {:get-line-number (fn [rdr] (.getLineNumber ^LineNumberingPushbackReader rdr)) 144 | :get-column-number (compile-if >=clojure-1-5-alpha*? 145 | (fn [rdr] 146 | (.getColumnNumber ^LineNumberingPushbackReader rdr)) 147 | (fn [rdr] 0))}) 148 | 149 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 150 | ;; Public API 151 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 152 | 153 | ;; fast check for provided implementations 154 | (defn indexing-reader? 155 | "Returns true if the reader satisfies IndexingReader" 156 | [rdr] 157 | (or (instance? codn.parser.reader_types.IndexingReader rdr) 158 | (instance? LineNumberingPushbackReader rdr) 159 | (and (not (instance? codn.parser.reader_types.PushbackReader rdr)) 160 | (not (instance? codn.parser.reader_types.StringReader rdr)) 161 | (not (instance? codn.parser.reader_types.InputStreamReader rdr)) 162 | (get (:impls IndexingReader) (class rdr))))) 163 | 164 | (defn string-reader 165 | "Creates a StringReader from a given string" 166 | ([^String s] 167 | (StringReader. s (count s) 0))) 168 | 169 | (defn string-push-back-reader 170 | "Creates a PushbackReader from a given string" 171 | ([s] 172 | (string-push-back-reader s 1)) 173 | ([^String s buf-len] 174 | (PushbackReader. (string-reader s) (object-array buf-len) buf-len buf-len))) 175 | 176 | (defn input-stream-reader 177 | "Creates an InputStreamReader from an InputStream" 178 | [is] 179 | (InputStreamReader. is nil)) 180 | 181 | (defn input-stream-push-back-reader 182 | "Creates a PushbackReader from a given InputStream" 183 | ([is] 184 | (input-stream-push-back-reader is 1)) 185 | ([^InputStream is buf-len] 186 | (PushbackReader. (input-stream-reader is) (object-array buf-len) buf-len buf-len))) 187 | 188 | (defn indexing-push-back-reader 189 | "Creates an IndexingPushbackReader from a given string or Reader" 190 | ([s-or-rdr] 191 | (indexing-push-back-reader s-or-rdr 1)) 192 | ([s-or-rdr buf-len] 193 | (IndexingPushbackReader. 194 | (if (string? s-or-rdr) (string-push-back-reader s-or-rdr buf-len) s-or-rdr) 0 1 true nil))) 195 | 196 | (defn read-line 197 | "Reads a line from the reader or from *in* if no reader is specified" 198 | ([] (read-line *in*)) 199 | ([rdr] 200 | (if (or (instance? LineNumberingPushbackReader rdr) 201 | (instance? BufferedReader rdr)) 202 | (clojure.core/read-line rdr) 203 | (loop [c (read-char rdr) s (StringBuilder.)] 204 | (if (newline? c) 205 | (str s) 206 | (recur (read-char rdr) (.append s c))))))) 207 | 208 | (defn reader-error 209 | "Throws an ExceptionInfo with the given message. 210 | If rdr is an IndexingReader, additional information about column and line number is provided" 211 | [rdr & msg] 212 | (throw (ex-info (apply str msg) 213 | (merge {:type :reader-exception} 214 | (when (indexing-reader? rdr) 215 | {:line (get-line-number rdr) 216 | :column (get-column-number rdr)}))))) 217 | -------------------------------------------------------------------------------- /src/codn/parser/utils.clj: -------------------------------------------------------------------------------- 1 | (ns codn.parser.utils 2 | (:require codn.parser.ExceptionInfo) ;; force loading 3 | (:refer-clojure :exclude [char])) 4 | 5 | (defn char [x] 6 | (when x 7 | (clojure.core/char x))) 8 | 9 | ;; getColumnNumber and *default-data-reader-fn* are available only since clojure-1.5.0-beta1 10 | (def >=clojure-1-5-alpha*? 11 | (let [{:keys [minor qualifier]} *clojure-version*] 12 | (or (and (= minor 5) 13 | (not= "alpha" 14 | (when qualifier 15 | (subs qualifier 0 (dec (count qualifier)))))) 16 | (> minor 5)))) 17 | 18 | (defmacro compile-if [cond then else] 19 | (if (eval cond) 20 | then 21 | else)) 22 | 23 | (compile-if (= 3 (:minor *clojure-version*)) 24 | (do 25 | (defn ex-info 26 | ([msg map] 27 | (clojure.tools.reader.impl.ExceptionInfo. msg map)) 28 | ([msg map cause] 29 | (clojure.tools.reader.impl.ExceptionInfo. msg map cause))) 30 | (defn ex-data 31 | [^clojure.tools.reader.impl.ExceptionInfo ex] 32 | (.getData ex)) 33 | (defn ex-info? [ex] 34 | (instance? clojure.tools.reader.impl.ExceptionInfo ex))) 35 | 36 | (defn ex-info? [ex] 37 | (instance? clojure.lang.ExceptionInfo ex))) 38 | 39 | (defn whitespace? 40 | "Checks whether a given character is whitespace" 41 | [ch] 42 | (when ch 43 | (or (Character/isWhitespace ^Character ch) 44 | (identical? \, ch)))) 45 | 46 | (defn numeric? 47 | "Checks whether a given character is numeric" 48 | [^Character ch] 49 | (when ch 50 | (Character/isDigit ch))) 51 | 52 | (defn comment-prefix? 53 | "Checks whether the character begins a comment." 54 | [ch] 55 | (identical? \; ch)) 56 | 57 | (defn newline? [c] 58 | "Checks whether the character is a newline" 59 | (or (identical? \newline c) 60 | (nil? c))) 61 | 62 | (defn desugar-meta 63 | [f] 64 | (cond 65 | (keyword? f) {f true} 66 | (symbol? f) {:tag f} 67 | (string? f) {:tag f} 68 | :else f)) 69 | -------------------------------------------------------------------------------- /src/codn/reader/core.clj: -------------------------------------------------------------------------------- 1 | (ns codn.reader.core 2 | (:refer-clojure :exclude [read read-line read-string char 3 | ]) 4 | ;; (:use clojure.walk) 5 | (:use codn.parser.reader-types 6 | [codn.parser utils commons]) 7 | (:import (clojure.lang PersistentHashSet IMeta 8 | RT Symbol Reflector Var IObj 9 | PersistentVector IRecord Namespace) 10 | java.lang.reflect.Constructor)) 11 | 12 | (defn- resolve-ns [sym] 13 | (or ((ns-aliases *ns*) sym) 14 | (find-ns sym))) 15 | 16 | (defn- garg [n] 17 | (symbol (str (if (== -1 n) "rest" (str "p" n)) 18 | "__" (RT/nextID) "#"))) 19 | 20 | (declare read-symbol) 21 | 22 | (def ^:private ^:dynamic gensym-env nil) 23 | 24 | 25 | (declare syntax-quote*) 26 | 27 | (defn- unquote-splicing? [form] 28 | (and (seq? form) 29 | (= (first form) 'clojure.core/unquote-splicing))) 30 | 31 | (defn- unquote? [form] 32 | (and (seq? form) 33 | (= (first form) 'clojure.core/unquote))) 34 | 35 | (defn- expand-list [s] 36 | (loop [s (seq s) r (transient [])] 37 | (if s 38 | (let [item (first s) 39 | ret (conj! r 40 | (cond 41 | (unquote? item) (list 'clojure.core/list (second item)) 42 | (unquote-splicing? item) (second item) 43 | :else (list 'clojure.core/list (syntax-quote* item))))] 44 | (recur (next s) ret)) 45 | (seq (persistent! r))))) 46 | 47 | 48 | (defn- flatten-map [form] 49 | (loop [s (seq form) key-vals (transient [])] 50 | (if s 51 | (let [e (first s)] 52 | (recur (next s) (-> key-vals 53 | (conj! (key e)) 54 | (conj! (val e))))) 55 | (seq (persistent! key-vals))))) 56 | 57 | (defn- register-gensym [sym] 58 | (if-not gensym-env 59 | (throw (IllegalStateException. "Gensym literal not in syntax-quote"))) 60 | (or (get gensym-env sym) 61 | (let [gs (symbol (str (subs (name sym) 62 | 0 (dec (count (name sym)))) 63 | "__" (RT/nextID) "__auto__"))] 64 | (set! gensym-env (assoc gensym-env sym gs)) 65 | gs))) 66 | 67 | (defn- resolve-symbol [s] 68 | (if (pos? (.indexOf (name s) ".")) 69 | s 70 | (if-let [ns-str (namespace s)] 71 | (let [^Namespace ns (resolve-ns (symbol ns-str))] 72 | (if (or (nil? ns) 73 | (= (name (ns-name ns)) ns-str)) ;; not an alias 74 | s 75 | (symbol (name (.name ns)) (name s)))) 76 | (if-let [o ((ns-map *ns*) s)] 77 | (if (class? o) 78 | (symbol (.getName ^Class o)) 79 | (if (var? o) 80 | (symbol (-> ^Var o .ns .name name) (-> ^Var o .sym name)))) 81 | (symbol (name (ns-name *ns*)) (name s)))))) 82 | 83 | (defn- add-meta [form ret] 84 | (if (and (instance? IObj form) 85 | (dissoc (meta form) :line :column)) 86 | (list 'clojure.core/with-meta ret (syntax-quote* (meta form))) 87 | ret)) 88 | 89 | (defn- syntax-quote-coll [type coll] 90 | (let [res (list 'clojure.core/seq 91 | (cons 'clojure.core/concat 92 | (expand-list coll)))] 93 | (if type 94 | (list 'clojure.core/apply type res) 95 | res))) 96 | 97 | 98 | (defn- syntax-quote? [form] 99 | (and (seq? form) 100 | (= (first form) 'read-syntax-quote*))) 101 | 102 | (declare read-syntax-quote*) 103 | 104 | (defn- syntax-quote* [form] 105 | (->> 106 | (cond 107 | (special-symbol? form) (list 'quote form) 108 | 109 | (symbol? form) 110 | (list 'quote 111 | (if (namespace form) 112 | (let [maybe-class ((ns-map *ns*) 113 | (symbol (namespace form)))] 114 | (if (class? class) 115 | (symbol (.getName ^Class maybe-class) (name form)) 116 | (resolve-symbol form))) 117 | (let [sym (name form)] 118 | (cond 119 | (.endsWith sym "#") 120 | (register-gensym form) 121 | 122 | (.startsWith sym ".") 123 | form 124 | 125 | (.endsWith sym ".") 126 | (let [csym (symbol (subs sym 0 (dec (count sym))))] 127 | (symbol (.concat (name (resolve-symbol csym)) "."))) 128 | :else (resolve-symbol form))))) 129 | 130 | (syntax-quote? form) (read-syntax-quote* (second form)) 131 | (unquote? form) (second form) 132 | (unquote-splicing? form) (throw (IllegalStateException. "splice not in list")) 133 | 134 | (coll? form) 135 | (cond 136 | (instance? IRecord form) form 137 | (map? form) (syntax-quote-coll 'clojure.core/hash-map (flatten-map form)) 138 | (vector? form) (syntax-quote-coll 'clojure.core/vector form) 139 | (set? form) (syntax-quote-coll 'clojure.core/hash-set form) 140 | (or (seq? form) (list? form)) 141 | (let [seq (seq form)] 142 | (if seq 143 | (syntax-quote-coll nil seq) 144 | '(clojure.core/list))) 145 | :else (throw (UnsupportedOperationException. "Unknown Collection type"))) 146 | 147 | (or (keyword? form) 148 | (number? form) 149 | (char? form) 150 | (string? form)) 151 | form 152 | 153 | :else (list 'quote form)) 154 | (add-meta form))) 155 | 156 | (defn- read-syntax-quote* 157 | [form] 158 | (binding [gensym-env {}] 159 | (syntax-quote* (second form)))) 160 | 161 | 162 | (defn read-tagged-literal [[tag value]] 163 | (if-let [f (*data-readers* tag)] 164 | (f value) 165 | (if-let [f (default-data-readers tag)] 166 | (f value) 167 | (if *default-data-reader-fn* 168 | (*default-data-reader-fn* value) 169 | (throw (Exception. "No reader function for tag ") (name tag)))))) 170 | 171 | 172 | (defn read-constructor [[class-name value]] 173 | 174 | (RT/baseLoader) 175 | (let [ class (if (class? class-name) class-name (Class/forName (name class-name) ))] 176 | (if (vector? value) 177 | (do (def xxx [class value]) 178 | (Reflector/invokeConstructor class (to-array value))) 179 | (if (map? value ) 180 | (Reflector/invokeStaticMethod class "create" (object-array [value])) 181 | (throw (Exception. "constructor value not a vector or map.")))))) 182 | 183 | 184 | (defn read-autoresolved-keyword [kw] 185 | (if (namespace kw) 186 | (keyword (str (resolve-ns (symbol (namespace kw)))) (name kw)) 187 | (keyword (str *ns*) (name kw)))) 188 | 189 | 190 | 191 | (defn make-expr [head body] 192 | (condp = head 193 | :nil nil 194 | :list (apply list body) 195 | :vector (vec body) 196 | :map (apply hash-map body) 197 | :set (set body) 198 | :fn (list 'read-fn (first body)) 199 | :deref (list 'clojure.core/deref (first body)) 200 | :var-quote (list 'var (first body)) 201 | :quote (list 'quote (first body)) 202 | :unquote (list 'clojure.core/unquote (first body)) 203 | :unquote-splicing (list 'clojure.core/unquote-splicing (first body)) 204 | :syntax-quote (list 'read-syntax-quote* (first body)) 205 | :read-eval (eval (first body)) 206 | :tagged-literal (read-tagged-literal body) 207 | :constructor (read-constructor body) 208 | :autoresolved-keyword (read-autoresolved-keyword (first body)) 209 | :regex (re-pattern (first body)) 210 | :ratio (apply / body) 211 | :meta (with-meta (second body) (desugar-meta (first body))) 212 | (throw (Exception. (str "Unknown codn head: " head))))) 213 | 214 | 215 | (defn un-edn [x] 216 | (if (not (#{clojure.lang.PersistentArrayMap clojure.lang.PersistentHashMap} (class x))) 217 | x 218 | (if (:value x) 219 | (:value x) 220 | (make-expr (:head x) (:body x))))) 221 | 222 | (defn un-syntax-quote [x] 223 | (if ( syntax-quote? x) (read-syntax-quote* x) x)) 224 | 225 | 226 | (defn get-slot [s] 227 | (if (= '%& s) :rest (Integer/parseInt (subs (name s) 1)))) 228 | 229 | 230 | (defn- garg2 [n] 231 | (symbol (str (if (= :rest n) "rest" (str "p" n)) 232 | "__" (RT/nextID) "#"))) 233 | 234 | (defn match-head? [form head] 235 | (and (seq? form) 236 | (= (first form) head))) 237 | 238 | (defn arg? [x] (and (symbol? x) (= "%" (subs (name x) 0 1)))) ;; bad 239 | 240 | (declare fn-slots) 241 | 242 | (defn remap-arg [fn-slots arg] 243 | (let [s (get-slot ({'% '%1} arg arg))] 244 | (if (@fn-slots s) 245 | (@fn-slots s) 246 | (let [arg2 (garg2 s)] (swap! fn-slots assoc s arg2) arg2)))) 247 | 248 | (defn build-fn-args [slots] 249 | (vec (concat 250 | (let [positional-slots (map first (dissoc slots :rest))] 251 | (if (not (empty? positional-slots)) 252 | (map #(slots % (garg2 %)) (range 1 (+ 1 (apply max positional-slots)))) 253 | [])) 254 | (if (slots :rest) ['& (slots :rest)] [])))) 255 | 256 | (defn un-fn-postwalk [x fn-slots] 257 | (cond 258 | (arg? x) (remap-arg fn-slots x) 259 | (match-head? x 'read-fn) 260 | (let [slots @fn-slots] 261 | (list 262 | 'fn* 263 | (build-fn-args slots) 264 | (second x))) 265 | true x)) 266 | 267 | (defn un-fn-prewalk [x fn-slots] 268 | (if (match-head? x 'read-fn) 269 | (reset! fn-slots {})) 270 | x) 271 | 272 | 273 | 274 | (defn walk 275 | [inner outer form] 276 | (cond 277 | ;;(list? form) (outer (apply list (map inner form))) 278 | (list? form) (outer (into (empty form) (reverse (map inner form)))) 279 | 280 | (instance? clojure.lang.IRecord form) (outer (read-constructor [(class form) (into {} (map inner form))])) 281 | (instance? clojure.lang.IMapEntry form) (outer (vec (map inner form))) 282 | (seq? form) (outer (doall (map inner form))) 283 | (coll? form) (outer (into (empty form) (map inner form))) 284 | :else (outer form))) 285 | 286 | (defn postwalk 287 | [f form] 288 | (walk (partial postwalk f) f form)) 289 | 290 | (defn prewalk 291 | [f form] 292 | (walk (partial prewalk f) identity (f form))) 293 | 294 | (defn read-codn [expr] 295 | (let [fn-slots (atom {})] 296 | (prewalk 297 | un-syntax-quote 298 | (postwalk 299 | #(un-fn-postwalk % fn-slots) 300 | (postwalk un-edn expr))))) 301 | -------------------------------------------------------------------------------- /target/classes/codn/parse/ExceptionInfo$_getData.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kovasb/codn/30bdf62d2b4b7f57acf984dbecbf7e50f47c1b44/target/classes/codn/parse/ExceptionInfo$_getData.class -------------------------------------------------------------------------------- /target/classes/codn/parse/ExceptionInfo$_init.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kovasb/codn/30bdf62d2b4b7f57acf984dbecbf7e50f47c1b44/target/classes/codn/parse/ExceptionInfo$_init.class -------------------------------------------------------------------------------- /target/classes/codn/parse/ExceptionInfo$_toString.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kovasb/codn/30bdf62d2b4b7f57acf984dbecbf7e50f47c1b44/target/classes/codn/parse/ExceptionInfo$_toString.class -------------------------------------------------------------------------------- /target/classes/codn/parse/ExceptionInfo$fn__14.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kovasb/codn/30bdf62d2b4b7f57acf984dbecbf7e50f47c1b44/target/classes/codn/parse/ExceptionInfo$fn__14.class -------------------------------------------------------------------------------- /target/classes/codn/parse/ExceptionInfo$loading__4916__auto__.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kovasb/codn/30bdf62d2b4b7f57acf984dbecbf7e50f47c1b44/target/classes/codn/parse/ExceptionInfo$loading__4916__auto__.class -------------------------------------------------------------------------------- /target/classes/codn/parse/ExceptionInfo.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kovasb/codn/30bdf62d2b4b7f57acf984dbecbf7e50f47c1b44/target/classes/codn/parse/ExceptionInfo.class -------------------------------------------------------------------------------- /target/classes/codn/parse/ExceptionInfo__init.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kovasb/codn/30bdf62d2b4b7f57acf984dbecbf7e50f47c1b44/target/classes/codn/parse/ExceptionInfo__init.class -------------------------------------------------------------------------------- /target/classes/codn/parser/ExceptionInfo$_getData.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kovasb/codn/30bdf62d2b4b7f57acf984dbecbf7e50f47c1b44/target/classes/codn/parser/ExceptionInfo$_getData.class -------------------------------------------------------------------------------- /target/classes/codn/parser/ExceptionInfo$_init.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kovasb/codn/30bdf62d2b4b7f57acf984dbecbf7e50f47c1b44/target/classes/codn/parser/ExceptionInfo$_init.class -------------------------------------------------------------------------------- /target/classes/codn/parser/ExceptionInfo$_toString.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kovasb/codn/30bdf62d2b4b7f57acf984dbecbf7e50f47c1b44/target/classes/codn/parser/ExceptionInfo$_toString.class -------------------------------------------------------------------------------- /target/classes/codn/parser/ExceptionInfo$fn__92.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kovasb/codn/30bdf62d2b4b7f57acf984dbecbf7e50f47c1b44/target/classes/codn/parser/ExceptionInfo$fn__92.class -------------------------------------------------------------------------------- /target/classes/codn/parser/ExceptionInfo$loading__4916__auto__.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kovasb/codn/30bdf62d2b4b7f57acf984dbecbf7e50f47c1b44/target/classes/codn/parser/ExceptionInfo$loading__4916__auto__.class -------------------------------------------------------------------------------- /target/classes/codn/parser/ExceptionInfo.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kovasb/codn/30bdf62d2b4b7f57acf984dbecbf7e50f47c1b44/target/classes/codn/parser/ExceptionInfo.class -------------------------------------------------------------------------------- /target/classes/codn/parser/ExceptionInfo__init.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kovasb/codn/30bdf62d2b4b7f57acf984dbecbf7e50f47c1b44/target/classes/codn/parser/ExceptionInfo__init.class -------------------------------------------------------------------------------- /target/repl-port: -------------------------------------------------------------------------------- 1 | 52434 -------------------------------------------------------------------------------- /target/stale/extract-native.dependencies: -------------------------------------------------------------------------------- 1 | ([:dependencies ([org.clojure/clojure "1.6.0-master-SNAPSHOT"] [org.thnetos/cd-client "0.3.6" :exclusions ([org.clojure/clojure])] [org.clojure/tools.nrepl "0.2.1" :exclusions ([org.clojure/clojure])] [clojure-complete/clojure-complete "0.2.2" :exclusions ([org.clojure/clojure])])]) -------------------------------------------------------------------------------- /test/codn/parser/common_tests.clj: -------------------------------------------------------------------------------- 1 | 2 | (deftest read-integer 3 | (is (= {:head :integer, :value 42} (parse-string "42"))) 4 | (is (= {:head :integer, :value 42} (parse-string "+42"))) 5 | (is (= {:head :integer, :value -42} (parse-string "-42"))) 6 | 7 | (is (= {:head :integer, :value 42N} (parse-string "42N"))) 8 | (is (= {:head :integer, :value 42N} (parse-string "+42N"))) 9 | (is (= {:head :integer, :value -42N} (parse-string "-42N"))) 10 | 11 | (is (= {:head :integer, :value 0} (parse-string "0"))) 12 | (is (= {:head :integer, :value 0N} (parse-string "0N"))) 13 | 14 | (is (= {:head :integer, :value 042} (parse-string "042"))) 15 | (is (= {:head :integer, :value +042} (parse-string "+042"))) 16 | (is (= {:head :integer, :value -042} (parse-string "-042"))) 17 | 18 | (is (= {:head :integer :value 0x42e} (parse-string "0x42e"))) 19 | (is (= {:head :integer :value +0x42e} (parse-string "+0x42e"))) 20 | (is (= {:head :integer :value -0x42e} (parse-string "-0x42e"))) 21 | 22 | (is (instance? Long (:value (parse-string "2147483647")))) 23 | (is (instance? Long (:value (parse-string "+1")))) 24 | (is (instance? Long (:value (parse-string "1")))) 25 | (is (instance? Long (:value (parse-string "+0")))) 26 | (is (instance? Long (:value (parse-string "0")))) 27 | (is (instance? Long (:value (parse-string "-0")))) 28 | (is (instance? Long (:value (parse-string "-1")))) 29 | (is (instance? Long (:value (parse-string "-2147483648")))) 30 | 31 | (is (instance? Long (:value (parse-string "2147483648")))) 32 | (is (instance? Long (:value (parse-string "-2147483649")))) 33 | (is (instance? Long (:value (parse-string "9223372036854775807")))) 34 | (is (instance? Long (:value (parse-string "-9223372036854775808")))) 35 | 36 | (is (instance? BigInt (:value (parse-string "9223372036854775808")))) 37 | (is (instance? BigInt (:value (parse-string "-9223372036854775809")))) 38 | (is (instance? BigInt (:value (parse-string "10000000000000000000000000000000000000000000000000")))) 39 | (is (instance? BigInt (:value (parse-string "-10000000000000000000000000000000000000000000000000"))))) 40 | 41 | (deftest read-floating 42 | (is (= {:head :float, :value 42.23} (parse-string "42.23"))) 43 | (is (= {:head :float, :value +42.23} (parse-string "+42.23"))) 44 | (is (= {:head :float, :value -42.23} (parse-string "-42.23"))) 45 | 46 | (is (= {:head :float, :value 42.23M} (parse-string "42.23M"))) 47 | (is (= {:head :float, :value +42.23M} (parse-string "+42.23M"))) 48 | (is (= {:head :float, :value -42.23M} (parse-string "-42.23M"))) 49 | 50 | (is (= {:head :float, :value 42.2e3} (parse-string "42.2e3"))) 51 | (is (= {:head :float, :value 42.2e+3} (parse-string "+42.2e+3"))) 52 | (is (= {:head :float, :value -42.2e-3} (parse-string "-42.2e-3"))) 53 | 54 | (is (= {:head :float, :value 42.2e3M} (parse-string "42.2e3M"))) 55 | (is (= {:head :float, :value +42.2e+3M} (parse-string "+42.2e+3M"))) 56 | (is (= {:head :float, :value -42.2e-3M} (parse-string "-42.2e-3M"))) 57 | 58 | (is (instance? Double (:value (parse-string "+1.0e+1")))) 59 | (is (instance? Double (:value (parse-string "+1.e+1")))) 60 | (is (instance? Double (:value (parse-string "+1e+1")))) 61 | 62 | (is (instance? Double (:value (parse-string "+1.0e+1")))) 63 | (is (instance? Double (:value (parse-string "+1.e+1")))) 64 | (is (instance? Double (:value (parse-string "+1e+1")))) 65 | 66 | (is (instance? Double (:value (parse-string "+1.0e1")))) 67 | (is (instance? Double (:value (parse-string "+1.e1")))) 68 | (is (instance? Double (:value (parse-string "+1e1")))) 69 | 70 | (is (instance? Double (:value (parse-string "+1.0e-1")))) 71 | (is (instance? Double (:value (parse-string "+1.e-1")))) 72 | (is (instance? Double (:value (parse-string "+1e-1")))) 73 | 74 | (is (instance? Double (:value (parse-string "1.0e+1")))) 75 | (is (instance? Double (:value (parse-string "1.e+1")))) 76 | (is (instance? Double (:value (parse-string "1e+1")))) 77 | 78 | (is (instance? Double (:value (parse-string "1.0e-1")))) 79 | (is (instance? Double (:value (parse-string "1.e-1")))) 80 | (is (instance? Double (:value (parse-string "1e-1")))) 81 | 82 | (is (instance? Double (:value (parse-string "-1.0e+1")))) 83 | (is (instance? Double (:value (parse-string "-1.e+1")))) 84 | (is (instance? Double (:value (parse-string "-1e+1")))) 85 | 86 | (is (instance? Double (:value (parse-string "-1.0e1")))) 87 | (is (instance? Double (:value (parse-string "-1.e1")))) 88 | (is (instance? Double (:value (parse-string "-1e1")))) 89 | 90 | (is (instance? Double (:value (parse-string "-1.0e-1")))) 91 | (is (instance? Double (:value (parse-string "-1.e-1")))) 92 | (is (instance? Double (:value (parse-string "-1e-1")))) 93 | 94 | (is (instance? Double (:value (parse-string "+1.0")))) 95 | (is (instance? Double (:value (parse-string "+1.")))) 96 | 97 | (is (instance? Double (:value (parse-string "1.0")))) 98 | (is (instance? Double (:value (parse-string "1.")))) 99 | 100 | (is (instance? Double (:value (parse-string "+0.0")))) 101 | (is (instance? Double (:value (parse-string "+0.")))) 102 | 103 | (is (instance? Double (:value (parse-string "0.0")))) 104 | (is (instance? Double (:value (parse-string "0.")))) 105 | 106 | (is (instance? Double (:value (parse-string "-0.0")))) 107 | (is (instance? Double (:value (parse-string "-0.")))) 108 | 109 | (is (instance? Double (:value (parse-string "-1.0")))) 110 | (is (instance? Double (:value (parse-string "-1.")))) 111 | 112 | (is (instance? BigDecimal (:value (parse-string "9223372036854775808M")))) 113 | (is (instance? BigDecimal (:value (parse-string "-9223372036854775809M")))) 114 | (is (instance? BigDecimal (:value (parse-string "2147483647M")))) 115 | (is (instance? BigDecimal (:value (parse-string "+1M")))) 116 | (is (instance? BigDecimal (:value (parse-string "1M")))) 117 | (is (instance? BigDecimal (:value (parse-string "+0M")))) 118 | (is (instance? BigDecimal (:value (parse-string "0M")))) 119 | (is (instance? BigDecimal (:value (parse-string "-0M")))) 120 | (is (instance? BigDecimal (:value (parse-string "-1M")))) 121 | (is (instance? BigDecimal (:value (parse-string "-2147483648M")))) 122 | 123 | (is (instance? BigDecimal (:value (parse-string "+1.0e+1M")))) 124 | (is (instance? BigDecimal (:value (parse-string "+1.e+1M")))) 125 | (is (instance? BigDecimal (:value (parse-string "+1e+1M")))) 126 | 127 | (is (instance? BigDecimal (:value (parse-string "+1.0e1M")))) 128 | (is (instance? BigDecimal (:value (parse-string "+1.e1M")))) 129 | (is (instance? BigDecimal (:value (parse-string "+1e1M")))) 130 | 131 | (is (instance? BigDecimal (:value (parse-string "+1.0e-1M")))) 132 | (is (instance? BigDecimal (:value (parse-string "+1.e-1M")))) 133 | (is (instance? BigDecimal (:value (parse-string "+1e-1M")))) 134 | 135 | (is (instance? BigDecimal (:value (parse-string "1.0e+1M")))) 136 | (is (instance? BigDecimal (:value (parse-string "1.e+1M")))) 137 | (is (instance? BigDecimal (:value (parse-string "1e+1M")))) 138 | 139 | (is (instance? BigDecimal (:value (parse-string "1.0e1M")))) 140 | (is (instance? BigDecimal (:value (parse-string "1.e1M")))) 141 | (is (instance? BigDecimal (:value (parse-string "1e1M")))) 142 | 143 | (is (instance? BigDecimal (:value (parse-string "1.0e-1M")))) 144 | (is (instance? BigDecimal (:value (parse-string "1.e-1M")))) 145 | (is (instance? BigDecimal (:value (parse-string "1e-1M")))) 146 | 147 | (is (instance? BigDecimal (:value (parse-string "-1.0e+1M")))) 148 | (is (instance? BigDecimal (:value (parse-string "-1.e+1M")))) 149 | (is (instance? BigDecimal (:value (parse-string "-1e+1M")))) 150 | 151 | (is (instance? BigDecimal (:value (parse-string "-1.0e1M")))) 152 | (is (instance? BigDecimal (:value (parse-string "-1.e1M")))) 153 | (is (instance? BigDecimal (:value (parse-string "-1e1M")))) 154 | 155 | (is (instance? BigDecimal (:value (parse-string "-1.0e-1M")))) 156 | (is (instance? BigDecimal (:value (parse-string "-1.e-1M")))) 157 | (is (instance? BigDecimal (:value (parse-string "-1e-1M")))) 158 | 159 | (is (instance? BigDecimal (:value (parse-string "+1.0M")))) 160 | (is (instance? BigDecimal (:value (parse-string "+1.M")))) 161 | 162 | (is (instance? BigDecimal (:value (parse-string "1.0M")))) 163 | (is (instance? BigDecimal (:value (parse-string "1.M")))) 164 | 165 | (is (instance? BigDecimal (:value (parse-string "+0.0M")))) 166 | (is (instance? BigDecimal (:value (parse-string "+0.M")))) 167 | 168 | (is (instance? BigDecimal (:value (parse-string "0.0M")))) 169 | (is (instance? BigDecimal (:value (parse-string "0.M")))) 170 | 171 | (is (instance? BigDecimal (:value (parse-string "-0.0M")))) 172 | (is (instance? BigDecimal (:value (parse-string "-0.M")))) 173 | 174 | (is (instance? BigDecimal (:value (parse-string "-1.0M")))) 175 | (is (instance? BigDecimal (:value (parse-string "-1.M"))))) 176 | 177 | (deftest read-ratio 178 | (is (= {:head :ratio, :body [{:head :integer, :value 5} {:head :integer, :value 2}]} (parse-string "5/2"))) 179 | (is (= {:head :ratio, :body [{:head :integer, :value 5} {:head :integer, :value 2}]} (parse-string "+5/2"))) 180 | (is (= {:head :ratio, :body [{:head :integer, :value -5} {:head :integer, :value 2}]} (parse-string "-5/2")))) 181 | 182 | 183 | (deftest read-symbol 184 | (is (= '{:head :symbol, :value foo} (parse-string "foo"))) 185 | (is (= '{:head :symbol, :value foo/bar} (parse-string "foo/bar"))) 186 | (is (= '{:head :symbol, :value *+!-_?} (parse-string "*+!-_?"))) 187 | (is (= '{:head :symbol, :value abc:def:ghi} (parse-string "abc:def:ghi"))) 188 | (is (= '{:head :symbol, :value abc.def/ghi} (parse-string "abc.def/ghi"))) 189 | (is (= '{:head :symbol, :value abc/def.ghi} (parse-string "abc/def.ghi"))) 190 | (is (= '{:head :symbol, :value abc:def/ghi:jkl.mno} (parse-string "abc:def/ghi:jkl.mno"))) 191 | (is (instance? clojure.lang.Symbol (:value (parse-string "alphabet")))) 192 | (is (= "foo//" (str (:value (parse-string "foo//"))))) ;; the clojure reader can't read this 193 | (is (= (str 'NaN) (str (:value (parse-string "NaN"))))) ;; the clojure reader can't read this 194 | (is (= {:head :positive-infinity, :value Double/POSITIVE_INFINITY} (parse-string "Infinity"))) ;; the clojure reader can't read this 195 | (is (= {:head :positive-infinity, :value Double/POSITIVE_INFINITY} (parse-string "+Infinity"))) ;; the clojure reader can't read this 196 | (is (= {:head :negative-infinity, :value Double/NEGATIVE_INFINITY} (parse-string "-Infinity")))) ;; the clojure reader can't read this 197 | 198 | (deftest read-specials 199 | (is (= '{:head :nil, :value nil} (parse-string "nil"))) 200 | (is (= '{:head :boolean, :value false} (parse-string "false"))) 201 | (is (= '{:head :boolean, :value true} (parse-string "true")))) 202 | 203 | (deftest read-char 204 | (is (= {:head :character, :value \f} (parse-string "\\f"))) 205 | (is (= {:head :character, :value \u0194} (parse-string "\\u0194"))) 206 | (is (= {:head :character, :value \a} (parse-string "\\x61"))) ;; the clojure reader can't read this 207 | (is (= {:head :character, :value \o123} (parse-string "\\o123"))) 208 | (is (= {:head :character, :value \newline } (parse-string "\\newline"))) 209 | (is (= {:head :character, :value (char 0) } (parse-string "\\o0"))) 210 | (is (= {:head :character, :value (char 0) } (parse-string "\\o000"))) 211 | (is (= {:head :character, :value (char 0377) } (parse-string "\\o377"))) 212 | (is (= {:head :character, :value \A } (parse-string "\\u0041"))) 213 | (is (= {:head :character, :value \@ } (parse-string "\\@"))) 214 | (is (= {:head :character, :value (char 0xd7ff)} (parse-string "\\ud7ff"))) 215 | (is (= {:head :character, :value (char 0xe000) } (parse-string "\\ue000"))) 216 | (is (= {:head :character, :value (char 0xffff)} (parse-string "\\uffff")))) 217 | 218 | (deftest parse-string* 219 | (is (= {:head :string, :value "foo bar"} (parse-string "\"foo bar\""))) 220 | (is (= {:head :string, :value "foo\\bar"} (parse-string "\"foo\\\\bar\""))) 221 | (is (= {:head :string, :value "foo\000bar"} (parse-string "\"foo\\000bar\""))) 222 | (is (= {:head :string, :value "foo\u0194bar"} (parse-string "\"foo\\u0194bar\""))) 223 | (is (= {:head :string, :value "fooabar" } (parse-string "\"foo\\x61bar\""))) ;; the clojure reader can't read this 224 | (is (= {:head :string, :value "foo\123bar"} (parse-string "\"foo\\123bar\"")))) 225 | 226 | (deftest read-list 227 | (is (= '{:head :list, :body []} (parse-string "()"))) 228 | (is (= '{:head :list, :body [{:head :symbol, :value foo} {:head :symbol, :value bar}]} (parse-string "(foo bar)"))) 229 | (is (= '{:head :list, :body [{:head :symbol, :value foo} {:head :list, :body [{:head :symbol, :value bar}]} {:head :symbol, :value baz}]} (parse-string "(foo (bar) baz)")))) 230 | 231 | (deftest read-vector 232 | (is (= '{:head :vector, :body []} (parse-string "[]"))) 233 | (is (= '{:head :vector, :body [{:head :symbol, :value foo} {:head :symbol, :value bar}]} (parse-string "[foo bar]"))) 234 | (is (= '{:head :vector, :body [{:head :symbol, :value foo} {:head :vector, :body [{:head :symbol, :value bar}]} {:head :symbol, :value baz}]} (parse-string "[foo [bar] baz]")))) 235 | 236 | (deftest read-map 237 | (is (= '{:head :map, :body []} (parse-string "{}"))) 238 | (is (= '{:head :map, :body [{:head :symbol, :value foo} {:head :symbol, :value bar}]} (parse-string "{foo bar}"))) 239 | (is (= '{:head :map, :body [{:head :symbol, :value foo} {:head :map, :body [{:head :symbol, :value bar} {:head :symbol, :value baz}]}]} (parse-string "{foo {bar baz}}")))) 240 | 241 | (deftest read-set 242 | (is (= '{:head :set, :body []} (parse-string "#{}"))) 243 | (is (= '{:head :set, :body [{:head :symbol, :value foo} {:head :symbol, :value bar}]} (parse-string "#{foo bar}"))) 244 | (is (= '{:head :set, :body [{:head :symbol, :value foo} {:head :set, :body [{:head :symbol, :value bar}]} {:head :symbol, :value baz}]} (parse-string "#{foo #{bar} baz}")))) 245 | 246 | (deftest read-metadata 247 | (is (= '{:head :meta, :body [{:head :keyword, :value :foo} {:head :quote, :body [{:head :symbol, :value bar}]}]} (parse-string "^:foo 'bar"))) 248 | (is (= '{:head :meta, :body [{:head :map, :body [{:head :keyword, :value :foo} {:head :symbol, :value bar}]} {:head :quote, :body [{:head :symbol, :value baz}]}]} (parse-string "^{:foo bar} 'baz"))) 249 | (is (= '{:head :meta, :body [{:head :string, :value "foo"} {:head :quote, :body [{:head :symbol, :value bar}]}]} (parse-string "^\"foo\" 'bar"))) 250 | (is (= '{:head :meta, :body [{:head :symbol, :value String} {:head :quote, :body [{:head :symbol, :value x}]}]} (parse-string "^String 'x")))) 251 | -------------------------------------------------------------------------------- /test/codn/parser/reader_edn_test.clj: -------------------------------------------------------------------------------- 1 | (ns codn.parser.reader-edn-test 2 | (:use [codn.parser.core :only [parse-string]] 3 | [clojure.test :only [deftest is]]) 4 | (:import clojure.lang.BigInt)) 5 | 6 | (load "common_tests") 7 | 8 | (deftest read-keyword 9 | (is (= :foo-bar (parse-string ":foo-bar"))) 10 | (is (= :foo/bar (parse-string ":foo/bar"))) 11 | (is (= :*+!-_? (parse-string ":*+!-_?"))) 12 | (is (= :abc:def:ghi (parse-string ":abc:def:ghi"))) 13 | (is (= :abc.def/ghi (parse-string ":abc.def/ghi"))) 14 | (is (= :abc/def.ghi (parse-string ":abc/def.ghi"))) 15 | (is (= :abc:def/ghi:jkl.mno (parse-string ":abc:def/ghi:jkl.mno"))) 16 | (is (instance? clojure.lang.Keyword (parse-string ":alphabet"))) ) 17 | 18 | (deftest read-tagged 19 | ;; (is (= #inst "2010-11-12T13:14:15.666" 20 | ;; (parse-string "#inst \"2010-11-12T13:14:15.666\""))) 21 | ;; (is (= #inst "2010-11-12T13:14:15.666" 22 | ;; (parse-string "#inst\"2010-11-12T13:14:15.666\""))) 23 | ;; (is (= #uuid "550e8400-e29b-41d4-a716-446655440000" 24 | ;; (parse-string "#uuid \"550e8400-e29b-41d4-a716-446655440000\""))) 25 | ;; (is (= #uuid "550e8400-e29b-41d4-a716-446655440000" 26 | ;; (parse-string "#uuid\"550e8400-e29b-41d4-a716-446655440000\""))) 27 | (is (= (java.util.UUID/fromString "550e8400-e29b-41d4-a716-446655440000") 28 | (parse-string "#uuid \"550e8400-e29b-41d4-a716-446655440000\""))) 29 | (is (= (java.util.UUID/fromString "550e8400-e29b-41d4-a716-446655440000") 30 | (parse-string "#uuid\"550e8400-e29b-41d4-a716-446655440000\""))) 31 | (let [my-unknown (fn [tag val] {:unknown-tag tag :value val})] 32 | (is (= {:unknown-tag 'foo :value 'bar} 33 | (parse-string {:default my-unknown} "#foo bar"))))) 34 | -------------------------------------------------------------------------------- /test/codn/parser/reader_test.clj: -------------------------------------------------------------------------------- 1 | (ns codn.parser.reader-test 2 | (:refer-clojure :exclude [*default-data-reader-fn*]) 3 | (:use [codn.parser.core :only [parse-string]] 4 | [clojure.test :only [deftest is]]) 5 | (:import clojure.lang.BigInt)) 6 | 7 | (load "common_tests") 8 | 9 | (deftest read-keyword 10 | (is (= {:head :keyword, :value :foo-bar} (parse-string ":foo-bar"))) 11 | (is (= {:head :keyword, :value :foo/bar} (parse-string ":foo/bar"))) 12 | (is (= {:head :autoresolved-keyword, :body [{:head :keyword, :value :foo-bar}]} (parse-string "::foo-bar"))) 13 | 14 | (is (= {:head :keyword, :value :*+!-_?} (parse-string ":*+!-_?"))) 15 | (is (= {:head :keyword, :value :abc:def:ghi} (parse-string ":abc:def:ghi"))) 16 | (is (= {:head :keyword, :value :abc.def/ghi} (parse-string ":abc.def/ghi"))) 17 | (is (= {:head :keyword, :value :abc/def.ghi} (parse-string ":abc/def.ghi"))) 18 | (is (= {:head :keyword, :value :abc:def/ghi:jkl.mno} (parse-string ":abc:def/ghi:jkl.mno"))) 19 | (is (instance? clojure.lang.Keyword (:value (parse-string ":alphabet")))) ) 20 | 21 | (deftest read-regex 22 | (is (= (str #"\[\]?(\")\\") 23 | (first (:body (parse-string "#\"\\[\\]?(\\\")\\\\\"")))))) 24 | 25 | (deftest read-quote 26 | (is (= '{:head :quote, :body [{:head :symbol, :value foo}]} (parse-string "'foo")))) 27 | 28 | (deftest read-syntax-quote 29 | (is (= '{:head :syntax-quote, :body [{:head :symbol, :value foo}]} (parse-string "`foo") )) 30 | (is (= '{:head :syntax-quote, :body [{:head :symbol, :value +}]} (parse-string "`+"))) 31 | (is (= '{:head :syntax-quote, :body [{:head :symbol, :value foo/bar}]} (parse-string "`foo/bar"))) 32 | (is (= {:head :syntax-quote, :body [{:head :integer, :value 1}]} (parse-string "`1"))) 33 | (is (= {:head :syntax-quote, :body [{:head :list, :body [{:head :integer, :value 1} {:head :list, :body [{:head :unquote, :body [{:head :integer, :value 2}]} {:head :unquote-splicing, :body [{:head :list, :body [{:head :integer, :value 3}]}]}]}]}]} (parse-string "`(1 (~2 ~@(3)))")))) 34 | 35 | (deftest read-deref 36 | (is (= '{:head :deref, :body [{:head :symbol, :value foo}]} (parse-string "@foo")))) 37 | 38 | (deftest read-var 39 | (is (= '{:head :var-quote, :body [{:head :symbol, :value foo}]} (parse-string "#'foo")))) 40 | 41 | (deftest read-fn 42 | (is (= '{:head :fn, :body [{:head :list, :body [{:head :symbol, :value foo} {:head :symbol, :value bar} {:head :symbol, :value baz}]}]} (parse-string "#(foo bar baz)")))) 43 | 44 | (deftest read-arg 45 | (is (= '{:head :fn, :body [{:head :list, :body [{:head :symbol, :value apply} {:head :symbol, :value +} {:head :symbol, :value %} {:head :symbol, :value %1} {:head :symbol, :value %3} {:head :symbol, :value %&}]}]} (parse-string "#(apply + % %1 %3 %&)")))) 46 | 47 | (deftest read-eval 48 | (is (= '{:head :read-eval, :body [{:head :list, :body [{:head :symbol, :value +} {:head :integer, :value 1} {:head :integer, :value 2}]}]} (parse-string "#=(+ 1 2)")))) 49 | 50 | (deftest read-tagged 51 | (is (= '{:head :tagged-literal, :body [{:head :symbol, :value inst} {:head :string, :value "2010-11-12T13:14:15.666"}]} 52 | (parse-string "#inst \"2010-11-12T13:14:15.666\""))) 53 | (is (= '{:head :tagged-literal, :body [{:head :symbol, :value uuid} {:head :string, :value "550e8400-e29b-41d4-a716-446655440000"}]} 54 | (parse-string "#uuid \"550e8400-e29b-41d4-a716-446655440000\""))) 55 | (is (= '{:head :tagged-literal, :body [{:head :symbol, :value foo} {:head :symbol, :value bar}]} (parse-string "#foo bar")))) 56 | 57 | 58 | (deftest read-record 59 | (is (= '{:head :constructor, :body [{:head :symbol, :value clojure.tools.reader_test.foo} {:head :vector, :body []}]} (parse-string "#clojure.tools.reader_test.foo[]"))) 60 | (is (= '{:head :constructor, :body [{:head :symbol, :value clojure.tools.reader_test.foo} {:head :vector, :body []}]} (parse-string "#clojure.tools.reader_test.foo []"))) ;; not valid in clojure 61 | (is (= '{:head :constructor, :body [{:head :symbol, :value clojure.tools.reader_test.foo} {:head :map, :body []}]} (parse-string "#clojure.tools.reader_test.foo{}"))) 62 | (is (= '{:head :constructor, :body [{:head :symbol, :value clojure.tools.reader_test.foo} {:head :map, :body [{:head :keyword, :value :foo} {:head :symbol, :value bar}]}]} (parse-string "#clojure.tools.reader_test.foo{:foo bar}"))) 63 | 64 | (is (= '{:head :constructor, :body [{:head :symbol, :value clojure.tools.reader_test.bar} {:head :map, :body []}]} (parse-string "#clojure.tools.reader_test.bar{}"))) 65 | (is (= '{:head :constructor, :body [{:head :symbol, :value clojure.tools.reader_test.bar} {:head :map, :body [{:head :keyword, :value :baz} {:head :integer, :value 1}]}]} (parse-string "#clojure.tools.reader_test.bar{:baz 1}"))) 66 | (is (= '{:head :constructor, :body [{:head :symbol, :value clojure.tools.reader_test.bar} {:head :vector, :body [{:head :integer, :value 1} {:head :nil, :value nil}]}]} (parse-string "#clojure.tools.reader_test.bar[1 nil]"))) 67 | (is (= '{:head :constructor, :body [{:head :symbol, :value clojure.tools.reader_test.bar} {:head :vector, :body [{:head :integer, :value 1} {:head :integer, :value 2}]}]} 68 | (parse-string "#clojure.tools.reader_test.bar[1 2]")))) 69 | -------------------------------------------------------------------------------- /test/codn/reader/common_tests.clj: -------------------------------------------------------------------------------- 1 | 2 | (deftest read-integer 3 | (is (== 42 (parse-read-string "42"))) 4 | (is (== +42 (parse-read-string "+42"))) 5 | (is (== -42 (parse-read-string "-42"))) 6 | 7 | (is (== 42 (parse-read-string "42N"))) 8 | (is (== +42 (parse-read-string "+42N"))) 9 | (is (== -42 (parse-read-string "-42N"))) 10 | 11 | (is (== 0 (parse-read-string "0"))) 12 | (is (== 0N (parse-read-string "0N"))) 13 | 14 | (is (== 042 (parse-read-string "042"))) 15 | (is (== +042 (parse-read-string "+042"))) 16 | (is (== -042 (parse-read-string "-042"))) 17 | 18 | (is (== 0x42e (parse-read-string "0x42e"))) 19 | (is (== +0x42e (parse-read-string "+0x42e"))) 20 | (is (== -0x42e (parse-read-string "-0x42e"))) 21 | 22 | (is (instance? Long (parse-read-string "2147483647"))) 23 | (is (instance? Long (parse-read-string "+1"))) 24 | (is (instance? Long (parse-read-string "1"))) 25 | (is (instance? Long (parse-read-string "+0"))) 26 | (is (instance? Long (parse-read-string "0"))) 27 | (is (instance? Long (parse-read-string "-0"))) 28 | (is (instance? Long (parse-read-string "-1"))) 29 | (is (instance? Long (parse-read-string "-2147483648"))) 30 | 31 | (is (instance? Long (parse-read-string "2147483648"))) 32 | (is (instance? Long (parse-read-string "-2147483649"))) 33 | (is (instance? Long (parse-read-string "9223372036854775807"))) 34 | (is (instance? Long (parse-read-string "-9223372036854775808"))) 35 | 36 | (is (instance? BigInt (parse-read-string "9223372036854775808"))) 37 | (is (instance? BigInt (parse-read-string "-9223372036854775809"))) 38 | (is (instance? BigInt (parse-read-string "10000000000000000000000000000000000000000000000000"))) 39 | (is (instance? BigInt (parse-read-string "-10000000000000000000000000000000000000000000000000")))) 40 | 41 | (deftest read-floating 42 | (is (== 42.23 (parse-read-string "42.23"))) 43 | (is (== +42.23 (parse-read-string "+42.23"))) 44 | (is (== -42.23 (parse-read-string "-42.23"))) 45 | 46 | (is (== 42.23M (parse-read-string "42.23M"))) 47 | (is (== +42.23M (parse-read-string "+42.23M"))) 48 | (is (== -42.23M (parse-read-string "-42.23M"))) 49 | 50 | (is (== 42.2e3 (parse-read-string "42.2e3"))) 51 | (is (== +42.2e+3 (parse-read-string "+42.2e+3"))) 52 | (is (== -42.2e-3 (parse-read-string "-42.2e-3"))) 53 | 54 | (is (== 42.2e3M (parse-read-string "42.2e3M"))) 55 | (is (== +42.2e+3M (parse-read-string "+42.2e+3M"))) 56 | (is (== -42.2e-3M (parse-read-string "-42.2e-3M"))) 57 | 58 | (is (instance? Double (parse-read-string "+1.0e+1"))) 59 | (is (instance? Double (parse-read-string "+1.e+1"))) 60 | (is (instance? Double (parse-read-string "+1e+1"))) 61 | 62 | (is (instance? Double (parse-read-string "+1.0e+1"))) 63 | (is (instance? Double (parse-read-string "+1.e+1"))) 64 | (is (instance? Double (parse-read-string "+1e+1"))) 65 | 66 | (is (instance? Double (parse-read-string "+1.0e1"))) 67 | (is (instance? Double (parse-read-string "+1.e1"))) 68 | (is (instance? Double (parse-read-string "+1e1"))) 69 | 70 | (is (instance? Double (parse-read-string "+1.0e-1"))) 71 | (is (instance? Double (parse-read-string "+1.e-1"))) 72 | (is (instance? Double (parse-read-string "+1e-1"))) 73 | 74 | (is (instance? Double (parse-read-string "1.0e+1"))) 75 | (is (instance? Double (parse-read-string "1.e+1"))) 76 | (is (instance? Double (parse-read-string "1e+1"))) 77 | 78 | (is (instance? Double (parse-read-string "1.0e-1"))) 79 | (is (instance? Double (parse-read-string "1.e-1"))) 80 | (is (instance? Double (parse-read-string "1e-1"))) 81 | 82 | (is (instance? Double (parse-read-string "-1.0e+1"))) 83 | (is (instance? Double (parse-read-string "-1.e+1"))) 84 | (is (instance? Double (parse-read-string "-1e+1"))) 85 | 86 | (is (instance? Double (parse-read-string "-1.0e1"))) 87 | (is (instance? Double (parse-read-string "-1.e1"))) 88 | (is (instance? Double (parse-read-string "-1e1"))) 89 | 90 | (is (instance? Double (parse-read-string "-1.0e-1"))) 91 | (is (instance? Double (parse-read-string "-1.e-1"))) 92 | (is (instance? Double (parse-read-string "-1e-1"))) 93 | 94 | (is (instance? Double (parse-read-string "+1.0"))) 95 | (is (instance? Double (parse-read-string "+1."))) 96 | 97 | (is (instance? Double (parse-read-string "1.0"))) 98 | (is (instance? Double (parse-read-string "1."))) 99 | 100 | (is (instance? Double (parse-read-string "+0.0"))) 101 | (is (instance? Double (parse-read-string "+0."))) 102 | 103 | (is (instance? Double (parse-read-string "0.0"))) 104 | (is (instance? Double (parse-read-string "0."))) 105 | 106 | (is (instance? Double (parse-read-string "-0.0"))) 107 | (is (instance? Double (parse-read-string "-0."))) 108 | 109 | (is (instance? Double (parse-read-string "-1.0"))) 110 | (is (instance? Double (parse-read-string "-1."))) 111 | 112 | (is (instance? BigDecimal (parse-read-string "9223372036854775808M"))) 113 | (is (instance? BigDecimal (parse-read-string "-9223372036854775809M"))) 114 | (is (instance? BigDecimal (parse-read-string "2147483647M"))) 115 | (is (instance? BigDecimal (parse-read-string "+1M"))) 116 | (is (instance? BigDecimal (parse-read-string "1M"))) 117 | (is (instance? BigDecimal (parse-read-string "+0M"))) 118 | (is (instance? BigDecimal (parse-read-string "0M"))) 119 | (is (instance? BigDecimal (parse-read-string "-0M"))) 120 | (is (instance? BigDecimal (parse-read-string "-1M"))) 121 | (is (instance? BigDecimal (parse-read-string "-2147483648M"))) 122 | 123 | (is (instance? BigDecimal (parse-read-string "+1.0e+1M"))) 124 | (is (instance? BigDecimal (parse-read-string "+1.e+1M"))) 125 | (is (instance? BigDecimal (parse-read-string "+1e+1M"))) 126 | 127 | (is (instance? BigDecimal (parse-read-string "+1.0e1M"))) 128 | (is (instance? BigDecimal (parse-read-string "+1.e1M"))) 129 | (is (instance? BigDecimal (parse-read-string "+1e1M"))) 130 | 131 | (is (instance? BigDecimal (parse-read-string "+1.0e-1M"))) 132 | (is (instance? BigDecimal (parse-read-string "+1.e-1M"))) 133 | (is (instance? BigDecimal (parse-read-string "+1e-1M"))) 134 | 135 | (is (instance? BigDecimal (parse-read-string "1.0e+1M"))) 136 | (is (instance? BigDecimal (parse-read-string "1.e+1M"))) 137 | (is (instance? BigDecimal (parse-read-string "1e+1M"))) 138 | 139 | (is (instance? BigDecimal (parse-read-string "1.0e1M"))) 140 | (is (instance? BigDecimal (parse-read-string "1.e1M"))) 141 | (is (instance? BigDecimal (parse-read-string "1e1M"))) 142 | 143 | (is (instance? BigDecimal (parse-read-string "1.0e-1M"))) 144 | (is (instance? BigDecimal (parse-read-string "1.e-1M"))) 145 | (is (instance? BigDecimal (parse-read-string "1e-1M"))) 146 | 147 | (is (instance? BigDecimal (parse-read-string "-1.0e+1M"))) 148 | (is (instance? BigDecimal (parse-read-string "-1.e+1M"))) 149 | (is (instance? BigDecimal (parse-read-string "-1e+1M"))) 150 | 151 | (is (instance? BigDecimal (parse-read-string "-1.0e1M"))) 152 | (is (instance? BigDecimal (parse-read-string "-1.e1M"))) 153 | (is (instance? BigDecimal (parse-read-string "-1e1M"))) 154 | 155 | (is (instance? BigDecimal (parse-read-string "-1.0e-1M"))) 156 | (is (instance? BigDecimal (parse-read-string "-1.e-1M"))) 157 | (is (instance? BigDecimal (parse-read-string "-1e-1M"))) 158 | 159 | (is (instance? BigDecimal (parse-read-string "+1.0M"))) 160 | (is (instance? BigDecimal (parse-read-string "+1.M"))) 161 | 162 | (is (instance? BigDecimal (parse-read-string "1.0M"))) 163 | (is (instance? BigDecimal (parse-read-string "1.M"))) 164 | 165 | (is (instance? BigDecimal (parse-read-string "+0.0M"))) 166 | (is (instance? BigDecimal (parse-read-string "+0.M"))) 167 | 168 | (is (instance? BigDecimal (parse-read-string "0.0M"))) 169 | (is (instance? BigDecimal (parse-read-string "0.M"))) 170 | 171 | (is (instance? BigDecimal (parse-read-string "-0.0M"))) 172 | (is (instance? BigDecimal (parse-read-string "-0.M"))) 173 | 174 | (is (instance? BigDecimal (parse-read-string "-1.0M"))) 175 | (is (instance? BigDecimal (parse-read-string "-1.M")))) 176 | 177 | (deftest read-ratio 178 | (is (== 4/2 (parse-read-string "4/2"))) 179 | (is (== 4/2 (parse-read-string "+4/2"))) 180 | (is (== -4/2 (parse-read-string "-4/2")))) 181 | 182 | 183 | (deftest read-symbol 184 | (is (= 'foo (parse-read-string "foo"))) 185 | (is (= 'foo/bar (parse-read-string "foo/bar"))) 186 | (is (= '*+!-_? (parse-read-string "*+!-_?"))) 187 | (is (= 'abc:def:ghi (parse-read-string "abc:def:ghi"))) 188 | (is (= 'abc.def/ghi (parse-read-string "abc.def/ghi"))) 189 | (is (= 'abc/def.ghi (parse-read-string "abc/def.ghi"))) 190 | (is (= 'abc:def/ghi:jkl.mno (parse-read-string "abc:def/ghi:jkl.mno"))) 191 | (is (instance? clojure.lang.Symbol (parse-read-string "alphabet"))) 192 | (is (= "foo//" (str (parse-read-string "foo//")))) ;; the clojure reader can't read this 193 | (is (= (str 'NaN) (str (parse-read-string "NaN")))) ;; the clojure reader can't read this 194 | (is (= Double/POSITIVE_INFINITY (parse-read-string "Infinity"))) ;; the clojure reader can't read this 195 | (is (= Double/POSITIVE_INFINITY (parse-read-string "+Infinity"))) ;; the clojure reader can't read this 196 | (is (= Double/NEGATIVE_INFINITY (parse-read-string "-Infinity")))) ;; the clojure reader can't read this 197 | 198 | (deftest read-specials 199 | (is (= 'nil nil)) 200 | (is (= 'false false)) 201 | (is (= 'true true))) 202 | 203 | (deftest read-char 204 | (is (= \f (parse-read-string "\\f"))) 205 | (is (= \u0194 (parse-read-string "\\u0194"))) 206 | (is (= \a (parse-read-string "\\x61"))) ;; the clojure reader can't read this 207 | (is (= \o123 (parse-read-string "\\o123"))) 208 | (is (= \newline (parse-read-string "\\newline"))) 209 | (is (= (char 0) (parse-read-string "\\o0"))) 210 | (is (= (char 0) (parse-read-string "\\o000"))) 211 | (is (= (char 0377) (parse-read-string "\\o377"))) 212 | (is (= \A (parse-read-string "\\u0041"))) 213 | (is (= \@ (parse-read-string "\\@"))) 214 | (is (= (char 0xd7ff) (parse-read-string "\\ud7ff"))) 215 | (is (= (char 0xe000) (parse-read-string "\\ue000"))) 216 | (is (= (char 0xffff) (parse-read-string "\\uffff")))) 217 | 218 | (deftest parse-read-string* 219 | (is (= "foo bar" (parse-read-string "\"foo bar\""))) 220 | (is (= "foo\\bar" (parse-read-string "\"foo\\\\bar\""))) 221 | (is (= "foo\000bar" (parse-read-string "\"foo\\000bar\""))) 222 | (is (= "foo\u0194bar" (parse-read-string "\"foo\\u0194bar\""))) 223 | (is (= "fooabar" (parse-read-string "\"foo\\x61bar\""))) ;; the clojure reader can't read this 224 | (is (= "foo\123bar" (parse-read-string "\"foo\\123bar\"")))) 225 | 226 | (deftest read-list 227 | (is (= '() (parse-read-string "()"))) 228 | (is (= '(foo bar) (parse-read-string "(foo bar)"))) 229 | (is (= '(foo (bar) baz) (parse-read-string "(foo (bar) baz)")))) 230 | 231 | (deftest read-vector 232 | (is (= '[] (parse-read-string "[]"))) 233 | (is (= '[foo bar] (parse-read-string "[foo bar]"))) 234 | (is (= '[foo [bar] baz] (parse-read-string "[foo [bar] baz]")))) 235 | 236 | (deftest read-map 237 | (is (= '{} (parse-read-string "{}"))) 238 | (is (= '{foo bar} (parse-read-string "{foo bar}"))) 239 | (is (= '{foo {bar baz}} (parse-read-string "{foo {bar baz}}")))) 240 | 241 | (deftest read-set 242 | (is (= '#{} (parse-read-string "#{}"))) 243 | (is (= '#{foo bar} (parse-read-string "#{foo bar}"))) 244 | (is (= '#{foo #{bar} baz} (parse-read-string "#{foo #{bar} baz}")))) 245 | 246 | (deftest read-metadata 247 | (is (= {:foo true} (meta (parse-read-string "^:foo 'bar")))) 248 | (is (= {:foo 'bar} (meta (parse-read-string "^{:foo bar} 'baz")))) 249 | (is (= {:tag "foo"} (meta (parse-read-string "^\"foo\" 'bar")))) 250 | (is (= {:tag 'String} (meta (parse-read-string "^String 'x"))))) 251 | -------------------------------------------------------------------------------- /test/codn/reader/reader_edn_test.clj: -------------------------------------------------------------------------------- 1 | (ns codn.reader.reader-edn-test 2 | 3 | (:use [clojure.tools.reader.edn :only [parse-read-string]] 4 | [clojure.test :only [deftest is]]) 5 | (:import clojure.lang.BigInt)) 6 | 7 | (load "common_tests") 8 | 9 | (deftest read-keyword 10 | (is (= :foo-bar (parse-read-string ":foo-bar"))) 11 | (is (= :foo/bar (parse-read-string ":foo/bar"))) 12 | (is (= :*+!-_? (parse-read-string ":*+!-_?"))) 13 | (is (= :abc:def:ghi (parse-read-string ":abc:def:ghi"))) 14 | (is (= :abc.def/ghi (parse-read-string ":abc.def/ghi"))) 15 | (is (= :abc/def.ghi (parse-read-string ":abc/def.ghi"))) 16 | (is (= :abc:def/ghi:jkl.mno (parse-read-string ":abc:def/ghi:jkl.mno"))) 17 | (is (instance? clojure.lang.Keyword (parse-read-string ":alphabet"))) ) 18 | 19 | (deftest read-tagged 20 | ;; (is (= #inst "2010-11-12T13:14:15.666" 21 | ;; (parse-read-string "#inst \"2010-11-12T13:14:15.666\""))) 22 | ;; (is (= #inst "2010-11-12T13:14:15.666" 23 | ;; (parse-read-string "#inst\"2010-11-12T13:14:15.666\""))) 24 | ;; (is (= #uuid "550e8400-e29b-41d4-a716-446655440000" 25 | ;; (parse-read-string "#uuid \"550e8400-e29b-41d4-a716-446655440000\""))) 26 | ;; (is (= #uuid "550e8400-e29b-41d4-a716-446655440000" 27 | ;; (parse-read-string "#uuid\"550e8400-e29b-41d4-a716-446655440000\""))) 28 | (is (= (java.util.UUID/fromString "550e8400-e29b-41d4-a716-446655440000") 29 | (parse-read-string "#uuid \"550e8400-e29b-41d4-a716-446655440000\""))) 30 | (is (= (java.util.UUID/fromString "550e8400-e29b-41d4-a716-446655440000") 31 | (parse-read-string "#uuid\"550e8400-e29b-41d4-a716-446655440000\""))) 32 | (let [my-unknown (fn [tag val] {:unknown-tag tag :value val})] 33 | (is (= {:unknown-tag 'foo :value 'bar} 34 | (parse-read-string {:default my-unknown} "#foo bar"))))) 35 | -------------------------------------------------------------------------------- /test/codn/reader/reader_test.clj: -------------------------------------------------------------------------------- 1 | (ns codn.reader.reader-test 2 | (:use [codn.reader.core :only [read-codn]] 3 | [codn.parser.core :only [parse-string]] 4 | [clojure.test :only [deftest is]]) 5 | (:import clojure.lang.BigInt)) 6 | 7 | (defn parse-read-string [x] 8 | (read-codn (parse-string x))) 9 | 10 | (load "common_tests") 11 | 12 | (deftest read-keyword 13 | (is (= :foo-bar (parse-read-string ":foo-bar"))) 14 | (is (= :foo/bar (parse-read-string ":foo/bar"))) 15 | (is (= :user/foo-bar (binding [*ns* (the-ns 'user)] 16 | (parse-read-string "::foo-bar")))) 17 | (is (= :clojure.core/foo-bar 18 | (do (alias 'core 'clojure.core) 19 | (parse-read-string "::core/foo-bar")))) 20 | (is (= :*+!-_? (parse-read-string ":*+!-_?"))) 21 | (is (= :abc:def:ghi (parse-read-string ":abc:def:ghi"))) 22 | (is (= :abc.def/ghi (parse-read-string ":abc.def/ghi"))) 23 | (is (= :abc/def.ghi (parse-read-string ":abc/def.ghi"))) 24 | (is (= :abc:def/ghi:jkl.mno (parse-read-string ":abc:def/ghi:jkl.mno"))) 25 | (is (instance? clojure.lang.Keyword (parse-read-string ":alphabet"))) ) 26 | 27 | (deftest read-regex 28 | (is (= (str #"\[\]?(\")\\") 29 | (str (parse-read-string "#\"\\[\\]?(\\\")\\\\\""))))) 30 | 31 | (deftest read-quote 32 | (is (= ''foo (parse-read-string "'foo")))) 33 | 34 | (deftest read-syntax-quote 35 | (is (= '`user/foo (binding [*ns* (the-ns 'user)] 36 | (parse-read-string "`foo")))) 37 | (is (= '`+ (parse-read-string "`+"))) 38 | (is (= '`foo/bar (parse-read-string "`foo/bar"))) 39 | (is (= '`1 (parse-read-string "`1"))) 40 | (is (= '`(1 (~2 ~@(3))) (parse-read-string "`(1 (~2 ~@(3)))")))) 41 | 42 | (deftest read-deref 43 | (is (= '@foo (parse-read-string "@foo")))) 44 | 45 | (deftest read-var 46 | (is (= '(var foo) (parse-read-string "#'foo")))) 47 | 48 | (deftest read-fn 49 | (is (= '(fn* [] (foo bar baz)) (parse-read-string "#(foo bar baz)")))) 50 | 51 | (deftest read-arg 52 | (is (= 14 ((eval (parse-read-string "#(apply + % %1 %3 %&)")) 1 2 3 4 5)))) 53 | 54 | (deftest read-eval 55 | (is (= 3 (parse-read-string "#=(+ 1 2)")))) 56 | 57 | (deftest read-tagged 58 | ;; (is (= #inst "2010-11-12T13:14:15.666" 59 | ;; (parse-read-string "#inst \"2010-11-12T13:14:15.666\""))) 60 | ;; (is (= #inst "2010-11-12T13:14:15.666" 61 | ;; (parse-read-string "#inst\"2010-11-12T13:14:15.666\""))) 62 | ;; (is (= #uuid "550e8400-e29b-41d4-a716-446655440000" 63 | ;; (parse-read-string "#uuid \"550e8400-e29b-41d4-a716-446655440000\""))) 64 | ;; (is (= #uuid "550e8400-e29b-41d4-a716-446655440000" 65 | ;; (parse-read-string "#uuid\"550e8400-e29b-41d4-a716-446655440000\""))) 66 | (is (= (java.util.UUID/fromString "550e8400-e29b-41d4-a716-446655440000") 67 | (parse-read-string "#uuid \"550e8400-e29b-41d4-a716-446655440000\""))) 68 | (is (= (java.util.UUID/fromString "550e8400-e29b-41d4-a716-446655440000") 69 | (parse-read-string "#uuid\"550e8400-e29b-41d4-a716-446655440000\""))) 70 | (when *default-data-reader-fn* 71 | (let [my-unknown (fn [tag val] {:unknown-tag tag :value val})] 72 | (is (= {:unknown-tag 'foo :value 'bar} 73 | (binding [*default-data-reader-fn* my-unknown] 74 | (parse-read-string "#foo bar"))))))) 75 | 76 | 77 | (defrecord bar [baz buz]) 78 | 79 | 80 | (defrecord foo []) 81 | 82 | 83 | (deftest read-record 84 | (is (= (foo.) 85 | (parse-read-string "#codn.reader.reader_test.foo[]"))) 86 | (is (= (foo.) (parse-read-string "#codn.reader.reader_test.foo []"))) ;; not valid in clojure 87 | (is (= (foo.) (parse-read-string "#codn.reader.reader_test.foo{}"))) 88 | (is (= (assoc (foo.) :foo 'bar) (parse-read-string "#codn.reader.reader_test.foo{:foo bar}"))) 89 | 90 | (is (= (map->bar {}) (parse-read-string "#codn.reader.reader_test.bar{}"))) 91 | (is (= (bar. 1 nil) (parse-read-string "#codn.reader.reader_test.bar{:baz 1}"))) 92 | (is (= (bar. 1 nil) (parse-read-string "#codn.reader.reader_test.bar[1 nil]"))) 93 | (is (= (bar. 1 2) (parse-read-string "#codn.reader.reader_test.bar[1 2]")))) 94 | --------------------------------------------------------------------------------