##// END OF EJS Templates
Use Parenscript's minifier and obfuscator... and namespaces. SAVES CURRENTLY BROKEN
naryl -
r25:4adc2646 default
parent child Browse files
Show More
@@ -0,0 +1,15 b''
1
2 (in-package sugar-qsp.api)
3
4 (defpsmacro with-call-args (args &body body)
5 `(progn
6 (init-args ,args)
7 ,@body
8 (get-result)))
9
10 (defpsmacro with-frame (&body body)
11 `(progn
12 (push-local-frame)
13 (unwind-protect
14 ,@body
15 (pop-local-frame))))
@@ -0,0 +1,37 b''
1
2 (in-package sugar-qsp.js)
3
4 ;;; Contains symbols from standard JS library to avoid obfuscating
5 ;;; and/or namespacing them
6
7 (cl:defmacro syms (cl:&rest syms)
8 `(cl:progn
9 ,@(cl:loop :for sym :in syms
10 :collect `(cl:export ',sym))))
11
12 (syms
13 ;; main
14 window
15 *object
16 now
17 onload
18 keys includes
19 has-own-property
20 ;; api
21 document get-element-by-id
22 onclick onchange
23 atob btoa
24 alert prompt
25 set-timeout set-interval clear-interval
26 *promise *j-s-o-n
27 href parse
28 set-prototype-of
29 body append-child remove-child
30 create-element set-attribute
31 *file-reader read-as-text
32 style display src
33 ;; lib
34 *number parse-int
35 to-upper-case concat
36 click target current-target files index-of
37 )
@@ -0,0 +1,15 b''
1
2 (in-package sugar-qsp.main)
3
4
5 (defpsmacro by-id (id)
6 `(chain document (get-element-by-id ,id)))
7
8 (defmacro+ps api-call (name &rest args)
9 `(,(intern (string-upcase name) "API") ,@args))
10
11 (defpsmacro root (&rest path)
12 `(@ data ,@path))
13
14 (defpsmacro in (key obj)
15 `(chain ,obj (has-own-property ,key)))
@@ -1,107 +1,106 b''
1 1
2 (in-package sugar-qsp)
2 (in-package sugar-qsp.api)
3 3
4 4 ;;; API deals with DOM manipulation and some bookkeeping for the
5 5 ;;; intrinsics, namely variables
6 6 ;;; API is an implementation detail and has no QSP documentation. It
7 7 ;;; doesn't call intrinsics
8 8
9 (setf (root api) (ps:create))
10
11 9 ;;; Utils
12 10
13 (defm (root api make-act-html) (title img)
14 (+ "<a class='qsp-act' href='#' onclick='SugarQSP.api.callAct(\"" title "\");'>"
11 (defun make-act-html (title img)
12 (+ "<a class='qsp-act' href='" (ps-inline call-act) "(\"" title "\");'>"
15 13 title
16 14 "</a>"))
17 15
18 (defm (root api make-menu-item-html) (num title img loc)
19 (+ "<a href='#' onclick='SugarQSP.api.runMenu(" num ", \"" loc "\")();'>"
16 (defun make-menu-item-html (num title img loc)
17 (+ "<a href='" (ps-inline run-menu) "(" num ", \"" loc "\")();'>"
20 18 "<img src='" img "'>"
21 19 title
22 20 "</a>"))
23 21
24 (defm (root api report-error) (text)
22 (defun report-error (text)
25 23 (alert text))
26 24
27 (defm (root api sleep) (msec)
28 (ps:new (*promise (ps:=> resolve (set-timeout resolve msec)))))
25 (defun sleep (msec)
26 (new (*promise (=> resolve (set-timeout resolve msec)))))
29 27
30 (defm (root api init-dom) ()
28 (defun init-dom ()
31 29 ;; Save/load buttons
32 (let ((btn (document.get-element-by-id "qsp-btn-save")))
33 (setf (ps:@ btn onclick) this.savegame)
34 (setf (ps:@ btn href) "#"))
35 (let ((btn (document.get-element-by-id "qsp-btn-open")))
36 (setf (ps:@ btn onclick) this.opengame)
37 (setf (ps:@ btn href) "#"))
30 (let ((btn (by-id "qsp-btn-save")))
31 (setf (@ btn onclick) savegame)
32 (setf (@ btn href) "#"))
33 (let ((btn (by-id "qsp-btn-open")))
34 (setf (@ btn onclick) opengame)
35 (setf (@ btn href) "#"))
38 36 ;; Close image on click
39 (setf (ps:@ (document.get-element-by-id "qsp-image-container") onclick)
40 (this.show-image nil))
37 (setf (@ (by-id "qsp-image-container") onclick)
38 (show-image nil))
41 39 ;; Close the dropdown on any click
42 (setf window.onclick
40 (setf (@ window onclick)
43 41 (lambda (event)
44 (setf (ps:@ (api-call get-frame :dropdown) style display) "none"))))
42 (setf (@ (get-frame :dropdown) style display) "none"))))
45 43
46 (defm (root api call-serv-loc) (var-name &rest args)
47 (let ((loc-name (api-call get-var name 0 :str)))
44 (defun call-serv-loc (var-name &rest args)
45 (let ((loc-name (get-var var-name 0 :str)))
48 46 (when loc-name
49 (let ((loc (ps:getprop (root locs) loc-name)))
47 (let ((loc (getprop (root locs) loc-name)))
50 48 (when loc
51 49 (funcall loc args))))))
52 50
53 51 ;;; Misc
54 52
55 (defm (root api newline) (key)
56 (this.append-id (this.key-to-id key) "<br>" t))
53 (defun newline (key)
54 (append-id (key-to-id key) "<br>" t))
57 55
58 (defm (root api clear-id) (id)
59 (setf (ps:inner-html (document.get-element-by-id id)) ""))
56 (defun clear-id (id)
57 (setf (inner-html (by-id id)) ""))
60 58
61 (setf (root api text-escaper) (document.create-element :textarea))
59 (defvar text-escaper (chain document (create-element :textarea)))
62 60
63 (defm (root api prepare-contents) (s &optional force-html)
64 (if (or force-html (var "USEHTML" 0 :num))
61 (defun prepare-contents (s &optional force-html)
62 (if (or force-html (get-var "USEHTML" 0 :num))
65 63 s
66 64 (progn
67 (setf (ps:@ (root api text-escaper) text-content) s)
68 (ps:inner-html (root api text-escaper)))))
65 (setf (@ text-escaper text-content) s)
66 (inner-html text-escaper))))
69 67
70 (defm (root api get-id) (id &optional force-html)
71 (ps:inner-html (document.get-element-by-id id)))
68 (defun get-id (id &optional force-html)
69 (inner-html (by-id id)))
72 70
73 (defm (root api set-id) (id contents &optional force-html)
74 (setf (ps:inner-html (document.get-element-by-id id)) (this.prepare-contents contents force-html)))
71 (defun set-id (id contents &optional force-html)
72 (setf (inner-html (by-id id)) (prepare-contents contents force-html)))
75 73
76 (defm (root api append-id) (id contents &optional force-html)
74 (defun append-id (id contents &optional force-html)
77 75 (when contents
78 (incf (ps:inner-html (document.get-element-by-id id)) (this.prepare-contents contents force-html))))
76 (incf (inner-html (by-id id)) (prepare-contents contents force-html))))
79 77
80 78 ;;; Function calls
81 79
82 (defm (root api init-args) (args)
80 (defun init-args (args)
83 81 (dotimes (i (length args))
84 82 (let ((arg (elt args i)))
85 83 (if (numberp arg)
86 (this.set-var args i :num arg)
87 (this.set-var args i :str arg)))))
84 (set-var args i :num arg)
85 (set-var args i :str arg)))))
88 86
89 (defm (root api get-result) ()
90 (if (not (equal "" (var result 0 :str)))
91 (var result 0 :str)
92 (var result 0 :num)))
87 (defun get-result ()
88 (if (not (equal "" (get-var result 0 :str)))
89 (get-var result 0 :str)
90 (get-var result 0 :num)))
93 91
94 (defm (root api call-loc) (name args)
92 (defun call-loc (name args)
95 93 (with-frame
96 (funcall (ps:getprop (root locs) name) args)))
94 (with-call-args args
95 (funcall (getprop (root locs) name) args))))
97 96
98 (defm (root api call-act) (title)
97 (defun call-act (title)
99 98 (with-frame
100 (funcall (ps:getprop (root acts) title))))
99 (funcall (getprop (root acts) title 'act))))
101 100
102 101 ;;; Text windows
103 102
104 (defm (root api key-to-id) (key)
103 (defun key-to-id (key)
105 104 (case key
106 105 (:main "qsp-main")
107 106 (:stat "qsp-stat")
@@ -109,288 +108,298 b''
109 108 (:acts "qsp-acts")
110 109 (:input "qsp-input")
111 110 (:dropdown "qsp-dropdown")
112 (t (this.report-error "Internal error!"))))
111 (t (report-error "Internal error!"))))
113 112
114 (defm (root api get-frame) (key)
115 (document.get-element-by-id (this.key-to-id key)))
113 (defun get-frame (key)
114 (by-id (key-to-id key)))
116 115
117 (defm (root api add-text) (key text)
118 (this.append-id (this.key-to-id key) text))
116 (defun add-text (key text)
117 (append-id (key-to-id key) text))
119 118
120 (defm (root api get-text) (key)
121 (this.get-id (this.key-to-id key)))
119 (defun get-text (key)
120 (get-id (key-to-id key)))
122 121
123 (defm (root api clear-text) (key)
124 (this.clear-id (this.key-to-id key)))
122 (defun clear-text (key)
123 (clear-id (key-to-id key)))
125 124
126 (defm (root api enable-frame) (key enable)
127 (let ((obj (this.get-frame key)))
128 (setf obj.style.display (if enable "block" "none"))
125 (defun enable-frame (key enable)
126 (let ((obj (get-frame key)))
127 (setf (@ obj style display) (if enable "block" "none"))
129 128 (values)))
130 129
131 130 ;;; Actions
132 131
133 (defm (root api add-act) (title img act)
134 (setf (ps:getprop (root acts) title)
135 (ps:create :img img :act act))
136 (this.update-acts))
132 (defun add-act (title img act)
133 (setf (getprop (root acts) title)
134 (create img img act act))
135 (update-acts))
137 136
138 (defm (root api del-act) (title)
139 (delete (ps:getprop (root acts) title))
140 (this.update-acts))
137 (defun del-act (title)
138 (delete (getprop (root acts) title))
139 (update-acts))
141 140
142 (defm (root api clear-act) ()
143 (setf (root acts) (ps:create))
144 (this.clear-id "qsp-acts"))
141 (defun clear-act ()
142 (setf (root acts) (create))
143 (clear-id "qsp-acts"))
145 144
146 (defm (root api update-acts) ()
147 (this.clear-id "qsp-acts")
148 (let ((elt (document.get-element-by-id "qsp-acts")))
149 (ps:for-in (title (root acts))
150 (let ((obj (ps:getprop (root acts) title)))
151 (incf (ps:inner-html elt) (this.make-act-html title (ps:getprop obj :img)))))))
145 (defun update-acts ()
146 (clear-id "qsp-acts")
147 (let ((elt (by-id "qsp-acts")))
148 (for-in (title (root acts))
149 (let ((obj (getprop (root acts) title)))
150 (incf (inner-html elt) (make-act-html title (getprop obj :img)))))))
152 151
153 152
154 153 ;;; "Syntax"
155 154
156 (defm (root api qspfor) (name index from to step body)
157 (block nil
158 (ps:for ((i from))
155 (defun qspfor (name index from to step body)
156 (for ((i from))
159 157 ((< i to))
160 158 ((incf i step))
161 (this.set-var name index :num i)
159 (set-var name index :num i)
162 160 (unless (funcall body)
163 (return)))))
161 (return-from qspfor))))
164 162
165 163 ;;; Variable class
166 164
167 (defm (root api *var) (name)
165 (defun *var (name)
168 166 ;; From strings to numbers
169 (setf this.indexes (ps:create))
167 (setf (@ this indexes) (create))
170 168 ;; From numbers to {num: 0, str: ""} objects
171 (setf this.values (list))
169 (setf (@ this values) (list))
172 170 (values))
173 171
174 (defm (root api *var prototype new-value) ()
175 (ps:create :num 0 :str ""))
172 (defun new-value ()
173 (create :num 0 :str ""))
176 174
177 (defm (root api *var prototype index-num) (index)
175 (setf (@ *var prototype index-num)
176 (lambda (index)
178 177 (let ((num-index
179 178 (if (stringp index)
180 (if (in index this.indexes)
181 (ps:getprop this.indexes index)
182 (let ((n (length this.values)))
183 (setf (ps:getprop this.indexes index) n)
179 (if (in index (@ this indexes))
180 (getprop (@ this indexes) index)
181 (let ((n (length (@ this values))))
182 (setf (getprop (@ this indexes) index) n)
184 183 n))
185 184 index)))
186 (unless (in num-index this.values)
187 (setf (elt this.values num-index) (this.new-value)))
188 num-index))
185 (unless (in num-index (@ this values))
186 (setf (elt (@ this values) num-index) (new-value)))
187 num-index)))
189 188
190 (defm (root api *var prototype get) (index slot)
189 (setf (@ *var prototype get)
190 (lambda (index slot)
191 191 (unless (or index (= 0 index))
192 (setf index (1- (length this.values))))
193 (ps:getprop this.values (this.index-num index) slot))
192 (setf index (1- (length (@ this values)))))
193 (getprop (@ this values) (chain this (index-num index)) slot)))
194 194
195 (defm (root api *var prototype set) (index slot value)
195 (setf (@ *var prototype set)
196 (lambda (index slot value)
196 197 (unless (or index (= 0 index))
197 (setf index (length store)))
198 (setf index (length (@ this values))))
198 199 (case slot
199 (:num (setf value (ps:chain *number (parse-int value))))
200 (:str (setf value (ps:chain value (to-string)))))
201 (setf (ps:getprop this.values (this.index-num index) slot) value)
202 (values))
200 (:num (setf value (chain *number (parse-int value))))
201 (:str (setf value (chain value (to-string)))))
202 (setf (getprop (@ this values)
203 (chain this (index-num index))
204 slot) value)
205 (values)))
203 206
204 (defm (root api *var prototype kill) (index)
205 (setf (elt this.values (this.index-num index)) (this.new-value)))
207 (setf (@ *var prototype kill)
208 (lambda (index)
209 (setf (elt (@ this values) (chain this (index-num index)))
210 (new-value))
211 (delete (getprop 'this 'indexes index))))
206 212
207 213 ;;; Variables
208 214
209 (defm (root api var-real-name) (name)
210 (if (= (ps:@ name 0) #\$)
211 (values (ps:chain name (substr 1)) :str)
215 (defun var-real-name (name)
216 (if (= (@ name 0) #\$)
217 (values (chain name (substr 1)) :str)
212 218 (values name :num)))
213 219
214 (defm (root api ensure-var) (name)
215 (let ((store (this.var-ref name)))
220 (defun ensure-var (name)
221 (let ((store (var-ref name)))
216 222 (unless store
217 (setf store (ps:new (this.-var name)))
218 (setf (ps:getprop (root vars) name) store))
223 (setf store (new (-var name)))
224 (setf (getprop (root vars) name) store))
219 225 store))
220 226
221 (defm (root api var-ref) (name)
222 (let ((local-store (this.current-local-frame)))
227 (defun var-ref (name)
228 (let ((local-store (current-local-frame)))
223 229 (cond ((and local-store (in name local-store))
224 (ps:getprop local-store name))
230 (getprop local-store name))
225 231 ((in name (root vars))
226 (ps:getprop (root vars) name))
232 (getprop (root vars) name))
227 233 (t nil))))
228 234
229 (defm (root api get-var) (name index slot)
230 (ps:chain (this.ensure-var name) (get index slot)))
235 (defun get-var (name index slot)
236 (chain (ensure-var name) (get index slot)))
231 237
232 (defm (root api set-var) (name index slot value)
233 (ps:chain (this.ensure-var name) (set index slot value))
238 (defun set-var (name index slot value)
239 (chain (ensure-var name) (set index slot value))
234 240 (values))
235 241
236 (defm (root api get-array) (name)
237 (this.var-ref name))
242 (defun get-array (name)
243 (var-ref name))
238 244
239 (defm (root api set-array) (name value)
240 (let ((store (this.var-ref name)))
241 (setf (ps:@ store values) (ps:@ value values))
242 (setf (ps:@ store indexes) (ps:@ value indexes)))
245 (defun set-array (name value)
246 (let ((store (var-ref name)))
247 (setf (@ store values) (@ value values))
248 (setf (@ store indexes) (@ value indexes)))
243 249 (values))
244 250
245 (defm (root api kill-var) (name &optional index)
251 (defun kill-var (name &optional index)
246 252 (if (and index (not (= 0 index)))
247 (ps:chain (ps:getprop (root vars) name) (kill index))
248 (ps:delete (ps:getprop (root vars) name)))
253 (chain (getprop (root vars) name) (kill index))
254 (delete (getprop (root vars) name)))
249 255 (values))
250 256
251 (defm (root api array-size) (name)
252 (ps:getprop (this.var-ref name) 'length))
257 (defun array-size (name)
258 (getprop (var-ref name) 'length))
253 259
254 260 ;;; Locals
255 261
256 (defm (root api push-local-frame) ()
257 (ps:chain (root locals) (push (ps:create)))
262 (defun push-local-frame ()
263 (chain (root locals) (push (create)))
258 264 (values))
259 265
260 (defm (root api pop-local-frame) ()
261 (ps:chain (root locals) (pop))
266 (defun pop-local-frame ()
267 (chain (root locals) (pop))
262 268 (values))
263 269
264 (defm (root api current-local-frame) ()
270 (defun current-local-frame ()
265 271 (elt (root locals) (1- (length (root locals)))))
266 272
267 (defm (root api new-local) (name)
268 (let ((frame (this.current-local-frame)))
273 (defun new-local (name)
274 (let ((frame (current-local-frame)))
269 275 (unless (in name frame)
270 (setf (ps:getprop frame name) (ps:create)))
276 (setf (getprop frame name) (create)))
271 277 (values)))
272 278
273 279 ;;; Objects
274 280
275 (defm (root api update-objs) ()
276 (let ((elt (document.get-element-by-id "qsp-objs")))
277 (setf (ps:inner-html elt) "<ul>")
281 (defun update-objs ()
282 (let ((elt (by-id "qsp-objs")))
283 (setf (inner-html elt) "<ul>")
278 284 (loop :for obj :in (root objs)
279 :do (incf (ps:inner-html elt) (+ "<li>" obj)))
280 (incf (ps:inner-html elt) "</ul>")))
285 :do (incf (inner-html elt) (+ "<li>" obj)))
286 (incf (inner-html elt) "</ul>")))
281 287
282 288 ;;; Menu
283 289
284 (defm (root api menu) (menu-data)
285 (let ((elt (document.get-element-by-id "qsp-dropdown"))
290 (defun menu (menu-data)
291 (let ((elt (by-id "qsp-dropdown"))
286 292 (i 0))
287 (setf (ps:inner-html elt) "")
293 (setf (inner-html elt) "")
288 294 (loop :for item :in menu-data
289 295 :do (incf i)
290 :do (incf (ps:inner-html elt) (this.make-menu-item-html i item.text item.icon item.loc)))
291 (setf elt.style.display "block")))
296 :do (incf (inner-html elt) (make-menu-item-html i
297 (@ item text)
298 (@ item icon)
299 (@ item loc))))
300 (setf (@ elt style display) "block")))
292 301
293 302 ;;; Content
294 303
295 (defm (root api clean-audio) ()
296 (loop :for k :in (*object.keys (root playing))
297 :for v := (ps:getprop (root playing) k)
298 :do (when (ps:@ v ended)
299 (ps:delete (ps:@ (root playing) k)))))
304 (defun clean-audio ()
305 (loop :for k :in (chain *object (keys (root playing)))
306 :for v := (getprop (root playing) k)
307 :do (when (@ v ended)
308 (delete (@ (root playing) k)))))
300 309
301 (defm (root api show-image) (path)
302 (let ((img (document.get-element-by-id "qsp-image")))
310 (defun show-image (path)
311 (let ((img (by-id "qsp-image")))
303 312 (cond (path
304 (setf img.src path)
305 (setf img.style.display "flex"))
313 (setf (@ img src) path)
314 (setf (@ img style display) "flex"))
306 315 (t
307 (setf img.src "")
308 (setf img.style.display "hidden")))))
316 (setf (@ img src) "")
317 (setf (@ img style display) "hidden")))))
309 318
310 319 ;;; Saves
311 320
312 (defm (root api opengame) ()
313 (let ((element (document.create-element :input)))
314 (element.set-attribute :type :file)
315 (element.set-attribute :id :qsp-opengame)
316 (element.set-attribute :tabindex -1)
317 (element.set-attribute "aria-hidden" t)
318 (setf element.style.display :block)
319 (setf element.style.visibility :hidden)
320 (setf element.style.position :fixed)
321 (setf element.onchange
321 (defun opengame ()
322 (let ((element (chain document (create-element :input))))
323 (chain element (set-attribute :type :file))
324 (chain element (set-attribute :id :qsp-opengame))
325 (chain element (set-attribute :tabindex -1))
326 (chain element (set-attribute "aria-hidden" t))
327 (setf (@ element style display) :block)
328 (setf (@ element style visibility) :hidden)
329 (setf (@ element style position) :fixed)
330 (setf (@ element onchange)
322 331 (lambda (event)
323 (let* ((file (elt event.target.files 0))
324 (reader (ps:new (*file-reader))))
325 (setf reader.onload
332 (let* ((file (@ event target files 0))
333 (reader (new (*file-reader))))
334 (setf (@ reader onload)
326 335 (lambda (ev)
327 336 (block nil
328 (let ((target ev.current-target))
329 (unless target.result
337 (let ((target (@ ev current-target)))
338 (unless (@ target result)
330 339 (return))
331 (api-call base64-to-state target.result)
332 (api-call unstash-state)))))
333 (reader.read-as-text file))))
334 (document.body.append-child element)
335 (element.click)
336 (document.body.remove-child element)))
340 (base64-to-state (@ target result))
341 (unstash-state)))))
342 (chain reader (read-as-text file)))))
343 (chain document body (append-child element))
344 (chain element (click))
345 (chain document body (remove-child element))))
337 346
338 (defm (root api savegame) ()
339 (let ((element (document.create-element :a)))
340 (element.set-attribute :href (+ "data:text/plain;charset=utf-8," (api-call state-to-base64)))
341 (element.set-attribute :download "savegame.sav")
342 (setf element.style.display :none)
343 (document.body.append-child element)
344 (element.click)
345 (document.body.remove-child element)))
347 (defun savegame ()
348 (let ((element (chain document (create-element :a))))
349 (chain element (set-attribute :href (+ "data:text/plain;charset=utf-8," (state-to-base64))))
350 (chain element (set-attribute :download "savegame.sav"))
351 (setf (@ element style display) :none)
352 (chain document body (append-child element))
353 (chain element (click))
354 (chain document body (remove-child element))))
346 355
347 (defm (root api stash-state) (args)
348 (api-call call-serv-loc "ONGSAVE")
356 (defun stash-state (args)
357 (call-serv-loc "ONGSAVE")
349 358 (setf (root state-stash)
350 (*j-s-o-n.stringify
351 (ps:create vars (root vars)
359 (chain *j-s-o-n (stringify
360 (create vars (root vars)
352 361 objs (root objs)
353 362 loc-args args
354 msecs (- (*date.now) (root started-at))
355 main-html (ps:inner-html
356 (document.get-element-by-id :qsp-main))
357 stat-html (ps:inner-html
358 (document.get-element-by-id :qsp-stat))
359 next-location (root current-location))))
363 msecs (- (chain *date (now)) (root started-at))
364 main-html (inner-html
365 (by-id :qsp-main))
366 stat-html (inner-html
367 (by-id :qsp-stat))
368 next-location (root current-location)))))
360 369 (values))
361 370
362 (defm (root api unstash-state) ()
363 (let ((data (*j-s-o-n.parse (root state-stash))))
364 (this.clear-act)
365 (setf (root vars) (ps:@ data vars))
366 (loop :for k :in (*object.keys (root vars))
367 :do (*object.set-prototype-of (ps:getprop (root vars) k)
368 (root api *var prototype)))
369 (setf (root started-at) (- (*date.now) (ps:@ data msecs)))
370 (setf (root objs) (ps:@ data objs))
371 (setf (root current-location) (ps:@ data next-location))
372 (setf (ps:inner-html (document.get-element-by-id :qsp-main))
373 (ps:@ data main-html))
374 (setf (ps:inner-html (document.get-element-by-id :qsp-stat))
375 (ps:@ data stat-html))
376 (this.update-objs)
377 (api-call call-serv-loc "ONGLOAD")
378 (api-call call-loc (root current-location) (ps:@ data loc-args))
371 (defun unstash-state ()
372 (let ((data (chain *j-s-o-n (parse (root state-stash)))))
373 (clear-act)
374 (setf (root vars) (@ data vars))
375 (loop :for k :in (chain *object (keys (root vars)))
376 :do (chain *object (set-prototype-of (getprop (root vars) k)
377 (@ *var prototype))))
378 (setf (root started-at) (- (chain *date (now)) (@ data msecs)))
379 (setf (root objs) (@ data objs))
380 (setf (root current-location) (@ data next-location))
381 (setf (inner-html (by-id :qsp-main))
382 (@ data main-html))
383 (setf (inner-html (by-id :qsp-stat))
384 (@ data stat-html))
385 (update-objs)
386 (call-serv-loc "ONGLOAD")
387 (call-loc (root current-location) (@ data loc-args))
379 388 (values)))
380 389
381 (defm (root api state-to-base64) ()
390 (defun state-to-base64 ()
382 391 (btoa (encode-u-r-i-component (root state-stash))))
383 392
384 (defm (root api base64-to-state) (data)
393 (defun base64-to-state (data)
385 394 (setf (root state-stash) (decode-u-r-i-component (atob data))))
386 395
387 396 ;;; Timers
388 397
389 (defm (root api set-timer) (interval)
398 (defun set-timer (interval)
390 399 (setf (root timer-interval) interval)
391 400 (clear-interval (root timer-obj))
392 401 (setf (root timer-obj)
393 402 (set-interval
394 403 (lambda ()
395 (api-call call-serv-loc "COUNTER"))
404 (call-serv-loc "COUNTER"))
396 405 interval)))
@@ -8,21 +8,25 b''
8 8 (asdf:system-source-directory :sugar-qsp)))
9 9 (defun read-code-from-string (string)
10 10 (with-input-from-string (in string)
11 (let ((*package* *package*))
11 12 `(progn
12 13 ,@(loop :for form := (read in nil :eof)
13 14 :until (eq form :eof)
14 :collect form))))
15 :when (eq (first form) 'cl:in-package)
16 :do (setf *package* (find-package (second form)))
17 :else
18 :collect form)))))
15 19 (defun load-src (filename)
16 20 (alexandria:read-file-into-string (src-file filename))))
17 21
18 22 (defclass compiler ()
19 23 ((body :accessor body :initform #.(load-src "extras/body.html"))
20 24 (css :accessor css :initform (list #.(load-src "extras/default.css")))
21 (js :accessor js :initform '#.(mapcar #'read-code-from-string
22 (mapcar #'load-src
23 (list "src/intrinsics.ps"
24 "src/api.ps"
25 "src/main.ps"))))
25 (js :accessor js :initform (reverse
26 (list
27 '#.(read-code-from-string (load-src "src/main.ps"))
28 '#.(read-code-from-string (load-src "src/api.ps"))
29 '#.(read-code-from-string (load-src "src/intrinsics.ps")))))
26 30 (compile :accessor compile-only :initarg :compile)
27 31 (target :accessor target :initarg :target)
28 32 (beautify :accessor beautify :initarg :beautify)))
@@ -1,5 +1,5 b''
1 1
2 (in-package sugar-qsp)
2 (in-package sugar-qsp.lib)
3 3
4 4 ;;;; Macros implementing some intrinsics where it makes sense
5 5 ;;;; E.g. an equivalent JS function exists, or it's a direct API call
@@ -8,74 +8,74 b''
8 8
9 9 ;;; 2var
10 10
11 (ps:defpsmacro killvar (varname &optional index)
12 `(api-call kill-var ,varname ,index))
11 (defpsmacro killvar (varname &optional index)
12 `(kill-var ,varname ,index))
13 13
14 (ps:defpsmacro killall ()
14 (defpsmacro killall ()
15 15 `(api-call kill-all))
16 16
17 17 ;;; 3expr
18 18
19 (ps:defpsmacro obj (name)
19 (defpsmacro obj (name)
20 20 `(funcall (root objs includes) ,name))
21 21
22 (ps:defpsmacro loc (name)
22 (defpsmacro loc (name)
23 23 `(funcall (root locs includes) ,name))
24 24
25 (ps:defpsmacro no (arg)
25 (defpsmacro no (arg)
26 26 `(- -1 ,arg))
27 27
28 28 ;;; 4code
29 29
30 (ps:defpsmacro qspver ()
30 (defpsmacro qspver ()
31 31 "0.0.1")
32 32
33 (ps:defpsmacro curloc ()
33 (defpsmacro curloc ()
34 34 `(root current-location))
35 35
36 (ps:defpsmacro rnd ()
37 `(funcall (root lib rand) 1 1000))
36 (defpsmacro rnd ()
37 `(funcall rand 1 1000))
38 38
39 (ps:defpsmacro qspmax (&rest args)
39 (defpsmacro qspmax (&rest args)
40 40 (if (= 1 (length args))
41 41 `(*math.max.apply nil ,@args)
42 42 `(*math.max ,@args)))
43 43
44 (ps:defpsmacro qspmin (&rest args)
44 (defpsmacro qspmin (&rest args)
45 45 (if (= 1 (length args))
46 46 `(*math.min.apply nil ,@args)
47 47 `(*math.min ,@args)))
48 48
49 49 ;;; 5arrays
50 50
51 (ps:defpsmacro arrsize (name)
51 (defpsmacro arrsize (name)
52 52 `(api-call array-size ,name))
53 53
54 54 ;;; 6str
55 55
56 (ps:defpsmacro len (s)
56 (defpsmacro len (s)
57 57 `(length ,s))
58 58
59 (ps:defpsmacro mid (s from &optional count)
60 `(ps:chain ,s (substring ,from ,count)))
59 (defpsmacro mid (s from &optional count)
60 `(chain ,s (substring ,from ,count)))
61 61
62 (ps:defpsmacro ucase (s)
63 `(ps:chain ,s (to-upper-case)))
62 (defpsmacro ucase (s)
63 `(chain ,s (to-upper-case)))
64 64
65 (ps:defpsmacro lcase (s)
66 `(ps:chain ,s (to-lower-case)))
65 (defpsmacro lcase (s)
66 `(chain ,s (to-lower-case)))
67 67
68 (ps:defpsmacro trim (s)
69 `(ps:chain ,s (trim)))
68 (defpsmacro trim (s)
69 `(chain ,s (trim)))
70 70
71 (ps:defpsmacro replace (s from to)
72 `(ps:chain ,s (replace ,from ,to)))
71 (defpsmacro replace (s from to)
72 `(chain ,s (replace ,from ,to)))
73 73
74 (ps:defpsmacro val (s)
74 (defpsmacro val (s)
75 75 `(parse-int ,s 10))
76 76
77 (ps:defpsmacro qspstr (n)
78 `(ps:chain ,n (to-string)))
77 (defpsmacro qspstr (n)
78 `(chain ,n (to-string)))
79 79
80 80 ;;; 7if
81 81
@@ -85,77 +85,77 b''
85 85
86 86 ;; JUMP is in ps-macros.lisp (because it's a part of a huge kludge)
87 87
88 (ps:defpsmacro exit ()
88 (defpsmacro exit ()
89 89 `(return-from nil (values)))
90 90
91 91 ;;; 10dynamic
92 92
93 93 ;;; 11main
94 94
95 (ps:defpsmacro desc (s)
95 (defpsmacro desc (s)
96 96 (declare (ignore s))
97 97 "")
98 98
99 99 ;;; 12stat
100 100
101 (ps:defpsmacro showstat (enable)
101 (defpsmacro showstat (enable)
102 102 `(api-call enable-frame :stat ,enable))
103 103
104 104 ;;; 13diag
105 105
106 (ps:defpsmacro msg (text)
106 (defpsmacro msg (text)
107 107 `(alert ,text))
108 108
109 109 ;;; 14act
110 110
111 (ps:defpsmacro showacts (enable)
111 (defpsmacro showacts (enable)
112 112 `(api-call enable-frame :acts ,enable))
113 113
114 (ps:defpsmacro delact (name)
114 (defpsmacro delact (name)
115 115 `(api-call del-act ,name))
116 116
117 (ps:defpsmacro cla ()
117 (defpsmacro cla ()
118 118 `(api-call clear-act))
119 119
120 120 ;;; 15objs
121 121
122 (ps:defpsmacro showobjs (enable)
122 (defpsmacro showobjs (enable)
123 123 `(api-call enable-frame :objs ,enable))
124 124
125 (ps:defpsmacro countobj ()
125 (defpsmacro countobj ()
126 126 `(length (root objs)))
127 127
128 (ps:defpsmacro getobj (index)
128 (defpsmacro getobj (index)
129 129 `(or (elt (root objs) ,index) ""))
130 130
131 131 ;;; 16menu
132 132
133 133 ;;; 17sound
134 134
135 (ps:defpsmacro isplay (filename)
135 (defpsmacro isplay (filename)
136 136 `(funcall (root playing includes) ,filename))
137 137
138 138 ;;; 18img
139 139
140 (ps:defpsmacro view (&optional path)
140 (defpsmacro view (&optional path)
141 141 `(api-call show-image ,path))
142 142
143 143 ;;; 19input
144 144
145 (ps:defpsmacro showinput (enable)
145 (defpsmacro showinput (enable)
146 146 `(api-call enable-frame :input ,enable))
147 147
148 148 ;;; 20time
149 149
150 (ps:defpsmacro wait (msec)
150 (defpsmacro wait (msec)
151 151 `(await (api-call sleep ,msec)))
152 152
153 (ps:defpsmacro settimer (interval)
153 (defpsmacro settimer (interval)
154 154 `(api-call set-timer ,interval))
155 155
156 156 ;;; 21local
157 157
158 (ps:defpsmacro local (var &optional expr)
158 (defpsmacro local (var &optional expr)
159 159 `(progn
160 160 (api-call new-local ,(string (second var)))
161 161 ,@(when expr
@@ -165,10 +165,10 b''
165 165
166 166 ;;; misc
167 167
168 (ps:defpsmacro opengame (&optional filename)
168 (defpsmacro opengame (&optional filename)
169 169 (declare (ignore filename))
170 170 `(api-call opengame))
171 171
172 (ps:defpsmacro savegame (&optional filename)
172 (defpsmacro savegame (&optional filename)
173 173 (declare (ignore filename))
174 174 `(api-call savegame))
@@ -1,24 +1,22 b''
1 1
2 (in-package sugar-qsp)
2 (in-package sugar-qsp.lib)
3 3
4 4 ;;;; Functions and procedures defined by the QSP language.
5 5 ;;;; They can call api and deal with locations and other data directly.
6 6 ;;;; Except vars. Use VAR and SET or GET-VAR and SET-VAR api calls.
7 7
8 (setf (root lib) (ps:create))
9
10 8 ;;; 1loc
11 9
12 (defm (root lib goto) (target args)
13 (api-call clear-text :main)
14 (funcall (root lib xgoto) target (or args (list)))
10 (defun goto (target args)
11 (api:clear-text :main)
12 (funcall xgoto target (or args (list)))
15 13 (values))
16 14
17 (defm (root lib xgoto) (target args)
18 (api-call clear-act)
19 (setf (root current-location) (ps:chain target (to-upper-case)))
20 (api-call stash-state args)
21 (funcall (ps:getprop (root locs) (root current-location))
15 (defun xgoto (target args)
16 (api:clear-act)
17 (setf (root current-location) (chain target (to-upper-case)))
18 (api:stash-state args)
19 (funcall (getprop (root locs) (root current-location))
22 20 (or args (list)))
23 21 (values))
24 22
@@ -28,164 +26,166 b''
28 26
29 27 ;;; 4code
30 28
31 (defm (root lib rand) (a &optional (b 1))
29 (defun rand (a &optional (b 1))
32 30 (let ((min (min a b))
33 31 (max (max a b)))
34 (+ min (ps:chain *math (random (- max min))))))
32 (+ min (chain *math (random (- max min))))))
35 33
36 34 ;;; 5arrays
37 35
38 (defm (root lib copyarr) (to from start count)
36 (defun copyarr (to from start count)
39 37 (multiple-value-bind (to-name to-slot)
40 (api-call var-real-name to)
38 (api:var-real-name to)
41 39 (multiple-value-bind (from-name from-slot)
42 (api-call var-real-name from)
43 (ps:for ((i start))
44 ((< i (min (api-call array-size from-name)
40 (api:var-real-name from)
41 (for ((i start))
42 ((< i (min (api:array-size from-name)
45 43 (+ start count))))
46 44 ((incf i))
47 (api-call set-var to-name (+ start i) to-slot
48 (api-call get-var from-name (+ start i) from-slot))))))
45 (api:set-var to-name (+ start i) to-slot
46 (api:get-var from-name (+ start i) from-slot))))))
49 47
50 (defm (root lib arrpos) (name value &optional (start 0))
48 (defun arrpos (name value &optional (start 0))
51 49 (multiple-value-bind (real-name slot)
52 (api-call var-real-name name)
53 (ps:for ((i start)) ((< i (api-call array-size name))) ((incf i))
54 (when (eq (api-call get-var real-name i slot) value)
55 (return i))))
50 (api:var-real-name name)
51 (for ((i start)) ((< i (api:array-size name))) ((incf i))
52 (when (eq (api:get-var real-name i slot) value)
53 (return-from arrpos i))))
56 54 -1)
57 55
58 (defm (root lib arrcomp) (name pattern &optional (start 0))
56 (defun arrcomp (name pattern &optional (start 0))
59 57 (multiple-value-bind (real-name slot)
60 (api-call var-real-name name)
61 (ps:for ((i start)) ((< i (api-call array-size name))) ((incf i))
62 (when (funcall (ps:getprop (api-call get-var real-name i slot) 'match) pattern)
63 (return i))))
58 (api:var-real-name name)
59 (for ((i start)) ((< i (api:array-size name))) ((incf i))
60 (when (funcall (getprop (api:get-var real-name i slot) 'match) pattern)
61 (return-from arrcomp i))))
64 62 -1)
65 63
66 64 ;;; 6str
67 65
68 (defm (root lib instr) (s subs &optional (start 1))
69 (+ start (ps:chain s (substring (- start 1)) (search subs))))
66 (defun instr (s subs &optional (start 1))
67 (+ start (chain s (substring (- start 1)) (search subs))))
70 68
71 (defm (root lib isnum) (s)
69 (defun isnum (s)
72 70 (if (is-na-n s)
73 71 0
74 72 -1))
75 73
76 (defm (root lib strcomp) (s pattern)
77 (if (s.match pattern)
74 (defun strcomp (s pattern)
75 (if (chain s (match pattern))
78 76 -1
79 77 0))
80 78
81 (defm (root lib strfind) (s pattern group)
82 (let* ((re (ps:new (*reg-exp pattern)))
83 (match (re.exec s)))
84 (match.group group)))
79 (defun strfind (s pattern group)
80 (let* ((re (new (*reg-exp pattern)))
81 (match (chain re (exec s))))
82 (chain match (group group))))
85 83
86 (defm (root lib strpos) (s pattern &optional (group 0))
87 (let* ((re (ps:new (*reg-exp pattern)))
88 (match (re.exec s))
89 (found (match.group group)))
84 (defun strpos (s pattern &optional (group 0))
85 (let* ((re (new (*reg-exp pattern)))
86 (match (chain re (exec s)))
87 (found (chain match (group group))))
90 88 (if found
91 (s.search found)
89 (chain s (search found))
92 90 0)))
93 91
94 92 ;;; 7if
95 93
96 94 ;; Has to be a function because it always evaluates all three of its
97 95 ;; arguments
98 (defm (root lib iif) (cond-expr then-expr else-expr)
96 (defun iif (cond-expr then-expr else-expr)
99 97 (if cond-expr then-expr else-expr))
100 98
101 99 ;;; 8sub
102 100
103 (defm (root lib gosub) (target &rest args)
104 (funcall (ps:getprop (root locs) target) args)
101 (defun gosub (target &rest args)
102 (funcall (getprop (root locs) target) args)
105 103 (values))
106 104
107 (defm (root lib func) (target &rest args)
108 (funcall (ps:getprop (root locs) target) args))
105 (defun func (target &rest args)
106 (funcall (getprop (root locs) target) args))
109 107
110 108 ;;; 9loops
111 109
112 110 ;;; 10dynamic
113 111
114 (defm (root lib dynamic) (block &rest args)
112 (defun dynamic (block &rest args)
115 113 (when (stringp block)
116 (api-call report-error "DYNAMIC can't evaluate arbitrary strings.\nUse {braces} to create blocks for DYNAMIC."))
117 (funcall block args)
114 (api:report-error "DYNAMIC can't evaluate arbitrary strings.\nUse {braces} to create blocks for DYNAMIC."))
115 (api:with-call-args args
116 (funcall block args))
118 117 (values))
119 118
120 (defm (root lib dyneval) (block &rest args)
119 (defun dyneval (block &rest args)
121 120 (when (stringp block)
122 (api-call report-error "DYNEVAL can't evaluate arbitrary strings.\nUse {braces} to create blocks for DYNEVAL."))
123 (funcall block args))
121 (api:report-error "DYNEVAL can't evaluate arbitrary strings.\nUse {braces} to create blocks for DYNEVAL."))
122 (api:with-call-args args
123 (funcall block args)))
124 124
125 125 ;;; 11main
126 126
127 (defm (root lib main-p) (s)
128 (api-call add-text :main s)
127 (defun main-p (s)
128 (api:add-text :main s)
129 129 (values))
130 130
131 (defm (root lib main-pl) (s)
132 (api-call add-text :main s)
133 (api-call newline :main)
131 (defun main-pl (s)
132 (api:add-text :main s)
133 (api:newline :main)
134 134 (values))
135 135
136 (defm (root lib main-nl) (s)
137 (api-call newline :main)
138 (api-call add-text :main s)
136 (defun main-nl (s)
137 (api:newline :main)
138 (api:add-text :main s)
139 139 (values))
140 140
141 (defm (root lib maintxt) (s)
142 (api-call get-text :main)
141 (defun maintxt (s)
142 (api:get-text :main)
143 143 (values))
144 144
145 145 ;; For clarity (it leaves a lib.desc() call in JS)
146 (defm (root lib desc) (s)
146 (defun desc (s)
147 147 "")
148 148
149 (defm (root lib main-clear) ()
150 (api-call clear-text :main)
149 (defun main-clear ()
150 (api:clear-text :main)
151 151 (values))
152 152
153 153 ;;; 12stat
154 154
155 (defm (root lib stat-p) (s)
156 (api-call add-text :stat s)
155 (defun stat-p (s)
156 (api:add-text :stat s)
157 157 (values))
158 158
159 (defm (root lib stat-pl) (s)
160 (api-call add-text :stat s)
161 (api-call newline :stat)
159 (defun stat-pl (s)
160 (api:add-text :stat s)
161 (api:newline :stat)
162 162 (values))
163 163
164 (defm (root lib stat-nl) (s)
165 (api-call newline :stat)
166 (api-call add-text :stat s)
164 (defun stat-nl (s)
165 (api:newline :stat)
166 (api:add-text :stat s)
167 167 (values))
168 168
169 (defm (root lib stattxt) (s)
170 (api-call get-text :stat)
169 (defun stattxt (s)
170 (api:get-text :stat)
171 171 (values))
172 172
173 (defm (root lib stat-clear) ()
174 (api-call clear-text :stat)
173 (defun stat-clear ()
174 (api:clear-text :stat)
175 175 (values))
176 176
177 (defm (root lib cls) ()
178 (funcall (root lib stat-clear))
179 (funcall (root lib main-clear))
180 (funcall (root lib cla))
181 (funcall (root lib cmdclear))
177 (defun cls ()
178 (stat-clear)
179 (main-clear)
180 (cla)
181 (cmdclear)
182 182 (values))
183 183
184 184 ;;; 13diag
185 185
186 186 ;;; 14act
187 187
188 (defm (root lib curacts) ()
188 (defun curacts ()
189 189 (let ((acts (root acts)))
190 190 (lambda ()
191 191 (setf (root acts) acts)
@@ -193,89 +193,89 b''
193 193
194 194 ;;; 15objs
195 195
196 (defm (root lib addobj) (name)
197 (ps:chain (root objs) (push name))
198 (api-call update-objs)
196 (defun addobj (name)
197 (chain (root objs) (push name))
198 (api:update-objs)
199 199 (values))
200 200
201 (defm (root lib delobj) (name)
202 (let ((index (ps:chain (root objs) (index-of name))))
201 (defun delobj (name)
202 (let ((index (chain (root objs) (index-of name))))
203 203 (when (> index -1)
204 (funcall (root lib killobj) (1+ index))))
204 (killobj (1+ index))))
205 205 (values))
206 206
207 (defm (root lib killobj) (&optional (num nil))
207 (defun killobj (&optional (num nil))
208 208 (if (eq nil num)
209 209 (setf (root objs) (list))
210 (ps:chain (root objs) (splice (1- num) 1)))
211 (api-call update-objs)
210 (chain (root objs) (splice (1- num) 1)))
211 (api:update-objs)
212 212 (values))
213 213
214 214 ;;; 16menu
215 215
216 (defm (root lib menu) (menu-name)
216 (defun menu (menu-name)
217 217 (let ((menu-data (list)))
218 (loop :for item :in (api-call get-array (api-call var-real-name menu-name))
218 (loop :for item :in (api:get-array (api:var-real-name menu-name))
219 219 :do (cond ((string= item "")
220 220 (break))
221 221 ((string= item "-:-")
222 (ps:chain menu-data (push :delimiter)))
222 (chain menu-data (push :delimiter)))
223 223 (t
224 (let* ((tokens (ps:chain item (split ":"))))
224 (let* ((tokens (chain item (split ":"))))
225 225 (when (= (length tokens) 2)
226 (tokens.push ""))
227 (let* ((text (ps:chain tokens (splice 0 (- tokens.length 2)) (join ":")))
228 (loc (ps:getprop tokens (- tokens.length 2)))
229 (icon (ps:getprop tokens (- tokens.length 1))))
230 (ps:chain menu-data
231 (push (ps:create text text
226 (chain tokens (push "")))
227 (let* ((text (chain tokens (splice 0 (- (length tokens) 2)) (join ":")))
228 (loc (getprop tokens (- (length tokens) 2)))
229 (icon (getprop tokens (- (length tokens) 1))))
230 (chain menu-data
231 (push (create text text
232 232 loc loc
233 233 icon icon))))))))
234 (api-call menu menu-data)
234 (api:menu menu-data)
235 235 (values)))
236 236
237 237 ;;; 17sound
238 238
239 (defm (root lib play) (filename &optional (volume 100))
240 (let ((audio (ps:new (*audio filename))))
241 (setf (ps:getprop (root playing) filename) audio)
242 (setf (ps:@ audio volume) (* volume 0.01))
243 (ps:chain audio (play))))
239 (defun play (filename &optional (volume 100))
240 (let ((audio (new (*audio filename))))
241 (setf (getprop (root playing) filename) audio)
242 (setf (@ audio volume) (* volume 0.01))
243 (chain audio (play))))
244 244
245 (defm (root lib close) (filename)
245 (defun close (filename)
246 246 (funcall (root playing filename) stop)
247 (ps:delete (root playing filename)))
247 (delete (root playing filename)))
248 248
249 (defm (root lib closeall) ()
250 (loop :for k :in (*object.keys (root playing))
251 :for v := (ps:getprop (root playing) k)
249 (defun closeall ()
250 (loop :for k :in (chain *object (keys (root playing)))
251 :for v := (getprop (root playing) k)
252 252 :do (funcall v stop))
253 (setf (root playing) (ps:create)))
253 (setf (root playing) (create)))
254 254
255 255 ;;; 18img
256 256
257 (defm (root lib refint) ()
257 (defun refint ()
258 258 ;; "Force interface update" Uh... what exactly do we do here?
259 (api-call report-error "REFINT is not supported")
259 (api:report-error "REFINT is not supported")
260 260 )
261 261
262 262 ;;; 19input
263 263
264 (defm (root lib usertxt) ()
265 (let ((input (document.get-element-by-id "qsp-input")))
266 (ps:@ input value)))
264 (defun usertxt ()
265 (let ((input (by-id "qsp-input")))
266 (@ input value)))
267 267
268 (defm (root lib cmdclear) ()
269 (let ((input (document.get-element-by-id "qsp-input")))
270 (setf (ps:@ input value) "")))
268 (defun cmdclear ()
269 (let ((input (by-id "qsp-input")))
270 (setf (@ input value) "")))
271 271
272 (defm (root lib input) (text)
273 (window.prompt text))
272 (defun input (text)
273 (chain window (prompt text)))
274 274
275 275 ;;; 20time
276 276
277 (defm (root lib msecscount) ()
278 (- (*date.now) (root started-at)))
277 (defun msecscount ()
278 (- (chain *date (now)) (root started-at)))
279 279
280 280 ;;; 21local
281 281
@@ -283,19 +283,19 b''
283 283
284 284 ;;; misc
285 285
286 (defm (root lib rgb) (red green blue)
286 (defun rgb (red green blue)
287 287 (flet ((rgb-to-hex (comp)
288 (let ((hex (ps:chain (*number comp) (to-string 16))))
288 (let ((hex (chain (*number comp) (to-string 16))))
289 289 (if (< (length hex) 2)
290 290 (+ "0" hex)
291 291 hex))))
292 292 (+ "#" (rgb-to-hex red) (rgb-to-hex green) (rgb-to-hex red))))
293 293
294 (defm (root lib openqst) ()
295 (api-call report-error "OPENQST is not supported."))
294 (defun openqst ()
295 (api:report-error "OPENQST is not supported."))
296 296
297 (defm (root lib addqst) ()
298 (api-call report-error "ADDQST is not supported. Bundle the library with the main game."))
297 (defun addqst ()
298 (api:report-error "ADDQST is not supported. Bundle the library with the main game."))
299 299
300 (defm (root lib killqst) ()
301 (api-call report-error "KILLQST is not supported."))
300 (defun killqst ()
301 (api:report-error "KILLQST is not supported."))
@@ -59,8 +59,22 b''
59 59
60 60 ;;; JS
61 61
62 (defun minify-package (package-designator minify prefix)
63 (setf (ps:ps-package-prefix package-designator) prefix)
64 (if minify
65 (ps:obfuscate-package package-designator)
66 (ps:unobfuscate-package package-designator)))
67
62 68 (defmethod js-sources ((compiler compiler))
63 69 (let ((ps:*ps-print-pretty* (beautify compiler)))
70 (cond ((beautify compiler)
71 (minify-package "SUGAR-QSP.MAIN" nil "qsp_")
72 (minify-package "SUGAR-QSP.API" nil "qsp_api_")
73 (minify-package "SUGAR-QSP.LIB" nil "qsp_lib_"))
74 (t
75 (minify-package "SUGAR-QSP.MAIN" t "_")
76 (minify-package "SUGAR-QSP.API" t "a_")
77 (minify-package "SUGAR-QSP.LIB" t "l_")))
64 78 (format nil "~{~A~^~%~%~}" (mapcar #'ps:ps* (reverse (js compiler))))))
65 79
66 80 ;;; CSS
@@ -1,41 +1,43 b''
1 1
2 (in-package sugar-qsp)
2 (in-package sugar-qsp.main)
3 3
4 4 (setf (root)
5 (ps:create
5 (create
6 6 ;;; Game session state
7 7 ;; Variables
8 vars (ps:create)
8 vars (create)
9 9 ;; Inventory (objects)
10 10 objs (list)
11 current-location nil
11 12 ;; Game time
12 started-at (*date.now)
13 started-at (chain *date (now))
13 14 ;; Timers
14 15 timer-interval 500
15 16 timer-obj nil
16 17 ;;; Transient state
17 18 ;; Savegame data
18 state-stash (ps:create)
19 state-stash (create)
19 20 ;; List of audio files being played
20 playing (ps:create)
21 playing (create)
21 22 ;; Local variables stack (starts with an empty frame)
22 23 locals (list)
23 24 ;;; Game data
24 25 ;; ACTions
25 acts (ps:create)
26 acts (create)
26 27 ;; Locations
27 locs (ps:create)))
28 locs (create)))
28 29
29 30 ;; Launch the game from the first location
30 (setf window.onload
31 (setf (@ window onload)
31 32 (lambda ()
32 (api-call init-dom)
33 (#.(intern "INIT-DOM" "SUGAR-QSP.API"))
33 34 ;; For MSECCOUNT
34 (setf (root started-at) (*date.now))
35 (setf (root started-at) (chain *date (now)))
35 36 ;; For $COUNTER and SETTIMER
36 (api-call set-timer (root timer-interval))
37 (funcall (ps:getprop (root locs)
38 (ps:chain *object (keys (root locs)) 0))
37 (#.(intern "SET-TIMER" "SUGAR-QSP.API")
38 (root timer-interval))
39 (funcall (getprop (root locs)
40 (chain *object (keys (root locs)) 0))
39 41 (list))
40 42 (values)))
41 43
@@ -1,7 +1,93 b''
1 1
2 2 (in-package cl-user)
3 3
4 (defpackage :sugar-qsp.js)
5
6 (defpackage :sugar-qsp.main
7 (:use :cl :ps :sugar-qsp.js)
8 (:export #:api-call #:by-id
9 #:root #:in
10 #:vars #:objs #:current-location
11 #:started-at #:timer-interval #:timer-obj
12 #:state-stash #:playing #:locals
13 #:acts #:locs))
14
15 ;;; API functions
16 (defpackage :sugar-qsp.api
17 (:use :cl :ps :sugar-qsp.main :sugar-qsp.js)
18 (:export #:with-frame #:with-call-args
19 #:stash-state
20
21 #:report-error #:sleep #:init-dom #:call-serv-loc
22 #:newline #:clear-id #:prepare-contents #:get-id #:set-id #:append-id
23 #:init-args #:get-result #:call-loc #:call-act
24 #:get-frame #:add-text #:get-text #:clear-text #:enable-frame
25 #:add-act #:del-act #:clear-act #:update-acts
26 #:qspfor
27 #:*var #:new-value #:index-num #:get #:set #:kill
28 #:var-real-name #:ensure-var #:var-ref #:get-var #:set-var
29 #:get-array #:set-array #:kill-var #:array-size
30 #:push-local-frame #:pop-local-frame #:current-local-frame #:new-local
31 #:update-objs
32 #:menu
33 #:clean-audio
34 #:show-image
35 #:opengame #:savegame
36 ))
37
38 ;;; QSP library functions and macros
39 (defpackage :sugar-qsp.lib
40 (:use :cl :ps :sugar-qsp.main :sugar-qsp.js)
41 (:local-nicknames (#:api :sugar-qsp.api))
42 (:export #:str #:exec #:qspblock #:qspfor #:location
43 #:qspcond #:qspvar #:set #:local
44
45 #:killvar #:killall
46 #:obj #:loc #:no
47 #:qspver #:curloc
48 #:rnd #:qspmax #:qspmin
49 #:arrsize #:len
50 #:mid #:ucase #:lcase #:trim #:replace #:val #:qspstr
51 #:exit #:desc
52 #:showstat #:msg
53 #:showacts #:delact #:cla
54 #:showobjs #:countobj #:getobj
55 #:isplay
56 #:view
57 #:showinput
58 #:wait #:settimer
59 #:local
60 #:opengame #:savegame
61
62 #:goto #:xgoto
63 #:rand
64 #:copyarr #:arrpos #:arrcomp
65 #:instr #:isnum #:strcomp #:strfind #:strpos
66 #:iif
67 #:gosub #:func
68 #:dynamic #:dyneval
69 #:main-p #:main-pl #:main-nl #:maintxt #:desc #:main-clear
70 #:stat-p #:stat-pl #:stat-nl #:stattxt #:stat-clear #:cls
71 #:curacts
72 #:addobj #:delobj #:killobj
73 #:menu
74 #:play #:close #:closeall
75 #:refint
76 #:usertxt #:cmdclear #:input
77 #:msecscount
78 #:rgb
79 #:openqst #:addqst #:killqst
80 ))
81
82 ;;; The compiler
4 83 (defpackage :sugar-qsp
5 84 (:use :cl)
6 (:local-nicknames (#:p #:esrap))
85 (:local-nicknames (#:p #:esrap)
86 (#:lib :sugar-qsp.lib)
87 (#:api :sugar-qsp.api)
88 (#:main :sugar-qsp.main))
7 89 (:export #:parse-file #:entry-point))
90
91 (setf (ps:ps-package-prefix "SUGAR-QSP.MAIN") "qsp_")
92 (setf (ps:ps-package-prefix "SUGAR-QSP.API") "qsp_api_")
93 (setf (ps:ps-package-prefix "SUGAR-QSP.LIB") "qsp_lib_")
@@ -33,7 +33,7 b''
33 33 (not (find char " !:&=<>+-*/,'\"()[]{}"))))
34 34
35 35 (defun intern-first (list)
36 (list* (intern (string-upcase (first list)))
36 (list* (intern (string-upcase (first list)) :lib)
37 37 (rest list)))
38 38
39 39 (eval-when (:compile-toplevel :load-toplevel :execute)
@@ -44,7 +44,7 b''
44 44 (destructuring-bind (ws1 operator ws2 operand2)
45 45 list
46 46 (declare (ignore ws1 ws2))
47 (list (intern (string-upcase operator)) operand2)))
47 (list (intern (string-upcase operator) :lib) operand2)))
48 48
49 49 (defun do-binop% (left-op other-ops)
50 50 (if (null other-ops)
@@ -127,7 +127,7 b''
127 127 (digit-char-p character)))
128 128 (p:defrule identifier-raw (and id-first (* id-next))
129 129 (:lambda (list)
130 (intern (string-upcase (p:text list)))))
130 (intern (string-upcase (p:text list)) :lib)))
131 131
132 132 (p:defrule identifier (not-qsp-keyword-p identifier-raw))
133 133
@@ -137,7 +137,7 b''
137 137
138 138 (p:defrule normal-string (or sstring dstring)
139 139 (:lambda (str)
140 (list* 'str (or str (list "")))))
140 (list* 'lib:str (or str (list "")))))
141 141
142 142 (p:defrule sstring (and #\' (* (or string-interpol
143 143 sstring-exec
@@ -162,15 +162,15 b''
162 162
163 163 (p:defrule sstring-exec (and (p:~ "\"exec:") sstring-exec-body #\")
164 164 (:lambda (list)
165 (list* 'exec (p:parse 'exec-body (second list)))))
165 (list* 'lib:exec (p:parse 'exec-body (second list)))))
166 166
167 167 (p:defrule dstring-exec (and (p:~ "'exec:") dstring-exec-body #\')
168 168 (:lambda (list)
169 (list* 'exec (p:parse 'exec-body (second list)))))
169 (list* 'lib:exec (p:parse 'exec-body (second list)))))
170 170
171 171 (p:defrule brace-string (and #\{ before-statement block-body #\})
172 172 (:lambda (list)
173 (list* 'qspblock (third list))))
173 (list* 'lib:qspblock (third list))))
174 174
175 175 ;;; Location
176 176
@@ -181,7 +181,7 b''
181 181 (p:defrule location (and location-header block-body location-end)
182 182 (:destructure (header body end)
183 183 (declare (ignore end))
184 `(location (,header) ,@body)))
184 `(lib:location (,header) ,@body)))
185 185
186 186 (p:defrule location-header (and #\#
187 187 (+ not-newline)
@@ -246,11 +246,11 b''
246 246
247 247 (p:defrule string-output qsp-string
248 248 (:lambda (string)
249 (list 'main-pl string)))
249 (list 'lib:main-pl string)))
250 250
251 251 (p:defrule expression-output expression
252 252 (:lambda (list)
253 (list 'main-pl list)))
253 (list 'lib:main-pl list)))
254 254
255 255 (p:defrule label (and colon identifier)
256 256 (:lambda (list)
@@ -264,7 +264,7 b''
264 264
265 265 (p:defrule local (and (p:~ "local") spaces variable (p:? (and spaces? #\= spaces? expression)))
266 266 (:lambda (list)
267 (list* 'local (third list)
267 (list* 'lib:local (third list)
268 268 (when (fourth list)
269 269 (list (fourth (fourth list)))))))
270 270
@@ -274,7 +274,7 b''
274 274
275 275 (p:defrule block-if (and block-if-head block-if-body)
276 276 (:destructure (head body)
277 `(qspcond (,@head ,@(first body))
277 `(lib:qspcond (,@head ,@(first body))
278 278 ,@(rest body))))
279 279
280 280 (p:defrule block-if-head (and (p:~ "if") spaces expression spaces? colon spaces?)
@@ -335,7 +335,7 b''
335 335 (:lambda (list)
336 336 (intern-first (list (first list)
337 337 (third list)
338 (or (fifth list) '(str ""))))))
338 (or (fifth list) '(lib:str ""))))))
339 339
340 340 (p:defrule block-act-head-img (and #\, spaces? qsp-string spaces?)
341 341 (:lambda (list)
@@ -352,7 +352,7 b''
352 352 (:lambda (list)
353 353 (unless (eq (fourth (third list)) :num)
354 354 (error "For counter variable must be numeric."))
355 (list 'qspfor
355 (list 'lib:qspfor
356 356 (elt list 2)
357 357 (elt list 6)
358 358 (elt list 9)
@@ -428,12 +428,12 b''
428 428 (unless (<= ,min-arity (length arguments) ,max-arity)
429 429 (error "Intrinsic ~A expects between ~A and ~A arguments but ~A were provided:~%~S"
430 430 name ,min-arity ,max-arity (length arguments) arguments))
431 (list* ',sym arguments))))
431 (list* ',(intern (string sym) :lib) arguments))))
432 432
433 433 (defintrinsics (intrinsic returning-intrinsic non-returning-intrinsic)
434 434 ;; Transitions
435 (goto nil 0 10 "gt" "goto")
436 (xgoto nil 0 10 "xgt" "xgoto")
435 (goto% nil 0 10 "gt" "goto")
436 (xgoto% nil 0 10 "xgt" "xgoto")
437 437 ;; Variables
438 438 (killvar nil 0 2)
439 439 ;; Expressions
@@ -583,24 +583,24 b''
583 583 (p:defrule variable (and identifier (p:? array-index))
584 584 (:destructure (id idx)
585 585 (if (char= #\$ (elt (string id) 0))
586 (list 'var (intern (subseq (string-upcase id) 1)) (or idx 0) :str)
587 (list 'var id (or idx 0) :num))))
586 (list 'lib:qspvar (intern (subseq (string-upcase id) 1) :lib) (or idx 0) :str)
587 (list 'lib:qspvar id (or idx 0) :num))))
588 588
589 589 (p:defrule array-index (and #\[ spaces? (p:? expression) spaces? #\])
590 590 (:function third))
591 591
592 592 (p:defrule assignment (or kw-assignment plain-assignment op-assignment)
593 (:destructure (var eq expr)
593 (:destructure (qspvar eq expr)
594 594 (declare (ignore eq))
595 (list 'set var expr)))
595 (list 'lib:set qspvar expr)))
596 596
597 597 (p:defrule kw-assignment (and (or (p:~ "let") (p:~ "set")) spaces? (or op-assignment plain-assignment))
598 598 (:function third))
599 599
600 600 (p:defrule op-assignment (and variable spaces? (or "+" "-" "*" "/") #\= spaces? expression)
601 (:destructure (var ws1 op eq ws2 expr)
601 (:destructure (qspvar ws1 op eq ws2 expr)
602 602 (declare (ignore ws1 ws2))
603 (list var eq (intern-first (list op var expr)))))
603 (list qspvar eq (intern-first (list op qspvar expr)))))
604 604
605 605 (p:defrule plain-assignment (and variable spaces? #\= spaces? expression)
606 606 (:function remove-nil))
@@ -1,5 +1,5 b''
1 1
2 (in-package sugar-qsp)
2 (in-package sugar-qsp.lib)
3 3
4 4 ;;;; Parenscript macros which make the parser's intermediate
5 5 ;;;; representation directly compilable by Parenscript
@@ -7,40 +7,9 b''
7 7
8 8 ;;; Utils
9 9
10 (ps:defpsmacro defm (path args &body body)
11 `(setf ,path (lambda ,args ,@body)))
12
13 (ps:defpsmacro root (&rest path)
14 `(ps:@ *sugar-q-s-p ,@path))
15
16 (ps:defpsmacro in (key obj)
17 `(ps:chain ,obj (has-own-property ,key)))
18
19 (ps:defpsmacro with-frame (&body body)
20 `(progn
21 (api-call push-local-frame)
22 (unwind-protect
23 ,@body
24 (api-call pop-local-frame))))
25
26 10 ;;; Common
27 11
28 (defmacro defpsintrinsic (name)
29 `(ps:defpsmacro ,name (&rest args)
30 `(funcall (root lib ,',name)
31 ,@args)))
32
33 (defmacro defpsintrinsics (() &rest names)
34 `(progn ,@(loop :for name :in names
35 :collect `(defpsintrinsic ,name))))
36
37 (defpsintrinsics ()
38 rand copyarr arrpos arrcomp instr isnum strcomp strfind strpos iif gosub func dynamic dyneval main-p main-pl main-nl maintxt main-clear stat-p stat-pl stat-nl stattxt stat-clear cls curacts addobj delobj killobj menu play isplay close closeall refint view rgb showinput usertxt cmdclear input openqst addqst killqst opengame savegame wait msecscount settimer)
39
40 (ps:defpsmacro api-call (func &rest args)
41 `(funcall (root api ,func) ,@args))
42
43 (ps:defpsmacro label-block ((&key (locals t)) &body body)
12 (defpsmacro label-block ((&key (locals t)) &body body)
44 13 (let ((has-labels (some #'keywordp body)))
45 14 `(block nil
46 15 ,@(when has-labels
@@ -51,7 +20,7 b''
51 20 `((tagbody
52 21 ,@body))))))
53 22
54 (ps:defpsmacro str (&rest forms)
23 (defpsmacro str (&rest forms)
55 24 (cond ((zerop (length forms))
56 25 "")
57 26 ((and (= 1 (length forms))
@@ -62,60 +31,54 b''
62 31
63 32 ;;; 1loc
64 33
65 (ps:defpsmacro location ((name) &body body)
34 (defpsmacro location ((name) &body body)
66 35 `(setf (root locs ,name)
67 (ps:async-lambda (args)
36 (async-lambda (args)
68 37 (label-block ()
69 (api-call init-args args)
70 ,@body
71 (api-call get-result)))))
38 ,@body))))
72 39
73 (ps:defpsmacro goto (target &rest args)
40 (defpsmacro goto% (target &rest args)
74 41 `(progn
75 (funcall (root lib goto) ,target ,args)
42 (goto ,target ,args)
76 43 (exit)))
77 44
78 (ps:defpsmacro xgoto (target &rest args)
45 (defpsmacro xgoto% (target &rest args)
79 46 `(progn
80 (funcall (root lib xgoto) ,target ,args)
47 (xgoto ,target ,args)
81 48 (exit)))
82 49
83 (ps:defpsmacro desc (target)
84 (declare (ignore target))
85 (report-error "DESC is not supported"))
86
87 50 ;;; 2var
88 51
89 (ps:defpsmacro var (name index slot)
52 (defpsmacro qspvar (name index slot)
90 53 `(api-call get-var ,(string name) ,index ,slot))
91 54
92 (ps:defpsmacro set ((var vname vindex vslot) value)
93 (assert (eq var 'var))
55 (defpsmacro set ((var vname vindex vslot) value)
56 (assert (eq var 'qspvar))
94 57 `(api-call set-var ,(string vname) ,vindex ,vslot ,value))
95 58
96 59 ;;; 3expr
97 60
98 (ps:defpsmacro <> (op1 op2)
61 (defpsmacro <> (op1 op2)
99 62 `(not (equal ,op1 ,op2)))
100 63
101 (ps:defpsmacro ! (op1 op2)
64 (defpsmacro ! (op1 op2)
102 65 `(not (equal ,op1 ,op2)))
103 66
104 67 ;;; 4code
105 68
106 (ps:defpsmacro exec (&body body)
107 (format nil "javascript: ~{~A~^~%~}" (mapcar #'ps:ps* body)))
69 (defpsmacro exec (&body body)
70 (format nil "javascript: ~{~A~^~%~}" (mapcar #'ps* body)))
108 71
109 72 ;;; 5arrays
110 73
111 74 ;;; 6str
112 75
113 (ps:defpsmacro & (&rest args)
114 `(ps:chain "" (concat ,@args)))
76 (defpsmacro & (&rest args)
77 `(chain "" (concat ,@args)))
115 78
116 79 ;;; 7if
117 80
118 (ps:defpsmacro qspcond (&rest clauses)
81 (defpsmacro qspcond (&rest clauses)
119 82 `(cond ,@(loop :for clause :in clauses
120 83 :collect (list (first clause)
121 84 `(tagbody
@@ -126,11 +89,11 b''
126 89 ;;; 9loops
127 90 ;;; Yep, that's a huge kludge since Javascript doesn't support arbitrary labels
128 91
129 (ps:defpsmacro jump (target)
92 (defpsmacro jump (target)
130 93 `(return-from ,(intern (string-upcase (second target)))
131 (funcall (ps:getprop __labels ,target))))
94 (funcall (getprop __labels ,target))))
132 95
133 (ps:defpsmacro tagbody (&body body)
96 (defpsmacro tagbody (&body body)
134 97 (let ((funcs (list nil :__nil)))
135 98 (dolist (form body)
136 99 (cond ((keywordp form)
@@ -146,28 +109,26 b''
146 109 ,@body)
147 110 `(progn
148 111 (setf ,@(loop :for f :on funcs :by #'cddr
149 :append `((ps:@ __labels ,(first f))
112 :append `((@ __labels ,(first f))
150 113 (block ,(intern (string-upcase (string (first f))))
151 114 ,@(second f)
152 115 ,@(when (third f)
153 116 `((funcall
154 (ps:getprop __labels ,(third f)))))))))
117 (getprop __labels ,(third f)))))))))
155 118 (jump (str "__nil"))))))
156 119
157 120 ;;; 10dynamic
158 121
159 (ps:defpsmacro qspblock (&body body)
160 `(lambda (args)
122 (defpsmacro qspblock (&body body)
123 `(async-lambda (args)
161 124 (label-block ()
162 (api-call init-args args)
163 ,@body
164 (api-call get-result))))
125 ,@body)))
165 126
166 127 ;;; 11main
167 128
168 (ps:defpsmacro act (name img &body body)
129 (defpsmacro act (name img &body body)
169 130 `(api-call add-act ,name ,img
170 (lambda ()
131 (async-lambda ()
171 132 (label-block ()
172 133 ,@body))))
173 134
@@ -193,8 +154,8 b''
193 154
194 155 ;;; 22for
195 156
196 (ps:defpsmacro qspfor (var from to step &body body)
197 `(api-call qspfor
157 (defpsmacro qspfor (var from to step &body body)
158 `((intern "QSPFOR" "API")
198 159 ,(string (second var)) ,(third var) ;; name and index
199 160 ,from ,to ,step
200 161 (lambda ()
@@ -9,7 +9,10 b''
9 9 :serial t
10 10 :components ((:file "package")
11 11 (:file "patches")
12 (:file "js-syms")
13 (:file "main-macros")
12 14 (:file "ps-macros")
15 (:file "api-macros")
13 16 (:file "intrinsic-macros")
14 17 (:file "class")
15 18 (:file "main")
General Comments 0
You need to be logged in to leave comments. Login now