99 lines
5.1 KiB
TypeScript
99 lines
5.1 KiB
TypeScript
import { refPosition } from '../reader';
|
|
import * as M from '../meta';
|
|
import { block, fnblock, Item, opseq, parens, seq } from './block';
|
|
import { FunctionContext } from './context';
|
|
|
|
export function predicateFor(ctx: FunctionContext, v: string, p: M.Definition, recordOkAsTuple = false): Item
|
|
{
|
|
switch (p.label) {
|
|
case M.$atom:
|
|
switch (p[0]) {
|
|
case M.$Boolean: return `typeof ${v} === 'boolean'`;
|
|
case M.$Float: return `_.Float.isSingle(${v})`;
|
|
case M.$Double: return `_.Float.isDouble(${v})`;
|
|
case M.$SignedInteger: return `typeof ${v} === 'number'`;
|
|
case M.$String: return `typeof ${v} === 'string'`;
|
|
case M.$ByteString: return `_.Bytes.isBytes(${v})`;
|
|
case M.$Symbol: return `typeof ${v} === 'symbol'`;
|
|
}
|
|
case M.$lit:
|
|
return `_.is(${v}, ${ctx.mod.literal(p[0])})`;
|
|
case M.$ref:
|
|
return M.lookup(refPosition(p), p, ctx.mod.env,
|
|
(_p) => `is${M.Ref._.name(p).description!}(${v})`,
|
|
(pp) => predicateFor(ctx, v, pp),
|
|
(modId, modPath, _p) => {
|
|
ctx.mod.imports.add([modId, modPath]);
|
|
return `${modId}.is${M.Ref._.name(p).description!}(${v})`;
|
|
});
|
|
case M.$or: {
|
|
const alts = p[0];
|
|
const recs = alts.map(p => ctx.mod.derefPattern(p));
|
|
if (recs.length > 1 && recs.every(pp => pp.label === M.$rec)) {
|
|
return seq(
|
|
`_.Record.isRecord<_val, _.Tuple<_val>, _ptr>(${v}) && `,
|
|
parens(opseq('false', ' || ',
|
|
... recs.map(r =>
|
|
(r.label !== M.$rec) ? '' : parens(seq(
|
|
predicateFor(ctx, `${v}.label`, M.unname(r[0])),
|
|
' && ',
|
|
predicateFor(ctx, v, M.unname(r[1]), true)))))));
|
|
} else {
|
|
return opseq('false', ' || ', ... p[0].map(pp => predicateFor(ctx, v, pp[1])));
|
|
}
|
|
}
|
|
case M.$and:
|
|
return opseq('true', ' && ', ...p[0].map(pp => predicateFor(ctx, v, M.unname(pp))));
|
|
case M.$pointer:
|
|
return `_.isPointer(${v})`;
|
|
case M.$rec:
|
|
return opseq('true', ' && ',
|
|
`_.Record.isRecord<_val, _.Tuple<_val>, _ptr>(${v})`,
|
|
predicateFor(ctx, `${v}.label`, M.unname(p[0])),
|
|
predicateFor(ctx, v, M.unname(p[1]), true));
|
|
case M.$tuple:
|
|
return opseq('true', ' && ',
|
|
... (recordOkAsTuple ? []
|
|
: [`_.Array.isArray(${v})`, `!_.Record.isRecord<_val, _.Tuple<_val>, _ptr>(${v})`]),
|
|
`(${v}.length === ${p[0].length})`,
|
|
...p[0].map((pp, i) => predicateFor(ctx, `${v}[${i}]`, M.unname(pp))));
|
|
case M.$tuple_STAR_:
|
|
return opseq('true', ' && ',
|
|
... (recordOkAsTuple ? []
|
|
: [`_.Array.isArray(${v})`, `!_.Record.isRecord<_val, _.Tuple<_val>, _ptr>(${v})`]),
|
|
`(${v}.length >= ${p[0].length})`,
|
|
seq(p[0].length > 0 ? `${v}.slice(${p[0].length})` : v,
|
|
`.every(v => `,
|
|
parens(predicateFor(ctx, 'v', M.unname(p[1]))),
|
|
`)`),
|
|
...p[0].map((pp, i) => predicateFor(ctx, `${v}[${i}]`, M.unname(pp))));
|
|
case M.$setof:
|
|
return opseq('true', ' && ',
|
|
`_.Set.isSet<_val>(${v})`,
|
|
fnblock(
|
|
seq(`for (const vv of ${v}) `, block(
|
|
seq('if (!(', predicateFor(ctx, 'vv', p[0]), ')) return false'))),
|
|
seq('return true')));
|
|
case M.$dictof:
|
|
return opseq('true', ' && ',
|
|
`_.Dictionary.isDictionary<_ptr>(${v})`,
|
|
fnblock(
|
|
seq(`for (const e of ${v}) `, block(
|
|
seq('if (!(', predicateFor(ctx, 'e[0]', p[0]), ')) return false'),
|
|
seq('if (!(', predicateFor(ctx, 'e[1]', p[1]), ')) return false'))),
|
|
seq('return true')));
|
|
case M.$dict:
|
|
return opseq('true', ' && ',
|
|
`_.Dictionary.isDictionary<_ptr>(${v})`,
|
|
... Array.from(p[0]).map(([k, vp]) => {
|
|
const tmp = ctx.gentemp();
|
|
return parens(seq(
|
|
`(${tmp} = ${v}.get(${ctx.mod.literal(k)})) !== void 0 && `,
|
|
predicateFor(ctx, tmp, M.unname(vp))));
|
|
}));
|
|
default:
|
|
((_p: never) => {})(p);
|
|
throw new Error("Unreachable");
|
|
}
|
|
}
|