forked from AbleOS/holey-bytes
fixing feature incompatibility
This commit is contained in:
parent
07638caff0
commit
c4826d3bfd
|
@ -1,2 +1,4 @@
|
|||
[alias]
|
||||
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
|
||||
rustc-ice-*
|
||||
db.sqlite
|
||||
/depell/src/*.wasm
|
||||
|
|
11
Cargo.lock
generated
11
Cargo.lock
generated
|
@ -810,6 +810,9 @@ name = "wasm-hbc"
|
|||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"hblang",
|
||||
"hbvm",
|
||||
"log",
|
||||
"wasm-rt",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -817,6 +820,14 @@ name = "wasm-hbfmt"
|
|||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"hblang",
|
||||
"wasm-rt",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-rt"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"log",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
11
Cargo.toml
11
Cargo.toml
|
@ -1,3 +1,5 @@
|
|||
cargo-features = ["profile-rustflags"]
|
||||
|
||||
[workspace]
|
||||
resolver = "2"
|
||||
members = [
|
||||
|
@ -9,11 +11,12 @@ members = [
|
|||
"depell",
|
||||
"depell/wasm-fmt",
|
||||
"depell/wasm-hbc",
|
||||
"depell/wasm-rt",
|
||||
]
|
||||
|
||||
[workspace.dependencies]
|
||||
hbbytecode = { path = "bytecode", default-features = false }
|
||||
hbvm = { path = "vm" }
|
||||
hbvm = { path = "vm", default-features = false }
|
||||
hbxrt = { path = "xrt" }
|
||||
hblang = { path = "lang", default-features = false }
|
||||
hbjit = { path = "jit" }
|
||||
|
@ -26,9 +29,13 @@ codegen-units = 1
|
|||
panic = "abort"
|
||||
|
||||
[profile.small]
|
||||
#rustflags = ["-Zfmt-debug=none", "-Zlocation-detail=none"]
|
||||
inherits = "release"
|
||||
opt-level = "z"
|
||||
strip = true
|
||||
strip = "debuginfo"
|
||||
#strip = true
|
||||
lto = true
|
||||
codegen-units = 1
|
||||
panic = "abort"
|
||||
|
||||
|
||||
|
|
|
@ -54,9 +54,6 @@ form {
|
|||
flex-direction: column;
|
||||
gap: var(--small-gap);
|
||||
|
||||
::placeholder {
|
||||
color: var(--placeholder);
|
||||
}
|
||||
|
||||
.error {
|
||||
color: var(--error);
|
||||
|
|
|
@ -3,19 +3,82 @@
|
|||
/** @return {never} */
|
||||
function never() { throw new Error() }
|
||||
|
||||
/**@type{WebAssembly.Instance}*/ let instance;
|
||||
/**@type{Promise<WebAssembly.WebAssemblyInstantiatedSource>}*/ let instaceFuture;
|
||||
/**@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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/** @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
|
||||
* @returns {Promise<string | undefined> | string | undefined} */
|
||||
function modifyCode(code, action) {
|
||||
if (!instance) {
|
||||
instaceFuture ??= WebAssembly.instantiateStreaming(fetch("/hbfmt.wasm"), {});
|
||||
if (!fmtInstance) {
|
||||
fmtInstaceFuture ??= WebAssembly.instantiateStreaming(fetch("/hbfmt.wasm"), {});
|
||||
return (async () => {
|
||||
instance = (await instaceFuture).instance;
|
||||
return modifyCodeSync(instance, code, action);
|
||||
fmtInstance = (await fmtInstaceFuture).instance;
|
||||
return modifyCodeSync(fmtInstance, code, action);
|
||||
})();
|
||||
} 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")) {
|
||||
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.addEventListener("input", function() {
|
||||
textarea.style.height = "auto";
|
||||
textarea.style.height = textarea.scrollHeight + "px";
|
||||
textarea.style.height = (textarea.scrollHeight - padding) + "px";
|
||||
});
|
||||
|
||||
textarea.onkeydown = (ev) => {
|
||||
|
@ -112,10 +179,7 @@ function bindTextareaAutoResize(target) {
|
|||
|
||||
if (ev.key === "Tab") {
|
||||
ev.preventDefault();
|
||||
const prevPos = textarea.selectionStart;
|
||||
textarea.value = textarea.value.slice(0, textarea.selectionStart) +
|
||||
' ' + textarea.value.slice(textarea.selectionEnd);
|
||||
textarea.selectionStart = textarea.selectionEnd = prevPos + 4;
|
||||
document.execCommand('insertText', false, " ");
|
||||
}
|
||||
|
||||
if (ev.key === "Backspace" && textarea.selectionStart != 0 && !selecting) {
|
||||
|
@ -125,10 +189,8 @@ function bindTextareaAutoResize(target) {
|
|||
ev.preventDefault();
|
||||
let toDelete = (textarea.selectionStart - (i + 1)) % 4;
|
||||
if (toDelete === 0) toDelete = 4;
|
||||
const prevPos = textarea.selectionStart;
|
||||
textarea.value = textarea.value.slice(0, textarea.selectionStart - toDelete) +
|
||||
textarea.value.slice(textarea.selectionEnd);
|
||||
textarea.selectionStart = textarea.selectionEnd = prevPos - toDelete;
|
||||
textarea.selectionStart -= toDelete;
|
||||
document.execCommand('delete', false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -165,11 +227,27 @@ if (window.location.hostname === 'localhost') {
|
|||
if (id !== new_id) window.location.reload();
|
||||
}, 300);
|
||||
|
||||
(async function testCodeChange() {
|
||||
const code = "main:=fn():void{return}";
|
||||
const fmtd = await modifyCode(code, "fmt") ?? never();
|
||||
const prev = await modifyCode(fmtd, "minify") ?? never();
|
||||
if (code != prev) console.error(code, prev);
|
||||
(async function test() {
|
||||
{
|
||||
const code = "main:=fn():void{return}";
|
||||
const fmtd = await modifyCode(code, "fmt") ?? never();
|
||||
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()
|
||||
.route("/", get(Index::page))
|
||||
.route("/index.css", static_asset!("text/css", "index.css"))
|
||||
.route("/index.js", static_asset!("text/javascript", "index.js"))
|
||||
.route(
|
||||
"/hbfmt.wasm",
|
||||
static_asset!(
|
||||
|
@ -47,6 +49,13 @@ async fn amain() {
|
|||
"../../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("/feed", get(Index::page))
|
||||
.route("/profile", get(Profile::page))
|
||||
|
@ -377,7 +386,7 @@ async fn base(body: impl FnOnce(&mut String), session: Option<&Session>) -> Html
|
|||
"<!DOCTYPE html>"
|
||||
<html lang="en">
|
||||
<head>
|
||||
<style>!{include_str!("index.css")}</style>
|
||||
<link rel="stylesheet" href="/index.css">
|
||||
</head>
|
||||
<body>
|
||||
<nav>
|
||||
|
@ -401,7 +410,7 @@ async fn base(body: impl FnOnce(&mut String), session: Option<&Session>) -> Html
|
|||
<main>|f|{body(f)}</main>
|
||||
</body>
|
||||
<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>
|
||||
})
|
||||
}
|
||||
|
|
|
@ -8,3 +8,4 @@ crate-type = ["cdylib"]
|
|||
|
||||
[dependencies]
|
||||
hblang = { workspace = true, features = ["no_log"] }
|
||||
wasm-rt = { version = "0.1.0", path = "../wasm-rt" }
|
||||
|
|
|
@ -1,115 +1,16 @@
|
|||
#![no_std]
|
||||
#![feature(slice_take)]
|
||||
#![feature(str_from_raw_parts)]
|
||||
#![feature(alloc_error_handler)]
|
||||
|
||||
use {
|
||||
core::{
|
||||
alloc::{GlobalAlloc, Layout},
|
||||
cell::UnsafeCell,
|
||||
},
|
||||
hblang::parser::ParserCtx,
|
||||
};
|
||||
use hblang::{fmt, parser};
|
||||
|
||||
wasm_rt::decl_runtime!(128 * 1024, 1024 * 4);
|
||||
|
||||
const ARENA_SIZE: usize = 128 * 1024;
|
||||
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;
|
||||
|
||||
#[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;
|
||||
wasm_rt::decl_buffer!(MAX_INPUT_SIZE, MAX_INPUT, INPUT, INPUT_LEN);
|
||||
|
||||
#[no_mangle]
|
||||
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 arena =
|
||||
hblang::parser::Arena::with_capacity(code.len() * hblang::parser::SOURCE_TO_AST_FACTOR);
|
||||
let mut ctx = ParserCtx::default();
|
||||
let exprs =
|
||||
hblang::parser::Parser::parse(&mut ctx, code, "source.hb", &mut |_, _| Ok(0), &arena);
|
||||
let arena = parser::Arena::with_capacity(code.len() * parser::SOURCE_TO_AST_FACTOR);
|
||||
let mut ctx = parser::ParserCtx::default();
|
||||
let exprs = parser::Parser::parse(&mut ctx, code, "source.hb", &mut |_, _| Ok(0), &arena);
|
||||
|
||||
let mut f = Write(&mut OUTPUT[..]);
|
||||
hblang::fmt::fmt_file(exprs, code, &mut f).unwrap();
|
||||
let mut f = wasm_rt::Write(&mut OUTPUT[..]);
|
||||
fmt::fmt_file(exprs, code, &mut f).unwrap();
|
||||
OUTPUT_LEN = MAX_OUTPUT_SIZE - f.0.len();
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn minify() {
|
||||
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"]
|
||||
|
||||
[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(slice_take)]
|
||||
#![no_std]
|
||||
|
||||
use {
|
||||
alloc::{borrow::ToOwned, string::String, vec::Vec},
|
||||
core::{
|
||||
alloc::{GlobalAlloc, Layout},
|
||||
cell::UnsafeCell,
|
||||
},
|
||||
alloc::{string::String, vec::Vec},
|
||||
hblang::{codegen::Codegen, parser::FileId},
|
||||
};
|
||||
|
||||
extern crate alloc;
|
||||
|
||||
const ARENA_SIZE: usize = 4 * 128 * 1024;
|
||||
const MAX_PANIC_SIZE: usize = 1024 * 4;
|
||||
wasm_rt::decl_runtime!(128 * 8 * 1024, 1024 * 4);
|
||||
|
||||
const MAX_INPUT_SIZE: usize = 32 * 4 * 1024;
|
||||
|
||||
#[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();
|
||||
}
|
||||
wasm_rt::decl_buffer!(MAX_INPUT_SIZE, MAX_INPUT, INPUT, INPUT_LEN);
|
||||
|
||||
#[no_mangle]
|
||||
static mut PANIC_MESSAGE: [u8; MAX_PANIC_SIZE] = [0; MAX_PANIC_SIZE];
|
||||
#[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() {
|
||||
unsafe fn compile_and_run(mut fuel: usize) {
|
||||
ALLOCATOR.reset();
|
||||
|
||||
log::set_logger(&wasm_rt::Logger).unwrap();
|
||||
log::set_max_level(log::LevelFilter::Error);
|
||||
|
||||
struct File<'a> {
|
||||
path: &'a str,
|
||||
code: &'a mut str,
|
||||
|
@ -118,7 +41,8 @@ unsafe fn compile_and_run() {
|
|||
});
|
||||
input_bytes = rest;
|
||||
}
|
||||
files.sort_unstable_by_key(|f| f.path);
|
||||
|
||||
hblang::quad_sort(&mut files, |a, b| a.path.cmp(b.path));
|
||||
files
|
||||
};
|
||||
|
||||
|
@ -140,12 +64,29 @@ unsafe fn compile_and_run() {
|
|||
.collect::<Vec<_>>()
|
||||
};
|
||||
|
||||
let code = {
|
||||
let mut ct = {
|
||||
let mut c = Codegen::default();
|
||||
c.files = files;
|
||||
c.generate();
|
||||
let mut buf = Vec::with_capacity(1024 * 8);
|
||||
c.assemble(&mut buf);
|
||||
buf
|
||||
c.assemble_comptime()
|
||||
};
|
||||
|
||||
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"] }
|
||||
hbbytecode = { workspace = true, features = ["disasm"] }
|
||||
hbvm = { workspace = true, features = ["nightly"] }
|
||||
log = { version = "0.4.22", features = ["release_max_level_error"] }
|
||||
log = "0.4.22"
|
||||
|
||||
[dependencies.regalloc2]
|
||||
git = "https://github.com/jakubDoka/regalloc2"
|
||||
|
|
|
@ -169,16 +169,16 @@ main := fn(): int {
|
|||
}
|
||||
|
||||
finst := Ty2.{ty: .{a: 4, b: 1}, c: 3}
|
||||
//inst := odher_pass(finst)
|
||||
//if finst.c == 3 {
|
||||
//return finst.ty.a - finst.ty.b
|
||||
return pass(&finst.ty)
|
||||
//}
|
||||
//return 0
|
||||
inst := odher_pass(finst)
|
||||
if inst.c == 3 {
|
||||
return pass(&inst.ty)
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
pass := fn(t: ^Ty): int {
|
||||
return t.a - t.b
|
||||
.{a, b} := *t
|
||||
return a - b
|
||||
}
|
||||
|
||||
odher_pass := fn(t: Ty2): Ty2 {
|
||||
|
|
|
@ -8,7 +8,7 @@ use {
|
|||
self, find_symbol, idfl, CommentOr, CtorField, Expr, ExprRef, FileId, Pos, StructField,
|
||||
},
|
||||
ty, Field, Func, Global, LoggedMem, OffsetIter, ParamAlloc, Reloc, Sig, Struct, SymKey,
|
||||
TypedReloc, Types,
|
||||
TypedReloc, Types, HEADER_SIZE,
|
||||
},
|
||||
alloc::{boxed::Box, string::String, vec::Vec},
|
||||
core::fmt::Display,
|
||||
|
@ -637,11 +637,19 @@ struct Pool {
|
|||
|
||||
const VM_STACK_SIZE: usize = 1024 * 64;
|
||||
|
||||
struct Comptime {
|
||||
vm: hbvm::Vm<LoggedMem, { 1024 * 10 }>,
|
||||
_stack: Box<[u8; VM_STACK_SIZE]>,
|
||||
pub struct Comptime {
|
||||
pub vm: hbvm::Vm<LoggedMem, { 1024 * 10 }>,
|
||||
stack: Box<[u8; VM_STACK_SIZE]>,
|
||||
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 {
|
||||
fn default() -> Self {
|
||||
|
@ -649,7 +657,7 @@ impl Default for Comptime {
|
|||
let mut vm = hbvm::Vm::default();
|
||||
let ptr = unsafe { stack.as_mut_ptr().cast::<u8>().add(VM_STACK_SIZE) as u64 };
|
||||
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:?}"),
|
||||
}
|
||||
}
|
||||
smh => self.report(
|
||||
_ => self.report(
|
||||
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(
|
||||
pos,
|
||||
format_args!(
|
||||
"this integer was inferred to be '{}' \
|
||||
which does not make sense",
|
||||
"this integer was inferred to be '{}'",
|
||||
self.ty_display(ty)
|
||||
),
|
||||
);
|
||||
|
@ -1715,16 +1725,11 @@ impl Codegen {
|
|||
unimplemented!("{:#?}", op)
|
||||
}
|
||||
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 {
|
||||
_ = self.assert_ty(
|
||||
expr.pos(),
|
||||
value.ty,
|
||||
ty,
|
||||
format_args!("'{}'", self.ast_display(expr)),
|
||||
);
|
||||
_ = self.assert_ty(expr.pos(), value.ty, ty, "somehow");
|
||||
}
|
||||
|
||||
Some(match ctx.loc {
|
||||
|
@ -2666,7 +2671,7 @@ impl Codegen {
|
|||
|
||||
fn run_vm(&mut self) {
|
||||
loop {
|
||||
match self.ct.vm.run().unwrap() {
|
||||
match self.ct.vm.run().unwrap_or_else(|e| panic!("{e:?}")) {
|
||||
hbvm::VmRunOk::End => break,
|
||||
hbvm::VmRunOk::Timer => unreachable!(),
|
||||
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]
|
||||
fn report(&self, pos: Pos, msg: impl core::fmt::Display) -> ! {
|
||||
self.report_log(pos, msg);
|
||||
log::error!("{}", self.cfile().report(pos, msg));
|
||||
unreachable!();
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
fn report_unhandled_ast(&self, ast: &Expr, hint: &str) -> ! {
|
||||
log::debug!("{ast:#?}");
|
||||
self.report(
|
||||
ast.pos(),
|
||||
format_args!(
|
||||
"compiler does not (yet) know how to handle ({hint}):\n{}",
|
||||
self.ast_display(ast)
|
||||
),
|
||||
)
|
||||
self.report(ast.pos(), format_args!("compiler does not (yet) know how to handle ({hint})",))
|
||||
}
|
||||
|
||||
fn cfile(&self) -> &parser::Ast {
|
||||
|
@ -2753,9 +2748,14 @@ impl Codegen {
|
|||
}
|
||||
|
||||
pub fn assemble(&mut self, buf: &mut Vec<u8>) {
|
||||
self.tys.ins.funcs.iter_mut().for_each(|f| f.offset = u32::MAX);
|
||||
self.tys.ins.globals.iter_mut().for_each(|g| g.offset = u32::MAX);
|
||||
self.tys.assemble(buf)
|
||||
self.tys.reassemble(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,
|
||||
};
|
||||
|
||||
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 {
|
||||
fn needs_space(c: u8) -> bool {
|
||||
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_newline = false;
|
||||
loop {
|
||||
let mut token = lexer::Lexer::new(reader).next();
|
||||
let mut token = lexer::Lexer::new(reader).eat();
|
||||
match token.kind {
|
||||
TokenKind::Eof => break,
|
||||
TokenKind::CtIdent | TokenKind::Directive => token.start -= 1,
|
||||
|
@ -321,25 +341,6 @@ impl<'a> Formatter<'a> {
|
|||
self.fmt_list(f, true, "}", "", stmts, Self::fmt)
|
||||
}
|
||||
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 {
|
||||
Radix::Decimal => "",
|
||||
Radix::Hex => "0x",
|
||||
|
@ -404,7 +405,7 @@ pub fn preserve_newlines(source: &str) -> usize {
|
|||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
|
@ -450,15 +451,15 @@ pub mod test {
|
|||
minned.truncate(len);
|
||||
|
||||
let ast = parser::Ast::new(ident, minned, &mut ParserCtx::default(), &mut |_, _| Ok(0));
|
||||
log::error!(
|
||||
"{} / {} = {} | {} / {} = {}",
|
||||
ast.mem.size(),
|
||||
input.len(),
|
||||
ast.mem.size() as f32 / input.len() as f32,
|
||||
ast.mem.size(),
|
||||
ast.file.len(),
|
||||
ast.mem.size() as f32 / ast.file.len() as f32
|
||||
);
|
||||
//log::error!(
|
||||
// "{} / {} = {} | {} / {} = {}",
|
||||
// ast.mem.size(),
|
||||
// input.len(),
|
||||
// ast.mem.size() as f32 / input.len() as f32,
|
||||
// ast.mem.size(),
|
||||
// ast.file.len(),
|
||||
// ast.mem.size() as f32 / ast.file.len() as f32
|
||||
//);
|
||||
let mut output = String::new();
|
||||
write!(output, "{ast}").unwrap();
|
||||
|
||||
|
|
|
@ -72,7 +72,6 @@ macro_rules! gen_token_kind {
|
|||
} + 1)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn from_ident(ident: &[u8]) -> Self {
|
||||
match ident {
|
||||
$($keyword_lit => Self::$keyword,)*
|
||||
|
@ -377,6 +376,25 @@ impl<'a> Lexer<'a> {
|
|||
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 {
|
||||
Self { pos, source: input.as_bytes() }
|
||||
}
|
||||
|
@ -404,9 +422,9 @@ impl<'a> Lexer<'a> {
|
|||
}
|
||||
|
||||
pub fn last(&mut self) -> Token {
|
||||
let mut token = self.next();
|
||||
let mut token = self.eat();
|
||||
loop {
|
||||
let next = self.next();
|
||||
let next = self.eat();
|
||||
if next.kind == TokenKind::Eof {
|
||||
break;
|
||||
}
|
||||
|
@ -415,7 +433,7 @@ impl<'a> Lexer<'a> {
|
|||
token
|
||||
}
|
||||
|
||||
pub fn next(&mut self) -> Token {
|
||||
pub fn eat(&mut self) -> Token {
|
||||
use TokenKind as T;
|
||||
loop {
|
||||
let mut start = self.pos;
|
||||
|
|
|
@ -20,7 +20,8 @@
|
|||
iter_intersperse,
|
||||
str_from_raw_parts,
|
||||
ptr_sub_ptr,
|
||||
slice_from_ptr_range
|
||||
slice_from_ptr_range,
|
||||
is_sorted
|
||||
)]
|
||||
#![warn(clippy::dbg_macro)]
|
||||
#![allow(stable_features, internal_features)]
|
||||
|
@ -36,7 +37,7 @@ use {
|
|||
ty::ArrayLen,
|
||||
},
|
||||
alloc::{collections::BTreeMap, string::String, vec::Vec},
|
||||
core::{cell::Cell, fmt::Display, ops::Range},
|
||||
core::{cell::Cell, ops::Range},
|
||||
hashbrown::hash_map,
|
||||
hbbytecode as instrs,
|
||||
};
|
||||
|
@ -68,7 +69,7 @@ pub mod parser;
|
|||
#[cfg(feature = "opts")]
|
||||
pub mod son;
|
||||
|
||||
mod lexer;
|
||||
pub mod lexer;
|
||||
#[cfg(feature = "opts")]
|
||||
mod vc;
|
||||
|
||||
|
@ -535,37 +536,54 @@ mod ty {
|
|||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
use Kind as TK;
|
||||
match TK::from_ty(self.ty) {
|
||||
TK::Module(idx) => write!(f, "@use({:?})[{}]", self.files[idx as usize].path, idx),
|
||||
TK::Builtin(ty) => write!(f, "{}", to_str(ty)),
|
||||
TK::Ptr(ty) => {
|
||||
write!(f, "^{}", self.rety(self.tys.ins.ptrs[ty as usize].base))
|
||||
TK::Module(idx) => {
|
||||
f.write_str("@use(\"")?;
|
||||
self.files[idx as usize].path.fmt(f)?;
|
||||
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) => {
|
||||
let record = &self.tys.ins.structs[idx as usize];
|
||||
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
|
||||
self.tys.struct_fields(idx).iter().enumerate()
|
||||
{
|
||||
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 {
|
||||
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::Global(idx) => write!(f, "global{idx}"),
|
||||
TK::Func(idx) => {
|
||||
f.write_str("fn")?;
|
||||
idx.fmt(f)
|
||||
}
|
||||
TK::Global(idx) => {
|
||||
f.write_str("global")?;
|
||||
idx.fmt(f)
|
||||
}
|
||||
TK::Slice(idx) => {
|
||||
let array = self.tys.ins.arrays[idx as usize];
|
||||
match array.len {
|
||||
ArrayLen::MAX => write!(f, "[{}]", self.rety(array.ty)),
|
||||
len => write!(f, "[{}; {len}]", self.rety(array.ty)),
|
||||
f.write_str("[")?;
|
||||
self.rety(array.ty).fmt(f)?;
|
||||
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>) {
|
||||
to.extend([0u8; HEADER_SIZE]);
|
||||
|
||||
|
@ -1505,7 +1529,7 @@ impl hbvm::mem::Memory for LoggedMem {
|
|||
|
||||
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 {
|
||||
for &b in self.0 {
|
||||
write!(f, "{b:02x}")?;
|
||||
|
@ -1513,3 +1537,14 @@ impl Display for AsHex<'_> {
|
|||
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);
|
||||
Self {
|
||||
loader,
|
||||
token: lexer.next(),
|
||||
token: lexer.eat(),
|
||||
lexer,
|
||||
path,
|
||||
ctx,
|
||||
|
@ -111,7 +111,7 @@ impl<'a, 'b> Parser<'a, 'b> {
|
|||
self.lexer.source(),
|
||||
self.path,
|
||||
ident::pos(id.ident),
|
||||
format_args!(
|
||||
&format_args!(
|
||||
"undeclared identifier: {}",
|
||||
self.lexer.slice(ident::range(id.ident))
|
||||
),
|
||||
|
@ -127,7 +127,7 @@ impl<'a, 'b> Parser<'a, 'b> {
|
|||
}
|
||||
|
||||
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> {
|
||||
|
@ -328,16 +328,9 @@ impl<'a, 'b> Parser<'a, 'b> {
|
|||
},
|
||||
captured: {
|
||||
self.ns_bound = prev_boundary;
|
||||
let mut captured = &mut self.ctx.captured[prev_captured..];
|
||||
while let Some(it) = captured.take_first_mut() {
|
||||
for ot in &mut *captured {
|
||||
if it > ot {
|
||||
core::mem::swap(it, ot);
|
||||
}
|
||||
}
|
||||
}
|
||||
debug_assert!(captured.is_sorted());
|
||||
let preserved = self.ctx.captured[prev_captured..].partition_dedup().0.len();
|
||||
let captured = &mut self.ctx.captured[prev_captured..];
|
||||
crate::quad_sort(captured, core::cmp::Ord::cmp);
|
||||
let preserved = captured.partition_dedup().0.len();
|
||||
self.ctx.captured.truncate(prev_captured + preserved);
|
||||
self.arena.alloc_slice(&self.ctx.captured[prev_captured..])
|
||||
},
|
||||
|
@ -983,7 +976,7 @@ impl AstInner<[Symbol]> {
|
|||
let exprs =
|
||||
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());
|
||||
|
||||
|
@ -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> {
|
||||
file: &'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)]
|
||||
pub struct Ast(NonNull<AstInner<[Symbol]>>);
|
||||
|
||||
|
|
|
@ -126,11 +126,33 @@ pub enum VmRunError {
|
|||
|
||||
/// Invalid operand
|
||||
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
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
pub enum VmRunOk {
|
||||
|
|
|
@ -13,28 +13,47 @@ fn root() -> &'static Path {
|
|||
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 c = std::process::Command::new(args.next().unwrap());
|
||||
for arg in args {
|
||||
c.arg(arg);
|
||||
}
|
||||
if !c.status()?.success() {
|
||||
return Err(io::Error::other(format!("command failed: {}", cmd.as_ref())));
|
||||
c
|
||||
}
|
||||
|
||||
fn exec(mut cmd: std::process::Command) -> io::Result<()> {
|
||||
if !cmd.status()?.success() {
|
||||
return Err(io::Error::other(format!("command failed: {:?}", cmd)));
|
||||
}
|
||||
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<()> {
|
||||
let args = std::env::args().skip(1).collect::<Vec<_>>();
|
||||
match args[0].as_str() {
|
||||
"fmt" => fmt(args[1] == "-r" || args[1] == "--renumber"),
|
||||
"build-depell" => {
|
||||
exec(
|
||||
"cargo build -p wasm-hbfmt --target wasm32-unknown-unknown \
|
||||
--profile=small -Zbuild-std=core,alloc",
|
||||
)?;
|
||||
exec("cargo build -p depell --release")?;
|
||||
build_wasm_blob("hbfmt", false)?;
|
||||
build_wasm_blob("hbc", false)?;
|
||||
exec(build_cmd("cargo build -p depell --release"))?;
|
||||
Ok(())
|
||||
}
|
||||
"build-depell-debug" => {
|
||||
build_wasm_blob("hbfmt", true)?;
|
||||
build_wasm_blob("hbc", true)?;
|
||||
Ok(())
|
||||
}
|
||||
_ => Ok(()),
|
||||
|
|
Loading…
Reference in a new issue