-
Notifications
You must be signed in to change notification settings - Fork 13
/
compiler.cljs
106 lines (91 loc) · 3.64 KB
/
compiler.cljs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
(ns quil-site.compiler
(:require [cljs.js :as cjs]
[cljs.analyzer :as ana :include-macros true]
[cljs.reader :as r]
[cljs.pprint :as pprint]
[goog.events :as events]
[goog.Promise :as Promise]
[goog.events.EventType :as EventType]
[quil-site.parser :as p])
(:import goog.net.XhrIo))
(defn fetch-file!
"Very simple implementation of XMLHttpRequests that given a file path
calls src-cb with the string fetched of nil in case of error.
See doc at https://developers.google.com/closure/library/docs/xhrio"
[file-url src-cb]
(try
(.send XhrIo file-url
(fn [e]
(if (.isSuccess (.-target e))
(src-cb (.. e -target getResponseText))
(src-cb nil))))
(catch :default e
(src-cb nil))))
(def macros-loaded (Promise/withResolver))
(def core-loaded (Promise/withResolver))
(def all-loaded (Promise/all #js [(.-promise macros-loaded)
(.-promise core-loaded)]))
(def cached-macros (atom {}))
; need to mock p5.prototype as during compilation
; cljs needs to evaluate quil.core$macros namespace and
; there it needs to prepare map of all constants and constants
; defined on p5.prototype. But we don't need to provide real
; values of constants as they are not used in macros and used in functions.
(set! js/p5
#js {"prototype" #js {}})
(set! js/window #js {})
(fetch-file! "/quil-cache.edn"
#(do
(.resolve macros-loaded)
(reset! cached-macros (r/read-string %))))
(def core-cache (atom nil))
(fetch-file! "/core-cache.edn" #(do
(.resolve core-loaded)
(reset! core-cache (r/read-string %))))
(defn load-macros-ns [{:keys [name macros] :as opts} cb]
(if-let [{:keys [source cache]} (@cached-macros
{:name name
:macros (boolean macros)})]
(cb {:lang :js
:source source
:cache cache})
(cb {:lang :clj
:source ""})))
(defn convert-warning [warning]
(let [{:keys [type env extra]} warning]
{:message (ana/error-message type extra)
:type :warning
:line (:line env)
:column (:column env)}))
(def state (atom nil))
(defn reset-state! []
(reset! state (cjs/empty-state))
(cjs/load-analysis-cache!
@state 'cljs.core (@core-cache 'cljs.core)))
(defn compile [source cb]
(when (nil? @state)
(reset-state!))
(let [warnings (atom [])]
(ana/with-warning-handlers [(fn [type env extra]
(swap! warnings conj
{:type type
:env env
:extra extra}))]
(cjs/compile-str @state source nil
{:load load-macros-ns
:eval cjs/js-eval
:verbose false}
#(cb (assoc % :warnings
(map convert-warning @warnings)))))))
(defn run [source cb]
(compile source (fn [res]
(when-let [value (:value res)]
(.postMessage (.-contentWindow (.querySelector js/document "iframe"))
#js {:type "eval"
:source (:value res)}
"*"))
(cb (update res
:error p/convert-error)))))
(goog/exportSymbol "compile" #(compile % (fn [res]
(println res))))
(goog/exportSymbol "run" run)