##// END OF EJS Templates
Working Linux build, some CLI improvements
naryl -
r44:65a579db default
parent child Browse files
Show More
@@ -1,5 +1,7 b''
1 1 .*~
2 2 .qlot
3 3 .html
4 4 .png
5 5 tests
6 sugar-qsp
7 sugar-qsp.tar.xz
@@ -1,25 +1,39 b''
1 1
2 2 BIN = sugar-qsp
3 3
4 4 LISP = sbcl
5 5
6 all: diagrams.png $(BIN)
6 all: $(BIN)
7
8 graphs: diagrams.png
7 9
8 10 $(BIN): src/*.lisp src/*.ps
9 11 buildapp.$(LISP) --asdf-path .\
10 12 --asdf-tree .qlot/dists\
11 13 --load-system sugar-qsp\
12 14 --entry sugar-qsp:entry-point\
15 --compress-core\
13 16 --output $(BIN)
14 17
15 18 install-deps:
16 19 sbcl --load install-deps.lisp
17 20
18 21 update-deps:
19 22 sbcl --load update-deps.lisp
20 23
21 diagrams.png: diagrams.dot
24 %.png: %.dot
22 25 dot $< -T png -o $@
23 26
27 dist: $(BIN)
28 tar cfvJ sugar-qsp.tar.xz $(BIN) extras
29
30 distclean: clean clean-deps
31
24 32 clean:
25 rm sugar-qsp
33 -rm sugar-qsp
34
35 clean-deps:
36 -rm qlfile.lock
37 -rm -rf .qlot
38
39 .PHONY: all graphs install-deps update-deps clean
@@ -1,14 +1,15 b''
1 1
2 2 * Save-load game in slots
3 * CLI build for Linux
3
4 4 * CLI build for Windows
5 5
6 6 * Reporting error lines in the parser
7 7 * Report duplicate label (in the parser)
8 8 * reporting error lines at runtime (by storing them in every form in the parser
9 9 * Report JUMP with missing label (in tagbody)
10 10
11 11 * Build Istreblenie
12 12 * Build ЦвСтохимия
13
13 14 * Windows GUI (for the compiler)
14 15 * Resizable frames
@@ -1,15 +1,10 b''
1 1 ql alexandria
2 2 ql esrap
3 3 ql parenscript
4 4 ql flute
5 5
6 6 ql cl-ppcre
7 7 ql anaphora
8 8 ql named-readtables
9 ql cl-unicode
10 ql flexi-streams
11 ql trivial-gray-streams
12 ql parse-number
13 ql iterate
14 9 ql assoc-utils
15 10 ql let-over-lambda
@@ -1,68 +1,40 b''
1 1 ("quicklisp" .
2 2 (:class qlot/source/dist:source-dist
3 3 :initargs (:distribution "http://beta.quicklisp.org/dist/quicklisp.txt" :%version :latest)
4 :version "2020-02-18"))
4 :version "2020-04-27"))
5 5 ("alexandria" .
6 6 (:class qlot/source/ql:source-ql
7 7 :initargs (:%version :latest)
8 :version "ql-2020-02-18"))
8 :version "ql-2020-04-27"))
9 9 ("esrap" .
10 10 (:class qlot/source/ql:source-ql
11 11 :initargs (:%version :latest)
12 :version "ql-2020-02-18"))
12 :version "ql-2020-04-27"))
13 13 ("parenscript" .
14 14 (:class qlot/source/ql:source-ql
15 15 :initargs (:%version :latest)
16 :version "ql-2020-02-18"))
17 ("cl-uglify-js" .
18 (:class qlot/source/ql:source-ql
19 :initargs (:%version :latest)
20 :version "ql-2020-02-18"))
16 :version "ql-2020-04-27"))
21 17 ("flute" .
22 18 (:class qlot/source/ql:source-ql
23 19 :initargs (:%version :latest)
24 :version "ql-2020-02-18"))
20 :version "ql-2020-04-27"))
25 21 ("cl-ppcre" .
26 22 (:class qlot/source/ql:source-ql
27 23 :initargs (:%version :latest)
28 :version "ql-2020-02-18"))
24 :version "ql-2020-04-27"))
29 25 ("anaphora" .
30 26 (:class qlot/source/ql:source-ql
31 27 :initargs (:%version :latest)
32 :version "ql-2020-02-18"))
28 :version "ql-2020-04-27"))
33 29 ("named-readtables" .
34 30 (:class qlot/source/ql:source-ql
35 31 :initargs (:%version :latest)
36 :version "ql-2020-02-18"))
37 ("parse-js" .
38 (:class qlot/source/ql:source-ql
39 :initargs (:%version :latest)
40 :version "ql-2020-02-18"))
41 ("cl-unicode" .
42 (:class qlot/source/ql:source-ql
43 :initargs (:%version :latest)
44 :version "ql-2020-02-18"))
45 ("flexi-streams" .
46 (:class qlot/source/ql:source-ql
47 :initargs (:%version :latest)
48 :version "ql-2020-02-18"))
49 ("trivial-gray-streams" .
50 (:class qlot/source/ql:source-ql
51 :initargs (:%version :latest)
52 :version "ql-2020-02-18"))
53 ("parse-number" .
54 (:class qlot/source/ql:source-ql
55 :initargs (:%version :latest)
56 :version "ql-2020-02-18"))
57 ("iterate" .
58 (:class qlot/source/ql:source-ql
59 :initargs (:%version :latest)
60 :version "ql-2020-02-18"))
32 :version "ql-2020-04-27"))
61 33 ("assoc-utils" .
62 34 (:class qlot/source/ql:source-ql
63 35 :initargs (:%version :latest)
64 :version "ql-2020-02-18"))
36 :version "ql-2020-04-27"))
65 37 ("let-over-lambda" .
66 38 (:class qlot/source/ql:source-ql
67 39 :initargs (:%version :latest)
68 :version "ql-2020-02-18"))
40 :version "ql-2020-04-27"))
@@ -1,500 +1,533 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 33 (defun copy-obj (obj)
34 34 (chain *j-s-o-n (parse (chain *j-s-o-n (stringify obj)))))
35 35
36 36 (defun report-error (text)
37 37 (alert text))
38 38
39 39 (defun start-sleeping ()
40 40 (chain (by-id "qsp") class-list (add "disable")))
41 41
42 42 (defun finish-sleeping ()
43 43 (chain (by-id "qsp") class-list (remove "disable")))
44 44
45 45 (defun sleep (msec)
46 46 (with-sleep (resume)
47 47 (set-timeout resume msec)))
48 48
49 49 (defun init-dom ()
50 50 ;; Save/load buttons
51 51 (let ((btn (by-id "qsp-btn-save")))
52 52 (setf (@ btn onclick) savegame)
53 53 (setf (@ btn href) "#"))
54 54 (let ((btn (by-id "qsp-btn-open")))
55 55 (setf (@ btn onclick) opengame)
56 56 (setf (@ btn href) "#"))
57 57 ;; Close image on click
58 58 (setf (@ (by-id "qsp-image-container") onclick)
59 59 show-image)
60 60 ;; Enter in input field
61 61 (setf (@ (get-frame :input) onkeyup)
62 62 on-input-key)
63 63 ;; Close the dropdown on any click
64 64 (setf (@ window onclick)
65 65 (lambda (event)
66 66 (setf (@ window mouse)
67 67 (list (@ event page-x)
68 68 (@ event page-y)))
69 69 (finish-menu nil))))
70 70
71 71 (defun call-serv-loc (var-name &rest args)
72 72 (let ((loc-name (get-global var-name 0)))
73 73 (when loc-name
74 74 (let ((loc (getprop *locs loc-name)))
75 75 (when loc
76 76 (call-loc loc-name args))))))
77 77
78 78 (defun filename-game (filename)
79 79 (let ((game-name (chain filename (match "(.*/)?([^.]+)(\\.[a-zA-Z]+)?") 2))))
80 80 (getprop *games game-name))
81 81
82 82 (defun run-game (name)
83 83 (let ((game (filename-game name)))
84 84 (setf *main-game name)
85 85 ;; Replace locations with the new game's
86 86 (setf *locs game)
87 87 (funcall (getprop game
88 88 (chain *object (keys game) 0))
89 89 (list))))
90 90
91 91 ;;; Misc
92 92
93 93 (defun newline (key)
94 94 (append-id (key-to-id key) "<br>" t))
95 95
96 96 (defun clear-id (id)
97 97 (setf (inner-html (by-id id)) ""))
98 98
99 99 (defun escape-html (text)
100 100 (chain text
101 101 (replace (regex "/&/g") "&amp;")
102 102 (replace (regex "/</g") "&lt;")
103 103 (replace (regex "/>/g") "&gt;")
104 104 (replace (regex "/\"/g") "&quot;")
105 105 (replace (regex "/'/g") "&apos;")))
106 106
107 107 (defun prepare-contents (s &optional force-html)
108 108 (setf s (chain s (to-string)))
109 109 (if (or force-html (get-global "USEHTML" 0))
110 110 s
111 111 (escape-html s)))
112 112
113 113 (defun get-id (id &optional force-html)
114 114 (inner-html (by-id id)))
115 115
116 116 (defun set-id (id contents &optional force-html)
117 117 (setf (inner-html (by-id id)) (prepare-contents contents force-html)))
118 118
119 119 (defun append-id (id contents &optional force-html)
120 120 (when contents
121 121 (incf (inner-html (by-id id)) (prepare-contents contents force-html))))
122 122
123 123 (defun on-input-key (ev)
124 124 (when (= 13 (@ ev key-code))
125 125 (chain ev (prevent-default))
126 126 (call-serv-loc "$USERCOM")))
127 127
128 128 ;;; Function calls
129 129
130 130 (defun init-args (args)
131 131 (dotimes (i (length args))
132 132 (let ((arg (elt args i)))
133 133 (if (numberp arg)
134 134 (set-var args i :num arg)
135 135 (set-var args i :str arg)))))
136 136
137 137 (defun get-result ()
138 138 (or (get-global "$RESULT" 0)
139 139 (get-global "RESULT" 0)))
140 140
141 141 (defun call-loc (name args)
142 142 (setf name (chain name (to-upper-case)))
143 143 (with-frame
144 144 (with-call-args args
145 145 (funcall (getprop *locs name))))
146 146 (void))
147 147
148 148 (defun call-act (title)
149 149 (setf *current-action title)
150 150 (with-frame
151 151 (funcall (getprop *acts title :act)))
152 152 (setf *current-action nil)
153 153 (void))
154 154
155 155 ;;; Text windows
156 156
157 157 (defun key-to-id (key)
158 158 (case key
159 159 (:all "qsp")
160 160 (:main "qsp-main")
161 161 (:stat "qsp-stat")
162 162 (:objs "qsp-objs")
163 163 (:acts "qsp-acts")
164 164 (:input "qsp-input")
165 165 (:image "qsp-image")
166 166 (:dropdown "qsp-dropdown")
167 167 (t (report-error "Internal error!"))))
168 168
169 169 (defun get-frame (key)
170 170 (by-id (key-to-id key)))
171 171
172 172 (defun add-text (key text)
173 173 (append-id (key-to-id key) text))
174 174
175 175 (defun get-text (key)
176 176 (get-id (key-to-id key)))
177 177
178 178 (defun clear-text (key)
179 179 (clear-id (key-to-id key)))
180 180
181 181 (defun enable-frame (key enable)
182 182 (let ((obj (get-frame key)))
183 183 (setf (@ obj style display) (if enable "block" "none"))
184 184 (void)))
185 185
186 186 ;;; Actions
187 187
188 188 (defun add-act (title img act)
189 189 (setf (getprop *acts title)
190 190 (create :title title :img img :act act :selected nil))
191 191 (update-acts))
192 192
193 193 (defun del-act (&optional title)
194 194 (delete (getprop *acts (or title *current-action)))
195 195 (update-acts))
196 196
197 197 (defun clear-act ()
198 198 (setf *acts (create))
199 199 (update-acts))
200 200
201 201 (defun update-acts ()
202 202 (clear-id "qsp-acts")
203 203 (let ((elt (by-id "qsp-acts")))
204 204 (for-in (title *acts)
205 205 (let ((obj (getprop *acts title)))
206 206 (incf (inner-html elt) (make-act-html title (getprop obj :img)))))))
207 207
208 208 (defun select-act (title)
209 209 (loop :for (k v) :of *acts
210 210 :do (setf (getprop v :selected) nil))
211 211 (setf (getprop *acts title :selected) t)
212 212 (call-serv-loc "$ONACTSEL"))
213 213
214 214 ;;; "Syntax"
215 215
216 216 (defun qspfor (name index from to step body)
217 217 (for ((i from))
218 218 ((< i to))
219 219 ((incf i step))
220 220 (set-var name index :num i)
221 221 (unless (await (funcall body))
222 222 (return-from qspfor))))
223 223
224 224 ;;; Variables
225 225
226 226 (defun new-var (slot &rest indexes)
227 227 (let ((v (list)))
228 228 (dolist (index indexes)
229 229 (setf (elt v index) (if (eq #\$ (elt slot 0)) "" 0)))
230 230 (setf (@ v :indexes) (create))
231 231 v))
232 232
233 233 (defun set-str-element (slot index value)
234 234 (if (has index (getprop slot :indexes))
235 235 (setf (elt (getprop slot)
236 236 (getprop slot :indexes index))
237 237 value)
238 238 (progn
239 239 (chain slot (push value))
240 240 (setf (elt slot index)
241 241 (length slot))))
242 242 (void))
243 243
244 244 (defun set-any-element (slot index value)
245 245 (cond ((null index)
246 246 (chain (elt slot) (push value)))
247 247 ((numberp index)
248 248 (setf (elt slot index) value))
249 249 ((stringp index)
250 250 (set-str-element slot index value))
251 251 (t (report-error "INTERNAL ERROR")))
252 252 (void))
253 253
254 254 (defun set-serv-var (name index value)
255 255 (let ((slot (getprop *globals name)))
256 256 (set-any-element slot index value))
257 257 (funcall (getprop serv-vars name :body) value index)
258 258 (void))
259 259
260 260 (defun get-element (slot index)
261 261 (if (numberp index)
262 262 (elt slot index)
263 263 (elt slot (getprop slot :indexes index))))
264 264
265 265 (defun get-global (name index)
266 266 (elt (getprop *globals name) index))
267 267
268 268 (defun kill-var (store name &optional index)
269 269 (setf name (chain name (to-upper-case)))
270 270 (if (and index (not (= 0 index)))
271 271 (chain (getprop *globals name) (kill index))
272 272 (delete (getprop *globals name)))
273 273 (void))
274 274
275 275 (defun array-size (name)
276 276 (@ (var-ref name) :values length))
277 277
278 278 ;;; Locals
279 279
280 280 (defun push-local-frame ()
281 281 (chain *locals (push (create)))
282 282 (void))
283 283
284 284 (defun pop-local-frame ()
285 285 (chain *locals (pop))
286 286 (void))
287 287
288 288 (defun current-local-frame ()
289 289 (elt *locals (1- (length *locals))))
290 290
291 291 ;;; Objects
292 292
293 293 (defun select-obj (title img)
294 294 (loop :for (k v) :of *objs
295 295 :do (setf (getprop v :selected) nil))
296 296 (setf (getprop *objs title :selected) t)
297 297 (call-serv-loc "$ONOBJSEL" title img))
298 298
299 299 (defun update-objs ()
300 300 (let ((elt (by-id "qsp-objs")))
301 301 (setf (inner-html elt) "<ul>")
302 302 (loop :for (name obj) :of *objs
303 303 :do (incf (inner-html elt)
304 304 (make-obj name (@ obj :img) (@ obj :selected))))
305 305 (incf (inner-html elt) "</ul>")))
306 306
307 307 ;;; Menu
308 308
309 309 (defun open-menu (menu-data)
310 310 (let ((elt (get-frame :dropdown))
311 311 (i 0))
312 312 (loop :for item :in menu-data
313 313 :do (incf i)
314 314 :do (incf (inner-html elt)
315 315 (if (eq item :delimiter)
316 316 (make-menu-delimiter i)
317 317 (make-menu-item-html i
318 318 (@ item :text)
319 319 (@ item :icon)
320 320 (@ item :loc)))))
321 321 (let ((mouse (@ window mouse)))
322 322 (setf (@ elt style left) (+ (elt mouse 0) "px"))
323 323 (setf (@ elt style top) (+ (elt mouse 1) "px"))
324 324 ;; Make sure it's inside the viewport
325 325 (when (> (@ document body inner-width)
326 326 (+ (elt mouse 0) (@ elt inner-width)))
327 327 (incf (@ elt style left) (@ elt inner-width)))
328 328 (when (> (@ document body inner-height)
329 329 (+ (elt mouse 0) (@ elt inner-height)))
330 330 (incf (@ elt style top) (@ elt inner-height))))
331 331 (setf (@ elt style display) "block")))
332 332
333 333 (defun finish-menu (loc)
334 334 (when *menu-resume
335 335 (let ((elt (get-frame :dropdown)))
336 336 (setf (inner-html elt) "")
337 337 (setf (@ elt style display) "none")
338 338 (funcall *menu-resume)
339 339 (setf *menu-resume nil))
340 340 (when loc
341 341 (call-loc loc)))
342 342 (void))
343 343
344 344 (defun menu (menu-data)
345 345 (with-sleep (resume)
346 346 (open-menu menu-data)
347 347 (setf *menu-resume resume))
348 348 (void))
349 349
350 350 ;;; Content
351 351
352 352 (defun clean-audio ()
353 353 (loop :for k :in (chain *object (keys *playing))
354 354 :for v := (getprop *playing k)
355 355 :do (when (@ v ended)
356 356 (delete (@ *playing k)))))
357 357
358 358 (defun show-image (path)
359 359 (let ((img (get-frame :image)))
360 360 (cond (path
361 361 (setf (@ img src) path)
362 362 (setf (@ img style display) "flex"))
363 363 (t
364 364 (setf (@ img src) "")
365 365 (setf (@ img style display) "hidden")))))
366 366
367 367 (defun show-inline-images (frame-name images)
368 368 (let ((frame (get-frame frame-name))
369 369 (text ""))
370 370 (incf text "<div style='position:relative; display: inline-block'>")
371 371 (incf text (+ "<img src='" (@ images 0) "'>"))
372 372 (loop :for image :in (chain images (slice 1))
373 373 :do (incf text
374 374 (+ "<img style='position:absolute' src='" image "'>")))
375 375 (incf text "</div>")
376 376 (incf (inner-html frame) text)))
377 377
378 378 (defun rgb-string (rgb)
379 379 (let ((red (ps::>> rgb 16))
380 380 (green (logand (ps::>> rgb 8) 255))
381 381 (blue (logand rgb 255)))
382 382 (flet ((rgb-to-hex (comp)
383 383 (let ((hex (chain (*number comp) (to-string 16))))
384 384 (if (< (length hex) 2)
385 385 (+ "0" hex)
386 386 hex))))
387 387 (+ "#" (rgb-to-hex red) (rgb-to-hex green) (rgb-to-hex blue)))))
388 388
389 (defun store-obj (key obj)
390 (store-str key (btoa (encode-u-r-i-component (chain *j-s-o-n (stringify obj)))))
391 (void))
392 (defun store-str (key str)
393 (chain local-storage (set-item (+ "qsp_" key) str))
394 (void))
395
396 (defun load-obj (key)
397 (chain *j-s-o-n (parse (encode-u-r-i-component (atob (load-str key))))))
398 (defun load-str (key)
399 (chain local-storage (get-item (+ "qsp_" key))))
400
389 401 ;;; Saves
390 402
403 (defun slot-savegame (slot comment)
404 (let ((saves (load-obj "saves")))
405 (setf (@ saves slot) comment)
406 (store-obj saves))
407 (store-str slot (state-to-base64))
408 (void))
409
410 (defun slot-loadgame (slot)
411 (base64-to-state (load-str slot))
412 (void))
413
414 (defun slot-deletegame (slot)
415 (let ((saves (load-obj "saves")))
416 (setf (@ saves slot) undefined)
417 (store-obj saves))
418 (store-str slot undefined)
419 (void))
420
421 (defun slot-listgames ()
422 (load-obj "saves"))
423
391 424 (defun opengame ()
392 425 (let ((element (chain document (create-element :input))))
393 426 (chain element (set-attribute :type :file))
394 427 (chain element (set-attribute :id :qsp-opengame))
395 428 (chain element (set-attribute :tabindex -1))
396 429 (chain element (set-attribute "aria-hidden" t))
397 430 (setf (@ element style display) :block)
398 431 (setf (@ element style visibility) :hidden)
399 432 (setf (@ element style position) :fixed)
400 433 (setf (@ element onchange)
401 434 (lambda (event)
402 435 (let* ((file (@ event target files 0))
403 436 (reader (new (*file-reader))))
404 437 (setf (@ reader onload)
405 438 (lambda (ev)
406 439 (block nil
407 440 (let ((target (@ ev current-target)))
408 441 (unless (@ target result)
409 442 (return))
410 443 (base64-to-state (@ target result))
411 444 (unstash-state)))))
412 445 (chain reader (read-as-text file)))))
413 446 (chain document body (append-child element))
414 447 (chain element (click))
415 448 (chain document body (remove-child element))))
416 449
417 450 (defun savegame ()
418 451 (let ((element (chain document (create-element :a))))
419 452 (chain element (set-attribute :href (+ "data:text/plain;charset=utf-8," (state-to-base64))))
420 453 (chain element (set-attribute :download "savegame.sav"))
421 454 (setf (@ element style display) :none)
422 455 (chain document body (append-child element))
423 456 (chain element (click))
424 457 (chain document body (remove-child element))))
425 458
426 459 (defun stash-state (args)
427 460 (call-serv-loc "$ONGSAVE")
428 461 (setf *state-stash
429 462 (chain *j-s-o-n (stringify
430 463 (create :vars *globals
431 464 :objs *objs
432 465 :loc-args args
433 466 :msecs (- (chain *date (now)) *started-at)
434 467 :timer-interval *timer-interval
435 468 :main-html (inner-html
436 469 (get-frame :main))
437 470 :stat-html (inner-html
438 471 (get-frame :stat))
439 472 :next-location *current-location))))
440 473 (void))
441 474
442 475 (defun unstash-state ()
443 476 (let ((data (chain *j-s-o-n (parse *state-stash))))
444 477 (clear-act)
445 478 (setf *globals (@ data :vars))
446 479 (loop :for k :in (chain *object (keys *globals))
447 480 :do (chain *object (set-prototype-of (getprop *globals k)
448 481 (@ *var prototype))))
449 482 (setf *started-at (- (chain *date (now)) (@ data :msecs)))
450 483 (setf *objs (@ data :objs))
451 484 (setf *current-location (@ data :next-location))
452 485 (setf (inner-html (get-frame :main))
453 486 (@ data :main-html))
454 487 (setf (inner-html (get-frame :stat))
455 488 (@ data :stat-html))
456 489 (update-objs)
457 490 (set-timer (@ data :timer-interval))
458 491 (call-serv-loc "$ONGLOAD")
459 492 (call-loc *current-location (@ data :loc-args))
460 493 (void)))
461 494
462 495 (defun state-to-base64 ()
463 496 (btoa (encode-u-r-i-component *state-stash)))
464 497
465 498 (defun base64-to-state (data)
466 499 (setf *state-stash (decode-u-r-i-component (atob data))))
467 500
468 501 ;;; Timers
469 502
470 503 (defun set-timer (interval)
471 504 (setf *timer-interval interval)
472 505 (clear-interval *timer-obj)
473 506 (setf *timer-obj
474 507 (set-interval
475 508 (lambda ()
476 509 (call-serv-loc "$COUNTER"))
477 510 interval)))
478 511
479 512 ;;; Special variables
480 513
481 514 (defvar serv-vars (create))
482 515
483 516 (define-serv-var $backimage (path)
484 517 (setf (@ (get-frame :main) style background-image) path))
485 518
486 519 (define-serv-var bcolor (color)
487 520 (setf (@ (get-frame :all) style background-color) (rgb-string color)))
488 521
489 522 (define-serv-var fcolor (color)
490 523 (setf (@ (get-frame :all) style color) (rgb-string color)))
491 524
492 525 (define-serv-var lcolor (color)
493 526 (setf (@ (get-frame :style) inner-text)
494 527 (+ "a { color: " (rgb-string color) ";}")))
495 528
496 529 (define-serv-var fsize (size)
497 530 (setf (@ (get-frame :all) style font-size) size))
498 531
499 532 (define-serv-var $fname (font-name)
500 533 (setf (@ (get-frame :all) style font-family) (+ font-name ",serif")))
@@ -1,43 +1,44 b''
1 1
2 2 (in-package sugar-qsp.js)
3 3
4 4 ;;; Contains symbols from standard JS library to avoid obfuscating
5 5 ;;; and/or namespacing them
6 6
7 7 (cl:defmacro syms (cl:&rest syms)
8 8 `(cl:progn
9 9 ,@(cl:loop :for sym :in syms
10 10 :collect `(cl:export ',sym))))
11 11
12 12 (syms
13 13 ;; main
14 14 window
15 15 *object assign
16 16 now
17 17 onload
18 18 keys includes
19 19 has-own-property
20 20 ;; api
21 21 document get-element-by-id get-elements-by-tag-name
22 22 onclick onchange
23 23 atob btoa split
24 24 alert prompt
25 25 set-timeout set-interval clear-interval
26 26 *promise *j-s-o-n
27 27 href parse match
28 28 set-prototype-of
29 29 body append-child remove-child
30 30 add ; remove (is already in COMMON-LISP)
31 31 create-element set-attribute class-list
32 32 *file-reader read-as-text
33 33 style display src
34 34 page-x page-y
35 35 top left
36 36 background-image background-color
37 37 color inner-text font-size font-family font-name
38 local-storage set-item get-item
38 39 ;; lib
39 40 *number parse-int
40 41 to-string to-upper-case concat
41 42 click target current-target files index-of result
42 43 decode-u-r-i-component splice
43 44 )
@@ -1,8 +1,8 b''
1 1
2 2 (in-package sugar-qsp.main)
3 3
4 4 (defmacro+ps api-call (name &rest args)
5 `(,(intern (string-upcase name) "API") ,@args))
5 `(,(intern (string-upcase name) "SUGAR-QSP.API") ,@args))
6 6
7 7 (defpsmacro has (key obj)
8 8 `(chain ,obj (has-own-property ,key)))
@@ -1,148 +1,163 b''
1 1
2 2 (in-package sugar-qsp)
3 3
4 (defvar *app-name* "")
5
4 6 (defun entry-point-no-args ()
5 7 (entry-point uiop:*command-line-arguments*))
6 8
7 9 (defun entry-point (args)
8 (catch :terminate
9 (let ((compiler (apply #'make-instance 'compiler (parse-opts args))))
10 (write-compiled-file compiler)))
10 (setf *app-name* (first args))
11 (let ((*package* (find-package :sugar-qsp)))
12 (catch :terminate
13 (let ((compiler (apply #'make-instance 'compiler (parse-opts (rest args)))))
14 (write-compiled-file compiler))))
11 15 (values))
12 16
13 17 (defun parse-opts (args)
14 18 (let ((mode :sources)
15 19 (data (list :sources nil :target nil :js nil :css nil :body nil :compile nil :beautify nil)))
16 20 (loop :for arg :in args
17 21 :do (alexandria:switch (arg :test #'string=)
18 22 ("-o" (setf mode :target))
19 23 ("--js" (setf mode :js))
20 24 ("--css" (setf mode :css))
21 25 ("--body" (setf mode :body))
22 26 ("-c" (setf (getf data :compile) t))
23 27 ("--beautify" (setf (getf data :beautify) t))
24 28 (t (push arg (getf data mode)))))
25 29 (unless (< 0 (length (getf data :sources)))
26 (print-usage)
27 30 (report-error "There should be at least one source"))
28 31 (unless (> 1 (length (getf data :target)))
29 (print-usage)
30 32 (report-error "There should be no more than one target"))
31 33 (unless (> 1 (length (getf data :body)))
32 (print-usage)
33 34 (report-error "There should be no more than one body"))
34 35 (unless (getf data :target)
35 36 (setf (getf data :target)
36 37 (let* ((sources (first (getf data :sources)))
37 38 (tokens (uiop:split-string sources :separator "."))
38 39 (target (format nil "~{~A~^.~}.html"
39 40 (butlast tokens))))
40 41 (list target))))
41 42 (list :sources (getf data :sources)
42 43 :target (first (getf data :target))
43 44 :js (getf data :js)
44 45 :css (getf data :css)
45 46 :body (first (getf data :body))
46 47 :compile (getf data :compile)
47 48 :beautify (getf data :beautify))))
48 49
49 50 (defun print-usage ()
50 (format t "USAGE: "))
51 (format t "Usage: ~A <source> [options]~%" *app-name*)
52 (format t "Options:~%")
53 (format t " -o <filename> - Output filename~%")
54 (format t " --js <filenames...> - List of extra .js files to include in the game~%")
55 (format t " --css <filenames...> - List of .css files to include in the game. Default is in extras/default.css~%")
56 (format t " --body <filename> - Alternative page body. Default is in extras/body.html~%")
57 (format t "~%")
58 (format t " -c - Just compile the game to a .js file without making it a full web page~%")
59 (format t " --beautify - Make the JS content pretty. By default it gets minified.~%")
60 (format t "~%")
61 (format t "Note that the files in extras/ are not actually used. They're just there for the reference"))
51 62
52 63 (defun parse-file (filename)
53 64 (p:parse 'sugar-qsp-grammar
54 65 (alexandria:read-file-into-string filename)))
55 66
56 67 (defun report-error (fmt &rest args)
57 (apply #'format t fmt args)
68 (format t "ERROR: ~A~%" (apply #'format nil fmt args))
69 (print-usage)
58 70 (throw :terminate nil))
59 71
60 72 ;;; JS
61 73
62 74 (defun minify-package (package-designator minify prefix)
63 75 (setf (ps:ps-package-prefix package-designator) prefix)
64 76 (if minify
65 77 (ps:obfuscate-package package-designator)
66 78 (ps:unobfuscate-package package-designator)))
67 79
68 80 (defmethod js-sources ((compiler compiler))
69 81 (let ((ps:*ps-print-pretty* (beautify compiler)))
70 82 (cond ((beautify compiler)
71 83 (minify-package "SUGAR-QSP.MAIN" nil "qsp_")
72 84 (minify-package "SUGAR-QSP.API" nil "qsp_api_")
73 85 (minify-package "SUGAR-QSP.LIB" nil "qsp_lib_"))
74 86 (t
75 87 (minify-package "SUGAR-QSP.MAIN" t "_")
76 88 (minify-package "SUGAR-QSP.API" t "a_")
77 89 (minify-package "SUGAR-QSP.LIB" t "l_")))
78 90 (format nil "~{~A~^~%~%~}" (mapcar #'ps:ps* (reverse (js compiler))))))
79 91
80 92 ;;; CSS
81 93
82 94 (defmethod css-sources ((compiler compiler))
83 95 (format nil "~{~A~^~%~%~}" (css compiler)))
84 96
85 97 ;;; HTML
86 98
87 99 (defmethod html-sources ((compiler compiler))
88 100 (let ((flute:*escape-html* nil)
89 101 (body-template (body compiler))
90 102 (js (js-sources compiler))
91 103 (css (css-sources compiler)))
92 104 (with-output-to-string (out)
93 105 (write
94 106 (flute:h
95 107 (html
96 108 (head
97 109 (title "SugarQSP"))
98 110 (body
99 111 body-template
100 112 (style css)
101 113 (script js))))
102 114 :stream out
103 115 :pretty nil))))
104 116
105 117 (defun filename-game (filename)
106 118 (let ((filename (alexandria:lastcar (uiop:split-string filename :separator "/"))))
107 119 (format nil "~{~A~^.~}" (butlast (uiop:split-string filename :separator ".")))))
108 120
109 121 (defmethod initialize-instance ((compiler compiler) &key sources ((:js js-files)) ((:css css-files)) ((:body body-file)) compile &allow-other-keys)
110 122 (call-next-method)
111 123 (with-slots (body css js)
112 124 compiler
113 125 ;; Compile the game's JS
114 126 (dolist (source sources)
115 127 (let ((ps (parse-file source))
116 128 (game-name (filename-game source)))
117 129 (destructuring-bind (kw &rest locations)
118 130 ps
119 131 (unless (eq kw 'lib:game)
120 132 (report-error "Internal error!"))
121 133 (push
122 134 `(lib:game (,game-name) ,@locations)
123 135 js))))
124 136 ;; Does the user need us to do anything else
125 137 (unless compile
126 138 ;; Read in body
127 139 (when body-file
128 140 (setf body
129 141 (alexandria:read-file-into-string body-file)))
130 142 ;; Include js files
131 143 (dolist (js-file js-files)
132 144 (push (format nil "////// Included file ~A~%~A" js-file
133 145 (alexandria:read-file-into-string js-file))
134 146 js))
135 147 ;; Include css files
136 (dolist (css-file css-files)
137 (push (format nil "////// Included file ~A~%~A" css-file
138 (alexandria:read-file-into-string css-file))
139 css)))))
148 (when css-files
149 ;; User option overrides the default css
150 (setf css nil)
151 (dolist (css-file css-files)
152 (push (format nil "////// Included file ~A~%~A" css-file
153 (alexandria:read-file-into-string css-file))
154 css))))))
140 155
141 156 (defmethod write-compiled-file ((compiler compiler))
142 157 (alexandria:write-string-into-file
143 158 (if (compile-only compiler)
144 159 ;; Just the JS
145 160 (js-sources compiler)
146 161 ;; All of it
147 162 (html-sources compiler))
148 163 (target compiler) :if-exists :supersede))
@@ -1,623 +1,624 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 18 (defun not-doublequote (char)
19 19 (not (eql #\" char)))
20 20
21 21 (defun not-brace (char)
22 22 (not (eql #\} char)))
23 23
24 24 (defun not-integer (string)
25 25 (when (find-if-not #'digit-char-p string)
26 26 t))
27 27
28 28 (defun not-newline (char)
29 29 (not (eql #\newline char)))
30 30
31 31 (defun id-any-char (char)
32 32 (and
33 33 (not (digit-char-p char))
34 34 (not (eql #\newline char))
35 35 (not (find char " !:&=<>+-*/,'\"()[]{}"))))
36 36
37 37 (defun intern-first (list)
38 (list* (intern (string-upcase (first list)) :lib)
38 (list* (intern (string-upcase (first list)) "SUGAR-QSP.LIB")
39 39 (rest list)))
40 40
41 41 (eval-when (:compile-toplevel :load-toplevel :execute)
42 42 (defun remove-nil (list)
43 43 (remove nil list)))
44 44
45 45 (defun binop-rest (list)
46 46 (destructuring-bind (ws1 operator ws2 operand2)
47 47 list
48 48 (declare (ignore ws1 ws2))
49 49 (list (intern (string-upcase operator) "SUGAR-QSP.LIB") operand2)))
50 50
51 51 (defun do-binop% (left-op other-ops)
52 52 (if (null other-ops)
53 53 left-op
54 54 (destructuring-bind ((operator right-op) &rest rest-ops)
55 55 other-ops
56 56 (if (and (listp left-op)
57 57 (eq (first left-op)
58 58 operator))
59 59 (do-binop% (append left-op (list right-op)) rest-ops)
60 60 (do-binop% (list operator left-op right-op) rest-ops)))))
61 61
62 62 (defun do-binop (list)
63 63 (destructuring-bind (left-op rest-ops)
64 64 list
65 65 (do-binop% left-op
66 66 (mapcar #'binop-rest rest-ops))))
67 67
68 68 (p:defrule line-continuation (and #\_ #\newline)
69 69 (:constant nil))
70 70
71 71 (p:defrule text-spaces (+ (or #\space #\tab line-continuation))
72 72 (:text t))
73 73
74 74 (p:defrule spaces (+ (or #\space #\tab line-continuation))
75 75 (:constant nil)
76 76 (:error-report nil))
77 77
78 78 (p:defrule spaces? (* (or #\space #\tab line-continuation))
79 79 (:constant nil)
80 80 (:error-report nil))
81 81
82 82 (p:defrule colon #\:
83 83 (:constant nil))
84 84
85 85 (p:defrule equal #\=
86 86 (:constant nil))
87 87
88 88 (p:defrule alphanumeric (alphanumericp character))
89 89
90 90 (p:defrule not-newline (not-newline character))
91 91
92 92 (p:defrule squote-esc "''"
93 93 (:lambda (list)
94 94 (p:text (elt list 0))))
95 95
96 96 (p:defrule dquote-esc "\"\""
97 97 (:lambda (list)
98 98 (p:text (elt list 0))))
99 99
100 100 (p:defrule sstring-chars (+ (and (and (p:! "<<") (p:! (p:~ "\"exec:")))
101 101 (or squote-esc (not-quote character))))
102 102 (:lambda (list)
103 103 (p:text (mapcar #'second list))))
104 104
105 105 (p:defrule dstring-chars (+ (and (and (p:! "<<") (p:! (p:~ "'exec:")))
106 106 (or dquote-esc (not-doublequote character))))
107 107 (:lambda (list)
108 108 (p:text (mapcar #'second list))))
109 109
110 110 ;;; Identifiers
111 111
112 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))
113 113
114 114 (defun trim-$ (str)
115 115 (if (char= #\$ (elt str 0))
116 116 (subseq str 1)
117 117 str))
118 118
119 119 (defun qsp-keyword-p (id)
120 120 (member (intern (trim-$ (string-upcase id))) *keywords*))
121 121
122 122 (defun not-qsp-keyword-p (id)
123 123 (not (member (intern (trim-$ (string-upcase id))) *keywords*)))
124 124
125 125 (p:defrule qsp-keyword (qsp-keyword-p identifier-raw))
126 126
127 127 (p:defrule id-first (id-any-char character))
128 128 (p:defrule id-next (or (id-any-char character)
129 129 (digit-char-p character)))
130 130 (p:defrule identifier-raw (and id-first (* id-next))
131 131 (:lambda (list)
132 (intern (string-upcase (p:text list)) :lib)))
132 (intern (string-upcase (p:text list)) "SUGAR-QSP.LIB")))
133 133
134 134 (p:defrule identifier (not-qsp-keyword-p identifier-raw))
135 135
136 136 ;;; Strings
137 137
138 138 (p:defrule qsp-string (or normal-string brace-string))
139 139
140 140 (p:defrule normal-string (or sstring dstring)
141 141 (:lambda (str)
142 142 (list* 'lib:str (or str (list "")))))
143 143
144 144 (p:defrule sstring (and #\' (* (or string-interpol
145 145 sstring-exec
146 146 sstring-chars))
147 147 #\')
148 148 (:function second))
149 149
150 150 (p:defrule dstring (and #\" (* (or string-interpol
151 151 dstring-exec
152 152 dstring-chars))
153 153 #\")
154 154 (:function second))
155 155
156 156 (p:defrule string-interpol (and "<<" expression ">>")
157 157 (:function second))
158 158
159 159 (p:defrule sstring-exec-body (+ (or squote-esc (not-doublequote character)))
160 160 (:text t))
161 161
162 162 (p:defrule dstring-exec-body (+ (or dquote-esc (not-quote character)))
163 163 (:text t))
164 164
165 165 (p:defrule sstring-exec (and (p:~ "\"exec:") sstring-exec-body #\")
166 166 (:lambda (list)
167 167 (list* 'lib:exec (p:parse 'exec-body (second list)))))
168 168
169 169 (p:defrule dstring-exec (and (p:~ "'exec:") dstring-exec-body #\')
170 170 (:lambda (list)
171 171 (list* 'lib:exec (p:parse 'exec-body (second list)))))
172 172
173 173 (p:defrule brace-string (and #\{ before-statement block-body #\})
174 174 (:lambda (list)
175 175 (list* 'lib:qspblock (third list))))
176 176
177 177 ;;; Location
178 178
179 179 (p:defrule sugar-qsp-grammar (and (* (or spaces #\newline))
180 180 (* location))
181 181 (:lambda (list)
182 182 `(lib:game ,@(second list))))
183 183
184 184 (p:defrule location (and location-header block-body location-end)
185 185 (:destructure (header body end)
186 186 (declare (ignore end))
187 187 `(lib:location (,header) ,@body)))
188 188
189 189 (p:defrule location-header (and #\#
190 190 (+ not-newline)
191 191 (and #\newline spaces? before-statement))
192 192 (:destructure (spaces1 name spaces2)
193 193 (declare (ignore spaces1 spaces2))
194 194 (string-upcase (string-trim " " (p:text name)))))
195 195
196 196 (p:defrule location-end (and #\- (* not-newline) #\newline before-statement)
197 197 (:constant nil))
198 198
199 199 ;;; Block body
200 200
201 201 (p:defrule newline-block-body (and #\newline spaces? block-body)
202 202 (:function third))
203 203
204 204 (p:defrule block-body (* statement)
205 205 (:function remove-nil))
206 206
207 207 ;; Just for <a href="exec:...'>
208 208 ;; Explicitly called from that rule's production
209 209 (p:defrule exec-body (and before-statement line-body)
210 210 (:function second))
211 211
212 212 (p:defrule line-body (and inline-statement (* next-inline-statement))
213 213 (:lambda (list)
214 214 (list* (first list) (second list))))
215 215
216 216 (p:defrule before-statement (* (or #\newline spaces))
217 217 (:constant nil))
218 218
219 219 (p:defrule statement-end (or statement-end-real statement-end-block-close))
220 220
221 221 (p:defrule statement-end-real (and (or #\newline
222 222 (and #\& spaces? (p:& statement%)))
223 223 before-statement)
224 224 (:constant nil))
225 225
226 226 (p:defrule statement-end-block-close (or (p:& #\}))
227 227 (:constant nil))
228 228
229 229 (p:defrule inline-statement (and statement% spaces?)
230 230 (:function first))
231 231
232 232 (p:defrule next-inline-statement (and #\& spaces? inline-statement)
233 233 (:function third))
234 234
235 235 (p:defrule not-a-non-statement (and (p:! (p:~ "elseif"))
236 236 (p:! (p:~ "else"))
237 237 (p:! (p:~ "end"))))
238 238
239 239 (p:defrule statement (and inline-statement statement-end)
240 240 (:function first))
241 241
242 242 (p:defrule statement% (and not-a-non-statement
243 243 (or label comment string-output
244 244 block non-returning-intrinsic local
245 245 assignment expression-output))
246 246 (:function second))
247 247
248 248 (p:defrule expr-stopper (or comment block non-returning-intrinsic))
249 249
250 250 (p:defrule string-output qsp-string
251 251 (:lambda (string)
252 252 (list 'lib:main-pl string)))
253 253
254 254 (p:defrule expression-output expression
255 255 (:lambda (list)
256 256 (list 'lib:main-pl list)))
257 257
258 258 (p:defrule label (and colon identifier)
259 259 (:lambda (list)
260 260 (intern (string (second list)) :keyword)))
261 261
262 262 (p:defrule comment (and #\! (* (or qsp-string brace-comment text-spaces not-newline)))
263 263 (:constant nil))
264 264
265 265 (p:defrule brace-comment (and #\{ (* (not-brace character)) #\})
266 266 (:constant nil))
267 267
268 268 (p:defrule local (and (p:~ "local") spaces variable (p:? (and spaces? #\= spaces? expression)))
269 269 (:lambda (list)
270 270 (list* 'lib:local (third list)
271 271 (when (fourth list)
272 272 (list (fourth (fourth list)))))))
273 273
274 274 ;;; Blocks
275 275
276 276 (p:defrule block (or block-act block-if block-for))
277 277
278 278 (p:defrule block-if (and block-if-head block-if-body)
279 279 (:destructure (head body)
280 280 `(lib:qspcond (,@head ,@(first body))
281 281 ,@(rest body))))
282 282
283 283 (p:defrule block-if-head (and (p:~ "if") spaces expression spaces? colon spaces?)
284 284 (:function remove-nil)
285 285 (:function cdr))
286 286
287 287 (p:defrule block-if-body (or block-if-ml block-if-sl)
288 288 (:destructure (if-body elseifs else &rest ws)
289 289 (declare (ignore ws))
290 290 `(,(remove-nil if-body) ,@elseifs ,@(when else `((else ,@else))))))
291 291
292 292 (p:defrule block-if-sl (and line-body
293 293 (p:? block-if-elseif-inline)
294 294 (p:? block-if-else-inline)
295 295 spaces?))
296 296
297 297 (p:defrule block-if-ml (and (and #\newline spaces?)
298 298 block-body
299 299 (p:? block-if-elseif)
300 300 (p:? block-if-else)
301 301 block-if-end)
302 302 (:lambda (list)
303 303 (cdr list)))
304 304
305 305 (p:defrule block-if-elseif-inline (and block-if-elseif-head line-body (p:? block-if-elseif-inline))
306 306 (:destructure (head statements elseif)
307 307 `((,@(rest head) ,@(remove-nil statements)) ,@elseif)))
308 308
309 309 (p:defrule block-if-elseif (and block-if-elseif-head statement-end block-body (p:? block-if-elseif))
310 310 (:destructure (head ws statements elseif)
311 311 (declare (ignore ws))
312 312 `((,@(rest head) ,@(remove-nil statements)) ,@elseif)))
313 313
314 314 (p:defrule block-if-elseif-head (and (p:~ "elseif") spaces expression spaces? colon spaces?)
315 315 (:function remove-nil)
316 316 (:function intern-first))
317 317
318 318 (p:defrule block-if-else-inline (and block-if-else-head line-body)
319 319 (:function second))
320 320
321 321 (p:defrule block-if-else (and block-if-else-head #\newline spaces? block-body)
322 322 (:function fourth))
323 323
324 324 (p:defrule block-if-else-head (and (p:~ "else") spaces?)
325 325 (:constant nil))
326 326
327 327 (p:defrule block-if-end (and (p:~ "end")
328 328 (p:? (and spaces (p:~ "if"))))
329 329 (:constant nil))
330 330
331 331 (p:defrule block-act (and block-act-head (or block-ml block-sl))
332 332 (:lambda (list)
333 333 (apply #'append list)))
334 334
335 335 (p:defrule block-act-head (and (p:~ "act") spaces? qsp-string spaces?
336 336 (p:? block-act-head-img)
337 337 colon spaces?)
338 338 (:lambda (list)
339 339 (intern-first (list (first list)
340 340 (third list)
341 341 (or (fifth list) '(lib:str ""))))))
342 342
343 343 (p:defrule block-act-head-img (and #\, spaces? qsp-string spaces?)
344 344 (:lambda (list)
345 345 (or (third list) "")))
346 346
347 347 (p:defrule block-for (and block-for-head (or block-ml block-sl))
348 348 (:lambda (list)
349 349 (apply #'append list)))
350 350
351 351 (p:defrule block-for-head (and (p:~ "for") spaces variable spaces? equal spaces? expression
352 352 (p:~ "to") spaces expression
353 353 block-for-head-step
354 354 colon spaces?)
355 355 (:lambda (list)
356 356 (list 'lib:qspfor
357 357 (elt list 2)
358 358 (elt list 6)
359 359 (elt list 9)
360 360 (elt list 10))))
361 361
362 362 (p:defrule block-for-head-step (p:? (and (p:~ "step") spaces expression spaces?))
363 363 (:lambda (list)
364 364 (if list
365 365 (third list)
366 366 1)))
367 367
368 368 (p:defrule block-sl line-body)
369 369
370 370 (p:defrule block-ml (and newline-block-body block-end)
371 371 (:lambda (list)
372 372 (apply #'list* (butlast list))))
373 373
374 374 (p:defrule block-end (and (p:~ "end"))
375 375 (:constant nil))
376 376
377 377 ;;; Calls
378 378
379 379 (p:defrule first-argument (and expression spaces?)
380 380 (:function first))
381 381 (p:defrule next-argument (and "," spaces? expression)
382 382 (:function third))
383 383 (p:defrule arguments (or parenthesized-arguments plain-arguments no-arguments))
384 384 (p:defrule parenthesized-arguments (and spaces? #\( base-arguments #\))
385 385 (:function third))
386 386 (p:defrule plain-arguments (and spaces? base-arguments)
387 387 (:function second))
388 388 (p:defrule no-arguments (or (and spaces? (p:& #\newline))
389 389 (and spaces? (p:& #\&))
390 390 spaces?)
391 391 (:constant nil))
392 392 (p:defrule base-arguments (or (and first-argument (* next-argument)) spaces?)
393 393 (:lambda (list)
394 394 (if (null list)
395 395 nil
396 396 (list* (first list) (second list)))))
397 397
398 398 ;;; Intrinsics
399 399
400 400 (defmacro defintrinsics ((rule-name returning-rule-name non-returning-rule-name) &body clauses)
401 401 `(progn
402 402 ,@(loop :for clause :in clauses
403 403 :collect `(defintrinsic ,@clause))
404 404 (p:defrule ,returning-rule-name (or ,@(remove-nil
405 405 (mapcar (lambda (clause)
406 406 (when (second clause)
407 407 (alexandria:symbolicate
408 408 'intrinsic- (first clause))))
409 409 clauses))))
410 410 (p:defrule ,non-returning-rule-name (or ,@(remove-nil
411 411 (mapcar (lambda (clause)
412 412 (unless (second clause)
413 413 (alexandria:symbolicate
414 414 'intrinsic- (first clause))))
415 415 clauses))))
416 416 (p:defrule ,rule-name (or ,returning-rule-name ,non-returning-rule-name))))
417 417
418 418 (defmacro defintrinsic (sym returning &optional (min-arity 0) max-arity &rest names)
419 419 (declare (ignore returning))
420 420 (unless max-arity
421 421 (setf max-arity *max-args*))
422 422 (setf names
423 423 (if names
424 424 (mapcar #'string-upcase names)
425 425 (list (string sym))))
426 426 `(p:defrule ,(alexandria:symbolicate 'intrinsic- sym)
427 427 (and (p:? #\$) (or ,@(loop :for name :in names :collect `(p:~ ,name)))
428 428 arguments)
429 429 (:destructure (dollar name arguments)
430 430 (declare (ignore dollar))
431 431 (unless (<= ,min-arity (length arguments) ,max-arity)
432 432 (error "Intrinsic ~A expects between ~A and ~A arguments but ~A were provided:~%~S"
433 433 name ,min-arity ,max-arity (length arguments) arguments))
434 (list* ',(intern (string sym) :lib) arguments))))
434 (list* ',(intern (string sym) "SUGAR-QSP.LIB") arguments))))
435 435
436 436 (defintrinsics (intrinsic returning-intrinsic non-returning-intrinsic)
437 437 ;; Transitions
438 438 (goto% nil 0 nil "gt" "goto")
439 439 (xgoto% nil 0 nil "xgt" "xgoto")
440 440 ;; Variables
441 441 (killvar nil 0 2)
442 442 ;; Expressions
443 443 (obj t 1 1)
444 444 (loc t 1 1)
445 445 (no t 1 1)
446 446 ;; Basic
447 447 (qspver t 0 0)
448 448 (curloc t 0 0)
449 449 (rand t 1 2)
450 450 (rnd t 0 0)
451 451 (qspmax t 1 nil "max")
452 452 (qspmin t 1 nil "min")
453 453 ;; Arrays
454 454 (killall nil 0 0)
455 455 (copyarr nil 2 4)
456 456 (arrsize t 1 1)
457 457 (arrpos t 2 3)
458 458 (arrcomp t 2 3)
459 459 ;; Strings
460 460 (len t 1 1)
461 461 (mid t 2 3)
462 462 (ucase t 1 1)
463 463 (lcase t 1 1)
464 464 (trim t 1 1)
465 465 (replace t 2 3)
466 466 (instr t 2 3)
467 467 (isnum t 1 1)
468 468 (val t 1 1)
469 469 (qspstr t 1 1 "str")
470 470 (strcomp t 2 2)
471 471 (strfind t 2 3)
472 472 (strpos t 2 3)
473 473 ;; IF
474 474 (iif t 2 3)
475 475 ;; Subs
476 476 (gosub nil 1 nil "gosub" "gs")
477 477 (func t 1 nil)
478 478 (exit nil 0 0)
479 479 ;; Jump
480 480 (jump nil 1 1)
481 481 ;; Dynamic
482 482 (dynamic nil 1 nil)
483 483 (dyneval t 1 nil)
484 484 ;; Sound
485 485 (play nil 1 2)
486 486 (isplay t 1 1)
487 487 (close nil 1 1)
488 488 (closeall nil 0 0 "close all")
489 489 ;; Main window
490 490 (main-pl nil 1 1 "*pl")
491 491 (main-nl nil 0 1 "*nl")
492 492 (main-p nil 1 1 "*p")
493 493 (maintxt t 0 0)
494 494 (desc t 1 1)
495 495 (main-clear nil 0 0 "*clear" "*clr")
496 496 ;; Aux window
497 497 (showstat nil 1 1)
498 (stat-pl nil 1 1 "pl")
499 (stat-nl nil 0 1 "nl")
500 (stat-p nil 1 1 "p")
498 (stat-pl nil 1 1 "pl")
499 (stat-nl nil 0 1 "nl")
500 (stat-p nil 1 1 "p")
501 501 (stattxt t 0 0)
502 502 (stat-clear nil 0 0 "clear" "clr")
503 503 (cls nil 0 0)
504 504 ;; Dialog
505 505 (msg nil 1 1)
506 506 ;; Acts
507 507 (showacts nil 1 1)
508 (delact nil 0 1 "delact" "del act")
509 (curact t 0 0)
508 (delact nil 1 1 "delact" "del act")
510 509 (curacts t 0 0)
510 (selact t 0 0)
511 511 (cla nil 0 0)
512 512 ;; Objects
513 513 (showobjs nil 1 1)
514 514 (addobj nil 1 3 "addobj" "add obj")
515 515 (delobj nil 1 1 "delobj" "del obj")
516 516 (killobj nil 0 1)
517 517 (countobj t 0 0)
518 518 (getobj t 1 1)
519 (selobj t 0 0)
519 520 ;; Menu
520 521 (menu nil 1 1)
521 522 ;; Images
522 523 (refint nil 0 0)
523 524 (view nil 0 1)
524 525 (img nil 1)
525 526 (*img nil 1)
526 527 ;; Fonts
527 528 (rgb t 3 3)
528 529 ;; Input
529 530 (showinput nil 1 1)
530 531 (usertxt t 0 0 "user_text" "usrtxt")
531 532 (cmdclear nil 0 0 "cmdclear" "cmdclr")
532 533 (input t 1 1)
533 534 ;; Files
534 535 (openqst nil 1 1)
535 536 (addqst nil 1 1 "addqst" "addlib" "inclib")
536 537 (killqst nil 1 1 "killqst" "dellib" "freelib")
537 538 (opengame nil 0 0)
538 539 (savegame nil 0 0)
539 540 ;; Real time
540 541 (wait nil 1 1)
541 542 (msecscount t 0 0)
542 543 (settimer nil 1 1))
543 544
544 545 ;;; Expression
545 546
546 547 (p:defrule expression or-expr)
547 548
548 549 (p:defrule or-expr (and and-expr (* (and spaces? or-op spaces? and-expr)))
549 550 (:function do-binop))
550 551
551 552 (p:defrule and-expr (and eq-expr (* (and spaces? and-op spaces? eq-expr)))
552 553 (:function do-binop))
553 554
554 555 (p:defrule eq-expr (and sum-expr (* (and spaces? (or "<=" ">=" "=<" "=>" "<>"
555 556 "=" "<" ">" "!")
556 557 spaces? sum-expr)))
557 558 (:function do-binop))
558 559
559 560 (p:defrule sum-expr (and mod-expr (* (and spaces? (or #\+ #\-) spaces? mod-expr)))
560 561 (:function do-binop))
561 562
562 563 (p:defrule mod-expr (and mul-expr (* (and spaces? (p:~ "mod") spaces? mul-expr)))
563 564 (:function do-binop))
564 565
565 566 (p:defrule mul-expr (and unary-expr (* (and spaces? (or #\* #\/) spaces? unary-expr)))
566 567 (:function do-binop))
567 568
568 569 (p:defrule unary-expr (and (p:? (or #\+ #\-)) atom-expr)
569 570 (:lambda (list)
570 571 (let ((expr (remove-nil list)))
571 572 (if (= 1 (length expr))
572 573 (first expr)
573 574 (intern-first expr)))))
574 575
575 576 (p:defrule atom-expr (and (or variable literal returning-intrinsic paren-expr) spaces?)
576 577 (:function first))
577 578
578 579 (p:defrule paren-expr (and #\( spaces? expression spaces? #\))
579 580 (:function third))
580 581
581 582 (p:defrule or-op (p:~ "or")
582 583 (:constant "or"))
583 584
584 585 (p:defrule and-op (p:~ "and")
585 586 (:constant "and"))
586 587
587 588 ;;; Variables
588 589
589 590 (p:defrule variable (and identifier (p:? array-index))
590 591 (:destructure (id idx-raw)
591 592 (let ((idx (case idx-raw
592 593 ((nil) 0)
593 594 (:last nil)
594 595 (t idx-raw))))
595 596 (list 'lib:qspvar id idx))))
596 597
597 598 (p:defrule array-index (and #\[ spaces? (p:? expression) spaces? #\])
598 599 (:lambda (list)
599 600 (or (third list) :last)))
600 601
601 602 (p:defrule assignment (or kw-assignment plain-assignment op-assignment)
602 603 (:destructure (qspvar eq expr)
603 604 (declare (ignore eq))
604 605 (list 'lib:set qspvar expr)))
605 606
606 607 (p:defrule kw-assignment (and (or (p:~ "let") (p:~ "set")) spaces? (or op-assignment plain-assignment))
607 608 (:function third))
608 609
609 610 (p:defrule op-assignment (and variable spaces? (or "+" "-" "*" "/") #\= spaces? expression)
610 611 (:destructure (qspvar ws1 op eq ws2 expr)
611 612 (declare (ignore ws1 ws2))
612 613 (list qspvar eq (intern-first (list op qspvar expr)))))
613 614
614 615 (p:defrule plain-assignment (and variable spaces? #\= spaces? expression)
615 616 (:function remove-nil))
616 617
617 618 ;;; Non-string literals
618 619
619 620 (p:defrule literal (or qsp-string brace-string number))
620 621
621 622 (p:defrule number (+ (or #\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9))
622 623 (:lambda (list)
623 624 (parse-integer (p:text list))))
General Comments 0
You need to be logged in to leave comments. Login now