3 % concatenate: concatenate two strings or two arrays
4 % From Thinking in PostScript 1990 Reid, Example 11.7
5 % (string1) (string2) concatenate string3
6 % array1 array2 concatenate array3
8 dup type
2 index type
2 copy ne
{ %if
10 errordict begin
(concatenate
) typecheck end
12 /stringtype ne
exch /arraytype ne and
{
13 errordict begin
(concatenate
) typecheck end
16 dup length
2 index length add
1 index type
17 /arraytype eq
{ array
}{ string
} ifelse
18 % stack: arg1 arg2 new
19 dup 0 4 index putinterval
20 % stack: arg1 arg2 new
21 dup 4 -1 roll length
4 -1 roll putinterval
25 % reverse: array1 -> reverse -> array2
28 aload
% push array onto stack
29 length
-1 0 { 1 roll } for % reverse
33 % string1 string2 string3 -> replace -> string4
34 % Return a string4 with all occurrences of string2 in string1 replaced
36 /replace
{ 4 dict begin
42 haystack needle search
44 % stack: post match pre
45 repstr concatenate
3 1 roll pop % stack: pre+ post
46 /haystack
exch def
% stack: pre+
47 result
exch concatenate
/result
exch def
49 result
exch concatenate
/result
exch def
57 % objA objB -> _equal? -> bool
58 /_equal?
{ 6 dict begin
63 a _sequential? b _sequential? and
64 or not
{ %if type mismatch and not sequential
67 a _sequential? b _sequential? and
{ %if list/vector
69 a _count b _count eq not
{ %if length mismatch
71 }{ %else (length is the same)
74 a idx _nth b idx _nth _equal? not
{ %if not items _equal?
81 }{ %else not list/vector
82 a _hash_map? b _hash_map? and
{ %if hash_map
85 a_keys _count b _keys _count eq not
{
88 a_keys
/data get
{ %foreach key in a_keys
90 a key _hash_map_get b key _hash_map_get _equal? not
{ %if not items _equal?
105 % Low-level sequence operations
107 /_sequential?
{ dup _list?
exch _vector? or
} def
109 /_count
{ /data get length
} def
113 dup length
0 gt
{ 0 get
}{ pop null
} ifelse
116 % seq start count -> _slice -> new_seq
118 3 -1 roll /data get
3 1 roll % stack: array start count
123 % seq idx -> _nth -> ith_item
125 exch /data get
% stack: idx array
126 dup length
0 gt
{ exch get
}{ pop pop null
} ifelse
129 % seq -> _rest -> rest_seq
133 dup length
1 sub 1 exch getinterval
140 % hashmap -> _keys -> key_list
143 [ exch { pop dup length string cvs
} forall
]
147 % hashmap key -> _hash_map_get -> val
149 exch % stack: key hashmap
150 /data get
% stack: key dict
151 exch % stack: dict key
152 2 copy known
{ %if has key
163 % Takes arbitrary data and puts it in $error:/errorinfo. Then calls
164 % stop to transfer control to end of nearest stopped context.
166 $error
exch /errorinfo
exch put
167 $error
/command
/throw put
172 $error
/errorinfo known
{ % if set
173 $error
/errorinfo get null ne
{
185 $error
/errorinfo get
187 $error
/errorname get
255 string cvs
189 $error
/command get
99 string cvs
191 $error
/position get
10 99 string cvrs
202 /_nil?
{ null eq
} def
203 /_true?
{ true eq
} def
204 /_false?
{ false eq
} def
210 dup length string
copy cvn
220 /_keyword
{ 1 dict begin
222 str length
1 add string
% str2
223 dup 1 str putinterval
224 dup 0 127 put
% TODO: something like (\x029e) would be better
228 dup type
/stringtype eq
{
239 % block -> _function -> boxed_function
243 %/data 5 -1 roll cvlit
246 %%dup length dict copy
249 % ast env params -> _mal_function -> boxed_mal_function
252 /_maltype_
/mal_function
% user defined function
253 /macro?
false % macro flag, false by default
254 /params null
% close over parameters
255 /ast null
% close over ast
256 /env null
% close over environment
257 /data
{ __self__ fload EVAL
} % forward reference to EVAL
258 dup length array
copy cvx
% actual copy/new instance of block
260 % make an actual copy/new instance of dict
261 dup length dict
copy % stack: ast env params mal_fn
262 % "Close over" parameters
263 dup 3 -1 roll % stack: ast env mal_fn mal_fn params
264 /params
exch put
% stack: ast env mal_fn
265 dup 3 -1 roll % stack: ast mal_fn mal_fn env
266 /env
exch put
% stack: ast mal_fn
267 dup 3 -1 roll % stack: mal_fn mal_fn ast
268 /ast
exch put
% stack: mal_fn
270 % insert self reference into position 0 of data
271 dup /data get
% stack: mal_fn data
272 1 index
% stack: mal_fn data mal_fn
273 0 exch % stack: mal_fn data 0 mal_fn
278 dup type
/dicttype eq
{
279 /_maltype_ get
/function eq
286 dup type
/dicttype eq
{
287 /_maltype_ get
/mal_function eq
293 % args mal_function -> fload -> ast new_env
294 % fload: sets up arguments on the stack for an EVAL call
296 dup /ast get
3 1 roll % stack: ast args mal_function
297 dup /env get
3 1 roll % stack: ast env args mal_function
298 /params get
exch % stack: ast env params args
299 env_new
% stack: ast new_env
302 % function_or_mal_function -> callable -> block
303 % if this is a function or mal_function, get its executable block
305 dup _mal_function?
{ %if mal_function
307 }{ dup _function?
{ %else if function
309 }{ %else something invalid
310 (callable called on non
-function
!\n) print
quit
318 % array -> _list_from_array -> mal_list
321 /data
3 -1 roll % grab the array argument
326 % elem... cnt -> _list -> mal_list
328 array astore _list_from_array
331 dup type
/dicttype eq
{
332 /_maltype_ get
/list eq
341 % array -> _vector_from_array -> mal_vector
342 /_vector_from_array
{
344 /data
3 -1 roll % grab the array argument
349 % elem... cnt -> _vector -> mal_vector
351 array astore _vector_from_array
354 dup type
/dicttype eq
{
355 /_maltype_ get
/vector eq
364 % dict -> _hash_map_from_dict -> mal_hash_map
365 /_hash_map_from_dict
{
372 % array -> _hash_map_from_array -> mal_hash_map
373 /_hash_map_from_array
{
376 4 -1 roll % grab the array argument
377 aload
pop % unpack the array
383 % elem... cnt -> _hash_map -> mal_hash_map
385 array astore _hash_map_from_array
388 dup type
/dicttype eq
{
389 /_maltype_ get
/hash_map eq
398 % obj -> atom -> new_atom
408 dup type
/dicttype eq
{
409 /_maltype_ get
/atom eq
417 % Sequence operations