2021-01-18 22:11:53 +00:00
|
|
|
import {
|
2021-01-19 23:52:40 +00:00
|
|
|
isToken, isTokenType, replace, commaJoin, startPos, fixPos, joinItems,
|
2021-01-25 21:16:52 +00:00
|
|
|
laxRead, itemText, match,
|
2021-01-18 22:11:53 +00:00
|
|
|
|
2021-01-22 23:12:11 +00:00
|
|
|
Items, Pattern, Templates, Substitution, TokenType,
|
|
|
|
SourceMap, CodeWriter, TemplateFunction, Token, SpanIndex,
|
2021-01-18 22:11:53 +00:00
|
|
|
} from '../syntax/index.js';
|
|
|
|
import {
|
2021-01-19 23:52:40 +00:00
|
|
|
SyndicateParser, SyndicateTypedParser,
|
|
|
|
Identifier,
|
|
|
|
FacetAction,
|
|
|
|
Statement,
|
|
|
|
ActivationImport,
|
|
|
|
FacetFields,
|
|
|
|
Binder,
|
2021-01-18 22:11:53 +00:00
|
|
|
|
|
|
|
compilePattern,
|
|
|
|
patternText,
|
2021-01-25 21:16:52 +00:00
|
|
|
instantiatePatternToPattern,
|
2021-01-18 22:11:53 +00:00
|
|
|
} from './grammar.js';
|
|
|
|
import {
|
|
|
|
BootProc,
|
|
|
|
} from './internals.js';
|
|
|
|
|
|
|
|
export function stripShebang(items: Items): Items {
|
|
|
|
if ((items.length > 0) &&
|
|
|
|
isToken(items[0]) &&
|
|
|
|
items[0].text.startsWith('#!')) {
|
|
|
|
while (items.length > 0 && !isTokenType(items[0], TokenType.NEWLINE)) items.shift();
|
|
|
|
}
|
|
|
|
return items;
|
|
|
|
}
|
|
|
|
|
|
|
|
export type ModuleType ='es6' | 'require' | 'global';
|
|
|
|
|
|
|
|
export interface CompileOptions {
|
|
|
|
source: string,
|
|
|
|
name?: string,
|
|
|
|
runtime?: string,
|
|
|
|
module?: ModuleType,
|
|
|
|
global?: string,
|
2021-01-19 18:54:48 +00:00
|
|
|
typescript?: boolean,
|
2021-01-18 22:11:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
export interface CompilerOutput {
|
|
|
|
text: string,
|
|
|
|
map: SourceMap,
|
2021-01-22 23:12:11 +00:00
|
|
|
targetToSourceMap: SpanIndex<Token>;
|
|
|
|
sourceToTargetMap: SpanIndex<number>;
|
2021-01-18 22:11:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
function receiverFor(s: FacetAction): Substitution {
|
|
|
|
return (s.implicitFacet) ? 'thisFacet.' : '.';
|
|
|
|
}
|
|
|
|
|
2021-01-19 14:13:42 +00:00
|
|
|
export interface ActivationRecord {
|
|
|
|
activation: ActivationImport;
|
|
|
|
activationScriptId: Identifier;
|
|
|
|
}
|
|
|
|
|
2021-01-19 18:54:48 +00:00
|
|
|
export class ExpansionContext {
|
2021-01-19 23:52:40 +00:00
|
|
|
readonly parser: SyndicateParser;
|
2021-01-19 18:54:48 +00:00
|
|
|
readonly moduleType: ModuleType;
|
|
|
|
readonly activationRecords: Array<ActivationRecord> = [];
|
|
|
|
hasBootProc: boolean = false;
|
|
|
|
readonly typescript: boolean;
|
2021-01-19 23:52:40 +00:00
|
|
|
_collectedFields: FacetFields | null = null;
|
2021-01-25 21:16:52 +00:00
|
|
|
nextIdNumber = 0;
|
2021-01-19 18:54:48 +00:00
|
|
|
|
|
|
|
constructor(moduleType: ModuleType,
|
|
|
|
typescript: boolean)
|
|
|
|
{
|
2021-01-19 23:52:40 +00:00
|
|
|
this.parser = typescript ? new SyndicateTypedParser : new SyndicateParser();
|
2021-01-19 18:54:48 +00:00
|
|
|
this.moduleType = moduleType;
|
|
|
|
this.typescript = typescript;
|
|
|
|
}
|
|
|
|
|
2021-01-25 21:16:52 +00:00
|
|
|
quasiRandomId(): string {
|
|
|
|
return '__SYNDICATE__id_' + (this.nextIdNumber++);
|
2021-01-19 18:54:48 +00:00
|
|
|
}
|
|
|
|
|
2021-01-19 23:52:40 +00:00
|
|
|
get collectedFields(): FacetFields {
|
2021-01-22 23:12:11 +00:00
|
|
|
// Allocates a transient array for collected fields in
|
|
|
|
// contexts lacking a surrounding collector - that is, for errors.
|
|
|
|
return this._collectedFields ?? [];
|
2021-01-19 23:52:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
collectField(f: Binder) {
|
|
|
|
this.collectedFields.push(f);
|
|
|
|
}
|
|
|
|
|
|
|
|
withCollectedFields<T>(fs: FacetFields, f: () => T): T {
|
|
|
|
const oldCollectedFields = this._collectedFields;
|
|
|
|
try {
|
|
|
|
this._collectedFields = fs;
|
|
|
|
return f();
|
|
|
|
} finally {
|
|
|
|
this._collectedFields = oldCollectedFields;
|
|
|
|
}
|
|
|
|
}
|
2021-01-29 18:56:12 +00:00
|
|
|
|
|
|
|
argDecl(t: TemplateFunction, name: Substitution, type: Substitution): Substitution {
|
|
|
|
return (this.typescript) ? t`${name}: ${type}` : name;
|
|
|
|
}
|
2021-01-19 23:52:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
function stringifyId(i: Identifier): Items {
|
|
|
|
return [ { ... i, type: TokenType.STRING, text: JSON.stringify(i.text) } ];
|
|
|
|
}
|
|
|
|
|
2021-03-03 09:28:10 +00:00
|
|
|
function facetFieldObjectType(
|
|
|
|
t: TemplateFunction,
|
|
|
|
fs: FacetFields,
|
|
|
|
defaultType?: Substitution): Substitution
|
|
|
|
{
|
2021-01-19 23:52:40 +00:00
|
|
|
function formatBinder(binder: Binder) {
|
2021-03-03 09:28:10 +00:00
|
|
|
const hasType = ((binder.type ?? defaultType) !== void 0);
|
|
|
|
return t`${[binder.id]}${hasType ? ': ': ''}${binder.type ?? defaultType ?? ''}`;
|
2021-01-19 18:54:48 +00:00
|
|
|
}
|
2021-01-19 23:52:40 +00:00
|
|
|
return t`{${commaJoin(fs.map(formatBinder))}}`;
|
|
|
|
}
|
|
|
|
|
2021-01-25 21:16:52 +00:00
|
|
|
function binderTypeGuard(t: TemplateFunction): (binder: Binder, index: number) => Items {
|
|
|
|
return (binder, index) => {
|
|
|
|
if (binder.id.text[0] === '_') {
|
|
|
|
return t`${`/* Ignoring underscore-prefixed binder ${binder.id.text} */`}`;
|
|
|
|
}
|
|
|
|
|
|
|
|
const raw = t`__vs[${''+index}]`;
|
|
|
|
const bind = t`const ${[binder.id]} = ${raw};`;
|
2021-01-19 23:52:40 +00:00
|
|
|
if (binder.type === void 0) {
|
2021-01-25 21:16:52 +00:00
|
|
|
return bind;
|
2021-01-19 23:52:40 +00:00
|
|
|
} else {
|
|
|
|
const typeText = itemText(binder.type);
|
|
|
|
switch (typeText) {
|
|
|
|
case 'boolean':
|
|
|
|
case 'string':
|
|
|
|
case 'number':
|
|
|
|
case 'symbol':
|
2021-01-25 21:16:52 +00:00
|
|
|
return t`if (typeof (${raw}) !== ${JSON.stringify(typeText)}) return;\n${bind}`;
|
|
|
|
case 'any':
|
|
|
|
return bind;
|
2021-01-19 23:52:40 +00:00
|
|
|
default:
|
2021-01-26 21:09:21 +00:00
|
|
|
// TODO: something better than this!!
|
2021-01-19 23:52:40 +00:00
|
|
|
throw new Error(`Unhandled binding type: ${JSON.stringify(typeText)}`);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
2021-01-19 14:13:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
export function expand(tree: Items, ctx: ExpansionContext): Items {
|
2021-01-25 21:16:52 +00:00
|
|
|
const macro = new Templates(undefined, { extraDelimiters: ':' });
|
2021-01-18 22:11:53 +00:00
|
|
|
|
|
|
|
function terminalWrap(t: TemplateFunction, isTerminal: boolean, body: Statement): Statement {
|
|
|
|
if (isTerminal) {
|
2021-01-19 23:52:40 +00:00
|
|
|
return t`thisFacet._stop(function (thisFacet) {${body}})`
|
2021-01-18 22:11:53 +00:00
|
|
|
} else {
|
|
|
|
return body;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function x<T>(p: Pattern<T>, f: (v: T, t: TemplateFunction) => Items) {
|
|
|
|
tree = replace(tree, p, (v, start) => f(v, macro.template(fixPos(start))));
|
|
|
|
}
|
|
|
|
|
|
|
|
function xf<T extends FacetAction>(p: Pattern<T>, f: (v: T, t: TemplateFunction) => Items) {
|
|
|
|
x(p, (v, t) => t`${receiverFor(v)}${f(v, t)}`);
|
|
|
|
}
|
|
|
|
|
2021-01-19 14:13:42 +00:00
|
|
|
const walk = (tree: Items): Items => expand(tree, ctx);
|
2021-01-18 22:11:53 +00:00
|
|
|
const maybeWalk = (tree?: Items) : Items | undefined => (tree === void 0) ? tree : walk(tree);
|
|
|
|
|
2021-01-19 23:52:40 +00:00
|
|
|
xf(ctx.parser.duringStatement, (s, t) => {
|
2021-01-25 21:16:52 +00:00
|
|
|
let spawn0 = match(ctx.parser.spawn, s.body, null);
|
|
|
|
if (spawn0 !== null) {
|
|
|
|
const spawn = spawn0;
|
|
|
|
const id = ctx.quasiRandomId();
|
|
|
|
const instantiated = patternText(instantiatePatternToPattern(s.pattern));
|
|
|
|
return t`on asserted ${patternText(s.pattern)} => {
|
|
|
|
const ${id} = __SYNDICATE__.genUuid();
|
|
|
|
const ${id}_inst = __SYNDICATE__.Instance(${id});
|
|
|
|
react {
|
|
|
|
stop on asserted ${id}_inst => react {
|
|
|
|
stop on retracted ${id}_inst;
|
|
|
|
stop on retracted :snapshot ${instantiated};
|
|
|
|
}
|
|
|
|
stop on retracted :snapshot ${instantiated} => react {
|
|
|
|
stop on asserted ${id}_inst;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
spawn
|
|
|
|
${spawn.isDataspace ? 'dataspace' : []}
|
|
|
|
${spawn.name === void 0 ? [] : t`named ${spawn.name}`}
|
|
|
|
:asserting ${id}_inst
|
|
|
|
${joinItems(spawn.initialAssertions.map(e => t`:asserting ${e}`), ' ')}
|
|
|
|
${joinItems(spawn.parentBinders.map((b, i) => {
|
|
|
|
const init = spawn.parentInits[i];
|
|
|
|
return t`:let ${[b.id]}${b.type === void 0 ? [] : t`: ${b.type}`} = ${init}`;
|
|
|
|
}), ' ')}
|
|
|
|
{
|
|
|
|
assert ${id}_inst;
|
|
|
|
stop on retracted __SYNDICATE__.Observe(${id}_inst);
|
|
|
|
${spawn.body}
|
|
|
|
}
|
|
|
|
}`;
|
|
|
|
} else {
|
2021-01-26 21:09:21 +00:00
|
|
|
// TODO: untyped template
|
2021-01-25 21:16:52 +00:00
|
|
|
const sa = compilePattern(s.pattern);
|
|
|
|
return t`withSelfDo(function (thisFacet) {
|
2021-05-17 14:26:01 +00:00
|
|
|
const _Facets = new __SYNDICATE__.Dictionary<any, __SYNDICATE__.Facet<any>>();
|
2021-01-25 21:16:52 +00:00
|
|
|
on asserted ${patternText(s.pattern)} => react {
|
|
|
|
_Facets.set([${commaJoin(sa.captureBinders.map(t=>[t.id]))}], thisFacet);
|
|
|
|
dataflow { } // TODO: horrible hack to keep the facet alive if no other endpoints
|
2021-01-29 14:36:53 +00:00
|
|
|
${s.body}
|
2021-01-25 21:16:52 +00:00
|
|
|
}
|
|
|
|
on retracted ${patternText(s.pattern)} => {
|
2021-05-17 14:26:01 +00:00
|
|
|
const ${ctx.argDecl(t, '_Key', '__SYNDICATE__.Value<any>[]')} =
|
2021-01-29 18:56:12 +00:00
|
|
|
[${commaJoin(sa.captureBinders.map(t=>[t.id]))}];
|
2021-01-25 21:16:52 +00:00
|
|
|
_Facets.get(_Key)?._stop();
|
|
|
|
_Facets.delete(_Key);
|
|
|
|
}
|
|
|
|
});`;
|
|
|
|
}
|
2021-01-18 22:11:53 +00:00
|
|
|
});
|
|
|
|
|
2021-01-19 23:52:40 +00:00
|
|
|
xf(ctx.parser.spawn, (s, t) => {
|
2021-01-25 21:16:52 +00:00
|
|
|
// TODO: parentBinders, parentInits
|
2021-01-19 23:52:40 +00:00
|
|
|
let body = ctx.withCollectedFields(s.facetFields, () => walk(s.body));
|
|
|
|
let proc = t`function (thisFacet) {${body}}`;
|
2021-01-18 22:11:53 +00:00
|
|
|
if (s.isDataspace) proc = t`__SYNDICATE__.inNestedDataspace(${proc})`;
|
|
|
|
let assertions = (s.initialAssertions.length > 0)
|
|
|
|
? t`, new __SYNDICATE__.Set([${commaJoin(s.initialAssertions.map(walk))}])`
|
|
|
|
: ``;
|
2021-01-19 23:52:40 +00:00
|
|
|
let fieldTypeParam = ctx.typescript ? t`<${facetFieldObjectType(t, s.facetFields)}>` : '';
|
|
|
|
return t`_spawn${fieldTypeParam}(${maybeWalk(s.name) ?? 'null'}, ${proc}${assertions});`;
|
2021-01-18 22:11:53 +00:00
|
|
|
});
|
|
|
|
|
2021-01-19 23:52:40 +00:00
|
|
|
xf(ctx.parser.fieldDeclarationStatement, (s, t) => {
|
|
|
|
ctx.collectField(s.property);
|
|
|
|
return t`declareField(this, ${stringifyId(s.property.id)}, ${maybeWalk(s.init) ?? 'void 0'});`;
|
2021-01-18 22:11:53 +00:00
|
|
|
});
|
|
|
|
|
2021-01-19 23:52:40 +00:00
|
|
|
xf(ctx.parser.assertionEndpointStatement, (s, t) => {
|
2021-01-18 22:11:53 +00:00
|
|
|
if (s.test == void 0) {
|
|
|
|
return t`addEndpoint(thisFacet => ({ assertion: ${walk(s.template)}, analysis: null }));`;
|
|
|
|
} else {
|
|
|
|
return t`addEndpoint(thisFacet => (${walk(s.test)})
|
|
|
|
? ({ assertion: ${walk(s.template)}, analysis: null })
|
|
|
|
: ({ assertion: void 0, analysis: null }), ${''+s.isDynamic});`;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2021-01-19 23:52:40 +00:00
|
|
|
xf(ctx.parser.dataflowStatement, (s, t) =>
|
|
|
|
t`addDataflow(function (thisFacet) {${walk(s.body)}});`);
|
2021-01-18 22:11:53 +00:00
|
|
|
|
2021-01-19 23:52:40 +00:00
|
|
|
xf(ctx.parser.eventHandlerEndpointStatement, (s, t) => {
|
2021-01-18 22:11:53 +00:00
|
|
|
switch (s.triggerType) {
|
|
|
|
case 'dataflow':
|
2021-01-19 23:52:40 +00:00
|
|
|
return t`withSelfDo(function (thisFacet) { dataflow { if (${walk(s.predicate)}) { ${terminalWrap(t, s.terminal, walk(s.body))} } } });`;
|
2021-01-18 22:11:53 +00:00
|
|
|
|
|
|
|
case 'start':
|
|
|
|
case 'stop': {
|
|
|
|
const m = s.triggerType === 'start' ? 'addStartScript' : 'addStopScript';
|
2021-01-19 23:52:40 +00:00
|
|
|
return t`${m}(function (thisFacet) {${walk(s.body)}});`;
|
2021-01-18 22:11:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
case 'asserted':
|
|
|
|
case 'retracted':
|
|
|
|
case 'message': {
|
|
|
|
const sa = compilePattern(s.pattern);
|
|
|
|
const expectedEvt = ({
|
|
|
|
'asserted': 'ADDED',
|
|
|
|
'retracted': 'REMOVED',
|
|
|
|
'message': 'MESSAGE',
|
|
|
|
})[s.triggerType];
|
|
|
|
return t`addEndpoint(thisFacet => ({
|
|
|
|
assertion: __SYNDICATE__.Observe(${walk(sa.assertion)}),
|
|
|
|
analysis: {
|
|
|
|
skeleton: ${walk(sa.skeleton)},
|
|
|
|
constPaths: ${JSON.stringify(sa.constPaths)},
|
|
|
|
constVals: [${commaJoin(sa.constVals.map(walk))}],
|
|
|
|
capturePaths: ${JSON.stringify(sa.capturePaths)},
|
2021-05-17 14:26:01 +00:00
|
|
|
callback: thisFacet.wrap((thisFacet, __Evt, ${ctx.argDecl(t, '__vs', 'Array<__SYNDICATE__.Value<any>>')}) => {
|
2021-01-18 22:11:53 +00:00
|
|
|
if (__Evt === __SYNDICATE__.Skeleton.EventType.${expectedEvt}) {
|
2021-01-29 18:56:12 +00:00
|
|
|
${joinItems(sa.captureBinders.map(binderTypeGuard(t)), '\n')}
|
2021-01-18 22:11:53 +00:00
|
|
|
thisFacet.scheduleScript(() => {${terminalWrap(t, s.terminal, walk(s.body))}});
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}), ${'' + s.isDynamic});`;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2021-01-19 23:52:40 +00:00
|
|
|
x(ctx.parser.typeDefinitionStatement, (s, t) => {
|
2021-03-03 09:28:10 +00:00
|
|
|
const l = `Symbol.for(${JSON.stringify(s.label.text)})`;
|
|
|
|
const fns = JSON.stringify(s.fields.map(f => f.id.text));
|
|
|
|
const fs = ctx.typescript
|
2021-05-17 14:26:01 +00:00
|
|
|
? t`<${facetFieldObjectType(t, s.fields, t`__SYNDICATE__.Value<any>`)}, any>`
|
2021-03-03 09:28:10 +00:00
|
|
|
: '';
|
|
|
|
return t`const ${[s.label]} = __SYNDICATE__.Record.makeConstructor${fs}()(${maybeWalk(s.wireName) ?? l}, ${fns});`;
|
2021-01-18 22:11:53 +00:00
|
|
|
});
|
|
|
|
|
2021-01-19 23:52:40 +00:00
|
|
|
xf(ctx.parser.messageSendStatement, (s, t) => t`_send(${walk(s.expr)});`);
|
2021-01-18 22:11:53 +00:00
|
|
|
|
2021-01-19 23:52:40 +00:00
|
|
|
xf(ctx.parser.reactStatement, (s, t) => {
|
|
|
|
const body = ctx.withCollectedFields(s.facetFields, () => walk(s.body));
|
|
|
|
const fieldTypeParam = ctx.typescript
|
2021-01-25 21:16:52 +00:00
|
|
|
? t`<${facetFieldObjectType(t, s.facetFields)}>`
|
2021-01-19 23:52:40 +00:00
|
|
|
: '';
|
|
|
|
return t`addChildFacet${fieldTypeParam}(function (thisFacet) {${body}});`;
|
|
|
|
});
|
2021-01-18 22:11:53 +00:00
|
|
|
|
2021-01-19 23:52:40 +00:00
|
|
|
x(ctx.parser.activationImport, (s) => {
|
2021-01-19 14:13:42 +00:00
|
|
|
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 [];
|
|
|
|
}),
|
|
|
|
|
2021-01-19 23:52:40 +00:00
|
|
|
x(ctx.parser.bootStatement, (s, t) => {
|
2021-01-19 14:13:42 +00:00
|
|
|
ctx.hasBootProc = true;
|
|
|
|
const activationStatements = ctx.activationRecords.map(({ activationScriptId: id }) =>
|
|
|
|
t`thisFacet.activate(${[id]}); `);
|
|
|
|
const body = t`${joinItems(activationStatements)}${walk(s)}`;
|
2021-01-19 23:52:40 +00:00
|
|
|
const facetDecl = ctx.typescript ? 'thisFacet: __SYNDICATE__.Facet<{}>' : 'thisFacet';
|
2021-01-19 14:13:42 +00:00
|
|
|
switch (ctx.moduleType) {
|
2021-01-18 22:11:53 +00:00
|
|
|
case 'es6':
|
2021-01-19 23:52:40 +00:00
|
|
|
return t`export function ${BootProc}(${facetDecl}) {${body}}`;
|
2021-01-18 22:11:53 +00:00
|
|
|
case 'require':
|
2021-01-19 23:52:40 +00:00
|
|
|
return t`module.exports.${BootProc} = function (${facetDecl}) {${body}};`;
|
2021-01-18 22:36:05 +00:00
|
|
|
case 'global':
|
2021-01-19 23:52:40 +00:00
|
|
|
return t`function ${BootProc}(${facetDecl}) {${body}}`;
|
2021-01-18 22:11:53 +00:00
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2021-01-19 23:52:40 +00:00
|
|
|
xf(ctx.parser.stopStatement, (s, t) =>
|
|
|
|
t`_stop(function (thisFacet) {${walk(s.body)}});`)
|
2021-01-18 22:11:53 +00:00
|
|
|
|
|
|
|
return tree;
|
|
|
|
}
|
|
|
|
|
|
|
|
export function compile(options: CompileOptions): CompilerOutput {
|
|
|
|
const inputFilename = options.name ?? '/dev/stdin';
|
2021-01-19 23:52:40 +00:00
|
|
|
|
2021-01-23 17:20:27 +00:00
|
|
|
// console.info(`Syndicate: compiling ${inputFilename}`);
|
2021-01-19 23:52:40 +00:00
|
|
|
|
2021-01-18 22:11:53 +00:00
|
|
|
const source = options.source;
|
|
|
|
const moduleType = options.module ?? 'es6';
|
2021-01-19 18:54:48 +00:00
|
|
|
const typescript = options.typescript ?? false;
|
2021-01-18 22:11:53 +00:00
|
|
|
|
|
|
|
const start = startPos(inputFilename);
|
2021-01-19 23:52:40 +00:00
|
|
|
let tree = stripShebang(laxRead(source, { start, extraDelimiters: ':' }));
|
2021-01-19 14:13:42 +00:00
|
|
|
const end = tree.length > 0 ? tree[tree.length - 1].end : start;
|
|
|
|
|
2021-01-25 21:16:52 +00:00
|
|
|
const macro = new Templates(undefined, { extraDelimiters: ':' });
|
2021-01-18 22:11:53 +00:00
|
|
|
|
2021-01-19 18:54:48 +00:00
|
|
|
const ctx = new ExpansionContext(moduleType, typescript);
|
2021-01-19 14:13:42 +00:00
|
|
|
|
|
|
|
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}`;
|
2021-01-18 22:11:53 +00:00
|
|
|
|
|
|
|
{
|
|
|
|
const runtime = options.runtime ?? '@syndicate-lang/core';
|
|
|
|
switch (moduleType) {
|
|
|
|
case 'es6':
|
2021-01-19 14:13:42 +00:00
|
|
|
tree = ts`import * as __SYNDICATE__ from ${JSON.stringify(runtime)};\n${tree}`;
|
2021-01-18 22:11:53 +00:00
|
|
|
break;
|
|
|
|
case 'require':
|
2021-01-19 14:13:42 +00:00
|
|
|
tree = ts`const __SYNDICATE__ = require(${JSON.stringify(runtime)});\n${tree}`;
|
2021-01-18 22:11:53 +00:00
|
|
|
break;
|
|
|
|
case 'global':
|
2021-01-19 14:13:42 +00:00
|
|
|
tree = ts`const __SYNDICATE__ = ${runtime};\n${tree}`;
|
2021-01-18 22:11:53 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const cw = new CodeWriter(inputFilename);
|
2021-01-19 14:13:42 +00:00
|
|
|
cw.emit(tree);
|
2021-01-18 22:11:53 +00:00
|
|
|
|
2021-01-22 23:12:11 +00:00
|
|
|
|
|
|
|
const text = cw.text;
|
2021-01-20 20:56:01 +00:00
|
|
|
|
2021-01-18 22:11:53 +00:00
|
|
|
return {
|
2021-01-22 23:12:11 +00:00
|
|
|
text,
|
2021-01-18 22:11:53 +00:00
|
|
|
map: cw.map,
|
2021-01-22 23:12:11 +00:00
|
|
|
targetToSourceMap: cw.targetToSourceMap.index(),
|
|
|
|
sourceToTargetMap: cw.sourceToTargetMap.index(),
|
2021-01-18 22:11:53 +00:00
|
|
|
};
|
|
|
|
}
|