Show More
@@ -1,14 +1,17 b'' | |||
|
1 | ||
|
2 | * @ for literal strings | |
|
3 | * LOOP | |
|
1 | 4 | |
|
2 | 5 | * Save-load game in slots |
|
3 | 6 | |
|
4 | 7 | * Reporting error lines in the parser |
|
5 | 8 | * Report duplicate label (in the parser) |
|
6 | 9 | * reporting error lines at runtime (by storing them in every form in the parser |
|
7 | 10 | * Report JUMP with missing label (in tagbody) |
|
8 | 11 | * Localizing parser errors... |
|
9 | 12 | |
|
10 | 13 | * Build Istreblenie |
|
11 | 14 | * Build Цветохимия |
|
12 | 15 | |
|
13 | 16 | * Windows GUI (for the compiler) |
|
14 | 17 | * Resizable frames |
This diff has been collapsed as it changes many lines, (1156 lines changed) Show them Hide them | |||
@@ -1,1134 +1,28 b'' | |||
|
1 | <!DOCTYPE html> | |
|
2 | <html><head><title>txt2web</title></head><body> | |
|
3 | <div id="qsp"> | |
|
4 | <div class="qsp-col qsp-col1"> | |
|
5 | <div id="qsp-main" class="qsp-frame">‌</div> | |
|
6 | <div id="qsp-acts" class="qsp-frame">‌</div> | |
|
7 | <input id="qsp-input" class="qsp-frame"> | |
|
8 | </div> | |
|
9 | <div class="qsp-col qsp-col2"> | |
|
10 | <div id="qsp-stat" class="qsp-frame">‌</div> | |
|
11 | <div id="qsp-objs" class="qsp-frame">‌</div> | |
|
12 | </div> | |
|
13 | <div class="qsp-col qsp-col3"> | |
|
14 | <a id="qsp-btn-save"><img></a> | |
|
15 | <a id="qsp-btn-open"><img></a> | |
|
16 | </div> | |
|
17 | </div> | |
|
18 | 1 | |
|
19 | <div id="qsp-dropdown"> | |
|
20 | </div> | |
|
21 | ||
|
22 | <div id="qsp-image-container" class="center-on-screen"> | |
|
23 | <img id="qsp-image"> | |
|
24 | </div> | |
|
25 | ||
|
26 | <style id="qsp-style"> | |
|
27 | </style> | |
|
28 | <style> | |
|
29 | .qsp-frame { | |
|
30 | border: 1px solid black; | |
|
31 | overflow: auto; | |
|
32 | padding: 5px; | |
|
33 | box-sizing: border-box; | |
|
34 | } | |
|
35 | ||
|
36 | #qsp { | |
|
37 | position: absolute; | |
|
38 | display: flex; | |
|
39 | flex-flow: row; | |
|
40 | top: 0; | |
|
41 | left: 0; | |
|
42 | width: 100%; | |
|
43 | height: 100%; | |
|
44 | } | |
|
45 | ||
|
46 | .qsp-col { | |
|
47 | display: flex; | |
|
48 | flex-flow: column; | |
|
49 | } | |
|
50 | ||
|
51 | .qsp-col1 { | |
|
52 | flex: 7 7 70px; | |
|
53 | } | |
|
54 | ||
|
55 | .qsp-col2 { | |
|
56 | flex: 3 3 30px; | |
|
57 | } | |
|
58 | ||
|
59 | .qsp-col3 { | |
|
60 | flex: 0 0 40px; | |
|
61 | } | |
|
62 | ||
|
63 | #qsp-main { | |
|
64 | flex: 6 6 60px; | |
|
65 | background-repeat: no-repeat; | |
|
66 | background-position: right top; | |
|
67 | background-attachment: fixed; | |
|
68 | } | |
|
69 | ||
|
70 | #qsp-acts { | |
|
71 | flex: 4 4 40px; | |
|
72 | } | |
|
73 | ||
|
74 | #qsp-input { | |
|
75 | } | |
|
76 | ||
|
77 | #qsp-stat { | |
|
78 | flex: 5 5 50px; | |
|
79 | } | |
|
80 | ||
|
81 | #qsp-objs { | |
|
82 | flex: 5 5 50px; | |
|
83 | } | |
|
84 | ||
|
85 | .qsp-act { | |
|
86 | display: block; | |
|
87 | padding: 2px; | |
|
88 | font-size: large; | |
|
89 | } | |
|
90 | ||
|
91 | .qsp-act:hover { | |
|
92 | outline: #9E9E9E outset 3px | |
|
93 | } | |
|
94 | ||
|
95 | /* Dropdown */ | |
|
96 | ||
|
97 | #qsp-dropdown { | |
|
98 | display: none; | |
|
99 | position: absolute; | |
|
100 | background-color: #f1f1f1; | |
|
101 | min-width: 160px; | |
|
102 | overflow: auto; | |
|
103 | box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2); | |
|
104 | z-index: 1; | |
|
105 | margin: auto; | |
|
106 | } | |
|
107 | ||
|
108 | #qsp-dropdown a { | |
|
109 | color: black; | |
|
110 | padding: 12px 16px; | |
|
111 | text-decoration: none; | |
|
112 | display: block; | |
|
113 | } | |
|
114 | ||
|
115 | #qsp-dropdown a:hover { | |
|
116 | background-color: #ddd; | |
|
117 | } | |
|
118 | ||
|
119 | /* Buttons */ | |
|
120 | ||
|
121 | .qsp-col3 a, .qsp-col3 img { | |
|
122 | width: 50px; | |
|
123 | height: 50px; | |
|
124 | } | |
|
125 | ||
|
126 | #qsp-btn-save img { | |
|
127 | background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAYAAAAeP4ixAAAABmJLR0QA/wD/AP+gvaeTAAACSklEQVRoge3ZPWgUQRjG8V/iRRFU/GoEQdSohSgWWqQSooVgK9iJFiKCnYV2BkFQsBRBQQwWVsHKIoWxEOzEwmhhogbRMhhFicTvYm+JCUlmZzabRLN/OPaWeZ+ZeW7feXf2lpqampqamnRaImKP4hA2VjCP27hbpoNGwbib2IPreB+IPYY7M7SvxWYM4QNO4QqWyQxVRgdeYnnB+K5A+wUcbB7z+O14hxPx08toLRDTgfv4mjrIJFrwwMS0HsABXMTxlE6LpNYqfE7pPJIBdOKhLM1uxIiLrpG5YtC4GSLMVGGkLdD+GpfxAuvxaVL7oCzN+jCG7iKDVmHkKS5hSSBuJ3bg6hRt+Zrpa553z8bEuoQrUVXk1exwKLBI1ZpPBnAL+0KBqanVim1Ykaifji+yNfIrVphipB1n8BgjCfqZ2IrTuIZXMcIUIydxFj8TtEW4JysW52JEKWtkVHUm4EdzjChSrkgLlmJXghb6m8ep9MN4K25XjvTF3sCaElrT6McS+0w2Mirb+JWhrH4CC/0+UpjUK7Ia5/86f4IeHMHeEvPJ+4km1chHE43k9KROpCyLPrViym8/vjW/b5Jt3acjL7/RzEX5bRg3sjKg+2fK7/PmZ9aZzdTqxwYzp06IBZFaDeHUCbFgUquy1Anx35TfRWXkd+WzCBOcQxEjQ7JH0PmiHW9CQUWM9Mr+Y+osO6ME8nF7Q4FFn8T2y14VDMt2vsFfqCRbZBvTdbLXFI9CgphHyjbsVu4+EcMInuH7HI1XU1NTU7OI+QMFe2N82rtssgAAAABJRU5ErkJggg=='); | |
|
128 | } | |
|
129 | ||
|
130 | #qsp-btn-open img { | |
|
131 | background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAYAAAAeP4ixAAAABmJLR0QA/wD/AP+gvaeTAAACCklEQVRoge3Yz6sNYRzH8deVexNxSaEoroWFUuomOceSPVnIwsbSxs6Psrgphf9AsrZR7kJWsroUlrZKKZRSl4USGouZk9Npzp1n5pm5x4/nXdNzpvP9Pp/v95nv82OGRCKRSCQS/w8zuIn3yGpe37CEQ6sedQk38BA7G/jO4BQ+YEebQZUxVfH/OxzGVSxX2D4o2mO4jmt4jpNFe6d5mPFkRbvQ0H9h6OqUtR33/xXr0celGn4/sYjXoQ5dJ3ILB7AOW2r4bcIj7GsrkEFpXWyrwwbaf15nXWqv6SqK1abuHDmPjV0EMobhBWIZt8cZVu0j2ZDNHF7gblRozdiO49g1zqDOEzmKJ7gcGVQTzmLDSgZ1Eunh2dD9aewpsXtctH3cwzn5ALysoVWlXZvhleMV5ovfU8J363mciAliRLsRg0Q244vfT3A/zgT2cQHbImIY1S4ldPk9Ii+NH8V9H08DfbfiY6BtiHYpoYmMBr4bbwN9YzfVoEELnew9+blpwBv5u0oI9wPtQrUbkWFaXqOzsZ01IFg7pLQOyp/A58igmhCsHZJInYndNsHa/0wiVWTy9/a5NjprQGvag0Qmwd462iGltdQ8lihqlVVIIn/9/CAvrajDWgTRB8VhMt1/aSljVr4RToc6hJTWioe1jujJD4rfQx2qRvuTyX1JuTIh3UQikUgkEhPnF+1xZ9hHnLjAAAAAAElFTkSuQmCC'); | |
|
132 | } | |
|
133 | ||
|
134 | .center-on-screen { | |
|
135 | position: absolute; | |
|
136 | top: 0; | |
|
137 | left: 0; | |
|
138 | height: 100%; | |
|
139 | width: 100%; | |
|
140 | pointer-events: none; | |
|
141 | display: flex; | |
|
142 | justify-content: center; | |
|
143 | align-items: center; | |
|
144 | } | |
|
145 | ||
|
146 | .center-on-screen > * { | |
|
147 | pointer-events: auto; | |
|
148 | } | |
|
149 | ||
|
150 | #qsp-image-container { | |
|
151 | display: none; | |
|
152 | } | |
|
153 | ||
|
154 | /* misc */ | |
|
155 | ||
|
156 | .disable a { | |
|
157 | pointer-events: none; | |
|
158 | cursor: default; | |
|
159 | } | |
|
160 | ||
|
161 | .qsp-objs li.qsp-obj-selected { | |
|
162 | background-color: blue; | |
|
163 | } | |
|
164 | </style><script>var qsp_Globals = { }; | |
|
165 | var qsp_Objs = { }; | |
|
166 | var qsp_CurrentLocation = null; | |
|
167 | var qsp_StartedAt = Date.now(); | |
|
168 | var qsp_TimerInterval = 500; | |
|
169 | var qsp_TimerObj = null; | |
|
170 | var qsp_LoadedGames = []; | |
|
171 | var qsp_Acts = { }; | |
|
172 | var qsp_StateStash = { }; | |
|
173 | var qsp_Playing = { }; | |
|
174 | var qsp_Locals = []; | |
|
175 | var qsp_MenuResume = null; | |
|
176 | var qsp_Games = []; | |
|
177 | var qsp_MainGame = null; | |
|
178 | var qsp_Locs = { }; | |
|
179 | window.onload = qsp_start; | |
|
180 | function qsp_start() { | |
|
181 | qsp_api_initDom(); | |
|
182 | qsp_StartedAt = Date.now(); | |
|
183 | qsp_api_setTimer(qsp_TimerInterval); | |
|
184 | qsp_api_runGame(Object.keys(qsp_Games)[0]); | |
|
185 | __PS_MV_REG = []; | |
|
186 | return; | |
|
187 | }; | |
|
188 | function qsp_byId(qsp_id) { | |
|
189 | return document.getElementById(qsp_id); | |
|
190 | }; | |
|
2 | # loops | |
|
3 | jump 'КонеЦ' | |
|
4 | p 'Это сообщение не будет выведено' | |
|
5 | :конец | |
|
6 | p 'А это сообщение пользователь увидит' | |
|
191 | 7 | |
|
192 | function qsp_api_makeActHtml(qsp_api_title, qsp_api_img) { | |
|
193 | return '<a class=\'qsp-act\' href=\'' + ('javascript:' + ('qsp_api_callAct' + '(\"' + qsp_api_title + '\");')) + '\' onmouseover=\'' + ('qsp_api_selectAct' + '(\"' + qsp_api_title + '\");') + '\'>' + (qsp_api_img ? '<img src=\'' + qsp_api_img + '\'>' : '') + qsp_api_title + '</a>'; | |
|
194 | }; | |
|
195 | function qsp_api_makeMenuItemHtml(qsp_api_num, qsp_api_title, qsp_api_img, qsp_api_loc) { | |
|
196 | return '<a href=\'' + ('javascript:' + ('qsp_api_finishMenu' + '(\"' + qsp_api_loc + '\");')) + '\'>' + (qsp_api_img ? '<img src=\'' + qsp_api_img + '\'>' : '') + qsp_api_title + '</a>'; | |
|
197 | }; | |
|
198 | function qsp_api_makeObj(qsp_api_title, qsp_api_img, qsp_api_selected) { | |
|
199 | return '<li onclick=\'' + ('qsp_api_selectObj' + '(\"' + qsp_api_title + '\", \"' + qsp_api_img + '\");') + '\'>' + '<a class=\'qsp-obj' + (qsp_api_selected ? ' qsp-obj-selected' : '') + '\'>' + (qsp_api_img ? '<img src=\'' + qsp_api_img + '\'>' : '') + qsp_api_title + '</a>'; | |
|
200 | }; | |
|
201 | function qsp_api_makeMenuDelimiter() { | |
|
202 | return '<hr>'; | |
|
203 | }; | |
|
204 | function qsp_api_copyObj(qsp_api_obj) { | |
|
205 | return JSON.parse(JSON.stringify(qsp_api_obj)); | |
|
206 | }; | |
|
207 | function qsp_api_reportError(qsp_api_text) { | |
|
208 | __PS_MV_REG = []; | |
|
209 | return alert(qsp_api_text); | |
|
210 | }; | |
|
211 | function qsp_api_startSleeping() { | |
|
212 | __PS_MV_REG = []; | |
|
213 | return qsp_byId('qsp').classList.add('disable'); | |
|
214 | }; | |
|
215 | function qsp_api_finishSleeping() { | |
|
216 | __PS_MV_REG = []; | |
|
217 | return qsp_byId('qsp').classList.remove('disable'); | |
|
218 | }; | |
|
219 | function sleep(qsp_api_msec) { | |
|
220 | __PS_MV_REG = []; | |
|
221 | return new Promise(function (qsp_api_resolve) { | |
|
222 | qsp_api_startSleeping(); | |
|
223 | var qsp_api_resume = function () { | |
|
224 | qsp_api_finishSleeping(); | |
|
225 | __PS_MV_REG = []; | |
|
226 | return qsp_api_resolve(); | |
|
227 | }; | |
|
228 | __PS_MV_REG = []; | |
|
229 | return setTimeout(qsp_api_resume, qsp_api_msec); | |
|
230 | }); | |
|
231 | }; | |
|
232 | function qsp_api_initDom() { | |
|
233 | var qsp_api_btn = qsp_byId('qsp-btn-save'); | |
|
234 | qsp_api_btn.onclick = qsp_api_savegame; | |
|
235 | qsp_api_btn.href = '#'; | |
|
236 | var btn1 = qsp_byId('qsp-btn-open'); | |
|
237 | btn1.onclick = qsp_api_opengame; | |
|
238 | btn1.href = '#'; | |
|
239 | qsp_byId('qsp-image-container').onclick = qsp_api_showImage; | |
|
240 | qsp_api_getFrame('input').qsp_api_onkeyup = qsp_api_onInputKey; | |
|
241 | __PS_MV_REG = []; | |
|
242 | return window.onclick = function (qsp_api_event) { | |
|
243 | window.qsp_api_mouse = [qsp_api_event.pageX, qsp_api_event.pageY]; | |
|
244 | __PS_MV_REG = []; | |
|
245 | return qsp_api_finishMenu(null); | |
|
246 | }; | |
|
247 | }; | |
|
248 | function qsp_api_callServLoc(qsp_api_varName) { | |
|
249 | var qsp_api_args = Array.prototype.slice.call(arguments, 1); | |
|
250 | var qsp_api_locName = qsp_api_getGlobal(qsp_api_varName, 0); | |
|
251 | if (qsp_api_locName) { | |
|
252 | var qsp_api_loc = qsp_Locs[qsp_api_locName]; | |
|
253 | __PS_MV_REG = []; | |
|
254 | return qsp_api_loc ? qsp_api_callLoc(qsp_api_locName, qsp_api_args) : null; | |
|
255 | }; | |
|
256 | }; | |
|
257 | function qsp_api_filenameGame(qsp_api_filename) { | |
|
258 | var qsp_api_gameName = qsp_api_filename.match('(.*/)?([^.]+)(\\.[a-zA-Z]+)?')[2]; | |
|
259 | return qsp_Games[qsp_api_gameName]; | |
|
260 | }; | |
|
261 | function qsp_api_runGame(qsp_api_name) { | |
|
262 | var qsp_api_game = qsp_api_filenameGame(qsp_api_name); | |
|
263 | qsp_MainGame = qsp_api_name; | |
|
264 | qsp_Locs = qsp_api_game; | |
|
265 | __PS_MV_REG = []; | |
|
266 | return qsp_api_game[Object.keys(qsp_api_game)[0]]([]); | |
|
267 | }; | |
|
268 | function qsp_api_newline(qsp_api_key) { | |
|
269 | __PS_MV_REG = []; | |
|
270 | return qsp_api_appendId(qsp_api_keyToId(qsp_api_key), '<br>', true); | |
|
271 | }; | |
|
272 | function qsp_api_clearId(qsp_api_id) { | |
|
273 | __PS_MV_REG = []; | |
|
274 | return qsp_byId(qsp_api_id).innerHTML = ''; | |
|
275 | }; | |
|
276 | function qsp_api_escapeHtml(qsp_api_text) { | |
|
277 | return qsp_api_text.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"').replace(/'/g, '''); | |
|
278 | }; | |
|
279 | function qsp_api_prepareContents(qsp_api_s, qsp_api_forceHtml) { | |
|
280 | qsp_api_s = qsp_api_s.toString(); | |
|
281 | __PS_MV_REG = []; | |
|
282 | return qsp_api_forceHtml || qsp_api_getGlobal('USEHTML', 0) ? qsp_api_s : qsp_api_escapeHtml(qsp_api_s); | |
|
283 | }; | |
|
284 | function qsp_api_getId(qsp_api_id, qsp_api_forceHtml) { | |
|
285 | __PS_MV_REG = []; | |
|
286 | return qsp_byId(qsp_api_id).innerHTML; | |
|
287 | }; | |
|
288 | function qsp_api_setId(qsp_api_id, qsp_api_contents, qsp_api_forceHtml) { | |
|
289 | __PS_MV_REG = []; | |
|
290 | return qsp_byId(qsp_api_id).innerHTML = qsp_api_prepareContents(qsp_api_contents, qsp_api_forceHtml); | |
|
291 | }; | |
|
292 | function qsp_api_appendId(qsp_api_id, qsp_api_contents, qsp_api_forceHtml) { | |
|
293 | __PS_MV_REG = []; | |
|
294 | return qsp_api_contents ? (qsp_byId(qsp_api_id).innerHTML += qsp_api_prepareContents(qsp_api_contents, qsp_api_forceHtml)) : null; | |
|
295 | }; | |
|
296 | function qsp_api_onInputKey(qsp_api_ev) { | |
|
297 | if (13 === qsp_api_ev.qsp_api_keyCode) { | |
|
298 | qsp_api_ev.qsp_api_preventDefault(); | |
|
299 | __PS_MV_REG = []; | |
|
300 | return qsp_api_callServLoc('$USERCOM'); | |
|
301 | }; | |
|
302 | }; | |
|
303 | function qsp_api_initArgs(qsp_api_args) { | |
|
304 | for (var qsp_api_i = 0; qsp_api_i < qsp_api_args.length; qsp_api_i += 1) { | |
|
305 | var qsp_api_arg = qsp_api_args[qsp_api_i]; | |
|
306 | if (typeof qsp_api_arg === 'number') { | |
|
307 | qsp_api_setVar(qsp_api_args, qsp_api_i, 'num', qsp_api_arg); | |
|
308 | } else { | |
|
309 | qsp_api_setVar(qsp_api_args, qsp_api_i, 'str', qsp_api_arg); | |
|
310 | }; | |
|
311 | }; | |
|
312 | }; | |
|
313 | function qsp_api_getResult() { | |
|
314 | __PS_MV_REG = []; | |
|
315 | return qsp_api_getGlobal('$RESULT', 0) || qsp_api_getGlobal('RESULT', 0); | |
|
316 | }; | |
|
317 | function qsp_api_callLoc(qsp_api_name, qsp_api_args) { | |
|
318 | qsp_api_name = qsp_api_name.toUpperCase(); | |
|
319 | qsp_api_pushLocalFrame(); | |
|
320 | try { | |
|
321 | qsp_api_initArgs(qsp_api_args); | |
|
322 | qsp_Locs[qsp_api_name](); | |
|
323 | qsp_api_getResult(); | |
|
324 | } finally { | |
|
325 | qsp_api_popLocalFrame(); | |
|
326 | }; | |
|
327 | }; | |
|
328 | function qsp_api_callAct(qsp_api_title) { | |
|
329 | qsp_api_pushLocalFrame(); | |
|
330 | try { | |
|
331 | qsp_Acts[qsp_api_title]['act'](); | |
|
332 | } finally { | |
|
333 | qsp_api_popLocalFrame(); | |
|
334 | }; | |
|
335 | }; | |
|
336 | function qsp_api_keyToId(qsp_api_key) { | |
|
337 | switch (qsp_api_key) { | |
|
338 | case 'all': | |
|
339 | return 'qsp'; | |
|
340 | case 'main': | |
|
341 | return 'qsp-main'; | |
|
342 | case 'stat': | |
|
343 | return 'qsp-stat'; | |
|
344 | case 'objs': | |
|
345 | return 'qsp-objs'; | |
|
346 | case 'acts': | |
|
347 | return 'qsp-acts'; | |
|
348 | case 'input': | |
|
349 | return 'qsp-input'; | |
|
350 | case 'image': | |
|
351 | return 'qsp-image'; | |
|
352 | case 'dropdown': | |
|
353 | return 'qsp-dropdown'; | |
|
354 | default: | |
|
355 | __PS_MV_REG = []; | |
|
356 | return qsp_api_reportError('Internal error!'); | |
|
357 | }; | |
|
358 | }; | |
|
359 | function qsp_api_getFrame(qsp_api_key) { | |
|
360 | __PS_MV_REG = []; | |
|
361 | return qsp_byId(qsp_api_keyToId(qsp_api_key)); | |
|
362 | }; | |
|
363 | function qsp_api_addText(qsp_api_key, qsp_api_text) { | |
|
364 | __PS_MV_REG = []; | |
|
365 | return qsp_api_appendId(qsp_api_keyToId(qsp_api_key), qsp_api_text); | |
|
366 | }; | |
|
367 | function qsp_api_getText(qsp_api_key) { | |
|
368 | __PS_MV_REG = []; | |
|
369 | return qsp_api_getId(qsp_api_keyToId(qsp_api_key)); | |
|
370 | }; | |
|
371 | function qsp_api_clearText(qsp_api_key) { | |
|
372 | __PS_MV_REG = []; | |
|
373 | return qsp_api_clearId(qsp_api_keyToId(qsp_api_key)); | |
|
374 | }; | |
|
375 | function qsp_api_enableFrame(qsp_api_key, qsp_api_enable) { | |
|
376 | var qsp_api_obj = qsp_api_getFrame(qsp_api_key); | |
|
377 | qsp_api_obj.style.display = qsp_api_enable ? 'block' : 'none'; | |
|
378 | }; | |
|
379 | function qsp_api_addAct(qsp_api_title, qsp_api_img, qsp_api_act) { | |
|
380 | qsp_Acts[qsp_api_title] = { 'title' : qsp_api_title, | |
|
381 | 'img' : qsp_api_img, | |
|
382 | 'act' : qsp_api_act, | |
|
383 | 'selected' : null | |
|
384 | }; | |
|
385 | __PS_MV_REG = []; | |
|
386 | return qsp_api_updateActs(); | |
|
387 | }; | |
|
388 | function qsp_api_delAct(qsp_api_title) { | |
|
389 | delete qsp_Acts[qsp_api_title]; | |
|
390 | __PS_MV_REG = []; | |
|
391 | return qsp_api_updateActs(); | |
|
392 | }; | |
|
393 | function qsp_api_clearAct() { | |
|
394 | qsp_Acts = { }; | |
|
395 | __PS_MV_REG = []; | |
|
396 | return qsp_api_updateActs(); | |
|
397 | }; | |
|
398 | function qsp_api_updateActs() { | |
|
399 | qsp_api_clearId('qsp-acts'); | |
|
400 | var elt = qsp_byId('qsp-acts'); | |
|
401 | for (var qsp_api_title in qsp_Acts) { | |
|
402 | var qsp_api_obj = qsp_Acts[qsp_api_title]; | |
|
403 | elt.innerHTML += qsp_api_makeActHtml(qsp_api_title, qsp_api_obj['img']); | |
|
404 | }; | |
|
405 | }; | |
|
406 | function qsp_api_selectAct(qsp_api_title) { | |
|
407 | for (var qsp_api_k in qsp_Acts) { | |
|
408 | var qsp_api_v = qsp_Acts[qsp_api_k]; | |
|
409 | qsp_api_v['selected'] = null; | |
|
410 | }; | |
|
411 | qsp_Acts[qsp_api_title]['selected'] = true; | |
|
412 | __PS_MV_REG = []; | |
|
413 | return qsp_api_callServLoc('$ONACTSEL'); | |
|
414 | }; | |
|
415 | function qsp_api_qspfor(qsp_api_name, qsp_api_index, qsp_api_from, qsp_api_to, step, body) { | |
|
416 | for (var qsp_api_i = qsp_api_from; qsp_api_i <= qsp_api_to; qsp_api_i += step) { | |
|
417 | qsp_api_setVar(qsp_api_name, qsp_api_index, 'num', qsp_api_i); | |
|
418 | if (!await (body())) { | |
|
419 | __PS_MV_REG = []; | |
|
420 | return; | |
|
421 | }; | |
|
422 | }; | |
|
423 | }; | |
|
424 | function qsp_api_newVar(qsp_api_slot) { | |
|
425 | var qsp_api_indexes = Array.prototype.slice.call(arguments, 1); | |
|
426 | var qsp_api_v = []; | |
|
427 | for (var qsp_api_index = null, _js_idx2 = 0; _js_idx2 < qsp_api_indexes.length; _js_idx2 += 1) { | |
|
428 | qsp_api_index = qsp_api_indexes[_js_idx2]; | |
|
429 | qsp_api_v[qsp_api_index] = '$' === qsp_api_slot[0] ? '' : 0; | |
|
430 | }; | |
|
431 | qsp_api_v['indexes'] = { }; | |
|
432 | __PS_MV_REG = []; | |
|
433 | return qsp_api_v; | |
|
434 | }; | |
|
435 | function qsp_api_setStrElement(qsp_api_slot, qsp_api_index, qsp_api_value) { | |
|
436 | if (qsp_api_slot['indexes'].hasOwnProperty(qsp_api_index)) { | |
|
437 | qsp_api_slot[null][qsp_api_slot['indexes'][qsp_api_index]] = qsp_api_value; | |
|
438 | } else { | |
|
439 | qsp_api_slot.push(qsp_api_value); | |
|
440 | qsp_api_slot[qsp_api_index] = qsp_api_slot.length; | |
|
441 | }; | |
|
442 | }; | |
|
443 | function qsp_api_setAnyElement(qsp_api_slot, qsp_api_index, qsp_api_value) { | |
|
444 | if (qsp_api_index == null) { | |
|
445 | qsp_api_slot.push(qsp_api_value); | |
|
446 | } else if (typeof qsp_api_index === 'number') { | |
|
447 | qsp_api_slot[qsp_api_index] = qsp_api_value; | |
|
448 | } else if (typeof qsp_api_index === 'string') { | |
|
449 | qsp_api_setStrElement(qsp_api_slot, qsp_api_index, qsp_api_value); | |
|
450 | } else { | |
|
451 | qsp_api_reportError('INTERNAL ERROR'); | |
|
452 | }; | |
|
453 | }; | |
|
454 | function qsp_api_setServVar(qsp_api_name, qsp_api_index, qsp_api_value) { | |
|
455 | var qsp_api_slot = qsp_Globals[qsp_api_name]; | |
|
456 | qsp_api_setAnyElement(qsp_api_slot, qsp_api_index, qsp_api_value); | |
|
457 | qsp_api_servVars[qsp_api_name]['body'](qsp_api_value, qsp_api_index); | |
|
458 | }; | |
|
459 | function qsp_api_getElement(qsp_api_slot, qsp_api_index) { | |
|
460 | return typeof qsp_api_index === 'number' ? qsp_api_slot[qsp_api_index] : qsp_api_slot[qsp_api_slot['indexes'][qsp_api_index]]; | |
|
461 | }; | |
|
462 | function qsp_api_getGlobal(qsp_api_name, qsp_api_index) { | |
|
463 | return qsp_Globals[qsp_api_name][qsp_api_index]; | |
|
464 | }; | |
|
465 | function qsp_api_killVar(qsp_api_store, qsp_api_name, qsp_api_index) { | |
|
466 | qsp_api_name = qsp_api_name.toUpperCase(); | |
|
467 | if (qsp_api_index && 0 !== qsp_api_index) { | |
|
468 | qsp_Globals[qsp_api_name].qsp_api_kill(qsp_api_index); | |
|
469 | } else { | |
|
470 | delete qsp_Globals[qsp_api_name]; | |
|
471 | }; | |
|
472 | }; | |
|
473 | function qsp_api_arraySize(qsp_api_name) { | |
|
474 | __PS_MV_REG = []; | |
|
475 | return qsp_api_varRef(qsp_api_name)['values'].length; | |
|
476 | }; | |
|
477 | function qsp_api_pushLocalFrame() { | |
|
478 | qsp_Locals.push({ }); | |
|
479 | }; | |
|
480 | function qsp_api_popLocalFrame() { | |
|
481 | qsp_Locals.pop(); | |
|
482 | }; | |
|
483 | function qsp_api_currentLocalFrame() { | |
|
484 | return qsp_Locals[qsp_Locals.length - 1]; | |
|
485 | }; | |
|
486 | function qsp_api_selectObj(qsp_api_title, qsp_api_img) { | |
|
487 | for (var qsp_api_k in qsp_Objs) { | |
|
488 | var qsp_api_v = qsp_Objs[qsp_api_k]; | |
|
489 | qsp_api_v['selected'] = null; | |
|
490 | }; | |
|
491 | qsp_Objs[qsp_api_title]['selected'] = true; | |
|
492 | __PS_MV_REG = []; | |
|
493 | return qsp_api_callServLoc('$ONOBJSEL', qsp_api_title, qsp_api_img); | |
|
494 | }; | |
|
495 | function qsp_api_updateObjs() { | |
|
496 | var elt = qsp_byId('qsp-objs'); | |
|
497 | elt.innerHTML = '<ul>'; | |
|
498 | for (var qsp_api_name in qsp_Objs) { | |
|
499 | var qsp_api_obj = qsp_Objs[qsp_api_name]; | |
|
500 | elt.innerHTML += qsp_api_makeObj(qsp_api_name, qsp_api_obj['img'], qsp_api_obj['selected']); | |
|
501 | }; | |
|
502 | __PS_MV_REG = []; | |
|
503 | return elt.innerHTML += '</ul>'; | |
|
504 | }; | |
|
505 | function qsp_api_openMenu(qsp_api_menuData) { | |
|
506 | var elt = qsp_api_getFrame('dropdown'); | |
|
507 | var qsp_api_i = 0; | |
|
508 | var _js4 = qsp_api_menuData.length; | |
|
509 | for (var _js3 = 0; _js3 < _js4; _js3 += 1) { | |
|
510 | var qsp_api_item = qsp_api_menuData[_js3]; | |
|
511 | ++qsp_api_i; | |
|
512 | elt.innerHTML += qsp_api_item === 'delimiter' ? qsp_api_makeMenuDelimiter(qsp_api_i) : qsp_api_makeMenuItemHtml(qsp_api_i, qsp_api_item['text'], qsp_api_item['icon'], qsp_api_item['loc']); | |
|
513 | }; | |
|
514 | var mouse5 = window.qsp_api_mouse; | |
|
515 | elt.style.left = mouse5[0] + 'px'; | |
|
516 | elt.style.top = mouse5[1] + 'px'; | |
|
517 | if (document.body.qsp_api_innerWidth > mouse5[0] + elt.qsp_api_innerWidth) { | |
|
518 | elt.style.left += elt.qsp_api_innerWidth; | |
|
519 | }; | |
|
520 | if (document.body.qsp_api_innerHeight > mouse5[0] + elt.qsp_api_innerHeight) { | |
|
521 | elt.style.top += elt.qsp_api_innerHeight; | |
|
522 | }; | |
|
523 | __PS_MV_REG = []; | |
|
524 | return elt.style.display = 'block'; | |
|
525 | }; | |
|
526 | function qsp_api_finishMenu(qsp_api_loc) { | |
|
527 | if (qsp_MenuResume) { | |
|
528 | var elt = qsp_api_getFrame('dropdown'); | |
|
529 | elt.innerHTML = ''; | |
|
530 | elt.style.display = 'none'; | |
|
531 | qsp_MenuResume(); | |
|
532 | qsp_MenuResume = null; | |
|
533 | if (qsp_api_loc) { | |
|
534 | qsp_api_callLoc(qsp_api_loc); | |
|
535 | }; | |
|
536 | }; | |
|
537 | }; | |
|
538 | function qsp_api_menu(qsp_api_menuData) { | |
|
539 | new Promise(function (qsp_api_resolve) { | |
|
540 | qsp_api_startSleeping(); | |
|
541 | var qsp_api_resume = function () { | |
|
542 | qsp_api_finishSleeping(); | |
|
543 | __PS_MV_REG = []; | |
|
544 | return qsp_api_resolve(); | |
|
545 | }; | |
|
546 | qsp_api_openMenu(qsp_api_menuData); | |
|
547 | __PS_MV_REG = []; | |
|
548 | return qsp_MenuResume = qsp_api_resume; | |
|
549 | }); | |
|
550 | }; | |
|
551 | function qsp_api_cleanAudio() { | |
|
552 | var _js6 = Object.keys(qsp_Playing); | |
|
553 | var _js8 = _js6.length; | |
|
554 | for (var _js7 = 0; _js7 < _js8; _js7 += 1) { | |
|
555 | var qsp_api_k = _js6[_js7]; | |
|
556 | var qsp_api_v = qsp_Playing[qsp_api_k]; | |
|
557 | if (qsp_api_v.qsp_api_ended) { | |
|
558 | delete qsp_Playing.qsp_api_k; | |
|
559 | }; | |
|
560 | }; | |
|
561 | }; | |
|
562 | function qsp_api_showImage(qsp_api_path) { | |
|
563 | var qsp_api_img = qsp_api_getFrame('image'); | |
|
564 | if (qsp_api_path) { | |
|
565 | qsp_api_img.src = qsp_api_path; | |
|
566 | __PS_MV_REG = []; | |
|
567 | return qsp_api_img.style.display = 'flex'; | |
|
568 | } else { | |
|
569 | qsp_api_img.src = ''; | |
|
570 | __PS_MV_REG = []; | |
|
571 | return qsp_api_img.style.display = 'hidden'; | |
|
572 | }; | |
|
573 | }; | |
|
574 | function qsp_api_showInlineImages(qsp_api_frameName, qsp_api_images) { | |
|
575 | var qsp_api_frame = qsp_api_getFrame(qsp_api_frameName); | |
|
576 | var qsp_api_text = ''; | |
|
577 | qsp_api_text += '<div style=\'position:relative; display: inline-block\'>'; | |
|
578 | qsp_api_text += '<img src=\'' + qsp_api_images[0] + '\'>'; | |
|
579 | var _js9 = qsp_api_images.slice(1); | |
|
580 | var _js11 = _js9.length; | |
|
581 | for (var _js10 = 0; _js10 < _js11; _js10 += 1) { | |
|
582 | var qsp_api_image = _js9[_js10]; | |
|
583 | qsp_api_text += '<img style=\'position:absolute\' src=\'' + qsp_api_image + '\'>'; | |
|
584 | }; | |
|
585 | qsp_api_text += '</div>'; | |
|
586 | __PS_MV_REG = []; | |
|
587 | return qsp_api_frame.innerHTML += qsp_api_text; | |
|
588 | }; | |
|
589 | function qsp_api_rgbString(qsp_api_rgb) { | |
|
590 | var qsp_api_red = qsp_api_rgb >> 16; | |
|
591 | var qsp_api_green = qsp_api_rgb >> 8 & 255; | |
|
592 | var qsp_api_blue = qsp_api_rgb & 255; | |
|
593 | var qsp_api_rgbToHex = function (qsp_api_comp) { | |
|
594 | var qsp_api_hex = Number(qsp_api_comp).toString(16); | |
|
595 | __PS_MV_REG = []; | |
|
596 | return qsp_api_hex.length < 2 ? '0' + qsp_api_hex : qsp_api_hex; | |
|
597 | }; | |
|
598 | __PS_MV_REG = []; | |
|
599 | return '#' + qsp_api_rgbToHex(qsp_api_red) + qsp_api_rgbToHex(qsp_api_green) + qsp_api_rgbToHex(qsp_api_blue); | |
|
600 | }; | |
|
601 | function qsp_api_storeObj(qsp_api_key, qsp_api_obj) { | |
|
602 | qsp_api_storeStr(qsp_api_key, btoa(encodeURIComponent(JSON.stringify(qsp_api_obj)))); | |
|
603 | }; | |
|
604 | function qsp_api_storeStr(qsp_api_key, qsp_api_str) { | |
|
605 | localStorage.setItem('qsp_' + qsp_api_key, qsp_api_str); | |
|
606 | }; | |
|
607 | function qsp_api_loadObj(qsp_api_key) { | |
|
608 | __PS_MV_REG = []; | |
|
609 | return JSON.parse(encodeURIComponent(atob(qsp_api_loadStr(qsp_api_key)))); | |
|
610 | }; | |
|
611 | function qsp_api_loadStr(qsp_api_key) { | |
|
612 | return localStorage.getItem('qsp_' + qsp_api_key); | |
|
613 | }; | |
|
614 | function qsp_api_slotSavegame(qsp_api_slot, qsp_api_comment) { | |
|
615 | var qsp_api_saves = qsp_api_loadObj('saves'); | |
|
616 | qsp_api_saves.qsp_api_slot = qsp_api_comment; | |
|
617 | qsp_api_storeObj(qsp_api_saves); | |
|
618 | qsp_api_storeStr(qsp_api_slot, qsp_api_stateToBase64()); | |
|
619 | }; | |
|
620 | function qsp_api_slotLoadgame(qsp_api_slot) { | |
|
621 | qsp_api_base64ToState(qsp_api_loadStr(qsp_api_slot)); | |
|
622 | }; | |
|
623 | function qsp_api_slotDeletegame(qsp_api_slot) { | |
|
624 | var qsp_api_saves = qsp_api_loadObj('saves'); | |
|
625 | qsp_api_saves.qsp_api_slot = undefined; | |
|
626 | qsp_api_storeObj(qsp_api_saves); | |
|
627 | qsp_api_storeStr(qsp_api_slot, undefined); | |
|
628 | }; | |
|
629 | function qsp_api_slotListgames() { | |
|
630 | __PS_MV_REG = []; | |
|
631 | return qsp_api_loadObj('saves'); | |
|
632 | }; | |
|
633 | function qsp_api_opengame() { | |
|
634 | var qsp_api_element = document.createElement('input'); | |
|
635 | qsp_api_element.setAttribute('type', 'file'); | |
|
636 | qsp_api_element.setAttribute('id', 'qsp-opengame'); | |
|
637 | qsp_api_element.setAttribute('tabindex', -1); | |
|
638 | qsp_api_element.setAttribute('aria-hidden', true); | |
|
639 | qsp_api_element.style.display = 'block'; | |
|
640 | qsp_api_element.style.qsp_api_visibility = 'hidden'; | |
|
641 | qsp_api_element.style.position = 'fixed'; | |
|
642 | qsp_api_element.onchange = function (qsp_api_event) { | |
|
643 | var qsp_api_file = qsp_api_event.target.files[0]; | |
|
644 | var qsp_api_reader = new FileReader(); | |
|
645 | qsp_api_reader.onload = function (qsp_api_ev) { | |
|
646 | var target = qsp_api_ev.currentTarget; | |
|
647 | if (!target.result) { | |
|
648 | return null; | |
|
649 | }; | |
|
650 | qsp_api_base64ToState(target.result); | |
|
651 | __PS_MV_REG = []; | |
|
652 | return qsp_api_unstashState(); | |
|
653 | }; | |
|
654 | __PS_MV_REG = []; | |
|
655 | return qsp_api_reader.readAsText(qsp_api_file); | |
|
656 | }; | |
|
657 | document.body.appendChild(qsp_api_element); | |
|
658 | qsp_api_element.click(); | |
|
659 | return document.body.removeChild(qsp_api_element); | |
|
660 | }; | |
|
661 | function qsp_api_savegame() { | |
|
662 | var qsp_api_element = document.createElement('a'); | |
|
663 | qsp_api_element.setAttribute('href', 'data:text/plain;charset=utf-8,' + qsp_api_stateToBase64()); | |
|
664 | qsp_api_element.setAttribute('download', 'savegame.sav'); | |
|
665 | qsp_api_element.style.display = 'none'; | |
|
666 | document.body.appendChild(qsp_api_element); | |
|
667 | qsp_api_element.click(); | |
|
668 | __PS_MV_REG = []; | |
|
669 | return document.body.removeChild(qsp_api_element); | |
|
670 | }; | |
|
671 | function qsp_api_stashState(qsp_api_args) { | |
|
672 | qsp_api_callServLoc('$ONGSAVE'); | |
|
673 | qsp_StateStash = JSON.stringify({ 'vars' : qsp_Globals, | |
|
674 | 'objs' : qsp_Objs, | |
|
675 | 'loc-args' : qsp_api_args, | |
|
676 | 'msecs' : Date.now() - qsp_StartedAt, | |
|
677 | 'timer-interval' : qsp_TimerInterval, | |
|
678 | 'main-html' : qsp_api_getFrame('main').innerHTML, | |
|
679 | 'stat-html' : qsp_api_getFrame('stat').innerHTML, | |
|
680 | 'next-location' : qsp_CurrentLocation | |
|
681 | }); | |
|
682 | }; | |
|
683 | function qsp_api_unstashState() { | |
|
684 | var qsp_api_data = JSON.parse(qsp_StateStash); | |
|
685 | qsp_api_clearAct(); | |
|
686 | qsp_Globals = qsp_api_data['vars']; | |
|
687 | var _js12 = Object.keys(qsp_Globals); | |
|
688 | var _js14 = _js12.length; | |
|
689 | for (var _js13 = 0; _js13 < _js14; _js13 += 1) { | |
|
690 | var qsp_api_k = _js12[_js13]; | |
|
691 | Object.setPrototypeOf(qsp_Globals[qsp_api_k], qsp_api_Var.prototype); | |
|
692 | }; | |
|
693 | qsp_StartedAt = Date.now() - qsp_api_data['msecs']; | |
|
694 | qsp_Objs = qsp_api_data['objs']; | |
|
695 | qsp_CurrentLocation = qsp_api_data['next-location']; | |
|
696 | qsp_api_getFrame('main').innerHTML = qsp_api_data['main-html']; | |
|
697 | qsp_api_getFrame('stat').innerHTML = qsp_api_data['stat-html']; | |
|
698 | qsp_api_updateObjs(); | |
|
699 | qsp_api_setTimer(qsp_api_data['timer-interval']); | |
|
700 | qsp_api_callServLoc('$ONGLOAD'); | |
|
701 | qsp_api_callLoc(qsp_CurrentLocation, qsp_api_data['loc-args']); | |
|
702 | }; | |
|
703 | function qsp_api_stateToBase64() { | |
|
704 | __PS_MV_REG = []; | |
|
705 | return btoa(encodeURIComponent(qsp_StateStash)); | |
|
706 | }; | |
|
707 | function qsp_api_base64ToState(qsp_api_data) { | |
|
708 | __PS_MV_REG = []; | |
|
709 | return qsp_StateStash = decodeURIComponent(atob(qsp_api_data)); | |
|
710 | }; | |
|
711 | function qsp_api_setTimer(qsp_api_interval) { | |
|
712 | qsp_TimerInterval = qsp_api_interval; | |
|
713 | clearInterval(qsp_TimerObj); | |
|
714 | __PS_MV_REG = []; | |
|
715 | return qsp_TimerObj = setInterval(function () { | |
|
716 | __PS_MV_REG = []; | |
|
717 | return qsp_api_callServLoc('$COUNTER'); | |
|
718 | }, qsp_api_interval); | |
|
719 | }; | |
|
720 | if ('undefined' === typeof qsp_api_servVars) { | |
|
721 | var qsp_api_servVars = { }; | |
|
722 | }; | |
|
723 | qsp_api_servVars['$BACKIMAGE'] = { 'name' : '$BACKIMAGE', 'body' : function (qsp_api_path) { | |
|
724 | __PS_MV_REG = []; | |
|
725 | return qsp_api_getFrame('main').style.backgroundImage = qsp_api_path; | |
|
726 | } }; | |
|
727 | qsp_api_servVars['BCOLOR'] = { 'name' : 'BCOLOR', 'body' : function (color) { | |
|
728 | __PS_MV_REG = []; | |
|
729 | return qsp_api_getFrame('all').style.backgroundColor = qsp_api_rgbString(color); | |
|
730 | } }; | |
|
731 | qsp_api_servVars['FCOLOR'] = { 'name' : 'FCOLOR', 'body' : function (color) { | |
|
732 | __PS_MV_REG = []; | |
|
733 | return qsp_api_getFrame('all').style.color = qsp_api_rgbString(color); | |
|
734 | } }; | |
|
735 | qsp_api_servVars['LCOLOR'] = { 'name' : 'LCOLOR', 'body' : function (color) { | |
|
736 | __PS_MV_REG = []; | |
|
737 | return qsp_api_getFrame('style').innerText = 'a { color: ' + qsp_api_rgbString(color) + ';}'; | |
|
738 | } }; | |
|
739 | qsp_api_servVars['FSIZE'] = { 'name' : 'FSIZE', 'body' : function (size) { | |
|
740 | __PS_MV_REG = []; | |
|
741 | return qsp_api_getFrame('all').style.fontSize = size; | |
|
742 | } }; | |
|
743 | qsp_api_servVars['$FNAME'] = { 'name' : '$FNAME', 'body' : function (fontName) { | |
|
744 | __PS_MV_REG = []; | |
|
745 | return qsp_api_getFrame('all').style.fontFamily = fontName + ',serif'; | |
|
746 | } }; | |
|
8 | s=0 | |
|
9 | :loop1 | |
|
10 | if s<9: | |
|
11 | s=s+1 | |
|
12 | pl s | |
|
13 | jump 'loop1' | |
|
14 | end | |
|
15 | p 'Всё!' | |
|
747 | 16 | |
|
748 | function qsp_lib_goto(target, qsp_lib_args) { | |
|
749 | qsp_api_clearText('main'); | |
|
750 | qsp_lib_xgoto(target, qsp_lib_args); | |
|
751 | }; | |
|
752 | function qsp_lib_xgoto(target, qsp_lib_args) { | |
|
753 | qsp_lib_args = qsp_lib_args || []; | |
|
754 | qsp_api_clearAct(); | |
|
755 | qsp_CurrentLocation = target.toUpperCase(); | |
|
756 | qsp_api_stashState(qsp_lib_args); | |
|
757 | qsp_api_callLoc(qsp_CurrentLocation, qsp_lib_args); | |
|
758 | qsp_api_callServLoc('$ONNEWLOC'); | |
|
759 | }; | |
|
760 | function qsp_lib_obj(qsp_lib_name) { | |
|
761 | return qsp_Objs.hasOwnProperty(qsp_lib_name); | |
|
762 | }; | |
|
763 | function qsp_lib_loc(qsp_lib_name) { | |
|
764 | return qsp_Locs.hasOwnProperty(qsp_lib_name); | |
|
765 | }; | |
|
766 | function qsp_lib_rand(qsp_lib_a, qsp_lib_b) { | |
|
767 | if (qsp_lib_b === undefined) { | |
|
768 | qsp_lib_b = 1; | |
|
769 | }; | |
|
770 | var min15 = Math.min(qsp_lib_a, qsp_lib_b); | |
|
771 | var max16 = Math.max(qsp_lib_a, qsp_lib_b); | |
|
772 | __PS_MV_REG = []; | |
|
773 | return min15 + qsp_lib_Math.random(max16 - min15); | |
|
774 | }; | |
|
775 | function qsp_lib_copyarr(qsp_lib_to, qsp_lib_from, qsp_lib_start, count) { | |
|
776 | __PS_MV_REG = []; | |
|
777 | var qsp_lib_toName = qsp_api_varRealName(qsp_lib_to); | |
|
778 | var qsp_lib_toSlot = __PS_MV_REG[0]; | |
|
779 | __PS_MV_REG = []; | |
|
780 | var qsp_lib_fromName = qsp_api_varRealName(qsp_lib_from); | |
|
781 | var qsp_lib_fromSlot = __PS_MV_REG[0]; | |
|
782 | var _js17 = Math.min(qsp_api_arraySize(qsp_lib_fromName), qsp_lib_start + count); | |
|
783 | for (var qsp_lib_i = qsp_lib_start; qsp_lib_i <= _js17; qsp_lib_i += 1) { | |
|
784 | qsp_api_setVar(qsp_lib_toName, qsp_lib_start + qsp_lib_i, qsp_lib_toSlot, qsp_api_getVar(qsp_lib_fromName, qsp_lib_start + qsp_lib_i, qsp_lib_fromSlot)); | |
|
785 | }; | |
|
786 | }; | |
|
787 | function qsp_lib_arrpos(qsp_lib_name, qsp_lib_value, qsp_lib_start) { | |
|
788 | if (qsp_lib_start === undefined) { | |
|
789 | qsp_lib_start = 0; | |
|
790 | }; | |
|
791 | __PS_MV_REG = []; | |
|
792 | var qsp_lib_realName = qsp_api_varRealName(qsp_lib_name); | |
|
793 | var qsp_lib_slot = __PS_MV_REG[0]; | |
|
794 | var _js18 = qsp_api_arraySize(qsp_lib_name); | |
|
795 | for (var qsp_lib_i = qsp_lib_start; qsp_lib_i <= _js18; qsp_lib_i += 1) { | |
|
796 | if (qsp_api_getVar(qsp_lib_realName, qsp_lib_i, qsp_lib_slot) === qsp_lib_value) { | |
|
797 | __PS_MV_REG = []; | |
|
798 | return qsp_lib_i; | |
|
799 | }; | |
|
800 | }; | |
|
801 | __PS_MV_REG = []; | |
|
802 | return -1; | |
|
803 | }; | |
|
804 | function qsp_lib_arrcomp(qsp_lib_name, qsp_lib_pattern, qsp_lib_start) { | |
|
805 | if (qsp_lib_start === undefined) { | |
|
806 | qsp_lib_start = 0; | |
|
807 | }; | |
|
808 | __PS_MV_REG = []; | |
|
809 | var qsp_lib_realName = qsp_api_varRealName(qsp_lib_name); | |
|
810 | var qsp_lib_slot = __PS_MV_REG[0]; | |
|
811 | var _js19 = qsp_api_arraySize(qsp_lib_name); | |
|
812 | for (var qsp_lib_i = qsp_lib_start; qsp_lib_i <= _js19; qsp_lib_i += 1) { | |
|
813 | if (qsp_api_getVar(qsp_lib_realName, qsp_lib_i, qsp_lib_slot).match(qsp_lib_pattern)) { | |
|
814 | __PS_MV_REG = []; | |
|
815 | return qsp_lib_i; | |
|
816 | }; | |
|
817 | }; | |
|
818 | __PS_MV_REG = []; | |
|
819 | return -1; | |
|
820 | }; | |
|
821 | function qsp_lib_instr(qsp_lib_s, qsp_lib_subs, qsp_lib_start) { | |
|
822 | if (qsp_lib_start === undefined) { | |
|
823 | qsp_lib_start = 1; | |
|
824 | }; | |
|
825 | return qsp_lib_start + qsp_lib_s.qsp_lib_substring(qsp_lib_start - 1).search(qsp_lib_subs); | |
|
826 | }; | |
|
827 | function qsp_lib_isnum(qsp_lib_s) { | |
|
828 | __PS_MV_REG = []; | |
|
829 | return qsp_lib_isNaN(qsp_lib_s) ? 0 : -1; | |
|
830 | }; | |
|
831 | function qsp_lib_strcomp(qsp_lib_s, qsp_lib_pattern) { | |
|
832 | return qsp_lib_s.match(qsp_lib_pattern) ? -1 : 0; | |
|
833 | }; | |
|
834 | function qsp_lib_strfind(qsp_lib_s, qsp_lib_pattern, qsp_lib_group) { | |
|
835 | var qsp_lib_re = new qsp_lib_RegExp(qsp_lib_pattern); | |
|
836 | var match = qsp_lib_re.qsp_lib_exec(qsp_lib_s); | |
|
837 | __PS_MV_REG = []; | |
|
838 | return match.qsp_lib_group(qsp_lib_group); | |
|
839 | }; | |
|
840 | function qsp_lib_strpos(qsp_lib_s, qsp_lib_pattern, qsp_lib_group) { | |
|
841 | if (qsp_lib_group === undefined) { | |
|
842 | qsp_lib_group = 0; | |
|
843 | }; | |
|
844 | var qsp_lib_re = new qsp_lib_RegExp(qsp_lib_pattern); | |
|
845 | var match = qsp_lib_re.qsp_lib_exec(qsp_lib_s); | |
|
846 | var qsp_lib_found = match.qsp_lib_group(qsp_lib_group); | |
|
847 | __PS_MV_REG = []; | |
|
848 | return qsp_lib_found ? qsp_lib_s.search(qsp_lib_found) : 0; | |
|
849 | }; | |
|
850 | function qsp_lib_iif(qsp_lib_condExpr, qsp_lib_thenExpr, qsp_lib_elseExpr) { | |
|
851 | return qsp_lib_condExpr ? qsp_lib_thenExpr : qsp_lib_elseExpr; | |
|
852 | }; | |
|
853 | function qsp_lib_gosub(target) { | |
|
854 | var qsp_lib_args = Array.prototype.slice.call(arguments, 1); | |
|
855 | qsp_api_callLoc(target, qsp_lib_args); | |
|
856 | }; | |
|
857 | function qsp_lib_func(target) { | |
|
858 | var qsp_lib_args = Array.prototype.slice.call(arguments, 1); | |
|
859 | __PS_MV_REG = []; | |
|
860 | return qsp_api_callLoc(target, qsp_lib_args); | |
|
861 | }; | |
|
862 | function qsp_lib_dynamic(block) { | |
|
863 | var qsp_lib_args = Array.prototype.slice.call(arguments, 1); | |
|
864 | if (typeof block === 'string') { | |
|
865 | qsp_api_reportError('DYNAMIC can\'t evaluate arbitrary strings.\\nUse {braces} to create blocks for DYNAMIC.'); | |
|
866 | }; | |
|
867 | qsp_api_initArgs(qsp_lib_args); | |
|
868 | block(qsp_lib_args); | |
|
869 | qsp_api_getResult(); | |
|
870 | }; | |
|
871 | function qsp_lib_dyneval(block) { | |
|
872 | var qsp_lib_args = Array.prototype.slice.call(arguments, 1); | |
|
873 | if (typeof block === 'string') { | |
|
874 | qsp_api_reportError('DYNEVAL can\'t evaluate arbitrary strings.\\nUse {braces} to create blocks for DYNEVAL.'); | |
|
875 | }; | |
|
876 | qsp_api_initArgs(qsp_lib_args); | |
|
877 | block(qsp_lib_args); | |
|
878 | __PS_MV_REG = []; | |
|
879 | return qsp_api_getResult(); | |
|
880 | }; | |
|
881 | function qsp_lib_mainP(qsp_lib_s) { | |
|
882 | qsp_api_addText('main', qsp_lib_s); | |
|
883 | }; | |
|
884 | function qsp_lib_mainPl(qsp_lib_s) { | |
|
885 | qsp_api_addText('main', qsp_lib_s); | |
|
886 | qsp_api_newline('main'); | |
|
887 | }; | |
|
888 | function qsp_lib_mainNl(qsp_lib_s) { | |
|
889 | qsp_api_newline('main'); | |
|
890 | qsp_api_addText('main', qsp_lib_s); | |
|
891 | }; | |
|
892 | function qsp_lib_maintxt(qsp_lib_s) { | |
|
893 | qsp_api_getText('main'); | |
|
894 | }; | |
|
895 | function qsp_lib_desc(qsp_lib_s) { | |
|
896 | return ''; | |
|
897 | }; | |
|
898 | function qsp_lib_mainClear() { | |
|
899 | qsp_api_clearText('main'); | |
|
900 | }; | |
|
901 | function qsp_lib_statP(qsp_lib_s) { | |
|
902 | qsp_api_addText('stat', qsp_lib_s); | |
|
903 | }; | |
|
904 | function qsp_lib_statPl(qsp_lib_s) { | |
|
905 | qsp_api_addText('stat', qsp_lib_s); | |
|
906 | qsp_api_newline('stat'); | |
|
907 | }; | |
|
908 | function qsp_lib_statNl(qsp_lib_s) { | |
|
909 | qsp_api_newline('stat'); | |
|
910 | qsp_api_addText('stat', qsp_lib_s); | |
|
911 | }; | |
|
912 | function qsp_lib_stattxt(qsp_lib_s) { | |
|
913 | qsp_api_getText('stat'); | |
|
914 | }; | |
|
915 | function qsp_lib_statClear() { | |
|
916 | qsp_api_clearText('stat'); | |
|
917 | }; | |
|
918 | function qsp_lib_cls() { | |
|
919 | qsp_lib_statClear(); | |
|
920 | qsp_lib_mainClear(); | |
|
921 | qsp_api_clearAct(); | |
|
922 | qsp_lib_cmdclear(); | |
|
923 | }; | |
|
924 | function qsp_lib_selact() { | |
|
925 | for (var qsp_lib_k in qsp_Acts) { | |
|
926 | var qsp_lib_v = qsp_Acts[qsp_lib_k]; | |
|
927 | if (qsp_lib_v['selected']) { | |
|
928 | return qsp_lib_v['name']; | |
|
929 | }; | |
|
930 | }; | |
|
931 | }; | |
|
932 | function qsp_lib_curacts() { | |
|
933 | var qsp_lib_acts = qsp_api_copyObj(qsp_Acts); | |
|
934 | __PS_MV_REG = []; | |
|
935 | return function () { | |
|
936 | qsp_Acts = qsp_lib_acts; | |
|
937 | }; | |
|
938 | }; | |
|
939 | function qsp_lib_addobj(qsp_lib_name, qsp_lib_img) { | |
|
940 | qsp_lib_img = qsp_lib_img || ''; | |
|
941 | qsp_Objs[qsp_lib_name] = { 'name' : qsp_lib_name, | |
|
942 | 'img' : qsp_lib_img, | |
|
943 | 'selected' : null | |
|
944 | }; | |
|
945 | qsp_api_updateObjs(); | |
|
946 | qsp_api_callServLoc('$ONOBJADD', qsp_lib_name, qsp_lib_img); | |
|
947 | }; | |
|
948 | function qsp_lib_delobj(qsp_lib_name) { | |
|
949 | delete qsp_Objs[qsp_lib_name]; | |
|
950 | qsp_api_updateObjs(); | |
|
951 | qsp_api_callServLoc('$ONOBJDEL', qsp_lib_name); | |
|
952 | }; | |
|
953 | function qsp_lib_killobj(qsp_lib_num) { | |
|
954 | if (null === qsp_lib_num) { | |
|
955 | qsp_Objs = { }; | |
|
956 | } else { | |
|
957 | qsp_lib_delobj(Object.keys(qsp_Objs)[qsp_lib_num]); | |
|
958 | }; | |
|
959 | qsp_api_updateObjs(); | |
|
960 | }; | |
|
961 | function qsp_lib_selobj() { | |
|
962 | for (var qsp_lib_k in qsp_Objs) { | |
|
963 | var qsp_lib_v = qsp_Objs[qsp_lib_k]; | |
|
964 | if (qsp_lib_v['selected']) { | |
|
965 | return qsp_lib_v['name']; | |
|
966 | }; | |
|
967 | }; | |
|
968 | }; | |
|
969 | function qsp_lib_unsel() { | |
|
970 | for (var qsp_lib_k in qsp_Objs) { | |
|
971 | var qsp_lib_v = qsp_Objs[qsp_lib_k]; | |
|
972 | qsp_lib_v['selected'] = null; | |
|
973 | }; | |
|
974 | }; | |
|
975 | function qsp_lib_menu(qsp_lib_menuName) { | |
|
976 | var qsp_lib_menuData = []; | |
|
977 | var _js20 = qsp_api_getArray(qsp_api_varRealName(qsp_lib_menuName)).values; | |
|
978 | var _js22 = _js20.length; | |
|
979 | for (var _js21 = 0; _js21 < _js22; _js21 += 1) { | |
|
980 | var qsp_lib_itemObj = _js20[_js21]; | |
|
981 | var qsp_lib_item = qsp_lib_itemObj['str']; | |
|
982 | if (qsp_lib_item === '') { | |
|
983 | break; | |
|
984 | } else if (qsp_lib_item === '-:-') { | |
|
985 | qsp_lib_menuData.push('delimiter'); | |
|
986 | } else { | |
|
987 | var qsp_lib_tokens = qsp_lib_item.split(':'); | |
|
988 | if (qsp_lib_tokens.length === 2) { | |
|
989 | qsp_lib_tokens.push(''); | |
|
990 | }; | |
|
991 | var qsp_lib_text = qsp_lib_tokens.splice(0, qsp_lib_tokens.length - 2).join(':'); | |
|
992 | var qsp_lib_loc = qsp_lib_tokens[qsp_lib_tokens.length - 2]; | |
|
993 | var qsp_lib_icon = qsp_lib_tokens[qsp_lib_tokens.length - 1]; | |
|
994 | qsp_lib_menuData.push({ 'text' : qsp_lib_text, | |
|
995 | 'loc' : qsp_lib_loc, | |
|
996 | 'icon' : qsp_lib_icon | |
|
997 | }); | |
|
998 | }; | |
|
999 | }; | |
|
1000 | qsp_api_menu(qsp_lib_menuData); | |
|
1001 | }; | |
|
1002 | function qsp_lib_play(qsp_lib_filename, qsp_lib_volume) { | |
|
1003 | if (qsp_lib_volume === undefined) { | |
|
1004 | qsp_lib_volume = 100; | |
|
1005 | }; | |
|
1006 | var qsp_lib_audio = new qsp_lib_Audio(qsp_lib_filename); | |
|
1007 | qsp_Playing[qsp_lib_filename] = qsp_lib_audio; | |
|
1008 | qsp_lib_audio.qsp_lib_volume = qsp_lib_volume * 0.01; | |
|
1009 | __PS_MV_REG = []; | |
|
1010 | return qsp_lib_audio.qsp_lib_play(); | |
|
1011 | }; | |
|
1012 | function close(qsp_lib_filename) { | |
|
1013 | qsp_Playing[qsp_lib_filename](qsp_lib_stop); | |
|
1014 | delete qsp_Playing[qsp_lib_filename]; | |
|
1015 | }; | |
|
1016 | function qsp_lib_closeall() { | |
|
1017 | var _js23 = Object.keys(qsp_Playing); | |
|
1018 | var _js25 = _js23.length; | |
|
1019 | for (var _js24 = 0; _js24 < _js25; _js24 += 1) { | |
|
1020 | var qsp_lib_k = _js23[_js24]; | |
|
1021 | var qsp_lib_v = qsp_Playing[qsp_lib_k]; | |
|
1022 | qsp_lib_v(qsp_lib_stop); | |
|
1023 | }; | |
|
1024 | return qsp_Playing = { }; | |
|
1025 | }; | |
|
1026 | function qsp_lib_refint() { | |
|
1027 | return null; | |
|
1028 | }; | |
|
1029 | function qsp_lib_usertxt() { | |
|
1030 | var qsp_lib_input = qsp_byId('qsp-input'); | |
|
1031 | __PS_MV_REG = []; | |
|
1032 | return qsp_lib_input.qsp_lib_value; | |
|
1033 | }; | |
|
1034 | function qsp_lib_cmdclear() { | |
|
1035 | var qsp_lib_input = qsp_byId('qsp-input'); | |
|
1036 | __PS_MV_REG = []; | |
|
1037 | return qsp_lib_input.qsp_lib_value = ''; | |
|
1038 | }; | |
|
1039 | function qsp_lib_input(qsp_lib_text) { | |
|
1040 | return window.prompt(qsp_lib_text); | |
|
1041 | }; | |
|
1042 | function qsp_lib_msecscount() { | |
|
1043 | return Date.now() - qsp_StartedAt; | |
|
1044 | }; | |
|
1045 | function qsp_lib_rgb(qsp_lib_red, qsp_lib_green, qsp_lib_blue) { | |
|
1046 | return (qsp_lib_red << 16) + (qsp_lib_green << 8) + qsp_lib_blue; | |
|
1047 | }; | |
|
1048 | function qsp_lib_openqst(qsp_lib_name) { | |
|
1049 | __PS_MV_REG = []; | |
|
1050 | return qsp_api_runGame(qsp_lib_name); | |
|
1051 | }; | |
|
1052 | function qsp_lib_addqst(qsp_lib_name) { | |
|
1053 | var qsp_lib_game = qsp_api_filenameGame(qsp_lib_name); | |
|
1054 | __PS_MV_REG = []; | |
|
1055 | return Object.assign(qsp_Locs, qsp_Games[qsp_lib_name]); | |
|
1056 | }; | |
|
1057 | function qsp_lib_killqst() { | |
|
1058 | var _js27 = qsp_Games.length; | |
|
1059 | for (var _js26 = 0; _js26 < _js27; _js26 += 1) { | |
|
1060 | var _db28 = qsp_Games[_js26]; | |
|
1061 | var qsp_lib_k = _db28[0]; | |
|
1062 | var qsp_lib_v = _db28[1]; | |
|
1063 | if (qsp_lib_k !== qsp_MainGame) { | |
|
1064 | delete qsp_Locs[qsp_lib_k]; | |
|
1065 | }; | |
|
1066 | }; | |
|
1067 | }; | |
|
1068 | ||
|
1069 | qsp_Games['9loops'] = { }; | |
|
1070 | Object.assign(qsp_Globals, { 'X0' : qsp_api_newVar('X0', 0), | |
|
1071 | 'X' : qsp_api_newVar('X', 0), | |
|
1072 | 'Y0' : qsp_api_newVar('Y0', 0), | |
|
1073 | 'Y' : qsp_api_newVar('Y', 0), | |
|
1074 | 'S' : qsp_api_newVar('S', 0), | |
|
1075 | 'USEHTML' : qsp_api_newVar('USEHTML', 0), | |
|
1076 | 'RESULT' : qsp_api_newVar('RESULT', 0), | |
|
1077 | '$RESULT' : qsp_api_newVar('$RESULT', 0), | |
|
1078 | '$ONGLOAD' : qsp_api_newVar('$ONGLOAD', 0), | |
|
1079 | '$ONGSAVE' : qsp_api_newVar('$ONGSAVE', 0), | |
|
1080 | '$ONOBJADD' : qsp_api_newVar('$ONOBJADD', 0), | |
|
1081 | '$ONOBJDEL' : qsp_api_newVar('$ONOBJDEL', 0), | |
|
1082 | '$ONOBJSEL' : qsp_api_newVar('$ONOBJSEL', 0), | |
|
1083 | '$ONNEWLOC' : qsp_api_newVar('$ONNEWLOC', 0), | |
|
1084 | '$ONACTSEL' : qsp_api_newVar('$ONACTSEL', 0), | |
|
1085 | '$COUNTER' : qsp_api_newVar('$COUNTER', 0), | |
|
1086 | '$USERCOM' : qsp_api_newVar('$USERCOM', 0) | |
|
1087 | }); | |
|
1088 | qsp_Games['9loops']['LOOPS'] = async function (qsp_lib_args) { | |
|
1089 | var qsp_lib__labels = []; | |
|
1090 | qsp_lib__labels['_nil'] = async function () { | |
|
1091 | return 'КОНЕЦ'; | |
|
1092 | qsp_lib_statP('Это сообщение не будет выведено'); | |
|
1093 | __PS_MV_REG = []; | |
|
1094 | return 'КОНЕЦ'; | |
|
1095 | }; | |
|
1096 | qsp_lib__labels['КОНЕЦ'] = async function () { | |
|
1097 | qsp_lib_statP('А это сообщение пользователь увидит'); | |
|
1098 | qsp_Globals['S'][0] = 0; | |
|
1099 | __PS_MV_REG = []; | |
|
1100 | return 'LOOP1'; | |
|
1101 | }; | |
|
1102 | qsp_lib__labels['LOOP1'] = async function () { | |
|
1103 | while (true) { | |
|
1104 | if (qsp_Globals['S'][0] < 9) { | |
|
1105 | qsp_Globals['S'][0] += 1; | |
|
1106 | qsp_lib_statPl(qsp_Globals['S'][0]); | |
|
1107 | continue; | |
|
1108 | }; | |
|
1109 | qsp_lib_statP('Всё!'); | |
|
1110 | break; | |
|
1111 | }; | |
|
1112 | __PS_MV_REG = []; | |
|
1113 | return 'LOOP2'; | |
|
1114 | }; | |
|
1115 | qsp_lib__labels['LOOP2'] = async function () { | |
|
1116 | while (true) { | |
|
1117 | if (qsp_Globals['Y'][0] < qsp_Globals['Y0'][0]) { | |
|
1118 | if (qsp_Globals['X'][0] < qsp_Globals['X0'][0]) { | |
|
1119 | qsp_Globals['X'][0] += 1; | |
|
1120 | continue; | |
|
1121 | }; | |
|
1122 | qsp_Globals['Y'][0] += 1; | |
|
1123 | qsp_Globals['X'][0] = 0; | |
|
1124 | continue; | |
|
1125 | if (qsp_Globals['Y'][0] > qsp_Globals['Y0'][0]) { | |
|
1126 | return; | |
|
1127 | }; | |
|
1128 | }; | |
|
1129 | break; | |
|
1130 | }; | |
|
1131 | }; | |
|
1132 | for (var qsp_lib__nextblock = '_nil'; qsp_lib__nextblock; qsp_lib__nextblock = await (qsp_lib__labels[qsp_lib__nextblock]())) { | |
|
1133 | }; | |
|
1134 | };</script></body></html> No newline at end of file | |
|
17 | :loop2 | |
|
18 | if y<y0: | |
|
19 | if x<x0: | |
|
20 | x=x+1 | |
|
21 | jump 'loop2' | |
|
22 | end | |
|
23 | y=y+1 | |
|
24 | x=0 | |
|
25 | jump 'loop2' | |
|
26 | if y > y0: exit | |
|
27 | end | |
|
28 | - |
@@ -1,14 +1,16 b'' | |||
|
1 | 1 | |
|
2 | 2 | (in-package txt2web) |
|
3 | 3 | |
|
4 | 4 | (defclass compiler () |
|
5 | 5 | ((body :accessor body :initform #.(load-src "extras/body.html")) |
|
6 | 6 | (css :accessor css :initform (list #.(load-src "extras/default.css"))) |
|
7 | (ast :accessor ast :initform nil) | |
|
7 | 8 | (js :accessor js :initform (reverse |
|
8 | 9 | (list |
|
9 | 10 | '#.(read-progn-from-string (load-src "src/main.ps")) |
|
10 | 11 | '#.(read-progn-from-string (load-src "src/api.ps")) |
|
11 | 12 | '#.(read-progn-from-string (load-src "src/intrinsics.ps"))))) |
|
13 | (parse :accessor parse-only :initarg :parse) | |
|
12 | 14 | (compile :accessor compile-only :initarg :compile) |
|
13 | 15 | (target :accessor target :initarg :target) |
|
14 | 16 | (beautify :accessor beautify :initarg :beautify))) |
@@ -1,158 +1,164 b'' | |||
|
1 | 1 | |
|
2 | 2 | (in-package txt2web) |
|
3 | 3 | |
|
4 | 4 | (defvar *app-name* "txt2web") |
|
5 | 5 | |
|
6 | 6 | (defun entry-point-no-args () |
|
7 | 7 | (setf *delivered* t) |
|
8 | 8 | (entry-point uiop:*command-line-arguments*)) |
|
9 | 9 | |
|
10 | 10 | (defun entry-point (args) |
|
11 | 11 | (let ((*package* (find-package :txt2web))) |
|
12 | 12 | (catch :terminate |
|
13 | 13 | (let ((compiler (apply #'make-instance 'compiler (parse-opts args)))) |
|
14 |
( |
|
|
14 | (if (parse-only compiler) | |
|
15 | (let ((*package* (find-package :txt2web.lib))) | |
|
16 | (format t "~{~S~^~%~%~}" (reverse (ast compiler)))) | |
|
17 | (write-compiled-file compiler))))) | |
|
15 | 18 | (values)) |
|
16 | 19 | |
|
17 | 20 | (defun parse-opts (args) |
|
18 | 21 | (let ((mode :sources) |
|
19 | (data (list :sources nil :target nil :js nil :css nil :body nil :compile nil :beautify nil))) | |
|
22 | (data (list :sources nil :target nil :js nil :css nil :body nil :compile nil :parse nil :beautify nil))) | |
|
20 | 23 | (loop :for arg :in args |
|
21 | 24 | :do (alexandria:switch (arg :test #'string=) |
|
22 | 25 | ("-o" (setf mode :target)) |
|
23 | 26 | ("--js" (setf mode :js)) |
|
24 | 27 | ("--css" (setf mode :css)) |
|
25 | 28 | ("--body" (setf mode :body)) |
|
26 | 29 | ("-c" (setf (getf data :compile) t)) |
|
30 | ("-p" (setf (getf data :parse) t)) | |
|
27 | 31 | ("--beautify" (setf (getf data :beautify) t)) |
|
28 | 32 | (t (push arg (getf data mode))))) |
|
29 | 33 | (unless (< 0 (length (getf data :sources))) |
|
30 | 34 | (report-error "There should be at least one source")) |
|
31 | 35 | (unless (> 1 (length (getf data :target))) |
|
32 | 36 | (report-error "There should be no more than one target")) |
|
33 | 37 | (unless (> 1 (length (getf data :body))) |
|
34 | 38 | (report-error "There should be no more than one body")) |
|
35 | 39 | (unless (getf data :target) |
|
36 | 40 | (setf (getf data :target) |
|
37 | 41 | (let* ((sources (first (getf data :sources))) |
|
38 | 42 | (tokens (uiop:split-string sources :separator ".")) |
|
39 | 43 | (target (format nil "~{~A~^.~}.html" |
|
40 | 44 | (butlast tokens)))) |
|
41 | 45 | (list target)))) |
|
42 | 46 | (list :sources (getf data :sources) |
|
43 | 47 | :target (first (getf data :target)) |
|
44 | 48 | :js (getf data :js) |
|
49 | :parse (getf data :parse) | |
|
45 | 50 | :css (getf data :css) |
|
46 | 51 | :body (first (getf data :body)) |
|
47 | 52 | :compile (getf data :compile) |
|
48 | 53 | :beautify (getf data :beautify)))) |
|
49 | 54 | |
|
50 | 55 | (defun print-usage () |
|
51 | 56 | (lformat t :usage *app-name*)) |
|
52 | 57 | |
|
53 | 58 | (defun parse-file (filename) |
|
54 | 59 | (handler-case |
|
55 | 60 | (p:parse 'txt2web-grammar |
|
56 | 61 | (alexandria:read-file-into-string filename :external-format :utf-8)) |
|
57 | 62 | (p:esrap-parse-error (e) |
|
58 | 63 | (format t "~A~%" e) |
|
59 | 64 | (throw :terminate nil)))) |
|
60 | 65 | |
|
61 | 66 | (defun report-error (fmt &rest args) |
|
62 | 67 | (format t "ERROR: ~A~%" (apply #'format nil fmt args)) |
|
63 | 68 | (print-usage) |
|
64 | 69 | (throw :terminate nil)) |
|
65 | 70 | |
|
66 | 71 | ;;; JS |
|
67 | 72 | |
|
68 | 73 | (defun minify-package (package-designator minify prefix) |
|
69 | 74 | (setf (ps:ps-package-prefix package-designator) prefix) |
|
70 | 75 | (if minify |
|
71 | 76 | (ps:obfuscate-package package-designator) |
|
72 | 77 | (ps:unobfuscate-package package-designator))) |
|
73 | 78 | |
|
74 | 79 | (defmethod js-sources ((compiler compiler)) |
|
75 | 80 | (let ((ps:*ps-print-pretty* (beautify compiler))) |
|
76 | 81 | (cond ((beautify compiler) |
|
77 | 82 | (minify-package "TXT2WEB.MAIN" nil "qsp_") |
|
78 | 83 | (minify-package "TXT2WEB.API" nil "qsp_api_") |
|
79 | 84 | (minify-package "TXT2WEB.LIB" nil "qsp_lib_")) |
|
80 | 85 | (t |
|
81 | 86 | (minify-package "TXT2WEB.MAIN" t "_") |
|
82 | 87 | (minify-package "TXT2WEB.API" t "a_") |
|
83 | 88 | (minify-package "TXT2WEB.LIB" t "l_"))) |
|
84 | 89 | (format nil "~{~A~^~%~%~}" (mapcar #'ps:ps* (reverse (js compiler)))))) |
|
85 | 90 | |
|
86 | 91 | ;;; CSS |
|
87 | 92 | |
|
88 | 93 | (defmethod css-sources ((compiler compiler)) |
|
89 | 94 | (format nil "~{~A~^~%~%~}" (css compiler))) |
|
90 | 95 | |
|
91 | 96 | ;;; HTML |
|
92 | 97 | |
|
93 | 98 | (defmethod html-sources ((compiler compiler)) |
|
94 | 99 | (let ((flute:*escape-html* nil) |
|
95 | 100 | (body-template (body compiler)) |
|
96 | 101 | (js (js-sources compiler)) |
|
97 | 102 | (css (css-sources compiler))) |
|
98 | 103 | (with-output-to-string (out) |
|
99 | 104 | (write |
|
100 | 105 | (flute:h |
|
101 | 106 | (html |
|
102 | 107 | (head |
|
103 | 108 | (meta :charset "utf-8") |
|
104 | 109 | (title "txt2web")) |
|
105 | 110 | (body |
|
106 | 111 | body-template |
|
107 | 112 | (style css) |
|
108 | 113 | (script js)))) |
|
109 | 114 | :stream out |
|
110 | 115 | :pretty nil)))) |
|
111 | 116 | |
|
112 | 117 | (defun filename-game (filename) |
|
113 | 118 | (let ((filename (alexandria:lastcar (uiop:split-string filename :separator "/")))) |
|
114 | 119 | (format nil "~{~A~^.~}" (butlast (uiop:split-string filename :separator "."))))) |
|
115 | 120 | |
|
116 | (defmethod initialize-instance ((compiler compiler) &key sources ((:js js-files)) ((:css css-files)) ((:body body-file)) compile &allow-other-keys) | |
|
121 | (defmethod initialize-instance ((compiler compiler) &key sources ((:js js-files)) ((:css css-files)) ((:body body-file)) compile parse &allow-other-keys) | |
|
117 | 122 | (call-next-method) |
|
118 | (with-slots (body css js) | |
|
123 | (with-slots (ast body css js) | |
|
119 | 124 | compiler |
|
120 | 125 | ;; Compile the game's JS |
|
121 | 126 | (dolist (source sources) |
|
122 | 127 | (let ((ps (parse-file source)) |
|
123 | 128 | (game-name (filename-game source))) |
|
124 | 129 | (destructuring-bind (kw &rest locations) |
|
125 | 130 | ps |
|
126 | 131 | (unless (eq kw 'lib:game) |
|
127 | 132 | (report-error "Internal error!")) |
|
128 | 133 | (push |
|
129 | 134 | `(lib:game (,game-name) ,@locations) |
|
130 |
|
|
|
135 | ast)))) | |
|
136 | (setf js (append ast js)) | |
|
131 | 137 | ;; Does the user need us to do anything else |
|
132 | (unless compile | |
|
138 | (unless (or parse compile) | |
|
133 | 139 | ;; Read in body |
|
134 | 140 | (when body-file |
|
135 | 141 | (setf body |
|
136 | 142 | (alexandria:read-file-into-string body-file :external-format :utf-8))) |
|
137 | 143 | ;; Include js files |
|
138 | 144 | (dolist (js-file js-files) |
|
139 | 145 | (push (format nil "////// Included file ~A~%~A" js-file |
|
140 | 146 | (alexandria:read-file-into-string js-file :external-format :utf-8)) |
|
141 | 147 | js)) |
|
142 | 148 | ;; Include css files |
|
143 | 149 | (when css-files |
|
144 | 150 | ;; User option overrides the default css |
|
145 | 151 | (setf css nil) |
|
146 | 152 | (dolist (css-file css-files) |
|
147 | 153 | (push (format nil "////// Included file ~A~%~A" css-file |
|
148 | 154 | (alexandria:read-file-into-string css-file :external-format :utf-8)) |
|
149 | 155 | css)))))) |
|
150 | 156 | |
|
151 | 157 | (defmethod write-compiled-file ((compiler compiler)) |
|
152 | 158 | (alexandria:write-string-into-file |
|
153 | 159 | (if (compile-only compiler) |
|
154 | 160 | ;; Just the JS |
|
155 | 161 | (js-sources compiler) |
|
156 | 162 | ;; All of it |
|
157 | 163 | (html-sources compiler)) |
|
158 | 164 | (target compiler) :if-exists :supersede)) |
@@ -1,664 +1,664 b'' | |||
|
1 | 1 | |
|
2 | 2 | (in-package txt2web) |
|
3 | 3 | |
|
4 | 4 | ;;;; Parses TXT source to an intermediate representation |
|
5 | 5 | |
|
6 | 6 | (eval-when (:compile-toplevel :load-toplevel :execute) |
|
7 | 7 | (defparameter *max-args* 10)) |
|
8 | 8 | |
|
9 | 9 | ;;; Utility |
|
10 | 10 | |
|
11 | 11 | (defun remove-nth (list nth) |
|
12 | 12 | (append (subseq list 0 nth) |
|
13 | 13 | (subseq list (1+ nth)))) |
|
14 | 14 | |
|
15 | 15 | (defun not-quote (char) |
|
16 | 16 | (not (eql #\' char))) |
|
17 | 17 | |
|
18 | 18 | (defun not-doublequote (char) |
|
19 | 19 | (not (eql #\" char))) |
|
20 | 20 | |
|
21 | 21 | (defun not-brace (char) |
|
22 | 22 | (not (eql #\} char))) |
|
23 | 23 | |
|
24 | 24 | (defun not-integer (string) |
|
25 | 25 | (when (find-if-not #'digit-char-p string) |
|
26 | 26 | t)) |
|
27 | 27 | |
|
28 | 28 | (defun not-newline (char) |
|
29 | 29 | (not (eql #\newline char))) |
|
30 | 30 | |
|
31 | 31 | (defun id-any-char (char) |
|
32 | 32 | (and |
|
33 | 33 | (not (digit-char-p char)) |
|
34 | 34 | (not (eql #\newline char)) |
|
35 | 35 | (not (find char " !:&=<>+-*/,'\"()[]{}")))) |
|
36 | 36 | |
|
37 | 37 | (defun intern-first (list) |
|
38 | 38 | (list* (intern (string-upcase (first list)) "TXT2WEB.LIB") |
|
39 | 39 | (rest list))) |
|
40 | 40 | |
|
41 | 41 | (eval-when (:compile-toplevel :load-toplevel :execute) |
|
42 | 42 | (defun remove-nil (list) |
|
43 | 43 | (remove nil list))) |
|
44 | 44 | |
|
45 | 45 | (defun binop-rest (list) |
|
46 | 46 | (destructuring-bind (ws1 operator ws2 operand2) |
|
47 | 47 | list |
|
48 | 48 | (declare (ignore ws1 ws2)) |
|
49 | 49 | (list (intern (string-upcase operator) "TXT2WEB.LIB") operand2))) |
|
50 | 50 | |
|
51 | 51 | (defun do-binop% (left-op other-ops) |
|
52 | 52 | (if (null other-ops) |
|
53 | 53 | left-op |
|
54 | 54 | (destructuring-bind ((operator right-op) &rest rest-ops) |
|
55 | 55 | other-ops |
|
56 | 56 | (if (and (listp left-op) |
|
57 | 57 | (eq (first left-op) |
|
58 | 58 | operator)) |
|
59 | 59 | (do-binop% (append left-op (list right-op)) rest-ops) |
|
60 | 60 | (do-binop% (list operator left-op right-op) rest-ops))))) |
|
61 | 61 | |
|
62 | 62 | (walker:deftransform parser-qspmod mod (&rest args) |
|
63 | 63 | (list* 'qspmod (mapcar #'walker:walk-continue args))) |
|
64 | 64 | |
|
65 | 65 | (defun do-binop (list) |
|
66 | 66 | (walker:walk 'parser-qspmod |
|
67 | 67 | (destructuring-bind (left-op rest-ops) |
|
68 | 68 | list |
|
69 | 69 | (do-binop% left-op |
|
70 | 70 | (mapcar #'binop-rest rest-ops))))) |
|
71 | 71 | |
|
72 | 72 | (defun maybe-text (list) |
|
73 | 73 | "Leaves lists in place and applies esrap:text to everything else" |
|
74 | 74 | (let ((parts nil) |
|
75 | 75 | (part (list 'text))) |
|
76 | 76 | (loop :for token :in list |
|
77 | 77 | :do (cond ((listp token) |
|
78 | 78 | (push (nreverse part) parts) |
|
79 | 79 | (setf part (list 'text)) |
|
80 | 80 | (push token parts)) |
|
81 | 81 | (t (push token part)))) |
|
82 | 82 | (push (nreverse part) parts) |
|
83 | 83 | (remove "" |
|
84 | 84 | (loop :for part :in (nreverse parts) |
|
85 | 85 | :collect (case (first part) |
|
86 | 86 | ('text (p:text (rest part))) |
|
87 | 87 | (t part))) |
|
88 | 88 | :test #'equal))) |
|
89 | 89 | |
|
90 | 90 | (p:defrule line-continuation (and #\_ #\newline) |
|
91 | 91 | (:constant nil)) |
|
92 | 92 | |
|
93 | 93 | (p:defrule text-spaces (+ (or #\space #\tab line-continuation)) |
|
94 | 94 | (:text t)) |
|
95 | 95 | |
|
96 | 96 | (p:defrule spaces (+ (or #\space #\tab line-continuation)) |
|
97 | 97 | (:constant nil) |
|
98 | 98 | (:error-report nil)) |
|
99 | 99 | |
|
100 | 100 | (p:defrule spaces? (* (or #\space #\tab line-continuation)) |
|
101 | 101 | (:constant nil) |
|
102 | 102 | (:error-report nil)) |
|
103 | 103 | |
|
104 | 104 | (p:defrule colon #\: |
|
105 | 105 | (:constant nil)) |
|
106 | 106 | |
|
107 | 107 | (p:defrule equal #\= |
|
108 | 108 | (:constant nil)) |
|
109 | 109 | |
|
110 | 110 | (p:defrule alphanumeric (alphanumericp character)) |
|
111 | 111 | |
|
112 | 112 | (p:defrule not-newline (not-newline character)) |
|
113 | 113 | |
|
114 | 114 | (p:defrule squote-esc "''" |
|
115 | 115 | (:lambda (list) |
|
116 | 116 | (p:text (elt list 0)))) |
|
117 | 117 | |
|
118 | 118 | (p:defrule dquote-esc "\"\"" |
|
119 | 119 | (:lambda (list) |
|
120 | 120 | (p:text (elt list 0)))) |
|
121 | 121 | |
|
122 | 122 | (p:defrule sstring-char (or squote-esc (not-quote character)) |
|
123 | 123 | (:text t)) |
|
124 | 124 | |
|
125 | 125 | (p:defrule dstring-char (or dquote-esc (not-doublequote character)) |
|
126 | 126 | (:text t)) |
|
127 | 127 | |
|
128 | 128 | ;;; Identifiers |
|
129 | 129 | |
|
130 |
(defparameter *keywords* '(act addlib addobj addqst and arrcomp arrpos arrsize cla clear *clear close clr *clr cls cmdclear cmdclr copyarr countobj curact curacts curloc debug delact dellib delobj desc disablescroll |
|
|
130 | (defparameter *keywords* '(act addlib addobj addqst and arrcomp arrpos arrsize cla clear *clear close clr *clr cls cmdclear cmdclr copyarr countobj curact curacts curloc debug delact dellib delobj desc disablescroll dynamic dyneval else elseif end exit loop freelib func getobj gosub goto gs gt if iif img *img inclib input instr isnum isplay jump killall killobj killqst killvar lcase len let loc local maintxt max menu mid min mod msecscount msg nl *nl no nosave obj opengame openqst or p *p pl *pl play qspver rand refint replace rgb rnd savegame selact selobj set settimer showacts showinput showobjs showstat stattxt step str strcomp strfind strpos to trim ucase unsel unselect user_text usrtxt val view wait while xgoto xgt)) | |
|
131 | 131 | |
|
132 | 132 | (defun trim-$ (str) |
|
133 | 133 | (if (char= #\$ (elt str 0)) |
|
134 | 134 | (subseq str 1) |
|
135 | 135 | str)) |
|
136 | 136 | |
|
137 | 137 | (defun qsp-keyword-p (id) |
|
138 | 138 | (member (intern (trim-$ (string-upcase id))) *keywords*)) |
|
139 | 139 | |
|
140 | 140 | (defun not-qsp-keyword-p (id) |
|
141 | 141 | (not (member (intern (trim-$ (string-upcase id))) *keywords*))) |
|
142 | 142 | |
|
143 | 143 | (p:defrule qsp-keyword (qsp-keyword-p identifier-raw)) |
|
144 | 144 | |
|
145 | 145 | (p:defrule id-first (id-any-char character)) |
|
146 | 146 | (p:defrule id-next (or (id-any-char character) |
|
147 | 147 | (digit-char-p character))) |
|
148 | 148 | (p:defrule identifier-raw (and id-first (* id-next)) |
|
149 | 149 | (:lambda (list) |
|
150 | 150 | (intern (string-upcase (p:text list)) "TXT2WEB.LIB"))) |
|
151 | 151 | |
|
152 | 152 | (p:defrule identifier (not-qsp-keyword-p identifier-raw)) |
|
153 | 153 | |
|
154 | 154 | ;;; Strings |
|
155 | 155 | |
|
156 | 156 | (p:defrule qsp-string (or normal-string brace-string)) |
|
157 | 157 | |
|
158 | 158 | (p:defrule brace-string (and #\{ before-statement block-body #\}) |
|
159 | 159 | (:lambda (list) |
|
160 | 160 | (list* 'lib:qspblock (third list)))) |
|
161 | 161 | |
|
162 | 162 | (p:defrule normal-string (or sstring dstring) |
|
163 | 163 | (:lambda (str) |
|
164 | 164 | (list* 'lib:str (or str (list ""))))) |
|
165 | 165 | |
|
166 | 166 | (p:defrule sstring (and #\' (* (or sstring-interpol |
|
167 | 167 | sstring-exec |
|
168 | 168 | sstring-char)) |
|
169 | 169 | #\') |
|
170 | 170 | (:lambda (list) |
|
171 | 171 | (maybe-text (second list)))) |
|
172 | 172 | |
|
173 | 173 | (p:defrule dstring (and #\" (* (or dstring-interpol |
|
174 | 174 | dstring-exec |
|
175 | 175 | dstring-char)) |
|
176 | 176 | #\") |
|
177 | 177 | (:lambda (list) |
|
178 | 178 | (maybe-text (second list)))) |
|
179 | 179 | |
|
180 | 180 | (defun parse-interpol (list) |
|
181 | 181 | (p:parse 'expression (p:text (mapcar 'second (second list))))) |
|
182 | 182 | |
|
183 | 183 | (defun parse-exec (list) |
|
184 | 184 | (list* 'lib:exec (p:parse 'exec-body (p:text (second list))))) |
|
185 | 185 | |
|
186 | 186 | (p:defrule sstring-interpol (and "<<" (+ (and (p:! ">>") |
|
187 | 187 | sstring-char)) |
|
188 | 188 | ">>") |
|
189 | 189 | (:function parse-interpol)) |
|
190 | 190 | |
|
191 | 191 | (p:defrule dstring-interpol (and "<<" (+ (and (p:! ">>") |
|
192 | 192 | dstring-char)) |
|
193 | 193 | ">>") |
|
194 | 194 | (:function parse-interpol)) |
|
195 | 195 | |
|
196 | 196 | (p:defrule sstring-exec (or (and (p:~ "\"exec:") |
|
197 | 197 | (+ (and (p:& (not-doublequote character)) sstring-char)) |
|
198 | 198 | #\") |
|
199 | 199 | (and (p:~ "''exec:") |
|
200 | 200 | (+ (not-quote character)) |
|
201 | 201 | "''")) |
|
202 | 202 | (:function parse-exec)) |
|
203 | 203 | |
|
204 | 204 | (p:defrule dstring-exec (or (and (p:~ "'exec:") |
|
205 | 205 | (+ (and (p:& (not-quote character)) dstring-char)) |
|
206 | 206 | #\') |
|
207 | 207 | (and (p:~ "\"\"exec") |
|
208 | 208 | (+ (not-doublequote character)) |
|
209 | 209 | "\"\"")) |
|
210 | 210 | (:function parse-exec)) |
|
211 | 211 | |
|
212 | 212 | ;;; Location |
|
213 | 213 | |
|
214 | 214 | (p:defrule txt2web-grammar (and (* (or spaces #\newline)) |
|
215 | 215 | (* location)) |
|
216 | 216 | (:lambda (list) |
|
217 | 217 | `(lib:game ,@(second list)))) |
|
218 | 218 | |
|
219 | 219 | (p:defrule location (and location-header block-body location-end) |
|
220 | 220 | (:destructure (header body end) |
|
221 | 221 | (declare (ignore end)) |
|
222 | 222 | `(lib:location (,header) ,@body))) |
|
223 | 223 | |
|
224 | 224 | (p:defrule location-header (and #\# |
|
225 | 225 | (+ not-newline) |
|
226 | 226 | (and #\newline spaces? before-statement)) |
|
227 | 227 | (:destructure (spaces1 name spaces2) |
|
228 | 228 | (declare (ignore spaces1 spaces2)) |
|
229 | 229 | (string-upcase (string-trim " " (p:text name))))) |
|
230 | 230 | |
|
231 | 231 | (p:defrule location-end (and #\- (* not-newline) #\newline before-statement) |
|
232 | 232 | (:constant nil)) |
|
233 | 233 | |
|
234 | 234 | ;;; Block body |
|
235 | 235 | |
|
236 | 236 | (p:defrule newline-block-body (and #\newline spaces? block-body) |
|
237 | 237 | (:function third)) |
|
238 | 238 | |
|
239 | 239 | (p:defrule block-body (* statement) |
|
240 | 240 | (:function remove-nil)) |
|
241 | 241 | |
|
242 | 242 | ;; Just for <a href="exec:...'> |
|
243 | 243 | ;; Explicitly called from that rule's production |
|
244 | 244 | (p:defrule exec-body (and before-statement line-body) |
|
245 | 245 | (:function second)) |
|
246 | 246 | |
|
247 | 247 | (p:defrule line-body (and inline-statement (* next-inline-statement)) |
|
248 | 248 | (:lambda (list) |
|
249 | 249 | (list* (first list) (second list)))) |
|
250 | 250 | |
|
251 | 251 | (p:defrule before-statement (* (or #\newline spaces)) |
|
252 | 252 | (:constant nil)) |
|
253 | 253 | |
|
254 | 254 | (p:defrule statement-end (or statement-end-real statement-end-block-close)) |
|
255 | 255 | |
|
256 | 256 | (p:defrule statement-end-real (and (or #\newline |
|
257 | 257 | (and #\& spaces? (p:& statement%))) |
|
258 | 258 | before-statement) |
|
259 | 259 | (:constant nil)) |
|
260 | 260 | |
|
261 | 261 | (p:defrule statement-end-block-close (or (p:& #\})) |
|
262 | 262 | (:constant nil)) |
|
263 | 263 | |
|
264 | 264 | (p:defrule inline-statement (and statement% spaces?) |
|
265 | 265 | (:function first)) |
|
266 | 266 | |
|
267 | 267 | (p:defrule next-inline-statement (and #\& spaces? inline-statement) |
|
268 | 268 | (:function third)) |
|
269 | 269 | |
|
270 | 270 | (p:defrule not-a-non-statement (and (p:! (p:~ "elseif")) |
|
271 | 271 | (p:! (p:~ "else")) |
|
272 | 272 | (p:! (p:~ "end")))) |
|
273 | 273 | |
|
274 | 274 | (p:defrule statement (and inline-statement statement-end) |
|
275 | 275 | (:function first)) |
|
276 | 276 | |
|
277 | 277 | (p:defrule statement% (and not-a-non-statement |
|
278 | 278 | (or label comment string-output |
|
279 | 279 | block non-returning-intrinsic local |
|
280 | 280 | assignment expression-output)) |
|
281 | 281 | (:function second)) |
|
282 | 282 | |
|
283 | 283 | (p:defrule expr-stopper (or comment block non-returning-intrinsic)) |
|
284 | 284 | |
|
285 | 285 | (p:defrule string-output qsp-string |
|
286 | 286 | (:lambda (string) |
|
287 | 287 | (list 'lib:main-pl string))) |
|
288 | 288 | |
|
289 | 289 | (p:defrule expression-output expression |
|
290 | 290 | (:lambda (list) |
|
291 | 291 | (list 'lib:main-pl list))) |
|
292 | 292 | |
|
293 | 293 | (p:defrule label (and colon identifier) |
|
294 | 294 | (:lambda (list) |
|
295 | 295 | (intern (string (second list)) :keyword))) |
|
296 | 296 | |
|
297 | 297 | (p:defrule comment (and #\! (* (or qsp-string brace-comment text-spaces not-newline))) |
|
298 | 298 | (:constant nil)) |
|
299 | 299 | |
|
300 | 300 | (p:defrule brace-comment (and #\{ (* (not-brace character)) #\}) |
|
301 | 301 | (:constant nil)) |
|
302 | 302 | |
|
303 | 303 | (p:defrule local (and (p:~ "local") spaces variable (p:? (and spaces? #\= spaces? expression))) |
|
304 | 304 | (:lambda (list) |
|
305 | 305 | (list* 'lib:local (third list) |
|
306 | 306 | (when (fourth list) |
|
307 | 307 | (list (fourth (fourth list))))))) |
|
308 | 308 | |
|
309 | 309 | ;;; Blocks |
|
310 | 310 | |
|
311 | 311 | (p:defrule block (or block-act block-if block-loop)) |
|
312 | 312 | |
|
313 | 313 | (p:defrule block-if (and block-if-head block-if-body) |
|
314 | 314 | (:destructure (head body) |
|
315 | 315 | `(lib:qspcond (,@head ,@(first body)) |
|
316 | 316 | ,@(rest body)))) |
|
317 | 317 | |
|
318 | 318 | (p:defrule block-if-head (and (p:~ "if") spaces expression spaces? colon spaces?) |
|
319 | 319 | (:function remove-nil) |
|
320 | 320 | (:function cdr)) |
|
321 | 321 | |
|
322 | 322 | (p:defrule block-if-body (or block-if-ml block-if-sl) |
|
323 | 323 | (:destructure (if-body elseifs else &rest ws) |
|
324 | 324 | (declare (ignore ws)) |
|
325 | 325 | `(,(remove-nil if-body) ,@elseifs ,@(when else `((else ,@else)))))) |
|
326 | 326 | |
|
327 | 327 | (p:defrule block-if-sl (and line-body |
|
328 | 328 | (p:? block-if-elseif-inline) |
|
329 | 329 | (p:? block-if-else-inline) |
|
330 | 330 | spaces?)) |
|
331 | 331 | |
|
332 | 332 | (p:defrule block-if-ml (and (and #\newline spaces?) |
|
333 | 333 | block-body |
|
334 | 334 | (p:? block-if-elseif) |
|
335 | 335 | (p:? block-if-else) |
|
336 | 336 | block-if-end) |
|
337 | 337 | (:lambda (list) |
|
338 | 338 | (cdr list))) |
|
339 | 339 | |
|
340 | 340 | (p:defrule block-if-elseif-inline (and block-if-elseif-head line-body (p:? block-if-elseif-inline)) |
|
341 | 341 | (:destructure (head statements elseif) |
|
342 | 342 | `((,@(rest head) ,@(remove-nil statements)) ,@elseif))) |
|
343 | 343 | |
|
344 | 344 | (p:defrule block-if-elseif (and block-if-elseif-head statement-end block-body (p:? block-if-elseif)) |
|
345 | 345 | (:destructure (head ws statements elseif) |
|
346 | 346 | (declare (ignore ws)) |
|
347 | 347 | `((,@(rest head) ,@(remove-nil statements)) ,@elseif))) |
|
348 | 348 | |
|
349 | 349 | (p:defrule block-if-elseif-head (and (p:~ "elseif") spaces expression spaces? colon spaces?) |
|
350 | 350 | (:function remove-nil) |
|
351 | 351 | (:function intern-first)) |
|
352 | 352 | |
|
353 | 353 | (p:defrule block-if-else-inline (and block-if-else-head line-body) |
|
354 | 354 | (:function second)) |
|
355 | 355 | |
|
356 | 356 | (p:defrule block-if-else (and block-if-else-head #\newline spaces? block-body) |
|
357 | 357 | (:function fourth)) |
|
358 | 358 | |
|
359 | 359 | (p:defrule block-if-else-head (and (p:~ "else") spaces?) |
|
360 | 360 | (:constant nil)) |
|
361 | 361 | |
|
362 | 362 | (p:defrule block-if-end (and (p:~ "end") |
|
363 | 363 | (p:? (and spaces (p:~ "if")))) |
|
364 | 364 | (:constant nil)) |
|
365 | 365 | |
|
366 | 366 | (p:defrule block-act (and block-act-head (or block-ml block-sl)) |
|
367 | 367 | (:lambda (list) |
|
368 | 368 | (apply #'append list))) |
|
369 | 369 | |
|
370 | 370 | (p:defrule block-act-head (and (p:~ "act") spaces? qsp-string spaces? |
|
371 | 371 | (p:? block-act-head-img) |
|
372 | 372 | colon spaces?) |
|
373 | 373 | (:lambda (list) |
|
374 | 374 | (intern-first (list (first list) |
|
375 | 375 | (third list) |
|
376 | 376 | (or (fifth list) '(lib:str "")))))) |
|
377 | 377 | |
|
378 | 378 | (p:defrule block-act-head-img (and #\, spaces? qsp-string spaces?) |
|
379 | 379 | (:lambda (list) |
|
380 | 380 | (or (third list) ""))) |
|
381 | 381 | |
|
382 | 382 | (p:defrule block-loop (and block-loop-head (or block-ml block-sl)) |
|
383 | 383 | (:lambda (list) |
|
384 | 384 | (apply #'append list))) |
|
385 | 385 | |
|
386 | 386 | (p:defrule block-loop-head (and (p:~ "loop") spaces |
|
387 | 387 | (p:? (and block-loop-head-init spaces?)) |
|
388 | 388 | block-loop-head-while spaces? |
|
389 | 389 | (p:? (and block-loop-head-step spaces?)) |
|
390 | 390 | colon spaces?) |
|
391 | 391 | (:lambda (list) |
|
392 | 392 | (break "~S" list) |
|
393 | 393 | (list 'lib:qsploop |
|
394 | 394 | (elt list 2) |
|
395 | 395 | (elt list 6) |
|
396 | 396 | (elt list 9) |
|
397 | 397 | (elt list 10)))) |
|
398 | 398 | |
|
399 | 399 | (p:defrule block-loop-head-init (or local plain-assignment)) |
|
400 | 400 | |
|
401 | 401 | (p:defrule block-loop-head-while (and (p:~ "while") eq-expr) |
|
402 | 402 | (:function second)) |
|
403 | 403 | |
|
404 | 404 | (p:defrule block-loop-head-step (and (p:~ "step") (or plain-assignment op-assignment)) |
|
405 | 405 | (:function second)) |
|
406 | 406 | |
|
407 | 407 | (p:defrule block-sl line-body) |
|
408 | 408 | |
|
409 | 409 | (p:defrule block-ml (and newline-block-body block-end) |
|
410 | 410 | (:lambda (list) |
|
411 | 411 | (apply #'list* (butlast list)))) |
|
412 | 412 | |
|
413 | 413 | (p:defrule block-end (and (p:~ "end")) |
|
414 | 414 | (:constant nil)) |
|
415 | 415 | |
|
416 | 416 | ;;; Calls |
|
417 | 417 | |
|
418 | 418 | (p:defrule first-argument (and expression spaces?) |
|
419 | 419 | (:function first)) |
|
420 | 420 | (p:defrule next-argument (and "," spaces? expression) |
|
421 | 421 | (:function third)) |
|
422 | 422 | (p:defrule arguments (or parenthesized-arguments plain-arguments no-arguments)) |
|
423 | 423 | (p:defrule parenthesized-arguments (and spaces? #\( base-arguments #\)) |
|
424 | 424 | (:function third)) |
|
425 | 425 | (p:defrule plain-arguments (and spaces? base-arguments) |
|
426 | 426 | (:function second)) |
|
427 | 427 | (p:defrule no-arguments (or (and spaces? (p:& #\newline)) |
|
428 | 428 | (and spaces? (p:& #\&)) |
|
429 | 429 | spaces?) |
|
430 | 430 | (:constant nil)) |
|
431 | 431 | (p:defrule base-arguments (or (and first-argument (* next-argument)) spaces?) |
|
432 | 432 | (:lambda (list) |
|
433 | 433 | (if (null list) |
|
434 | 434 | nil |
|
435 | 435 | (list* (first list) (second list))))) |
|
436 | 436 | |
|
437 | 437 | ;;; Intrinsics |
|
438 | 438 | |
|
439 | 439 | (defmacro defintrinsics ((rule-name returning-rule-name non-returning-rule-name) &body clauses) |
|
440 | 440 | `(progn |
|
441 | 441 | ,@(loop :for clause :in clauses |
|
442 | 442 | :collect `(defintrinsic ,@clause)) |
|
443 | 443 | (p:defrule ,returning-rule-name (or ,@(remove-nil |
|
444 | 444 | (mapcar (lambda (clause) |
|
445 | 445 | (when (second clause) |
|
446 | 446 | (alexandria:symbolicate |
|
447 | 447 | 'intrinsic- (first clause)))) |
|
448 | 448 | clauses)))) |
|
449 | 449 | (p:defrule ,non-returning-rule-name (or ,@(remove-nil |
|
450 | 450 | (mapcar (lambda (clause) |
|
451 | 451 | (unless (second clause) |
|
452 | 452 | (alexandria:symbolicate |
|
453 | 453 | 'intrinsic- (first clause)))) |
|
454 | 454 | clauses)))) |
|
455 | 455 | (p:defrule ,rule-name (or ,returning-rule-name ,non-returning-rule-name)))) |
|
456 | 456 | |
|
457 | 457 | (defmacro defintrinsic (sym returning &optional (min-arity 0) max-arity &rest names) |
|
458 | 458 | (declare (ignore returning)) |
|
459 | 459 | (unless max-arity |
|
460 | 460 | (setf max-arity *max-args*)) |
|
461 | 461 | (setf names |
|
462 | 462 | (if names |
|
463 | 463 | (mapcar #'string-upcase names) |
|
464 | 464 | (list (string sym)))) |
|
465 | 465 | `(p:defrule ,(alexandria:symbolicate 'intrinsic- sym) |
|
466 | 466 | (and (p:? #\$) (or ,@(loop :for name :in names :collect `(p:~ ,name))) |
|
467 | 467 | arguments) |
|
468 | 468 | (:destructure (dollar name arguments) |
|
469 | 469 | (declare (ignore dollar)) |
|
470 | 470 | (unless (<= ,min-arity (length arguments) ,max-arity) |
|
471 | 471 | (error "Intrinsic ~A expects between ~A and ~A arguments but ~A were provided:~%~S" |
|
472 | 472 | name ,min-arity ,max-arity (length arguments) arguments)) |
|
473 | 473 | (list* ',(intern (string sym) "TXT2WEB.LIB") arguments)))) |
|
474 | 474 | |
|
475 | 475 | (defintrinsics (intrinsic returning-intrinsic non-returning-intrinsic) |
|
476 | 476 | ;; Transitions |
|
477 | 477 | (goto% nil 0 nil "gt" "goto") |
|
478 | 478 | (xgoto% nil 0 nil "xgt" "xgoto") |
|
479 | 479 | ;; Variables |
|
480 | 480 | (killvar nil 0 2) |
|
481 | 481 | ;; Expressions |
|
482 | 482 | (obj t 1 1) |
|
483 | 483 | (loc t 1 1) |
|
484 | 484 | (no t 1 1) |
|
485 | 485 | ;; Basic |
|
486 | 486 | (qspver t 0 0) |
|
487 | 487 | (curloc t 0 0) |
|
488 | 488 | (rand t 1 2) |
|
489 | 489 | (rnd t 0 0) |
|
490 | 490 | (qspmax t 1 nil "max") |
|
491 | 491 | (qspmin t 1 nil "min") |
|
492 | 492 | ;; Arrays |
|
493 | 493 | (killall nil 0 0) |
|
494 | 494 | (copyarr nil 2 4) |
|
495 | 495 | (arrsize t 1 1) |
|
496 | 496 | (arrpos t 2 3) |
|
497 | 497 | (arrcomp t 2 3) |
|
498 | 498 | ;; Strings |
|
499 | 499 | (len t 1 1) |
|
500 | 500 | (mid t 2 3) |
|
501 | 501 | (ucase t 1 1) |
|
502 | 502 | (lcase t 1 1) |
|
503 | 503 | (trim t 1 1) |
|
504 | 504 | (qspreplace t 2 3 "replace") |
|
505 | 505 | (instr t 2 3) |
|
506 | 506 | (isnum t 1 1) |
|
507 | 507 | (val t 1 1) |
|
508 | 508 | (qspstr t 1 1 "str") |
|
509 | 509 | (strcomp t 2 2) |
|
510 | 510 | (strfind t 2 3) |
|
511 | 511 | (strpos t 2 3) |
|
512 | 512 | ;; IF |
|
513 | 513 | (iif t 2 3) |
|
514 | 514 | ;; Subs |
|
515 | 515 | (gosub nil 1 nil "gosub" "gs") |
|
516 | 516 | (func t 1 nil) |
|
517 | 517 | (exit nil 0 0) |
|
518 | 518 | ;; Jump |
|
519 | 519 | (jump nil 1 1) |
|
520 | 520 | ;; Dynamic |
|
521 | 521 | (dynamic nil 1 nil) |
|
522 | 522 | (dyneval t 1 nil) |
|
523 | 523 | ;; Sound |
|
524 | 524 | (play nil 1 2) |
|
525 | 525 | (isplay t 1 1) |
|
526 | 526 | (close nil 1 1) |
|
527 | 527 | (closeall nil 0 0 "close all") |
|
528 | 528 | ;; Main window |
|
529 | 529 | (main-pl nil 1 1 "*pl") |
|
530 | 530 | (main-nl nil 0 1 "*nl") |
|
531 | 531 | (main-p nil 1 1 "*p") |
|
532 | 532 | (maintxt t 0 0) |
|
533 | 533 | (desc t 1 1) |
|
534 | 534 | (main-clear nil 0 0 "*clear" "*clr") |
|
535 | 535 | ;; Aux window |
|
536 | 536 | (showstat nil 1 1) |
|
537 | 537 | (stat-pl nil 1 1 "pl") |
|
538 | 538 | (stat-nl nil 0 1 "nl") |
|
539 | 539 | (stat-p nil 1 1 "p") |
|
540 | 540 | (stattxt t 0 0) |
|
541 | 541 | (stat-clear nil 0 0 "clear" "clr") |
|
542 | 542 | (cls nil 0 0) |
|
543 | 543 | ;; Dialog |
|
544 | 544 | (msg nil 1 1) |
|
545 | 545 | ;; Acts |
|
546 | 546 | (showacts nil 1 1) |
|
547 | 547 | (delact nil 1 1 "delact" "del act") |
|
548 | 548 | (curacts t 0 0) |
|
549 | 549 | (selact t 0 0) |
|
550 | 550 | (cla nil 0 0) |
|
551 | 551 | ;; Objects |
|
552 | 552 | (showobjs nil 1 1) |
|
553 | 553 | (addobj nil 1 3 "addobj" "add obj") |
|
554 | 554 | (delobj nil 1 1 "delobj" "del obj") |
|
555 | 555 | (killobj nil 0 1) |
|
556 | 556 | (countobj t 0 0) |
|
557 | 557 | (getobj t 1 1) |
|
558 | 558 | (selobj t 0 0) |
|
559 | 559 | (unsel nil 0 0 "unsel" "unselect") |
|
560 | 560 | ;; Menu |
|
561 | 561 | (menu nil 1 1) |
|
562 | 562 | ;; Images |
|
563 | 563 | (refint nil 0 0) |
|
564 | 564 | (view nil 0 1) |
|
565 | 565 | (img nil 1) |
|
566 | 566 | (*img nil 1) |
|
567 | 567 | ;; Fonts |
|
568 | 568 | (rgb t 3 3) |
|
569 | 569 | ;; Input |
|
570 | 570 | (showinput nil 1 1) |
|
571 | 571 | (usertxt t 0 0 "user_text" "usrtxt") |
|
572 | 572 | (cmdclear nil 0 0 "cmdclear" "cmdclr") |
|
573 | 573 | (input t 1 1) |
|
574 | 574 | ;; Files |
|
575 | 575 | (openqst nil 1 1) |
|
576 | 576 | (addqst nil 1 1 "addqst" "addlib" "inclib") |
|
577 | 577 | (killqst nil 1 1 "killqst" "dellib" "freelib") |
|
578 | 578 | (opengame nil 0 0) |
|
579 | 579 | (savegame nil 0 0) |
|
580 | 580 | ;; Real time |
|
581 | 581 | (wait nil 1 1) |
|
582 | 582 | (msecscount t 0 0) |
|
583 | 583 | (settimer nil 1 1)) |
|
584 | 584 | |
|
585 | 585 | ;;; Expression |
|
586 | 586 | |
|
587 | 587 | (p:defrule expression or-expr) |
|
588 | 588 | |
|
589 | 589 | (p:defrule or-expr (and and-expr (* (and spaces? or-op spaces? and-expr))) |
|
590 | 590 | (:function do-binop)) |
|
591 | 591 | |
|
592 | 592 | (p:defrule and-expr (and eq-expr (* (and spaces? and-op spaces? eq-expr))) |
|
593 | 593 | (:function do-binop)) |
|
594 | 594 | |
|
595 | 595 | (p:defrule eq-expr (and sum-expr (* (and spaces? (or "<=" ">=" "=<" "=>" "<>" |
|
596 |
"=" "<" ">" |
|
|
596 | "=" "<" ">") | |
|
597 | 597 | spaces? sum-expr))) |
|
598 | 598 | (:function do-binop)) |
|
599 | 599 | |
|
600 | 600 | (p:defrule sum-expr (and mod-expr (* (and spaces? (or #\+ #\-) spaces? mod-expr))) |
|
601 | 601 | (:function do-binop)) |
|
602 | 602 | |
|
603 | 603 | (p:defrule mod-expr (and mul-expr (* (and spaces? (p:~ "mod") spaces? mul-expr))) |
|
604 | 604 | (:function do-binop)) |
|
605 | 605 | |
|
606 | 606 | (p:defrule mul-expr (and unary-expr (* (and spaces? (or #\* #\/) spaces? unary-expr))) |
|
607 | 607 | (:function do-binop)) |
|
608 | 608 | |
|
609 | 609 | (p:defrule unary-expr (and (p:? (or #\+ #\-)) atom-expr) |
|
610 | 610 | (:lambda (list) |
|
611 | 611 | (let ((expr (remove-nil list))) |
|
612 | 612 | (if (= 1 (length expr)) |
|
613 | 613 | (first expr) |
|
614 | 614 | (intern-first expr))))) |
|
615 | 615 | |
|
616 | 616 | (p:defrule atom-expr (and (or variable literal returning-intrinsic paren-expr) spaces?) |
|
617 | 617 | (:function first)) |
|
618 | 618 | |
|
619 | 619 | (p:defrule paren-expr (and #\( spaces? expression spaces? #\)) |
|
620 | 620 | (:function third)) |
|
621 | 621 | |
|
622 | 622 | (p:defrule or-op (p:~ "or") |
|
623 | 623 | (:constant "or")) |
|
624 | 624 | |
|
625 | 625 | (p:defrule and-op (p:~ "and") |
|
626 | 626 | (:constant "and")) |
|
627 | 627 | |
|
628 | 628 | ;;; Variables |
|
629 | 629 | |
|
630 | 630 | (p:defrule variable (and identifier (p:? array-index)) |
|
631 | 631 | (:destructure (id idx-raw) |
|
632 | 632 | (let ((idx (case idx-raw |
|
633 | 633 | ((nil) 0) |
|
634 | 634 | (:last nil) |
|
635 | 635 | (t idx-raw)))) |
|
636 | 636 | (list 'lib:qspvar id idx)))) |
|
637 | 637 | |
|
638 | 638 | (p:defrule array-index (and #\[ spaces? (p:? expression) spaces? #\]) |
|
639 | 639 | (:lambda (list) |
|
640 | 640 | (or (third list) :last))) |
|
641 | 641 | |
|
642 | 642 | (p:defrule assignment (or kw-assignment plain-assignment op-assignment) |
|
643 | 643 | (:destructure (qspvar eq expr) |
|
644 | 644 | (declare (ignore eq)) |
|
645 | 645 | (list 'lib:set qspvar expr))) |
|
646 | 646 | |
|
647 | 647 | (p:defrule kw-assignment (and (or (p:~ "let") (p:~ "set")) spaces? (or op-assignment plain-assignment)) |
|
648 | 648 | (:function third)) |
|
649 | 649 | |
|
650 | 650 | (p:defrule op-assignment (and variable spaces? (or "+" "-" "*" "/") #\= spaces? expression) |
|
651 | 651 | (:destructure (qspvar ws1 op eq ws2 expr) |
|
652 | 652 | (declare (ignore ws1 ws2)) |
|
653 | 653 | (list qspvar eq (intern-first (list op qspvar expr))))) |
|
654 | 654 | |
|
655 | 655 | (p:defrule plain-assignment (and variable spaces? #\= spaces? expression) |
|
656 | 656 | (:function remove-nil)) |
|
657 | 657 | |
|
658 | 658 | ;;; Non-string literals |
|
659 | 659 | |
|
660 | 660 | (p:defrule literal (or qsp-string brace-string number)) |
|
661 | 661 | |
|
662 | 662 | (p:defrule number (+ (or #\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9)) |
|
663 | 663 | (:lambda (list) |
|
664 | 664 | (parse-integer (p:text list)))) |
@@ -1,394 +1,391 b'' | |||
|
1 | 1 | |
|
2 | 2 | (in-package txt2web.lib) |
|
3 | 3 | |
|
4 | 4 | ;;;; Parenscript macros which make the parser's intermediate |
|
5 | 5 | ;;;; representation directly compilable by Parenscript |
|
6 | 6 | ;;;; Some utility macros for other .ps sources too. |
|
7 | 7 | |
|
8 | 8 | ;;;; Block type | Has own locals | Has labels | async |
|
9 | 9 | ;;; Location | TRUE | TRUE | TRUE |
|
10 | 10 | ;;; Act | TRUE | TRUE | TRUE |
|
11 | 11 | ;;; {} | TRUE | TRUE | TRUE |
|
12 | 12 | ;;; IF | FALSE | TRUE | TRUE |
|
13 | 13 | ;;; FOR | FALSE | TRUE | TRUE |
|
14 | 14 | ;;; |
|
15 | 15 | ;;; IF and FOR are actually not blocks at all. They're implemented as Javascript's if and for loops. |
|
16 | 16 | ;;; Jumps back are also optimized to Javascript's while loops. |
|
17 | 17 | |
|
18 | 18 | ;;; Utils |
|
19 | 19 | |
|
20 | 20 | ;;; Common |
|
21 | 21 | |
|
22 | 22 | (defpsmacro label-block (() &body body) |
|
23 | 23 | (let ((has-labels (some #'keywordp body))) |
|
24 | 24 | `(block nil |
|
25 | 25 | ,@(when has-labels |
|
26 | 26 | '((var _labels (list)))) |
|
27 | 27 | (tagbody |
|
28 | 28 | ,@body |
|
29 | 29 | (void))))) |
|
30 | 30 | |
|
31 | 31 | (defpsmacro str (&rest forms) |
|
32 | 32 | (cond ((zerop (length forms)) |
|
33 | 33 | "") |
|
34 | 34 | ((and (= 1 (length forms)) |
|
35 | 35 | (stringp (first forms))) |
|
36 | 36 | (first forms)) |
|
37 | 37 | (t |
|
38 | 38 | `(& ,@forms)))) |
|
39 | 39 | |
|
40 | 40 | (defpsmacro locals-block (&body body) |
|
41 | 41 | "Includes labels too (through qsp-lambda)" |
|
42 | 42 | (let ((*locals* nil)) |
|
43 | 43 | (walker:walk 'locals body) |
|
44 | 44 | `(qsp-lambda |
|
45 | 45 | (create-locals ,*locals*) |
|
46 | 46 | ,@(walker:walk 'apply-vars body)))) |
|
47 | 47 | |
|
48 | 48 | ;;; 1loc |
|
49 | 49 | |
|
50 | 50 | (defparameter *special-variables* |
|
51 | 51 | '((usehtml 0) |
|
52 | 52 | (result 0) |
|
53 | 53 | ($result 0) |
|
54 | 54 | ($ongload 0) |
|
55 | 55 | ($ongsave 0) |
|
56 | 56 | ($onobjadd 0) |
|
57 | 57 | ($onobjdel 0) |
|
58 | 58 | ($onobjsel 0) |
|
59 | 59 | ($onnewloc 0) |
|
60 | 60 | ($onactsel 0) |
|
61 | 61 | ($counter 0) |
|
62 | 62 | ($usercom 0))) |
|
63 | 63 | |
|
64 | 64 | (defpsmacro game ((name) &body body) |
|
65 | 65 | (setf body (walker:walk 'for-transform body)) |
|
66 | 66 | (setf *globals* *special-variables*) |
|
67 | 67 | (walker:walk 'globals body) |
|
68 | 68 | `(progn |
|
69 | 69 | ;; Game object |
|
70 | 70 | (setf (@ *games ,name) |
|
71 | 71 | (create)) |
|
72 | 72 | ;; Global variables from this game |
|
73 | 73 | (create-globals ,*globals*) |
|
74 | 74 | ;; Locations |
|
75 | 75 | ,@(loop :for location :in body |
|
76 | 76 | :collect `(setf (@ *games ,name ,(caadr location)) |
|
77 | 77 | ,location)))) |
|
78 | 78 | |
|
79 | 79 | (defpsmacro location ((name) &body body) |
|
80 | 80 | (declare (ignore name)) |
|
81 | 81 | "Name is used by the game macro above" |
|
82 | 82 | `(locals-block ,@body)) |
|
83 | 83 | |
|
84 | 84 | (defpsmacro goto% (target &rest args) |
|
85 | 85 | `(progn |
|
86 | 86 | (goto ,target ,args) |
|
87 | 87 | (exit))) |
|
88 | 88 | |
|
89 | 89 | (defpsmacro xgoto% (target &rest args) |
|
90 | 90 | `(progn |
|
91 | 91 | (xgoto ,target ,args) |
|
92 | 92 | (exit))) |
|
93 | 93 | |
|
94 | 94 | ;;; 2var |
|
95 | 95 | |
|
96 | 96 | (defvar *globals* nil) |
|
97 | 97 | (defvar *locals* nil) |
|
98 | 98 | |
|
99 | 99 | (defpsmacro create-globals (globals) |
|
100 | 100 | (flet ((indexes (name) |
|
101 | 101 | (remove nil |
|
102 | 102 | (remove-if #'listp |
|
103 | 103 | (mapcar #'second |
|
104 | 104 | (remove name globals |
|
105 | 105 | :key #'first |
|
106 | 106 | :test-not #'eq)))))) |
|
107 | 107 | (let ((names (remove-duplicates (mapcar #'first globals)))) |
|
108 | 108 | `(chain *object |
|
109 | 109 | (assign *globals |
|
110 | 110 | (create |
|
111 | 111 | ,@(loop :for sym :in names |
|
112 | 112 | :for indexes := (indexes sym) |
|
113 | 113 | :for name := (string-upcase sym) |
|
114 | 114 | :append `(,name |
|
115 | 115 | (api-call new-var ,name ,@indexes))))))))) |
|
116 | 116 | |
|
117 | 117 | (walker:deftransform globals qspvar (&rest var) |
|
118 | 118 | (pushnew var *globals* :test #'equal) |
|
119 | 119 | (walker:walk-continue)) |
|
120 | 120 | |
|
121 | 121 | (walker:deftransform globals local (var &rest expr) |
|
122 | 122 | (declare (ignore var)) |
|
123 | 123 | (walker:walk 'globals expr)) |
|
124 | 124 | |
|
125 | 125 | (defpsmacro create-locals (locals) |
|
126 | 126 | (when locals |
|
127 | 127 | `(progn |
|
128 | 128 | (var locals (create |
|
129 | 129 | ,@(loop :for (sym index) :in locals |
|
130 | 130 | :for name := (string-upcase sym) |
|
131 | 131 | :append `(,name (api-call new-var ,name)))))))) |
|
132 | 132 | |
|
133 | 133 | ;; locations, blocks, and acts all have their own locals namespace |
|
134 | 134 | (walker:deftransform-stop locals qspblock) |
|
135 | 135 | (walker:deftransform-stop locals act) |
|
136 | 136 | |
|
137 | 137 | (walker:deftransform locals local (var &optional expr) |
|
138 | 138 | (declare (ignore expr)) |
|
139 | 139 | (pushnew (rest var) *locals* :test #'equal) |
|
140 | 140 | nil) |
|
141 | 141 | |
|
142 | 142 | ;; index types: |
|
143 | 143 | ;; literal number |
|
144 | 144 | ;; literal string |
|
145 | 145 | ;; variable number |
|
146 | 146 | ;; variable string |
|
147 | 147 | ;; expression (may be possible to determine if it's a string or a number) |
|
148 | 148 | |
|
149 | 149 | (defun $-var-p (sym) |
|
150 | 150 | (char= #\$ (elt (string-upcase (symbol-name sym)) 0))) |
|
151 | 151 | |
|
152 | 152 | (defun literal-string-p (form) |
|
153 | 153 | (and (listp form) |
|
154 | 154 | (= 2 (length form)) |
|
155 | 155 | (eq 'str (first form)) |
|
156 | 156 | (stringp (second form)))) |
|
157 | 157 | |
|
158 | 158 | (defun variable-number-p (form) |
|
159 | 159 | (and (listp form) |
|
160 | 160 | (eq 'qspvar (first form)) |
|
161 | 161 | (not ($-var-p (second form))))) |
|
162 | 162 | |
|
163 | 163 | (defun variable-string-p (form) |
|
164 | 164 | (and (listp form) |
|
165 | 165 | (eq 'qspvar (first form)) |
|
166 | 166 | ($-var-p (second form)))) |
|
167 | 167 | |
|
168 | 168 | (walker:deftransform apply-vars set (var expr) |
|
169 | 169 | (destructuring-bind (qspvar name index) |
|
170 | 170 | var |
|
171 | 171 | (declare (ignore qspvar)) |
|
172 | 172 | (setf name (string-upcase name)) |
|
173 | 173 | (let ((slot `(getprop |
|
174 | 174 | ,(if (member name *locals* :key #'first) |
|
175 | 175 | 'locals '*globals) |
|
176 | 176 | ,name)) |
|
177 | 177 | (index (walker:walk 'apply-vars index)) |
|
178 | 178 | (value (walker:walk 'apply-vars expr))) |
|
179 | 179 | (cond |
|
180 | 180 | ((member name api:*serv-vars* :test #'equalp) |
|
181 | 181 | `(api:set-serv-var ,name ,index ,value)) |
|
182 | 182 | ((null index) |
|
183 | 183 | `(chain (elt ,slot) (push ,value))) |
|
184 | 184 | ((or (numberp index) |
|
185 | 185 | (variable-number-p index)) |
|
186 | 186 | `(setf (elt ,slot ,index) ,value)) |
|
187 | 187 | ((or (literal-string-p index) |
|
188 | 188 | (variable-string-p index)) |
|
189 | 189 | `(api:set-str-element ,slot ,index ,value)) |
|
190 | 190 | (t |
|
191 | 191 | `(api:set-any-element ,slot ,index ,value)))))) |
|
192 | 192 | |
|
193 | 193 | (walker:deftransform apply-vars local (var &optional expr) |
|
194 | 194 | ;; TODO: var can't be a service variable |
|
195 | 195 | (when expr |
|
196 | 196 | (walker:walk 'apply-vars (list 'set var expr)))) |
|
197 | 197 | |
|
198 | 198 | (walker:deftransform apply-vars qspvar (name index) |
|
199 | 199 | (let ((slot `(getprop |
|
200 | 200 | ,(if (member name *locals* :key #'first) 'locals '*globals) |
|
201 | 201 | ,(string-upcase name)))) |
|
202 | 202 | (cond |
|
203 | 203 | ((null index) |
|
204 | 204 | `(elt ,slot (1- (length ,slot)))) |
|
205 | 205 | ((or (numberp index) |
|
206 | 206 | (variable-number-p index)) |
|
207 | 207 | `(elt ,slot ,(walker:walk-continue index))) |
|
208 | 208 | ((or (literal-string-p index) |
|
209 | 209 | (variable-string-p index)) |
|
210 | 210 | `(elt ,slot (@ ,slot :indexes ,(walker:walk-continue index)))) |
|
211 | 211 | (t |
|
212 | 212 | `(get-element ,slot ,(walker:walk-continue index)))))) |
|
213 | 213 | |
|
214 | 214 | (walker:deftransform apply-vars qspblock (&rest block) |
|
215 | 215 | (declare (ignore block)) |
|
216 | 216 | (walker:whole)) |
|
217 | 217 | (walker:deftransform apply-vars act (&rest block) |
|
218 | 218 | (declare (ignore block)) |
|
219 | 219 | (walker:whole)) |
|
220 | 220 | (walker:deftransform apply-vars qspfor (var from to step body) |
|
221 | 221 | (list* 'qspfor var (mapcar #'walker:walk-continue (list from to step body)))) |
|
222 | 222 | |
|
223 | 223 | ;;; 3expr |
|
224 | 224 | |
|
225 | 225 | (defpsmacro <> (op1 op2) |
|
226 | 226 | `(not (equal ,op1 ,op2))) |
|
227 | 227 | |
|
228 | (defpsmacro ! (op1 op2) | |
|
229 | `(not (equal ,op1 ,op2))) | |
|
230 | ||
|
231 | 228 | (defpsmacro qspmod (&rest ops) |
|
232 | 229 | (case (length ops) |
|
233 | 230 | (1 (first ops)) |
|
234 | 231 | (2 `(mod ,@ops)) |
|
235 | 232 | (t `(mod ,(first ops) (qspmod ,@(rest ops)))))) |
|
236 | 233 | |
|
237 | 234 | ;;; 4code |
|
238 | 235 | |
|
239 | 236 | (defpsmacro exec (&body body) |
|
240 | 237 | (format nil "javascript: ~{~A~^~%~}" (mapcar #'ps* body))) |
|
241 | 238 | |
|
242 | 239 | ;;; 5arrays |
|
243 | 240 | |
|
244 | 241 | ;;; 6str |
|
245 | 242 | |
|
246 | 243 | (defpsmacro & (&rest args) |
|
247 | 244 | `(chain "" (concat ,@args))) |
|
248 | 245 | |
|
249 | 246 | ;;; 7if |
|
250 | 247 | |
|
251 | 248 | (defpsmacro qspcond (&rest clauses) |
|
252 | 249 | `(cond ,@(loop :for clause :in clauses |
|
253 | 250 | :for f := (if (eq 'txt2web::else (first clause)) |
|
254 | 251 | 't |
|
255 | 252 | (first clause)) |
|
256 | 253 | :collect (list f |
|
257 | 254 | `(tagbody |
|
258 | 255 | ,@(rest clause)))))) |
|
259 | 256 | |
|
260 | 257 | ;;; 8sub |
|
261 | 258 | |
|
262 | 259 | ;;; 9jump |
|
263 | 260 | ;;; Yep, that's a huge kludge since Javascript doesn't support arbitrary labels |
|
264 | 261 | |
|
265 | 262 | (defpsmacro jump (target) |
|
266 | 263 | `(return-from label-body ,(string-upcase (second target)))) |
|
267 | 264 | |
|
268 | 265 | (defpsmacro tagbody (&body body) |
|
269 | 266 | (let ((create-locals (if (eq (caar body) 'create-locals) |
|
270 | 267 | (list (car body)))) |
|
271 | 268 | (void (if (equal (car (last body)) '(void)) |
|
272 | 269 | '((void))))) |
|
273 | 270 | (when create-locals |
|
274 | 271 | (setf body (cdr body))) |
|
275 | 272 | (when void |
|
276 | 273 | (setf body (butlast body))) |
|
277 | 274 | (let ((funcs (list nil "_nil"))) |
|
278 | 275 | (dolist (form body) |
|
279 | 276 | (cond ((keywordp form) |
|
280 | 277 | (setf (first funcs) (reverse (first funcs))) |
|
281 | 278 | (push (string-upcase form) funcs) |
|
282 | 279 | (push nil funcs)) |
|
283 | 280 | (t |
|
284 | 281 | (push form (first funcs))))) |
|
285 | 282 | (setf (first funcs) (reverse (first funcs))) |
|
286 | 283 | (setf funcs (reverse funcs)) |
|
287 | 284 | `(progn |
|
288 | 285 | ,@create-locals |
|
289 | 286 | ,(if (= 2 (length funcs)) |
|
290 | 287 | `(progn |
|
291 | 288 | ,@body) |
|
292 | 289 | `(progn |
|
293 | 290 | (tagbody-blocks ,funcs) |
|
294 | 291 | (loop |
|
295 | 292 | :for _nextblock |
|
296 | 293 | := :_nil |
|
297 | 294 | :then (await (funcall (getprop _labels _nextblock))) |
|
298 | 295 | :while _nextblock))) |
|
299 | 296 | ,@void)))) |
|
300 | 297 | |
|
301 | 298 | (defvar *current-label*) |
|
302 | 299 | (defvar *has-jump-back*) |
|
303 | 300 | (walker:deftransform optimize-jump jump (target) |
|
304 | 301 | (cond ((string= (string-upcase (second target)) *current-label*) |
|
305 | 302 | (setf *has-jump-back* t) |
|
306 | 303 | '(continue)) |
|
307 | 304 | (t |
|
308 | 305 | (walker:walk-continue)))) |
|
309 | 306 | |
|
310 | 307 | (defpsmacro tagbody-blocks (funcs) |
|
311 | 308 | `(setf ,@(loop :for (label code . rest-labels) :on funcs :by #'cddr |
|
312 | 309 | :append `((@ _labels ,label) |
|
313 | 310 | (async-lambda () |
|
314 | 311 | (block label-body |
|
315 | 312 | (tagbody-block-body ,label ,code |
|
316 | 313 | ,(first rest-labels)))))))) |
|
317 | 314 | |
|
318 | 315 | (defpsmacro tagbody-block-body (label code next-label) |
|
319 | 316 | (let ((*current-label* label) |
|
320 | 317 | (*has-jump-back* nil)) |
|
321 | 318 | (let ((code (walker:walk 'optimize-jump code))) |
|
322 | 319 | (if *has-jump-back* |
|
323 | 320 | `(progn |
|
324 | 321 | (loop :do (progn |
|
325 | 322 | ,@code |
|
326 | 323 | (break))) |
|
327 | 324 | ,@(if next-label |
|
328 | 325 | (list next-label) |
|
329 | 326 | nil)) |
|
330 | 327 | `(progn |
|
331 | 328 | ,@code |
|
332 | 329 | ,@(if next-label |
|
333 | 330 | (list next-label) |
|
334 | 331 | nil)))))) |
|
335 | 332 | |
|
336 | 333 | (defpsmacro exit () |
|
337 | 334 | '(return-from nil (values))) |
|
338 | 335 | |
|
339 | 336 | ;;; 10dynamic |
|
340 | 337 | |
|
341 | 338 | (defpsmacro qspblock (&body body) |
|
342 | 339 | `(locals-block |
|
343 | 340 | ,@body)) |
|
344 | 341 | |
|
345 | 342 | (defpsmacro qsp-lambda (&body body) |
|
346 | 343 | `(async-lambda (args) |
|
347 | 344 | (label-block () |
|
348 | 345 | ,@body))) |
|
349 | 346 | |
|
350 | 347 | ;;; 11main |
|
351 | 348 | |
|
352 | 349 | (defpsmacro act (name img &body body) |
|
353 | 350 | `(api-call add-act ,name ,img |
|
354 | 351 | (locals-block |
|
355 | 352 | ,@body))) |
|
356 | 353 | |
|
357 | 354 | ;;; 12aux |
|
358 | 355 | |
|
359 | 356 | ;;; 13diag |
|
360 | 357 | |
|
361 | 358 | ;;; 14act |
|
362 | 359 | |
|
363 | 360 | ;;; 15objs |
|
364 | 361 | |
|
365 | 362 | ;;; 16menu |
|
366 | 363 | |
|
367 | 364 | ;;; 17sound |
|
368 | 365 | |
|
369 | 366 | ;;; 18img |
|
370 | 367 | |
|
371 | 368 | ;;; 19input |
|
372 | 369 | |
|
373 | 370 | ;;; 20time |
|
374 | 371 | |
|
375 | 372 | ;;; 21local |
|
376 | 373 | |
|
377 | 374 | ;;; 22loop |
|
378 | 375 | |
|
379 | 376 | (defpsmacro qsploop (init cond step &body body) |
|
380 | 377 | `(progn |
|
381 | 378 | ,init |
|
382 | 379 | (loop :while ,cond |
|
383 | 380 | :do (progn |
|
384 | 381 | ,@body |
|
385 | 382 | ,step)))) |
|
386 | 383 | |
|
387 | 384 | ;; Transform because it creates a (set ...) hence it has to be processed |
|
388 | 385 | ;; before the apply-vars transform. And macros are processed after all |
|
389 | 386 | ;; the transforms |
|
390 | 387 | (walker:deftransform for-transform qspfor (var from to step &rest body) |
|
391 | 388 | `(loop :for i :from ,from :to ,to :by ,step |
|
392 | 389 | :do (set ,var i) |
|
393 | 390 | :do (block nil |
|
394 | 391 | ,@(walker:walk-continue body)))) |
|
1 | NO CONTENT: file was removed |
General Comments 0
You need to be logged in to leave comments.
Login now