Part of a snippet emulator
This commit is contained in:
+1
-1
@@ -2,4 +2,4 @@
|
||||
|
||||
wasm-pack build --target web
|
||||
npm run bundle
|
||||
cp pkg/vweb_bg.wasm src/index.html build
|
||||
cp pkg/vweb_bg.wasm src/index.html src/snippet_emulator_demo.html build
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
require('esbuild').build({
|
||||
entryPoints: ['src/app.js'],
|
||||
entryPoints: ['src/app.js', 'src/snippet_emulator_demo.js'],
|
||||
bundle: true,
|
||||
outdir: 'build',
|
||||
format: 'esm',
|
||||
|
||||
Generated
+87
@@ -6,6 +6,8 @@
|
||||
"": {
|
||||
"dependencies": {
|
||||
"esbuild": "^0.16.12",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"xterm": "^5.0.0",
|
||||
"xterm-readline": "^1.1.1"
|
||||
}
|
||||
@@ -397,6 +399,53 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/js-tokens": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
|
||||
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="
|
||||
},
|
||||
"node_modules/loose-envify": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
|
||||
"integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
|
||||
"dependencies": {
|
||||
"js-tokens": "^3.0.0 || ^4.0.0"
|
||||
},
|
||||
"bin": {
|
||||
"loose-envify": "cli.js"
|
||||
}
|
||||
},
|
||||
"node_modules/react": {
|
||||
"version": "18.2.0",
|
||||
"resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz",
|
||||
"integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==",
|
||||
"dependencies": {
|
||||
"loose-envify": "^1.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-dom": {
|
||||
"version": "18.2.0",
|
||||
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz",
|
||||
"integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==",
|
||||
"dependencies": {
|
||||
"loose-envify": "^1.1.0",
|
||||
"scheduler": "^0.23.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^18.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/scheduler": {
|
||||
"version": "0.23.0",
|
||||
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz",
|
||||
"integrity": "sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==",
|
||||
"dependencies": {
|
||||
"loose-envify": "^1.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/string-width": {
|
||||
"version": "4.2.3",
|
||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
|
||||
@@ -615,6 +664,44 @@
|
||||
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
|
||||
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="
|
||||
},
|
||||
"js-tokens": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
|
||||
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="
|
||||
},
|
||||
"loose-envify": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
|
||||
"integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
|
||||
"requires": {
|
||||
"js-tokens": "^3.0.0 || ^4.0.0"
|
||||
}
|
||||
},
|
||||
"react": {
|
||||
"version": "18.2.0",
|
||||
"resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz",
|
||||
"integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==",
|
||||
"requires": {
|
||||
"loose-envify": "^1.1.0"
|
||||
}
|
||||
},
|
||||
"react-dom": {
|
||||
"version": "18.2.0",
|
||||
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz",
|
||||
"integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==",
|
||||
"requires": {
|
||||
"loose-envify": "^1.1.0",
|
||||
"scheduler": "^0.23.0"
|
||||
}
|
||||
},
|
||||
"scheduler": {
|
||||
"version": "0.23.0",
|
||||
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz",
|
||||
"integrity": "sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==",
|
||||
"requires": {
|
||||
"loose-envify": "^1.1.0"
|
||||
}
|
||||
},
|
||||
"string-width": {
|
||||
"version": "4.2.3",
|
||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
{
|
||||
"dependencies": {
|
||||
"esbuild": "^0.16.12",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"xterm": "^5.0.0",
|
||||
"xterm-readline": "^1.1.1"
|
||||
},
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
body {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr auto 1fr;
|
||||
grid-template-rows: 2em auto;
|
||||
background: #777777;
|
||||
}
|
||||
.react-root {
|
||||
grid-area: 2 / 2 / 3 / 3;
|
||||
}
|
||||
|
||||
.snippetEmulator {
|
||||
display: grid;
|
||||
grid-template-rows: 10em 1em 1em 2em;
|
||||
grid-template-columns: 20em;
|
||||
background: #333333;
|
||||
font-family: monospace;
|
||||
padding: 0.5em;
|
||||
}
|
||||
|
||||
.snippetEmulator textarea { grid-area: 1 / 1 / 2 / 2 }
|
||||
.snippetEmulator .src { grid-area: 1 / 1 / 2 / 2 }
|
||||
.snippetEmulator .message { grid-area: 2 / 1 / 3 / 2 }
|
||||
.snippetEmulator .buttons { grid-area: 3 / 1 / 4 / 2 }
|
||||
.snippetEmulator .stacks { grid-area: 4 / 1 / 5 / 2 }
|
||||
.snippetEmulator textarea {
|
||||
background: #0f0f0f;
|
||||
color: #dddddd;
|
||||
min-height: 10em;
|
||||
border: none;
|
||||
resize: vertical;
|
||||
padding: 0;
|
||||
border: none;
|
||||
}
|
||||
.snippetEmulator textarea:focus { outline: none; }
|
||||
.snippetEmulator .buttons a {
|
||||
margin-right: 0.5em;
|
||||
cursor: pointer;
|
||||
}
|
||||
.snippetEmulator .step, .snippetEmulator .run { color: lightgreen }
|
||||
.snippetEmulator .reset { color: lightcoral }
|
||||
.snippetEmulator .build, .snippetEmulator .edit { color: dodgerblue }
|
||||
@@ -0,0 +1,69 @@
|
||||
import './snippet_emulator.css'
|
||||
import React, {useState, useCallback, useEffect} from 'react'
|
||||
import { WasmCPU, assemble_snippet, source_map } from '../pkg/vweb.js'
|
||||
|
||||
export default function({ snippet }) {
|
||||
const [editing, setEditing] = useState(false) // Whether we're editing the snippet or running it
|
||||
const [src, setSrc] = useState(snippet) // The source code currently set
|
||||
const [message, setMessage] = useState('') // A status / error message
|
||||
const onChangeSrc = useCallback((e) => setSrc(e.target.value), []) // Callback for the textarea
|
||||
const [binary, setBinary] = useState(null) // The assembled binary
|
||||
const [sourceMap, setSourceMap] = useState({}) // A map from byte address to line number
|
||||
|
||||
// A callback to build the binary from source
|
||||
const rebuild = useCallback((code) => {
|
||||
try {
|
||||
const bin = assemble_snippet(code)
|
||||
const sm = source_map(code)
|
||||
setBinary(bin)
|
||||
setSourceMap(sm)
|
||||
setMessage(`Assembled ${bin.length} bytes`)
|
||||
console.log(sm.Ok)
|
||||
return true
|
||||
} catch(e) {
|
||||
setMessage(e.message)
|
||||
}
|
||||
}, [])
|
||||
|
||||
// On load, build the stuff
|
||||
useEffect(() => { rebuild(snippet) }, [snippet])
|
||||
|
||||
if (editing) {
|
||||
return (
|
||||
<div className='snippetEmulator'>
|
||||
<textarea value={src} onChange={onChangeSrc}></textarea>
|
||||
<div className='message'>{message}</div>
|
||||
<div className='buttons'>
|
||||
<a className='build' onClick={() => { rebuild(src) && setEditing(false) }}>[Build]</a>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
} else {
|
||||
return (
|
||||
<div className='snippetEmulator'>
|
||||
<SourceDisplay src={src}/>
|
||||
<div className='message'>{message}</div>
|
||||
<div className='buttons'>
|
||||
<a className='step'>[Step]</a>
|
||||
<a className='step'>[Run]</a>
|
||||
<a className='reset'>[Reset]</a>
|
||||
<a className='edit' onClick={() => { setEditing(true); setMessage('') }}>[Edit]</a>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
function SourceDisplay({ src }) {
|
||||
const lines = src.split('\n')
|
||||
const lineDivs = lines.map((line, i) => {
|
||||
return (<div key={`line_${i}`}>{line || <> </>}</div>)
|
||||
})
|
||||
return (
|
||||
<div className='src'>{lineDivs}</div>
|
||||
)
|
||||
}
|
||||
|
||||
function StackDisplay({ data, call }) {
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user