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[] { switch (p._variant) { case 'or': { const alts = [p.pattern, ... p.patterns]; function loop(i: number): Item[] { ctx.variantName = alts[i].variantLabel; return [... converterForPattern(ctx, alts[i].pattern, src, dest), ... ((i < alts.length - 1) ? [seq(`if (${dest} === void 0) `, ctx.block(() => loop(i + 1)))] : [])]; } return loop(0); } case 'and': { const pcs = [p.pattern, ... p.patterns]; function loop(i: number): Item[] { return (i < pcs.length) ? converterFor(ctx, pcs[i], src, () => loop(i + 1)) : [ctx.buildCapturedCompound(dest)]; } return loop(0); } case 'Pattern': ctx.variantName = void 0; return converterForPattern(ctx, p.value, src, dest); } } function converterForPattern( ctx: FunctionContext, p: M.Pattern, src: string, dest: string): Item[] { return converterFor(ctx, M.NamedPattern.anonymous(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.unnameSimplePattern(arrayType)))); const v = ctx.gentempname(); return [ seq(`${r} = []`), seq(`for (const ${v} of ${src}) `, ctx.block(() => [ ... converterFor(ctx, M.promoteNamedSimplePattern(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.unnamePattern(np); let maybeName = M.nameFor(np); if (p._variant === 'SimplePattern') { const dest = ctx.gentemp(simpleType(ctx.mod, p.value)); return [... converterForSimple(ctx, p.value, src, dest), ctx.convertCapture(maybeName, dest, ks)]; } else { switch (p.value._variant) { case 'setof': { const setPattern = p.value.pattern; const r = ctx.gentemp(setType(ctx.mod, setPattern)); const v = ctx.gentempname(); return [ seq(`if (_.Set.isSet<_embedded>(${src})) `, ctx.block(() => [ seq(`${r} = new _.KeyedSet()`), seq(`for (const ${v} of ${src}) `, ctx.block(() => [ ... converterFor(ctx, M.anonymousSimplePattern(setPattern), v, vv => [`${r}.add(${vv})`, `continue`]), seq(`${r} = void 0`), seq(`break`)])), ctx.convertCapture(maybeName, r, ks)]))]; } case 'dictof': { const keyPattern = p.value.key; const valPattern = p.value.value; const r = ctx.gentemp(dictionaryType(ctx.mod, keyPattern, valPattern)); const v = ctx.gentempname(); const k = ctx.gentempname(); return [ seq(`if (_.Dictionary.isDictionary<_embedded>(${src})) `, ctx.block(() => [ seq(`${r} = new _.KeyedDictionary()`), seq(`for (const [${k}, ${v}] of ${src}) `, ctx.block(() => [ ... converterFor(ctx, M.anonymousSimplePattern(keyPattern), k, kk => converterFor(ctx, M.anonymousSimplePattern(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.value); if (arrayType === void 0) { return converterForCompound(ctx, p.value, src, recordFields, () => ks(void 0)); } else { return [converterForArray( ctx, M.NamedSimplePattern.anonymous(arrayType), src, !recordFields, ks)]; } } } } } export function converterForSimple( ctx: FunctionContext, p: M.SimplePattern, src: string, dest: string): Item[] { switch (p._variant) { case 'any': return [`${dest} = ${src}`]; case 'atom': { let test: Item; let valexp: Item = `${src}`; switch (p.atomKind._variant) { case 'Boolean': test = `typeof ${src} === 'boolean'`; break; case 'Float': test = `_.Float.isSingle(${src})`; valexp = `${src}.value`; break; case 'Double': test =`_.Float.isDouble(${src})`; valexp = `${src}.value`; break; case 'SignedInteger': test = `typeof ${src} === 'number'`; break; case 'String': test = `typeof ${src} === 'string'`; break; case 'ByteString': test = `_.Bytes.isBytes(${src})`; break; case 'Symbol': test = `typeof ${src} === 'symbol'`; break; } return [seq(`${dest} = `, test, ` ? `, valexp, ` : void 0`)]; } case 'lit': return [`${dest} = _.is(${src}, ${ctx.mod.literal(p.value)}) ? null : void 0`]; case 'Ref': return M.lookup(refPosition(p.value), p.value, ctx.mod.env, (_p) => [`${dest} = to${p.value.name.description!}(${src})`], (modId, modPath,_p) => { ctx.mod.imports.add([modId, modPath]); return [`${dest} = ${modId}.to${p.value.name.description!}(${src})`]; }); case 'embedded': return [`${dest} = _.isEmbedded<_embedded>(${src}) ? ${src}.embeddedValue : void 0`]; default: ((_p: never) => {})(p); throw new Error("Unreachable"); } } function converterForCompound( ctx: FunctionContext, p: M.CompoundPattern, src: string, recordFields: boolean, ks: () => Item[]): Item[] { switch (p._variant) { case 'rec': return [seq(`if (_.Record.isRecord<_val, _.Tuple<_val>, _embedded>(${src})) `, ctx.block(() => converterFor(ctx, p.label, `${src}.label`, () => converterFor(ctx, p.fields, src, ks, true))))]; case 'tuple': return converterForTuple(ctx, p.patterns, src, recordFields, void 0, ks); case 'tuple*': return converterForTuple(ctx, p.fixed, src, recordFields, p.variable, ks); case 'setof': case 'dictof': throw new Error('Internal error: setof and dictof are handled in converterFor()'); case 'dict': { const entries = Array.from(p.entries); 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.promoteNamedSimplePattern(M.addNameIfAbsent(n, k)), tmpSrc, () => loop(i + 1))))]; } else { return ks(); } } return [seq(`if (_.Dictionary.isDictionary<_embedded>(${src})) `, ctx.block(() => loop(0)))]; } default: ((_p: never) => {})(p); throw new Error("Unreachable"); } }