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