Prof. Fateman CS 164 Lecture 18 1
Language definition by interpreter
Lecture 18
Prof. Fateman CS 164 Lecture 18 2
Routes to defining a language
• Formal mathematics– Context free grammar– Mathematical semantics (axioms, theorems, proofs)– Rarely used (the downfall of Algol 68)– Theoretical interest
• Informal textual– CFG + natural language (Algol 60)– Just natural language (Tiger?)– Almost universally used
• Operational– Here’s a program that does the job– Metacircular evaluator for Scheme, Lisp– Evaluator/ interpreter for Tiger
Prof. Fateman CS 164 Lecture 18 3
input
Typical compiler structure
Source program
AST
Intermediate form
outputLex, parse
Typecheck, cleanup
Assembly lang
Object code
Machine
Prof. Fateman CS 164 Lecture 18 4
input
“Tigrun” structure
Source program
AST
Intermediate form
output
interpreter
Lex, parse
Typecheck, cleanup
What language is interpreter written in? What machine does it run on?
Prof. Fateman CS 164 Lecture 18 5
Interpreter structure: advantages
interpreter
•Interpreter in a higher level language: source language derives semantics from interpreter code and the semantics of the language of the interpreter (e.g. Lisp).
•What does EACH STATEMENT MEAN?
•Exhaustive case analysis
•What are the boundaries of legal semantics?
•What exactly is the model of scope, etc..
Prof. Fateman CS 164 Lecture 18 6
Interpreter structure: more advantages
interpreter
•Prototyping / debugging easier
•Portable intermediate form
•(Byte code ) intermediate form may be compact
•Security may be more easily enforced
Prof. Fateman CS 164 Lecture 18 7
Interpreter structure: disadvantages
interpreter
•Typically unable to reach full machine speed
•Difficult to transcend the limits of the underlying language implementation (not full access to machine)
•Code depends on presence of infrastructure (all of Lisp??)
•(if meta-circular) bootstrapping…
Prof. Fateman CS 164 Lecture 18 8
An interpreter compromise (e.g. Java VM)
interpreter
•“Compile” to a hypothetical byte-code stack machine appropriate for Java (etc), easily simulated on most real machines
•Implement this virtual byte-code stack machine on all real machines
•When speed is an issue, try Just In Time compiling; convert sections of code to machine language for a specific machine.
Prof. Fateman CS 164 Lecture 18 9
Interpreter to Compiler is a small step
• Modest modification of an interpreter can become a compiler.
•For example:
•Interpreter: To evaluate a sequence {s1, s2}, evaluate s1 then evaluate s2, returning the last of these.
•Compiler: To compile a sequence {s1, s2}, compile s1
then compile s2, returning the concatenation of the code for s1 and the code for s2.
•Interpreter: To evaluate a sum (+ A B) evaluate A, evaluate B and add.
•Compiler: To compile a sum, (+ A B) compile A, compile B, concatenate results, compile + to “add the results of the two previous sections of code”.
•Program structure is a walk over the intermediate code.
Prof. Fateman CS 164 Lecture 18 10
AST for merge.tig
;;; -*- Mode: Lisp; Syntax: Common-Lisp; package: tiger -*- (in-package :tiger)
(defparameter merge-ast '(LetExp (1 . 3) (DecList (TypeDec (3 . 9) any (FieldList (FieldDescriptor (3 . 16) any int))) (VarDec (4 . 4) buffer nil (CallExp (4 . 22) (getchar (4 . 22)) (ExpList))) (FunctionDec (6 . 16) readint (ExpList (IdType (6 . 20) any any)) (int (6 . 32)) (LetExp (7 . 4) (DecList (VarDec (7 . 8) i nil (IntExp (7 . 15) 0)) (FunctionDec (8 . 21) isdigit (ExpList (IdType (8 . 23) s string)) ;Explist or
FieldList? (int (8 . 39)) (IfExp (9 . 12) (OpExp (9 . 12) (>= (9 . 12)) (CallExp (9 . 7) (ord (9 . 7)) (ExpList (SimpleVar (9 . 9) s))) (CallExp (9 . 15) (ord (9 . 15)) (ExpList (StringExp (9 . 19) "0")))) (OpExp (9 . 31) (<= (9 . 31)) (CallExp (9 . 26) (ord (9 . 26)) (ExpList (SimpleVar (9 . 28) s))); which
line/col#? (CallExp (9 . 34) (ord (9 . 34)) (ExpList (StringExp (9 . 38) "9")))) (IntExp (9 . 12) 0)))…….
Prof. Fateman CS 164 Lecture 18 11
Intermediate form (cleanup) for merge.ast
;;; -*- Mode: Lisp; Syntax: Common-Lisp; package: tiger -*- (in-package :tiger)
(defparameter merge-cleaned ;; the line numbers removed; other excess fluff removed. '(LetExp (DecList (TypeDec any (FieldList (FieldDescriptor any int))) (VarDec buffer nil (getchar)) (FunctionDec readint (ExpList (IdType any any)) (int);; ? (int) or int? (LetExp (DecList (VarDec i nil 0) (FunctionDec isdigit (ExpList (IdType s string)) (int) (IfExp (>= (ord s) (ord "0")) (<= (ord s) (ord "9")) 0)) (FunctionDec skipto (ExpList) nil (WhileExp (IfExp (= buffer " ") 1 (= buffer "")) (AssignExp buffer (getchar))))) (ExpList (skipto) (AssignExp (FieldVar any any) (isdigit buffer)) (WhileExp (isdigit buffer) (ExpList (AssignExp i (- (+ (* i 10) (ord buffer)) (ord "0"))) (AssignExp buffer (getchar))))
Prof. Fateman CS 164 Lecture 18 12
Start of the interpreter
(defun tig-interp (x env) ; x= intermediate code, env=environment of bindings
;; first deal with interp of trivial or error cases which might occur. (cond ((null x) nil)
((eq x 'VOID) 'VOID) ;;or (error "attempt to evaluate VOID")
((symbolp x) (get-var x env))((atom x) x);; integers and strings are atoms.
(t (tig-interp-list x env)))) ;; the real business is in lisp lists
Prof. Fateman CS 164 Lecture 18 13
Start of the interpreter: tig-interp-list
(defun tig-interp-list (x env) ; environment of bindings (case (first x) (ExpList;; a sequence of expressions. (last1 (mapcar #'(lambda(r)(tig-interp r env))
(cdr x)))) (AssignExp (set-var (elt x 1)(elt x 2) env)) (IfExp (tig-interp-if (elt x 1)(elt x 2) (elt x 3) env)) ;; see next slide (LetExp (tig-interp-let (elt x 1)(elt x 2) env)) (WhileExp (tig-interp-while (elt x 1)(elt x 2) env)) (ArrayExp (make-array (tig-interp (elt x 2) env) ; size
:initial-element (tig-interp (elt x 3) env))) (RecordExp (tig-interp-rec x env)) (FieldVar (cadr (assoc (elt x 2)
(tig-interp (elt x 1) env)))) (BreakExp (throw 'tig-break nil)) (SubscriptVar (aref (get-var (elt x 1) env); get the array
(tig-interp (elt x 2)env))) (ForExp (tig-interp-for (elt x 1)(elt x 2) (elt x 3)(elt x 4) env)) (t;; a CallExp ;; get the procedure ;; evaluate the arguments (tig-call (get-fun (car x) env)
(mapcar #'(lambda(p) (tig-interp p env)) (cdr x)) env ))))))
Prof. Fateman CS 164 Lecture 18 14
if
(defun tig-interp-if (a b c env) (if (equal 1 (tig-interp a env)) (tig-interp b env) (tig-interp c env)))
What if in interp… we did (tig-interp-if (tig-interp (elt x 1) env) (tig-interp (elt x 2) …)
This would be quite wrong
(defun tig-interp-if (a b c env) (if (equal 1 a) b c)
What if in interp… we did (if (tig-interp (elt x 1) env) (tig-interp (elt x 2) …) ?
Prof. Fateman CS 164 Lecture 18 15
Loops: while
(defun tig-interp-while (test body env) (catch 'tig-break ;; watch out for breaks in body ;see interp (loop while (not (equal (tig-interp test env) 0)) do
(tig-interp body env) ‘VOID ;;why?))
;;semantics translates conveniently into the lisp loop while…;; but we could write this out using block/ if/ goto/ …
(block foo label1 (if (equal (tig-interp test env) 0) (return-from ‘foo ‘VOID)) (tig-interp body env) (go label1))
Or anything else with the same semantics
Prof. Fateman CS 164 Lecture 18 16
loops: for
(defun tig-interp-for (index low hi body env) ;; put index into the environment; initialize it to low. (let ((start (tig-interp low env))) (setf env (extend-env1 index start env)) ;; key step. Introduce new
variable (catch 'tig-break ;catch any break
statements here (loop while (<= start (tig-interp hi env));; loop like Lisp loop!
do (tig-interp body env) (set-var index (incf start) env)); another key step..increment start and index by 1 ‘VOID)));; note what happens to env on exit
Prof. Fateman CS 164 Lecture 18 17
Revisit the main interpreter
(defun tig-interp-list (x env) ; environment of bindings (case (first x) (ExpList;; a sequence of expressions. (last1 (mapcar #'(lambda(r)(tig-interp r env))
(cdr x)))) ;; self explanatory (AssignExp (set-var (elt x 1)(elt x 2) env)) (IfExp (tig-interp-if (elt x 1)(elt x 2) (elt x 3) env)) (LetExp (tig-interp-let (elt x 1)(elt x 2) env)) (WhileExp (tig-interp-while (elt x 1)(elt x 2) env)) (ArrayExp (make-array (tig-interp (elt x 2) env) ; size
:initial-element (tig-interp (elt x 3) env))) (RecordExp (tig-interp-rec x env)) (FieldVar (cadr (assoc (elt x 2)
(tig-interp (elt x 1) env)))) (BreakExp (throw 'tig-break nil)) (SubscriptVar (aref (get-var (elt x 1) env); get the array
(tig-interp (elt x 2)env))) (ForExp (tig-interp-for (elt x 1)(elt x 2) (elt x 3)(elt x 4) env)) (t;; a CallExp ;; get the procedure ;; evaluate the arguments (tig-call (get-fun (car x) env)
(mapcar #'(lambda(p) (tig-interp p env)) (cdr x)) env ))))))
Prof. Fateman CS 164 Lecture 18 18
Construct a record
(defun tig-interp-rec(x env) ;; This constructs a record. Cheap trick: ;; make an association list with ;; the appropriate name-value pairs. ;; Recall, we've already type-checked this guy.. (let* ((actualnames (mapcar #'(lambda(z)(elt z 1))(cddr x))) (actualvals (mapcar #'(lambda(z)(tig-interp (elt z 2) env))
(cddr x))) (res (mapcar #'list actualnames actualvals))) res))
sometype{a=1,b=2} /*create a record of type “sometype” with two fields in it
AST = (RecordExp (1 . 8) (sometype (1 . 8)) (ExpList (FieldId (1 . 10) a (IntExp (1 . 12) 1)) (FieldId (1 . 14) b (IntExp (1 . 16)
2))))Cleans up to(RecordExp sometype (FieldId a 1) (FieldId b 2))In lisp, actualsnames is (a b), actualvals is (1 2), result is ((a 1) (b 2)). THIS IS THE
RECORD!
Prof. Fateman CS 164 Lecture 18 19
Getting a value from a field
(defun tig-interp-list (x env) (case (first x) …. (FieldVar (cadr (assoc (elt x 2)
(tig-interp (elt x 1) env))))….))
If you see (FieldVar x a) then get the value for x, say ((a 1)(b 2) andthen look for the value associated with a with assoc.. (a 1) . Then takecadr of it to get 1.
Prof. Fateman CS 164 Lecture 18 20
Revisit the main interpreter
(defun tig-interp-list (x env) ; environment of bindings (case (first x) … (AssignExp (set-var (elt x 1)(elt x 2) env)) ;; what does set-var do …
)
Prof. Fateman CS 164 Lecture 18 21
Set the value of a “variable” set-var
(defun set-var (name val env) ;; Change the value associated with the name in the environment. ;; In this case the name may be complex: an l-value. (cond ((symbolp name)
(let ((r (assoc name env))) ;; look for a pair ( …. (r . Val) ….) on env (if (null r)(error "can't set unbound tiger var ~s" name) (setf (cdr r) (tig-interp val env)))))
((eq (car name) 'SubscriptVar) (setf (aref (get-var (elt name 1) env); get the array
(tig-interp (elt name 2)env)); the index ;;; (setf (aref xxx) (tig-interp val env) (tig-interp val env)))
((eq (car name) 'FieldVar) (let ((therecord (assoc (elt name 2)
(get-var (elt name 1) env) ))) (setf (cadr therecord) (tig-interp val env)))) ;; plop something down in the record.. ((a 1)(b 2))
(t;; no other option for set-var (error "unimplemented setting of ~s" name))))
Prof. Fateman CS 164 Lecture 18 22
Set the value of a “variable” set-var dealing with l-values…
….Works for x.a :=34 x[9]:=34(AssignExp (Fieldvar x a) 34). (AssignExp (SubscriptVar x 9) 34)
What about z.x.a := 34 or x[9].a :=34(AssignExp (Fieldvar z (Fieldvar x a)) 34). (AssignExp (Fieldvar (SubscriptVar x 9)
a )34)
Or x.a[9]:=34 ?(AssignExp (SubscriptVar (FieldVar x a) 9) 34)
Or x[3][9]:=34 ?(AssignExp (SubscriptVar (SubscriptVar x 3) 9) 34);; note: for this last expression to make sense, let type ar1 = array of int type ar2 = array of ar1 var x:ar2:= ar2[10] of (ar1[4] of 0)
Prof. Fateman CS 164 Lecture 18 23
Get the variable’s value (also used by set-var)
(defun get-var (name env) ;; find the value associated with the name in the environment (cond ((symbolp name)
(let ((r (assoc name env))) (cond ((null r)(error "unbound tiger var ~s" name))
(t (cdr r)))))(t;; could be subscript or fieldvar (tig-interp name env) )))
;; note that lvalues are used only by AssignExp, and evaluation there stops one level above the call to get-var.
Prof. Fateman CS 164 Lecture 18 24
Get the function associated with a name
(defun get-fun (name env) ;; find the function associated with the name in the environment (let ((r (assoc name env))) (cond ((null r)(error "unbound tiger fun ~s" name))
((Ent-p (cdr r)) (cdr r)) ; (Ent-body (cdr r)) holds the function (t (error "accessing var as function ~s" name)))))
(defstruct (Ent (:print-function print-ent)) name params body env kind def ty init lev readonly pos esc used ret) ;; these
guys will live on the environment stack.
(defun print-ent (p &optional (stream *standard-output*) depth) (format stream "[[ Ent:~a ]]" (Ent-name p)))
Prof. Fateman CS 164 Lecture 18 25
What is the environment, initially?
(defparameter init-env ;; for built-in functions we don't give the :params list because ;; we don't check it and bind vars etc. ;; we have the count of params in (built-in fun count) ;; each Ent has an environment, :env, but it is nil ;; we have bunches of other fields we don't use here. (list (cons 'print (make-Ent :name 'print :body `(built-in ,#'princ 1);one arg
:ret 'VOID)) (cons 'flush (make-Ent :name 'flush :body `(built-in ,#'finish-output 0)
:ret 'VOID)) (cons 'getchar (make-Ent :name 'getchar :body
`(built-in ,#'(lambda()(coerce (vector (read-char))'string )) 0) :ret 'string ))
(cons 'ord (make-Ent :name 'ord :body `(built-in ,#'(lambda(s)(if (or (not (stringp s))(string= s "")) -1
(char-code (elt s 0)))) 1):ret 'int))
Prof. Fateman CS 164 Lecture 18 26
What is the environment, initially?
(defparameter init-env… continued (cons 'not
(make-Ent :name 'not :body `(built-in ,#'(lambda(i)(if (= i 0) 1 0)) 1) :ret 'int))
(cons 'exit (make-Ent :name 'exit :body `(built-in ,#'(lambda(i)(error "termination with code ~s" i))1) :ret 'VOID))
(cons '+ (make-Ent :name '+ :body `(built-in ,#'+ 2) :ret 'int)) (cons '* (make-Ent :name '* :body `(built-in ,#'* 2) :ret 'int)) (cons '/ (make-Ent :name '/ :body `(built-in ,#'truncate 2) :ret 'int)) (cons '- (make-Ent :name '- :body `(built-in ,#'- 2) :ret 'int)) (cons '> (make-Ent :name '> :body `(built-in ,#'tig> 2) :ret 'int)) (cons '>= (make-Ent :name '>= :body `(built-in ,#'tig>= 2) :ret 'int)) (cons '< (make-Ent :name '< :body `(built-in ,#'tig< 2) :ret 'int)) (cons '<= (make-Ent :name '<= :body `(built-in ,#'tig<= 2) :ret 'int)) (cons '<> (make-Ent :name '<> :body `(built-in ,#'tig<> 2) :ret 'int)) (cons '= (make-Ent :name '= :body `(built-in ,#'tig= 2) :ret 'int)) (cons nil nil);; hm should be read-only. tig-interp nil should be nil regardless (cons 'VOID 'VOID);; ditto. ))
Prof. Fateman CS 164 Lecture 18 27
What are these little functions like tig<= ?
(defun tig>(r s)(tig-truth (typecase r (string (string> r s))(number(> r s)))))(defun tig<(r s)(tig-truth (typecase r (string (string< r s))(number(< r s)))))(defun tig>=(r s)(tig-truth (typecase r (string (string>= r s))(number(>= r
s)))))(defun tig<=(r s)(tig-truth (typecase r (string (string<= r s))(number(<= r
s)))))(defun tig<>(r s)(tig-truth (not (equal r s))))(defun tig=(r s)(tig-truth (equal r s)))(defun tig-truth (r)(if (null r) 0 1));convert lisp boolean to tiger boolean ))
Prof. Fateman CS 164 Lecture 18 28
Prof. Fateman CS 164 Lecture 18 29
What else is in env and how does it get there
• This is the meat of the interpreter and requires some real intellectual effort
• It also explains completely dynamic and static scope.
• ….to be continued….