Study one assembled evaluator core in which syntax predicates, environment operations, compound-procedure records, eval, and apply cooperate.
Guiding question
Which responsibilities belong to eval, which belong to apply, and which data structures connect the two?
Classify self-evaluating values, variables, special forms, and applications
Route assignment, definition, if, lambda, begin, and quote to distinct rules
Evaluate an operator and its operands before applicative-order application
Apply either a host primitive or a represented compound procedure
Extend a captured lexical environment for every compound application
Keep evaluator control separate from the guest program represented as data
evaluate is the central syntax dispatcher. Direct values return themselves, names use environment lookup, special forms receive their own evaluation rules, and every remaining pair is treated as an application. The application case evaluates the operator and operand expressions, then transfers the resulting procedure and argument values to apply-procedure.
apply-procedure receives already classified procedure values. A host procedure receives the argument list through apply. A compound-procedure record supplies parameters, body expressions, and its creation environment; application extends that environment with a fresh frame and evaluates the body sequence. The reused canonical driver exercises every core form in one persistent guest environment and makes the educational eval/apply loop visible.
SICP code7,837 of 1,048,576 UTF-8 bytes
(begin(define(tagged-list?expressiontag)(if(pair?expression)(eq?(carexpression)tag)#f))(define(self-evaluating?expression)(cond((number?expression)#t)((string?expression)#t)((boolean?expression)#t)(else#f)))(define(quoted?expression)(tagged-list?expression'quote))(define(assignment?expression)(tagged-list?expression'set!))(define(definition?expression)(tagged-list?expression'define))(define(if?expression)(tagged-list?expression'if))(define(lambda?expression)(tagged-list?expression'lambda))(define(begin?expression)(tagged-list?expression'begin))(define(text-of-quotationexpression)(cadrexpression))(define(assignment-variableexpression)(cadrexpression))(define(assignment-valueexpression)(caddrexpression))(define(definition-variableexpression)(if(symbol?(cadrexpression))(cadrexpression)(car(cadrexpression))))(define(definition-valueexpression)(if(symbol?(cadrexpression))(caddrexpression)(cons'lambda(cons(cdr(cadrexpression))(cddrexpression)))))(define(if-predicateexpression)(cadrexpression))(define(if-consequentexpression)(caddrexpression))(define(if-alternativeexpression)(cadddrexpression))(define(lambda-parametersexpression)(cadrexpression))(define(lambda-bodyexpression)(cddrexpression))(define(begin-actionsexpression)(cdrexpression))(define(operatorexpression)(carexpression))(define(operandsexpression)(cdrexpression))(define(pair-bindingsvariablesvalues)(cond((and(null?variables)(null?values))'())((null?variables)(error"too many arguments"))((null?values)(error"too few arguments"))(else(cons(cons(carvariables)(carvalues))(pair-bindings(cdrvariables)(cdrvalues))))))(define(make-framevariablesvalues)(cons'*frame*(pair-bindingsvariablesvalues)))(define(first-frameenvironment)(carenvironment))(define(frame-bindingsframe)(cdrframe))(define(extend-environmentvariablesvaluesbase)(cons(make-framevariablesvalues)base))(define(lookup-variable-valuevariableenvironment)(if(null?environment)(error"unbound variable"variable)(let((binding(assocvariable(frame-bindings(first-frameenvironment)))))(ifbinding(cdrbinding)(lookup-variable-valuevariable(cdrenvironment))))))(define(define-variable!variablevalueenvironment)(let*((frame(first-frameenvironment))(binding(assocvariable(frame-bindingsframe))))(ifbinding(set-cdr!bindingvalue)(set-cdr!frame(cons(consvariablevalue)(frame-bindingsframe)))))(list'definedvariable))(define(set-variable-value!variablevalueenvironment)(if(null?environment)(error"unbound assignment"variable)(let((binding(assocvariable(frame-bindings(first-frameenvironment)))))(ifbinding(begin(set-cdr!bindingvalue)(list'assignedvariable))(set-variable-value!variablevalue(cdrenvironment))))))(define(make-procedureparametersbodyenvironment)(list'compoundparametersbodyenvironment))(define(compound-procedure?procedure)(tagged-list?procedure'compound))(define(procedure-parametersprocedure)(cadrprocedure))(define(procedure-bodyprocedure)(caddrprocedure))(define(procedure-environmentprocedure)(cadddrprocedure))(define(list-of-valuesexpressionsenvironment)(if(null?expressions)'()(cons(evaluate(carexpressions)environment)(list-of-values(cdrexpressions)environment))))(define(eval-sequenceexpressionsenvironment)(if(null?(cdrexpressions))(evaluate(carexpressions)environment)(begin(evaluate(carexpressions)environment)(eval-sequence(cdrexpressions)environment))))(define(eval-ifexpressionenvironment)(if(evaluate(if-predicateexpression)environment)(evaluate(if-consequentexpression)environment)(evaluate(if-alternativeexpression)environment)))(define(eval-assignmentexpressionenvironment)(set-variable-value!(assignment-variableexpression)(evaluate(assignment-valueexpression)environment)environment))(define(eval-definitionexpressionenvironment)(define-variable!(definition-variableexpression)(evaluate(definition-valueexpression)environment)environment))(define(apply-procedureprocedurearguments)(cond((procedure?procedure)(applyprocedurearguments))((compound-procedure?procedure)(eval-sequence(procedure-bodyprocedure)(extend-environment(procedure-parametersprocedure)arguments(procedure-environmentprocedure))))(else(error"not a guest procedure"procedure))))(define(evaluateexpressionenvironment)(cond((self-evaluating?expression)expression)((symbol?expression)(lookup-variable-valueexpressionenvironment))((quoted?expression)(text-of-quotationexpression))((assignment?expression)(eval-assignmentexpressionenvironment))((definition?expression)(eval-definitionexpressionenvironment))((if?expression)(eval-ifexpressionenvironment))((lambda?expression)(make-procedure(lambda-parametersexpression)(lambda-bodyexpression)environment))((begin?expression)(eval-sequence(begin-actionsexpression)environment))((pair?expression)(apply-procedure(evaluate(operatorexpression)environment)(list-of-values(operandsexpression)environment)))(else(error"unknown guest expression"expression))))(defineprimitive-bindings(list(cons'++)(cons'--)(cons'**)(cons'//)(cons'==)(cons'<<)(cons'>>)(cons'conscons)(cons'carcar)(cons'cdrcdr)(cons'listlist)(cons'null?null?)(cons'pair?pair?)(cons'notnot)))(define(make-global-environment)(list(cons'*frame*primitive-bindings)))(define(run-programformsenvironmentform-budget)(define(loopremainingbudgettranscript)(cond((null?remaining)(list'complete(reversetranscript)0))((=budget0)(list'truncated(reversetranscript)(lengthremaining)))(else(loop(cdrremaining)(-budget1)(cons(evaluate(carremaining)environment)transcript)))))(loopformsform-budget'()))(defineguest-program'((define(squarex)(*xx))(define(make-adderx)(lambda(y)(+xy)))(defineadd-five(make-adder5))(definebase5)(square(+base2))(add-five7)(set!base9)(if(>base8)(squarebase)0)(add-five1)))(list(run-programguest-program(make-global-environment)20)(run-programguest-program(make-global-environment)5)))
Examples
Result—
Output
—
Value
—
Diagnostic
—
Execution trace0 / 0 events
Expected result
The complete run returns (complete ((defined square) (defined make-adder) (defined add-five) (defined base) 49 12 (assigned base) 81 6) 0). The five-form-budget run returns (truncated ((defined square) (defined make-adder) (defined add-five) (defined base) 49) 4).
Trace focus
For each guest form, begin at evaluate and identify the selected clause. On applications, separate operator evaluation, left-to-right list-of-values construction, and apply-procedure. Follow each compound procedure into a new frame linked to the environment stored in its procedure record. The top-level form budget belongs to the driver and is not part of eval or apply.
Try it yourself
Change the program and compare the result.
Add a guest begin expression whose first form mutates base and whose last form calls add-five. Before running, identify every evaluate clause and the one apply-procedure branch the new form will use.
Show hint
begin delegates to eval-sequence. set! mutates an existing frame, while add-five remains a compound procedure carrying the environment created by make-adder.