0% found this document useful (0 votes)
448 views14 pages

Lisp Interpreter in Python Guide

This document summarizes how to write a Lisp interpreter in Python. It describes parsing program text into an abstract syntax tree and evaluating that tree to produce a result. Specifically, it defines a simplified Lisp-like language called Lispy Calculator that can perform basic math operations. The interpreter works by parsing text into a list representation using functions tokenize and read_from_tokens, then evaluating that list using an eval function within an environment that maps variables to values.
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
448 views14 pages

Lisp Interpreter in Python Guide

This document summarizes how to write a Lisp interpreter in Python. It describes parsing program text into an abstract syntax tree and evaluating that tree to produce a result. Specifically, it defines a simplified Lisp-like language called Lispy Calculator that can perform basic math operations. The interpreter works by parsing text into a list representation using functions tokenize and read_from_tokens, then evaluating that list using an eval function within an environment that maps variables to values.
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd

23/07/2016

(How to Write a (Lisp) Interpreter (in Python))

(HowtoWritea(Lisp)Interpreter(inPython))
Thispagehastwopurposes:todescribehowtoimplementcomputerlanguageinterpretersingeneral,andinparticular
[Link]
([Link]).Yearsago,[Link]
aroundthegoalistodemonstrate,asconciselyandaccessiblyaspossible,whatAlanKaycalled"Maxwell'sEquations
ofSoftware."
Whydoesthismatter?AsSteveYeggesaid,"Ifyoudon'tknowhowcompilerswork,thenyoudon'tknowhow
computerswork."Yeggedescribes8problemsthatcanbesolvedwithcompilers(orequallywithinterpreters,orwith
Yegge'stypicalheavydosageofcynicism).

SyntaxandSemanticsofSchemePrograms
Thesyntaxofalanguageisthearrangementofcharacterstoformcorrectstatementsorexpressionsthesemanticsis
[Link],inthelanguageofmathematicalexpressions(andin
manyprogramminglanguages),thesyntaxforaddingoneplustwois"1+2"andthesemanticsistheapplicationofthe
additionoperatortothetwonumbers,[Link]
itsvaluewewouldsaythat"1+2"evaluatesto3,andwritethatas"1+2"3.
[Link]:
Java
if ([Link]() > 0) {
fn(A[i] + 1,
return new String[] {"one", "two"});
}

Scheme
(if (> (val x) 0)
(fn (+ (aref A i) 1)
(quote (one two)))

Javahasawidevarietyofsyntacticconventions(keywords,infixoperators,threekindsofbrackets,operator
precedence,dotnotation,quotes,commas,semicolons),butSchemesyntaxismuchsimpler:
[Link]/expressiondistinction.
Numbers(e.g.1)andsymbols(e.g.A)[Link]
similartotheirJavacounterparts,exceptthatinScheme,operatorssuchas+and>aresymbolstoo.
[Link]"(",followedbyzeroormoreexpressions,followedbya")".The
firstelementofthelistdetermineswhatitmeans:
Aliststartingwithakeyword,e.g.(if ...),isaspecialformthemeaningdependsonthekeyword.
Aliststartingwithanonkeyword,e.g.(fn ...),isafunctioncall.
ThebeautyofSchemeisthatthefulllanguageonlyneedseightsyntacticforms.(Incomparison,Pythonhas110and
Javahas133.)Usingsomanyparenthesesmayseemunfamiliar,butithasthevirtuesofsimplicityandconsistency.
(Somehavejokedthat"Lisp"standsfor"LotsofIrritatingSillyParentheses"Ithinkitstandfor"LispIsSyntactically
Pure".)
InthispagewewillcoveralltheimportantpointsofScheme(omittingsomeminordetails),butwewilltaketwosteps
togetthere,definingasimplifiedlanguagefirst,beforedefiningthenearfullSchemelanguage.

Language1:LispyCalculator
LispyCalculatorisasubsetofSchemeusingonlyfivesyntacticforms(twoatomic,twospecialforms,andthe
procedurecall).LispyCalculatorletsyoudoanycomputationyoucoulddoonatypicalcalculatoraslongasyouare
[Link]:"if"
expressions,[Link]'sanexampleprogram,thatcomputestheareaofacircleofradius
10,usingtheformular2:
(begin
(define r 10)
(* pi (* r r)))

Hereisatableofalltheallowableexpressions:
Expression
[Link]

Syntax

SemanticsandExample
1/14

23/07/2016

(How to Write a (Lisp) Interpreter (in Python))

variable
reference

var

Asymbolisinterpretedasavariablenameitsvalueisthevariable'svalue.

Example:r10(assumingrwaspreviouslydefinedtobe10)

constant
literal

number

Anumberevaluatestoitself.
Examples:12 12or-3.45e+6 -3.45e+6

conditional (iftestconseqalt)

Evaluatetestiftrue,evaluateandreturnconseqotherwisealt.
Example:(if (> 10 20) (+ 1 1) (+ 3 3)) 6

definition (definevarexp)

Defineanewvariableandgiveitthevalueofevaluatingtheexpressionexp.
Examples:(define r 10)

procedure
(procarg...)
call

Ifprocisanythingotherthanoneofthesymbolsif, define, orquotethen


[Link],andthenthe
procedureisappliedtothelistofargvalues.
Example:(sqrt (* 2 8)) 4.0

IntheSyntaxcolumnofthistable,varmustbeasymbol,numbermustbeanintegerorfloatingpointnumber,andthe
[Link]...meanszeroormorerepetitionsofarg.

WhatALanguageInterpreterDoes
Alanguageinterpreterhastwoparts:
[Link]:Theparsingcomponenttakesaninputprogramintheformofasequenceofcharacters,verifiesit
accordingtothesyntacticrulesofthelanguage,[Link]
simpleinterpretertheinternalrepresentationisatreestructure(oftencalledanabstractsyntaxtree)thatclosely
[Link]
compilerthereisoftenaseriesofinternalrepresentations,startingwithanabstractsyntaxtree,andprogressingto
[Link]
thefunctionparse.
[Link]:Theinternalrepresentationisthenprocessedaccordingtothesemanticrulesofthelanguage,thereby
[Link]'sexecutionfunctioniscalledeval(notethisshadowsPython'sbuiltin
functionofthesamename).
Hereisapictureoftheinterpretationprocess:
program(str) parse abstractsyntaxtree(list) eval result(object)
Andhereisashortexampleofwhatwewantparseandevaltobeabletodo:
>> program = "(begin (define r 10) (* pi (* r r)))"
>>> parse(program)
['begin', ['define', 'r', 10], ['*', 'pi', ['*', 'r', 'r']]]
>>> eval(parse(program))
314.1592653589793

Parsing:parse,tokenizeandread_from_tokens
Parsingistraditionallyseparatedintotwoparts:lexicalanalysis,inwhichtheinputcharacterstringisbrokenupintoa
sequenceoftokens,andsyntacticanalysis,[Link]
tokensareparentheses,symbols,[Link](suchasMikeLeskandEric
Schmidt'slex),butwe'lluseaverysimpletool:Python'[Link]
charactersitaddsspacesaroundeachparen,[Link]:
def tokenize(chars):
"Convert a string of characters into a list of tokens."
return [Link]('(', ' ( ').replace(')', ' ) ').split()
>>> program = "(begin (define r 10) (* pi (* r r)))"
>>> tokenize(program)
['(', 'begin', '(', 'define', 'r', '10', ')', '(', '*', 'pi', '(', '*', 'r', 'r', ')', ')', ')']

Ourfunctionparsewilltakeastringrepresentationofaprogramasinput,calltokenizetogetalistoftokens,and
thencallread_from_tokenstoassembleanabstractsyntaxtree.read_from_tokenslooksatthefirsttokenifitisa
[Link]

2/14

23/07/2016

(How to Write a (Lisp) Interpreter (in Python))

')'that'[Link]'(',thenwestartbuildingupalistofsubexpressionsuntilwehitamatching')'.

[Link]'llletPythonmakethedistinctionbetweenthem:foreach
nonparentoken,firsttrytointerpretitasanint,thenasafloat,[Link]:
def parse(program):
"Read a Scheme expression from a string."
return read_from_tokens(tokenize(program))
def read_from_tokens(tokens):
"Read an expression from a sequence of tokens."
if len(tokens) == 0:
raise SyntaxError('unexpected EOF while reading')
token = [Link](0)
if '(' == token:
L = []
while tokens[0] != ')':
[Link](read_from_tokens(tokens))
[Link](0) # pop off ')'
return L
elif ')' == token:
raise SyntaxError('unexpected )')
else:
return atom(token)
def atom(token):
"Numbers become numbers; every other token is a symbol."
try: return int(token)
except ValueError:
try: return float(token)
except ValueError:
return Symbol(token)
parseworkslikethis:
>>> program = "(begin (define r 10) (* pi (* r r)))"
>>> parse(program)
['begin', ['define', 'r', 10], ['*', 'pi', ['*', 'r', 'r']]]

[Link]:
Symbol = str # A Scheme Symbol is implemented as a Python str
List = list # A Scheme List is implemented as a Python list
Number = (int, float) # A Scheme Number is implemented as a Python int or float

We'[Link].

Environments
Thefunctionevaltakestwoarguments:anexpression,x,thatwewanttoevaluate,andanenvironment,env,inwhich
[Link],evalwilluseaglobal
environentthatincludesthenamesforabunchofstandardfunctions(likesqrtandmax,andalsooperatorslike*).This
environmentcanbeaugmentedwithuserdefinedvariables,usingtheexpression(define variable value).Fornow,
wecanimplementanenvironmentasaPythondictof{variable:value}pairs.
import math
imoort operator as op
Env = dict # An environment is a mapping of {variable: value}
def standard_env():
"An environment with some Scheme standard procedures."
env = Env()
[Link](vars(math)) # sin, cos, sqrt, pi, ...
[Link]({
'+':[Link], '-':[Link], '*':[Link], '/':[Link],
'>':[Link], '<':[Link], '>=':[Link], '<=':[Link], '=':[Link],
'abs': abs,
'append': [Link],
'apply': apply,
'begin': lambda *x: x[-1],
'car': lambda x: x[0],
'cdr': lambda x: x[1:],
'cons': lambda x,y: [x] + y,
[Link]

3/14

23/07/2016

(How to Write a (Lisp) Interpreter (in Python))

'eq?': op.is_,
'equal?': [Link],
'length': len,
'list': lambda *x: list(x),
'list?': lambda x: isinstance(x,list),
'map': map,
'max': max,
'min': min,
'not': op.not_,
'null?': lambda x: x == [],
'number?': lambda x: isinstance(x, Number),
'procedure?': callable,
'round': round,
'symbol?': lambda x: isinstance(x, Symbol),
})
return env
global_env = standard_env()

Evaluation:eval
[Link],werepeatthetableofSchemeforms:
Expression

Syntax

SemanticsandExample

variable
reference

var

Asymbolisinterpretedasavariablenameitsvalueisthevariable'svalue.
Example:r10(assumingrwaspreviouslydefinedtobe10)

constant
literal

number

Anumberevaluatestoitself.
Examples:12 12or-3.45e+6 -3.45e+6

conditional (iftestconseqalt)

Evaluatetestiftrue,evaluateandreturnconseqotherwisealt.
Example:(if (> 10 20) (+ 1 1) (+ 3 3)) 6

definition (definevarexp)

Defineanewvariableandgiveitthevalueofevaluatingtheexpressionexp.
Examples:(define r 10)

procedure
(procarg...)
call

Ifprocisanythingotherthanoneofthesymbolsif, define, orquotethen


[Link],andthenthe
procedureisappliedtothelistofargvalues.
Example:(sqrt (* 2 8)) 4.0

Noticehowcloselythecodeforevalfollowsthetable:
def eval(x, env=global_env):
"Evaluate an expression in an environment."
if isinstance(x, Symbol): # variable reference
return env[x]
elif not isinstance(x, List): # constant literal
return x
elif x[0] == 'if': # conditional
(_, test, conseq, alt) = x
exp = (conseq if eval(test, env) else alt)
return eval(exp, env)
elif x[0] == 'define': # definition
(_, var, exp) = x
env[var] = eval(exp, env)
else: # procedure call
proc = eval(x[0], env)
args = [eval(arg, env) for arg in x[1:]]
return proc(*args)

We'redone!Youcanseeitallinaction:
>>> eval(parse("(define r 10)"))
>>> eval(parse("(* pi (* r r))"))
314.1592653589793

Interaction:AREPL
Itistedioustohavetoenter"eval(parse(...))"[Link]'sgreatlegaciesisthenotionofan
interactivereadevalprintloop:awayforaprogrammertoenteranexpression,andseeitimmediatelyread,evaluated,
[Link]

4/14

23/07/2016

(How to Write a (Lisp) Interpreter (in Python))

andprinted,withouthavingtogothroughalengthybuild/[Link]'sdefinethefunctionrepl(whichstands
forreadevalprintloop),andthefunctionschemestrwhichreturnsastringrepresentingaSchemeobject.
def repl(prompt='[Link]> '):
"A prompt-read-eval-print loop."
while True:
val = eval(parse(raw_input(prompt)))
if val is not None:
print(schemestr(val))
def schemestr(exp):
"Convert a Python object back into a Scheme-readable string."
if isinstance(exp, list):
return '(' + ' '.join(map(schemestr, exp)) + ')'
else:
return str(exp)

Hereisreplinaction:
>>> repl()
[Link]> (define r 10)
[Link]> (* pi (* r r))
314.159265359
[Link]> (if (> (* 11 11) 120) (* 7 6) oops)
42
[Link]>

Language2:FullLispy
Wewillnowextendourlanguagewiththreenewspecialforms,givingusamuchmorenearlycompleteScheme
subset:
Expression

Syntax

quotation (quote exp)

SemanticsandExample
Returntheexpliterallydonotevaluateit.
Example:(quote (+ 1 2)) (+ 1 2)

assignment (set!varexp)

Evaluateexpandassignthatvaluetovar,whichmusthavebeenpreviously
defined(withadefineorasaparametertoanenclosingprocedure).
Example:(set! r2 (* r r))

procedure (lambda (var...)exp)

Createaprocedurewithparameter(s)namedvar...andexpasthebody.
Example:(lambda (r) (* pi (* r r)))

Thelambdaspecialform(anobscurenomenclaturechoicethatreferstoAlonzoChurch'slambdacalculus)createsa
[Link]:
[Link]> (define circle-area (lambda (r) (* pi (* r r)))
[Link]> (circle-area 10)
314.159265359

Theprocedurecall(circle-area 10)causesustoevaluatethebodyoftheprocedure,(* pi (* r r)),inan


environmentinwhichpiand*havethesameglobalvaluestheyalwaysdid,[Link],it
wouldn'[Link]?We
wouldn'[Link],wewanttoarrangefortheretobealocalvariable
namedrthatwecansetto10withoutworryingaboutinterferingwithanyothervariablethathappenstohavethesame
[Link],onewhichallowsforbothlocalandglobalvariables.
Theideaisthatwhenweevaluate(circle-area 10),wewillfetchtheprocedurebody,(* pi (* r r)),and
evaluateitinanenvironmentthathasrasthesolelocalvariable,[Link]
words,wewantanenvironmentthatlookslikethis,withthelocalenvironmentnestedinsidethe"outer"global
environment:
pi: 3.141592653589793
*: <built-in function mul>
...
r: 10

Whenwelookupavariableinsuchanestedenvironment,welookfirstattheinnermostlevel,butifwedon'tfindthe
[Link]

5/14

23/07/2016

(How to Write a (Lisp) Interpreter (in Python))

variablenamethere,wemovetothenextouterlevel.
Itisclearthatproceduresandenvironmentsareintertwined,solet'sdefinethemtogether:
class Procedure(object):
"A user-defined Scheme procedure."
def __init__(self, parms, body, env):
[Link], [Link], [Link] = parms, body, env
def __call__(self, *args):
return eval([Link], Env([Link], args, [Link]))
class Env(dict):
"An environment: a dict of {'var':val} pairs, with an outer Env."
def __init__(self, parms=(), args=(), outer=None):
[Link](zip(parms, args))
[Link] = outer
def find(self, var):
"Find the innermost Env where var appears."
return self if (var in self) else [Link](var)
global_env = standard_env()

Weseethateveryprocedurehasthreecomponents:alistofparameternames,abodyexpression,andanenvironment
thattellsuswhatnonlocalvariablesareaccessiblefromthebody.
Anenvironmentisasubclassofdict,[Link]:the
constructor__init__buildsanewenvironmentbytakingalistofparameternamesandacorrespondinglistof
argumentvalues,andcreatinganewenvironmentthathasthose{variable:value}pairsastheinnerpart,andalsorefers
[Link]:eithertheinner
oneoranouterone.
Toseehowtheseallgotogether,[Link]
changed:[Link](x)tofindatwhatlevelthevariablexexiststhenwecanfetchthevalueofx
fromthatlevel.(Theclausefordefinehasnotchanged,becauseadefinealwaysaddsanewvariabletotheinnermost
environment.)Therearetwonewclauses:forset!,wefindtheenvironmentlevelwherethevariableexistsandsetitto
[Link],wecreateanewprocedureobjectwiththegivenparameterlist,body,andenvironment.
def eval(x, env=global_env):
"Evaluate an expression in an environment."
if isinstance(x, Symbol): # variable reference
return [Link](x)[x]
elif not isinstance(x, List): # constant literal
return x
elif x[0] == 'quote': # quotation
(_, exp) = x
return exp
elif x[0] == 'if': # conditional
(_, test, conseq, alt) = x
exp = (conseq if eval(test, env) else alt)
return eval(exp, env)
elif x[0] == 'define': # definition
(_, var, exp) = x
env[var] = eval(exp, env)
elif x[0] == 'set!': # assignment
(_, var, exp) = x
[Link](var)[var] = eval(exp, env)
elif x[0] == 'lambda': # procedure
(_, parms, body) = x
return Procedure(parms, body, env)
else: # procedure call
proc = eval(x[0], env)
args = [eval(arg, env) for arg in x[1:]]
return proc(*args)

Toappreciatehowproceduresandenvironmentsworktogether,considerthisprogramandtheenvironmentthatgets
formedwhenweevaluate(account1 -20.00):
+:<builtinoperatoradd>
make-account: <a Procedure>

(define make-account
(lambda (balance)
(lambda (amt)
(begin (set! balance (+ balance amt))
balance))))
[Link]

balance: 100.00

amt: -20.00
6/14

23/07/2016

(How to Write a (Lisp) Interpreter (in Python))

(define account1 (make-account 100.00))


(account1 -20.00)

account1: <a Procedure>

Eachrectangularboxrepresentsanenvironment,andthecoloroftheboxmatchesthecolorofthevariablesthatare
newlydefinedintheenvironment.Inthelasttwolinesoftheprogramwedefineaccount1andcall(account1
-20.00)thisrepresentsthecreationofabankaccountwitha100dollaropeningbalance,followedbya20dollar
[Link](account1 -20.00),[Link]
[Link](green)[Link]
balanceisnotdefinedthere:wehavetolookatthegreenenvironment'souterenv,[Link],the
variable+isnotfoundineitherofthoseweneedtodoonemoreouterstep,totheglobal(red)[Link]
[Link](var)findsthe
rightenvironmentaccordingtolexicalscopingrules.
Let'sseewhatwecandonow:
>>> repl()
[Link]> (define circle-area (lambda (r) (* pi (* r r))))
[Link]> (circle-area 3)
28.274333877
[Link]> (define fact (lambda (n) (if (<= n 1) 1 (* n (fact (- n 1))))))
[Link]> (fact 10)
3628800
[Link]> (fact 100)
9332621544394415268169923885626670049071596826438162146859296389521759999322991
5608941463976156518286253697920827223758251185210916864000000000000000000000000
[Link]> (circle-area (fact 10))
4.1369087198e+13
[Link]> (define first car)
[Link]> (define rest cdr)
[Link]> (define count (lambda (item L) (if L (+ (equal? item (first L)) (count item (rest L))) 0)))
[Link]> (count 0 (list 0 1 2 3 0 0))
3
[Link]> (count (quote the) (quote (the more the merrier the bigger the better)))
4
[Link]> (define twice (lambda (x) (* 2 x)))
[Link]> (twice 5)
10
[Link]> (define repeat (lambda (f) (lambda (x) (f (f x)))))
[Link]> ((repeat twice) 10)
40
[Link]> ((repeat (repeat twice)) 10)
160
[Link]> ((repeat (repeat (repeat twice))) 10)
2560
[Link]> ((repeat (repeat (repeat (repeat twice)))) 10)
655360
[Link]> (pow 2 16)
65536.0
[Link]> (define fib (lambda (n) (if (< n 2) 1 (+ (fib (- n 1)) (fib (- n 2))))))
[Link]> (define range (lambda (a b) (if (= a b) (quote ()) (cons a (range (+ a 1) b)))))
[Link]> (range 0 10)
(0 1 2 3 4 5 6 7 8 9)
[Link]> (map fib (range 0 10))
(1 1 2 3 5 8 13 21 34 55)
[Link]> (map fib (range 0 20))
(1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597 2584 4181 6765)

Wenowhavealanguagewithprocedures,variables,conditionals(if),andsequentialexecution(thebeginprocedure).
Ifyouarefamiliarwithotherlanguages,youmightthinkthatawhileorforloopwouldbeneeded,butScheme
[Link]"Schemedemonstratesthataverysmallnumberofrules
forformingexpressions,withnorestrictionsonhowtheyarecomposed,sufficetoformapracticalandefficient
programminglanguage."InSchemeyouiteratebydefiningrecursivefunctions.

HowSmall/Fast/Complete/GoodisLispy?
InwhichwejudgeLispyonseveralcriteria:
Small:Lispyisverysmall:117noncommentnonblanklines4Kofsourcecode.(Anearlierversionwasjust90
lines,buthadfewerstandardproceduresandwasperhapsabittooterse.)ThesmallestversionofmySchemein
[Link]

7/14

23/07/2016

(How to Write a (Lisp) Interpreter (in Python))

Java,Jscheme,[Link](SchemeinFifty
Kilobytes),[Link]
IthinkitmeetsAlanKay's1972claimthatyoucoulddefinethe"mostpowerfullanguageintheworld"in"a
pageofcode."(However,IthinkAlanmightdisagree,becausehewouldcountthePythoncompileraspartofthe
code,puttingmewelloverapage.)
bash$ grep "^\s*[^#\s]" [Link] | wc
117 497 4276

Fast:Lispycomputes(fact 100)[Link]'sfastenoughforme(althoughfarslowerthanmost
otherwaysofcomputingit).
Complete:[Link]:
Syntax:Missingcomments,quoteandquasiquotenotation,#literals,thederivedexpressiontypes(suchas
cond,derivedfromif,orlet,derivedfromlambda),anddottedlistnotation.
Semantics:Missingcall/ccandtailrecursion.
DataTypes:Missingstrings,characters,booleans,ports,vectors,exact/[Link]
actuallyclosertoSchemevectorsthantotheSchemepairsandliststhatweimplementwiththem.
Procedures:Missingover100primitiveprocedures:alltheonesforthemissingdatatypes,plussome
otherslikeset-car!andset-cdr!,becausewecan'timplementset-cdr!completelyusingPythonlists.
Errorrecovery:Lispydoesnotattempttodetect,reasonablyreport,[Link]
theprogrammertobeperfect.
Good:That'[Link].

TrueStory
Tobackuptheideathatitcanbeveryhelpfultoknowhowinterpreterswork,here'sastory.Waybackin1984Iwas
[Link],[Link],
troffhadnofacilityforforwardreferencestosymboliclabels:Iwantedtobeabletowrite"Aswewillseeonpage
@theoremx"andthenwritesomethinglike"@(settheoremx\n%)"intheappropriateplace(thetroffregister\n%
holdsthepagenumber).MyfellowgradstudentTonyDeRosefeltthesameneed,andtogetherwesketchedouta
[Link],itturnedoutthattheLispwehadatthetime
wasgoodatreadingLispexpressions,butsoslowatreadingcharacteratatimenonLispexpressionsthatourprogram
wasannoyingtouse.
[Link]
that,butheknewhowtowriteatinyCroutineforreadingandechoingthenonLispcharactersandlinkitintotheLisp
[Link]'tknowhowtodothatlinking,butIreasonedthatwritinganinterpreterforthistriviallanguage(allit
hadwassetvariable,fetchvariable,andstringconcatenate)waseasy,[Link],ironically,Tony
wroteaLispprogram(withonesmallroutineinC)becausehewasaCprogrammer,andIwroteaCprogrambecauseI
wasaLispprogrammer.
Intheend,webothgotourthesesdone(Tony,Peter).

TheWholeThing
Thewholeprogramishere:[Link].

FurtherReading
TolearnmoreaboutSchemeconsultsomeofthefinebooks(byFriedmanandFellesein,Dybvig,Queinnec,Harvey
andWrightorSussmanandAbelson),videos(byAbelsonandSussman),tutorials(byDorai,PLT,orNeller),orthe
referencemanual.
IalsohaveanotherpagedescribingamoreadvancedversionofLispy.
PeterNorvig
96Comments

[Link]

Recommend 134
[Link]

Share

Login

SortbyBest
8/14

23/07/2016

(How to Write a (Lisp) Interpreter (in Python))

Jointhediscussion
thedeemon6yearsago

Here'saRubyanalogin60lineswithaddedsupportofcontinuations(call/cc,famousSchemefeature):
[Link]
RequiresRuby1.8.x.
22

Reply Share

PeterNorvig

Mod >thedeemon6yearsago

Verynice!
9

Reply Share

tarpsocks>thedeemon5yearsago

[Link].
3

Reply Share

PeterNorvig>tarpsocks5yearsago

tarpsocks,Ithinkitisonlylackingclarityifyoudon'[Link],itisclear.
OnethingIwouldobjectto:call/ccshouldbearegularfunction,notaspecialform.
8

Reply Share

m00nlight223>thedeemon2yearsago

It'strickytosaytheinterpretersupportcall/ccsincetheimplementationusecallccoftheRubylanguage

Reply Share

LostProtocol6yearsago

[Link]'howtowritesimplelanguageinterpretersinpython'andreachedthispage(thankyou
google).Thisarticlehadtwoprofoundimpactsonme.1)Iunderstoodhowtowritelispinterpretersin
python(obvious)and2)[Link],
neverreally'getting'[Link]
[Link](notagain)andalsolearningLispwith
[Link],IwishPythonhadequivalentof(defmacro)oflisp.:)ThanksMr.
NorvigIwillbuyyourbookonArtificialIntelligenceto(somehow)supportthiswebpage:D.
15

Reply Share

Dave5yearsago

Chapter11ofthisbook[Link]
implementationofaminiSchemeinterpreter.(Thisproblemset,[Link]
thatinterpreter).
7

Reply Share

TimFinin6yearsago

[Link]
(quote())forthetheemptylist,asin(definex(cons1(cons2(quote())))),butthewaylistsworkinPython,(eq?
(quote())(quote()))[Link][]:
global_env['null']=[]
6

Reply Share

Sainamdar5yearsago

[Link]/exercisesinSICPwhile
commuting.
[Link]
5

Reply Share

[Link]

9/14

23/07/2016

(How to Write a (Lisp) Interpreter (in Python))

Sainamdar>Sainamdar5yearsago

HereisanothervariationinJavascriptthatseparatessyntacticanalysisfromexecution.
[Link]
2

Reply Share

affiszervmention>Sainamdar3yearsago

stringsdon'tseemtoworkonyourinterpreter
(="str""str")givesTypeError:[Link]

Reply Share

ShantanuInamdar>affiszervmention3yearsago

[Link]"bydesign".AsPeterNorvigwroteinthe
completenessevaluationofLispy,itismissingmanydatatypeslike"Missingstrings,characters,
booleans,ports,vectors,exact/inexactnumbers".

Reply Share

PatHayes6yearsago

[Link],whatwouldreallybeinterestingwouldbetoseehowmanylinesofcodeyouneedtodo
thisinsomethingthatdoesn'thavebuiltinrecursionandinwhichyouhavetodescribegarbagecollection.
Somethinglikeanassemblylanguage...
4

Reply Share

TheoD'Hondt>PatHayes6yearsago

Havealookat[Link]
trampolinesandgarbagecollection...Shouldmakeyouhappy.
4

Reply Share

an691>TheoD'Hondt5yearsago

HelloTheo,aretheslidesandpossiblyvideosoftheselecturesavailable?


PeterNorvig

Reply Share

Mod >PatHayes6yearsago

Exactly,Pat!Thatisadifferentchallenge,[Link]
methatthe"onepage"didthat,ratherthanrelyingonahostlanguagetodoit.(Ontheotherhand,the"one
page"didnotincludethedefinitionsofindividualprocedures,includingsyntacticprocedures.)
2

Reply Share

MikeParr>PatHayes4yearsago

Pat:LispKitlisp(originallybyHenderson),hasanimplementationinPascal,butitusesarraysforlist
storage,ratherthanrecords/structsquiteclosetoassembler.
1

Reply Share

Frakturfreund5yearsago

Thiscodelooksverynice,[Link]
isahighlevellanguage,[Link],isuggesttohavealookintoZozotez,aLisp
InterpeterinBrainfuck(whichisaridiculouslylowleveltoylanguage):
[Link]
3

Reply Share

PeterNorvig

Mod >Frakturfreund5yearsago

YouarerightwearerelyingonmanyfeaturesofPython:callstack,datatypes,garbagecollection,[Link]
[Link]
[Link]'[Link]
myPAIPbook.
[Link]

10/14

23/07/2016

(How to Write a (Lisp) Interpreter (in Python))

myPAIPbook.
3

Reply Share

Frakturfreund>PeterNorvig5yearsago

Thanksforthehint!Illputthebookonmychristmaslist:).

Reply Share

espin5yearsago

Veryniceandinspiring!
FYIthereisanicesmalllispimplementationinCfromPiumatra(aresearcherwithAlanKayatVPRI)
at[Link]
3

Reply Share

missingparen3yearsago

there'samissingclosingpareninbelowexample
>>program="(definearea(lambda(r)(*3.141592653(*rr)))"
itshouldbe
>>program="(definearea(lambda(r)(*3.141592653(*rr))))"
2

Reply Share

fishyrecondite3yearsago

Fabulous...!!
2

Reply Share

Reborn22665yearsago

HiPeter
[Link]
[Link],pleaseletmeknowandIwill
removethepost.
[Link]
Thanks.
2

Reply Share

PeterNorvig

Mod >Reborn22665yearsago

[Link].
4

Reply Share

Reborn2266>PeterNorvig5yearsago

[Link].:)
2

Reply Share

JukkaVlimaa6yearsago

[Link]'tconsbelambdax,y:[x]+[y]?
2

Reply Share

PeterNorvig

Mod >JukkaVlimaa6yearsago

Shouldn'tconsbelambdax,y:[x]+[y]?Mostlyno.I'mtryingtomakeitworkforthecasewhereyisalist
(possiblyempty)[Link],(cons1'(23))shouldbe(123),andifx=1andy=[2,3],then
[1]+[2,3]is[1,2,3],sothat's[x]+y.(Imakeitlist(y)ratherthanyjustincaseyisatuple,notalist.)
Istillcan'tdotheequivalentof(cons12),whichshouldyield(1.2)thatcan'tberepresentedinmy
approachtolists.
2
[Link]

Reply Share

11/14

23/07/2016

(How to Write a (Lisp) Interpreter (in Python))

RalphCorderoy6yearsago

[Link]"[Link](0)#popoff')'"bemoreclearasthefaster,smallerbytecode,
"deltokens[0]"sincetokens[0],returnedbypop(),isn'twanted.
2

Reply Share

PeterNorvig

Mod 6yearsago

[Link]:[Link]
necessarytohaveaccesstotheexptodotailrecursionelimination,butIdon'[Link]:you
[Link],whichcan'teasily
[Link]:IdidthisbecauseIstillgetalotofinterestinJScheme,and
[Link]:YesIcan!Massaro:youarerightthatamutable
[Link]
that,theywouldbedifferentfromPython'slists,andIcouldn'tusePython'smap,etc.
2

Reply Share

EricCooper6yearsago

Hereisamoremetacircularimplementationofprocedures:
defprocedure(parms,exp,env):
"AuserdefinedSchemeprocedure."
returnlambda*args:eval(exp,Env(parms,args,env))
2

Reply Share

PeterNorvig

Mod >EricCooper6yearsago

Thanks,[Link]'tuseitoriginallybecauseIwasthinkingaheadtoLispy2,
whereIcouldn'[Link],yoursuggestionisrighton.

Reply Share

KisituAugustine2yearsago

maybeammissingsomethinghere,whatdoes(_,vars,exp)=xmean??
1

Reply Share

steni>KisituAugustine2yearsago

Itmeansthatwhateverisinx,is"explodedinto"threevariables,ofwhichthefirstisdiscarded(becauseitis
notneeded).
Let'ssayxisanarray:x=[1,2,3].
Then(_,vars,exp)=xwilldiscardthe1,andnowvars=2,andexp=3.

Reply Share

KisituAugustine>steni2yearsago

thankssteni

Reply Share

cym133yearsago

GreatinterpretationofthatmagicalpartofSICP,happytoseethatthisbookstillinspirepeople!
1

Reply Share

[Link].

Konstantinos>Lolol3yearsago

What/prog/,aboardaboutprogramming,hastodowithLISP,aprogramminglanguage?
And/prog/asamatteroffactlikedLISPthoughnotmanyareusingit.
#Yes,fuckingrule14well,Ineverlikedfollowingtherulesanyway.
[Link]

12/14

23/07/2016

(How to Write a (Lisp) Interpreter (in Python))

Reply Share

Lulzy4yearsago

inallhonestythisiscrap
1

Reply Share

wang8monthsago

>(definecirclearea(lambda(r)(*pi(*rr)))
shouldbe
(definecirclearea(lambda(r)(*pi(*rr))))

Reply Share

wang8monthsago

[Link].

Reply Share

FloydLeeayearago

Ithinkschemegenerallyallowsmultipleexpressionsinlambdas,e.g.,localdefinitions.
Sointheevalfunction,thelambdacaseshouldbemorelike:
elif(ast[0]=='lambda'):
params=ast[1]
body=ast[2:]
returnProcedure(params,body,env)
andinProcedure:
def__call__(self,*args):
e=Env([Link],args,[Link])
foridx,exprinenumerate([Link]):
ifidx==len([Link])1:
returnmyeval(expr,e)
else:
myeval(expr,e)
Sorry,can'tseemtogetwhitespacetowork.

Reply Share

alexzhouayearago

verygood

Reply Share

Caridorcayearago

Ithasbeennicetoreadthis,clearlywritten,[Link].

Reply Share

marnout2yearsago

Brillant!

Reply Share

Eashan2yearsago

awesome

Reply Share

DivyanshPrakash3yearsago

[Link].

Reply Share

[Link]

13/14

23/07/2016

(How to Write a (Lisp) Interpreter (in Python))

Guest3yearsago

Howtotestaboveexamplesinpython2.7.5

Reply Share

victoracid3yearsago

Thatsremarkable...

Reply Share

affiszervmention3yearsago

howtodotailcalloptimization?

Reply Share

Loadmorecomments

Subscribe d AddDisqustoyoursiteAddDisqusAdd

[Link]

Privacy

14/14

You might also like