Com S 342 meeting -*- Outline -*- * A Simple Interpreter (5.1) ** overview ------------------------------------------ WHAT AN INTERPRETER DOES (5.1) concrete syntax | v abstract --------> Expressed syntax Value ------------------------------------------ show the defining language (Scheme) define the *defined* language concrete run syntax ------\ / or parse | \ interpreter - read-eval-print v eval-exp v abstract --------> Expressed syntax Value | ^ defined| | A | | defined v eval | Scheme ----------> datum | ^ | | A v machine | Scheme Machine ---------> bytes code ** language design Q: What's the difference between an expressed and denoted value? ------------------------------------------ DESIGN OF THE DEFINED LANGUAGE 1. What values? Expressed Value = Number + Procedure Denoted Value = Number + Procedure 2. What grammar? (concrete syntax) ::= | | ::= | () ::= ( ) | ( {, }*) ::= ::= ------------------------------------------ ------------------------------------------ FOR YOU TO DO (IN PAIRS) 1. Give 4 examples of in the grammar 2. Give 2 examples of things that are expressions in Scheme, but not in grammar ------------------------------------------ Q: Why is the grammar for operands like that? Could it be simplified? because it uses commas, no... ** implementation Q: What cases are there in the syntax of ? ------------------------------------------ ABSTRACT SYNTAX ------------------------------------------ ... (define-record lit (datum)) (define-record varref (var)) (define-record app (rator rands)) Q: Does this look familiar? Q: How can that be when the concrete syntax is Algol-like? The parser will be given to us. The code is in appendix D (see also ch 11), and the main procedure is called character-string-parser and you rename it by writing: (define parse character-string-parser) Get it by typing (load-from-lib "ch5-char-parser.scm") Whole package for this section is gotten by (load-from-lib "ch5-1.scm") See also $PUB/lib/README for details. *** domains ------------------------------------------ DOMAINS def: the domain *Denoted-Value* is def: the domain *Expressed-Value* is To start, the defined languge will have: Denoted-Value = Number + Procedure Expressed-Value = Number + Procedure ------------------------------------------ ... the set of values that can be bound in an environment ... the set of values that can be the results of expressions we've mentioned these before ------------------------------------------ DENOTED-VALUE OPERATIONS ;; found in ch5-1-denoted-value.scm number->denoted : (-> (number) Denoted-Value) denoted->number : (-> (Denoted-Value) number) procedure->denoted : (-> ((prim-proc T)) Denoted-Value) denoted->procedure : (-> (Denoted-Value) (prim-proc T)) ------------------------------------------ ------------------------------------------ EXPRESSED-VALUE OPERATIONS ;; found in ch5-1-expressed-value.scm number->expressed : (-> (number) Expressed-Value) expressed->number : (-> (Expressed-Value) number) procedure->expressed : (-> ((prim-proc T)) Expressed-Value) expressed->procedure : (-> (Expressed-Value) (prim-proc T)) denoted->expressed : (-> (Denoted-Value) Expressed-Value) expressed->denoted : (-> (Expressed-Value) Denoted-Value) ------------------------------------------ *** interpreter itself ------------------------------------------ SIMPLE INTERPRETER ------------------------------------------ ...; figure 5.1.1, page 141, $PUB/lib/ch5-1.scm (define eval-exp ;; TYPE: (-> (parsed-exp) Expressed-Value) (lambda (exp) (variant-case exp (lit (datum) (number->expressed datum)) (varref (var) (denoted->expressed (apply-env init-env var))) (app (rator rands) (let ((proc (expressed->procedure (eval-exp rator))) (args (eval-rands rands))) (apply-proc proc args))) (else (error "Invalid abstract syntax: " exp))))) (define eval-rands ;; TYPE: (-> ((list parsed-exp)) (list Expressed-Value)) (lambda (rands) (map eval-exp rands))) Q: What order of evaluation does this specify? applicative, because that what the defining language does. Q: Is the ordering between arguments specified? no, because map in Scheme doesn't specify that. Q: Will this run as is? What's left to program? (Any other free variables?) make a list on the board, check them off: - environments: apply-env, init-env - procedures: apply-proc *** environments Note: when drawing pictures of environments, make the drawing grow down the page. In this section, use the representation (the records) **** definition and implementation ------------------------------------------ ENVIRONMENTS Def: an *environment* is a mapping from variable names to denoted values: Environment = -> Denoted Value ------------------------------------------ Q: Can you ever have an infinite domain in this mapping? Q: How shall we implement them? ... ;;; from $PUB/lib/environments.scm (load-quietly-from-lib "ff-as-record.scm") ;;; Anonymous figure : page 142, 143 (define the-empty-env ;; TYPE: Environment (create-empty-ff)) (define extend-env ;; TYPE: (-> ((list symbol) (list Denoted-Value) Environment) Environment) extend-ff*) (define apply-env ;; TYPE: (-> (Environment symbol) Denoted-Value) apply-ff) (define defined-in-env? ;; TYPE: (-> (Environment symbol) boolean) defined-in-ff?) This relies on an implementation of the ff ADT, but we've done that See the load above. **** initial environment Q: What goes in the initial environment? built-in procedures postpone this until later... ------------------------------------------ INITIAL ENVIRONMENT (define init-env ; TYPE: Environment ------------------------------------------ ... ;;; from $PUB/lib/ch5-1.scm (define prim-op-names ; TYPE: (list symbol) '(+ - * add1 sub1)) (define init-env ; TYPE: Environment (extend-env prim-op-names (map procedure->denoted (map make-prim-proc prim-op-names)) the-empty-env)) NOTE: leave room for adding more primitives on the slide (extend-env '(div) (list (procedure->denoted (make-prim-proc 'div))) ...) *** procedures **** design and abstract syntax for now, we'll only have primitive procedures ------------------------------------------ WHAT PROCEDURES TO BUILD-IN? Concrete syntax: ::= + | - | * | add1 | sub1 Abstract syntax: (define-record ------------------------------------------ Q: What information do we need to store? ... prim-proc (prim-op)) The book on page 142 is wrong here in the abstract syntax of this; there isn't a separate kind of record for each symbol. **** application ------------------------------------------ APPLYING PROCEDURE REPS (define apply-proc ;; TYPE: (-> (Procedure ;; (list Expressed-Value)) ;; Expressed-Value) (lambda (proc args) (variant-case proc (prim-proc (prim-op) (else (error "Invalid procedure:" proc))))) ------------------------------------------ ... (apply-prim-op prim-op args)) (define apply-prim-op ;; TYPE: (-> (symbol (list Expressed-Value)) Expressed-Value) (lambda (prim-op args) (let ((arg1 (expressed->number (car args)))) (number->expressed (case prim-op ((+) (+ arg1 (expressed->number (cadr args)))) ((-) (- arg1 (expressed->number (cadr args)))) ((*) (* arg1 (expressed->number (cadr args)))) ((add1) (+ arg1 1)) ((sub1) (- arg1 1)) ;; ask them how to do div ... ((div) (quotient arg1 (expressed->number (cadr args)))) (else (error "Invalid prim-op name:" prim-op))))))) Q: What else could this check? length of args Q: Now what should be the initial environment? Q: What would be needed to add division as a primitive? IMPORTANT, be sure to do this!! (define init-env (extend-env '(div) (list (procedure->denoted (make-prim-proc 'div))) ...) Q: What would be needed to add zero as the name of a constant? just bind the name to 0 in the initial env, be sure to use lists! (define init-env (extend-env '(zero) (list (number->denoted 0)) ...) Q: Could we just define apply-proc as apply? What would have to change? Would the error checking be as good? (this is a homework problem) **** tracing it ------------------------------------------ TRACING THE INTERPRETER > (trace eval-exp eval-rands) > (trace apply-proc apply-env) > (load-from-lib "ch5-1.scm") > (load-from-lib "ch5-1.scm") > (load-from-lib "rep5-1.scm") > (read-eval-print) --> 3; CALL eval-exp #(lit 3) RETN eval-exp 3 3 --> +; CALL eval-exp #(varref +) CALL apply-env #(extend...) + RETN apply-env #(prim-proc +) RETN eval-exp #(prim-proc +) #(prim-proc +) ------------------------------------------ ------------------------------------------ MORE TRACES --> +(3,4); CALL eval-exp #(app #(varref +) (#(lit 3) #(lit 4))) CALL eval-exp #(varref +) CALL apply-env #(extend...) + RETN apply-env #(prim-proc +) RETN eval-exp #(prim-proc +) CALL eval-rands (#(lit 3) #(lit 4)) CALL eval-exp #(lit 3) RETN eval-exp 3 CALL eval-exp #(lit 4) RETN eval-exp 4 RETN eval-rands (3 4) CALL apply-proc #(prim-proc +) (3 4) RETN apply-proc 7 RETN eval-exp 7 7 ------------------------------------------ You can get it all by typing: (load-from-lib "ch5-1.scm") To this you have to add... *** front-end note that there are two ways of doing this, and only one works at a time **** run ------------------------------------------ PARSING ;;; $PUB/lib/ch5-1.scm ... (load-from-lib "ch5-char-parser.scm") ... (define eval-exp ...) ;;; $PUB/lib/char-parser-connections.scm ... (define parse character-string-parser) ;;; $PUB/lib/run5-1.scm (load-from-lib "char-parser-connections.scm") (define run ; one-shot interpreter ;; TYPE: (-> (
) Expressed-Value) (lambda (x) ------------------------------------------ ... (eval-exp (form->exp (parse x))))) The grammar for forms is ::= This uses the character string parser (appendix D, and E), the define of parse is in $PUB/lib/char-parser-connections.scm Might draw the folowing pictures undefine-run undefine-read-eval-print ff-as-record ch5-char-parser \ / \ / ch5-1 undefine-read-eval-print char-parser-connections | \ run5-1 run see $PUB/lib/README and $PUB/lib/char-parser-README.txt for details ------------------------------------------ USING IT > (load-from-lib "ch5-1.scm") loading $PUB/lib/ch5-1.scm ... loading $PUB/lib/undefine-read-eval-print. loading $PUB/lib/undefine-run.scm ... > (load-from-lib "run5-1.scm") loading $PUB/lib/run5-1.scm ... loading $PUB/lib/char-parser-connections.s > (run "5") > ------------------------------------------ ... 5 ... (run "-(4,1)") 3 etc. get this from (load-from-lib "run5-1.scm") **** read-eval-print loop ------------------------------------------ A READ-EVAL-PRINT LOOP ;; $PUB/lib/rep5-1.scm (load-from-lib "appendix-f.scm") ;;; override of App. F for sections 5.1-2 (define eval-print ;; TYPE: (-> (parsed-form) void) (lambda (tree) USING IT > (load-from-lib "ch5-1.scm") > (load-from-lib "rep5-1.scm") > (read-eval-print) --> --> ------------------------------------------ ... (write (eval-exp (form->exp tree))))) This uses the character string read-eval-print loop (appendix F) ... 5; 5 --> add1(2); 3 --> -(4,3); 1 --> *(add1(2), -(6,4)); 6 --> end > get this from (load-from-lib "rep5-1.scm")