diff options
author | Chouser <chouser@n01se.net> | 2009-01-12 07:54:16 +0000 |
---|---|---|
committer | Chouser <chouser@n01se.net> | 2009-01-12 07:54:16 +0000 |
commit | 2b1e44f04489b5d70faff703bf824f07b74432d2 (patch) | |
tree | 2bc686386c956028744f7411a4404f59efca9115 /clojurescript/tojs.clj | |
parent | 6e53cf73c313d2bf76f5ee3c9a847548665b32e2 (diff) |
Rearrange ClojureScript code for AOT compilation and its eventual place in clojure.contrib. Also fix major breaking errors for Clojure SVN 1205, but it's not well tested with this latest version.
Diffstat (limited to 'clojurescript/tojs.clj')
-rw-r--r-- | clojurescript/tojs.clj | 439 |
1 files changed, 0 insertions, 439 deletions
diff --git a/clojurescript/tojs.clj b/clojurescript/tojs.clj deleted file mode 100644 index 8c9f9e4d..00000000 --- a/clojurescript/tojs.clj +++ /dev/null @@ -1,439 +0,0 @@ -; Copyright (c) Chris Houser, Sep 2008. All rights reserved. -; The use and distribution terms for this software are covered by the -; Common Public License 1.0 (http://opensource.org/licenses/cpl.php) -; which can be found in the file CPL.TXT at the root of this distribution. -; By using this software in any fashion, you are agreeing to be bound by -; the terms of this license. -; You must not remove this notice, or any other, from this software. - -; Reads Clojure code and emits equivalent JavaScript - -(ns clojurescript.tojs - (:import (clojure.lang Compiler Compiler$C Compiler$BodyExpr - Compiler$DefExpr Compiler$InstanceMethodExpr) - (java.io BufferedReader InputStreamReader StringReader PrintWriter) - (java.net URLDecoder)) - (:use [clojure.contrib.command-line :only (with-command-line)]) - (:require (clojure.contrib [duck-streams :as ds] - [seq-utils :as su]))) - -(defn vstr [v] - (let [sb (StringBuilder.) - lvl (fn lvl [v] - (doseq [i v] - (if (vector? i) - (lvl i) - (.append sb (str i)))))] - (lvl v) - (str sb))) - -(def *debug-fn-names* true) -(def *debug-comments* true) -(def *eval-defmacro* true) - -; used internally -(def *has-recur*) -(def *local-names* {}) - -(defmulti tojs (fn [e ctx] (class e))) - -(defn fnmethod [fm maxm ctx] - (let [lm (into {} (for [[lb lb] (.locals fm)] - [lb (str (.name lb) "_" (.idx lb))])) - thisfn (first (filter #(= 0 (.idx %)) (keys lm))) - [body has-recur] (binding [*has-recur* false] - [(tojs (.body fm) - (merge-with merge ctx {:localmap lm})) - *has-recur*]) - mparm (into {} (for [p (.reqParms maxm)] [(.idx p) p])) - inits (concat - (when has-recur ["_cnt" "_rtn"]) - (vals (reduce dissoc lm - (conj (.reqParms fm) thisfn (.restParm fm)))) - (when (:fnname ctx) [(str (lm thisfn) "=arguments.callee")]) - (when (not= fm maxm) - (for [lb (.reqParms fm) - :when (not= (.name lb) (.name (mparm (.idx lb))))] - [(lm lb) "=arguments[" (dec (.idx lb)) "]"])) - (when-let [lb (.restParm fm)] - [(str (lm lb) "=clojure.JS.rest_args(this,arguments," - (count (.reqParms fm)) ")")]))] - (.reqParms maxm) - (vstr [(when (seq inits) - [(apply vector "var " (interpose "," inits)) ";\n"]) - (if has-recur - ["do{_cnt=0;_rtn=" - body - "\n}while(_cnt);return _rtn;"] - ["return (" body ")"])]))) - -(defmethod tojs clojure.lang.Compiler$FnExpr [e ctx] - (let [maxm (or (.variadicMethod e) - (-> (into (sorted-map) - (for [fm (.methods e) - :when (not= fm (.variadicMethod e))] - [(count (.reqParms fm)) fm])) - last val)) - manym (< 1 (count (.methods e))) - newctx (assoc ctx :fnname (.thisName e)) - [methods local-names] (binding [*local-names* *local-names*] - [(into {} (for [fm (.methods e)] - [fm (fnmethod fm maxm newctx)])) - *local-names*])] - (vstr [(when (.variadicMethod e) - ["clojure.JS.variadic(" (count (.reqParms maxm)) ","]) - "(function" - (when *debug-fn-names* - [" __" (.replaceAll (.name e) "[\\W_]+" "_")]) - "(" - (vec (interpose "," (for [lb (.reqParms maxm)] - [(.name lb) "_" (.idx lb)]))) - "){" - ;"\n//" (vec (interpose "," (vals local-names))) "\n" - (when manym - ["switch(arguments.length){" - (vec (for [[fm body] methods :when (not= fm maxm)] - ["\ncase " (count (.reqParms fm)) ":" body])) - "}"]) - "\n" - (methods maxm) "})" - (when (.variadicMethod e) - ")") - ]))) - -(defmethod tojs clojure.lang.Compiler$BodyExpr [e ctx] - (apply str (interpose ",\n" (map #(tojs % ctx) (.exprs e))))) - -(defmethod tojs clojure.lang.Compiler$LetExpr [e ctx] - (let [inits (vec (interpose ",\n" (for [bi (.bindingInits e)] - ["(" ((:localmap ctx) (.binding bi)) - "=" (tojs (.init bi) ctx) ")"])))] - (if (.isLoop e) - (binding [*has-recur* false] - (vstr ["((function" - (when *debug-fn-names* " __loop") - "(){var _rtn,_cnt;" - inits ";" - "do{_cnt=0;\n_rtn=" (tojs (.body e) ctx) - "}while(_cnt);return _rtn;})())"])) - (vstr ["(" inits ",\n" (tojs (.body e) ctx) ")"])))) - -(defmethod tojs clojure.lang.Compiler$VectorExpr [e ctx] - (vstr ["clojure.JS.lit_vector([" - (vec (interpose "," (map #(tojs % ctx) (.args e)))) - "])"])) - -(defn const-str [c] - (cond - (or (instance? Character c) - (string? c)) (pr-str (str c)) - (keyword? c) (str "clojure.core.keyword(\"" (namespace c) "\",\"" (name c) "\")") - (symbol? c) (str "clojure.core.symbol(\"" c "\")") - (class? c) (.getCanonicalName c) - (list? c) (vstr ["clojure.JS.lit_list([" - (vec (interpose "," (map const-str c))) - "])"]) - (fn? c) (str \" c \") - (instance? java.util.regex.Pattern c) (str "(/" - (.replace (str c) "/" "\\/") - "/)") - :else (str "(" c ")"))) - -(defmethod tojs clojure.lang.Compiler$ConstantExpr [e ctx] - (const-str (.v e))) - -(def js-reserved '#{import boolean short byte char class}) - -(defn var-munge [x] - (let [n (-> x str Compiler/munge (.replace "." "_DOT_"))] - (if (js-reserved (symbol n)) - (str n "_") - n))) - -(defn var-parts [e] - (let [{:keys [name ns]} ^(.var e)] - [(Compiler/munge (str (.getName ns))) (var-munge name)])) - -(defmethod tojs clojure.lang.Compiler$UnresolvedVarExpr [e ctx] - (vstr ["clojure.JS.resolveVar(\"" - (var-munge (name (.symbol e))) "\"," - (Compiler/munge (name (.name *ns*))) ")"])) - -(defmethod tojs clojure.lang.Compiler$VarExpr [e ctx] - (let [[vns vname] (var-parts e)] - (if (and (= vns "clojurescript.js") (#{"this"} vname)) - vname - (str vns "." vname)))) - -(defmethod tojs clojure.lang.Compiler$TheVarExpr [e ctx] - (let [[vns vname] (var-parts e)] - (str vns "._var_" vname))) - -(defmethod tojs clojure.lang.Compiler$AssignExpr [e ctx] - (let [target (.target e)] - (if (instance? clojure.lang.Compiler$InstanceFieldExpr target) - (vstr ["(" (tojs (.target target) ctx) "." - (var-munge (.fieldName target)) "=" (tojs (.val e) ctx) ")"]) - (let [[vns vname] (var-parts target)] - (str vns "._var_" vname ".set(" (tojs (.val e) ctx) ")"))))) - -(defmethod tojs clojure.lang.Compiler$DefExpr [e ctx] - (let [[vns vname] (var-parts e)] - (str "clojure.JS.def(" vns ",\"" vname "\"," (tojs (.init e) ctx) ")"))) - - -(defmethod tojs clojure.lang.Compiler$InvokeExpr [e ctx] - (vstr [(tojs (.fexpr e) ctx) - ".apply(null,[" - (vec (interpose "," (map #(tojs % ctx) (.args e)))) - "])"])) - -(defmethod tojs clojure.lang.Compiler$LocalBindingExpr [e ctx] - (let [local-name ((:localmap ctx) (.b e))] - (set! *local-names* (assoc *local-names* (.b e) local-name)) - local-name)) - -(defmethod tojs clojure.lang.Compiler$NilExpr [e ctx] - "null") - -(defmethod tojs clojure.lang.Compiler$EmptyExpr [e ctx] - (str (.getCanonicalName (class (.coll e))) ".EMPTY")) - -(defmethod tojs clojure.lang.Compiler$StringExpr [e ctx] - (const-str (.str e))) - -(defmethod tojs clojure.lang.Compiler$KeywordExpr [e ctx] - (const-str (.k e))) - -(defmethod tojs clojure.lang.Compiler$StaticFieldExpr [e ctx] - (str "clojure.JS.getOrRun(" (.getCanonicalName (.c e)) ",\"" - (var-munge (.fieldName e)) "\")")) - -(defmethod tojs clojure.lang.Compiler$StaticMethodExpr [e ctx] - (vstr [(.getCanonicalName (.c e)) "." (.methodName e) "(" - (vec (interpose "," (map #(tojs % ctx) (.args e)))) - ")"])) - -(defmethod tojs clojure.lang.Compiler$NewExpr [e ctx] - (vstr ["(new " (.getCanonicalName (.c e)) "(" - (vec (interpose "," (map #(tojs % ctx) (.args e)))) - "))"])) - -(defmethod tojs clojure.lang.Compiler$InstanceMethodExpr [e ctx] - (vstr ["(" (tojs (.target e) ctx) ")." (var-munge (.methodName e)) - "(" (vec (interpose "," (map #(tojs % ctx) (.args e)))) ")"])) - -(defmethod tojs clojure.lang.Compiler$InstanceFieldExpr [e ctx] - (vstr ["clojure.JS.getOrRun(" (tojs (.target e) ctx) ",\"" - (var-munge (.fieldName e)) "\")"])) - -(defmethod tojs clojure.lang.Compiler$IfExpr [e ctx] - (str "((" (tojs (.testExpr e) ctx) - ")?(" (tojs (.thenExpr e) ctx) - "):(" (tojs (.elseExpr e) ctx) "))")) - -(defmethod tojs clojure.lang.Compiler$RecurExpr [e ctx] - (set! *has-recur* true) - (vstr ["(_cnt=1,_rtn=[" - (vec (interpose "," (map #(tojs % ctx) (.args e)))) - "]" - (vec (map #(str "," ((:localmap ctx) %1) "=_rtn[" %2 "]") - (.loopLocals e) (iterate inc 0))) - ")"])) - -(defmethod tojs clojure.lang.Compiler$MapExpr [e ctx] - (vstr ["clojure.core.hash_map(" - (vec (interpose "," (map #(tojs % ctx) (.keyvals e)))) - ")"])) - -(defmethod tojs clojure.lang.Compiler$SetExpr [e ctx] - (vstr ["clojure.core.hash_set(" - (vec (interpose "," (map #(tojs % ctx) (.keys e)))) - ")"])) - -(defmethod tojs clojure.lang.Compiler$BooleanExpr [e ctx] - (if (.val e) "true" "false")) - -(defmethod tojs clojure.lang.Compiler$ThrowExpr [e ctx] - (vstr ["(function" - (when *debug-fn-names* " __throw") - "(){throw " (tojs (.excExpr e) ctx) "})()"])) - -(defmethod tojs clojure.lang.Compiler$TryExpr [e ctx] - (vstr ["(function" - (when *debug-fn-names* " __try") - "(){try{var _rtn=(" - (tojs (.tryExpr e) ctx) - ")}" - (when (seq (.catchExprs e)) - (when (not= 1 (count (.catchExprs e))) - (throw (Exception. "tojs only supports one catch clause per try"))) - (let [cc (first (.catchExprs e))] - ["\ncatch(" ((:localmap ctx) (.lb cc)) "){_rtn=" - (tojs (.handler cc) ctx) - "}"])) - (when (.finallyExpr e) - ["\nfinally{" - (tojs (.finallyExpr e) ctx) - "}"]) - "return _rtn})()"])) - - -(defmulti toclj class) -(defmethod toclj clojure.lang.Compiler$KeywordExpr [e] (.k e)) -(defmethod toclj clojure.lang.Compiler$StringExpr [e] (.str e)) -(defmethod toclj clojure.lang.Compiler$ConstantExpr [e] (.v e)) - - -(def skip-def '#{;-- implemented directly in clj.js - seq instance? assoc apply refer first rest import - hash-map count find keys vals get class contains? - print-method class? number? string? integer? nth - to-array cons keyword symbol load - ;-- not supported yet - make-array to-array-2d re-pattern re-matcher re-groups - re-seq re-matches re-find format - ;-- macros defined without using defmacro - let loop fn defn defmacro - ;-- will probably never be supported in clojurescript - eval resolve ns-resolve await await-for macroexpand - macroexpand-1 load-reader load-string special-symbol? - bigint bigdec floats doubles ints longs float-array - double-array int-array long-array aset-int - aset-long aset-boolean aset-float aset-double - aset-short aset-char aset-byte slurp seque - decimal? float? pmap primitives-classnames}) - -(def skip-method #{"java.lang.Class"}) - -(defn formtojs [f] - (when-not (and (coll? f) (= 'definline (first f))) - (binding [*allow-unresolved-vars* true - *private-compiler-loader* (clojure.lang.RT/makeClassLoader)] - (let [expr (Compiler/analyze Compiler$C/STATEMENT `((fn [] ~f))) - mainexpr (-> expr .fexpr .methods first .body .exprs first) - defmacro? (and (instance? Compiler$BodyExpr mainexpr) - (instance? Compiler$DefExpr (first (.exprs mainexpr))) - (instance? Compiler$InstanceMethodExpr (second (.exprs mainexpr))) - (= "setMacro" (.methodName (second (.exprs mainexpr)))))] - (if defmacro? - (when *eval-defmacro* - (eval f) - nil) - (when-not (or (and (instance? Compiler$DefExpr mainexpr) - (skip-def (:name ^(.var mainexpr)))) - (and (instance? Compiler$InstanceMethodExpr mainexpr) - (or (= "setMacro" (.methodName mainexpr)) - (and (= "addMethod" (.methodName mainexpr)) - (skip-method (tojs (first (.args mainexpr)) - nil))))) - (and (instance? Compiler$BodyExpr mainexpr) - (instance? Compiler$DefExpr (first (.exprs mainexpr))) - (instance? Compiler$InstanceMethodExpr (second (.exprs mainexpr))) - (= "setMacro" (.methodName (second (.exprs mainexpr)))))) - (tojs expr {:localmap {}}))))))) - -(defn filetojs [filename] - (let [reader (java.io.PushbackReader. (ds/reader filename))] - (binding [*ns* (create-ns 'user)] - (loop [] - (let [f (read reader false reader false)] - (when-not (identical? f reader) - (if-let [js (formtojs f)] - (do - (when *debug-comments* - (println "\n//======") - (print "//") - (prn f) - (println "//---")) - (println (str js ";")) - (when (and (coll? f) - (or (= 'ns (first f)) - (= 'in-ns (first f)))) - (eval f))) - (when *debug-comments* - (print "// Skipping: ") - (prn f))) - (recur))))))) - -(defn simple-tests [] - (println (formtojs - '(defn foo - ([a b c & d] (prn 3 a b c)) - ([c] - ;(String/asd "hello") - ;(.foo 55) - (let [[a b] [1 2]] - (prn a b c) - "hi"))))) - - (println (formtojs - '(defn foo [a] - (prn "hi") - (let [a 5] - (let [a 10] - (prn "yo") - (prn a)) - (prn a)) - (prn a)))) - - (println (formtojs - '(defn x [] (conj [] (loop [i 5] (if (pos? i) (recur (- i 2)) i)))))) - - ;(println (formtojs '(binding [*out* 5] (set! *out* 10)))) - (println (formtojs '(.replace "a/b/c" "/" "."))) - (println (formtojs '(.getName ":foo"))) - (println (formtojs '(list '(1 "str" 'sym :key) 4 "str2" 6 #{:set 9 8}))) - (println (formtojs '(fn forever[] (forever)))) - (println (formtojs '(fn forever[] (loop [] (recur)))))) - -(defn start-server [port] - ;(println "Opening port" port) - (loop [server (java.net.ServerSocket. port)] ; should bind only to 127.0.0.1 - (send-off (agent (.accept server)) - (fn [socket] - (with-open [socket socket] - (binding [*debug-fn-names* false - *debug-comments* false - *eval-defmacro* false - *out* (-> socket .getOutputStream ds/writer)] - (try - (print "HTTP/1.0 200 OK\nContent-Type: text/javascript\n\n") - (let [line1 (-> socket .getInputStream ds/reader .readLine) - [_ url] (re-find #"^GET /\?(.*?) HTTP" line1) - codestr (URLDecoder/decode url) - js (with-out-str (filetojs (StringReader. codestr)))] - (println "jsrepl.state('compiled');try{") - (println "jsrepl.lastval=" js ) - (println "jsrepl.state('done');}catch(e){jsrepl.err(e)};")) - (catch Exception e - (if (= (.getMessage e) "EOF while reading") - (println "jsrepl.state('incomplete');") - (let [trace (with-out-str - (.printStackTrace e (PrintWriter. *out*)))] - (println "jsrepl.state('error',\"" - (.replace trace "\n" "\\n") "\");"))))))))) - (recur server))) - -(defn mkcore [] - (binding [*out* (ds/writer "core.js")] - (doseq [file ["clojure/core.clj" "clojure/core-print.clj"]] - (filetojs (.getResourceAsStream (clojure.lang.RT/baseLoader) file))))) - -(defn -main [& args] - (with-command-line args - "tojs -- Compile ClojureScript to JavaScript" - [[simple? "Runs some simple built-in tests"] - [serve "Starts a repl server on the given port" 8081] - [mkcore? "Generates a core.js file"] - [v? "Includes extra fn names and comments in js"] - filenames] - (binding [*debug-fn-names* v? *debug-comments* v?] - (cond - simple? (simple-tests) - serve (start-server (Integer/parseInt serve)) - mkcore? (mkcore) - :else (doseq [filename filenames] - (filetojs filename)))))) - -;(when-not *compile-files* (apply -main *command-line-args*)) |