2017-11-11 23:59:45 -05:00
|
|
|
|
/**
|
2018-02-08 23:40:00 -05:00
|
|
|
|
* Bootstrap procedure for Ulambda Scheme
|
2017-11-11 23:59:45 -05:00
|
|
|
|
*
|
2018-02-08 23:40:00 -05:00
|
|
|
|
* Copyright (C) 2017, 2018 Mike Gerwitz
|
2017-11-11 23:59:45 -05:00
|
|
|
|
*
|
2018-02-08 23:40:00 -05:00
|
|
|
|
* This file is part of Ulambda Scheme.
|
2017-11-11 23:59:45 -05:00
|
|
|
|
*
|
2018-02-08 23:40:00 -05:00
|
|
|
|
* Ulambda Scheme is free software: you can redistribute it and/or modify
|
2017-11-11 23:59:45 -05:00
|
|
|
|
* it under the terms of the GNU Affero General Public License as
|
|
|
|
|
* published by the Free Software Foundation, either version 3 of the
|
|
|
|
|
* License, or (at your option) any later version.
|
|
|
|
|
*
|
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
|
*
|
|
|
|
|
* You should have received a copy of the GNU Affero General Public License
|
|
|
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
|
*
|
2018-02-08 23:40:00 -05:00
|
|
|
|
* Ideally, the user should be able to bootstrap Ulambda Scheme with nothing
|
2017-11-11 23:59:45 -05:00
|
|
|
|
* more than what they already have installed on their computer, in the
|
2018-02-08 23:40:00 -05:00
|
|
|
|
* environment that Ulambda was designed to run in---the web browser.
|
|
|
|
|
* Node.js was used during official development, but that is a large system
|
|
|
|
|
* that should not be a necessary dependency---it should be needed only for
|
|
|
|
|
* convenience.
|
2017-11-11 23:59:45 -05:00
|
|
|
|
*
|
|
|
|
|
* To run this process on a local development environment using Node.js, see
|
|
|
|
|
* `../bootstrap.js'. To run in your web browser, see `../bootstrap.html'.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
'use strict';
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
2018-02-08 23:40:00 -05:00
|
|
|
|
* Bootstrap procedure for Ulambda Scheme
|
2017-11-11 23:59:45 -05:00
|
|
|
|
*
|
|
|
|
|
* This abstracts the bootstrap process in such a way that it can be run in
|
|
|
|
|
* any JavaScript environment. Notably, we need to support not only Node.js
|
|
|
|
|
* (which is convenient for development and automation), but also a web
|
|
|
|
|
* browser, which allows users to bootstrap using only their runtime
|
|
|
|
|
* environment and no additional tools.
|
|
|
|
|
*
|
|
|
|
|
* Prebirth and every compiler thereafter are designed to be able to be run
|
|
|
|
|
* from the command line, accepting source code on standard input. Such a
|
|
|
|
|
* concept does not exist in a browser environment, and therefore cannot
|
|
|
|
|
* exist here; there is an awkward abstraction to work around that.
|
|
|
|
|
*/
|
|
|
|
|
class Bootstrap
|
|
|
|
|
{
|
|
|
|
|
/**
|
|
|
|
|
* Initialize bootstrap process
|
|
|
|
|
*
|
|
|
|
|
* The file loader `getf' must accept a path to a file to load and
|
|
|
|
|
* return a Promise representing the contents of that file. The logger
|
|
|
|
|
* function `logf' must accept a string message and, as an optional
|
|
|
|
|
* argument an Error. `prebirth' should be `Prebirth' from
|
|
|
|
|
* `prebirth.js'.
|
|
|
|
|
*
|
|
|
|
|
* @param {function(string):Promise} getf file loader
|
|
|
|
|
* @param {function(string,Error=}} logf logger
|
|
|
|
|
* @param {Prebirth} prebirth Prebirth
|
|
|
|
|
*/
|
|
|
|
|
constructor( getf, logf, prebirth )
|
|
|
|
|
{
|
|
|
|
|
this._getf = getf;
|
|
|
|
|
this._logf = logf;
|
|
|
|
|
this._prebirth = prebirth;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Perform bootstrapping process
|
|
|
|
|
*
|
2018-02-08 23:40:00 -05:00
|
|
|
|
* This compiles each of the phases of Ulambda Scheme beginning with
|
2017-11-11 23:59:45 -05:00
|
|
|
|
* Prebirth. This will evolve in complexity as we continue to move
|
2018-08-10 23:22:14 -04:00
|
|
|
|
* forward.
|
2017-11-11 23:59:45 -05:00
|
|
|
|
*
|
|
|
|
|
* There is currently no final result from this method other than
|
|
|
|
|
* log output and an indication of success or failure; that'll change as
|
|
|
|
|
* we get further along and will produce the final compiler.
|
|
|
|
|
*
|
|
|
|
|
* @return {undefined} nothing yet.
|
|
|
|
|
*/
|
|
|
|
|
bootstrap()
|
|
|
|
|
{
|
|
|
|
|
this._strout( 'header' );
|
|
|
|
|
|
2018-08-10 23:22:14 -04:00
|
|
|
|
return this._birth()
|
|
|
|
|
.then( birth => this._rebirth( birth ) )
|
|
|
|
|
.catch( e => this._error( e ) )
|
|
|
|
|
.then( status =>
|
|
|
|
|
this._log( "=> " + this._doneMessage( status ) )
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Produce self-hosted Birth
|
|
|
|
|
*
|
|
|
|
|
* Prebirth will be used to compile Birth, which is written in
|
|
|
|
|
* Prebirth Lisp. Birth will then be used to compile itself, becoming
|
|
|
|
|
* self-hosting.
|
|
|
|
|
*
|
|
|
|
|
* This process is self-verifying: Birth compiled with both Prebirth and
|
|
|
|
|
* Birth itself should produce output that is identical (with regards to
|
|
|
|
|
* JavaScript's string representation). In practice, since Birth uses
|
|
|
|
|
* only ASCII, this amounts to verifying that the outputs are
|
|
|
|
|
* bytewise-identical.
|
|
|
|
|
*
|
|
|
|
|
* The result of this method will be a unary function that, given a
|
|
|
|
|
* Birth Lisp source string, will compile that string into JavaScript.
|
|
|
|
|
*
|
|
|
|
|
* @return {Promise<function(string):string>} Birth compiler
|
|
|
|
|
*/
|
|
|
|
|
_birth()
|
|
|
|
|
{
|
|
|
|
|
return this._loadPaths( [
|
2017-11-11 23:59:45 -05:00
|
|
|
|
[ "birth.scm", "Birth" ],
|
|
|
|
|
[ "libprebirth.js", "libprebirth" ],
|
|
|
|
|
] )
|
|
|
|
|
.then( ( [ scm, lib ] ) =>
|
|
|
|
|
{
|
|
|
|
|
this._strout( 'prebirthDesc' );
|
|
|
|
|
|
2018-08-10 23:22:14 -04:00
|
|
|
|
const preout = this._prebirth.compile( scm, lib );
|
|
|
|
|
|
|
|
|
|
return [ preout, scm, lib ];
|
2017-11-11 23:59:45 -05:00
|
|
|
|
} )
|
|
|
|
|
.then( ( [ birthjs, scm, lib ] ) =>
|
|
|
|
|
{
|
|
|
|
|
this._strout( 'prebirthComplete', birthjs.length );
|
|
|
|
|
this._strout( 'birthCompiled' );
|
|
|
|
|
this._strout( 'birthSelfCompiling' );
|
|
|
|
|
|
2018-08-10 23:22:14 -04:00
|
|
|
|
const birthf = this._makeCompiler( birthjs, {
|
|
|
|
|
"libprebirth.js": lib
|
2017-11-11 23:59:45 -05:00
|
|
|
|
} );
|
|
|
|
|
|
2018-08-10 23:22:14 -04:00
|
|
|
|
const birthout = birthf( scm );
|
|
|
|
|
|
|
|
|
|
this._verifyBirthOutput( birthout, birthjs );
|
|
|
|
|
|
|
|
|
|
return birthf;
|
|
|
|
|
} );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Verify that self-compiled Birth output BIRTHOUT matches that of
|
|
|
|
|
* Prebirth-compiled Birth BIRTHJS
|
|
|
|
|
*
|
|
|
|
|
* @param {string} birthout self-compiled Birth
|
|
|
|
|
* @param {string} birthjs Prebirth-compiled Birth
|
|
|
|
|
*
|
|
|
|
|
* @throws {Error} on non-match
|
|
|
|
|
*
|
|
|
|
|
* @return {undefined}
|
|
|
|
|
*/
|
|
|
|
|
_verifyBirthOutput( birthout, birthjs )
|
|
|
|
|
{
|
|
|
|
|
if ( birthout === '' ) {
|
|
|
|
|
throw Error( "Self-compilation yielded no output" );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
this._strout( 'birthVerify' );
|
2017-11-11 23:59:45 -05:00
|
|
|
|
|
2018-08-10 23:22:14 -04:00
|
|
|
|
if ( birthout !== birthjs ) {
|
|
|
|
|
this._strout( 'birthVerifyFail' );
|
2017-11-11 23:59:45 -05:00
|
|
|
|
|
2018-08-10 23:22:14 -04:00
|
|
|
|
throw Error(
|
|
|
|
|
"Birth self-compilation output does not match Prebirth!"
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
this._strout( 'birthVerifyOk' );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Create unary function wrapping the compiler JS with a stub
|
|
|
|
|
* filesystem FS
|
|
|
|
|
*
|
|
|
|
|
* The unary function accepts a source file which is then passed to the
|
|
|
|
|
* compiler via the stub filesystem on "/dev/stdin". The output of the
|
|
|
|
|
* compiler is returned as a string.
|
|
|
|
|
*
|
|
|
|
|
* The stub filesystem should contain the contents of all files
|
|
|
|
|
* dynamically loaded by the compiler JS. This abstraction allows the
|
|
|
|
|
* bootstrapping process to work in any environment without regards to
|
|
|
|
|
* whether a filesystem even exists, and regardless of whether loading
|
|
|
|
|
* is a synchronous or asynchronous operation.
|
|
|
|
|
*
|
|
|
|
|
* @param {string} js JavaScript code of compiler (to be eval'd)
|
|
|
|
|
* @param {Object} fs mapping of filename to content for stub filesystem
|
|
|
|
|
*
|
|
|
|
|
* @return {string} compiler output
|
|
|
|
|
*/
|
|
|
|
|
_makeCompiler( js, fs = {} )
|
|
|
|
|
{
|
|
|
|
|
const birth = new Function(
|
|
|
|
|
'let __fsinit = this.__fsinit;' +
|
|
|
|
|
'let require = this.require;' +
|
|
|
|
|
'let birthout = "";\n' +
|
|
|
|
|
'const console = { log: str => birthout = str + "\\n" };\n' +
|
|
|
|
|
js +
|
|
|
|
|
"return birthout;"
|
|
|
|
|
);
|
2017-11-11 23:59:45 -05:00
|
|
|
|
|
2018-08-10 23:22:14 -04:00
|
|
|
|
return scm =>
|
|
|
|
|
{
|
|
|
|
|
fs[ "/dev/stdin" ] = scm;
|
|
|
|
|
return birth.call( { __fsinit: fs } );
|
|
|
|
|
};
|
|
|
|
|
}
|
2017-11-11 23:59:45 -05:00
|
|
|
|
|
|
|
|
|
|
2018-08-10 23:22:14 -04:00
|
|
|
|
/**
|
|
|
|
|
* Compile Rebirth using Birth and yield unary compiler function
|
|
|
|
|
*
|
|
|
|
|
* This begins the recursive compilation of Rebirth, beginning with
|
|
|
|
|
* the first generation Re¹birth, using the self-hosted Birth. The
|
|
|
|
|
* first generation of Rebirth is written purely in Birth Lisp. The
|
|
|
|
|
* resulting compiler has more features than Birth, which is then used
|
|
|
|
|
* to compile itself again, producing a compiler with even more
|
|
|
|
|
* features. This process repeats until the output does not change.
|
|
|
|
|
*
|
|
|
|
|
* @param {function(string):string} birth Birth
|
|
|
|
|
*
|
|
|
|
|
* @return {Promise<function(string):string>} final Rebirth generation
|
|
|
|
|
*/
|
|
|
|
|
_rebirth( birth )
|
|
|
|
|
{
|
|
|
|
|
return this._loadPaths( [
|
|
|
|
|
[ "rebirth.scm", "Rebirth" ],
|
|
|
|
|
[ "rebirth/es.scm" ],
|
|
|
|
|
[ "rebirth/relibprebirth.scm" ],
|
|
|
|
|
[ "rebirth/macro.scm" ],
|
|
|
|
|
] ).then( ( [ scm, es, relibprebirth, macro ] ) =>
|
|
|
|
|
this._compileRebirth( birth, scm, {
|
|
|
|
|
"rebirth/es.scm": es,
|
|
|
|
|
"rebirth/relibprebirth.scm": relibprebirth,
|
|
|
|
|
"rebirth/macro.scm": macro,
|
2017-11-11 23:59:45 -05:00
|
|
|
|
} )
|
2018-08-10 23:22:14 -04:00
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Recursively compile Rebirth until two consecutive generations match
|
|
|
|
|
* and yield the unary compiler function for the final generation
|
|
|
|
|
*
|
|
|
|
|
* The first time this method is called, it should be called with Birth
|
|
|
|
|
* as the unary compiler function COMPILE. It should each time be
|
|
|
|
|
* provided with the Rebirth source code SCM and the necessary stub
|
|
|
|
|
* filesystem FS (these are identical for each recursive invocation of
|
|
|
|
|
* this method).
|
|
|
|
|
*
|
|
|
|
|
* Recursion terminates when the compiler COMPILE output matches that of
|
|
|
|
|
* the previous generation PREV, at which point the unary compiler
|
|
|
|
|
* function COMPILE will be yielded as the final generation (with the
|
|
|
|
|
* final generation number being N-1 to account for the duplicate).
|
|
|
|
|
*
|
|
|
|
|
* @param {function(string):string} compile compiler (Birth or Rebirth)
|
|
|
|
|
* @param {string} scm Rebirth source
|
|
|
|
|
* @param {Object} fs stub filesystem for Rebirth
|
|
|
|
|
* @param {number=} n target Rebirth generation id
|
|
|
|
|
* @param {string=} prev previous Rebirth generation
|
|
|
|
|
*
|
|
|
|
|
* @throws {Error} if compiler COMPILE produces no output
|
|
|
|
|
*
|
|
|
|
|
* @return {Promise<function(string):string>} final Rebirth generation
|
|
|
|
|
*/
|
|
|
|
|
_compileRebirth( compile, scm, fs, n = 1, prev = "" )
|
|
|
|
|
{
|
|
|
|
|
this._strout( 'rebirthCompiling', n );
|
|
|
|
|
|
|
|
|
|
const birthout = compile( scm );
|
|
|
|
|
|
|
|
|
|
if ( birthout === '' ) {
|
|
|
|
|
return Promise.reject(
|
|
|
|
|
Error( "Rebirth compilation yielded no output" )
|
2017-11-11 23:59:45 -05:00
|
|
|
|
);
|
2018-08-10 23:22:14 -04:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
this._strout( 'rebirthCompiled', n, birthout.length );
|
|
|
|
|
|
|
|
|
|
const rebirthf = this._makeCompiler( birthout, fs );
|
|
|
|
|
|
|
|
|
|
if ( birthout === prev ) {
|
|
|
|
|
this._strout( 'rebirthDone', ( n - 1 ) );
|
|
|
|
|
return Promise.resolve( compile );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// recurse, but just in case we're running in a browser, give a
|
|
|
|
|
// change to repaint the log (otherwise we'd just hang until every
|
|
|
|
|
// Rebirth is compiled)
|
|
|
|
|
return new Promise( accept =>
|
|
|
|
|
setTimeout( () => accept( this._compileRebirth(
|
|
|
|
|
rebirthf, scm, fs, ( n + 1 ), birthout
|
|
|
|
|
) ) )
|
|
|
|
|
);
|
2017-11-11 23:59:45 -05:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Produce a promise for the file contents of each of `path'
|
|
|
|
|
*
|
|
|
|
|
* See also `#_loadPath'.
|
|
|
|
|
*
|
|
|
|
|
* @param {Array<string>} paths file paths
|
|
|
|
|
*
|
|
|
|
|
* @return {Promise} resolved with file contents or failure
|
|
|
|
|
*/
|
|
|
|
|
_loadPaths( paths )
|
|
|
|
|
{
|
|
|
|
|
return Promise.all(
|
|
|
|
|
paths.map( ( [ path, desc ] ) =>
|
|
|
|
|
this._loadPath( path, desc )
|
|
|
|
|
)
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Produce a promise for the file contents of `path'
|
|
|
|
|
*
|
|
|
|
|
* This action is logged with the description `desc' and the length of
|
|
|
|
|
* the result.
|
|
|
|
|
*
|
|
|
|
|
* This uses the loader function provided via the constructor, which
|
|
|
|
|
* must return a Promise.
|
|
|
|
|
*
|
2018-08-10 23:22:14 -04:00
|
|
|
|
* @param {string} path file path
|
|
|
|
|
* @param {string=} desc file description for logging
|
2017-11-11 23:59:45 -05:00
|
|
|
|
*
|
|
|
|
|
* @return {Promise} promise of string file contents
|
|
|
|
|
*/
|
2018-08-10 23:22:14 -04:00
|
|
|
|
_loadPath( path, desc = "" )
|
2017-11-11 23:59:45 -05:00
|
|
|
|
{
|
|
|
|
|
this._strout( 'loadingf', desc, path );
|
|
|
|
|
|
|
|
|
|
return this._getf( path )
|
|
|
|
|
.then( data =>
|
|
|
|
|
{
|
|
|
|
|
this._strout( 'loadedf', path, data.length );
|
|
|
|
|
|
|
|
|
|
return data;
|
|
|
|
|
} );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Promise to log a string identified by `id'
|
|
|
|
|
*
|
|
|
|
|
* All given arguments in `args' will be passed to the function handling
|
|
|
|
|
* that identifier.
|
|
|
|
|
*
|
|
|
|
|
* @param {string} id string identifier (see `_strmap')
|
|
|
|
|
* @param {Array} args string arguments
|
|
|
|
|
*
|
|
|
|
|
* @return {Promise}
|
|
|
|
|
*/
|
|
|
|
|
_strout( id, ...args )
|
|
|
|
|
{
|
|
|
|
|
return Promise.resolve(
|
|
|
|
|
this._log( this._str.apply( this, arguments ) )
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Generate a string identified by `id'
|
|
|
|
|
*
|
|
|
|
|
* All given arguments in `args' will be passed to the function handling
|
|
|
|
|
* that identifier.
|
|
|
|
|
*
|
|
|
|
|
* @param {string} id string identifier (see `_strmap')
|
|
|
|
|
* @param {Array} args string arguments
|
|
|
|
|
*
|
|
|
|
|
* @return {string} generated string
|
|
|
|
|
*/
|
|
|
|
|
_str( id, ...args )
|
|
|
|
|
{
|
|
|
|
|
const strf = Bootstrap._strmap[ id ];
|
|
|
|
|
|
|
|
|
|
if ( strf === undefined ) {
|
|
|
|
|
throw Error( `Unknown strmap '${id}'` );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return strf.apply( null, args );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Log string using logger function
|
|
|
|
|
*
|
|
|
|
|
* @param {string} str string to log
|
|
|
|
|
*
|
|
|
|
|
* @return {undefined}
|
|
|
|
|
*/
|
|
|
|
|
_log( str )
|
|
|
|
|
{
|
|
|
|
|
this._logf( str );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Log error using logger function
|
|
|
|
|
*
|
|
|
|
|
* `e.message' will be used as the log string, with `e' itself being
|
|
|
|
|
* passed as the second argument to the logger function.
|
|
|
|
|
*
|
|
|
|
|
* @param {Error} e error
|
|
|
|
|
*
|
2018-09-10 23:04:53 -04:00
|
|
|
|
* @throws {Error} e
|
|
|
|
|
*
|
|
|
|
|
* @return {undefined}
|
2017-11-11 23:59:45 -05:00
|
|
|
|
*/
|
|
|
|
|
_error( e )
|
|
|
|
|
{
|
|
|
|
|
const str = this._str( 'fatal', e );
|
|
|
|
|
|
|
|
|
|
this._logf( str, e );
|
|
|
|
|
|
2018-09-10 23:04:53 -04:00
|
|
|
|
throw e;
|
2017-11-11 23:59:45 -05:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Return either success or failure message given `status'
|
|
|
|
|
*
|
|
|
|
|
* @param {boolean} status success/failure indicator
|
|
|
|
|
*
|
|
|
|
|
* @return {string} success/failure message
|
|
|
|
|
*/
|
|
|
|
|
_doneMessage( status )
|
|
|
|
|
{
|
|
|
|
|
return ( status === false )
|
|
|
|
|
? this._str( 'fail' )
|
|
|
|
|
: this._str( 'ok' );
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Output strings in an easily accessible map
|
|
|
|
|
*
|
|
|
|
|
* This both keeps the code a bit more easily comprehensible by removing
|
|
|
|
|
* large strings from procedural logic, and allows for future localization.
|
|
|
|
|
*
|
|
|
|
|
* We can do better once we get to a localization stage---Error messages
|
|
|
|
|
* aren't part of this map, for example.
|
|
|
|
|
*
|
|
|
|
|
* @type {string}
|
|
|
|
|
*/
|
|
|
|
|
Bootstrap._strmap = {
|
|
|
|
|
header: () =>
|
2018-08-10 23:22:14 -04:00
|
|
|
|
"\\\\ // \\\\\\\n" +
|
|
|
|
|
" \\\\ // \\\\\\\n" +
|
|
|
|
|
" \\\\// Ulambda \\\\\\\n" +
|
|
|
|
|
" \\\\\\ Scheme ///\n" +
|
|
|
|
|
" \\\\\\ ///\n" +
|
|
|
|
|
" \\\\\\ ///\n",
|
2017-11-11 23:59:45 -05:00
|
|
|
|
|
|
|
|
|
loadingf: ( desc, path ) =>
|
2018-08-10 23:22:14 -04:00
|
|
|
|
( desc )
|
|
|
|
|
? `Loading ${desc} from ${path}...`
|
|
|
|
|
: `Loading ${path}...`,
|
2017-11-11 23:59:45 -05:00
|
|
|
|
|
|
|
|
|
loadedf: ( path, len ) =>
|
|
|
|
|
`Loaded ${path} (len=${len}).`,
|
|
|
|
|
|
|
|
|
|
prebirthDesc: () =>
|
2018-08-10 23:22:14 -04:00
|
|
|
|
"+ Prebirth is a very basic Lisp dialect with a compiler\n" +
|
|
|
|
|
"+ implemented in ECMAScript. Birth is the same\n" +
|
|
|
|
|
"+ compiler, but re-implemented in Prebirth Lisp.",
|
2017-11-11 23:59:45 -05:00
|
|
|
|
|
|
|
|
|
prebirthComplete: ( len ) =>
|
2018-08-10 23:22:14 -04:00
|
|
|
|
`Birth compilation complete (len=${len}).`,
|
2017-11-11 23:59:45 -05:00
|
|
|
|
|
|
|
|
|
birthCompiled: () =>
|
2018-08-10 23:22:14 -04:00
|
|
|
|
"+ Birth has been compiled with Prebirth. Since Birth is\n" +
|
|
|
|
|
"+ a re-implementation of Prebirth, it can now be used\n" +
|
|
|
|
|
"+ to compile itself.",
|
2017-11-11 23:59:45 -05:00
|
|
|
|
|
|
|
|
|
birthSelfCompiling: () =>
|
2018-08-10 23:22:14 -04:00
|
|
|
|
"Self-compiling Birth...",
|
2017-11-11 23:59:45 -05:00
|
|
|
|
|
|
|
|
|
birthVerify: () =>
|
2018-08-10 23:22:14 -04:00
|
|
|
|
"Verifying self-compilation output...",
|
2017-11-11 23:59:45 -05:00
|
|
|
|
|
|
|
|
|
birthVerifyFail: () =>
|
|
|
|
|
"\n" +
|
|
|
|
|
"The self-compilation of Birth yielded output\n" +
|
|
|
|
|
"that differs from Prebirth's compilation of Birth.\n" +
|
|
|
|
|
"This verification step is a self-test to ensure\n" +
|
|
|
|
|
"consistency between the two implementations.\n\n" +
|
|
|
|
|
"Unfortunately, to fix this, you need to hack\n" +
|
|
|
|
|
"Prebirth and/or Birth. Please report a bug!",
|
|
|
|
|
|
|
|
|
|
birthVerifyOk: () =>
|
2018-08-10 23:22:14 -04:00
|
|
|
|
"Birth output matches that of Prebirth.\n" +
|
|
|
|
|
"+ We are now bootstrapped using a very primitive\n" +
|
|
|
|
|
"+ Birth Lisp. Birth can now be used to compile the\n" +
|
|
|
|
|
"+ next generation of bootstrap compilers, Rebirth.",
|
|
|
|
|
|
|
|
|
|
rebirthCompiling: n =>
|
|
|
|
|
"Compiling Re" + Bootstrap._supmap[ n ] + "birth...",
|
|
|
|
|
|
|
|
|
|
rebirthCompiled: ( n, len ) =>
|
|
|
|
|
( n > 1 ) ? `Compilation complete (len=${len}).` :
|
|
|
|
|
`+ The first generation of Rebirth (Re¹birth) has been\n` +
|
|
|
|
|
`+ compiled using Birth (len=${len}). The next step is\n` +
|
|
|
|
|
`+ to have Re¹birth build itself, producing Re²birth.\n` +
|
|
|
|
|
`+ This will repeat, each time producing a compiler with\n` +
|
|
|
|
|
`+ additional features capable of compiling the next\n` +
|
|
|
|
|
`+ generation. This process will end once two\n` +
|
|
|
|
|
`+ consecutive generations yield identical output.`,
|
|
|
|
|
|
|
|
|
|
rebirthDone: n =>
|
|
|
|
|
"+ Rebirth stopped changing after Re" + Bootstrap._supmap[ n ] +
|
|
|
|
|
"birth, so that\n" +
|
|
|
|
|
"+ generation will serve as our final one. The last\n" +
|
|
|
|
|
"+ step is to use it to compile Ulambda.",
|
2017-11-11 23:59:45 -05:00
|
|
|
|
|
|
|
|
|
fatal: ( e ) =>
|
|
|
|
|
"\n\n!!! " + e.toString() + "\n\n" +
|
|
|
|
|
"Something has gone terribly wrong!\n" +
|
|
|
|
|
"See the console for a stack trace.\n\n",
|
|
|
|
|
|
|
|
|
|
ok: () =>
|
2018-08-10 23:22:14 -04:00
|
|
|
|
"Bootstrap successful (but not yet complete)!",
|
2017-11-11 23:59:45 -05:00
|
|
|
|
|
|
|
|
|
fail: () =>
|
|
|
|
|
"Bootstrap failed.",
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
2018-08-10 23:22:14 -04:00
|
|
|
|
/**
|
|
|
|
|
* Map of number to Unicode superscript
|
|
|
|
|
*
|
|
|
|
|
* This may be implemented as either a string or an array; the notation
|
|
|
|
|
* _supmap[n] will work the same in either case.
|
|
|
|
|
*
|
|
|
|
|
* @type {string}
|
|
|
|
|
*/
|
|
|
|
|
Bootstrap._supmap = "⁰¹²³⁴⁵⁶⁷⁸⁹";
|
|
|
|
|
|
|
|
|
|
|
2017-11-11 23:59:45 -05:00
|
|
|
|
// for use in a CommonJS (e.g. Node.js) environment
|
|
|
|
|
if ( typeof module !== 'undefined' ) {
|
|
|
|
|
module.exports = Bootstrap;
|
|
|
|
|
}
|