This document describes a Lisp interpreter implemented in Postscript. It summarizes key concepts used including:
1) Using dictionaries to parse code and change parsing state. Different dictionaries are used for different contexts like procedure definition vs normal code.
2) The eval function reorders operands on the stack before applying functions.
3) Dictionaries are used to map Scheme special forms and variables to Postscript words.
4) Lists are represented by arrays and functions like cons, car, cdr are defined to manipulate them.
5) Lambda functions are implemented via code injection - generating Postscript code for the function body and enclosing it.
2. A Lisp Interpreter in Postscript
¡ñ Scheme dialect based on Peter Norvig's
lispy (lisp interpreter in python)
http://norvig.com/lispy.html
¡ñ Scheme dialect
¡ð variables var
¡ð conditional (if test conseq alt)
¡ð definition / assignment (define var value)
¡ð procedure (lambda (var...) exp)
¡ð sequencing (begin exp...)
¡ð procedure call (procedure exp...)
3. Good use of Concepts
¡ñ Parser
¡ð List with arbitrary length
/pdict_default <<
40 { count 0 eq { mark } { isMark not { mark } if } ifelse mark } %
'('
41 { isMark { pop } { genStr transit } ifelse eval mark } % ')'
32 { isMark not { genStr transit mark } if } % ' '
>> def
readline
{
dup pdict exch known % is current character in dict
{ pdict exch get exec } % execute mapped code
if
} forall % for all characters in readline
4. Good use of Concepts
¡ñ Dictionaries / Parser
¡ð state change with dictionaries
/pdict pdict_default def
/pdict_states <<
(lambda) { /pdict pdict_lambda def mark }
(quote) { /pdict pdict_quote def }
>> def
/pdict_lambda <<
40 { } % '(' do not delete
41 { isMark { pop } { genStr } ifelse ]
mark /pdict pdict_default def } % ')'
32 { isMark not { genStr mark } if } % ' '
>> def
5. Good use of Concepts
¡ñ eval
¡ð reorder with stack
/eval % op p1..pn - { p1..pn { op } exec }
{
counttomark -1 roll
dup dictionary exch known % test if command is known
{ dictionary exch get exec }
{ { call_fun } }
ifelse
0 ] cvx dup dup length 1 sub {exec} putinterval
} def
6. Good use of Concepts
¡ñ Dictionaries
¡ð map for variables
¡ð bind Scheme commands to PostScript words
/dictionary <<
...
(if) {{myif}} (quote) {{quote}} (define) {{mydef}}
(set!) {{myset}} (lambda) {lambda}
(begin) { prebegin {mybegin}} (equal?) {{eq}} (eq?) {{eq}}
(length) {{getVal length}} (cons) {{cons}} (car) {{getVal 0 get}}
(cdr) {{cdr}} (append) {{append}} (list) { list } (list?) {{isList}}
(null?) {{null}}
>> def
7. Good use of Concepts
¡ñ Lists
¡ð example: Scheme command begin
(begin) {prebegin {mybegin}}
/prebegin {
] mark exch % make an array of all parameters begin will get and
recreate the used mark for parser
} def
/mybegin {
mark exch % set mark where this command's parameter begins
{ getVal } forall % evaluate each subexpression in right order
] dup length 1 sub get % make array of all outputs and get last one
} def
8. Good use of Concepts
¡ñ lambda
¡ð code Injection
/lambda {
12 array dup 0 % workaround
{ null % function that is executed
null % array of par_names
isCall 3 array dup 0
{ set_pars getVal clearpars }
putinterval cvx
{ exch lambda append_par_arr }
ifelse
} putinterval cvx
dup 1 5 -1 roll put
dup 0 4 -1 roll put
} def