aboutsummaryrefslogtreecommitdiff
path: root/ps/reader.ps
blob: f1f63f69e22e9e14ffb58ddf22a8c6400b2c86db (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
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
% requires types.ps to be included first

/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 { 5 dict begin
    %(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
end } def


% read_symbol: read a single symbol from string/idx
% string idx -> read_symbol -> name string new_idx
/read_symbol { 5 dict begin
    %(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
end } def


% read_string: read a single string from string/idx
% string idx -> read_string -> new_string string new_idx
/read_string { 5 dict begin
    %(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 92 eq { % if \
            str idx get 34 eq { %if \"
                /idx idx 1 add def
                /cnt cnt 1 add def % 1 more below
            } if
        } if
        ch 34 eq { exit } if % '"' is end of string
        /cnt cnt 1 add def
    } loop
    str start cnt getinterval % the matched string
    (\\") (") replace
    str idx % return: new_string string new_idx
end } def


% read_atom: read a single atom from string/idx
% string idx -> read_atom -> int string new_idx
/read_atom { 3 dict begin
    %(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
end } def

% read_until: read a list from string/idx until stopchar is found
% string idx stopchar -> read_until -> list string new_idx
/read_until { 3 dict begin
    %(in read_until\n) print
    /stopchar exch def
    /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 stopchar eq { exit } if % stop at stopchar
        str idx read_form /idx exch def pop
    } loop
    ]
    str idx 1 add
end } def

% read_spaces: advance idx to the first non-whitespace
% string idx -> read_form -> string new_idx
/read_spaces { 3 dict begin
    %(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
        %(left1.1:) print str idx str length idx sub getinterval print (\n) print
        % eliminate comments
        ch 59 eq { %if ';'
            { % loop
                /idx idx 1 add def % increment idx
                str length idx le { exit } if % EOF, break loop
                /ch str idx get def  % current character
                %(left1.2:) print str idx str length idx sub getinterval print (\n) print
                % if newline then we are done
                ch 10 eq { exit } if
            } loop
            /idx idx 1 add def
            str length idx le { exit } if % EOF, break loop
            /ch str idx get def  % current character
        } if
        % 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

    %(left1.3:) print str idx str length idx sub getinterval print (\n) print
    str idx % return: string new_idx
end } def

% read_form: read the next form from string start at idx
% string idx -> read_form -> ast string new_idx
/read_form { 3 dict begin
    %(in read_form\n) print
    read_spaces
    /idx exch def
    /str exch def

    %idx str length ge { (unexpected EOF) _throw } if % EOF
    idx str length ge { null str idx }{ %if EOF

    /ch str idx get def  % current character
    %(LEFT2.1:) print str idx str length idx sub getinterval print (\n) print
    ch 39 eq { %if '\''
        /idx idx 1 add def
        str idx read_form
        3 -1 roll   /quote exch 2 _list   3 1 roll
    }{ ch 96 eq { %if '`'
        /idx idx 1 add def
        str idx read_form
        3 -1 roll   /quasiquote exch 2 _list   3 1 roll
    }{ ch 126 eq { %if '~'
        /idx idx 1 add def
        /ch str idx get def  % current character
        ch 64 eq { %if '~@'
            /idx idx 1 add def
            str idx read_form
            3 -1 roll   /splice-unquote exch 2 _list   3 1 roll
        }{ %else just '~'
            str idx read_form
            3 -1 roll   /unquote exch 2 _list   3 1 roll
        } ifelse
    }{ ch 94 eq { %if '^'
        /idx idx 1 add def
        str idx read_form read_form % stack: meta form str idx
        4 2 roll exch   /with-meta 3 1 roll 3 _list   3 1 roll
    }{ ch 64 eq { %if '@'
        /idx idx 1 add def
        str idx read_form
        3 -1 roll   /deref exch 2 _list   3 1 roll
    }{ ch 40 eq { %if '('
        str idx 41 read_until dup /idx exch def
        %(LEFT2.2:) print str idx str length idx sub getinterval print (\n) print
        3 -1 roll   _list_from_array   3 1 roll
        %(LEFT2.3:) print str idx str length idx sub getinterval print (\n) print
    }{ ch 41 eq { %elseif ')'
        (unexpected '\)') _throw
    }{ ch 91 eq { %if '['
        str idx 93 read_until dup /idx exch def
        %(LEFT2.4:) print str idx str length idx sub getinterval print (\n) print
        3 -1 roll   _vector_from_array   3 1 roll
    }{ ch 93 eq { %elseif ']'
        (unexpected ']') _throw
    }{ ch 123 eq { %elseif '{'
        str idx 125 read_until dup /idx exch def
        3 -1 roll   _hash_map_from_array   3 1 roll
    }{ ch 125 eq { %elseif '}'
        (unexpected '}') _throw
    }{ % else
        str idx read_atom
    } ifelse } ifelse } ifelse } ifelse } ifelse } ifelse } ifelse } ifelse } ifelse } ifelse } ifelse

    } ifelse % not EOF

    % return: ast string new_idx
end } 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