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"); } }