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 { 4 dict begin
115 %(in read_atom\n) print
118 str length idx le { % ifelse
121 /ch str idx get def % current character
122 str length idx 1 add le {
125 /nextch str idx 1 add get def % next character
127 ch 48 ge ch 57 le and { %if digit (number)
129 }{ ch 45 eq nextch 48 ge nextch 57 le and and { %elseif minus and digit
131 }{ ch 34 eq { %elseif double-quote (string)
133 }{ ch 58 eq { %elseif colon (keyword)
138 dup /nil eq { %if nil
140 }{ dup /true eq { %elseif true
142 }{ dup /false eq { %elseif false
145 str idx % return the original symbol/name
146 } ifelse } ifelse } ifelse
147 } ifelse } ifelse } ifelse } ifelse
150 % return: atom string new_idx
153 % read_until: read a list from string/idx until stopchar is found
154 % string idx stopchar -> read_until -> list string new_idx
155 /read_until { 3 dict begin
156 %(in read_until\n) print
162 str idx read_spaces /idx exch def pop
163 str length idx le { %if EOF
164 (unexpected EOF reading list) _throw
166 /ch str idx get def % current character
167 ch stopchar eq { exit } if % stop at stopchar
168 str idx read_form /idx exch def pop
174 % read_spaces: advance idx to the first non-whitespace
175 % string idx -> read_form -> string new_idx
176 /read_spaces { 3 dict begin
177 %(in read_spaces\n) print
181 str length idx le { exit } if % EOF, break loop
182 /ch str idx get def % current character
183 %(left1.1:) print str idx str length idx sub getinterval print (\n) print
187 /idx idx 1 add def % increment idx
188 str length idx le { exit } if % EOF, break loop
189 /ch str idx get def % current character
190 %(left1.2:) print str idx str length idx sub getinterval print (\n) print
191 % if newline then we are done
195 str length idx le { exit } if % EOF, break loop
196 /ch str idx get def % current character
198 % if not whitespace then exit
199 ch 32 ne ch 10 ne ch 44 ne and and { exit } if
200 /idx idx 1 add def % increment idx
203 %(left1.3:) print str idx str length idx sub getinterval print (\n) print
204 str idx % return: string new_idx
207 % read_form: read the next form from string start at idx
208 % string idx -> read_form -> ast string new_idx
209 /read_form { 3 dict begin
210 %(in read_form\n) print
215 %idx str length ge { (unexpected EOF) _throw } if % EOF
216 idx str length ge { null str idx }{ %if EOF
218 /ch str idx get def % current character
219 %(LEFT2.1:) print str idx str length idx sub getinterval print (\n) print
223 3 -1 roll /quote exch 2 _list 3 1 roll
224 }{ ch 96 eq { %if '`
'
227 3 -1 roll /quasiquote exch 2 _list 3 1 roll
228 }{ ch 126 eq { %if '~
'
230 /ch str idx get def % current character
234 3 -1 roll /splice-unquote exch 2 _list 3 1 roll
237 3 -1 roll /unquote exch 2 _list 3 1 roll
239 }{ ch 94 eq { %if '^
'
241 str idx read_form read_form % stack: meta form str idx
242 4 2 roll exch /with-meta 3 1 roll 3 _list 3 1 roll
243 }{ ch 64 eq { %if '@
'
246 3 -1 roll /deref exch 2 _list 3 1 roll
247 }{ ch 40 eq { %if '('
248 str idx 41 read_until dup /idx exch def
249 %(LEFT2.2:) print str idx str length idx sub getinterval print (\n) print
250 3 -1 roll _list_from_array 3 1 roll
251 %(LEFT2.3:) print str idx str length idx sub getinterval print (\n) print
252 }{ ch 41 eq { %elseif ')'
253 (unexpected '\
)') _throw
254 }{ ch 91 eq { %if '['
255 str idx 93 read_until dup /idx exch def
256 %(LEFT2.4:) print str idx str length idx sub getinterval print (\n) print
257 3 -1 roll _vector_from_array 3 1 roll
258 }{ ch 93 eq { %elseif ']'
259 (unexpected ']') _throw
260 }{ ch 123 eq { %elseif '{'
261 str idx 125 read_until dup /idx exch def
262 3 -1 roll _hash_map_from_array 3 1 roll
263 }{ ch 125 eq { %elseif '}'
264 (unexpected '}') _throw
267 } ifelse } ifelse } ifelse } ifelse } ifelse } ifelse } ifelse } ifelse } ifelse } ifelse } ifelse
271 % return: ast string new_idx
274 % string -> read_str -> ast
276 %(in read_str\n) print
277 0 % current index into the string
280 pop pop % drop the string, idx. return: ast