summaryrefslogtreecommitdiffstats
path: root/Tools
diff options
context:
space:
mode:
Diffstat (limited to 'Tools')
-rw-r--r--Tools/wasm/python.html67
-rw-r--r--Tools/wasm/python.worker.js10
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',