380 likes | 465 Views
The metacircular evaluator (Cont.). 6. Defining new procedures (define (lambda? e) (tag-check e 'lambda)) (define (eval exp env) (cond ((number? exp) exp) ((symbol? exp) (lookup exp env)) ((define? exp) (eval-define exp env))
E N D
6. Defining new procedures (define (lambda? e) (tag-check e 'lambda)) (define (eval exp env) (cond ((number? exp) exp) ((symbol? 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)))) (else (error "unknown expression " exp)))) (define (eval-lambda exp env) (make-procedure (lambda-parameters exp) (lambda-body exp) env)) (define (lambda-parameters exp) (cadr exp)) (define (lambda-body exp) (cddr exp)) (define (make-procedure parameters body env) (list 'procedure parameters body env))
GE symbolprocedure This datastructure isa procedure! symbol+ symbolx Implementation of lambda (eval '(lambda (x) (+ x x)) GE) (eval-lambda '(lambda (x) (+ x x)) GE) (make-procedure '(x) ’((+ x x)) GE) (list ’procedure '(x) ’((+ x x)) GE)
symbol primitive schemeprocedure + symbol+ symbolx Naming the procedure (eval '(define twice (lambda (x) (+ x x))) GE) names values z 9 true #t + twice symbolprocedure
6. Defining new procedures (define (apply procedure arguments) (cond ((primitive-procedure? procedure) (apply-primitive-procedure procedure arguments)) ((compound-procedure? procedure) (eval-sequence (procedure-body procedure) (extend-environment (procedure-parameters procedure) arguments (procedure-env procedure)))) (else (error "Unknown procedure type -- APPLY" procedure)))) (define (compound-procedure? exp) (tag-check exp ‘procedure)) (define (procedure-parameters compound) (cadr compound)) (define (procedure-body compound) (caddr compound)) (define (procedure-env compound) (cadddr compound))
(define (eval-sequence exps env) (cond ((last-exp? exps) (eval (first-exp exps) env)) (else (eval (first-exp exps) env) (eval-sequence (rest-exps exps) env)))) (define (last-exp? seq) (null? (cdr seq))) (define (first-exp seq) (car seq)) (define (rest-exps seq) (cdr seq))
E1 E2 x: 10 plus: (procedure ...) 3. environment manipulation enclosing- environment E2 frame list ofvariables list of values x plus 10 procedure How the Environment Works • Abstractly – in our environment diagrams: • Concretely – our implementation (as in SICP)
E1 x: 10 plus: (procedure ...) E2 E2 x: 4 y: 5 E3 E3 E1 frame list ofvariables list of values 5 x y 4 Extending the Environment • (extend-environment '(x y)(list 4 5) E2) Abstractly Concretely
(define (extend-environment vars vals base-env) (if (= (length vars) (length vals)) (cons (make-frame vars vals) base-env) (if (< (length vars) (length vals)) (error "Too many arguments supplied" vars vals) (error "Too few arguments supplied" vars vals))))
GE A name value x 4 E1 Implementation of apply (1) (eval '(twice 4) GE) (apply (eval 'twice GE) (map (lambda (e) (eval e GE)) '(4))) (apply (list 'procedure '(x) ’((+ x x)) GE) '(4)) (eval-seq ’((+ x x)) (extend-environment '(x) '(4) GE)) (eval '(+ x x) E1)
GE A name value x 4 E1 Implementation of apply (2) (eval '(+ x x) E1) (apply (eval + E1) (map (lambda (e) (eval e E1)) '(x x))) (apply '(primitive #[add]) (list (eval 'x E1) (eval 'x E1))) (apply '(primitive #[add]) '(4 4)) (scheme-apply #[add] '(4 4)) 8
"Scanning" the environment • Look for a variable in the environment... • Look for a variable in a frame... • loop through the list of vars and list of vals in parallel • detect if the variable is found in the frame • If not found in frame (out of variables in the frame),look in enclosing environment
(define (lookup-variable-value var env) (define (env-loop env) (define (scan vars vals) (cond ((null? vars) (env-loop (enclosing-environment env))) ((eq? var (car vars)) (car vals)) (else (scan (cdr vars) (cdr vals))))) (if (eq? env the-empty-environment) (error "Unbound variable" var) (let ((frame (first-frame env))) (scan (frame-variables frame) (frame-values frame))))) (env-loop env)) (define (enclosing-environment env) (cdr env)) (define (frame-variables frame) (car frame)) (define (frame-values frame) (cdr frame))
(define (define-variable! var val env) (let ((frame (first-frame env))) (define (scan vars vals) (cond ((null? vars) (add-binding-to-frame! var val frame)) ((eq? var (car vars)) (set-car! vals val)) (else (scan (cdr vars) (cdr vals))))) (scan (frame-variables frame) (frame-values frame)))) (define (eval-define exp env) (let ((name (cadr exp)) (defined-to-be (eval (caddr exp) env))) (define-variable! name defined-to-be env) ‘undefined))
primitives and initial env. 4. The Initial (Global) Environment (define (setup-environment) (let ((initial-env (extend-environment (primitive-procedure-names) (primitive-procedure-objects) the-empty-environment))) (define-variable! 'true #t initial-env) (define-variable! 'false #f initial-env) initial-env)) (define the-empty-environment '()) (define the-global-environment (setup-environment))
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 • What is still missing from scheme ? • Some special forms • data types other than numbers and booleans
SICP’s version of eval (define (eval exp env) (cond ((self-evaluating? exp) exp) ((variable? exp) (lookup-variable-value exp env)) ((quoted? exp) (text-of-quotation exp)) ((assignment? exp) (eval-assignment exp env)) ((definition? exp) (eval-definition exp env)) ((if? exp) (eval-if exp env)) ((lambda? exp) (make-procedure (lambda-parameters exp) (lambda-body exp) env)) ((begin? exp) (eval-sequence (begin-actions exp) env)) ((cond? exp) (eval (cond->if exp) env)) ((application? exp) (apply (eval (operator exp) env) (list-of-values (operands exp) env))) (else (error "Unknown expression type -- EVAL" exp))))
Note some minor differences like (define (self-evaluating? exp) (cond ((number? exp) true) ((string? exp) true) (else false))) (define (variable? exp) (symbol? exp)) (define (list-of-values exps env) (if (no-operands? exps) '() (cons (eval (first-operand exps) env) (list-of-values (rest-operands exps) env))))
syntax procedures eval/apply Note: Syntactic Abstraction • Semantics • What the language means • Model of computation • Syntax • Particulars of writing expressions • E.g. how to signal different expressions • Separation of syntax and semantics: allows one to easily alter syntax
Basic Syntax • Routines to detect expressions (define (if? exp) (tagged-list? exp 'if)) (define (lambda? exp) (tagged-list? exp 'lambda)) (define (application? exp) (pair? exp)) • Routines to get information out of expressions (define (operator app) (car app)) (define (operands app) (cdr app)) (define (first-operand args) (car args)) (define (rest-operands args) (cdr args)) • Routines to build expressions (define (make-if predicate consequent alternative) (list 'if predicate consequent alternative))
Example – Changing Syntax • Suppose you wanted a "verbose" application syntax: (CALL <proc> ARGS <arg1> <arg2> ...) • Changes – only in the syntax routines! (define (application? exp) (tagged-list? 'CALL)) (define (operator app) (cadr app)) (define (operands app) (cdddr app))
Implementing "Syntactic Sugar" • Idea: • Implement a simple fundamental "core" in the evaluator • Easy way to add alternative/convenient syntax? • "let" as sugared procedure application: (let ((<name1> <val1>) (<name2> <val2>)) <body>) ((lambda (<name1> <name2>) <body>) <val1> <val2>)
Detect and Transform the Alternative Syntax (define (eval exp env) (cond ((self-evaluating? exp) exp) ((variable? exp) (lookup-variable-value exp env)) ((quoted? exp) (text-of-quotation exp)) . . . ((cond? exp) (eval (cond->if exp) env)) ((let? exp) (eval (let->combination exp) env)) ((application? exp) (apply (eval (operator exp) env) (list-of-values (operands exp) env))) (else (error "Unknown expression" exp))))
Implementing cond: Syntax procedures (define (cond-clauses exp) (cdr exp)) (define (cond-else-clause? clause) (eq? (cond-predicate clause) 'else)) (define (cond-predicate clause) (car clause)) (define (cond-actions clause) (cdr clause))
23 1 1 Cond syntax (cond ((= x 23) (+ x 1)) (else (- x 1))) cond else - x = x + x
begin 2 * x y car Transforming sequence of expression toan expression (define (sequence->exp seq) (cond ((null? seq) seq) ((last-exp? seq) (first-exp seq)) (else (make-begin seq)))) (define (make-begin seq) (cons 'begin seq))
Implementing cond (Cont.) (cond ((= x 23) (+ x 1)) (else (- x 1))) (if (= x 23) (+ x 1) (- x 1))
Implementing cond (define (cond->if exp) (expand-clauses (cond-clauses exp))) (define (expand-clauses clauses) (if (null? clauses) 'false ; no else clause (let ((first (car clauses)) (rest (cdr clauses))) (if (cond-else-clause? first) (if (null? rest) (sequence->exp (cond-actions first)) (error "ELSE clause isn't last -- COND->IF" clauses)) (make-if (cond-predicate first) (sequence->exp (cond-actions first)) (expand-clauses rest))))))
23 1 1 Details of cond syntax transformation (cond ((= x 23) (+ x 1)) (else (- x 1))) cond else - x = x + x
23 1 1 Details of cond syntax transformation (expand-clauses else - x = x ) + x
23 1 1 Details of cond syntax transformation rest first else - x = x + x
23 1 1 = x + x Details of cond syntax transformation (make-if )) (expand-clauses else - x
23 1 1 = x + x - x Details of cond syntax transformation (make-if )
23 1 1 = x + x - x Details of cond syntax transformation if
Named Procedures – Syntax vs. Semantics (define (foo <parm>) <body>) • Semantic implementation – just another define: (define (eval-definition exp env) (define-variable! (definition-variable exp) (eval (definition-value exp) env) env)) (define (definition-variable exp) (if (symbol? (cadr exp)) (cadr exp) (caadr exp))) (define (definition-value exp) (if (symbol? (cadr exp)) (caddr exp) (make-lambda (cdadr exp) ;formal params (cddr exp)))) ;body
Read-Eval-Print Loop (define (driver-loop) (prompt-for-input input-prompt) (let ((input (read))) (let ((output (eval input the-global-env))) (announce-output output-prompt) (user-print output))) (driver-loop)) (define (prompt-for-input string) (newline) (newline) (display string) (newline)) (define (announce-output string) (newline) (display string) (newline)) (define (user-print object) (if (compound-procedure? object) (display (list 'compound-procedure (procedure-parameters object) (procedure-body object) '<procedure-env>)) (display object))) (define input-prompt ";;; M-Eval input:") (define output-prompt ";;; M-Eval value:")