make/make.rkt
#lang typed/racket/base

(require "../compiler/compiler.rkt"
         "../compiler/il-structs.rkt"
         "../compiler/lexical-structs.rkt"
         "../compiler/compiler-structs.rkt"
         "../compiler/expression-structs.rkt"
         "../parameters.rkt"
         "../sets.rkt"
         "get-dependencies.rkt"
         "make-structs.rkt"
         racket/list
         racket/match)


(require/typed "../logger.rkt"
               [log-debug (String -> Void)])

(require/typed "../parser/parse-bytecode.rkt"
               [parse-bytecode (Any -> Expression)])

(require/typed "../get-module-bytecode.rkt"
               [get-module-bytecode ((U String Path Input-Port) -> Bytes)])


(provide make)





(: current-module-source-compiling-hook
   (Parameterof (Source -> Source)))
(define current-module-source-compiling-hook
  (make-parameter (lambda: ([s : Source]) s)))




   
(: get-ast-and-statements (Source -> (values (U False Expression)
                                             (Listof Statement))))
(define (get-ast-and-statements a-source)
  (cond
   [(StatementsSource? a-source)
    (values #f (StatementsSource-stmts a-source))]

   [(UninterpretedSource? a-source)
    (values #f '())]
   
   [(MainModuleSource? a-source)
    (let-values ([(ast stmts)
                  (get-ast-and-statements (MainModuleSource-source a-source))])
      (let ([maybe-module-locator (find-module-locator ast)])
        (cond
         [(ModuleLocator? maybe-module-locator)
          (values ast (append stmts
                             ;; Set the main module name
                              (list (make-PerformStatement
                                     (make-AliasModuleAsMain!
                                      maybe-module-locator)))))]
         [else
          (values ast stmts)])))]

   [else
    (let ([ast (get-ast a-source)])
      (define start-time (current-inexact-milliseconds))
      (define compiled-code (compile ast 'val next-linkage/drop-multiple))
      (define stop-time (current-inexact-milliseconds))
      (fprintf (current-timing-port) "  compile ast: ~a milliseconds\n" (- stop-time start-time))
      (values ast compiled-code))]))



(: get-ast ((U ModuleSource SexpSource) -> Expression))
(define (get-ast a-source)
  (define start-time (current-inexact-milliseconds))
  (: ast Expression)
  (define ast (cond
               [(ModuleSource? a-source)
                (parse-bytecode (ModuleSource-path a-source))]
               [(SexpSource? a-source)
                (let ([source-code-op (open-output-bytes)])
                  (write (SexpSource-sexp a-source) source-code-op)
                  (parse-bytecode
                   (open-input-bytes
                    (get-module-bytecode
                     (open-input-bytes
                      (get-output-bytes source-code-op))))))]))
  (define stop-time (current-inexact-milliseconds))
  (fprintf (current-timing-port) "  get-ast: ~a milliseconds\n" (- stop-time start-time))
  ast)






(: find-module-locator ((U Expression False) -> (U False ModuleLocator)))
;; Tries to look for the module locator of this expression.
(define (find-module-locator exp)
  (match exp
    [(struct Top ((? Prefix?)
                  (struct Module (name
                                  (and path (? ModuleLocator?))
                                  prefix
                                  requires
                                  provides
                                  code))))
     path]
    [else
     #f]))




(: make ((Listof Source) Configuration -> Void))
(define (make sources config)
  (parameterize ([current-seen-unimplemented-kernel-primitives
                  ((inst new-seteq Symbol))])

    (match config
      [(struct Configuration (wrap-source
                              should-follow-children?
                              on-module-statements
                              after-module-statements
                              after-last))


       (: follow-dependencies ((Listof Source) -> Void))
       (define (follow-dependencies sources)
         (define visited ((inst make-hash Source Boolean)))

         (: collect-new-dependencies
            (Source (U False Expression) -> (Listof Source)))
         (define (collect-new-dependencies this-source ast)
           (cond
            [(UninterpretedSource? this-source)
             (UninterpretedSource-neighbors this-source)]
            [else
             (cond
              [(eq? ast #f)
               empty]
              [(not (should-follow-children? this-source))
               empty]
              [else
               (let* ([dependent-module-names (get-dependencies ast)]
                      [paths
                       (foldl (lambda: ([mp : ModuleLocator]
                                        [acc : (Listof Source)])
                                       (let ([rp [ModuleLocator-real-path mp]])
                                         (cond
                                          ;; Ignore modules that are implemented by Whalesong.
                                          [((current-kernel-module-locator?)
                                            mp)
                                           acc]
                                          [(path? rp)
                                           (cons (make-ModuleSource rp) acc)]
                                          [else
                                           acc])))
                              '()
                              dependent-module-names)])
                 paths)])]))
         
         (let: loop : Void ([sources : (Listof Source) sources])
           (cond
            [(empty? sources)
             (after-last)]
            [(hash-has-key? visited (first sources))
             (loop (rest sources))]
            [else
             (fprintf (current-timing-port) "compiling a module ~a\n" (source-name (first sources)))
             (hash-set! visited (first sources) #t)
             (let*-values ([(this-source)
                            ((current-module-source-compiling-hook)
                             (first sources))]
                           [(ast stmts)
                            (get-ast-and-statements this-source)])
               (log-debug (format "visiting ~a\n" (source-name this-source)))
               (on-module-statements this-source ast stmts)
               (define start-time (current-inexact-milliseconds))
               (define new-dependencies (map wrap-source (collect-new-dependencies this-source ast)))
               (define end-time (current-inexact-milliseconds))
               (fprintf (current-timing-port) "  computing dependencies: ~a milliseconds\n" (- end-time start-time))
               (loop (append new-dependencies (rest sources)))
               (after-module-statements this-source))])))

       (follow-dependencies (map wrap-source sources))])))