;; The first three lines of this file were inserted by DrScheme. They record metadata ;; about the language level of this file in a form that our tools can easily process. #reader(lib "reader.ss" "plai" "lang") ;; SuperCalc: A substitution interpreter supporting simple boolean and integer ;; operations. ;; ;; To simplify the implementation, there is minimal error checking, the parse tree ;; is directly represented using Scheme lists, and the corresponding Scheme values ;; are used to implement SuperCalc values. ;; ;; In the starter code, areas that you definitely have to modify are marked with ;; TODO. Depending on your implementation strategy, you may want to modify other ;; areas. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Expression Domain ; Use Scheme numbers directly ; Tests whether an expression is a NUM (define NUM? integer?) ; Extracts the value of the NUM to a Scheme integer (trivial, since we ; chose to make the NUM be a Scheme integer!) (define (NUM-value exp) exp) ; Turns a Scheme integer into a NUM (again, trivial) (define (NUM v) v) ;; TODO: Define booleans (define (BOOLEAN exp) (error 'TODO "Unimplemented")) ;; ... ; LITERAL ::= NUM | BOOLEAN (define (LITERAL? exp) (NUM? exp)) ;; TODO: Extend to booleans ; Abstraction of both booleans and nums (define (LITERAL-value exp) exp) (define (LITERAL exp) exp) ; IF ::= (if ) (define (IF? exp) (and (list? exp) (eq? (first exp) 'if) (equal? (length exp) 4))) (define IF-test second) (define IF-consequent third) (define IF-alternate fourth) ; Makes an IF expression out of three subexpressions (define (IF test consequent alternate) (list 'if test consequent alternate)) ; AND ::= (and ) ; TODO: write AND?, AND-lhs, AND-rhs (define (AND? exp) (error 'TODO "Unimplemented")) ; OR ::= (or ) ; TODO: write OR?, OR-lhs, OR-rhs (define (OR? exp) (error 'TODO "Unimplemented")) ; For convenience, the implementation collapses all three binary operators ; BINOP ::= "(" ( "+" | "-" | "=" ) ")" ; True for ADD, SUB, and EQ (binary operator) expressions (define (BINOP? exp) (and (list? exp) (case (first exp) [(+ - =) #t] [else #f]) (equal? 3 (length exp)))) (define BINOP-op first) (define BINOP-lhs second) (define BINOP-rhs third) ; NOT ::= (not ) (define (NOT? exp) (and (list? exp) (equal? 2 (length exp)) (eq? (first exp) 'not))) (define NOT-rhs second) (define (CONDITIONAL? exp) (or (IF? exp) (AND? exp) (OR? exp))) ; All expressions (define (EXP? exp) (or (LITERAL? exp) (BINOP? exp) (NOT? exp) (CONDITIONAL? exp))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;; Value Domain ;; ;; The value domain is the literal domain from the expression domain, ;; however we rename the procedures to abstract (albeit without the ;; benefit of type checking) this in the implementation of the ;; interpreter. ; Is this a legal value in SuperCalc? (define VALUE? LITERAL?) ; Construct a SuperCalc value from a Scheme value (define VALUE LITERAL) ; Extract the Scheme value from a SuperCalc value (define VALUE-value LITERAL-value) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;; The Interpreter (evaluator) ; sc-eval: EXP -> VALUE ; TODO: Add evaluation rules for AND, OR, and BOOLEANS (define (sc-eval exp) (cond [(NUM? exp) exp] [(BINOP? exp) (sc-eval-BINOP exp)] [(IF? exp) (sc-eval-IF exp)] [(NOT? exp) ; Rewrite as an IF (done for you as an example) and then re-invoke ; the evaluator on the new expression. ; Note that the interpreter would give correct results but would be ; incorrectly abstracted if we omitted the calls to BOOLEAN ; below because it would be constructing the SuperCalc IF from ; Scheme values, not SuperCalc literal expressions. (sc-eval (IF (NOT-rhs exp) (BOOLEAN #f) (BOOLEAN #t)))] [else (error 'sc-eval "Illegal Expression")])) ; sc-eval-BINOP: BINOP -> VALUE (define (sc-eval-BINOP exp) (let ([lhs (BINOP-lhs exp)] [rhs (BINOP-rhs exp)] ; Turn the operator into a Scheme procedure [op (case (BINOP-op exp) ((+) +) ((-) -) ((=) equal?) (else (error 'sc-eval "Illegal binary operator")))]) ; Turn the result from a Scheme value into a SuperCalc value (VALUE ; Apply the Scheme procedure to the values (op ; Evaluate the subexpressions and convert the results to Scheme values (VALUE-value (sc-eval lhs)) (VALUE-value (sc-eval rhs)))))) ; sc-eval-IF: IF -> VALUE (define (sc-eval-IF exp) ; TODO: Implement IF. Don't forget to recursively call ; sc-eval, and be careful to properly abstract the ; expression and value domains. (error 'TODO "Unimplemented")) (define super-calc sc-eval) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Some test code: ; Test an expression (define (test exp expected-result) (display "Input: ") (display exp) (newline) (display "Expected: ") (display expected-result) (newline) (display "Actual: ") (let ([r (VALUE-value (super-calc exp))]) (display r) (newline) (newline) (if (not (equal? r expected-result)) (error 'test "Test failed") (void)))) ;; Comment out any that you don't want to run while you are debugging your ;; partial implementation. (display "Tests") (newline)(newline) (test '(+ 1 2) 3) (test '(- 1 2) -1) (test '(and #t #t) #t) (test '(and #t #f) #f) (test '(and #f #t) #f) (test '(and #f #f) #f) (test '(and 1 #f) #f) (test '(and #t 1) 1) (test '(not #t) #f) (test '(not 1) #f) (test '(not #f) #t) (test '(if (= (+ 1 2) 3) 8 9) 8) (test '(if (= (+ 1 2) 4) 8 9) 9) (test '(if (= (+ 1 2) 4) (or #f #t) (not #t)) #f) ;; TODO: Add your own tests here...