1 % requires types.ps to be included first
3 /token_delim
(;,"` \n{}\(\)[]) def
4 /token_number (0123456789-) def
6 % read_number: read a single number from string/idx
7 % string idx -> read_number -> number string new_idx
8 /read_number { 5 dict begin
9 %(in read_number\n) print
15 idx str length ge { exit } if % EOF, break loop
16 /ch str idx get def % current character
17 ch 48 ge ch 57 le and 45 ch eq or { %if number
22 /idx idx 1 add def % increment idx
25 str start cnt getinterval cvi % the matched number
26 str idx % return: number string new_idx
30 % read_symbol: read a single symbol from string/idx
31 % string idx -> read_symbol -> name string new_idx
32 /read_symbol { 5 dict begin
33 %(in read_symbol\n) print
39 idx str length ge { exit } if % EOF, break loop
40 /ch str idx 1 getinterval def
41 token_delim ch search { % if token delimeter
47 /idx idx 1 add def % increment idx
50 str start cnt getinterval cvn % the matched symbol
51 str idx % return: symbol string new_idx
55 % read_keyword: read a single keyword from string/idx
56 % string idx -> read_keyword -> name string new_idx
57 /read_keyword { 5 dict begin
58 %(in read_keyword\n) print
64 idx str length ge { exit } if % EOF, break loop
65 /ch str idx 1 getinterval def
66 token_delim ch search { % if token delimeter
72 /idx idx 1 add def % increment idx
75 str start cnt getinterval % the matched keyword string
76 dup 0 127 put % TODO: something like (\x029e) would be better
77 str idx % return: keyword string new_idx
81 % read_string: read a single string from string/idx
82 % string idx -> read_string -> new_string string new_idx
83 /read_string { 5 dict begin
84 %(in read_string\n) print
90 idx str length ge { %if EOF
91 (unexpected EOF reading string) _throw
93 /ch str idx get def % current character
96 str idx get 34 eq { %if \"
98 /cnt cnt 1 add def % 1 more below
101 ch 34 eq { exit } if % '"' is end of string
104 str start cnt getinterval % the matched string
108 str idx % return: new_string string new_idx
112 % read_atom: read a single atom from string/idx
113 % string idx -> read_atom -> int string new_idx
114 /read_atom { 3 dict begin
115 %(in read_atom\n) print
118 str length idx le { % ifelse
121 /ch str idx get def % current character
122 %ch 48 ge ch 57 le and 45 ch eq or { %if number
123 ch 48 ge ch 57 le and { %if number
125 }{ ch 34 eq { %elseif double-quote (string)
127 }{ ch 58 eq { %elseif colon (keyword)
132 dup /nil eq { %if nil
134 }{ dup /true eq { %elseif true
136 }{ dup /false eq { %elseif false
139 str idx % return the original symbol/name
140 } ifelse } ifelse } ifelse
141 } ifelse } ifelse } ifelse
144 % return: atom string new_idx
147 % read_until: read a list from string/idx until stopchar is found
148 % string idx stopchar -> read_until -> list string new_idx
149 /read_until { 3 dict begin
150 %(in read_until\n) print
156 str idx read_spaces /idx exch def pop
157 str length idx le { %if EOF
158 (unexpected EOF reading list) _throw
160 /ch str idx get def % current character
161 ch stopchar eq { exit } if % stop at stopchar
162 str idx read_form /idx exch def pop
168 % read_spaces: advance idx to the first non-whitespace
169 % string idx -> read_form -> string new_idx
170 /read_spaces { 3 dict begin
171 %(in read_spaces\n) print
175 str length idx le { exit } if % EOF, break loop
176 /ch str idx get def % current character
177 %(left1.1:) print str idx str length idx sub getinterval print (\n) print
181 /idx idx 1 add def % increment idx
182 str length idx le { exit } if % EOF, break loop
183 /ch str idx get def % current character
184 %(left1.2:) print str idx str length idx sub getinterval print (\n) print
185 % if newline then we are done
189 str length idx le { exit } if % EOF, break loop
190 /ch str idx get def % current character
192 % if not whitespace then exit
193 ch 32 ne ch 10 ne ch 44 ne and and { exit } if
194 /idx idx 1 add def % increment idx
197 %(left1.3:) print str idx str length idx sub getinterval print (\n) print
198 str idx % return: string new_idx
201 % read_form: read the next form from string start at idx
202 % string idx -> read_form -> ast string new_idx
203 /read_form { 3 dict begin
204 %(in read_form\n) print
209 %idx str length ge { (unexpected EOF) _throw } if % EOF
210 idx str length ge { null str idx }{ %if EOF
212 /ch str idx get def % current character
213 %(LEFT2.1:) print str idx str length idx sub getinterval print (\n) print
217 3 -1 roll /quote exch 2 _list 3 1 roll
218 }{ ch 96 eq { %if '`
'
221 3 -1 roll /quasiquote exch 2 _list 3 1 roll
222 }{ ch 126 eq { %if '~
'
224 /ch str idx get def % current character
228 3 -1 roll /splice-unquote exch 2 _list 3 1 roll
231 3 -1 roll /unquote exch 2 _list 3 1 roll
233 }{ ch 94 eq { %if '^
'
235 str idx read_form read_form % stack: meta form str idx
236 4 2 roll exch /with-meta 3 1 roll 3 _list 3 1 roll
237 }{ ch 64 eq { %if '@
'
240 3 -1 roll /deref exch 2 _list 3 1 roll
241 }{ ch 40 eq { %if '('
242 str idx 41 read_until dup /idx exch def
243 %(LEFT2.2:) print str idx str length idx sub getinterval print (\n) print
244 3 -1 roll _list_from_array 3 1 roll
245 %(LEFT2.3:) print str idx str length idx sub getinterval print (\n) print
246 }{ ch 41 eq { %elseif ')'
247 (unexpected '\
)') _throw
248 }{ ch 91 eq { %if '['
249 str idx 93 read_until dup /idx exch def
250 %(LEFT2.4:) print str idx str length idx sub getinterval print (\n) print
251 3 -1 roll _vector_from_array 3 1 roll
252 }{ ch 93 eq { %elseif ']'
253 (unexpected ']') _throw
254 }{ ch 123 eq { %elseif '{'
255 str idx 125 read_until dup /idx exch def
256 3 -1 roll _hash_map_from_array 3 1 roll
257 }{ ch 125 eq { %elseif '}'
258 (unexpected '}') _throw
261 } ifelse } ifelse } ifelse } ifelse } ifelse } ifelse } ifelse } ifelse } ifelse } ifelse } ifelse
265 % return: ast string new_idx
268 % string -> read_str -> ast
270 %(in read_str\n) print
271 0 % current index into the string
274 pop pop % drop the string, idx. return: ast