helper.ss
(module helper mzscheme
  (require (lib "contract.ss")
           "reduction-semantics.ss")
  
  (define counter 0)
  (define (generate-string)
    (set! counter (add1 counter))
    (format "s~a" counter))
  
  (define (unique-names? l)
    (let ([ht (make-hash-table)])
      (andmap (lambda (n)
                (if (hash-table-get ht n (lambda () #f))
                    #f
                    (begin
                      (hash-table-put! ht n #t)
                      #t)))
              l)))
  
  (define (all-of P ?)
    ;; Traverse P as an sexp, and look for class-name uses:
    (let ([l (let loop ([sexp P])
               (cond
                 [(? sexp) (list sexp)]
                 [(pair? sexp) (append (loop (car sexp)) (loop (cdr sexp)))]
                 [else null]))]
          [ht (make-hash-table)])
      ;; Filter duplicates by hashing:
      (for-each (lambda (i) (hash-table-put! ht i #t)) l)
      (hash-table-map ht (lambda (k v) k))))
  
  (define-syntaxes (lang-match-lambda* 
		    lang-match-lambda-memoized*
		    lang-match-lambda
		    lang-match-lambda-memoized)
    (let ([generic
           (lambda (lam)
             (lambda (stx)
               (syntax-case stx ()
                 [(_ (id ...) main-id grammar [pattern result] ...)
                  (with-syntax ([red (generate-temporaries #'(pattern ...))]
                                [lam lam]
				[ids #'(id ...)])
                    (syntax/loc
                     stx
                     (let ([lang grammar]
                           [escape (make-parameter void)])
                       (let ([reds (list (reduction grammar pattern ((escape) (lambda ids result)))
                                         ...)])
                         (lam (id ...)
                              ((let/ec esc
                                 (parameterize ([escape esc])
                                   (reduce reds main-id)
                                   (error 'lang-match-lambda "no pattern matched input: ~e" main-id)))
                               id ...))))))])))]
	  [single
	   (lambda (multi)
	     (lambda (stx)
	       (syntax-case stx ()
		 [(_ (id) grammar [pattern result] ...)
		  (with-syntax ([multi multi])
		    #'(multi (id) id grammar [pattern result] ...))])))])
      (values
       (generic #'lambda)
       (generic #'lambda-memoized)
       (single #'lang-match-lambda*)
       (single #'lang-match-lambda-memoized*))))
  
    (define (transitive-closure orig)
    ;; Copy initial mapping:
    (let ([map (map (lambda (p) (list (car p) (cdr p))) orig)])
      ;; Extend the map list until nothing changes
      (let loop ()
        (let ([changed? #f])
          (for-each (lambda (pair)
                      (let ([mapping (cdr pair)])
                        (for-each (lambda (item)
                                    (let ([trans (ormap (lambda (transitive)
                                                          (and (not (memq transitive mapping))
                                                               transitive))
                                                        (cdr (assq item map)))])
                                      (when trans
                                        (append! pair (list trans))
                                        (set! changed? #t))))
                                  mapping)))
                    map)
          (when changed? (loop))))
      ;; Done
      map))
  
  (define-syntax (lambda-memoized stx)
    (syntax-case stx ()
      [(_ () body1 body ...)
       (syntax/loc stx (lambda () body1 body ...))]
      [(_ (arg) body1 body ...)
       (syntax/loc 
        stx 
        (let ([ht (make-hash-table 'weak)])
          (lambda (arg)
            (hash-table-get
             ht
             arg
             (lambda ()
               (let ([v (begin body1 body ...)])
                 (hash-table-put! ht arg v)
                 v))))))]
      [(_ (arg1 arg ...) body1 body ...)
       (syntax/loc
        stx
        (let ([memo (lambda-memoized (arg1) (lambda-memoized (arg ...) body1 body ...))])
          (lambda (arg1 arg ...)
            ((memo arg1) arg ...))))]))
  
  (define-syntax define-memoized
    (syntax-rules ()
      [(_ (f . args) body1 body ...)
       (define f (lambda-memoized args body1 body ...))]))


  ;; function-reduce*
  (define (function-reduce* reds expr done? max-steps)
    (cons 
     expr
     (if (or (zero? max-steps) (done? expr))
	 null
	 (let ([l (reduce reds expr)])
	   (cond
	    [(null? l) null]
	    [(= 1 (length l))
	     (function-reduce* reds (car l) done? (sub1 max-steps))]
	    [else
	     (error 'function-reduce* 
		    "found ~a possible steps from ~e"
		    (length l)
		    expr)])))))

  (define-struct multi-result (choices))

  ;; ----------------------------------------
  ;; Path exploration:
  
  (define-syntax (explore-results stx)
    (syntax-case stx ()
      [(_ (id) result-expr body-expr bes ...)
       #'(let ([try (lambda (id) body-expr bes ...)])
           (let ([r result-expr])
             (do-explore r try)))]))

  (define-syntax (explore-parallel-results stx)
    (syntax-case stx ()
      [(_ (list-id) result-list-expr body-expr bes ...)
       #'(let ([try (lambda (list-id) body-expr bes ...)])
           (let loop ([rs result-list-expr][es null])
             (if (null? rs)
                 (try (reverse es))
                 (do-explore 
                  (car rs)
                  (lambda (e)
                    (loop (cdr rs) (cons e es)))))))]))
  
  (define (do-explore r try)
    (cond
      [(multi-result? r)
       (let loop ([l (multi-result-choices r)])
         (if (null? l)
             #f
	     (let ([a ((car l))])
	       (if (multi-result? a)
		   (loop (append (multi-result-choices a)
				 (cdr l)))
		   (let ([v (try a)])
		     (if (not v)
			 (loop (cdr l))
			 (make-multi-result 
			  (append (if (multi-result? v)
				      (multi-result-choices v)
				      (list (lambda () v)))
				  (list (lambda () (loop (cdr l))))))))))))]
      [else (try r)]))

  (define (many-results l)
    (make-multi-result (map (lambda (v) (lambda () v)) l)))
  
  (define (first-result result)
    (let/ec k
      (explore-results (x) result
        (k x))))
  
  (provide
   define-memoized
   lambda-memoized
   lang-match-lambda
   lang-match-lambda-memoized
   lang-match-lambda*
   lang-match-lambda-memoized*
   explore-results
   explore-parallel-results)
  (provide/contract
   (function-reduce* ((listof red?) any/c (any/c . -> . boolean?) number?
		      . -> . (listof any/c)))
   (unique-names? ((listof symbol?) . -> . boolean?))
   (generate-string (-> string?))
   (all-of (any/c (any/c . -> . any) . -> . (listof any/c)))
   (transitive-closure ((listof pair?) . -> . (listof (listof any/c))))
   (many-results ((listof (lambda (x) (not (multi-result? x)))) . -> . any))
   (first-result (any/c . -> . any))))