#lang scheme/base
(provide spec->sequence
(struct-out node) node-value node-assert!
(struct-out rule-class)
(struct-out defined)
(struct-out undefined)
emit
)
(define-struct rule-class (behaviour name))
(define code-stack (make-parameter #f))
(define value-stack (make-parameter #f))
(define (ppush! param val)
(param (cons val (param))))
(require "choice.ss")
(define (code-stack-print)
(for ((c (reverse (code-stack))))
(display (syntax->datum c)) (newline)))
(define (emit stx)
(ppush! code-stack stx))
(define (bind name value)
(ppush! value-stack (cons name value)))
(define (node-value node)
(let ((rec (assoc node (value-stack))))
(if rec (cdr rec) (make-undefined))))
(define (set-node-value! node value)
(value-stack (cons (cons node value) (value-stack))))
(define-struct rule (action nodes))
(define-struct node (name rules) #:mutable)
(define (node-register-rule! node rule)
(set-node-rules! node (cons rule (node-rules node))))
(define (rule-values r) (map node-value (rule-nodes r)))
(define (rule-bang! rule)
((rule-class-behaviour (rule-action rule))
(rule-nodes rule)))
(define (make-node-net names)
(for/list ((n names)) (make-node n '())))
(define (net-find-node net key)
(if (null? net) #f
(let ((r (car net)))
(if (bound-identifier=? key (node-name r))
r (net-find-node (cdr net) key)))))
(define (net-nodes net) net)
(define (node->string node)
(format "~a = ~a"
(syntax->datum (node-name node))
(node-value node)))
(define (net-print net)
(for ((n net))
(display (node->string n))
(newline)))
(define (net-constrain! net rule-class node-names)
(let* ((nodes (for/list ((n node-names)) (net-find-node net n)))
(rule (make-rule rule-class nodes)))
(for ((node nodes)) (node-register-rule! node rule))
rule))
(define (net-assert! net var [value (make-defined)])
(node-assert! (net-find-node net var) value))
(define (net-float! net var)
(set-node-value! (net-find-node net var) (make-undefined)))
(define (net->rules net)
(define h (make-hash))
(for ((n (net-nodes net)))
(for ((r (node-rules n)))
(hash-set! h r #t)))
(for/list ((k (in-hash-keys h))) k))
(define-struct defined ())
(define-struct undefined ())
(define (node-assert! node value)
(unless (undefined? (node-value node))
(error 'redefine-node "~a with ~a" (node->string node) value))
(set-node-value! node value) (for-each rule-bang! (node-rules node)))
(define (spec->net inputs outputs internal rules params)
(let ((net (make-node-net (append inputs outputs internal))))
(for ((r rules) (p params)) (net-constrain! net r p))
net))
(require srfi/41
"enum.ss")
(define (net-eval net inputs)
(define enum
(solutions
(query/parameterize
((code-stack '())
(value-stack '()))
(for ((i inputs)) (net-assert! net i))
(code-stack))))
(stream-car
(enum->stream enum)))
(define (spec->sequence inputs outputs . rest-spec)
(let ((net (apply spec->net inputs outputs rest-spec)))
#`(lambda #,inputs
(let* #,(reverse (net-eval net inputs))
(values #,@outputs)))))