#lang racket (require rackunit "4-2-2-lazy-repl-program.rkt") ;; Same as 4-1-4-repl-test.rkt but includes tests for lazy evaluation and ;; memoization. (test-case "self-evaluating expression" (check-equal? (my-eval "x" the-global-environment) "x") (check-equal? (my-eval 23 the-global-environment) 23) ) (test-case "quotation" (define quote-expr '(quote ("x" 23 a))) (check-equal? (my-eval quote-expr the-global-environment) '("x" 23 a)) ) (test-case "variable definition" (define def-expr '(define a 42)) (check-equal? (my-eval def-expr the-global-environment) 'ok) (check-equal? (my-eval 'a the-global-environment) 42) ) (test-case "procedure definition" (define proc-expr '(define (multiply x y) (* x y))) (check-equal? (my-eval proc-expr the-global-environment) 'ok) ) (test-case "procedure application" (define application-expr '(multiply 3 4)) (check-equal? (my-eval application-expr the-global-environment) 12) (check-equal? (my-eval '(car (cons 1 (cons 2 '()))) the-global-environment) 1) ) (test-case "assignment" (define assign-expr '(set! a 23)) (check-equal? (my-eval assign-expr the-global-environment) 'ok) (check-equal? (my-eval 'a the-global-environment) 23) ) (test-case "predicate" (check-true (true? (my-eval 'a the-global-environment))) (check-true (true? (my-eval "false" the-global-environment))) (check-false (true? (my-eval 'false the-global-environment))) (check-true (false? (my-eval 'false the-global-environment))) ) (test-case "if statement" (define ie '(if true 1 2)) (check-equal? (my-eval ie the-global-environment) 1) (define ie2 '(if false 1 2)) (check-equal? (my-eval ie2 the-global-environment) 2) ) (test-case "cond statement" (define ce '(cond ("a" 1 2) ("b" 3))) (check-equal? (my-eval ce the-global-environment) 2) (define alt-ce '(cond ((cons 1 (cons 2 '())) => car) (else 87))) (check-equal? (my-eval alt-ce the-global-environment) 1) ) (test-case "begin statement" (define be '(begin "a" "b" 1 2 (* 3 4))) (check-equal? (my-eval be the-global-environment) 12) ) (test-case "boolean operator" (my-eval '(define c 38) the-global-environment) (check-equal? (my-eval '(and 1 "a" c) the-global-environment) 38) (check-equal? (my-eval '(and 1 1 false) the-global-environment) false) (check-equal? (my-eval '(or false false 1) the-global-environment) 1) (check-equal? (my-eval '(or false false false) the-global-environment) false) ) (test-case "let" (define le '(let ((x 1) (y 2) (z 3)) 'side-effect (+ x y z))) (check-equal? (my-eval le the-global-environment) 6) (define le2 '(let ((x 1) (y 2)) (+ x y))) (check-equal? (my-eval le2 the-global-environment) 3) (define le3 '(let ((x 1)) 'side-effect (+ x 2))) (check-equal? (my-eval le3 the-global-environment) 3) (define le4 '(let () 'side-effect (+ 3 2))) (check-equal? (my-eval le4 the-global-environment) 5) (define le5 '(let () (+ 3 2))) (check-equal? (my-eval le5 the-global-environment) 5) (define le6 '(let ([x 1]) (define y 2) (+ x y))) (check-equal? (my-eval le6 the-global-environment) 3) ) (test-case "named-let" (my-eval '(define (fib n) (let fib-iter ((a 1) (b 0) (my-count n)) (if (= my-count 0) b (fib-iter (+ a b) a (+ my-count -1))))) the-global-environment) ; use actual-value to force the values (check-equal? (actual-value '(fib 0) the-global-environment) 0) (check-equal? (actual-value '(fib 1) the-global-environment) 1) (check-equal? (actual-value '(fib 10) the-global-environment) 55) ) (test-case "let*" (define lse '(let* ((x 1) (y (+ x 1)) (z (+ y 1))) (+ x y z))) (check-equal? (my-eval lse the-global-environment) 6) (define lse2 '(let* ((a 1) (b 2)) 'side-effect (+ a b))) (check-equal? (my-eval lse2 the-global-environment) 3) (define lse3 '(let* ((a 1)) 'side-effect (+ a 2))) (check-equal? (my-eval lse3 the-global-environment) 3) (define lse4 '(let* ((a 1)) (+ a 2))) (check-equal? (my-eval lse4 the-global-environment) 3) ) (test-case "lazy evaluation and memoization" (my-eval '(define (try a b) (if (= a 0) 1 b)) the-global-environment) (check-equal? (my-eval '(try 0 (/ 1 0)) the-global-environment) 1) (my-eval '(define test-count 0) the-global-environment) (check-equal? (my-eval 'test-count the-global-environment) 0) (my-eval '(define (id x) (set! test-count (+ test-count 1)) x) the-global-environment) (my-eval '(define w (id (id 10))) the-global-environment) (check-equal? (my-eval 'test-count the-global-environment) 1) (check-equal? (actual-value 'w the-global-environment) 10) (check-equal? (my-eval 'test-count the-global-environment) 2) ; check if test-count is incremented again (check-equal? (actual-value 'w the-global-environment) 10) ; test-count should not increment again (check-equal? (my-eval 'test-count the-global-environment) 2) )