eval.ss
#lang scheme

;; A (Result X) is (make-success X) or (make-failure Exn)
(define-struct success (results))
(define-struct failure (results))

;; A Program is (make-program src exp ev out err)
;; src : (Listof Syntax)
;; exp : (Promise (Result (Listof Syntax)))
;; ev : (Promise (Result (Listof List)))
;; out : StringOutputPort
;; err : StringOutputPort
(define-struct program (source expansion evaluation output-port error-port))

(define (build-program module-path definitions interactions)
  (let* ([source (make-source module-path definitions interactions)]
         [output-port (open-output-string)]
         [error-port (open-output-string)]
         [expansion (delay (expand-program source output-port error-port))]
         [evaluation (delay (eval-program expansion output-port error-port))])
    (make-program source expansion evaluation output-port error-port)))

(define (program-expands? program)
  (success? (force (program-expansion program))))

(define (program-evals? program)
  (success? (force (program-evaluation program))))

(define (program-original-syntax program)
  (program-source program))

(define (program-expanded-syntax program)
  (success-results (force (program-expansion program))))

(define (program-expansion-error program)
  (failure-results (force (program-expansion program))))

(define (program-repl-values program)
  (success-results (force (program-evaluation program))))

(define (program-evaluation-error program)
  (failure-results (force (program-evaluation program))))

(define (program-output program)
  (get-output-string (program-output-port program)))

(define (program-error-output program)
  (get-output-string (program-error-port program)))

(provide/contract
 [program? (-> any/c boolean?)]
 [rename build-program make-program
         (->* [module-path? (listof any/c)] [(listof any/c)] program?)]
 [program-source (-> program? (listof syntax?))]

 [program-expand? (-> program? boolean?)]
 [program-expanded (-> program? (listof syntax?))]
 [program-expand-error (-> program? any/c)]

 [program-eval? (-> program? boolean?)]
 [program-value (-> program any)]
 [program-eval-error (-> program any/c)]

 [program-output (-> program string?)]
 [program-error-output (-> program string?)])