##// 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,396 +1,405 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")
108 107 (:objs "qsp-objs")
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)))))))
152
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)))))))
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))
159 ((< i to))
160 ((incf i step))
161 (this.set-var name index :num i)
162 (unless (funcall body)
163 (return)))))
155 (defun qspfor (name index from to step body)
156 (for ((i from))
157 ((< i to))
158 ((incf i step))
159 (set-var name index :num i)
160 (unless (funcall body)
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)
178 (let ((num-index
179 (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)
184 n))
185 index)))
186 (unless (in num-index this.values)
187 (setf (elt this.values num-index) (this.new-value)))
188 num-index))
175 (setf (@ *var prototype index-num)
176 (lambda (index)
177 (let ((num-index
178 (if (stringp index)
179 (if (in index (@ this indexes))
180 (getprop (@ this indexes) index)
181 (let ((n (length (@ this values))))
182 (setf (getprop (@ this indexes) index) n)
183 n))
184 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)
191 (unless (or index (= 0 index))
192 (setf index (1- (length this.values))))
193 (ps:getprop this.values (this.index-num index) slot))
189 (setf (@ *var prototype get)
190 (lambda (index slot)
191 (unless (or index (= 0 index))
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)
196 (unless (or index (= 0 index))
197 (setf index (length store)))
198 (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))
195 (setf (@ *var prototype set)
196 (lambda (index slot value)
197 (unless (or index (= 0 index))
198 (setf index (length (@ this values))))
199 (case slot
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
330 (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)))
337 (let ((target (@ ev current-target)))
338 (unless (@ target result)
339 (return))
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)
352 objs (root objs)
353 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))))
359 (chain *j-s-o-n (stringify
360 (create vars (root vars)
361 objs (root objs)
362 loc-args args
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)))
@@ -1,28 +1,32 b''
1 1
2 2 (in-package sugar-qsp)
3 3
4 4 (eval-when (:compile-toplevel :load-toplevel :execute)
5 5 (defun src-file (filename)
6 6 (uiop/pathname:merge-pathnames*
7 7 filename
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 `(progn
12 ,@(loop :for form := (read in nil :eof)
13 :until (eq form :eof)
14 :collect form))))
11 (let ((*package* *package*))
12 `(progn
13 ,@(loop :for form := (read in nil :eof)
14 :until (eq form :eof)
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,174 +1,174 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
6 6
7 7 ;;; 1loc
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
82 82 ;;; 8sub
83 83
84 84 ;;; 9loops
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
162 162 `((set ,var ,expr)))))
163 163
164 164 ;;; 22for
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,301 +1,301 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
25 23 ;;; 2var
26 24
27 25 ;;; 3expr
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)
45 (+ start count))))
46 ((incf i))
47 (api-call set-var to-name (+ start i) to-slot
48 (api-call get-var from-name (+ start i) from-slot))))))
40 (api:var-real-name from)
41 (for ((i start))
42 ((< i (min (api:array-size from-name)
43 (+ start count))))
44 ((incf i))
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)
192 192 (values))))
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
232 loc loc
233 icon icon))))))))
234 (api-call menu menu-data)
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 loc loc
233 icon icon))))))))
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
282 282 ;;; 22for
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."))
@@ -1,121 +1,135 b''
1 1
2 2 (in-package sugar-qsp)
3 3
4 4 (defun entry-point-no-args ()
5 5 (entry-point uiop:*command-line-arguments*))
6 6
7 7 (defun entry-point (args)
8 8 (catch :terminate
9 9 (let ((compiler (apply #'make-instance 'compiler (parse-opts args))))
10 10 (write-compiled-file compiler)))
11 11 (values))
12 12
13 13 (defun parse-opts (args)
14 14 (let ((mode :source)
15 15 (data (list :source nil :target nil :js nil :css nil :body nil :compile nil :beautify nil)))
16 16 (loop :for arg :in args
17 17 :do (alexandria:switch (arg :test #'string=)
18 18 ("-o" (setf mode :target))
19 19 ("--js" (setf mode :js))
20 20 ("--css" (setf mode :css))
21 21 ("--body" (setf mode :body))
22 22 ("-c" (setf (getf data :compile) t))
23 23 ("--beautify" (setf (getf data :beautify) t))
24 24 (t (push arg (getf data mode)))))
25 25 (unless (= 1 (length (getf data :source)))
26 26 (print-usage)
27 27 (report-error "There should be exactly one source"))
28 28 (unless (> 1 (length (getf data :target)))
29 29 (print-usage)
30 30 (report-error "There should be no more than one target"))
31 31 (unless (> 1 (length (getf data :body)))
32 32 (print-usage)
33 33 (report-error "There should be no more than one body"))
34 34 (unless (getf data :target)
35 35 (setf (getf data :target)
36 36 (let* ((source (first (getf data :source)))
37 37 (tokens (uiop:split-string source :separator "."))
38 38 (target (format nil "~{~A~^.~}.html"
39 39 (butlast tokens))))
40 40 (list target))))
41 41 (list :source (first (getf data :source))
42 42 :target (first (getf data :target))
43 43 :js (getf data :js)
44 44 :css (getf data :css)
45 45 :body (first (getf data :body))
46 46 :compile (getf data :compile)
47 47 :beautify (getf data :beautify))))
48 48
49 49 (defun print-usage ()
50 50 (format t "USAGE: "))
51 51
52 52 (defun parse-file (filename)
53 53 (p:parse 'sugar-qsp-grammar
54 54 (alexandria:read-file-into-string filename)))
55 55
56 56 (defun report-error (fmt &rest args)
57 57 (apply #'format t fmt args)
58 58 (throw :terminate nil))
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
67 81
68 82 (defmethod css-sources ((compiler compiler))
69 83 (format nil "~{~A~^~%~%~}" (css compiler)))
70 84
71 85 ;;; HTML
72 86
73 87 (defmethod html-sources ((compiler compiler))
74 88 (let ((flute:*escape-html* nil)
75 89 (body-template (body compiler))
76 90 (js (js-sources compiler))
77 91 (css (css-sources compiler)))
78 92 (with-output-to-string (out)
79 93 (write
80 94 (flute:h
81 95 (html
82 96 (head
83 97 (title "SugarQSP"))
84 98 (body
85 99 body-template
86 100 (style css)
87 101 (script js))))
88 102 :stream out
89 103 :pretty nil))))
90 104
91 105 (defmethod initialize-instance ((compiler compiler) &key source ((:js js-files)) ((:css css-files)) ((:body body-file)) compile &allow-other-keys)
92 106 (call-next-method)
93 107 (with-slots (body css js)
94 108 compiler
95 109 ;; Compile the game's JS
96 110 (push (list* 'progn (parse-file source)) js)
97 111 ;; Does the user need us to do anything else
98 112 (unless compile
99 113 ;; Read in body
100 114 (when body-file
101 115 (setf body
102 116 (alexandria:read-file-into-string body-file)))
103 117 ;; Include js files
104 118 (dolist (js-file js-files)
105 119 (push (format nil "////// Included file ~A~%~A" js-file
106 120 (alexandria:read-file-into-string js-file))
107 121 js))
108 122 ;; Include css files
109 123 (dolist (css-file css-files)
110 124 (push (format nil "////// Included file ~A~%~A" css-file
111 125 (alexandria:read-file-into-string css-file))
112 126 css)))))
113 127
114 128 (defmethod write-compiled-file ((compiler compiler))
115 129 (alexandria:write-string-into-file
116 130 (if (compile-only compiler)
117 131 ;; Just the JS
118 132 (preprocess-js (js-sources compiler) (beautify compiler))
119 133 ;; All of it
120 134 (html-sources compiler))
121 135 (target compiler) :if-exists :supersede))
@@ -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_")
@@ -1,614 +1,614 b''
1 1
2 2 (in-package sugar-qsp)
3 3
4 4 ;;;; Parses TXT source to an intermediate representation
5 5
6 6 ;;; Utility
7 7
8 8 (defun remove-nth (list nth)
9 9 (append (subseq list 0 nth)
10 10 (subseq list (1+ nth))))
11 11
12 12 (defun not-quote (char)
13 13 (not (eql #\' char)))
14 14
15 15
16 16 (defun not-doublequote (char)
17 17 (not (eql #\" char)))
18 18
19 19 (defun not-brace (char)
20 20 (not (eql #\} char)))
21 21
22 22 (defun not-integer (string)
23 23 (when (find-if-not #'digit-char-p string)
24 24 t))
25 25
26 26 (defun not-newline (char)
27 27 (not (eql #\newline char)))
28 28
29 29 (defun id-any-char (char)
30 30 (and
31 31 (not (digit-char-p char))
32 32 (not (eql #\newline char))
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)
40 40 (defun remove-nil (list)
41 41 (remove nil list)))
42 42
43 43 (defun binop-rest (list)
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)
51 51 left-op
52 52 (destructuring-bind ((operator right-op) &rest rest-ops)
53 53 other-ops
54 54 (if (and (listp left-op)
55 55 (eq (first left-op)
56 56 operator))
57 57 (do-binop% (append left-op (list right-op)) rest-ops)
58 58 (do-binop% (list operator left-op right-op) rest-ops)))))
59 59
60 60 (defun do-binop (list)
61 61 (destructuring-bind (left-op rest-ops)
62 62 list
63 63 (do-binop% left-op
64 64 (mapcar #'binop-rest rest-ops))))
65 65
66 66 (p:defrule line-continuation (and #\_ #\newline)
67 67 (:constant nil))
68 68
69 69 (p:defrule text-spaces (+ (or #\space #\tab line-continuation))
70 70 (:text t))
71 71
72 72 (p:defrule spaces (+ (or #\space #\tab line-continuation))
73 73 (:constant nil)
74 74 (:error-report nil))
75 75
76 76 (p:defrule spaces? (* (or #\space #\tab line-continuation))
77 77 (:constant nil)
78 78 (:error-report nil))
79 79
80 80 (p:defrule colon #\:
81 81 (:constant nil))
82 82
83 83 (p:defrule equal #\=
84 84 (:constant nil))
85 85
86 86 (p:defrule alphanumeric (alphanumericp character))
87 87
88 88 (p:defrule not-newline (not-newline character))
89 89
90 90 (p:defrule squote-esc "''"
91 91 (:lambda (list)
92 92 (p:text (elt list 0))))
93 93
94 94 (p:defrule dquote-esc "\"\""
95 95 (:lambda (list)
96 96 (p:text (elt list 0))))
97 97
98 98 (p:defrule sstring-chars (+ (and (and (p:! "<<") (p:! (p:~ "\"exec:")))
99 99 (or squote-esc (not-quote character))))
100 100 (:lambda (list)
101 101 (p:text (mapcar #'second list))))
102 102
103 103 (p:defrule dstring-chars (+ (and (and (p:! "<<") (p:! (p:~ "'exec:")))
104 104 (or dquote-esc (not-doublequote character))))
105 105 (:lambda (list)
106 106 (p:text (mapcar #'second list))))
107 107
108 108 ;;; Identifiers
109 109
110 110 (defparameter *keywords* '(act addlib addobj addqst and arrcomp arrpos arrsize cla clear *clear close clr *clr cls cmdclear cmdclr copyarr counter countobj curacts curloc debug delact dellib delobj desc disablescroll disablesubex dynamic dyneval else elseif end exit for freelib func getobj gosub goto gs gt if iif inclib input instr isnum isplay jump killall killobj killqst killvar lcase lcolor len let loc local maintxt max menu mid min mod msecscount msg nl *nl no nosave obj opengame openqst or p *p pl *pl play qspver rand refint replace rgb rnd savegame selact selobj set settimer showacts showinput showobjs showstat stattxt step str strcomp strfind strpos to trim ucase unsel unselect usercom user_text usrtxt val view wait xgoto xgt))
111 111
112 112 (defun trim-$ (str)
113 113 (if (char= #\$ (elt str 0))
114 114 (subseq str 1)
115 115 str))
116 116
117 117 (defun qsp-keyword-p (id)
118 118 (member (intern (trim-$ (string-upcase id))) *keywords*))
119 119
120 120 (defun not-qsp-keyword-p (id)
121 121 (not (member (intern (trim-$ (string-upcase id))) *keywords*)))
122 122
123 123 (p:defrule qsp-keyword (qsp-keyword-p identifier-raw))
124 124
125 125 (p:defrule id-first (id-any-char character))
126 126 (p:defrule id-next (or (id-any-char character)
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
134 134 ;;; Strings
135 135
136 136 (p:defrule qsp-string (or normal-string brace-string))
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
144 144 sstring-chars))
145 145 #\')
146 146 (:function second))
147 147
148 148 (p:defrule dstring (and #\" (* (or string-interpol
149 149 dstring-exec
150 150 dstring-chars))
151 151 #\")
152 152 (:function second))
153 153
154 154 (p:defrule string-interpol (and "<<" expression ">>")
155 155 (:function second))
156 156
157 157 (p:defrule sstring-exec-body (+ (or squote-esc (not-doublequote character)))
158 158 (:text t))
159 159
160 160 (p:defrule dstring-exec-body (+ (or dquote-esc (not-quote character)))
161 161 (:text t))
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
177 177 (p:defrule sugar-qsp-grammar (and (* (or spaces #\newline))
178 178 (* location))
179 179 (:function second))
180 180
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)
188 188 (and #\newline spaces? before-statement))
189 189 (:destructure (spaces1 name spaces2)
190 190 (declare (ignore spaces1 spaces2))
191 191 (string-upcase (string-trim " " (p:text name)))))
192 192
193 193 (p:defrule location-end (and #\- (* not-newline) #\newline before-statement)
194 194 (:constant nil))
195 195
196 196 ;;; Block body
197 197
198 198 (p:defrule newline-block-body (and #\newline spaces? block-body)
199 199 (:function third))
200 200
201 201 (p:defrule block-body (* statement)
202 202 (:function remove-nil))
203 203
204 204 ;; Just for <a href="exec:...'>
205 205 ;; Explicitly called from that rule's production
206 206 (p:defrule exec-body (and before-statement line-body)
207 207 (:function second))
208 208
209 209 (p:defrule line-body (and inline-statement (* next-inline-statement))
210 210 (:lambda (list)
211 211 (list* (first list) (second list))))
212 212
213 213 (p:defrule before-statement (* (or #\newline spaces))
214 214 (:constant nil))
215 215
216 216 (p:defrule statement-end (or statement-end-real statement-end-block-close))
217 217
218 218 (p:defrule statement-end-real (and (or #\newline
219 219 (and #\& spaces? (p:& statement%)))
220 220 before-statement)
221 221 (:constant nil))
222 222
223 223 (p:defrule statement-end-block-close (or (p:& #\}))
224 224 (:constant nil))
225 225
226 226 (p:defrule inline-statement (and statement% spaces?)
227 227 (:function first))
228 228
229 229 (p:defrule next-inline-statement (and #\& spaces? inline-statement)
230 230 (:function third))
231 231
232 232 (p:defrule not-a-non-statement (and (p:! (p:~ "elseif"))
233 233 (p:! (p:~ "else"))
234 234 (p:! (p:~ "end"))))
235 235
236 236 (p:defrule statement (and inline-statement statement-end)
237 237 (:function first))
238 238
239 239 (p:defrule statement% (and not-a-non-statement
240 240 (or label comment string-output
241 241 block non-returning-intrinsic local
242 242 assignment expression-output))
243 243 (:function second))
244 244
245 245 (p:defrule expr-stopper (or comment block non-returning-intrinsic))
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)
257 257 (intern (string (second list)) :keyword)))
258 258
259 259 (p:defrule comment (and #\! (* (or qsp-string brace-comment text-spaces not-newline)))
260 260 (:constant nil))
261 261
262 262 (p:defrule brace-comment (and #\{ (* (not-brace character)) #\})
263 263 (:constant nil))
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
271 271 ;;; Blocks
272 272
273 273 (p:defrule block (or block-act block-if block-for))
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))
278 ,@(rest body))))
277 `(lib:qspcond (,@head ,@(first body))
278 ,@(rest body))))
279 279
280 280 (p:defrule block-if-head (and (p:~ "if") spaces expression spaces? colon spaces?)
281 281 (:function remove-nil)
282 282 (:function cdr))
283 283
284 284 (p:defrule block-if-body (or block-if-ml block-if-sl)
285 285 (:destructure (if-body elseifs else &rest ws)
286 286 (declare (ignore ws))
287 287 `(,(remove-nil if-body) ,@elseifs ,@(when else `((else ,@else))))))
288 288
289 289 (p:defrule block-if-sl (and line-body
290 290 (p:? block-if-elseif-inline)
291 291 (p:? block-if-else-inline)
292 292 spaces?))
293 293
294 294 (p:defrule block-if-ml (and (and #\newline spaces?)
295 295 block-body
296 296 (p:? block-if-elseif)
297 297 (p:? block-if-else)
298 298 block-if-end)
299 299 (:lambda (list)
300 300 (cdr list)))
301 301
302 302 (p:defrule block-if-elseif-inline (and block-if-elseif-head line-body (p:? block-if-elseif-inline))
303 303 (:destructure (head statements elseif)
304 304 `((,@(rest head) ,@(remove-nil statements)) ,@elseif)))
305 305
306 306 (p:defrule block-if-elseif (and block-if-elseif-head statement-end block-body (p:? block-if-elseif))
307 307 (:destructure (head ws statements elseif)
308 308 (declare (ignore ws))
309 309 `((,@(rest head) ,@(remove-nil statements)) ,@elseif)))
310 310
311 311 (p:defrule block-if-elseif-head (and (p:~ "elseif") spaces expression spaces? colon spaces?)
312 312 (:function remove-nil)
313 313 (:function intern-first))
314 314
315 315 (p:defrule block-if-else-inline (and block-if-else-head line-body)
316 316 (:function second))
317 317
318 318 (p:defrule block-if-else (and block-if-else-head #\newline spaces? block-body)
319 319 (:function fourth))
320 320
321 321 (p:defrule block-if-else-head (and (p:~ "else") spaces?)
322 322 (:constant nil))
323 323
324 324 (p:defrule block-if-end (and (p:~ "end")
325 325 (p:? (and spaces (p:~ "if"))))
326 326 (:constant nil))
327 327
328 328 (p:defrule block-act (and block-act-head (or block-ml block-sl))
329 329 (:lambda (list)
330 330 (apply #'append list)))
331 331
332 332 (p:defrule block-act-head (and (p:~ "act") spaces? qsp-string spaces?
333 333 (p:? block-act-head-img)
334 334 colon spaces?)
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)
342 342 (or (third list) "")))
343 343
344 344 (p:defrule block-for (and block-for-head (or block-ml block-sl))
345 345 (:lambda (list)
346 346 (apply #'append list)))
347 347
348 348 (p:defrule block-for-head (and (p:~ "for") spaces variable spaces? equal spaces? expression
349 349 (p:~ "to") spaces expression
350 350 block-for-head-step
351 351 colon spaces?)
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)
359 359 (elt list 10))))
360 360
361 361 (p:defrule block-for-head-step (p:? (and (p:~ "step") spaces expression spaces?))
362 362 (:lambda (list)
363 363 (if list
364 364 (third list)
365 365 1)))
366 366
367 367 (p:defrule block-sl line-body)
368 368
369 369 (p:defrule block-ml (and newline-block-body block-end)
370 370 (:lambda (list)
371 371 (apply #'list* (butlast list))))
372 372
373 373 (p:defrule block-end (and (p:~ "end"))
374 374 (:constant nil))
375 375
376 376 ;;; Calls
377 377
378 378 (p:defrule first-argument (and expression spaces?)
379 379 (:function first))
380 380 (p:defrule next-argument (and "," spaces? expression)
381 381 (:function third))
382 382 (p:defrule arguments (or parenthesized-arguments plain-arguments no-arguments))
383 383 (p:defrule parenthesized-arguments (and spaces? #\( base-arguments #\))
384 384 (:function third))
385 385 (p:defrule plain-arguments (and spaces? base-arguments)
386 386 (:function second))
387 387 (p:defrule no-arguments (or (and spaces? (p:& #\newline))
388 388 (and spaces? (p:& #\&))
389 389 spaces?)
390 390 (:constant nil))
391 391 (p:defrule base-arguments (or (and first-argument (* next-argument)) spaces?)
392 392 (:lambda (list)
393 393 (if (null list)
394 394 nil
395 395 (list* (first list) (second list)))))
396 396
397 397 ;;; Intrinsics
398 398
399 399 (defmacro defintrinsics ((rule-name returning-rule-name non-returning-rule-name) &body clauses)
400 400 `(progn
401 401 ,@(loop :for clause :in clauses
402 402 :collect `(defintrinsic ,@clause))
403 403 (p:defrule ,returning-rule-name (or ,@(remove-nil
404 404 (mapcar (lambda (clause)
405 405 (when (second clause)
406 406 (alexandria:symbolicate
407 407 'intrinsic- (first clause))))
408 408 clauses))))
409 409 (p:defrule ,non-returning-rule-name (or ,@(remove-nil
410 410 (mapcar (lambda (clause)
411 411 (unless (second clause)
412 412 (alexandria:symbolicate
413 413 'intrinsic- (first clause))))
414 414 clauses))))
415 415 (p:defrule ,rule-name (or ,returning-rule-name ,non-returning-rule-name))))
416 416
417 417 (defmacro defintrinsic (sym returning &optional (min-arity 0) (max-arity 10) &rest names)
418 418 (declare (ignore returning))
419 419 (setf names
420 420 (if names
421 421 (mapcar #'string-upcase names)
422 422 (list (string sym))))
423 423 `(p:defrule ,(alexandria:symbolicate 'intrinsic- sym)
424 424 (and (p:? #\$) (or ,@(loop :for name :in names :collect `(p:~ ,name)))
425 425 arguments)
426 426 (:destructure (dollar name arguments)
427 427 (declare (ignore dollar))
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
440 440 (obj t 1 1)
441 441 (loc t 1 1)
442 442 (no t 1 1)
443 443 ;; Basic
444 444 (qspver t 0 0)
445 445 (curloc t 0 0)
446 446 (rand t 1 2)
447 447 (rnd t 0 0)
448 448 (qspmax t 1 10 "max")
449 449 (qspmin t 1 10 "min")
450 450 ;; Arrays
451 451 (killall nil 0 0)
452 452 (copyarr nil 2 4)
453 453 (arrsize t 1 1)
454 454 (arrpos t 2 3)
455 455 (arrcomp t 2 3)
456 456 ;; Strings
457 457 (len t 1 1)
458 458 (mid t 2 3)
459 459 (ucase t 1 1)
460 460 (lcase t 1 1)
461 461 (trim t 1 1)
462 462 (replace t 2 3)
463 463 (instr t 2 3)
464 464 (isnum t 1 1)
465 465 (val t 1 1)
466 466 (qspstr t 1 1 "str")
467 467 (strcomp t 2 2)
468 468 (strfind t 2 3)
469 469 (strpos t 2 3)
470 470 ;; IF
471 471 (iif t 2 3)
472 472 ;; Subs
473 473 (gosub nil 1 10 "gosub" "gs")
474 474 (func t 1 10)
475 475 (exit nil 0 0)
476 476 ;; Jump
477 477 (jump nil 1 1)
478 478 ;; Dynamic
479 479 (dynamic nil 1 10)
480 480 (dyneval t 1 10)
481 481 ;; Sound
482 482 (play nil 1 2)
483 483 (isplay t 1 1)
484 484 (close nil 1 1)
485 485 (closeall nil 0 0 "close all")
486 486 ;; Main window
487 487 (main-pl nil 1 1 "*pl")
488 488 (main-nl nil 0 1 "*nl")
489 489 (main-p nil 1 1 "*p")
490 490 (maintxt t 0 0)
491 491 (desc t 1 1)
492 492 (main-clear nil 0 0 "*clear" "*clr")
493 493 ;; Aux window
494 494 (showstat nil 1 1)
495 495 (stat-pl nil 1 1 "pl")
496 496 (stat-nl nil 0 1 "nl")
497 497 (stat-p nil 1 1 "p")
498 498 (stattxt t 0 0)
499 499 (stat-clear nil 0 0 "clear" "clr")
500 500 (cls nil 0 0)
501 501 ;; Dialog
502 502 (msg nil 1 1)
503 503 ;; Acts
504 504 (showacts nil 1 1)
505 505 (delact nil 1 1 "delact" "del act")
506 506 (curacts t 0 0)
507 507 (cla nil 0 0)
508 508 ;; Objects
509 509 (showobjs nil 1 1)
510 510 (addobj nil 1 3 "addobj" "add obj")
511 511 (delobj nil 1 1 "delobj" "del obj")
512 512 (killobj nil 0 1)
513 513 (countobj t 0 0)
514 514 (getobj t 1 1)
515 515 ;; Menu
516 516 (menu nil 1 1)
517 517 ;; Images
518 518 (refint nil 0 0)
519 519 (view nil 0 1)
520 520 ;; Fonts
521 521 (rgb t 3 3)
522 522 ;; Input
523 523 (showinput nil 1 1)
524 524 (usertxt t 0 0 "user_text" "usrtxt")
525 525 (cmdclear nil 0 0 "cmdclear" "cmdclr")
526 526 (input t 1 1)
527 527 ;; Files
528 528 (openqst nil 1 1)
529 529 (addqst nil 1 1 "addqst" "addlib" "inclib")
530 530 (killqst nil 1 1 "killqst" "dellib" "freelib")
531 531 (opengame nil 0 0)
532 532 (savegame nil 0 0)
533 533 ;; Real time
534 534 (wait nil 1 1)
535 535 (msecscount t 0 0)
536 536 (settimer nil 1 1))
537 537
538 538 ;;; Expression
539 539
540 540 (p:defrule expression or-expr)
541 541
542 542 (p:defrule or-expr (and and-expr (* (and spaces? or-op spaces? and-expr)))
543 543 (:function do-binop))
544 544
545 545 (p:defrule and-expr (and eq-expr (* (and spaces? and-op spaces? eq-expr)))
546 546 (:function do-binop))
547 547
548 548 (p:defrule eq-expr (and sum-expr (* (and spaces? (or "<=" ">=" "=<" "=>" "<>"
549 549 "=" "<" ">" "!")
550 550 spaces? sum-expr)))
551 551 (:function do-binop))
552 552
553 553 (p:defrule sum-expr (and mod-expr (* (and spaces? (or #\+ #\-) spaces? mod-expr)))
554 554 (:function do-binop))
555 555
556 556 (p:defrule mod-expr (and mul-expr (* (and spaces? (p:~ "mod") spaces? mul-expr)))
557 557 (:function do-binop))
558 558
559 559 (p:defrule mul-expr (and unary-expr (* (and spaces? (or #\* #\/) spaces? unary-expr)))
560 560 (:function do-binop))
561 561
562 562 (p:defrule unary-expr (and (p:? (or #\+ #\-)) atom-expr)
563 563 (:lambda (list)
564 564 (let ((expr (remove-nil list)))
565 565 (if (= 1 (length expr))
566 566 (first expr)
567 567 (intern-first expr)))))
568 568
569 569 (p:defrule atom-expr (and (or variable literal returning-intrinsic paren-expr) spaces?)
570 570 (:function first))
571 571
572 572 (p:defrule paren-expr (and #\( spaces? expression spaces? #\))
573 573 (:function third))
574 574
575 575 (p:defrule or-op (p:~ "or")
576 576 (:constant "or"))
577 577
578 578 (p:defrule and-op (p:~ "and")
579 579 (:constant "and"))
580 580
581 581 ;;; Variables
582 582
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))
607 607
608 608 ;;; Non-string literals
609 609
610 610 (p:defrule literal (or qsp-string brace-string number))
611 611
612 612 (p:defrule number (+ (or #\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9))
613 613 (:lambda (list)
614 614 (parse-integer (p:text list))))
@@ -1,203 +1,164 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
6 6 ;;;; Some utility macros for other .ps sources too.
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
47 16 '((defvar __labels)))
48 17 ,@(if locals
49 18 `((tagbody
50 19 ,@body))
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))
58 27 (stringp (first forms)))
59 28 (first forms))
60 29 (t
61 30 `(& ,@forms))))
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)
68 (label-block ()
69 (api-call init-args args)
70 ,@body
71 (api-call get-result)))))
36 (async-lambda (args)
37 (label-block ()
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
122 85 ,@(rest clause))))))
123 86
124 87 ;;; 8sub
125 88
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)
137 100 (setf (first funcs) (reverse (first funcs)))
138 101 (push form funcs)
139 102 (push nil funcs))
140 103 (t
141 104 (push form (first funcs)))))
142 105 (setf (first funcs) (reverse (first funcs)))
143 106 (setf funcs (reverse funcs))
144 107 (if (= 2 (length funcs))
145 108 `(progn
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 ,@body))))
133 ,@body))))
173 134
174 135 ;;; 12aux
175 136
176 137 ;;; 13diag
177 138
178 139 ;;; 14act
179 140
180 141 ;;; 15objs
181 142
182 143 ;;; 16menu
183 144
184 145 ;;; 17sound
185 146
186 147 ;;; 18img
187 148
188 149 ;;; 19input
189 150
190 151 ;;; 20time
191 152
192 153 ;;; 21local
193 154
194 155 ;;; 22for
195 156
196 (ps:defpsmacro qspfor (var from to step &body body)
197 `(api-call qspfor
198 ,(string (second var)) ,(third var) ;; name and index
199 ,from ,to ,step
200 (lambda ()
201 (block nil
202 ,@body
203 t))))
157 (defpsmacro qspfor (var from to step &body body)
158 `((intern "QSPFOR" "API")
159 ,(string (second var)) ,(third var) ;; name and index
160 ,from ,to ,step
161 (lambda ()
162 (block nil
163 ,@body
164 t))))
@@ -1,16 +1,19 b''
1 1
2 2 (defsystem sugar-qsp
3 3 :description "QSP compiler to monolithic HTML page"
4 4 :depends-on (:alexandria ;; General
5 5 :esrap ;; Parsing
6 6 :parenscript :flute ;; Codegening
7 7 )
8 8 :pathname "src/"
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")
16 19 (:file "parser")))
General Comments 0
You need to be logged in to leave comments. Login now