blob: 8575d649f4c70ff63111e381e40052cdf4c4ae50 (
plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
|
(in reader\n) print
/token_delim (;,"` \n{}\(\)[]) def
/token_number (0123456789-) def
% read_number: read a single number from string/idx
% string idx -> read_number -> number string new_idx
/read_number {
%(in read_number\n) print
/idx exch def
/str exch def
/start idx def
/cnt 0 def
{ % loop
idx str length ge { exit } if % EOF, break loop
/ch str idx get def % current character
ch 48 ge ch 57 le and 45 ch eq or { %if number
/cnt cnt 1 add def
}{ % else
exit
} ifelse
/idx idx 1 add def % increment idx
} loop
str start cnt getinterval cvi % the matched number
str idx % return: number string new_idx
} def
% read_symbol: read a single symbol from string/idx
% string idx -> read_symbol -> name string new_idx
/read_symbol {
%(in read_symbol\n) print
/idx exch def
/str exch def
/start idx def
/cnt 0 def
{ % loop
idx str length ge { exit } if % EOF, break loop
/ch str idx 1 getinterval def
token_delim ch search { % if token delimeter
pop pop pop exit
}{ % else not a delim
pop
/cnt cnt 1 add def
} ifelse
/idx idx 1 add def % increment idx
} loop
str start cnt getinterval cvn % the matched symbol
str idx % return: symbol string new_idx
} def
% read_string: read a single string from string/idx
% string idx -> read_string -> new_string string new_idx
/read_string {
%(in read_string\n) print
/idx exch 1 add def
/str exch def
/start idx def
/cnt 0 def
{ % loop
idx str length ge { %if EOF
(unexpected EOF reading string)
throw
} if
/ch str idx get def % current character
/idx idx 1 add def
ch 34 eq { exit } if % '"' is end of string
/cnt cnt 1 add def
} loop
str start cnt getinterval % the matched string
str idx % return: new_string string new_idx
} def
% read_atom: read a single atom from string/idx
% string idx -> read_atom -> int string new_idx
/read_atom {
%(in read_atom\n) print
/idx exch def
/str exch def
str length idx le { % ifelse
exit % EOF
}{
/ch str idx get def % current character
%ch 48 ge ch 57 le and 45 ch eq or { %if number
ch 48 ge ch 57 le and { %if number
str idx read_number
}{ ch 34 eq { %elseif double-quote
str idx read_string
}{
str idx read_symbol
/idx exch def pop
dup /nil eq { %if nil
pop null str idx
}{ dup /true eq { %elseif true
pop true str idx
}{ dup /false eq { %elseif false
pop false str idx
}{ %else
str idx % return the original symbol/name
} ifelse } ifelse } ifelse
} ifelse } ifelse
}ifelse
% return: atom string new_idx
} def
% read_list: read a single list from string/idx
% string idx -> read_list -> list string new_idx
/read_list {
%(in read_list\n) print
/idx exch 1 add def
/str exch def
[
{ % loop
str idx read_spaces /idx exch def pop
str length idx le { %if EOF
(unexpected EOF reading list)
throw
} if
/ch str idx get def % current character
ch 41 eq { exit } if % ')' is end of list
str idx read_form /idx exch def pop
} loop
]
str idx 1 add
} def
% read_spaces: advance idx to the first non-whitespace
% string idx -> read_form -> string new_idx
/read_spaces {
%(in read_spaces\n) print
/idx exch def
/str exch def
{ % loop
str length idx le { exit } if % EOF, break loop
/ch str idx get def % current character
% if not whitespace then exit
ch 32 ne ch 10 ne ch 44 ne and and { exit } if
/idx idx 1 add def % increment idx
} loop
str idx % return: string new_idx
} def
% read_form: read the next form from string start at idx
% string idx -> read_form -> ast string new_idx
/read_form {
%(in read_form\n) print
read_spaces
/idx exch def
/str exch def
idx str length ge { exit } if % EOF, break loop
/ch str idx get def % current character
ch 40 eq { %if (
str idx read_list
}{ ch 91 eq { %elseif [
(unexpected '[') throw
}{ ch 93 eq { %elseif ]
(unexpected ']') throw
}{ % else
str idx read_atom
} ifelse } ifelse } ifelse
%(stack vvv\n) print
%pstack
%(stack ^^^\n) print
% return: ast string new_idx
} def
% string -> read_str -> ast
/read_str {
%(in read_str\n) print
0 % current index into the string
read_form
pop pop % drop the string, idx. return: ast
} def
|