import { FunctionContext } from "./context"; import * as M from '../meta'; import { block, Item, seq } from "./block"; import { simpleType, dictionaryType, setType, typeFor } from "./gentype"; import { refPosition } from "../reader"; import { ANY_TYPE, Type } from "./type"; export function converterForDefinition( ctx: FunctionContext, p: M.Definition, src: string, dest: string): Item[] { if (p.label === M.$or) { const alts = p[0]; function loop(i: number): Item[] { ctx.variantName = alts[i][0]; return [... converterForAlternative(ctx, alts[i][1], src, dest), ... ((i < alts.length - 1) ? [seq(`if (${dest} === void 0) `, ctx.block(() => loop(i + 1)))] : [])]; } return alts.length === 0 ? [] : loop(0); } else { ctx.variantName = void 0; return converterForAlternative(ctx, p, src, dest); } } function converterForAlternative( ctx: FunctionContext, p: M.Alternative, src: string, dest: string): Item[] { if (p.label === M.$and) { const alts = p[0]; function loop(i: number): Item[] { return (i < alts.length) ? converterFor(ctx, alts[i], src, () => loop(i + 1)) : [ctx.buildCapturedCompound(dest)]; } return alts.length === 0 ? [seq(`${dest} = ${src}`)] : loop(0); } else { return converterFor(ctx, p, src, simpleValue => { if (simpleValue === void 0) { return [ctx.buildCapturedCompound(dest)]; } else if (ctx.variantName !== void 0) { if (typeFor(ctx.mod, p).kind === 'unit') { return [ctx.buildCapturedCompound(dest)]; } else { return [ctx.withCapture('value', simpleValue, () => ctx.buildCapturedCompound(dest))]; } } else { return [`${dest} = ${simpleValue}`]; } }); } } function converterForTuple(ctx: FunctionContext, ps: M.NamedPattern[], src: string, recordFields: boolean, variablePattern: M.NamedSimplePattern | undefined, k: () => Item[]): Item[] { function loop(i: number): Item[] { if (i < ps.length) { return converterFor(ctx, ps[i], `${src}[${i}]`, () => loop(i + 1)); } else { if (variablePattern === void 0) { return k(); } else { const vN = ctx.gentemp(Type.array(ANY_TYPE)); return [ps.length > 0 ? `${vN} = ${src}.slice(${ps.length})` : `${vN} = ${src}`, converterForArray(ctx, variablePattern, vN, false, k)]; } } } const lengthCheck = variablePattern === void 0 ? seq(` && ${src}.length === ${ps.length}`) : ((ps.length === 0) ? '' : seq(` && ${src}.length >= ${ps.length}`)); return recordFields ? loop(0) : [seq(`if (_.Array.isArray(${src})`, lengthCheck, `) `, ctx.block(() => loop(0)))]; } function converterForArray(ctx: FunctionContext, arrayType: M.NamedSimplePattern, src: string, checkArray: boolean, k: (dest: string) => Item[]): Item { const postCheck = () => { const r = ctx.gentemp(Type.array(simpleType(ctx.mod, M.unname(arrayType)))); const v = ctx.gentempname(); return [ seq(`${r} = []`), seq(`for (const ${v} of ${src}) `, ctx.block(() => [ ... converterFor(ctx, arrayType, v, vv => [`${r}.push(${vv})`, `continue`]), seq(`${r} = void 0`), seq(`break`)])), ctx.convertCapture(M.nameFor(arrayType), r, k)]; }; return (checkArray ? seq(`if (_.Array.isArray(${src})) `, ctx.block(postCheck)) : block(... postCheck())); } function converterFor( ctx: FunctionContext, np: M.NamedPattern, src: string, ks: (dest: string | undefined) => Item[], recordFields = false): Item[] { let p = M.unname(np); let maybeName = M.nameFor(np); if (M.isSimplePattern(p)) { const dest = ctx.gentemp(simpleType(ctx.mod, p)); return [... converterForSimple(ctx, p, src, dest), ctx.convertCapture(maybeName, dest, ks)]; } else { switch (p.label) { case M.$setof: { const setPattern = p[0]; const r = ctx.gentemp(setType(ctx.mod, setPattern)); const v = ctx.gentempname(); return [ seq(`if (_.Set.isSet<_ptr>(${src})) `, ctx.block(() => [ seq(`${r} = new _.KeyedSet()`), seq(`for (const ${v} of ${src}) `, ctx.block(() => [ ... converterFor(ctx, setPattern, v, vv => [`${r}.add(${vv})`, `continue`]), seq(`${r} = void 0`), seq(`break`)])), ctx.convertCapture(maybeName, r, ks)]))]; } case M.$dictof: { const keyPattern = p[0]; const valPattern = p[1]; const r = ctx.gentemp(dictionaryType(ctx.mod, keyPattern, valPattern)); const v = ctx.gentempname(); const k = ctx.gentempname(); return [ seq(`if (_.Dictionary.isDictionary<_ptr>(${src})) `, ctx.block(() => [ seq(`${r} = new _.KeyedDictionary()`), seq(`for (const [${k}, ${v}] of ${src}) `, ctx.block(() => [ ... converterFor(ctx, keyPattern, k, kk => converterFor(ctx, valPattern, v, vv => [`${r}.set(${kk}, ${vv})`, `continue`])), seq(`${r} = void 0`), seq(`break`)])), ctx.convertCapture(maybeName, r, ks)]))]; } default: { const arrayType = M.simpleArray(p); if (arrayType === void 0) { return converterForCompound(ctx, p, src, recordFields, () => ks(void 0)); } else { return [converterForArray(ctx, arrayType, src, !recordFields, ks)]; } } } } } function converterForSimple( ctx: FunctionContext, p: M.SimplePattern, src: string, dest: string): Item[] { switch (p.label) { case M.$any: return [`${dest} = ${src}`]; case M.$atom: { let test: Item; switch (p[0]) { case M.$Boolean: test = `typeof ${src} === 'boolean'`; break; case M.$Float: test = `_.Float.isSingle(${src})`; break; case M.$Double: test =`_.Float.isDouble(${src})`; break; case M.$SignedInteger: test = `typeof ${src} === 'number'`; break; case M.$String: test = `typeof ${src} === 'string'`; break; case M.$ByteString: test = `_.Bytes.isBytes(${src})`; break; case M.$Symbol: test = `typeof ${src} === 'symbol'`; break; } return [seq(`${dest} = `, test, ` ? ${src} : void 0`)]; } case M.$lit: return [`${dest} = _.is(${src}, ${ctx.mod.literal(p[0])}) ? null : void 0`]; case M.$ref: return M.lookup(refPosition(p), p, ctx.mod.env, (_p) => [`${dest} = to${p[1].description!}(${src})`], (modId, modPath,_p) => { ctx.mod.imports.add([modId, modPath]); return [`${dest} = ${modId}.decode${p[1].description!}(${src})`]; }); case M.$pointer: return [`${dest} = _toPtr(${src})`]; default: ((_p: never) => {})(p); throw new Error("Unreachable"); } } function converterForCompound( ctx: FunctionContext, p: M.CompoundPattern, src: string, recordFields: boolean, ks: () => Item[]): Item[] { switch (p.label) { case M.$rec: return [seq(`if (_.Record.isRecord<_val, _.Tuple<_val>, _ptr>(${src})) `, ctx.block(() => converterFor(ctx, p[0], `${src}.label`, () => converterFor(ctx, p[1], src, ks, true))))]; case M.$tuple: return converterForTuple(ctx, p[0], src, recordFields, void 0, ks); case M.$tuple_STAR_: return converterForTuple(ctx, p[0], src, recordFields, p[1], ks); case M.$setof: case M.$dictof: throw new Error('Internal error: setof and dictof are handled in converterFor()'); case M.$dict: { const entries = Array.from(p[0]); function loop(i: number): Item[] { if (i < entries.length) { const [k, n] = entries[i]; const tmpSrc = ctx.gentemp(); return [seq(`if ((${tmpSrc} = ${src}.get(${ctx.mod.literal(k)})) !== void 0) `, ctx.block(() => converterFor(ctx, M.addNameIfAbsent(n, k), tmpSrc, () => loop(i + 1))))]; } else { return ks(); } } return [seq(`if (_.Dictionary.isDictionary<_ptr>(${src})) `, ctx.block(() => loop(0)))]; } default: ((_p: never) => {})(p); throw new Error("Unreachable"); } }