#lang s-exp "../lang.ss"
(require "anormal-frag-helpers.ss")
(require "../../collects/moby/runtime/stx.ss")
(require "box-local-defs.ss")
(require "../toplevel.ss")
(require "../env.ss")
(define temp-begin "temp~a")
(define higher-order-prims '(andmap argmax argmin build-list build-string compose
filter foldl foldr map memf ormap quicksort sort))
(define other-prims '(quote set!))
(define prims (make-hash))
(define (reset-prims prim-hash) (set! prims prim-hash))
(define (get-struct-defs program)
(filter (lambda (statement) (and (cons? statement)
(equal? (first statement) 'define-struct)))
program))
(define (generate-prims program language)
(let* ([prim-hash (make-hash)]
[add-key (lambda (key) (if (member key higher-order-prims)
(void)
(hash-set! prim-hash key #t)))])
(begin
(for-each add-key other-prims)
(for-each add-key (env-keys (get-toplevel-env language)))
(map (lambda (struct-def) (for-each add-key (get-struct-procs struct-def)))
(get-struct-defs program))
prim-hash)))
(define (primitive-expr? expr)
(or (stx:atom? expr)
(hash-ref prims (stx-e (first (stx-e expr))) #f)))
(define (gen-temp-symbol num)
(string->symbol (format temp-begin num)))
(define (fold-anormal-help expr)
(let ([reversed-result
(foldl (lambda (an-expr rest-info)
(let ([rec-info (anormal-help an-expr)])
(make-linfo (cons (linfo-return rec-info)
(linfo-return rest-info))
(append (reverse (linfo-raise rec-info))
(linfo-raise rest-info)))))
(make-linfo empty empty)
expr)])
(make-linfo (reverse (linfo-return reversed-result))
(reverse (linfo-raise reversed-result)))))
(define (anormal-help expr)
(if (stx:atom? expr)
(make-linfo expr empty)
(let* ([expr-list (stx-e expr)]
[first-elt (stx-e (first expr-list))])
(cond
[(equal? first-elt 'define)
(let ([body-info (anormal-help (third expr-list))])
(make-linfo (datum->stx false
(list (first expr-list)
(second expr-list)
(if (empty? (linfo-raise body-info))
(linfo-return body-info)
(list 'local
(linfo-raise body-info)
(linfo-return body-info))))
(stx-loc expr))
empty))]
[(equal? first-elt 'local)
(let ([defs (map make-anormal (stx-e (second expr-list)))]
[body-info (anormal-help (third expr-list))])
(make-linfo (datum->stx false
(list (first expr-list)
(append defs
(linfo-raise body-info))
(linfo-return body-info))
(stx-loc expr))
empty))]
[(equal? first-elt 'if)
(let ([condition (anormal-help (second expr-list))]
[then-clause (make-anormal (third expr-list))]
[else-clause (make-anormal (fourth expr-list))])
(if (primitive-expr? (linfo-return condition))
(make-linfo (datum->stx false
(list 'if
(linfo-return condition)
then-clause
else-clause)
(stx-loc expr))
(linfo-raise condition))
(let ([temp-symbol (gen-temp-symbol (gensym))])
(make-linfo (datum->stx false
(list 'if
temp-symbol
then-clause
else-clause)
(stx-loc expr))
(append (linfo-raise condition)
(list (datum->stx
false
(list 'define
temp-symbol
(linfo-return condition))
(stx-loc (second expr-list)))))))))]
[(or (equal? first-elt 'and)
(equal? first-elt 'or)
(equal? first-elt 'begin))
(make-linfo (datum->stx false
(map make-anormal expr-list)
(stx-loc expr))
empty)]
[(or (equal? first-elt 'quote)
(equal? first-elt 'define-struct)
(equal? first-elt 'require))
(make-linfo expr empty)]
[else
(let* ([arg-info (fold-anormal-help expr-list)]
[anormal-expr
(foldl (lambda (an-expr rest-args)
(if (primitive-expr? an-expr)
(make-linfo (cons an-expr (linfo-return rest-args))
(linfo-raise rest-args))
(let ([temp-symbol (gen-temp-symbol (gensym))])
(make-linfo (cons (datum->stx false
temp-symbol
(stx-loc an-expr))
(linfo-return rest-args))
(cons (datum->stx false
(list 'define
temp-symbol
an-expr)
(stx-loc an-expr))
(linfo-raise rest-args))))))
(make-linfo empty empty)
(linfo-return arg-info))])
(make-linfo (datum->stx false
(reverse (linfo-return anormal-expr))
(stx-loc expr))
(append (linfo-raise arg-info)
(reverse (linfo-raise anormal-expr)))))]))))
(define (make-anormal expr)
(if (stx:atom? expr)
expr
(let ([linfo-out (anormal-help expr)])
(if (empty? (linfo-raise linfo-out))
(linfo-return linfo-out)
(datum->stx false
(list 'local
(linfo-raise linfo-out)
(linfo-return linfo-out))
(stx-loc expr))))))
(define (anormalize program)
(let ([readied (ready-anormalize program)])
(begin
(reset-gensym)
(reset-prims (generate-prims (stx->datum readied) 'language-here))
(datum->stx false
(map make-anormal (stx-e readied))
(stx-loc readied)))))
(provide/contract
[anormalize (stx:list? . -> . stx:list?)])