Show More
@@ -1,44 +1,44 b'' | |||
|
1 | 1 | (инструкции на Русском - внизу) |
|
2 | 2 | |
|
3 | 3 | # sugar-qsp |
|
4 | 4 | Compiler for QSP games which creates monolithic HTML pages. |
|
5 | 5 | |
|
6 | 6 | ## Usage |
|
7 | 7 | |
|
8 | 8 | There are three mastery levels |
|
9 | 9 | |
|
10 | 10 | 1. Just build me the game:<br/> |
|
11 | 11 | `sugar-qsp game.txt`<br/> |
|
12 | 12 | And it will create the game in game.html |
|
13 | 13 | |
|
14 | 14 | 2. I know what I'm doing:<br/> |
|
15 | 15 | `sugar-qsp game.txt -o game.html --body body.html --js jquery.js my-js-library.js --css styles/*.css`<br/> |
|
16 | 16 | All options are self-explanatory. The result is a monolithic html specified |
|
17 | 17 | with the `-o` option. Default `body.html` (used by the first mastery level) can |
|
18 | 18 | be found in `extas` directory. |
|
19 | 19 | |
|
20 | 20 | 3. I'm a frontend developer!<br/> |
|
21 | 21 | `sugar-qsp game.txt -c -o game.js`<br/> |
|
22 | 22 | It just builds the game script into a js you can put on your website. To run |
|
23 | 23 | the game execute `SugarQSP.start()` |
|
24 | 24 | |
|
25 | 25 | # sugar-qsp |
|
26 | 26 | Компилятор для игр на QSP создающий монолитные страницы на HTML. |
|
27 | 27 | |
|
28 | 28 | ## Инструкции |
|
29 | 29 | |
|
30 | 30 | Есть три уровня мастерства. |
|
31 | 31 | |
|
32 | 32 | 1. **Просто собери мне игру**:<br/> |
|
33 | 33 | `sugar-qsp game.txt`<br/> |
|
34 | 34 | Создаст игру в game.html |
|
35 | 35 | |
|
36 | 36 | 2. **Я знаю что делаю**:<br/> |
|
37 | 37 | `sugar-qsp game.txt -o game.html --body body.html --js jquery.js my-js-library.js --css styles/*.css`<br/> |
|
38 | Если вы знаете что делаете, то для вас смысл опций очевиден. `body.html` | |
|
39 |
|
|
|
38 | Если вы знаете что делаете, то для вас смысл опций очевиден. `body.html` и `default.css` | |
|
39 | лежат в каталоге `extras`. | |
|
40 | 40 | |
|
41 | 41 | 3. **Я - фронтендер!**<br/> |
|
42 | 42 | `sugar-qsp game.txt -c -o game.js`<br/> |
|
43 | 43 | Просто соберёт игру в Javascript файл который вы можете разместить на своём |
|
44 | сайте как вам угодно. Для запуска игры вызовите `SugarQSP.start()`. | |
|
44 | сайте как вам угодно. |
@@ -1,140 +1,144 b'' | |||
|
1 | 1 | |
|
2 | 2 | (in-package sugar-qsp) |
|
3 | 3 | |
|
4 | 4 | ;;;; Macros implementing some intrinsics where it makes sense |
|
5 | 5 | ;;;; E.g. an equivalent JS function exists, or it's a direct API call |
|
6 | 6 | |
|
7 | 7 | ;;; 1loc |
|
8 | 8 | |
|
9 | 9 | ;;; 2var |
|
10 | 10 | |
|
11 | 11 | (ps:defpsmacro killvar (varname &optional (index :whole)) |
|
12 | 12 | `(api-call kill-var ,varname ,index)) |
|
13 | 13 | |
|
14 | 14 | (ps:defpsmacro killall () |
|
15 | 15 | `(api-call kill-all)) |
|
16 | 16 | |
|
17 | 17 | ;;; 3expr |
|
18 | 18 | |
|
19 | 19 | (ps:defpsmacro obj (name) |
|
20 | 20 | `(funcall (root objs includes) ,name)) |
|
21 | 21 | |
|
22 | 22 | (ps:defpsmacro loc (name) |
|
23 | 23 | `(funcall (root locs includes) ,name)) |
|
24 | 24 | |
|
25 | 25 | (ps:defpsmacro no (arg) |
|
26 | 26 | `(- -1 ,arg)) |
|
27 | 27 | |
|
28 | 28 | ;;; 4code |
|
29 | 29 | |
|
30 | 30 | (ps:defpsmacro qspver () |
|
31 | 31 | "0.0.1") |
|
32 | 32 | |
|
33 | 33 | (ps:defpsmacro curloc () |
|
34 | 34 | `(root current-location)) |
|
35 | 35 | |
|
36 | 36 | (ps:defpsmacro rnd () |
|
37 | 37 | `(funcall (root lib rand) 1 1000)) |
|
38 | 38 | |
|
39 | 39 | (ps:defpsmacro qspmax (&rest args) |
|
40 | `(max ,@args)) | |
|
40 | (if (= 1 (length args)) | |
|
41 | `(*math.max.apply nil ,@args) | |
|
42 | `(*math.max ,@args))) | |
|
41 | 43 | |
|
42 | 44 | (ps:defpsmacro qspmin (&rest args) |
|
43 | `(min ,@args)) | |
|
45 | (if (= 1 (length args)) | |
|
46 | `(*math.min.apply nil ,@args) | |
|
47 | `(*math.min ,@args))) | |
|
44 | 48 | |
|
45 | 49 | ;;; 5arrays |
|
46 | 50 | |
|
47 | 51 | (ps:defpsmacro arrsize (name) |
|
48 | 52 | `(api-call array-size ,name)) |
|
49 | 53 | |
|
50 | 54 | ;;; 6str |
|
51 | 55 | |
|
52 | 56 | (ps:defpsmacro len (s) |
|
53 | 57 | `(length ,s)) |
|
54 | 58 | |
|
55 | 59 | (ps:defpsmacro mid (s from &optional count) |
|
56 | 60 | `(ps:chain ,s (substring ,from ,count))) |
|
57 | 61 | |
|
58 | 62 | (ps:defpsmacro ucase (s) |
|
59 | 63 | `(ps:chain ,s (to-upper-case))) |
|
60 | 64 | |
|
61 | 65 | (ps:defpsmacro lcase (s) |
|
62 | 66 | `(ps:chain ,s (to-lower-case))) |
|
63 | 67 | |
|
64 | 68 | (ps:defpsmacro trim (s) |
|
65 | 69 | `(ps:chain ,s (trim))) |
|
66 | 70 | |
|
67 | 71 | (ps:defpsmacro replace (s from to) |
|
68 | 72 | `(ps:chain ,s (replace ,from ,to))) |
|
69 | 73 | |
|
70 | 74 | (ps:defpsmacro val (s) |
|
71 | 75 | `(parse-int ,s 10)) |
|
72 | 76 | |
|
73 | 77 | (ps:defpsmacro qspstr (n) |
|
74 | 78 | `(ps:chain ,n (to-string))) |
|
75 | 79 | |
|
76 | 80 | ;;; 7if |
|
77 | 81 | |
|
78 | 82 | ;;; 8sub |
|
79 | 83 | |
|
80 | 84 | ;;; 9loops |
|
81 | 85 | |
|
82 | 86 | ;; JUMP is in ps-macros.lisp (because it's a part of a huge kludge) |
|
83 | 87 | |
|
84 | 88 | (ps:defpsmacro exit () |
|
85 | 89 | `(return-from nil (values))) |
|
86 | 90 | |
|
87 | 91 | ;;; 10dynamic |
|
88 | 92 | |
|
89 | 93 | ;;; 11main |
|
90 | 94 | |
|
91 | 95 | (ps:defpsmacro desc (s) |
|
92 | 96 | (declare (ignore s)) |
|
93 | 97 | "") |
|
94 | 98 | |
|
95 | 99 | ;;; 12stat |
|
96 | 100 | |
|
97 | 101 | (ps:defpsmacro showstat (enable) |
|
98 | 102 | `(api-call enable-frame :stat ,enable)) |
|
99 | 103 | |
|
100 | 104 | ;;; 13diag |
|
101 | 105 | |
|
102 | 106 | (ps:defpsmacro msg (text) |
|
103 | 107 | `(alert ,text)) |
|
104 | 108 | |
|
105 | 109 | ;;; 14act |
|
106 | 110 | |
|
107 | 111 | (ps:defpsmacro showacts (enable) |
|
108 | 112 | `(api-call enable-frame :acts ,enable)) |
|
109 | 113 | |
|
110 | 114 | (ps:defpsmacro delact (name) |
|
111 | 115 | `(api-call del-act ,name)) |
|
112 | 116 | |
|
113 | 117 | (ps:defpsmacro cla () |
|
114 | 118 | `(api-call clear-act)) |
|
115 | 119 | |
|
116 | 120 | ;;; 15objs |
|
117 | 121 | |
|
118 | 122 | (ps:defpsmacro showobjs (enable) |
|
119 | 123 | `(api-call enable-frame :objs ,enable)) |
|
120 | 124 | |
|
121 | 125 | (ps:defpsmacro countobj () |
|
122 | 126 | `(length (root objs))) |
|
123 | 127 | |
|
124 | 128 | (ps:defpsmacro getobj (index) |
|
125 | 129 | `(or (elt (root objs) ,index) "")) |
|
126 | 130 | |
|
127 | 131 | ;;; 16menu |
|
128 | 132 | |
|
129 | 133 | ;;; 17sound |
|
130 | 134 | |
|
131 | 135 | (ps:defpsmacro isplay (filename) |
|
132 | 136 | `(funcall (root playing includes) ,filename)) |
|
133 | 137 | |
|
134 | 138 | ;;; 18img |
|
135 | 139 | |
|
136 | 140 | ;;; 19input |
|
137 | 141 | |
|
138 | 142 | ;;; 20time |
|
139 | 143 | |
|
140 | 144 | ;;; misc |
@@ -1,571 +1,580 b'' | |||
|
1 | 1 | |
|
2 | 2 | (in-package sugar-qsp) |
|
3 | 3 | |
|
4 | 4 | ;;;; Parses TXT source to an intermediate representation |
|
5 | 5 | |
|
6 | 6 | ;;; Utility |
|
7 | 7 | |
|
8 | 8 | (defun remove-nth (list nth) |
|
9 | 9 | (append (subseq list 0 nth) |
|
10 | 10 | (subseq list (1+ nth)))) |
|
11 | 11 | |
|
12 | 12 | (defun not-quote (char) |
|
13 | 13 | (not (eql #\' char))) |
|
14 | 14 | |
|
15 | 15 | |
|
16 | 16 | (defun not-doublequote (char) |
|
17 | 17 | (not (eql #\" char))) |
|
18 | 18 | |
|
19 | 19 | (defun not-brace (char) |
|
20 | 20 | (not (eql #\} char))) |
|
21 | 21 | |
|
22 | 22 | (defun not-integer (string) |
|
23 | 23 | (when (find-if-not #'digit-char-p string) |
|
24 | 24 | t)) |
|
25 | 25 | |
|
26 | 26 | (defun not-newline (char) |
|
27 | 27 | (not (eql #\newline char))) |
|
28 | 28 | |
|
29 | 29 | (defun id-any-char (char) |
|
30 | 30 | (and |
|
31 | 31 | (not (digit-char-p char)) |
|
32 | 32 | (not (eql #\newline char)) |
|
33 | 33 | (not (find char " !:&=<>+-*/,'\"()[]{}")))) |
|
34 | 34 | |
|
35 | 35 | (defun intern-first (list) |
|
36 | 36 | (list* (intern (string-upcase (first list))) |
|
37 | 37 | (rest list))) |
|
38 | 38 | |
|
39 | 39 | (eval-when (:compile-toplevel :load-toplevel :execute) |
|
40 | 40 | (defun remove-nil (list) |
|
41 | 41 | (remove nil list))) |
|
42 | 42 | |
|
43 | 43 | (defun binop-rest (list) |
|
44 | 44 | (destructuring-bind (ws1 operator ws2 operand2) |
|
45 | 45 | list |
|
46 | 46 | (declare (ignore ws1 ws2)) |
|
47 | 47 | (list (intern (string-upcase operator)) operand2))) |
|
48 | 48 | |
|
49 | 49 | (defun do-binop% (left-op other-ops) |
|
50 | 50 | (if (null other-ops) |
|
51 | 51 | left-op |
|
52 | 52 | (destructuring-bind ((operator right-op) &rest rest-ops) |
|
53 | 53 | other-ops |
|
54 | 54 | (if (and (listp left-op) |
|
55 | 55 | (eq (first left-op) |
|
56 | 56 | operator)) |
|
57 | 57 | (do-binop% (append left-op (list right-op)) rest-ops) |
|
58 | 58 | (do-binop% (list operator left-op right-op) rest-ops))))) |
|
59 | 59 | |
|
60 | 60 | (defun do-binop (list) |
|
61 | 61 | (destructuring-bind (left-op rest-ops) |
|
62 | 62 | list |
|
63 | 63 | (do-binop% left-op |
|
64 | 64 | (mapcar #'binop-rest rest-ops)))) |
|
65 | 65 | |
|
66 | 66 | (p:defrule line-continuation (and #\_ #\newline) |
|
67 | 67 | (:constant nil)) |
|
68 | 68 | |
|
69 | 69 | (p:defrule text-spaces (+ (or #\space #\tab line-continuation)) |
|
70 | 70 | (:text t)) |
|
71 | 71 | |
|
72 | 72 | (p:defrule spaces (+ (or #\space #\tab line-continuation)) |
|
73 | 73 | (:constant nil)) |
|
74 | 74 | |
|
75 | 75 | (p:defrule spaces? (* (or #\space #\tab line-continuation)) |
|
76 | 76 | (:constant nil)) |
|
77 | 77 | |
|
78 | 78 | (p:defrule colon #\: |
|
79 | 79 | (:constant nil)) |
|
80 | 80 | |
|
81 | 81 | (p:defrule alphanumeric (alphanumericp character)) |
|
82 | 82 | |
|
83 | 83 | (p:defrule not-newline (not-newline character)) |
|
84 | 84 | |
|
85 | 85 | (p:defrule squote-esc "''" |
|
86 | 86 | (:lambda (list) |
|
87 | 87 | (p:text (elt list 0)))) |
|
88 | 88 | |
|
89 | 89 | (p:defrule dquote-esc "\"\"" |
|
90 | 90 | (:lambda (list) |
|
91 | 91 | (p:text (elt list 0)))) |
|
92 | 92 | |
|
93 | 93 | (p:defrule sstring-chars (+ (and (and (p:! "<<") (p:! (p:~ "\"exec:"))) |
|
94 | 94 | (or squote-esc (not-quote character)))) |
|
95 | 95 | (:lambda (list) |
|
96 | 96 | (p:text (mapcar #'second list)))) |
|
97 | 97 | |
|
98 | 98 | (p:defrule dstring-chars (+ (and (and (p:! "<<") (p:! (p:~ "'exec:"))) |
|
99 | 99 | (or dquote-esc (not-doublequote character)))) |
|
100 | 100 | (:lambda (list) |
|
101 | 101 | (p:text (mapcar #'second list)))) |
|
102 | 102 | |
|
103 | 103 | ;;; Identifiers |
|
104 | 104 | |
|
105 | 105 | ;; From the official docs |
|
106 |
(defparameter *keywords* '(act addlib addobj addqst and arrcomp arrpos arrsize cla clear *clear close clr *clr cls cmdclear cmdclr copyarr |
|
|
106 | (defparameter *keywords* '(act addlib addobj addqst and arrcomp arrpos arrsize cla clear *clear close clr *clr cls cmdclear cmdclr copyarr counter countobj curacts curloc debug delact dellib delobj desc disablescroll disablesubex dynamic dyneval else elseif end exit fcolor fname freelib fsize func getobj gosub goto gs gt if iif inclib input instr isnum isplay jump killall killobj killqst killvar lcase lcolor len let loc maintxt max menu mid min mod msecscount msg nl *nl no nosave obj onactsel ongload ongsave onnewloc onobjadd onobjdel onobjsel opengame openqst or p *p pl *pl play qspver rand refint replace rgb rnd savegame selact selobj set settimer showacts showinput showobjs showstat stattxt str strcomp strfind strpos trim ucase unsel unselect usercom user_text usrtxt val view wait xgoto xgt)) | |
|
107 | ||
|
108 | (defun trim-$ (str) | |
|
109 | (if (char= #\$ (elt str 0)) | |
|
110 | (subseq str 1) | |
|
111 | str)) | |
|
107 | 112 | |
|
108 | 113 | (defun qsp-keyword-p (id) |
|
109 | (member (intern (string-upcase id)) *keywords*)) | |
|
114 | (member (intern (trim-$ (string-upcase id))) *keywords*)) | |
|
110 | 115 | |
|
111 | 116 | (defun not-qsp-keyword-p (id) |
|
112 | (not (member (intern (string-upcase id)) *keywords*))) | |
|
117 | (not (member (intern (trim-$ (string-upcase id))) *keywords*))) | |
|
113 | 118 | |
|
114 | 119 | (p:defrule qsp-keyword (qsp-keyword-p identifier-raw)) |
|
115 | 120 | |
|
116 | 121 | (p:defrule id-first (id-any-char character)) |
|
117 | 122 | (p:defrule id-next (or (id-any-char character) |
|
118 | 123 | (digit-char-p character))) |
|
119 | 124 | (p:defrule identifier-raw (and id-first (* id-next)) |
|
120 | 125 | (:lambda (list) |
|
121 |
( |
|
|
122 | (when (member id *keywords*) | |
|
123 | (error "~A is a keyword" id)) | |
|
124 | (intern (string-upcase id))))) | |
|
126 | (intern (string-upcase (p:text list))))) | |
|
125 | 127 | |
|
126 | 128 | (p:defrule identifier (not-qsp-keyword-p identifier-raw)) |
|
127 | 129 | |
|
128 | 130 | ;;; Strings |
|
129 | 131 | |
|
130 | 132 | (p:defrule qsp-string (or normal-string brace-string)) |
|
131 | 133 | |
|
132 | 134 | (p:defrule normal-string (or sstring dstring) |
|
133 | 135 | (:lambda (str) |
|
134 | 136 | (list* 'str (or str (list ""))))) |
|
135 | 137 | |
|
136 | 138 | (p:defrule sstring (and #\' (* (or string-interpol |
|
137 | 139 | sstring-exec |
|
138 | 140 | sstring-chars)) |
|
139 | 141 | #\') |
|
140 | 142 | (:function second)) |
|
141 | 143 | |
|
142 | 144 | (p:defrule dstring (and #\" (* (or string-interpol |
|
143 | 145 | dstring-exec |
|
144 | 146 | dstring-chars)) |
|
145 | 147 | #\") |
|
146 | 148 | (:function second)) |
|
147 | 149 | |
|
148 | 150 | (p:defrule string-interpol (and "<<" expression ">>") |
|
149 | 151 | (:function second)) |
|
150 | 152 | |
|
151 | 153 | (p:defrule sstring-exec-body (+ (or squote-esc (not-doublequote character))) |
|
152 | 154 | (:text t)) |
|
153 | 155 | |
|
154 | 156 | (p:defrule dstring-exec-body (+ (or dquote-esc (not-quote character))) |
|
155 | 157 | (:text t)) |
|
156 | 158 | |
|
157 | 159 | (p:defrule sstring-exec (and (p:~ "\"exec:") sstring-exec-body #\") |
|
158 | 160 | (:lambda (list) |
|
159 | 161 | (list* 'exec (p:parse 'exec-body (second list))))) |
|
160 | 162 | |
|
161 | 163 | (p:defrule dstring-exec (and (p:~ "'exec:") dstring-exec-body #\') |
|
162 | 164 | (:lambda (list) |
|
163 | 165 | (list* 'exec (p:parse 'exec-body (second list))))) |
|
164 | 166 | |
|
165 | 167 | (p:defrule brace-string (and #\{ before-statement block-body #\}) |
|
166 | 168 | (:lambda (list) |
|
167 | 169 | (list* 'qspblock (third list)))) |
|
168 | 170 | |
|
169 | 171 | ;;; Location |
|
170 | 172 | |
|
171 | 173 | (p:defrule sugar-qsp-grammar (and (* (or spaces #\newline)) |
|
172 | 174 | (* location)) |
|
173 | 175 | (:function second)) |
|
174 | 176 | |
|
175 | 177 | (p:defrule location (and location-header block-body location-end) |
|
176 | 178 | (:destructure (header body end) |
|
177 | 179 | (declare (ignore end)) |
|
178 | 180 | `(location (,header) ,@body))) |
|
179 | 181 | |
|
180 | 182 | (p:defrule location-header (and #\# |
|
181 | 183 | (+ not-newline) |
|
182 | 184 | (and #\newline spaces? before-statement)) |
|
183 | 185 | (:destructure (spaces1 name spaces2) |
|
184 | 186 | (declare (ignore spaces1 spaces2)) |
|
185 | 187 | (string-upcase (string-trim " " (p:text name))))) |
|
186 | 188 | |
|
187 | 189 | (p:defrule location-end (and #\- (* not-newline) #\newline before-statement) |
|
188 | 190 | (:constant nil)) |
|
189 | 191 | |
|
190 | 192 | ;;; Block body |
|
191 | 193 | |
|
192 | 194 | (p:defrule newline-block-body (and #\newline spaces? block-body) |
|
193 | 195 | (:function third)) |
|
194 | 196 | |
|
195 | 197 | (p:defrule block-body (* statement) |
|
196 | 198 | (:function remove-nil)) |
|
197 | 199 | |
|
198 | 200 | ;; Just for <a href="exec:...'> |
|
199 | 201 | ;; Explicitly called from that rule's production |
|
200 | 202 | (p:defrule exec-body (and before-statement line-body) |
|
201 | 203 | (:function second)) |
|
202 | 204 | |
|
203 | 205 | (p:defrule line-body (and inline-statement (* next-inline-statement)) |
|
204 | 206 | (:lambda (list) |
|
205 | 207 | (list* (first list) (second list)))) |
|
206 | 208 | |
|
207 | 209 | (p:defrule before-statement (* (or #\newline spaces)) |
|
208 | 210 | (:constant nil)) |
|
209 | 211 | |
|
210 | 212 | (p:defrule statement-end (or statement-end-real statement-end-block-close)) |
|
211 | 213 | |
|
212 | 214 | (p:defrule statement-end-real (and (or #\newline |
|
213 | 215 | (and #\& spaces? (p:& statement%))) |
|
214 | 216 | before-statement) |
|
215 | 217 | (:constant nil)) |
|
216 | 218 | |
|
217 | 219 | (p:defrule statement-end-block-close (or (p:& #\})) |
|
218 | 220 | (:constant nil)) |
|
219 | 221 | |
|
220 | 222 | (p:defrule inline-statement (and statement% spaces?) |
|
221 | 223 | (:function first)) |
|
222 | 224 | |
|
223 | 225 | (p:defrule next-inline-statement (and #\& spaces? inline-statement) |
|
224 | 226 | (:function third)) |
|
225 | 227 | |
|
226 | 228 | (p:defrule not-a-non-statement (and (p:! (p:~ "elseif")) |
|
227 | 229 | (p:! (p:~ "else")) |
|
228 | 230 | (p:! (p:~ "end")))) |
|
229 | 231 | |
|
230 | 232 | (p:defrule statement (and inline-statement statement-end) |
|
231 | 233 | (:function first)) |
|
232 | 234 | |
|
233 | 235 | (p:defrule statement% (and not-a-non-statement |
|
234 | 236 | (or label comment string-output |
|
235 | 237 | block non-returning-intrinsic assignment |
|
236 | 238 | expression-output)) |
|
237 | 239 | (:function second)) |
|
238 | 240 | |
|
239 | 241 | (p:defrule expr-stopper (or comment block non-returning-intrinsic)) |
|
240 | 242 | |
|
241 | 243 | (p:defrule string-output qsp-string |
|
242 | 244 | (:lambda (string) |
|
243 | 245 | (list 'main-pl string))) |
|
244 | 246 | |
|
245 | 247 | (p:defrule expression-output expression |
|
246 | 248 | (:lambda (list) |
|
247 | 249 | (list 'main-pl list))) |
|
248 | 250 | |
|
249 | 251 | (p:defrule label (and colon identifier) |
|
250 | 252 | (:lambda (list) |
|
251 | 253 | (intern (string (second list)) :keyword))) |
|
252 | 254 | |
|
253 |
(p:defrule comment (and #\! (* (or |
|
|
255 | (p:defrule comment (and #\! (* (or qsp-string brace-comment text-spaces not-newline))) | |
|
256 | (:constant nil)) | |
|
257 | ||
|
258 | (p:defrule brace-comment (and #\{ (* (not-brace character)) #\}) | |
|
254 | 259 | (:constant nil)) |
|
255 | 260 | |
|
256 | 261 | ;;; Blocks |
|
257 | 262 | |
|
258 | 263 | (p:defrule block (or block-act block-if)) |
|
259 | 264 | |
|
260 | 265 | (p:defrule block-if (and block-if-head block-if-body) |
|
261 | 266 | (:destructure (head body) |
|
262 | 267 | `(qspcond (,@head ,@(first body)) |
|
263 | 268 | ,@(rest body)))) |
|
264 | 269 | |
|
265 | 270 | (p:defrule block-if-head (and (p:~ "if") spaces expression spaces? colon spaces?) |
|
266 | 271 | (:function remove-nil) |
|
267 | 272 | (:function cdr)) |
|
268 | 273 | |
|
269 | 274 | (p:defrule block-if-body (or block-if-ml block-if-sl) |
|
270 | 275 | (:destructure (if-body elseifs else &rest ws) |
|
271 | 276 | (declare (ignore ws)) |
|
272 | 277 | `(,(remove-nil if-body) ,@elseifs ,@(when else `((else ,@else)))))) |
|
273 | 278 | |
|
274 | 279 | (p:defrule block-if-sl (and line-body |
|
275 | 280 | (p:? block-if-elseif-inline) |
|
276 | 281 | (p:? block-if-else-inline) |
|
277 | 282 | spaces?)) |
|
278 | 283 | |
|
279 | 284 | (p:defrule block-if-ml (and (and #\newline spaces?) |
|
280 | 285 | block-body |
|
281 | 286 | (p:? block-if-elseif) |
|
282 | 287 | (p:? block-if-else) |
|
283 | 288 | block-if-end) |
|
284 | 289 | (:lambda (list) |
|
285 | 290 | (cdr list))) |
|
286 | 291 | |
|
287 | 292 | (p:defrule block-if-elseif-inline (and block-if-elseif-head line-body (p:? block-if-elseif-inline)) |
|
288 | 293 | (:destructure (head statements elseif) |
|
289 | 294 | `((,@(rest head) ,@(remove-nil statements)) ,@elseif))) |
|
290 | 295 | |
|
291 | 296 | (p:defrule block-if-elseif (and block-if-elseif-head statement-end block-body (p:? block-if-elseif)) |
|
292 | 297 | (:destructure (head ws statements elseif) |
|
293 | 298 | (declare (ignore ws)) |
|
294 | 299 | `((,@(rest head) ,@(remove-nil statements)) ,@elseif))) |
|
295 | 300 | |
|
296 | 301 | (p:defrule block-if-elseif-head (and (p:~ "elseif") spaces expression spaces? colon spaces?) |
|
297 | 302 | (:function remove-nil) |
|
298 | 303 | (:function intern-first)) |
|
299 | 304 | |
|
300 | 305 | (p:defrule block-if-else-inline (and block-if-else-head line-body) |
|
301 | 306 | (:function second)) |
|
302 | 307 | |
|
303 | 308 | (p:defrule block-if-else (and block-if-else-head #\newline spaces? block-body) |
|
304 | 309 | (:function fourth)) |
|
305 | 310 | |
|
306 | 311 | (p:defrule block-if-else-head (and (p:~ "else") spaces?) |
|
307 | 312 | (:constant nil)) |
|
308 | 313 | |
|
309 | 314 | (p:defrule block-if-end (and (p:~ "end") |
|
310 | 315 | (p:? (and spaces (p:~ "if")))) |
|
311 | 316 | (:constant nil)) |
|
312 | 317 | |
|
313 | 318 | (p:defrule block-act (and block-act-head (or block-act-ml block-act-sl)) |
|
314 | 319 | (:lambda (list) |
|
315 | 320 | (apply #'append list))) |
|
316 | 321 | |
|
317 | 322 | (p:defrule block-act-sl line-body) |
|
318 | 323 | |
|
319 | 324 | (p:defrule block-act-ml (and newline-block-body block-act-end) |
|
320 | 325 | (:lambda (list) |
|
321 | 326 | (apply #'list* (butlast list)))) |
|
322 | 327 | |
|
323 | 328 | (p:defrule block-act-head (and (p:~ "act") spaces qsp-string spaces? |
|
324 | 329 | (p:? block-act-head-img) |
|
325 | 330 | colon spaces?) |
|
326 | 331 | (:lambda (list) |
|
327 | 332 | (intern-first (list (first list) |
|
328 | 333 | (third list) |
|
329 | 334 | (or (fifth list) '(str "")))))) |
|
330 | 335 | |
|
331 | 336 | (p:defrule block-act-head-img (and #\, spaces? qsp-string spaces?) |
|
332 | 337 | (:lambda (list) |
|
333 | 338 | (or (third list) ""))) |
|
334 | 339 | |
|
335 | 340 | (p:defrule block-act-end (and (p:~ "end")) |
|
336 | 341 | (:constant nil)) |
|
337 | 342 | |
|
338 | 343 | ;;; Calls |
|
339 | 344 | |
|
340 | 345 | (p:defrule first-argument (and expression spaces?) |
|
341 | 346 | (:function first)) |
|
342 | 347 | (p:defrule next-argument (and "," spaces? expression) |
|
343 | 348 | (:function third)) |
|
344 | 349 | (p:defrule arguments (or parenthesized-arguments plain-arguments no-arguments)) |
|
345 | 350 | (p:defrule parenthesized-arguments (and spaces? #\( base-arguments #\)) |
|
346 | 351 | (:function third)) |
|
347 | 352 | (p:defrule plain-arguments (and spaces base-arguments) |
|
348 | 353 | (:function second)) |
|
349 |
(p:defrule no-arguments (or spaces (p:& #\newline) |
|
|
354 | (p:defrule no-arguments (or (and spaces? (p:& #\newline)) | |
|
355 | (and spaces? (p:& #\&)) | |
|
356 | spaces?) | |
|
350 | 357 | (:constant nil)) |
|
351 | (p:defrule base-arguments (and first-argument (* next-argument)) | |
|
352 | (:destructure (first rest) | |
|
353 | (list* first rest))) | |
|
358 | (p:defrule base-arguments (or (and first-argument (* next-argument)) spaces?) | |
|
359 | (:lambda (list) | |
|
360 | (if (null list) | |
|
361 | nil | |
|
362 | (list* (first list) (second list))))) | |
|
354 | 363 | |
|
355 | 364 | ;;; Intrinsics |
|
356 | 365 | |
|
357 | 366 | (defmacro defintrinsics ((rule-name returning-rule-name non-returning-rule-name) &body clauses) |
|
358 | 367 | `(progn |
|
359 | 368 | ,@(loop :for clause :in clauses |
|
360 | 369 | :collect `(defintrinsic ,@clause)) |
|
361 | 370 | (p:defrule ,returning-rule-name (or ,@(remove-nil |
|
362 | 371 | (mapcar (lambda (clause) |
|
363 | 372 | (when (second clause) |
|
364 | 373 | (alexandria:symbolicate |
|
365 | 374 | 'intrinsic- (first clause)))) |
|
366 | 375 | clauses)))) |
|
367 | 376 | (p:defrule ,non-returning-rule-name (or ,@(remove-nil |
|
368 | 377 | (mapcar (lambda (clause) |
|
369 | 378 | (unless (second clause) |
|
370 | 379 | (alexandria:symbolicate |
|
371 | 380 | 'intrinsic- (first clause)))) |
|
372 | 381 | clauses)))) |
|
373 | 382 | (p:defrule ,rule-name (or ,returning-rule-name ,non-returning-rule-name)))) |
|
374 | 383 | |
|
375 | 384 | (defmacro defintrinsic (sym returning &optional (min-arity 0) (max-arity 10) &rest names) |
|
376 | 385 | (declare (ignore returning)) |
|
377 | 386 | (setf names |
|
378 | 387 | (if names |
|
379 | 388 | (mapcar #'string-upcase names) |
|
380 | 389 | (list (string sym)))) |
|
381 | 390 | `(p:defrule ,(alexandria:symbolicate 'intrinsic- sym) |
|
382 | 391 | (and (p:? #\$) (or ,@(loop :for name :in names :collect `(p:~ ,name))) |
|
383 | 392 | arguments) |
|
384 | 393 | (:destructure (dollar name arguments) |
|
385 | 394 | (declare (ignore dollar)) |
|
386 | 395 | (unless (<= ,min-arity (length arguments) ,max-arity) |
|
387 | 396 | (error "Intrinsic ~A expects between ~A and ~A arguments but ~A were provided:~%~S" |
|
388 | 397 | name ,min-arity ,max-arity (length arguments) arguments)) |
|
389 | 398 | (list* ',sym arguments)))) |
|
390 | 399 | |
|
391 | 400 | (defintrinsics (intrinsic returning-intrinsic non-returning-intrinsic) |
|
392 | 401 | ;; Transitions |
|
393 | 402 | (goto nil 0 10 "gt" "goto") |
|
394 | 403 | (xgoto nil 0 10 "xgt" "xgoto") |
|
395 | 404 | ;; Variables |
|
396 | 405 | (killvar nil 0 2) |
|
397 | 406 | ;; Expressions |
|
398 | 407 | (obj t 1 1) |
|
399 | 408 | (loc t 1 1) |
|
400 | 409 | (no t 1 1) |
|
401 | 410 | ;; Basic |
|
402 | 411 | (qspver t 0 0) |
|
403 | 412 | (curloc t 0 0) |
|
404 | 413 | (rand t 1 2) |
|
405 | 414 | (rnd t 0 0) |
|
406 | 415 | (qspmax t 1 10 "max") |
|
407 | 416 | (qspmin t 1 10 "min") |
|
408 | 417 | ;; Arrays |
|
409 | 418 | (killall nil 0 0) |
|
410 | 419 | (copyarr nil 2 4) |
|
411 | 420 | (arrsize t 1 1) |
|
412 | 421 | (arrpos t 2 3) |
|
413 | 422 | (arrcomp t 2 3) |
|
414 | 423 | ;; Strings |
|
415 | 424 | (len t 1 1) |
|
416 | 425 | (mid t 2 3) |
|
417 | 426 | (ucase t 1 1) |
|
418 | 427 | (lcase t 1 1) |
|
419 | 428 | (trim t 1 1) |
|
420 | 429 | (replace t 2 3) |
|
421 | 430 | (instr t 2 3) |
|
422 | 431 | (isnum t 1 1) |
|
423 | 432 | (val t 1 1) |
|
424 | 433 | (qspstr t 1 1 "str") |
|
425 | 434 | (strcomp t 2 2) |
|
426 | 435 | (strfind t 2 3) |
|
427 | 436 | (strpos t 2 3) |
|
428 | 437 | ;; IF |
|
429 | 438 | (iif t 2 3) |
|
430 | 439 | ;; Subs |
|
431 | 440 | (gosub nil 1 10 "gosub" "gs") |
|
432 | 441 | (func t 1 10) |
|
433 | 442 | (exit nil 0 0) |
|
434 | 443 | ;; Jump |
|
435 | 444 | (jump nil 1 1) |
|
436 | 445 | ;; Dynamic |
|
437 | 446 | (dynamic nil 1 10) |
|
438 | 447 | (dyneval t 1 10) |
|
439 | 448 | ;; Main window |
|
440 | (main-p nil 1 1 "*p") | |
|
441 | 449 | (main-pl nil 1 1 "*pl") |
|
442 | 450 | (main-nl nil 0 1 "*nl") |
|
451 | (main-p nil 1 1 "*p") | |
|
443 | 452 | (maintxt t 0 0) |
|
444 | 453 | (desc t 1 1) |
|
445 | 454 | (main-clear nil 0 0 "*clear" "*clr") |
|
446 | 455 | ;; Aux window |
|
447 | 456 | (showstat nil 1 1) |
|
448 | (stat-p nil 1 1 "p") | |
|
449 | 457 | (stat-pl nil 1 1 "pl") |
|
450 | 458 | (stat-nl nil 0 1 "nl") |
|
459 | (stat-p nil 1 1 "p") | |
|
451 | 460 | (stattxt t 0 0) |
|
452 | 461 | (stat-clear nil 0 0 "clear" "clr") |
|
453 | 462 | (cls nil 0 0) |
|
454 | 463 | ;; Dialog |
|
455 | 464 | (msg nil 1 1) |
|
456 | 465 | ;; Acts |
|
457 | 466 | (showacts nil 1 1) |
|
458 | 467 | (delact nil 1 1 "delact" "del act") |
|
459 | 468 | (curacts t 0 0) |
|
460 | 469 | (cla nil 0 0) |
|
461 | 470 | ;; Objects |
|
462 | 471 | (showobjs nil 1 1) |
|
463 | 472 | (addobj nil 1 3 "addobj" "add obj") |
|
464 | 473 | (delobj nil 1 1 "delobj" "del obj") |
|
465 | 474 | (killobj nil 0 1) |
|
466 | 475 | (countobj t 0 0) |
|
467 | 476 | (getobj t 1 1) |
|
468 | 477 | ;; Menu |
|
469 | 478 | (menu nil 1 1) |
|
470 | 479 | ;; Sound |
|
471 | 480 | (play nil 1 2) |
|
472 | 481 | (isplay t 1 1) |
|
473 | 482 | (close nil 1 1) |
|
474 | 483 | (closeall nil 0 0 "close all") |
|
475 | 484 | ;; Images |
|
476 | 485 | (refint nil 0 0) |
|
477 | 486 | (view nil 0 1) |
|
478 | 487 | ;; Fonts |
|
479 | 488 | (rgb t 3 3) |
|
480 | 489 | ;; Input |
|
481 | 490 | (showinput nil 1 1) |
|
482 | 491 | (usertxt t 0 0 "user_text" "usrtxt") |
|
483 | 492 | (cmdclear nil 0 0 "cmdclear" "cmdclr") |
|
484 | 493 | (input t 1 1) |
|
485 | 494 | ;; Files |
|
486 | 495 | (openqst nil 1 1) |
|
487 | 496 | (addqst nil 1 1 "addqst" "addlib" "inclib") |
|
488 | 497 | (killqst nil 1 1 "killqst" "dellib" "freelib") |
|
489 | 498 | (opengame nil 0 0) |
|
490 | 499 | (savegame nil 0 0) |
|
491 | 500 | ;; Real time |
|
492 | 501 | (wait nil 1 1) |
|
493 | 502 | (msecscount t 0 0) |
|
494 | 503 | (settimer nil 1 1)) |
|
495 | 504 | |
|
496 | 505 | ;;; Expression |
|
497 | 506 | |
|
498 | 507 | (p:defrule expression or-expr) |
|
499 | 508 | |
|
500 | 509 | (p:defrule or-expr (and and-expr (* (and spaces? or-op spaces? and-expr))) |
|
501 | 510 | (:function do-binop)) |
|
502 | 511 | |
|
503 | 512 | (p:defrule and-expr (and eq-expr (* (and spaces? and-op spaces? eq-expr))) |
|
504 | 513 | (:function do-binop)) |
|
505 | 514 | |
|
506 |
(p:defrule eq-expr (and |
|
|
507 |
|
|
|
515 | (p:defrule eq-expr (and sum-expr (* (and spaces? (or "<=" ">=" "=<" "=>" "<>" | |
|
516 | "=" "<" ">" "!") | |
|
508 | 517 | spaces? cat-expr))) |
|
509 | 518 | (:function do-binop)) |
|
510 | 519 | |
|
511 | (p:defrule cat-expr (and sum-expr (* (and spaces? #\& spaces? (p:! expr-stopper) sum-expr))) | |
|
512 | (:lambda (list) | |
|
513 | (do-binop (list (first list) (mapcar (lambda (l) | |
|
514 | (remove-nth l 3)) | |
|
515 | (second list)))))) | |
|
516 | ||
|
517 | 520 | (p:defrule sum-expr (and mod-expr (* (and spaces? (or #\+ #\-) spaces? mod-expr))) |
|
518 | 521 | (:function do-binop)) |
|
519 | 522 | |
|
520 | 523 | (p:defrule mod-expr (and mul-expr (* (and spaces? (p:~ "mod") spaces? mul-expr))) |
|
521 | 524 | (:function do-binop)) |
|
522 | 525 | |
|
523 | 526 | (p:defrule mul-expr (and unary-expr (* (and spaces? (or #\* #\/) spaces? unary-expr))) |
|
524 | 527 | (:function do-binop)) |
|
525 | 528 | |
|
526 | 529 | (p:defrule unary-expr (and (p:? (or #\+ #\-)) atom-expr) |
|
527 | 530 | (:lambda (list) |
|
528 | 531 | (let ((expr (remove-nil list))) |
|
529 | 532 | (if (= 1 (length expr)) |
|
530 | 533 | (first expr) |
|
531 | 534 | (intern-first expr))))) |
|
532 | 535 | |
|
533 | 536 | (p:defrule atom-expr (and (or variable literal returning-intrinsic paren-expr) spaces?) |
|
534 | 537 | (:function first)) |
|
535 | 538 | |
|
536 | 539 | (p:defrule paren-expr (and #\( spaces? expression spaces? #\)) |
|
537 | 540 | (:function third)) |
|
538 | 541 | |
|
539 | 542 | (p:defrule or-op (p:~ "or") |
|
540 | 543 | (:constant "or")) |
|
541 | 544 | |
|
542 | 545 | (p:defrule and-op (p:~ "and") |
|
543 | 546 | (:constant "and")) |
|
544 | 547 | |
|
545 | 548 | ;;; Variables |
|
546 | 549 | |
|
547 | 550 | (p:defrule variable (and identifier (p:? array-index)) |
|
548 | 551 | (:destructure (id idx) |
|
549 | 552 | (list 'var id (or idx 0)))) |
|
550 | 553 | |
|
551 | 554 | (p:defrule array-index (and #\[ spaces? (p:? expression) spaces? #\]) |
|
552 | 555 | (:lambda (list) |
|
553 | 556 | (or (third list) :end))) |
|
554 | 557 | |
|
555 | (p:defrule assignment (or kw-assignment plain-assignment) | |
|
558 | (p:defrule assignment (or kw-assignment plain-assignment op-assignment) | |
|
556 | 559 | (:destructure (var eq expr) |
|
557 | 560 | (declare (ignore eq)) |
|
558 | 561 | (list 'set var expr))) |
|
559 | 562 | |
|
563 | (p:defrule kw-assignment (and (or (p:~ "let") (p:~ "set")) spaces? (or op-assignment plain-assignment)) | |
|
564 | (:function third)) | |
|
565 | ||
|
566 | (p:defrule op-assignment (and variable spaces? (or "+" "-" "*" "/") #\= spaces? expression) | |
|
567 | (:destructure (var ws1 op eq ws2 expr) | |
|
568 | (declare (ignore ws1 ws2)) | |
|
569 | (list var eq (intern-first (list op var expr))))) | |
|
570 | ||
|
560 | 571 | (p:defrule plain-assignment (and variable spaces? #\= spaces? expression) |
|
561 | 572 | (:function remove-nil)) |
|
562 | (p:defrule kw-assignment (and (or (p:~ "let") (p:~ "set")) spaces? plain-assignment) | |
|
563 | (:function third)) | |
|
564 | 573 | |
|
565 | 574 | ;;; Non-string literals |
|
566 | 575 | |
|
567 | 576 | (p:defrule literal (or qsp-string brace-string number)) |
|
568 | 577 | |
|
569 | 578 | (p:defrule number (+ (or #\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9)) |
|
570 | 579 | (:lambda (list) |
|
571 | 580 | (parse-integer (p:text list)))) |
General Comments 0
You need to be logged in to leave comments.
Login now