UNPKG

derw

Version:

An Elm-inspired language that transpiles to TypeScript

1,268 lines 51.8 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.generateTypescript = void 0; const Blocks_1 = require("../Blocks"); const Utils_1 = require("../Utils"); const builtins_1 = require("../builtins"); const List = __importStar(require("../stdlib/List")); const types_1 = require("../types"); const Common_1 = require("./Common"); const CommonToEcma_1 = require("./CommonToEcma"); function generateTypeArg(arg, imports) { return `${arg.name}: ${generateType(arg.type, imports)};`; } function generateTagCreator(tag, imports) { const typeDefArgs = (function (y) { return y.join("\n "); })(List.map(function (arg) { return generateTypeArg(arg, imports); }, tag.args)); const funcDefArgs = (function (y) { return y.join(", "); })(List.map(function (arg) { return `${arg.name}: ${generateType(arg.type, imports)}`; }, tag.args)); function filterTypes(arg) { const isBuiltin = (function () { switch (arg.kind) { case "FixedType": { const { name } = arg; return (0, builtins_1.isBuiltinType)(name); } case "GenericType": { const { name } = arg; return (0, builtins_1.isBuiltinType)(name); } case "FunctionType": { return false; } } })(); if (isBuiltin) { return false; } else { return true; } } const newArgs = List.filter(filterTypes, List.map(function (arg) { return arg.type; }, tag.args)); const tagType = (function (y) { return generateType(y, imports); })({ kind: "FixedType", name: tag.name, args: newArgs }); const funcDefArgsStr = tag.args.length === 0 ? "{}" : `{ ${funcDefArgs} }`; const typeDefStr = typeDefArgs === "" ? "" : "\n " + typeDefArgs; return (function (y) { return y.join("\n"); })([`type ${tagType} = {`, ` kind: "${tag.name}";${typeDefStr}`, "};", "", `function ${tagType}(args: ${funcDefArgsStr}): ${tagType} {`, " return {", ` kind: "${tag.name}",`, ` ...args,`, ` };`, "}"]); } function generateTag(tag, imports) { function filterTypes(arg) { const isBuiltin = (function () { switch (arg.kind) { case "FixedType": { const { name } = arg; return (0, builtins_1.isBuiltinType)(name); } case "GenericType": { const { name } = arg; return (0, builtins_1.isBuiltinType)(name); } case "FunctionType": { return false; } } })(); if (isBuiltin) { return false; } else { return true; } } const newArgs = List.filter(filterTypes, List.map(function (arg) { return arg.type; }, tag.args)); const tagType = (function (y) { return generateType(y, imports); })({ kind: "FixedType", name: tag.name, args: newArgs }); return tagType; } function generateUnionType(syntax, imports) { const tagCreators = (function (y) { return y.join("\n\n"); })(List.map(function (tag) { return generateTagCreator(tag, imports); }, syntax.tags)); const tags = (function (y) { return y.join(" | "); })(List.map(function (tag) { return generateTag(tag, imports); }, syntax.tags)); return (function (y) { return y.join("\n"); })([tagCreators, "", `type ${generateType(syntax.type, imports)} = ${tags};`]); } function generateUnionUntaggedType(syntax) { const values = (function (y) { return y.join(" | "); })(List.map(function (y) { return `"${y.body}"`; }, syntax.values)); return `type ${generateType(syntax.type, [])} = ${values};`; } function generateProperty(syntax, imports) { return `${syntax.name}: ${generateTopLevelType(syntax.type, imports)}`; } function generateTypeAlias(syntax, imports) { const generatedProperties = List.map(function (prop) { return generateProperty(prop, imports); }, syntax.properties); const properties = generatedProperties.length === 0 ? "" : ` ${generatedProperties.join(";\n ")};`; const typeDef = generateType(syntax.type, imports); const args = generatedProperties.length === 0 ? " " : ` ${generatedProperties.join(", ")} `; return (function (y) { return y.join("\n"); })([`type ${typeDef} = {`, properties, "}", "", `function ${typeDef}(args: {${args}}): ${typeDef} {`, " return {", " ...args,", " };", "}"]); } function generateListType(args, imports) { if (args.length > 0 && args[0].kind === "GenericType") { return `${generateType(args[0], imports)}[]`; } else { const fixedArgs = List.filter(function (type_) { return type_.kind === "FixedType"; }, args); const generatedArgs = List.map(function (arg) { return generateType(arg, imports); }, fixedArgs); switch (fixedArgs.length) { case 0: { return "any[]"; } case fixedArgs.length: { if (fixedArgs.length === 1) { const [x] = fixedArgs; return `${generateType(x, imports)}[]`; } } default: { return `(${generatedArgs.join(" | ")})[]`; } } ; } } function generateListTopLevelType(args, imports) { if (args.length > 0 && args[0].kind === "GenericType") { return `${generateTopLevelType(args[0], imports)}[]`; } else { const fixedArgs = List.filter(function (type_) { return type_.kind === "FixedType"; }, args); const generatedArgs = List.map(function (arg) { return generateTopLevelType(arg, imports); }, fixedArgs); switch (fixedArgs.length) { case 0: { return "any[]"; } case fixedArgs.length: { if (fixedArgs.length === 1) { const [x] = fixedArgs; return `${generateTopLevelType(x, imports)}[]`; } } default: { return `(${generatedArgs.join(" | ")})[]`; } } ; } } function getGenericTypesFromFunctionType(type_) { return List.filter(function (arg) { return arg.kind === "GenericType"; }, type_.args); } function modulesHasOverlap(type_, modules) { switch (modules.length) { case 0: { return false; } case modules.length: { if (modules.length >= 1) { const [module_, ...xs] = modules; switch (module_.alias.kind) { case "Just": { const { value } = module_.alias; if (value === type_.name) { return true; } else { return modulesHasOverlap(type_, xs); } ; } case "Nothing": { return modulesHasOverlap(type_, xs); } } ; } } default: { return false; } } } function typeHasOverlapWithImportedModule(type_, imports) { switch (imports.length) { case 0: { return false; } case imports.length: { if (imports.length >= 1) { const [import_, ...xs] = imports; if (modulesHasOverlap(type_, import_.modules)) { return true; } else { return typeHasOverlapWithImportedModule(type_, xs); } ; } } default: { return false; } } } function generateTopLevelType(type_, imports) { switch (type_.kind) { case "GenericType": { return generateType(type_, imports); } case "FixedType": { const { name, args } = type_; if (name === "List") { return generateListTopLevelType(args, imports); } else { if (args.length > 0 && args[0].kind === "FixedType" && args[0].args.length > 0) { const generatedArgs = List.map(function (arg) { return generateTopLevelType(arg, imports); }, args); return `${name}<${generatedArgs.join(", ")}>`; } else { function getGenericArgs(type_) { switch (type_.kind) { case "GenericType": { return [type_]; } case "FunctionType": { return getGenericTypesFromFunctionType(type_); } case "FixedType": { return [type_]; } case "ObjectLiteralType": { return []; } } } const genericArgs = List.foldl(function (arg, xs) { return List.append(xs, getGenericArgs(arg)); }, [], args); const generatedGenericArgs = List.map(function (arg) { return generateType(arg, imports); }, genericArgs); const qualifiedName = typeHasOverlapWithImportedModule(type_, imports) ? `${type_.name}.${type_.name}` : type_.name; if (genericArgs.length === 0) { return qualifiedName; } else { return `${qualifiedName}<${generatedGenericArgs.join(", ")}>`; } ; } ; } ; } case "FunctionType": { const { args } = type_; const typeToReturn = generateType(args[args.length - 1], imports); const parts = List.indexedMap(function (arg, index) { return `arg${index}: ${generateTopLevelType(arg, imports)}`; }, args.slice(0, -1)); return `(${parts.join(", ")}) => ${typeToReturn}`; } case "ObjectLiteralType": { return ``; } } } function getGenericTypes(type_) { switch (type_.kind) { case "GenericType": { return [type_]; } case "FunctionType": { return getGenericTypesFromFunctionType(type_); } case "FixedType": { const { args } = type_; return List.foldl(function (newType, collection) { return List.append(collection, getGenericTypes(newType)); }, [], args); } case "ObjectLiteralType": { return []; } } } function removeDuplicateTypes(xs) { switch (xs.length) { case 0: { return []; } case xs.length: { if (xs.length >= 1) { const [x, ...rest] = xs; const restNames = List.map(function (y) { return y.name; }, rest); if (restNames.includes(x.name)) { return removeDuplicateTypes(rest); } else { return [x, ...removeDuplicateTypes(rest)]; } ; } } default: { return []; } } } function generateType(type_, imports) { switch (type_.kind) { case "GenericType": { const { name } = type_; return name; } case "FixedType": { const { name, args } = type_; if (name === "List") { return generateListType(args, imports); } else { const genericArgs = removeDuplicateTypes(List.foldl(function (arg, xs) { return List.append(xs, getGenericTypes(arg)); }, [], args)); const generatedGenericArgs = List.map(function (arg) { return generateType(arg, imports); }, genericArgs); const qualifiedName = typeHasOverlapWithImportedModule(type_, imports) ? `${type_.name}.${type_.name}` : type_.name; if (genericArgs.length === 0) { return qualifiedName; } else { return `${qualifiedName}<${generatedGenericArgs.join(", ")}>`; } ; } ; } case "FunctionType": { const { args } = type_; const typeToReturn = generateType(args[args.length - 1], imports); const parts = List.indexedMap(function (arg, index) { return `arg${index}: ${generateType(arg, imports)}`; }, args.slice(0, -1)); return `(${parts.join(", ")}) => ${typeToReturn}`; } case "ObjectLiteralType": { return ``; } } } function generateField(field) { const value = generateExpression(field.value); if (field.name === value) { return field.name; } else { return `${field.name}: ${value}`; } } function generateObjectLiteral(literal) { const fields = (function (y) { return y.join(",\n "); })(literal.fields.map(generateField)); if (literal.base === null) { switch (literal.fields.length) { case 0: { return "{ }"; } case literal.fields.length: { if (literal.fields.length === 1) { const [x] = literal.fields; return `{ ${fields} }`; } } default: { return `{\n ${fields}\n}`; } } ; } else { switch (literal.fields.length) { case 0: { return `{ ${literal.base.body} }`; } case literal.fields.length: { if (literal.fields.length === 1) { const [x] = literal.fields; return `{ ${literal.base.body}, ${fields} }`; } } default: { return `{\n ${literal.base.body},\n ${fields}\n}`; } } ; } } function generateListValue(list) { function generator(expression) { switch (expression.kind) { case "IfStatement": { return generateInlineIf(expression); } case "CaseStatement": { return generateInlineCase(expression); } default: { return generateExpression(expression); } } } const items = List.map(generator, list.items); switch (items.length) { case 0: { return "[ ]"; } case items.length: { if (items.length === 1) { const [x] = items; return `[ ${x} ]`; } } default: { return `[ ${items.join(", ")} ]`; } } } function generateLetBlock(body, parentTypeArguments, imports) { switch (body.length) { case 0: { return ""; } case body.length: { if (body.length >= 1) { const [x, ...ys] = body; const prefixedBody = (function (y) { return y.join("\n"); })(List.map(function (block) { return generateBlock(block, parentTypeArguments, [], imports); }, body)); const prefixedLines = (0, Common_1.prefixLines)(prefixedBody, 4); return `\n${prefixedLines}`; } } default: { return ""; } } } function generateElseIfStatement(elseIfStatement, parentTypeArguments) { const isSimpleBody = (0, types_1.isSimpleValue)(elseIfStatement.body.kind); const bodyPrefix = isSimpleBody ? "return " : ""; const predicate = generateExpression(elseIfStatement.predicate); const body = (function (y) { return (0, Common_1.prefixLines)(y, 4); })((function (y) { return bodyPrefix + y; })(generateExpression(elseIfStatement.body, parentTypeArguments))); const bodySuffix = isSimpleBody ? ";" : ""; const maybeLetBody = generateLetBlock(elseIfStatement.letBody, parentTypeArguments, []); return `} else if (${predicate}) {${maybeLetBody}\n${body}${bodySuffix}`; } function generateIfStatement(ifStatement, parentTypeArguments, isAsync, neverSimple) { const isSimpleIfBody = neverSimple ? false : (0, types_1.isSimpleValue)(ifStatement.ifBody.kind); const isSimpleElseBody = neverSimple ? false : (0, types_1.isSimpleValue)(ifStatement.elseBody.kind); const ifBodyPrefix = isSimpleIfBody ? "return " : ""; const asyncPrefix = isAsync ? "await " : ""; const elseBodyPrefix = isSimpleElseBody ? "return " : ""; const maybeIfLetBody = generateLetBlock(ifStatement.ifLetBody, parentTypeArguments, []); const maybeElseLetBody = generateLetBlock(ifStatement.elseLetBody, parentTypeArguments, []); const predicate = generateExpression(ifStatement.predicate); const ifBody = generateExpression(ifStatement.ifBody, parentTypeArguments); const indentedIfBody = (function () { const _res1668698078 = ifBody.split("\n"); switch (_res1668698078.length) { case 0: { return ifBody; } case _res1668698078.length: { if (_res1668698078.length === 1) { const [x] = _res1668698078; return ifBody; } } case _res1668698078.length: { if (_res1668698078.length >= 1) { const [x, ...xs] = _res1668698078; return (function (y) { return y.join("\n"); })([x, (0, Common_1.prefixLines)(xs.join("\n"), 4)]); } } default: { return ifBody; } } })(); const elseBody = generateExpression(ifStatement.elseBody, parentTypeArguments); const indentedElseBody = (function () { const _res415623174 = elseBody.split("\n"); switch (_res415623174.length) { case 0: { return elseBody; } case _res415623174.length: { if (_res415623174.length === 1) { const [x] = _res415623174; return elseBody; } } case _res415623174.length: { if (_res415623174.length >= 1) { const [x, ...xs] = _res415623174; return (function (y) { return y.join("\n"); })([x, (0, Common_1.prefixLines)(xs.join("\n"), 4)]); } } default: { return elseBody; } } })(); const elseIfs = (function (y) { return y.join("\n"); })(List.map(function (elseIf) { return generateElseIfStatement(elseIf, parentTypeArguments); }, ifStatement.elseIf)); const prefixedElseIfs = elseIfs === "" ? "}" : `${elseIfs}\n}`; return `if (${predicate}) {${maybeIfLetBody}\n ${ifBodyPrefix}${asyncPrefix}${indentedIfBody};\n${prefixedElseIfs} else {${maybeElseLetBody}\n ${elseBodyPrefix}${asyncPrefix}${indentedElseBody};\n}`; } function generateConstructor(constructor) { switch (constructor.pattern.fields.length) { case 0: { return `${constructor.constructor}({ })`; } default: { return `${constructor.constructor}(${generateObjectLiteral(constructor.pattern)})`; } } } const replaceKey = "$REPLACE_ME"; function GapsInfo(args) { return Object.assign({}, args); } function generateListDestructurePartWithGaps(predicate, parts, part, info) { if (info.partIndex < info.currentIndex) { return Object.assign(Object.assign({}, info), { partIndex: info.partIndex + 1 }); } else { const index = info.currentIndex; const output = info.output; const isLastValue = index === parts.length - 1; switch (part.kind) { case "Destructure": { const isNextAValue = isLastValue ? false : parts[index + 1].kind === "Value"; const hasADestructureAfter = index < parts.length - 2 ? parts[index + 2].kind === "Destructure" : false; if (isNextAValue && hasADestructureAfter) { const nextValue = (function (y) { return y; })(parts[index + 1]); const destructorAfter = (function (y) { return y; })(parts[index + 2]); return (function (y) { return Object.assign(Object.assign({}, info), { output: y, partIndex: info.partIndex + 1, currentIndex: info.currentIndex + 2 }); })((function (y) { return (0, Common_1.prefixLines)(y, 8); })((function (y) { return y.join("\n"); })([`const [ _0, ..._rest ] = ${predicate};`, `if (_0.kind === "${part.constructor}") {`, ` let _foundIndex: number = -1;`, ` for (let _i = 0; _i < _rest.length; _i++) {`, ` if (_rest[_i].kind === "${destructorAfter.constructor}") {`, ` _foundIndex = _i;`, ` break;`, ` }`, ` }`, ``, ` if (_foundIndex > -1) {`, ` const ${nextValue.body} = _rest.slice(0, _foundIndex);`, ` ${replaceKey}`, ` }`, `}`]))); } else { return Object.assign(Object.assign({}, info), { partIndex: info.partIndex + 1 }); } ; } case "Value": { const newOutput = output.length === 0 ? `\nconst ${part.body} = _rest;\n ` : (parts[info.partIndex - 1].kind === "Destructure" ? output.replace(replaceKey, `const ${part.body} = _rest.slice(_foundIndex, _rest.length);\n${replaceKey}`) : output.replace(replaceKey, `const ${part.body} = _rest;\n${replaceKey}`)); return Object.assign(Object.assign({}, info), { output: newOutput, partIndex: info.partIndex + 1, currentIndex: info.currentIndex + 1 }); } default: { return Object.assign(Object.assign({}, info), { currentIndex: info.currentIndex + 1, partIndex: info.partIndex + 1 }); } } ; } } function generateListDestructureWithGaps(predicate, branch, pattern) { const isFinalEmptyList = pattern.parts[pattern.parts.length - 1].kind === "EmptyList"; const partsWithLength = (0, Common_1.destructureLength)(pattern); const startingInfo = { output: "", partIndex: 0, currentIndex: 0 }; const parts = (function (y) { return y.output; })(List.foldl(function (x, info) { return generateListDestructurePartWithGaps(predicate, pattern.parts, x, info); }, startingInfo, pattern.parts)); const conditional = isFinalEmptyList ? `${predicate}.length === ${partsWithLength}` : `${predicate}.length >= ${partsWithLength}`; const isSimpleBody = (0, types_1.isSimpleValue)(branch.body.kind); const wrapper = isSimpleBody ? ` return ` : ""; const bodyIndent = isSimpleBody ? 0 : 4; const body = (function (y) { return (0, Common_1.prefixLines)(y, bodyIndent); })(generateExpression(branch.body)); const inner = (0, Common_1.prefixLines)(`${wrapper}${body};`, 12); return `case ${predicate}.length: {\n if (${conditional}) {\n${parts.replace(replaceKey, inner)}\n }\n}`; } function generateBranch(predicate, branch, parentTypeArguments) { const isSimpleBody = (0, types_1.isSimpleValue)(branch.body.kind); const wrapper = isSimpleBody ? " return " : ""; const maybeLetBody = generateLetBlock(branch.letBody, parentTypeArguments, []); const bodyIndent = isSimpleBody ? 0 : 4; const branchBody = (function (y) { return (0, Common_1.prefixLines)(y, bodyIndent); })(generateExpression(branch.body, parentTypeArguments)); switch (branch.pattern.kind) { case "Destructure": { const { pattern } = branch.pattern; const generatedPattern = pattern.trim().length > 0 ? `\n const ${pattern} = ${predicate};` : ""; return `case "${branch.pattern.constructor}": {${generatedPattern}${maybeLetBody}\n${wrapper}${branchBody};\n}`; } case "StringValue": { const { body } = branch.pattern; return `case "${body}": {${maybeLetBody}\n${wrapper}${branchBody};\n}`; } case "FormatStringValue": { const { body } = branch.pattern; return `case ` + "`" + body + "`" + `: {${maybeLetBody}\n${wrapper}${branchBody};\n}`; } case "EmptyList": { return `case 0: {${maybeLetBody}\n${wrapper}${branchBody};\n}`; } case "ListDestructure": { const { parts } = branch.pattern; const length = parts.length; const isFinalEmptyList = parts[length - 1].kind === "EmptyList"; const partsWithLength = (0, Common_1.destructureLength)(branch.pattern); const hasGaps = (0, Common_1.patternHasGaps)(branch.pattern); const gapPositions = (0, Common_1.patternGapPositions)(branch.pattern); const isOnlyFinalGap = gapPositions.length === 1 && gapPositions[0] === length - 1; function not(value) { if (value) { return false; } else { return true; } } const conditional = isFinalEmptyList && not(hasGaps) ? `${predicate}.length === ${partsWithLength}` : `${predicate}.length >= ${partsWithLength}`; const firstPart = parts[0]; const isFirstDestructure = firstPart.kind === "Destructure"; if (hasGaps && not(isOnlyFinalGap)) { return generateListDestructureWithGaps(predicate, branch, branch.pattern); } else { if (isFirstDestructure) { const destructors = List.filter(function (t) { return t.kind === "Destructure"; }, parts); const destructorParts = List.indexedMap(function (_, i) { return `_${i}`; }, destructors); const allButFinalPart = List.map(CommonToEcma_1.generateListDestructurePart, parts.slice(destructorParts.length, -1)); const generatedParts = List.append(destructorParts, allButFinalPart); const joinedGeneratedParts = generatedParts.join(", "); const partsString = isFinalEmptyList ? joinedGeneratedParts : `${joinedGeneratedParts}, ...${(0, CommonToEcma_1.generateListDestructurePart)(parts[length - 1])}`; const conditionals = List.indexedMap(function (destructor, index) { return `_${index}.kind === "${destructor.constructor}"`; }, destructors); const joinedConditionals = conditionals.join(" && "); function unpackFn(destructor, index) { if (destructor.pattern.length === 0) { return ""; } else { return `\n const ${destructor.pattern} = _${index};`; } } const unpacked = List.indexedMap(unpackFn, destructors); const joinedUnpacked = unpacked.length === 0 ? "" : unpacked.join(""); return (function (y) { return y.join("\n"); })([`case ${predicate}.length: {`, ` if (${conditional}) {`, ` const [ ${partsString} ] = ${predicate};`, ` if (${joinedConditionals}) {${joinedUnpacked}${maybeLetBody ? (0, Common_1.prefixLines)(maybeLetBody, 8) : ""}`, ` ${wrapper}${branchBody};`, ` }`, ` }`, `}`]); } else { const isFirstValue = firstPart.kind === "StringValue" || firstPart.kind === "FormatStringValue"; const partsToGenerate = isFirstValue ? [{ kind: "Value", body: "_temp" }, ...branch.pattern.parts.slice(1, -1)] : branch.pattern.parts.slice(0, -1); const generatedParts = List.map(CommonToEcma_1.generateListDestructurePart, partsToGenerate); const joinedParts = generatedParts.join(", "); const partsString = isFinalEmptyList ? joinedParts : `${joinedParts}, ...${(0, CommonToEcma_1.generateListDestructurePart)(branch.pattern.parts[length - 1])}`; if (isFirstValue) { const tempConditional = (function () { switch (firstPart.kind) { case "StringValue": { const { body } = firstPart; return `"${body}"`; } case "FormatStringValue": { const { body } = firstPart; return "`" + body + "`"; } default: { return ""; } } })(); return (function (y) { return y.join("\n"); })([`case ${predicate}.length: {`, ` if (${conditional}) {`, ` const [ ${partsString} ] = ${predicate};${maybeLetBody ? (0, Common_1.prefixLines)(maybeLetBody, 4) : ""}`, ` if (_temp === ${tempConditional}) {`, ` ${wrapper}${branchBody};`, ` }`, ` }`, `}`]); } else { return (function (y) { return y.join("\n"); })([`case ${predicate}.length: {`, ` if (${conditional}) {`, ` const [ ${partsString} ] = ${predicate};${maybeLetBody ? (0, Common_1.prefixLines)(maybeLetBody, 4) : ""}`, ` ${wrapper}${branchBody};`, ` }`, `}`]); } ; } ; } ; } case "Default": { return `default: {${maybeLetBody}\n${wrapper}${branchBody};\n}`; } } } function isModuleReferenceToAValue(moduleReference) { return moduleReference.value.kind === "Value"; } function generateCaseStatement(caseStatement, parentTypeArguments) { const predicate = generateExpression(caseStatement.predicate); const isValue = (function () { switch (caseStatement.predicate.kind) { case "Value": { return true; } case "ModuleReference": { return isModuleReferenceToAValue(caseStatement.predicate); } default: { return false; } } })(); const name = isValue ? predicate : `_res${(0, Utils_1.hashCode)(predicate)}`; const maybePredicateAssignment = isValue ? "" : `const ${name} = ${predicate};\n`; const branches = (function (y) { return (0, Common_1.prefixLines)(y, 4); })((function (y) { return y.join("\n"); })(List.map(function (branch) { return generateBranch(name, branch, parentTypeArguments); }, caseStatement.branches))); const isString = (function (y) { return y.length > 0; })(List.filter(function (branch) { return branch.pattern.kind === "StringValue"; }, caseStatement.branches)); const isList = (function (y) { return y.length > 0; })(List.filter(function (branch) { return branch.pattern.kind === "EmptyList" || branch.pattern.kind === "ListDestructure"; }, caseStatement.branches)); if (isString) { return `${maybePredicateAssignment}switch (${name}) {\n${branches}\n}`; } else { if (isList) { return `${maybePredicateAssignment}switch (${name}.length) {\n${branches}\n}`; } else { return `${maybePredicateAssignment}switch (${name}.kind) {\n${branches}\n}`; } ; } } function generateAddition(addition) { const left = generateExpression(addition.left); const right = generateExpression(addition.right); return `${left} + ${right}`; } function generateSubtraction(subtraction) { const left = generateExpression(subtraction.left); const right = generateExpression(subtraction.right); return `${left} - ${right}`; } function generateMultiplication(multiplication) { const left = generateExpression(multiplication.left); const right = generateExpression(multiplication.right); return `${left} * ${right}`; } function generateDivision(division) { const left = generateExpression(division.left); const right = generateExpression(division.right); return `${left} / ${right}`; } function generateMod(mod) { const left = generateExpression(mod.left); const right = generateExpression(mod.right); return `${left} % ${right}`; } function generateLeftPipe(leftPipe) { return generateExpression((0, CommonToEcma_1.flattenLeftPipe)(leftPipe)); } function generateRightPipe(rightPipe) { const left = generateExpression(rightPipe.left); const right = generateExpression(rightPipe.right); return `${left}(${right})`; } function generateModuleReference(moduleReference) { switch (moduleReference.path.length) { case 0: { return `(arg0) => arg0.${generateExpression(moduleReference.value)}`; } default: { const left = moduleReference.path.join("."); const right = generateExpression(moduleReference.value); return `${left}.${right}`; } } } function generateFunctionCall(functionCall) { const args = (function (y) { return y.join(", "); })(List.map(generateExpression, functionCall.args)); return `${functionCall.name}(${args})`; } function generateLambda(lambda) { const isSimple = (0, types_1.isSimpleValue)(lambda.body.kind); const args = (function (y) { return y.join(", "); })(List.map(function (arg) { return `${arg}: any`; }, lambda.args)); const body = isSimple ? generateExpression(lambda.body) : (function (y) { return (0, Common_1.prefixLines)(y, 4); })(generateExpression(lambda.body)); if (isSimple) { return `function(${args}) {\n return ${body};\n}`; } else { return `function(${args}) {\n${body}\n}`; } } function generateLambdaCall(lambdaCall) { const args = (function (y) { return y.join(", "); })(List.map(function (arg) { return `${arg}: any`; }, lambdaCall.lambda.args)); const argsValues = (function (y) { return y.join(", "); })(List.map(generateExpression, lambdaCall.args)); const body = generateExpression(lambdaCall.lambda.body); return `(function(${args}) {\n return ${body};\n})(${argsValues})`; } function generateEquality(equality) { const left = generateExpression(equality.left); const right = generateExpression(equality.right); return `${left} === ${right}`; } function generateInEquality(inEquality) { const left = generateExpression(inEquality.left); const right = generateExpression(inEquality.right); return `${left} !== ${right}`; } function generateLessThan(lessThan) { const left = generateExpression(lessThan.left); const right = generateExpression(lessThan.right); return `${left} < ${right}`; } function generateLessThanOrEqual(lessThanOrEqual) { const left = generateExpression(lessThanOrEqual.left); const right = generateExpression(lessThanOrEqual.right); return `${left} <= ${right}`; } function generateGreaterThan(greaterThan) { const left = generateExpression(greaterThan.left); const right = generateExpression(greaterThan.right); return `${left} > ${right}`; } function generateGreaterThanOrEqual(greaterThanOrEqual) { const left = generateExpression(greaterThanOrEqual.left); const right = generateExpression(greaterThanOrEqual.right); return `${left} >= ${right}`; } function generateAnd(and) { const left = generateExpression(and.left); const right = generateExpression(and.right); return `${left} && ${right}`; } function generateOr(or) { const left = generateExpression(or.left); const right = generateExpression(or.right); return `${left} || ${right}`; } function generateListPrepend(prepend) { const left = generateExpression(prepend.left); const right = generateExpression(prepend.right); return `[ ${left}, ...${right} ]`; } function generateExpression(expression, parentTypeArguments, parentTypes) { switch (expression.kind) { case "Value": { return (0, CommonToEcma_1.generateValue)(expression); } case "StringValue": { return (0, CommonToEcma_1.generateStringValue)(expression); } case "FormatStringValue": { return (0, CommonToEcma_1.generateFormatStringValue)(expression); } case "ListValue": { return generateListValue(expression); } case "ListRange": { return (0, CommonToEcma_1.generateListRange)(expression); } case "ObjectLiteral": { return generateObjectLiteral(expression); } case "IfStatement": { const actualParentTypeArguments = parentTypeArguments || []; return generateIfStatement(expression, actualParentTypeArguments, false, false); } case "CaseStatement": { const actualParentTypeArguments = parentTypeArguments || []; return generateCaseStatement(expression, actualParentTypeArguments); } case "Addition": { return generateAddition(expression); } case "Subtraction": { return generateSubtraction(expression); } case "Multiplication": { return generateMultiplication(expression); } case "Division": { return generateDivision(expression); } case "Mod": { return generateMod(expression); } case "And": { return generateAnd(expression); } case "Or": { return generateOr(expression); } case "ListPrepend": { return generateListPrepend(expression); } case "LeftPipe": { return generateLeftPipe(expression); } case "RightPipe": { return generateRightPipe(expression); } case "ModuleReference": { return generateModuleReference(expression); } case "FunctionCall": { return generateFunctionCall(expression); } case "Lambda": { return generateLambda(expression); } case "LambdaCall": { return generateLambdaCall(expression); } case "Constructor": { return generateConstructor(expression); } case "Equality": { return generateEquality(expression); } case "InEquality": { return generateInEquality(expression); } case "LessThan": { return generateLessThan(expression); } case "LessThanOrEqual": { return generateLessThanOrEqual(expression); } case "GreaterThan": { return generateGreaterThan(expression); } case "GreaterThanOrEqual": { return generateGreaterThanOrEqual(expression); } } } function collectTypeArguments(type_) { switch (type_.kind) { case "GenericType": { const { name } = type_; if ((0, builtins_1.isBuiltinType)(name)) { return []; } else { return [name]; } ; } case "FixedType": { const { name, args } = type_; if ((0, builtins_1.isBuiltinType)(name)) { return []; } else { return List.foldl(function (xs, ys) { return List.append(ys, xs); }, [], List.map(collectTypeArguments, args)); } ; } case "FunctionType": { const { args } = type_; return List.foldl(function (xs, ys) { return List.append(ys, xs); }, [], List.map(collectTypeArguments, args)); } case "ObjectLiteralType": { return []; } } } function generateDoExpression(expression, parentTypeArguments, parentTypes, imports) { switch (expression.kind) { case "Const": { return generateConst(expression, imports, true); } case "Function": { return generateFunction(expression, parentTypeArguments, parentTypes, imports); } case "FunctionCall": { return (function (y) { return "await " + y + ";"; })(generateFunctionCall(expression)); } case "ModuleReference": { return (function (y) { return "await " + y + ";"; })(generateModuleReference(expression)); } case "IfStatement": { return generateIfStatement(expression, parentTypeArguments, true, true); } } } function generateDoBlock(doBody, parentTypeArguments, parentTypes, imports) { return (function (y) { return y.join("\n"); })(List.map(function (expression) { return generateDoExpression(expression, parentTypeArguments, parentTypes, imports); }, doBody.expressions)); } function generateFunctionArg(arg, imports) { switch (arg.kind) { case "FunctionArg": { const { name, type } = arg; return `${name}: ${generateTopLevelType(type, imports)}`; } case "AnonFunctionArg": { const { index, type } = arg; return `_${index}: ${generateTopLevelType(type, imports)}`; } } } function generateFunction(function_, parentTypeArguments, parentTypes, imports) { const args = (function (y) { return y.join(", "); })(List.map(function (arg) { return generateFunctionArg(arg, imports); }, function_.args)); const flattenedArgTypeArguments = List.foldl(function (xs, ys) { return List.append(ys, xs); }, [], List.map(function (arg) { return collectTypeArguments(arg.type); }, function_.args)); const typeArguments = List.append(flattenedArgTypeArguments, collectTypeArguments(function_.returnType)); const filteredTypeArguments = typeArguments.filter(function (value, index, arr) { return arr.indexOf(value) === index && parentTypeArguments.indexOf(value) === -1; }); const maybeLetBody = generateLetBlock(function_.letBody, List.append(filteredTypeArguments, parentTypeArguments), imports); const isAsync = function_.doBody !== null; const maybeAsyncPrefix = isAsync ? "async " : ""; const maybeDoBody = function_.doBody !== null ? (function (y) { return `\n${(0, Common_1.prefixLines)(y, 4)}`; })(generateDoBlock(function_.doBody, parentTypeArguments, parentTypes, imports)) : ""; const returnType = isAsync ? generateTopLevelType({ kind: "FixedType", name: "Promise", args: [function_.returnType] }, imports) : generateTopLevelType(function_.returnType, imports); const isSimpleBody = (0, types_1.isSimpleValue)(function_.body.kind); const bodyPrefix = isSimpleBody ? "return " : ""; const bodySuffix = isSimpleBody ? ";" : ""; const joinedTypeArguments = List.append(typeArguments, parentTypeArguments); const allParentTypes = List.append(parentTypes, [function_.returnType]); const body = (function (y) { return (0, Common_1.prefixLines)(y, 4); })((function (y) { return bodyPrefix + y + bodySuffix; })(generateExpression(function_.body, joinedTypeArguments, allParentTypes))); const typeArgumentsString = filteredTypeArguments.length === 0 ? "" : `<${filteredTypeArguments.join(", ")}>`; return (function (y) { return y.join("\n"); })([`${maybeAsyncPrefix}function ${function_.name}${typeArgumentsString}(${args}): ${returnType} {${maybeLetBody}${maybeDoBody}`, `${body}`, `}`]); } function generateInlineIf(expression) { const ifBody = (function () { switch (expression.ifBody.kind) { case "IfStatement": { return `( ${generateInlineIf(expression.ifBody)} )`; } default: { return generateExpression(expression.ifBody); } } })(); const elseBody = (function () { switch (expression.elseBody.kind) { case "IfStatement": { return `( ${generateInlineIf(expression.elseBody)} )`; } default: { return generateExpression(expression.elseBody); } } })(); return `${generateExpression(expression.predicate)} ? ${ifBody} : ${elseBody}`; } function generateInlineCase(expression) { return `(function (): any {\n${(0, Common_1.prefixLines)(generateExpression(expression), 4)}\n})()`; } function generateNestedConst(constDef, body, imports) { const typeDef = generateTopLevelType(constDef.type, imports); const generatedBlocks = List.map(function (block) { return generateBlock(block, [], [], imports); }, constDef.letBody); const joinedBlocks = (function (y) { return (0, Common_1.prefixLines)(y, 4); })(generatedBlocks.join("\n")); return `(function(): ${typeDef} {\n${joinedBlocks}\n return ${body};\n})()`; } function generateConst(constDef, imports, isAsync) { const body = (function () { switch (constDef.value.kind) { case "IfStatement": { if (constDef.letBody.length === 0) { return generateInlineIf(constDef.value); } else { return generateNestedConst(constDef, generateInlineIf(constDef.value), imports); } ; } case "CaseStatement": { if (constDef.letBody.length === 0) { return generateInlineCase(constDef.value); } else { return generateNestedConst(constDef, generateInlineCase(constDef.value), imports); } ; } default: { if (constDef.letBody.length === 0) { return generateExpression(constDef.value); } else { return generateNestedConst(constDef, generateExpression(constDef.value), imports); } ; } } })(); const maybeAsyncPrefix = isAsync ? "await " : ""; const typeDef = generateTopLevelType(constDef.type, imports); return `const ${constDef.name}: ${typeDef} = ${maybeAsyncPrefix}${body};`; } function generateBlock(syntax, parentTypeArguments, parentTypes, im