(in printer.ps\n) print % requires types.ps to be included first % ast print_readably -> _pr_str -> string /_pr_str { 4 dict begin /print_readably exch def dup /func? exch xcheck def % executable function /obj exch cvlit 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 _mal_function? { % if user defined 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 func? { () }{ (\)) } ifelse concatenate }{ /integertype obj type eq { % if number /slen obj 10 add log ceiling cvi def obj 10 slen string cvrs }{ /stringtype obj type eq { % if string print_readably { (") obj (\\) (\\\\) replace (") (\\") replace (") concatenate concatenate }{ obj } 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 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 print_readably _pr_str } forall ] { concatenate delim concatenate } forall dup length delim length sub 0 exch getinterval % strip off final delim } if end } def