##// END OF EJS Templates
WAIT with async
naryl -
r24:2cc68984 default
parent child Browse files
Show More
@@ -1,14 +1,16 b''
1 1
2 * Use async/await
2 * Use Parenscript's packages
3 3 * Use Parenscript's minifier
4 * WAIT and MENU with async/await
4 * Remove dots
5 * MENU with async/await
6 * Find a way to minify syntax (extra returns at least)
5 7 * Special locations
6 8 * Special variables
7 9 * CLI build for Linux
8 10 * CLI build for Windows
9 11
10 12 * Build Istreblenie
11 13 * Windows GUI (for the compiler)
12 14 * Save-load game in slots
13 15 * Resizable frames
14 16 ** modifying it to suit compiler specifics
@@ -1,393 +1,396 b''
1 1
2 2 (in-package sugar-qsp)
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 9 (setf (root api) (ps:create))
10 10
11 11 ;;; Utils
12 12
13 13 (defm (root api make-act-html) (title img)
14 14 (+ "<a class='qsp-act' href='#' onclick='SugarQSP.api.callAct(\"" title "\");'>"
15 15 title
16 16 "</a>"))
17 17
18 18 (defm (root api make-menu-item-html) (num title img loc)
19 19 (+ "<a href='#' onclick='SugarQSP.api.runMenu(" num ", \"" loc "\")();'>"
20 20 "<img src='" img "'>"
21 21 title
22 22 "</a>"))
23 23
24 24 (defm (root api report-error) (text)
25 25 (alert text))
26 26
27 (defm (root api sleep) (msec)
28 (ps:new (*promise (ps:=> resolve (set-timeout resolve msec)))))
29
27 30 (defm (root api init-dom) ()
28 31 ;; Save/load buttons
29 32 (let ((btn (document.get-element-by-id "qsp-btn-save")))
30 33 (setf (ps:@ btn onclick) this.savegame)
31 34 (setf (ps:@ btn href) "#"))
32 35 (let ((btn (document.get-element-by-id "qsp-btn-open")))
33 36 (setf (ps:@ btn onclick) this.opengame)
34 37 (setf (ps:@ btn href) "#"))
35 38 ;; Close image on click
36 39 (setf (ps:@ (document.get-element-by-id "qsp-image-container") onclick)
37 40 (this.show-image nil))
38 41 ;; Close the dropdown on any click
39 42 (setf window.onclick
40 43 (lambda (event)
41 44 (setf (ps:@ (api-call get-frame :dropdown) style display) "none"))))
42 45
43 46 (defm (root api call-serv-loc) (var-name &rest args)
44 47 (let ((loc-name (api-call get-var name 0 :str)))
45 48 (when loc-name
46 49 (let ((loc (ps:getprop (root locs) loc-name)))
47 50 (when loc
48 51 (funcall loc args))))))
49 52
50 53 ;;; Misc
51 54
52 55 (defm (root api newline) (key)
53 56 (this.append-id (this.key-to-id key) "<br>" t))
54 57
55 58 (defm (root api clear-id) (id)
56 59 (setf (ps:inner-html (document.get-element-by-id id)) ""))
57 60
58 61 (setf (root api text-escaper) (document.create-element :textarea))
59 62
60 63 (defm (root api prepare-contents) (s &optional force-html)
61 64 (if (or force-html (var "USEHTML" 0 :num))
62 65 s
63 66 (progn
64 67 (setf (ps:@ (root api text-escaper) text-content) s)
65 68 (ps:inner-html (root api text-escaper)))))
66 69
67 70 (defm (root api get-id) (id &optional force-html)
68 71 (ps:inner-html (document.get-element-by-id id)))
69 72
70 73 (defm (root api set-id) (id contents &optional force-html)
71 74 (setf (ps:inner-html (document.get-element-by-id id)) (this.prepare-contents contents force-html)))
72 75
73 76 (defm (root api append-id) (id contents &optional force-html)
74 77 (when contents
75 78 (incf (ps:inner-html (document.get-element-by-id id)) (this.prepare-contents contents force-html))))
76 79
77 80 ;;; Function calls
78 81
79 82 (defm (root api init-args) (args)
80 83 (dotimes (i (length args))
81 84 (let ((arg (elt args i)))
82 85 (if (numberp arg)
83 86 (this.set-var args i :num arg)
84 87 (this.set-var args i :str arg)))))
85 88
86 89 (defm (root api get-result) ()
87 90 (if (not (equal "" (var result 0 :str)))
88 91 (var result 0 :str)
89 92 (var result 0 :num)))
90 93
91 94 (defm (root api call-loc) (name args)
92 95 (with-frame
93 96 (funcall (ps:getprop (root locs) name) args)))
94 97
95 98 (defm (root api call-act) (title)
96 99 (with-frame
97 100 (funcall (ps:getprop (root acts) title))))
98 101
99 102 ;;; Text windows
100 103
101 104 (defm (root api key-to-id) (key)
102 105 (case key
103 106 (:main "qsp-main")
104 107 (:stat "qsp-stat")
105 108 (:objs "qsp-objs")
106 109 (:acts "qsp-acts")
107 110 (:input "qsp-input")
108 111 (:dropdown "qsp-dropdown")
109 112 (t (this.report-error "Internal error!"))))
110 113
111 114 (defm (root api get-frame) (key)
112 115 (document.get-element-by-id (this.key-to-id key)))
113 116
114 117 (defm (root api add-text) (key text)
115 118 (this.append-id (this.key-to-id key) text))
116 119
117 120 (defm (root api get-text) (key)
118 121 (this.get-id (this.key-to-id key)))
119 122
120 123 (defm (root api clear-text) (key)
121 124 (this.clear-id (this.key-to-id key)))
122 125
123 126 (defm (root api enable-frame) (key enable)
124 127 (let ((obj (this.get-frame key)))
125 128 (setf obj.style.display (if enable "block" "none"))
126 129 (values)))
127 130
128 131 ;;; Actions
129 132
130 133 (defm (root api add-act) (title img act)
131 134 (setf (ps:getprop (root acts) title)
132 135 (ps:create :img img :act act))
133 136 (this.update-acts))
134 137
135 138 (defm (root api del-act) (title)
136 139 (delete (ps:getprop (root acts) title))
137 140 (this.update-acts))
138 141
139 142 (defm (root api clear-act) ()
140 143 (setf (root acts) (ps:create))
141 144 (this.clear-id "qsp-acts"))
142 145
143 146 (defm (root api update-acts) ()
144 147 (this.clear-id "qsp-acts")
145 148 (let ((elt (document.get-element-by-id "qsp-acts")))
146 149 (ps:for-in (title (root acts))
147 150 (let ((obj (ps:getprop (root acts) title)))
148 151 (incf (ps:inner-html elt) (this.make-act-html title (ps:getprop obj :img)))))))
149 152
150 153
151 154 ;;; "Syntax"
152 155
153 156 (defm (root api qspfor) (name index from to step body)
154 157 (block nil
155 158 (ps:for ((i from))
156 159 ((< i to))
157 160 ((incf i step))
158 161 (this.set-var name index :num i)
159 162 (unless (funcall body)
160 163 (return)))))
161 164
162 165 ;;; Variable class
163 166
164 167 (defm (root api *var) (name)
165 168 ;; From strings to numbers
166 169 (setf this.indexes (ps:create))
167 170 ;; From numbers to {num: 0, str: ""} objects
168 171 (setf this.values (list))
169 172 (values))
170 173
171 174 (defm (root api *var prototype new-value) ()
172 175 (ps:create :num 0 :str ""))
173 176
174 177 (defm (root api *var prototype index-num) (index)
175 178 (let ((num-index
176 179 (if (stringp index)
177 180 (if (in index this.indexes)
178 181 (ps:getprop this.indexes index)
179 182 (let ((n (length this.values)))
180 183 (setf (ps:getprop this.indexes index) n)
181 184 n))
182 185 index)))
183 186 (unless (in num-index this.values)
184 187 (setf (elt this.values num-index) (this.new-value)))
185 188 num-index))
186 189
187 190 (defm (root api *var prototype get) (index slot)
188 191 (unless (or index (= 0 index))
189 192 (setf index (1- (length this.values))))
190 193 (ps:getprop this.values (this.index-num index) slot))
191 194
192 195 (defm (root api *var prototype set) (index slot value)
193 196 (unless (or index (= 0 index))
194 197 (setf index (length store)))
195 198 (case slot
196 199 (:num (setf value (ps:chain *number (parse-int value))))
197 200 (:str (setf value (ps:chain value (to-string)))))
198 201 (setf (ps:getprop this.values (this.index-num index) slot) value)
199 202 (values))
200 203
201 204 (defm (root api *var prototype kill) (index)
202 205 (setf (elt this.values (this.index-num index)) (this.new-value)))
203 206
204 207 ;;; Variables
205 208
206 209 (defm (root api var-real-name) (name)
207 210 (if (= (ps:@ name 0) #\$)
208 211 (values (ps:chain name (substr 1)) :str)
209 212 (values name :num)))
210 213
211 214 (defm (root api ensure-var) (name)
212 215 (let ((store (this.var-ref name)))
213 216 (unless store
214 217 (setf store (ps:new (this.-var name)))
215 218 (setf (ps:getprop (root vars) name) store))
216 219 store))
217 220
218 221 (defm (root api var-ref) (name)
219 222 (let ((local-store (this.current-local-frame)))
220 223 (cond ((and local-store (in name local-store))
221 224 (ps:getprop local-store name))
222 225 ((in name (root vars))
223 226 (ps:getprop (root vars) name))
224 227 (t nil))))
225 228
226 229 (defm (root api get-var) (name index slot)
227 230 (ps:chain (this.ensure-var name) (get index slot)))
228 231
229 232 (defm (root api set-var) (name index slot value)
230 233 (ps:chain (this.ensure-var name) (set index slot value))
231 234 (values))
232 235
233 236 (defm (root api get-array) (name)
234 237 (this.var-ref name))
235 238
236 239 (defm (root api set-array) (name value)
237 240 (let ((store (this.var-ref name)))
238 241 (setf (ps:@ store values) (ps:@ value values))
239 242 (setf (ps:@ store indexes) (ps:@ value indexes)))
240 243 (values))
241 244
242 245 (defm (root api kill-var) (name &optional index)
243 246 (if (and index (not (= 0 index)))
244 247 (ps:chain (ps:getprop (root vars) name) (kill index))
245 248 (ps:delete (ps:getprop (root vars) name)))
246 249 (values))
247 250
248 251 (defm (root api array-size) (name)
249 252 (ps:getprop (this.var-ref name) 'length))
250 253
251 254 ;;; Locals
252 255
253 256 (defm (root api push-local-frame) ()
254 257 (ps:chain (root locals) (push (ps:create)))
255 258 (values))
256 259
257 260 (defm (root api pop-local-frame) ()
258 261 (ps:chain (root locals) (pop))
259 262 (values))
260 263
261 264 (defm (root api current-local-frame) ()
262 265 (elt (root locals) (1- (length (root locals)))))
263 266
264 267 (defm (root api new-local) (name)
265 268 (let ((frame (this.current-local-frame)))
266 269 (unless (in name frame)
267 270 (setf (ps:getprop frame name) (ps:create)))
268 271 (values)))
269 272
270 273 ;;; Objects
271 274
272 275 (defm (root api update-objs) ()
273 276 (let ((elt (document.get-element-by-id "qsp-objs")))
274 277 (setf (ps:inner-html elt) "<ul>")
275 278 (loop :for obj :in (root objs)
276 279 :do (incf (ps:inner-html elt) (+ "<li>" obj)))
277 280 (incf (ps:inner-html elt) "</ul>")))
278 281
279 282 ;;; Menu
280 283
281 284 (defm (root api menu) (menu-data)
282 285 (let ((elt (document.get-element-by-id "qsp-dropdown"))
283 286 (i 0))
284 287 (setf (ps:inner-html elt) "")
285 288 (loop :for item :in menu-data
286 289 :do (incf i)
287 290 :do (incf (ps:inner-html elt) (this.make-menu-item-html i item.text item.icon item.loc)))
288 291 (setf elt.style.display "block")))
289 292
290 293 ;;; Content
291 294
292 295 (defm (root api clean-audio) ()
293 296 (loop :for k :in (*object.keys (root playing))
294 297 :for v := (ps:getprop (root playing) k)
295 298 :do (when (ps:@ v ended)
296 299 (ps:delete (ps:@ (root playing) k)))))
297 300
298 301 (defm (root api show-image) (path)
299 302 (let ((img (document.get-element-by-id "qsp-image")))
300 303 (cond (path
301 304 (setf img.src path)
302 305 (setf img.style.display "flex"))
303 306 (t
304 307 (setf img.src "")
305 308 (setf img.style.display "hidden")))))
306 309
307 310 ;;; Saves
308 311
309 312 (defm (root api opengame) ()
310 313 (let ((element (document.create-element :input)))
311 314 (element.set-attribute :type :file)
312 315 (element.set-attribute :id :qsp-opengame)
313 316 (element.set-attribute :tabindex -1)
314 317 (element.set-attribute "aria-hidden" t)
315 318 (setf element.style.display :block)
316 319 (setf element.style.visibility :hidden)
317 320 (setf element.style.position :fixed)
318 321 (setf element.onchange
319 322 (lambda (event)
320 323 (let* ((file (elt event.target.files 0))
321 324 (reader (ps:new (*file-reader))))
322 325 (setf reader.onload
323 326 (lambda (ev)
324 327 (block nil
325 328 (let ((target ev.current-target))
326 329 (unless target.result
327 330 (return))
328 331 (api-call base64-to-state target.result)
329 332 (api-call unstash-state)))))
330 333 (reader.read-as-text file))))
331 334 (document.body.append-child element)
332 335 (element.click)
333 336 (document.body.remove-child element)))
334 337
335 338 (defm (root api savegame) ()
336 339 (let ((element (document.create-element :a)))
337 340 (element.set-attribute :href (+ "data:text/plain;charset=utf-8," (api-call state-to-base64)))
338 341 (element.set-attribute :download "savegame.sav")
339 342 (setf element.style.display :none)
340 343 (document.body.append-child element)
341 344 (element.click)
342 345 (document.body.remove-child element)))
343 346
344 347 (defm (root api stash-state) (args)
345 348 (api-call call-serv-loc "ONGSAVE")
346 349 (setf (root state-stash)
347 350 (*j-s-o-n.stringify
348 351 (ps:create vars (root vars)
349 352 objs (root objs)
350 353 loc-args args
351 354 msecs (- (*date.now) (root started-at))
352 355 main-html (ps:inner-html
353 356 (document.get-element-by-id :qsp-main))
354 357 stat-html (ps:inner-html
355 358 (document.get-element-by-id :qsp-stat))
356 359 next-location (root current-location))))
357 360 (values))
358 361
359 362 (defm (root api unstash-state) ()
360 363 (let ((data (*j-s-o-n.parse (root state-stash))))
361 364 (this.clear-act)
362 365 (setf (root vars) (ps:@ data vars))
363 366 (loop :for k :in (*object.keys (root vars))
364 367 :do (*object.set-prototype-of (ps:getprop (root vars) k)
365 368 (root api *var prototype)))
366 369 (setf (root started-at) (- (*date.now) (ps:@ data msecs)))
367 370 (setf (root objs) (ps:@ data objs))
368 371 (setf (root current-location) (ps:@ data next-location))
369 372 (setf (ps:inner-html (document.get-element-by-id :qsp-main))
370 373 (ps:@ data main-html))
371 374 (setf (ps:inner-html (document.get-element-by-id :qsp-stat))
372 375 (ps:@ data stat-html))
373 376 (this.update-objs)
374 377 (api-call call-serv-loc "ONGLOAD")
375 378 (api-call call-loc (root current-location) (ps:@ data loc-args))
376 379 (values)))
377 380
378 381 (defm (root api state-to-base64) ()
379 382 (btoa (encode-u-r-i-component (root state-stash))))
380 383
381 384 (defm (root api base64-to-state) (data)
382 385 (setf (root state-stash) (decode-u-r-i-component (atob data))))
383 386
384 387 ;;; Timers
385 388
386 389 (defm (root api set-timer) (interval)
387 390 (setf (root timer-interval) interval)
388 391 (clear-interval (root timer-obj))
389 392 (setf (root timer-obj)
390 393 (set-interval
391 394 (lambda ()
392 395 (api-call call-serv-loc "COUNTER"))
393 396 interval)))
@@ -1,171 +1,174 b''
1 1
2 2 (in-package sugar-qsp)
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 11 (ps:defpsmacro killvar (varname &optional index)
12 12 `(api-call kill-var ,varname ,index))
13 13
14 14 (ps:defpsmacro killall ()
15 15 `(api-call kill-all))
16 16
17 17 ;;; 3expr
18 18
19 19 (ps:defpsmacro obj (name)
20 20 `(funcall (root objs includes) ,name))
21 21
22 22 (ps:defpsmacro loc (name)
23 23 `(funcall (root locs includes) ,name))
24 24
25 25 (ps:defpsmacro no (arg)
26 26 `(- -1 ,arg))
27 27
28 28 ;;; 4code
29 29
30 30 (ps:defpsmacro qspver ()
31 31 "0.0.1")
32 32
33 33 (ps:defpsmacro curloc ()
34 34 `(root current-location))
35 35
36 36 (ps:defpsmacro rnd ()
37 37 `(funcall (root lib rand) 1 1000))
38 38
39 39 (ps: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 44 (ps: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 51 (ps:defpsmacro arrsize (name)
52 52 `(api-call array-size ,name))
53 53
54 54 ;;; 6str
55 55
56 56 (ps:defpsmacro len (s)
57 57 `(length ,s))
58 58
59 59 (ps:defpsmacro mid (s from &optional count)
60 60 `(ps:chain ,s (substring ,from ,count)))
61 61
62 62 (ps:defpsmacro ucase (s)
63 63 `(ps:chain ,s (to-upper-case)))
64 64
65 65 (ps:defpsmacro lcase (s)
66 66 `(ps:chain ,s (to-lower-case)))
67 67
68 68 (ps:defpsmacro trim (s)
69 69 `(ps:chain ,s (trim)))
70 70
71 71 (ps:defpsmacro replace (s from to)
72 72 `(ps:chain ,s (replace ,from ,to)))
73 73
74 74 (ps:defpsmacro val (s)
75 75 `(parse-int ,s 10))
76 76
77 77 (ps:defpsmacro qspstr (n)
78 78 `(ps: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 88 (ps:defpsmacro exit ()
89 89 `(return-from nil (values)))
90 90
91 91 ;;; 10dynamic
92 92
93 93 ;;; 11main
94 94
95 95 (ps:defpsmacro desc (s)
96 96 (declare (ignore s))
97 97 "")
98 98
99 99 ;;; 12stat
100 100
101 101 (ps:defpsmacro showstat (enable)
102 102 `(api-call enable-frame :stat ,enable))
103 103
104 104 ;;; 13diag
105 105
106 106 (ps:defpsmacro msg (text)
107 107 `(alert ,text))
108 108
109 109 ;;; 14act
110 110
111 111 (ps:defpsmacro showacts (enable)
112 112 `(api-call enable-frame :acts ,enable))
113 113
114 114 (ps:defpsmacro delact (name)
115 115 `(api-call del-act ,name))
116 116
117 117 (ps:defpsmacro cla ()
118 118 `(api-call clear-act))
119 119
120 120 ;;; 15objs
121 121
122 122 (ps:defpsmacro showobjs (enable)
123 123 `(api-call enable-frame :objs ,enable))
124 124
125 125 (ps:defpsmacro countobj ()
126 126 `(length (root objs)))
127 127
128 128 (ps:defpsmacro getobj (index)
129 129 `(or (elt (root objs) ,index) ""))
130 130
131 131 ;;; 16menu
132 132
133 133 ;;; 17sound
134 134
135 135 (ps:defpsmacro isplay (filename)
136 136 `(funcall (root playing includes) ,filename))
137 137
138 138 ;;; 18img
139 139
140 140 (ps:defpsmacro view (&optional path)
141 141 `(api-call show-image ,path))
142 142
143 143 ;;; 19input
144 144
145 145 (ps:defpsmacro showinput (enable)
146 146 `(api-call enable-frame :input ,enable))
147 147
148 148 ;;; 20time
149 149
150 (ps:defpsmacro wait (msec)
151 `(await (api-call sleep ,msec)))
152
150 153 (ps:defpsmacro settimer (interval)
151 154 `(api-call set-timer ,interval))
152 155
153 156 ;;; 21local
154 157
155 158 (ps:defpsmacro local (var &optional expr)
156 159 `(progn
157 160 (api-call new-local ,(string (second var)))
158 161 ,@(when expr
159 162 `((set ,var ,expr)))))
160 163
161 164 ;;; 22for
162 165
163 166 ;;; misc
164 167
165 168 (ps:defpsmacro opengame (&optional filename)
166 169 (declare (ignore filename))
167 170 `(api-call opengame))
168 171
169 172 (ps:defpsmacro savegame (&optional filename)
170 173 (declare (ignore filename))
171 174 `(api-call savegame))
@@ -1,307 +1,301 b''
1 1
2 2 (in-package sugar-qsp)
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 8 (setf (root lib) (ps:create))
9 9
10 10 ;;; 1loc
11 11
12 12 (defm (root lib goto) (target args)
13 13 (api-call clear-text :main)
14 14 (funcall (root lib xgoto) target (or args (list)))
15 15 (values))
16 16
17 17 (defm (root lib xgoto) (target args)
18 18 (api-call clear-act)
19 19 (setf (root current-location) (ps:chain target (to-upper-case)))
20 20 (api-call stash-state args)
21 21 (funcall (ps:getprop (root locs) (root current-location))
22 22 (or args (list)))
23 23 (values))
24 24
25 25 ;;; 2var
26 26
27 27 ;;; 3expr
28 28
29 29 ;;; 4code
30 30
31 31 (defm (root lib rand) (a &optional (b 1))
32 32 (let ((min (min a b))
33 33 (max (max a b)))
34 34 (+ min (ps:chain *math (random (- max min))))))
35 35
36 36 ;;; 5arrays
37 37
38 38 (defm (root lib copyarr) (to from start count)
39 39 (multiple-value-bind (to-name to-slot)
40 40 (api-call var-real-name to)
41 41 (multiple-value-bind (from-name from-slot)
42 42 (api-call var-real-name from)
43 43 (ps:for ((i start))
44 44 ((< i (min (api-call array-size from-name)
45 45 (+ start count))))
46 46 ((incf i))
47 47 (api-call set-var to-name (+ start i) to-slot
48 48 (api-call get-var from-name (+ start i) from-slot))))))
49 49
50 50 (defm (root lib arrpos) (name value &optional (start 0))
51 51 (multiple-value-bind (real-name slot)
52 52 (api-call var-real-name name)
53 53 (ps:for ((i start)) ((< i (api-call array-size name))) ((incf i))
54 54 (when (eq (api-call get-var real-name i slot) value)
55 55 (return i))))
56 56 -1)
57 57
58 58 (defm (root lib arrcomp) (name pattern &optional (start 0))
59 59 (multiple-value-bind (real-name slot)
60 60 (api-call var-real-name name)
61 61 (ps:for ((i start)) ((< i (api-call array-size name))) ((incf i))
62 62 (when (funcall (ps:getprop (api-call get-var real-name i slot) 'match) pattern)
63 63 (return i))))
64 64 -1)
65 65
66 66 ;;; 6str
67 67
68 68 (defm (root lib instr) (s subs &optional (start 1))
69 69 (+ start (ps:chain s (substring (- start 1)) (search subs))))
70 70
71 71 (defm (root lib isnum) (s)
72 72 (if (is-na-n s)
73 73 0
74 74 -1))
75 75
76 76 (defm (root lib strcomp) (s pattern)
77 77 (if (s.match pattern)
78 78 -1
79 79 0))
80 80
81 81 (defm (root lib strfind) (s pattern group)
82 82 (let* ((re (ps:new (*reg-exp pattern)))
83 83 (match (re.exec s)))
84 84 (match.group group)))
85 85
86 86 (defm (root lib strpos) (s pattern &optional (group 0))
87 87 (let* ((re (ps:new (*reg-exp pattern)))
88 88 (match (re.exec s))
89 89 (found (match.group group)))
90 90 (if found
91 91 (s.search found)
92 92 0)))
93 93
94 94 ;;; 7if
95 95
96 96 ;; Has to be a function because it always evaluates all three of its
97 97 ;; arguments
98 98 (defm (root lib iif) (cond-expr then-expr else-expr)
99 99 (if cond-expr then-expr else-expr))
100 100
101 101 ;;; 8sub
102 102
103 103 (defm (root lib gosub) (target &rest args)
104 104 (funcall (ps:getprop (root locs) target) args)
105 105 (values))
106 106
107 107 (defm (root lib func) (target &rest args)
108 108 (funcall (ps:getprop (root locs) target) args))
109 109
110 110 ;;; 9loops
111 111
112 112 ;;; 10dynamic
113 113
114 114 (defm (root lib dynamic) (block &rest args)
115 115 (when (stringp block)
116 116 (api-call report-error "DYNAMIC can't evaluate arbitrary strings.\nUse {braces} to create blocks for DYNAMIC."))
117 117 (funcall block args)
118 118 (values))
119 119
120 120 (defm (root lib dyneval) (block &rest args)
121 121 (when (stringp block)
122 122 (api-call report-error "DYNEVAL can't evaluate arbitrary strings.\nUse {braces} to create blocks for DYNEVAL."))
123 123 (funcall block args))
124 124
125 125 ;;; 11main
126 126
127 127 (defm (root lib main-p) (s)
128 128 (api-call add-text :main s)
129 129 (values))
130 130
131 131 (defm (root lib main-pl) (s)
132 132 (api-call add-text :main s)
133 133 (api-call newline :main)
134 134 (values))
135 135
136 136 (defm (root lib main-nl) (s)
137 137 (api-call newline :main)
138 138 (api-call add-text :main s)
139 139 (values))
140 140
141 141 (defm (root lib maintxt) (s)
142 142 (api-call get-text :main)
143 143 (values))
144 144
145 145 ;; For clarity (it leaves a lib.desc() call in JS)
146 146 (defm (root lib desc) (s)
147 147 "")
148 148
149 149 (defm (root lib main-clear) ()
150 150 (api-call clear-text :main)
151 151 (values))
152 152
153 153 ;;; 12stat
154 154
155 155 (defm (root lib stat-p) (s)
156 156 (api-call add-text :stat s)
157 157 (values))
158 158
159 159 (defm (root lib stat-pl) (s)
160 160 (api-call add-text :stat s)
161 161 (api-call newline :stat)
162 162 (values))
163 163
164 164 (defm (root lib stat-nl) (s)
165 165 (api-call newline :stat)
166 166 (api-call add-text :stat s)
167 167 (values))
168 168
169 169 (defm (root lib stattxt) (s)
170 170 (api-call get-text :stat)
171 171 (values))
172 172
173 173 (defm (root lib stat-clear) ()
174 174 (api-call clear-text :stat)
175 175 (values))
176 176
177 177 (defm (root lib cls) ()
178 178 (funcall (root lib stat-clear))
179 179 (funcall (root lib main-clear))
180 180 (funcall (root lib cla))
181 181 (funcall (root lib cmdclear))
182 182 (values))
183 183
184 184 ;;; 13diag
185 185
186 186 ;;; 14act
187 187
188 188 (defm (root lib 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 196 (defm (root lib addobj) (name)
197 197 (ps:chain (root objs) (push name))
198 198 (api-call update-objs)
199 199 (values))
200 200
201 201 (defm (root lib delobj) (name)
202 202 (let ((index (ps:chain (root objs) (index-of name))))
203 203 (when (> index -1)
204 204 (funcall (root lib killobj) (1+ index))))
205 205 (values))
206 206
207 207 (defm (root lib killobj) (&optional (num nil))
208 208 (if (eq nil num)
209 209 (setf (root objs) (list))
210 210 (ps:chain (root objs) (splice (1- num) 1)))
211 211 (api-call update-objs)
212 212 (values))
213 213
214 214 ;;; 16menu
215 215
216 216 (defm (root lib menu) (menu-name)
217 217 (let ((menu-data (list)))
218 218 (loop :for item :in (api-call get-array (api-call var-real-name menu-name))
219 219 :do (cond ((string= item "")
220 220 (break))
221 221 ((string= item "-:-")
222 222 (ps:chain menu-data (push :delimiter)))
223 223 (t
224 224 (let* ((tokens (ps:chain item (split ":"))))
225 225 (when (= (length tokens) 2)
226 226 (tokens.push ""))
227 227 (let* ((text (ps:chain tokens (splice 0 (- tokens.length 2)) (join ":")))
228 228 (loc (ps:getprop tokens (- tokens.length 2)))
229 229 (icon (ps:getprop tokens (- tokens.length 1))))
230 230 (ps:chain menu-data
231 231 (push (ps:create text text
232 232 loc loc
233 233 icon icon))))))))
234 234 (api-call menu menu-data)
235 235 (values)))
236 236
237 237 ;;; 17sound
238 238
239 239 (defm (root lib play) (filename &optional (volume 100))
240 240 (let ((audio (ps:new (*audio filename))))
241 241 (setf (ps:getprop (root playing) filename) audio)
242 242 (setf (ps:@ audio volume) (* volume 0.01))
243 243 (ps:chain audio (play))))
244 244
245 245 (defm (root lib close) (filename)
246 246 (funcall (root playing filename) stop)
247 247 (ps:delete (root playing filename)))
248 248
249 249 (defm (root lib closeall) ()
250 250 (loop :for k :in (*object.keys (root playing))
251 251 :for v := (ps:getprop (root playing) k)
252 252 :do (funcall v stop))
253 253 (setf (root playing) (ps:create)))
254 254
255 255 ;;; 18img
256 256
257 257 (defm (root lib refint) ()
258 258 ;; "Force interface update" Uh... what exactly do we do here?
259 259 (api-call report-error "REFINT is not supported")
260 260 )
261 261
262 262 ;;; 19input
263 263
264 264 (defm (root lib usertxt) ()
265 265 (let ((input (document.get-element-by-id "qsp-input")))
266 266 (ps:@ input value)))
267 267
268 268 (defm (root lib cmdclear) ()
269 269 (let ((input (document.get-element-by-id "qsp-input")))
270 270 (setf (ps:@ input value) "")))
271 271
272 272 (defm (root lib input) (text)
273 273 (window.prompt text))
274 274
275 275 ;;; 20time
276 276
277 ;; I wonder if there's a better solution than busy-wait
278 (defm (root lib wait) (msec)
279 (let* ((now (ps:new (*date)))
280 (exit-time (+ (funcall now.get-time) msec)))
281 (loop :while (< (funcall now.get-time) exit-time))))
282
283 277 (defm (root lib msecscount) ()
284 278 (- (*date.now) (root started-at)))
285 279
286 280 ;;; 21local
287 281
288 282 ;;; 22for
289 283
290 284 ;;; misc
291 285
292 286 (defm (root lib rgb) (red green blue)
293 287 (flet ((rgb-to-hex (comp)
294 288 (let ((hex (ps:chain (*number comp) (to-string 16))))
295 289 (if (< (length hex) 2)
296 290 (+ "0" hex)
297 291 hex))))
298 292 (+ "#" (rgb-to-hex red) (rgb-to-hex green) (rgb-to-hex red))))
299 293
300 294 (defm (root lib openqst) ()
301 295 (api-call report-error "OPENQST is not supported."))
302 296
303 297 (defm (root lib addqst) ()
304 298 (api-call report-error "ADDQST is not supported. Bundle the library with the main game."))
305 299
306 300 (defm (root lib killqst) ()
307 301 (api-call report-error "KILLQST is not supported."))
@@ -1,203 +1,203 b''
1 1
2 2 (in-package sugar-qsp)
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 10 (ps:defpsmacro defm (path args &body body)
11 11 `(setf ,path (lambda ,args ,@body)))
12 12
13 13 (ps:defpsmacro root (&rest path)
14 14 `(ps:@ *sugar-q-s-p ,@path))
15 15
16 16 (ps:defpsmacro in (key obj)
17 17 `(ps:chain ,obj (has-own-property ,key)))
18 18
19 19 (ps:defpsmacro with-frame (&body body)
20 20 `(progn
21 21 (api-call push-local-frame)
22 22 (unwind-protect
23 23 ,@body
24 24 (api-call pop-local-frame))))
25 25
26 26 ;;; Common
27 27
28 28 (defmacro defpsintrinsic (name)
29 29 `(ps:defpsmacro ,name (&rest args)
30 30 `(funcall (root lib ,',name)
31 31 ,@args)))
32 32
33 33 (defmacro defpsintrinsics (() &rest names)
34 34 `(progn ,@(loop :for name :in names
35 35 :collect `(defpsintrinsic ,name))))
36 36
37 37 (defpsintrinsics ()
38 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 39
40 40 (ps:defpsmacro api-call (func &rest args)
41 41 `(funcall (root api ,func) ,@args))
42 42
43 43 (ps:defpsmacro label-block ((&key (locals t)) &body body)
44 44 (let ((has-labels (some #'keywordp body)))
45 45 `(block nil
46 46 ,@(when has-labels
47 47 '((defvar __labels)))
48 48 ,@(if locals
49 49 `((tagbody
50 50 ,@body))
51 51 `((tagbody
52 52 ,@body))))))
53 53
54 54 (ps:defpsmacro str (&rest forms)
55 55 (cond ((zerop (length forms))
56 56 "")
57 57 ((and (= 1 (length forms))
58 58 (stringp (first forms)))
59 59 (first forms))
60 60 (t
61 61 `(& ,@forms))))
62 62
63 63 ;;; 1loc
64 64
65 65 (ps:defpsmacro location ((name) &body body)
66 66 `(setf (root locs ,name)
67 (lambda (args)
67 (ps:async-lambda (args)
68 68 (label-block ()
69 69 (api-call init-args args)
70 70 ,@body
71 71 (api-call get-result)))))
72 72
73 73 (ps:defpsmacro goto (target &rest args)
74 74 `(progn
75 75 (funcall (root lib goto) ,target ,args)
76 76 (exit)))
77 77
78 78 (ps:defpsmacro xgoto (target &rest args)
79 79 `(progn
80 80 (funcall (root lib xgoto) ,target ,args)
81 81 (exit)))
82 82
83 83 (ps:defpsmacro desc (target)
84 84 (declare (ignore target))
85 85 (report-error "DESC is not supported"))
86 86
87 87 ;;; 2var
88 88
89 89 (ps:defpsmacro var (name index slot)
90 90 `(api-call get-var ,(string name) ,index ,slot))
91 91
92 92 (ps:defpsmacro set ((var vname vindex vslot) value)
93 93 (assert (eq var 'var))
94 94 `(api-call set-var ,(string vname) ,vindex ,vslot ,value))
95 95
96 96 ;;; 3expr
97 97
98 98 (ps:defpsmacro <> (op1 op2)
99 99 `(not (equal ,op1 ,op2)))
100 100
101 101 (ps:defpsmacro ! (op1 op2)
102 102 `(not (equal ,op1 ,op2)))
103 103
104 104 ;;; 4code
105 105
106 106 (ps:defpsmacro exec (&body body)
107 107 (format nil "javascript: ~{~A~^~%~}" (mapcar #'ps:ps* body)))
108 108
109 109 ;;; 5arrays
110 110
111 111 ;;; 6str
112 112
113 113 (ps:defpsmacro & (&rest args)
114 114 `(ps:chain "" (concat ,@args)))
115 115
116 116 ;;; 7if
117 117
118 118 (ps:defpsmacro qspcond (&rest clauses)
119 119 `(cond ,@(loop :for clause :in clauses
120 120 :collect (list (first clause)
121 121 `(tagbody
122 122 ,@(rest clause))))))
123 123
124 124 ;;; 8sub
125 125
126 126 ;;; 9loops
127 127 ;;; Yep, that's a huge kludge since Javascript doesn't support arbitrary labels
128 128
129 129 (ps:defpsmacro jump (target)
130 130 `(return-from ,(intern (string-upcase (second target)))
131 131 (funcall (ps:getprop __labels ,target))))
132 132
133 133 (ps:defpsmacro tagbody (&body body)
134 134 (let ((funcs (list nil :__nil)))
135 135 (dolist (form body)
136 136 (cond ((keywordp form)
137 137 (setf (first funcs) (reverse (first funcs)))
138 138 (push form funcs)
139 139 (push nil funcs))
140 140 (t
141 141 (push form (first funcs)))))
142 142 (setf (first funcs) (reverse (first funcs)))
143 143 (setf funcs (reverse funcs))
144 144 (if (= 2 (length funcs))
145 145 `(progn
146 146 ,@body)
147 147 `(progn
148 148 (setf ,@(loop :for f :on funcs :by #'cddr
149 149 :append `((ps:@ __labels ,(first f))
150 150 (block ,(intern (string-upcase (string (first f))))
151 151 ,@(second f)
152 152 ,@(when (third f)
153 153 `((funcall
154 154 (ps:getprop __labels ,(third f)))))))))
155 155 (jump (str "__nil"))))))
156 156
157 157 ;;; 10dynamic
158 158
159 159 (ps:defpsmacro qspblock (&body body)
160 160 `(lambda (args)
161 161 (label-block ()
162 162 (api-call init-args args)
163 163 ,@body
164 164 (api-call get-result))))
165 165
166 166 ;;; 11main
167 167
168 168 (ps:defpsmacro act (name img &body body)
169 169 `(api-call add-act ,name ,img
170 170 (lambda ()
171 171 (label-block ()
172 172 ,@body))))
173 173
174 174 ;;; 12aux
175 175
176 176 ;;; 13diag
177 177
178 178 ;;; 14act
179 179
180 180 ;;; 15objs
181 181
182 182 ;;; 16menu
183 183
184 184 ;;; 17sound
185 185
186 186 ;;; 18img
187 187
188 188 ;;; 19input
189 189
190 190 ;;; 20time
191 191
192 192 ;;; 21local
193 193
194 194 ;;; 22for
195 195
196 196 (ps:defpsmacro qspfor (var from to step &body body)
197 197 `(api-call qspfor
198 198 ,(string (second var)) ,(third var) ;; name and index
199 199 ,from ,to ,step
200 200 (lambda ()
201 201 (block nil
202 202 ,@body
203 203 t))))
General Comments 0
You need to be logged in to leave comments. Login now