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