Commit | Line | Data |
---|---|---|
1b4a9012 JM |
1 | (in reader\n) print |
2 | ||
ea81a808 | 3 | % requires types.ps to be included first |
1b4a9012 JM |
4 | |
5 | /token_delim (;,"` \n{}\(\)[]) def | |
6 | /token_number (0123456789-) def | |
7 | ||
8 | % read_number: read a single number from string/idx | |
9 | % string idx -> read_number -> number string new_idx | |
10 | /read_number { | |
11 | %(in read_number\n) print | |
12 | /idx exch def | |
13 | /str exch def | |
14 | /start idx def | |
15 | /cnt 0 def | |
16 | { % loop | |
17 | idx str length ge { exit } if % EOF, break loop | |
18 | /ch str idx get def % current character | |
19 | ch 48 ge ch 57 le and 45 ch eq or { %if number | |
20 | /cnt cnt 1 add def | |
21 | }{ % else | |
22 | exit | |
23 | } ifelse | |
24 | /idx idx 1 add def % increment idx | |
25 | } loop | |
26 | ||
27 | str start cnt getinterval cvi % the matched number | |
28 | str idx % return: number string new_idx | |
29 | } def | |
30 | ||
31 | ||
32 | % read_symbol: read a single symbol from string/idx | |
33 | % string idx -> read_symbol -> name string new_idx | |
34 | /read_symbol { | |
35 | %(in read_symbol\n) print | |
36 | /idx exch def | |
37 | /str exch def | |
38 | /start idx def | |
39 | /cnt 0 def | |
40 | { % loop | |
41 | idx str length ge { exit } if % EOF, break loop | |
42 | /ch str idx 1 getinterval def | |
43 | token_delim ch search { % if token delimeter | |
44 | pop pop pop exit | |
45 | }{ % else not a delim | |
46 | pop | |
47 | /cnt cnt 1 add def | |
48 | } ifelse | |
49 | /idx idx 1 add def % increment idx | |
50 | } loop | |
51 | ||
52 | str start cnt getinterval cvn % the matched symbol | |
53 | str idx % return: symbol string new_idx | |
54 | } def | |
55 | ||
56 | ||
57 | % read_string: read a single string from string/idx | |
58 | % string idx -> read_string -> new_string string new_idx | |
59 | /read_string { | |
60 | %(in read_string\n) print | |
61 | /idx exch 1 add def | |
62 | /str exch def | |
63 | /start idx def | |
64 | /cnt 0 def | |
65 | { % loop | |
66 | idx str length ge { %if EOF | |
5ce65382 | 67 | (unexpected EOF reading string) _throw |
1b4a9012 JM |
68 | } if |
69 | /ch str idx get def % current character | |
70 | /idx idx 1 add def | |
e7c1a2f6 JM |
71 | ch 92 eq { % if \ |
72 | str idx get 34 eq { %if \" | |
73 | /idx idx 1 add def | |
74 | /cnt cnt 1 add def % 1 more below | |
75 | } if | |
76 | } if | |
1b4a9012 JM |
77 | ch 34 eq { exit } if % '"' is end of string |
78 | /cnt cnt 1 add def | |
79 | } loop | |
80 | str start cnt getinterval % the matched string | |
e7c1a2f6 | 81 | (\\") (") replace |
1b4a9012 JM |
82 | str idx % return: new_string string new_idx |
83 | } def | |
84 | ||
85 | ||
86 | % read_atom: read a single atom from string/idx | |
87 | % string idx -> read_atom -> int string new_idx | |
88 | /read_atom { | |
89 | %(in read_atom\n) print | |
90 | /idx exch def | |
91 | /str exch def | |
92 | str length idx le { % ifelse | |
93 | exit % EOF | |
94 | }{ | |
95 | /ch str idx get def % current character | |
55e2bfa8 JM |
96 | %ch 48 ge ch 57 le and 45 ch eq or { %if number |
97 | ch 48 ge ch 57 le and { %if number | |
1b4a9012 JM |
98 | str idx read_number |
99 | }{ ch 34 eq { %elseif double-quote | |
100 | str idx read_string | |
101 | }{ | |
102 | str idx read_symbol | |
103 | /idx exch def pop | |
104 | dup /nil eq { %if nil | |
105 | pop null str idx | |
106 | }{ dup /true eq { %elseif true | |
107 | pop true str idx | |
108 | }{ dup /false eq { %elseif false | |
109 | pop false str idx | |
110 | }{ %else | |
111 | str idx % return the original symbol/name | |
112 | } ifelse } ifelse } ifelse | |
113 | } ifelse } ifelse | |
114 | }ifelse | |
115 | ||
116 | % return: atom string new_idx | |
117 | } def | |
118 | ||
5ce65382 JM |
119 | % read_until: read a list from string/idx until stopchar is found |
120 | % string idx stopchar -> read_until -> list string new_idx | |
121 | /read_until { | |
122 | %(in read_until\n) print | |
123 | /stopchar exch def | |
1b4a9012 JM |
124 | /idx exch 1 add def |
125 | /str exch def | |
126 | [ | |
127 | { % loop | |
128 | str idx read_spaces /idx exch def pop | |
129 | str length idx le { %if EOF | |
5ce65382 | 130 | (unexpected EOF reading list) _throw |
1b4a9012 JM |
131 | } if |
132 | /ch str idx get def % current character | |
5ce65382 | 133 | ch stopchar eq { exit } if % stop at stopchar |
1b4a9012 JM |
134 | str idx read_form /idx exch def pop |
135 | } loop | |
136 | ] | |
137 | str idx 1 add | |
138 | } def | |
139 | ||
140 | % read_spaces: advance idx to the first non-whitespace | |
141 | % string idx -> read_form -> string new_idx | |
142 | /read_spaces { | |
143 | %(in read_spaces\n) print | |
144 | /idx exch def | |
145 | /str exch def | |
146 | { % loop | |
147 | str length idx le { exit } if % EOF, break loop | |
148 | /ch str idx get def % current character | |
149 | % if not whitespace then exit | |
150 | ch 32 ne ch 10 ne ch 44 ne and and { exit } if | |
151 | /idx idx 1 add def % increment idx | |
152 | } loop | |
153 | ||
154 | str idx % return: string new_idx | |
155 | } def | |
156 | ||
157 | % read_form: read the next form from string start at idx | |
158 | % string idx -> read_form -> ast string new_idx | |
8e7e339d | 159 | /read_form { 3 dict begin |
1b4a9012 JM |
160 | %(in read_form\n) print |
161 | read_spaces | |
162 | /idx exch def | |
163 | /str exch def | |
164 | ||
5ce65382 | 165 | idx str length ge { (unexpected EOF) _throw } if % EOF |
1b4a9012 | 166 | /ch str idx get def % current character |
5ce65382 JM |
167 | ch 59 eq { %if ';' |
168 | { % loop | |
169 | /idx idx 1 add def % increment idx | |
170 | str length idx le { exit } if % EOF, break loop | |
171 | /ch str idx get def % current character | |
172 | % if newline then add 1 more idx and exit | |
173 | ch 10 eq { | |
174 | /idx idx 1 add def | |
175 | exit | |
176 | } if | |
177 | } loop | |
178 | str idx read_form % recur to get next form | |
179 | }{ ch 39 eq { %if '\'' | |
8e7e339d JM |
180 | /idx idx 1 add def |
181 | str idx read_form | |
182 | 3 -1 roll /quote exch 2 _list 3 1 roll | |
183 | }{ ch 96 eq { %if '`' | |
184 | /idx idx 1 add def | |
185 | str idx read_form | |
186 | 3 -1 roll /quasiquote exch 2 _list 3 1 roll | |
187 | }{ ch 126 eq { %if '~' | |
188 | /idx idx 1 add def | |
189 | /ch str idx get def % current character | |
190 | ch 64 eq { %if '~@' | |
191 | /idx idx 1 add def | |
192 | str idx read_form | |
193 | 3 -1 roll /splice-unquote exch 2 _list 3 1 roll | |
194 | }{ %else just '~' | |
195 | str idx read_form | |
196 | 3 -1 roll /unquote exch 2 _list 3 1 roll | |
197 | } ifelse | |
5ce65382 JM |
198 | }{ ch 94 eq { %if '^' |
199 | /idx idx 1 add def | |
200 | str idx read_form read_form % stack: meta form str idx | |
201 | 4 2 roll exch /with-meta 3 1 roll 3 _list 3 1 roll | |
202 | }{ ch 64 eq { %if '@' | |
203 | /idx idx 1 add def | |
204 | str idx read_form | |
205 | 3 -1 roll /deref exch 2 _list 3 1 roll | |
8e7e339d | 206 | }{ ch 40 eq { %if '(' |
5ce65382 JM |
207 | str idx 41 read_until |
208 | 3 -1 roll _list_from_array 3 1 roll | |
8e7e339d | 209 | }{ ch 41 eq { %elseif ')' |
5ce65382 JM |
210 | (unexpected '\)') _throw |
211 | }{ ch 91 eq { %if '(' | |
212 | str idx 93 read_until | |
213 | 3 -1 roll _vector_from_array 3 1 roll | |
8e7e339d | 214 | }{ ch 93 eq { %elseif ']' |
5ce65382 | 215 | (unexpected ']') _throw |
950e3c76 | 216 | }{ ch 123 eq { %elseif '{' |
5ce65382 JM |
217 | str idx 125 read_until |
218 | 3 -1 roll _hash_map_from_array 3 1 roll | |
950e3c76 | 219 | }{ ch 125 eq { %elseif '}' |
5ce65382 | 220 | (unexpected '}') _throw |
1b4a9012 JM |
221 | }{ % else |
222 | str idx read_atom | |
5ce65382 | 223 | } ifelse } ifelse } ifelse } ifelse } ifelse } ifelse } ifelse } ifelse } ifelse } ifelse } ifelse } ifelse |
1b4a9012 JM |
224 | |
225 | % return: ast string new_idx | |
8e7e339d | 226 | end } def |
1b4a9012 JM |
227 | |
228 | % string -> read_str -> ast | |
229 | /read_str { | |
230 | %(in read_str\n) print | |
231 | 0 % current index into the string | |
232 | read_form | |
233 | ||
234 | pop pop % drop the string, idx. return: ast | |
235 | } def |