CS 342 Lecture -*- Outline -*- * Meta-Circular (recursive) interpreters another way to describe the semantics (operational semantics) LISP evaluation is a process (algorithm) so it can be described in LISP ** Read-eval-print loop sample r-e-p loop ----------- (define (meta env) (begin (newline) (display "Meta> ") (write (eval (read) env)) (meta env))) ---------- example (meta user-initial-environment) Meta> (cons 'a '(b c)) ; Value: (A B C) Meta> (car '(a b c)) ; Value: A Meta> ^G it's an infinite loop! Can program different interfaces but only the top-level syntax ------------- (define (evalquote env) (begin (newline) (display "EvalQuote> ") (define operator (eval (read) env)) (define operand-list (read)) (write (apply operator operand-list))) (evalquote env)) -------------- (evalquote user-initial-environment) EvalQuote> cons(a (b c)) ; Value: (A B C) EvalQuote> car((a b c)) ; Value: A ** Eval and apply to make fundamental changes, have to interpret eval and apply So basis for interpreter becomes: primitive? (p) apply-primitive (p arg-list) various functions for dealing with environments (See handout) *** Eval list of test-action pairs, allows easy syntactic changes cases spread out in other modules: self-evaluating quote, returns quoted text (reader handles magic) primitives lambda expressions special forms (e.g., cond) applications ?Where is the run-time stack? activation records? static links? Can answer some questions: reserved words vs. keywords? But to really answer the questions have to know semantics of LISP e.g., normal order evaluation? LISP is too close to LISP to help explain LISP however only have to understand the subset used in interpreter Other operational definitions may be better ** Changes to the semantics of intepreted language *** syntax e.g., change lambda to fun *** new primitives e.g., square, mutation on lists *** new special forms? if (translate to cond, call m-eval recursively) let (translate to lambda) apply (recognize apply and call m-apply) set! (not a simple translation) *** Scope rules **** interpreter is statically scoped (look at m-apply) **** dynamic scoping m-apply would get calling env --------------- (define (m-apply proc args env) (cond ((primitive? proc) (apply-primitive proc args)) ((compound-procedure? proc) (eval-sequence (procedure-body proc) (extend-environment (parameters proc) args env))) ;*** (else (error "Unknown procedure type in m-apply")))) (install-m-eval-default! application? (lambda (exp env) (m-apply (m-eval (operator exp) env) (list-of-values (operands exp) env) env))) ;*** --------------------- *** Calling conventions **** interpreter is call by value evaluate arguments before calling function **** call by name (normal order evaluation) when evaluating an application of non-primitive proc make parameterless closure for all arguments --------- (define (m-delay exp env) (list 'thunk exp env)) (define (thunk? exp) (and (pair? exp) (eq? (car exp) 'thunk))) (define (thunk-body thunk) (cadr thunk)) (define (thunk-env thunk) (caddr thunk)) --------- when evaluating primitive, force these closures until get a value -------- (define (m-force obj) (define (force-aux thunk) (m-eval (thunk-body thunk) (thunk-env thunk))) (cond ((thunk? obj) (m-force (force-aux obj))) (else obj))) --------- changes for call by name ---------- ; in m-apply (install-m-eval-default! application? (lambda (exp env) (m-apply (m-force (m-eval (operator exp) env)) (list-of-thunks (operands exp) env)))) ; in m-eval-cond have to force predicate ((true? (m-force (m-eval (predicate (first-clause clist)) env))) (eval-sequence (actions (first-clause clist)) env)) ; make m-apply force arguments to primitives ;... (apply-primitive (cadr proc) (map m-force args))... ; in m-print, must force value ((thunk? object) (m-print (m-force object))) -------- **** call by need -------- (define (m-force obj) (cond ((thunk? obj) (m-force (memoize! obj))) ((memo? obj) (m-force (memo-value obj))) (else obj))) (define (memoize! thunk) (define (force-aux thunk) (m-eval (thunk-body thunk) (thunk-env thunk))) (let ((val (force-aux thunk))) (set-car! (cdr thunk) val) (set-cdr! (cdr thunk) '()) (set-car! thunk 'memo) val)) (define (memo? exp) (and (pair? exp) (eq? (car exp) 'memo))) (define (memo-value exp) (cadr exp)) -------- **** call by text don't use list-of-values when calling m-apply notice that envrionments are screwed up as in dynamic scoping.