forked from AbleOS/holey-bytes
progress
This commit is contained in:
parent
5364b66629
commit
69b58c2b36
10
.gitignore
vendored
10
.gitignore
vendored
|
@ -1,6 +1,12 @@
|
|||
# garbage
|
||||
/target
|
||||
/bytecode/src/instrs.rs
|
||||
/.rgignore
|
||||
rustc-ice-*
|
||||
|
||||
# sqlite
|
||||
db.sqlite
|
||||
db.sqlite-journal
|
||||
|
||||
# assets
|
||||
/depell/src/*.gz
|
||||
/depell/src/*.wasm
|
||||
/bytecode/src/instrs.rs
|
||||
|
|
16
Cargo.lock
generated
16
Cargo.lock
generated
|
@ -137,6 +137,15 @@ version = "1.7.2"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "428d9aa8fbc0670b7b8d6030a7fadd0f86151cae55e4dbbece15f3780a3dfaf3"
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.1.30"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b16803a61b81d9eabb7eae2588776c4c1e584b738ede45fdbb4c972cec1e9945"
|
||||
dependencies = [
|
||||
"shlex",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
|
@ -417,6 +426,7 @@ version = "0.30.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2e99fb7a497b1e3339bc746195567ed8d3e24945ecd636e3619d20b9de9e9149"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"pkg-config",
|
||||
"vcpkg",
|
||||
]
|
||||
|
@ -649,6 +659,12 @@ dependencies = [
|
|||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "shlex"
|
||||
version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
|
||||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
version = "1.13.2"
|
||||
|
|
|
@ -8,10 +8,7 @@ axum = "0.7.7"
|
|||
getrandom = "0.2.15"
|
||||
htmlm = "0.5.0"
|
||||
log = "0.4.22"
|
||||
rusqlite = "0.32.1"
|
||||
rusqlite = { version = "0.32.1", features = ["bundled"] }
|
||||
serde = { version = "1.0.210", features = ["derive"] }
|
||||
time = "0.3.36"
|
||||
tokio = { version = "1.40.0", features = ["rt"] }
|
||||
|
||||
[features]
|
||||
gzip = []
|
||||
|
|
10
depell/README.md
Normal file
10
depell/README.md
Normal file
|
@ -0,0 +1,10 @@
|
|||
# Depell
|
||||
|
||||
Depell is a website that allows users to import/post/run hblang code and create huge dependency graphs
|
||||
|
||||
## Local Development
|
||||
|
||||
```bash
|
||||
cargo xtask watch-depell
|
||||
# browser http://localhost:8080
|
||||
```
|
|
@ -70,6 +70,7 @@ textarea {
|
|||
padding-top: calc(var(--small-gap) * 1.5);
|
||||
font-family: var(--monospace);
|
||||
resize: none;
|
||||
tab-size: 4;
|
||||
}
|
||||
|
||||
pre {
|
||||
|
|
|
@ -5,28 +5,21 @@ function never() { throw new Error() }
|
|||
|
||||
/**@type{WebAssembly.Instance}*/ let hbcInstance;
|
||||
/**@type{Promise<WebAssembly.WebAssemblyInstantiatedSource>}*/ let hbcInstaceFuture;
|
||||
/** @param {Uint8Array} code @param {number} fuel
|
||||
* @returns {Promise<string | undefined> | string | undefined} */
|
||||
function compileCode(code, fuel) {
|
||||
if (!hbcInstance) {
|
||||
hbcInstaceFuture ??= WebAssembly.instantiateStreaming(fetch("/hbc.wasm"), {});
|
||||
return (async () => {
|
||||
hbcInstance = (await hbcInstaceFuture).instance;
|
||||
return compileCodeSync(hbcInstance, code, fuel);
|
||||
})();
|
||||
} else {
|
||||
return compileCodeSync(hbcInstance, code, fuel);
|
||||
}
|
||||
|
||||
async function getHbcInstance() {
|
||||
hbcInstaceFuture ??= WebAssembly.instantiateStreaming(fetch("/hbc.wasm"), {});
|
||||
return hbcInstance ??= (await hbcInstaceFuture).instance;
|
||||
}
|
||||
|
||||
/** @param {WebAssembly.Instance} instance @param {Uint8Array} code @param {number} fuel @returns {string | undefined} */
|
||||
function compileCodeSync(instance, code, fuel) {
|
||||
const stack_pointer_offset = 1 << 20;
|
||||
|
||||
/** @param {WebAssembly.Instance} instance @param {Uint8Array} code @param {number} fuel
|
||||
* @returns {string} */
|
||||
function compileCode(instance, code, fuel) {
|
||||
let {
|
||||
INPUT, INPUT_LEN,
|
||||
LOG_MESSAGES, LOG_MESSAGES_LEN,
|
||||
PANIC_MESSAGE, PANIC_MESSAGE_LEN,
|
||||
memory, compile_and_run
|
||||
memory, compile_and_run,
|
||||
} = instance.exports;
|
||||
|
||||
if (!(true
|
||||
|
@ -48,48 +41,57 @@ function compileCodeSync(instance, code, fuel) {
|
|||
} catch (e) {
|
||||
if (PANIC_MESSAGE instanceof WebAssembly.Global
|
||||
&& PANIC_MESSAGE_LEN instanceof WebAssembly.Global) {
|
||||
console.error(bufToString(memory, PANIC_MESSAGE, PANIC_MESSAGE_LEN));
|
||||
console.error(e, bufToString(memory, PANIC_MESSAGE, PANIC_MESSAGE_LEN));
|
||||
}
|
||||
let log = bufToString(memory, LOG_MESSAGES, LOG_MESSAGES_LEN);
|
||||
console.error(log, e);
|
||||
return undefined;
|
||||
return bufToString(memory, LOG_MESSAGES, LOG_MESSAGES_LEN);
|
||||
}
|
||||
}
|
||||
|
||||
/** @typedef {Object} Post
|
||||
* @property {string} path
|
||||
* @property {string} code */
|
||||
|
||||
/** @param {Post[]} posts @returns {Uint8Array} */
|
||||
function packPosts(posts) {
|
||||
let len = 0; for (const post of posts) len += 2 + post.path.length + 2 + post.code.length;
|
||||
|
||||
const buf = new Uint8Array(len), view = new DataView(buf.buffer), enc = new TextEncoder();
|
||||
len = 0; for (const post of posts) {
|
||||
view.setUint16(len, post.path.length, true); len += 2;
|
||||
buf.set(enc.encode(post.path), len); len += post.path.length;
|
||||
view.setUint16(len, post.code.length, true); len += 2;
|
||||
buf.set(enc.encode(post.code), len); len += post.code.length;
|
||||
}
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
/** @param {WebAssembly.Memory} mem
|
||||
* @param {WebAssembly.Global} ptr
|
||||
* @param {WebAssembly.Global} len
|
||||
* @return {string} */
|
||||
function bufToString(mem, ptr, len) {
|
||||
return new TextDecoder()
|
||||
const res = new TextDecoder()
|
||||
.decode(new Uint8Array(mem.buffer, ptr.value,
|
||||
new DataView(mem.buffer).getUint32(len.value, true)));
|
||||
new DataView(mem.buffer).setUint32(len.value, 0, true);
|
||||
return res;
|
||||
}
|
||||
|
||||
/**@type{WebAssembly.Instance}*/ let fmtInstance;
|
||||
/**@type{Promise<WebAssembly.WebAssemblyInstantiatedSource>}*/ let fmtInstaceFuture;
|
||||
/** @param {string} code @param {"fmt" | "minify"} action
|
||||
* @returns {Promise<string | undefined> | string | undefined} */
|
||||
function modifyCode(code, action) {
|
||||
if (!fmtInstance) {
|
||||
fmtInstaceFuture ??= WebAssembly.instantiateStreaming(fetch("/hbfmt.wasm"), {});
|
||||
return (async () => {
|
||||
fmtInstance = (await fmtInstaceFuture).instance;
|
||||
return modifyCodeSync(fmtInstance, code, action);
|
||||
})();
|
||||
} else {
|
||||
return modifyCodeSync(fmtInstance, code, action);
|
||||
}
|
||||
}
|
||||
* @returns {Promise<string | undefined>} */
|
||||
async function modifyCode(code, action) {
|
||||
fmtInstaceFuture ??= WebAssembly.instantiateStreaming(fetch("/hbfmt.wasm"), {});
|
||||
fmtInstance ??= (await fmtInstaceFuture).instance;
|
||||
|
||||
/** @param {WebAssembly.Instance} instance @param {string} code @param {"fmt" | "minify"} action @returns {string | undefined} */
|
||||
function modifyCodeSync(instance, code, action) {
|
||||
let {
|
||||
INPUT, INPUT_LEN,
|
||||
OUTPUT, OUTPUT_LEN,
|
||||
PANIC_MESSAGE, PANIC_MESSAGE_LEN,
|
||||
memory, fmt, minify
|
||||
} = instance.exports;
|
||||
} = fmtInstance.exports;
|
||||
|
||||
if (!(true
|
||||
&& INPUT instanceof WebAssembly.Global
|
||||
|
@ -136,17 +138,36 @@ function wireUp(target) {
|
|||
execApply(target);
|
||||
cacheInputs(target);
|
||||
bindTextareaAutoResize(target);
|
||||
bindCodeEdit(target);
|
||||
}
|
||||
|
||||
/** @type {{ [key: string]: (content: string) => Promise<string> | string }} */
|
||||
const applyFns = {
|
||||
timestamp: (content) => new Date(parseInt(content) * 1000).toLocaleString(),
|
||||
fmt: (content) => {
|
||||
let res = modifyCode(content, "fmt");
|
||||
return res instanceof Promise ? res.then(c => c ?? content) : res ?? content;
|
||||
},
|
||||
fmt: (content) => modifyCode(content, "fmt").then(c => c ?? "post has invalid code"),
|
||||
};
|
||||
|
||||
/** @param {HTMLElement} target */
|
||||
async function bindCodeEdit(target) {
|
||||
const edit = target.querySelector("#code-edit");
|
||||
if (!(edit instanceof HTMLTextAreaElement)) return;
|
||||
const errors = target.querySelector("#compiler-output");
|
||||
if (!(errors instanceof HTMLPreElement)) never();
|
||||
|
||||
const hbc = await getHbcInstance();
|
||||
|
||||
const debounce = 0;
|
||||
let timeout = 0;
|
||||
edit.addEventListener("input", () => {
|
||||
if (timeout) clearTimeout(timeout);
|
||||
timeout = setTimeout(() => {
|
||||
const buf = packPosts([{ path: "local.hb", code: edit.value }]);
|
||||
errors.textContent = compileCode(hbc, buf, 1);
|
||||
timeout = 0;
|
||||
}, debounce);
|
||||
});
|
||||
}
|
||||
|
||||
/** @param {HTMLElement} target */
|
||||
function execApply(target) {
|
||||
for (const elem of target.querySelectorAll('[apply]')) {
|
||||
|
@ -175,23 +196,9 @@ function bindTextareaAutoResize(target) {
|
|||
});
|
||||
|
||||
textarea.onkeydown = (ev) => {
|
||||
const selecting = textarea.selectionStart !== textarea.selectionEnd;
|
||||
|
||||
if (ev.key === "Tab") {
|
||||
ev.preventDefault();
|
||||
document.execCommand('insertText', false, " ");
|
||||
}
|
||||
|
||||
if (ev.key === "Backspace" && textarea.selectionStart != 0 && !selecting) {
|
||||
let i = textarea.selectionStart, looped = false;
|
||||
while (textarea.value.charCodeAt(--i) === ' '.charCodeAt(0)) looped = true;
|
||||
if (textarea.value.charCodeAt(i) === '\n'.charCodeAt(0) && looped) {
|
||||
ev.preventDefault();
|
||||
let toDelete = (textarea.selectionStart - (i + 1)) % 4;
|
||||
if (toDelete === 0) toDelete = 4;
|
||||
textarea.selectionStart -= toDelete;
|
||||
document.execCommand('delete', false);
|
||||
}
|
||||
document.execCommand('insertText', false, "\t");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -235,17 +242,13 @@ if (window.location.hostname === 'localhost') {
|
|||
if (code != prev) console.error(code, prev);
|
||||
}
|
||||
{
|
||||
|
||||
const name = "foo.hb";
|
||||
const code = "main:=fn():void{return 42}";
|
||||
const buf = new Uint8Array(2 + name.length + 2 + code.length);
|
||||
const view = new DataView(buf.buffer);
|
||||
view.setUint16(0, name.length, true);
|
||||
buf.set(new TextEncoder().encode(name), 2);
|
||||
view.setUint16(2 + name.length, code.length, true);
|
||||
buf.set(new TextEncoder().encode(code), 2 + name.length + 2);
|
||||
const res = await compileCode(buf, 1) ?? never();
|
||||
const expected = "";
|
||||
const posts = [{
|
||||
path: "foo.hb",
|
||||
code: "main:=fn():int{return 42}",
|
||||
}];
|
||||
const buf = packPosts(posts);
|
||||
const res = compileCode(await getHbcInstance(), buf, 1) ?? never();
|
||||
const expected = "exit code: 42\n";
|
||||
if (expected != res) console.error(expected, res);
|
||||
}
|
||||
})()
|
||||
|
|
|
@ -153,8 +153,9 @@ impl Page for Post {
|
|||
<input name="author" type="text" value={session.name} hidden>
|
||||
<input name="name" type="text" placeholder="name" value=name
|
||||
required maxlength=MAX_POSTNAME_LENGTH>
|
||||
<textarea name="code" placeholder="code" rows=1 required>code</textarea>
|
||||
<textarea id="code-edit" name="code" placeholder="code" rows=1 required>code</textarea>
|
||||
<input type="submit" value="submit">
|
||||
<pre id="compiler-output"></pre>
|
||||
</form>
|
||||
!{include_str!("post-page.html")}
|
||||
}
|
||||
|
|
|
@ -18,7 +18,7 @@ wasm_rt::decl_buffer!(MAX_INPUT_SIZE, MAX_INPUT, INPUT, INPUT_LEN);
|
|||
unsafe fn compile_and_run(mut fuel: usize) {
|
||||
ALLOCATOR.reset();
|
||||
|
||||
log::set_logger(&wasm_rt::Logger).unwrap();
|
||||
_ = log::set_logger(&wasm_rt::Logger);
|
||||
log::set_max_level(log::LevelFilter::Error);
|
||||
|
||||
struct File<'a> {
|
||||
|
@ -81,7 +81,12 @@ unsafe fn compile_and_run(mut fuel: usize) {
|
|||
let unknown = ct.vm.read_reg(2).0;
|
||||
log::error!("unknown ecall: {unknown}")
|
||||
}
|
||||
Ok(hbvm::VmRunOk::Timer) => fuel -= 1,
|
||||
Ok(hbvm::VmRunOk::Timer) => {
|
||||
fuel -= 1;
|
||||
if fuel == 0 {
|
||||
log::error!("program timed out");
|
||||
}
|
||||
}
|
||||
Ok(hbvm::VmRunOk::Breakpoint) => todo!(),
|
||||
Err(e) => {
|
||||
log::error!("vm error: {e}");
|
||||
|
|
|
@ -25,16 +25,18 @@ macro_rules! decl_buffer {
|
|||
#[macro_export]
|
||||
macro_rules! decl_runtime {
|
||||
($memory_size:expr, $max_panic_size:expr) => {
|
||||
#[cfg(debug_assertions)]
|
||||
#[no_mangle]
|
||||
static mut PANIC_MESSAGE: [u8; $max_panic_size] = [0; $max_panic_size];
|
||||
#[cfg(debug_assertions)]
|
||||
#[no_mangle]
|
||||
static mut PANIC_MESSAGE_LEN: usize = 0;
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
#[panic_handler]
|
||||
pub fn handle_panic(_info: &core::panic::PanicInfo) -> ! {
|
||||
#[cfg(debug_assertions)]
|
||||
{
|
||||
#[no_mangle]
|
||||
static mut PANIC_MESSAGE: [u8; $max_panic_size] = [0; $max_panic_size];
|
||||
#[no_mangle]
|
||||
static mut PANIC_MESSAGE_LEN: usize = 0;
|
||||
|
||||
unsafe {
|
||||
use core::fmt::Write;
|
||||
let mut f = $crate::Write(&mut PANIC_MESSAGE[..]);
|
||||
|
@ -52,6 +54,16 @@ macro_rules! decl_runtime {
|
|||
#[cfg(target_arch = "wasm32")]
|
||||
#[alloc_error_handler]
|
||||
fn alloc_error(_: core::alloc::Layout) -> ! {
|
||||
#[cfg(debug_assertions)]
|
||||
{
|
||||
unsafe {
|
||||
use core::fmt::Write;
|
||||
let mut f = $crate::Write(&mut PANIC_MESSAGE[..]);
|
||||
_ = writeln!(f, "out of memory");
|
||||
PANIC_MESSAGE_LEN = $max_panic_size - f.0.len();
|
||||
}
|
||||
}
|
||||
|
||||
core::arch::wasm32::unreachable()
|
||||
}
|
||||
};
|
||||
|
|
|
@ -52,7 +52,6 @@ fn main() -> io::Result<()> {
|
|||
build_wasm_blob("hbc", true)?;
|
||||
exec(build_cmd("gzip -k -f depell/src/index.js"))?;
|
||||
exec(build_cmd("gzip -k -f depell/src/index.css"))?;
|
||||
exec(build_cmd("cargo run -p depell --features gzip"))?;
|
||||
Ok(())
|
||||
}
|
||||
"build-depell" => {
|
||||
|
@ -60,7 +59,12 @@ fn main() -> io::Result<()> {
|
|||
build_wasm_blob("hbc", false)?;
|
||||
exec(build_cmd("gzip -k -f depell/src/index.js"))?;
|
||||
exec(build_cmd("gzip -k -f depell/src/index.css"))?;
|
||||
exec(build_cmd("cargo run -p depell --features gzip --release"))?;
|
||||
Ok(())
|
||||
}
|
||||
"watch-depell" => {
|
||||
let mut c = build_cmd("cargo watch --why");
|
||||
c.arg("--exec=xtask build-depell-debug").arg("--exec=run -p depell");
|
||||
exec(c)?;
|
||||
Ok(())
|
||||
}
|
||||
_ => Ok(()),
|
||||
|
|
Loading…
Reference in a new issue