470 likes | 593 Views
Continue Evaluator. symbol procedure. symbol +. symbol x. Representing procedures. ( eval '(define twice (lambda (x) (+ x x))) GE). symbol primitive. scheme procedure +. Representing the Environment. Abstractly. (eval ‘(twice 4)) ( extend-environment ' (x) ' (4) GE).
E N D
symbolprocedure symbol+ symbolx Representing procedures (eval '(define twice (lambda (x) (+ x x))) GE) symbol primitive schemeprocedure +
Representing the Environment Abstractly • (eval ‘(twice 4)) • (extend-environment '(x) '(4) GE) x: 10 +: (primitive ...) twice: (procedure ..) GE x: 4 E1 Concretely GE E1 frame list ofvariables list of values 10 x 4 primitive + x twice
Representing the Environment E2 • (eval ‘(twice 5)) • (extend-environment '(x) '(5) GE) x 5 GE E1 frame list ofvariables list of values 10 x 4 primitive + x twice
(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 (first-frame env) (car 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-definition exp env) (let ((name (cadr exp)) (defined-to-be (eval (caddr exp) env))) (define-variable! name defined-to-be env) ‘undefined))
Assignments (define (eval-assignment exp env) (set-variable-value! (assignment-variable exp) (eval (assignment-value exp) env) env) 'ok) (define (assignment? exp) (tagged-list? exp 'set!)) (define (assignment-variable exp) (cadr exp)) (define (assignment-value exp) (caddr exp))
set-variable-value! (define (set-variable-value! var val env) (define (env-loop env) (define (scan vars vals) (cond ((null? vars) (env-loop (enclosing-environment env))) ((eq? var (car vars)) (set-car! vals val)) (else (scan (cdr vars) (cdr vals))))) (if (eq? env the-empty-environment) (error "Unbound variable -- SET!" var) (let ((frame (first-frame env))) (scan (frame-variables frame) (frame-values frame))))( (env-loop env))
primitives and initial env. Initialization (define the-global-environment (setup-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 primitive-procedure (list (list 'car car) (list 'cdr cdr) (list 'cons cons) (list 'null? null?) (list '+ +) ;; more primitives )) (define (primitive-procedure-names) (map car primitive-procedures)) (define (primitive-procedure-objects) (map (lambda (proc) (list 'primitive (cadr proc))) primitive-procedures))
Read-Eval-PrintLoop (define input-prompt ";;; M-Eval input:") (define output-prompt ";;; M-Eval value:") (define (prompt-for-input string) (newline) (newline) (display string) (newline)) (define (announce-output string) (newline) (display string) (newline)) (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)) 11
Apply (reminder) (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))))
Execution Examples • An iterative algorithm in scheme (eval '(define odd (lambda (n) (odd (- n 2)))) GE) • A recursive algorithm in scheme (eval '(define sum (lambda (n) (+ n (sum (- n 1))))) GE) • Base case and if check omitted from both algorithmsto simplify the example
(eval '(define odd (lambda (n) (odd (- n 2)))) GE) (eval '(odd 4) GE) [call apply, which creates E1: n = 4, then eval body of odd] (eval '(odd (- n 2)) E1) (apply (eval 'odd E1) (list (eval '(- n 2) E1))) [skip some steps in which (- n 2) ==> 2] (apply (list ’procedure '(n) '(odd (- n 2)) GE) '(2)) [apply creates E2: n = 2, then eval body of odd] (eval '(odd (- n 2)) E2)) • No pending operations on the recursive call to eval
(eval '(define sum (lambda (n) (+ n (sum (- n 1))))) GE) (eval '(sum 4) GE) [call apply, which creates E1: n = 4, then eval body of sum] (eval '(+ n (sum (- n 1))) E1) (apply '(primitive #[add]) (list (eval 'n E1) (eval '(sum (- n 1)) E1))) [skip some steps in which (- n 1) ==> 3] (apply '(primitive #[add]) (list 4 (apply (eval 'sum E1) '(3)))) [apply creates E2: n = 3, then eval body of sum] (apply '(primitive #[add])(list 4 (eval '(+ n (sum (- n 1))) E2)) • There are pending operations on the recursive call to eval
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
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 (cond ((= x 23) (+ x 1)) (else (- x 1))) (if (= x 23) (+ x 1) (- x 1)) (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))
Cond syntax (cond ((= x 23) (+ x 1)) (else (- x 1))) cond else - x 1 x 23 = + x 1
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)) begin * x 2 y car
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))))))
Details of cond syntax transformation (cond ((= x 23) (+ x 1)) (else (- x 1))) cond else - x 1 = x 23 + x 1
Details of cond syntax transformation (expand-clauses else - x 1 = x 23 ) + x 1
Details of cond syntax transformation rest first else - x 1 = x 23 + x 1
Details of cond syntax transformation (make-if = x 23 + x 1 )) (expand-clauses else - x 1
Details of cond syntax transformation (make-if = x 23 + x 1 - x 1 )
Details of cond syntax transformation if = x 23 + x 1 - x 1
Named Procedures Support (define (foo <parm>) <body>) (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
Different frame implementation (define make-frame (lambda (variables values) (lambda (var) (cond ((empty? variables) empty) ((eq? var (car variables)) (make-binding (car variables) (car values))) (else (apply (make-frame (cdr variables) (cdr values)) (list var)))) )) New ADT: Box (whose implementation is a black box) for mutable objects! Details omitted
Substitution Model Instead of the Env. Model • What changes to the evaluator are required?
Substitution Model Instead of the Env. Model • Main changes: • No generation (and passing as parameters) of environments (only global environment is generated) • Scoping is handled differently (see next) • No support of assignment • Assignment does not fit well in the functional programming framework • Besides application, support of other operations stays almost intact • Omit the environment parameter, and use global env. Instead • Application requires some work
(define applicative-eval (lambda (exp) (cond ((atomic? exp) (eval-atomic exp)) … ((application? exp) (let ((renamed-exp (rename exp))) (apply-procedure (applicative-eval (operator renamed-exp)) (list-of-values (operands renamed-exp))))) (else (error "Unknown expression type -- EVAL" exp)))))
(define apply-procedure (lambda (procedure arguments) (cond ((primitive-procedure? procedure) (apply-primitive-procedure procedure arguments)) ((compound-procedure? procedure) (eval-sequence (substitute (procedure-body procedure) (procedure-parameters procedure) arguments))) (else (error "Unknown procedure type -- APPLY" procedure)))))
substitute (define substitute (letrec ((substitute-var-val (lambda (exp var val) (cond ((variable? exp) (if (eq? exp var) val exp)) ((or (number? exp) (boolean? exp) (quoted? exp) ) exp) ((value? exp) (substitute-var-val-in-value exp var val)) (else (map (lambda(e) (substitute-var-val e var val)) exp)))) )
rename • Implementation can be found in lecture notes • Idea: rename the formal arguments of the closure as well as all occurrences of them in the body • To distinguish from other variables with the same name that are e.g. in the global scope
Auxiliary function (define (make-new-names old-names) (if (null? old-names) ‘() (cons (gensym) (make-new-names (cdr old-names))))))) This should be inside the code of rename, put as external for clarification!
One more auxiliary function • (define (replace val-exp) (cond ((or (evaluator-symbol? val-exp) (primitive-procedure? val-exp)) val-exp) ((evaluator-list? val-exp) (make-list (map rename (list-content val-exp)))) ((compound-procedure? val-exp) (let* ((params (procedure-parameters val-exp)) (new-params (make-new-names params)) (renamed-subs-body (map rename (procedure-body val-exp))) (renamed-body (substitute renamed-subs-body params new-params))) (make-procedure new-params renamed-body))))
Finally- rename (define (rename exp) (cond ((atomic? exp) exp) ((lambda? exp) (let* ((params (lambda-parameters exp)) (new-params (make-new-names params)) (renamed-subs (map rename exp))) (substitute renamed-subs params new-params)) ) ((evaluator-value? exp) (replace exp)) (else (map rename exp))
Lists • (define (evaluator-value? val) (or (evaluator-symbol? val) (evaluator-list? val) (primitive-procedure? val) (compound-procedure? val)))) • (define (list-form? exp) (or (tagged-list? exp ’cons) (tagged-list? exp ’list) (tagged-list? exp ’append)))) • (define (eval-list lst) (make-list (apply-primitive-procedure (eval(operator lst)) (list-of-values (operands lst))))))
The Analyzer • Main idea: separate the syntactic analysis from evaluation • Main observation: this corresponds to separating the management of expressions from those of the enviornments • Simplest analysis: (define (analyze-self-evaluating exp) (lambda (env) exp))
The Analyzer (cont.) (define (analyze-quoted exp) (let ((qval (text-of-quotation exp))) (lambda (env) qval))) (define (analyze-definition exp) (let ((var (definition-variable exp)) (val (analyze (definition-value exp)))) (lambda (env) (if (not (eq? env the-global-environment)) (error ’eval "non global definition: ~s" exp) (begin (add-binding! (make-binding var (val the-global-environment))) ’ok))))))
The Analyzer (cont.) • (define (analyze-if exp) (let ((pred (analyze (if-predicate exp))) (consequent (analyze (if-consequent exp))) (alternative (analyze (if-alternative exp)))) (lambda (env) (if (true? (predenv)) (consequent env) (alternative env)))))) And so on…