Support toplevel typed binders in patterns
This commit is contained in:
parent
73b7759816
commit
1e29d528d7
|
@ -193,7 +193,8 @@ export class SyndicateParser {
|
||||||
readonly exprBoundary = alt<any>(atom(';'), atom(','), group('{', discard), Matcher.end);
|
readonly exprBoundary = alt<any>(atom(';'), atom(','), group('{', discard), Matcher.end);
|
||||||
|
|
||||||
readonly identifier: Pattern<Identifier> = atom();
|
readonly identifier: Pattern<Identifier> = atom();
|
||||||
get binder(): Pattern<Binder> { return scope(o => bind(o, 'id', this.identifier)); }
|
binder(... _extraStops: Pattern<any>[]): Pattern<Binder> { return scope(o => bind(o, 'id', this.identifier)); }
|
||||||
|
readonly defaultBinder = this.binder();
|
||||||
|
|
||||||
expr(... extraStops: Pattern<any>[]): Pattern<Expr> {
|
expr(... extraStops: Pattern<any>[]): Pattern<Expr> {
|
||||||
return withoutSpace(upTo(alt(this.exprBoundary, ... extraStops)));
|
return withoutSpace(upTo(alt(this.exprBoundary, ... extraStops)));
|
||||||
|
@ -248,7 +249,7 @@ export class SyndicateParser {
|
||||||
map(scope(
|
map(scope(
|
||||||
(l: { b: Binder, init: Expr }) =>
|
(l: { b: Binder, init: Expr }) =>
|
||||||
seq(kw('let'),
|
seq(kw('let'),
|
||||||
bind(l, 'b', this.binder),
|
bind(l, 'b', this.defaultBinder),
|
||||||
atom('='),
|
atom('='),
|
||||||
bind(l, 'init', this.headerExpr))),
|
bind(l, 'init', this.headerExpr))),
|
||||||
l => {
|
l => {
|
||||||
|
@ -262,7 +263,7 @@ export class SyndicateParser {
|
||||||
readonly fieldDeclarationStatement: Pattern<FieldDeclarationStatement> =
|
readonly fieldDeclarationStatement: Pattern<FieldDeclarationStatement> =
|
||||||
this.turnAction(o => {
|
this.turnAction(o => {
|
||||||
return seq(atom('field'),
|
return seq(atom('field'),
|
||||||
bind(o, 'field', this.binder),
|
bind(o, 'field', this.defaultBinder),
|
||||||
option(seq(atom('='), bind(o, 'init', this.expr()))),
|
option(seq(atom('='), bind(o, 'init', this.expr()))),
|
||||||
this.statementBoundary);
|
this.statementBoundary);
|
||||||
});
|
});
|
||||||
|
@ -333,7 +334,7 @@ export class SyndicateParser {
|
||||||
scope(o => seq(bind(o, 'expectedUse', alt(atomString('message'), atomString('assertion'))),
|
scope(o => seq(bind(o, 'expectedUse', alt(atomString('message'), atomString('assertion'))),
|
||||||
atom('type'),
|
atom('type'),
|
||||||
bind(o, 'label', this.identifier),
|
bind(o, 'label', this.identifier),
|
||||||
group('(', bind(o, 'fields', repeat(this.binder, { separator: atom(',') }))),
|
group('(', bind(o, 'fields', repeat(this.defaultBinder, { separator: atom(',') }))),
|
||||||
option(seq(atom('='),
|
option(seq(atom('='),
|
||||||
bind(o, 'wireName', withoutSpace(upTo(this.statementBoundary))))),
|
bind(o, 'wireName', withoutSpace(upTo(this.statementBoundary))))),
|
||||||
this.statementBoundary));
|
this.statementBoundary));
|
||||||
|
@ -407,12 +408,13 @@ export class SyndicateParser {
|
||||||
// {expr: expr, ...} - constant
|
// {expr: expr, ...} - constant
|
||||||
// other - constant
|
// other - constant
|
||||||
|
|
||||||
readonly pCaptureBinder: Pattern<Binder> =
|
pCaptureBinder = (b: Pattern<Binder>): Pattern<Binder> =>
|
||||||
mapm(this.binder, i => {
|
mapm(b, i => {
|
||||||
return i.id.text.startsWith('$')
|
return i.id.text.startsWith('$')
|
||||||
? succeed({ id: { ... i.id, text: i.id.text.slice(1) }, type: i.type })
|
? succeed({ id: { ... i.id, text: i.id.text.slice(1) }, type: i.type })
|
||||||
: fail;
|
: fail;
|
||||||
});
|
});
|
||||||
|
readonly pCaptureDefaultBinder = this.pCaptureBinder(this.defaultBinder);
|
||||||
|
|
||||||
readonly pDiscard: Pattern<void> =
|
readonly pDiscard: Pattern<void> =
|
||||||
mapm(this.identifier, i => i.text === '_' ? succeed(void 0) : fail);
|
mapm(this.identifier, i => i.text === '_' ? succeed(void 0) : fail);
|
||||||
|
@ -440,7 +442,7 @@ export class SyndicateParser {
|
||||||
|
|
||||||
hasCapturesOrDiscards(e: Expr): boolean {
|
hasCapturesOrDiscards(e: Expr): boolean {
|
||||||
return foldItems(e,
|
return foldItems(e,
|
||||||
t => match(alt<any>(this.pCaptureBinder, this.pDiscard), [t], null, '(') !== null,
|
t => match(alt<any>(this.pCaptureDefaultBinder, this.pDiscard), [t], null, '(') !== null,
|
||||||
(_g, b, _k) => b,
|
(_g, b, _k) => b,
|
||||||
bs => bs.some(b => b));
|
bs => bs.some(b => b));
|
||||||
}
|
}
|
||||||
|
@ -503,7 +505,7 @@ export class SyndicateParser {
|
||||||
// });
|
// });
|
||||||
// } else
|
// } else
|
||||||
if (this.hasCapturesOrDiscards(o.ctor)) {
|
if (this.hasCapturesOrDiscards(o.ctor)) {
|
||||||
const r = match(this.pCaptureBinder, o.ctor, null, '(');
|
const r = match(this.pCaptureDefaultBinder, o.ctor, null, '(');
|
||||||
if (r !== null && o.arguments.length === 1) {
|
if (r !== null && o.arguments.length === 1) {
|
||||||
return succeed({
|
return succeed({
|
||||||
type: 'PCapture',
|
type: 'PCapture',
|
||||||
|
@ -520,7 +522,7 @@ export class SyndicateParser {
|
||||||
scope<PCapture>(o => {
|
scope<PCapture>(o => {
|
||||||
o.type = 'PCapture';
|
o.type = 'PCapture';
|
||||||
o.inner = { type: 'PDiscard' };
|
o.inner = { type: 'PDiscard' };
|
||||||
return bind(o, 'binder', this.pCaptureBinder);
|
return bind(o, 'binder', this.pCaptureBinder(this.binder(... extraStops)));
|
||||||
}),
|
}),
|
||||||
map(this.expr(... extraStops), e => ({ type: 'PConstant', value: e }))
|
map(this.expr(... extraStops), e => ({ type: 'PConstant', value: e }))
|
||||||
));
|
));
|
||||||
|
@ -528,10 +530,10 @@ export class SyndicateParser {
|
||||||
}
|
}
|
||||||
|
|
||||||
export class SyndicateTypedParser extends SyndicateParser {
|
export class SyndicateTypedParser extends SyndicateParser {
|
||||||
get binder(): Pattern<Binder> {
|
binder(... extraStops: Pattern<any>[]): Pattern<Binder> {
|
||||||
return scope(o => seq(bind(o, 'id', this.identifier),
|
return scope(o => seq(bind(o, 'id', this.identifier),
|
||||||
option(seq(atom(':'),
|
option(seq(atom(':'),
|
||||||
bind(o, 'type', this.type(atom('=')))))));
|
bind(o, 'type', this.type(atom('='), ... extraStops))))));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -19,14 +19,14 @@ function translate(source: string, options: Partial<CompileOptions> = {}): { cod
|
||||||
return { code: result.text, errors };
|
return { code: result.text, errors };
|
||||||
}
|
}
|
||||||
|
|
||||||
function translateNoErrors(source: string): string {
|
function translateNoErrors(source: string, options?: Partial<CompileOptions>): string {
|
||||||
const o = translate(source);
|
const o = translate(source, options);
|
||||||
expect(o.errors.length).toBe(0);
|
expect(o.errors.length).toBe(0);
|
||||||
return o.code;
|
return o.code;
|
||||||
}
|
}
|
||||||
|
|
||||||
function expectCodeEqual(input: string, output: string) {
|
function expectCodeEqual(input: string, output: string, options?: Partial<CompileOptions>) {
|
||||||
expect(format(translateNoErrors(input))).toBe(format(output));
|
expect(format(translateNoErrors(input, options))).toBe(format(output));
|
||||||
}
|
}
|
||||||
|
|
||||||
describe('react', () => {
|
describe('react', () => {
|
||||||
|
@ -150,6 +150,25 @@ __SYNDICATE__.Turn.active.assertDataflow(() => ({
|
||||||
})
|
})
|
||||||
}));`));
|
}));`));
|
||||||
|
|
||||||
|
it('capture with type at top', () => expectCodeEqual(`during $v: T => { ok() }`, `
|
||||||
|
__SYNDICATE__.Turn.active.assertDataflow(() => ({
|
||||||
|
target: currentSyndicateTarget,
|
||||||
|
assertion: __SYNDICATE__.Observe({
|
||||||
|
pattern: __SYNDICATE__.QuasiValue.finish((__SYNDICATE__.QuasiValue.bind((__SYNDICATE__.QuasiValue._)))),
|
||||||
|
observer: __SYNDICATE__.Turn.ref(__SYNDICATE__.assertionFacetObserver(
|
||||||
|
(__vs: __SYNDICATE__.AnyValue) => {
|
||||||
|
if (Array.isArray(__vs)) {
|
||||||
|
const __v_0 = T.__from_preserve__(__vs[0]);
|
||||||
|
if (__v_0 === void 0) return;
|
||||||
|
const v = __v_0;
|
||||||
|
ok()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
))
|
||||||
|
})
|
||||||
|
}));
|
||||||
|
`, { typescript: true }));
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('once', () => {
|
describe('once', () => {
|
||||||
|
|
Loading…
Reference in New Issue