Permalink
Browse files

First pass at Real Keywords(tm) in ClojureScript

Cleanup debug js output

Collapse nested ifs into a cond
  • Loading branch information...
1 parent 30a27d2 commit 2cef52840fcd9d88f077d374cc81e50c7e645b9f @sgrove sgrove committed with swannodette Aug 27, 2013
Showing with 104 additions and 40 deletions.
  1. +27 −0 src/clj/cljs/analyzer.clj
  2. +6 −0 src/clj/cljs/closure.clj
  3. +34 −7 src/clj/cljs/compiler.clj
  4. +37 −33 src/cljs/cljs/core.cljs
View
@@ -24,6 +24,9 @@
(def ^:dynamic *cljs-macros-is-classpath* true)
(def -cljs-macros-loaded (atom false))
+(def ^:dynamic *real-keywords* true)
+(def ^:dynamic *constant-table* (atom {}))
+
(def ^:dynamic *cljs-warnings*
{:undeclared false
:redef true
@@ -33,6 +36,28 @@
:fn-deprecated true
:protocol-deprecated true})
+(def keyword_counter (atom 0))
+
+(defn genid
+ "Returns a new symbol with a unique name. If a prefix string is
+ supplied, the name is prefix# where # is some unique number. If
+ prefix is not supplied, the prefix is 'K__'."
+ ([] (genid "K__"))
+ ([prefix-string]
+ (when (nil? keyword_counter)
+ (set! keyword_counter (atom 0)))
+ (symbol (str prefix-string (swap! keyword_counter inc)))))
+
+(defn reset-constant-table! []
+ (reset! *constant-table* {}))
+
+(defn register-constant! [k]
+ (swap! *constant-table*
+ (fn [table]
+ (if (get table k)
+ table
+ (assoc table k (genid))))))
+
(defonce namespaces (atom '{cljs.core {:name cljs.core}
cljs.user {:name cljs.user}}))
@@ -227,6 +252,8 @@
(defn analyze-keyword
[env sym]
+ (when *real-keywords*
+ (register-constant! sym))
{:op :constant :env env
:form sym})
View
@@ -642,6 +642,9 @@
(:provides %)
(:requires %))
(assoc :group (:group %))) required-js)
+ [(when ana/*real-keywords*
+ (let [url (to-url (str (output-directory opts) "/constants_table.js"))]
+ (javascript-file nil url url ["constants-table"] ["cljs.core"] nil nil)))]
required-cljs
inputs)))))
@@ -953,6 +956,9 @@
ana/*cljs-warnings*
(assoc ana/*cljs-warnings* :undeclared (true? (opts :warnings)))]
(let [compiled (-compile source all-opts)
+ ; Cause the constants table file to be written
+ const-table (when ana/*real-keywords*
+ (comp/emit-constants-table-to-file @ana/*constant-table* (str (output-directory all-opts) "/constants_table.js")))
js-sources (concat
(apply add-dependencies all-opts
(concat (if (coll? compiled) compiled [compiled])
View
@@ -150,11 +150,14 @@
(emits \/ (.replaceAll (re-matcher #"/" pattern) "\\\\/") \/ flags)))
(defmethod emit-constant clojure.lang.Keyword [x]
- (emits \" "\\uFDD0" \:
- (if (namespace x)
- (str (namespace x) "/") "")
- (name x)
- \"))
+ (if ana/*real-keywords*
+ (let [value (get @ana/*constant-table* x)]
+ (emits value))
+ (emits \" "\\uFDD0" \:
+ (if (namespace x)
+ (str (namespace x) "/") "")
+ (name x)
+ \")))
(def ^:const goog-hash-max 0x100000000)
@@ -627,7 +630,9 @@
(emits (first args) "." pimpl "(" (comma-sep args) ")"))
keyword?
- (emits "(new cljs.core.Keyword(" f ")).call(" (comma-sep (cons "null" args)) ")")
+ (if ana/*real-keywords*
+ (emits f ".call(" (comma-sep (cons "null" args)) ")")
+ (emits "(new cljs.core.Keyword(" f ")).call(" (comma-sep (cons "null" args)) ")"))
variadic-invoke
(let [mfa (:max-fixed-arity variadic-invoke)]
@@ -770,7 +775,10 @@
(merge
{:ns (or ns-name 'cljs.user)
:provides [ns-name]
- :requires (if (= ns-name 'cljs.core) (set (vals deps)) (conj (set (vals deps)) 'cljs.core))
+ :requires (if (= ns-name 'cljs.core)
+ (set (vals deps))
+ (set (remove nil? (conj (set (vals deps)) 'cljs.core
+ (when ana/*real-keywords* 'constants-table)))))
:file dest
:source-file src
:lines @*cljs-gen-line*}
@@ -905,6 +913,25 @@
;; files will be compiled to js.
)
+(defn emit-constants-table [table]
+ (doseq [[keyword value] table]
+ (let [ns (namespace keyword)
+ name (name keyword)]
+ (emits value "=new cljs.core.Keyword(")
+ (emit-constant ns)
+ (emits ",")
+ (emit-constant name)
+ (emits ",")
+ (emit-constant (if ns
+ (str ns "/" name)
+ name))
+ (emits ");\n"))))
+
+(defn emit-constants-table-to-file [table dest]
+ (with-open [out ^java.io.Writer (io/make-writer dest {})]
+ (binding [*out* out]
+ (emit-constants-table table))))
+
(comment
;;the new way - use the REPL!!
View
@@ -1192,9 +1192,7 @@ reduces them without incurring seq initialization"
(defn ^boolean boolean [x]
(if x true false))
-(defn ^boolean keyword? [x]
- (and ^boolean (goog/isString x)
- (identical? (.charAt x 0) \uFDD0)))
+(declare keyword?)
(defn ^boolean ifn? [f]
(or (fn? f) (satisfies? IFn f)))
@@ -1766,7 +1764,7 @@ reduces them without incurring seq initialization"
one arg, returns the concatenation of the str values of the args."
([] "")
([x] (cond
- (keyword? x) (str* ":" (. x (substring 2 (alength x))))
+ (keyword? x) (str* ":" (.-fqn x))
(nil? x) ""
:else (. x (toString))))
([x & ys]
@@ -1794,13 +1792,7 @@ reduces them without incurring seq initialization"
args)]
(apply gstring/format fmt args)))
-(defn keyword
- "Returns a Keyword with the given namespace and name. Do not use :
- in the keyword strings, it will be added automatically."
- ([name] (cond (keyword? name) name
- (symbol? name) (str* "\uFDD0" ":" (cljs.core/name name))
- :else (str* "\uFDD0" ":" name)))
- ([ns name] (keyword (str* ns "/" name))))
+(declare keyword)
(defn- equiv-sequential
"Assumes x is sequential. Returns true if x equals y, otherwise
@@ -2045,36 +2037,48 @@ reduces them without incurring seq initialization"
IHash
(-hash [o] (goog.string/hashCode o)))
-(deftype Keyword [k]
+(deftype Keyword [ns name fqn ^:mutable _hash]
+ Object
+ (toString [_] fqn)
+ IEquiv
+ (-equiv [_ other]
+ (if (instance? Keyword other)
+ (identical? fqn (.-fqn other))
+ false))
IFn
- (invoke [_ coll]
+ (invoke [kw coll]
(when-not (nil? coll)
(when (satisfies? ILookup coll)
- (-lookup coll k nil))))
- (invoke [_ coll not-found]
+ (-lookup coll kw nil))))
+ (invoke [kw coll not-found]
(if (nil? coll)
not-found
(when (satisfies? ILookup coll)
- (-lookup coll k not-found)))))
-
-;;hrm
-(extend-type js/String
- IFn
- (-invoke
- ([this coll]
- (get coll (.toString this)))
- ([this coll not-found]
- (get coll (.toString this) not-found))))
+ (-lookup coll kw not-found))))
+ IHash
+ (-hash [_]
+ ; This was checking if _hash == -1, should it stay that way?
+ (if (nil? _hash)
+ (do
+ (set! _hash (hash-combine (hash ns) (hash name)))
+ _hash)
+ _hash))
+ INamed
+ (-name [_] name)
+ (-namespace [_] ns)
+ IPrintWithWriter
+ (-pr-writer [o writer _] (-write writer (str ":" fqn))))
-(set! js/String.prototype.apply
- (fn
- [s args]
- (if (< (alength args) 2)
- (get (aget args 0) s)
- (get (aget args 0) s (aget args 1)))))
+(defn ^boolean keyword? [x]
+ (js* "~{} instanceof ~{}" x Keyword))
-; could use reify
-;;; LazySeq ;;;
+(defn keyword
+ "Returns a Keyword with the given namespace and name. Do not use :
+ in the keyword strings, it will be added automatically."
+ ([name] (cond (keyword? name) (Keyword. nil name name nil)
+ (symbol? name) (Keyword. nil (cljs.core/name name) (cljs.core/name name) nil)
+ :else (Keyword. nil name name nil)))
+ ([ns name] (Keyword. ns name (str (when ns (str ns "/")) name) nil)))
(defn- lazy-seq-value [lazy-seq]
(let [x (.-x lazy-seq)]

0 comments on commit 2cef528

Please sign in to comment.