Module activation; batch compilation
This commit is contained in:
parent
8c2729e3d8
commit
7be246a400
|
@ -1,8 +1,8 @@
|
||||||
import {
|
import {
|
||||||
isToken, isTokenType, replace, commaJoin, startPos, fixPos,
|
isToken, isTokenType, replace, commaJoin, startPos, fixPos, joinItems,
|
||||||
|
|
||||||
Items, Pattern, Templates, Substitution, TokenType,
|
Items, Pattern, Templates, Substitution, TokenType,
|
||||||
SourceMap, StringScanner, LaxReader, CodeWriter, TemplateFunction,
|
SourceMap, StringScanner, LaxReader, CodeWriter, TemplateFunction, Token,
|
||||||
} from '../syntax/index.js';
|
} from '../syntax/index.js';
|
||||||
import {
|
import {
|
||||||
FacetAction, Statement,
|
FacetAction, Statement,
|
||||||
|
@ -21,6 +21,9 @@ import {
|
||||||
reactStatement,
|
reactStatement,
|
||||||
bootStatement,
|
bootStatement,
|
||||||
stopStatement,
|
stopStatement,
|
||||||
|
Identifier,
|
||||||
|
activationImport,
|
||||||
|
ActivationImport,
|
||||||
} from './grammar.js';
|
} from './grammar.js';
|
||||||
import {
|
import {
|
||||||
BootProc,
|
BootProc,
|
||||||
|
@ -54,7 +57,18 @@ function receiverFor(s: FacetAction): Substitution {
|
||||||
return (s.implicitFacet) ? 'thisFacet.' : '.';
|
return (s.implicitFacet) ? 'thisFacet.' : '.';
|
||||||
}
|
}
|
||||||
|
|
||||||
export function expand(tree: Items, moduleType: ModuleType): Items {
|
export interface ActivationRecord {
|
||||||
|
activation: ActivationImport;
|
||||||
|
activationScriptId: Identifier;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ExpansionContext {
|
||||||
|
moduleType: ModuleType;
|
||||||
|
activationRecords: Array<ActivationRecord>;
|
||||||
|
hasBootProc: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function expand(tree: Items, ctx: ExpansionContext): Items {
|
||||||
const macro = new Templates();
|
const macro = new Templates();
|
||||||
|
|
||||||
function terminalWrap(t: TemplateFunction, isTerminal: boolean, body: Statement): Statement {
|
function terminalWrap(t: TemplateFunction, isTerminal: boolean, body: Statement): Statement {
|
||||||
|
@ -73,7 +87,7 @@ export function expand(tree: Items, moduleType: ModuleType): Items {
|
||||||
x(p, (v, t) => t`${receiverFor(v)}${f(v, t)}`);
|
x(p, (v, t) => t`${receiverFor(v)}${f(v, t)}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const walk = (tree: Items): Items => expand(tree, moduleType);
|
const walk = (tree: Items): Items => expand(tree, ctx);
|
||||||
const maybeWalk = (tree?: Items) : Items | undefined => (tree === void 0) ? tree : walk(tree);
|
const maybeWalk = (tree?: Items) : Items | undefined => (tree === void 0) ? tree : walk(tree);
|
||||||
|
|
||||||
xf(duringStatement, (s, t) => {
|
xf(duringStatement, (s, t) => {
|
||||||
|
@ -173,14 +187,29 @@ export function expand(tree: Items, moduleType: ModuleType): Items {
|
||||||
|
|
||||||
xf(reactStatement, (s, t) => t`addChildFacet(function (thisFacet) {${walk(s.body)}});`);
|
xf(reactStatement, (s, t) => t`addChildFacet(function (thisFacet) {${walk(s.body)}});`);
|
||||||
|
|
||||||
|
x(activationImport, (s, t) => {
|
||||||
|
const activationScriptId: Token = {
|
||||||
|
start: s.activationKeyword.start,
|
||||||
|
end: s.activationKeyword.end,
|
||||||
|
text: `__SYNDICATE__activationScript${'' + ctx.activationRecords.length}`,
|
||||||
|
type: TokenType.ATOM
|
||||||
|
};
|
||||||
|
ctx.activationRecords.push({ activation: s, activationScriptId });
|
||||||
|
return [];
|
||||||
|
}),
|
||||||
|
|
||||||
x(bootStatement, (s, t) => {
|
x(bootStatement, (s, t) => {
|
||||||
switch (moduleType) {
|
ctx.hasBootProc = true;
|
||||||
|
const activationStatements = ctx.activationRecords.map(({ activationScriptId: id }) =>
|
||||||
|
t`thisFacet.activate(${[id]}); `);
|
||||||
|
const body = t`${joinItems(activationStatements)}${walk(s)}`;
|
||||||
|
switch (ctx.moduleType) {
|
||||||
case 'es6':
|
case 'es6':
|
||||||
return t`export function ${BootProc}(thisFacet) {${walk(s)}}`;
|
return t`export function ${BootProc}(thisFacet) {${body}}`;
|
||||||
case 'require':
|
case 'require':
|
||||||
return t`module.exports.${BootProc} = function (thisFacet) {${walk(s)}};`;
|
return t`module.exports.${BootProc} = function (thisFacet) {${body}};`;
|
||||||
case 'global':
|
case 'global':
|
||||||
return t`function ${BootProc}(thisFacet) {${walk(s)}}`;
|
return t`function ${BootProc}(thisFacet) {${body}}`;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -195,34 +224,67 @@ export function compile(options: CompileOptions): CompilerOutput {
|
||||||
const moduleType = options.module ?? 'es6';
|
const moduleType = options.module ?? 'es6';
|
||||||
|
|
||||||
const start = startPos(inputFilename);
|
const start = startPos(inputFilename);
|
||||||
|
|
||||||
const scanner = new StringScanner(start, source);
|
const scanner = new StringScanner(start, source);
|
||||||
const reader = new LaxReader(scanner);
|
const reader = new LaxReader(scanner);
|
||||||
let tree = stripShebang(reader.readToEnd());
|
let tree = stripShebang(reader.readToEnd());
|
||||||
|
const end = tree.length > 0 ? tree[tree.length - 1].end : start;
|
||||||
|
|
||||||
let macro = new Templates();
|
let macro = new Templates();
|
||||||
|
|
||||||
const end = tree.length > 0 ? tree[tree.length - 1].end : start;
|
const ctx: ExpansionContext = {
|
||||||
|
moduleType,
|
||||||
|
activationRecords: [],
|
||||||
|
hasBootProc: false,
|
||||||
|
}
|
||||||
|
|
||||||
|
tree = expand(tree, ctx);
|
||||||
|
|
||||||
|
const ts = macro.template(fixPos(start));
|
||||||
|
const te = macro.template(fixPos(end));
|
||||||
|
|
||||||
|
if (ctx.hasBootProc) {
|
||||||
|
let bp;
|
||||||
|
switch (moduleType) {
|
||||||
|
case 'es6':
|
||||||
|
case 'global':
|
||||||
|
bp = BootProc;
|
||||||
|
break;
|
||||||
|
case 'require':
|
||||||
|
bp = te`module.exports.${BootProc}`;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
tree = te`${tree}\nif (typeof module !== 'undefined' && ((typeof require === 'undefined' ? {main: void 0} : require).main === module)) __SYNDICATE__.bootModule(${bp});`;
|
||||||
|
}
|
||||||
|
|
||||||
|
const activationImports = ctx.activationRecords.map(r => {
|
||||||
|
const a = r.activation;
|
||||||
|
const t = macro.template(a.activationKeyword.start);
|
||||||
|
switch (a.target.type) {
|
||||||
|
case 'import':
|
||||||
|
return t`import { ${BootProc} as ${[r.activationScriptId]} } from ${[a.target.moduleName]};\n`;
|
||||||
|
case 'expr':
|
||||||
|
return t`const ${[r.activationScriptId]} = (${a.target.moduleExpr}).${BootProc};\n`;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
tree = ts`${joinItems(activationImports)}${tree}`;
|
||||||
|
|
||||||
{
|
{
|
||||||
const runtime = options.runtime ?? '@syndicate-lang/core';
|
const runtime = options.runtime ?? '@syndicate-lang/core';
|
||||||
const t = macro.template(fixPos(start));
|
|
||||||
switch (moduleType) {
|
switch (moduleType) {
|
||||||
case 'es6':
|
case 'es6':
|
||||||
tree = t`import * as __SYNDICATE__ from ${JSON.stringify(runtime)};\n${tree}`;
|
tree = ts`import * as __SYNDICATE__ from ${JSON.stringify(runtime)};\n${tree}`;
|
||||||
break;
|
break;
|
||||||
case 'require':
|
case 'require':
|
||||||
tree = t`const __SYNDICATE__ = require(${JSON.stringify(runtime)});\n${tree}`;
|
tree = ts`const __SYNDICATE__ = require(${JSON.stringify(runtime)});\n${tree}`;
|
||||||
break;
|
break;
|
||||||
case 'global':
|
case 'global':
|
||||||
tree = t`const __SYNDICATE__ = ${runtime};\n${tree}`;
|
tree = ts`const __SYNDICATE__ = ${runtime};\n${tree}`;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tree = macro.template(fixPos(end))`${tree}\nif (typeof module !== 'undefined' && ((typeof require === 'undefined' ? {main: void 0} : require).main === module)) __SYNDICATE__.bootModule(${BootProc});`;
|
|
||||||
|
|
||||||
const cw = new CodeWriter(inputFilename);
|
const cw = new CodeWriter(inputFilename);
|
||||||
cw.emit(expand(tree, moduleType));
|
cw.emit(tree);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
text: cw.text,
|
text: cw.text,
|
||||||
|
|
|
@ -6,7 +6,7 @@ import {
|
||||||
|
|
||||||
scope, bind, seq, alt, upTo, atom, atomString, group, exec,
|
scope, bind, seq, alt, upTo, atom, atomString, group, exec,
|
||||||
repeat, option, withoutSpace, map, mapm, rest, discard,
|
repeat, option, withoutSpace, map, mapm, rest, discard,
|
||||||
value, succeed, fail, separatedBy, anything, not,
|
value, succeed, fail, separatedBy, anything, not, follows,
|
||||||
} from '../syntax/index.js';
|
} from '../syntax/index.js';
|
||||||
import * as Matcher from '../syntax/matcher.js';
|
import * as Matcher from '../syntax/matcher.js';
|
||||||
import { Path, Skeleton } from './internals.js';
|
import { Path, Skeleton } from './internals.js';
|
||||||
|
@ -244,6 +244,21 @@ export const bootStatement: Pattern<Statement> =
|
||||||
// Principal: Facet
|
// Principal: Facet
|
||||||
export const stopStatement = blockFacetAction(atom('stop'));
|
export const stopStatement = blockFacetAction(atom('stop'));
|
||||||
|
|
||||||
|
export interface ActivationImport {
|
||||||
|
activationKeyword: Identifier;
|
||||||
|
target: { type: 'import', moduleName: Token } | { type: 'expr', moduleExpr: Expr };
|
||||||
|
}
|
||||||
|
|
||||||
|
// Principal: none
|
||||||
|
export const activationImport: Pattern<ActivationImport> =
|
||||||
|
scope(o => seq(bind(o, 'activationKeyword', atom('activate')),
|
||||||
|
follows(alt<any>(seq(atom('import'),
|
||||||
|
upTo(seq(
|
||||||
|
map(atom(void 0, { tokenType: TokenType.STRING }),
|
||||||
|
n => o.target = { type: 'import', moduleName: n }),
|
||||||
|
statementBoundary))),
|
||||||
|
map(expr(), e => o.target = { type: 'expr', moduleExpr: e })))));
|
||||||
|
|
||||||
//---------------------------------------------------------------------------
|
//---------------------------------------------------------------------------
|
||||||
// Syntax of patterns over Value, used in endpoints
|
// Syntax of patterns over Value, used in endpoints
|
||||||
|
|
||||||
|
|
|
@ -56,6 +56,14 @@ export function not<T>(p: Pattern<any>, v: T): Pattern<T> {
|
||||||
return i => p(i) === null ? [v, i] : null;
|
return i => p(i) === null ? [v, i] : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function follows(p: Pattern<any>): Pattern<undefined> {
|
||||||
|
return i => {
|
||||||
|
const r = p(i);
|
||||||
|
if (r === null) return null;
|
||||||
|
return [r[0], i];
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export function seq(... patterns: Pattern<any>[]): Pattern<any> {
|
export function seq(... patterns: Pattern<any>[]): Pattern<any> {
|
||||||
return i => {
|
return i => {
|
||||||
for (const p of patterns) {
|
for (const p of patterns) {
|
||||||
|
|
|
@ -53,7 +53,7 @@ export class Templates {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function joinItems(itemss: Items[], separator0: Substitution): Items {
|
export function joinItems(itemss: Items[], separator0: Substitution = ''): Items {
|
||||||
if (itemss.length === 0) return [];
|
if (itemss.length === 0) return [];
|
||||||
const separator = toItems(separator0, startPos(null));
|
const separator = toItems(separator0, startPos(null));
|
||||||
const acc = itemss[0];
|
const acc = itemss[0];
|
||||||
|
|
|
@ -68,6 +68,8 @@ export function _canonicalizeDataflowDependent(i: DataflowDependent): string {
|
||||||
return '' + i.id;
|
return '' + i.id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type ActivationScript = Script<void>;
|
||||||
|
|
||||||
export abstract class Dataspace {
|
export abstract class Dataspace {
|
||||||
nextId: ActorId = 0;
|
nextId: ActorId = 0;
|
||||||
index = new Skeleton.Index();
|
index = new Skeleton.Index();
|
||||||
|
@ -77,6 +79,7 @@ export abstract class Dataspace {
|
||||||
runnable: Array<Actor> = [];
|
runnable: Array<Actor> = [];
|
||||||
pendingTurns: Array<Turn>;
|
pendingTurns: Array<Turn>;
|
||||||
actors: IdentityMap<number, Actor> = new IdentityMap();
|
actors: IdentityMap<number, Actor> = new IdentityMap();
|
||||||
|
activations: IdentitySet<ActivationScript> = new IdentitySet();
|
||||||
|
|
||||||
constructor(bootProc: Script<void>) {
|
constructor(bootProc: Script<void>) {
|
||||||
this.pendingTurns = [new Turn(null, [new Spawn(null, bootProc, new Set())])];
|
this.pendingTurns = [new Turn(null, [new Spawn(null, bootProc, new Set())])];
|
||||||
|
@ -390,6 +393,23 @@ class DeferredTurn extends Action {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class Activation extends Action {
|
||||||
|
readonly script: ActivationScript;
|
||||||
|
readonly name: any;
|
||||||
|
|
||||||
|
constructor(script: ActivationScript, name: any) {
|
||||||
|
super();
|
||||||
|
this.script = script;
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
perform(ds: Dataspace, ac: Actor | null): void {
|
||||||
|
if (ds.activations.has(this.script)) return;
|
||||||
|
ds.activations.add(this.script);
|
||||||
|
ds.addActor(this.name, rootFacet => rootFacet.addStartScript(this.script), new Set(), ac);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export class Turn {
|
export class Turn {
|
||||||
readonly actor: Actor | null;
|
readonly actor: Actor | null;
|
||||||
readonly actions: Array<Action>;
|
readonly actions: Array<Action>;
|
||||||
|
@ -623,9 +643,9 @@ export class Facet {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ensureNonFacetSetup(what: string, keyword: string) {
|
ensureNonFacetSetup(what: string) {
|
||||||
if (!this.inScript) {
|
if (!this.inScript) {
|
||||||
throw new Error(`Cannot ${what} during facet setup; are you missing \`${keyword} { ... }\`?`);
|
throw new Error(`Cannot ${what} during facet setup; are you missing \`on start { ... }\`?`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -635,7 +655,7 @@ export class Facet {
|
||||||
}
|
}
|
||||||
|
|
||||||
send(body: any) {
|
send(body: any) {
|
||||||
this.ensureNonFacetSetup('`send`', 'on start');
|
this.ensureNonFacetSetup('`send`');
|
||||||
this.enqueueScriptAction(new Message(body));
|
this.enqueueScriptAction(new Message(body));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -645,15 +665,20 @@ export class Facet {
|
||||||
}
|
}
|
||||||
|
|
||||||
spawn(name: any, bootProc: Script<void>, initialAssertions?: Set) {
|
spawn(name: any, bootProc: Script<void>, initialAssertions?: Set) {
|
||||||
this.ensureNonFacetSetup('`spawn`', 'on start');
|
this.ensureNonFacetSetup('`spawn`');
|
||||||
this.enqueueScriptAction(new Spawn(name, bootProc, initialAssertions));
|
this.enqueueScriptAction(new Spawn(name, bootProc, initialAssertions));
|
||||||
}
|
}
|
||||||
|
|
||||||
deferTurn(continuation: Script<void>) {
|
deferTurn(continuation: Script<void>) {
|
||||||
this.ensureNonFacetSetup('`deferTurn`', 'on start');
|
this.ensureNonFacetSetup('`deferTurn`');
|
||||||
this.enqueueScriptAction(new DeferredTurn(this.wrap(continuation)));
|
this.enqueueScriptAction(new DeferredTurn(this.wrap(continuation)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
activate(script: ActivationScript, name?: any) {
|
||||||
|
this.ensureNonFacetSetup('`activate`');
|
||||||
|
this.enqueueScriptAction(new Activation(script, name ?? null));
|
||||||
|
}
|
||||||
|
|
||||||
scheduleScript(script: Script<void>, priority?: Priority) {
|
scheduleScript(script: Script<void>, priority?: Priority) {
|
||||||
this.actor.scheduleTask(this.wrap(script), priority);
|
this.actor.scheduleTask(this.wrap(script), priority);
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
//---------------------------------------------------------------------------
|
//---------------------------------------------------------------------------
|
||||||
|
|
||||||
import { Dataspace, Script } from './dataspace.js';
|
import { ActivationScript, Dataspace } from './dataspace.js';
|
||||||
|
|
||||||
export type StopHandler<D extends Dataspace> = (ds: D) => void;
|
export type StopHandler<D extends Dataspace> = (ds: D) => void;
|
||||||
|
|
||||||
|
@ -33,7 +33,7 @@ export class Ground extends Dataspace {
|
||||||
stopHandlers: Array<StopHandler<this>> = [];
|
stopHandlers: Array<StopHandler<this>> = [];
|
||||||
backgroundTaskCount = 0;
|
backgroundTaskCount = 0;
|
||||||
|
|
||||||
constructor(bootProc: Script<void>) {
|
constructor(bootProc: ActivationScript) {
|
||||||
super(function (rootFacet) { rootFacet.addStartScript(bootProc); });
|
super(function (rootFacet) { rootFacet.addStartScript(bootProc); });
|
||||||
if (typeof window !== 'undefined') {
|
if (typeof window !== 'undefined') {
|
||||||
window._ground = this;
|
window._ground = this;
|
||||||
|
@ -118,12 +118,14 @@ export class Ground extends Dataspace {
|
||||||
// if (k) k(g);
|
// if (k) k(g);
|
||||||
// }
|
// }
|
||||||
|
|
||||||
export function bootModule(bootProc: Script<void>): Ground {
|
export function bootModule(bootProc: ActivationScript): Ground {
|
||||||
const g = new Ground(bootProc);
|
const g = new Ground(bootProc);
|
||||||
if (typeof document !== 'undefined') {
|
Ground.laterCall(() => {
|
||||||
document.addEventListener('DOMContentLoaded', () => g.start());
|
if (typeof document !== 'undefined') {
|
||||||
} else {
|
document.addEventListener('DOMContentLoaded', () => g.start());
|
||||||
g.start();
|
} else {
|
||||||
}
|
g.start();
|
||||||
|
}
|
||||||
|
});
|
||||||
return g;
|
return g;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1 +1,2 @@
|
||||||
index.js
|
index.js
|
||||||
|
index.js.map
|
||||||
|
|
|
@ -0,0 +1,13 @@
|
||||||
|
<!doctype HTML>
|
||||||
|
<html>
|
||||||
|
<meta charset=utf-8>
|
||||||
|
<title>Demo</title>
|
||||||
|
<script src="node_modules/@syndicate-lang/core/dist/syndicate.js"></script>
|
||||||
|
<script src="index.js"></script>
|
||||||
|
<h1>Look in the JavaScript console for output.</h1>
|
||||||
|
<main id="main">
|
||||||
|
</main>
|
||||||
|
<script>
|
||||||
|
Syndicate.bootModule(Main.__SYNDICATE__bootProc);
|
||||||
|
</script>
|
||||||
|
</html>
|
|
@ -4,9 +4,10 @@
|
||||||
"description": "Simple syndicatec example",
|
"description": "Simple syndicatec example",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"prepare": "npm run compile",
|
"prepare": "npm run compile && npm run rollup",
|
||||||
"compile": "npx syndicatec -o index.js --module global --runtime Syndicate index.syndicate.js",
|
"compile": "npx syndicatec -d lib -b src 'src/**/*.js'",
|
||||||
"clean": "rm -f index.js"
|
"rollup": "npx rollup -c",
|
||||||
|
"clean": "rm -rf lib/ index.js index.js.map"
|
||||||
},
|
},
|
||||||
"author": "Tony Garnock-Jones <tonyg@leastfixedpoint.com>",
|
"author": "Tony Garnock-Jones <tonyg@leastfixedpoint.com>",
|
||||||
"license": "GPL-3.0+",
|
"license": "GPL-3.0+",
|
||||||
|
@ -14,6 +15,8 @@
|
||||||
"@syndicate-lang/core": "file:../../../core"
|
"@syndicate-lang/core": "file:../../../core"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@syndicate-lang/syndicatec": "file:../.."
|
"@syndicate-lang/syndicatec": "file:../..",
|
||||||
|
"rollup": "^2.37.0",
|
||||||
|
"rollup-plugin-sourcemaps": "^0.6.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
import sourcemaps from 'rollup-plugin-sourcemaps';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
input: 'lib/index.js',
|
||||||
|
plugins: [sourcemaps()],
|
||||||
|
output: {
|
||||||
|
file: 'index.js',
|
||||||
|
format: 'umd',
|
||||||
|
name: 'Main',
|
||||||
|
sourcemap: true,
|
||||||
|
globals: {
|
||||||
|
'@syndicate-lang/core': 'Syndicate',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
15
packages/syndicatec/examples/javascript/index.syndicate.js → packages/syndicatec/examples/javascript/src/box.js
Executable file → Normal file
15
packages/syndicatec/examples/javascript/index.syndicate.js → packages/syndicatec/examples/javascript/src/box.js
Executable file → Normal file
|
@ -16,12 +16,7 @@
|
||||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
//---------------------------------------------------------------------------
|
//---------------------------------------------------------------------------
|
||||||
|
|
||||||
assertion type BoxState(value);
|
import { BoxState, SetBox, N } from './protocol.js';
|
||||||
message type SetBox(newValue);
|
|
||||||
|
|
||||||
const N = 100000;
|
|
||||||
|
|
||||||
console.time('box-and-client-' + N.toString());
|
|
||||||
|
|
||||||
boot {
|
boot {
|
||||||
spawn named 'box' {
|
spawn named 'box' {
|
||||||
|
@ -31,12 +26,4 @@ boot {
|
||||||
console.log('terminated box root facet');
|
console.log('terminated box root facet');
|
||||||
on message SetBox($v) => this.value = v;
|
on message SetBox($v) => this.value = v;
|
||||||
}
|
}
|
||||||
|
|
||||||
spawn named 'client' {
|
|
||||||
on asserted BoxState($v) => send message SetBox(v + 1);
|
|
||||||
on retracted BoxState(_) => console.log('box gone');
|
|
||||||
}
|
|
||||||
|
|
||||||
thisFacet.actor.dataspace.addStopHandler(() =>
|
|
||||||
console.timeEnd('box-and-client-' + N.toString()));
|
|
||||||
}
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
// @syndicate-lang/core, an implementation of Syndicate dataspaces for JS.
|
||||||
|
// Copyright (C) 2016-2021 Tony Garnock-Jones <tonyg@leastfixedpoint.com>
|
||||||
|
//
|
||||||
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU 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 General Public License
|
||||||
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
import { BoxState, SetBox } from './protocol.js';
|
||||||
|
|
||||||
|
boot {
|
||||||
|
spawn named 'client' {
|
||||||
|
on asserted BoxState($v) => send message SetBox(v + 1);
|
||||||
|
on retracted BoxState(_) => console.log('box gone');
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,27 @@
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
// @syndicate-lang/core, an implementation of Syndicate dataspaces for JS.
|
||||||
|
// Copyright (C) 2016-2021 Tony Garnock-Jones <tonyg@leastfixedpoint.com>
|
||||||
|
//
|
||||||
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU 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 General Public License
|
||||||
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
import { N } from './protocol.js';
|
||||||
|
activate import './box.js';
|
||||||
|
activate import './client.js';
|
||||||
|
|
||||||
|
console.time('box-and-client-' + N.toString());
|
||||||
|
boot {
|
||||||
|
thisFacet.actor.dataspace.addStopHandler(() =>
|
||||||
|
console.timeEnd('box-and-client-' + N.toString()));
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
// @syndicate-lang/core, an implementation of Syndicate dataspaces for JS.
|
||||||
|
// Copyright (C) 2016-2021 Tony Garnock-Jones <tonyg@leastfixedpoint.com>
|
||||||
|
//
|
||||||
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU 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 General Public License
|
||||||
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
export assertion type BoxState(value);
|
||||||
|
export message type SetBox(newValue);
|
||||||
|
|
||||||
|
export const N = 100000;
|
|
@ -16,6 +16,7 @@
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@syndicate-lang/compiler": "file:../compiler",
|
"@syndicate-lang/compiler": "file:../compiler",
|
||||||
"@syndicate-lang/core": "file:../core",
|
"@syndicate-lang/core": "file:../core",
|
||||||
|
"glob": "^7.1.6",
|
||||||
"yargs": "^16.2.0"
|
"yargs": "^16.2.0"
|
||||||
},
|
},
|
||||||
"bin": {
|
"bin": {
|
||||||
|
|
|
@ -2,6 +2,9 @@ import yargs from 'yargs/yargs';
|
||||||
import { Argv } from 'yargs';
|
import { Argv } from 'yargs';
|
||||||
|
|
||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
|
import path from 'path';
|
||||||
|
import { glob } from 'glob';
|
||||||
|
|
||||||
import { compile } from '@syndicate-lang/compiler';
|
import { compile } from '@syndicate-lang/compiler';
|
||||||
|
|
||||||
export type ModuleChoice = 'es6' | 'require' | 'global';
|
export type ModuleChoice = 'es6' | 'require' | 'global';
|
||||||
|
@ -9,7 +12,9 @@ const moduleChoices: ReadonlyArray<ModuleChoice> = ['es6', 'require', 'global'];
|
||||||
|
|
||||||
export type CommandLineArguments = {
|
export type CommandLineArguments = {
|
||||||
input: string | undefined;
|
input: string | undefined;
|
||||||
output: string | undefined;
|
outputDirectory?: string | undefined;
|
||||||
|
rootDirectory?: string;
|
||||||
|
rename: string | undefined;
|
||||||
map: boolean;
|
map: boolean;
|
||||||
mapExtension?: string;
|
mapExtension?: string;
|
||||||
runtime: string;
|
runtime: string;
|
||||||
|
@ -22,19 +27,62 @@ function checkModuleChoice<T>(t: T & { module: string }): T & { module: ModuleCh
|
||||||
throw new Error("Illegal --module argument: " + t.module);
|
throw new Error("Illegal --module argument: " + t.module);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function makeRenamer(outputDir: string,
|
||||||
|
rootDir: string,
|
||||||
|
renamePattern: string | undefined): (f: string) => string
|
||||||
|
{
|
||||||
|
const rewrites: Array<(f: string) => (string | null)> =
|
||||||
|
(renamePattern === void 0 ? [] : renamePattern.split(/,/)).map(p => {
|
||||||
|
const [ from, to ] = p.split(/:/);
|
||||||
|
let mFrom = /([^%]*)%([^%]*)/.exec(from);
|
||||||
|
let mTo = /([^%]*)%([^%]*)/.exec(to);
|
||||||
|
if (mFrom === null && mTo === null) {
|
||||||
|
return f => (f === from) ? to : null;
|
||||||
|
} else if (mFrom === null || mTo === null) {
|
||||||
|
throw new Error(`Invalid --rename pattern: ${JSON.stringify(p)}`);
|
||||||
|
} else {
|
||||||
|
const [fh, ft] = mFrom.slice(1);
|
||||||
|
const [th, tt] = mTo.slice(1);
|
||||||
|
return f =>
|
||||||
|
(f.startsWith(fh) && f.endsWith(ft))
|
||||||
|
? th + f.substring(fh.length, f.length - ft.length) + tt
|
||||||
|
: null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const relocate = (f: string) => path.join(outputDir, path.relative(rootDir, f));
|
||||||
|
return f => {
|
||||||
|
for (const rewrite of rewrites) {
|
||||||
|
const t = rewrite(f);
|
||||||
|
if (t !== null) return relocate(t);
|
||||||
|
}
|
||||||
|
return relocate(f);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export function main(argv: string[]) {
|
export function main(argv: string[]) {
|
||||||
const options: CommandLineArguments = checkModuleChoice(yargs(argv)
|
const options: CommandLineArguments = checkModuleChoice(yargs(argv)
|
||||||
.command('$0 [input]',
|
.command('$0 [input]',
|
||||||
'Compile a single file',
|
'Compile away Syndicate extensions',
|
||||||
yargs => yargs
|
yargs => yargs
|
||||||
.positional('input', {
|
.positional('input', {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
description: 'Input filename',
|
description: 'Input filename or glob (stdin if omitted)',
|
||||||
})
|
})
|
||||||
.option('output', {
|
.option('root-directory', {
|
||||||
alias: 'o',
|
alias: 'b',
|
||||||
type: 'string',
|
type: 'string',
|
||||||
description: 'Output filename (stdout if omitted)',
|
description: 'Root directory for input files',
|
||||||
|
default: '.',
|
||||||
|
})
|
||||||
|
.option('output-directory', {
|
||||||
|
alias: 'd',
|
||||||
|
type: 'string',
|
||||||
|
description: 'Output directory (if omitted: stdout if stdin as input, else cwd)',
|
||||||
|
})
|
||||||
|
.option('rename', {
|
||||||
|
type: 'string',
|
||||||
|
description: 'Rewrite input filenames',
|
||||||
|
default: '%.syndicate.js:%.js,%.syndicate.ts:%.ts',
|
||||||
})
|
})
|
||||||
.option('map', {
|
.option('map', {
|
||||||
type: 'boolean',
|
type: 'boolean',
|
||||||
|
@ -59,37 +107,52 @@ export function main(argv: string[]) {
|
||||||
argv => argv)
|
argv => argv)
|
||||||
.argv);
|
.argv);
|
||||||
|
|
||||||
const inputFilename = options.input ?? '/dev/stdin';
|
const rename = makeRenamer(options.outputDirectory ?? '',
|
||||||
const source = fs.readFileSync(inputFilename, 'utf-8');
|
options.rootDirectory ?? '.',
|
||||||
|
options.rename);
|
||||||
|
|
||||||
const { text, map } = compile({
|
const STDIN = '/dev/stdin';
|
||||||
source,
|
|
||||||
name: inputFilename,
|
|
||||||
runtime: options.runtime,
|
|
||||||
module: options.module,
|
|
||||||
});
|
|
||||||
map.sourcesContent = [source];
|
|
||||||
|
|
||||||
function mapDataURL() {
|
const inputGlob = options.input ?? STDIN;
|
||||||
const mapData = Buffer.from(JSON.stringify(map)).toString('base64')
|
const inputFilenames = glob.sync(inputGlob);
|
||||||
return `data:application/json;base64,${mapData}`;
|
|
||||||
}
|
for (const inputFilename of inputFilenames) {
|
||||||
|
const outputFilename =
|
||||||
|
(inputFilename === STDIN) ? '/dev/stdout' :
|
||||||
|
(inputFilename[0] === '/') ? (() => { throw new Error("Absolute input paths are not supported"); })() :
|
||||||
|
rename(inputFilename);
|
||||||
|
|
||||||
|
if (inputFilenames.indexOf(outputFilename) !== -1) {
|
||||||
|
throw new Error(`Output from ${JSON.stringify(inputFilename)} would trample on existing input file ${JSON.stringify(outputFilename)}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const source = fs.readFileSync(inputFilename, 'utf-8');
|
||||||
|
|
||||||
|
const { text, map } = compile({
|
||||||
|
source,
|
||||||
|
name: inputFilename,
|
||||||
|
runtime: options.runtime,
|
||||||
|
module: options.module,
|
||||||
|
});
|
||||||
|
map.sourcesContent = [source];
|
||||||
|
|
||||||
|
function mapDataURL() {
|
||||||
|
const mapData = Buffer.from(JSON.stringify(map)).toString('base64')
|
||||||
|
return `data:application/json;base64,${mapData}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (inputFilename !== STDIN) {
|
||||||
|
fs.mkdirSync(path.dirname(outputFilename), { recursive: true });
|
||||||
|
}
|
||||||
|
|
||||||
if (options.output !== void 0) {
|
|
||||||
if (!options.map) {
|
if (!options.map) {
|
||||||
fs.writeFileSync(options.output, text);
|
fs.writeFileSync(outputFilename, text);
|
||||||
} else if (options.mapExtension) {
|
} else if (options.mapExtension && inputFilename !== STDIN) {
|
||||||
const mapFilename = options.output + options.mapExtension;
|
const mapFilename = outputFilename + options.mapExtension;
|
||||||
fs.writeFileSync(options.output, text + `\n//# sourceMappingURL=${mapFilename}`);
|
fs.writeFileSync(outputFilename, text + `\n//# sourceMappingURL=${mapFilename}`);
|
||||||
fs.writeFileSync(mapFilename, JSON.stringify(map));
|
fs.writeFileSync(mapFilename, JSON.stringify(map));
|
||||||
} else {
|
} else {
|
||||||
fs.writeFileSync(options.output, text + `\n//# sourceMappingURL=${mapDataURL()}`);
|
fs.writeFileSync(outputFilename, text + `\n//# sourceMappingURL=${mapDataURL()}`);
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (!options.map) {
|
|
||||||
console.log(text);
|
|
||||||
} else {
|
|
||||||
console.log(text + `\n//# sourceMappingURL=${mapDataURL()}`);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue