From f1c5a9a85fea7f17035615adf40c3e5fff7d05fc Mon Sep 17 00:00:00 2001
From: Roman Scherer <roman.scherer@burningswell.com>
Date: Thu, 19 Jul 2012 16:09:52 +0200
Subject: [PATCH] Implemented conditional compilation.

---
 src/clj/cljs/closure.clj        |  3 +-
 src/clj/cljs/repl.clj           |  3 +-
 src/cljs/cljs/core.cljs         |  4 +++
 src/cljs/cljs/reader.cljs       | 26 +++++++++++++-
 test/cljs/cljs/reader_test.cljs | 76 +++++++++++++++++++++++++++++++++++++++++
 5 files changed, 109 insertions(+), 3 deletions(-)

diff --git a/src/clj/cljs/closure.clj b/src/clj/cljs/closure.clj
index 62352f1..679b361 100644
--- a/src/clj/cljs/closure.clj
+++ b/src/clj/cljs/closure.clj
@@ -873,7 +873,8 @@
                   (:static-fns opts)
                   ana/*cljs-static-fns*)
               ana/*cljs-warn-on-undeclared*
-              (true? (opts :warnings))]
+              (true? (opts :warnings))
+              *features* #{:clojurescript}]
       (let [compiled (-compile source all-opts)
             compiled (concat
                       (if (coll? compiled) compiled [compiled])
diff --git a/src/clj/cljs/repl.clj b/src/clj/cljs/repl.clj
index 6d0bcab..060d155 100644
--- a/src/clj/cljs/repl.clj
+++ b/src/clj/cljs/repl.clj
@@ -155,7 +155,8 @@
   (prn "Type: " :cljs/quit " to quit")
   (binding [ana/*cljs-ns* 'cljs.user
             *cljs-verbose* verbose
-            ana/*cljs-warn-on-undeclared* warn-on-undeclared]
+            ana/*cljs-warn-on-undeclared* warn-on-undeclared
+            *features* #{:clojurescript}]
     (let [env {:context :statement :locals {}}
           special-fns (merge default-special-fns special-fns)
           is-special-fn? (set (keys special-fns))]
diff --git a/src/cljs/cljs/core.cljs b/src/cljs/cljs/core.cljs
index 8741472..bf4a873 100644
--- a/src/cljs/cljs/core.cljs
+++ b/src/cljs/cljs/core.cljs
@@ -6946,3 +6946,7 @@ reduces them without incurring seq initialization"
   (-hash [this]
     (goog.string/hashCode (pr-str this))))
 
+(def
+ ^{:dynamic true
+   :doc "Set of platform specific features."}
+ *features* #{:clojurescript})
diff --git a/src/cljs/cljs/reader.cljs b/src/cljs/cljs/reader.cljs
index d6b7e91..58701ef 100644
--- a/src/cljs/cljs/reader.cljs
+++ b/src/cljs/cljs/reader.cljs
@@ -260,7 +260,7 @@ nil if the end of stream has been reached")
   (let [ch (read-char rdr)
         dm (dispatch-macros ch)]
     (if dm
-      (dm rdr _)
+      (dm rdr ch)
       (if-let [obj (maybe-read-tagged-type rdr ch)]
         obj
         (reader-error rdr "No dispatch macro for " ch)))))
@@ -381,6 +381,28 @@ nil if the end of stream has been reached")
   (read rdr true nil true)
   rdr)
 
+(defn- supported-feature? [expr]
+  (cond
+   (symbol? expr)
+   (contains? *features* (keyword expr))
+   (list? expr)
+   (let [[op & r] expr]
+     (condp = op
+       'and (or (empty? r)
+                (every? supported-feature? r))
+       'not (not (supported-feature? (first r)))
+       'or (and (not (empty? r))
+                (not (not-any? supported-feature? r)))
+       (throw (str "Invalid feature expression operator: " op))))
+   :else (throw (str "Invalid feature expression: " expr))))
+
+(defn read-feature
+  [rdr mode]
+  (let [test (read rdr true nil true)
+        expr (read rdr true nil true)]
+    (if (= (supported-feature? test) (= mode \+))
+      expr rdr)))
+
 (defn macros [c]
   (cond
    (identical? c \") read-string*
@@ -410,6 +432,8 @@ nil if the end of stream has been reached")
    (identical? s "\"") read-regex
    (identical? s"!") read-comment
    (identical? s "_") read-discard
+   (identical? s "+") read-feature
+   (identical? s "-") read-feature
    :else nil))
 
 (defn read
diff --git a/test/cljs/cljs/reader_test.cljs b/test/cljs/cljs/reader_test.cljs
index 8506acb..e40d6d3 100644
--- a/test/cljs/cljs/reader_test.cljs
+++ b/test/cljs/cljs/reader_test.cljs
@@ -121,4 +121,80 @@
               (catch js/Error e :ok))]
       (assert (= r :ok) (str "Failed to throw reader error for: " unicode-error))))
 
+  ;; CONDITIONAL COMPILATION / FEATURE EXPRESSIONS
+
+  ;; #+ with symbol
+  (assert (= ["x"] [#+clojurescript "x"]))
+  (assert (= [] [#+clojure "x"]))
+  (assert (= ["x"] (reader/read-string "[#+clojurescript \"x\"]")))
+  (assert (= [] (reader/read-string "[#+clojure \"x\"]")))
+
+  ;; #+ with and
+  (assert (= ["x"] [#+(and) "x"]))
+  (assert (= ["x"] [#+(and clojurescript) "x"]))
+  (assert (= ["x"] [#+(and clojurescript clojurescript) "x"]))
+  (assert (= [] [#+(and clojurescript clojure) "x"]))
+  (assert (= ["x"] (reader/read-string "[#+(and) \"x\"]")))
+  (assert (= ["x"] (reader/read-string "[#+(and clojurescript) \"x\"]")))
+  (assert (= ["x"] (reader/read-string "[#+(and clojurescript clojurescript) \"x\"]")))
+  (assert (= [] (reader/read-string "[#+(and clojurescript clojure) \"x\"]")))
+
+  ;; #+ with or
+  (assert (= [] [#+(or) "x"]))
+  (assert (= ["x"] [#+(or clojurescript) "x"]))
+  (assert (= ["x"] [#+(or clojurescript clojure) "x"]))
+  (assert (= [] [#+(or clojure) "x"]))
+  (assert (= [] (reader/read-string "[#+(or) \"x\"]")))
+  (assert (= ["x"] (reader/read-string "[#+(or clojurescript) \"x\"]")))
+  (assert (= ["x"] (reader/read-string "[#+(or clojurescript clojure) \"x\"]")))
+  (assert (= [] (reader/read-string "[#+(or clojure) \"x\"]")))
+
+  ;; #+ with not
+  (assert (= ["x"] [#+(not clojure) "x"]))
+  (assert (= ["x"] (reader/read-string "[#+(not clojure) \"x\"]")))
+
+  ;; #+ with nested expressions
+  (assert (= [] [#+(not (and clojurescript (or clojure (not clojure)))) "x"]))
+  (assert (= ["x"] [#+(and clojurescript (or clojure (not clojure))) "x"]))
+  (assert (= ["x"] [#+(or clojure (or #+clojurescript clojurescript)) "x"]))
+  (assert (= [] (reader/read-string "[#+(not (and clojurescript (or clojure (not clojure)))) \"x\"]")))
+  (assert (= ["x"] (reader/read-string "[#+(and clojurescript (or clojure (not clojure))) \"x\"]")))
+  (assert (= ["x"] (reader/read-string "[#+(or clojure (or #+clojurescript clojurescript)) \"x\"]")))
+
+  ;; #- with symbol
+  (assert (= [] [#-clojurescript "x"]))
+  (assert (= ["x"] [#-clojure "x"]))
+  (assert (= [] (reader/read-string "[#-clojurescript \"x\"]")))
+  (assert (= ["x"] (reader/read-string "[#-clojure \"x\"]")))
+
+  ;; #- with not
+  (assert (= ["x"] [#-(not clojurescript) "x"]))
+  (assert (= ["x"] (reader/read-string "[#-(not clojurescript) \"x\"]")))
+
+  ;; #- with and
+  (assert (= [] [#-(and) "x"]))
+  (assert (= [] [#-(and clojurescript clojurescript) "x"]))
+  (assert (= ["x"] [#-(and clojurescript clojure) "x"]))
+  (assert (= [] (reader/read-string "[#-(and) \"x\"]")))
+  (assert (= [] (reader/read-string "[#-(and clojurescript clojurescript) \"x\"]")))
+  (assert (= ["x"] (reader/read-string "[#-(and clojurescript clojure) \"x\"]")))
+
+  ;; #- with or
+  (assert (= ["x"] [#-(or) "x"]))
+  (assert (= [] [#-(or clojurescript) "x"]))
+  (assert (= [] [#-(or clojurescript clojure) "x"]))
+  (assert (= ["x"] [#-(or clojure) "x"]))
+  (assert (= ["x"] (reader/read-string "[#-(or) \"x\"]")))
+  (assert (= [] (reader/read-string "[#-(or clojurescript) \"x\"]")))
+  (assert (= [] (reader/read-string "[#-(or clojurescript clojure) \"x\"]")))
+  (assert (= ["x"] (reader/read-string "[#-(or clojure) \"x\"]")))
+
+  ;; #- with nested expressions
+  (assert (= ["x"] [#-(not (and clojurescript (or clojure (not clojure)))) "x"]))
+  (assert (= [] [#-(and clojurescript (or clojure (not clojure))) "x"]))
+  (assert (= ["x"] [#-(or clojure (or #-clojurescript clojurescript)) "x"]))  
+  (assert (= ["x"] (reader/read-string "[#-(not (and clojurescript (or clojure (not clojure)))) \"x\"]")))
+  (assert (= [] (reader/read-string "[#-(and clojurescript (or clojure (not clojure))) \"x\"]")))
+  (assert (= ["x"] (reader/read-string "[#-(or clojure (or #-clojurescript clojurescript)) \"x\"]")))
+
   :ok)
-- 
1.7.11.2

