;; Problem 1, java-syntax part:

(define (do? obj)
  ;; (DO <statement> <boolean expr>)
  (and (simple? obj 'DO 3 3)
       (statement? (second obj))
       (typed-expr? (third obj) 'BOOLEAN)))

(define (do.statement exp) (second exp))
(define (do.predicate exp) (third exp))

;; Problem 1, java-eval part:

(define (j-eval-do exp env next)
  ;; (DO <statement> <boolean expr>)
  (let ((labels *labels*)
        (statement (do.statement exp))
        (bool-expr (do.predicate exp)))
    (define (done)
      (set! *labels* labels)
      (next "End of DO"))
    (define (test)
      (j-eval bool-expr env
        (lambda (bool)
          (cond ((eq? bool #T) (again))
                ((eq? bool #F) (done))
                (else (error "DO: Not a boolean value"))))))
    (define (again)
      (j-eval statement env (lambda (ignore) test)))
    (add-label! '*BREAK-LABEL* done)
    (add-label! '*CONTINUE-LABEL* test) ; Double check this
    (again)))

;; Problem 2:

(define (supertype-loop desired-type type count)
  ;; Returns 0 for same type, 1 for one step away, etc., or #F
  ;; if type-name isn't a supertype of type
  (if (eq? type desired-type)
      count
      (let ((super (ev-class.superclass type)))
        (if (ev-class? super)
            (supertype-loop desired-type super (+ count 1))
            #F))))

(define (decide new best decision)
  ;; We start out TIEd, and only change that if we find
  ;; something definitely better or definitely worse.  If we
  ;; have to change our mind from NO to YES (or vice versa)
  ;; then it's really a TIE.
  (cond ((null? new) decision)
        ((= (car new) (car best))
         (decide (cdr new) (cdr best) decision))
        ((> (car new) (car best))
         (if (eq? decision 'YES)
             'TIE
             (decide (cdr new) (cdr best) 'NO)))
        (else (if (eq? decision 'NO)
                  'TIE
                  (decide (cdr new) (cdr best) 'YES)))))

;; Problem 3

(define (j-eval-variables exp env next)
  ;; (VARIABLES <type> (<VariableDecl> ...) <statement>)
  (let ((vdecls (variables.var-decls exp))
        (type-name (variables.type exp)))
    (let ((names (map var-decl.name vdecls))
          (type (lookup-class type-name))
          (init-exprs
           (map (get-initializer type-name) vdecls)))
      ;; INIT-EXPRS is a list of Java-in-Scheme expressions that must
      ;; be evaluated to initialize the new variables.
      (j-eval-in-order init-exprs env
       (lambda (init-vals)
         ;; Extend the environment by creating bindings for the new
         ;; variables (including "assignment conversion" of the
         ;; values), and then in this extended environment evaluate
         ;; the statement.
         (j-eval (variables.statement exp)
                 (extend-environment
                  (make-frame 'VARIABLES
                   (make-bindings names
                    (map (lambda (name) type-name) names)
                    (map (lambda (val) (coerce val type))
                         init-vals)))
                  env)
                 next))))))
