CS 360 Programming Languages InterpretersInterpreter vs compiler vs combinations is about a...
Transcript of CS 360 Programming Languages InterpretersInterpreter vs compiler vs combinations is about a...
CS360ProgrammingLanguages
Interpreters
ImplementingPLsMostofthecourseislearningfundamentalconceptsforusing andunderstanding PLs.
– Syntaxvs.semanticsvs.idioms.– Powerfulconstructslikeclosures,first-classobjects,iterators(streams),multithreading,…
Aneducatedcomputerscientistshouldalsoknowsomethingsaboutimplementing PLs.
– Implementingsomethingrequiresfullyunderstandingitssemantics.
– Thingslikeclosuresandobjectsarenot“magic.”– ManyprogrammingtasksarelikeimplementingsmallPLs.
• Example:"connect-the-dotsprogramminglanguage"from141.
WaystoimplementalanguageTwofundamentalwaystoimplementaprogramminglanguageX.
• Writeaninterpreter inanotherlanguageY.– Betternames:evaluator,executor.– Immediatelyexecutestheinputprogramasit'sread.
• Writeacompiler inanotherlanguageY thatcompilestoathirdlanguageZ.– Bettername:translator– TakesaprograminX andproduceanequivalentprograminZ.
Firstprogramminglanguage?
Firstprogramminglanguage?
Interpretersvs compilers
• Interpreters– Takesone"statement"ofcodeatatimeandexecutesitinthelanguageoftheinterpreter.
– Likehavingahumaninterpreterwithyouinaforeigncountry.
• Compilers– TranslatecodeinlanguageXintocodeinlanguageZandsaveitforlater.(Typicallytoafileondisk.)
– Likehavingapersontranslateadocumentintoaforeignlanguageforyou.
Realityismorecomplicated
Evaluation(interpreter)andtranslation(compiler)areyouroptions.
– Butinmodernpracticewecanhavemultiplelayersofboth.
AexamplewithJava:– Javawasdesignedtobeplatformindependent.
• AnyprogramwritteninJavashouldbeabletorunonanycomputer.
– Achievedwiththe"JavaVirtualMachine."• Anidealizedcomputerforwhichpeoplehavewritteninterpretersthatrunon"real"computers.
Example:Java
• Javaprogramsarecompiledtoan"intermediaterepresentation"calledbytecode.– Thinkofbytecode asaninstructionsetfortheJVM.
• Bytecode istheninterpretedbya(software)interpreterinmachine-code.
• Complication:Bytecodeinterpretercancompilefrequently-usedfunctionstomachinecodeifitdesires (just-in-timecompilation).
• CPUitselfisaninterpreterformachinecode.
SermonInterpretervscompilervscombinationsisaboutaparticularlanguageimplementation,notthelanguagedefinition.
Sothereisnosuchthingasa“compiledlanguage”oran“interpretedlanguage.”
– Programscannot“see”howtheimplementationworks.
Unfortunately,youhearthesephrasesallthetime:– “Cisfasterbecauseit’scompiledandLISPisinterpreted.”– IcanwriteaCinterpreteroraLISPcompiler,regardlessofwhatmostimplementationshappentodo.
OnecomplicationInatraditionalimplementationviacompiler,youdonotneedthelanguageimplementation(thecompiler)toruntheprogram.
– Onlytocompileit.– Toletotherpeoplerunyourprogram,yougivethemthecompiledbinarycode.
ButRacket,Scheme,LISP,Javascript,Ruby,…haveeval– Allowsaprogram,whileitisrunning,tocreateastring(orinRacket,alist)witharbitrarycodeandexecuteit.
– Sincewedon’tknowaheadoftimewhatthecodewillbe,weneedalanguageimplementationatrun-timetosupporteval
– Usuallyyouseethisinlanguagesthataretraditionallyinterpreted,becausethenimplementingeval iseasy:theinterpreterisalreadyavailable.
eval /apply• ThesefunctionsarebuiltintoRacket,andareatraditionalpartofLISP/Schemeimplementations.
• eval:takesalistargumentandtreatsitlikeprogramcode,executingitandreturningtheresult.
• apply:takesafunctionandalist,andcallsthefunctionontheargumentsinthelist.
quote• Alsobuilt-in,butwedon'tnoticeitbecauseit'scalledautomaticallywheneverweuseasinglequote.
• (quote …) or'(…) isaspecialformthatmakes“everythingunderneath”intoplainsymbolsandlists,insteadofinterpretingthemasvariablesorfunctioncalls.
• eval andquote areinverses:– quote stopsevaluationofsomething.– eval forcesevaluationofsomething.
Backtoimplementingalanguage"(cons 1 '(2 3))"
ParsingCall
Function Integer
Constant
1
cons
Staticchecking(whatischeckeddependsonPL)
PossibleErrors/warnings
Restofimplementation
PossibleErrors/warnings
List
Constant Constant
2 3
SkippingthosestepsLISP/Scheme-likelanguageshaveaninterestingproperty:
– Theformatinwhichwewriteatheprogramcode(alist)isidenticaltothemaindatastructurethelanguageitselfusestorepresentdata(alist).
– Becausetheselistsarealwaysfullyparenthesized,wegetparsingessentiallyforfree!
– NotsoinPython,C++,Java,etc.
Wecanalso,forsimplicity,skipstaticchecking.– Assumesubexpressionshavecorrecttypes.
• Wewillnotworryabout(add #f "hi").– Interpreterwilljustcrash.
Write(Mini-)RacketinRacket
Mini-Racket
• Onlyonedatatype:numbers.– Nobooleans,lists,etc.
• Mini-Racketwillusenames forallfunctionsratherthansymbols.– add,sub,mul,etc.
• Simplifiedmechanismsforconditionals,functiondefinitions,andfunctioncalls.
Heartoftheinterpreter
• mini-eval:Evaluatesanexpressionandreturnsavalue(willcallmini-apply tohandlefunctions)
• mini-apply:Takesafunctionandargumentvaluesandevaluateitsbody(callsmini-eval).
(define (mini-eval expr env)is this a ______ expression?if so, then call our special handler
for that type of expression.)What kind of expressions will we have?• numbers• variables (symbols)• math function calls• others as we need them
• Howdoweevaluatea(literal)number?– Thatis,whentheinterpreterseesanumber,whatvalueshoulditreturn?
• Justreturnit!
• Psuedocode forfirstlineofmini-eval:– Ifthisexpressionisanumber,thenreturnit.
• Howdowehandle(add 3 4)?
• Needtwofunctions:– Onetodetectthatanexpressionisanadditionexpression.
– Onetoevaluatetheexpression.
(add 3 4)
• Isthisanexpressionanadditionexpression?(equal? 'add (car expr))
• Evaluateanadditionexpression:(+ (cadr expr) (caddr expr))
Youtry(lab,part1)
• Addsubtraction(e.g.,sub)• Addmultiplication(mul)• Adddivision(div)• Addexponentiation(exp)• Optional:
– Addotherprimitives,likesqrt,abs,etc.– Makesubworkwithsinglearguments(returnsthenegation).
(add3(add45))
• Whydoesn'tthiswork?
(add3(add45))
• Howshould ourlanguageevaluatethissortofexpression?
• Wecouldforbidthiskindofexpression.– Insistthingstobeaddedalwaysbenumbers.
• Or,wecouldallowthethingstobeaddedtobeexpressionsthemselves.– Needarecursivecalltomini-eval insideeval-add.
Youtry(lab,part2)
• Fixyourmathcommandssothattheywillrecursivelyevaluatetheirarguments.
AddingVariables
Implementingvariables
• Representaframe asahashtable.• Racket'shashtables:(define ht (make-hash))(hash-set! ht key value)(hash-has-key? ht key)(hash-ref ht key)
Implementingvariables
• Representanenvironmentasalistofframes.
globalx 2y 3
f .
x 7y 1
hashtablex: 7y: 1
hashtablex: 2y: 3
Implementingvariables
• Twothingswecandowithavariableinourprogramminglanguage:– Defineavariable– Getthevalueofavariable
• PrettymuchthesametwothingswecandowithavariableinregularRacket(exceptforset!)
Gettingthevalueofavariable
• Newtypeofexpression:asymbol.• Whenevermini-eval seesasymbol,itshouldlookupthevalueofthevariablecorrespondingtothatsymbol.
GettingthevalueofavariableFollowtherulesoflexicalscoping:
(define (lookup-variable-value var env); Pseudocode:; If our current frame has a value for the; variable, then get its value and return it.; Otherwise, if our current frame has a frame; pointer, then follow it and try the lookup; there.; Otherwise, (if we are out of frames), throw an; error.
GettingthevalueofavariableFollowtherulesoflexicalscoping:
(define (lookup-variable-value var env)(cond ((hash-has-key? (car env) var)
(hash-ref (car env) var))((not (null? env))
(lookup-variable-value var (cdr env)))((null? env)
(error "unbound variable" var))))
Definingavariable(lab,part3)
• mini-eval needstohandleexpressionsthatlooklike(define variable expr)– expr cancontainsub-expressions.
• Addtwofunctionstotheevaluator:– definition?:testsifanexpressionfitstheformofadefinition.
– eval-definition:extractthevariable,recursivelyevaluatetheexpressionthatholdsthevalue,andaddabindingtothecurrentframe.
Implementingconditionals
• WewillhaveoneconditionalinMini-Racket:ifzero
• Syntax:(ifzero expr1 expr2 expr3)• Semantics:
– Evaluateexpr1,testifit'sequaltozero.– Ifyes,evaluateandreturnexpr2.– Ifno,evaluateandreturnexpr3.
Implementingconditionals(lab,part4)
• Addfunctionsifzero? andeval-ifzero.
• Iftime,trychallengesonthebackofthelab.
• Designingourinterpreteraroundmini-eval.
• (define (mini-eval expr env) …• Determineswhattypeofexpressionexpr is• Dispatchtheevaluationoftheexpressiontotheappropriatefunction– number? ->evaluateinplace– symbol? ->lookup-variable-value– add?/subtract?/multiply? ->appropriatemathfunc
– definition? ->eval-define– ifzero? ->eval-ifzero
Today
• Twomorepiecestoadd:– Closures(lambda? /eval-lambda)– Functioncalls(call? /eval-call)
Mini-RacketwillhavesomesimplificationsfromnormalRacket.
• Allfunctionswillhaveexactlyoneargument.• Removestheneedforlambdaexpressionstohaveparentheses.
• NormalRacket:(lambda (x) (+ x 1))
• Mini-Racket:(lambda x (add x 1))
Mini-RacketwillhavesomesimplificationsfromnormalRacket.
• NormalRackethastwoversionsofdefine,oneforvariablesandoneforfunctions:(define x 3)(define (add1 x) (+ x 1)).
• The2nd versionisjustashortcutfor(define add1 (lambda (x) (+ x 1))
Mini-RacketwillhavesomesimplificationsfromnormalRacket.
• Mini-Racketwillnothavethe"shortcut"define;wemustuseanexplicitlambda.
• NormalRacket:(define (add1 x) (+ x 1)) (define add1 (lambda (x) (+ x 1)))
• Mini-Racket:(define add1 (lambda x (add x 1))
Mini-RacketwillhavesomesimplificationsfromnormalRacket.
• NormalRacketrecognizesafunctioncallasanylistthatstartswithafunctionname(oranythingthatevaluatestoaclosure).
• Mini-Racketwillrecognizefunctioncallsasliststhatstartswiththesymbolcall.– ThismakestheMini-Racketsyntaxmorecomplicated,butsimplifiestheinterpretercode.
Mini-RacketwillhavesomesimplificationsfromnormalRacket.
• NormalRacket:(add1 5)
• Mini-Racket:(call add1 5)
Implementingclosures
• InMini-Racket,all(user-defined)functionsandclosureswillhaveasingleargument.
• Syntax:(lambda var expr)– Notethedifferentsyntaxfrom"real"Racket.
• Semantics:Createsanewclosure(anonymousfunction)ofthesingleargumentvar,whosebodyisexpr.
(lambda var expr)
• Needanewdatastructuretorepresentaclosure.
• Whycan'twejustrepresentthemasthelist(lambdavar closure)above?– Hint:Whatismissing?Thinkofenvironmentdiagrams.
(lambda var expr)
• WewillrepresentclosuresinternallyinMini-Racketusingalistoffourcomponents:– Thesymbol'closure– Theargumentvariable(var)– Thebody(expr)– Theenvironmentinwhichthisclosurewasdefined.
Evaluateattoplevel:(lambda x (add x 1))
Ourevaluatorshouldreturn'(closure x (add x 1) (#hash(…)))
Arg:xCode:(addx1)
global
Writelambda? andeval-lambda
• lambda? iseasy.• eval-lambda should:
– Extractthevariablenameandthebody,butdon’tevaluatethebody (notuntilwecallthefunction)
– Returnalistofthesymbol'closure,thevariable,thebody,andthecurrentenvironment.
(define (eval-lambda expr env) (list 'closure
(cadr expr) ; the variable(caddr expr) ; the bodyenv))
Functioncalls
• Firstweneedtheotherhalfoftheeval/apply paradigm.
• Rememberfromenvironmentdiagrams:
• Toevaluateafunctioncall,makeanewframewiththefunction'sargumentsboundtotheirvalues,thenrunthebodyofthefunctionusingthenewenvironmentforvariablelookups.
Mini-Apply
(define (mini-apply closure argval)
Pseudocode:• Makeanewframemappingtheclosure'sargument(i.e.,
thevariablename)toargval.• Makeanewenvironmentconsistingofthenewframe
pointingtotheclosure'senvironment.• Evaluatetheclosure'sbodyinthenewenvironment(and
returntheresult).
Mini-Apply
(define (mini-apply closure argval)(let ((new-frame (make-hash)))(hash-set! new-frame <arg-name> argval)(let ((new-env
<construct new environment>))<eval body of closure in new-env>
)))
Mini-Apply
(define (mini-apply closure argval)(let ((new-frame (make-hash)))(hash-set! new-frame (cadr closure) argval)(let ((new-env
(cons new-frame (cadddr closure))))(mini-eval (caddr closure) new-env))))
Functioncalls
• Syntax:(call expr1 expr2)• Semantics:
– Evaluateexpr1 (mustevaluatetoaclosure)– Evaluateexpr2 toavalue(theargumentvalue)– Applyclosuretovalue(andreturnresult)
Youtryit
• Writecall? (easy)• Writeeval-call (alittleharder)
– Evaluateexpr1 (mustevaluatetoaclosure)– Evaluateexpr2 toavalue(theargumentvalue)– Applyclosuretovalue(andreturnresult)
• Whendone,younowhaveaTuring-completelanguage!
; expr looks like; (call expr1 expr2)(define (eval-call expr env)
(mini-apply <eval the function><eval the argument>)
; expr looks like; (call expr1 expr2)(define (eval-call expr env)
(mini-apply (mini-eval (cadr expr) env)(mini-eval (caddr expr) env)))
Magicinhigher-orderfunctionsThe“magic”:Howisthe“rightenvironment”aroundforlexicalscopewhenfunctionsmayreturnotherfunctions,storethemindatastructures,etc.?
Lackofmagic:Theinterpreterusesaclosuredatastructuretokeeptheenvironmentitwillneedtouselater
Isthisexpensive?
• Time tobuildaclosureistiny:makealistwithfouritems.
• Space tostoreclosuresmight belargeifenvironmentislarge.
Interpretersteps
• Parser– Takescodeandproducesanintermediaterepresentation(IR),e.g.,abstractsyntaxtree.
• Staticchecking– Typicallyincludessyntacticalanalysisandtypechecking.
• InterpreterdirectlyrunscodeintheIR.
Compilersteps
• Parser• Staticchecking• Codeoptimizer
– TakeASTandalterittomakethecodeexecutefaster.
• Codegenerator– Producecodeinoutputlanguage(andsaveit,asopposedtorunningit).
Codeoptimization
// Test if n is primeboolean isPrime(int n) {for (int x = 2; x < sqrt(n); x++) {if (n % x == 0) return false;
}return true;}
Codeoptimization
// Test if n is primeboolean isPrime(int n) {double temp = sqrt(n);for (int x = 2; x < temp; x++) {if (n % x == 0) return false;
}return true;}
Commoncodeoptimizations
• Replacingconstantexpressionswiththeirevaluations.
• Ex:Gamethatdisplaysan8by8grid.Eachcellwillbe50pixelsby50pixelsonthescreen.– int CELL_WIDTH = 50;– int BOARD_WIDTH = 8 * CELL_WIDTH;
Commoncodeoptimizations
• Replacingconstantexpressionswiththeirevaluations.
• Ex:Gamethatdisplaysan8by8grid.Eachcellwillbe50pixelsby50pixelsonthescreen.– int CELL_WIDTH = 50;– int BOARD_WIDTH = 400;
• Referencestothesevariableswouldprobablyreplacedwithconstantsaswell.
Commoncodeoptimizations
• Reorderingcodetoimprovecacheperformance.for (int x = 0; x < HUGE_NUMBER; x++) {huge_array[x] = f(x)another_huge_array[x] = g(x)
}
Commoncodeoptimizations
• Reorderingcodetoimprovecacheperformance.for (int x = 0; x < HUGE_NUMBER; x++) {huge_array[x] = f(x)
}for (int x = 0; x < HUGE_NUMBER; x++) {another_huge_array[x] = g(x)
}
Commoncodeoptimizations
• Loops:unrolling,combining/distribution,changenesting
• Findingcommonsubexpressions andreplacingwithareferencetoatemporaryvariable.– (a + b)/4 + (a + b)/3
• Recursion:replacewithiterationifpossible.– That'swhattail-recursionoptimizationdoes!
• Whydon'tinterpretersdotheseoptimizations?
• Usually,there'snotenoughtime.– WeneedthecodetorunNOW!– Sometimes,canoptimizealittle(e.g.,tail-recursion).
Codegeneration
• Lastphaseofcompilation.• Choosewhatoperationstouseintheoutputlanguageandwhatordertoputthemin(instructionselection,instructionscheduling).
• Ifoutputinalow-levellanguage:– Pickwhatvariablesarestoredinwhichregisters(registerallocation).
– Includedebuggingcode?(store"true"function/variablenamesandlinenumbers?)
Java
• Usesbothinterpretationandcompilation!• Step1:CompileJavasourcetobytecode.
– Bytecode is"machinecode"foramade-upcomputer,theJavaVirtualMachine(JVM).
• Step2:Aninterpreterinterpretsthebytecode.
• Historically,thebytecode interpretermadeJavacodeexecuteveryslowly(1990s).
Just-in-timecompilation
• Bytecode interpretershistoricallywouldtranslateeachbytecode commandintomachinecodeandimmediatelyexecuteit.
• Ajust-in-timecompilerhastwooptimizations:– Cachesbytecode ->machinecodetranslationssoitcanre-usethemlater.
– Dynamicallycompilessectionsofbytecode intomachinecode"whenitthinksitshould."
JIT:aclassictrade-off
• Startupisslightlyslower– Needtimetodosomeinitialdynamiccompilation.
• Oncetheprogramstarts,itrunsfasterthanaregularinterpreter.– Becausesomesectionsarenowcompiled.