; ---- T h e M e t a - C i r c u l a r E v a l u a t o r ---- ; ---- @a u t h o r J o h n n y E d u a r d o W e b e r ---- (load "lst-environment.scm") ; ==== E v a l ==== (define (eval exp env) (cond ((self-evaluating? exp) exp) ((variable? exp) (eval-variable exp env)) ((quoted? exp) (eval-quotation exp env)) ((definition? exp) (eval-definition exp env)) ((if? exp) (eval-if exp env)) ((lambda? exp) (eval-lambda exp env)) ((begin? exp) (eval-sequence (begin-actions exp) env)) ((cond? exp) (eval (cond->if exp) env)) ((primitive? exp) (eval-primitive exp env)) ((application? exp) (eval-application exp env)) (else (error "Unknown expression type -- EVAL" exp)))) ; ---- s e l f - e v a l u a t i n g ---- ; exp is self-evaluting if it is a string or a number (define (self-evaluating? exp) (or (string? exp) (number? exp))) ; ---- v a r i a b l e ---- ; exp is a variable if it is a symbol (define (variable? exp) (symbol? exp)) ; to evaluate a variable, lookup variable value in env (define (eval-variable exp env) (lookup-variable-value exp env)) ; ---- q u o t e ---- ; exp is a quotation if it is a list and it begins with 'quote (define (quoted? exp) (if (pair? exp) (eq? (car exp) 'quote) #f)) ; to evaluate a quotation, simply return the text after quote (define (eval-quotation exp env) (cadr exp)) ; ---- d e f i n e ---- ; exp is a define if it is a list and it begins with 'define (define (definition? exp) (if (pair? exp) (eq? (car exp) 'define) #f)) (define (definition-var exp) (cadr exp)) (define (definition-val exp) (caddr exp)) ; to evaluate a define: ; (1) If the first param to define is a symbol, add mapping to env ; (2) If the first param to define is a pair, then it is a function ; definition, so rewrite it as symbol -> lambda define. (define (eval-definition exp env) (if (symbol? (definition-var exp)) (if (empty-env? env) (extend-environment (list (definition-var exp)) (list (eval (definition-val exp) env)) env) (define-variable (definition-var exp) (eval (definition-val exp) env) env)) (eval-definition (list 'define (caadr exp) (func->lambda exp)) env))) (define (func->lambda exp) (cons 'lambda (cons (cdadr exp) (cons (caddr exp) '())))) ; ---- i f ---- ; exp is an if statement if it is a list and it begins with 'if (define (if? exp) (if (pair? exp) (eq? (car exp) 'if) #f)) ; if abstraction (define (if-predicate exp) (cadr exp)) (define (if-consequent exp) (caddr exp)) (define (if-alternative exp) (if (not (null? (cdddr exp))) (cadddr exp) 'false)) (define (make-if predicate consequent alternative) (list 'if predicate consequent alternative)) ; to evaluate an if, evaluate the predicate ; if the predicate is true evaluate consequent ; if the predicate is false evaluate alternative (define (true? exp) exp) (define (false? exp) exp) (define (eval-if exp env) (if (true? (eval (if-predicate exp) env)) (eval (if-consequent exp) env) (eval (if-alternative exp) env))) ; ---- c o n d ---- ; exp is an cond statement if it is a list and it begins with 'cond (define (cond? exp) (if (pair? exp) (eq? (car exp) 'cond) #f)) ; cond abstraction (define (cond-clauses exp) (cdr exp)) (define (cond-predicate clause) (car clause)) (define (cond-actions clause) (cdr clause)) (define (cond-else-clause? clause) (eq? (cond-predicate clause) 'else)) ; to evaluate a cond convert it to an expanded if (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 clauses isn't clast -- COND->IF" clauses)) (make-if (cond-predicate first) (sequence->exp (cond-actions first)) (expand-clauses rest)))))) (define (sequence->exp seq) (cond ((null? seq) seq) ((last-exp? seq) (first-exp seq)) (else (make-begin seq)))) ; ---- b e g i n ---- ; exp is a sequence if it is a list and it begins with 'begin (define (begin? exp) (if (pair? exp) (eq? (car exp) 'begin) #f)) ; sequence abstration (define (make-begin seq) (cons 'begin seq)) (define (begin-actions exp) (cdr exp)) (define (first-exp seq) (car seq)) (define (rest-exps seq) (cdr seq)) (define (last-exp? seq) (null? (cdr seq))) ; to evaluate a sequence, if we are at the last sequence eval exp ; else evaluate (define (eval-sequence exps env) (if (last-exp? exps) (eval (first-exp exps) env) (eval-sequence (rest-exps exps) (cons (first-frame (eval (first-exp exps) env)) env)))) ; ---- a p p l i c a t i o n ---- ; exp is an application if it is a list (define (application? exp) (pair? exp)) ; application abstraction (define (operator exp) (car exp)) (define (operands exp) (cdr exp)) (define (no-operands? ops) (null? ops)) (define (first-operand ops) (car ops)) (define (second-operand ops) (cadr ops)) (define (rest-operands ops) (cdr ops)) ; to evaluate an application, apply eval'ed operator to eval'ed operands (define (eval-application exp env) (apply-proc (eval (operator exp) env) (evaluate-operands (operands exp) env) env)) ; recursively evalute the expressions in list exps (define (evaluate-operands exps env) (if (no-operands? exps) '() (cons (eval (first-operand exps) env) (evaluate-operands (rest-operands exps) env)))) ; ---- p r i m i t i v e ---- ; exp is a primitive if it is in primitive list (define (primitive? exp) (contains? (operator exp) primitive-procedures)) (define primitive-procedures (list 'append 'car 'cdr 'cons 'list '= '+ '- '* '/ '> '< '>= '<= 'eq? 'null? 'quit)) (define (contains? s los) (cond ((null? los) #f) ((eq? s (car los)) #t) (else (contains? s (cdr los))))) ; to evaluate a primitive simply evaluate the operands and ; dispatch on the operator (define (eval-primitive exp env) (let ((len (length (operands exp)))) (if (or (= len 0) (= len 1)) (eval-primitive-uop (operator exp) (evaluate-operands (operands exp) env)) (eval-primitive-bop (operator exp) (evaluate-operands (operands exp) env))))) (define (eval-primitive-uop rator rands) (cond ((eq? rator 'length) (length (first-operand rands))) ((eq? rator 'null?) (null? (first-operand rands))) ((eq? rator 'car) (car (first-operand rands))) ((eq? rator 'cdr) (cdr (first-operand rands))) ((eq? rator 'random) (random (first-operand rands))) ((eq? rator 'list) (list (first-operand rands))) ((eq? rator 'quit) 'quit) (else (error "Unknown primitive uop -- EVAL-PRIM" rator)))) (define (eval-primitive-bop rator rands) (cond ((eq? rator '+) (prim-proc-add rands)) ((eq? rator '-) (prim-proc-sub rands)) ((eq? rator '*) (prim-proc-mul rands)) ((eq? rator '=) (prim-proc-equ rands)) ((eq? rator '<) (prim-proc-ltn rands)) ((eq? rator '>) (prim-proc-gtn rands)) ((eq? rator '<=) (prim-proc-lte rands)) ((eq? rator '>=) (prim-proc-gte rands)) ((eq? rator 'eq?) (prim-proc-equ rands)) ((eq? rator 'cons) (prim-proc-con rands)) ((eq? rator 'append) (prim-proc-app rands)) ((eq? rator 'list) (prim-proc-lst rands)) (else (error "Unknown primitive bop -- EVAL-PRIM" rator)))) (define (prim-proc-add rands) (if (null? rands) 0 (+ (first-operand rands) (prim-proc-add (rest-operands rands))))) (define (prim-proc-sub rands) (if (null? rands) 0 (- (first-operand rands) (prim-proc-sub (rest-operands rands))))) (define (prim-proc-mul rands) (if (null? rands) 1 (* (first-operand rands) (prim-proc-mul (rest-operands rands))))) (define (prim-proc-equ rands) (eq? (first-operand rands) (second-operand rands))) (define (prim-proc-ltn rands) (< (first-operand rands) (second-operand rands))) (define (prim-proc-gtn rands) (> (first-operand rands) (second-operand rands))) (define (prim-proc-lte rands) (>= (first-operand rands) (second-operand rands))) (define (prim-proc-gte rands) (<= (first-operand rands) (second-operand rands))) (define (prim-proc-con rands) (cons (first-operand rands) (second-operand rands))) (define (prim-proc-app rands) (prim-proc-app-aux (car rands) (cdr rands))) (define (prim-proc-app-aux l1 ll2) (if (null? ll2) l1 (prim-proc-app-aux (append l1 (car ll2)) (cdr ll2)))) (define (append l1 l2) (if (null? l1) l2 (cons (car l1) (append (cdr l1) l2)))) (define (prim-proc-lst rands) rands) (define (empty-list) '()) ; ---- l a m b d a ---- (define (make-procedure parameters body env) (list 'procedure parameters body env)) (define (procedure-parameters p) (cadr p)) (define (procedure-body p) (caddr p)) (define (procedure-environment p) (cadddr p)) ; exp is a lambda expression if it is a list and it begins with 'lambda (define (lambda? exp) (if (pair? exp) (eq? (car exp) 'lambda) #f)) ; lambda abstraction (define (lambda-parameters exp) (cadr exp)) (define (lambda-body exp) (cddr exp)) ; to evaluate a lambda, build a compound-procedure (define (eval-lambda exp env) (make-procedure (lambda-parameters exp) (lambda-body exp) env)) ; ==== a p p l y ==== (define (apply-proc procedure arguments environment) (eval-sequence (procedure-body procedure) (extend-environment (procedure-parameters procedure) arguments environment))) (define (compound-procedure? proc) (if (pair? proc) (eq? (car proc) 'procedure) #f)) ; c o m p o u n d p r o c e d u r e ; Read-Eval-Print Loop (define the-global-environment the-empty-environment) (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) (if (let ((input (read))) (let ((output (eval input the-global-environment))) (announce-output output-prompt) (display output) (eq? output 'quit))) #t ; quit command was issued (driver-loop)))