diff options
Diffstat (limited to 'Tools')
-rw-r--r-- | Tools/wasm/python.html | 67 | ||||
-rw-r--r-- | Tools/wasm/python.worker.js | 10 |
2 files changed, 69 insertions, 8 deletions
diff --git a/Tools/wasm/python.html b/Tools/wasm/python.html index 17ffa0e..81a035a 100644 --- a/Tools/wasm/python.html +++ b/Tools/wasm/python.html @@ -35,11 +35,12 @@ <script src="https://unpkg.com/xterm@4.18.0/lib/xterm.js" crossorigin integrity="sha384-yYdNmem1ioP5Onm7RpXutin5A8TimLheLNQ6tnMi01/ZpxXdAwIm2t4fJMx1Djs+"/></script> <script type="module"> class WorkerManager { - constructor(workerURL, standardIO, readyCallBack) { + constructor(workerURL, standardIO, readyCallBack, finishedCallback) { this.workerURL = workerURL this.worker = null this.standardIO = standardIO this.readyCallBack = readyCallBack + this.finishedCallback = finishedCallback this.initialiseWorker() } @@ -59,6 +60,15 @@ class WorkerManager { }) } + reset() { + if (this.worker) { + this.worker.terminate() + this.worker = null + } + this.standardIO.message('Worker process terminated.') + this.initialiseWorker() + } + handleStdinData(inputValue) { if (this.stdinbuffer && this.stdinbufferInt) { let startingIndex = 1 @@ -92,7 +102,8 @@ class WorkerManager { this.handleStdinData(inputValue) }) } else if (type === 'finished') { - this.standardIO.stderr(`Exited with status: ${event.data.returnCode}\r\n`) + this.standardIO.message(`Exited with status: ${event.data.returnCode}`) + this.finishedCallback() } } } @@ -168,9 +179,14 @@ class WasmTerminal { break; case "\x7F": // BACKSPACE case "\x08": // CTRL+H - case "\x04": // CTRL+D this.handleCursorErase(true); break; + case "\x04": // CTRL+D + // Send empty input + if (this.input === '') { + this.resolveInput('') + this.activeInput = false; + } } } else { this.handleCursorInsert(data); @@ -265,9 +281,13 @@ class BufferQueue { } } +const runButton = document.getElementById('run') const replButton = document.getElementById('repl') +const stopButton = document.getElementById('stop') const clearButton = document.getElementById('clear') +const codeBox = document.getElementById('codebox') + window.onload = () => { const terminal = new WasmTerminal() terminal.open(document.getElementById('terminal')) @@ -277,35 +297,72 @@ window.onload = () => { stderr: (charCode) => { terminal.print(charCode) }, stdin: async () => { return await terminal.prompt() + }, + message: (text) => { terminal.writeLine(`\r\n${text}\r\n`) }, + } + + const programRunning = (isRunning) => { + if (isRunning) { + replButton.setAttribute('disabled', true) + runButton.setAttribute('disabled', true) + stopButton.removeAttribute('disabled') + } else { + replButton.removeAttribute('disabled') + runButton.removeAttribute('disabled') + stopButton.setAttribute('disabled', true) } } + runButton.addEventListener('click', (e) => { + terminal.clear() + programRunning(true) + const code = codeBox.value + pythonWorkerManager.run({args: ['main.py'], files: {'main.py': code}}) + }) + replButton.addEventListener('click', (e) => { + terminal.clear() + programRunning(true) // Need to use "-i -" to force interactive mode. // Looks like isatty always returns false in emscripten pythonWorkerManager.run({args: ['-i', '-'], files: {}}) }) + stopButton.addEventListener('click', (e) => { + programRunning(false) + pythonWorkerManager.reset() + }) + clearButton.addEventListener('click', (e) => { terminal.clear() }) const readyCallback = () => { replButton.removeAttribute('disabled') + runButton.removeAttribute('disabled') clearButton.removeAttribute('disabled') } - const pythonWorkerManager = new WorkerManager('./python.worker.js', stdio, readyCallback) + const finishedCallback = () => { + programRunning(false) + } + + const pythonWorkerManager = new WorkerManager('./python.worker.js', stdio, readyCallback, finishedCallback) } </script> </head> <body> <h1>Simple REPL for Python WASM</h1> - <div id="terminal"></div> +<textarea id="codebox" cols="108" rows="16"> +print('Welcome to WASM!') +</textarea> <div class="button-container"> + <button id="run" disabled>Run</button> <button id="repl" disabled>Start REPL</button> + <button id="stop" disabled>Stop</button> <button id="clear" disabled>Clear</button> </div> + <div id="terminal"></div> <div id="info"> The simple REPL provides a limited Python experience in the browser. <a href="https://github.com/python/cpython/blob/main/Tools/wasm/README.md"> diff --git a/Tools/wasm/python.worker.js b/Tools/wasm/python.worker.js index 1b79460..4ce4e16 100644 --- a/Tools/wasm/python.worker.js +++ b/Tools/wasm/python.worker.js @@ -19,18 +19,18 @@ class StdinBuffer { } stdin = () => { - if (this.numberOfCharacters + 1 === this.readIndex) { + while (this.numberOfCharacters + 1 === this.readIndex) { if (!this.sentNull) { // Must return null once to indicate we're done for now. this.sentNull = true return null } this.sentNull = false + // Prompt will reset this.readIndex to 1 this.prompt() } const char = this.buffer[this.readIndex] this.readIndex += 1 - // How do I send an EOF?? return char } } @@ -71,7 +71,11 @@ var Module = { onmessage = (event) => { if (event.data.type === 'run') { - // TODO: Set up files from event.data.files + if (event.data.files) { + for (const [filename, contents] of Object.entries(event.data.files)) { + Module.FS.writeFile(filename, contents) + } + } const ret = callMain(event.data.args) postMessage({ type: 'finished', |