clementine 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (139) hide show
  1. data/.gitignore +1 -0
  2. data/Gemfile +4 -2
  3. data/LICENSE.txt +22 -0
  4. data/README.md +11 -6
  5. data/Rakefile +22 -0
  6. data/clementine.gemspec +2 -1
  7. data/ext/clojure-clojurescript-bef56a7/.gitignore +13 -0
  8. data/ext/clojure-clojurescript-bef56a7/Clojurescript.iml +12 -0
  9. data/ext/clojure-clojurescript-bef56a7/README.md +29 -0
  10. data/ext/clojure-clojurescript-bef56a7/benchmark/cljs/benchmark_runner.cljs +155 -0
  11. data/ext/clojure-clojurescript-bef56a7/bin/cljsc +21 -0
  12. data/ext/clojure-clojurescript-bef56a7/bin/cljsc.bat +18 -0
  13. data/{vendor/assets → ext/clojure-clojurescript-bef56a7}/bin/cljsc.clj +0 -0
  14. data/ext/clojure-clojurescript-bef56a7/devnotes/README.org +35 -0
  15. data/ext/clojure-clojurescript-bef56a7/devnotes/bcrepl.org +13 -0
  16. data/ext/clojure-clojurescript-bef56a7/devnotes/cljs.org +500 -0
  17. data/ext/clojure-clojurescript-bef56a7/devnotes/corelib.org +583 -0
  18. data/ext/clojure-clojurescript-bef56a7/devnotes/day1.org +203 -0
  19. data/ext/clojure-clojurescript-bef56a7/devnotes/day2.org +44 -0
  20. data/ext/clojure-clojurescript-bef56a7/devnotes/talk.org +126 -0
  21. data/ext/clojure-clojurescript-bef56a7/devnotes/testing +13 -0
  22. data/ext/clojure-clojurescript-bef56a7/devnotes/todo.org +121 -0
  23. data/ext/clojure-clojurescript-bef56a7/epl-v10.html +261 -0
  24. data/ext/clojure-clojurescript-bef56a7/pom.template.xml +88 -0
  25. data/ext/clojure-clojurescript-bef56a7/samples/dom/.gitignore +2 -0
  26. data/ext/clojure-clojurescript-bef56a7/samples/dom/src/dom/test.cljs +48 -0
  27. data/ext/clojure-clojurescript-bef56a7/samples/dom/test.html +30 -0
  28. data/ext/clojure-clojurescript-bef56a7/samples/hello-js/.gitignore +2 -0
  29. data/ext/clojure-clojurescript-bef56a7/samples/hello-js/README.md +53 -0
  30. data/ext/clojure-clojurescript-bef56a7/samples/hello-js/externed-lib.js +7 -0
  31. data/ext/clojure-clojurescript-bef56a7/samples/hello-js/externs.js +3 -0
  32. data/ext/clojure-clojurescript-bef56a7/samples/hello-js/hello-extern.html +14 -0
  33. data/ext/clojure-clojurescript-bef56a7/samples/hello-js/hello-js-dev.html +18 -0
  34. data/ext/clojure-clojurescript-bef56a7/samples/hello-js/hello-js.html +17 -0
  35. data/ext/clojure-clojurescript-bef56a7/samples/hello-js/my-external-lib.js +3 -0
  36. data/ext/clojure-clojurescript-bef56a7/samples/hello-js/src/hello-js/core.cljs +9 -0
  37. data/ext/clojure-clojurescript-bef56a7/samples/hello-js/src/hello-js/extern-example.cljs +5 -0
  38. data/ext/clojure-clojurescript-bef56a7/samples/hello/.gitignore +2 -0
  39. data/ext/clojure-clojurescript-bef56a7/samples/hello/README.md +34 -0
  40. data/ext/clojure-clojurescript-bef56a7/samples/hello/hello-dev.html +18 -0
  41. data/ext/clojure-clojurescript-bef56a7/samples/hello/hello.html +13 -0
  42. data/ext/clojure-clojurescript-bef56a7/samples/hello/src/hello/core.cljs +8 -0
  43. data/ext/clojure-clojurescript-bef56a7/samples/hello/src/hello/foo/bar.cljs +4 -0
  44. data/ext/clojure-clojurescript-bef56a7/samples/nodehello.cljs +18 -0
  45. data/ext/clojure-clojurescript-bef56a7/samples/nodels.cljs +17 -0
  46. data/ext/clojure-clojurescript-bef56a7/samples/repl/.gitignore +2 -0
  47. data/ext/clojure-clojurescript-bef56a7/samples/repl/README.md +101 -0
  48. data/ext/clojure-clojurescript-bef56a7/samples/repl/index.html +27 -0
  49. data/ext/clojure-clojurescript-bef56a7/samples/repl/src/repl/test.cljs +73 -0
  50. data/ext/clojure-clojurescript-bef56a7/samples/twitterbuzz/.gitignore +2 -0
  51. data/ext/clojure-clojurescript-bef56a7/samples/twitterbuzz/README.md +42 -0
  52. data/ext/clojure-clojurescript-bef56a7/samples/twitterbuzz/index-advanced.html +80 -0
  53. data/ext/clojure-clojurescript-bef56a7/samples/twitterbuzz/index.html +88 -0
  54. data/ext/clojure-clojurescript-bef56a7/samples/twitterbuzz/reset.css +48 -0
  55. data/ext/clojure-clojurescript-bef56a7/samples/twitterbuzz/src/twitterbuzz/anneal.cljs +66 -0
  56. data/ext/clojure-clojurescript-bef56a7/samples/twitterbuzz/src/twitterbuzz/core.cljs +307 -0
  57. data/ext/clojure-clojurescript-bef56a7/samples/twitterbuzz/src/twitterbuzz/dom-helpers.cljs +95 -0
  58. data/ext/clojure-clojurescript-bef56a7/samples/twitterbuzz/src/twitterbuzz/layout.cljs +100 -0
  59. data/ext/clojure-clojurescript-bef56a7/samples/twitterbuzz/src/twitterbuzz/leaderboard.cljs +40 -0
  60. data/ext/clojure-clojurescript-bef56a7/samples/twitterbuzz/src/twitterbuzz/radial.cljs +91 -0
  61. data/ext/clojure-clojurescript-bef56a7/samples/twitterbuzz/src/twitterbuzz/showgraph.cljs +121 -0
  62. data/ext/clojure-clojurescript-bef56a7/samples/twitterbuzz/src/twitterbuzz/timeline.cljs +39 -0
  63. data/ext/clojure-clojurescript-bef56a7/samples/twitterbuzz/style.css +301 -0
  64. data/ext/clojure-clojurescript-bef56a7/samples/twitterbuzz/test_data.txt +1 -0
  65. data/ext/clojure-clojurescript-bef56a7/samples/twitterbuzz/tweet_maps.txt +1 -0
  66. data/ext/clojure-clojurescript-bef56a7/script/benchmark +30 -0
  67. data/ext/clojure-clojurescript-bef56a7/script/bootstrap +70 -0
  68. data/ext/clojure-clojurescript-bef56a7/script/browser-repl +16 -0
  69. data/ext/clojure-clojurescript-bef56a7/script/build +59 -0
  70. data/ext/clojure-clojurescript-bef56a7/script/clean +5 -0
  71. data/ext/clojure-clojurescript-bef56a7/script/closure-library-release/google-closure-library-third-party.pom.template +59 -0
  72. data/ext/clojure-clojurescript-bef56a7/script/closure-library-release/google-closure-library.pom.template +54 -0
  73. data/ext/clojure-clojurescript-bef56a7/script/closure-library-release/make-closure-library-jars.sh +87 -0
  74. data/ext/clojure-clojurescript-bef56a7/script/compile +41 -0
  75. data/ext/clojure-clojurescript-bef56a7/script/repl +13 -0
  76. data/ext/clojure-clojurescript-bef56a7/script/repl.bat +13 -0
  77. data/ext/clojure-clojurescript-bef56a7/script/repljs +15 -0
  78. data/ext/clojure-clojurescript-bef56a7/script/repljs.bat +14 -0
  79. data/ext/clojure-clojurescript-bef56a7/script/test +38 -0
  80. data/ext/clojure-clojurescript-bef56a7/script/test-compile +30 -0
  81. data/ext/clojure-clojurescript-bef56a7/src/clj/cljs/analyzer.clj +975 -0
  82. data/{vendor/assets → ext/clojure-clojurescript-bef56a7}/src/clj/cljs/closure.clj +173 -73
  83. data/ext/clojure-clojurescript-bef56a7/src/clj/cljs/compiler.clj +1081 -0
  84. data/ext/clojure-clojurescript-bef56a7/src/clj/cljs/core.clj +1158 -0
  85. data/{vendor/assets → ext/clojure-clojurescript-bef56a7}/src/clj/cljs/repl.clj +51 -25
  86. data/ext/clojure-clojurescript-bef56a7/src/clj/cljs/repl/browser.clj +258 -0
  87. data/ext/clojure-clojurescript-bef56a7/src/clj/cljs/repl/reflect.clj +75 -0
  88. data/{vendor/assets → ext/clojure-clojurescript-bef56a7}/src/clj/cljs/repl/rhino.clj +6 -5
  89. data/ext/clojure-clojurescript-bef56a7/src/clj/cljs/repl/server.clj +173 -0
  90. data/ext/clojure-clojurescript-bef56a7/src/clj/cljs/tagged_literals.clj +30 -0
  91. data/ext/clojure-clojurescript-bef56a7/src/cljs/cljs/core.cljs +7197 -0
  92. data/{vendor/assets → ext/clojure-clojurescript-bef56a7}/src/cljs/cljs/nodejs.cljs +1 -1
  93. data/{vendor/assets → ext/clojure-clojurescript-bef56a7}/src/cljs/cljs/nodejs_externs.js +0 -0
  94. data/{vendor/assets → ext/clojure-clojurescript-bef56a7}/src/cljs/cljs/nodejscli.cljs +1 -1
  95. data/ext/clojure-clojurescript-bef56a7/src/cljs/cljs/reader.cljs +551 -0
  96. data/{vendor/assets → ext/clojure-clojurescript-bef56a7}/src/cljs/clojure/browser/dom.cljs +59 -13
  97. data/{vendor/assets → ext/clojure-clojurescript-bef56a7}/src/cljs/clojure/browser/event.cljs +0 -0
  98. data/{vendor/assets → ext/clojure-clojurescript-bef56a7}/src/cljs/clojure/browser/net.cljs +8 -7
  99. data/{vendor/assets → ext/clojure-clojurescript-bef56a7}/src/cljs/clojure/browser/repl.cljs +2 -2
  100. data/ext/clojure-clojurescript-bef56a7/src/cljs/clojure/core/reducers.cljs +298 -0
  101. data/ext/clojure-clojurescript-bef56a7/src/cljs/clojure/data.cljs +162 -0
  102. data/ext/clojure-clojurescript-bef56a7/src/cljs/clojure/reflect.cljs +48 -0
  103. data/{vendor/assets → ext/clojure-clojurescript-bef56a7}/src/cljs/clojure/set.cljs +0 -0
  104. data/{vendor/assets → ext/clojure-clojurescript-bef56a7}/src/cljs/clojure/string.cljs +4 -10
  105. data/{vendor/assets → ext/clojure-clojurescript-bef56a7}/src/cljs/clojure/walk.cljs +0 -0
  106. data/{vendor/assets → ext/clojure-clojurescript-bef56a7}/src/cljs/clojure/zip.cljs +0 -0
  107. data/ext/clojure-clojurescript-bef56a7/test/cljs/cljs/binding_test.cljs +7 -0
  108. data/ext/clojure-clojurescript-bef56a7/test/cljs/cljs/binding_test_other_ns.cljs +3 -0
  109. data/ext/clojure-clojurescript-bef56a7/test/cljs/cljs/core_test.cljs +1678 -0
  110. data/ext/clojure-clojurescript-bef56a7/test/cljs/cljs/import_test.cljs +11 -0
  111. data/ext/clojure-clojurescript-bef56a7/test/cljs/cljs/import_test/foo.cljs +5 -0
  112. data/ext/clojure-clojurescript-bef56a7/test/cljs/cljs/letfn_test.cljs +19 -0
  113. data/ext/clojure-clojurescript-bef56a7/test/cljs/cljs/macro_test.cljs +6 -0
  114. data/ext/clojure-clojurescript-bef56a7/test/cljs/cljs/macro_test/macros.clj +5 -0
  115. data/ext/clojure-clojurescript-bef56a7/test/cljs/cljs/ns_test.cljs +14 -0
  116. data/ext/clojure-clojurescript-bef56a7/test/cljs/cljs/ns_test/bar.cljs +3 -0
  117. data/ext/clojure-clojurescript-bef56a7/test/cljs/cljs/ns_test/foo.cljs +7 -0
  118. data/ext/clojure-clojurescript-bef56a7/test/cljs/cljs/reader_test.cljs +124 -0
  119. data/ext/clojure-clojurescript-bef56a7/test/cljs/clojure/data_test.cljs +22 -0
  120. data/ext/clojure-clojurescript-bef56a7/test/cljs/clojure/string_test.cljs +97 -0
  121. data/ext/clojure-clojurescript-bef56a7/test/cljs/foo/ns_shadow_test.cljs +9 -0
  122. data/ext/clojure-clojurescript-bef56a7/test/cljs/test_runner.cljs +26 -0
  123. data/lib/clementine.rb +3 -24
  124. data/lib/clementine/clojurescript_engine.rb +9 -48
  125. data/lib/clementine/clojurescript_engine/base.rb +15 -0
  126. data/lib/clementine/clojurescript_engine/jruby.rb +46 -0
  127. data/lib/clementine/{clojurescript_engine_mri.rb → clojurescript_engine/mri.rb} +17 -10
  128. data/lib/clementine/version.rb +1 -1
  129. data/test/clojurescript_engine_test.rb +36 -14
  130. metadata +177 -83
  131. data/vendor/assets/lib/clojure.jar +0 -0
  132. data/vendor/assets/lib/compiler.jar +0 -0
  133. data/vendor/assets/lib/goog.jar +0 -0
  134. data/vendor/assets/lib/js.jar +0 -0
  135. data/vendor/assets/src/clj/cljs/compiler.clj +0 -1341
  136. data/vendor/assets/src/clj/cljs/core.clj +0 -702
  137. data/vendor/assets/src/clj/cljs/repl/browser.clj +0 -341
  138. data/vendor/assets/src/cljs/cljs/core.cljs +0 -3330
  139. data/vendor/assets/src/cljs/cljs/reader.cljs +0 -360
@@ -0,0 +1,975 @@
1
+ ; Copyright (c) Rich Hickey. All rights reserved.
2
+ ; The use and distribution terms for this software are covered by the
3
+ ; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
4
+ ; which can be found in the file epl-v10.html at the root of this distribution.
5
+ ; By using this software in any fashion, you are agreeing to be bound by
6
+ ; the terms of this license.
7
+ ; You must not remove this notice, or any other, from this software.
8
+
9
+ (set! *warn-on-reflection* true)
10
+
11
+ (ns cljs.analyzer
12
+ (:refer-clojure :exclude [macroexpand-1])
13
+ (:require [clojure.java.io :as io]
14
+ [clojure.string :as string]
15
+ [cljs.tagged-literals :as tags])
16
+ (:import java.lang.StringBuilder))
17
+
18
+ (declare resolve-var)
19
+ (declare resolve-existing-var)
20
+ (declare warning)
21
+ (def ^:dynamic *cljs-warn-on-undeclared* false)
22
+ (declare confirm-bindings)
23
+ (declare ^:dynamic *cljs-file*)
24
+
25
+ ;; to resolve keywords like ::foo - the namespace
26
+ ;; must be determined during analysis - the reader
27
+ ;; did not know
28
+ (def ^:dynamic *reader-ns-name* (gensym))
29
+ (def ^:dynamic *reader-ns* (create-ns *reader-ns-name*))
30
+
31
+ (defonce namespaces (atom '{cljs.core {:name cljs.core}
32
+ cljs.user {:name cljs.user}}))
33
+
34
+ (defn reset-namespaces! []
35
+ (reset! namespaces
36
+ '{cljs.core {:name cljs.core}
37
+ cljs.user {:name cljs.user}}))
38
+
39
+ (defn get-namespace [key]
40
+ (@namespaces key))
41
+
42
+ (defn set-namespace [key val]
43
+ (swap! namespaces assoc key val))
44
+
45
+ (def ^:dynamic *cljs-ns* 'cljs.user)
46
+ (def ^:dynamic *cljs-file* nil)
47
+ (def ^:dynamic *cljs-warn-on-redef* true)
48
+ (def ^:dynamic *cljs-warn-on-dynamic* true)
49
+ (def ^:dynamic *cljs-warn-on-fn-var* true)
50
+ (def ^:dynamic *cljs-warn-fn-arity* true)
51
+ (def ^:dynamic *cljs-warn-fn-deprecated* true)
52
+ (def ^:dynamic *cljs-warn-protocol-deprecated* true)
53
+ (def ^:dynamic *unchecked-if* (atom false))
54
+ (def ^:dynamic *cljs-static-fns* false)
55
+ (def ^:dynamic *cljs-macros-path* "/cljs/core")
56
+ (def ^:dynamic *cljs-macros-is-classpath* true)
57
+ (def -cljs-macros-loaded (atom false))
58
+
59
+ (defn load-core []
60
+ (when (not @-cljs-macros-loaded)
61
+ (reset! -cljs-macros-loaded true)
62
+ (if *cljs-macros-is-classpath*
63
+ (load *cljs-macros-path*)
64
+ (load-file *cljs-macros-path*))))
65
+
66
+ (defmacro with-core-macros
67
+ [path & body]
68
+ `(do
69
+ (when (not= *cljs-macros-path* ~path)
70
+ (reset! -cljs-macros-loaded false))
71
+ (binding [*cljs-macros-path* ~path]
72
+ ~@body)))
73
+
74
+ (defmacro with-core-macros-file
75
+ [path & body]
76
+ `(do
77
+ (when (not= *cljs-macros-path* ~path)
78
+ (reset! -cljs-macros-loaded false))
79
+ (binding [*cljs-macros-path* ~path
80
+ *cljs-macros-is-classpath* false]
81
+ ~@body)))
82
+
83
+ (defn empty-env []
84
+ {:ns (@namespaces *cljs-ns*) :context :statement :locals {}})
85
+
86
+ (defmacro ^:private debug-prn
87
+ [& args]
88
+ `(.println System/err (str ~@args)))
89
+
90
+ (defn warning [env s]
91
+ (binding [*out* *err*]
92
+ (println
93
+ (str s (when (:line env)
94
+ (str " at line " (:line env) " " *cljs-file*))))))
95
+
96
+ (defn confirm-var-exists [env prefix suffix]
97
+ (when *cljs-warn-on-undeclared*
98
+ (let [crnt-ns (-> env :ns :name)]
99
+ (when (= prefix crnt-ns)
100
+ (when-not (-> @namespaces crnt-ns :defs suffix)
101
+ (warning env
102
+ (str "WARNING: Use of undeclared Var " prefix "/" suffix)))))))
103
+
104
+ (defn resolve-ns-alias [env name]
105
+ (let [sym (symbol name)]
106
+ (get (:requires (:ns env)) sym sym)))
107
+
108
+ (defn core-name?
109
+ "Is sym visible from core in the current compilation namespace?"
110
+ [env sym]
111
+ (and (get (:defs (@namespaces 'cljs.core)) sym)
112
+ (not (contains? (-> env :ns :excludes) sym))))
113
+
114
+ (defn resolve-existing-var [env sym]
115
+ (if (= (namespace sym) "js")
116
+ {:name sym :ns 'js}
117
+ (let [s (str sym)
118
+ lb (-> env :locals sym)]
119
+ (cond
120
+ lb lb
121
+
122
+ (namespace sym)
123
+ (let [ns (namespace sym)
124
+ ns (if (= "clojure.core" ns) "cljs.core" ns)
125
+ full-ns (resolve-ns-alias env ns)]
126
+ (confirm-var-exists env full-ns (symbol (name sym)))
127
+ (merge (get-in @namespaces [full-ns :defs (symbol (name sym))])
128
+ {:name (symbol (str full-ns) (str (name sym)))
129
+ :ns full-ns}))
130
+
131
+ (.contains s ".")
132
+ (let [idx (.indexOf s ".")
133
+ prefix (symbol (subs s 0 idx))
134
+ suffix (subs s (inc idx))
135
+ lb (-> env :locals prefix)]
136
+ (if lb
137
+ {:name (symbol (str (:name lb) suffix))}
138
+ (do
139
+ (confirm-var-exists env prefix (symbol suffix))
140
+ (merge (get-in @namespaces [prefix :defs (symbol suffix)])
141
+ {:name (if (= "" prefix) (symbol suffix) (symbol (str prefix) suffix))
142
+ :ns prefix}))))
143
+
144
+ (get-in @namespaces [(-> env :ns :name) :uses sym])
145
+ (let [full-ns (get-in @namespaces [(-> env :ns :name) :uses sym])]
146
+ (merge
147
+ (get-in @namespaces [full-ns :defs sym])
148
+ {:name (symbol (str full-ns) (str sym))
149
+ :ns (-> env :ns :name)}))
150
+
151
+ (get-in @namespaces [(-> env :ns :name) :imports sym])
152
+ (recur env (get-in @namespaces [(-> env :ns :name) :imports sym]))
153
+
154
+ :else
155
+ (let [full-ns (if (core-name? env sym)
156
+ 'cljs.core
157
+ (-> env :ns :name))]
158
+ (confirm-var-exists env full-ns sym)
159
+ (merge (get-in @namespaces [full-ns :defs sym])
160
+ {:name (symbol (str full-ns) (str sym))
161
+ :ns full-ns}))))))
162
+
163
+ (defn resolve-var [env sym]
164
+ (if (= (namespace sym) "js")
165
+ {:name sym}
166
+ (let [s (str sym)
167
+ lb (-> env :locals sym)]
168
+ (cond
169
+ lb lb
170
+
171
+ (namespace sym)
172
+ (let [ns (namespace sym)
173
+ ns (if (= "clojure.core" ns) "cljs.core" ns)]
174
+ {:name (symbol (str (resolve-ns-alias env ns)) (name sym))})
175
+
176
+ (.contains s ".")
177
+ (let [idx (.indexOf s ".")
178
+ prefix (symbol (subs s 0 idx))
179
+ suffix (subs s idx)
180
+ lb (-> env :locals prefix)]
181
+ (if lb
182
+ {:name (symbol (str (:name lb) suffix))}
183
+ {:name sym}))
184
+
185
+ (get-in @namespaces [(-> env :ns :name) :uses sym])
186
+ (let [full-ns (get-in @namespaces [(-> env :ns :name) :uses sym])]
187
+ (merge
188
+ (get-in @namespaces [full-ns :defs sym])
189
+ {:name (symbol (str full-ns) (name sym))}))
190
+
191
+ (get-in @namespaces [(-> env :ns :name) :imports sym])
192
+ (recur env (get-in @namespaces [(-> env :ns :name) :imports sym]))
193
+
194
+ :else
195
+ (let [ns (if (core-name? env sym)
196
+ 'cljs.core
197
+ (-> env :ns :name))]
198
+ {:name (symbol (str ns) (name sym))})))))
199
+
200
+ (defn confirm-bindings [env names]
201
+ (doseq [name names]
202
+ (let [env (merge env {:ns (@namespaces *cljs-ns*)})
203
+ ev (resolve-existing-var env name)]
204
+ (when (and *cljs-warn-on-dynamic*
205
+ ev (not (-> ev :dynamic)))
206
+ (warning env
207
+ (str "WARNING: " (:name ev) " not declared ^:dynamic"))))))
208
+
209
+ (declare analyze analyze-symbol analyze-seq)
210
+
211
+ (def specials '#{if def fn* do let* loop* letfn* throw try* recur new set! ns deftype* defrecord* . js* & quote})
212
+
213
+ (def ^:dynamic *recur-frames* nil)
214
+ (def ^:dynamic *loop-lets* nil)
215
+
216
+ (defmacro disallowing-recur [& body]
217
+ `(binding [*recur-frames* (cons nil *recur-frames*)] ~@body))
218
+
219
+ (defn analyze-keyword
220
+ [env sym]
221
+ {:op :constant :env env
222
+ :form (if (= (namespace sym) (name *reader-ns-name*))
223
+ (keyword (-> env :ns :name name) (name sym))
224
+ sym)})
225
+
226
+ (defn analyze-block
227
+ "returns {:statements .. :ret ..}"
228
+ [env exprs]
229
+ (let [statements (disallowing-recur
230
+ (seq (map #(analyze (assoc env :context :statement) %) (butlast exprs))))
231
+ ret (if (<= (count exprs) 1)
232
+ (analyze env (first exprs))
233
+ (analyze (assoc env :context (if (= :statement (:context env)) :statement :return)) (last exprs)))]
234
+ {:statements statements :ret ret}))
235
+
236
+ (defmulti parse (fn [op & rest] op))
237
+
238
+ (defmethod parse 'if
239
+ [op env [_ test then else :as form] name]
240
+ (let [test-expr (disallowing-recur (analyze (assoc env :context :expr) test))
241
+ then-expr (analyze env then)
242
+ else-expr (analyze env else)]
243
+ {:env env :op :if :form form
244
+ :test test-expr :then then-expr :else else-expr
245
+ :unchecked @*unchecked-if*
246
+ :children [test-expr then-expr else-expr]}))
247
+
248
+ (defmethod parse 'throw
249
+ [op env [_ throw :as form] name]
250
+ (let [throw-expr (disallowing-recur (analyze (assoc env :context :expr) throw))]
251
+ {:env env :op :throw :form form
252
+ :throw throw-expr
253
+ :children [throw-expr]}))
254
+
255
+ (defn- block-children [{:keys [statements ret] :as block}]
256
+ (when block (conj (vec statements) ret)))
257
+
258
+ (defmethod parse 'try*
259
+ [op env [_ & body :as form] name]
260
+ (let [body (vec body)
261
+ catchenv (update-in env [:context] #(if (= :expr %) :return %))
262
+ tail (peek body)
263
+ fblock (when (and (seq? tail) (= 'finally (first tail)))
264
+ (rest tail))
265
+ finally (when fblock
266
+ (analyze-block
267
+ (assoc env :context :statement)
268
+ fblock))
269
+ body (if finally (pop body) body)
270
+ tail (peek body)
271
+ cblock (when (and (seq? tail)
272
+ (= 'catch (first tail)))
273
+ (rest tail))
274
+ name (first cblock)
275
+ locals (:locals catchenv)
276
+ locals (if name
277
+ (assoc locals name {:name name})
278
+ locals)
279
+ catch (when cblock
280
+ (analyze-block (assoc catchenv :locals locals) (rest cblock)))
281
+ body (if name (pop body) body)
282
+ try (when body
283
+ (analyze-block (if (or name finally) catchenv env) body))]
284
+ (when name (assert (not (namespace name)) "Can't qualify symbol in catch"))
285
+ {:env env :op :try* :form form
286
+ :try try
287
+ :finally finally
288
+ :name name
289
+ :catch catch
290
+ :children (vec (mapcat block-children
291
+ [try catch finally]))}))
292
+
293
+ (defmethod parse 'def
294
+ [op env form name]
295
+ (let [pfn (fn
296
+ ([_ sym] {:sym sym})
297
+ ([_ sym init] {:sym sym :init init})
298
+ ([_ sym doc init] {:sym sym :doc doc :init init}))
299
+ args (apply pfn form)
300
+ sym (:sym args)
301
+ sym-meta (meta sym)
302
+ tag (-> sym meta :tag)
303
+ protocol (-> sym meta :protocol)
304
+ dynamic (-> sym meta :dynamic)
305
+ ns-name (-> env :ns :name)]
306
+ (assert (not (namespace sym)) "Can't def ns-qualified name")
307
+ (let [env (if (or (and (not= ns-name 'cljs.core)
308
+ (core-name? env sym))
309
+ (get-in @namespaces [ns-name :uses sym]))
310
+ (let [ev (resolve-existing-var (dissoc env :locals) sym)]
311
+ (when *cljs-warn-on-redef*
312
+ (warning env
313
+ (str "WARNING: " sym " already refers to: " (symbol (str (:ns ev)) (str sym))
314
+ " being replaced by: " (symbol (str ns-name) (str sym)))))
315
+ (swap! namespaces update-in [ns-name :excludes] conj sym)
316
+ (update-in env [:ns :excludes] conj sym))
317
+ env)
318
+ name (:name (resolve-var (dissoc env :locals) sym))
319
+ init-expr (when (contains? args :init)
320
+ (disallowing-recur
321
+ (analyze (assoc env :context :expr) (:init args) sym)))
322
+ fn-var? (and init-expr (= (:op init-expr) :fn))
323
+ export-as (when-let [export-val (-> sym meta :export)]
324
+ (if (= true export-val) name export-val))
325
+ doc (or (:doc args) (-> sym meta :doc))]
326
+ (when-let [v (get-in @namespaces [ns-name :defs sym])]
327
+ (when (and *cljs-warn-on-fn-var*
328
+ (not (-> sym meta :declared))
329
+ (and (:fn-var v) (not fn-var?)))
330
+ (warning env
331
+ (str "WARNING: " (symbol (str ns-name) (str sym))
332
+ " no longer fn, references are stale"))))
333
+ (swap! namespaces assoc-in [ns-name :defs sym]
334
+ (merge
335
+ {:name name}
336
+ sym-meta
337
+ (when doc {:doc doc})
338
+ (when dynamic {:dynamic true})
339
+ (when-let [line (:line env)]
340
+ {:file *cljs-file* :line line})
341
+ ;; the protocol a protocol fn belongs to
342
+ (when protocol
343
+ {:protocol protocol})
344
+ ;; symbol for reified protocol
345
+ (when-let [protocol-symbol (-> sym meta :protocol-symbol)]
346
+ {:protocol-symbol protocol-symbol})
347
+ (when fn-var?
348
+ {:fn-var true
349
+ ;; protocol implementation context
350
+ :protocol-impl (:protocol-impl init-expr)
351
+ ;; inline protocol implementation context
352
+ :protocol-inline (:protocol-inline init-expr)
353
+ :variadic (:variadic init-expr)
354
+ :max-fixed-arity (:max-fixed-arity init-expr)
355
+ :method-params (map :params (:methods init-expr))})))
356
+ (merge {:env env :op :def :form form
357
+ :name name :doc doc :init init-expr}
358
+ (when tag {:tag tag})
359
+ (when dynamic {:dynamic true})
360
+ (when export-as {:export export-as})
361
+ (when init-expr {:children [init-expr]})))))
362
+
363
+ (defn- analyze-fn-method [env locals meth type]
364
+ (let [param-names (first meth)
365
+ variadic (boolean (some '#{&} param-names))
366
+ param-names (vec (remove '#{&} param-names))
367
+ body (next meth)
368
+ [locals params] (reduce (fn [[locals params] name]
369
+ (let [param {:name name
370
+ :tag (-> name meta :tag)
371
+ :shadow (locals name)}]
372
+ [(assoc locals name param) (conj params param)]))
373
+ [locals []] param-names)
374
+ fixed-arity (count (if variadic (butlast params) params))
375
+ recur-frame {:params params :flag (atom nil)}
376
+ block (binding [*recur-frames* (cons recur-frame *recur-frames*)]
377
+ (analyze-block (assoc env :context :return :locals locals) body))]
378
+ (merge {:env env :variadic variadic :params params :max-fixed-arity fixed-arity
379
+ :type type :recurs @(:flag recur-frame)}
380
+ block)))
381
+
382
+ (defmethod parse 'fn*
383
+ [op env [_ & args :as form] name]
384
+ (let [[name meths] (if (symbol? (first args))
385
+ [(first args) (next args)]
386
+ [name (seq args)])
387
+ ;;turn (fn [] ...) into (fn ([]...))
388
+ meths (if (vector? (first meths)) (list meths) meths)
389
+ locals (:locals env)
390
+ locals (if name (assoc locals name {:name name :shadow (locals name)}) locals)
391
+ type (-> form meta ::type)
392
+ fields (-> form meta ::fields)
393
+ protocol-impl (-> form meta :protocol-impl)
394
+ protocol-inline (-> form meta :protocol-inline)
395
+ locals (reduce (fn [m fld]
396
+ (assoc m fld
397
+ {:name fld
398
+ :field true
399
+ :mutable (-> fld meta :mutable)
400
+ :tag (-> fld meta :tag)
401
+ :shadow (m fld)}))
402
+ locals fields)
403
+
404
+ menv (if (> (count meths) 1) (assoc env :context :expr) env)
405
+ menv (merge menv
406
+ {:protocol-impl protocol-impl
407
+ :protocol-inline protocol-inline})
408
+ methods (map #(analyze-fn-method menv locals % type) meths)
409
+ max-fixed-arity (apply max (map :max-fixed-arity methods))
410
+ variadic (boolean (some :variadic methods))
411
+ locals (if name
412
+ (update-in locals [name] assoc
413
+ :fn-var true
414
+ :variadic variadic
415
+ :max-fixed-arity max-fixed-arity
416
+ :method-params (map :params methods))
417
+ locals)
418
+ methods (if name
419
+ ;; a second pass with knowledge of our function-ness/arity
420
+ ;; lets us optimize self calls
421
+ (map #(analyze-fn-method menv locals % type) meths)
422
+ methods)]
423
+ ;;todo - validate unique arities, at most one variadic, variadic takes max required args
424
+ {:env env :op :fn :form form :name name :methods methods :variadic variadic
425
+ :recur-frames *recur-frames* :loop-lets *loop-lets*
426
+ :jsdoc [(when variadic "@param {...*} var_args")]
427
+ :max-fixed-arity max-fixed-arity
428
+ :protocol-impl protocol-impl
429
+ :protocol-inline protocol-inline
430
+ :children (vec (mapcat block-children
431
+ methods))}))
432
+
433
+ (defmethod parse 'letfn*
434
+ [op env [_ bindings & exprs :as form] name]
435
+ (assert (and (vector? bindings) (even? (count bindings))) "bindings must be vector of even number of elements")
436
+ (let [n->fexpr (into {} (map (juxt first second) (partition 2 bindings)))
437
+ names (keys n->fexpr)
438
+ context (:context env)
439
+ [meth-env bes]
440
+ (reduce (fn [[{:keys [locals] :as env} bes] n]
441
+ (let [be {:name n
442
+ :tag (-> n meta :tag)
443
+ :local true
444
+ :shadow (locals n)}]
445
+ [(assoc-in env [:locals n] be)
446
+ (conj bes be)]))
447
+ [env []] names)
448
+ meth-env (assoc meth-env :context :expr)
449
+ bes (vec (map (fn [{:keys [name shadow] :as be}]
450
+ (let [env (assoc-in meth-env [:locals name] shadow)]
451
+ (assoc be :init (analyze env (n->fexpr name)))))
452
+ bes))
453
+ {:keys [statements ret]}
454
+ (analyze-block (assoc meth-env :context (if (= :expr context) :return context)) exprs)]
455
+ {:env env :op :letfn :bindings bes :statements statements :ret ret :form form
456
+ :children (into (vec (map :init bes))
457
+ (conj (vec statements) ret))}))
458
+
459
+ (defmethod parse 'do
460
+ [op env [_ & exprs :as form] _]
461
+ (let [block (analyze-block env exprs)]
462
+ (merge {:env env :op :do :form form :children (block-children block)} block)))
463
+
464
+ (defn analyze-let
465
+ [encl-env [_ bindings & exprs :as form] is-loop]
466
+ (assert (and (vector? bindings) (even? (count bindings))) "bindings must be vector of even number of elements")
467
+ (let [context (:context encl-env)
468
+ [bes env]
469
+ (disallowing-recur
470
+ (loop [bes []
471
+ env (assoc encl-env :context :expr)
472
+ bindings (seq (partition 2 bindings))]
473
+ (if-let [[name init] (first bindings)]
474
+ (do
475
+ (assert (not (or (namespace name) (.contains (str name) "."))) (str "Invalid local name: " name))
476
+ (let [init-expr (analyze env init)
477
+ be {:name name
478
+ :init init-expr
479
+ :tag (or (-> name meta :tag)
480
+ (-> init-expr :tag)
481
+ (-> init-expr :info :tag))
482
+ :local true
483
+ :shadow (-> env :locals name)}
484
+ be (if (= (:op init-expr) :fn)
485
+ (merge be
486
+ {:fn-var true
487
+ :variadic (:variadic init-expr)
488
+ :max-fixed-arity (:max-fixed-arity init-expr)
489
+ :method-params (map :params (:methods init-expr))})
490
+ be)]
491
+ (recur (conj bes be)
492
+ (assoc-in env [:locals name] be)
493
+ (next bindings))))
494
+ [bes env])))
495
+ recur-frame (when is-loop {:params bes :flag (atom nil)})
496
+ {:keys [statements ret]}
497
+ (binding [*recur-frames* (if recur-frame (cons recur-frame *recur-frames*) *recur-frames*)
498
+ *loop-lets* (cond
499
+ is-loop (or *loop-lets* ())
500
+ *loop-lets* (cons {:params bes} *loop-lets*))]
501
+ (analyze-block (assoc env :context (if (= :expr context) :return context)) exprs))]
502
+ {:env encl-env :op :let :loop is-loop
503
+ :bindings bes :statements statements :ret ret :form form
504
+ :children (into (vec (map :init bes))
505
+ (conj (vec statements) ret))}))
506
+
507
+ (defmethod parse 'let*
508
+ [op encl-env form _]
509
+ (analyze-let encl-env form false))
510
+
511
+ (defmethod parse 'loop*
512
+ [op encl-env form _]
513
+ (analyze-let encl-env form true))
514
+
515
+ (defmethod parse 'recur
516
+ [op env [_ & exprs :as form] _]
517
+ (let [context (:context env)
518
+ frame (first *recur-frames*)
519
+ exprs (disallowing-recur (vec (map #(analyze (assoc env :context :expr) %) exprs)))]
520
+ (assert frame "Can't recur here")
521
+ (assert (= (count exprs) (count (:params frame))) "recur argument count mismatch")
522
+ (reset! (:flag frame) true)
523
+ (assoc {:env env :op :recur :form form}
524
+ :frame frame
525
+ :exprs exprs
526
+ :children exprs)))
527
+
528
+ (defmethod parse 'quote
529
+ [_ env [_ x] _]
530
+ {:op :constant :env env :form x})
531
+
532
+ (defmethod parse 'new
533
+ [_ env [_ ctor & args :as form] _]
534
+ (assert (symbol? ctor) "First arg to new must be a symbol")
535
+ (disallowing-recur
536
+ (let [enve (assoc env :context :expr)
537
+ ctorexpr (analyze enve ctor)
538
+ argexprs (vec (map #(analyze enve %) args))
539
+ known-num-fields (:num-fields (resolve-existing-var env ctor))
540
+ argc (count args)]
541
+ (when (and known-num-fields (not= known-num-fields argc))
542
+ (warning env
543
+ (str "WARNING: Wrong number of args (" argc ") passed to " ctor)))
544
+
545
+ {:env env :op :new :form form :ctor ctorexpr :args argexprs
546
+ :children (into [ctorexpr] argexprs)})))
547
+
548
+ (defmethod parse 'set!
549
+ [_ env [_ target val alt :as form] _]
550
+ (let [[target val] (if alt
551
+ ;; (set! o -prop val)
552
+ [`(. ~target ~val) alt]
553
+ [target val])]
554
+ (disallowing-recur
555
+ (let [enve (assoc env :context :expr)
556
+ targetexpr (cond
557
+ ;; TODO: proper resolve
558
+ (= target '*unchecked-if*)
559
+ (do
560
+ (reset! *unchecked-if* val)
561
+ ::set-unchecked-if)
562
+
563
+ (symbol? target)
564
+ (do
565
+ (let [local (-> env :locals target)]
566
+ (assert (or (nil? local)
567
+ (and (:field local)
568
+ (:mutable local)))
569
+ "Can't set! local var or non-mutable field"))
570
+ (analyze-symbol enve target))
571
+
572
+ :else
573
+ (when (seq? target)
574
+ (let [targetexpr (analyze-seq enve target nil)]
575
+ (when (:field targetexpr)
576
+ targetexpr))))
577
+ valexpr (analyze enve val)]
578
+ (assert targetexpr "set! target must be a field or a symbol naming a var")
579
+ (cond
580
+ (= targetexpr ::set-unchecked-if) {:env env :op :no-op}
581
+ :else {:env env :op :set! :form form :target targetexpr :val valexpr
582
+ :children [targetexpr valexpr]})))))
583
+
584
+ (defn munge-path [ss]
585
+ (clojure.lang.Compiler/munge (str ss)))
586
+
587
+ (defn ns->relpath [s]
588
+ (str (string/replace (munge-path s) \. \/) ".cljs"))
589
+
590
+ (declare analyze-file)
591
+
592
+ (defn analyze-deps [deps]
593
+ (doseq [dep deps]
594
+ (when-not (:defs (@namespaces dep))
595
+ (let [relpath (ns->relpath dep)]
596
+ (when (io/resource relpath)
597
+ (analyze-file relpath))))))
598
+
599
+ (defmethod parse 'ns
600
+ [_ env [_ name & args :as form] _]
601
+ (let [docstring (if (string? (first args)) (first args) nil)
602
+ args (if docstring (next args) args)
603
+ excludes
604
+ (reduce (fn [s [k exclude xs]]
605
+ (if (= k :refer-clojure)
606
+ (do
607
+ (assert (= exclude :exclude) "Only [:refer-clojure :exclude (names)] form supported")
608
+ (assert (not (seq s)) "Only one :refer-clojure form is allowed per namespace definition")
609
+ (into s xs))
610
+ s))
611
+ #{} args)
612
+ deps (atom #{})
613
+ valid-forms (atom #{:use :use-macros :require :require-macros :import})
614
+ error-msg (fn [spec msg] (str msg "; offending spec: " (pr-str spec)))
615
+ parse-require-spec (fn parse-require-spec [macros? spec]
616
+ (assert (or (symbol? spec) (vector? spec))
617
+ (error-msg spec "Only [lib.ns & options] and lib.ns specs supported in :require / :require-macros"))
618
+ (when (vector? spec)
619
+ (assert (symbol? (first spec))
620
+ (error-msg spec "Library name must be specified as a symbol in :require / :require-macros"))
621
+ (assert (odd? (count spec))
622
+ (error-msg spec "Only :as alias and :refer (names) options supported in :require"))
623
+ (assert (every? #{:as :refer} (map first (partition 2 (next spec))))
624
+ (error-msg spec "Only :as and :refer options supported in :require / :require-macros"))
625
+ (assert (let [fs (frequencies (next spec))]
626
+ (and (<= (fs :as 0) 1)
627
+ (<= (fs :refer 0) 1)))
628
+ (error-msg spec "Each of :as and :refer options may only be specified once in :require / :require-macros")))
629
+ (if (symbol? spec)
630
+ (recur macros? [spec])
631
+ (let [[lib & opts] spec
632
+ {alias :as referred :refer :or {alias lib}} (apply hash-map opts)
633
+ [rk uk] (if macros? [:require-macros :use-macros] [:require :use])]
634
+ (assert (or (symbol? alias) (nil? alias))
635
+ (error-msg spec ":as must be followed by a symbol in :require / :require-macros"))
636
+ (assert (or (and (sequential? referred) (every? symbol? referred))
637
+ (nil? referred))
638
+ (error-msg spec ":refer must be followed by a sequence of symbols in :require / :require-macros"))
639
+ (when-not macros?
640
+ (swap! deps conj lib))
641
+ (merge (when alias {rk {alias lib}})
642
+ (when referred {uk (apply hash-map (interleave referred (repeat lib)))})))))
643
+ use->require (fn use->require [[lib kw referred :as spec]]
644
+ (assert (and (symbol? lib) (= :only kw) (sequential? referred) (every? symbol? referred))
645
+ (error-msg spec "Only [lib.ns :only (names)] specs supported in :use / :use-macros"))
646
+ [lib :refer referred])
647
+ parse-import-spec (fn parse-import-spec [spec]
648
+ (assert (and (symbol? spec) (nil? (namespace spec)))
649
+ (error-msg spec "Only lib.Ctor specs supported in :import"))
650
+ (swap! deps conj spec)
651
+ (let [ctor-sym (symbol (last (string/split (str spec) #"\.")))]
652
+ {:import {ctor-sym spec}
653
+ :require {ctor-sym spec}}))
654
+ spec-parsers {:require (partial parse-require-spec false)
655
+ :require-macros (partial parse-require-spec true)
656
+ :use (comp (partial parse-require-spec false) use->require)
657
+ :use-macros (comp (partial parse-require-spec true) use->require)
658
+ :import parse-import-spec}
659
+ {uses :use requires :require uses-macros :use-macros requires-macros :require-macros imports :import :as params}
660
+ (reduce (fn [m [k & libs]]
661
+ (assert (#{:use :use-macros :require :require-macros :import} k)
662
+ "Only :refer-clojure, :require, :require-macros, :use and :use-macros libspecs supported")
663
+ (assert (@valid-forms k)
664
+ (str "Only one " k " form is allowed per namespace definition"))
665
+ (swap! valid-forms disj k)
666
+ (apply merge-with merge m (map (spec-parsers k) libs)))
667
+ {} (remove (fn [[r]] (= r :refer-clojure)) args))]
668
+ (when (seq @deps)
669
+ (analyze-deps @deps))
670
+ (set! *cljs-ns* name)
671
+ (load-core)
672
+ (doseq [nsym (concat (vals requires-macros) (vals uses-macros))]
673
+ (clojure.core/require nsym))
674
+ (swap! namespaces #(-> %
675
+ (assoc-in [name :name] name)
676
+ (assoc-in [name :doc] docstring)
677
+ (assoc-in [name :excludes] excludes)
678
+ (assoc-in [name :uses] uses)
679
+ (assoc-in [name :requires] requires)
680
+ (assoc-in [name :uses-macros] uses-macros)
681
+ (assoc-in [name :requires-macros]
682
+ (into {} (map (fn [[alias nsym]]
683
+ [alias (find-ns nsym)])
684
+ requires-macros)))
685
+ (assoc-in [name :imports] imports)))
686
+ {:env env :op :ns :form form :name name :doc docstring :uses uses :requires requires :imports imports
687
+ :uses-macros uses-macros :requires-macros requires-macros :excludes excludes}))
688
+
689
+ (defmethod parse 'deftype*
690
+ [_ env [_ tsym fields pmasks :as form] _]
691
+ (let [t (:name (resolve-var (dissoc env :locals) tsym))]
692
+ (swap! namespaces update-in [(-> env :ns :name) :defs tsym]
693
+ (fn [m]
694
+ (let [m (assoc (or m {})
695
+ :name t
696
+ :type true
697
+ :num-fields (count fields))]
698
+ (merge m
699
+ {:protocols (-> tsym meta :protocols)}
700
+ (when-let [line (:line env)]
701
+ {:file *cljs-file*
702
+ :line line})))))
703
+ {:env env :op :deftype* :form form :t t :fields fields :pmasks pmasks}))
704
+
705
+ (defmethod parse 'defrecord*
706
+ [_ env [_ tsym fields pmasks :as form] _]
707
+ (let [t (:name (resolve-var (dissoc env :locals) tsym))]
708
+ (swap! namespaces update-in [(-> env :ns :name) :defs tsym]
709
+ (fn [m]
710
+ (let [m (assoc (or m {}) :name t :type true)]
711
+ (merge m
712
+ {:protocols (-> tsym meta :protocols)}
713
+ (when-let [line (:line env)]
714
+ {:file *cljs-file*
715
+ :line line})))))
716
+ {:env env :op :defrecord* :form form :t t :fields fields :pmasks pmasks}))
717
+
718
+ ;; dot accessor code
719
+
720
+ (def ^:private property-symbol? #(boolean (and (symbol? %) (re-matches #"^-.*" (name %)))))
721
+
722
+ (defn- classify-dot-form
723
+ [[target member args]]
724
+ [(cond (nil? target) ::error
725
+ :default ::expr)
726
+ (cond (property-symbol? member) ::property
727
+ (symbol? member) ::symbol
728
+ (seq? member) ::list
729
+ :default ::error)
730
+ (cond (nil? args) ()
731
+ :default ::expr)])
732
+
733
+ (defmulti build-dot-form #(classify-dot-form %))
734
+
735
+ ;; (. o -p)
736
+ ;; (. (...) -p)
737
+ (defmethod build-dot-form [::expr ::property ()]
738
+ [[target prop _]]
739
+ {:dot-action ::access :target target :field (-> prop name (.substring 1) symbol)})
740
+
741
+ ;; (. o -p <args>)
742
+ (defmethod build-dot-form [::expr ::property ::list]
743
+ [[target prop args]]
744
+ (throw (Error. (str "Cannot provide arguments " args " on property access " prop))))
745
+
746
+ (defn- build-method-call
747
+ "Builds the intermediate method call map used to reason about the parsed form during
748
+ compilation."
749
+ [target meth args]
750
+ (if (symbol? meth)
751
+ {:dot-action ::call :target target :method meth :args args}
752
+ {:dot-action ::call :target target :method (first meth) :args args}))
753
+
754
+ ;; (. o m 1 2)
755
+ (defmethod build-dot-form [::expr ::symbol ::expr]
756
+ [[target meth args]]
757
+ (build-method-call target meth args))
758
+
759
+ ;; (. o m)
760
+ (defmethod build-dot-form [::expr ::symbol ()]
761
+ [[target meth args]]
762
+ (build-method-call target meth args))
763
+
764
+ ;; (. o (m))
765
+ ;; (. o (m 1 2))
766
+ (defmethod build-dot-form [::expr ::list ()]
767
+ [[target meth-expr _]]
768
+ (build-method-call target (first meth-expr) (rest meth-expr)))
769
+
770
+ (defmethod build-dot-form :default
771
+ [dot-form]
772
+ (throw (Error. (str "Unknown dot form of " (list* '. dot-form) " with classification " (classify-dot-form dot-form)))))
773
+
774
+ (defmethod parse '.
775
+ [_ env [_ target & [field & member+] :as form] _]
776
+ (disallowing-recur
777
+ (let [{:keys [dot-action target method field args]} (build-dot-form [target field member+])
778
+ enve (assoc env :context :expr)
779
+ targetexpr (analyze enve target)]
780
+ (case dot-action
781
+ ::access {:env env :op :dot :form form
782
+ :target targetexpr
783
+ :field field
784
+ :children [targetexpr]
785
+ :tag (-> form meta :tag)}
786
+ ::call (let [argexprs (map #(analyze enve %) args)]
787
+ {:env env :op :dot :form form
788
+ :target targetexpr
789
+ :method method
790
+ :args argexprs
791
+ :children (into [targetexpr] argexprs)
792
+ :tag (-> form meta :tag)})))))
793
+
794
+ (defmethod parse 'js*
795
+ [op env [_ jsform & args :as form] _]
796
+ (assert (string? jsform))
797
+ (if args
798
+ (disallowing-recur
799
+ (let [seg (fn seg [^String s]
800
+ (let [idx (.indexOf s "~{")]
801
+ (if (= -1 idx)
802
+ (list s)
803
+ (let [end (.indexOf s "}" idx)]
804
+ (cons (subs s 0 idx) (seg (subs s (inc end))))))))
805
+ enve (assoc env :context :expr)
806
+ argexprs (vec (map #(analyze enve %) args))]
807
+ {:env env :op :js :segs (seg jsform) :args argexprs
808
+ :tag (-> form meta :tag) :form form :children argexprs}))
809
+ (let [interp (fn interp [^String s]
810
+ (let [idx (.indexOf s "~{")]
811
+ (if (= -1 idx)
812
+ (list s)
813
+ (let [end (.indexOf s "}" idx)
814
+ inner (:name (resolve-existing-var env (symbol (subs s (+ 2 idx) end))))]
815
+ (cons (subs s 0 idx) (cons inner (interp (subs s (inc end)))))))))]
816
+ {:env env :op :js :form form :code (apply str (interp jsform))
817
+ :tag (-> form meta :tag)})))
818
+
819
+ (defn parse-invoke
820
+ [env [f & args :as form]]
821
+ (disallowing-recur
822
+ (let [enve (assoc env :context :expr)
823
+ fexpr (analyze enve f)
824
+ argexprs (vec (map #(analyze enve %) args))
825
+ argc (count args)]
826
+ (if (and *cljs-warn-fn-arity* (-> fexpr :info :fn-var))
827
+ (let [{:keys [variadic max-fixed-arity method-params name]} (:info fexpr)]
828
+ (when (and (not (some #{argc} (map count method-params)))
829
+ (or (not variadic)
830
+ (and variadic (< argc max-fixed-arity))))
831
+ (warning env
832
+ (str "WARNING: Wrong number of args (" argc ") passed to " name)))))
833
+ (if (and *cljs-warn-fn-deprecated* (-> fexpr :info :deprecated)
834
+ (not (-> form meta :deprecation-nowarn)))
835
+ (warning env
836
+ (str "WARNING: " (-> fexpr :info :name) " is deprecated.")))
837
+ {:env env :op :invoke :form form :f fexpr :args argexprs
838
+ :tag (or (-> fexpr :info :tag) (-> form meta :tag)) :children (into [fexpr] argexprs)})))
839
+
840
+ (defn analyze-symbol
841
+ "Finds the var associated with sym"
842
+ [env sym]
843
+ (let [ret {:env env :form sym}
844
+ lb (-> env :locals sym)]
845
+ (if lb
846
+ (assoc ret :op :var :info lb)
847
+ (assoc ret :op :var :info (resolve-existing-var env sym)))))
848
+
849
+ (defn get-expander [sym env]
850
+ (let [mvar
851
+ (when-not (or (-> env :locals sym) ;locals hide macros
852
+ (and (or (-> env :ns :excludes sym)
853
+ (get-in @namespaces [(-> env :ns :name) :excludes sym]))
854
+ (not (or (-> env :ns :uses-macros sym)
855
+ (get-in @namespaces [(-> env :ns :name) :uses-macros sym])))))
856
+ (if-let [nstr (namespace sym)]
857
+ (when-let [ns (cond
858
+ (= "clojure.core" nstr) (find-ns 'cljs.core)
859
+ (.contains nstr ".") (find-ns (symbol nstr))
860
+ :else
861
+ (-> env :ns :requires-macros (get (symbol nstr))))]
862
+ (.findInternedVar ^clojure.lang.Namespace ns (symbol (name sym))))
863
+ (if-let [nsym (-> env :ns :uses-macros sym)]
864
+ (.findInternedVar ^clojure.lang.Namespace (find-ns nsym) sym)
865
+ (.findInternedVar ^clojure.lang.Namespace (find-ns 'cljs.core) sym))))]
866
+ (when (and mvar (.isMacro ^clojure.lang.Var mvar))
867
+ @mvar)))
868
+
869
+ (defn macroexpand-1 [env form]
870
+ (let [op (first form)]
871
+ (if (specials op)
872
+ form
873
+ (if-let [mac (and (symbol? op) (get-expander op env))]
874
+ (binding [*ns* (create-ns *cljs-ns*)]
875
+ (apply mac form env (rest form)))
876
+ (if (symbol? op)
877
+ (let [opname (str op)]
878
+ (cond
879
+ (= (first opname) \.) (let [[target & args] (next form)]
880
+ (with-meta (list* '. target (symbol (subs opname 1)) args)
881
+ (meta form)))
882
+ (= (last opname) \.) (with-meta
883
+ (list* 'new (symbol (subs opname 0 (dec (count opname)))) (next form))
884
+ (meta form))
885
+ :else form))
886
+ form)))))
887
+
888
+ (defn analyze-seq
889
+ [env form name]
890
+ (let [env (assoc env :line
891
+ (or (-> form meta :line)
892
+ (:line env)))]
893
+ (let [op (first form)]
894
+ (assert (not (nil? op)) "Can't call nil")
895
+ (let [mform (macroexpand-1 env form)]
896
+ (if (identical? form mform)
897
+ (if (specials op)
898
+ (parse op env form name)
899
+ (parse-invoke env form))
900
+ (analyze env mform name))))))
901
+
902
+ (declare analyze-wrap-meta)
903
+
904
+ (defn analyze-map
905
+ [env form name]
906
+ (let [expr-env (assoc env :context :expr)
907
+ simple-keys? (every? #(or (string? %) (keyword? %))
908
+ (keys form))
909
+ ks (disallowing-recur (vec (map #(analyze expr-env % name) (keys form))))
910
+ vs (disallowing-recur (vec (map #(analyze expr-env % name) (vals form))))]
911
+ (analyze-wrap-meta {:op :map :env env :form form
912
+ :keys ks :vals vs :simple-keys? simple-keys?
913
+ :children (vec (interleave ks vs))}
914
+ name)))
915
+
916
+ (defn analyze-vector
917
+ [env form name]
918
+ (let [expr-env (assoc env :context :expr)
919
+ items (disallowing-recur (vec (map #(analyze expr-env % name) form)))]
920
+ (analyze-wrap-meta {:op :vector :env env :form form :items items :children items} name)))
921
+
922
+ (defn analyze-set
923
+ [env form name]
924
+ (let [expr-env (assoc env :context :expr)
925
+ items (disallowing-recur (vec (map #(analyze expr-env % name) form)))]
926
+ (analyze-wrap-meta {:op :set :env env :form form :items items :children items} name)))
927
+
928
+ (defn analyze-wrap-meta [expr name]
929
+ (let [form (:form expr)]
930
+ (if (meta form)
931
+ (let [env (:env expr) ; take on expr's context ourselves
932
+ expr (assoc-in expr [:env :context] :expr) ; change expr to :expr
933
+ meta-expr (analyze-map (:env expr) (meta form) name)]
934
+ {:op :meta :env env :form form
935
+ :meta meta-expr :expr expr :children [meta-expr expr]})
936
+ expr)))
937
+
938
+ (defn analyze
939
+ "Given an environment, a map containing {:locals (mapping of names to bindings), :context
940
+ (one of :statement, :expr, :return), :ns (a symbol naming the
941
+ compilation ns)}, and form, returns an expression object (a map
942
+ containing at least :form, :op and :env keys). If expr has any (immediately)
943
+ nested exprs, must have :children [exprs...] entry. This will
944
+ facilitate code walking without knowing the details of the op set."
945
+ ([env form] (analyze env form nil))
946
+ ([env form name]
947
+ (let [form (if (instance? clojure.lang.LazySeq form)
948
+ (or (seq form) ())
949
+ form)]
950
+ (load-core)
951
+ (cond
952
+ (symbol? form) (analyze-symbol env form)
953
+ (and (seq? form) (seq form)) (analyze-seq env form name)
954
+ (map? form) (analyze-map env form name)
955
+ (vector? form) (analyze-vector env form name)
956
+ (set? form) (analyze-set env form name)
957
+ (keyword? form) (analyze-keyword env form)
958
+ :else {:op :constant :env env :form form}))))
959
+
960
+ (defn analyze-file
961
+ [^String f]
962
+ (let [res (if (re-find #"^file://" f) (java.net.URL. f) (io/resource f))]
963
+ (assert res (str "Can't find " f " in classpath"))
964
+ (binding [*cljs-ns* 'cljs.user
965
+ *cljs-file* (.getPath ^java.net.URL res)
966
+ *ns* *reader-ns*]
967
+ (with-open [r (io/reader res)]
968
+ (let [env (empty-env)
969
+ pbr (clojure.lang.LineNumberingPushbackReader. r)
970
+ eof (Object.)]
971
+ (loop [r (read pbr false eof false)]
972
+ (let [env (assoc env :ns (get-namespace *cljs-ns*))]
973
+ (when-not (identical? eof r)
974
+ (analyze env r)
975
+ (recur (read pbr false eof false))))))))))