#lang scheme/base
(require (only-in scheme/list empty? empty first rest)
(only-in scheme/runtime-path define-runtime-path)
(only-in scheme/path find-relative-path normalize-path path-only)
(only-in scheme/port copy-port)
(only-in scheme/file file->bytes make-directory*)
(only-in scheme/local local)
(only-in scheme/list second)
"compile-helpers.ss"
"compile-helpers-with-images.ss"
"program-resources.ss"
"collects/moby/runtime/stx.ss"
"collects/moby/runtime/binding.ss"
"compiler/beginner-to-javascript.ss"
"compiler/desugar.ss"
"compiler/analyzer.ss"
"compiler/helpers.ss"
"compiler/pinfo.ss"
"compiler/modules.ss")
(require (for-syntax (only-in scheme/base build-path #%app #%datum)))
(define-struct module-record (name path))
(define COLLECTS/MOBY/RUNTIME (build-path "collects" "moby" "runtime"))
(define COLLECTS/BOOTSTRAP (build-path "collects" "bootstrap"))
(define RUNTIME-MODULES
(list (make-module-record 'moby/runtime/runtime-modules
(build-path COLLECTS/MOBY/RUNTIME "runtime-modules.ss"))
(make-module-record 'moby/runtime/stx
(build-path COLLECTS/MOBY/RUNTIME "stx.ss"))
(make-module-record 'moby/runtime/binding
(build-path COLLECTS/MOBY/RUNTIME "binding.ss"))
(make-module-record 'moby/runtime/permission-struct
(build-path COLLECTS/MOBY/RUNTIME "permission-struct.ss"))
(make-module-record 'moby/runtime/effect-struct
(build-path COLLECTS/MOBY/RUNTIME "effect-struct.ss"))
(make-module-record 'moby/runtime/arity-struct
(build-path COLLECTS/MOBY/RUNTIME "arity-struct.ss"))
(make-module-record 'moby/runtime/error-struct
(build-path COLLECTS/MOBY/RUNTIME "error-struct.ss"))
(make-module-record 'moby/runtime/scheme-value-to-dom
(build-path COLLECTS/MOBY/RUNTIME "scheme-value-to-dom.ss"))
(make-module-record 'moby/runtime/dom-helpers
(build-path COLLECTS/MOBY/RUNTIME "dom-helpers.ss"))
(make-module-record 'moby/runtime/dom-parameters
(build-path COLLECTS/MOBY/RUNTIME "dom-parameters.ss"))
(make-module-record 'moby/runtime/error-struct-to-dom
(build-path COLLECTS/MOBY/RUNTIME "error-struct-to-dom.ss"))
(make-module-record 'bootstrap/bootstrap-teachpack
(build-path COLLECTS/BOOTSTRAP "bootstrap-teachpack.ss"))
(make-module-record 'bootstrap/cage-teachpack
(build-path COLLECTS/BOOTSTRAP "cage-teachpack.ss"))
(make-module-record 'bootstrap/function-teachpack
(build-path COLLECTS/BOOTSTRAP "function-teachpack.ss"))))
(define-runtime-path moby-runtime-path
"../support/js/runtime")
(define-runtime-path runtime-manifest-path
"../support/js/runtime/MANIFEST")
(define-runtime-path
compiler-path
"../support/js/runtime/compiler.js")
(define-runtime-path
standalone-compiler-parent-path
"../support/js/standalone-compiler")
(define-runtime-path
standalone-compiler-path
"../support/js/standalone-compiler/standalone-compiler.js")
(define-runtime-path
compressed-standalone-compiler-path
"../support/js/standalone-compiler/compressed-standalone-compiler.js")
(define-runtime-path base.js "../support/js/runtime/base.js")
(define-runtime-path jshashtable.js "../support/js/runtime/jshashtable.js")
(define-runtime-path types.js "../support/js/runtime/types.js")
(define-runtime-path kernel.js "../support/js/runtime/kernel.js")
(define-runtime-path read.js "../support/js/runtime/read.js")
(define-runtime-path compressed-runtime.js "../support/js/runtime/compressed-runtime.js")
(define-runtime-path whole-runtime.js "../support/js/runtime/whole-runtime.js")
(define (write-compressed-runtime)
(let* ([runtime-source (get-runtime-source)]
[compressed-runtime-source (compress-and-optimize-source runtime-source)])
(call-with-output-file whole-runtime.js
(lambda (op) (write-bytes runtime-source op))
#:exists 'replace)
(call-with-output-file compressed-runtime.js
(lambda (op) (write-bytes compressed-runtime-source op))
#:exists 'replace)
)
(void))
(define (write-compressed-compilers)
(let* ([runtime-source (file->bytes "../support/js/runtime/compiler.js")]
[compressed-runtime-source (compress-and-optimize-source runtime-source)])
(call-with-output-file "../support/js/runtime/compressed-compiler.js"
(lambda (op) (write-bytes compressed-runtime-source op))
#:exists 'replace))
(let* ([standalone-source (file->bytes standalone-compiler-path)]
[compressed-standalone-source (aggressively-compile-and-optimize-source standalone-source)])
(call-with-output-file compressed-standalone-compiler-path
(lambda (op) (write-bytes compressed-standalone-source op))
#:exists 'replace))
(void))
(define (compress-and-optimize-source bytes)
(google-closure-compile bytes)
(yui-compress bytes))
(define (aggressively-compile-and-optimize-source bytes)
(google-closure-compile bytes #:aggressive? #t))
(define (write-runtime-toplevel-bindings-descriptions)
(printf "Writing out the toplevel binding descriptions.~n")
(printf "Warning: if collects/moby/runtime/runtime-modules.ss does get changed, you may see an exception during the bootstrap. You will~n")
(printf "need to run the bootstrapper one more time to use the refreshed bindings.~n")
(let ([moby-runtime-module-bindings-description
`(define MOBY-RUNTIME-MODULE-BINDINGS
(list ,@
(for/list ([a-runtime-module (in-list RUNTIME-MODULES)])
(let* ([a-program+resources
(open-program/resources (module-record-path a-runtime-module))]
[desugared-program+pinfo
(desugar-program (program/resources-program a-program+resources)
(pinfo-update-current-module-path
(get-base-pinfo 'base)
(path->string
(find-relative-path
(normalize-path "collects")
(normalize-path
(path->string (module-record-path a-runtime-module)))))))]
[a-pinfo (program-analyze/pinfo (first desugared-program+pinfo)
(second desugared-program+pinfo))])
(list 'quote
(list
(module-record-name a-runtime-module)
(path->string (find-relative-path (normalize-path
"collects")
(normalize-path
(module-record-path a-runtime-module))))
(map (lambda (a-binding)
(binding->sexp
(localize-binding-to-module
a-binding
(module-record-name a-runtime-module))))
(pinfo-get-exposed-bindings a-pinfo))))))))])
(make-directory* COLLECTS/MOBY/RUNTIME)
(call-with-output-file "collects/moby/runtime/runtime-modules.ss"
(lambda (op)
(fprintf op "#lang s-exp \"../../../private/restricted-runtime-scheme.ss\"\n")
(fprintf op ";; This file is automagically generated and maintained by bootstrap-js-compiler.\n;; (in write-runtime-toplevel-bindings-descriptions)\n;; Do not edit this file by hand.\n")
(write moby-runtime-module-bindings-description op)
(newline op)
(display '(provide MOBY-RUNTIME-MODULE-BINDINGS) op))
#:exists 'replace)))
(define (write-runtime-library-modules)
(local [(define (get-js-target a-path-string)
(string-append
(substring (string-append "../support/js/runtime/" a-path-string)
0
(- (string-length
(string-append "../support/js/runtime/" a-path-string))
3))
".js"))]
(for ([a-runtime-module (in-list RUNTIME-MODULES)])
(printf "Booting the runtime module ~s~n" (module-record-name a-runtime-module))
(boot-compile-runtime-library
(module-record-name a-runtime-module)
(path->string (module-record-path a-runtime-module))
(get-js-target (path->string (module-record-path a-runtime-module)))))))
(define (boot-compile-runtime-library a-module-name a-program-path an-output-path)
(unless (directory-exists? (path-only an-output-path))
(make-directory* (path-only an-output-path)))
(printf "Writing out to ~s~n" an-output-path)
(call-with-output-file an-output-path
(lambda (op)
(display "// This is automatically generated by bootstrap-js-compiler.ss\n" op)
(display "// Please don't hand-edit this file.\n" op)
(display (phase-1-bootstrap-compile a-module-name a-program-path)
op))
#:exists 'replace))
(define (phase-1-bootstrap-compile a-module-name a-path-string)
(let* ([pinfo-without-debugging-location-emits
(pinfo-update-with-location-emits? (get-base-pinfo 'moby 'base)
#f)]
[path-resolver
(pinfo-module-path-resolver pinfo-without-debugging-location-emits)])
(compiled-program-main/expose-as-module
(program->compiled-program/pinfo (get-big-program a-path-string pinfo-without-debugging-location-emits)
(pinfo-update-module-path-resolver
(pinfo-update-current-module-path
pinfo-without-debugging-location-emits
(let ([result
(path->string
(find-relative-path
(normalize-path "collects")
(normalize-path a-path-string)))])
(printf "path is: ~s~n" result)
result))
(lambda (path parent-path)
(printf "Module path resolver. path is ~s, parent ~s~n" path parent-path)
(let ([retargeted-path
(path->string
(find-relative-path (normalize-path "collects")
(normalize-path
(module-path-join
a-path-string
path))))])
(printf "Retargeted to ~s~n" retargeted-path)
(path-resolver retargeted-path "")))))
a-module-name)))
(define (write-compiler)
(printf "Writing out the compiler~n")
(boot-compile-runtime-library 'moby/compiler "compiler/beginner-to-javascript.ss" compiler-path)
(unless (directory-exists? standalone-compiler-parent-path)
(make-directory standalone-compiler-parent-path))
(call-with-output-file standalone-compiler-path
(lambda (op)
(display "// This is the standalone compiler.\n" op)
(display "// It's been automatically generated by bootstrap-js-compiler.ss\n" op)
(display "// Please don't hand-edit this file.\n" op)
(copy-path-to-port jshashtable.js op)
(copy-path-to-port types.js op)
(copy-path-to-port kernel.js op)
(copy-path-to-port "../support/js/runtime/collects/moby/runtime/stx.js" op)
(copy-path-to-port read.js op)
(copy-path-to-port compiler-path op)
(display (phase-1-bootstrap-compile "compiler/beginner-to-javascript.ss") op)
(display "function listToArray(aList) {
var anArray = [];
while (!aList.isEmpty()) {
anArray.push(aList.first());
aList = aList.rest();
}
return anArray;
}
var aPinfo = get_dash_base_dash_pinfo(plt.types.Symbol.makeInstance('moby'));"
op)
(display "// compileScheme: string -> (array string (arrayof string))\n" op)
(display "
function compileScheme(s) {
var _permissionStruct = plt.Kernel.invokeModule('moby/runtime/permission-struct');
var _compiler = plt.Kernel.invokeModule('moby/compiler');
var exprs = plt.reader.readSchemeExpressions(s, 'standalone');
var compiledProgram =
_compiler.EXPORTS['program->compiled-program/pinfo'](exprs, aPinfo);
var compiledSrc = _compiler.EXPORTS['compiled-program-main'](compiledProgram);
var permList = _compiler.EXPORTS['pinfo-permissions'](_compiler.EXPORTS['compiled-program-pinfo'](compiledProgram));
var perms = [];
while (!permList.isEmpty()) {
perms.push(_permissionStruct.EXPORTS['permission->string'](permList.first()));
permList = permList.rest();
}
return [compiledSrc, perms];
}
this['compileScheme'] = compileScheme;
"
op))
#:exists 'replace))
(define (get-big-program a-path a-pinfo)
(let* ([modules (find-transitive-required-modules a-path a-pinfo)]
[big-program (apply append (map (lambda (p)
(remove-requires
(read-program/forget-resources p)
a-path
p
a-pinfo))
modules))])
big-program))
(define (module-needs-inclusion? a-path a-pinfo)
(let ([path-resolver (pinfo-module-path-resolver a-pinfo)]
[module-resolver (pinfo-module-resolver a-pinfo)])
(not
(and (module-name? (path-resolver a-path ""))
(module-binding? (module-resolver (path-resolver a-path "")))))))
(define (find-transitive-required-modules a-path a-pinfo)
(let ()
(unique
(let loop ([a-path a-path])
(let ([new-paths
(filter (lambda (a-subpath)
(module-needs-inclusion? (path->string (find-relative-path
(normalize-path "collects")
(normalize-path
(module-path-join a-path a-subpath))))
a-pinfo))
(get-require-paths (read-program/forget-resources a-path)
(path-only a-path)))])
(cond
[(empty? new-paths)
(list a-path)]
[else
(append
(apply append
(map (lambda (a-subpath)
(loop (path->string (build-path (path-only a-path) a-subpath))))
new-paths))
(list a-path))]))))))
(define (read-program/forget-resources a-path)
(call-with-program/resources
a-path
(lambda (a-program/resources)
(program/resources-program a-program/resources))))
(define (get-require-paths a-program base-path)
(cond
[(empty? a-program)
empty]
[(library-require? (first a-program))
(append (map (lambda (x)
(let ([a-path (stx-e x)])
a-path))
(rest (stx-e (first a-program))))
(get-require-paths (rest a-program) base-path))]
[else
(get-require-paths (rest a-program) base-path)]))
(define (remove-requires a-program parent-path a-subpath a-pinfo)
(apply
append
(map (lambda (top-level)
(cond [(stx-begins-with? top-level 'require)
(cond
[(module-needs-inclusion? (path->string
(find-relative-path (normalize-path "collects")
(normalize-path (module-path-join
parent-path
(second (stx->datum top-level))))))
a-pinfo)
(printf " DEBUG: erasing the require statement ~s in ~s~n"
(stx->datum top-level)
a-subpath)
(list)]
[else
(printf " DEBUG: preserving the require statement ~s in ~s~n"
(stx->datum top-level)
a-subpath)
(list top-level)
(let ([result
(list (datum->stx `(require ,(second (stx->datum top-level)))
(stx-loc top-level)))])
(printf "Rewritten to ~s~n" (stx->datum (first result)))
result)])]
[else
(list top-level)]))
a-program)))
(define (unique elts)
(let ([ht (make-hash)])
(let loop ([elts elts])
(cond
[(empty? elts)
empty]
[(hash-ref ht (first elts) #f)
(loop (rest elts))]
[else
(hash-set! ht (first elts) #t)
(cons (first elts)
(loop (rest elts)))]))))
(define (get-runtime-source)
(call-with-input-file runtime-manifest-path
(lambda (ip)
(apply bytes-append
(for/list ([line (in-lines ip)])
(let ([fip (open-input-file (build-path moby-runtime-path line))])
(bytes-append (file->bytes (build-path moby-runtime-path line))
#"\n")))))))
(define (call-with-program/resources path f)
(f (open-program/resources path)))
(define (copy-path-to-port path outp)
(call-with-input-file path
(lambda (ip)
(copy-port ip outp))))
(write-runtime-toplevel-bindings-descriptions)
(write-runtime-library-modules)
(write-compressed-runtime)
(write-compiler)
(write-compressed-compilers)
(provide/contract
[write-compiler (-> any)]
[write-compressed-runtime (-> any)])