1 ;"Copyright (C) 2018 Keziah Wesley
3 You can redistribute and/or modify this file under the terms of the
4 GNU Affero General Public License as published by the Free Software
5 Foundation, either version 3 of the License, or (at your option) any
8 This file is distributed in the hope that it will be useful, but
9 WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 Affero General Public License for more details.
13 You should have received a copy of the GNU Affero General Public
14 License along with this file. If not, see
15 <http://www.gnu.org/licenses/>."
17 <FLOAD "chartable.mud">
28 %<AND <BLOCK (<MOBLIST pp> <GET PARSER OBLIST> <GET CHARTABLE OBLIST> <ROOT>)> <>>
31 Input string is provided to parser. Parser consumes from input and updates state.
32 Beginning and end of input will be treated as token boundaries.
33 Parser will return producing a value after reading 1 complete expression
34 (possibly leaving some input leftover)."
36 ;"If PARSE receives a partial input, it emits an ERROR. READ installs a
37 handler for the ERROR INTERRUPT that attempts to get more data and
40 <DEFINE CHARSTREAM (s) [.s 1 1]>
41 <DEFINE COPY-CHARSTREAM (s) <MAPF ,VECTOR ,AND? .s>>
42 <DEFINE SET-CHARSTREAM (x y) <PUT .x 1 <1 .y>> <PUT .x 2 <2 .y>> <PUT .x 3 <3 .y>>>
43 <DEFINE NEWLINE-POS (s) <PUT .s 2 <+ 1 <2 .s>>> <PUT .s 3 0>>
44 <DEFINE GET-POS (x) (<2 .x> <3 .x>)>
45 <DEFINE EAT-CHAR (s) <PUT .s 1 <REST <1 .s>>> <PUT .s 3 <+ 1 <3 .s>>>>
46 <DEFINE PEEK-CHAR (s) <ASCII <1 <1 .s>>>>
47 <DEFINE NEXT-CHAR (s "AUX" (c <PEEK-CHAR .s>))
49 <COND (<==? 33 .c> <SET c <bang <PEEK-CHAR .s>>> <EAT-CHAR .s>)>
51 ;"TODO: eliminate this and rely on peeking"
52 <DEFINE RETURN-CHAR (s "AUX")
53 <COND (<==? %<ASCII 10> <1 <BACK <1 .s>>>> <PUT .s 2 <- <2 .s> 1>>)>
54 <PUT .s 1 <BACK <1 .s>>>>
56 <DEFINE parse (t s "AUX" c sp "NAME" a)
57 <COND (<EMPTY? <1 .s>> <RETURN EOF .a>)>
58 <SET c <NEXT-CHAR .s>>
59 <SET sp <SPECIAL? .t .c>>
60 <COND (.sp <APPLY .sp .t .s .a>)
61 (ELSE <APPLY <DEFAULT-HANDLER .t> .t .c .s .a>)>>
63 ;"BLOCK is documented as modifying the value of .OBLIST, so I'm
64 assuming passing a lookup param to READ has a similar effect rather
65 than setting an internal variable."
67 <DEFINE read (s "OPTIONAL" (OBLIST .OBLIST))
68 <parse ,default <CHARSTREAM .s>>>
70 <DEFINE read-all (s "OPTIONAL" (OBLIST .OBLIST))
71 <SET s <CHARSTREAM .s>>
72 <MAPF ,LIST <FUNCTION ("AUX" res)
73 <COND (<EMPTY? <1 .s>> <MAPSTOP>)
74 (ELSE <SET res <parse ,default .s>>
75 <COND (<==? .res EOF> <MAPSTOP>) (ELSE .res)>)>>>>
77 ;"Don't try to READ in objects of these types."
79 <NEWTYPE prototemplate VECTOR>
80 ;"TODO: set appropriate ERROR as closer's EVALTYPE."
82 <DEFINE bang (c) <+ .c #FIX %<LSH 1 21>>>
83 <DEFINE banged? (c) <G=? .c #FIX %<LSH 1 21>>>
84 <DEFINE unbang (c) <- .c #FIX %<LSH 1 21>>>
85 <DEFINE explode-bang (c)
86 <COND (<banged? .c> <STRING %<1 "!"> <CHTYPE .c CHARACTER>>) (ELSE <CHTYPE .c CHARACTER>)>>
88 ;"Create the appropriate ERROR if there's a brace mismatch."
89 ;"Otherwise do nothing. Return value is ignored."
90 <DEFINE check-closer (pos c opener s "TUPLE" closers)
94 (<MEMQ <CHTYPE .c FIX> .closers>)
95 ;"TODO: position info"
96 (ELSE <ERROR BRACE-MISMATCH (POS .pos
97 OPENER <explode-bang .opener>
98 EXPECTED <MAPF ,LIST ,explode-bang .closers>
99 FOUND <explode-bang <CHTYPE .c FIX>>)>)>)
100 (<==? .c EOF> <ERROR UNCLOSED-BRACKET <explode-bang .opener> .pos>)>>
102 ;"TODO: accept radix in PARSE"
105 ;"Create a standard Muddle CHARTABLE."
106 <DEFINE make-default ("AUX" (t <CHARTABLE>))
108 <SET-SPECIAL .t 9 T <FUNCTION (t s a) <AGAIN .a>>> ;"HT"
109 <SET-SPECIAL .t 10 T <FUNCTION (t s a) <NEWLINE-POS .s> <AGAIN .a>>> ;"LF"
110 <SET-SPECIAL .t 11 T <FUNCTION (t s a) <AGAIN .a>>> ;"VT"
111 <SET-SPECIAL .t 12 T <FUNCTION (t s a) <AGAIN .a>>> ;"FF"
112 <SET-SPECIAL .t 13 T <FUNCTION (t s a) <AGAIN .a>>> ;"CR"
113 <SET-SPECIAL .t 32 T <FUNCTION (t s a) <AGAIN .a>>> ;"space"
114 <SET-SPECIAL .t <ASCII !\.> <> <FUNCTION (t s a) <FORM LVAL <parse .t .s>>>>
115 <SET-SPECIAL .t <ASCII !\,> T <FUNCTION (t s a) <FORM GVAL <parse .t .s>>>>
116 <SET-SPECIAL .t <bang <ASCII !\.>> <> <FUNCTION (t s a) <CHTYPE <FORM LVAL <parse .t .s>> SEGMENT>>>
117 <SET-SPECIAL .t <bang <ASCII !\,>> T <FUNCTION (t s a) <CHTYPE <FORM GVAL <parse .t .s>> SEGMENT>>>
118 <SET-SPECIAL .t <ASCII !\'> T <FUNCTION (t s a) <FORM QUOTE <parse .t .s>>>>
119 <SET-SPECIAL .t <ASCII !\;> T <FUNCTION (t s a) <parse .t .s> <AGAIN .a>>>
120 <SET-SPECIAL .t <ASCII !\%> T <FUNCTION (t s a)
121 <COND (<==? %<ASCII !\%> <PEEK-CHAR .s>> <EAT-CHAR .s> <EVAL <parse .t .s>> <AGAIN .a>)>
122 <EVAL <parse .t .s>>>>
123 <SET-SPECIAL .t <ASCII !\#> T <FUNCTION (t s a "AUX" (type <parse .t .s>)) <CHTYPE <parse .t .s> .type>>>
124 <SET-SPECIAL .t <bang <ASCII !\\>> T <FUNCTION (t s a) <NEXT-CHAR .s>>>
125 <SET-SPECIAL .t <ASCII !\*> <> <FUNCTION (t s a) <parse-octal .t .s .a>>>
127 <SET-SPECIAL .t <ASCII !\"> T <FUNCTION (t s a)
128 <MAPF ,STRING <FUNCTION ("AUX" (c <NEXT-CHAR .s>))
129 <COND (<==? .c %<ASCII !\\>> <MAPRET <CHTYPE <NEXT-CHAR .s> CHARACTER>>)
130 (<banged? .c> <MAPRET %<1 "!"> <CHTYPE <unbang .c> CHARACTER>>)
131 (<==? .c %<ASCII !\">> <MAPSTOP>)
132 (<==? .c 10> <NEWLINE-POS .s> <MAPRET <CHTYPE .c CHARACTER>>)
133 (ELSE <MAPRET <CHTYPE .c CHARACTER>>)>>>>>
135 ;"TODO: macros for reducing the redundancy of all these openers and closers."
137 ;"A closer is an illegal object for anything except the corresponding opener."
138 <SET-SPECIAL .t <ASCII !\>> T <FUNCTION (t s a) <CHTYPE <ASCII !\>> closer>>>
139 <SET-SPECIAL .t <ASCII !\)> T <FUNCTION (t s a) <CHTYPE <ASCII !\)> closer>>>
140 <SET-SPECIAL .t <ASCII !\]> T <FUNCTION (t s a) <CHTYPE <ASCII !\]> closer>>>
141 <SET-SPECIAL .t <ASCII !\}> T <FUNCTION (t s a) <CHTYPE <ASCII !\}> closer>>>
142 <SET-SPECIAL .t <bang <ASCII !\>>> T <FUNCTION (t s a) <CHTYPE <bang <ASCII !\>>> closer>>>
143 <SET-SPECIAL .t <bang <ASCII !\)>> T <FUNCTION (t s a) <CHTYPE <bang <ASCII !\)>> closer>>>
144 <SET-SPECIAL .t <bang <ASCII !\]>> T <FUNCTION (t s a) <CHTYPE <bang <ASCII !\]>> closer>>>
145 <SET-SPECIAL .t <bang <ASCII !\}>> T <FUNCTION (t s a) <CHTYPE <bang <ASCII !\}>> closer>>>
147 <SET-SPECIAL .t <ASCII !\(> T <FUNCTION (t s a "AUX" pos)
148 <SET pos <GET-POS .s>>
149 <MAPF ,LIST <FUNCTION ("AUX" (x <parse .t .s>))
150 <COND (<check-closer .x <ASCII !\(> .s <ASCII !\)>> <MAPSTOP>)
151 (ELSE <MAPRET .x>)>>>>>
153 <SET-SPECIAL .t <ASCII !\<> T <FUNCTION (t s a "AUX" pos)
154 <SET pos <GET-POS .s>>
155 <MAPF ,FORM <FUNCTION ("AUX" (x <parse .t .s>))
156 <COND (<check-closer .pos .x <ASCII !\<> .s <ASCII !\>>> <MAPSTOP>)
157 (ELSE <MAPRET .x>)>>>>>
159 <SET-SPECIAL .t <ASCII !\[> T <FUNCTION (t s a "AUX" pos)
160 <SET pos <GET-POS .s>>
161 <MAPF ,VECTOR <FUNCTION ("AUX" (x <parse .t .s>))
162 <COND (<check-closer .pos .x <ASCII !\[> .s <ASCII !\]>> <MAPSTOP>)
163 (ELSE <MAPRET .x>)>>>>>
165 <SET-SPECIAL .t <bang <ASCII !\[>> T <FUNCTION (t s a "AUX" pos)
166 <SET pos <GET-POS .s>>
167 <MAPF ,UVECTOR <FUNCTION ("AUX" (x <parse .t .s>))
168 <COND (<check-closer .pos .x <bang <ASCII !\[>> .s <ASCII !\]>> <bang <ASCII !\]>> <MAPSTOP>)
169 (ELSE <MAPRET .x>)>>>>>
171 <SET-SPECIAL .t <bang <ASCII !\<>> T <FUNCTION (t s a "AUX" pos)
172 <SET pos <GET-POS .s>>
173 <CHTYPE <MAPF ,FORM <FUNCTION ("AUX" (x <parse .t .s>))
174 <COND (<check-closer .pos .x <bang <ASCII !\<>> .s <ASCII !\>>> <bang <ASCII !\>>> <MAPSTOP>)
175 (ELSE <MAPRET .x>)>>> SEGMENT>>>
177 ;"The prototemplate returned is an illegal object for anything except #-syntax."
178 <SET-SPECIAL .t <ASCII !\{> T <FUNCTION (t s a "AUX" pos)
179 <SET pos <GET-POS .s>>
180 <CHTYPE <MAPF ,VECTOR <FUNCTION ("AUX" (x <parse .t .s>))
181 <COND (<check-closer .pos .x <ASCII !\{> .s <ASCII !\}>> <MAPSTOP>)
182 (ELSE <MAPRET .x>)>>> prototemplate>>>
184 ;"Plain backslash just gets default handling in the lexer. It's only
185 special to the scalar sublexer."
187 <SET-DEFAULT .t ,parse-scalar>>>
189 ;"Lookup a string in an OBLIST / OBLIST list. If it isn't found,
190 insert it into the OBLIST / head of the OBLIST list."
191 <DEFINE lookup-multi (a os)
192 <COND (<TYPE? .os OBLIST> <OR <LOOKUP .a .os> <INSERT .a .os>>)
193 (ELSE <OR <MAPF <> <FUNCTION (o "AUX" (atm <LOOKUP .a .o>))
194 <COND (.atm <MAPLEAVE .atm>)>> .os>
195 <INSERT .a <1 .os>>>)>>
198 <DEFINE parse-atom (t s a "AUX" (atoms ()) atm pn is-trailed "NAME" getpn)
199 ;"Copy up to any trailer into a temporary STRING."
201 <SET pn <MAPF ,STRING <FUNCTION ("AUX" (k <PEEK-CHAR .s>))
202 <COND (<BREAKER? .t .k> <MAPSTOP>)
203 (<==? .k %<bang <ASCII !\->>> <SET is-trailed T> <EAT-CHAR .s> <MAPSTOP>)
204 (<banged? .k> <EAT-CHAR .s> <MAPRET %<1 "!"> <CHTYPE <unbang .k> CHARACTER>>)
205 (ELSE <EAT-CHAR .s> <MAPRET <CHTYPE .k CHARACTER>>)>>>>
206 ;"Gather trailer-separated PNAMEs into a stack."
207 <COND (.is-trailed <SET atoms (.pn !.atoms)> <AGAIN .getpn>)>
208 ;"Now we have a LIST of STRINGs containing our trailed PNAMEs, with
209 the last still in .pn. The final PNAME is special: because it has
210 no trailer, we look it up in our oblist list."
213 ;"Special case: we have an OBLIST, not an ATOM identifying an OBLIST."
214 <SET atm <OR <LOOKUP <1 .atoms> .atm> <INSERT <1 .atoms> .atm>>>
215 <SET atoms <REST .atoms>>)
216 (ELSE <SET atm <lookup-multi .pn .OBLIST>>)>
217 <COND (<EMPTY? .atoms> <RETURN .atm .getpn>)>
218 ;"Lookup each atom in the oblist denoted the previous (trailer),"
219 ;"except the last, which is our ATOM."
221 <FUNCTION (x "AUX" ob)
222 <SET ob <MOBLIST .atm>>
223 <SET atm <OR <LOOKUP .x .ob> <INSERT .x .ob>>>>
226 <DEFINE parse-octal (t s a "AUX" (z <COPY-CHARSTREAM .s>) c (n 0) (havedigits <>) "NAME" oct)
227 <SET c <NEXT-CHAR .z>>
229 ;"Ended without a trailing *?"
230 (<BREAKER? .t .c> <RETURN-CHAR .s> <parse-atom .t .s .a>)
232 (<==? .c %<ASCII !\*>>
233 <COND (.havedigits <SET-CHARSTREAM .s .z> <RETURN <CHTYPE .n FIX> .a>)
234 (ELSE <RETURN-CHAR .s> <parse-atom .t .s .a>)>)
235 ;"Got another octit?"
236 (<AND <L=? %<ASCII !\0> .c> <L=? .c %<ASCII !\9>>>
237 <SET n <ORB <- .c %<ASCII !\0>> <LSH .n 3>>>
240 ;"Not octal after all."
241 (ELSE <RETURN-CHAR .s> <parse-atom .t .s .a>)>>
243 ;"If current radix isn't 10, pre-scan to see whether there's a decimal point."
248 <REPEAT ((z <COPY-CHARSTREAM .s>) "NAME" rdx)
249 <SET c <NEXT-CHAR .z>>
250 <COND (<BREAKER? .t .c> <RETURN ,radix .rdx>)
251 ;"Docs aren't clear on exponent-notation fixes without decimal points."
252 ;"Decimal makes more sense..."
253 (<==? %<ASCII !\e> .c> <RETURN 10 .rdx>)
254 (<==? %<ASCII !\E> .c> <RETURN 10 .rdx>)
255 (<==? %<ASCII !\.> .c> <RETURN 10 .rdx>)>>)>>
257 <DEFINE parse-decimal (t c s a "AUX" (havedigits <>) (n 0) (sgn 1) (z <COPY-CHARSTREAM .s>) (rad <num-rad .s>))
258 <COND (<==? .c %<ASCII !\->> <SET sgn -1>)
259 (ELSE <RETURN-CHAR .z> <SET havedigits T>)>
260 <REPEAT ("NAME" decimal)
261 <SET c <NEXT-CHAR .z>>
265 <COND (.havedigits <RETURN-CHAR .z> <SET-CHARSTREAM .s .z> <RETURN <* .n .sgn> .a>)
266 (ELSE <RETURN-CHAR .s> <RETURN <parse-atom .t .s .a> .a>)>)
268 (<AND <L=? %<ASCII !\0> .c> <L? .c <+ %<ASCII !\0> .rad>>>
269 <SET n <+ <* .n .rad> <- .c %<ASCII !\0>>>>
274 (ELSE <RETURN-CHAR .s> <RETURN <parse-atom .t .s .a> .a>)>>>
276 ;"The Scalar Sublexer."
277 ;"Parse an object that begins with a non-special character: FIX/FLOAT/ATOM."
278 <DEFINE parse-scalar (t c s a "AUX" rad)
280 (<OR <==? .c %<ASCII !\->> <AND <L=? %<ASCII !\0> .c> <L=? .c %<ASCII !\9>>>>
281 <parse-decimal .t .c .s .a>)
282 (ELSE <RETURN-CHAR .s> <parse-atom .t .s .a>)>>
284 ;"Fallback to use if PARSE-TABLE!- is empty and no argument is provided."
285 <SETG default <make-default>>
286 <DEFINE DEFAULT-TABLE () ,default>
290 ;"Like MAPF ,AND with early termination."
291 <DEFINE ALL? (fn "TUPLE" argses "NAME" all)
292 <MAPF <> <FUNCTION ("TUPLE" xs) <COND (<NOT <APPLY .fn !.xs>> <RETURN <> .all>)>> !.argses>
295 ;"This belongs in a PRINT module. Here for testing."
296 <DEFINE PPRINT (x "OPTIONAL" (lvl 0))
298 (<TYPE? .x LIST> <PRINC !\(> <PPRINT-SEQ .x <+ .lvl 1>> <PRINC !\)>)
299 (<AND <TYPE? .x FORM> <NOT <OR <EMPTY? .x> <==? <1 .x> LVAL> <==? <1 .x> GVAL>>>>
300 <PRINC !\<> <PPRINT-SEQ .x <+ .lvl 1>> <PRINC !\>>)
302 <DEFINE PPRINT-SEQ (x lvl)
304 ;"TODO: more aggressive single-lining"
305 (<ALL? <FUNCTION (y) <OR <NOT <TYPE? .y FORM LIST>>
308 <==? <1 .y> GVAL>>> .x>
310 <MAPF <> <FUNCTION (y) <PRINC !\ > <PRIN1 .y>> <REST .x>>)
313 <MAPF <> <FUNCTION (y) <CRLF> <INDENT .lvl> <PPRINT .y .lvl>> <REST .x>>)>>
314 <DEFINE INDENT (lvl) <PRINC <ISTRING .lvl !\ >>>
317 <SET sss <ISTRING 40000>>
318 <SET sss <SUBSTRUC .sss 0 <READSTRING .sss <OPEN "READB" "./chartable.mud">>>>
319 <PPRINT <read-all!-PARSER .sss>>