#lang eopl
(require tests/eopl/private/utils)

(require "data-structures.rkt")  ; for expval constructors
(require "interp.rkt")           ; for value-of-program
(require "cps-out-lang.rkt")     ; for cps-program->string, cps-out-scan&parse 

(require (only-in racket pretty-print))

(define instrument-cps (make-parameter #f))  

;; run : String -> ExpVal
(define run
  (lambda (string)
    (value-of-program (cps-out-scan&parse string))))

(define equal-answer?
  (lambda (ans correct-ans)
    (equal? ans (sloppy->expval correct-ans))))

(define sloppy->expval 
  (lambda (sloppy-val)
    (cond
      ((number? sloppy-val) (num-val sloppy-val))
      ((boolean? sloppy-val) (bool-val sloppy-val))
      (else
       (eopl:error 'sloppy->expval 
                   "Can't convert sloppy value to expval: ~s"
                   sloppy-val)))))

(define-syntax-rule (check-run (name str res) ...)
  (begin
    (cond [(eqv? 'res 'error)
           (check-exn always? (lambda () (run str)))]
          [else
           (check equal-answer? (run str) 'res (symbol->string 'name))])
    ...))



;; this consists entirely of expressions that are already in cps.

;; exercise: for each expression that is marked "not in cps",
;; explain why it is not cps.

;;;;;;;;;;;;;;;; tests ;;;;;;;;;;;;;;;;

(check-run  
 ;; simple arithmetic
 (positive-const "11" 11)
 (negative-const "-33" -33)
 (simple-arith-1 "-(44,33)" 11)
 
 
 ;; nested arithmetic
 (nested-arith-left "-(-(44,33),22)" -11)
 (nested-arith-right "-(55, -(22,11))" 44)
 
 (cps-nested-arith-left "let x = -(44,33) in -(x,22)" -11)
 (cps-nested-arith-right "let y = -(22,11) in -(55, y)" 44)
 
 ;; simple variables
 (test-var-1 "x" 10)
 (test-var-2 "-(x,1)" 9)
 (test-var-3 "-(1,x)" -9)
 
 ;; simple unbound variables
 (test-unbound-var-1 "foo" error)
 (test-unbound-var-2 "-(x,foo)" error)
 
 ;; simple conditionals
 (if-true "if zero?(0) then 3 else 4" 3)
 (if-false "if zero?(1) then 3 else 4" 4)
 
 ;; test dynamic typechecking
 (no-bool-to-diff-1 "-(zero?(0),1)" error)
 (no-bool-to-diff-2 "-(1,zero?(0))" error)
 (no-int-to-if "if 1 then 2 else 3" error)
 
 ;; make sure that the test and both arms get evaluated
 ;; properly. 
 (if-eval-test-true "let x = -(11,11) in if zero?(x) then 3 else 4" 3)
 (if-eval-test-false "let x = -(11,12)in if zero?(x) then 3 else 4" 4)
 
 ;; and make sure the other arm doesn't get evaluated.
 (if-eval-test-true-2 "if zero?(-(11, 11)) then 3 else foo" 3)
 (if-eval-test-false-2 "if zero?(-(11,12)) then foo else 4" 4)
 
 ;; simple let
 (simple-let-1 "let x = 3 in x" 3)
 
 ;; make sure the body and rhs get evaluated
 (eval-let-body "let x = 3 in -(x,1)" 2)
 (eval-let-rhs "let x = -(4,1) in -(x,1)" 2)
 
 ;; check nested let and shadowing
 (simple-nested-let "let x = 3 in let y = 4 in -(x,y)" -1)
 (check-shadowing-in-body "let x = 3 in let x = 4 in x" 4)
 (check-shadowing-in-rhs "let x = 3 in let x = -(x,1) in x" 2)
 
 ;; simple applications
 (apply-proc-in-rator-pos "(proc(x) -(x,1)  30)" 29)
 (apply-simple-proc "let f = proc (x) -(x,1) in (f 30)" 29)
 (let-to-proc-1 "(proc(f)(f 30)  proc(x)-(x,1))" 29)
 
 ;; (nested-procs-not-in-cps "((proc (x) proc (y) -(x,y)  5) 6)" -1)
 
 (nested-procs-in-tf "(proc (x y) -(x,y)  5 6)" -1)
 
 (nested-procs2 "let f = proc(x y) -(x,y) in (f -(10,5) 6)"
                -1)
 
 ;;       (y-combinator-1-not-in-tf "
 ;; let fix =  proc (f)
 ;;             let d = proc (x) proc (z) ((f (x x)) z)
 ;;             in proc (n) ((f (d d)) n)
 ;; in let
 ;;     t4m = proc (f) proc(x) if zero?(x) then 0 else -((f -(x,1)),-4)
 ;; in let times4 = (fix t4m)
 ;;    in (times4 3)" 12)
 
 
 ;;       ;; this one is not in cps
 ;;     (twice "
 ;;         (proc (twice)
 ;;            ((twice proc (z) -(z,1)) 11)
 ;;          proc (f) proc (x) (f (f x)))"
 ;;       9)
 
 (twice-in-cps "
      let twice = proc(f x k)
                    (f x  proc (z) (f z k))
      in (twice 
          proc (x k) (k -(x,1))
          11
          proc(z) z)"
               9)
 
 (cps-both-simple "
       let f = proc (x) -(x,1)
       in (f 27)"
                  26)
 
 (sum-test-1 "+()" 0)
 (sum-test-2 "+(2,3,4)" 9)
 
 (letrec-test-1 "letrec f(x) = 17 in 34" 34)
 
 (letrec-test-2 "letrec f(x y) = -(x,y) in -(34, 2)" 32)
 
 (letrec-test-3 "
       letrec even(x) = if zero?(x) then zero?(0) else (odd -(x,1))
              odd (x) = if zero?(x) then zero?(1) else (even -(x,1))
       in (even 5)"
                #f)
 
 ;; not in cps
 ;;     (cps-simple-rator "
 ;;        let f = proc (x) -(x,1)
 ;;        in (f (f 27))"
 ;;       25)
 
 ;;     (cps-simple-rand "
 ;;        let f = proc (x) proc (y) -(x,y)
 ;;        in ((f 27) 4)"
 ;;       23)
 
 ;;     (cps-neither-simple "
 ;;       let f = proc (x) proc (y) -(x, y)
 ;;       in let g = proc (z) -(z, 1)
 ;;       in ((f 27) (g 11))"
 ;;       17)
 
 ;;     (cps-serious-zero-test "
 ;;       let f = proc (x) -(x, 1)
 ;;       in if zero?((f 1)) then 11 else 22"
 ;;       11)
 
 (print-test-1
  "let x = 3 in printk(-(x,1)); 33"
  33)
 
 (store-test-0
  "newrefk(33, proc (loc1) 44)"
  44)
 
 (store-test-1
  "newrefk(33, proc (loc1)
                    newrefk(44, proc (loc2)
                                 derefk(loc1, proc(ans)ans)))"
  33)
 
 (store-test-2 "
      newrefk(33, proc (loc1)
                   newrefk(44, proc (loc2)
                                setrefk(loc1, 22);
                                derefk(loc1, proc(ans)ans)))"
               22)
 
 (store-test-2a "
      newrefk(33, proc (loc1)
                   newrefk(44, proc (loc2)
                                setrefk(loc1, 22);
                                derefk(loc1, proc (ans) -(ans,1))))"
                21)
 
 (store-test-3  "
      newrefk(33, proc (loc1)
                   newrefk(44, proc (loc2)
                                setrefk(loc2, 22);
                                derefk(loc1, proc(ans)ans)))"
                33)
 
 (gensym-cps "
       newrefk(0,
        proc(ctr)
         let g = proc(k) derefk(ctr,
                                proc(v) setrefk(ctr, -(v,-1)); (k v))
         in (g 
            proc (x) (g 
                      proc (y) -(x,y))))"
             -1)
 
 ;; in the example above, ctr is public.  Here it is local.
 (gensym-cps-2 "
      let makeg = proc (k1)
                   newrefk(0, proc (ctr)
                    (k1  proc (k) 
                          derefk(ctr, 
                                 proc (v) 
                                  setrefk(ctr,-(v,-1));(k v))))
      in (makeg
          proc(g)
           (g 
            proc (x) (g 
                      proc (y) -(x,y))))"
               -1)
 
 )
