fixing feature incompatibility
This commit is contained in:
parent
07638caff0
commit
c4826d3bfd
|
@ -1,2 +1,4 @@
|
||||||
[alias]
|
[alias]
|
||||||
xtask = "r -p xtask --"
|
xtask = "r -p xtask --"
|
||||||
|
wasm-build = "b --target wasm32-unknown-unknown --profile=small -Zbuild-std=core,alloc -Zbuild-std-features=optimize_for_size,panic_immediate_abort -p"
|
||||||
|
wasm-build-debug = "b --target wasm32-unknown-unknown -p"
|
||||||
|
|
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -3,3 +3,4 @@
|
||||||
/.rgignore
|
/.rgignore
|
||||||
rustc-ice-*
|
rustc-ice-*
|
||||||
db.sqlite
|
db.sqlite
|
||||||
|
/depell/src/*.wasm
|
||||||
|
|
11
Cargo.lock
generated
11
Cargo.lock
generated
|
@ -810,6 +810,9 @@ name = "wasm-hbc"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"hblang",
|
"hblang",
|
||||||
|
"hbvm",
|
||||||
|
"log",
|
||||||
|
"wasm-rt",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -817,6 +820,14 @@ name = "wasm-hbfmt"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"hblang",
|
"hblang",
|
||||||
|
"wasm-rt",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-rt"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"log",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
11
Cargo.toml
11
Cargo.toml
|
@ -1,3 +1,5 @@
|
||||||
|
cargo-features = ["profile-rustflags"]
|
||||||
|
|
||||||
[workspace]
|
[workspace]
|
||||||
resolver = "2"
|
resolver = "2"
|
||||||
members = [
|
members = [
|
||||||
|
@ -9,11 +11,12 @@ members = [
|
||||||
"depell",
|
"depell",
|
||||||
"depell/wasm-fmt",
|
"depell/wasm-fmt",
|
||||||
"depell/wasm-hbc",
|
"depell/wasm-hbc",
|
||||||
|
"depell/wasm-rt",
|
||||||
]
|
]
|
||||||
|
|
||||||
[workspace.dependencies]
|
[workspace.dependencies]
|
||||||
hbbytecode = { path = "bytecode", default-features = false }
|
hbbytecode = { path = "bytecode", default-features = false }
|
||||||
hbvm = { path = "vm" }
|
hbvm = { path = "vm", default-features = false }
|
||||||
hbxrt = { path = "xrt" }
|
hbxrt = { path = "xrt" }
|
||||||
hblang = { path = "lang", default-features = false }
|
hblang = { path = "lang", default-features = false }
|
||||||
hbjit = { path = "jit" }
|
hbjit = { path = "jit" }
|
||||||
|
@ -26,9 +29,13 @@ codegen-units = 1
|
||||||
panic = "abort"
|
panic = "abort"
|
||||||
|
|
||||||
[profile.small]
|
[profile.small]
|
||||||
|
#rustflags = ["-Zfmt-debug=none", "-Zlocation-detail=none"]
|
||||||
inherits = "release"
|
inherits = "release"
|
||||||
opt-level = "z"
|
opt-level = "z"
|
||||||
strip = true
|
strip = "debuginfo"
|
||||||
|
#strip = true
|
||||||
lto = true
|
lto = true
|
||||||
codegen-units = 1
|
codegen-units = 1
|
||||||
panic = "abort"
|
panic = "abort"
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -54,9 +54,6 @@ form {
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: var(--small-gap);
|
gap: var(--small-gap);
|
||||||
|
|
||||||
::placeholder {
|
|
||||||
color: var(--placeholder);
|
|
||||||
}
|
|
||||||
|
|
||||||
.error {
|
.error {
|
||||||
color: var(--error);
|
color: var(--error);
|
||||||
|
|
|
@ -3,19 +3,82 @@
|
||||||
/** @return {never} */
|
/** @return {never} */
|
||||||
function never() { throw new Error() }
|
function never() { throw new Error() }
|
||||||
|
|
||||||
/**@type{WebAssembly.Instance}*/ let instance;
|
/**@type{WebAssembly.Instance}*/ let hbcInstance;
|
||||||
/**@type{Promise<WebAssembly.WebAssemblyInstantiatedSource>}*/ let instaceFuture;
|
/**@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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @param {WebAssembly.Instance} instance @param {Uint8Array} code @param {number} fuel @returns {string | undefined} */
|
||||||
|
function compileCodeSync(instance, code, fuel) {
|
||||||
|
let {
|
||||||
|
INPUT, INPUT_LEN,
|
||||||
|
LOG_MESSAGES, LOG_MESSAGES_LEN,
|
||||||
|
PANIC_MESSAGE, PANIC_MESSAGE_LEN,
|
||||||
|
memory, compile_and_run
|
||||||
|
} = instance.exports;
|
||||||
|
|
||||||
|
if (!(true
|
||||||
|
&& INPUT instanceof WebAssembly.Global
|
||||||
|
&& INPUT_LEN instanceof WebAssembly.Global
|
||||||
|
&& LOG_MESSAGES instanceof WebAssembly.Global
|
||||||
|
&& LOG_MESSAGES_LEN instanceof WebAssembly.Global
|
||||||
|
&& memory instanceof WebAssembly.Memory
|
||||||
|
&& typeof compile_and_run === "function"
|
||||||
|
)) console.log(instance.exports), never();
|
||||||
|
|
||||||
|
const dw = new DataView(memory.buffer);
|
||||||
|
dw.setUint32(INPUT_LEN.value, code.length, true);
|
||||||
|
new Uint8Array(memory.buffer, INPUT.value).set(code);
|
||||||
|
|
||||||
|
try {
|
||||||
|
compile_and_run(fuel);
|
||||||
|
return bufToString(memory, LOG_MESSAGES, LOG_MESSAGES_LEN);
|
||||||
|
} catch (e) {
|
||||||
|
if (PANIC_MESSAGE instanceof WebAssembly.Global
|
||||||
|
&& PANIC_MESSAGE_LEN instanceof WebAssembly.Global) {
|
||||||
|
console.error(bufToString(memory, PANIC_MESSAGE, PANIC_MESSAGE_LEN));
|
||||||
|
}
|
||||||
|
let log = bufToString(memory, LOG_MESSAGES, LOG_MESSAGES_LEN);
|
||||||
|
console.error(log, e);
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @param {WebAssembly.Memory} mem
|
||||||
|
* @param {WebAssembly.Global} ptr
|
||||||
|
* @param {WebAssembly.Global} len
|
||||||
|
* @return {string} */
|
||||||
|
function bufToString(mem, ptr, len) {
|
||||||
|
return new TextDecoder()
|
||||||
|
.decode(new Uint8Array(mem.buffer, ptr.value,
|
||||||
|
new DataView(mem.buffer).getUint32(len.value, true)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**@type{WebAssembly.Instance}*/ let fmtInstance;
|
||||||
|
/**@type{Promise<WebAssembly.WebAssemblyInstantiatedSource>}*/ let fmtInstaceFuture;
|
||||||
/** @param {string} code @param {"fmt" | "minify"} action
|
/** @param {string} code @param {"fmt" | "minify"} action
|
||||||
* @returns {Promise<string | undefined> | string | undefined} */
|
* @returns {Promise<string | undefined> | string | undefined} */
|
||||||
function modifyCode(code, action) {
|
function modifyCode(code, action) {
|
||||||
if (!instance) {
|
if (!fmtInstance) {
|
||||||
instaceFuture ??= WebAssembly.instantiateStreaming(fetch("/hbfmt.wasm"), {});
|
fmtInstaceFuture ??= WebAssembly.instantiateStreaming(fetch("/hbfmt.wasm"), {});
|
||||||
return (async () => {
|
return (async () => {
|
||||||
instance = (await instaceFuture).instance;
|
fmtInstance = (await fmtInstaceFuture).instance;
|
||||||
return modifyCodeSync(instance, code, action);
|
return modifyCodeSync(fmtInstance, code, action);
|
||||||
})();
|
})();
|
||||||
} else {
|
} else {
|
||||||
return modifyCodeSync(instance, code, action);
|
return modifyCodeSync(fmtInstance, code, action);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -100,11 +163,15 @@ function bindTextareaAutoResize(target) {
|
||||||
for (const textarea of target.querySelectorAll("textarea")) {
|
for (const textarea of target.querySelectorAll("textarea")) {
|
||||||
if (!(textarea instanceof HTMLTextAreaElement)) never();
|
if (!(textarea instanceof HTMLTextAreaElement)) never();
|
||||||
|
|
||||||
textarea.style.height = textarea.scrollHeight + "px";
|
const taCssMap = window.getComputedStyle(textarea);
|
||||||
|
const padding = parseInt(taCssMap.getPropertyValue('padding-top') ?? "0")
|
||||||
|
+ parseInt(taCssMap.getPropertyValue('padding-top') ?? "0");
|
||||||
|
textarea.style.height = "auto";
|
||||||
|
textarea.style.height = (textarea.scrollHeight - padding) + "px";
|
||||||
textarea.style.overflowY = "hidden";
|
textarea.style.overflowY = "hidden";
|
||||||
textarea.addEventListener("input", function() {
|
textarea.addEventListener("input", function() {
|
||||||
textarea.style.height = "auto";
|
textarea.style.height = "auto";
|
||||||
textarea.style.height = textarea.scrollHeight + "px";
|
textarea.style.height = (textarea.scrollHeight - padding) + "px";
|
||||||
});
|
});
|
||||||
|
|
||||||
textarea.onkeydown = (ev) => {
|
textarea.onkeydown = (ev) => {
|
||||||
|
@ -112,10 +179,7 @@ function bindTextareaAutoResize(target) {
|
||||||
|
|
||||||
if (ev.key === "Tab") {
|
if (ev.key === "Tab") {
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
const prevPos = textarea.selectionStart;
|
document.execCommand('insertText', false, " ");
|
||||||
textarea.value = textarea.value.slice(0, textarea.selectionStart) +
|
|
||||||
' ' + textarea.value.slice(textarea.selectionEnd);
|
|
||||||
textarea.selectionStart = textarea.selectionEnd = prevPos + 4;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ev.key === "Backspace" && textarea.selectionStart != 0 && !selecting) {
|
if (ev.key === "Backspace" && textarea.selectionStart != 0 && !selecting) {
|
||||||
|
@ -125,10 +189,8 @@ function bindTextareaAutoResize(target) {
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
let toDelete = (textarea.selectionStart - (i + 1)) % 4;
|
let toDelete = (textarea.selectionStart - (i + 1)) % 4;
|
||||||
if (toDelete === 0) toDelete = 4;
|
if (toDelete === 0) toDelete = 4;
|
||||||
const prevPos = textarea.selectionStart;
|
textarea.selectionStart -= toDelete;
|
||||||
textarea.value = textarea.value.slice(0, textarea.selectionStart - toDelete) +
|
document.execCommand('delete', false);
|
||||||
textarea.value.slice(textarea.selectionEnd);
|
|
||||||
textarea.selectionStart = textarea.selectionEnd = prevPos - toDelete;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -165,11 +227,27 @@ if (window.location.hostname === 'localhost') {
|
||||||
if (id !== new_id) window.location.reload();
|
if (id !== new_id) window.location.reload();
|
||||||
}, 300);
|
}, 300);
|
||||||
|
|
||||||
(async function testCodeChange() {
|
(async function test() {
|
||||||
const code = "main:=fn():void{return}";
|
{
|
||||||
const fmtd = await modifyCode(code, "fmt") ?? never();
|
const code = "main:=fn():void{return}";
|
||||||
const prev = await modifyCode(fmtd, "minify") ?? never();
|
const fmtd = await modifyCode(code, "fmt") ?? never();
|
||||||
if (code != prev) console.error(code, prev);
|
const prev = await modifyCode(fmtd, "minify") ?? never();
|
||||||
|
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 = "";
|
||||||
|
if (expected != res) console.error(expected, res);
|
||||||
|
}
|
||||||
})()
|
})()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -40,6 +40,8 @@ async fn amain() {
|
||||||
|
|
||||||
let router = axum::Router::new()
|
let router = axum::Router::new()
|
||||||
.route("/", get(Index::page))
|
.route("/", get(Index::page))
|
||||||
|
.route("/index.css", static_asset!("text/css", "index.css"))
|
||||||
|
.route("/index.js", static_asset!("text/javascript", "index.js"))
|
||||||
.route(
|
.route(
|
||||||
"/hbfmt.wasm",
|
"/hbfmt.wasm",
|
||||||
static_asset!(
|
static_asset!(
|
||||||
|
@ -47,6 +49,13 @@ async fn amain() {
|
||||||
"../../target/wasm32-unknown-unknown/small/wasm_hbfmt.wasm"
|
"../../target/wasm32-unknown-unknown/small/wasm_hbfmt.wasm"
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
.route(
|
||||||
|
"/hbc.wasm",
|
||||||
|
static_asset!(
|
||||||
|
"application/wasm",
|
||||||
|
"../../target/wasm32-unknown-unknown/small/wasm_hbc.wasm"
|
||||||
|
),
|
||||||
|
)
|
||||||
.route("/index-view", get(Index::get))
|
.route("/index-view", get(Index::get))
|
||||||
.route("/feed", get(Index::page))
|
.route("/feed", get(Index::page))
|
||||||
.route("/profile", get(Profile::page))
|
.route("/profile", get(Profile::page))
|
||||||
|
@ -377,7 +386,7 @@ async fn base(body: impl FnOnce(&mut String), session: Option<&Session>) -> Html
|
||||||
"<!DOCTYPE html>"
|
"<!DOCTYPE html>"
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<style>!{include_str!("index.css")}</style>
|
<link rel="stylesheet" href="/index.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<nav>
|
<nav>
|
||||||
|
@ -401,7 +410,7 @@ async fn base(body: impl FnOnce(&mut String), session: Option<&Session>) -> Html
|
||||||
<main>|f|{body(f)}</main>
|
<main>|f|{body(f)}</main>
|
||||||
</body>
|
</body>
|
||||||
<script src="https://unpkg.com/htmx.org@2.0.3" integrity="sha384-0895/pl2MU10Hqc6jd4RvrthNlDiE9U1tWmX7WRESftEDRosgxNsQG/Ze9YMRzHq" crossorigin="anonymous"></script>
|
<script src="https://unpkg.com/htmx.org@2.0.3" integrity="sha384-0895/pl2MU10Hqc6jd4RvrthNlDiE9U1tWmX7WRESftEDRosgxNsQG/Ze9YMRzHq" crossorigin="anonymous"></script>
|
||||||
<script>!{include_str!("index.js")}</script>
|
<script src="/index.js"></script>
|
||||||
</html>
|
</html>
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,3 +8,4 @@ crate-type = ["cdylib"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
hblang = { workspace = true, features = ["no_log"] }
|
hblang = { workspace = true, features = ["no_log"] }
|
||||||
|
wasm-rt = { version = "0.1.0", path = "../wasm-rt" }
|
||||||
|
|
|
@ -1,115 +1,16 @@
|
||||||
#![no_std]
|
#![no_std]
|
||||||
#![feature(slice_take)]
|
|
||||||
#![feature(str_from_raw_parts)]
|
#![feature(str_from_raw_parts)]
|
||||||
#![feature(alloc_error_handler)]
|
#![feature(alloc_error_handler)]
|
||||||
|
|
||||||
use {
|
use hblang::{fmt, parser};
|
||||||
core::{
|
|
||||||
alloc::{GlobalAlloc, Layout},
|
wasm_rt::decl_runtime!(128 * 1024, 1024 * 4);
|
||||||
cell::UnsafeCell,
|
|
||||||
},
|
|
||||||
hblang::parser::ParserCtx,
|
|
||||||
};
|
|
||||||
|
|
||||||
const ARENA_SIZE: usize = 128 * 1024;
|
|
||||||
const MAX_OUTPUT_SIZE: usize = 1024 * 10;
|
const MAX_OUTPUT_SIZE: usize = 1024 * 10;
|
||||||
|
wasm_rt::decl_buffer!(MAX_OUTPUT_SIZE, MAX_OUTPUT, OUTPUT, OUTPUT_LEN);
|
||||||
|
|
||||||
const MAX_INPUT_SIZE: usize = 1024 * 4;
|
const MAX_INPUT_SIZE: usize = 1024 * 4;
|
||||||
|
wasm_rt::decl_buffer!(MAX_INPUT_SIZE, MAX_INPUT, INPUT, INPUT_LEN);
|
||||||
#[cfg(target_arch = "wasm32")]
|
|
||||||
#[panic_handler]
|
|
||||||
pub fn handle_panic(_info: &core::panic::PanicInfo) -> ! {
|
|
||||||
//unsafe {
|
|
||||||
// use core::fmt::Write;
|
|
||||||
// let mut f = Write(&mut PANIC_MESSAGE[..]);
|
|
||||||
// _ = writeln!(f, "{}", info);
|
|
||||||
// PANIC_MESSAGE_LEN = 1024 - f.0.len();
|
|
||||||
//}
|
|
||||||
|
|
||||||
core::arch::wasm32::unreachable();
|
|
||||||
}
|
|
||||||
|
|
||||||
//#[no_mangle]
|
|
||||||
//static mut PANIC_MESSAGE: [u8; 1024] = [0; 1024];
|
|
||||||
//#[no_mangle]
|
|
||||||
//static mut PANIC_MESSAGE_LEN: usize = 0;
|
|
||||||
|
|
||||||
#[global_allocator]
|
|
||||||
static ALLOCATOR: ArenaAllocator = ArenaAllocator::new();
|
|
||||||
|
|
||||||
#[cfg(target_arch = "wasm32")]
|
|
||||||
#[alloc_error_handler]
|
|
||||||
fn alloc_error(_: core::alloc::Layout) -> ! {
|
|
||||||
core::arch::wasm32::unreachable()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[repr(C, align(32))]
|
|
||||||
struct ArenaAllocator {
|
|
||||||
arena: UnsafeCell<[u8; ARENA_SIZE]>,
|
|
||||||
head: UnsafeCell<*mut u8>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ArenaAllocator {
|
|
||||||
const fn new() -> Self {
|
|
||||||
ArenaAllocator {
|
|
||||||
arena: UnsafeCell::new([0; ARENA_SIZE]),
|
|
||||||
head: UnsafeCell::new(core::ptr::null_mut()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe fn reset(&self) {
|
|
||||||
(*self.head.get()) = self.arena.get().cast::<u8>().add(ARENA_SIZE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe impl Sync for ArenaAllocator {}
|
|
||||||
|
|
||||||
unsafe impl GlobalAlloc for ArenaAllocator {
|
|
||||||
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
|
|
||||||
let size = layout.size();
|
|
||||||
let align = layout.align();
|
|
||||||
|
|
||||||
let until = self.arena.get() as *mut u8;
|
|
||||||
|
|
||||||
let new_head = (*self.head.get()).sub(size);
|
|
||||||
let aligned_head = (new_head as usize & !(1 << (align - 1))) as *mut u8;
|
|
||||||
|
|
||||||
if until > aligned_head {
|
|
||||||
return core::ptr::null_mut();
|
|
||||||
}
|
|
||||||
|
|
||||||
*self.head.get() = aligned_head;
|
|
||||||
aligned_head
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe fn dealloc(&self, _ptr: *mut u8, _layout: Layout) {
|
|
||||||
/* lol */
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct Write<'a>(&'a mut [u8]);
|
|
||||||
|
|
||||||
impl core::fmt::Write for Write<'_> {
|
|
||||||
fn write_str(&mut self, s: &str) -> core::fmt::Result {
|
|
||||||
if let Some(m) = self.0.take_mut(..s.len()) {
|
|
||||||
m.copy_from_slice(s.as_bytes());
|
|
||||||
Ok(())
|
|
||||||
} else {
|
|
||||||
Err(core::fmt::Error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[no_mangle]
|
|
||||||
static mut OUTPUT: [u8; MAX_OUTPUT_SIZE] = [0; MAX_OUTPUT_SIZE];
|
|
||||||
#[no_mangle]
|
|
||||||
static mut OUTPUT_LEN: usize = 0;
|
|
||||||
|
|
||||||
#[no_mangle]
|
|
||||||
static MAX_INPUT: usize = MAX_INPUT_SIZE;
|
|
||||||
#[no_mangle]
|
|
||||||
static mut INPUT: [u8; MAX_INPUT_SIZE] = [0; MAX_INPUT_SIZE];
|
|
||||||
#[no_mangle]
|
|
||||||
static mut INPUT_LEN: usize = 0;
|
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
unsafe extern "C" fn fmt() {
|
unsafe extern "C" fn fmt() {
|
||||||
|
@ -117,19 +18,17 @@ unsafe extern "C" fn fmt() {
|
||||||
|
|
||||||
let code = core::str::from_raw_parts(core::ptr::addr_of!(INPUT).cast(), INPUT_LEN);
|
let code = core::str::from_raw_parts(core::ptr::addr_of!(INPUT).cast(), INPUT_LEN);
|
||||||
|
|
||||||
let arena =
|
let arena = parser::Arena::with_capacity(code.len() * parser::SOURCE_TO_AST_FACTOR);
|
||||||
hblang::parser::Arena::with_capacity(code.len() * hblang::parser::SOURCE_TO_AST_FACTOR);
|
let mut ctx = parser::ParserCtx::default();
|
||||||
let mut ctx = ParserCtx::default();
|
let exprs = parser::Parser::parse(&mut ctx, code, "source.hb", &mut |_, _| Ok(0), &arena);
|
||||||
let exprs =
|
|
||||||
hblang::parser::Parser::parse(&mut ctx, code, "source.hb", &mut |_, _| Ok(0), &arena);
|
|
||||||
|
|
||||||
let mut f = Write(&mut OUTPUT[..]);
|
let mut f = wasm_rt::Write(&mut OUTPUT[..]);
|
||||||
hblang::fmt::fmt_file(exprs, code, &mut f).unwrap();
|
fmt::fmt_file(exprs, code, &mut f).unwrap();
|
||||||
OUTPUT_LEN = MAX_OUTPUT_SIZE - f.0.len();
|
OUTPUT_LEN = MAX_OUTPUT_SIZE - f.0.len();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
unsafe extern "C" fn minify() {
|
unsafe extern "C" fn minify() {
|
||||||
let code = core::str::from_raw_parts_mut(core::ptr::addr_of_mut!(OUTPUT).cast(), OUTPUT_LEN);
|
let code = core::str::from_raw_parts_mut(core::ptr::addr_of_mut!(OUTPUT).cast(), OUTPUT_LEN);
|
||||||
OUTPUT_LEN = hblang::fmt::minify(code);
|
OUTPUT_LEN = fmt::minify(code);
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,4 +7,8 @@ edition = "2021"
|
||||||
crate-type = ["cdylib"]
|
crate-type = ["cdylib"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
hblang.workspace = true
|
hblang = { workspace = true, features = [] }
|
||||||
|
hbvm.workspace = true
|
||||||
|
log = "0.4.22"
|
||||||
|
wasm-rt = { version = "0.1.0", path = "../wasm-rt", features = ["log"] }
|
||||||
|
|
||||||
|
|
|
@ -1,103 +1,26 @@
|
||||||
#![feature(alloc_error_handler)]
|
#![feature(alloc_error_handler)]
|
||||||
|
#![feature(slice_take)]
|
||||||
#![no_std]
|
#![no_std]
|
||||||
|
|
||||||
use {
|
use {
|
||||||
alloc::{borrow::ToOwned, string::String, vec::Vec},
|
alloc::{string::String, vec::Vec},
|
||||||
core::{
|
|
||||||
alloc::{GlobalAlloc, Layout},
|
|
||||||
cell::UnsafeCell,
|
|
||||||
},
|
|
||||||
hblang::{codegen::Codegen, parser::FileId},
|
hblang::{codegen::Codegen, parser::FileId},
|
||||||
};
|
};
|
||||||
|
|
||||||
extern crate alloc;
|
extern crate alloc;
|
||||||
|
|
||||||
const ARENA_SIZE: usize = 4 * 128 * 1024;
|
wasm_rt::decl_runtime!(128 * 8 * 1024, 1024 * 4);
|
||||||
const MAX_PANIC_SIZE: usize = 1024 * 4;
|
|
||||||
const MAX_INPUT_SIZE: usize = 32 * 4 * 1024;
|
const MAX_INPUT_SIZE: usize = 32 * 4 * 1024;
|
||||||
|
wasm_rt::decl_buffer!(MAX_INPUT_SIZE, MAX_INPUT, INPUT, INPUT_LEN);
|
||||||
#[cfg(target_arch = "wasm32")]
|
|
||||||
#[panic_handler]
|
|
||||||
pub fn handle_panic(_info: &core::panic::PanicInfo) -> ! {
|
|
||||||
unsafe {
|
|
||||||
use core::fmt::Write;
|
|
||||||
let mut f = Write(&mut PANIC_MESSAGE[..]);
|
|
||||||
_ = writeln!(f, "{}", info);
|
|
||||||
PANIC_MESSAGE_LEN = 1024 - f.0.len();
|
|
||||||
}
|
|
||||||
|
|
||||||
core::arch::wasm32::unreachable();
|
|
||||||
}
|
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
static mut PANIC_MESSAGE: [u8; MAX_PANIC_SIZE] = [0; MAX_PANIC_SIZE];
|
unsafe fn compile_and_run(mut fuel: usize) {
|
||||||
#[no_mangle]
|
|
||||||
static mut PANIC_MESSAGE_LEN: usize = 0;
|
|
||||||
|
|
||||||
#[global_allocator]
|
|
||||||
static ALLOCATOR: ArenaAllocator = ArenaAllocator::new();
|
|
||||||
|
|
||||||
#[cfg(target_arch = "wasm32")]
|
|
||||||
#[alloc_error_handler]
|
|
||||||
fn alloc_error(_: core::alloc::Layout) -> ! {
|
|
||||||
core::arch::wasm32::unreachable()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[repr(C, align(32))]
|
|
||||||
struct ArenaAllocator {
|
|
||||||
arena: UnsafeCell<[u8; ARENA_SIZE]>,
|
|
||||||
head: UnsafeCell<*mut u8>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ArenaAllocator {
|
|
||||||
const fn new() -> Self {
|
|
||||||
ArenaAllocator {
|
|
||||||
arena: UnsafeCell::new([0; ARENA_SIZE]),
|
|
||||||
head: UnsafeCell::new(core::ptr::null_mut()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe fn reset(&self) {
|
|
||||||
(*self.head.get()) = self.arena.get().cast::<u8>().add(ARENA_SIZE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe impl Sync for ArenaAllocator {}
|
|
||||||
|
|
||||||
unsafe impl GlobalAlloc for ArenaAllocator {
|
|
||||||
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
|
|
||||||
let size = layout.size();
|
|
||||||
let align = layout.align();
|
|
||||||
|
|
||||||
let until = self.arena.get() as *mut u8;
|
|
||||||
|
|
||||||
let new_head = (*self.head.get()).sub(size);
|
|
||||||
let aligned_head = (new_head as usize & !(1 << (align - 1))) as *mut u8;
|
|
||||||
|
|
||||||
if until > aligned_head {
|
|
||||||
return core::ptr::null_mut();
|
|
||||||
}
|
|
||||||
|
|
||||||
*self.head.get() = aligned_head;
|
|
||||||
aligned_head
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe fn dealloc(&self, _ptr: *mut u8, _layout: Layout) {
|
|
||||||
/* lol */
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[no_mangle]
|
|
||||||
static MAX_INPUT: usize = MAX_INPUT_SIZE;
|
|
||||||
#[no_mangle]
|
|
||||||
static mut INPUT_LEN: usize = 0;
|
|
||||||
#[no_mangle]
|
|
||||||
static mut INPUT: [u8; MAX_INPUT_SIZE] = [0; MAX_INPUT_SIZE];
|
|
||||||
|
|
||||||
#[no_mangle]
|
|
||||||
unsafe fn compile_and_run() {
|
|
||||||
ALLOCATOR.reset();
|
ALLOCATOR.reset();
|
||||||
|
|
||||||
|
log::set_logger(&wasm_rt::Logger).unwrap();
|
||||||
|
log::set_max_level(log::LevelFilter::Error);
|
||||||
|
|
||||||
struct File<'a> {
|
struct File<'a> {
|
||||||
path: &'a str,
|
path: &'a str,
|
||||||
code: &'a mut str,
|
code: &'a mut str,
|
||||||
|
@ -118,7 +41,8 @@ unsafe fn compile_and_run() {
|
||||||
});
|
});
|
||||||
input_bytes = rest;
|
input_bytes = rest;
|
||||||
}
|
}
|
||||||
files.sort_unstable_by_key(|f| f.path);
|
|
||||||
|
hblang::quad_sort(&mut files, |a, b| a.path.cmp(b.path));
|
||||||
files
|
files
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -140,12 +64,29 @@ unsafe fn compile_and_run() {
|
||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
};
|
};
|
||||||
|
|
||||||
let code = {
|
let mut ct = {
|
||||||
let mut c = Codegen::default();
|
let mut c = Codegen::default();
|
||||||
c.files = files;
|
c.files = files;
|
||||||
c.generate();
|
c.generate();
|
||||||
let mut buf = Vec::with_capacity(1024 * 8);
|
c.assemble_comptime()
|
||||||
c.assemble(&mut buf);
|
|
||||||
buf
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
while fuel != 0 {
|
||||||
|
match ct.vm.run() {
|
||||||
|
Ok(hbvm::VmRunOk::End) => {
|
||||||
|
log::error!("exit code: {}", ct.vm.read_reg(1).0);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
Ok(hbvm::VmRunOk::Ecall) => {
|
||||||
|
let unknown = ct.vm.read_reg(2).0;
|
||||||
|
log::error!("unknown ecall: {unknown}")
|
||||||
|
}
|
||||||
|
Ok(hbvm::VmRunOk::Timer) => fuel -= 1,
|
||||||
|
Ok(hbvm::VmRunOk::Breakpoint) => todo!(),
|
||||||
|
Err(e) => {
|
||||||
|
log::error!("vm error: {e}");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
7
depell/wasm-rt/Cargo.toml
Normal file
7
depell/wasm-rt/Cargo.toml
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
[package]
|
||||||
|
name = "wasm-rt"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
log = { version = "0.4.22", optional = true }
|
144
depell/wasm-rt/src/lib.rs
Normal file
144
depell/wasm-rt/src/lib.rs
Normal file
|
@ -0,0 +1,144 @@
|
||||||
|
#![feature(alloc_error_handler)]
|
||||||
|
#![feature(slice_take)]
|
||||||
|
#![no_std]
|
||||||
|
|
||||||
|
use core::{
|
||||||
|
alloc::{GlobalAlloc, Layout},
|
||||||
|
cell::UnsafeCell,
|
||||||
|
};
|
||||||
|
|
||||||
|
extern crate alloc;
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! decl_buffer {
|
||||||
|
($cap:expr, $export_cap:ident, $export_base:ident, $export_len:ident) => {
|
||||||
|
#[no_mangle]
|
||||||
|
static $export_cap: usize = $cap;
|
||||||
|
#[no_mangle]
|
||||||
|
static mut $export_base: [u8; $cap] = [0; $cap];
|
||||||
|
#[no_mangle]
|
||||||
|
static mut $export_len: usize = 0;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! decl_runtime {
|
||||||
|
($memory_size:expr, $max_panic_size:expr) => {
|
||||||
|
#[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 = Write(&mut PANIC_MESSAGE[..]);
|
||||||
|
_ = writeln!(f, "{}", _info);
|
||||||
|
PANIC_MESSAGE_LEN = 1024 - f.0.len();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
core::arch::wasm32::unreachable();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[global_allocator]
|
||||||
|
static ALLOCATOR: $crate::ArenaAllocator<{ $memory_size }> = $crate::ArenaAllocator::new();
|
||||||
|
|
||||||
|
#[cfg(target_arch = "wasm32")]
|
||||||
|
#[alloc_error_handler]
|
||||||
|
fn alloc_error(_: core::alloc::Layout) -> ! {
|
||||||
|
core::arch::wasm32::unreachable()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "log")]
|
||||||
|
pub struct Logger;
|
||||||
|
|
||||||
|
#[cfg(feature = "log")]
|
||||||
|
impl log::Log for Logger {
|
||||||
|
fn enabled(&self, _: &log::Metadata) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
fn log(&self, record: &log::Record) {
|
||||||
|
if self.enabled(record.metadata()) {
|
||||||
|
const MAX_LOG_MESSAGE: usize = 1024 * 4;
|
||||||
|
#[no_mangle]
|
||||||
|
static mut LOG_MESSAGES: [u8; MAX_LOG_MESSAGE] = [0; MAX_LOG_MESSAGE];
|
||||||
|
#[no_mangle]
|
||||||
|
static mut LOG_MESSAGES_LEN: usize = 0;
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
use core::fmt::Write;
|
||||||
|
let mut f = Write(&mut LOG_MESSAGES[LOG_MESSAGES_LEN..]);
|
||||||
|
_ = writeln!(f, "{}", record.args());
|
||||||
|
LOG_MESSAGES_LEN = MAX_LOG_MESSAGE - f.0.len();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn flush(&self) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct ArenaAllocator<const SIZE: usize> {
|
||||||
|
arena: UnsafeCell<[u8; SIZE]>,
|
||||||
|
head: UnsafeCell<*mut u8>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<const SIZE: usize> ArenaAllocator<SIZE> {
|
||||||
|
#[allow(clippy::new_without_default)]
|
||||||
|
pub const fn new() -> Self {
|
||||||
|
ArenaAllocator {
|
||||||
|
arena: UnsafeCell::new([0; SIZE]),
|
||||||
|
head: UnsafeCell::new(core::ptr::null_mut()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::missing_safety_doc)]
|
||||||
|
pub unsafe fn reset(&self) {
|
||||||
|
(*self.head.get()) = self.arena.get().cast::<u8>().add(SIZE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe impl<const SIZE: usize> Sync for ArenaAllocator<SIZE> {}
|
||||||
|
|
||||||
|
unsafe impl<const SIZE: usize> GlobalAlloc for ArenaAllocator<SIZE> {
|
||||||
|
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
|
||||||
|
let size = layout.size();
|
||||||
|
let align = layout.align();
|
||||||
|
|
||||||
|
let until = self.arena.get() as *mut u8;
|
||||||
|
|
||||||
|
let new_head = (*self.head.get()).sub(size);
|
||||||
|
let aligned_head = (new_head as usize & !(1 << (align - 1))) as *mut u8;
|
||||||
|
|
||||||
|
if until > aligned_head {
|
||||||
|
return core::ptr::null_mut();
|
||||||
|
}
|
||||||
|
|
||||||
|
*self.head.get() = aligned_head;
|
||||||
|
aligned_head
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn dealloc(&self, _ptr: *mut u8, _layout: Layout) {
|
||||||
|
/* lol */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Write<'a>(pub &'a mut [u8]);
|
||||||
|
|
||||||
|
impl core::fmt::Write for Write<'_> {
|
||||||
|
fn write_str(&mut self, s: &str) -> core::fmt::Result {
|
||||||
|
if let Some(m) = self.0.take_mut(..s.len()) {
|
||||||
|
m.copy_from_slice(s.as_bytes());
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(core::fmt::Error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -11,7 +11,7 @@ path = "src/main.rs"
|
||||||
hashbrown = { version = "0.15.0", default-features = false, features = ["raw-entry"] }
|
hashbrown = { version = "0.15.0", default-features = false, features = ["raw-entry"] }
|
||||||
hbbytecode = { workspace = true, features = ["disasm"] }
|
hbbytecode = { workspace = true, features = ["disasm"] }
|
||||||
hbvm = { workspace = true, features = ["nightly"] }
|
hbvm = { workspace = true, features = ["nightly"] }
|
||||||
log = { version = "0.4.22", features = ["release_max_level_error"] }
|
log = "0.4.22"
|
||||||
|
|
||||||
[dependencies.regalloc2]
|
[dependencies.regalloc2]
|
||||||
git = "https://github.com/jakubDoka/regalloc2"
|
git = "https://github.com/jakubDoka/regalloc2"
|
||||||
|
|
|
@ -169,16 +169,16 @@ main := fn(): int {
|
||||||
}
|
}
|
||||||
|
|
||||||
finst := Ty2.{ty: .{a: 4, b: 1}, c: 3}
|
finst := Ty2.{ty: .{a: 4, b: 1}, c: 3}
|
||||||
//inst := odher_pass(finst)
|
inst := odher_pass(finst)
|
||||||
//if finst.c == 3 {
|
if inst.c == 3 {
|
||||||
//return finst.ty.a - finst.ty.b
|
return pass(&inst.ty)
|
||||||
return pass(&finst.ty)
|
}
|
||||||
//}
|
return 0
|
||||||
//return 0
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pass := fn(t: ^Ty): int {
|
pass := fn(t: ^Ty): int {
|
||||||
return t.a - t.b
|
.{a, b} := *t
|
||||||
|
return a - b
|
||||||
}
|
}
|
||||||
|
|
||||||
odher_pass := fn(t: Ty2): Ty2 {
|
odher_pass := fn(t: Ty2): Ty2 {
|
||||||
|
|
|
@ -8,7 +8,7 @@ use {
|
||||||
self, find_symbol, idfl, CommentOr, CtorField, Expr, ExprRef, FileId, Pos, StructField,
|
self, find_symbol, idfl, CommentOr, CtorField, Expr, ExprRef, FileId, Pos, StructField,
|
||||||
},
|
},
|
||||||
ty, Field, Func, Global, LoggedMem, OffsetIter, ParamAlloc, Reloc, Sig, Struct, SymKey,
|
ty, Field, Func, Global, LoggedMem, OffsetIter, ParamAlloc, Reloc, Sig, Struct, SymKey,
|
||||||
TypedReloc, Types,
|
TypedReloc, Types, HEADER_SIZE,
|
||||||
},
|
},
|
||||||
alloc::{boxed::Box, string::String, vec::Vec},
|
alloc::{boxed::Box, string::String, vec::Vec},
|
||||||
core::fmt::Display,
|
core::fmt::Display,
|
||||||
|
@ -637,11 +637,19 @@ struct Pool {
|
||||||
|
|
||||||
const VM_STACK_SIZE: usize = 1024 * 64;
|
const VM_STACK_SIZE: usize = 1024 * 64;
|
||||||
|
|
||||||
struct Comptime {
|
pub struct Comptime {
|
||||||
vm: hbvm::Vm<LoggedMem, { 1024 * 10 }>,
|
pub vm: hbvm::Vm<LoggedMem, { 1024 * 10 }>,
|
||||||
_stack: Box<[u8; VM_STACK_SIZE]>,
|
stack: Box<[u8; VM_STACK_SIZE]>,
|
||||||
code: Vec<u8>,
|
code: Vec<u8>,
|
||||||
}
|
}
|
||||||
|
impl Comptime {
|
||||||
|
fn reset(&mut self) {
|
||||||
|
let ptr = unsafe { self.stack.as_mut_ptr().cast::<u8>().add(VM_STACK_SIZE) as u64 };
|
||||||
|
self.vm.registers.fill(hbvm::value::Value(0));
|
||||||
|
self.vm.write_reg(STACK_PTR, ptr);
|
||||||
|
self.vm.pc = hbvm::mem::Address::new(self.code.as_ptr() as u64 + HEADER_SIZE as u64);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Default for Comptime {
|
impl Default for Comptime {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
|
@ -649,7 +657,7 @@ impl Default for Comptime {
|
||||||
let mut vm = hbvm::Vm::default();
|
let mut vm = hbvm::Vm::default();
|
||||||
let ptr = unsafe { stack.as_mut_ptr().cast::<u8>().add(VM_STACK_SIZE) as u64 };
|
let ptr = unsafe { stack.as_mut_ptr().cast::<u8>().add(VM_STACK_SIZE) as u64 };
|
||||||
vm.write_reg(STACK_PTR, ptr);
|
vm.write_reg(STACK_PTR, ptr);
|
||||||
Self { vm, _stack: unsafe { stack.assume_init() }, code: Default::default() }
|
Self { vm, stack: unsafe { stack.assume_init() }, code: Default::default() }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1233,9 +1241,12 @@ impl Codegen {
|
||||||
e => unimplemented!("{e:?}"),
|
e => unimplemented!("{e:?}"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
smh => self.report(
|
_ => self.report(
|
||||||
target.pos(),
|
target.pos(),
|
||||||
format_args!("the field operation is not supported: {smh:?}"),
|
format_args!(
|
||||||
|
"the field operation is not supported: {}",
|
||||||
|
self.ty_display(tal.ty)
|
||||||
|
),
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1456,8 +1467,7 @@ impl Codegen {
|
||||||
self.report(
|
self.report(
|
||||||
pos,
|
pos,
|
||||||
format_args!(
|
format_args!(
|
||||||
"this integer was inferred to be '{}' \
|
"this integer was inferred to be '{}'",
|
||||||
which does not make sense",
|
|
||||||
self.ty_display(ty)
|
self.ty_display(ty)
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
@ -1715,16 +1725,11 @@ impl Codegen {
|
||||||
unimplemented!("{:#?}", op)
|
unimplemented!("{:#?}", op)
|
||||||
}
|
}
|
||||||
E::Comment { .. } => Some(Value::void()),
|
E::Comment { .. } => Some(Value::void()),
|
||||||
ref ast => self.report_unhandled_ast(ast, "expression"),
|
ref ast => self.report_unhandled_ast(ast, "something"),
|
||||||
}?;
|
}?;
|
||||||
|
|
||||||
if let Some(ty) = ctx.ty {
|
if let Some(ty) = ctx.ty {
|
||||||
_ = self.assert_ty(
|
_ = self.assert_ty(expr.pos(), value.ty, ty, "somehow");
|
||||||
expr.pos(),
|
|
||||||
value.ty,
|
|
||||||
ty,
|
|
||||||
format_args!("'{}'", self.ast_display(expr)),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Some(match ctx.loc {
|
Some(match ctx.loc {
|
||||||
|
@ -2666,7 +2671,7 @@ impl Codegen {
|
||||||
|
|
||||||
fn run_vm(&mut self) {
|
fn run_vm(&mut self) {
|
||||||
loop {
|
loop {
|
||||||
match self.ct.vm.run().unwrap() {
|
match self.ct.vm.run().unwrap_or_else(|e| panic!("{e:?}")) {
|
||||||
hbvm::VmRunOk::End => break,
|
hbvm::VmRunOk::End => break,
|
||||||
hbvm::VmRunOk::Timer => unreachable!(),
|
hbvm::VmRunOk::Timer => unreachable!(),
|
||||||
hbvm::VmRunOk::Ecall => self.handle_ecall(),
|
hbvm::VmRunOk::Ecall => self.handle_ecall(),
|
||||||
|
@ -2702,26 +2707,16 @@ impl Codegen {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn report_log(&self, pos: Pos, msg: impl core::fmt::Display) {
|
|
||||||
log::error!("{}", self.cfile().report(pos, msg));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
fn report(&self, pos: Pos, msg: impl core::fmt::Display) -> ! {
|
fn report(&self, pos: Pos, msg: impl core::fmt::Display) -> ! {
|
||||||
self.report_log(pos, msg);
|
log::error!("{}", self.cfile().report(pos, msg));
|
||||||
unreachable!();
|
unreachable!();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
fn report_unhandled_ast(&self, ast: &Expr, hint: &str) -> ! {
|
fn report_unhandled_ast(&self, ast: &Expr, hint: &str) -> ! {
|
||||||
log::debug!("{ast:#?}");
|
log::debug!("{ast:#?}");
|
||||||
self.report(
|
self.report(ast.pos(), format_args!("compiler does not (yet) know how to handle ({hint})",))
|
||||||
ast.pos(),
|
|
||||||
format_args!(
|
|
||||||
"compiler does not (yet) know how to handle ({hint}):\n{}",
|
|
||||||
self.ast_display(ast)
|
|
||||||
),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn cfile(&self) -> &parser::Ast {
|
fn cfile(&self) -> &parser::Ast {
|
||||||
|
@ -2753,9 +2748,14 @@ impl Codegen {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn assemble(&mut self, buf: &mut Vec<u8>) {
|
pub fn assemble(&mut self, buf: &mut Vec<u8>) {
|
||||||
self.tys.ins.funcs.iter_mut().for_each(|f| f.offset = u32::MAX);
|
self.tys.reassemble(buf);
|
||||||
self.tys.ins.globals.iter_mut().for_each(|g| g.offset = u32::MAX);
|
}
|
||||||
self.tys.assemble(buf)
|
|
||||||
|
pub fn assemble_comptime(mut self) -> Comptime {
|
||||||
|
self.ct.code.clear();
|
||||||
|
self.tys.reassemble(&mut self.ct.code);
|
||||||
|
self.ct.reset();
|
||||||
|
self.ct
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,26 @@ use {
|
||||||
core::fmt,
|
core::fmt,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pub fn display_radix(radix: Radix, mut value: u64, buf: &mut [u8; 64]) -> &str {
|
||||||
|
fn conv_radix(d: u8) -> u8 {
|
||||||
|
match d {
|
||||||
|
0..=9 => d + b'0',
|
||||||
|
_ => d - 10 + b'A',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i, b) in buf.iter_mut().enumerate().rev() {
|
||||||
|
let d = (value % radix as u64) as u8;
|
||||||
|
value /= radix as u64;
|
||||||
|
*b = conv_radix(d);
|
||||||
|
if value == 0 {
|
||||||
|
return unsafe { core::str::from_utf8_unchecked(&buf[i..]) };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unreachable!()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn minify(source: &mut str) -> usize {
|
pub fn minify(source: &mut str) -> usize {
|
||||||
fn needs_space(c: u8) -> bool {
|
fn needs_space(c: u8) -> bool {
|
||||||
matches!(c, b'a'..=b'z' | b'A'..=b'Z' | b'0'..=b'9' | 127..)
|
matches!(c, b'a'..=b'z' | b'A'..=b'Z' | b'0'..=b'9' | 127..)
|
||||||
|
@ -16,7 +36,7 @@ pub fn minify(source: &mut str) -> usize {
|
||||||
let mut prev_needs_whitecpace = false;
|
let mut prev_needs_whitecpace = false;
|
||||||
let mut prev_needs_newline = false;
|
let mut prev_needs_newline = false;
|
||||||
loop {
|
loop {
|
||||||
let mut token = lexer::Lexer::new(reader).next();
|
let mut token = lexer::Lexer::new(reader).eat();
|
||||||
match token.kind {
|
match token.kind {
|
||||||
TokenKind::Eof => break,
|
TokenKind::Eof => break,
|
||||||
TokenKind::CtIdent | TokenKind::Directive => token.start -= 1,
|
TokenKind::CtIdent | TokenKind::Directive => token.start -= 1,
|
||||||
|
@ -321,25 +341,6 @@ impl<'a> Formatter<'a> {
|
||||||
self.fmt_list(f, true, "}", "", stmts, Self::fmt)
|
self.fmt_list(f, true, "}", "", stmts, Self::fmt)
|
||||||
}
|
}
|
||||||
Expr::Number { value, radix, .. } => {
|
Expr::Number { value, radix, .. } => {
|
||||||
fn display_radix(radix: Radix, mut value: u64, buf: &mut [u8; 64]) -> &str {
|
|
||||||
fn conv_radix(d: u8) -> u8 {
|
|
||||||
match d {
|
|
||||||
0..=9 => d + b'0',
|
|
||||||
_ => d - 10 + b'A',
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (i, b) in buf.iter_mut().enumerate().rev() {
|
|
||||||
let d = (value % radix as u64) as u8;
|
|
||||||
value /= radix as u64;
|
|
||||||
*b = conv_radix(d);
|
|
||||||
if value == 0 {
|
|
||||||
return unsafe { core::str::from_utf8_unchecked(&buf[i..]) };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
unreachable!()
|
|
||||||
}
|
|
||||||
f.write_str(match radix {
|
f.write_str(match radix {
|
||||||
Radix::Decimal => "",
|
Radix::Decimal => "",
|
||||||
Radix::Hex => "0x",
|
Radix::Hex => "0x",
|
||||||
|
@ -404,7 +405,7 @@ pub fn preserve_newlines(source: &str) -> usize {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn insert_needed_semicolon(source: &str) -> bool {
|
pub fn insert_needed_semicolon(source: &str) -> bool {
|
||||||
let kind = lexer::Lexer::new(source).next().kind;
|
let kind = lexer::Lexer::new(source).eat().kind;
|
||||||
kind.precedence().is_some() || matches!(kind, TokenKind::Ctor | TokenKind::Tupl)
|
kind.precedence().is_some() || matches!(kind, TokenKind::Ctor | TokenKind::Tupl)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -450,15 +451,15 @@ pub mod test {
|
||||||
minned.truncate(len);
|
minned.truncate(len);
|
||||||
|
|
||||||
let ast = parser::Ast::new(ident, minned, &mut ParserCtx::default(), &mut |_, _| Ok(0));
|
let ast = parser::Ast::new(ident, minned, &mut ParserCtx::default(), &mut |_, _| Ok(0));
|
||||||
log::error!(
|
//log::error!(
|
||||||
"{} / {} = {} | {} / {} = {}",
|
// "{} / {} = {} | {} / {} = {}",
|
||||||
ast.mem.size(),
|
// ast.mem.size(),
|
||||||
input.len(),
|
// input.len(),
|
||||||
ast.mem.size() as f32 / input.len() as f32,
|
// ast.mem.size() as f32 / input.len() as f32,
|
||||||
ast.mem.size(),
|
// ast.mem.size(),
|
||||||
ast.file.len(),
|
// ast.file.len(),
|
||||||
ast.mem.size() as f32 / ast.file.len() as f32
|
// ast.mem.size() as f32 / ast.file.len() as f32
|
||||||
);
|
//);
|
||||||
let mut output = String::new();
|
let mut output = String::new();
|
||||||
write!(output, "{ast}").unwrap();
|
write!(output, "{ast}").unwrap();
|
||||||
|
|
||||||
|
|
|
@ -72,7 +72,6 @@ macro_rules! gen_token_kind {
|
||||||
} + 1)
|
} + 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
fn from_ident(ident: &[u8]) -> Self {
|
fn from_ident(ident: &[u8]) -> Self {
|
||||||
match ident {
|
match ident {
|
||||||
$($keyword_lit => Self::$keyword,)*
|
$($keyword_lit => Self::$keyword,)*
|
||||||
|
@ -377,6 +376,25 @@ impl<'a> Lexer<'a> {
|
||||||
Self::restore(input, 0)
|
Self::restore(input, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn imports(input: &'a str) -> impl Iterator<Item = &'a str> {
|
||||||
|
let mut s = Self::new(input);
|
||||||
|
core::iter::from_fn(move || loop {
|
||||||
|
let t = s.eat();
|
||||||
|
if t.kind == TokenKind::Eof {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
if t.kind == TokenKind::Directive
|
||||||
|
&& s.slice(t.range()) == "use"
|
||||||
|
&& s.eat().kind == TokenKind::LParen
|
||||||
|
{
|
||||||
|
let t = s.eat();
|
||||||
|
if t.kind == TokenKind::DQuote {
|
||||||
|
return Some(&s.slice(t.range())[1..t.range().len() - 1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
pub fn restore(input: &'a str, pos: u32) -> Self {
|
pub fn restore(input: &'a str, pos: u32) -> Self {
|
||||||
Self { pos, source: input.as_bytes() }
|
Self { pos, source: input.as_bytes() }
|
||||||
}
|
}
|
||||||
|
@ -404,9 +422,9 @@ impl<'a> Lexer<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn last(&mut self) -> Token {
|
pub fn last(&mut self) -> Token {
|
||||||
let mut token = self.next();
|
let mut token = self.eat();
|
||||||
loop {
|
loop {
|
||||||
let next = self.next();
|
let next = self.eat();
|
||||||
if next.kind == TokenKind::Eof {
|
if next.kind == TokenKind::Eof {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -415,7 +433,7 @@ impl<'a> Lexer<'a> {
|
||||||
token
|
token
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn next(&mut self) -> Token {
|
pub fn eat(&mut self) -> Token {
|
||||||
use TokenKind as T;
|
use TokenKind as T;
|
||||||
loop {
|
loop {
|
||||||
let mut start = self.pos;
|
let mut start = self.pos;
|
||||||
|
|
|
@ -20,7 +20,8 @@
|
||||||
iter_intersperse,
|
iter_intersperse,
|
||||||
str_from_raw_parts,
|
str_from_raw_parts,
|
||||||
ptr_sub_ptr,
|
ptr_sub_ptr,
|
||||||
slice_from_ptr_range
|
slice_from_ptr_range,
|
||||||
|
is_sorted
|
||||||
)]
|
)]
|
||||||
#![warn(clippy::dbg_macro)]
|
#![warn(clippy::dbg_macro)]
|
||||||
#![allow(stable_features, internal_features)]
|
#![allow(stable_features, internal_features)]
|
||||||
|
@ -36,7 +37,7 @@ use {
|
||||||
ty::ArrayLen,
|
ty::ArrayLen,
|
||||||
},
|
},
|
||||||
alloc::{collections::BTreeMap, string::String, vec::Vec},
|
alloc::{collections::BTreeMap, string::String, vec::Vec},
|
||||||
core::{cell::Cell, fmt::Display, ops::Range},
|
core::{cell::Cell, ops::Range},
|
||||||
hashbrown::hash_map,
|
hashbrown::hash_map,
|
||||||
hbbytecode as instrs,
|
hbbytecode as instrs,
|
||||||
};
|
};
|
||||||
|
@ -68,7 +69,7 @@ pub mod parser;
|
||||||
#[cfg(feature = "opts")]
|
#[cfg(feature = "opts")]
|
||||||
pub mod son;
|
pub mod son;
|
||||||
|
|
||||||
mod lexer;
|
pub mod lexer;
|
||||||
#[cfg(feature = "opts")]
|
#[cfg(feature = "opts")]
|
||||||
mod vc;
|
mod vc;
|
||||||
|
|
||||||
|
@ -535,37 +536,54 @@ mod ty {
|
||||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||||
use Kind as TK;
|
use Kind as TK;
|
||||||
match TK::from_ty(self.ty) {
|
match TK::from_ty(self.ty) {
|
||||||
TK::Module(idx) => write!(f, "@use({:?})[{}]", self.files[idx as usize].path, idx),
|
TK::Module(idx) => {
|
||||||
TK::Builtin(ty) => write!(f, "{}", to_str(ty)),
|
f.write_str("@use(\"")?;
|
||||||
TK::Ptr(ty) => {
|
self.files[idx as usize].path.fmt(f)?;
|
||||||
write!(f, "^{}", self.rety(self.tys.ins.ptrs[ty as usize].base))
|
f.write_str(")[")?;
|
||||||
|
idx.fmt(f)?;
|
||||||
|
f.write_str("]")
|
||||||
}
|
}
|
||||||
|
TK::Builtin(ty) => f.write_str(to_str(ty)),
|
||||||
|
TK::Ptr(ty) => self.rety(self.tys.ins.ptrs[ty as usize].base).fmt(f),
|
||||||
TK::Struct(idx) => {
|
TK::Struct(idx) => {
|
||||||
let record = &self.tys.ins.structs[idx as usize];
|
let record = &self.tys.ins.structs[idx as usize];
|
||||||
if ident::is_null(record.name) {
|
if ident::is_null(record.name) {
|
||||||
write!(f, "[{idx}]{{")?;
|
f.write_str("[")?;
|
||||||
|
idx.fmt(f)?;
|
||||||
|
f.write_str("]{")?;
|
||||||
for (i, &super::Field { name, ty }) in
|
for (i, &super::Field { name, ty }) in
|
||||||
self.tys.struct_fields(idx).iter().enumerate()
|
self.tys.struct_fields(idx).iter().enumerate()
|
||||||
{
|
{
|
||||||
if i != 0 {
|
if i != 0 {
|
||||||
write!(f, ", ")?;
|
f.write_str(", ")?;
|
||||||
}
|
}
|
||||||
write!(f, "{}: {}", self.tys.names.ident_str(name), self.rety(ty))?;
|
f.write_str(self.tys.names.ident_str(name))?;
|
||||||
|
f.write_str(": ")?;
|
||||||
|
self.rety(ty).fmt(f)?;
|
||||||
}
|
}
|
||||||
write!(f, "}}")
|
f.write_str("}")
|
||||||
} else {
|
} else {
|
||||||
let file = &self.files[record.file as usize];
|
let file = &self.files[record.file as usize];
|
||||||
write!(f, "{}", file.ident_str(record.name))
|
f.write_str(file.ident_str(record.name))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
TK::Func(idx) => write!(f, "fn{idx}"),
|
TK::Func(idx) => {
|
||||||
TK::Global(idx) => write!(f, "global{idx}"),
|
f.write_str("fn")?;
|
||||||
|
idx.fmt(f)
|
||||||
|
}
|
||||||
|
TK::Global(idx) => {
|
||||||
|
f.write_str("global")?;
|
||||||
|
idx.fmt(f)
|
||||||
|
}
|
||||||
TK::Slice(idx) => {
|
TK::Slice(idx) => {
|
||||||
let array = self.tys.ins.arrays[idx as usize];
|
let array = self.tys.ins.arrays[idx as usize];
|
||||||
match array.len {
|
f.write_str("[")?;
|
||||||
ArrayLen::MAX => write!(f, "[{}]", self.rety(array.ty)),
|
self.rety(array.ty).fmt(f)?;
|
||||||
len => write!(f, "[{}; {len}]", self.rety(array.ty)),
|
if array.len != ArrayLen::MAX {
|
||||||
|
f.write_str("; ")?;
|
||||||
|
array.len.fmt(f)?;
|
||||||
}
|
}
|
||||||
|
f.write_str("]")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -911,6 +929,12 @@ impl Types {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn reassemble(&mut self, buf: &mut Vec<u8>) {
|
||||||
|
self.ins.funcs.iter_mut().for_each(|f| f.offset = u32::MAX);
|
||||||
|
self.ins.globals.iter_mut().for_each(|g| g.offset = u32::MAX);
|
||||||
|
self.assemble(buf)
|
||||||
|
}
|
||||||
|
|
||||||
fn assemble(&mut self, to: &mut Vec<u8>) {
|
fn assemble(&mut self, to: &mut Vec<u8>) {
|
||||||
to.extend([0u8; HEADER_SIZE]);
|
to.extend([0u8; HEADER_SIZE]);
|
||||||
|
|
||||||
|
@ -1505,7 +1529,7 @@ impl hbvm::mem::Memory for LoggedMem {
|
||||||
|
|
||||||
struct AsHex<'a>(&'a [u8]);
|
struct AsHex<'a>(&'a [u8]);
|
||||||
|
|
||||||
impl Display for AsHex<'_> {
|
impl core::fmt::Display for AsHex<'_> {
|
||||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||||
for &b in self.0 {
|
for &b in self.0 {
|
||||||
write!(f, "{b:02x}")?;
|
write!(f, "{b:02x}")?;
|
||||||
|
@ -1513,3 +1537,14 @@ impl Display for AsHex<'_> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn quad_sort<T>(mut slice: &mut [T], mut cmp: impl FnMut(&T, &T) -> core::cmp::Ordering) {
|
||||||
|
while let Some(it) = slice.take_first_mut() {
|
||||||
|
for ot in &mut *slice {
|
||||||
|
if cmp(it, ot) == core::cmp::Ordering::Greater {
|
||||||
|
core::mem::swap(it, ot);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
debug_assert!(slice.is_sorted_by(|a, b| cmp(a, b) != core::cmp::Ordering::Greater));
|
||||||
|
}
|
||||||
|
|
|
@ -85,7 +85,7 @@ impl<'a, 'b> Parser<'a, 'b> {
|
||||||
let mut lexer = Lexer::new(input);
|
let mut lexer = Lexer::new(input);
|
||||||
Self {
|
Self {
|
||||||
loader,
|
loader,
|
||||||
token: lexer.next(),
|
token: lexer.eat(),
|
||||||
lexer,
|
lexer,
|
||||||
path,
|
path,
|
||||||
ctx,
|
ctx,
|
||||||
|
@ -111,7 +111,7 @@ impl<'a, 'b> Parser<'a, 'b> {
|
||||||
self.lexer.source(),
|
self.lexer.source(),
|
||||||
self.path,
|
self.path,
|
||||||
ident::pos(id.ident),
|
ident::pos(id.ident),
|
||||||
format_args!(
|
&format_args!(
|
||||||
"undeclared identifier: {}",
|
"undeclared identifier: {}",
|
||||||
self.lexer.slice(ident::range(id.ident))
|
self.lexer.slice(ident::range(id.ident))
|
||||||
),
|
),
|
||||||
|
@ -127,7 +127,7 @@ impl<'a, 'b> Parser<'a, 'b> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn next(&mut self) -> Token {
|
fn next(&mut self) -> Token {
|
||||||
core::mem::replace(&mut self.token, self.lexer.next())
|
core::mem::replace(&mut self.token, self.lexer.eat())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ptr_expr(&mut self) -> &'a Expr<'a> {
|
fn ptr_expr(&mut self) -> &'a Expr<'a> {
|
||||||
|
@ -328,16 +328,9 @@ impl<'a, 'b> Parser<'a, 'b> {
|
||||||
},
|
},
|
||||||
captured: {
|
captured: {
|
||||||
self.ns_bound = prev_boundary;
|
self.ns_bound = prev_boundary;
|
||||||
let mut captured = &mut self.ctx.captured[prev_captured..];
|
let captured = &mut self.ctx.captured[prev_captured..];
|
||||||
while let Some(it) = captured.take_first_mut() {
|
crate::quad_sort(captured, core::cmp::Ord::cmp);
|
||||||
for ot in &mut *captured {
|
let preserved = captured.partition_dedup().0.len();
|
||||||
if it > ot {
|
|
||||||
core::mem::swap(it, ot);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
debug_assert!(captured.is_sorted());
|
|
||||||
let preserved = self.ctx.captured[prev_captured..].partition_dedup().0.len();
|
|
||||||
self.ctx.captured.truncate(prev_captured + preserved);
|
self.ctx.captured.truncate(prev_captured + preserved);
|
||||||
self.arena.alloc_slice(&self.ctx.captured[prev_captured..])
|
self.arena.alloc_slice(&self.ctx.captured[prev_captured..])
|
||||||
},
|
},
|
||||||
|
@ -983,7 +976,7 @@ impl AstInner<[Symbol]> {
|
||||||
let exprs =
|
let exprs =
|
||||||
unsafe { core::mem::transmute(Parser::parse(ctx, &file, path, loader, &arena)) };
|
unsafe { core::mem::transmute(Parser::parse(ctx, &file, path, loader, &arena)) };
|
||||||
|
|
||||||
ctx.symbols.sort_unstable_by_key(|s| s.name);
|
crate::quad_sort(&mut ctx.symbols, |a, b| a.name.cmp(&b.name));
|
||||||
|
|
||||||
let layout = Self::layout(ctx.symbols.len());
|
let layout = Self::layout(ctx.symbols.len());
|
||||||
|
|
||||||
|
@ -1012,22 +1005,6 @@ impl AstInner<[Symbol]> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn report_to(file: &str, path: &str, pos: Pos, msg: impl fmt::Display, out: &mut impl fmt::Write) {
|
|
||||||
let (line, mut col) = lexer::line_col(file.as_bytes(), pos);
|
|
||||||
#[cfg(feature = "std")]
|
|
||||||
let disp = crate::fs::display_rel_path(path);
|
|
||||||
#[cfg(not(feature = "std"))]
|
|
||||||
let disp = path;
|
|
||||||
_ = writeln!(out, "{}:{}:{}: {}", disp, line, col, msg);
|
|
||||||
|
|
||||||
let line = &file[file[..pos as usize].rfind('\n').map_or(0, |i| i + 1)
|
|
||||||
..file[pos as usize..].find('\n').unwrap_or(file.len()) + pos as usize];
|
|
||||||
col += line.matches('\t').count() * 3;
|
|
||||||
|
|
||||||
_ = writeln!(out, "{}", line.replace("\t", " "));
|
|
||||||
_ = writeln!(out, "{}^", " ".repeat(col - 1));
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Report<'a, D> {
|
pub struct Report<'a, D> {
|
||||||
file: &'a str,
|
file: &'a str,
|
||||||
path: &'a str,
|
path: &'a str,
|
||||||
|
@ -1048,6 +1025,22 @@ impl<D: core::fmt::Display> core::fmt::Display for Report<'_, D> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn report_to(file: &str, path: &str, pos: Pos, msg: &dyn fmt::Display, out: &mut impl fmt::Write) {
|
||||||
|
let (line, mut col) = lexer::line_col(file.as_bytes(), pos);
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
let disp = crate::fs::display_rel_path(path);
|
||||||
|
#[cfg(not(feature = "std"))]
|
||||||
|
let disp = path;
|
||||||
|
_ = writeln!(out, "{}:{}:{}: {}", disp, line, col, msg);
|
||||||
|
|
||||||
|
let line = &file[file[..pos as usize].rfind('\n').map_or(0, |i| i + 1)
|
||||||
|
..file[pos as usize..].find('\n').map_or(file.len(), |i| i + pos as usize)];
|
||||||
|
col += line.matches('\t').count() * 3;
|
||||||
|
|
||||||
|
_ = writeln!(out, "{}", line.replace("\t", " "));
|
||||||
|
_ = writeln!(out, "{}^", " ".repeat(col - 1));
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(PartialEq, Eq, Hash)]
|
#[derive(PartialEq, Eq, Hash)]
|
||||||
pub struct Ast(NonNull<AstInner<[Symbol]>>);
|
pub struct Ast(NonNull<AstInner<[Symbol]>>);
|
||||||
|
|
||||||
|
|
|
@ -126,11 +126,33 @@ pub enum VmRunError {
|
||||||
|
|
||||||
/// Invalid operand
|
/// Invalid operand
|
||||||
InvalidOperand,
|
InvalidOperand,
|
||||||
|
|
||||||
/// Unimplemented feature
|
|
||||||
Unimplemented,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl core::fmt::Display for VmRunError {
|
||||||
|
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||||
|
match self {
|
||||||
|
VmRunError::InvalidOpcode(op) => {
|
||||||
|
f.write_str("invalid op code: ").and_then(|_| op.fmt(f))
|
||||||
|
}
|
||||||
|
VmRunError::LoadAccessEx(address) => {
|
||||||
|
f.write_str("falied to load at ").and_then(|_| address.fmt(f))
|
||||||
|
}
|
||||||
|
VmRunError::ProgramFetchLoadEx(address) => {
|
||||||
|
f.write_str("falied to load instruction at ").and_then(|_| address.fmt(f))
|
||||||
|
}
|
||||||
|
VmRunError::StoreAccessEx(address) => {
|
||||||
|
f.write_str("falied to store at ").and_then(|_| address.fmt(f))
|
||||||
|
}
|
||||||
|
VmRunError::RegOutOfBounds => f.write_str("reg out of bounds"),
|
||||||
|
VmRunError::AddrOutOfBounds => f.write_str("addr out-of-bounds"),
|
||||||
|
VmRunError::Unreachable => f.write_str("unreachable"),
|
||||||
|
VmRunError::InvalidOperand => f.write_str("invalud operand"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl core::error::Error for VmRunError {}
|
||||||
|
|
||||||
/// Virtual machine halt ok
|
/// Virtual machine halt ok
|
||||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||||
pub enum VmRunOk {
|
pub enum VmRunOk {
|
||||||
|
|
|
@ -13,28 +13,47 @@ fn root() -> &'static Path {
|
||||||
Path::new(env!("CARGO_MANIFEST_DIR")).parent().unwrap()
|
Path::new(env!("CARGO_MANIFEST_DIR")).parent().unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn exec(cmd: impl AsRef<str>) -> io::Result<()> {
|
fn build_cmd(cmd: impl AsRef<str>) -> std::process::Command {
|
||||||
let mut args = cmd.as_ref().split_whitespace();
|
let mut args = cmd.as_ref().split_whitespace();
|
||||||
let mut c = std::process::Command::new(args.next().unwrap());
|
let mut c = std::process::Command::new(args.next().unwrap());
|
||||||
for arg in args {
|
for arg in args {
|
||||||
c.arg(arg);
|
c.arg(arg);
|
||||||
}
|
}
|
||||||
if !c.status()?.success() {
|
c
|
||||||
return Err(io::Error::other(format!("command failed: {}", cmd.as_ref())));
|
}
|
||||||
|
|
||||||
|
fn exec(mut cmd: std::process::Command) -> io::Result<()> {
|
||||||
|
if !cmd.status()?.success() {
|
||||||
|
return Err(io::Error::other(format!("command failed: {:?}", cmd)));
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn build_wasm_blob(name: &str, debug: bool) -> io::Result<()> {
|
||||||
|
let mut c = build_cmd(if debug { "cargo wasm-build-debug" } else { "cargo wasm-build" });
|
||||||
|
c.arg(format!("wasm-{name}"));
|
||||||
|
exec(c)?;
|
||||||
|
|
||||||
|
let out_path = format!("target/wasm32-unknown-unknown/small/wasm_{name}.wasm");
|
||||||
|
if !debug {
|
||||||
|
exec(build_cmd(format!("wasm-opt -Oz {out_path} -o {out_path}")))?;
|
||||||
|
}
|
||||||
|
exec(build_cmd(format!("cp {out_path} depell/src/wasm-{name}.wasm")))
|
||||||
|
}
|
||||||
|
|
||||||
fn main() -> io::Result<()> {
|
fn main() -> io::Result<()> {
|
||||||
let args = std::env::args().skip(1).collect::<Vec<_>>();
|
let args = std::env::args().skip(1).collect::<Vec<_>>();
|
||||||
match args[0].as_str() {
|
match args[0].as_str() {
|
||||||
"fmt" => fmt(args[1] == "-r" || args[1] == "--renumber"),
|
"fmt" => fmt(args[1] == "-r" || args[1] == "--renumber"),
|
||||||
"build-depell" => {
|
"build-depell" => {
|
||||||
exec(
|
build_wasm_blob("hbfmt", false)?;
|
||||||
"cargo build -p wasm-hbfmt --target wasm32-unknown-unknown \
|
build_wasm_blob("hbc", false)?;
|
||||||
--profile=small -Zbuild-std=core,alloc",
|
exec(build_cmd("cargo build -p depell --release"))?;
|
||||||
)?;
|
Ok(())
|
||||||
exec("cargo build -p depell --release")?;
|
}
|
||||||
|
"build-depell-debug" => {
|
||||||
|
build_wasm_blob("hbfmt", true)?;
|
||||||
|
build_wasm_blob("hbc", true)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
_ => Ok(()),
|
_ => Ok(()),
|
||||||
|
|
Loading…
Reference in a new issue