% requires types.ps to be included first % ast print_readably -> _pr_str -> string /_pr_str { 4 dict begin /print_readably exch def dup xcheck { (Cannot print proc: ) print dup == quit } if % assert /obj exch def obj _sequential? { obj _list? { (\() (\)) }{ ([) (]) } ifelse obj /data get ( ) print_readably _pr_str_args exch concatenate concatenate }{ obj _hash_map? { ({) % get array of contents with keys stringified [ obj /data get { exch dup length string cvs exch } forall ] ( ) print_readably _pr_str_args concatenate (}) concatenate }{ obj _function? { % if builtin function (<\(builtin_fn* {) obj /data get dup length array copy cvlit ( ) print_readably _pr_str_args (}>) concatenate concatenate }{ obj _mal_function? { % if user defined mal_function (<\(fn* ) obj /params get print_readably _pr_str ( ) obj /ast get print_readably _pr_str (\)>) concatenate concatenate concatenate concatenate }{ obj _atom? { % if atom (\(atom ) obj /data get print_readably _pr_str (\)) concatenate concatenate }{ /arraytype obj type eq { % if list or code block % accumulate an array of strings (\() obj ( ) print_readably _pr_str_args concatenate (\)) concatenate }{ /integertype obj type eq { % if number /slen obj abs 1 max log floor cvi 1 add % positive size obj 0 lt { 1 add } if % account for sign def obj 10 slen string cvrs }{ /stringtype obj type eq { % if string obj length 0 gt { % if string length > 0 obj 0 get 127 eq { %if starts with 0x7f (keyword) obj dup length string copy dup 0 58 put % 58 is ':' }{ print_readably { (") obj (\\) (\\\\) replace (") (\\") replace (\n) (\\n) replace (") concatenate concatenate }{ obj } ifelse } ifelse }{ % else empty string print_readably { ("") }{ obj } ifelse } ifelse }{ null obj eq { % if nil (nil) }{ true obj eq { % if true (true) }{ false obj eq { % if false (false) }{ /nametype obj type eq { % if symbol obj dup length string cvs }{ () } ifelse } ifelse } ifelse } ifelse } ifelse } ifelse } ifelse } ifelse } ifelse } ifelse } ifelse } ifelse end } def % array delim print_readably -> _pr_str_args -> new_string /_pr_str_args { 3 dict begin /print_readably exch def /delim exch def /args exch def () args length 0 gt { %if any elements [ args { %foreach argument in array dup xcheck { %if executable 255 string cvs }{ print_readably _pr_str } ifelse } forall ] { concatenate delim concatenate } forall dup length delim length sub 0 exch getinterval % strip off final delim } if end } def % utility function /print_dict { (DICT contents:\n) print { ( - ) print exch dup length string cvs print % key (: ) print == } forall } def