summaryrefslogtreecommitdiff
path: root/js
diff options
context:
space:
mode:
Diffstat (limited to 'js')
-rw-r--r--js/main.ts6
-rw-r--r--js/xeval.ts99
2 files changed, 104 insertions, 1 deletions
diff --git a/js/main.ts b/js/main.ts
index ad9e0e99d..cb27690b5 100644
--- a/js/main.ts
+++ b/js/main.ts
@@ -9,7 +9,9 @@ import { assert, log } from "./util";
import * as os from "./os";
import { args } from "./deno";
import { replLoop } from "./repl";
+import { xevalMain, XevalFunc } from "./xeval";
import { setVersions } from "./version";
+import { window } from "./window";
import { setLocation } from "./location";
// builtin modules
@@ -43,7 +45,9 @@ export default function denoMain(name?: string): void {
log("args", args);
Object.freeze(args);
- if (!mainModule) {
+ if (window["_xevalWrapper"] !== undefined) {
+ xevalMain(window["_xevalWrapper"] as XevalFunc, startResMsg.xevalDelim());
+ } else if (!mainModule) {
replLoop();
}
}
diff --git a/js/xeval.ts b/js/xeval.ts
new file mode 100644
index 000000000..f769a2ead
--- /dev/null
+++ b/js/xeval.ts
@@ -0,0 +1,99 @@
+import { Buffer } from "./buffer";
+import { stdin } from "./files";
+import { TextEncoder, TextDecoder } from "./text_encoding";
+import { Reader } from "./io";
+
+export type XevalFunc = (v: string) => void;
+
+async function writeAll(buffer: Buffer, arr: Uint8Array): Promise<void> {
+ let bytesWritten = 0;
+ while (bytesWritten < arr.length) {
+ try {
+ const nwritten = await buffer.write(arr.subarray(bytesWritten));
+ bytesWritten += nwritten;
+ } catch {
+ return;
+ }
+ }
+}
+
+// TODO(kevinkassimo): Move this utility to deno_std.
+// Import from there once doable.
+// Read from reader until EOF and emit string chunks separated
+// by the given delimiter.
+async function* chunks(
+ reader: Reader,
+ delim: string
+): AsyncIterableIterator<string> {
+ const inputBuffer = new Buffer();
+ const inspectArr = new Uint8Array(1024);
+ const encoder = new TextEncoder();
+ const decoder = new TextDecoder();
+ // Avoid unicode problems
+ const delimArr = encoder.encode(delim);
+
+ // Record how far we have gone with delimiter matching.
+ let nextMatchIndex = 0;
+ while (true) {
+ const rr = await reader.read(inspectArr);
+ if (rr.nread < 0) {
+ // Silently fail.
+ break;
+ }
+ const sliceRead = inspectArr.subarray(0, rr.nread);
+ // Remember how far we have scanned through inspectArr.
+ let nextSliceStartIndex = 0;
+ for (let i = 0; i < sliceRead.length; i++) {
+ if (sliceRead[i] == delimArr[nextMatchIndex]) {
+ // One byte matches with delimiter, move 1 step forward.
+ nextMatchIndex++;
+ } else {
+ // Match delimiter failed. Start from beginning.
+ nextMatchIndex = 0;
+ }
+ // A complete match is found.
+ if (nextMatchIndex === delimArr.length) {
+ nextMatchIndex = 0; // Reset delim match index.
+ const sliceToJoin = sliceRead.subarray(nextSliceStartIndex, i + 1);
+ // Record where to start next chunk when a subsequent match is found.
+ nextSliceStartIndex = i + 1;
+ // Write slice to buffer before processing, since potentially
+ // part of the delimiter is stored in the buffer.
+ await writeAll(inputBuffer, sliceToJoin);
+
+ let readyBytes = inputBuffer.bytes();
+ inputBuffer.reset();
+ // Remove delimiter from buffer bytes.
+ readyBytes = readyBytes.subarray(
+ 0,
+ readyBytes.length - delimArr.length
+ );
+ let readyChunk = decoder.decode(readyBytes);
+ yield readyChunk;
+ }
+ }
+ // Write all unprocessed chunk to buffer for future inspection.
+ await writeAll(inputBuffer, sliceRead.subarray(nextSliceStartIndex));
+ if (rr.eof) {
+ // Flush the remainder unprocessed chunk.
+ const lastChunk = inputBuffer.toString();
+ yield lastChunk;
+ break;
+ }
+ }
+}
+
+export async function xevalMain(
+ xevalFunc: XevalFunc,
+ delim_: string | null
+): Promise<void> {
+ if (!delim_) {
+ delim_ = "\n";
+ }
+ for await (const chunk of chunks(stdin, delim_)) {
+ // Ignore empty chunks.
+ if (chunk.length > 0) {
+ xevalFunc(chunk);
+ }
+ }
+}