derw
Version:
An Elm-inspired language that transpiles to TypeScript
160 lines (141 loc) • 5.38 kB
text/typescript
import { writeFile } from "fs/promises";
import { stdin as input, stdout as output } from "process";
import * as readline from "readline";
import { generateTypescript } from "../generators/Ts";
import { addTypeErrors, parseWithContext } from "../parser";
import { IdentifierToken, tokenize } from "../Tokens";
import { ContextModule, contextModuleToModule, Export } from "../types";
import { ensureDirectoryExists } from "./utils";
function exportEverything(module: ContextModule): ContextModule {
const exposing = [ ];
for (const block of module.body) {
switch (block.kind) {
case "Const":
case "Function": {
exposing.push(block.name);
break;
}
case "TypeAlias":
case "UnionType": {
exposing.push(block.type.name);
break;
}
}
}
module.body.push(Export(exposing));
return module;
}
export async function repl(
isInPackageDirectory: boolean,
argv: string[]
): Promise<void> {
let currentBuffer: string[] = [ ];
function completer(text: string): readline.CompleterResult {
const tokens = tokenize(currentBuffer.join("\n"));
const identifiers = tokens.filter(
(token) => token.kind === "IdentifierToken"
);
return [
[
...new Set(
identifiers.map((token) => (token as IdentifierToken).body)
),
],
text,
];
}
const rl = readline.createInterface({
input,
output,
completer,
tabSize: 4,
});
rl.prompt();
rl.on("close", () => {});
let currentlyImportedModule = null;
let module = null;
await ensureDirectoryExists("derw-packages/.cli/");
async function run(currentBuffer: string[]): Promise<void> {
module = parseWithContext([ ...currentBuffer ].join("\n"), "Main");
module = exportEverything(module);
const generated = generateTypescript(contextModuleToModule(module));
const fileTimestamp = new Date().getTime();
const filename = `${process.cwd()}/derw-packages/.cli/${fileTimestamp}.ts`;
await writeFile(filename, generated);
currentlyImportedModule = await import(filename);
}
let linesAddedToCurrentBufferSinceLastParsing: string[] = [ ];
for await (const line of rl) {
if (line.trim() === "") {
module = parseWithContext(
[
...currentBuffer,
"",
...linesAddedToCurrentBufferSinceLastParsing,
].join("\n"),
"Main"
);
module = addTypeErrors(module, [ ]);
if (module.errors.length > 0) {
console.log(
`Errors while parsing: ${module.errors.join("\n")}`
);
linesAddedToCurrentBufferSinceLastParsing = [ ];
} else {
for (const newLine of linesAddedToCurrentBufferSinceLastParsing) {
currentBuffer.push(newLine);
}
linesAddedToCurrentBufferSinceLastParsing = [ ];
currentBuffer.push("");
console.log("Parsed successfully");
}
} else if (line.trim() === ":run") {
await run(currentBuffer);
} else if (line.startsWith(":show")) {
const name = line.split(" ")[1].trim();
await run(currentBuffer);
1;
if (currentlyImportedModule[name] === undefined) {
console.log(`Couldn't find ${name} in current scope.`);
} else {
console.log(currentlyImportedModule[name]);
}
} else if (line.trim() === ":help") {
console.log("Enter some code, followed by a blank newline.");
console.log("Run the current namespace via :run");
console.log("And check the values of code via :show <name>");
console.log(
"Or evaluate a constant or function with :eval <function> <args>"
);
} else if (line.startsWith(":eval")) {
const expression = `
_cli: any
_cli = ${line.split(" ").slice(1).join(" ")}
`;
module = parseWithContext(
[ ...currentBuffer, "", expression ].join("\n"),
"Main"
);
module = exportEverything(module);
if (module.errors.length > 0) {
console.log(
`Errors while parsing: ${module.errors.join("\n")}`
);
continue;
} else {
console.log("Parsed successfully");
}
const generated = generateTypescript(contextModuleToModule(module));
const fileTimestamp = new Date().getTime();
const filename = `${process.cwd()}/derw-packages/.cli/${fileTimestamp}.ts`;
await writeFile(filename, generated);
currentlyImportedModule = await import(filename);
console.log(currentlyImportedModule["_cli"]);
} else {
linesAddedToCurrentBufferSinceLastParsing.push(
line.split("\t").join(" ")
);
}
rl.prompt();
}
}