Compare commits

...

2 commits

Author SHA1 Message Date
Jakub Doka a64383e72b
saving 2024-11-16 20:52:38 +01:00
Igor null 2034152c83 added syntax highlighting to depell 2024-11-16 13:48:31 -06:00
7 changed files with 204 additions and 44 deletions

View file

@ -141,3 +141,24 @@ div#dep-list {
} }
} }
} }
.fmt {
font-family: monospace;
}
.syn {
font-family: monospace;
&.Comment { color: #939f91; }
&.Keyword { color: #f85552; }
&.Identifier { color: #3a94c5; }
&.Directive { color: #3a94c5; }
&.Number {}
&.String { color: #8da101; }
&.Op { color: #f57d26; }
&.Assign { color: #f57d26; }
&.Paren { color: #5c6a72; }
&.Bracket { color: #5c6a72; }
&.Colon { color: #5c6a72; }
&.Comma { color: #5c6a72; }
&.Dot { color: #5c6a72; }
&.Ctor { color: #3a94c5; }
}

View file

@ -50,18 +50,20 @@ function modifyCode(instance, code, action) {
let { let {
INPUT, INPUT_LEN, INPUT, INPUT_LEN,
OUTPUT, OUTPUT_LEN, OUTPUT, OUTPUT_LEN,
memory, fmt, minify memory, fmt, tok, minify
} = instance.exports; } = instance.exports;
let funs = { fmt, tok, minify };
if (!(true if (!(true
&& memory instanceof WebAssembly.Memory && memory instanceof WebAssembly.Memory
&& INPUT instanceof WebAssembly.Global && INPUT instanceof WebAssembly.Global
&& INPUT_LEN instanceof WebAssembly.Global && INPUT_LEN instanceof WebAssembly.Global
&& OUTPUT instanceof WebAssembly.Global && OUTPUT instanceof WebAssembly.Global
&& OUTPUT_LEN instanceof WebAssembly.Global && OUTPUT_LEN instanceof WebAssembly.Global
&& typeof fmt === "function" && funs.hasOwnProperty(action)
&& typeof minify === "function" && typeof funs[action] === "function"
)) never(); )) never();
let fun = funs[action];
if (action !== "fmt") { if (action !== "fmt") {
INPUT = OUTPUT; INPUT = OUTPUT;
@ -72,8 +74,14 @@ function modifyCode(instance, code, action) {
dw.setUint32(INPUT_LEN.value, code.length, true); dw.setUint32(INPUT_LEN.value, code.length, true);
new Uint8Array(memory.buffer, INPUT.value).set(new TextEncoder().encode(code)); new Uint8Array(memory.buffer, INPUT.value).set(new TextEncoder().encode(code));
return runWasmFunction(instance, action === "fmt" ? fmt : minify) ? if (!runWasmFunction(instance, fun)) {
bufToString(memory, OUTPUT, OUTPUT_LEN) : undefined; return undefined;
}
if (action === "tok") {
return bufSlice(memory, OUTPUT, OUTPUT_LEN);
} else {
return bufToString(memory, OUTPUT, OUTPUT_LEN);
}
} }
@ -119,6 +127,15 @@ function packPosts(posts, view) {
return len; return len;
} }
/** @param {WebAssembly.Memory} mem
* @param {WebAssembly.Global} ptr
* @param {WebAssembly.Global} len
* @return {Uint8Array} */
function bufSlice(mem, ptr, len) {
return new Uint8Array(mem.buffer, ptr.value,
new DataView(mem.buffer).getUint32(len.value, true));
}
/** @param {WebAssembly.Memory} mem /** @param {WebAssembly.Memory} mem
* @param {WebAssembly.Global} ptr * @param {WebAssembly.Global} ptr
* @param {WebAssembly.Global} len * @param {WebAssembly.Global} len
@ -265,19 +282,80 @@ async function bindCodeEdit(target) {
edit.dispatchEvent(new InputEvent("input")); edit.dispatchEvent(new InputEvent("input"));
} }
/** @type {{ [key: string]: (content: string) => Promise<string> | string }} */ /**
* @type {{ Array<string> }}
* to be synched with `enum TokenGroup` in bytecode/src/fmt.rs */
const TOK_CLASSES = [
'Blank',
'Comment',
'Keyword',
'Identifier',
'Directive',
'Number',
'String',
'Op',
'Assign',
'Paren',
'Bracket',
'Colon',
'Comma',
'Dot',
'Ctor',
];
/** @type {{ [key: string]: (el: HTMLElement) => undefined | Promise<undefined> }} */
const applyFns = { const applyFns = {
timestamp: (content) => new Date(parseInt(content) * 1000).toLocaleString(), timestamp: (el) => {
fmt: (content) => getFmtInstance().then(i => modifyCode(i, content, "fmt") ?? "invalid code"), const timestamp = el.innerText;
const date = new Date(parseInt(timestamp) * 1000);
el.innerText = date.toLocaleString();
},
fmt,
}; };
/**
* @param {HTMLElement} target
* @param {string} code */
async function fmt(target) {
const code = target.innerText;
const instance = await getFmtInstance();
const decoder = new TextDecoder('utf-8');
const fmt = modifyCode(instance, code, 'fmt');
const codeBytes = new TextEncoder('utf-8').encode(fmt);
const tok = modifyCode(instance, fmt, 'tok');
target.innerHTML = '';
let start = 0;
let kind = tok[0];
for (let ii = 1; ii <= tok.length; ii += 1) {
// split over same tokens and buffer end
if (tok[ii] === kind && ii < tok.length) {
continue;
}
const text = decoder.decode(codeBytes.subarray(start, ii));
const textNode = document.createTextNode(text);;
if (kind === 0) {
target.appendChild(textNode);
} else {
const el = document.createElement('span');
el.classList.add('syn');
el.classList.add(TOK_CLASSES[kind]);
el.appendChild(textNode);
target.appendChild(el);
}
if (ii == tok.length) {
break;
}
start = ii;
kind = tok[ii];
}
}
/** @param {HTMLElement} target */ /** @param {HTMLElement} target */
function execApply(target) { async function execApply(target) {
for (const elem of target.querySelectorAll('[apply]')) { for (const elem of target.querySelectorAll('[apply]')) {
if (!(elem instanceof HTMLElement)) continue; if (!(elem instanceof HTMLElement)) continue;
const funcname = elem.getAttribute('apply') ?? never(); const funcname = elem.getAttribute('apply') ?? never();
let res = applyFns[funcname](elem.textContent ?? ""); applyFns[funcname](elem);
if (res instanceof Promise) res.then(c => elem.textContent = c);
else elem.textContent = res;
} }
} }

View file

@ -27,8 +27,16 @@ unsafe extern "C" fn fmt() {
OUTPUT_LEN = MAX_OUTPUT_SIZE - f.0.len(); OUTPUT_LEN = MAX_OUTPUT_SIZE - f.0.len();
} }
#[no_mangle]
unsafe extern "C" fn tok() {
let code = core::slice::from_raw_parts_mut(
core::ptr::addr_of_mut!(OUTPUT).cast(), OUTPUT_LEN);
OUTPUT_LEN = fmt::get_token_kinds(code);
}
#[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 = fmt::minify(code); OUTPUT_LEN = fmt::minify(code);
} }

View file

@ -707,40 +707,24 @@ main := fn(): void {
#### very_nested_loops #### very_nested_loops
```hb ```hb
$W := 200
$H := 200 $H := 3
$MAX_ITER := 20 $W := 3
$ZOOM := 0.5
main := fn(): int { main := fn(): int {
mv_x := 0.5 y := @as(int, 0)
mv_y := 0.0 loop if y == H break else {
x := @as(int, 0)
y := 0 loop if x == W break else {
loop if y < H break else { c_r := @itf(x)
x := 0 c_i := @itf(y)
loop if x < W break else { if c_i * c_r >= 10.0 return 0
i := MAX_ITER - 1 x += 1
c_i := (2.0 * @floatcast(@itf(@as(i32, @intcast(x)))) - @floatcast(@itf(@as(i32, @intcast(W))))) / (W * ZOOM) + mv_x
c_r := (2.0 * @floatcast(@itf(@as(i32, @intcast(y)))) - @floatcast(@itf(@as(i32, @intcast(H))))) / (H * ZOOM) + mv_y
z_i := c_i
z_r := c_r
// iteration
loop if (z_r + z_i) * (z_r + z_i) >= 4 | i == 0 break else {
// z = z * z + c
z_i = z_i * z_i + c_i
z_r = z_r * z_r + c_r
i -= 1
}
// b g r
put_pixel(.(x, y), .(@intcast(i), @intcast(i), @intcast(i), 255))
} }
y += 1
} }
return 0 return 1
} }
Color := struct {r: u8, g: u8, b: u8, a: u8} Color := struct {r: u8, g: u8, b: u8, a: u8}

View file

@ -26,6 +26,75 @@ pub fn display_radix(radix: Radix, mut value: u64, buf: &mut [u8; 64]) -> &str {
unreachable!() unreachable!()
} }
#[repr(u8)]
enum TokenGroup {
Blank = 0,
Comment = 1,
Keyword = 2,
Identifier = 3,
Directive = 4,
Number = 5,
String = 6,
Op = 7,
Assign = 8,
Paren = 9,
Bracket = 10,
Colon = 11,
Comma = 12,
Dot = 13,
Ctor = 14,
}
fn token_group(kind: TokenKind) -> TokenGroup {
use crate::lexer::TokenKind::*;
match kind {
// unused/unimplemented
| BSlash | Pound | Eof | Ct => TokenGroup::Blank,
| Comment => TokenGroup::Comment,
| Directive => TokenGroup::Directive,
| Colon => TokenGroup::Colon,
| Semi | Comma => TokenGroup::Comma,
| Dot => TokenGroup::Dot,
| Ctor | Tupl => TokenGroup::Ctor,
| LParen | RParen => TokenGroup::Paren,
| LBrace | RBrace | LBrack | RBrack => TokenGroup::Bracket,
| Number | Float => TokenGroup::Number,
| Under | CtIdent | Ident => TokenGroup::Identifier,
| Tick | Tilde | Que
| Not | Mod | Band | Bor | Xor
| Mul | Add | Sub | Div
| Shl | Shr | Or | And
| Lt | Gt | Eq | Le | Ge | Ne => TokenGroup::Op,
| Decl | Assign
| BorAss | XorAss | BandAss
| AddAss | SubAss | MulAss | DivAss | ModAss
| ShrAss | ShlAss => TokenGroup::Assign,
| DQuote | Quote => TokenGroup::String,
| Return | If | Else | Loop | Break | Continue | Fn | Idk | Die
| Struct | Packed | True | False | Null => TokenGroup::Keyword,
}
}
pub fn get_token_kinds(mut source: &mut [u8]) -> usize {
let len = source.len();
loop {
let src = unsafe { core::str::from_utf8_unchecked(source) };
let mut token = lexer::Lexer::new(src).eat();
match token.kind {
TokenKind::Eof => break,
// ???
TokenKind::CtIdent | TokenKind::Directive => token.start -= 1,
_ => {}
}
let start = token.start as usize;
let end = token.end as usize;
source[..start].fill(0);
source[start..end].fill(token_group(token.kind) as u8);
source = &mut source[end..];
}
len
}
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..)

View file

@ -4844,7 +4844,7 @@ mod tests {
fn generate(ident: &'static str, input: &'static str, output: &mut String) { fn generate(ident: &'static str, input: &'static str, output: &mut String) {
_ = log::set_logger(&crate::fs::Logger); _ = log::set_logger(&crate::fs::Logger);
log::set_max_level(log::LevelFilter::Info); log::set_max_level(log::LevelFilter::Info);
//log::set_max_level(log::LevelFilter::Trace); log::set_max_level(log::LevelFilter::Trace);
let mut ctx = CodegenCtx::default(); let mut ctx = CodegenCtx::default();
let (ref files, embeds) = crate::test_parse_files(ident, input, &mut ctx.parser); let (ref files, embeds) = crate::test_parse_files(ident, input, &mut ctx.parser);

View file

@ -25,7 +25,7 @@ fn build_cmd(cmd: impl AsRef<str>) -> std::process::Command {
} }
fn exec(mut cmd: std::process::Command) -> io::Result<()> { fn exec(mut cmd: std::process::Command) -> io::Result<()> {
if !cmd.status()?.success() { if !cmd.status().inspect_err(|e| println!("failed to execute '{cmd:?}': {e}"))?.success() {
return Err(io::Error::other(format!("command failed: {:?}", cmd))); return Err(io::Error::other(format!("command failed: {:?}", cmd)));
} }
Ok(()) Ok(())