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