##// END OF EJS Templates
Bugfixes
naryl -
r41:097aa130 default
parent child Browse files
Show More
@@ -1,482 +1,488 b''
1 1
2 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 9 ;;; Utils
10 10
11 11 (defun make-act-html (title img)
12 12 (+ "<a class='qsp-act' href='" (href-call call-act title) "' onmouseover='" (inline-call select-act title) "'>"
13 13 (if img (+ "<img src='" img "'>") "")
14 14 title
15 15 "</a>"))
16 16
17 17 (defun make-menu-item-html (num title img loc)
18 18 (+ "<a href='" (href-call finish-menu loc) "'>"
19 19 (if img (+ "<img src='" img "'>") "")
20 20 title
21 21 "</a>"))
22 22
23 23 (defun make-obj (title img selected)
24 24 (+ "<li onclick='" (inline-call select-obj title img) "'>"
25 25 "<a class='qsp-obj" (if selected " qsp-obj-selected" "") "'>"
26 26 (if img (+ "<img src='" img "'>") "")
27 27 title
28 28 "</a>"))
29 29
30 30 (defun make-menu-delimiter ()
31 31 "<hr>")
32 32
33 (defun copy-obj (obj)
34 (chain *j-s-o-n (parse (chain *j-s-o-n (stringify obj)))))
35
33 36 (defun report-error (text)
34 37 (alert text))
35 38
36 39 (defun start-sleeping ()
37 40 (chain (by-id "qsp") class-list (add "disable")))
38 41
39 42 (defun finish-sleeping ()
40 43 (chain (by-id "qsp") class-list (remove "disable")))
41 44
42 45 (defun sleep (msec)
43 46 (with-sleep (resume)
44 47 (set-timeout resume msec)))
45 48
46 49 (defun init-dom ()
47 50 ;; Save/load buttons
48 51 (let ((btn (by-id "qsp-btn-save")))
49 52 (setf (@ btn onclick) savegame)
50 53 (setf (@ btn href) "#"))
51 54 (let ((btn (by-id "qsp-btn-open")))
52 55 (setf (@ btn onclick) opengame)
53 56 (setf (@ btn href) "#"))
54 57 ;; Close image on click
55 58 (setf (@ (by-id "qsp-image-container") onclick)
56 59 show-image)
57 60 ;; Enter in input field
58 61 (setf (@ (get-frame :input) onkeyup)
59 62 on-input-key)
60 63 ;; Close the dropdown on any click
61 64 (setf (@ window onclick)
62 65 (lambda (event)
63 66 (setf (@ window mouse)
64 67 (list (@ event page-x)
65 68 (@ event page-y)))
66 69 (finish-menu nil))))
67 70
68 71 (defun call-serv-loc (var-name &rest args)
69 72 (let ((loc-name (get-global var-name 0)))
70 73 (when loc-name
71 74 (let ((loc (getprop *locs loc-name)))
72 75 (when loc
73 76 (call-loc loc-name args))))))
74 77
75 78 (defun filename-game (filename)
76 79 (let ((game-name (chain filename (match "(.*/)?([^.]+)(\\.[a-zA-Z]+)?") 2))))
77 80 (getprop *games game-name))
78 81
79 82 (defun run-game (name)
80 83 (let ((game (filename-game name)))
81 84 (setf *main-game name)
82 85 ;; Replace locations with the new game's
83 86 (setf *locs game)
84 87 (funcall (getprop game
85 88 (chain *object (keys game) 0))
86 89 (list))))
87 90
88 91 ;;; Misc
89 92
90 93 (defun newline (key)
91 94 (append-id (key-to-id key) "<br>" t))
92 95
93 96 (defun clear-id (id)
94 97 (setf (inner-html (by-id id)) ""))
95 98
96 99 (defun escape-html (text)
97 100 (chain text
98 101 (replace (regex "/&/g") "&amp;")
99 102 (replace (regex "/</g") "&lt;")
100 103 (replace (regex "/>/g") "&gt;")
101 104 (replace (regex "/\"/g") "&quot;")
102 105 (replace (regex "/'/g") "&apos;")))
103 106
104 107 (defun prepare-contents (s &optional force-html)
105 108 (setf s (chain s (to-string)))
106 109 (if (or force-html (get-global "USEHTML" 0))
107 110 s
108 111 (escape-html s)))
109 112
110 113 (defun get-id (id &optional force-html)
111 114 (inner-html (by-id id)))
112 115
113 116 (defun set-id (id contents &optional force-html)
114 117 (setf (inner-html (by-id id)) (prepare-contents contents force-html)))
115 118
116 119 (defun append-id (id contents &optional force-html)
117 120 (when contents
118 121 (incf (inner-html (by-id id)) (prepare-contents contents force-html))))
119 122
120 123 (defun on-input-key (ev)
121 124 (when (= 13 (@ ev key-code))
122 125 (chain ev (prevent-default))
123 126 (call-serv-loc "$USERCOM")))
124 127
125 128 ;;; Function calls
126 129
127 130 (defun init-args (args)
128 131 (dotimes (i (length args))
129 132 (let ((arg (elt args i)))
130 133 (if (numberp arg)
131 134 (set-var args i :num arg)
132 135 (set-var args i :str arg)))))
133 136
134 137 (defun get-result ()
135 138 (or (get-global "$RESULT" 0)
136 139 (get-global "RESULT" 0)))
137 140
138 141 (defun call-loc (name args)
139 142 (setf name (chain name (to-upper-case)))
140 143 (with-frame
141 144 (with-call-args args
142 (funcall (getprop *locs name)))))
145 (funcall (getprop *locs name))))
146 (void))
143 147
144 148 (defun call-act (title)
145 (let ((*current-act title))
146 (with-frame
147 (funcall (getprop *acts title :act)))))
149 (setf *current-action title)
150 (with-frame
151 (funcall (getprop *acts title :act)))
152 (setf *current-action nil)
153 (void))
148 154
149 155 ;;; Text windows
150 156
151 157 (defun key-to-id (key)
152 158 (case key
153 159 (:all "qsp")
154 160 (:main "qsp-main")
155 161 (:stat "qsp-stat")
156 162 (:objs "qsp-objs")
157 163 (:acts "qsp-acts")
158 164 (:input "qsp-input")
159 165 (:image "qsp-image")
160 166 (:dropdown "qsp-dropdown")
161 167 (t (report-error "Internal error!"))))
162 168
163 169 (defun get-frame (key)
164 170 (by-id (key-to-id key)))
165 171
166 172 (defun add-text (key text)
167 173 (append-id (key-to-id key) text))
168 174
169 175 (defun get-text (key)
170 176 (get-id (key-to-id key)))
171 177
172 178 (defun clear-text (key)
173 179 (clear-id (key-to-id key)))
174 180
175 181 (defun enable-frame (key enable)
176 182 (let ((obj (get-frame key)))
177 183 (setf (@ obj style display) (if enable "block" "none"))
178 184 (void)))
179 185
180 186 ;;; Actions
181 187
182 188 (defun add-act (title img act)
183 189 (setf (getprop *acts title)
184 190 (create :title title :img img :act act :selected nil))
185 191 (update-acts))
186 192
187 (defun del-act (title)
193 (defun del-act (&optional title)
188 194 (delete (getprop *acts (or title *current-action)))
189 195 (update-acts))
190 196
191 197 (defun clear-act ()
192 198 (setf *acts (create))
193 199 (update-acts))
194 200
195 201 (defun update-acts ()
196 202 (clear-id "qsp-acts")
197 203 (let ((elt (by-id "qsp-acts")))
198 204 (for-in (title *acts)
199 205 (let ((obj (getprop *acts title)))
200 206 (incf (inner-html elt) (make-act-html title (getprop obj :img)))))))
201 207
202 208 (defun select-act (title)
203 209 (loop :for (k v) :of *acts
204 210 :do (setf (getprop v :selected) nil))
205 211 (setf (getprop *acts title :selected) t)
206 212 (call-serv-loc "$ONACTSEL"))
207 213
208 214 ;;; "Syntax"
209 215
210 216 (defun qspfor (name index from to step body)
211 217 (for ((i from))
212 218 ((< i to))
213 219 ((incf i step))
214 220 (set-var name index :num i)
215 221 (unless (await (funcall body))
216 222 (return-from qspfor))))
217 223
218 224 ;;; Variables
219 225
220 226 (defun new-var (slot &rest indexes)
221 227 (let ((v (list)))
222 228 (dolist (index indexes)
223 229 (setf (elt v index) (if (eq #\$ (elt slot 0)) "" 0)))
224 230 (setf (@ v :indexes) (create))
225 231 v))
226 232
227 233 (defun set-str-element (slot index value)
228 (if (in index (getprop slot :indexes))
234 (if (has index (getprop slot :indexes))
229 235 (setf (elt (getprop slot)
230 236 (getprop slot :indexes index))
231 237 value)
232 238 (progn
233 239 (chain slot (push value))
234 240 (setf (elt slot index)
235 241 (length slot)))))
236 242
237 243 (defun set-any-element (slot index value)
238 244 (if (numberp index)
239 245 (setf (elt slot index) value)
240 246 (set-str-element slot index value)))
241 247
242 248 (defun get-element (slot index)
243 249 (if (numberp index)
244 250 (elt slot index)
245 251 (elt slot (getprop slot :indexes index))))
246 252
247 253 (defun get-global (name index)
248 254 (elt (getprop *globals name) index))
249 255
250 256 (defun kill-var (store name &optional index)
251 257 (setf name (chain name (to-upper-case)))
252 258 (if (and index (not (= 0 index)))
253 259 (chain (getprop *globals name) (kill index))
254 260 (delete (getprop *globals name)))
255 261 (void))
256 262
257 263 (defun array-size (name)
258 264 (@ (var-ref name) :values length))
259 265
260 266 ;;; Locals
261 267
262 268 (defun push-local-frame ()
263 269 (chain *locals (push (create)))
264 270 (void))
265 271
266 272 (defun pop-local-frame ()
267 273 (chain *locals (pop))
268 274 (void))
269 275
270 276 (defun current-local-frame ()
271 277 (elt *locals (1- (length *locals))))
272 278
273 279 ;;; Objects
274 280
275 281 (defun select-obj (title img)
276 282 (loop :for (k v) :of *objs
277 283 :do (setf (getprop v :selected) nil))
278 284 (setf (getprop *objs title :selected) t)
279 285 (call-serv-loc "$ONOBJSEL" title img))
280 286
281 287 (defun update-objs ()
282 288 (let ((elt (by-id "qsp-objs")))
283 289 (setf (inner-html elt) "<ul>")
284 290 (loop :for (name obj) :of *objs
285 291 :do (incf (inner-html elt)
286 292 (make-obj name (@ obj :img) (@ obj :selected))))
287 293 (incf (inner-html elt) "</ul>")))
288 294
289 295 ;;; Menu
290 296
291 297 (defun open-menu (menu-data)
292 298 (let ((elt (get-frame :dropdown))
293 299 (i 0))
294 300 (loop :for item :in menu-data
295 301 :do (incf i)
296 302 :do (incf (inner-html elt)
297 303 (if (eq item :delimiter)
298 304 (make-menu-delimiter i)
299 305 (make-menu-item-html i
300 306 (@ item :text)
301 307 (@ item :icon)
302 308 (@ item :loc)))))
303 309 (let ((mouse (@ window mouse)))
304 310 (setf (@ elt style left) (+ (elt mouse 0) "px"))
305 311 (setf (@ elt style top) (+ (elt mouse 1) "px"))
306 312 ;; Make sure it's inside the viewport
307 313 (when (> (@ document body inner-width)
308 314 (+ (elt mouse 0) (@ elt inner-width)))
309 315 (incf (@ elt style left) (@ elt inner-width)))
310 316 (when (> (@ document body inner-height)
311 317 (+ (elt mouse 0) (@ elt inner-height)))
312 318 (incf (@ elt style top) (@ elt inner-height))))
313 319 (setf (@ elt style display) "block")))
314 320
315 321 (defun finish-menu (loc)
316 322 (when *menu-resume
317 323 (let ((elt (get-frame :dropdown)))
318 324 (setf (inner-html elt) "")
319 325 (setf (@ elt style display) "none")
320 326 (funcall *menu-resume)
321 327 (setf *menu-resume nil))
322 328 (when loc
323 329 (call-loc loc)))
324 330 (void))
325 331
326 332 (defun menu (menu-data)
327 333 (with-sleep (resume)
328 334 (open-menu menu-data)
329 335 (setf *menu-resume resume))
330 336 (void))
331 337
332 338 ;;; Content
333 339
334 340 (defun clean-audio ()
335 341 (loop :for k :in (chain *object (keys *playing))
336 342 :for v := (getprop *playing k)
337 343 :do (when (@ v ended)
338 344 (delete (@ *playing k)))))
339 345
340 346 (defun show-image (path)
341 347 (let ((img (get-frame :image)))
342 348 (cond (path
343 349 (setf (@ img src) path)
344 350 (setf (@ img style display) "flex"))
345 351 (t
346 352 (setf (@ img src) "")
347 353 (setf (@ img style display) "hidden")))))
348 354
349 355 (defun show-inline-images (frame-name images)
350 356 (let ((frame (get-frame frame-name))
351 357 (text ""))
352 358 (incf text "<div style='position:relative; display: inline-block'>")
353 359 (incf text (+ "<img src='" (@ images 0) "'>"))
354 360 (loop :for image :in (chain images (slice 1))
355 361 :do (incf text
356 362 (+ "<img style='position:absolute' src='" image "'>")))
357 363 (incf text "</div>")
358 364 (incf (inner-html frame) text)))
359 365
360 366 (defun rgb-string (rgb)
361 367 (let ((red (ps::>> rgb 16))
362 368 (green (logand (ps::>> rgb 8) 255))
363 369 (blue (logand rgb 255)))
364 370 (flet ((rgb-to-hex (comp)
365 371 (let ((hex (chain (*number comp) (to-string 16))))
366 372 (if (< (length hex) 2)
367 373 (+ "0" hex)
368 374 hex))))
369 375 (+ "#" (rgb-to-hex red) (rgb-to-hex green) (rgb-to-hex red)))))
370 376
371 377 ;;; Saves
372 378
373 379 (defun opengame ()
374 380 (let ((element (chain document (create-element :input))))
375 381 (chain element (set-attribute :type :file))
376 382 (chain element (set-attribute :id :qsp-opengame))
377 383 (chain element (set-attribute :tabindex -1))
378 384 (chain element (set-attribute "aria-hidden" t))
379 385 (setf (@ element style display) :block)
380 386 (setf (@ element style visibility) :hidden)
381 387 (setf (@ element style position) :fixed)
382 388 (setf (@ element onchange)
383 389 (lambda (event)
384 390 (let* ((file (@ event target files 0))
385 391 (reader (new (*file-reader))))
386 392 (setf (@ reader onload)
387 393 (lambda (ev)
388 394 (block nil
389 395 (let ((target (@ ev current-target)))
390 396 (unless (@ target result)
391 397 (return))
392 398 (base64-to-state (@ target result))
393 399 (unstash-state)))))
394 400 (chain reader (read-as-text file)))))
395 401 (chain document body (append-child element))
396 402 (chain element (click))
397 403 (chain document body (remove-child element))))
398 404
399 405 (defun savegame ()
400 406 (let ((element (chain document (create-element :a))))
401 407 (chain element (set-attribute :href (+ "data:text/plain;charset=utf-8," (state-to-base64))))
402 408 (chain element (set-attribute :download "savegame.sav"))
403 409 (setf (@ element style display) :none)
404 410 (chain document body (append-child element))
405 411 (chain element (click))
406 412 (chain document body (remove-child element))))
407 413
408 414 (defun stash-state (args)
409 415 (call-serv-loc "$ONGSAVE")
410 416 (setf *state-stash
411 417 (chain *j-s-o-n (stringify
412 418 (create :vars *globals
413 419 :objs *objs
414 420 :loc-args args
415 421 :msecs (- (chain *date (now)) *started-at)
416 422 :timer-interval *timer-interval
417 423 :main-html (inner-html
418 424 (get-frame :main))
419 425 :stat-html (inner-html
420 426 (get-frame :stat))
421 427 :next-location *current-location))))
422 428 (void))
423 429
424 430 (defun unstash-state ()
425 431 (let ((data (chain *j-s-o-n (parse *state-stash))))
426 432 (clear-act)
427 433 (setf *globals (@ data :vars))
428 434 (loop :for k :in (chain *object (keys *globals))
429 435 :do (chain *object (set-prototype-of (getprop *globals k)
430 436 (@ *var prototype))))
431 437 (setf *started-at (- (chain *date (now)) (@ data :msecs)))
432 438 (setf *objs (@ data :objs))
433 439 (setf *current-location (@ data :next-location))
434 440 (setf (inner-html (get-frame :main))
435 441 (@ data :main-html))
436 442 (setf (inner-html (get-frame :stat))
437 443 (@ data :stat-html))
438 444 (update-objs)
439 445 (set-timer (@ data :timer-interval))
440 446 (call-serv-loc "$ONGLOAD")
441 447 (call-loc *current-location (@ data :loc-args))
442 448 (void)))
443 449
444 450 (defun state-to-base64 ()
445 451 (btoa (encode-u-r-i-component *state-stash)))
446 452
447 453 (defun base64-to-state (data)
448 454 (setf *state-stash (decode-u-r-i-component (atob data))))
449 455
450 456 ;;; Timers
451 457
452 458 (defun set-timer (interval)
453 459 (setf *timer-interval interval)
454 460 (clear-interval *timer-obj)
455 461 (setf *timer-obj
456 462 (set-interval
457 463 (lambda ()
458 464 (call-serv-loc "$COUNTER"))
459 465 interval)))
460 466
461 467 ;;; Special variables
462 468
463 469 (defvar serv-vars (create))
464 470
465 471 (define-serv-var backimage (:str path)
466 472 (setf (@ (get-frame :main) style background-image) path))
467 473
468 474 (define-serv-var bcolor (:num color)
469 475 (setf (@ (get-frame :all) style background-color) (rgb-string color)))
470 476
471 477 (define-serv-var fcolor (:num color)
472 478 (setf (@ (get-frame :all) style color) (rgb-string color)))
473 479
474 480 (define-serv-var lcolor (:num color)
475 481 (setf (@ (get-frame :style) inner-text)
476 482 (+ "a { color: " (rgb-string color) ";}")))
477 483
478 484 (define-serv-var fsize (:num size)
479 485 (setf (@ (get-frame :all) style font-size) size))
480 486
481 487 (define-serv-var fname (:str font-name)
482 488 (setf (@ (get-frame :all) style font-family) (+ font-name ",serif")))
@@ -1,177 +1,173 b''
1 1
2 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 11 (defpsmacro killvar (varname &optional index)
12 12 `(api-call kill-var ,varname ,index))
13 13
14 14 (defpsmacro killall ()
15 15 `(api-call kill-all))
16 16
17 17 ;;; 3expr
18 18
19 (defpsmacro obj (name)
20 `(in ,name *objs))
21
22 (defpsmacro loc (name)
23 `(in ,name *locs))
24
25 19 (defpsmacro no (arg)
26 20 `(- -1 ,arg))
27 21
28 22 ;;; 4code
29 23
30 24 (defpsmacro qspver ()
31 25 "0.0.1")
32 26
33 27 (defpsmacro curloc ()
34 28 `*current-location)
35 29
36 30 (defpsmacro rnd ()
37 31 `(funcall rand 1 1000))
38 32
39 33 (defpsmacro qspmax (&rest args)
40 34 (if (= 1 (length args))
41 35 `(*math.max.apply nil ,@args)
42 36 `(*math.max ,@args)))
43 37
44 38 (defpsmacro qspmin (&rest args)
45 39 (if (= 1 (length args))
46 40 `(*math.min.apply nil ,@args)
47 41 `(*math.min ,@args)))
48 42
49 43 ;;; 5arrays
50 44
51 45 (defpsmacro arrsize (name)
52 46 `(api-call array-size ,name))
53 47
54 48 ;;; 6str
55 49
56 50 (defpsmacro len (s)
57 51 `(length ,s))
58 52
59 53 (defpsmacro mid (s from &optional count)
60 54 `(chain ,s (substring ,from ,count)))
61 55
62 56 (defpsmacro ucase (s)
63 57 `(chain ,s (to-upper-case)))
64 58
65 59 (defpsmacro lcase (s)
66 60 `(chain ,s (to-lower-case)))
67 61
68 62 (defpsmacro trim (s)
69 63 `(chain ,s (trim)))
70 64
71 65 (defpsmacro replace (s from to)
72 66 `(chain ,s (replace ,from ,to)))
73 67
74 68 (defpsmacro val (s)
75 69 `(parse-int ,s 10))
76 70
77 71 (defpsmacro qspstr (n)
78 72 `(chain ,n (to-string)))
79 73
80 74 ;;; 7if
81 75
82 76 ;;; 8sub
83 77
84 78 ;;; 9loops
85 79
86 80 ;; JUMP is in ps-macros.lisp (because it's a part of a huge kludge)
87 81
88 82 (defpsmacro exit ()
89 83 `(return-from nil (values)))
90 84
91 85 ;;; 10dynamic
92 86
93 87 ;;; 11main
94 88
95 89 (defpsmacro desc (s)
96 90 (declare (ignore s))
97 91 "")
98 92
99 93 ;;; 12stat
100 94
101 95 (defpsmacro showstat (enable)
102 96 `(api-call enable-frame :stat ,enable))
103 97
104 98 ;;; 13diag
105 99
106 100 (defpsmacro msg (text)
107 101 `(alert ,text))
108 102
109 103 ;;; 14act
110 104
111 105 (defpsmacro curact ()
112 106 `*current-action)
113 107
114 108 (defpsmacro showacts (enable)
115 109 `(api-call enable-frame :acts ,enable))
116 110
117 111 (defpsmacro delact (&optional name)
118 `(api-call del-act ,name))
112 (if name
113 `(api-call del-act ,name)
114 `(api-call del-act)))
119 115
120 116 (defpsmacro cla ()
121 117 `(api-call clear-act))
122 118
123 119 ;;; 15objs
124 120
125 121 (defpsmacro showobjs (enable)
126 122 `(api-call enable-frame :objs ,enable))
127 123
128 124 (defpsmacro countobj ()
129 125 `(length *objs))
130 126
131 127 (defpsmacro getobj (index)
132 128 `(or (elt *objs ,index) ""))
133 129
134 130 ;;; 16menu
135 131
136 132 ;;; 17sound
137 133
138 134 (defpsmacro isplay (filename)
139 135 `(funcall (@ playing includes) ,filename))
140 136
141 137 ;;; 18img
142 138
143 139 (defpsmacro view (&optional path)
144 140 `(api-call show-image ,path))
145 141
146 142 (defpsmacro img (&rest images)
147 143 `(api-call show-inline-images :stat (list ,@images)))
148 144
149 145 (defpsmacro *img (&rest images)
150 146 `(api-call show-inline-images :main (list ,@images)))
151 147
152 148 ;;; 19input
153 149
154 150 (defpsmacro showinput (enable)
155 151 `(api-call enable-frame :input ,enable))
156 152
157 153 ;;; 20time
158 154
159 155 (defpsmacro wait (msec)
160 156 `(await (api-call sleep ,msec)))
161 157
162 158 (defpsmacro settimer (interval)
163 159 `(api-call set-timer ,interval))
164 160
165 161 ;;; 21local
166 162
167 163 ;;; 22for
168 164
169 165 ;;; misc
170 166
171 167 (defpsmacro opengame (&optional filename)
172 168 (declare (ignore filename))
173 169 `(api-call opengame))
174 170
175 171 (defpsmacro savegame (&optional filename)
176 172 (declare (ignore filename))
177 173 `(api-call savegame))
@@ -1,314 +1,321 b''
1 1
2 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 8 ;;; 1loc
9 9
10 10 (defun goto (target args)
11 11 (api:clear-text :main)
12 12 (funcall xgoto target args)
13 13 (void))
14 14
15 15 (defun xgoto (target args)
16 16 (setf args (or args (list)))
17 17 (api:clear-act)
18 18 (setf *current-location (chain target (to-upper-case)))
19 19 (api:stash-state args)
20 20 (api:call-loc *current-location args)
21 21 (api:call-serv-loc "$ONNEWLOC")
22 22 (void))
23 23
24 24 ;;; 2var
25 25
26 26 ;;; 3expr
27 27
28 (defun obj (name)
29 (has name *objs))
30
31 (defun loc (name)
32 (has name *locs))
33
28 34 ;;; 4code
29 35
30 36 (defun rand (a &optional (b 1))
31 37 (let ((min (min a b))
32 38 (max (max a b)))
33 39 (+ min (chain *math (random (- max min))))))
34 40
35 41 ;;; 5arrays
36 42
37 43 (defun copyarr (to from start count)
38 44 (multiple-value-bind (to-name to-slot)
39 45 (api:var-real-name to)
40 46 (multiple-value-bind (from-name from-slot)
41 47 (api:var-real-name from)
42 48 (for ((i start))
43 49 ((< i (min (api:array-size from-name)
44 50 (+ start count))))
45 51 ((incf i))
46 52 (api:set-var to-name (+ start i) to-slot
47 53 (api:get-var from-name (+ start i) from-slot))))))
48 54
49 55 (defun arrpos (name value &optional (start 0))
50 56 (multiple-value-bind (real-name slot)
51 57 (api:var-real-name name)
52 58 (for ((i start)) ((< i (api:array-size name))) ((incf i))
53 59 (when (eq (api:get-var real-name i slot) value)
54 60 (return-from arrpos i))))
55 61 -1)
56 62
57 63 (defun arrcomp (name pattern &optional (start 0))
58 64 (multiple-value-bind (real-name slot)
59 65 (api:var-real-name name)
60 66 (for ((i start)) ((< i (api:array-size name))) ((incf i))
61 67 (when (funcall (getprop (api:get-var real-name i slot) 'match) pattern)
62 68 (return-from arrcomp i))))
63 69 -1)
64 70
65 71 ;;; 6str
66 72
67 73 (defun instr (s subs &optional (start 1))
68 74 (+ start (chain s (substring (- start 1)) (search subs))))
69 75
70 76 (defun isnum (s)
71 77 (if (is-na-n s)
72 78 0
73 79 -1))
74 80
75 81 (defun strcomp (s pattern)
76 82 (if (chain s (match pattern))
77 83 -1
78 84 0))
79 85
80 86 (defun strfind (s pattern group)
81 87 (let* ((re (new (*reg-exp pattern)))
82 88 (match (chain re (exec s))))
83 89 (chain match (group group))))
84 90
85 91 (defun strpos (s pattern &optional (group 0))
86 92 (let* ((re (new (*reg-exp pattern)))
87 93 (match (chain re (exec s)))
88 94 (found (chain match (group group))))
89 95 (if found
90 96 (chain s (search found))
91 97 0)))
92 98
93 99 ;;; 7if
94 100
95 101 ;; Has to be a function because it always evaluates all three of its
96 102 ;; arguments
97 103 (defun iif (cond-expr then-expr else-expr)
98 104 (if cond-expr then-expr else-expr))
99 105
100 106 ;;; 8sub
101 107
102 108 (defun gosub (target &rest args)
103 109 (api:call-loc target args)
104 110 (void))
105 111
106 112 (defun func (target &rest args)
107 113 (api:call-loc target args))
108 114
109 115 ;;; 9loops
110 116
111 117 ;;; 10dynamic
112 118
113 119 (defun dynamic (block &rest args)
114 120 (when (stringp block)
115 121 (api:report-error "DYNAMIC can't evaluate arbitrary strings.\\nUse {braces} to create blocks for DYNAMIC."))
116 122 (api:with-call-args args
117 123 (funcall block args))
118 124 (void))
119 125
120 126 (defun dyneval (block &rest args)
121 127 (when (stringp block)
122 128 (api:report-error "DYNEVAL can't evaluate arbitrary strings.\\nUse {braces} to create blocks for DYNEVAL."))
123 129 (api:with-call-args args
124 130 (funcall block args)))
125 131
126 132 ;;; 11main
127 133
128 134 (defun main-p (s)
129 135 (api:add-text :main s)
130 136 (void))
131 137
132 138 (defun main-pl (s)
133 139 (api:add-text :main s)
134 140 (api:newline :main)
135 141 (void))
136 142
137 143 (defun main-nl (s)
138 144 (api:newline :main)
139 145 (api:add-text :main s)
140 146 (void))
141 147
142 148 (defun maintxt (s)
143 149 (api:get-text :main)
144 150 (void))
145 151
146 152 (defun desc (s)
147 153 "")
148 154
149 155 (defun main-clear ()
150 156 (api:clear-text :main)
151 157 (void))
152 158
153 159 ;;; 12stat
154 160
155 161 (defun stat-p (s)
156 162 (api:add-text :stat s)
157 163 (void))
158 164
159 165 (defun stat-pl (s)
160 166 (api:add-text :stat s)
161 167 (api:newline :stat)
162 168 (void))
163 169
164 170 (defun stat-nl (s)
165 171 (api:newline :stat)
166 172 (api:add-text :stat s)
167 173 (void))
168 174
169 175 (defun stattxt (s)
170 176 (api:get-text :stat)
171 177 (void))
172 178
173 179 (defun stat-clear ()
174 180 (api:clear-text :stat)
175 181 (void))
176 182
177 183 (defun cls ()
178 184 (stat-clear)
179 185 (main-clear)
180 186 (cla)
181 187 (cmdclear)
182 188 (void))
183 189
184 190 ;;; 13diag
185 191
186 192 ;;; 14act
187 193
188 194 (defun curacts ()
189 (let ((acts *acts))
195 (let ((acts (api-call copy-obj *acts)))
190 196 (lambda ()
191 197 (setf *acts acts)
192 198 (void))))
193 199
194 200 ;;; 15objs
195 201
196 202 (defun addobj (name img)
197 203 (setf img (or img ""))
198 204 (setf (getprop *objs name)
199 205 (create :name name :img img :selected nil))
200 206 (api:update-objs)
201 207 (api-call call-serv-loc "$ONOBJADD" name img)
202 208 (void))
203 209
204 210 (defun delobj (name)
205 211 (delete (getprop *objs name))
212 (api:update-objs)
206 213 (api-call call-serv-loc "$ONOBJDEL" name)
207 214 (void))
208 215
209 216 (defun killobj (&optional (num nil))
210 217 (if (eq nil num)
211 218 (setf *objs (create))
212 219 (delobj (elt (chain *object (keys *objs)) num)))
213 220 (api:update-objs)
214 221 (void))
215 222
216 223 (defun selobj ()
217 224 (loop :for (k v) :of *objs
218 225 :do (when (@ v :selected)
219 226 (return-from selobj (@ v :name)))))
220 227
221 228 ;;; 16menu
222 229
223 230 (defun menu (menu-name)
224 231 (let ((menu-data (list)))
225 232 (loop :for item-obj :in (@ (api:get-array (api:var-real-name menu-name)) values)
226 233 :for item := (@ item-obj :str)
227 234 :do (cond ((string= item "")
228 235 (break))
229 236 ((string= item "-:-")
230 237 (chain menu-data (push :delimiter)))
231 238 (t
232 239 (let* ((tokens (chain item (split ":"))))
233 240 (when (= (length tokens) 2)
234 241 (chain tokens (push "")))
235 242 (let* ((text (chain tokens (splice 0 (- (length tokens) 2)) (join ":")))
236 243 (loc (getprop tokens (- (length tokens) 2)))
237 244 (icon (getprop tokens (- (length tokens) 1))))
238 245 (chain menu-data
239 246 (push (create :text text
240 247 :loc loc
241 248 :icon icon))))))))
242 249 (api:menu menu-data)
243 250 (void)))
244 251
245 252 ;;; 17sound
246 253
247 254 (defun play (filename &optional (volume 100))
248 255 (let ((audio (new (*audio filename))))
249 256 (setf (getprop *playing filename) audio)
250 257 (setf (@ audio volume) (* volume 0.01))
251 258 (chain audio (play))))
252 259
253 260 (defun close (filename)
254 261 (funcall (getprop *playing filename) stop)
255 262 (delete (getprop *playing filename))
256 263 (void))
257 264
258 265 (defun closeall ()
259 266 (loop :for k :in (chain *object (keys *playing))
260 267 :for v := (getprop *playing k)
261 268 :do (funcall v stop))
262 269 (setf *playing (create)))
263 270
264 271 ;;; 18img
265 272
266 273 (defun refint ()
267 274 ;; "Force interface update" Uh... what exactly do we do here?
268 275 ;(api:report-error "REFINT is not supported")
269 276 )
270 277
271 278 ;;; 19input
272 279
273 280 (defun usertxt ()
274 281 (let ((input (by-id "qsp-input")))
275 282 (@ input value)))
276 283
277 284 (defun cmdclear ()
278 285 (let ((input (by-id "qsp-input")))
279 286 (setf (@ input value) "")))
280 287
281 288 (defun input (text)
282 289 (chain window (prompt text)))
283 290
284 291 ;;; 20time
285 292
286 293 (defun msecscount ()
287 294 (- (chain *date (now)) *started-at))
288 295
289 296 ;;; 21local
290 297
291 298 ;;; 22for
292 299
293 300 ;;; misc
294 301
295 302 (defun rgb (red green blue)
296 303 (+ (<< red 16)
297 304 (<< green 8)
298 305 blue))
299 306
300 307 (defun openqst (name)
301 308 (api-call run-game name))
302 309
303 310 (defun addqst (name)
304 311 (let ((game (api-call filename-game name)))
305 312 ;; Add the game's locations
306 313 (chain *object (assign *locs
307 314 (getprop *games name)))))
308 315
309 316 (defun killqst ()
310 317 ;; Delete all locations not from the current main game
311 318 (loop :for (k v) :in *games
312 319 :do (unless (string= k *main-game)
313 320 (delete (getprop *locs k)))))
314 321
@@ -1,12 +1,8 b''
1 1
2 2 (in-package sugar-qsp.main)
3 3
4
5 (defpsmacro by-id (id)
6 `(chain document (get-element-by-id ,id)))
7
8 4 (defmacro+ps api-call (name &rest args)
9 5 `(,(intern (string-upcase name) "API") ,@args))
10 6
11 (defpsmacro in (key obj)
7 (defpsmacro has (key obj)
12 8 `(chain ,obj (has-own-property ,key)))
@@ -1,51 +1,55 b''
1 1
2 2 (in-package sugar-qsp.main)
3 3
4 4 ;;; Game session state (saved in savegames)
5 5 ;; Variables
6 6 (var *globals (create))
7 7 ;; Inventory (objects)
8 8 (var *objs (create))
9 9 (var *current-location nil)
10 10 (var *current-action nil)
11 11 ;; Game time
12 12 (var *started-at (chain *date (now)))
13 13 ;; Timers
14 14 (var *timer-interval 500)
15 15 (var *timer-obj nil)
16 16 ;; Games
17 17 (var *loaded-games (list))
18 18
19 19 ;;; Transient state
20 20 ;; ACTions
21 21 (var *acts (create))
22 22 ;; Savegame data
23 23 (var *state-stash (create))
24 24 ;; List of audio files being played
25 25 (var *playing (create))
26 26 ;; Local variables stack (starts with an empty frame)
27 27 (var *locals (list))
28 28 ;; Promise to continue running the game after menu
29 29 (var *menu-resume nil)
30 30
31 31 ;;; Game data
32 32 ;; Games (filename -> [locations])
33 33 (var *games (list))
34 34 ;; The main (non library) game. Updated by openqst
35 35 (var *main-game nil)
36 36 ;; Active locations
37 37 (var *locs (create))
38 38
39 ;; Launch the game from the first location
40 39 (setf (@ window onload)
41 40 (lambda ()
42 41 (#.(intern "INIT-DOM" "SUGAR-QSP.API"))
43 42 ;; For MSECCOUNT
44 43 (setf *started-at (chain *date (now)))
45 44 ;; For $COUNTER and SETTIMER
46 45 (#.(intern "SET-TIMER" "SUGAR-QSP.API")
47 46 *timer-interval)
48 47 ;; Start the first game
49 48 (#.(intern "RUN-GAME" "SUGAR-QSP.API")
50 49 (chain *object (keys *games) 0))
51 50 (values)))
51
52 ;;; Some very common utilities (for both api and lib)
53
54 (defun by-id (id)
55 (chain document (get-element-by-id id)))
@@ -1,108 +1,108 b''
1 1
2 2 (in-package cl-user)
3 3
4 4 (defpackage :sugar-qsp.js)
5 5
6 6 (defpackage :sugar-qsp.main
7 7 (:use :cl :ps :sugar-qsp.js)
8 8 (:export #:api-call #:by-id
9 #:in
9 #:has
10 10
11 11 #:*globals #:*objs #:*current-location #:*current-action
12 12 #:*started-at #:*timer-interval #:*timer-obj #:*loaded-games
13 13
14 14 #:*acts #:*state-stash #:*playing #:*locals
15 15
16 16 #:*games #:*main-game #:*locs #:*menu-resume
17 17 ))
18 18
19 19 (defpackage :code-walker
20 20 (:use :cl)
21 21 (:export #:deftransform
22 22 #:deftransform-stop
23 23 #:walk
24 24 #:whole
25 25 #:walk-continue))
26 26
27 27 ;;; API functions
28 28 (defpackage :sugar-qsp.api
29 29 (:use :cl :ps :sugar-qsp.main :sugar-qsp.js)
30 30 (:export #:with-frame #:with-call-args
31 31 #:stash-state
32 32
33 33 #:report-error #:sleep #:init-dom #:call-serv-loc
34 34 #:newline #:clear-id #:prepare-contents #:get-id #:set-id #:append-id
35 35 #:init-args #:get-result #:call-loc #:call-act
36 36 #:get-frame #:add-text #:get-text #:clear-text #:enable-frame
37 37 #:add-act #:del-act #:clear-act #:update-acts
38 38 #:set-str-element #:set-any-element
39 39 #:*var #:new-value #:index-num #:get #:set #:kill
40 40 #:var-real-name #:ensure-var #:var-ref #:get-var #:set-var
41 41 #:get-array #:set-array #:kill-var #:array-size
42 42 #:push-local-frame #:pop-local-frame #:current-local-frame #:new-local
43 43 #:update-objs
44 44 #:menu
45 45 #:clean-audio
46 46 #:show-image
47 47 #:opengame #:savegame
48 48 ))
49 49
50 50 ;;; QSP library functions and macros
51 51 (defpackage :sugar-qsp.lib
52 52 (:use :cl :ps :sugar-qsp.main :sugar-qsp.js)
53 53 (:local-nicknames (#:api :sugar-qsp.api)
54 54 (#:walker :code-walker))
55 55 (:export #:str #:exec #:qspblock #:qspfor #:game #:location
56 56 #:qspcond #:qspvar #:set #:local #:jump
57 57
58 58 #:killvar #:killall
59 59 #:obj #:loc #:no
60 60 #:qspver #:curloc
61 61 #:rnd #:qspmax #:qspmin
62 62 #:arrsize #:len
63 63 #:mid #:ucase #:lcase #:trim #:replace #:val #:qspstr
64 64 #:exit #:desc
65 65 #:showstat #:msg
66 66 #:showacts #:delact #:cla
67 67 #:showobjs #:countobj #:getobj
68 68 #:isplay
69 69 #:view
70 70 #:showinput
71 71 #:wait #:settimer
72 72 #:local
73 73 #:opengame #:savegame
74 74
75 75 #:goto #:xgoto
76 76 #:rand
77 77 #:copyarr #:arrpos #:arrcomp
78 78 #:instr #:isnum #:strcomp #:strfind #:strpos
79 79 #:iif
80 80 #:gosub #:func
81 81 #:dynamic #:dyneval
82 82 #:main-p #:main-pl #:main-nl #:maintxt #:desc #:main-clear
83 83 #:stat-p #:stat-pl #:stat-nl #:stattxt #:stat-clear #:cls
84 84 #:curacts
85 85 #:addobj #:delobj #:killobj
86 86 #:menu
87 87 #:play #:close #:closeall
88 88 #:refint
89 89 #:usertxt #:cmdclear #:input
90 90 #:msecscount
91 91 #:rgb
92 92 #:openqst #:addqst #:killqst
93 93 ))
94 94
95 95 (setf (ps:ps-package-prefix "SUGAR-QSP.MAIN") "qsp_")
96 96 (setf (ps:ps-package-prefix "SUGAR-QSP.API") "qsp_api_")
97 97 (setf (ps:ps-package-prefix "SUGAR-QSP.LIB") "qsp_lib_")
98 98
99 99 ;;; The compiler
100 100 (defpackage :sugar-qsp
101 101 (:use :cl)
102 102 (:local-nicknames (#:p #:esrap)
103 103 (#:lib :sugar-qsp.lib)
104 104 (#:api :sugar-qsp.api)
105 105 (#:main :sugar-qsp.main)
106 106 (#:walker :code-walker))
107 107 (:export #:parse-file #:entry-point))
108 108
@@ -1,624 +1,623 b''
1 1
2 2 (in-package sugar-qsp)
3 3
4 4 ;;;; Parses TXT source to an intermediate representation
5 5
6 6 (eval-when (:compile-toplevel :load-toplevel :execute)
7 7 (defparameter *max-args* 10))
8 8
9 9 ;;; Utility
10 10
11 11 (defun remove-nth (list nth)
12 12 (append (subseq list 0 nth)
13 13 (subseq list (1+ nth))))
14 14
15 15 (defun not-quote (char)
16 16 (not (eql #\' char)))
17 17
18
19 18 (defun not-doublequote (char)
20 19 (not (eql #\" char)))
21 20
22 21 (defun not-brace (char)
23 22 (not (eql #\} char)))
24 23
25 24 (defun not-integer (string)
26 25 (when (find-if-not #'digit-char-p string)
27 26 t))
28 27
29 28 (defun not-newline (char)
30 29 (not (eql #\newline char)))
31 30
32 31 (defun id-any-char (char)
33 32 (and
34 33 (not (digit-char-p char))
35 34 (not (eql #\newline char))
36 35 (not (find char " !:&=<>+-*/,'\"()[]{}"))))
37 36
38 37 (defun intern-first (list)
39 38 (list* (intern (string-upcase (first list)) :lib)
40 39 (rest list)))
41 40
42 41 (eval-when (:compile-toplevel :load-toplevel :execute)
43 42 (defun remove-nil (list)
44 43 (remove nil list)))
45 44
46 45 (defun binop-rest (list)
47 46 (destructuring-bind (ws1 operator ws2 operand2)
48 47 list
49 48 (declare (ignore ws1 ws2))
50 49 (list (intern (string-upcase operator) "SUGAR-QSP.LIB") operand2)))
51 50
52 51 (defun do-binop% (left-op other-ops)
53 52 (if (null other-ops)
54 53 left-op
55 54 (destructuring-bind ((operator right-op) &rest rest-ops)
56 55 other-ops
57 56 (if (and (listp left-op)
58 57 (eq (first left-op)
59 58 operator))
60 59 (do-binop% (append left-op (list right-op)) rest-ops)
61 60 (do-binop% (list operator left-op right-op) rest-ops)))))
62 61
63 62 (defun do-binop (list)
64 63 (destructuring-bind (left-op rest-ops)
65 64 list
66 65 (do-binop% left-op
67 66 (mapcar #'binop-rest rest-ops))))
68 67
69 68 (p:defrule line-continuation (and #\_ #\newline)
70 69 (:constant nil))
71 70
72 71 (p:defrule text-spaces (+ (or #\space #\tab line-continuation))
73 72 (:text t))
74 73
75 74 (p:defrule spaces (+ (or #\space #\tab line-continuation))
76 75 (:constant nil)
77 76 (:error-report nil))
78 77
79 78 (p:defrule spaces? (* (or #\space #\tab line-continuation))
80 79 (:constant nil)
81 80 (:error-report nil))
82 81
83 82 (p:defrule colon #\:
84 83 (:constant nil))
85 84
86 85 (p:defrule equal #\=
87 86 (:constant nil))
88 87
89 88 (p:defrule alphanumeric (alphanumericp character))
90 89
91 90 (p:defrule not-newline (not-newline character))
92 91
93 92 (p:defrule squote-esc "''"
94 93 (:lambda (list)
95 94 (p:text (elt list 0))))
96 95
97 96 (p:defrule dquote-esc "\"\""
98 97 (:lambda (list)
99 98 (p:text (elt list 0))))
100 99
101 100 (p:defrule sstring-chars (+ (and (and (p:! "<<") (p:! (p:~ "\"exec:")))
102 101 (or squote-esc (not-quote character))))
103 102 (:lambda (list)
104 103 (p:text (mapcar #'second list))))
105 104
106 105 (p:defrule dstring-chars (+ (and (and (p:! "<<") (p:! (p:~ "'exec:")))
107 106 (or dquote-esc (not-doublequote character))))
108 107 (:lambda (list)
109 108 (p:text (mapcar #'second list))))
110 109
111 110 ;;; Identifiers
112 111
113 112 (defparameter *keywords* '(act addlib addobj addqst and arrcomp arrpos arrsize cla clear *clear close clr *clr cls cmdclear cmdclr copyarr counter countobj curact 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 img *img 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))
114 113
115 114 (defun trim-$ (str)
116 115 (if (char= #\$ (elt str 0))
117 116 (subseq str 1)
118 117 str))
119 118
120 119 (defun qsp-keyword-p (id)
121 120 (member (intern (trim-$ (string-upcase id))) *keywords*))
122 121
123 122 (defun not-qsp-keyword-p (id)
124 123 (not (member (intern (trim-$ (string-upcase id))) *keywords*)))
125 124
126 125 (p:defrule qsp-keyword (qsp-keyword-p identifier-raw))
127 126
128 127 (p:defrule id-first (id-any-char character))
129 128 (p:defrule id-next (or (id-any-char character)
130 129 (digit-char-p character)))
131 130 (p:defrule identifier-raw (and id-first (* id-next))
132 131 (:lambda (list)
133 132 (intern (string-upcase (p:text list)) :lib)))
134 133
135 134 (p:defrule identifier (not-qsp-keyword-p identifier-raw))
136 135
137 136 ;;; Strings
138 137
139 138 (p:defrule qsp-string (or normal-string brace-string))
140 139
141 140 (p:defrule normal-string (or sstring dstring)
142 141 (:lambda (str)
143 142 (list* 'lib:str (or str (list "")))))
144 143
145 144 (p:defrule sstring (and #\' (* (or string-interpol
146 145 sstring-exec
147 146 sstring-chars))
148 147 #\')
149 148 (:function second))
150 149
151 150 (p:defrule dstring (and #\" (* (or string-interpol
152 151 dstring-exec
153 152 dstring-chars))
154 153 #\")
155 154 (:function second))
156 155
157 156 (p:defrule string-interpol (and "<<" expression ">>")
158 157 (:function second))
159 158
160 159 (p:defrule sstring-exec-body (+ (or squote-esc (not-doublequote character)))
161 160 (:text t))
162 161
163 162 (p:defrule dstring-exec-body (+ (or dquote-esc (not-quote character)))
164 163 (:text t))
165 164
166 165 (p:defrule sstring-exec (and (p:~ "\"exec:") sstring-exec-body #\")
167 166 (:lambda (list)
168 167 (list* 'lib:exec (p:parse 'exec-body (second list)))))
169 168
170 169 (p:defrule dstring-exec (and (p:~ "'exec:") dstring-exec-body #\')
171 170 (:lambda (list)
172 171 (list* 'lib:exec (p:parse 'exec-body (second list)))))
173 172
174 173 (p:defrule brace-string (and #\{ before-statement block-body #\})
175 174 (:lambda (list)
176 175 (list* 'lib:qspblock (third list))))
177 176
178 177 ;;; Location
179 178
180 179 (p:defrule sugar-qsp-grammar (and (* (or spaces #\newline))
181 180 (* location))
182 181 (:lambda (list)
183 182 `(lib:game ,@(second list))))
184 183
185 184 (p:defrule location (and location-header block-body location-end)
186 185 (:destructure (header body end)
187 186 (declare (ignore end))
188 187 `(lib:location (,header) ,@body)))
189 188
190 189 (p:defrule location-header (and #\#
191 190 (+ not-newline)
192 191 (and #\newline spaces? before-statement))
193 192 (:destructure (spaces1 name spaces2)
194 193 (declare (ignore spaces1 spaces2))
195 194 (string-upcase (string-trim " " (p:text name)))))
196 195
197 196 (p:defrule location-end (and #\- (* not-newline) #\newline before-statement)
198 197 (:constant nil))
199 198
200 199 ;;; Block body
201 200
202 201 (p:defrule newline-block-body (and #\newline spaces? block-body)
203 202 (:function third))
204 203
205 204 (p:defrule block-body (* statement)
206 205 (:function remove-nil))
207 206
208 207 ;; Just for <a href="exec:...'>
209 208 ;; Explicitly called from that rule's production
210 209 (p:defrule exec-body (and before-statement line-body)
211 210 (:function second))
212 211
213 212 (p:defrule line-body (and inline-statement (* next-inline-statement))
214 213 (:lambda (list)
215 214 (list* (first list) (second list))))
216 215
217 216 (p:defrule before-statement (* (or #\newline spaces))
218 217 (:constant nil))
219 218
220 219 (p:defrule statement-end (or statement-end-real statement-end-block-close))
221 220
222 221 (p:defrule statement-end-real (and (or #\newline
223 222 (and #\& spaces? (p:& statement%)))
224 223 before-statement)
225 224 (:constant nil))
226 225
227 226 (p:defrule statement-end-block-close (or (p:& #\}))
228 227 (:constant nil))
229 228
230 229 (p:defrule inline-statement (and statement% spaces?)
231 230 (:function first))
232 231
233 232 (p:defrule next-inline-statement (and #\& spaces? inline-statement)
234 233 (:function third))
235 234
236 235 (p:defrule not-a-non-statement (and (p:! (p:~ "elseif"))
237 236 (p:! (p:~ "else"))
238 237 (p:! (p:~ "end"))))
239 238
240 239 (p:defrule statement (and inline-statement statement-end)
241 240 (:function first))
242 241
243 242 (p:defrule statement% (and not-a-non-statement
244 243 (or label comment string-output
245 244 block non-returning-intrinsic local
246 245 assignment expression-output))
247 246 (:function second))
248 247
249 248 (p:defrule expr-stopper (or comment block non-returning-intrinsic))
250 249
251 250 (p:defrule string-output qsp-string
252 251 (:lambda (string)
253 252 (list 'lib:main-pl string)))
254 253
255 254 (p:defrule expression-output expression
256 255 (:lambda (list)
257 256 (list 'lib:main-pl list)))
258 257
259 258 (p:defrule label (and colon identifier)
260 259 (:lambda (list)
261 260 (intern (string (second list)) :keyword)))
262 261
263 262 (p:defrule comment (and #\! (* (or qsp-string brace-comment text-spaces not-newline)))
264 263 (:constant nil))
265 264
266 265 (p:defrule brace-comment (and #\{ (* (not-brace character)) #\})
267 266 (:constant nil))
268 267
269 268 (p:defrule local (and (p:~ "local") spaces variable (p:? (and spaces? #\= spaces? expression)))
270 269 (:lambda (list)
271 270 (list* 'lib:local (third list)
272 271 (when (fourth list)
273 272 (list (fourth (fourth list)))))))
274 273
275 274 ;;; Blocks
276 275
277 276 (p:defrule block (or block-act block-if block-for))
278 277
279 278 (p:defrule block-if (and block-if-head block-if-body)
280 279 (:destructure (head body)
281 280 `(lib:qspcond (,@head ,@(first body))
282 281 ,@(rest body))))
283 282
284 283 (p:defrule block-if-head (and (p:~ "if") spaces expression spaces? colon spaces?)
285 284 (:function remove-nil)
286 285 (:function cdr))
287 286
288 287 (p:defrule block-if-body (or block-if-ml block-if-sl)
289 288 (:destructure (if-body elseifs else &rest ws)
290 289 (declare (ignore ws))
291 290 `(,(remove-nil if-body) ,@elseifs ,@(when else `((else ,@else))))))
292 291
293 292 (p:defrule block-if-sl (and line-body
294 293 (p:? block-if-elseif-inline)
295 294 (p:? block-if-else-inline)
296 295 spaces?))
297 296
298 297 (p:defrule block-if-ml (and (and #\newline spaces?)
299 298 block-body
300 299 (p:? block-if-elseif)
301 300 (p:? block-if-else)
302 301 block-if-end)
303 302 (:lambda (list)
304 303 (cdr list)))
305 304
306 305 (p:defrule block-if-elseif-inline (and block-if-elseif-head line-body (p:? block-if-elseif-inline))
307 306 (:destructure (head statements elseif)
308 307 `((,@(rest head) ,@(remove-nil statements)) ,@elseif)))
309 308
310 309 (p:defrule block-if-elseif (and block-if-elseif-head statement-end block-body (p:? block-if-elseif))
311 310 (:destructure (head ws statements elseif)
312 311 (declare (ignore ws))
313 312 `((,@(rest head) ,@(remove-nil statements)) ,@elseif)))
314 313
315 314 (p:defrule block-if-elseif-head (and (p:~ "elseif") spaces expression spaces? colon spaces?)
316 315 (:function remove-nil)
317 316 (:function intern-first))
318 317
319 318 (p:defrule block-if-else-inline (and block-if-else-head line-body)
320 319 (:function second))
321 320
322 321 (p:defrule block-if-else (and block-if-else-head #\newline spaces? block-body)
323 322 (:function fourth))
324 323
325 324 (p:defrule block-if-else-head (and (p:~ "else") spaces?)
326 325 (:constant nil))
327 326
328 327 (p:defrule block-if-end (and (p:~ "end")
329 328 (p:? (and spaces (p:~ "if"))))
330 329 (:constant nil))
331 330
332 331 (p:defrule block-act (and block-act-head (or block-ml block-sl))
333 332 (:lambda (list)
334 333 (apply #'append list)))
335 334
336 335 (p:defrule block-act-head (and (p:~ "act") spaces? qsp-string spaces?
337 336 (p:? block-act-head-img)
338 337 colon spaces?)
339 338 (:lambda (list)
340 339 (intern-first (list (first list)
341 340 (third list)
342 341 (or (fifth list) '(lib:str ""))))))
343 342
344 343 (p:defrule block-act-head-img (and #\, spaces? qsp-string spaces?)
345 344 (:lambda (list)
346 345 (or (third list) "")))
347 346
348 347 (p:defrule block-for (and block-for-head (or block-ml block-sl))
349 348 (:lambda (list)
350 349 (apply #'append list)))
351 350
352 351 (p:defrule block-for-head (and (p:~ "for") spaces variable spaces? equal spaces? expression
353 352 (p:~ "to") spaces expression
354 353 block-for-head-step
355 354 colon spaces?)
356 355 (:lambda (list)
357 356 (list 'lib:qspfor
358 357 (elt list 2)
359 358 (elt list 6)
360 359 (elt list 9)
361 360 (elt list 10))))
362 361
363 362 (p:defrule block-for-head-step (p:? (and (p:~ "step") spaces expression spaces?))
364 363 (:lambda (list)
365 364 (if list
366 365 (third list)
367 366 1)))
368 367
369 368 (p:defrule block-sl line-body)
370 369
371 370 (p:defrule block-ml (and newline-block-body block-end)
372 371 (:lambda (list)
373 372 (apply #'list* (butlast list))))
374 373
375 374 (p:defrule block-end (and (p:~ "end"))
376 375 (:constant nil))
377 376
378 377 ;;; Calls
379 378
380 379 (p:defrule first-argument (and expression spaces?)
381 380 (:function first))
382 381 (p:defrule next-argument (and "," spaces? expression)
383 382 (:function third))
384 383 (p:defrule arguments (or parenthesized-arguments plain-arguments no-arguments))
385 384 (p:defrule parenthesized-arguments (and spaces? #\( base-arguments #\))
386 385 (:function third))
387 386 (p:defrule plain-arguments (and spaces? base-arguments)
388 387 (:function second))
389 388 (p:defrule no-arguments (or (and spaces? (p:& #\newline))
390 389 (and spaces? (p:& #\&))
391 390 spaces?)
392 391 (:constant nil))
393 392 (p:defrule base-arguments (or (and first-argument (* next-argument)) spaces?)
394 393 (:lambda (list)
395 394 (if (null list)
396 395 nil
397 396 (list* (first list) (second list)))))
398 397
399 398 ;;; Intrinsics
400 399
401 400 (defmacro defintrinsics ((rule-name returning-rule-name non-returning-rule-name) &body clauses)
402 401 `(progn
403 402 ,@(loop :for clause :in clauses
404 403 :collect `(defintrinsic ,@clause))
405 404 (p:defrule ,returning-rule-name (or ,@(remove-nil
406 405 (mapcar (lambda (clause)
407 406 (when (second clause)
408 407 (alexandria:symbolicate
409 408 'intrinsic- (first clause))))
410 409 clauses))))
411 410 (p:defrule ,non-returning-rule-name (or ,@(remove-nil
412 411 (mapcar (lambda (clause)
413 412 (unless (second clause)
414 413 (alexandria:symbolicate
415 414 'intrinsic- (first clause))))
416 415 clauses))))
417 416 (p:defrule ,rule-name (or ,returning-rule-name ,non-returning-rule-name))))
418 417
419 418 (defmacro defintrinsic (sym returning &optional (min-arity 0) max-arity &rest names)
420 419 (declare (ignore returning))
421 420 (unless max-arity
422 421 (setf max-arity *max-args*))
423 422 (setf names
424 423 (if names
425 424 (mapcar #'string-upcase names)
426 425 (list (string sym))))
427 426 `(p:defrule ,(alexandria:symbolicate 'intrinsic- sym)
428 427 (and (p:? #\$) (or ,@(loop :for name :in names :collect `(p:~ ,name)))
429 428 arguments)
430 429 (:destructure (dollar name arguments)
431 430 (declare (ignore dollar))
432 431 (unless (<= ,min-arity (length arguments) ,max-arity)
433 432 (error "Intrinsic ~A expects between ~A and ~A arguments but ~A were provided:~%~S"
434 433 name ,min-arity ,max-arity (length arguments) arguments))
435 434 (list* ',(intern (string sym) :lib) arguments))))
436 435
437 436 (defintrinsics (intrinsic returning-intrinsic non-returning-intrinsic)
438 437 ;; Transitions
439 438 (goto% nil 0 nil "gt" "goto")
440 439 (xgoto% nil 0 nil "xgt" "xgoto")
441 440 ;; Variables
442 441 (killvar nil 0 2)
443 442 ;; Expressions
444 443 (obj t 1 1)
445 444 (loc t 1 1)
446 445 (no t 1 1)
447 446 ;; Basic
448 447 (qspver t 0 0)
449 448 (curloc t 0 0)
450 449 (rand t 1 2)
451 450 (rnd t 0 0)
452 451 (qspmax t 1 nil "max")
453 452 (qspmin t 1 nil "min")
454 453 ;; Arrays
455 454 (killall nil 0 0)
456 455 (copyarr nil 2 4)
457 456 (arrsize t 1 1)
458 457 (arrpos t 2 3)
459 458 (arrcomp t 2 3)
460 459 ;; Strings
461 460 (len t 1 1)
462 461 (mid t 2 3)
463 462 (ucase t 1 1)
464 463 (lcase t 1 1)
465 464 (trim t 1 1)
466 465 (replace t 2 3)
467 466 (instr t 2 3)
468 467 (isnum t 1 1)
469 468 (val t 1 1)
470 469 (qspstr t 1 1 "str")
471 470 (strcomp t 2 2)
472 471 (strfind t 2 3)
473 472 (strpos t 2 3)
474 473 ;; IF
475 474 (iif t 2 3)
476 475 ;; Subs
477 476 (gosub nil 1 nil "gosub" "gs")
478 477 (func t 1 nil)
479 478 (exit nil 0 0)
480 479 ;; Jump
481 480 (jump nil 1 1)
482 481 ;; Dynamic
483 482 (dynamic nil 1 nil)
484 483 (dyneval t 1 nil)
485 484 ;; Sound
486 485 (play nil 1 2)
487 486 (isplay t 1 1)
488 487 (close nil 1 1)
489 488 (closeall nil 0 0 "close all")
490 489 ;; Main window
491 490 (main-pl nil 1 1 "*pl")
492 491 (main-nl nil 0 1 "*nl")
493 492 (main-p nil 1 1 "*p")
494 493 (maintxt t 0 0)
495 494 (desc t 1 1)
496 495 (main-clear nil 0 0 "*clear" "*clr")
497 496 ;; Aux window
498 497 (showstat nil 1 1)
499 498 (stat-pl nil 1 1 "pl")
500 499 (stat-nl nil 0 1 "nl")
501 500 (stat-p nil 1 1 "p")
502 501 (stattxt t 0 0)
503 502 (stat-clear nil 0 0 "clear" "clr")
504 503 (cls nil 0 0)
505 504 ;; Dialog
506 505 (msg nil 1 1)
507 506 ;; Acts
508 507 (showacts nil 1 1)
509 508 (delact nil 0 1 "delact" "del act")
510 509 (curact t 0 0)
511 510 (curacts t 0 0)
512 511 (cla nil 0 0)
513 512 ;; Objects
514 513 (showobjs nil 1 1)
515 514 (addobj nil 1 3 "addobj" "add obj")
516 515 (delobj nil 1 1 "delobj" "del obj")
517 516 (killobj nil 0 1)
518 517 (countobj t 0 0)
519 518 (getobj t 1 1)
520 519 ;; Menu
521 520 (menu nil 1 1)
522 521 ;; Images
523 522 (refint nil 0 0)
524 523 (view nil 0 1)
525 524 (img nil 1)
526 525 (*img nil 1)
527 526 ;; Fonts
528 527 (rgb t 3 3)
529 528 ;; Input
530 529 (showinput nil 1 1)
531 530 (usertxt t 0 0 "user_text" "usrtxt")
532 531 (cmdclear nil 0 0 "cmdclear" "cmdclr")
533 532 (input t 1 1)
534 533 ;; Files
535 534 (openqst nil 1 1)
536 535 (addqst nil 1 1 "addqst" "addlib" "inclib")
537 536 (killqst nil 1 1 "killqst" "dellib" "freelib")
538 537 (opengame nil 0 0)
539 538 (savegame nil 0 0)
540 539 ;; Real time
541 540 (wait nil 1 1)
542 541 (msecscount t 0 0)
543 542 (settimer nil 1 1))
544 543
545 544 ;;; Expression
546 545
547 546 (p:defrule expression or-expr)
548 547
549 548 (p:defrule or-expr (and and-expr (* (and spaces? or-op spaces? and-expr)))
550 549 (:function do-binop))
551 550
552 551 (p:defrule and-expr (and eq-expr (* (and spaces? and-op spaces? eq-expr)))
553 552 (:function do-binop))
554 553
555 554 (p:defrule eq-expr (and sum-expr (* (and spaces? (or "<=" ">=" "=<" "=>" "<>"
556 555 "=" "<" ">" "!")
557 556 spaces? sum-expr)))
558 557 (:function do-binop))
559 558
560 559 (p:defrule sum-expr (and mod-expr (* (and spaces? (or #\+ #\-) spaces? mod-expr)))
561 560 (:function do-binop))
562 561
563 562 (p:defrule mod-expr (and mul-expr (* (and spaces? (p:~ "mod") spaces? mul-expr)))
564 563 (:function do-binop))
565 564
566 565 (p:defrule mul-expr (and unary-expr (* (and spaces? (or #\* #\/) spaces? unary-expr)))
567 566 (:function do-binop))
568 567
569 568 (p:defrule unary-expr (and (p:? (or #\+ #\-)) atom-expr)
570 569 (:lambda (list)
571 570 (let ((expr (remove-nil list)))
572 571 (if (= 1 (length expr))
573 572 (first expr)
574 573 (intern-first expr)))))
575 574
576 575 (p:defrule atom-expr (and (or variable literal returning-intrinsic paren-expr) spaces?)
577 576 (:function first))
578 577
579 578 (p:defrule paren-expr (and #\( spaces? expression spaces? #\))
580 579 (:function third))
581 580
582 581 (p:defrule or-op (p:~ "or")
583 582 (:constant "or"))
584 583
585 584 (p:defrule and-op (p:~ "and")
586 585 (:constant "and"))
587 586
588 587 ;;; Variables
589 588
590 589 (p:defrule variable (and identifier (p:? array-index))
591 590 (:destructure (id idx-raw)
592 591 (let ((idx (case idx-raw
593 592 ((nil) 0)
594 593 (:last nil)
595 594 (t idx-raw))))
596 595 (list 'lib:qspvar id idx))))
597 596
598 597 (p:defrule array-index (and #\[ spaces? (p:? expression) spaces? #\])
599 598 (:lambda (list)
600 599 (or (third list) :last)))
601 600
602 601 (p:defrule assignment (or kw-assignment plain-assignment op-assignment)
603 602 (:destructure (qspvar eq expr)
604 603 (declare (ignore eq))
605 604 (list 'lib:set qspvar expr)))
606 605
607 606 (p:defrule kw-assignment (and (or (p:~ "let") (p:~ "set")) spaces? (or op-assignment plain-assignment))
608 607 (:function third))
609 608
610 609 (p:defrule op-assignment (and variable spaces? (or "+" "-" "*" "/") #\= spaces? expression)
611 610 (:destructure (qspvar ws1 op eq ws2 expr)
612 611 (declare (ignore ws1 ws2))
613 612 (list qspvar eq (intern-first (list op qspvar expr)))))
614 613
615 614 (p:defrule plain-assignment (and variable spaces? #\= spaces? expression)
616 615 (:function remove-nil))
617 616
618 617 ;;; Non-string literals
619 618
620 619 (p:defrule literal (or qsp-string brace-string number))
621 620
622 621 (p:defrule number (+ (or #\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9))
623 622 (:lambda (list)
624 623 (parse-integer (p:text list))))
General Comments 0
You need to be logged in to leave comments. Login now