Why do we need an interpreter? SICP Interpretation part 1. Role of each part of the interpreter. 1. Arithmetic calculator.

Similar documents
6.184 Lecture 4. Interpretation. Tweaked by Ben Vandiver Compiled by Mike Phillips Original material by Eric Grimson

6.037 Lecture 4. Interpretation. What is an interpreter? Why do we need an interpreter? Stages of an interpreter. Role of each part of the interpreter

Scheme in Scheme: The Metacircular Evaluator Eval and Apply

Streams, Delayed Evaluation and a Normal Order Interpreter. CS 550 Programming Languages Jeremy Johnson

Fall Semester, The Metacircular Evaluator. Today we shift perspective from that of a user of computer langugaes to that of a designer of

Project 2: Scheme Interpreter

6.001 Notes: Section 15.1

An Explicit-Continuation Metacircular Evaluator

Discussion 12 The MCE (solutions)

Lexical vs. Dynamic Scope

CS61A Midterm 2 Review (v1.1)

Building a system for symbolic differentiation

Functional Programming. Pure Functional Programming

CS 275 Name Final Exam Solutions December 16, 2016

Computer Science 21b (Spring Term, 2015) Structure and Interpretation of Computer Programs. Lexical addressing

Syntactic Sugar: Using the Metacircular Evaluator to Implement the Language You Want

;;; Determines if e is a primitive by looking it up in the primitive environment. ;;; Define indentation and output routines for the output for

A Brief Introduction to Scheme (II)

6.037 Lecture 7B. Scheme Variants Normal Order Lazy Evaluation Streams

(scheme-1) has lambda but NOT define

Building up a language SICP Variations on a Scheme. Meval. The Core Evaluator. Eval. Apply. 2. syntax procedures. 1.

CS 360 Programming Languages Interpreters

regsim.scm ~/umb/cs450/ch5.base/ 1 11/11/13

Deferred operations. Continuations Structure and Interpretation of Computer Programs. Tail recursion in action.

CSc 520 Principles of Programming Languages

Building a system for symbolic differentiation

Summer 2017 Discussion 10: July 25, Introduction. 2 Primitives and Define

Lecture 09: Data Abstraction ++ Parsing is the process of translating a sequence of characters (a string) into an abstract syntax tree.

Basic Scheme February 8, Compound expressions Rules of evaluation Creating procedures by capturing common patterns

Fall 2018 Discussion 8: October 24, 2018 Solutions. 1 Introduction. 2 Primitives

The Eval/Apply Cycle Eval. Evaluation and universal machines. Examining the role of Eval. Eval from perspective of language designer

CS 314 Principles of Programming Languages. Lecture 16

Fall Semester, Lecture Notes { November 12, Variations on a Scheme Nondeterministic evaluation

Review Analyzing Evaluator. CS61A Lecture 25. What gets returned by mc-eval? (not analyzing eval) REVIEW What is a procedure?

SCHEME 8. 1 Introduction. 2 Primitives COMPUTER SCIENCE 61A. March 23, 2017

Data abstraction, revisited

SCHEME AND CALCULATOR 5b

Principles of Programming Languages 2017W, Functional Programming

Below are example solutions for each of the questions. These are not the only possible answers, but they are the most common ones.

TAIL RECURSION, SCOPE, AND PROJECT 4 11

6.001: Structure and Interpretation of Computer Programs

SCHEME 7. 1 Introduction. 2 Primitives COMPUTER SCIENCE 61A. October 29, 2015

Chapter 1. Fundamentals of Higher Order Programming

User-defined Functions. Conditional Expressions in Scheme

Documentation for LISP in BASIC

Introduction to Scheme

Using Symbols in Expressions (1) evaluate sub-expressions... Number. ( ) machine code to add

Procedural abstraction SICP Data abstractions. The universe of procedures forsqrt. Procedural abstraction example: sqrt

LECTURE 16. Functional Programming

Scheme. Functional Programming. Lambda Calculus. CSC 4101: Programming Languages 1. Textbook, Sections , 13.7


University of Massachusetts Lowell

From Syntactic Sugar to the Syntactic Meth Lab:

An Explicit Continuation Evaluator for Scheme

An Introduction to Scheme

Lecture 12: Conditional Expressions and Local Binding

Comp 311: Sample Midterm Examination

6.945 Adventures in Advanced Symbolic Programming

CSE 413 Languages & Implementation. Hal Perkins Winter 2019 Structs, Implementing Languages (credits: Dan Grossman, CSE 341)

CS 314 Principles of Programming Languages

CS61A Discussion Notes: Week 11: The Metacircular Evaluator By Greg Krimer, with slight modifications by Phoebus Chen (using notes from Todd Segal)

CS61A Summer 2010 George Wang, Jonathan Kotker, Seshadri Mahalingam, Eric Tzeng, Steven Tang

Announcements. The current topic: Scheme. Review: BST functions. Review: Representing trees in Scheme. Reminder: Lab 2 is due on Monday at 10:30 am.

Code example: sfact SICP Explicit-control evaluator. Example register machine: instructions. Goal: a tail-recursive evaluator.

Turtles All The Way Down

Administrivia. Simple data types

COP4020 Programming Languages. Functional Programming Prof. Robert van Engelen

Modern Programming Languages. Lecture LISP Programming Language An Introduction

This exam is worth 70 points, or about 23% of your total course grade. The exam contains 15 questions.

Type Inference and Type Habitation (5/10/2004)

FP Foundations, Scheme

Spring 2018 Discussion 7: March 21, Introduction. 2 Primitives

CS 61A Interpreters, Tail Calls, Macros, Streams, Iterators. Spring 2019 Guerrilla Section 5: April 20, Interpreters.

Fall 2017 Discussion 7: October 25, 2017 Solutions. 1 Introduction. 2 Primitives

COP4020 Programming Assignment 1 - Spring 2011

4.2 Variations on a Scheme -- Lazy Evaluation

MORE SCHEME. 1 What Would Scheme Print? COMPUTER SCIENCE MENTORS 61A. October 30 to November 3, Solution: Solutions begin on the following page.

Scheme Quick Reference

CS 314 Principles of Programming Languages

Normal Order (Lazy) Evaluation SICP. Applicative Order vs. Normal (Lazy) Order. Applicative vs. Normal? How can we implement lazy evaluation?

6.001 Notes: Section 8.1

Lecture Notes on Lisp A Brief Introduction

Recursive Functions of Symbolic Expressions and Their Computation by Machine Part I

Principles of Programming Languages

CS 342 Lecture 8 Data Abstraction By: Hridesh Rajan

SCHEME The Scheme Interpreter. 2 Primitives COMPUTER SCIENCE 61A. October 29th, 2012

Lecture 3: Expressions

Interpreters and Tail Calls Fall 2017 Discussion 8: November 1, 2017 Solutions. 1 Calculator. calc> (+ 2 2) 4

Programming Languages

CSE341: Programming Languages Lecture 17 Implementing Languages Including Closures. Dan Grossman Autumn 2018

Scheme Quick Reference

Parsing Scheme (+ (* 2 3) 1) * 1

CSE 341: Programming Languages

6.001, Spring Semester, 1998, Final Exam Solutions Your Name: 2 Part c: The procedure eval-until: (define (eval-until exp env) (let ((return (eval-seq

Functional programming with Common Lisp

Ways to implement a language

CSE341 Autumn 2017, Final Examination December 12, 2017

Notes on Higher Order Programming in Scheme. by Alexander Stepanov

Not really what happens in the computer. The Environment Model. (set-car! l 'z) (car l) z

Functional Programming. Pure Functional Languages

Transcription:

.00 SICP Interpretation part Parts of an interpreter Arithmetic calculator Names Conditionals and if Store procedures in the environment Environment as explicit parameter Defining new procedures Why do we need an interpreter? Abstractions let us bury details and focus on use of modules to solve large systems Need to unwind abstractions at execution time to deduce meaning Have seen such a process Environment Model Now want to describe that process as a procedure Stages of an interpreter input to each stage Lexical analyzer "(average (+ ))" Parser Evaluator Environment Printer ( average ( average + ) ) "" + Role of each part of the interpreter Lexical analyzer break up input string into "words" called tokens Parser convert linear sequence of tokens to a tree like diagramming sentences in elementary school also convert self-evaluating tokens to their internal values #f is converted to the internal false value Evaluator follow language rules to convert parse tree to a value read and modify the environment as needed Printer convert value to human-readable output string Goal of lecture Implement an interpreter for a programming language Only write evaluator and environment use scheme's reader for lexical analysis and parsing use scheme's printer for output to do this, our language must look like scheme. Arithmetic calculator Want to evaluate arithmetic expressions of two arguments, like: ( ( )) Call the language scheme* All names end with a star Start with a simple calculator for arithmetic Progressively add scheme* features only get halfway there today

. Arithmetic calculator We are just walking through a tree (define (tag-check e sym) (and (pair? e) (eq? (car e) sym))) (define (sum? e) (tag-check e ')) ((sum? exp) (eval-sum exp)) (eval ) (define (eval-sum exp) (+ (eval (cadr exp)) (eval (caddr exp)))) (eval '( ( ))) sum? checks the tag We are just walking through a tree (eval-sum ). Arithmetic calculator ( ( )) What are the argument and return values of eval each time it is called in the evaluation of line? (+ (eval ) (eval )) (eval ) (eval ) (eval ) (eval-sum '( )) (eval '( )) (eval-sum '( ( ))) (eval '( ( ))) (+ (eval ) (eval )) 0. Things to observe cond determines the expression type no work to do on numbers scheme's reader has already done the work it converts a sequence of characters like "" to an internal binary representation of the number eval-sum recursively calls eval on both argument expressions. Names Extend the calculator to store intermediate results as named values (define* x* ( )) store result as x* ( x* ) use that result Store bindings between names and values in a table What are the argument and return values of eval each time it is called in lines and? Show the environment each time it changes during evaluation of these two lines.

. Names (define (define? exp) (tag-check exp 'define*)) ((sum? exp) (eval-sum exp)) ((? exp) (lookup exp)) ((define? exp) (eval-define exp)) ; table ADT from prior lecture: ; make-table void -> table ; table-get table, -> (binding null) ; table-put! table,, anytype -> undef ; binding-value binding -> anytype (define environment (make-table)). Names (define (lookup name) (let ((binding (table-get environment name))) (if (null? binding) (error "unbound variable: " name) (binding-value binding)))) (define (eval-define exp) (let ((name (cadr exp)) (defined-to-be (caddr exp))) (table-put! environment name (eval defined-to-be)) 'undefined)) (eval '(define* x* ( ))) (eval '( x* )) How many times is eval called in these two evaluations? Evaluation of page lines and (eval '(define* x* ( ))) (eval '( )) (eval ) ==> (eval ) ==> ==> names values ==> undefined x* (eval '( x* )) (eval 'x*) ==> (eval ) ==> ==>. Things to observe Use scheme function? to check for a name the reader converts sequences of characters like "x*" to s in the parse tree Can use any implementation of the table ADT eval-define recursively calls eval on the second subtree but not on the first one eval-define returns a special undefined value. Conditionals and if Extend the calculator to handle conditionals and if: (if* (greater* y* ) ( y* ) ) greater* if* an operation that returns a boolean an operation that evaluates the first subexp, checks if value is true or false What are the argument and return values of eval each time it is called in line? (define (greater? exp) (tag-check exp 'greater*)) (define (if? exp) (tag-check exp 'if*))... ((greater? exp) (eval-greater exp)) ((if? exp) (eval-if exp)) (define (eval-greater exp) (> (eval (cadr exp)) (eval (caddr exp)))). Conditionals and If (define (eval-if exp) (let ((predicate (cadr exp)) Note: if* is stricter (consequent (caddr exp)) (alternative (cadddr exp))) than Scheme s if (let ((test (eval predicate))) ((eq? test #t) (eval consequent)) ((eq? test #f) (eval alternative)) (error "predicate not boolean: " predicate)))))) (eval '(define* y* )) (eval '(if* (greater* y* ) ( y* ) ))

We are just walking through a tree if* greater* y* y* (eval ) greater* y* Then (eval ) or (eval ) Evaluation of page line (eval '(if* (greater* y* ) ( y* ) )) (eval '(greater* y* )) (eval 'y*) ==> (eval ) ==> ==> #t (eval '( y* )) (eval 'y*) ==> (eval ) ==> ==> ==> y* 0. Things to observe eval-greater is just like eval-sum from page recursively call eval on both argument expressions call scheme > to compute value eval-if does not call eval on all argument expressions: call eval on the predicate call eval on the consequent or on the alternative but not both. Store operators in the environment Want to add lots of operators but keep eval short Operations like and greater* are similar evaluate all the argument subexpressions perform the operation on the resulting values Call this standard pattern an application Implement a single case in eval for all applications Approach: eval the first subexpression of an application put a name in the environment for each operation value of that name is a procedure apply the procedure to the operands (define (application? e) (pair? e)) ((? exp) (lookup exp)) ((define? exp) (eval-define exp)) ((if? exp) (eval-if exp)) ((application? exp) (apply (eval (car exp)) (map eval (cdr exp)))). Store operators in the environment (define scheme-apply apply) ;; rename scheme s apply so we can reuse the name (define (apply operator operands) (if (primitive? operator) (scheme-apply (get-scheme-procedure operator) operands) (error "operator not a procedure: " operator))) ;; primitive: an ADT that stores scheme procedures (define prim-tag 'primitive) (define (make-primitive scheme-proc)(list prim-tag scheme-proc)) (define (primitive? e) (tag-check e prim-tag)) (define (get-scheme-procedure prim) (cadr prim)) (define environment (make-table)) (table-put! environment ' (make-primitive +)) (table-put! environment 'greater* (make-primitive >)) (table-put! environment 'true* #t) Environment after eval line names values z* true* #t greater* primitive scheme procedure + (eval '(define* z* )) (eval '( )) (eval '(if* true* 0 )) primitive scheme procedure >

Evaluation of eval line (eval '( )) (apply (eval ') (map eval '( ))) (apply '(primitive #[add]) (list (eval ) (eval )) (apply '(primitive #[add]) '( )) (scheme-apply (get-scheme-procedure '(primitive #[add])) '( )) (scheme-apply #[add] '( )) Evaluation of eval line (eval '(if* true* 0 )) (eval-if '(if* true* 0 )) (let ((test (eval 'true*)))...)) (let ((test (lookup 'true*)))...)) (let ((test #t))...)) (eval 0) 0 Apply is never called!. Things to observe applications must be last case in eval no tag check apply is never called in line applications evaluate all subexpressions expressions that need special handling, like if*, gets their own case in eval. Environment as explicit parameter change from (eval '( )) to (eval '( ) environment) all procedures that call eval have extra argument lookup and define use environment from argument No other change from evaluator Only nontrivial code: case for application? in eval (define (eval exp env) ((? exp) (lookup exp env)) ((define? exp) (eval-define exp env)) ((if? exp) (eval-if exp env)) ((application? exp) (apply (eval (car exp) env) (map (lambda (e) (eval e env)) (cdr exp)))) (define (lookup name env) (let ((binding (table-get env name))) (if (null? binding) (error "unbound variable: " name) (binding-value binding)))). Environment as explicit parameter This change is boring! Exactly the same functionality as #. (define (eval-define exp env) (let ((name (cadr exp)) (defined-to-be (caddr exp))) (table-put! env name (eval defined-to-be env)) 'undefined)) (define (eval-if exp env) (let ((predicate (cadr exp)) (eval '(define* z* ( )) (consequent (caddr exp)) environment) (alternative (cadddr exp))) (eval '(if* (greater* z* ) 0 ) (let ((test (eval predicate env))) environment) ((eq? test #t) (eval consequent env)) ((eq? test #f) (eval alternative env)) (error predicate not boolean: " predicate)))))). Defining new procedures Want to add new procedures For example, a scheme* program: (define* twice* (lambda* (x*) ( x* x*))) (twice* ) Strategy: Add a case for lambda* to eval the value of lambda* is a compound procedure Extend apply to handle compound procedures Implement environment model 0

(define (lambda? e) (tag-check e 'lambda*)) (define (eval exp env) ((? exp) (lookup exp env)) ((define? exp) (eval-define exp env)) ((if? exp) (eval-if exp env)) ((lambda? exp) (eval-lambda exp env)) ((application? exp) (apply (eval (car exp) env) (map (lambda (e) (eval e env)) (cdr exp)))) (define (eval-lambda exp env) (let ((args (cadr exp)) (body (caddr exp))) (make-compound args body env))). Defining new procedures Implementation of lambda* (eval '(lambda* (x*) ( x* x*)) GE) (eval-lambda '(lambda* (x*) ( x* x*)) GE) (make-compound '(x*) '( x* x*) GE) (list 'compound '(x*) '( x* x*) GE) (define (apply operator operands) ((primitive? operator) (scheme-apply (get-scheme-procedure operator) operands)) ((compound? operator) (eval (body operator) (extend-env-with-new-frame (parameters operator) operands (env operator)))) (error "operator not a procedure: " operator)))) ;; ADT that implements the double bubble (define compound-tag 'compound) (define (make-compound parameters body env) (list compound-tag parameters body env)) (define (compound? exp) (tag-check exp compound-tag)) (define (parameters compound) (cadr compound)) (define (body compound) (caddr compound)) (define (env compound) (cadddr compound)) compound x* GE This data structure is a procedure! Defining a named procedure (eval '(define* twice* (lambda* (x*) ( x* x*))) GE) names values z* true* #t twice* primitive scheme procedure + Implementation of apply () (eval '(twice* ) GE) some-other-environment) (apply (eval 'twice* GE) (map (lambda (e) (eval e GE)) '())) (apply (list 'compound '(x*) '( x* x*) GE) '()) (eval '( x* x*) (extend-env-with-new-frame '(x*) '() GE)) (eval '( x* x*) E) compound A name value E x* x* GE Implementation of apply () (eval '( x* x*) E) (apply (eval ' E) (map (lambda (e) (eval e E)) '(x* x*))) (apply '(primitive #[add]) (list (eval 'x* E) (eval 'x* E))) (apply '(primitive #[add]) '( )) (scheme-apply #[add] '( )) After is returned by (eval '( x* x*) E), where is frame A stored? GE A E name value x* Implementation of environment model Environment = list<table> E E name value x* GE A name value x* GE name value (primitive #[add]) greater* (primitive #[grt])...

; Environment model code (part of eval ) ; Environment = list<table> (define (extend-env-with-new-frame names values env) (let ((new-frame (make-table))) (make-bindings! names values new-frame) (cons new-frame env))) (define (make-bindings! names values table) (for-each (lambda (name value) (table-put! table name value)) names values)) ; the initial global environment (define GE (extend-env-with-new-frame (list ' 'greater*) (list (make-primitive +) (make-primitive >)) nil)) ; lookup searches the list of frames for the first match (define (lookup name env) (if (null? env) (error "unbound variable: " name) (let ((binding (table-get (car env) name))) (if (null? binding) (lookup name (cdr env)) (binding-value binding))))) Summary Cycle between eval and apply is the core of the evaluator eval calls apply with operator and argument values apply calls eval with expression and environment no pending operations on either call an iterative algorithm if the expression is iterative What is still missing from scheme*? ability to evaluate a sequence of expressions data types other than numbers and booleans ; define changes the first frame in the environment (define (eval-define exp env) (let ((name (cadr exp)) (defined-to-be (caddr exp))) (table-put! (car env) name (eval defined-to-be env)) 'undefined)) (eval '(define* twice* (lambda* (x*) ( x* x*))) GE) (eval '(twice* ) GE) Everything in these lectures would still work if you deleted the stars from the names!

0. Arithmetic calculator (define (tag-check e sym) (and (pair? e) (eq? (car e) sym))) (define (sum? e) (tag-check e ')) ((sum? exp) (eval-sum exp)) (define (eval-sum exp) (+ (eval (cadr exp)) (eval (caddr exp)))) (eval '( ( )))

0 0 0 0. Names (define (define? exp) (tag-check exp 'define*)) ((sum? exp) (eval-sum exp)) ((? exp) (lookup exp)) ((define? exp) (eval-define exp)) ; variation on table ADT from March lecture (only difference is ; that table-get returns a binding, while original version ; returned a value): ; make-table void -> table ; table-get table, -> (binding null) ; table-put! table,, anytype -> undef ; binding-value binding -> anytype (define environment (make-table)) (define (lookup name) (let ((binding (table-get environment name))) (if (null? binding) (error "unbound variable: " name) (binding-value binding)))) (define (eval-define exp) (let ((name (cadr exp)) (defined-to-be (caddr exp))) (table-put! environment name (eval defined-to-be)) 'undefined)) (eval '(define* x* ( ))) (eval '( x* )) ; Index to procedures that have not changed: ; procedure page line ; sum? ; eval-sum

0 0 0 0. Conditionals and if (define (greater? exp) (tag-check exp 'greater*)) (define (if? exp) (tag-check exp 'if*)) ((sum? exp) (eval-sum exp)) ((? exp) (lookup exp)) ((define? exp) (eval-define exp)) ((greater? exp) (eval-greater exp)) ((if? exp) (eval-if exp)) (define (eval-greater exp) (> (eval (cadr exp)) (eval (caddr exp)))) (define (eval-if exp) (let ((predicate (cadr exp)) (consequent (caddr exp)) (alternative (cadddr exp))) (let ((test (eval predicate))) ((eq? test #t) (eval consequent)) ((eq? test #f) (eval alternative)) (error "predicate not boolean: " predicate)))))) (eval '(define* y* )) (eval '(if* (greater* y* ) ( y* ) )) ; Index to procedures that have not changed: ; procedure page line ; sum? ; eval-sum ; lookup ; define? ; eval-define

0 0 0 0. Store operators in the environment (define (application? e) (pair? e)) ((? exp) (lookup exp)) ((define? exp) (eval-define exp)) ((if? exp) (eval-if exp)) ((application? exp) (apply (eval (car exp)) (map eval (cdr exp)))) ;; rename scheme s apply so we can reuse the name (define scheme-apply apply) (define (apply operator operands) (if (primitive? operator) (scheme-apply (get-scheme-procedure operator) operands) (error "operator not a procedure: " operator))) ;; primitive: an ADT that stores scheme procedures (define prim-tag 'primitive) (define (make-primitive scheme-proc)(list prim-tag scheme-proc)) (define (primitive? e) (tag-check e prim-tag)) (define (get-scheme-procedure prim) (cadr prim)) (define environment (make-table)) (table-put! environment ' (make-primitive +)) (table-put! environment 'greater* (make-primitive >)) (table-put! environment 'true* #t) (eval '(define* z* )) (eval '( )) (eval '(if* true* 0 )) ; Index to procedures that have not changed: ; procedure evaluator line ; lookup ; define? ; eval-define ; if? ; eval-if 0

0 0 0 0 0. Environment as explicit parameter ;This change is boring! Exactly the same functionality as #. (define (eval exp env) ((? exp) (lookup exp env)) ((define? exp) (eval-define exp env)) ((if? exp) (eval-if exp env)) ((application? exp) (apply (eval (car exp) env) (map (lambda (e) (eval e env)) (cdr exp)))) (define (lookup name env) (let ((binding (table-get env name))) (if (null? binding) (error "unbound variable: " name) (binding-value binding)))) (define (eval-define exp env) (let ((name (cadr exp)) (defined-to-be (caddr exp))) (table-put! env name (eval defined-to-be env)) 'undefined)) (define (eval-if exp env) (let ((predicate (cadr exp)) (consequent (caddr exp)) (alternative (cadddr exp))) (let ((test (eval predicate env))) ((eq? test #t) (eval consequent env)) ((eq? test #f) (eval alternative env)) (error "predicate not boolean: " predicate)))))) (eval '(define* z* ( )) environment) (eval '(if* (greater* z* ) 0 ) environment) Index to procedures that have not changed: procedure evaluator line define? if? application? apply

0 0 0 0. Defining new procedures (define (lambda? e) (tag-check e 'lambda*)) (define (eval exp env) ((? exp) (lookup exp env)) ((define? exp) (eval-define exp env)) ((if? exp) (eval-if exp env)) ((lambda? exp) (eval-lambda exp env)) ((application? exp) (apply (eval (car exp) env) (map (lambda (e) (eval e env)) (cdr exp)))) (define (eval-lambda exp env) (let ((args (cadr exp)) (body (caddr exp))) (make-compound args body env))) (define (apply operator operands) ((primitive? operator) (scheme-apply (get-scheme-procedure operator) operands)) ((compound? operator) (eval (body operator) (extend-env-with-new-frame (parameters operator) operands (env operator)))) (error "operator not a procedure: " operator)))) ;; ADT that implements the double bubble (define compound-tag 'compound) (define (make-compound parameters body env) (list compound-tag parameters body env)) (define (compound? exp) (tag-check exp compound-tag)) (define (parameters compound) (cadr compound)) (define (body compound) (caddr compound)) (define (env compound) (cadddr compound))

0 0 0 0 ; Environment model code (part of eval ) ; Environment = list<table> (define (extend-env-with-new-frame names values env) (let ((new-frame (make-table))) (make-bindings! names values new-frame) (cons new-frame env))) (define (make-bindings! names values table) (for-each (lambda (name value) (table-put! table name value)) names values)) ; the initial global environment (define GE (extend-env-with-new-frame (list ' 'greater*) (list (make-primitive +) (make-primitive >)) nil)) ; lookup searches the list of frames for the first match (define (lookup name env) (if (null? env) (error "unbound variable: " name) (let ((binding (table-get (car env) name))) (if (null? binding) (lookup name (cdr env)) (binding-value binding))))) ; define changes the first frame in the environment (define (eval-define exp env) (let ((name (cadr exp)) (defined-to-be (caddr exp))) (table-put! (car env) name (eval defined-to-be env)) 'undefined)) (eval '(define* twice* (lambda* (x*) ( x* x*))) GE) (eval '(twice* ) GE) Index to procedures that have not changed: procedure evaluator line define? if? application? eval-i