derw
Version:
An Elm-inspired language that transpiles to TypeScript
274 lines (255 loc) • 8.43 kB
text/typescript
import { getNameFromPath } from "../Utils";
import * as List from "../stdlib/List";
import { Value, StringValue, FormatStringValue, ListRange, ListDestructurePart, ModuleReference, Expression, LeftPipe, Lambda, Export, ImportModule, Import } from "../types";
export { generateValue };
export { generateStringValue };
export { generateFormatStringValue };
export { generateListRange };
export { generateListDestructurePart };
export { flattenLeftPipe };
export { generateImportBlock };
export { generateExportBlock };
type FunctionCall = {
kind: "FunctionCall";
name: string;
args: Expression[];
};
function FunctionCall(args: { name: string, args: Expression[] }): FunctionCall {
return {
kind: "FunctionCall",
...args,
};
}
type LambdaCall = {
kind: "LambdaCall";
args: Expression[];
lambda: Lambda;
};
function LambdaCall(args: { args: Expression[], lambda: Lambda }): LambdaCall {
return {
kind: "LambdaCall",
...args,
};
}
type MyExpressions = FunctionCall | LambdaCall;
function generateValue(value: Value): string {
return value.body;
}
function generateStringValue(string: StringValue): string {
return `"${string.body}"`;
}
function generateFormatStringValue(string: FormatStringValue): string {
return "`" + string.body + "`";
}
function generateListRange(list: ListRange): string {
const gap: string = `${list.end.body} - ${list.start.body} + 1`;
return `Array.from({ length: ${gap} }, (_ReservedX, _ReservedI) => _ReservedI + ${list.start.body})`;
}
function generateListDestructurePart(part: ListDestructurePart): string {
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 { constructor, pattern } = part;
const generatedPattern: string = pattern ? ` ${pattern}` : "";
return `${constructor}${pattern}`;
}
}
}
function addArgsToModuleReference(moduleReference: ModuleReference, newArgs: Expression[]): ModuleReference {
switch (moduleReference.value.kind) {
case "FunctionCall": {
const { args, name } = moduleReference.value;
return { ...moduleReference, value: FunctionCall({
name,
args: List.append(args, newArgs)
}) };
}
case "Value": {
const { body } = moduleReference.value;
return { ...moduleReference, value: FunctionCall({
name: body,
args: newArgs
}) };
}
default: {
return moduleReference;
}
}
}
function flattenLeftPipe(leftPipe: LeftPipe): any {
const left: Expression = leftPipe.left;
const right: Expression = leftPipe.right;
switch (right.kind) {
case "FunctionCall": {
const { name, args } = right;
return FunctionCall({
name,
args: List.append(args, [ left ])
});
}
case "Value": {
const { body } = right;
return FunctionCall({
name: body,
args: [ left ]
});
}
case "ModuleReference": {
return addArgsToModuleReference(right, [ left ]);
}
case "Lambda": {
return LambdaCall({
lambda: right,
args: [ left ]
});
}
case "LeftPipe": {
switch (right.left.kind) {
case "FunctionCall": {
const { args, name } = right.left;
const fn: FunctionCall = FunctionCall({
name,
args: List.append(args, [ left ])
});
return flattenLeftPipe({
kind: "LeftPipe",
left: fn,
right: right.right
});
}
case "Value": {
const { body } = right.left;
const fn: FunctionCall = FunctionCall({
name: body,
args: [ left ]
});
return flattenLeftPipe({
kind: "LeftPipe",
left: fn,
right: right.right
});
}
case "ModuleReference": {
const fn: ModuleReference = addArgsToModuleReference(right.left, [ left ]);
return flattenLeftPipe({
kind: "LeftPipe",
left: fn,
right: right.right
});
}
case "Lambda": {
const fn: any = LambdaCall({
lambda: right.left,
args: [ left ]
});
return flattenLeftPipe({
kind: "LeftPipe",
left: fn,
right: right.right
});
}
case "LeftPipe": {
return right;
}
default: {
return right.left;
}
};
}
}
}
function generateModule(module: ImportModule): string {
if (module.namespace === "Relative") {
const withoutQuotes: string = module.name.slice(1, -1);
const name: string = (function (): any {
switch (module.alias.kind) {
case "Just": {
const { value } = module.alias;
return value;
}
case "Nothing": {
return getNameFromPath(withoutQuotes);
}
}
})();
const filteredExposing: string[] = (function (): any {
switch (module.alias.kind) {
case "Just": {
const { value } = module.alias;
return List.filter(function(expose: any) {
return expose !== value;
}, module.exposing);
}
case "Nothing": {
return module.exposing;
}
}
})();
const exposed: string = `import { ${filteredExposing.join(", ")} } from ${module.name};`;
if (module.exposing.length === 0) {
return `import * as ${name} from ${module.name};`;
} else {
switch (module.alias.kind) {
case "Just": {
return `import * as ${name} from ${module.name};\n${exposed}`;
}
case "Nothing": {
return exposed;
}
};
};
} else {
const name: string = (function (): any {
switch (module.alias.kind) {
case "Just": {
const { value } = module.alias;
return value;
}
case "Nothing": {
return module.name;
}
}
})();
const exposed: string = `import { ${module.exposing.join(", ")} } from "${module.name}";`;
if (module.exposing.length === 0) {
return `import * as ${name} from "${module.name}";`;
} else {
switch (module.alias.kind) {
case "Just": {
return `import * as ${name} from "${module.name}";\n${exposed}`;
}
case "Nothing": {
return exposed;
}
};
};
}
}
function generateImportBlock(imports: Import): string {
return (function(y: any) {
return y.join("\n");
})(List.map(generateModule, List.filter(function(module: any) {
return module.name !== "globalThis";
}, imports.modules)));
}
function generateExportBlock(exports: Export): string {
return (function(x: any) {
return x.join("\n");
})(List.map(function(name: any) {
return `export { ${name} };`;
}, exports.names));
}