From f23396de2e6ba223f3d69af7fbaebd5bd7d32dc8 Mon Sep 17 00:00:00 2001 From: Mike Gerwitz Date: Mon, 28 Aug 2017 00:45:34 -0400 Subject: [PATCH] prebirth: Abandon `define-block' in favor of `define' Turns out, I'll kill myself before writing a Prebirth compiler in a block-define-based Prebirth Lisp. So, let's degrade even further into a primitive Scheme. This is going down a dangerous path to simply implementing Scheme... Nonetheless, here I remove `define-block' in favor of a simple shorthand function definition `define', as is custom in Scheme. We will worry about block definitions later as metadata mapping to normal functions. --- build-aux/bootstrap/prebirth.js | 120 ++++++++++---------------------- 1 file changed, 35 insertions(+), 85 deletions(-) diff --git a/build-aux/bootstrap/prebirth.js b/build-aux/bootstrap/prebirth.js index b1cb739..25d5492 100644 --- a/build-aux/bootstrap/prebirth.js +++ b/build-aux/bootstrap/prebirth.js @@ -293,11 +293,11 @@ class Compiler /** * Compile AST into ECMAScript * - * Every block is mapped 1:1 to a function in ECMAScript. So, we just - * map all root children (which are expected to be block definitions) to - * functions. + * Every function is mapped 1:1 to a function in ECMAScript. So, we + * just map all root children (which are expected to be Scheme-style + * shorthand function definitions) to functions. * - * @param {Array} tree root of tree containing top-level block definitions + * @param {Array} tree root containing top-level function definitions */ compile( tree ) { @@ -309,57 +309,45 @@ class Compiler /** - * Compile block definition into a ES function definition + * Compile function definition into a ES function definition * - * This will fail if the given token is not a `define-block'. + * This will fail if the given token is not a `define'. * * @param {Object} t token * - * @return {string} compiled block definition + * @return {string} compiled function definition */ _cdfn( t ) { - this.assertApply( t, 'define-block' ); + this.assertApply( t, 'define' ); - // e.g. (define-block doc ((input ...)) body) - const [ , { value: name }, doc, desc, ...body ] = t; + // e.g. (define (foo ...) body) + const [ , [ { value: name }, ...params ], ...body ] = t; - const id = this._idFromName( name ); - const docstr = this._docstring( doc ); - const bodyjs = this._bodyToEs( body ); + const id = this._idFromName( name ); + const paramjs = this._paramsToEs( params ); + const bodyjs = this._bodyToEs( body ); - // this is the final format---each block becomes its own function - // definition - return `${docstr}\nfunction ${id}()\n{\n${bodyjs}\n};`; + // this is the final format---each function becomes its own function + // definition in ES + return `function ${id}(${paramjs})\n{\n${bodyjs}\n};`; } /** - * Compile docblock string + * Compile parameter list * - * This converts to the docstring T into a docblock. No annotations are - * generated---it is output verbatim. If the docstring is the empty - * string, then the empty string is returned. + * This simply takes the value of the symbol and outputs it (formatted), + * delimited by commas. * - * @param {string} t docstring token + * @param {Array} args token parameter list * - * @return {string} generated docblock or the empty string + * @return {string} compiled parameter list */ - _docstring( t ) + _paramsToEs( args ) { - if ( t.type !== 'string' ) { - throw TypeError( `Expected string docblock, but found ${t.type}` ); - } - - const doc = t.value; - - // don't bother with the docblock generation if we have nothing useful - if ( !doc ) { - return ""; - } - - // enclose in multi-line comment delimiters and prefix each line - return "/**\n" + doc.replace( /^/g, " * " ) + "\n */"; + return args.map( ({ value: name }) => this._idFromName( name ) ) + .join( ", " ); } @@ -382,7 +370,7 @@ class Compiler * This produces a 1:1 mapping of BODY s-expressions to ES statements, * recursively. The heavy lifting is done by `#_sexpToEs'. * - * @param {Array} body s-expressions representing block body + * @param {Array} body s-expressions representing function body * * @return {string} compiled BODY */ @@ -431,71 +419,35 @@ class Compiler case 'string': return `"${t.value}"`; - // symbols have the same concerns as block definitions: the + // symbols have the same concerns as function definitions: the // identifiers generated need to be ES-friendly case 'symbol': return this._idFromName( t.value ); default: - throw Error( "Cannot compile unknown token `${t.type}'" ); + throw Error( `Cannot compile unknown token \`${t.type}'` ); } } - // only support block form for now, and assume that `fn' is a - // string value (in the future, this doesn't have to be the - // case---fn should be able to be an arbitrary sexp) - const [ { value: fn }, argmap ] = t; - - if ( !this._isBlockForm( t ) ) { - throw Error( `\`${fn}' application is not in block form`) - } + // simple function application (fn ...args) + const [ { value: fn }, ...args ] = t; // convert all remaining symbols (after the symbol representing the // function application) into arguments by parsing their sexps or - // scalar values; we're not going to worry about mapping them for - // now; they will be compiled in the order in which they appear + // scalar values const idfn = this._idFromName( fn ); - const args = argmap.map( ([ , v ]) => this._sexpToEs( v ) ); - const argstr = args.join( ", " ); + const argstr = args.map( arg => this._sexpToEs( arg ) ).join( ", " ); - // make the dangerous assumption that arguments are ordered - // for now + // final function application return `${idfn}(${argstr})`; } - /** - * Determine whether T represents a block form - * - * Block form is an application of a block, which has a certain - * syntax. Specifically: `( ((key value) ...))'. - * - * @param {*} t hopefully a token list - * - * @return {boolean} whether T represents a block form - */ - _isBlockForm( t ) - { - // the first symbol is the function name, second is an sexp - // containing each of the key/value argument mappings - const [ fn, argmap ] = t; - - // enforce block id convention (at least for now) - const isblockid = /^<[^>]+>$/.test( fn.value ); - - return ( - Array.isArray( t ) - && isblockid - && Array.isArray( argmap ) - ); - } - - /** * Determine whether T is an application of a symbol NAME, or error * * @param {*} t hopefully a token or token list - * @param {string} name block name to assert against + * @param {string} name function name to assert against */ assertApply( t, name ) { @@ -554,10 +506,8 @@ class Compiler * * Here is an example Hello, World!: * - * (define-block - * "A simple 'Hello, World!' program." - * () - * ( ((message "Hello, world!")))) + * (define (hello x) + * (js:console "Hello," x, "!")) * * * ยน This term should invoke visuals of an abstract being entering existence