#lang s-exp "../lang.ss"
(require "anormal-frag-helpers.ss")
(require "anormalize.ss")
(require "../../collects/moby/runtime/stx.ss")
(define frag-prepend "f~a_~a")
(define statement-name "statement~a")
(define-struct finfo (return fragments gensym))
(define-struct split (keep current move))
(define (get-bound-id defn)
(if (stx-begins-with? defn 'define)
(if (stx:atom? (second (stx-e defn)))
(stx-e (second (stx-e defn)))
(stx-e (first (stx-e (second (stx-e defn))))))
(error 'get-bound-id (format "expected definition, found: ~a" defn))))
(define (split-def-list def-list)
(cond
[(empty? def-list) (make-split empty #f empty)]
[(and (cons? def-list)
(stx-begins-with? (first def-list) 'define))
(let ([components (stx-e (first def-list))])
(if (or (stx:list? (second components))
(equal? (stx->datum (third components))
'(box 'undefined)))
(let ([rec-return (split-def-list (rest def-list))])
(make-split (cons (first def-list)
(split-keep rec-return))
(split-current rec-return)
(split-move rec-return)))
(make-split (list (first def-list))
(first def-list)
(rest def-list))))]
[else (error 'split-def-list
(format "expected list of defininitions, found: ~a" def-list))]))
(define (fragment-help expr args name frag-counter)
(if (stx:atom? expr)
(make-finfo expr empty frag-counter)
(let* ([expr-list (stx-e expr)]
[first-elt (stx-e (first expr-list))])
(cond
[(equal? first-elt 'local)
(let* ( [split-defs (split-def-list (stx-e (second expr-list)))]
[new-bound-ids (map get-bound-id (split-keep split-defs))]
[rec-rest
(if (false? (split-current split-defs))
(fragment-help (third expr-list)
(append new-bound-ids args)
name
frag-counter)
(fragment-help
(datum->stx
false
(list 'define
(cons (string->symbol (format frag-prepend
frag-counter
name))
(append new-bound-ids args))
(if (empty? (split-move split-defs))
(third expr-list)
(list 'local
(split-move split-defs)
(third expr-list))))
(stx-loc expr))
args
name
(add1 frag-counter)))]
[more-fragments? (stx-begins-with? (finfo-return rec-rest) 'define)])
(make-finfo (datum->stx
false
(list 'local
(apply append
(map get-fragments
(split-keep split-defs)))
(if more-fragments?
(second (stx-e (finfo-return rec-rest)))
(finfo-return rec-rest)))
(stx-loc expr))
(if more-fragments?
(cons (finfo-return rec-rest)
(finfo-fragments rec-rest))
(finfo-fragments rec-rest))
(finfo-gensym rec-rest)))]
[(or (equal? first-elt 'begin)
(equal? first-elt 'and)
(equal? first-elt 'or))
(let* ([first-expr (fragment-help (second expr-list) args name frag-counter)]
[rec-rest
(fragment-help (datum->stx
false
(list 'define
(cons (string->symbol
(format frag-prepend
(finfo-gensym first-expr)
name))
args)
(if (empty? (rest (rest (rest expr-list))))
(third expr-list)
(cons (first expr-list)
(rest (rest expr-list)))))
(stx-loc expr))
args
name
(add1 (finfo-gensym first-expr)))])
(make-finfo (datum->stx false
(list (first expr-list)
(finfo-return first-expr)
(second (stx-e (finfo-return rec-rest))))
(stx-loc expr))
(append (finfo-fragments first-expr)
(cons (finfo-return rec-rest)
(finfo-fragments rec-rest)))
(finfo-gensym rec-rest)))]
[(equal? first-elt 'define)
(let* ([new-args (if (stx:list? (second expr-list))
(rest (stx->datum (second expr-list)))
empty)]
[filtered-args (append new-args
(filter (lambda (elt)
(not (member elt new-args)))
args))]
[rec-rest (fragment-help (third expr-list)
filtered-args
name
frag-counter)])
(make-finfo (datum->stx false
(list 'define
(second expr-list)
(finfo-return rec-rest))
(stx-loc expr))
(finfo-fragments rec-rest)
(finfo-gensym rec-rest)))]
[(equal? first-elt 'if)
(let* ([then-info (fragment-help (third expr-list) args name frag-counter)]
[else-info (fragment-help (fourth expr-list)
args
name
(finfo-gensym then-info))])
(make-finfo (datum->stx false
(list 'if
(second expr-list)
(finfo-return then-info)
(finfo-return else-info))
(stx-loc expr))
(append (finfo-fragments then-info)
(finfo-fragments else-info))
(finfo-gensym else-info)))]
[else (make-finfo expr empty frag-counter)]))))
(define (get-fragments expr)
(let* ([name (if (stx-begins-with? expr 'define)
(get-bound-id expr)
(string->symbol (format statement-name (gensym))))]
[frag-info (fragment-help expr empty name 0)])
(reverse (cons (finfo-return frag-info)
(finfo-fragments frag-info)))))
(define (fragment program)
(begin
(reset-gensym)
(datum->stx false
(apply append (map get-fragments
(stx-e (anormalize program))))
(stx-loc program))))
(provide/contract
[fragment (stx:list? . -> . stx:list?)])