#lang at-exp racket/base
(require algol60/algol60
         rackunit
         (for-syntax racket/base))

(define-syntax (capture-output stx)
  (syntax-case stx ()
    [(_ exp)
     (with-handlers ((exn:fail?
                      (λ (exn)
                        #`(list 'expand
                                #,(exn-message exn)))))
       (define expanded (local-expand #'exp 'expression #f))
       #`(let ([op (open-output-string)]
               [ep (open-output-string)])
           (let/ec k
             (parameterize ([current-output-port op]
                            [current-error-port ep]
                            [error-escape-handler (λ () (k (void)))])
               #,expanded))
           (list 'run
                 (get-output-string op)
                 (get-output-string ep))))]))

(check-equal?
 (capture-output
  @literal-algol{
    begin
      printsln (`hello world')
    end
  })
 '(run "hello world\n" ""))

(check-pred
 (λ (x) (and (eq? (list-ref x 0) 'expand)
             (regexp-match #rx"parse error near BEGIN"
                           (list-ref x 1))))
 (capture-output
  @literal-algol{
    begin
  }))


(check-pred
 (λ (x) (and (eq? (list-ref x 0) 'expand)
             (regexp-match #rx"parse error near PROCEDURE"
                           (list-ref x 1))))
 (capture-output
  @literal-algol{
    procedure Absmax(a) Size:(n, m) Result:(y) Subscripts:(i, k);
       value n, m; array a; integer n, m, i, k; real y;
    begin integer p, q;
       y := 0; i := k := 1;
       for p:=1 step 1 until n do
       for q:=1 step 1 until m do
           if abs(a[p, q]) > y then
               begin y := abs(a[p, q]);
               i := p; k := q
               end
    end Absmax
  }))
