Compare commits
2 commits
13714eb513
...
a64383e72b
Author | SHA1 | Date | |
---|---|---|---|
a64383e72b | |||
2034152c83 |
|
@ -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; }
|
||||||
|
}
|
||||||
|
|
|
@ -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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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}
|
||||||
|
|
|
@ -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..)
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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(())
|
||||||
|
|
Loading…
Reference in a new issue