Reusable compiler
This commit is contained in:
parent
6408493ea3
commit
2ff3067fad
|
@ -1,4 +1,79 @@
|
||||||
#!/usr/bin/env node
|
#!/usr/bin/env node
|
||||||
|
|
||||||
import { main } from '../lib/compiler/main.js';
|
const yargs = require('yargs/yargs');
|
||||||
main(process.argv);
|
const { hideBin } = require('yargs/helpers');
|
||||||
|
const argv =
|
||||||
|
yargs(hideBin(process.argv))
|
||||||
|
.completion()
|
||||||
|
.command('$0 [input]', 'Compile a single file', (yargs) => {
|
||||||
|
yargs
|
||||||
|
.positional('input', {
|
||||||
|
type: 'string',
|
||||||
|
description: 'Input filename',
|
||||||
|
})
|
||||||
|
.option('output', {
|
||||||
|
alias: 'o',
|
||||||
|
type: 'string',
|
||||||
|
description: 'Output filename (stdout if omitted)',
|
||||||
|
default: null,
|
||||||
|
})
|
||||||
|
.option('map', {
|
||||||
|
type: 'boolean',
|
||||||
|
description: 'Generate source maps',
|
||||||
|
default: true,
|
||||||
|
})
|
||||||
|
.option('map-extension', {
|
||||||
|
type: 'string',
|
||||||
|
description: 'Extension (e.g. ".map") to add to source map files; if omitted, source maps are generated inline',
|
||||||
|
default: null,
|
||||||
|
})
|
||||||
|
.option('runtime', {
|
||||||
|
type: 'string',
|
||||||
|
description: 'Path to require or import to get the Syndicate runtime',
|
||||||
|
default: '@syndicate/core',
|
||||||
|
})
|
||||||
|
.option('module', {
|
||||||
|
type: 'string',
|
||||||
|
description: 'es6 | require | global',
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.argv;
|
||||||
|
|
||||||
|
const fs = require('fs');
|
||||||
|
const { compile } = require('../dist/syndicate.js').Compiler;
|
||||||
|
|
||||||
|
// console.log(argv);
|
||||||
|
|
||||||
|
const inputFilename = 'input' in argv ? argv.input : '/dev/stdin';
|
||||||
|
const source = fs.readFileSync(inputFilename, 'utf-8');
|
||||||
|
|
||||||
|
const { text, map } = compile({
|
||||||
|
source,
|
||||||
|
name: inputFilename,
|
||||||
|
runtime: argv.runtime,
|
||||||
|
module: argv.module,
|
||||||
|
});
|
||||||
|
map.sourcesContent = [source];
|
||||||
|
|
||||||
|
function mapDataURL() {
|
||||||
|
const mapData = Buffer.from(JSON.stringify(map)).toString('base64')
|
||||||
|
return `data:application/json;base64,${mapData}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (argv.output !== null) {
|
||||||
|
if (!argv.map) {
|
||||||
|
fs.writeFileSync(argv.output, text);
|
||||||
|
} else if (argv.mapExtension) {
|
||||||
|
const mapFilename = argv.output + argv.mapExtension;
|
||||||
|
fs.writeFileSync(argv.output, text + `\n//# sourceMappingURL=${mapFilename}`);
|
||||||
|
fs.writeFileSync(mapFilename, JSON.stringify(map));
|
||||||
|
} else {
|
||||||
|
fs.writeFileSync(argv.output, text + `\n//# sourceMappingURL=${mapDataURL()}`);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!argv.map) {
|
||||||
|
console.log(text);
|
||||||
|
} else {
|
||||||
|
console.log(text + `\n//# sourceMappingURL=${mapDataURL()}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -20,7 +20,8 @@
|
||||||
"types": "lib/index.d.ts",
|
"types": "lib/index.d.ts",
|
||||||
"author": "Tony Garnock-Jones <tonyg@leastfixedpoint.com>",
|
"author": "Tony Garnock-Jones <tonyg@leastfixedpoint.com>",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"preserves": "0.4.0"
|
"preserves": "0.4.0",
|
||||||
|
"yargs": "^16.2.0"
|
||||||
},
|
},
|
||||||
"bin": {
|
"bin": {
|
||||||
"syndicatec": "./bin/syndicatec.js"
|
"syndicatec": "./bin/syndicatec.js"
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import * as fs from 'fs';
|
|
||||||
import * as S from '../syntax/index.js';
|
import * as S from '../syntax/index.js';
|
||||||
import { Substitution } from '../syntax/index.js';
|
import { Substitution } from '../syntax/index.js';
|
||||||
import * as G from './grammar.js';
|
import * as G from './grammar.js';
|
||||||
|
@ -13,17 +12,41 @@ export function stripShebang(items: S.Items): S.Items {
|
||||||
return items;
|
return items;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function main(argv: string[]) {
|
export interface CompileOptions {
|
||||||
let [ inputFilename ] = argv.slice(2);
|
source: string,
|
||||||
inputFilename = inputFilename ?? '/dev/stdin';
|
name?: string,
|
||||||
const source = fs.readFileSync(inputFilename, 'utf-8');
|
runtime?: string,
|
||||||
|
module?: 'es6' | 'require' | 'global',
|
||||||
|
global?: string,
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CompilerOutput {
|
||||||
|
text: string,
|
||||||
|
map: S.SourceMap,
|
||||||
|
}
|
||||||
|
|
||||||
|
export function compile(options: CompileOptions): CompilerOutput {
|
||||||
|
const inputFilename = options.name ?? '/dev/stdin';
|
||||||
|
const source = options.source;
|
||||||
|
const moduleType = options.module ?? 'es6';
|
||||||
|
|
||||||
const scanner = new S.StringScanner(S.startPos(inputFilename), source);
|
const scanner = new S.StringScanner(S.startPos(inputFilename), source);
|
||||||
const reader = new S.LaxReader(scanner);
|
const reader = new S.LaxReader(scanner);
|
||||||
let tree = stripShebang(reader.readToEnd());
|
let tree = stripShebang(reader.readToEnd());
|
||||||
let macro = new S.Templates();
|
let macro = new S.Templates();
|
||||||
|
|
||||||
tree = macro.template()`import * as __SYNDICATE__ from '@syndicate/core';\n${tree}`;
|
const runtime = options.runtime ?? '@syndicate/core';
|
||||||
|
switch (moduleType) {
|
||||||
|
case 'es6':
|
||||||
|
tree = macro.template()`import * as __SYNDICATE__ from ${JSON.stringify(runtime)};\n${tree}`;
|
||||||
|
break;
|
||||||
|
case 'require':
|
||||||
|
tree = macro.template()`const __SYNDICATE__ = require(${JSON.stringify(runtime)});\n${tree}`;
|
||||||
|
break;
|
||||||
|
case 'global':
|
||||||
|
tree = macro.template()`const __SYNDICATE__ = ${runtime};\n${tree}`;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
let passNumber = 0;
|
let passNumber = 0;
|
||||||
let expansionNeeded = true;
|
let expansionNeeded = true;
|
||||||
|
@ -167,19 +190,29 @@ export function main(argv: string[]) {
|
||||||
s => macro.template()`addChildFacet(function (thisFacet) {${s.body}});`);
|
s => macro.template()`addChildFacet(function (thisFacet) {${s.body}});`);
|
||||||
expand(
|
expand(
|
||||||
G.bootStatement,
|
G.bootStatement,
|
||||||
s => macro.template()`export function ${BootProc}(thisFacet) {${s}}`);
|
s => {
|
||||||
|
switch (moduleType) {
|
||||||
|
case 'es6':
|
||||||
|
return macro.template()`export function ${BootProc}(thisFacet) {${s}}`;
|
||||||
|
case 'global':
|
||||||
|
return macro.template()`module.exports.${BootProc} = function (thisFacet) {${s}};`;
|
||||||
|
case 'require':
|
||||||
|
return macro.template()`function ${BootProc}(thisFacet) {${s}}`;
|
||||||
|
}
|
||||||
|
});
|
||||||
expandFacetAction(
|
expandFacetAction(
|
||||||
G.stopStatement,
|
G.stopStatement,
|
||||||
s => macro.template()`_stop(function (thisFacet) {${s.body}});`);
|
s => macro.template()`_stop(function (thisFacet) {${s.body}});`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// console.log(`\n\n\n======================================== FINAL OUTPUT\n`);
|
// console.log(`\n\n\n======================================== FINAL OUTPUT\n`);
|
||||||
console.log(S.itemText(tree));
|
// console.log(S.itemText(tree));
|
||||||
|
|
||||||
const cw = new S.CodeWriter(inputFilename);
|
const cw = new S.CodeWriter(inputFilename);
|
||||||
cw.emit(tree);
|
cw.emit(tree);
|
||||||
fs.writeFileSync('/tmp/adhoc.syndicate', cw.text);
|
|
||||||
const mm = cw.map;
|
return {
|
||||||
mm.sourcesContent = [source];
|
text: cw.text,
|
||||||
fs.writeFileSync('/tmp/adhoc.syndicate.map', JSON.stringify(mm));
|
map: cw.map,
|
||||||
|
};
|
||||||
}
|
}
|
|
@ -1 +1,4 @@
|
||||||
export * as Grammar from './grammar.js';
|
export * as Grammar from './grammar.js';
|
||||||
|
export * as Internals from './internals.js';
|
||||||
|
export * as Codegen from './codegen.js';
|
||||||
|
export { compile, CompileOptions } from './codegen.js';
|
||||||
|
|
Loading…
Reference in New Issue