derw
Version:
An Elm-inspired language that transpiles to TypeScript
641 lines (588 loc) • 19.1 kB
text/typescript
import { isTestFile } from "./Utils";
import * as List from "./stdlib/List";
import { BlockKinds, Block, TypedBlock, Module } from "./types";
export { blockKind };
export { createUnparsedBlock };
export { intoBlocks };
export { typeBlocks };
export { exportTests };
type Validator = {
test: (arg0: string) => boolean;
blockKind: BlockKinds;
}
function Validator(args: { test: (arg0: string) => boolean, blockKind: BlockKinds }): Validator {
return {
...args,
};
}
type Ok<value> = {
kind: "Ok";
value: value;
};
function Ok<value>(args: { value: value }): Ok<value> {
return {
kind: "Ok",
...args,
};
}
type Err<error> = {
kind: "Err";
error: error;
};
function Err<error>(args: { error: error }): Err<error> {
return {
kind: "Err",
...args,
};
}
type Result<error, value> = Ok<value> | Err<error>;
type ImportBlock = {
kind: "ImportBlock";
lineStart: number;
lines: string[];
};
function ImportBlock(args: { lineStart: number, lines: string[] }): ImportBlock {
return {
kind: "ImportBlock",
...args,
};
}
type ExportBlock = {
kind: "ExportBlock";
lineStart: number;
lines: string[];
};
function ExportBlock(args: { lineStart: number, lines: string[] }): ExportBlock {
return {
kind: "ExportBlock",
...args,
};
}
type UnionTypeBlock = {
kind: "UnionTypeBlock";
lineStart: number;
lines: string[];
};
function UnionTypeBlock(args: { lineStart: number, lines: string[] }): UnionTypeBlock {
return {
kind: "UnionTypeBlock",
...args,
};
}
type UnionUntaggedTypeBlock = {
kind: "UnionUntaggedTypeBlock";
lineStart: number;
lines: string[];
};
function UnionUntaggedTypeBlock(args: { lineStart: number, lines: string[] }): UnionUntaggedTypeBlock {
return {
kind: "UnionUntaggedTypeBlock",
...args,
};
}
type TypeAliasBlock = {
kind: "TypeAliasBlock";
lineStart: number;
lines: string[];
};
function TypeAliasBlock(args: { lineStart: number, lines: string[] }): TypeAliasBlock {
return {
kind: "TypeAliasBlock",
...args,
};
}
type TypeclassBlock = {
kind: "TypeclassBlock";
lineStart: number;
lines: string[];
};
function TypeclassBlock(args: { lineStart: number, lines: string[] }): TypeclassBlock {
return {
kind: "TypeclassBlock",
...args,
};
}
type ImplBlock = {
kind: "ImplBlock";
lineStart: number;
lines: string[];
};
function ImplBlock(args: { lineStart: number, lines: string[] }): ImplBlock {
return {
kind: "ImplBlock",
...args,
};
}
type FunctionBlock = {
kind: "FunctionBlock";
lineStart: number;
lines: string[];
};
function FunctionBlock(args: { lineStart: number, lines: string[] }): FunctionBlock {
return {
kind: "FunctionBlock",
...args,
};
}
type ConstBlock = {
kind: "ConstBlock";
lineStart: number;
lines: string[];
};
function ConstBlock(args: { lineStart: number, lines: string[] }): ConstBlock {
return {
kind: "ConstBlock",
...args,
};
}
type CommentBlock = {
kind: "CommentBlock";
lineStart: number;
lines: string[];
};
function CommentBlock(args: { lineStart: number, lines: string[] }): CommentBlock {
return {
kind: "CommentBlock",
...args,
};
}
type MultilineCommentBlock = {
kind: "MultilineCommentBlock";
lineStart: number;
lines: string[];
};
function MultilineCommentBlock(args: { lineStart: number, lines: string[] }): MultilineCommentBlock {
return {
kind: "MultilineCommentBlock",
...args,
};
}
type UnknownBlock = {
kind: "UnknownBlock";
lineStart: number;
lines: string[];
};
function UnknownBlock(args: { lineStart: number, lines: string[] }): UnknownBlock {
return {
kind: "UnknownBlock",
...args,
};
}
type UnparsedBlock = ImportBlock | ExportBlock | UnionTypeBlock | UnionUntaggedTypeBlock | TypeAliasBlock | TypeclassBlock | ImplBlock | FunctionBlock | ConstBlock | CommentBlock | MultilineCommentBlock | UnknownBlock;
function hasTypeLine(block: string): boolean {
const _res1328002030 = block.split(":");
switch (_res1328002030.length) {
case _res1328002030.length: {
if (_res1328002030.length === 1) {
const [ x ] = _res1328002030;
return false;
}
}
case _res1328002030.length: {
if (_res1328002030.length >= 1) {
const [ x, ...xs ] = _res1328002030;
const trimmed: string = x.trim();
const split: string[] = trimmed.split(" ");
const length: number = split.length;
return length === 1;
}
}
default: {
return false;
}
}
}
function isAFunction(block: string): boolean {
const _res1780524276 = block.split("\n");
switch (_res1780524276.length) {
case _res1780524276.length: {
if (_res1780524276.length >= 1) {
const [ firstLine, ...xs ] = _res1780524276;
return (function(y: any) {
return y.length > 1;
})(firstLine.split("->"));
}
}
default: {
return false;
}
}
}
const validators: Validator[] = [ {
test: function(x: any) {
return x.startsWith("--");
},
blockKind: "Comment"
}, {
test: function(x: any) {
return x.startsWith("{-");
},
blockKind: "MultilineComment"
}, {
test: function(x: any) {
return x.startsWith("type alias");
},
blockKind: "TypeAlias"
}, {
test: function(x: any) {
return x.startsWith("typeclass ");
},
blockKind: "Typeclass"
}, {
test: function(x: any) {
return x.startsWith("impl ");
},
blockKind: "Impl"
}, {
test: function(x: any) {
return x.startsWith("type ") && x.includes(`"`);
},
blockKind: "UnionUntaggedType"
}, {
test: function(x: any) {
return x.startsWith("type ");
},
blockKind: "UnionType"
}, {
test: function(x: any) {
return x.startsWith(" ") || x.startsWith("}");
},
blockKind: "Indent"
}, {
test: function(x: any) {
return x.startsWith("import");
},
blockKind: "Import"
}, {
test: function(x: any) {
return x.startsWith("exposing");
},
blockKind: "Export"
}, {
test: function(x: any) {
return hasTypeLine(x) && isAFunction(x);
},
blockKind: "Function"
}, {
test: function(x: any) {
return hasTypeLine(x);
},
blockKind: "Const"
}, {
test: function(x: any) {
return (function(y: any) {
return y.length > 1;
})(x.split("="));
},
blockKind: "Definition"
} ];
function blockKindStep(block: string, validators: Validator[]): Result<string, BlockKinds> {
switch (validators.length) {
case validators.length: {
if (validators.length >= 1) {
const [ validator, ...ys ] = validators;
if (validator.test(block)) {
return Ok({ value: validator.blockKind });
} else {
return blockKindStep(block, ys);
};
}
}
default: {
return Err({ error: "Unknown block type" });
}
}
}
function blockKind(block: string): Result<string, BlockKinds> {
return blockKindStep(block, validators);
}
function createUnparsedBlock(blockKind: BlockKinds, lineStart: number, lines: string[]): UnparsedBlock {
switch (blockKind) {
case "Import": {
return ImportBlock({
lineStart,
lines
});
}
case "Export": {
return ExportBlock({
lineStart,
lines
});
}
case "Const": {
return ConstBlock({
lineStart,
lines
});
}
case "Function": {
return FunctionBlock({
lineStart,
lines
});
}
case "UnionType": {
return UnionTypeBlock({
lineStart,
lines
});
}
case "UnionUntaggedType": {
return UnionUntaggedTypeBlock({
lineStart,
lines
});
}
case "TypeAlias": {
return TypeAliasBlock({
lineStart,
lines
});
}
case "Typeclass": {
return TypeclassBlock({
lineStart,
lines
});
}
case "Impl": {
return ImplBlock({
lineStart,
lines
});
}
case "Indent": {
return UnknownBlock({
lineStart,
lines
});
}
case "Definition": {
return UnknownBlock({
lineStart,
lines
});
}
case "Comment": {
return CommentBlock({
lineStart,
lines
});
}
case "MultilineComment": {
return MultilineCommentBlock({
lineStart,
lines
});
}
case "Unknown": {
return UnknownBlock({
lineStart,
lines
});
}
}
}
type IntoBlockInfo = {
currentBlock: string[];
previousLine: string;
currentBlockKind: Result<string, BlockKinds>;
lineStart: number;
}
function IntoBlockInfo(args: { currentBlock: string[], previousLine: string, currentBlockKind: Result<string, BlockKinds>, lineStart: number }): IntoBlockInfo {
return {
...args,
};
}
function stepMultilineComment(lineNumber: number, info: IntoBlockInfo, line: string, lines: string[]): UnparsedBlock[] {
if (line === "-}") {
const infoWithCurrentLine: IntoBlockInfo = {
...info,
previousLine: line,
currentBlock: List.append(info.currentBlock, [ line ])
};
const block: UnparsedBlock = createUnparsedBlock("MultilineComment", infoWithCurrentLine.lineStart, infoWithCurrentLine.currentBlock);
const nextInfo: IntoBlockInfo = {
currentBlock: [ ],
previousLine: line,
lineStart: lineNumber,
currentBlockKind: Err({ error: "Nothing" })
};
return [ block, ...intoBlocksStep(lineNumber + 1, nextInfo, lines) ];
} else {
const infoWithCurrentLine: IntoBlockInfo = {
...info,
previousLine: line,
currentBlock: List.append(info.currentBlock, [ line ])
};
return intoBlocksStep(lineNumber + 1, infoWithCurrentLine, lines);
}
}
function indentOrDefinitionStep(lineNumber: number, info: IntoBlockInfo, line: string, lines: string[]): UnparsedBlock[] {
const nextLines: string[] = lineNumber > 0 && info.previousLine.trim() === "" ? [ info.previousLine, line ] : [ line ];
const nextInfo: IntoBlockInfo = {
...info,
currentBlock: List.append(info.currentBlock, nextLines),
previousLine: line
};
return intoBlocksStep(lineNumber + 1, nextInfo, lines);
}
function intoBlocksStep(lineNumber: number, info: IntoBlockInfo, lines: string[]): UnparsedBlock[] {
switch (lines.length) {
case lines.length: {
if (lines.length >= 1) {
const [ line, ...xs ] = lines;
if (line.trim().length === 0) {
const nextInfo: IntoBlockInfo = { ...info, previousLine: line };
return intoBlocksStep(lineNumber + 1, nextInfo, xs);
} else {
switch (info.currentBlock.length) {
case 0: {
const nextInfo: IntoBlockInfo = {
previousLine: line,
currentBlock: [ line ],
lineStart: lineNumber,
currentBlockKind: blockKind(line)
};
return intoBlocksStep(lineNumber + 1, nextInfo, xs);
}
default: {
const isInMultilineComment: boolean = info.currentBlockKind.kind === "Ok" && info.currentBlockKind.value === "MultilineComment";
if (isInMultilineComment) {
return stepMultilineComment(lineNumber, info, line, xs);
} else {
const currentLineBlockKind: Result<string, BlockKinds> = blockKind(line);
const isIndent: boolean = currentLineBlockKind.kind === "Ok" && currentLineBlockKind.value === "Indent";
const isDefinition: boolean = currentLineBlockKind.kind === "Ok" && currentLineBlockKind.value === "Definition";
if (isIndent || isDefinition) {
return indentOrDefinitionStep(lineNumber, info, line, xs);
} else {
switch (info.currentBlockKind.kind) {
case "Ok": {
const { value } = info.currentBlockKind;
const hasSpeech: boolean = (function(y: any) {
return y.length > 0;
})(info.currentBlock.filter(function(line: any) {
return line.indexOf(`"`) > -1;
}));
const kind: BlockKinds = (function (): any {
switch (value) {
case "UnionType": {
if (hasSpeech) {
return "UnionUntaggedType";
} else {
return value;
};
}
default: {
return value;
}
}
})();
const block: UnparsedBlock = createUnparsedBlock(kind, info.lineStart, info.currentBlock);
const nextInfo: IntoBlockInfo = {
previousLine: line,
currentBlock: [ line ],
lineStart: lineNumber,
currentBlockKind: currentLineBlockKind
};
return [ block, ...intoBlocksStep(lineNumber + 1, nextInfo, xs) ];
}
case "Err": {
const nextInfo: IntoBlockInfo = {
...info,
previousLine: line,
currentBlock: List.append(info.currentBlock, [ line ])
};
return intoBlocksStep(lineNumber + 1, nextInfo, xs);
}
};
};
};
}
};
};
}
}
default: {
if (info.currentBlock.length > 0) {
switch (info.currentBlockKind.kind) {
case "Ok": {
const { value } = info.currentBlockKind;
const hasSpeech: boolean = (function(y: any) {
return y.length > 0;
})(info.currentBlock.filter(function(line: any) {
return line.indexOf(`"`) > -1;
}));
const kind: BlockKinds = (function (): any {
switch (value) {
case "UnionType": {
if (hasSpeech) {
return "UnionUntaggedType";
} else {
return value;
};
}
default: {
return value;
}
}
})();
return [ createUnparsedBlock(kind, info.lineStart, info.currentBlock) ];
}
case "Err": {
return [ createUnparsedBlock("Unknown", info.lineStart, info.currentBlock) ];
}
};
} else {
return [ ];
};
}
}
}
function intoBlocks(body: string): UnparsedBlock[] {
const lines: string[] = body.split("\n");
return intoBlocksStep(0, {
previousLine: "",
currentBlock: [ ],
currentBlockKind: Err({ error: "Nothing" }),
lineStart: 0
}, lines);
}
function typeBlocks(blocks: Block[]): TypedBlock[] {
return List.filter(function(block: any) {
return block.kind === "UnionType" || block.kind === "TypeAlias" || block.kind === "UnionUntaggedType";
}, blocks);
}
type Export = {
kind: "Export";
names: string[];
};
function Export(args: { names: string[] }): Export {
return {
kind: "Export",
...args,
};
}
type MyExport = Export;
function exportTests(module: Module): MyExport {
const isTest: boolean = isTestFile(module.name);
const namesToExpose: string[] = isTest ? List.filter(function(name: any) {
return name.startsWith("test") || name.startsWith("snapshot");
}, List.map(function(block: any) {
return block.name;
}, List.filter(function(block: any) {
return block.kind === "Function" || block.kind === "Const";
}, module.body))) : [ ];
const exports: Export[] = List.filter(function(block: any) {
return block.kind === "Export";
}, module.body);
const exportNames: string[] = List.foldl(function(export_: any, allNames: any) {
return List.append(export_.names, allNames);
}, [ ], exports);
const exposeWithoutDuplicates: string[] = List.filter(function(name: any) {
return !exportNames.includes(name);
}, namesToExpose);
return Export({ names: exposeWithoutDuplicates });
}