UNPKG

derw

Version:

An Elm-inspired language that transpiles to TypeScript

859 lines (858 loc) 27.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.generateElm = void 0; const List = __importStar(require("../stdlib/List")); const types_1 = require("../types"); const Common_1 = require("./Common"); function generateTag(tag) { function generateTypeArg(arg) { return (function (type_) { return arg.name + ": " + type_ + ""; })(generateType(arg.type)); } const typeDefArgs = (function (y) { return y.join(",\n "); })(List.map(generateTypeArg, tag.args)); const funcDefArgsStr = tag.args.length > 0 ? ` { ${typeDefArgs} }` : ""; return (function (y) { return y + funcDefArgsStr; })(generateType({ kind: "FixedType", name: tag.name, args: [] })); } function generateUnionType(syntax) { const tags = (function (y) { return y.join("\n| "); })(List.map(generateTag, syntax.tags)); const prefixed = (0, Common_1.prefixLines)(tags, 4); return `type ${generateType(syntax.type)} =\n${prefixed}`; } function generateUnionUntaggedType(syntax) { const values = (function (y) { return y.join("\n| "); })(List.map(generateStringValue, syntax.values)); const prefixed = (0, Common_1.prefixLines)(values, 4); return `type ${generateType(syntax.type)} =\n${prefixed}`; } function generateProperty(syntax) { const generatedType = generateTopLevelType(syntax.type); switch (syntax.type.kind) { case "FunctionType": { return `${syntax.name}: ${generatedType.slice(1, -1)}`; } default: { return `${syntax.name}: ${generatedType}`; } } } function generateTypeAlias(syntax) { const properties = (function (y) { return y.join(",\n "); })(List.map(generateProperty, syntax.properties)); const typeDef = generateType(syntax.type); if (syntax.properties.length === 0) { return `type alias ${typeDef} = {\n}`; } else { return `type alias ${typeDef} = {\n ${properties}\n}`; } } function generateListType(args) { if (args.length > 0 && args[0].kind === "GenericType") { return `List ${generateType(args[0])}`; } else { const fixedArgs = List.filter(function (type_) { return type_.kind === "FixedType"; }, args); switch (fixedArgs.length) { case 0: { return "List any"; } case fixedArgs.length: { if (fixedArgs.length === 1) { const [x] = fixedArgs; if (x.kind === "FixedType" && x.args.length > 0) { return `List (${generateType(x)})`; } else { return `List ${generateType(x)}`; } ; } } default: { return `List (${fixedArgs.map(generateType).join(" | ")})`; } } ; } } const typeMap = { boolean: "Bool", number: "Float", string: "String", void: "String" }; function typeMapNameLookup(name) { if (typeMap[name]) { return typeMap[name]; } else { return name; } } function generateTopLevelType(type_) { switch (type_.kind) { case "GenericType": { return generateType(type_); } case "FixedType": { const { name, args } = type_; if (args.length > 0 && args[0].kind === "FixedType" && args[0].args.length > 0) { return `${name} (${args.map(generateTopLevelType).join(" ")})`; } else { const genericArgs = List.filter(function (type_) { return type_.kind === "GenericType" || type_.kind === "FixedType"; }, args); if (genericArgs.length === 0) { return typeMapNameLookup(name); } else { return `${name} ${genericArgs.map(generateType).join(" ")}`; } ; } ; } case "FunctionType": { const { args } = type_; return `(${args.map(generateTopLevelType).join(" -> ")})`; } case "ObjectLiteralType": { return ``; } } } function generateType(type_) { switch (type_.kind) { case "GenericType": { const { name } = type_; return typeMapNameLookup(name); } case "FixedType": { const { name, args } = type_; if (name === "List") { return generateListType(args); } else { const genericArgs = List.filter(function (type_) { return type_.kind === "GenericType"; }, args); if (genericArgs.length === 0) { return typeMapNameLookup(name); } else { return `${name} ${genericArgs.map(generateType).join(" ")}`; } ; } ; } case "FunctionType": { const { args } = type_; return `(${args.map(generateType).join(" -> ")})`; } case "ObjectLiteralType": { return ``; } } } function generateField(field) { const value = generateExpression(field.value); return `${field.name} = ${value}`; } function generateObjectLiteral(literal) { const fields = (function (y) { return y.join(",\n "); })(literal.fields.map(generateField)); if (literal.base === null) { if (literal.fields.length === 1) { return `{ ${fields} }`; } else { return `{\n ${fields}\n}`; } ; } else { const baseWithoutDots = `${literal.base.body.split("...")[1]}`; if (literal.fields.length === 1) { return `{ ${baseWithoutDots} | ${fields} }`; } else { return `{\n ${baseWithoutDots} |\n ${fields}\n}`; } ; } } function generateValue(value) { switch (value.body) { case "true": { return "True"; } case "false": { return "False"; } default: { return value.body; } } } function generateStringValue(string) { return `"${string.body}"`; } function generateFormatStringValue(string) { return `"${string.body}"`; } function generateListValue(list) { switch (list.items.length) { case 0: { return "[ ]"; } case list.items.length: { if (list.items.length === 1) { const [x] = list.items; return `[ ${generateExpression(x)} ]`; } } default: { return `[\n${(0, Common_1.prefixLines)(list.items.map(generateExpression).join(",\n"), 4)}\n]`; } } } function generateListRange(list) { return `[ ${list.start.body}..${list.end.body} ]`; } function generateLetBlock(body) { switch (body.length) { case 0: { return ""; } case body.length: { if (body.length >= 1) { const [x, ...ys] = body; const prefixedLet = (0, Common_1.prefixLines)("\nlet", 4); const prefixedBody = (function (y) { return y.join("\n\n"); })(List.map(generateBlock, body)); const prefixedLines = (0, Common_1.prefixLines)(prefixedBody, 8); const prefixedIn = (0, Common_1.prefixLines)("\nin", 4); return `${prefixedLet}\n${prefixedLines}${prefixedIn}${(0, Common_1.prefixLines)("", 8)}`; } } default: { return ""; } } } function generateIfStatement(ifStatement) { const maybeIfLetBody = generateLetBlock(ifStatement.ifLetBody); const maybeElseLetBody = generateLetBlock(ifStatement.elseLetBody); const predicate = generateExpression(ifStatement.predicate); const ifIndent = maybeIfLetBody === "" ? 4 : 8; const ifBody = (function (lines) { return (0, Common_1.prefixLines)(lines, ifIndent); })(generateExpression(ifStatement.ifBody)); const elseIndent = maybeElseLetBody === "" ? 4 : 8; const elseBody = (function (lines) { return (0, Common_1.prefixLines)(lines, elseIndent); })(generateExpression(ifStatement.elseBody)); return `if ${predicate} then${maybeIfLetBody}\n${ifBody}\nelse${maybeElseLetBody}\n${elseBody}`; } function generateConstructor(constructor) { switch (constructor.pattern.fields.length) { case 0: { return constructor.constructor; } default: { return `${constructor.constructor} ${generateObjectLiteral(constructor.pattern)}`; } } } function generateListDestructurePart(part) { switch (part.kind) { case "EmptyList": { return "[]"; } case "StringValue": { const { body } = part; return `"${body}"`; } case "FormatStringValue": { const { body } = part; return "`" + body + "`"; } case "Value": { const { body } = part; return body; } case "Destructure": { const { pattern } = part; if (pattern.length === 0) { return part.constructor; } else { return `${part.constructor} ${pattern}`; } ; } } } function generateBranchPattern(branchPattern) { switch (branchPattern.kind) { case "Destructure": { const { pattern } = branchPattern; if (pattern.length === 0) { return branchPattern.constructor; } else { return `${branchPattern.constructor} ${pattern}`; } ; } case "StringValue": { const { body } = branchPattern; return `"${body}"`; } case "FormatStringValue": { const { body } = branchPattern; return "`" + body + "`"; } case "EmptyList": { return "[]"; } case "ListDestructure": { const { parts } = branchPattern; return (function (y) { return y.join(" :: "); })(List.map(generateListDestructurePart, parts)); } case "Default": { return "default"; } } } function generateBranch(branch) { const maybeLetBody = generateLetBlock(branch.letBody); const bodyIndent = maybeLetBody === "" ? 4 : 8; const body = (function (y) { return (0, Common_1.prefixLines)(y, bodyIndent); })(generateExpression(branch.body)); const pattern = generateBranchPattern(branch.pattern); return `${pattern} ->${maybeLetBody}\n${body}`; } function generateCaseStatement(caseStatement) { const predicate = generateExpression(caseStatement.predicate); const branches = (function (y) { return (0, Common_1.prefixLines)(y, 4); })((function (y) { return y.join("\n\n"); })(List.map(generateBranch, caseStatement.branches))); return `case ${predicate} of\n${branches}`; } function needsBrackets(expression) { switch (expression.kind) { case "FunctionCall": { return true; } default: { return false; } } } function applyBrackets(needsBrackets, generated) { if (needsBrackets) { return "(" + generated + ")"; } else { return generated; } } function generateAddition(addition) { const left = applyBrackets(needsBrackets(addition.left), generateExpression(addition.left)); const right = applyBrackets(needsBrackets(addition.right), generateExpression(addition.right)); if (addition.left.kind === "StringValue" || addition.right.kind === "StringValue") { return `${left} ++ ${right}`; } else { return `${left} + ${right}`; } } function generateSubtraction(subtraction) { const left = applyBrackets(needsBrackets(subtraction.left), generateExpression(subtraction.left)); const right = applyBrackets(needsBrackets(subtraction.right), generateExpression(subtraction.right)); return `${left} - ${right}`; } function generateMultiplication(multiplication) { const left = applyBrackets(needsBrackets(multiplication.left), generateExpression(multiplication.left)); const right = applyBrackets(needsBrackets(multiplication.right), generateExpression(multiplication.right)); return `${left} * ${right}`; } function generateDivision(division) { const left = applyBrackets(needsBrackets(division.left), generateExpression(division.left)); const right = applyBrackets(needsBrackets(division.right), generateExpression(division.right)); return `${left} / ${right}`; } function generateMod(mod) { const left = applyBrackets(needsBrackets(mod.left), generateExpression(mod.left)); const right = applyBrackets(needsBrackets(mod.right), generateExpression(mod.right)); return `${left} % ${right}`; } function generateLeftPipe(leftPipe) { const left = generateExpression(leftPipe.left); const right = generateExpression(leftPipe.right); return `${left}\n |> ${right}`; } function generateRightPipe(rightPipe) { const left = generateExpression(rightPipe.left); const right = generateExpression(rightPipe.right); return `${left}\n <| ${right}`; } function generateModuleReference(moduleReference) { if (moduleReference.path.length === 0) { return `.${generateExpression(moduleReference.value)}`; } else { const left = moduleReference.path.join("."); const right = generateExpression(moduleReference.value); const value = `${left}.${right}`; if (value === "console.log") { return `Debug.log ""`; } else { return value; } ; } } function generateFunctionCallArg(arg) { switch (arg.kind) { case "Constructor": { const { pattern } = arg; switch (pattern.fields.length) { case 0: { return generateExpression(arg); } default: { return `(${generateExpression(arg)})`; } } ; } case "FunctionCall": { const { args } = arg; switch (args.length) { case 0: { return generateExpression(arg); } default: { return `(${generateExpression(arg)})`; } } ; } case "ModuleReference": { const { value } = arg; switch (value.kind) { case "Constructor": { return `(${generateExpression(arg)})`; } case "FunctionCall": { return `(${generateExpression(arg)})`; } default: { return generateExpression(arg); } } ; } case "ListPrepend": { return `(${generateExpression(arg)})`; } case "Addition": { return `(${generateExpression(arg)})`; } case "Subtraction": { return `(${generateExpression(arg)})`; } case "Multiplication": { return `(${generateExpression(arg)})`; } case "Division": { return `(${generateExpression(arg)})`; } case "Equality": { return `(${generateExpression(arg)})`; } case "InEquality": { return `(${generateExpression(arg)})`; } case "LessThan": { return `(${generateExpression(arg)})`; } case "GreaterThan": { return `(${generateExpression(arg)})`; } case "LessThanOrEqual": { return `(${generateExpression(arg)})`; } case "GreaterThanOrEqual": { return `(${generateExpression(arg)})`; } case "LeftPipe": { return `(${generateExpression(arg)})`; } case "RightPipe": { return `(${generateExpression(arg)})`; } default: { return generateExpression(arg); } } } function generateFunctionCall(functionCall) { if (functionCall.args.length === 0) { return `${functionCall.name}()`; } else { const args = (function (y) { return y.join(" "); })(List.map(generateFunctionCallArg, functionCall.args)); return `${functionCall.name} ${args}`; } } function generateLambda(lambda) { const args = (function (y) { return y.join(" "); })(List.map(function (arg) { return arg; }, lambda.args)); const body = generateExpression(lambda.body); const indent = (0, types_1.isSimpleValue)(lambda.body.kind) ? ` ${body}` : (function (y) { return "\n" + y; })((0, Common_1.prefixLines)(body, 4)); return `\\${args} ->${indent}`; } function generateLambdaCall(lambdaCall) { const args = (function (y) { return y.join(", "); })(List.map(function (arg) { return `${arg}: any`; }, lambdaCall.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) { switch (expression.kind) { case "Value": { return generateValue(expression); } case "StringValue": { return generateStringValue(expression); } case "FormatStringValue": { return generateFormatStringValue(expression); } case "ListValue": { return generateListValue(expression); } case "ListRange": { return generateListRange(expression); } case "ObjectLiteral": { return generateObjectLiteral(expression); } case "IfStatement": { return generateIfStatement(expression); } case "CaseStatement": { return generateCaseStatement(expression); } 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 generateFunctionArg(arg) { switch (arg.kind) { case "FunctionArg": { return arg.name; } case "AnonFunctionArg": { return `_${arg.index}`; } } } function generateFunctionArgType(arg) { switch (arg.kind) { case "FunctionArg": { return generateTopLevelType(arg.type); } case "AnonFunctionArg": { return generateTopLevelType(arg.type); } } } function generateFunction(function_) { const argsTypes = (function (y) { return y.join(" -> "); })(List.map(generateFunctionArgType, function_.args)); const args = (function (y) { return y.join(" "); })(List.map(generateFunctionArg, function_.args)); const maybeLetBody = generateLetBlock(function_.letBody); const returnType = generateTopLevelType(function_.returnType); const bodyIndent = maybeLetBody === "" ? 4 : 8; const body = (function (y) { return (0, Common_1.prefixLines)(y, bodyIndent); })(generateExpression(function_.body)); return (function (y) { return y.join("\n"); })([`${function_.name}: ${argsTypes} -> ${returnType}`, `${function_.name} ${args} =${maybeLetBody}`, `${body}`]); } function generateConst(constDef) { const maybeLetBody = generateLetBlock(constDef.letBody); const bodyIndent = maybeLetBody === "" ? 4 : 8; const body = (function (y) { return (0, Common_1.prefixLines)(y, bodyIndent); })(generateExpression(constDef.value)); const typeDef = generateTopLevelType(constDef.type); return (function (y) { return y.join("\n"); })([`${constDef.name}: ${typeDef}`, `${constDef.name} =${maybeLetBody}`, `${body}`]); } function generateImportModule(module) { const moduleName = module.namespace === "Global" ? module.name : (function (y) { return y.join(""); })((function (y) { return y.split(`"`); })((function (y) { return y.join("."); })((function (y) { return y.split("/"); })((function (y) { return y.replace("./", ""); })(module.name))))); const partExposing = module.exposing.length === 0 ? "" : ` exposing ( ${module.exposing.join(", ")} )`; switch (module.alias.kind) { case "Just": { const { value } = module.alias; return `import ${moduleName} as ${value}${partExposing}`; } case "Nothing": { return `import ${moduleName}${partExposing}`; } } } function generateImportBlock(imports) { return (function (y) { return y.join("\n"); })(List.map(generateImportModule, imports.modules)); } function generateExportBlock(moduleName, names) { const toUpper = `${moduleName.toUpperCase()[0] + moduleName.slice(1)}`; const withoutDerw = `${toUpper.split("/").join(".").replace(".derw", "")}`; if (names.length === 0) { return `module ${withoutDerw} exposing (..)`; } else { return `module ${withoutDerw} exposing (${names.join(", ")})`; } } function generateBlock(syntax) { switch (syntax.kind) { case "Import": { return generateImportBlock(syntax); } case "Export": { return ""; } case "UnionType": { return generateUnionType(syntax); } case "UnionUntaggedType": { return generateUnionUntaggedType(syntax); } case "TypeAlias": { return generateTypeAlias(syntax); } case "Typeclass": { return ""; } case "Impl": { return ""; } case "Function": { return generateFunction(syntax); } case "Const": { return generateConst(syntax); } case "Comment": { return ""; } case "MultilineComment": { return ""; } } } function generateElm(module) { const onlyExports = List.foldl(function (block, names) { return List.append(names, block.names); }, [], List.filter(function (block) { return block.kind === "Export"; }, module.body)); const generatedExports = generateExportBlock(module.name, onlyExports); const blocks = List.map(generateBlock, module.body); return (function (y) { return y.join("\n\n"); })(List.filter(function (line) { return line.trim().length > 0; }, [generatedExports, ...blocks])); } exports.generateElm = generateElm;