#lang scheme/base
(require scheme/list
"../syntax/ast-core.ss")
(provide (all-defined-out))
(define-struct (FunctionDeclaration/hoisted FunctionDeclaration) (functions variables imports exports))
(define-struct (FunctionExpression/hoisted FunctionExpression) (functions variables imports exports))
(define-struct (BlockStatement/hoisted BlockStatement) (functions variables))
(define-struct hoisted (scope element) #:transparent)
(define (return x)
(lambda (k)
(k null x)))
(define (>>= m f)
(lambda (k)
(m (lambda (h1 x)
((f x) (lambda (h2 y)
(k (append h1 h2) y)))))))
(define (hoist scope element)
(lambda (k)
(k (list (make-hoisted scope element)) #f)))
(define (capture scope m)
(lambda (k)
(m (lambda (hs val)
(let-values ([(captured hs) (partition (lambda (h) (eq? (hoisted-scope h) scope)) hs)])
(k hs (list captured val)))))))
(define (execute m)
(m (lambda (hs v)
(let*-values ([(block-hoists hoists) (partition (lambda (h) (eq? (hoisted-scope h) 'block)) hs)])
(let ([block-hoists (map hoisted-element block-hoists)]
[elements (map hoisted-element hoists)])
(let*-values ([(let-vars let-funs) (partition Identifier? block-hoists)]
[(vars elements) (partition Identifier? elements)]
[(funs elements) (partition FunctionDeclaration/hoisted? elements)]
[(imports exports) (partition ImportDeclaration? elements)])
(values v let-vars let-funs vars funs imports exports)))))))
(define-syntax begin-hoist
(syntax-rules (<-)
[(begin-hoist stmt)
stmt]
[(begin-hoist (x <- stmt) stmts ...)
(>>= stmt (lambda (x) (begin-hoist stmts ...)))]
[(begin-hoist stmt stmts ...)
(>>= stmt (lambda (_) (begin-hoist stmts ...)))]))
(define (map/m f ls)
(let g ([ls ls])
(if (null? ls)
(return null)
(>>= (f (car ls))
(lambda (x)
(>>= (g (cdr ls))
(lambda (xs)
(return (cons x xs)))))))))
(define (filter-map/m f ls)
(let g ([ls ls])
(if (null? ls)
(return null)
(>>= (f (car ls))
(lambda (x)
(>>= (g (cdr ls))
(lambda (xs)
(return (if x (cons x xs) xs)))))))))
(define (append-map/m f ls)
(let g ([ls ls])
(if (null? ls)
(return null)
(>>= (f (car ls))
(lambda (x)
(>>= (g (cdr ls))
(lambda (xs)
(return (append x xs)))))))))