0
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2024-10-31 09:14:20 -04:00
denoland-deno/js/repl.ts
2019-01-29 14:41:12 -05:00

147 lines
3.6 KiB
TypeScript

// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
import * as msg from "gen/msg_generated";
import * as flatbuffers from "./flatbuffers";
import { assert } from "./util";
import * as deno from "./deno";
import { close } from "./files";
import * as dispatch from "./dispatch";
import { exit } from "./os";
import { globalEval } from "./global_eval";
const window = globalEval("this");
const helpMsg = [
"exit Exit the REPL",
"help Print this help message"
].join("\n");
const replCommands = {
exit: {
get() {
exit(0);
}
},
help: {
get() {
return helpMsg;
}
}
};
function startRepl(historyFile: string): number {
const builder = flatbuffers.createBuilder();
const historyFile_ = builder.createString(historyFile);
msg.ReplStart.startReplStart(builder);
msg.ReplStart.addHistoryFile(builder, historyFile_);
const inner = msg.ReplStart.endReplStart(builder);
const baseRes = dispatch.sendSync(builder, msg.Any.ReplStart, inner);
assert(baseRes != null);
assert(msg.Any.ReplStartRes === baseRes!.innerType());
const innerRes = new msg.ReplStartRes();
assert(baseRes!.inner(innerRes) != null);
const rid = innerRes.rid();
return rid;
}
// @internal
export async function readline(rid: number, prompt: string): Promise<string> {
const builder = flatbuffers.createBuilder();
const prompt_ = builder.createString(prompt);
msg.ReplReadline.startReplReadline(builder);
msg.ReplReadline.addRid(builder, rid);
msg.ReplReadline.addPrompt(builder, prompt_);
const inner = msg.ReplReadline.endReplReadline(builder);
const baseRes = await dispatch.sendAsync(
builder,
msg.Any.ReplReadline,
inner
);
assert(baseRes != null);
assert(msg.Any.ReplReadlineRes === baseRes!.innerType());
const innerRes = new msg.ReplReadlineRes();
assert(baseRes!.inner(innerRes) != null);
const line = innerRes.line();
assert(line !== null);
return line || "";
}
// @internal
export async function replLoop(): Promise<void> {
window.deno = deno; // FIXME use a new scope (rather than window).
Object.defineProperties(window, replCommands);
const historyFile = "deno_history.txt";
const rid = startRepl(historyFile);
let code = "";
while (true) {
try {
code = await readBlock(rid, "> ", " ");
} catch (err) {
if (err.message === "EOF") {
break;
}
console.error(err);
exit(1);
}
evaluate(code);
}
close(rid);
}
function evaluate(code: string): void {
try {
const result = eval.call(window, code); // FIXME use a new scope.
console.log(result);
} catch (err) {
if (err instanceof Error) {
console.error(`${err.constructor.name}: ${err.message}`);
} else {
console.error("Thrown:", err);
}
}
}
async function readBlock(
rid: number,
prompt: string,
continuedPrompt: string
): Promise<string> {
let code = "";
do {
code += await readline(rid, prompt);
prompt = continuedPrompt;
} while (parenthesesAreOpen(code));
return code;
}
// modified from
// https://codereview.stackexchange.com/a/46039/148556
function parenthesesAreOpen(code: string): boolean {
const parentheses = "[]{}()";
const stack = [];
for (const ch of code) {
const bracePosition = parentheses.indexOf(ch);
if (bracePosition === -1) {
// not a paren
continue;
}
if (bracePosition % 2 === 0) {
stack.push(bracePosition + 1); // push next expected brace position
} else {
if (stack.pop() !== bracePosition) {
return false;
}
}
}
return stack.length > 0;
}