##// END OF EJS Templates
API call for FOR loop to make the main code less cluttered
naryl -
r19:c40f6d7d default
parent child Browse files
Show More
@@ -1,348 +1,359 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 24 (defm (root api init-dom) ()
25 25 ;; Save/load buttons
26 26 (let ((btn (document.get-element-by-id "qsp-btn-save")))
27 27 (setf (ps:@ btn onclick) this.savegame)
28 28 (setf (ps:@ btn href) "#"))
29 29 (let ((btn (document.get-element-by-id "qsp-btn-open")))
30 30 (setf (ps:@ btn onclick) this.opengame)
31 31 (setf (ps:@ btn href) "#"))
32 32 ;; Close image on click
33 33 (setf (ps:@ (document.get-element-by-id "qsp-image-container") onclick)
34 34 (this.show-image nil))
35 35 ;; Close the dropdown on any click
36 36 (setf window.onclick
37 37 (lambda (event)
38 38 (setf (ps:@ (api-call get-frame :dropdown) style display) "none"))))
39 39
40 40 ;; To be used in saving game
41 41 (defm (root api stash-state) (args)
42 42 (setf (root state-stash)
43 43 (*j-s-o-n.stringify
44 44 (ps:create vars (root vars)
45 45 objs (root objs)
46 46 loc-args args
47 47 main-html (ps:@
48 48 (document.get-element-by-id :qsp-main)
49 49 inner-h-t-m-l)
50 50 stat-html (ps:@
51 51 (document.get-element-by-id :qsp-stat)
52 52 inner-h-t-m-l)
53 53 next-location (root current-location))))
54 54 (values))
55 55
56 56 (defm (root api unstash-state) ()
57 57 (let ((data (*j-s-o-n.parse (root state-stash))))
58 58 (this.clear-act)
59 59 (setf (root vars) (ps:@ data vars))
60 60 (loop :for k :in (*object.keys (root vars))
61 61 :do (*object.set-prototype-of (ps:getprop (root vars) k)
62 62 (root api *var prototype)))
63 63 (setf (root objs) (ps:@ data objs))
64 64 (setf (root current-location) (ps:@ data next-location))
65 65 (setf (ps:@ (document.get-element-by-id :qsp-main) inner-h-t-m-l)
66 66 (ps:@ data main-html))
67 67 (setf (ps:@ (document.get-element-by-id :qsp-stat) inner-h-t-m-l)
68 68 (ps:@ data stat-html))
69 69 (funcall (root locs (root current-location)) (ps:@ data loc-args))
70 70 (this.update-objs)
71 71 (values)))
72 72
73 73 (defm (root api state-to-base64) ()
74 74 (btoa (encode-u-r-i-component (root state-stash))))
75 75
76 76 (defm (root api base64-to-state) (data)
77 77 (setf (root state-stash) (decode-u-r-i-component (atob data))))
78 78
79 79 ;;; Misc
80 80
81 81 (defm (root api clear-id) (id)
82 82 (setf (ps:chain document (get-element-by-id id) inner-text) ""))
83 83
84 84 (defm (root api get-id) (id)
85 85 (if (var "USEHTML" 0 :num)
86 86 (ps:chain (document.get-element-by-id id) inner-h-t-m-l)
87 87 (ps:chain (document.get-element-by-id id) inner-text)))
88 88
89 89 (defm (root api set-id) (id contents)
90 90 (if (var "USEHTML" 0 :num)
91 91 (setf (ps:chain (document.get-element-by-id id) inner-h-t-m-l) contents)
92 92 (setf (ps:chain (document.get-element-by-id id) inner-text) contents)))
93 93
94 94 (defm (root api append-id) (id contents)
95 95 (if (var "USEHTML" 0 :num)
96 96 (incf (ps:chain (document.get-element-by-id id) inner-h-t-m-l) contents)
97 97 (incf (ps:chain (document.get-element-by-id id) inner-text) contents)))
98 98
99 99 ;;; Function calls
100 100
101 101 (defm (root api init-args) (args)
102 102 (dotimes (i (length args))
103 103 (let ((arg (elt args i)))
104 104 (if (numberp arg)
105 105 (this.set-var args i :num arg)
106 106 (this.set-var args i :str arg)))))
107 107
108 108 (defm (root api get-result) ()
109 109 (if (not (equal "" (var result 0 :str)))
110 110 (var result 0 :str)
111 111 (var result 0 :num)))
112 112
113 113 ;;; Text windows
114 114
115 115 (defm (root api key-to-id) (key)
116 116 (case key
117 117 (:main "qsp-main")
118 118 (:stat "qsp-stat")
119 119 (:objs "qsp-objs")
120 120 (:acts "qsp-acts")
121 121 (:input "qsp-input")
122 122 (:dropdown "qsp-dropdown")
123 123 (t (report-error "Internal error!"))))
124 124
125 125 (defm (root api get-frame) (key)
126 126 (document.get-element-by-id (this.key-to-id key)))
127 127
128 128 (defm (root api add-text) (key text)
129 129 (this.append-id (this.key-to-id key) text))
130 130
131 131 (defm (root api get-text) (key)
132 132 (this.get-id (this.key-to-id key)))
133 133
134 134 (defm (root api clear-text) (key)
135 135 (this.clear-id (this.key-to-id key)))
136 136
137 137 (defm (root api newline) (key)
138 138 (let ((div (this.get-frame key)))
139 139 (ps:chain div (append-child (document.create-element "br")))))
140 140
141 141 (defm (root api enable-frame) (key enable)
142 142 (let ((clss (ps:getprop (this.get-frame key) 'class-list)))
143 143 (setf clss.style.display (if enable "block" "none"))
144 144 (values)))
145 145
146 146 ;;; Actions
147 147
148 148 (defm (root api add-act) (title img act)
149 149 (setf (ps:getprop (root acts) title)
150 150 (ps:create :img img :act act))
151 151 (this.update-acts))
152 152
153 153 (defm (root api del-act) (title)
154 154 (delete (ps:getprop (root acts) title))
155 155 (this.update-acts))
156 156
157 157 (defm (root api clear-act) ()
158 158 (setf (root acts) (ps:create))
159 159 (this.clear-id "qsp-acts"))
160 160
161 161 (defm (root api update-acts) ()
162 162 (this.clear-id "qsp-acts")
163 163 (ps:for-in (title (root acts))
164 164 (let ((obj (ps:getprop (root acts) title)))
165 165 (this.append-id "qsp-acts"
166 166 (this.make-act-html title (ps:getprop obj :img))))))
167 167
168 ;;; "Syntax"
169
170 (defm (root api qspfor) (name index from to step body)
171 (block nil
172 (ps:for ((i from))
173 ((< i to))
174 ((incf i step))
175 (this.set-var name index :num i)
176 (unless (funcall body)
177 (return)))))
178
168 179 ;;; Variable class
169 180
170 181 (defm (root api *var) (name)
171 182 ;; From strings to numbers
172 183 (setf this.indexes (ps:create))
173 184 ;; From numbers to {num: 0, str: ""} objects
174 185 (setf this.values (list))
175 186 (values))
176 187
177 188 (defm (root api *var prototype new-value) ()
178 189 (ps:create :num 0 :str ""))
179 190
180 191 (defm (root api *var prototype index-num) (index)
181 192 (let ((num-index
182 193 (if (stringp index)
183 194 (if (in index this.indexes)
184 195 (ps:getprop this.indexes index)
185 196 (let ((n (length this.values)))
186 197 (setf (ps:getprop this.indexes index) n)
187 198 n))
188 199 index)))
189 200 (unless (in num-index this.values)
190 201 (setf (elt this.values num-index) (this.new-value)))
191 202 num-index))
192 203
193 204 (defm (root api *var prototype get) (index slot)
194 205 (unless (or index (= 0 index))
195 206 (setf index (1- (length this.values))))
196 207 (ps:getprop this.values (this.index-num index) slot))
197 208
198 209 (defm (root api *var prototype set) (index slot value)
199 210 (unless (or index (= 0 index))
200 211 (setf index (length store)))
201 212 (case slot
202 213 (:num (setf value (ps:chain *number (parse-int value))))
203 214 (:str (setf value (ps:chain value (to-string)))))
204 215 (setf (ps:getprop this.values (this.index-num index) slot) value)
205 216 (values))
206 217
207 218 (defm (root api *var prototype kill) (index)
208 219 (setf (elt this.values (this.index-num index)) (this.new-value)))
209 220
210 221 ;;; Variables
211 222
212 223 (defm (root api var-real-name) (name)
213 224 (if (= (ps:@ name 0) #\$)
214 225 (values (ps:chain name (substr 1)) :str)
215 226 (values name :num)))
216 227
217 228 (defm (root api ensure-var) (name)
218 229 (let ((store (this.var-ref name)))
219 230 (unless store
220 231 (setf store (ps:new (this.-var name)))
221 232 (setf (ps:getprop (root vars) name) store))
222 233 store))
223 234
224 235 (defm (root api var-ref) (name)
225 236 (let ((local-store (this.current-local-frame)))
226 237 (cond ((in name local-store)
227 238 (ps:getprop local-store name))
228 239 ((in name (root vars))
229 240 (ps:getprop (root vars) name))
230 241 (t nil))))
231 242
232 243 (defm (root api get-var) (name index slot)
233 244 (ps:chain (this.ensure-var name) (get index slot)))
234 245
235 246 (defm (root api set-var) (name index slot value)
236 247 (ps:chain (this.ensure-var name) (set index slot value))
237 248 (values))
238 249
239 250 (defm (root api get-array) (name)
240 251 (this.var-ref name))
241 252
242 253 (defm (root api set-array) (name value)
243 254 (let ((store (this.var-ref name)))
244 255 (setf (ps:@ store values) (ps:@ value values))
245 256 (setf (ps:@ store indexes) (ps:@ value indexes)))
246 257 (values))
247 258
248 259 (defm (root api kill-var) (name &optional index)
249 260 (if (and index (not (= 0 index)))
250 261 (ps:chain (ps:getprop (root vars) name) (kill index))
251 262 (ps:delete (ps:getprop (root vars) name)))
252 263 (values))
253 264
254 265 (defm (root api array-size) (name)
255 266 (ps:getprop (this.var-ref name) 'length))
256 267
257 268 ;;; Locals
258 269
259 270 (defm (root api push-local-frame) ()
260 271 (ps:chain (root locals) (push (ps:create)))
261 272 (values))
262 273
263 274 (defm (root api pop-local-frame) ()
264 275 (ps:chain (root locals) (pop))
265 276 (values))
266 277
267 278 (defm (root api current-local-frame) ()
268 279 (elt (root locals) (1- (length (root locals)))))
269 280
270 281 (defm (root api new-local) (name)
271 282 (let ((frame (this.current-local-frame)))
272 283 (unless (in name frame)
273 284 (setf (ps:getprop frame name) (ps:create)))
274 285 (values)))
275 286
276 287 ;;; Objects
277 288
278 289 (defm (root api update-objs) ()
279 290 (let ((elt (document.get-element-by-id "qsp-objs")))
280 291 (setf elt.inner-h-t-m-l "<ul>")
281 292 (loop :for obj :in (root objs)
282 293 :do (incf elt.inner-h-t-m-l (+ "<li>" obj)))
283 294 (incf elt.inner-h-t-m-l "</ul>")))
284 295
285 296 ;;; Menu
286 297
287 298 (defm (root api menu) (menu-data)
288 299 (let ((elt (document.get-element-by-id "qsp-dropdown"))
289 300 (i 0))
290 301 (setf elt.inner-h-t-m-l "")
291 302 (loop :for item :in menu-data
292 303 :do (incf i)
293 304 :do (incf elt.inner-h-t-m-l (this.make-menu-item-html i item.text item.icon item.loc)))
294 305 (setf elt.style.display "block")))
295 306
296 307 ;;; Content
297 308
298 309 (defm (root api clean-audio) ()
299 310 (loop :for k :in (*object.keys (root playing))
300 311 :for v := (ps:getprop (root playing) k)
301 312 :do (when (ps:@ v ended)
302 313 (ps:delete (ps:@ (root playing) k)))))
303 314
304 315 (defm (root api show-image) (path)
305 316 (let ((img (document.get-element-by-id "qsp-image")))
306 317 (cond (path
307 318 (setf img.src path)
308 319 (setf img.style.display "flex"))
309 320 (t
310 321 (setf img.src "")
311 322 (setf img.style.display "hidden")))))
312 323
313 324 ;;; Saves
314 325
315 326 (defm (root api opengame) ()
316 327 (let ((element (document.create-element :input)))
317 328 (element.set-attribute :type :file)
318 329 (element.set-attribute :id :qsp-opengame)
319 330 (element.set-attribute :tabindex -1)
320 331 (element.set-attribute "aria-hidden" t)
321 332 (setf element.style.display :block)
322 333 (setf element.style.visibility :hidden)
323 334 (setf element.style.position :fixed)
324 335 (setf element.onchange
325 336 (lambda (event)
326 337 (let* ((file (elt event.target.files 0))
327 338 (reader (ps:new (*file-reader))))
328 339 (setf reader.onload
329 340 (lambda (ev)
330 341 (block nil
331 342 (let ((target ev.current-target))
332 343 (unless target.result
333 344 (return))
334 345 (api-call base64-to-state target.result)
335 346 (api-call unstash-state)))))
336 347 (reader.read-as-text file))))
337 348 (document.body.append-child element)
338 349 (element.click)
339 350 (document.body.remove-child element)))
340 351
341 352 (defm (root api savegame) ()
342 353 (let ((element (document.create-element :a)))
343 354 (element.set-attribute :href (+ "data:text/plain;charset=utf-8," (api-call state-to-base64)))
344 355 (element.set-attribute :download "savegame.sav")
345 356 (setf element.style.display :none)
346 357 (document.body.append-child element)
347 358 (element.click)
348 359 (document.body.remove-child element)))
@@ -1,203 +1,204 b''
1 1
2 2 (in-package sugar-qsp)
3 3
4 4 ;;;; Parenscript macros which make the parser's intermediate
5 5 ;;;; representation directly compilable by Parenscript
6 6 ;;;; Some utility macros for other .ps sources too.
7 7
8 8 ;;; Utils
9 9
10 10 (ps:defpsmacro defm (path args &body body)
11 11 `(setf ,path (lambda ,args ,@body)))
12 12
13 13 (ps:defpsmacro root (&rest path)
14 14 `(ps:@ *sugar-q-s-p ,@path))
15 15
16 16 (ps:defpsmacro in (key obj)
17 17 `(ps:chain ,obj (has-own-property ,key)))
18 18
19 19 (ps:defpsmacro with-frame (&body body)
20 20 `(progn
21 21 (api-call push-local-frame)
22 22 (unwind-protect
23 23 ,@body
24 24 (api-call pop-local-frame))))
25 25
26 26 ;;; Common
27 27
28 28 (defmacro defpsintrinsic (name)
29 29 `(ps:defpsmacro ,name (&rest args)
30 30 `(funcall (root lib ,',name)
31 31 ,@args)))
32 32
33 33 (defmacro defpsintrinsics (() &rest names)
34 34 `(progn ,@(loop :for name :in names
35 35 :collect `(defpsintrinsic ,name))))
36 36
37 37 (defpsintrinsics ()
38 38 rand copyarr arrpos arrcomp instr isnum strcomp strfind strpos iif gosub func dynamic dyneval main-p main-pl main-nl maintxt main-clear stat-p stat-pl stat-nl stattxt stat-clear cls curacts addobj delobj killobj menu play isplay close closeall refint view rgb showinput usertxt cmdclear input openqst addqst killqst opengame savegame wait msecscount settimer)
39 39
40 40 (ps:defpsmacro api-call (func &rest args)
41 41 `(funcall (root api ,func) ,@args))
42 42
43 43 (ps:defpsmacro label-block ((&key (locals t)) &body body)
44 44 (let ((has-labels (some #'keywordp body)))
45 45 `(block nil
46 46 ,@(when has-labels
47 47 '((defvar __labels)))
48 48 ,@(if locals
49 49 `((with-frame
50 50 (tagbody
51 51 ,@body)))
52 52 `((tagbody
53 53 ,@body))))))
54 54
55 55 (ps:defpsmacro str (&rest forms)
56 56 (cond ((zerop (length forms))
57 57 "")
58 58 ((and (= 1 (length forms))
59 59 (stringp (first forms)))
60 60 (first forms))
61 61 (t
62 62 `(& ,@forms))))
63 63
64 64 ;;; 1loc
65 65
66 66 (ps:defpsmacro location ((name) &body body)
67 67 `(setf (root locs ,name)
68 68 (lambda (args)
69 69 (label-block ()
70 70 (api-call init-args args)
71 71 ,@body
72 72 (api-call get-result)))))
73 73
74 74 (ps:defpsmacro goto (target &rest args)
75 75 `(progn
76 76 (funcall (root lib goto) ,target ,args)
77 77 (exit)))
78 78
79 79 (ps:defpsmacro xgoto (target &rest args)
80 80 `(progn
81 81 (funcall (root lib xgoto) ,target ,args)
82 82 (exit)))
83 83
84 84 (ps:defpsmacro desc (target)
85 85 (declare (ignore target))
86 86 (report-error "DESC is not supported"))
87 87
88 88 ;;; 2var
89 89
90 90 (ps:defpsmacro var (name index slot)
91 91 `(api-call get-var ,(string name) ,index ,slot))
92 92
93 93 (ps:defpsmacro set ((var vname vindex vslot) value)
94 94 (assert (eq var 'var))
95 95 `(api-call set-var ,(string vname) ,vindex ,vslot ,value))
96 96
97 97 ;;; 3expr
98 98
99 99 (ps:defpsmacro <> (op1 op2)
100 100 `(not (equal ,op1 ,op2)))
101 101
102 102 (ps:defpsmacro ! (op1 op2)
103 103 `(not (equal ,op1 ,op2)))
104 104
105 105 ;;; 4code
106 106
107 107 (ps:defpsmacro exec (&body body)
108 108 (format nil "javascript: ~{~A~^~%~}" (mapcar #'ps:ps* body)))
109 109
110 110 ;;; 5arrays
111 111
112 112 ;;; 6str
113 113
114 114 (ps:defpsmacro & (&rest args)
115 115 `(ps:chain "" (concat ,@args)))
116 116
117 117 ;;; 7if
118 118
119 119 (ps:defpsmacro qspcond (&rest clauses)
120 120 `(cond ,@(loop :for clause :in clauses
121 121 :collect (list (first clause)
122 122 `(tagbody
123 123 ,@(rest clause))))))
124 124
125 125 ;;; 8sub
126 126
127 127 ;;; 9loops
128 128 ;;; Yep, that's a huge kludge since Javascript doesn't support arbitrary labels
129 129
130 130 (ps:defpsmacro jump (target)
131 131 `(return-from ,(intern (string-upcase (second target)))
132 132 (funcall (ps:getprop __labels ,target))))
133 133
134 134 (ps:defpsmacro tagbody (&body body)
135 135 (let ((funcs (list nil :__nil)))
136 136 (dolist (form body)
137 137 (cond ((keywordp form)
138 138 (setf (first funcs) (reverse (first funcs)))
139 139 (push form funcs)
140 140 (push nil funcs))
141 141 (t
142 142 (push form (first funcs)))))
143 143 (setf (first funcs) (reverse (first funcs)))
144 144 (setf funcs (reverse funcs))
145 145 (if (= 2 (length funcs))
146 146 `(progn
147 147 ,@body)
148 148 `(progn
149 149 (setf ,@(loop :for f :on funcs :by #'cddr
150 150 :append `((ps:@ __labels ,(first f))
151 151 (block ,(intern (string-upcase (string (first f))))
152 152 ,@(second f)
153 153 ,@(when (third f)
154 154 `((funcall
155 155 (ps:getprop __labels ,(third f)))))))))
156 156 (jump (str "__nil"))))))
157 157
158 158 ;;; 10dynamic
159 159
160 160 (ps:defpsmacro qspblock (&body body)
161 161 `(lambda (args)
162 162 (label-block ()
163 163 (api-call init-args args)
164 164 ,@body
165 165 (api-call get-result))))
166 166
167 167 ;;; 11main
168 168
169 169 (ps:defpsmacro act (name img &body body)
170 170 `(api-call add-act ,name ,img
171 171 (lambda ()
172 172 (label-block ()
173 173 ,@body))))
174 174
175 175 ;;; 12aux
176 176
177 177 ;;; 13diag
178 178
179 179 ;;; 14act
180 180
181 181 ;;; 15objs
182 182
183 183 ;;; 16menu
184 184
185 185 ;;; 17sound
186 186
187 187 ;;; 18img
188 188
189 189 ;;; 19input
190 190
191 191 ;;; 20time
192 192
193 193 ;;; 21local
194 194
195 195 ;;; 22for
196 196
197 197 (ps:defpsmacro qspfor (var from to step &body body)
198 `(block nil
199 (set ,var ,from)
200 (ps:for ()
201 ((< ,var ,to))
202 ((set ,var (+ ,var ,step)))
203 ,@body)))
198 `(api-call qspfor
199 ,(string (second var)) ,(third var) ;; name and index
200 ,from ,to ,step
201 (lambda ()
202 (block nil
203 ,@body
204 t))))
General Comments 0
You need to be logged in to leave comments. Login now