diff options
Diffstat (limited to 'js')
-rw-r--r-- | js/main.ts | 6 | ||||
-rw-r--r-- | js/xeval.ts | 99 |
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); + } + } +} |