#lang scheme/base
(provide query/parameterize
query
solutions choice choice/mark choice/enum
choice/fail
cut)
(require "enum.ss"
"fail.ss"
srfi/41
scheme/control
scheme/match)
(define choice-tag (new-prompt))
(define-syntax shift/parameters
(syntax-rules ()
((_ () k expr)
(shift-at choice-tag k expr))
((_ parameters k expr)
(let ((ps (list . parameters)))
(apply (lambda (x . pvs)
(for ((p ps) (pv pvs)) (p pv))
x)
(let ((pvs (for/list ((p ps)) (p))))
(shift-at choice-tag _k
(let ((k (lambda (x) (_k (cons x pvs)))))
expr))))))))
(define-struct choicepoint (k enum mark?))
(define-struct result (cut? solution? value))
(define (no-driver . _) (error 'choice-no-driver))
(define current-choice (make-parameter no-driver))
(define (choice . vals) ((current-choice) (list->enum vals) #f))
(define (choice/mark . vals) ((current-choice) (list->enum vals) #t))
(define (choice/enum enum [mark? #f]) ((current-choice) enum mark?))
(define (choice/fail . _) (abort-current-continuation
choice-tag (lambda () (make-result #f #f #f))))
(define (cut . lst)
(abort-current-continuation
choice-tag (lambda ()
(cond
((null? lst) (make-result #t #f #f))
((null? (cdr lst)) (make-result #t #t (car lst)))
(else 'cut-nargs "~a" lst)))))
(define-struct choice-prog (thunk))
(define (choice-foldl/list fn init program [breadth-first? #f])
(define _cut_ (stream (lambda () (make-result #t #f #f))))
(let next ((paths (stream (choice-prog-thunk program)))
(marks '())
(state init))
(if (stream-null? paths)
state (let ((resume (stream-car paths))
(paths- (stream-cdr paths)))
(match (resume)
((struct choicepoint (k enum mark?))
(let ((thunks (stream-map
(lambda (val)
(lambda () (k val)))
(enum->stream enum))))
(if breadth-first?
(if (not mark?)
(next (stream-append thunks paths-) marks state)
(error 'breadth-first-with-mark))
(if (not mark?)
(next (stream-append paths- thunks) marks state)
(next
(stream-append thunks _cut_) (cons paths- marks) state)))))
((struct result (cut? solution? value))
(when (and cut? (null? marks))
(error 'cut-without-marks))
(let-values
(((cont? state)
(if solution?
(call-with-values
(lambda () (apply fn value state))
(lambda (cont? . state+)
(values cont? state+)))
(values #t state)))
((paths marks)
(if cut?
(values (car marks) (cdr marks))
(values paths- marks))))
(if cont?
(next paths marks state)
state)))
(ins (error 'choice-foldl "invalid instruction: ~a" ins)))))))
(define (solutions prompt-thunk [breadth-first #f])
(lambda (fn . init)
(apply values (choice-foldl/list fn init prompt-thunk breadth-first))))
(define-syntax-rule (query/parameterize ((p pv) ...) . body)
(make-choice-prog
(lambda ()
(prompt-at
choice-tag
(parameterize
((current-fail choice/fail)
(current-choice
(lambda (enum mark?)
(shift/parameters
(p ...) k
(make-choicepoint k enum mark?))))
(p pv) ...)
(make-result #f #t (begin . body)))))))
(define-syntax-rule (query . body)
(query/parameterize () . body))