#lang scheme/base
(require scheme/match
scheme/contract)
(provide
current-identifier=
current-string->identifier
current-number=
current-unpack
current-pack
bindings
variable?
number?
emit
make-staged-op
make-staged-binop
)
(define-struct variable (id))
(define-struct number (value))
(define (abstract-value? x)
(or (variable? x) (number? x)))
(provide/contract
[symbol->identifier (-> symbol? identifier?)]
[string->identifier (-> string? identifier?)]
[make-variable (-> any/c variable?)] [variable-id (-> variable? any/c)]
[make-number (-> any/c number?)] [number-value (-> number? any/c)]
[->number (-> abstract-value? (or/c #f any/c))]
[numbers/variables (-> (listof abstract-value?)
(values (listof number?)
(listof variable?)))]
[variable->expr (-> variable? (or/c #f any/c))]
[expr->variable (-> any/c (or/c #f variable?))]
)
(define current-postpone-error
(make-parameter (lambda () (error 'postpone-error))))
(define current-identifier= (make-parameter eq?))
(define current-string->identifier (make-parameter string->symbol))
(define current-number= (make-parameter =))
(define current-unpack (make-parameter (lambda (x) x)))
(define current-pack (make-parameter (lambda (x) x)))
(define (symbol->identifier x) (datum->syntax #f x))
(define (string->identifier x) (symbol->identifier (string->symbol x)))
(define (->number x)
(match x
((struct number (val)) val)
((struct variable (name))
(let ((expr (variable->expr x)))
(match expr
((struct number (val)) val)
(else #f))))))
(define (make= type? unpack param-type=)
(lambda (a b)
(and (type? a)
(type? b)
((param-type=) (unpack a) (unpack b)))))
(define variable= (make= variable? variable-id current-identifier=))
(define number= (make= number? number-value current-number=))
(define (ob= ob1 ob2)
(or (variable= ob1 ob2)
(number= ob1 ob2)))
(define tmp-count (make-parameter 0))
(define (make-temp)
(let ((n (tmp-count)))
(tmp-count (add1 n))
(make-variable
((current-string->identifier) (format "r~a" n)))))
(define bindings (make-parameter '()))
(define (print-expr x)
(printf ";; ~a\n" ((current-unpack) x)))
(define (emit statement)
(bindings (cons statement (bindings)))
(print-expr statement))
(define (expr= t1 t2)
(or (and (pair? t1)
(pair? t2)
(expr= (car t1) (car t2))
(expr= (cdr t1) (cdr t2)))
(and (null? t1) (null? t2) #t)
(ob= t1 t2)))
(define (expr->variable expr [env (bindings)])
(ormap (match-lambda
((list var expr_)
(and (expr= expr expr_) var)))
env))
(define (variable->expr var [env (bindings)])
(ormap (match-lambda
((list var_ expr)
(and (variable= var var_) expr)))
env))
(define (make-statement expr)
(let* ((tmp (make-temp))
(st (list tmp expr)))
(emit st)
tmp))
(define (staged-postpone-binop comm fn a b)
(let ((expr (cons fn (list a b)))
(expr/swap (and comm (cons fn (list b a)))))
(or (expr->variable expr)
(expr->variable expr/swap)
(make-statement expr))))
(define (make-staged-binop #:eval eval
#:postpone [postpone #f]
#:communitative [comm #f]
#:unit? [unit? #f]
#:->null [->null (lambda (x) #f)])
(lambda (x y)
(define (make-code)
(unless postpone ((current-postpone-error)))
(staged-postpone-binop comm
(make-variable postpone) x y))
(define (number-op x/y)
(lambda (n)
(if (and unit? (unit? n)) x/y
(or (->null n) (make-code)))))
(cond
((let ((nx (->number x))
(ny (->number y)))
(and nx ny (make-number (eval nx ny)))))
((->number x) => (number-op y))
((->number y) => (number-op x))
(else (make-code)))))
(define (numbers/variables lst)
(values (filter number? lst)
(filter (compose not number?) lst)))
(define (staged-postpone-op fn x)
(let ((expr (list fn x)))
(or (expr->variable expr)
(make-statement expr))))
(define (make-staged-op #:eval eval
#:postpone [postpone #f])
(lambda (x)
(let ((n (->number x)))
(if n
(make-number (eval n))
(begin
(unless postpone ((current-postpone-error)))
(staged-postpone-op (make-variable postpone) x))))))