prebirth: Initial function map
The Prebirth JS compiler is getting much more sophisticated than I had hoped, but I need something to work with here. This starts to get us in a good spot. There are other conveniences I will want. More to come. * build-aux/bootstrap/prebirth.js (Compiler#constructor): Add method, accept fnmap. (Compiler#_sexpToEs): Use fnmap as needed. (fnmap): Add initial map. Instantiate `Compiler' with fnmap.master
parent
61f6ba1470
commit
ef4f70bf96
|
@ -300,6 +300,21 @@ class Parser
|
||||||
*/
|
*/
|
||||||
class Compiler
|
class Compiler
|
||||||
{
|
{
|
||||||
|
/**
|
||||||
|
* Initialize with function map
|
||||||
|
*
|
||||||
|
* The function map will be used to map certain functions into other
|
||||||
|
* names or forms. For example, `js:console' may map to `console.log'
|
||||||
|
* and `if' to an `if' statement+expression.
|
||||||
|
*
|
||||||
|
* @param {Object} fnmap function map
|
||||||
|
*/
|
||||||
|
constructor( fnmap )
|
||||||
|
{
|
||||||
|
this._fnmap = fnmap;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Compile AST into ECMAScript
|
* Compile AST into ECMAScript
|
||||||
*
|
*
|
||||||
|
@ -484,10 +499,22 @@ class Compiler
|
||||||
// simple function application (fn ...args)
|
// simple function application (fn ...args)
|
||||||
const [ { value: fn }, ...args ] = t;
|
const [ { value: fn }, ...args ] = t;
|
||||||
|
|
||||||
|
const mapentry = this._fnmap[ fn ];
|
||||||
|
|
||||||
|
// if the fnmap contains a function entry, then it will handle the
|
||||||
|
// remaining processing
|
||||||
|
if ( mapentry && ( typeof mapentry === 'function' ) ) {
|
||||||
|
return mapentry(
|
||||||
|
args,
|
||||||
|
this._sexpToEs.bind( this ),
|
||||||
|
this._bodyToEs.bind( this )
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// convert all remaining symbols (after the symbol representing the
|
// convert all remaining symbols (after the symbol representing the
|
||||||
// function application) into arguments by parsing their sexps or
|
// function application) into arguments by parsing their sexps or
|
||||||
// scalar values
|
// scalar values
|
||||||
const idfn = this._idFromName( fn, true );
|
const idfn = mapentry || this._idFromName( fn, true );
|
||||||
const argstr = args.map( arg => this._sexpToEs( arg ) ).join( ", " );
|
const argstr = args.map( arg => this._sexpToEs( arg ) ).join( ", " );
|
||||||
|
|
||||||
// final function application
|
// final function application
|
||||||
|
@ -496,6 +523,54 @@ class Compiler
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Function aliases and special forms
|
||||||
|
*
|
||||||
|
* This map allows for a steady transition---items can be removed as they
|
||||||
|
* are written in Prebirth Lisp. This should give us a sane (but still
|
||||||
|
* simple) environment with which we can start to self-host.
|
||||||
|
*
|
||||||
|
* String values are simple function aliases. Function values take over the
|
||||||
|
* compilation of that function and allow for defining special forms. The
|
||||||
|
* first argument to the function is the list of raw arguments (not yet
|
||||||
|
* compiled); the second argument is `Compiler#_sexpToEs'; and the third is
|
||||||
|
* `Compiler#bodyToEs'.
|
||||||
|
*
|
||||||
|
* These are by no means meant to be solid implementations.
|
||||||
|
*
|
||||||
|
* @type {Object}
|
||||||
|
*/
|
||||||
|
const fnmap = {
|
||||||
|
'js:console': 'console.log',
|
||||||
|
|
||||||
|
// simple if statement with optional else
|
||||||
|
'if': ( [ pred, t, f ], stoes ) =>
|
||||||
|
`if (${stoes(pred)}) {${stoes(t)};} ` +
|
||||||
|
( ( f === undefined ) ? '' : `else {${stoes(f)};}` ),
|
||||||
|
|
||||||
|
// (let ((binding val) ...) ...body), compiled as a self-executing
|
||||||
|
// function which allows us to easily represent the return value of the
|
||||||
|
// entire expression while maintaining local scope
|
||||||
|
'let*': ( [ bindings, ...body ], stoes, btoes ) =>
|
||||||
|
"(function(){\n" +
|
||||||
|
bindings
|
||||||
|
.map( ([ x, val ]) => ` const ${stoes(x)} = ${stoes(val)};\n` )
|
||||||
|
.join( '' ) +
|
||||||
|
btoes( body ) + "\n" +
|
||||||
|
" })()",
|
||||||
|
|
||||||
|
// similar to the above, but variables cannot reference one-another
|
||||||
|
'let': ( [ bindings, ...body ], stoes, btoes ) =>
|
||||||
|
"(function(" +
|
||||||
|
bindings.map( ([ x ]) => stoes( x ) ).join( ", " ) +
|
||||||
|
"){\n" +
|
||||||
|
btoes( body ) + "\n" +
|
||||||
|
"})(" +
|
||||||
|
bindings.map( ([ , val ]) => stoes( val ) ).join( ", " ) +
|
||||||
|
")",
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Prebirth was originally intended to be run via the command line using
|
* Prebirth was originally intended to be run via the command line using
|
||||||
|
@ -510,7 +585,7 @@ class Compiler
|
||||||
}
|
}
|
||||||
|
|
||||||
const p = new Parser();
|
const p = new Parser();
|
||||||
const c = new Compiler();
|
const c = new Compiler( fnmap );
|
||||||
|
|
||||||
const fs = require( 'fs' );
|
const fs = require( 'fs' );
|
||||||
const src = fs.readFileSync( '/dev/stdin' ).toString();
|
const src = fs.readFileSync( '/dev/stdin' ).toString();
|
||||||
|
|
Loading…
Reference in New Issue