diff --git a/.trinity/seals/t27o_T27oZig.json b/.trinity/seals/t27o_T27oZig.json new file mode 100644 index 0000000000..92ce0b6a27 --- /dev/null +++ b/.trinity/seals/t27o_T27oZig.json @@ -0,0 +1,33 @@ +{ + "built_by": "t27c-bootstrap@0.5.2+ea35d9b72", + "closure": [ + { + "spec": "specs/tools/t27o/rust_convert.t27", + "spec_hash": "sha256:11f3aefa0a152d9ca963773018e35b1e39730ac53b25604be67252aaf85522a3" + } + ], + "config": "gen=c,rust,verilog,zig;tests=zig", + "gen_hash_c": "sha256:1902658b3fdd4234f5995a344bff8e880b6469635ea1fb0af023899d9cc6ead8", + "gen_hash_rust": "sha256:b804e5f4927cb4226bd6623c919e290f26a54d42d2c09ae9b949daeb1f00ee46", + "gen_hash_verilog": "sha256:1824c0407ae4c0cf97e7afb4b103ad6adcc20052636dbad081dd9330c1439e8e", + "gen_hash_zig": "sha256:c6e3f12811dd2eeaab6f29949789c444d9b65bdfa683d7a9d2ddb46d0359b906", + "module": "T27oZig", + "ring": 12, + "seal_schema": 2, + "sealed_at": "2026-10-19T03:31:24Z", + "sealed_by": "t27c-bootstrap@0.5.2", + "spec_hash": "sha256:a21f02d266648e370fcaf0e2d9cc50f30b4b23277caa3dc4a625369a1b4c59e0", + "spec_path": "specs/tools/t27o/zig_convert.t27", + "tests": { + "failed": 0, + "failing": [], + "forced": false, + "passed": 45, + "total": 45, + "vacuous": 0 + }, + "toolchain": { + "test_runner": "sha256:8512b80da533b79289110fca4a65f3336abd7a059ca4b72ea595d5e33ff8ad10", + "zig": "0.16.0" + } +} \ No newline at end of file diff --git a/cli/tri/src/main.rs b/cli/tri/src/main.rs index f7ba521530..a8f9cbd728 100644 --- a/cli/tri/src/main.rs +++ b/cli/tri/src/main.rs @@ -60,8 +60,8 @@ mod t27o_ledger; // t27c gen-rust of specs/tools/t27o/ledger.t27: which runner's mod lanes; // t27c gen-rust of specs/tri/lanes/lanes.t27: every rule of `tri lanes` #[path = "../../../gen/rust/tools/t27o/rust_convert.rs"] #[allow(dead_code, unused_parens, unused_mut, unused_variables, non_snake_case, unused_assignments, clippy::all)] mod t27o; // t27c gen-rust of specs/tools/t27o/rust_convert.t27: every rule of `tri t27o` (#8967) -#[path = "../../../gen/rust/tools/t27o/spice_convert.rs"] #[allow(dead_code, unused_parens, unused_mut, unused_variables, non_snake_case, unused_assignments, clippy::all)] -mod spice; // t27c gen-rust of specs/tools/t27o/spice_convert.t27: the SPICE front end of `tri t27o` (#10509) +#[path = "../../../gen/rust/tools/t27o/spice_convert.rs"] #[allow(dead_code, unused_parens, unused_mut, unused_variables, non_snake_case, unused_assignments, clippy::all)] mod spice; // t27c gen-rust of specs/tools/t27o/spice_convert.t27: the SPICE front end of `tri t27o` (#10509) +#[path = "../../../gen/rust/tools/t27o/zig_convert.rs"] #[allow(dead_code, unused_parens, unused_mut, unused_variables, non_snake_case, unused_assignments, clippy::all)] mod zig; // t27c gen-rust of specs/tools/t27o/zig_convert.t27: the Zig front end of `tri t27o` (#10765) mod renum; mod reseal; mod rtl; @@ -428,7 +428,7 @@ enum Commands { /// Print one JSON object instead of the table. #[arg(long)] json: bool, }, - /// t27o (Ouroboros): a Rust file as a t27 spec; --oracle prints the rustc harness, --check runs it and t27c test-report on the spec and its oracle tests (rules: specs/tools/t27o/rust_convert.t27). A .spice/.cir/.sp netlist prints its inventory or the refusal, and with --emit-cell the constants of the M1 linear cell folded from it (rules: specs/tools/t27o/spice_convert.t27). + /// t27o (Ouroboros): a Rust file as a t27 spec; --oracle prints the rustc harness, --check runs it and t27c test-report on the spec and its oracle tests (rules: specs/tools/t27o/rust_convert.t27). A .spice/.cir/.sp netlist prints its inventory or the refusal, and with --emit-cell the constants of the M1 linear cell folded from it (rules: specs/tools/t27o/spice_convert.t27). A .zig file prints the spec and its count line (rules: specs/tools/t27o/zig_convert.t27); its own tests are the oracle, so run `t27b test` on the spec (--oracle and --check are for Rust). T27o { file: String, /// The module name; default the file's stem. @@ -1349,8 +1349,8 @@ fn cmd_t27o(file: &str, name: Option<&str>, oracle: bool, check: bool, t27c: Opt return Ok(spice::exit_code(&st)); } let stem = name.map_or_else(|| Path::new(file).file_stem().map_or("Converted".into(), |s| s.to_string_lossy().into_owned()), str::to_string); - let (mut out, mut st, mut spec) = (vec![0u8; 1 << 24], vec![0usize; 1 << 16], vec![0u8; 1 << 24]); - let n = if oracle || check { t27o::oracle(&src, &mut out, &mut st) } else { t27o::convert(&src, stem.as_bytes(), &mut out, &mut st) }; + let (mut out, mut st, mut spec) = (vec![0u8; 1 << 24], vec![0usize; 1 << 18], vec![0u8; 1 << 24]); + let n = if zig::is_zig_name(file.as_bytes()) { zig::to_t27(&src, stem.as_bytes(), &mut out, &mut st) } else if oracle || check { t27o::oracle(&src, &mut out, &mut st) } else { t27o::convert(&src, stem.as_bytes(), &mut out, &mut st) }; if !check { std::io::Write::write_all(&mut std::io::stdout(), &out[..n.min(out.len())])?; return Ok(0); diff --git a/gen/rust/tools/t27o/zig_convert.rs b/gen/rust/tools/t27o/zig_convert.rs new file mode 100644 index 0000000000..df27576097 --- /dev/null +++ b/gen/rust/tools/t27o/zig_convert.rs @@ -0,0 +1,4111 @@ +// Generated from .t27 spec +// DO NOT EDIT — generated by t27c +// NOT LOWERED BY THIS BACKEND: 45 test(s), 0 invariant(s), 0 function(s) with no body. +// This backend emits declarations only. The spec's checks live in +// the Zig and Verilog outputs; do not read this file as verified. + +pub const Z_NFN: usize = 8; + +pub const Z_NOK: usize = 9; + +pub const Z_NTS: usize = 10; + +pub const Z_NTEST: usize = 11; + +pub const Z_CLS: usize = 12; + +pub const Z_STMT: usize = 13; + +pub const Z_TMP: usize = 14; + +pub const Z_INTEST: usize = 15; + +pub const Z_NNAMES: usize = 16; + +pub const Z_NFILE: usize = 17; + +pub const Z_ROUND: usize = 18; + +pub const Z_RET: usize = 19; + +pub const Z_GREW: usize = 20; + +pub const Z_HIT: usize = 21; + +pub const Z_NARGS: usize = 22; + +pub const Z_ARG0: usize = 23; + +pub const Z_ARG1: usize = 24; + +pub const Z_ARG2: usize = 25; + +pub const Z_CLOSE: usize = 26; + +pub const Z_LIMIT: usize = 27; + +pub const Z_HASHON: usize = 28; + +pub const Z_INCONST: usize = 29; + +pub const Z_CRAMPED: usize = 31; + +pub const Z_ONE: usize = 32; + +pub const Z_OWNER: usize = 33; + +pub const Z_CUR: usize = 34; + +pub const Z_QUAL: usize = 35; + +pub const Z_FIXED: usize = 128; + +pub const Z_NAMES: usize = 128; + +pub const Z_HSLOTS: usize = 8192; + +pub const Z_NAME_SLOTS: usize = 5; + +pub const Z_B_AT: u8 = 64; + +pub const Z_B_TILDE: u8 = 126; + +pub const Z_B_P: u8 = 112; + +pub const Z_B_I: u8 = 105; + +pub const Z_B_G: u8 = 103; + +pub fn z_tok_end(src: &[u8], p: usize) -> usize { + if (p >= src.len()) { + return p; + } + let c: u8 = src[(p) as usize]; + let d: u8 = head(src, (p + 1)); + if (c == Z_B_AT) { + let mut e: usize = (p + 1); + while ((e < src.len()) && is_word(src[(e) as usize])) { + e = (e + 1); + } + return e; + } + if ((((c == B_PLUS) || (c == B_MINUS)) || (c == B_STAR)) && (d == B_PCT)) { + if (head(src, (p + 2)) == B_EQ) { + return (p + 3); + } + return (p + 2); + } + if (((c == B_PLUS) && (d == B_PLUS)) || ((c == B_STAR) && (d == B_STAR))) { + return (p + 2); + } + if ((c == B_DOT) && ((d == B_QMARK) || (d == B_STAR))) { + return (p + 2); + } + if (c == B_BACKSLASH) { + return (p + 1); + } + return tok_end(src, p); +} + +pub fn z_is(src: &[u8], p: usize, s: &[u8]) -> bool { + return word_is(src, p, z_tok_end(src, p), s); +} + +pub fn z_step(src: &[u8], st: &mut [usize]) -> () { + st[(P) as usize] = skip_ws(src, z_tok_end(src, st[(P) as usize])); +} + +pub fn z_take(src: &[u8], st: &mut [usize], s: &[u8]) -> bool { + if z_is(src, st[(P) as usize], s) { + z_step(src, st); + return true; + } + return false; +} + +pub fn z_refuse_here(src: &[u8], st: &mut [usize], code: usize) -> () { + refuse(st, code, st[(P) as usize], st[(P) as usize], z_tok_end(src, st[(P) as usize])); +} + +pub fn z_want(src: &[u8], st: &mut [usize], s: &[u8]) -> () { + if !(z_take(src, st, s)) { + z_refuse_here(src, st, Z_R_SYNTAX); + } +} + +pub const Z_R_SYNTAX: usize = 1; + +pub const Z_R_GENERIC: usize = 2; + +pub const Z_R_POINTER: usize = 3; + +pub const Z_R_OPTIONAL: usize = 4; + +pub const Z_R_ERRORS: usize = 5; + +pub const Z_R_TYPE: usize = 6; + +pub const Z_R_BUILTIN: usize = 7; + +pub const Z_R_STDCALL: usize = 8; + +pub const Z_R_FIELD: usize = 9; + +pub const Z_R_DEPENDS: usize = 10; + +pub const Z_R_EXTERNAL: usize = 11; + +pub const Z_R_UNDECL: usize = 12; + +pub const Z_R_LITERAL: usize = 13; + +pub const Z_R_LINKAGE: usize = 14; + +pub const Z_R_UNREACH: usize = 15; + +pub const Z_R_PAYLOAD: usize = 16; + +pub const Z_R_LABEL: usize = 17; + +pub const Z_R_LOOPFORM: usize = 18; + +pub const Z_R_SWITCH: usize = 19; + +pub const Z_R_ANON: usize = 20; + +pub const Z_R_ARRAYLIT: usize = 21; + +pub const Z_R_OPERATOR: usize = 22; + +pub const Z_R_DEFER: usize = 23; + +pub const Z_R_BRACES: usize = 24; + +pub const Z_R_GLOBALVAR: usize = 25; + +pub const Z_R_TYPEDECL: usize = 26; + +pub const Z_R_TESTCALL: usize = 27; + +pub const Z_R_VALUE: usize = 28; + +pub const Z_R_INDEX: usize = 29; + +pub const Z_R_FULL: usize = 30; + +pub const Z_R_ITEM: usize = 31; + +pub const Z_R_BOPERAND: usize = 32; + +pub const Z_R_HOIST: usize = 33; + +pub fn z_put_why(st: &mut [usize], out: &mut [u8], code: usize) -> () { + if (code == Z_R_GENERIC) { + puts(st, out, b"comptime or generic"); + return (); + } + if (code == Z_R_POINTER) { + puts(st, out, b"pointer"); + return (); + } + if (code == Z_R_OPTIONAL) { + puts(st, out, b"optional"); + return (); + } + if (code == Z_R_ERRORS) { + puts(st, out, b"error handling"); + return (); + } + if (code == Z_R_TYPE) { + puts(st, out, b"type"); + return (); + } + if (code == Z_R_BUILTIN) { + puts(st, out, b"builtin"); + return (); + } + if (code == Z_R_STDCALL) { + puts(st, out, b"std call"); + return (); + } + if (code == Z_R_FIELD) { + puts(st, out, b"field or call through a value"); + return (); + } + if (code == Z_R_DEPENDS) { + puts(st, out, b"uses refused"); + return (); + } + if (code == Z_R_EXTERNAL) { + puts(st, out, b"uses external"); + return (); + } + if (code == Z_R_UNDECL) { + puts(st, out, b"undeclared"); + return (); + } + if (code == Z_R_LITERAL) { + puts(st, out, b"literal"); + return (); + } + if (code == Z_R_LINKAGE) { + puts(st, out, b"export, extern or inline fn"); + return (); + } + if (code == Z_R_UNREACH) { + puts(st, out, b"unreachable or panic"); + return (); + } + if (code == Z_R_PAYLOAD) { + puts(st, out, b"payload capture"); + return (); + } + if (code == Z_R_LABEL) { + puts(st, out, b"label"); + return (); + } + if (code == Z_R_LOOPFORM) { + puts(st, out, b"loop form"); + return (); + } + if (code == Z_R_SWITCH) { + puts(st, out, b"switch form"); + return (); + } + if (code == Z_R_ANON) { + puts(st, out, b"anonymous literal"); + return (); + } + if (code == Z_R_ARRAYLIT) { + puts(st, out, b"array literal"); + return (); + } + if (code == Z_R_OPERATOR) { + puts(st, out, b"operator"); + return (); + } + if (code == Z_R_DEFER) { + puts(st, out, b"defer"); + return (); + } + if (code == Z_R_BRACES) { + puts(st, out, b"bare braces statement"); + return (); + } + if (code == Z_R_GLOBALVAR) { + puts(st, out, b"mutable file-level var"); + return (); + } + if (code == Z_R_TYPEDECL) { + puts(st, out, b"type declaration"); + return (); + } + if (code == Z_R_TESTCALL) { + puts(st, out, b"testing call"); + return (); + } + if (code == Z_R_VALUE) { + puts(st, out, b"if, switch or loop as a value"); + return (); + } + if (code == Z_R_INDEX) { + puts(st, out, b"indexing a value that is no slice or array"); + return (); + } + if (code == Z_R_FULL) { + puts(st, out, b"name table full"); + return (); + } + if (code == Z_R_ITEM) { + puts(st, out, b"declaration"); + return (); + } + if (code == Z_R_BOPERAND) { + puts(st, out, b"builtin operand t27b cannot run"); + return (); + } + if (code == Z_R_HOIST) { + puts(st, out, b"array literal outside a statement"); + return (); + } + puts(st, out, b"syntax"); +} + +pub const ZC_NONE: usize = 0; + +pub const ZC_INT: usize = 1; + +pub const ZC_F64: usize = 2; + +pub const ZC_BOOL: usize = 3; + +pub const ZC_VOID: usize = 4; + +pub const ZC_LITINT: usize = 5; + +pub const ZC_LITFLOAT: usize = 6; + +pub const ZC_F32: usize = 7; + +pub const ZC_SLICE: usize = 16; + +pub const ZC_ARRAY: usize = 32; + +pub fn z_is_float(c: usize) -> bool { + return (((c == ZC_F64) || (c == ZC_F32)) || (c == ZC_LITFLOAT)); +} + +pub fn z_is_int(c: usize) -> bool { + return ((c == ZC_INT) || (c == ZC_LITINT)); +} + +pub fn z_is_seq(c: usize) -> bool { + return (c >= ZC_SLICE); +} + +pub fn z_elem(c: usize) -> usize { + if (c >= ZC_ARRAY) { + return (c - ZC_ARRAY); + } + if (c >= ZC_SLICE) { + return (c - ZC_SLICE); + } + return ZC_NONE; +} + +pub fn z_combine(lv: usize, a: usize, b: usize) -> usize { + if (((lv == L_OR) || (lv == L_AND)) || (lv == L_CMP)) { + return ZC_BOOL; + } + if (lv == L_SHIFT) { + return a; + } + if ((a == ZC_F64) || (b == ZC_F64)) { + return ZC_F64; + } + if ((a == ZC_F32) || (b == ZC_F32)) { + return ZC_F32; + } + if ((a == ZC_LITFLOAT) || (b == ZC_LITFLOAT)) { + return ZC_LITFLOAT; + } + if ((a == ZC_INT) || (b == ZC_INT)) { + return ZC_INT; + } + if ((a == ZC_LITINT) && (b == ZC_LITINT)) { + return ZC_LITINT; + } + return ZC_NONE; +} + +pub fn z_scalar(src: &[u8], a: usize, b: usize) -> bool { + return ((((((((((((word_is(src, a, b, b"u8") || word_is(src, a, b, b"u16")) || word_is(src, a, b, b"u32")) || word_is(src, a, b, b"u64")) || word_is(src, a, b, b"i8")) || word_is(src, a, b, b"i16")) || word_is(src, a, b, b"i32")) || word_is(src, a, b, b"i64")) || word_is(src, a, b, b"usize")) || word_is(src, a, b, b"isize")) || word_is(src, a, b, b"bool")) || word_is(src, a, b, b"f32")) || word_is(src, a, b, b"f64")); +} + +pub fn z_type_class(src: &[u8], a: usize, b: usize) -> usize { + if word_is(src, a, b, b"f64") { + return ZC_F64; + } + if word_is(src, a, b, b"f32") { + return ZC_F32; + } + if word_is(src, a, b, b"bool") { + return ZC_BOOL; + } + if word_is(src, a, b, b"void") { + return ZC_VOID; + } + if z_scalar(src, a, b) { + return ZC_INT; + } + return ZC_NONE; +} + +pub const Z_K_FN: usize = 1; + +pub const Z_K_FNX: usize = 2; + +pub const Z_K_CONST: usize = 3; + +pub const Z_K_CONSTX: usize = 4; + +pub const Z_K_NS: usize = 5; + +pub const Z_K_EXT: usize = 6; + +pub const Z_K_TYPEX: usize = 7; + +pub const Z_K_LOCAL: usize = 8; + +pub const Z_K_PARAM: usize = 9; + +pub const Z_K_VARX: usize = 10; + +pub const Z_K_HOIST: usize = 11; + +pub const Z_K_NSPACE: usize = 12; + +pub const Z_NS_STD: usize = 1; + +pub const Z_NS_MATH: usize = 2; + +pub const Z_NS_MEM: usize = 3; + +pub const Z_NS_TESTING: usize = 4; + +pub const Z_NS_ASCII: usize = 5; + +pub const Z_NS_OTHER: usize = 6; + +pub const Z_NS_DEBUG: usize = 7; + +pub fn z_slot(e: usize) -> usize { + return (Z_NAMES + ((e - 1) * Z_NAME_SLOTS)); +} + +pub fn z_add_name(st: &mut [usize], kind: usize, lo: usize, hi: usize, cls: usize) -> bool { + let k: usize = st[(Z_NNAMES) as usize]; + let s: usize = (Z_NAMES + (k * Z_NAME_SLOTS)); + if ((s + Z_NAME_SLOTS) > st[(Z_LIMIT) as usize]) { + return false; + } + st[(s) as usize] = lo; + st[((s + 1)) as usize] = hi; + st[((s + 2)) as usize] = kind; + st[((s + 3)) as usize] = cls; + st[((s + 4)) as usize] = st[(Z_OWNER) as usize]; + st[(Z_NNAMES) as usize] = (k + 1); + return true; +} + +pub fn z_find(src: &[u8], st: &mut [usize], a: usize, b: usize) -> usize { + st[(Z_HIT) as usize] = 0; + let mut k: usize = st[(Z_NNAMES) as usize]; + while (k > st[(Z_NFILE) as usize]) { + k = (k - 1); + let s: usize = (Z_NAMES + (k * Z_NAME_SLOTS)); + if ((st[((s + 2)) as usize] != Z_K_HOIST) && same_text(src, st[(s) as usize], st[((s + 1)) as usize], a, b)) { + st[(Z_HIT) as usize] = (k + 1); + return (k + 1); + } + } + let mut phase: usize = 0; + while (phase < 2) { + let want_owner: usize = (st[(Z_CUR) as usize] * (1 - phase)); + if ((phase == 1) || (st[(Z_CUR) as usize] != 0)) { + k = st[(Z_NFILE) as usize]; + while (k > 0) { + k = (k - 1); + let s: usize = (Z_NAMES + (k * Z_NAME_SLOTS)); + if ((st[((s + 4)) as usize] == want_owner) && same_text(src, st[(s) as usize], st[((s + 1)) as usize], a, b)) { + st[(Z_HIT) as usize] = (k + 1); + return (k + 1); + } + } + } + phase = (phase + 1); + } + return 0; +} + +pub fn z_find_member(src: &[u8], st: &mut [usize], owner: usize, a: usize, b: usize) -> usize { + let mut k: usize = 0; + st[(Z_HIT) as usize] = 0; + while (k < st[(Z_NFILE) as usize]) { + let s: usize = (Z_NAMES + (k * Z_NAME_SLOTS)); + if ((st[((s + 4)) as usize] == owner) && same_text(src, st[(s) as usize], st[((s + 1)) as usize], a, b)) { + st[(Z_HIT) as usize] = (k + 1); + return (k + 1); + } + k = (k + 1); + } + return 0; +} + +pub fn z_find_file(src: &[u8], st: &mut [usize], a: usize, b: usize) -> usize { + return z_find_member(src, st, st[(Z_CUR) as usize], a, b); +} + +pub fn z_put_entry(src: &[u8], st: &mut [usize], out: &mut [u8], e: usize, a: usize, b: usize) -> () { + let owner: usize = st[((z_slot(e) + 4)) as usize]; + if (owner != 0) { + put_ident(src, st, out, st[(z_slot(owner)) as usize], st[((z_slot(owner) + 1)) as usize]); + puts(st, out, b"_"); + } + put_ident(src, st, out, a, b); +} + +pub fn z_put_qualified(src: &[u8], st: &mut [usize], out: &mut [u8], a: usize, b: usize) -> () { + let owner: usize = st[(Z_CUR) as usize]; + if (owner != 0) { + put_ident(src, st, out, st[(z_slot(owner)) as usize], st[((z_slot(owner) + 1)) as usize]); + puts(st, out, b"_"); + } + put_ident(src, st, out, a, b); +} + +pub fn z_lookup(src: &[u8], st: &mut [usize], a: usize, b: usize) -> usize { + let e: usize = z_find(src, st, a, b); + if (e == 0) { + refuse(st, Z_R_UNDECL, a, a, b); + return 0; + } + let k: usize = st[((z_slot(e) + 2)) as usize]; + if ((k == Z_K_FNX) || (k == Z_K_CONSTX)) { + refuse(st, Z_R_DEPENDS, a, a, b); + } + if (k == Z_K_TYPEX) { + refuse(st, Z_R_TYPE, a, a, b); + } + if (k == Z_K_VARX) { + refuse(st, Z_R_GLOBALVAR, a, a, b); + } + if (k == Z_K_EXT) { + refuse(st, Z_R_EXTERNAL, a, a, b); + } + if bad(st[(ERR) as usize]) { + return 0; + } + return e; +} + +pub fn z_lower(c: u8) -> u8 { + if is_upper(c) { + return (c + 32); + } + return c; +} + +pub fn z_str_ok(src: &[u8], a: usize, b: usize) -> bool { + let mut i: usize = (a + 1); + while ((i + 1) < b) { + let c: u8 = src[(i) as usize]; + if ((c < B_SPACE) || (c >= B_DEL)) { + return false; + } + if (c == B_BACKSLASH) { + let d: u8 = src[((i + 1)) as usize]; + if !(((((d == B_LOWER_N) || (d == B_LOWER_T)) || (d == B_BACKSLASH)) || (d == B_QUOTE))) { + return false; + } + i = (i + 2); + } else { + i = (i + 1); + } + } + return true; +} + +pub fn z_chr_ok(src: &[u8], a: usize, b: usize) -> bool { + if ((b - a) == 3) { + let c: u8 = src[((a + 1)) as usize]; + return ((((c >= B_SPACE) && (c < B_DEL)) && (c != B_APOS)) && (c != B_BACKSLASH)); + } + if (((b - a) == 4) && (src[((a + 1)) as usize] == B_BACKSLASH)) { + let d: u8 = src[((a + 2)) as usize]; + return (((((d == B_LOWER_N) || (d == B_LOWER_T)) || (d == B_LOWER_R)) || (d == B_BACKSLASH)) || (d == B_APOS)); + } + return false; +} + +pub fn z_octal(src: &[u8], a: usize, b: usize) -> usize { + let mut v: usize = 0; + let mut i: usize = (a + 2); + while (i < b) { + let c: u8 = src[(i) as usize]; + if (c != B_UNDERSCORE) { + if (((c < B_ZERO) || (c > (B_ZERO + 7))) || (v > 1152921504606846975)) { + return 18446744073709551615; + } + v = ((v * 8) + ((c - B_ZERO) as usize)); + } + i = (i + 1); + } + return v; +} + +pub fn z_number(src: &[u8], st: &mut [usize], out: &mut [u8], a: usize, b: usize) -> () { + st[(Z_CLS) as usize] = ZC_LITINT; + if ((((b - a) >= 2) && (src[(a) as usize] == B_ZERO)) && (src[((a + 1)) as usize] == B_LOWER_O)) { + let v: usize = z_octal(src, a, b); + if (v == 18446744073709551615) { + refuse(st, Z_R_LITERAL, a, a, b); + return (); + } + put_num(st, out, v); + return (); + } + if ((((b - a) >= 2) && (src[(a) as usize] == B_ZERO)) && ((src[((a + 1)) as usize] == B_LOWER_X) || (src[((a + 1)) as usize] == B_LOWER_B))) { + let mut i: usize = (a + 2); + while (i < b) { + if ((src[(i) as usize] == B_DOT) || (src[(i) as usize] == Z_B_P)) { + refuse(st, Z_R_LITERAL, a, a, b); + return (); + } + i = (i + 1); + } + if ((head(src, b) == B_DOT) && is_word(head(src, (b + 1)))) { + refuse(st, Z_R_LITERAL, a, a, b); + return (); + } + put_src(src, st, out, a, b); + return (); + } + let mut i: usize = a; + while (i < b) { + if (((src[(i) as usize] == B_DOT) || (src[(i) as usize] == B_LOWER_E)) || (src[(i) as usize] == B_UPPER_E)) { + st[(Z_CLS) as usize] = ZC_LITFLOAT; + } + i = (i + 1); + } + put_src(src, st, out, a, b); +} + +pub fn z_bad_type_code(src: &[u8], a: usize, b: usize) -> usize { + let c: u8 = head(src, a); + if (c == B_STAR) { + return Z_R_POINTER; + } + if (c == B_QMARK) { + return Z_R_OPTIONAL; + } + if (c == B_BANG) { + return Z_R_ERRORS; + } + if (word_is(src, a, b, b"error") || word_is(src, a, b, b"anyerror")) { + return Z_R_ERRORS; + } + if (((((word_is(src, a, b, b"comptime") || word_is(src, a, b, b"anytype")) || word_is(src, a, b, b"type")) || word_is(src, a, b, b"noalias")) || word_is(src, a, b, b"comptime_int")) || word_is(src, a, b, b"comptime_float")) { + return Z_R_GENERIC; + } + return Z_R_TYPE; +} + +pub fn z_scalar_type(src: &[u8], st: &mut [usize], out: &mut [u8]) -> () { + st[(Z_CLS) as usize] = ZC_NONE; + let a: usize = st[(P) as usize]; + let b: usize = z_tok_end(src, a); + if z_scalar(src, a, b) { + put_src(src, st, out, a, b); + st[(Z_CLS) as usize] = z_type_class(src, a, b); + z_step(src, st); + return (); + } + refuse(st, z_bad_type_code(src, a, b), a, a, b); +} + +pub fn z_type(src: &[u8], st: &mut [usize], out: &mut [u8]) -> () { + st[(Z_CLS) as usize] = ZC_NONE; + if bad(st[(ERR) as usize]) { + return (); + } + let a: usize = st[(P) as usize]; + let b: usize = z_tok_end(src, a); + if word_is(src, a, b, b"void") { + put_src(src, st, out, a, b); + st[(Z_CLS) as usize] = ZC_VOID; + z_step(src, st); + return (); + } + if (head(src, a) != B_LBRACKET) { + z_scalar_type(src, st, out); + return (); + } + z_step(src, st); + if z_take(src, st, b"]") { + puts(st, out, b"[]"); + if z_take(src, st, b"const") { + puts(st, out, b"const "); + } + z_scalar_type(src, st, out); + if (st[(Z_CLS) as usize] != ZC_NONE) { + st[(Z_CLS) as usize] = (ZC_SLICE + st[(Z_CLS) as usize]); + } + return (); + } + let n0: usize = st[(P) as usize]; + let n1: usize = z_tok_end(src, n0); + if ((head(src, n0) == B_STAR) || (head(src, n0) == B_COLON)) { + refuse(st, Z_R_POINTER, n0, n0, n1); + return (); + } + if is_alpha(head(src, n0)) { + let e: usize = z_find(src, st, n0, n1); + if ((e == 0) || (st[((z_slot(e) + 2)) as usize] != Z_K_CONST)) { + refuse(st, Z_R_SYNTAX, n0, n0, n1); + return (); + } + } else { + if !(is_digit(head(src, n0))) { + refuse(st, Z_R_SYNTAX, n0, n0, n1); + return (); + } + } + puts(st, out, b"["); + put_src(src, st, out, n0, n1); + puts(st, out, b"]"); + z_step(src, st); + z_want(src, st, b"]"); + z_scalar_type(src, st, out); + if (st[(Z_CLS) as usize] != ZC_NONE) { + st[(Z_CLS) as usize] = (ZC_ARRAY + st[(Z_CLS) as usize]); + } +} + +pub const Z_OP_BAD: usize = 100; + +pub fn z_op_level(src: &[u8], a: usize, b: usize) -> usize { + if (b == (a + 1)) { + let c: u8 = src[(a) as usize]; + if ((c == B_LT) || (c == B_GT)) { + return L_CMP; + } + if (c == B_PIPE) { + return L_BOR; + } + if (c == B_CARET) { + return L_XOR; + } + if (c == B_AMP) { + return L_BAND; + } + if ((c == B_PLUS) || (c == B_MINUS)) { + return L_ADD; + } + if (((c == B_STAR) || (c == B_SLASH)) || (c == B_PCT)) { + return L_MUL; + } + return 0; + } + if word_is(src, a, b, b"or") { + return L_OR; + } + if word_is(src, a, b, b"and") { + return L_AND; + } + if (((word_is(src, a, b, b"==") || word_is(src, a, b, b"!=")) || word_is(src, a, b, b"<=")) || word_is(src, a, b, b">=")) { + return L_CMP; + } + if (word_is(src, a, b, b"<<") || word_is(src, a, b, b">>")) { + return L_SHIFT; + } + if (word_is(src, a, b, b"+%") || word_is(src, a, b, b"-%")) { + return L_ADD; + } + if word_is(src, a, b, b"*%") { + return L_MUL; + } + if ((((word_is(src, a, b, b"++") || word_is(src, a, b, b"**")) || word_is(src, a, b, b"||")) || word_is(src, a, b, b"orelse")) || word_is(src, a, b, b"catch")) { + return Z_OP_BAD; + } + return 0; +} + +pub fn z_bad_op_code(src: &[u8], a: usize, b: usize) -> usize { + if word_is(src, a, b, b"orelse") { + return Z_R_OPTIONAL; + } + if word_is(src, a, b, b"catch") { + return Z_R_ERRORS; + } + return Z_R_OPERATOR; +} + +pub const Z_L_COND: usize = 1; + +pub fn z_expr_bp(src: &[u8], st: &mut [usize], out: &mut [u8], min: usize) -> usize { + let start: usize = st[(W) as usize]; + let mut lvl: usize = z_unary(src, st, out); + let mut cls: usize = st[(Z_CLS) as usize]; + while !(bad(st[(ERR) as usize])) { + let a: usize = st[(P) as usize]; + let b: usize = z_tok_end(src, a); + let lv: usize = z_op_level(src, a, b); + if ((lv == 0) || (lv < min)) { + st[(Z_CLS) as usize] = cls; + return lvl; + } + if (lv == Z_OP_BAD) { + refuse(st, z_bad_op_code(src, a, b), a, a, b); + return lvl; + } + z_step(src, st); + if !(keep_bare(lvl, lv, true)) { + wrap(st, out, start); + } + puts(st, out, b" "); + put_src(src, st, out, a, b); + puts(st, out, b" "); + let rs: usize = st[(W) as usize]; + let ra: usize = st[(P) as usize]; + let rl: usize = z_expr_bp(src, st, out, (lv + 1)); + if !(keep_bare(rl, lv, false)) { + wrap(st, out, rs); + } + if (((lv == L_SHIFT) && z_is(src, ra, b"@intCast")) && !(bad(st[(ERR) as usize]))) { + refuse(st, Z_R_BOPERAND, ra, ra, z_tok_end(src, ra)); + } + cls = z_combine(lv, cls, st[(Z_CLS) as usize]); + lvl = lv; + } + st[(Z_CLS) as usize] = cls; + return lvl; +} + +pub fn z_expr(src: &[u8], st: &mut [usize], out: &mut [u8]) -> usize { + return z_expr_bp(src, st, out, L_OR); +} + +pub fn z_hoisted(st: &mut [usize], pos: usize) -> usize { + let mut k: usize = st[(Z_NNAMES) as usize]; + st[(Z_HIT) as usize] = 0; + while (k > 0) { + k = (k - 1); + let s: usize = (Z_NAMES + (k * Z_NAME_SLOTS)); + if ((st[((s + 2)) as usize] == Z_K_HOIST) && (st[(s) as usize] == pos)) { + st[(Z_HIT) as usize] = (k + 1); + return (k + 1); + } + } + return 0; +} + +pub fn z_unary(src: &[u8], st: &mut [usize], out: &mut [u8]) -> usize { + if bad(st[(ERR) as usize]) { + return L_ATOM; + } + let a: usize = st[(P) as usize]; + let b: usize = z_tok_end(src, a); + if z_is(src, a, b"-") { + z_step(src, st); + puts(st, out, b"-"); + z_unary(src, st, out); + return L_UNARY; + } + if z_is(src, a, b"!") { + z_step(src, st); + puts(st, out, b"!"); + z_unary(src, st, out); + st[(Z_CLS) as usize] = ZC_BOOL; + return L_UNARY; + } + if z_is(src, a, b"~") { + z_step(src, st); + puts(st, out, b"~"); + z_unary(src, st, out); + return L_UNARY; + } + if z_is(src, a, b"&") { + z_step(src, st); + let n0: usize = st[(P) as usize]; + let n1: usize = z_tok_end(src, n0); + if (head(src, n0) == B_LBRACKET) { + let h: usize = z_hoisted(st, a); + if (h == 0) { + refuse(st, Z_R_HOIST, a, a, n1); + return L_ATOM; + } + let brace: usize = find_at(src, n0, B_LBRACE); + puts(st, out, b"&t27o_a"); + put_num(st, out, st[((z_slot(h) + 1)) as usize]); + st[(P) as usize] = skip_ws(src, block_end(src, brace)); + st[(Z_CLS) as usize] = (ZC_SLICE + z_elem(st[((z_slot(h) + 3)) as usize])); + return L_UNARY; + } + if is_alpha(head(src, n0)) { + let e: usize = z_find(src, st, n0, n1); + if ((((e != 0) && (st[((z_slot(e) + 3)) as usize] >= ZC_ARRAY)) && (st[((z_slot(e) + 2)) as usize] != Z_K_FNX)) && (st[((z_slot(e) + 2)) as usize] != Z_K_CONSTX)) { + puts(st, out, b"&"); + z_put_entry(src, st, out, e, n0, n1); + st[(Z_CLS) as usize] = (ZC_SLICE + z_elem(st[((z_slot(e) + 3)) as usize])); + z_step(src, st); + return L_UNARY; + } + } + refuse(st, Z_R_POINTER, a, a, b); + return L_ATOM; + } + if z_is(src, a, b"-%") { + refuse(st, Z_R_OPERATOR, a, a, b); + return L_ATOM; + } + if z_is(src, a, b"try") { + refuse(st, Z_R_ERRORS, a, a, b); + return L_ATOM; + } + return z_postfix(src, st, out); +} + +pub fn z_postfix(src: &[u8], st: &mut [usize], out: &mut [u8]) -> usize { + let mut lvl: usize = z_primary(src, st, out); + while !(bad(st[(ERR) as usize])) { + let a: usize = st[(P) as usize]; + let b: usize = z_tok_end(src, a); + let cls: usize = st[(Z_CLS) as usize]; + if z_is(src, a, b"[") { + if !(z_is_seq(cls)) { + refuse(st, Z_R_INDEX, a, a, b); + return lvl; + } + z_step(src, st); + puts(st, out, b"["); + z_expr(src, st, out); + let mut res: usize = z_elem(cls); + if z_take(src, st, b"..") { + puts(st, out, b".."); + if !(z_is(src, st[(P) as usize], b"]")) { + z_expr(src, st, out); + } + res = (ZC_SLICE + z_elem(cls)); + } + z_want(src, st, b"]"); + puts(st, out, b"]"); + st[(Z_CLS) as usize] = res; + lvl = L_ATOM; + } else { + if z_is(src, a, b".") { + z_step(src, st); + let f0: usize = st[(P) as usize]; + let f1: usize = z_tok_end(src, f0); + if (z_is_seq(cls) && word_is(src, f0, f1, b"len")) { + puts(st, out, b".len"); + z_step(src, st); + st[(Z_CLS) as usize] = ZC_INT; + lvl = L_ATOM; + } else { + refuse(st, Z_R_FIELD, f0, f0, f1); + return lvl; + } + } else { + if z_is(src, a, b".?") { + refuse(st, Z_R_OPTIONAL, a, a, b); + return lvl; + } else { + if z_is(src, a, b".*") { + refuse(st, Z_R_POINTER, a, a, b); + return lvl; + } else { + return lvl; + } + } + } + } + } + return lvl; +} + +pub fn z_args_rest(src: &[u8], st: &mut [usize], out: &mut [u8]) -> () { + let mut n: usize = 0; + let mut c0: usize = ZC_NONE; + let mut c1: usize = ZC_NONE; + let mut c2: usize = ZC_NONE; + while ((!(bad(st[(ERR) as usize])) && (st[(P) as usize] < src.len())) && !(z_is(src, st[(P) as usize], b")"))) { + if (n > 0) { + puts(st, out, b", "); + } + z_expr(src, st, out); + let c: usize = st[(Z_CLS) as usize]; + if (n == 0) { + c0 = c; + } + if (n == 1) { + c1 = c; + } + if (n == 2) { + c2 = c; + } + n = (n + 1); + if !(z_take(src, st, b",")) { + break; + } + } + st[(Z_CLOSE) as usize] = z_tok_end(src, st[(P) as usize]); + z_want(src, st, b")"); + puts(st, out, b")"); + st[(Z_NARGS) as usize] = n; + st[(Z_ARG0) as usize] = c0; + st[(Z_ARG1) as usize] = c1; + st[(Z_ARG2) as usize] = c2; +} + +pub fn z_args(src: &[u8], st: &mut [usize], out: &mut [u8]) -> () { + z_want(src, st, b"("); + puts(st, out, b"("); + z_args_rest(src, st, out); +} + +pub fn z_ns_of(src: &[u8], a: usize, b: usize) -> usize { + if word_is(src, a, b, b"math") { + return Z_NS_MATH; + } + if word_is(src, a, b, b"mem") { + return Z_NS_MEM; + } + if word_is(src, a, b, b"testing") { + return Z_NS_TESTING; + } + if word_is(src, a, b, b"ascii") { + return Z_NS_ASCII; + } + if word_is(src, a, b, b"debug") { + return Z_NS_DEBUG; + } + return Z_NS_OTHER; +} + +pub fn z_int_limit(src: &[u8], a: usize, b: usize, max: bool) -> &'static str { + if word_is(src, a, b, b"u8") { + if max { + return "255"; + } + return "0"; + } + if word_is(src, a, b, b"u16") { + if max { + return "65535"; + } + return "0"; + } + if word_is(src, a, b, b"u32") { + if max { + return "4294967295"; + } + return "0"; + } + if (word_is(src, a, b, b"u64") || word_is(src, a, b, b"usize")) { + if max { + return "18446744073709551615"; + } + return "0"; + } + if word_is(src, a, b, b"i8") { + if max { + return "127"; + } + return "-128"; + } + if word_is(src, a, b, b"i16") { + if max { + return "32767"; + } + return "-32768"; + } + if word_is(src, a, b, b"i32") { + if max { + return "2147483647"; + } + return "-2147483648"; + } + if (word_is(src, a, b, b"i64") || word_is(src, a, b, b"isize")) { + if max { + return "9223372036854775807"; + } + return "-9223372036854775808"; + } + return ""; +} + +pub fn z_math_const(src: &[u8], a: usize, b: usize) -> &'static str { + if word_is(src, a, b, b"tau") { + return "6.283185307179586"; + } + if word_is(src, a, b, b"phi") { + return "1.618033988749895"; + } + if word_is(src, a, b, b"sqrt2") { + return "1.4142135623730951"; + } + if word_is(src, a, b, b"sqrt1_2") { + return "0.7071067811865476"; + } + if word_is(src, a, b, b"ln2") { + return "0.6931471805599453"; + } + if word_is(src, a, b, b"ln10") { + return "2.302585092994046"; + } + if word_is(src, a, b, b"log2e") { + return "1.4426950408889634"; + } + if word_is(src, a, b, b"log10e") { + return "0.4342944819032518"; + } + if word_is(src, a, b, b"pi_2") { + return "1.5707963267948966"; + } + if word_is(src, a, b, b"pi_4") { + return "0.7853981633974483"; + } + return ""; +} + +pub fn z_std_member(src: &[u8], st: &mut [usize], out: &mut [u8], a0: usize, ns: usize, ma: usize, mb: usize) -> usize { + st[(Z_CLS) as usize] = ZC_NONE; + if (ns == Z_NS_MATH) { + if (word_is(src, ma, mb, b"pi") || word_is(src, ma, mb, b"e")) { + puts(st, out, b"std.math."); + put_src(src, st, out, ma, mb); + st[(Z_CLS) as usize] = ZC_LITFLOAT; + return L_ATOM; + } + let k: &'static str = z_math_const(src, ma, mb); + if (k.len() > 0) { + puts(st, out, k.as_bytes()); + st[(Z_CLS) as usize] = ZC_LITFLOAT; + return L_ATOM; + } + if ((((word_is(src, ma, mb, b"sqrt") || word_is(src, ma, mb, b"floor")) || word_is(src, ma, mb, b"ceil")) || word_is(src, ma, mb, b"exp")) || word_is(src, ma, mb, b"cos")) { + puts(st, out, b"@"); + put_src(src, st, out, ma, mb); + z_args(src, st, out); + if (st[(Z_NARGS) as usize] != 1) { + refuse(st, Z_R_SYNTAX, a0, a0, mb); + } + z_builtin_check(st, z_builtin_id(src, ma, mb), a0, st[(Z_CLOSE) as usize]); + return L_ATOM; + } + if ((word_is(src, ma, mb, b"isNan") || word_is(src, ma, mb, b"isPositiveInf")) || word_is(src, ma, mb, b"isNegativeInf")) { + puts(st, out, b"std.math."); + put_src(src, st, out, ma, mb); + z_args(src, st, out); + if (st[(Z_NARGS) as usize] != 1) { + refuse(st, Z_R_SYNTAX, a0, a0, mb); + } + st[(Z_CLS) as usize] = ZC_BOOL; + return L_ATOM; + } + if (word_is(src, ma, mb, b"inf") || word_is(src, ma, mb, b"nan")) { + z_want(src, st, b"("); + let fa: usize = st[(P) as usize]; + let fb: usize = z_tok_end(src, fa); + if !((word_is(src, fa, fb, b"f64") || word_is(src, fa, fb, b"f32"))) { + refuse(st, Z_R_STDCALL, a0, a0, fb); + return L_ATOM; + } + puts(st, out, b"std.math."); + put_src(src, st, out, ma, mb); + puts(st, out, b"("); + put_src(src, st, out, fa, fb); + puts(st, out, b")"); + st[(Z_CLS) as usize] = z_type_class(src, fa, fb); + z_step(src, st); + z_want(src, st, b")"); + return L_ATOM; + } + if (word_is(src, ma, mb, b"maxInt") || word_is(src, ma, mb, b"minInt")) { + z_want(src, st, b"("); + let ta: usize = st[(P) as usize]; + let tb: usize = z_tok_end(src, ta); + let lit: &'static str = z_int_limit(src, ta, tb, word_is(src, ma, mb, b"maxInt")); + if (lit.len() == 0) { + refuse(st, Z_R_STDCALL, a0, a0, tb); + return L_ATOM; + } + z_step(src, st); + z_want(src, st, b")"); + puts(st, out, lit.as_bytes()); + st[(Z_CLS) as usize] = ZC_LITINT; + if (lit.as_bytes()[0] == B_MINUS) { + return L_UNARY; + } + return L_ATOM; + } + } + if ((ns == Z_NS_DEBUG) && word_is(src, ma, mb, b"assert")) { + puts(st, out, b"assert"); + z_args(src, st, out); + if (st[(Z_NARGS) as usize] != 1) { + refuse(st, Z_R_SYNTAX, a0, a0, mb); + } + st[(Z_CLS) as usize] = ZC_VOID; + return L_ATOM; + } + if ((ns == Z_NS_MEM) && word_is(src, ma, mb, b"eql")) { + z_want(src, st, b"("); + if !(z_take(src, st, b"u8")) { + refuse(st, Z_R_STDCALL, a0, a0, mb); + return L_ATOM; + } + z_want(src, st, b","); + puts(st, out, b"std.mem.eql(u8, "); + z_args_rest(src, st, out); + if (st[(Z_NARGS) as usize] != 2) { + refuse(st, Z_R_SYNTAX, a0, a0, mb); + } + st[(Z_CLS) as usize] = ZC_BOOL; + return L_ATOM; + } + refuse(st, Z_R_STDCALL, a0, a0, mb); + return L_ATOM; +} + +pub fn z_std_chain(src: &[u8], st: &mut [usize], out: &mut [u8], a0: usize, ns0: usize) -> usize { + z_step(src, st); + let mut ns: usize = ns0; + if (ns == Z_NS_STD) { + if !(z_take(src, st, b".")) { + refuse(st, Z_R_STDCALL, a0, a0, st[(P) as usize]); + return L_ATOM; + } + let sa: usize = st[(P) as usize]; + let sb: usize = z_tok_end(src, sa); + ns = z_ns_of(src, sa, sb); + z_step(src, st); + } + if !(z_take(src, st, b".")) { + refuse(st, Z_R_STDCALL, a0, a0, st[(P) as usize]); + return L_ATOM; + } + let ma: usize = st[(P) as usize]; + let mb: usize = z_tok_end(src, ma); + z_step(src, st); + return z_std_member(src, st, out, a0, ns, ma, mb); +} + +pub fn z_merge(a: usize, b: usize) -> usize { + if (a == b) { + return a; + } + return z_combine(L_ADD, a, b); +} + +pub fn z_if_expr(src: &[u8], st: &mut [usize], out: &mut [u8]) -> usize { + z_step(src, st); + puts(st, out, b"if ("); + z_want(src, st, b"("); + z_expr(src, st, out); + z_want(src, st, b")"); + puts(st, out, b") "); + if z_is(src, st[(P) as usize], b"|") { + z_refuse_here(src, st, Z_R_PAYLOAD); + return Z_L_COND; + } + z_expr(src, st, out); + let c1: usize = st[(Z_CLS) as usize]; + if !(z_take(src, st, b"else")) { + z_refuse_here(src, st, Z_R_SYNTAX); + return Z_L_COND; + } + puts(st, out, b" else "); + z_expr(src, st, out); + st[(Z_CLS) as usize] = z_merge(c1, st[(Z_CLS) as usize]); + return Z_L_COND; +} + +pub fn z_switch_expr(src: &[u8], st: &mut [usize], out: &mut [u8]) -> usize { + z_step(src, st); + puts(st, out, b"switch ("); + z_want(src, st, b"("); + z_expr(src, st, out); + z_want(src, st, b")"); + puts(st, out, b") {"); + z_want(src, st, b"{"); + st[(IND) as usize] = (st[(IND) as usize] + 1); + let mut cls: usize = ZC_NONE; + let mut n: usize = 0; + while ((!(bad(st[(ERR) as usize])) && (st[(P) as usize] < src.len())) && !(z_is(src, st[(P) as usize], b"}"))) { + newline(st, out); + let pa: usize = st[(P) as usize]; + if z_is(src, pa, b"else") { + puts(st, out, b"else"); + z_step(src, st); + } else { + if (is_digit(head(src, pa)) || is_alpha(head(src, pa))) { + z_primary(src, st, out); + if (z_is(src, st[(P) as usize], b",") || z_is(src, st[(P) as usize], b"...")) { + z_refuse_here(src, st, Z_R_SWITCH); + return Z_L_COND; + } + } else { + z_refuse_here(src, st, Z_R_SWITCH); + return Z_L_COND; + } + } + z_want(src, st, b"=>"); + puts(st, out, b" => "); + if z_is(src, st[(P) as usize], b"|") { + z_refuse_here(src, st, Z_R_PAYLOAD); + return Z_L_COND; + } + z_expr(src, st, out); + if (n == 0) { + cls = st[(Z_CLS) as usize]; + } else { + cls = z_merge(cls, st[(Z_CLS) as usize]); + } + n = (n + 1); + puts(st, out, b","); + if !(z_take(src, st, b",")) { + break; + } + } + z_want(src, st, b"}"); + st[(IND) as usize] = (st[(IND) as usize] - 1); + newline(st, out); + puts(st, out, b"}"); + st[(Z_CLS) as usize] = cls; + return Z_L_COND; +} + +pub const ZB_NONE: usize = 0; + +pub const ZB_INTCAST: usize = 1; + +pub const ZB_FLOATCAST: usize = 2; + +pub const ZB_FLOATFROMINT: usize = 3; + +pub const ZB_INTFROMFLOAT: usize = 4; + +pub const ZB_INTFROMBOOL: usize = 5; + +pub const ZB_SQRT: usize = 6; + +pub const ZB_FLOOR: usize = 7; + +pub const ZB_CEIL: usize = 8; + +pub const ZB_ROUND: usize = 9; + +pub const ZB_EXP: usize = 10; + +pub const ZB_LOG: usize = 11; + +pub const ZB_COS: usize = 12; + +pub const ZB_ABS: usize = 13; + +pub const ZB_MIN: usize = 14; + +pub const ZB_MAX: usize = 15; + +pub const ZB_DIVTRUNC: usize = 16; + +pub const ZB_REM: usize = 17; + +pub const ZB_MOD: usize = 18; + +pub fn z_builtin_id(src: &[u8], a: usize, b: usize) -> usize { + if word_is(src, a, b, b"intCast") { + return ZB_INTCAST; + } + if word_is(src, a, b, b"floatCast") { + return ZB_FLOATCAST; + } + if word_is(src, a, b, b"floatFromInt") { + return ZB_FLOATFROMINT; + } + if word_is(src, a, b, b"intFromFloat") { + return ZB_INTFROMFLOAT; + } + if word_is(src, a, b, b"intFromBool") { + return ZB_INTFROMBOOL; + } + if word_is(src, a, b, b"sqrt") { + return ZB_SQRT; + } + if word_is(src, a, b, b"floor") { + return ZB_FLOOR; + } + if word_is(src, a, b, b"ceil") { + return ZB_CEIL; + } + if word_is(src, a, b, b"round") { + return ZB_ROUND; + } + if word_is(src, a, b, b"exp") { + return ZB_EXP; + } + if word_is(src, a, b, b"log") { + return ZB_LOG; + } + if word_is(src, a, b, b"cos") { + return ZB_COS; + } + if word_is(src, a, b, b"abs") { + return ZB_ABS; + } + if word_is(src, a, b, b"min") { + return ZB_MIN; + } + if word_is(src, a, b, b"max") { + return ZB_MAX; + } + if word_is(src, a, b, b"divTrunc") { + return ZB_DIVTRUNC; + } + if word_is(src, a, b, b"rem") { + return ZB_REM; + } + if word_is(src, a, b, b"mod") { + return ZB_MOD; + } + return ZB_NONE; +} + +pub fn z_builtin_arity(id: usize) -> usize { + if (id == ZB_NONE) { + return 0; + } + if (id >= ZB_MIN) { + return 2; + } + return 1; +} + +pub fn z_is_lit(c: usize) -> bool { + return ((c == ZC_LITINT) || (c == ZC_LITFLOAT)); +} + +pub fn z_builtin_check(st: &mut [usize], id: usize, a: usize, end: usize) -> () { + let c0: usize = st[(Z_ARG0) as usize]; + let c1: usize = st[(Z_ARG1) as usize]; + if ((st[(Z_INCONST) as usize] == 1) && !((z_is_lit(c0) && ((id < ZB_MIN) || z_is_lit(c1))))) { + refuse(st, Z_R_BOPERAND, a, a, end); + } + if (((id == ZB_INTCAST) || (id == ZB_INTFROMFLOAT)) || (id == ZB_INTFROMBOOL)) { + st[(Z_CLS) as usize] = ZC_INT; + } else { + if ((id == ZB_FLOATCAST) || (id == ZB_FLOATFROMINT)) { + st[(Z_CLS) as usize] = ZC_F64; + } else { + if (id == ZB_ABS) { + if !(z_is_float(c0)) { + refuse(st, Z_R_BOPERAND, a, a, end); + } + st[(Z_CLS) as usize] = c0; + } else { + if ((id == ZB_MIN) || (id == ZB_MAX)) { + if ((!(z_is_float(c0)) && !(z_is_float(c1))) && ((c0 == ZC_LITINT) || (c1 == ZC_LITINT))) { + refuse(st, Z_R_BOPERAND, a, a, end); + } + st[(Z_CLS) as usize] = z_merge(c0, c1); + } else { + if (((id == ZB_DIVTRUNC) || (id == ZB_REM)) || (id == ZB_MOD)) { + if (z_is_float(c0) || z_is_float(c1)) { + refuse(st, Z_R_BOPERAND, a, a, end); + } + if ((id != ZB_DIVTRUNC) && (c0 == ZC_LITINT)) { + refuse(st, Z_R_BOPERAND, a, a, end); + } + st[(Z_CLS) as usize] = ZC_INT; + } else { + if ((((((id == ZB_EXP) || (id == ZB_FLOOR)) || (id == ZB_CEIL)) || (id == ZB_ROUND)) || (id == ZB_COS)) && z_is_lit(c0)) { + refuse(st, Z_R_BOPERAND, a, a, end); + } + st[(Z_CLS) as usize] = c0; + } + } + } + } + } +} + +pub fn z_builtin(src: &[u8], st: &mut [usize], out: &mut [u8]) -> usize { + let a: usize = st[(P) as usize]; + let b: usize = z_tok_end(src, a); + z_step(src, st); + if word_is(src, a, b, b"@as") { + z_want(src, st, b"("); + puts(st, out, b"@as("); + z_scalar_type(src, st, out); + let tc: usize = st[(Z_CLS) as usize]; + z_want(src, st, b","); + puts(st, out, b", "); + z_expr(src, st, out); + _ = z_take(src, st, b","); + z_want(src, st, b")"); + puts(st, out, b")"); + st[(Z_CLS) as usize] = tc; + return L_ATOM; + } + let id: usize = z_builtin_id(src, (a + 1), b); + if (id == ZB_NONE) { + refuse(st, Z_R_BUILTIN, a, a, b); + return L_ATOM; + } + put_src(src, st, out, a, b); + z_args(src, st, out); + if bad(st[(ERR) as usize]) { + return L_ATOM; + } + let end: usize = st[(Z_CLOSE) as usize]; + if (st[(Z_NARGS) as usize] != z_builtin_arity(id)) { + refuse(st, Z_R_SYNTAX, a, a, end); + return L_ATOM; + } + z_builtin_check(st, id, a, end); + return L_ATOM; +} + +pub fn z_member_ref(src: &[u8], st: &mut [usize], out: &mut [u8], a: usize, b: usize, e: usize) -> usize { + z_step(src, st); + if !(z_take(src, st, b".")) { + refuse(st, Z_R_TYPE, a, a, b); + return L_ATOM; + } + let ma: usize = st[(P) as usize]; + let mb: usize = z_tok_end(src, ma); + if !(is_alpha(head(src, ma))) { + z_refuse_here(src, st, Z_R_SYNTAX); + return L_ATOM; + } + z_step(src, st); + let m: usize = z_find_member(src, st, e, ma, mb); + if (m == 0) { + if (st[((z_slot(e) + 3)) as usize] == 1) { + refuse(st, Z_R_TYPE, a, a, b); + } else { + refuse(st, Z_R_UNDECL, a, a, mb); + } + return L_ATOM; + } + let kind: usize = st[((z_slot(m) + 2)) as usize]; + let cls: usize = st[((z_slot(m) + 3)) as usize]; + if ((kind == Z_K_FNX) || (kind == Z_K_CONSTX)) { + refuse(st, Z_R_DEPENDS, a, a, mb); + return L_ATOM; + } + if ((kind != Z_K_FN) && (kind != Z_K_CONST)) { + refuse(st, Z_R_FIELD, a, a, mb); + return L_ATOM; + } + z_put_entry(src, st, out, m, ma, mb); + if (kind == Z_K_FN) { + if !(z_is(src, st[(P) as usize], b"(")) { + refuse(st, Z_R_FIELD, a, a, mb); + return L_ATOM; + } + z_args(src, st, out); + } + st[(Z_CLS) as usize] = cls; + return L_ATOM; +} + +pub fn z_name(src: &[u8], st: &mut [usize], out: &mut [u8], a: usize, b: usize) -> usize { + if (word_is(src, a, b, b"true") || word_is(src, a, b, b"false")) { + put_src(src, st, out, a, b); + z_step(src, st); + st[(Z_CLS) as usize] = ZC_BOOL; + return L_ATOM; + } + if word_is(src, a, b, b"if") { + return z_if_expr(src, st, out); + } + if word_is(src, a, b, b"switch") { + return z_switch_expr(src, st, out); + } + if word_is(src, a, b, b"null") { + refuse(st, Z_R_OPTIONAL, a, a, b); + return L_ATOM; + } + if word_is(src, a, b, b"unreachable") { + refuse(st, Z_R_UNREACH, a, a, b); + return L_ATOM; + } + if ((word_is(src, a, b, b"error") || word_is(src, a, b, b"try")) || word_is(src, a, b, b"catch")) { + refuse(st, Z_R_ERRORS, a, a, b); + return L_ATOM; + } + if (((((((word_is(src, a, b, b"comptime") || word_is(src, a, b, b"inline")) || word_is(src, a, b, b"async")) || word_is(src, a, b, b"await")) || word_is(src, a, b, b"suspend")) || word_is(src, a, b, b"resume")) || word_is(src, a, b, b"nosuspend")) || word_is(src, a, b, b"anytype")) { + refuse(st, Z_R_GENERIC, a, a, b); + return L_ATOM; + } + if ((((((word_is(src, a, b, b"struct") || word_is(src, a, b, b"enum")) || word_is(src, a, b, b"union")) || word_is(src, a, b, b"opaque")) || word_is(src, a, b, b"fn")) || word_is(src, a, b, b"packed")) || word_is(src, a, b, b"extern")) { + refuse(st, Z_R_TYPE, a, a, b); + return L_ATOM; + } + if (((((word_is(src, a, b, b"while") || word_is(src, a, b, b"for")) || word_is(src, a, b, b"return")) || word_is(src, a, b, b"break")) || word_is(src, a, b, b"continue")) || word_is(src, a, b, b"undefined")) { + refuse(st, Z_R_VALUE, a, a, b); + return L_ATOM; + } + let e: usize = z_find(src, st, a, b); + if ((e == 0) && word_is(src, a, b, b"std")) { + return z_std_chain(src, st, out, a, Z_NS_STD); + } + if ((e != 0) && (st[((z_slot(e) + 2)) as usize] == Z_K_NS)) { + return z_std_chain(src, st, out, a, st[((z_slot(e) + 3)) as usize]); + } + if ((e != 0) && (st[((z_slot(e) + 2)) as usize] == Z_K_NSPACE)) { + return z_member_ref(src, st, out, a, b, e); + } + let k: usize = z_lookup(src, st, a, b); + if (k == 0) { + return L_ATOM; + } + let kind: usize = st[((z_slot(k) + 2)) as usize]; + let cls: usize = st[((z_slot(k) + 3)) as usize]; + z_put_entry(src, st, out, k, a, b); + z_step(src, st); + if (kind == Z_K_FN) { + if !(z_is(src, st[(P) as usize], b"(")) { + refuse(st, Z_R_FIELD, a, a, b); + return L_ATOM; + } + z_args(src, st, out); + st[(Z_CLS) as usize] = cls; + return L_ATOM; + } + st[(Z_CLS) as usize] = cls; + return L_ATOM; +} + +pub fn z_primary(src: &[u8], st: &mut [usize], out: &mut [u8]) -> usize { + st[(Z_CLS) as usize] = ZC_NONE; + if bad(st[(ERR) as usize]) { + return L_ATOM; + } + if (st[(P) as usize] >= src.len()) { + refuse(st, Z_R_SYNTAX, st[(P) as usize], st[(P) as usize], st[(P) as usize]); + return L_ATOM; + } + let a: usize = st[(P) as usize]; + let b: usize = z_tok_end(src, a); + let c: u8 = src[(a) as usize]; + if is_digit(c) { + z_number(src, st, out, a, b); + z_step(src, st); + return L_ATOM; + } + if (c == B_QUOTE) { + if !(z_str_ok(src, a, b)) { + refuse(st, Z_R_LITERAL, a, a, b); + return L_ATOM; + } + put_src(src, st, out, a, b); + z_step(src, st); + st[(Z_CLS) as usize] = (ZC_SLICE + ZC_INT); + return L_ATOM; + } + if (c == B_APOS) { + if !(z_chr_ok(src, a, b)) { + refuse(st, Z_R_LITERAL, a, a, b); + return L_ATOM; + } + put_src(src, st, out, a, b); + z_step(src, st); + st[(Z_CLS) as usize] = ZC_LITINT; + return L_ATOM; + } + if (c == B_LPAREN) { + z_step(src, st); + puts(st, out, b"("); + z_expr(src, st, out); + z_want(src, st, b")"); + puts(st, out, b")"); + return L_ATOM; + } + if (c == Z_B_AT) { + return z_builtin(src, st, out); + } + if (c == B_DOT) { + refuse(st, Z_R_ANON, a, a, b); + return L_ATOM; + } + if (c == B_LBRACKET) { + refuse(st, Z_R_ARRAYLIT, a, a, b); + return L_ATOM; + } + if is_alpha(c) { + return z_name(src, st, out, a, b); + } + refuse(st, Z_R_SYNTAX, a, a, b); + return L_ATOM; +} + +pub fn z_peek_is(src: &[u8], p: usize, s: &[u8]) -> bool { + return z_is(src, skip_ws(src, z_tok_end(src, p)), s); +} + +pub fn z_copy_out(st: &mut [usize], out: &mut [u8], lo: usize, hi: usize) -> () { + let mut i: usize = lo; + while (i < hi) { + let mut c: u8 = 63; + if (i < out.len()) { + c = out[(i) as usize]; + } + put(st, out, c); + i = (i + 1); + } +} + +pub fn z_asg_kind(src: &[u8], a: usize, b: usize) -> usize { + if word_is(src, a, b, b"=") { + return 1; + } + if (((((((word_is(src, a, b, b"+=") || word_is(src, a, b, b"-=")) || word_is(src, a, b, b"*=")) || word_is(src, a, b, b"/=")) || word_is(src, a, b, b"%=")) || word_is(src, a, b, b"&=")) || word_is(src, a, b, b"|=")) || word_is(src, a, b, b"^=")) { + return 2; + } + if ((((word_is(src, a, b, b"<<=") || word_is(src, a, b, b">>=")) || word_is(src, a, b, b"+%=")) || word_is(src, a, b, b"-%=")) || word_is(src, a, b, b"*%=")) { + return 3; + } + return 0; +} + +pub fn z_asg_level(src: &[u8], a: usize, b: usize) -> usize { + if (word_is(src, a, b, b"<<=") || word_is(src, a, b, b">>=")) { + return L_SHIFT; + } + if word_is(src, a, b, b"*%=") { + return L_MUL; + } + return L_ADD; +} + +pub fn z_semi(src: &[u8], st: &mut [usize], out: &mut [u8]) -> () { + if z_take(src, st, b";") { + puts(st, out, b";"); + return (); + } + if ((st[(Z_ONE) as usize] == 1) && z_is(src, st[(P) as usize], b"else")) { + puts(st, out, b";"); + return (); + } + z_refuse_here(src, st, Z_R_SYNTAX); +} + +pub fn z_simple(src: &[u8], st: &mut [usize], out: &mut [u8], semi: bool) -> () { + let a: usize = st[(P) as usize]; + let b: usize = z_tok_end(src, a); + if (word_is(src, a, b, b"_") && z_peek_is(src, a, b"=")) { + z_step(src, st); + z_step(src, st); + puts(st, out, b"_ = "); + let ra: usize = st[(P) as usize]; + let rb: usize = z_tok_end(src, ra); + z_expr(src, st, out); + let one: bool = (is_alpha(head(src, ra)) && (skip_ws(src, rb) == st[(P) as usize])); + if semi { + z_semi(src, st, out); + } + if ((semi && one) && !(bad(st[(ERR) as usize]))) { + let e: usize = z_find(src, st, ra, rb); + if ((e != 0) && (st[((z_slot(e) + 2)) as usize] != Z_K_PARAM)) { + st[(W) as usize] = ((st[(Z_STMT) as usize] - 1) - (4 * st[(IND) as usize])); + } + } + return (); + } + let lw: usize = st[(W) as usize]; + z_postfix(src, st, out); + if bad(st[(ERR) as usize]) { + return (); + } + let rw: usize = st[(W) as usize]; + let oa: usize = st[(P) as usize]; + let ob: usize = z_tok_end(src, oa); + let kind: usize = z_asg_kind(src, oa, ob); + if (kind != 0) { + z_step(src, st); + if (kind == 3) { + let lv: usize = z_asg_level(src, oa, ob); + puts(st, out, b" = "); + z_copy_out(st, out, lw, rw); + puts(st, out, b" "); + put_src(src, st, out, oa, (ob - 1)); + puts(st, out, b" "); + let rs: usize = st[(W) as usize]; + let rl: usize = z_expr(src, st, out); + if !(keep_bare(rl, lv, false)) { + wrap(st, out, rs); + } + } else { + puts(st, out, b" "); + put_src(src, st, out, oa, ob); + puts(st, out, b" "); + z_expr(src, st, out); + } + } + if semi { + z_semi(src, st, out); + } +} + +pub fn z_count_elems(src: &[u8], p: usize) -> usize { + let mut n: usize = 0; + let mut depth: usize = 0; + let mut any: bool = false; + let mut q: usize = p; + while (q < src.len()) { + let e: usize = z_tok_end(src, q); + if ((e == (q + 1)) && is_close(src[(q) as usize])) { + if (depth == 0) { + if any { + n = (n + 1); + } + return n; + } + depth = (depth - 1); + any = true; + } else { + if ((e == (q + 1)) && is_open(src[(q) as usize])) { + depth = (depth + 1); + any = true; + } else { + if (((e == (q + 1)) && (src[(q) as usize] == B_COMMA)) && (depth == 0)) { + n = (n + 1); + any = false; + } else { + any = true; + } + } + } + q = skip_ws(src, e); + } + return n; +} + +pub fn z_array_lit(src: &[u8], st: &mut [usize], out: &mut [u8]) -> () { + z_want(src, st, b"["); + let la: usize = st[(P) as usize]; + let lb: usize = z_tok_end(src, la); + let q1: usize = skip_ws(src, lb); + let q2: usize = skip_ws(src, z_tok_end(src, q1)); + let q3: usize = skip_ws(src, z_tok_end(src, q2)); + if !((word_is(src, la, lb, b"_") || is_digit(head(src, la)))) { + z_refuse_here(src, st, Z_R_ARRAYLIT); + return (); + } + if ((head(src, q1) != B_RBRACKET) || (head(src, q3) != B_LBRACE)) { + z_refuse_here(src, st, Z_R_ARRAYLIT); + return (); + } + let k: usize = z_count_elems(src, skip_ws(src, z_tok_end(src, q3))); + if (k == 0) { + refuse(st, Z_R_ARRAYLIT, la, la, lb); + return (); + } + z_step(src, st); + z_want(src, st, b"]"); + puts(st, out, b" : ["); + put_num(st, out, k); + puts(st, out, b"]"); + z_scalar_type(src, st, out); + let elem: usize = st[(Z_CLS) as usize]; + z_want(src, st, b"{"); + puts(st, out, b" = ["); + let mut n: usize = 0; + while ((!(bad(st[(ERR) as usize])) && (st[(P) as usize] < src.len())) && !(z_is(src, st[(P) as usize], b"}"))) { + if (n > 0) { + puts(st, out, b", "); + } + z_expr(src, st, out); + n = (n + 1); + if !(z_take(src, st, b",")) { + break; + } + } + z_want(src, st, b"}"); + puts(st, out, b"]"); + st[(Z_CLS) as usize] = (ZC_ARRAY + elem); +} + +pub fn z_decl(src: &[u8], st: &mut [usize], out: &mut [u8]) -> () { + let ka: usize = st[(P) as usize]; + let kb: usize = z_tok_end(src, ka); + let is_var: bool = word_is(src, ka, kb, b"var"); + z_step(src, st); + let na: usize = st[(P) as usize]; + let nb: usize = z_tok_end(src, na); + if (!(is_alpha(head(src, na))) || word_is(src, na, nb, b"_")) { + z_refuse_here(src, st, Z_R_SYNTAX); + return (); + } + z_step(src, st); + if is_var { + puts(st, out, b"var "); + } else { + puts(st, out, b"const "); + } + if (st[(Z_QUAL) as usize] == 1) { + z_put_qualified(src, st, out, na, nb); + } else { + put_ident(src, st, out, na, nb); + } + let mut cls: usize = ZC_NONE; + if z_take(src, st, b":") { + puts(st, out, b" : "); + z_type(src, st, out); + cls = st[(Z_CLS) as usize]; + z_want(src, st, b"="); + puts(st, out, b" = "); + if z_is(src, st[(P) as usize], b"undefined") { + puts(st, out, b"undefined"); + z_step(src, st); + } else { + z_expr(src, st, out); + } + } else { + z_want(src, st, b"="); + let ns: usize = z_ns_init(src, st, st[(P) as usize]); + if ((ns != 0) && !(bad(st[(ERR) as usize]))) { + let semi: usize = find_at(src, st[(P) as usize], B_SEMI); + st[(P) as usize] = skip_ws(src, (semi + 1)); + st[(W) as usize] = ((st[(Z_STMT) as usize] - 1) - (4 * st[(IND) as usize])); + if !(z_add_name(st, Z_K_NS, na, nb, ns)) { + refuse(st, Z_R_FULL, na, na, nb); + } + return (); + } + if (head(src, st[(P) as usize]) == B_LBRACKET) { + z_array_lit(src, st, out); + } else { + puts(st, out, b" = "); + z_expr(src, st, out); + } + cls = st[(Z_CLS) as usize]; + } + z_want(src, st, b";"); + puts(st, out, b";"); + if (!(bad(st[(ERR) as usize])) && !(z_add_name(st, Z_K_LOCAL, na, nb, cls))) { + refuse(st, Z_R_FULL, na, na, nb); + } +} + +pub fn z_body(src: &[u8], st: &mut [usize], out: &mut [u8]) -> () { + if bad(st[(ERR) as usize]) { + return (); + } + puts(st, out, b"{"); + st[(IND) as usize] = (st[(IND) as usize] + 1); + let saved: usize = st[(Z_NNAMES) as usize]; + let one: usize = st[(Z_ONE) as usize]; + if z_take(src, st, b"{") { + st[(Z_ONE) as usize] = 0; + z_stmts(src, st, out); + } else { + st[(Z_ONE) as usize] = 1; + z_line(src, st, out); + } + st[(Z_ONE) as usize] = one; + st[(Z_NNAMES) as usize] = saved; + st[(IND) as usize] = (st[(IND) as usize] - 1); + newline(st, out); + puts(st, out, b"}"); +} + +pub fn z_stmts(src: &[u8], st: &mut [usize], out: &mut [u8]) -> () { + while ((!(bad(st[(ERR) as usize])) && (st[(P) as usize] < src.len())) && !(z_is(src, st[(P) as usize], b"}"))) { + z_line(src, st, out); + } + z_want(src, st, b"}"); +} + +pub fn z_line(src: &[u8], st: &mut [usize], out: &mut [u8]) -> () { + if bad(st[(ERR) as usize]) { + return (); + } + newline(st, out); + let mut w0: usize = st[(W) as usize]; + let p0: usize = st[(P) as usize]; + let mut tries: usize = 0; + while (tries < 13) { + st[(Z_STMT) as usize] = w0; + z_stmt(src, st, out); + if ((st[(ERR) as usize] != Z_R_HOIST) || (tries == 12)) { + return (); + } + let hpos: usize = st[(AT) as usize]; + st[(ERR) as usize] = 0; + st[(W) as usize] = w0; + st[(Z_TMP) as usize] = (st[(Z_TMP) as usize] + 1); + let tmp: usize = st[(Z_TMP) as usize]; + st[(P) as usize] = skip_ws(src, z_tok_end(src, hpos)); + puts(st, out, b"const t27o_a"); + put_num(st, out, tmp); + z_array_lit(src, st, out); + puts(st, out, b";"); + if bad(st[(ERR) as usize]) { + return (); + } + if !(z_add_name(st, Z_K_HOIST, hpos, tmp, st[(Z_CLS) as usize])) { + refuse(st, Z_R_FULL, hpos, hpos, (hpos + 1)); + return (); + } + newline(st, out); + w0 = st[(W) as usize]; + st[(P) as usize] = p0; + tries = (tries + 1); + } +} + +pub fn z_if_stmt(src: &[u8], st: &mut [usize], out: &mut [u8]) -> () { + z_step(src, st); + puts(st, out, b"if ("); + z_want(src, st, b"("); + z_expr(src, st, out); + z_want(src, st, b")"); + puts(st, out, b") "); + if z_is(src, st[(P) as usize], b"|") { + z_refuse_here(src, st, Z_R_PAYLOAD); + return (); + } + z_body(src, st, out); + if (!(bad(st[(ERR) as usize])) && z_take(src, st, b"else")) { + puts(st, out, b" else "); + if z_is(src, st[(P) as usize], b"if") { + z_if_stmt(src, st, out); + } else { + z_body(src, st, out); + } + } +} + +pub fn z_while_stmt(src: &[u8], st: &mut [usize], out: &mut [u8]) -> () { + z_step(src, st); + puts(st, out, b"while ("); + z_want(src, st, b"("); + z_expr(src, st, out); + z_want(src, st, b")"); + puts(st, out, b")"); + if z_is(src, st[(P) as usize], b"|") { + z_refuse_here(src, st, Z_R_PAYLOAD); + return (); + } + if z_take(src, st, b":") { + z_want(src, st, b"("); + puts(st, out, b" : ("); + z_simple(src, st, out, false); + z_want(src, st, b")"); + puts(st, out, b")"); + } + puts(st, out, b" "); + z_body(src, st, out); +} + +pub fn z_jump(src: &[u8], st: &mut [usize], out: &mut [u8]) -> () { + let a: usize = st[(P) as usize]; + let b: usize = z_tok_end(src, a); + let is_ret: bool = word_is(src, a, b, b"return"); + z_step(src, st); + put_src(src, st, out, a, b); + if !(z_is(src, st[(P) as usize], b";")) { + if is_ret { + puts(st, out, b" "); + z_expr(src, st, out); + } else { + z_refuse_here(src, st, Z_R_LABEL); + return (); + } + } + z_semi(src, st, out); +} + +pub fn z_for_stmt(src: &[u8], st: &mut [usize], out: &mut [u8]) -> () { + z_step(src, st); + z_want(src, st, b"("); + let base: usize = st[(Z_NNAMES) as usize]; + puts(st, out, b"for ("); + let w0: usize = st[(W) as usize]; + let mut is_a: [usize; 4] = [0; 4]; + let mut is_b: [usize; 4] = [0; 4]; + let mut is_k: [usize; 4] = [0; 4]; + let mut is_c: [usize; 4] = [0; 4]; + let f0: usize = st[(P) as usize]; + let f1: usize = z_tok_end(src, f0); + z_expr(src, st, out); + if bad(st[(ERR) as usize]) { + return (); + } + let c1: usize = st[(Z_CLS) as usize]; + is_a[0] = f0; + is_b[0] = f1; + is_k[0] = 1; + is_c[0] = c1; + let mut n: usize = 1; + let mut range: bool = false; + if z_take(src, st, b"..") { + range = true; + puts(st, out, b".."); + z_expr(src, st, out); + } else { + if ((n == 1) && (skip_ws(src, f1) != st[(P) as usize])) { + is_k[0] = 2; + } + while (!(bad(st[(ERR) as usize])) && z_take(src, st, b",")) { + let g0: usize = st[(P) as usize]; + let g1: usize = z_tok_end(src, g0); + if ((n >= 4) || (is_k[0] == 2)) { + z_refuse_here(src, st, Z_R_LOOPFORM); + return (); + } + if (z_is(src, g0, b"0") && z_peek_is(src, g0, b"..")) { + z_step(src, st); + z_step(src, st); + is_k[(n) as usize] = 3; + } else { + let e: usize = z_find(src, st, g0, g1); + if ((!(is_alpha(head(src, g0))) || (e == 0)) || !(z_is_seq(st[((z_slot(e) + 3)) as usize]))) { + z_refuse_here(src, st, Z_R_LOOPFORM); + return (); + } + is_a[(n) as usize] = g0; + is_b[(n) as usize] = g1; + is_k[(n) as usize] = 1; + is_c[(n) as usize] = st[((z_slot(e) + 3)) as usize]; + z_step(src, st); + } + n = (n + 1); + } + if ((n == 1) && !(z_is_seq(c1))) { + refuse(st, Z_R_LOOPFORM, f0, f0, f1); + return (); + } + } + z_want(src, st, b")"); + let mut ca: [usize; 4] = [0; 4]; + let mut cb: [usize; 4] = [0; 4]; + let mut m: usize = 0; + z_want(src, st, b"|"); + while (!(bad(st[(ERR) as usize])) && (m < 4)) { + let xa: usize = st[(P) as usize]; + let xb: usize = z_tok_end(src, xa); + if !(is_alpha(head(src, xa))) { + refuse(st, Z_R_PAYLOAD, xa, xa, xb); + return (); + } + ca[(m) as usize] = xa; + cb[(m) as usize] = xb; + m = (m + 1); + z_step(src, st); + if !(z_take(src, st, b",")) { + break; + } + } + z_want(src, st, b"|"); + if bad(st[(ERR) as usize]) { + return (); + } + if (m != n) { + z_refuse_here(src, st, Z_R_LOOPFORM); + return (); + } + let mut k: usize = 0; + let mut seqs: usize = 0; + while (k < n) { + if (is_k[(k) as usize] == 1) { + seqs = (seqs + 1); + } + k = (k + 1); + } + let mut lead: usize = 0; + let mut idx: usize = 0; + let mut have_idx: bool = false; + k = 0; + while (k < n) { + if (is_k[(k) as usize] == 3) { + idx = k; + have_idx = true; + } + k = (k + 1); + } + let several: bool = (n > 1); + if (several && ((seqs == 0) || !(is_alpha(head(src, f0))))) { + z_refuse_here(src, st, Z_R_LOOPFORM); + return (); + } + if several { + st[(W) as usize] = w0; + puts(st, out, b"0.."); + put_ident(src, st, out, is_a[0], is_b[0]); + puts(st, out, b".len) |"); + if have_idx { + put_ident(src, st, out, ca[(idx) as usize], cb[(idx) as usize]); + } else { + st[(Z_TMP) as usize] = (st[(Z_TMP) as usize] + 1); + lead = st[(Z_TMP) as usize]; + puts(st, out, b"t27o_i"); + put_num(st, out, lead); + } + puts(st, out, b"| {"); + } else { + puts(st, out, b") |"); + put_ident(src, st, out, ca[0], cb[0]); + puts(st, out, b"| {"); + } + k = 0; + while (k < n) { + let mut cls: usize = ZC_INT; + if ((is_k[(k) as usize] == 1) || (is_k[(k) as usize] == 2)) { + cls = z_elem(is_c[(k) as usize]); + if range { + cls = ZC_INT; + } + } + if (!(word_is(src, ca[(k) as usize], cb[(k) as usize], b"_")) && !(z_add_name(st, Z_K_LOCAL, ca[(k) as usize], cb[(k) as usize], cls))) { + refuse(st, Z_R_FULL, ca[(k) as usize], ca[(k) as usize], cb[(k) as usize]); + return (); + } + k = (k + 1); + } + st[(IND) as usize] = (st[(IND) as usize] + 1); + if several { + k = 0; + while (k < n) { + if ((is_k[(k) as usize] == 1) && !(word_is(src, ca[(k) as usize], cb[(k) as usize], b"_"))) { + newline(st, out); + puts(st, out, b"const "); + put_ident(src, st, out, ca[(k) as usize], cb[(k) as usize]); + puts(st, out, b" = "); + put_ident(src, st, out, is_a[(k) as usize], is_b[(k) as usize]); + puts(st, out, b"["); + if have_idx { + put_ident(src, st, out, ca[(idx) as usize], cb[(idx) as usize]); + } else { + puts(st, out, b"t27o_i"); + put_num(st, out, lead); + } + puts(st, out, b"];"); + } + k = (k + 1); + } + } + if z_take(src, st, b"{") { + z_stmts(src, st, out); + } else { + z_line(src, st, out); + } + st[(IND) as usize] = (st[(IND) as usize] - 1); + newline(st, out); + puts(st, out, b"}"); + st[(Z_NNAMES) as usize] = base; +} + +pub fn z_item_is_range(src: &[u8], p: usize) -> bool { + let mut q: usize = p; + if z_is(src, q, b"-") { + q = skip_ws(src, z_tok_end(src, q)); + } + return z_peek_is(src, q, b"..."); +} + +pub fn z_put_scrutinee(src: &[u8], st: &mut [usize], out: &mut [u8], simple: bool, sa: usize, sb: usize, tmp: usize) -> () { + if simple { + put_ident(src, st, out, sa, sb); + } else { + puts(st, out, b"t27o_s"); + put_num(st, out, tmp); + } +} + +pub fn z_switch_item(src: &[u8], st: &mut [usize], out: &mut [u8], simple: bool, sa: usize, sb: usize, tmp: usize) -> () { + let range: bool = z_item_is_range(src, st[(P) as usize]); + if range { + puts(st, out, b"("); + } + z_put_scrutinee(src, st, out, simple, sa, sb, tmp); + if range { + puts(st, out, b" >= "); + } else { + puts(st, out, b" == "); + } + z_unary(src, st, out); + if range { + z_want(src, st, b"..."); + puts(st, out, b" and "); + z_put_scrutinee(src, st, out, simple, sa, sb, tmp); + puts(st, out, b" <= "); + z_unary(src, st, out); + puts(st, out, b")"); + } +} + +pub fn z_prong_body(src: &[u8], st: &mut [usize], out: &mut [u8]) -> () { + if z_is(src, st[(P) as usize], b"{") { + z_body(src, st, out); + return (); + } + puts(st, out, b"{"); + st[(IND) as usize] = (st[(IND) as usize] + 1); + newline(st, out); + st[(Z_STMT) as usize] = st[(W) as usize]; + let a: usize = st[(P) as usize]; + let b: usize = z_tok_end(src, a); + if ((word_is(src, a, b, b"return") || word_is(src, a, b, b"break")) || word_is(src, a, b, b"continue")) { + z_step(src, st); + put_src(src, st, out, a, b); + if (!(z_is(src, st[(P) as usize], b",")) && !(z_is(src, st[(P) as usize], b"}"))) { + if word_is(src, a, b, b"return") { + puts(st, out, b" "); + z_expr(src, st, out); + } else { + z_refuse_here(src, st, Z_R_LABEL); + } + } + } else { + if word_is(src, a, b, b"unreachable") { + refuse(st, Z_R_UNREACH, a, a, b); + } else { + z_simple(src, st, out, false); + } + } + puts(st, out, b";"); + st[(IND) as usize] = (st[(IND) as usize] - 1); + newline(st, out); + puts(st, out, b"}"); +} + +pub fn z_switch_stmt(src: &[u8], st: &mut [usize], out: &mut [u8]) -> () { + z_step(src, st); + z_want(src, st, b"("); + let sa: usize = st[(P) as usize]; + let sb: usize = z_tok_end(src, sa); + let simple: bool = (is_alpha(head(src, sa)) && z_peek_is(src, sa, b")")); + let mut tmp: usize = 0; + if simple { + let known: usize = z_lookup(src, st, sa, sb); + if (known == 0) { + return (); + } + z_step(src, st); + } else { + st[(Z_TMP) as usize] = (st[(Z_TMP) as usize] + 1); + tmp = st[(Z_TMP) as usize]; + puts(st, out, b"const t27o_s"); + put_num(st, out, tmp); + puts(st, out, b" = "); + z_expr(src, st, out); + puts(st, out, b";"); + newline(st, out); + } + z_want(src, st, b")"); + z_want(src, st, b"{"); + let mut first: bool = true; + while ((!(bad(st[(ERR) as usize])) && (st[(P) as usize] < src.len())) && !(z_is(src, st[(P) as usize], b"}"))) { + if z_is(src, st[(P) as usize], b"else") { + z_step(src, st); + z_want(src, st, b"=>"); + if first { + puts(st, out, b"if (true) "); + } else { + puts(st, out, b" else "); + } + } else { + if first { + puts(st, out, b"if ("); + } else { + puts(st, out, b" else if ("); + } + let mut n: usize = 0; + while !(bad(st[(ERR) as usize])) { + if (n > 0) { + puts(st, out, b" or "); + } + z_switch_item(src, st, out, simple, sa, sb, tmp); + n = (n + 1); + if !(z_take(src, st, b",")) { + break; + } + } + z_want(src, st, b"=>"); + puts(st, out, b") "); + } + first = false; + z_prong_body(src, st, out); + _ = z_take(src, st, b","); + } + z_want(src, st, b"}"); +} + +pub fn z_operand(src: &[u8], st: &mut [usize], out: &mut [u8], lv: usize, left: bool) -> () { + let w: usize = st[(W) as usize]; + let l: usize = z_expr(src, st, out); + if !(keep_bare(l, lv, left)) { + wrap(st, out, w); + } +} + +pub fn z_next_arg(src: &[u8], p: usize) -> usize { + let cm: usize = find_at(src, p, B_COMMA); + if (cm >= src.len()) { + return src.len(); + } + return skip_ws(src, (cm + 1)); +} + +pub fn z_test_call(src: &[u8], st: &mut [usize], out: &mut [u8]) -> () { + let ta: usize = st[(P) as usize]; + z_step(src, st); + let a0: usize = st[(P) as usize]; + let b0: usize = z_tok_end(src, a0); + let mut ns: usize = 0; + let e: usize = z_find(src, st, a0, b0); + if ((e != 0) && (st[((z_slot(e) + 2)) as usize] == Z_K_NS)) { + ns = st[((z_slot(e) + 3)) as usize]; + } + if ((e == 0) && word_is(src, a0, b0, b"std")) { + ns = Z_NS_STD; + } + if (ns == 0) { + refuse(st, Z_R_ERRORS, ta, ta, b0); + return (); + } + z_step(src, st); + if (ns == Z_NS_STD) { + z_want(src, st, b"."); + let sa: usize = st[(P) as usize]; + ns = z_ns_of(src, sa, z_tok_end(src, sa)); + z_step(src, st); + } + if (ns != Z_NS_TESTING) { + refuse(st, Z_R_ERRORS, ta, ta, b0); + return (); + } + z_want(src, st, b"."); + let ma: usize = st[(P) as usize]; + let mb: usize = z_tok_end(src, ma); + z_step(src, st); + z_want(src, st, b"("); + let mut after: usize = 0; + if word_is(src, ma, mb, b"expect") { + puts(st, out, b"assert("); + z_expr(src, st, out); + after = st[(P) as usize]; + puts(st, out, b")"); + } else { + if word_is(src, ma, mb, b"expectEqual") { + let pe: usize = st[(P) as usize]; + let pa: usize = z_next_arg(src, pe); + puts(st, out, b"assert("); + st[(P) as usize] = pa; + z_operand(src, st, out, L_CMP, true); + after = st[(P) as usize]; + puts(st, out, b" == "); + st[(P) as usize] = pe; + z_operand(src, st, out, L_CMP, false); + puts(st, out, b")"); + } else { + if word_is(src, ma, mb, b"expectApproxEqAbs") { + let pe: usize = st[(P) as usize]; + let pa: usize = z_next_arg(src, pe); + let pt: usize = z_next_arg(src, pa); + puts(st, out, b"assert(@abs("); + st[(P) as usize] = pa; + z_operand(src, st, out, L_ADD, true); + puts(st, out, b" - "); + st[(P) as usize] = pe; + z_operand(src, st, out, L_ADD, false); + puts(st, out, b") <= "); + st[(P) as usize] = pt; + z_operand(src, st, out, L_CMP, false); + after = st[(P) as usize]; + puts(st, out, b")"); + } else { + if word_is(src, ma, mb, b"expectApproxEqRel") { + let pe: usize = st[(P) as usize]; + let pa: usize = z_next_arg(src, pe); + let pt: usize = z_next_arg(src, pa); + puts(st, out, b"assert(@abs("); + st[(P) as usize] = pa; + z_operand(src, st, out, L_ADD, true); + puts(st, out, b" - "); + st[(P) as usize] = pe; + z_operand(src, st, out, L_ADD, false); + puts(st, out, b") <= @max(@abs("); + st[(P) as usize] = pa; + z_expr(src, st, out); + puts(st, out, b"), @abs("); + st[(P) as usize] = pe; + z_expr(src, st, out); + puts(st, out, b")) * "); + st[(P) as usize] = pt; + z_operand(src, st, out, L_MUL, false); + after = st[(P) as usize]; + puts(st, out, b")"); + } else { + if (word_is(src, ma, mb, b"expectEqualStrings") || word_is(src, ma, mb, b"expectEqualSlices")) { + if word_is(src, ma, mb, b"expectEqualSlices") { + if !(z_take(src, st, b"u8")) { + refuse(st, Z_R_TESTCALL, ma, ma, mb); + return (); + } + z_want(src, st, b","); + } + let pe: usize = st[(P) as usize]; + let pa: usize = z_next_arg(src, pe); + puts(st, out, b"assert(std.mem.eql(u8, "); + st[(P) as usize] = pa; + z_expr(src, st, out); + after = st[(P) as usize]; + puts(st, out, b", "); + st[(P) as usize] = pe; + z_expr(src, st, out); + puts(st, out, b"))"); + } else { + refuse(st, Z_R_TESTCALL, ma, ma, mb); + return (); + } + } + } + } + } + if bad(st[(ERR) as usize]) { + return (); + } + st[(P) as usize] = after; + _ = z_take(src, st, b","); + z_want(src, st, b")"); + z_want(src, st, b";"); + puts(st, out, b";"); +} + +pub fn z_stmt(src: &[u8], st: &mut [usize], out: &mut [u8]) -> () { + if bad(st[(ERR) as usize]) { + return (); + } + if (st[(P) as usize] >= src.len()) { + z_refuse_here(src, st, Z_R_SYNTAX); + return (); + } + let a: usize = st[(P) as usize]; + let b: usize = z_tok_end(src, a); + if (word_is(src, a, b, b"const") || word_is(src, a, b, b"var")) { + z_decl(src, st, out); + } else { + if word_is(src, a, b, b"if") { + z_if_stmt(src, st, out); + } else { + if word_is(src, a, b, b"while") { + z_while_stmt(src, st, out); + } else { + if word_is(src, a, b, b"for") { + z_for_stmt(src, st, out); + } else { + if word_is(src, a, b, b"switch") { + z_switch_stmt(src, st, out); + } else { + if ((word_is(src, a, b, b"return") || word_is(src, a, b, b"break")) || word_is(src, a, b, b"continue")) { + z_jump(src, st, out); + } else { + if word_is(src, a, b, b"try") { + if (st[(Z_INTEST) as usize] == 1) { + z_test_call(src, st, out); + } else { + refuse(st, Z_R_ERRORS, a, a, b); + } + } else { + if (word_is(src, a, b, b"defer") || word_is(src, a, b, b"errdefer")) { + refuse(st, Z_R_DEFER, a, a, b); + } else { + if ((((((word_is(src, a, b, b"comptime") || word_is(src, a, b, b"inline")) || word_is(src, a, b, b"async")) || word_is(src, a, b, b"suspend")) || word_is(src, a, b, b"nosuspend")) || word_is(src, a, b, b"resume")) || word_is(src, a, b, b"await")) { + refuse(st, Z_R_GENERIC, a, a, b); + } else { + if word_is(src, a, b, b"unreachable") { + refuse(st, Z_R_UNREACH, a, a, b); + } else { + if (head(src, a) == B_LBRACE) { + refuse(st, Z_R_BRACES, a, a, b); + } else { + if (is_alpha(head(src, a)) && z_peek_is(src, a, b":")) { + refuse(st, Z_R_LABEL, a, a, b); + } else { + z_simple(src, st, out, true); + } + } + } + } + } + } + } + } + } + } + } + } +} + +pub fn z_stop(src: &[u8], from: usize) -> usize { + let mut depth: usize = 0; + let mut q: usize = from; + while (q < src.len()) { + let e: usize = z_tok_end(src, q); + if (e == (q + 1)) { + let c: u8 = src[(q) as usize]; + if ((depth == 0) && ((c == B_SEMI) || (c == B_LBRACE))) { + return q; + } + if ((c == B_LPAREN) || (c == B_LBRACKET)) { + depth = (depth + 1); + } + if ((c == B_RPAREN) || (c == B_RBRACKET)) { + if (depth == 0) { + return src.len(); + } + depth = (depth - 1); + } + } + q = skip_ws(src, e); + } + return src.len(); +} + +pub fn z_item_end(src: &[u8], p: usize) -> usize { + let mut q: usize = p; + let mut guard: usize = 0; + while ((guard < 6) && (((((z_is(src, q, b"pub") || z_is(src, q, b"export")) || z_is(src, q, b"extern")) || z_is(src, q, b"inline")) || z_is(src, q, b"noinline")) || z_is(src, q, b"threadlocal"))) { + q = skip_ws(src, z_tok_end(src, q)); + guard = (guard + 1); + } + let e: usize = z_tok_end(src, q); + if (word_is(src, q, e, b"const") || word_is(src, q, e, b"var")) { + let semi: usize = find_at(src, q, B_SEMI); + if (semi < src.len()) { + return (semi + 1); + } + return src.len(); + } + let s: usize = z_stop(src, q); + if ((s < src.len()) && (src[(s) as usize] == B_LBRACE)) { + return block_end(src, s); + } + if (s < src.len()) { + return (s + 1); + } + return src.len(); +} + +pub fn z_mod_word(src: &[u8], q: usize) -> bool { + return ((((z_is(src, q, b"pub") || z_is(src, q, b"export")) || z_is(src, q, b"extern")) || z_is(src, q, b"inline")) || z_is(src, q, b"noinline")); +} + +pub fn z_ret_class(src: &[u8], q: usize) -> usize { + let open: usize = find_at(src, q, B_LPAREN); + if (open >= src.len()) { + return ZC_NONE; + } + let t: usize = skip_ws(src, block_end(src, open)); + return z_type_class(src, t, z_tok_end(src, t)); +} + +pub fn z_init_class(src: &[u8], p: usize) -> usize { + let b: usize = z_tok_end(src, p); + let c: u8 = head(src, p); + if is_digit(c) { + let mut i: usize = p; + while (i < b) { + if ((src[(i) as usize] == B_DOT) || (src[(i) as usize] == B_LOWER_E)) { + return ZC_LITFLOAT; + } + i = (i + 1); + } + return ZC_LITINT; + } + if (c == B_QUOTE) { + return (ZC_SLICE + ZC_INT); + } + if (word_is(src, p, b, b"true") || word_is(src, p, b, b"false")) { + return ZC_BOOL; + } + return ZC_NONE; +} + +pub fn z_type_init(src: &[u8], p: usize) -> bool { + let b: usize = z_tok_end(src, p); + return ((((((((word_is(src, p, b, b"struct") || word_is(src, p, b, b"enum")) || word_is(src, p, b, b"union")) || word_is(src, p, b, b"opaque")) || word_is(src, p, b, b"error")) || word_is(src, p, b, b"packed")) || word_is(src, p, b, b"extern")) || word_is(src, p, b, b"fn")) || word_is(src, p, b, b"anyerror")); +} + +pub fn z_enter(st: &mut [usize], kind: usize, lo: usize, hi: usize, cls: usize) -> () { + if !(z_add_name(st, kind, lo, hi, cls)) { + st[(Z_CRAMPED) as usize] = 1; + } +} + +pub fn z_ns_init(src: &[u8], st: &mut [usize], p: usize) -> usize { + let b: usize = z_tok_end(src, p); + if word_is(src, p, b, b"@import") { + let arg: usize = skip_ws(src, z_tok_end(src, skip_ws(src, b))); + let close: usize = skip_ws(src, z_tok_end(src, arg)); + if ((z_is(src, arg, b"\"std\"") && z_is(src, close, b")")) && z_peek_is(src, close, b";")) { + return Z_NS_STD; + } + return 0; + } + let mut root: usize = 0; + if word_is(src, p, b, b"std") { + root = Z_NS_STD; + } else { + let e: usize = z_find(src, st, p, b); + if ((e != 0) && (st[((z_slot(e) + 2)) as usize] == Z_K_NS)) { + root = st[((z_slot(e) + 3)) as usize]; + } + } + if (root == 0) { + return 0; + } + let q: usize = skip_ws(src, b); + if (head(src, q) == B_SEMI) { + return root; + } + if ((root == Z_NS_STD) && z_is(src, q, b".")) { + let m: usize = skip_ws(src, z_tok_end(src, q)); + let mb: usize = z_tok_end(src, m); + if (is_alpha(head(src, m)) && (head(src, skip_ws(src, mb)) == B_SEMI)) { + return z_ns_of(src, m, mb); + } + } + return 0; +} + +pub fn z_scan_const(src: &[u8], st: &mut [usize], q: usize) -> () { + let na: usize = skip_ws(src, z_tok_end(src, q)); + let nb: usize = z_tok_end(src, na); + if !(is_alpha(head(src, na))) { + return (); + } + if z_is(src, q, b"var") { + z_enter(st, Z_K_VARX, na, nb, ZC_NONE); + return (); + } + let eq: usize = find_at(src, nb, B_EQ); + if (eq >= src.len()) { + return (); + } + let ip: usize = skip_ws(src, (eq + 1)); + let ib: usize = z_tok_end(src, ip); + let ns: usize = z_ns_init(src, st, ip); + if (ns != 0) { + z_enter(st, Z_K_NS, na, nb, ns); + return (); + } + if (word_is(src, ip, ib, b"@import") || word_is(src, ip, ib, b"std")) { + z_enter(st, Z_K_EXT, na, nb, ZC_NONE); + return (); + } + let lb: usize = z_container_open(src, ip); + if ((lb < src.len()) && (st[(Z_OWNER) as usize] == 0)) { + let mut pure: usize = 1; + if !(z_container_pure(src, lb)) { + pure = 0; + } + z_enter(st, Z_K_NSPACE, na, nb, (1 - pure)); + let owner: usize = st[(Z_NNAMES) as usize]; + let close: usize = block_end(src, lb); + st[(Z_OWNER) as usize] = owner; + let mut p: usize = z_next_member(src, skip_ws(src, (lb + 1)), close); + while ((p + 1) < close) { + z_scan_item(src, st, p); + p = z_next_member(src, skip_ws(src, z_item_end(src, p)), close); + } + st[(Z_OWNER) as usize] = 0; + return (); + } + if (z_type_init(src, ip) || (z_scalar(src, ip, ib) && z_peek_is(src, ip, b";"))) { + z_enter(st, Z_K_TYPEX, na, nb, ZC_NONE); + return (); + } + let mut cls: usize = z_init_class(src, ip); + if z_is(src, skip_ws(src, nb), b":") { + let ta: usize = skip_ws(src, z_tok_end(src, skip_ws(src, nb))); + cls = z_type_class(src, ta, z_tok_end(src, ta)); + } + z_enter(st, Z_K_CONST, na, nb, cls); +} + +pub fn z_container_open(src: &[u8], ip: usize) -> usize { + if !(((z_is(src, ip, b"struct") || z_is(src, ip, b"enum")) || z_is(src, ip, b"union"))) { + return src.len(); + } + let mut q: usize = skip_ws(src, z_tok_end(src, ip)); + if (head(src, q) == B_LPAREN) { + q = skip_ws(src, block_end(src, q)); + } + if (head(src, q) != B_LBRACE) { + return src.len(); + } + return q; +} + +pub fn z_decl_start(src: &[u8], p: usize) -> bool { + let mut q: usize = p; + let mut guard: usize = 0; + while ((guard < 6) && (z_mod_word(src, q) || z_is(src, q, b"threadlocal"))) { + q = skip_ws(src, z_tok_end(src, q)); + guard = (guard + 1); + } + if ((((z_is(src, q, b"fn") || z_is(src, q, b"const")) || z_is(src, q, b"var")) || z_is(src, q, b"test")) || z_is(src, q, b"usingnamespace")) { + return true; + } + return (z_is(src, q, b"comptime") && z_peek_is(src, q, b"{")); +} + +pub fn z_next_member(src: &[u8], p: usize, close: usize) -> usize { + let mut q: usize = p; + while (((q + 1) < close) && !(z_decl_start(src, q))) { + let cm: usize = find_at(src, q, B_COMMA); + if ((cm + 1) >= close) { + return (close - 1); + } + q = skip_ws(src, (cm + 1)); + } + return q; +} + +pub fn z_container_pure(src: &[u8], lb: usize) -> bool { + let close: usize = block_end(src, lb); + let mut p: usize = skip_ws(src, (lb + 1)); + while ((p + 1) < close) { + let mut q: usize = p; + let mut guard: usize = 0; + while ((guard < 6) && z_mod_word(src, q)) { + q = skip_ws(src, z_tok_end(src, q)); + guard = (guard + 1); + } + if z_is(src, q, b"const") { + let eq: usize = find_at(src, q, B_EQ); + if ((eq >= src.len()) || z_type_init(src, skip_ws(src, (eq + 1)))) { + return false; + } + } else { + if !(z_is(src, q, b"fn")) { + return false; + } + } + let end: usize = z_item_end(src, p); + if (end <= p) { + return false; + } + p = skip_ws(src, end); + } + return true; +} + +pub fn z_scan_item(src: &[u8], st: &mut [usize], p: usize) -> () { + let mut q: usize = p; + let mut guard: usize = 0; + while ((guard < 6) && z_mod_word(src, q)) { + q = skip_ws(src, z_tok_end(src, q)); + guard = (guard + 1); + } + if z_is(src, q, b"fn") { + let na: usize = skip_ws(src, z_tok_end(src, q)); + let nb: usize = z_tok_end(src, na); + if (is_alpha(head(src, na)) && !(z_add_name(st, Z_K_FN, na, nb, z_ret_class(src, nb)))) { + st[(Z_CRAMPED) as usize] = 1; + } + } else { + if (z_is(src, q, b"const") || z_is(src, q, b"var")) { + z_scan_const(src, st, q); + } + } +} + +pub fn z_prescan(src: &[u8], st: &mut [usize]) -> () { + let mut p: usize = skip_ws(src, 0); + while (p < src.len()) { + let mut end: usize = z_item_end(src, p); + if (end <= p) { + end = (p + 1); + } + z_scan_item(src, st, p); + p = skip_ws(src, end); + } + st[(Z_NFILE) as usize] = st[(Z_NNAMES) as usize]; +} + +pub fn z_mark_refused(src: &[u8], st: &mut [usize], a: usize, b: usize) -> () { + if (st[(Z_ROUND) as usize] < 2) { + return (); + } + let e: usize = z_find_file(src, st, a, b); + if (e == 0) { + return (); + } + let s: usize = z_slot(e); + if (st[((s + 2)) as usize] == Z_K_FN) { + st[((s + 2)) as usize] = Z_K_FNX; + st[(Z_GREW) as usize] = (st[(Z_GREW) as usize] + 1); + } else { + if (st[((s + 2)) as usize] == Z_K_CONST) { + st[((s + 2)) as usize] = Z_K_CONSTX; + st[(Z_GREW) as usize] = (st[(Z_GREW) as usize] + 1); + } + } +} + +pub fn z_put_refusal(src: &[u8], st: &mut [usize], out: &mut [u8], what: &[u8], na: usize, nb: usize, pos: usize, nf: usize) -> () { + puts(st, out, b"// t27o: refused "); + puts(st, out, what); + puts(st, out, b" "); + if (st[(Z_CUR) as usize] != 0) { + put_text(src, st, out, st[(z_slot(st[(Z_CUR) as usize])) as usize], st[((z_slot(st[(Z_CUR) as usize]) + 1)) as usize], 60); + puts(st, out, b"."); + } + put_text(src, st, out, na, nb, 60); + puts(st, out, b" (line "); + let ln: usize = line_at(src, st, pos); + put_num(st, out, ln); + puts(st, out, b"): "); + z_put_why(st, out, st[(ERR) as usize]); + if (st[(NHI) as usize] > st[(NLO) as usize]) { + puts(st, out, b" `"); + put_text(src, st, out, st[(NLO) as usize], st[(NHI) as usize], 40); + puts(st, out, b"` a"); + puts(st, out, b"t line "); + let at_ln: usize = line_at(src, st, st[(AT) as usize]); + put_num(st, out, at_ln); + } + if (nf > 0) { + puts(st, out, b"; "); + put_num(st, out, nf); + puts(st, out, b" fn inside"); + } + puts(st, out, b"\n\n"); +} + +pub fn z_params(src: &[u8], st: &mut [usize], out: &mut [u8]) -> () { + z_want(src, st, b"("); + puts(st, out, b"("); + let mut n: usize = 0; + while ((!(bad(st[(ERR) as usize])) && (st[(P) as usize] < src.len())) && !(z_is(src, st[(P) as usize], b")"))) { + if (n > 0) { + puts(st, out, b", "); + } + let a: usize = st[(P) as usize]; + let b: usize = z_tok_end(src, a); + if ((word_is(src, a, b, b"comptime") || word_is(src, a, b, b"noalias")) || word_is(src, a, b, b"anytype")) { + refuse(st, Z_R_GENERIC, a, a, b); + return (); + } + if (!(is_alpha(head(src, a))) || word_is(src, a, b, b"_")) { + refuse(st, Z_R_SYNTAX, a, a, b); + return (); + } + z_step(src, st); + z_want(src, st, b":"); + put_ident(src, st, out, a, b); + puts(st, out, b": "); + z_type(src, st, out); + if (!(bad(st[(ERR) as usize])) && !(z_add_name(st, Z_K_PARAM, a, b, st[(Z_CLS) as usize]))) { + refuse(st, Z_R_FULL, a, a, b); + } + n = (n + 1); + if !(z_take(src, st, b",")) { + break; + } + } + z_want(src, st, b")"); + puts(st, out, b")"); +} + +pub fn z_fn_item(src: &[u8], st: &mut [usize], out: &mut [u8], p0: usize, q: usize, end: usize, is_pub: bool) -> () { + st[(Z_NFN) as usize] = (st[(Z_NFN) as usize] + 1); + let w0: usize = st[(W) as usize]; + st[(P) as usize] = skip_ws(src, z_tok_end(src, q)); + let na: usize = st[(P) as usize]; + let nb: usize = z_tok_end(src, na); + if !(is_alpha(head(src, na))) { + refuse(st, Z_R_SYNTAX, na, na, nb); + } else { + z_step(src, st); + if is_pub { + puts(st, out, b"pub "); + } + puts(st, out, b"fn "); + z_put_qualified(src, st, out, na, nb); + z_params(src, st, out); + let la: usize = st[(P) as usize]; + let lb: usize = z_tok_end(src, la); + if (((word_is(src, la, lb, b"align") || word_is(src, la, lb, b"callconv")) || word_is(src, la, lb, b"linksection")) || word_is(src, la, lb, b"addrspace")) { + refuse(st, Z_R_LINKAGE, la, la, lb); + } + puts(st, out, b" -> "); + z_type(src, st, out); + let rc: usize = st[(Z_CLS) as usize]; + z_want(src, st, b"{"); + puts(st, out, b" {"); + st[(IND) as usize] = 1; + z_stmts(src, st, out); + st[(IND) as usize] = 0; + newline(st, out); + puts(st, out, b"}"); + let e: usize = z_find_file(src, st, na, nb); + if ((e != 0) && !(bad(st[(ERR) as usize]))) { + st[((z_slot(e) + 3)) as usize] = rc; + } + } + st[(Z_NNAMES) as usize] = st[(Z_NFILE) as usize]; + if bad(st[(ERR) as usize]) { + st[(W) as usize] = w0; + z_put_refusal(src, st, out, b"fn", na, nb, p0, 0); + z_mark_refused(src, st, na, nb); + } else { + puts(st, out, b"\n\n"); + st[(Z_NOK) as usize] = (st[(Z_NOK) as usize] + 1); + } + st[(ERR) as usize] = 0; +} + +pub fn z_linkage_item(src: &[u8], st: &mut [usize], out: &mut [u8], p0: usize, q: usize, end: usize) -> () { + st[(Z_NFN) as usize] = (st[(Z_NFN) as usize] + 1); + let na: usize = skip_ws(src, z_tok_end(src, q)); + let nb: usize = z_tok_end(src, na); + refuse(st, Z_R_LINKAGE, p0, p0, z_tok_end(src, p0)); + z_put_refusal(src, st, out, b"fn", na, nb, p0, 0); + z_mark_refused(src, st, na, nb); + st[(ERR) as usize] = 0; +} + +pub fn z_const_item(src: &[u8], st: &mut [usize], out: &mut [u8], p0: usize, q: usize, end: usize, is_pub: bool) -> () { + let na: usize = skip_ws(src, z_tok_end(src, q)); + let nb: usize = z_tok_end(src, na); + let w0: usize = st[(W) as usize]; + let eq: usize = find_at(src, nb, B_EQ); + let mut ip: usize = src.len(); + if (eq < src.len()) { + ip = skip_ws(src, (eq + 1)); + } + let ib: usize = z_tok_end(src, ip); + let kind: usize = z_find_file(src, st, na, nb); + if z_is(src, q, b"var") { + refuse(st, Z_R_GLOBALVAR, na, na, nb); + } else { + if (ip >= src.len()) { + refuse(st, Z_R_SYNTAX, na, na, nb); + } else { + if (word_is(src, ip, ib, b"@import") || word_is(src, ip, ib, b"std")) { + return (); + } else { + if ((kind != 0) && (st[((z_slot(kind) + 2)) as usize] == Z_K_NSPACE)) { + z_nspace_item(src, st, out, p0, na, nb, ip, kind); + return (); + } else { + if z_type_init(src, ip) { + let nf: usize = count_fns(src, p0, end); + st[(Z_NFN) as usize] = (st[(Z_NFN) as usize] + nf); + refuse(st, Z_R_TYPEDECL, ip, ip, ib); + z_put_refusal(src, st, out, b"const", na, nb, p0, nf); + st[(ERR) as usize] = 0; + return (); + } else { + if (z_scalar(src, ip, ib) && z_peek_is(src, ip, b";")) { + return (); + } else { + st[(P) as usize] = q; + if is_pub { + puts(st, out, b"pub "); + } + st[(Z_INCONST) as usize] = 1; + st[(Z_QUAL) as usize] = 1; + z_decl(src, st, out); + st[(Z_QUAL) as usize] = 0; + st[(Z_INCONST) as usize] = 0; + let last: usize = st[(Z_NNAMES) as usize]; + if (((kind != 0) && (last > st[(Z_NFILE) as usize])) && !(bad(st[(ERR) as usize]))) { + st[((z_slot(kind) + 3)) as usize] = st[((z_slot(last) + 3)) as usize]; + } + } + } + } + } + } + } + st[(Z_NNAMES) as usize] = st[(Z_NFILE) as usize]; + if bad(st[(ERR) as usize]) { + st[(W) as usize] = w0; + z_put_refusal(src, st, out, b"const", na, nb, p0, 0); + z_mark_refused(src, st, na, nb); + } else { + puts(st, out, b"\n\n"); + } + st[(ERR) as usize] = 0; +} + +pub fn z_nspace_item(src: &[u8], st: &mut [usize], out: &mut [u8], p0: usize, na: usize, nb: usize, ip: usize, e: usize) -> () { + let lb: usize = z_container_open(src, ip); + let close: usize = block_end(src, lb); + if (st[((z_slot(e) + 3)) as usize] == 1) { + refuse(st, Z_R_TYPEDECL, ip, ip, z_tok_end(src, ip)); + z_put_refusal(src, st, out, b"const", na, nb, p0, 0); + st[(ERR) as usize] = 0; + } + st[(Z_CUR) as usize] = e; + st[(P) as usize] = z_next_member(src, skip_ws(src, (lb + 1)), close); + while ((st[(P) as usize] + 1) < close) { + z_item(src, st, out); + st[(P) as usize] = z_next_member(src, st[(P) as usize], close); + } + st[(Z_CUR) as usize] = 0; +} + +pub fn z_out_has(out: &[u8], j: usize, s: &[u8]) -> bool { + if ((j + s.len()) > out.len()) { + return false; + } + let mut k: usize = 0; + while (k < s.len()) { + if (out[((j + k)) as usize] != s[(k) as usize]) { + return false; + } + k = (k + 1); + } + return true; +} + +pub fn z_test_taken(out: &[u8], w1: usize, w2: usize) -> bool { + if ((w2 > out.len()) || (w2 < w1)) { + return false; + } + let n: usize = (w2 - w1); + let mut j: usize = 0; + while ((((j + 5) + n) + 2) <= w1) { + if ((((j == 0) || (out[((j - 1)) as usize] == B_NL)) && z_out_has(out, j, b"test ")) && z_out_has(out, ((j + 5) + n), b" {")) { + let mut k: usize = 0; + while ((k < n) && (out[(((j + 5) + k)) as usize] == out[((w1 + k)) as usize])) { + k = (k + 1); + } + if (k == n) { + return true; + } + } + j = (j + 1); + } + return false; +} + +pub fn z_hash(out: &[u8], a: usize, b: usize) -> usize { + let mut h: usize = 2166136261; + let mut i: usize = a; + while ((i < b) && (i < out.len())) { + h = (((h ^ (out[(i) as usize] as usize)) * 16777619) % 4294967296); + i = (i + 1); + } + return h; +} + +pub fn z_test_seen(st: &mut [usize], out: &[u8], w1: usize, w2: usize) -> bool { + if (st[(Z_HASHON) as usize] == 0) { + return z_test_taken(out, w1, w2); + } + let h: usize = z_hash(out, w1, w2); + let base: usize = st[(Z_LIMIT) as usize]; + let mut k: usize = 0; + while (k < 16) { + let s: usize = (base + ((h + k) % Z_HSLOTS)); + if (st[(s) as usize] == 0) { + st[(s) as usize] = (h + 1); + return false; + } + if ((st[(s) as usize] == (h + 1)) && z_test_taken(out, w1, w2)) { + return true; + } + k = (k + 1); + } + return false; +} + +pub fn z_put_test_name(src: &[u8], st: &mut [usize], out: &mut [u8], na: usize, nb: usize, ord: usize) -> () { + let w1: usize = st[(W) as usize]; + let mut lo: usize = na; + let mut hi: usize = nb; + if ((head(src, na) == B_QUOTE) && (nb > (na + 1))) { + lo = (na + 1); + hi = (nb - 1); + } + let mut pending: bool = false; + let mut any: bool = false; + let mut i: usize = lo; + while (i < hi) { + let c: u8 = src[(i) as usize]; + if is_word(c) { + if pending { + put(st, out, B_UNDERSCORE); + } + pending = false; + if (!(any) && is_digit(c)) { + puts(st, out, b"t_"); + } + put(st, out, c); + any = true; + } else { + if any { + pending = true; + } + } + i = (i + 1); + } + if !(any) { + puts(st, out, b"t27o_test"); + } + if (any && reserved(src, lo, hi)) { + puts(st, out, b"_"); + } + if z_test_seen(st, out, w1, st[(W) as usize]) { + puts(st, out, b"_"); + put_num(st, out, ord); + } +} + +pub fn z_test_item(src: &[u8], st: &mut [usize], out: &mut [u8], p0: usize, q: usize, end: usize) -> () { + st[(Z_NTS) as usize] = (st[(Z_NTS) as usize] + 1); + let w0: usize = st[(W) as usize]; + st[(P) as usize] = skip_ws(src, z_tok_end(src, q)); + let na: usize = st[(P) as usize]; + let nb: usize = z_tok_end(src, na); + let mut labeled: bool = false; + if ((head(src, na) == B_QUOTE) || is_alpha(head(src, na))) { + labeled = true; + z_step(src, st); + } + puts(st, out, b"test "); + z_put_test_name(src, st, out, na, nb, st[(Z_NTS) as usize]); + puts(st, out, b" {"); + z_want(src, st, b"{"); + st[(Z_INTEST) as usize] = 1; + st[(IND) as usize] = 1; + z_stmts(src, st, out); + st[(IND) as usize] = 0; + st[(Z_INTEST) as usize] = 0; + newline(st, out); + puts(st, out, b"}"); + st[(Z_NNAMES) as usize] = st[(Z_NFILE) as usize]; + if bad(st[(ERR) as usize]) { + st[(W) as usize] = w0; + z_put_refusal(src, st, out, b"test", na, nb, p0, 0); + } else { + puts(st, out, b"\n\n"); + st[(Z_NTEST) as usize] = (st[(Z_NTEST) as usize] + 1); + } + st[(ERR) as usize] = 0; +} + +pub fn z_other_item(src: &[u8], st: &mut [usize], out: &mut [u8], p0: usize, q: usize, end: usize) -> () { + let kb: usize = z_tok_end(src, q); + let nf: usize = count_fns(src, p0, end); + st[(Z_NFN) as usize] = (st[(Z_NFN) as usize] + nf); + refuse(st, Z_R_ITEM, q, q, kb); + z_put_refusal(src, st, out, b"top-level", q, kb, p0, nf); + st[(ERR) as usize] = 0; +} + +pub fn z_item(src: &[u8], st: &mut [usize], out: &mut [u8]) -> () { + let p0: usize = st[(P) as usize]; + let mut end: usize = z_item_end(src, p0); + if (end <= p0) { + end = (p0 + 1); + } + let mut q: usize = p0; + let mut is_pub: bool = false; + let mut linkage: bool = false; + let mut guard: usize = 0; + while ((guard < 6) && z_mod_word(src, q)) { + if z_is(src, q, b"pub") { + is_pub = true; + } else { + linkage = true; + } + q = skip_ws(src, z_tok_end(src, q)); + if (head(src, q) == B_QUOTE) { + q = skip_ws(src, z_tok_end(src, q)); + } + guard = (guard + 1); + } + st[(ERR) as usize] = 0; + st[(IND) as usize] = 0; + if z_is(src, q, b"fn") { + if linkage { + z_linkage_item(src, st, out, p0, q, end); + } else { + z_fn_item(src, st, out, p0, q, end, is_pub); + } + } else { + if (z_is(src, q, b"const") || z_is(src, q, b"var")) { + z_const_item(src, st, out, p0, q, end, is_pub); + } else { + if (z_is(src, q, b"test") && !(is_pub)) { + z_test_item(src, st, out, p0, q, end); + } else { + z_other_item(src, st, out, p0, q, end); + } + } + } + st[(P) as usize] = skip_ws(src, end); +} + +pub fn is_zig_name(name: &[u8]) -> bool { + if (name.len() < 4) { + return false; + } + let f: usize = (name.len() - 4); + return ((((name[(f) as usize] == B_DOT) && (name[((f + 1)) as usize] == B_LOWER_Z)) && (name[((f + 2)) as usize] == Z_B_I)) && (name[((f + 3)) as usize] == Z_B_G)); +} + +pub fn z_ci_at(src: &[u8], i: usize, s: &[u8]) -> bool { + if ((i + s.len()) > src.len()) { + return false; + } + let mut k: usize = 0; + while (k < s.len()) { + if (z_lower(src[((i + k)) as usize]) != s[(k) as usize]) { + return false; + } + k = (k + 1); + } + return true; +} + +pub fn z_generated(src: &[u8]) -> bool { + let mut lines: usize = 0; + let mut i: usize = 0; + while ((i < src.len()) && (lines < 20)) { + if (src[(i) as usize] == B_NL) { + lines = (lines + 1); + } + if ((z_ci_at(src, i, b".vibee") || z_ci_at(src, i, b"generated")) || z_ci_at(src, i, b"do not edit")) { + return true; + } + i = (i + 1); + } + return false; +} + +pub fn z_put_module(st: &mut [usize], out: &mut [u8], name: &[u8]) -> () { + if (name.len() == 0) { + puts(st, out, b"Converted"); + } + let mut i: usize = 0; + while (i < name.len()) { + let c: u8 = name[(i) as usize]; + if (is_word(c) || (c == B_MINUS)) { + put(st, out, c); + } else { + put(st, out, B_UNDERSCORE); + } + i = (i + 1); + } +} + +pub fn z_header(st: &mut [usize], out: &mut [u8], name: &[u8]) -> () { + puts(st, out, b"// SPDX-License-Identifier: Apache-2.0\n; "); + z_put_module(st, out, name); + puts(st, out, b".t27 -- converted from Zig by t27o (Ouroboros), specs/tools/t27o/zig_convert.t27; read it before it lands.\n; ASCII only (L3)\n\nmodule "); + z_put_module(st, out, name); + puts(st, out, b";\n\n"); +} + +pub fn z_report(st: &mut [usize], out: &mut [u8]) -> () { + puts(st, out, b"// t27o: "); + put_num(st, out, st[(Z_NFN) as usize]); + puts(st, out, b" fn seen, "); + put_num(st, out, st[(Z_NOK) as usize]); + puts(st, out, b" converted ("); + put_num(st, out, st[(Z_NTEST) as usize]); + puts(st, out, b" of "); + put_num(st, out, st[(Z_NTS) as usize]); + puts(st, out, b" tests), "); + put_num(st, out, (st[(Z_NFN) as usize] - st[(Z_NOK) as usize])); + puts(st, out, b" refused (0 kept as stubs); "); + if (st[(Z_NFN) as usize] == 0) { + puts(st, out, b"coverage unknown: no fn seen\n"); + return (); + } + puts(st, out, b"coverage "); + put_num(st, out, ((st[(Z_NOK) as usize] * 100) / st[(Z_NFN) as usize])); + puts(st, out, b"%\n"); +} + +pub fn z_pass(src: &[u8], name: &[u8], out: &mut [u8], st: &mut [usize], whole: bool) -> () { + st[(W) as usize] = 0; + st[(ERR) as usize] = 0; + st[(IND) as usize] = 0; + st[(Z_NFN) as usize] = 0; + st[(Z_NOK) as usize] = 0; + st[(Z_NTS) as usize] = 0; + st[(Z_NTEST) as usize] = 0; + st[(Z_TMP) as usize] = 0; + st[(Z_INTEST) as usize] = 0; + st[(Z_GREW) as usize] = 0; + st[(Z_NNAMES) as usize] = st[(Z_NFILE) as usize]; + st[(Z_CUR) as usize] = 0; + st[(Z_QUAL) as usize] = 0; + st[(Z_INCONST) as usize] = 0; + st[(Z_ONE) as usize] = 0; + st[(Z_OWNER) as usize] = 0; + st[(LNPOS) as usize] = 0; + st[(LNNL) as usize] = 0; + if (st[(Z_HASHON) as usize] == 1) { + let mut h: usize = st[(Z_LIMIT) as usize]; + while (h < st.len()) { + st[(h) as usize] = 0; + h = (h + 1); + } + } + if whole { + z_header(st, out, name); + } + st[(P) as usize] = skip_ws(src, 0); + while (st[(P) as usize] < src.len()) { + z_item(src, st, out); + } + if whole { + z_report(st, out); + } +} + +pub fn z_passes(src: &[u8], name: &[u8], out: &mut [u8], st: &mut [usize], whole: bool) -> usize { + if (st.len() < Z_NAMES) { + return 0; + } + let mut i: usize = 0; + while (i < Z_FIXED) { + st[(i) as usize] = 0; + i = (i + 1); + } + st[(Z_LIMIT) as usize] = st.len(); + if (st.len() >= ((Z_NAMES + 4096) + Z_HSLOTS)) { + st[(Z_LIMIT) as usize] = (st.len() - Z_HSLOTS); + st[(Z_HASHON) as usize] = 1; + } + if z_generated(src) { + if whole { + z_header(st, out, name); + } + puts(st, out, b"// t27o: refused file (line 1): generated; conv"); + puts(st, out, b"ert its source spec instead\n\n"); + if whole { + z_report(st, out); + } + return st[(W) as usize]; + } + z_prescan(src, st); + if (st[(Z_CRAMPED) as usize] == 1) { + if whole { + z_header(st, out, name); + } + puts(st, out, b"// t27o: refused file (line 1): name table full\n\n"); + if whole { + z_report(st, out); + } + return st[(W) as usize]; + } + let mut round: usize = 1; + let mut again: bool = true; + while (again && (round <= 64)) { + st[(Z_ROUND) as usize] = round; + z_pass(src, name, out, st, whole); + again = ((round < 2) || (st[(Z_GREW) as usize] > 0)); + round = (round + 1); + } + return st[(W) as usize]; +} + +pub fn z_body_text(src: &[u8], out: &mut [u8], st: &mut [usize]) -> usize { + return z_passes(src, b"", out, st, false); +} + +pub fn to_t27(src: &[u8], name: &[u8], out: &mut [u8], st: &mut [usize]) -> usize { + return z_passes(src, name, out, st, true); +} + +pub const AT: usize = 3; + +pub const B_AMP: u8 = 38; + +pub const B_APOS: u8 = 39; + +pub const B_BACKSLASH: u8 = 92; + +pub const B_BANG: u8 = 33; + +pub const B_CARET: u8 = 94; + +pub const B_COLON: u8 = 58; + +pub const B_COMMA: u8 = 44; + +pub const B_CR: u8 = 13; + +pub const B_DEL: u8 = 127; + +pub const B_DOT: u8 = 46; + +pub const B_EQ: u8 = 61; + +pub const B_GT: u8 = 62; + +pub const B_HASH: u8 = 35; + +pub const B_LBRACE: u8 = 123; + +pub const B_LBRACKET: u8 = 91; + +pub const B_LOWER_A: u8 = 97; + +pub const B_LOWER_B: u8 = 98; + +pub const B_LOWER_E: u8 = 101; + +pub const B_LOWER_N: u8 = 110; + +pub const B_LOWER_O: u8 = 111; + +pub const B_LOWER_R: u8 = 114; + +pub const B_LOWER_T: u8 = 116; + +pub const B_LOWER_X: u8 = 120; + +pub const B_LOWER_Z: u8 = 122; + +pub const B_LPAREN: u8 = 40; + +pub const B_LT: u8 = 60; + +pub const B_MINUS: u8 = 45; + +pub const B_NINE: u8 = 57; + +pub const B_NL: u8 = 10; + +pub const B_PCT: u8 = 37; + +pub const B_PIPE: u8 = 124; + +pub const B_PLUS: u8 = 43; + +pub const B_QMARK: u8 = 63; + +pub const B_QUOTE: u8 = 34; + +pub const B_RBRACE: u8 = 125; + +pub const B_RBRACKET: u8 = 93; + +pub const B_RPAREN: u8 = 41; + +pub const B_SEMI: u8 = 59; + +pub const B_SLASH: u8 = 47; + +pub const B_SPACE: u8 = 32; + +pub const B_STAR: u8 = 42; + +pub const B_TAB: u8 = 9; + +pub const B_UNDERSCORE: u8 = 95; + +pub const B_UPPER_A: u8 = 65; + +pub const B_UPPER_E: u8 = 69; + +pub const B_UPPER_Z: u8 = 90; + +pub const B_ZERO: u8 = 48; + +pub const ERR: usize = 2; + +pub const IND: usize = 6; + +pub const LNNL: usize = 52; + +pub const LNPOS: usize = 51; + +pub const L_ADD: usize = 10; + +pub const L_AND: usize = 4; + +pub const L_AS: usize = 12; + +pub const L_ATOM: usize = 99; + +pub const L_BAND: usize = 8; + +pub const L_BOR: usize = 6; + +pub const L_CMP: usize = 5; + +pub const L_MUL: usize = 11; + +pub const L_OR: usize = 3; + +pub const L_SHIFT: usize = 9; + +pub const L_UNARY: usize = 13; + +pub const L_XOR: usize = 7; + +pub const NHI: usize = 5; + +pub const NLO: usize = 4; + +pub const P: usize = 0; + +pub const W: usize = 1; + +pub fn bad(e: usize) -> bool { + return (e != 0); +} + +pub fn block_end(src: &[u8], o: usize) -> usize { + let mut depth: usize = 0; + let mut q: usize = o; + while (q < src.len()) { + let e: usize = tok_end(src, q); + if ((e == (q + 1)) && is_open(src[(q) as usize])) { + depth = (depth + 1); + } + if ((e == (q + 1)) && is_close(src[(q) as usize])) { + if (depth <= 1) { + return e; + } + depth = (depth - 1); + } + q = skip_ws(src, e); + } + return src.len(); +} + +pub fn count_fns(src: &[u8], lo: usize, hi: usize) -> usize { + let mut n: usize = 0; + let mut q: usize = lo; + while (q < hi) { + let e: usize = tok_end(src, q); + if word_is(src, q, e, b"fn") { + let k: usize = skip_ws(src, e); + if ((k < src.len()) && is_alpha(src[(k) as usize])) { + n = (n + 1); + } + } + q = skip_ws(src, e); + } + return n; +} + +pub fn find_at(src: &[u8], from: usize, c: u8) -> usize { + let mut depth: usize = 0; + let mut q: usize = from; + while (q < src.len()) { + let e: usize = tok_end(src, q); + if (e == (q + 1)) { + if ((src[(q) as usize] == c) && (depth == 0)) { + return q; + } + if is_open(src[(q) as usize]) { + depth = (depth + 1); + } + if is_close(src[(q) as usize]) { + if (depth == 0) { + return src.len(); + } + depth = (depth - 1); + } + } + q = skip_ws(src, e); + } + return src.len(); +} + +pub fn head(src: &[u8], p: usize) -> u8 { + if (p >= src.len()) { + return 0; + } + return src[(p) as usize]; +} + +pub fn insert(st: &mut [usize], out: &mut [u8], pos: usize, s: &[u8]) -> () { + if open_gap(st, out, pos, s.len()) { + let mut k: usize = 0; + while (k < s.len()) { + out[((pos + k)) as usize] = s[(k) as usize]; + k = (k + 1); + } + } +} + +pub fn is_alpha(c: u8) -> bool { + return ((((c >= B_LOWER_A) && (c <= B_LOWER_Z)) || is_upper(c)) || (c == B_UNDERSCORE)); +} + +pub fn is_close(c: u8) -> bool { + return (((c == B_RPAREN) || (c == B_RBRACKET)) || (c == B_RBRACE)); +} + +pub fn is_digit(c: u8) -> bool { + return ((c >= B_ZERO) && (c <= B_NINE)); +} + +pub fn is_open(c: u8) -> bool { + return (((c == B_LPAREN) || (c == B_LBRACKET)) || (c == B_LBRACE)); +} + +pub fn is_space(c: u8) -> bool { + return ((((c == B_SPACE) || (c == B_TAB)) || (c == B_NL)) || (c == B_CR)); +} + +pub fn is_upper(c: u8) -> bool { + return ((c >= B_UPPER_A) && (c <= B_UPPER_Z)); +} + +pub fn is_word(c: u8) -> bool { + return (is_alpha(c) || is_digit(c)); +} + +pub fn keep_bare(child: usize, parent: usize, left: bool) -> bool { + if ((child == L_ATOM) || (child == L_UNARY)) { + return true; + } + if (child == parent) { + return ((left && (parent != L_CMP)) && (parent != L_AS)); + } + if ((((child == L_BOR) || (child == L_XOR)) || (child == L_BAND)) || (child == L_AS)) { + return false; + } + if (((parent == L_BOR) || (parent == L_XOR)) || (parent == L_BAND)) { + return false; + } + return (child > parent); +} + +pub fn line_at(src: &[u8], st: &mut [usize], pos: usize) -> usize { + let mut i: usize = 0; + let mut n: usize = 0; + if (st[(LNPOS) as usize] <= pos) { + i = st[(LNPOS) as usize]; + n = st[(LNNL) as usize]; + } + let mut end: usize = pos; + if (end > src.len()) { + end = src.len(); + } + while (i < end) { + if (src[(i) as usize] == B_NL) { + n = (n + 1); + } + i = (i + 1); + } + st[(LNPOS) as usize] = end; + st[(LNNL) as usize] = n; + return (n + 1); +} + +pub fn newline(st: &mut [usize], out: &mut [u8]) -> () { + put(st, out, B_NL); + let mut k: usize = 0; + while (k < st[(IND) as usize]) { + puts(st, out, b" "); + k = (k + 1); + } +} + +pub fn num_end(src: &[u8], p: usize) -> usize { + let mut e: usize = (p + 1); + if (((src[(p) as usize] == B_ZERO) && (e < src.len())) && (((src[(e) as usize] == B_LOWER_X) || (src[(e) as usize] == B_LOWER_O)) || (src[(e) as usize] == B_LOWER_B))) { + e = (e + 1); + while ((e < src.len()) && is_word(src[(e) as usize])) { + e = (e + 1); + } + return e; + } + while ((e < src.len()) && (is_digit(src[(e) as usize]) || (src[(e) as usize] == B_UNDERSCORE))) { + e = (e + 1); + } + if ((((e + 1) < src.len()) && (src[(e) as usize] == B_DOT)) && is_digit(src[((e + 1)) as usize])) { + e = (e + 1); + while ((e < src.len()) && (is_digit(src[(e) as usize]) || (src[(e) as usize] == B_UNDERSCORE))) { + e = (e + 1); + } + } + if ((e < src.len()) && ((src[(e) as usize] == B_LOWER_E) || (src[(e) as usize] == B_UPPER_E))) { + let mut x: usize = (e + 1); + if ((x < src.len()) && ((src[(x) as usize] == B_PLUS) || (src[(x) as usize] == B_MINUS))) { + x = (x + 1); + } + if ((x < src.len()) && is_digit(src[(x) as usize])) { + e = x; + while ((e < src.len()) && (is_digit(src[(e) as usize]) || (src[(e) as usize] == B_UNDERSCORE))) { + e = (e + 1); + } + } + } + while ((e < src.len()) && is_word(src[(e) as usize])) { + e = (e + 1); + } + return e; +} + +pub fn op2(src: &[u8], p: usize) -> bool { + let a: u8 = src[(p) as usize]; + let b: u8 = src[((p + 1)) as usize]; + if (b == B_EQ) { + return ((((((((((((a == B_EQ) || (a == B_BANG)) || (a == B_LT)) || (a == B_GT)) || (a == B_PLUS)) || (a == B_MINUS)) || (a == B_STAR)) || (a == B_SLASH)) || (a == B_PCT)) || (a == B_CARET)) || (a == B_AMP)) || (a == B_PIPE)); + } + if ((a == B_COLON) && (b == B_COLON)) { + return true; + } + if (((a == B_MINUS) || (a == B_EQ)) && (b == B_GT)) { + return true; + } + if ((a == b) && (((((a == B_AMP) || (a == B_PIPE)) || (a == B_LT)) || (a == B_GT)) || (a == B_DOT))) { + return true; + } + return false; +} + +pub fn op3(src: &[u8], p: usize) -> bool { + let a: u8 = src[(p) as usize]; + let b: u8 = src[((p + 1)) as usize]; + let c: u8 = src[((p + 2)) as usize]; + if ((((a == B_LT) || (a == B_GT)) && (b == a)) && (c == B_EQ)) { + return true; + } + return (((a == B_DOT) && (b == B_DOT)) && ((c == B_EQ) || (c == B_DOT))); +} + +pub fn open_gap(st: &mut [usize], out: &mut [u8], pos: usize, n: usize) -> bool { + let w: usize = st[(W) as usize]; + let grown: usize = (w + n); + st[(W) as usize] = grown; + if (grown > out.len()) { + return false; + } + let mut i: usize = w; + while (i > pos) { + i = (i - 1); + out[((i + n)) as usize] = out[(i) as usize]; + } + return true; +} + +pub fn out_is(w: usize, out: &[u8], from: usize, s: &[u8]) -> bool { + if (((w < from) || ((w - from) != s.len())) || (w > out.len())) { + return false; + } + let mut k: usize = 0; + while (k < s.len()) { + if (out[((from + k)) as usize] != s[(k) as usize]) { + return false; + } + k = (k + 1); + } + return true; +} + +pub fn put(st: &mut [usize], out: &mut [u8], c: u8) -> () { + let w: usize = st[(W) as usize]; + if (w < out.len()) { + out[(w) as usize] = c; + } + st[(W) as usize] = (w + 1); +} + +pub fn put_ident(src: &[u8], st: &mut [usize], out: &mut [u8], a: usize, b: usize) -> () { + put_src(src, st, out, a, b); + if reserved(src, a, b) { + puts(st, out, b"_"); + } +} + +pub fn put_num(st: &mut [usize], out: &mut [u8], v: usize) -> () { + if (v >= 10) { + put_num(st, out, (v / 10)); + } + put(st, out, (B_ZERO + ((v % 10) as u8))); +} + +pub fn put_src(src: &[u8], st: &mut [usize], out: &mut [u8], a: usize, b: usize) -> () { + let mut i: usize = a; + while ((i < b) && (i < src.len())) { + put(st, out, src[(i) as usize]); + i = (i + 1); + } +} + +pub fn put_text(src: &[u8], st: &mut [usize], out: &mut [u8], a: usize, b: usize, max: usize) -> () { + let mut i: usize = a; + while (((i < b) && (i < src.len())) && (i < (a + max))) { + let c: u8 = src[(i) as usize]; + if ((c >= B_SPACE) && (c < B_DEL)) { + put(st, out, c); + } else { + if ((c < 128) || (c >= 192)) { + put(st, out, 63); + } + } + i = (i + 1); + } +} + +pub fn puts(st: &mut [usize], out: &mut [u8], s: &[u8]) -> () { + let mut i: usize = 0; + while (i < s.len()) { + put(st, out, s[(i) as usize]); + i = (i + 1); + } +} + +pub fn quoted_end(src: &[u8], q: usize) -> usize { + let quote: u8 = src[(q) as usize]; + let mut p: usize = (q + 1); + while (p < src.len()) { + if (src[(p) as usize] == B_BACKSLASH) { + p = (p + 2); + } else { + if (src[(p) as usize] == quote) { + return (p + 1); + } else { + p = (p + 1); + } + } + } + return src.len(); +} + +pub fn raw_end(src: &[u8], r: usize) -> usize { + let mut p: usize = (r + 1); + let mut hashes: usize = 0; + while ((p < src.len()) && (src[(p) as usize] == B_HASH)) { + hashes = (hashes + 1); + p = (p + 1); + } + if ((p >= src.len()) || (src[(p) as usize] != B_QUOTE)) { + return (r + 1); + } + p += 1; + while (p < src.len()) { + if (src[(p) as usize] == B_QUOTE) { + let mut k: usize = 0; + while (((k < hashes) && (((p + 1) + k) < src.len())) && (src[(((p + 1) + k)) as usize] == B_HASH)) { + k = (k + 1); + } + if (k == hashes) { + return ((p + 1) + hashes); + } + } + p = (p + 1); + } + return src.len(); +} + +pub fn refuse(st: &mut [usize], code: usize, pos: usize, lo: usize, hi: usize) -> () { + if (st[(ERR) as usize] == 0) { + st[(ERR) as usize] = code; + st[(AT) as usize] = pos; + st[(NLO) as usize] = lo; + st[(NHI) as usize] = hi; + } +} + +pub fn reserved(src: &[u8], a: usize, b: usize) -> bool { + return ((((((((((((((((((((((((((word_is(src, a, b, b"and") || word_is(src, a, b, b"or")) || word_is(src, a, b, b"test")) || word_is(src, a, b, b"var")) || word_is(src, a, b, b"error")) || word_is(src, a, b, b"null")) || word_is(src, a, b, b"undefined")) || word_is(src, a, b, b"switch")) || word_is(src, a, b, b"orelse")) || word_is(src, a, b, b"try")) || word_is(src, a, b, b"defer")) || word_is(src, a, b, b"comptime")) || word_is(src, a, b, b"module")) || word_is(src, a, b, b"invariant")) || word_is(src, a, b, b"bench")) || word_is(src, a, b, b"catch")) || word_is(src, a, b, b"unreachable")) || word_is(src, a, b, b"anytype")) || word_is(src, a, b, b"opaque")) || word_is(src, a, b, b"packed")) || word_is(src, a, b, b"export")) || word_is(src, a, b, b"inline")) || word_is(src, a, b, b"align")) || word_is(src, a, b, b"noreturn")) || word_is(src, a, b, b"errdefer")) || word_is(src, a, b, b"spec")) || word_is(src, a, b, b"void")); +} + +pub fn same_at(src: &[u8], q: usize, e: usize, a: usize) -> bool { + let mut k: usize = 0; + while ((q + k) < e) { + if (((a + k) >= src.len()) || (src[((q + k)) as usize] != src[((a + k)) as usize])) { + return false; + } + k = (k + 1); + } + return true; +} + +pub fn same_text(src: &[u8], a: usize, b: usize, c: usize, d: usize) -> bool { + if (((b < a) || (d < c)) || ((b - a) != (d - c))) { + return false; + } + return same_at(src, a, b, c); +} + +pub fn skip_ws(src: &[u8], from: usize) -> usize { + let mut p: usize = from; + while (p < src.len()) { + let c: u8 = src[(p) as usize]; + if is_space(c) { + p = (p + 1); + } else { + if (((c == B_SLASH) && ((p + 1) < src.len())) && (src[((p + 1)) as usize] == B_SLASH)) { + while ((p < src.len()) && (src[(p) as usize] != B_NL)) { + p = (p + 1); + } + } else { + if (((c == B_SLASH) && ((p + 1) < src.len())) && (src[((p + 1)) as usize] == B_STAR)) { + let mut depth: usize = 1; + p = (p + 2); + while ((p < src.len()) && (depth > 0)) { + if (((src[(p) as usize] == B_STAR) && ((p + 1) < src.len())) && (src[((p + 1)) as usize] == B_SLASH)) { + depth = (depth - 1); + p = (p + 2); + } else { + if (((src[(p) as usize] == B_SLASH) && ((p + 1) < src.len())) && (src[((p + 1)) as usize] == B_STAR)) { + depth = (depth + 1); + p = (p + 2); + } else { + p = (p + 1); + } + } + } + } else { + return p; + } + } + } + } + return p; +} + +pub fn tok_end(src: &[u8], p: usize) -> usize { + if (p >= src.len()) { + return p; + } + let c: u8 = src[(p) as usize]; + if (((c == B_LOWER_B) && ((p + 1) < src.len())) && ((src[((p + 1)) as usize] == B_QUOTE) || (src[((p + 1)) as usize] == B_APOS))) { + return quoted_end(src, (p + 1)); + } + if (((c == B_LOWER_R) && ((p + 1) < src.len())) && ((src[((p + 1)) as usize] == B_QUOTE) || (src[((p + 1)) as usize] == B_HASH))) { + return raw_end(src, p); + } + if is_alpha(c) { + let mut e: usize = (p + 1); + while ((e < src.len()) && is_word(src[(e) as usize])) { + e = (e + 1); + } + return e; + } + if is_digit(c) { + return num_end(src, p); + } + if (c == B_QUOTE) { + return quoted_end(src, p); + } + if (c == B_APOS) { + if (((p + 1) < src.len()) && (src[((p + 1)) as usize] == B_BACKSLASH)) { + return quoted_end(src, p); + } + if (((p + 2) < src.len()) && (src[((p + 2)) as usize] == B_APOS)) { + return (p + 3); + } + let mut e: usize = (p + 1); + while ((e < src.len()) && is_word(src[(e) as usize])) { + e = (e + 1); + } + return e; + } + if (((p + 2) < src.len()) && op3(src, p)) { + return (p + 3); + } + if (((p + 1) < src.len()) && op2(src, p)) { + return (p + 2); + } + return (p + 1); +} + +pub fn word_is(src: &[u8], a: usize, b: usize, s: &[u8]) -> bool { + if ((b < a) || ((b - a) != s.len())) { + return false; + } + let mut k: usize = 0; + while (k < s.len()) { + if (src[((a + k)) as usize] != s[(k) as usize]) { + return false; + } + k = (k + 1); + } + return true; +} + +pub fn wrap(st: &mut [usize], out: &mut [u8], from: usize) -> () { + insert(st, out, from, b"("); + puts(st, out, b")"); +} + diff --git a/specs/tools/t27o/zig_convert.t27 b/specs/tools/t27o/zig_convert.t27 new file mode 100644 index 0000000000..8c673a0eff --- /dev/null +++ b/specs/tools/t27o/zig_convert.t27 @@ -0,0 +1,3377 @@ +// SPDX-License-Identifier: Apache-2.0 +// specs/tools/t27o/zig_convert.t27 -- t27o (Ouroboros), its Zig front end (#10765, step 1 of #10597, option B of #10522): +// hand-written Zig becomes a t27 spec, judged by the file's own `test` blocks. It sits beside rust_convert.t27 and +// spice_convert.t27 and is reached by file extension: `tri t27o file.zig` prints the spec and the count line. +// -- +// The contract is the Rust front end's: an item converts exactly or is refused with its reason, never approximated. A +// refused item is a comment where it stood, `// t27o: refused fn NAME (line N): `` at line M`, and the last +// line counts: `// t27o: N fn seen, M converted (a of b tests), R refused (0 kept as stubs); coverage P%`. No refused fn +// is kept as a stub: whatever calls one is refused with it, in passes until no new name is refused ("uses refused `f`"), +// so nothing that converted reaches a missing fn. A file with a generator marker (`.vibee`, "generated", "do not edit", +// any case) in its first 20 lines is refused whole: "generated; convert its source spec instead" (the owner's rule of +// #10597, 2026-10-19). The caller passes the source, an output buffer and a state vector; the result is how many bytes +// the output needs, above out.len when the buffer was too small. The state vector holds Z_FIXED slots (7 of them are +// rust_convert's, its put, newline and refuse read them), then the name table (Z_NAME_SLOTS a name, as many as fit); +// a vector of 12416 slots or more also keeps a table of test-name hashes at its tail, so that a file of 20000 tests +// is one scan and not 20000. +// -- +// What converts (step 1; scalar and slice leaf fns, #10597): +// - Items: `fn` and `pub fn`; file-level `const`s of a scalar type, of a literal or of other consts (`const A = 4;`, +// `const B: f64 = std.math.pi * 2.0;`), and of an array literal (`const T = [_]u8{ 1, 2, 3 };` is +// `const T : [3]u8 = [1, 2, 3];`); `test "name" { .. }`. `const std = @import("std");` and `const math = std.math;` leave +// no trace: the table knows them as namespaces (std, math, mem, testing, ascii) and `math.pi`, `std.math.pi` mean the same. +// - Containers: the fns and consts of a `struct`, `enum` or `union` are read one by one and named `Container_name` +// (`Optimizer.bitLength(n)` and a bare `bitLength(n)` inside it are `Optimizer_bitLength(n)`), as the Rust front end names +// a method `Type_method`. A container that holds only fns and consts is a namespace and leaves no comment; one with a +// field, a variant, a var or a nested type is a type, refused as such (`refused const Point .. type declaration`), and +// its static fns still convert while each method is refused for the type it takes. A container inside a container is +// a type, not read further. +// - Types: u8 .. u64, i8 .. i64, usize, isize, bool, f32, f64, void; `[]T` and `[]const T` of those; `[N]T`. +// - Statements: const and var, assignment and compound assignment, if / else if / else (braces are always written; an +// unbraced `if (c) x = 1 else x = 2;` is read), while with its `: (step)`, for over a slice, a range or several objects, +// switch, return, break, continue, `_ = x;`, a call. +// - Expressions: literals, the operators of Zig with its precedence (a parenthesis wherever t27 might read a bitwise +// operator differently), `+% -% *%`, calls of converted fns of the file, `s[i]`, `s[a..b]`, `s.len`, `if (c) a else b`, +// `switch (e) { 1 => a, else => b }`, `@as` `@intCast` `@floatCast` `@floatFromInt` `@intFromFloat` `@intFromBool` `@sqrt` +// `@floor` `@ceil` `@round` `@exp` `@log` `@cos` `@abs` `@min` `@max` `@divTrunc` `@rem` `@mod`, and std.math.pi / e / tau / phi +// / sqrt2 / ln2 ..., maxInt / minInt (written as the literal), sqrt / floor / ceil / exp / cos (the builtin of the same +// name), isNan / isPositiveInf / isNegativeInf / inf / nan, std.mem.eql(u8, a, b) and std.debug.assert(c) (an `assert(c)`). +// - Tests: `try std.testing.expectEqual(e, a)` is `assert(a == e)`, `expect(c)` is `assert(c)`, `expectApproxEqAbs(e, a, t)` +// is `assert(@abs(a - e) <= t)` (Zig also passes two equal infinities; this does not), `expectApproxEqRel(e, a, t)` is +// `assert(@abs(a - e) <= @max(@abs(a), @abs(e)) * t)`, `expectEqualStrings(e, a)` and `expectEqualSlices(u8, e, a)` are +// `assert(std.mem.eql(u8, a, e))`. A test is `test { .. }`; names that collide get their ordinal. +// A test that calls a refused fn is refused with it. The tests are the oracle: `t27b test` runs them on the converted fns. +// -- +// Where t27 is not Zig, and what is done about it. Each was measured under t27b AND t27c (Zig 0.16 backend) on 2026-10-19: +// - `while (c) : (step)` is kept, not moved to the end of the body (a `continue` would skip a moved step). t27 reads it as Zig. +// - `<<=` `>>=` `+%=` `-%=` `*%=` are parse errors in t27: `x = x << e` is written (the left side is written again). +// - `for (s, 0..) |x, i|` and `for (a, b) |x, y|` are parse errors: `for (0..s.len) |i| { const x = s[i]; .. }`. +// - A switch statement is silently dropped by t27c; it is an if chain over the scrutinee (bound once to `t27o_sN` unless it +// is a name). A switch expression stays a switch when every prong is one literal or const; t27 has no prong list and no +// range there, and t27c prints a char prong in a form Zig does not parse (#6329), so those are refused. +// - `[_]T{ .. }` is not an expression in t27 (`[3]u32 = [1, 2, 3]` is): a declaration is written with its type, and an +// `&[_]T{ .. }` argument is declared on the line ahead of its statement (`const t27o_a1 : [3]u32 = [1, 2, 3];`). +// - `0o17` has no t27 spelling (written in decimal); a string has no \r \x \u escape and a char no \x (refused). +// - `_ = x;` is a no-op for Zig's unused-name error; t27b reads it for a parameter and refuses it for a local or a const, +// neither backend minds an unused name, so it is dropped for anything but a parameter. +// - What t27b cannot run is refused with the call: an integer `@abs` (Zig gives a uN), an integer `@min` / `@max` beside a +// literal (Zig narrows the result type), a float `@rem` / `@mod` / `@divTrunc`, a literal operand of `@exp` `@floor` +// `@ceil` `@round` `@cos` (Zig types it from the result location), a builtin whose operand is not a literal inside a +// file-level const, and an `@intCast` that is a shift count. `@truncate` `@bitCast` `@sin` `@log2` `@Vector` and the like have no +// t27b routine and are refused by name, as are `std.math.pow` / log / clamp / maxInt of a non-integer, `std.mem.startsWith` +// and the rest of std: "std call `std.math.pow`". +// -- +// Refused, with the kind a sweep ranks them by: pointers and `.*`, optionals and `.?` / orelse, error unions / try / catch / +// error values, types other than the scalars above (structs, enums, unions as values, u128, arbitrary-width ints), comptime and +// anytype, payload captures, labels, defer, unreachable, anonymous literals, export / extern / inline fns, mutable +// file-level vars, a const that is another file's, names nobody declares, bare braces as a statement. Step 2 of #10597 +// reads same-file structs / enums and `*T` self methods; step 3 error unions. +// -- +// Three t27c behaviours shaped this spec: `use` splices by NAME (every word outside a `//` comment, string literals +// included, that is a declaration of rust_convert, so no word of a message here is one: "conv" "ert" are written +// in two pieces, and the loop variable is not called `named`); a slice parameter a fn only reads becomes an +// `impl AsRef` in the generated Rust (every fn here that takes `st` writes it or passes it on), and `f(.., g(.., st))` +// borrows st twice (the line number is bound first). +// ASCII only (L3) +// phi^2 + 1/phi^2 = 3 | TRINITY + +module T27oZig; + +use tools::t27o::rust_convert::put; +use tools::t27o::rust_convert::puts; +use tools::t27o::rust_convert::put_num; +use tools::t27o::rust_convert::put_text; +use tools::t27o::rust_convert::put_src; +use tools::t27o::rust_convert::put_ident; +use tools::t27o::rust_convert::newline; +use tools::t27o::rust_convert::is_digit; +use tools::t27o::rust_convert::is_alpha; +use tools::t27o::rust_convert::is_word; +use tools::t27o::rust_convert::skip_ws; +use tools::t27o::rust_convert::tok_end; +use tools::t27o::rust_convert::word_is; +use tools::t27o::rust_convert::same_text; +use tools::t27o::rust_convert::line_at; +use tools::t27o::rust_convert::out_is; +use tools::t27o::rust_convert::refuse; +use tools::t27o::rust_convert::bad; +use tools::t27o::rust_convert::head; +use tools::t27o::rust_convert::keep_bare; +use tools::t27o::rust_convert::wrap; +use tools::t27o::rust_convert::count_fns; +use tools::t27o::rust_convert::block_end; +use tools::t27o::rust_convert::find_at; +use tools::t27o::rust_convert::reserved; +use tools::t27o::rust_convert::is_upper; +use tools::t27o::rust_convert::is_open; +use tools::t27o::rust_convert::is_close; + +// ---------------------------------------------------------------- state + +/// Slots 0 .. 6 are rust_convert's (its put, newline and refuse read them): P the reader's position, W how much +/// is written, ERR the refusal, AT where it was found, NLO and NHI the source text it names, IND the indent. +/// They are used here by their names and not declared again. +pub const Z_NFN : usize = 8; +pub const Z_NOK : usize = 9; +pub const Z_NTS : usize = 10; +pub const Z_NTEST : usize = 11; +pub const Z_CLS : usize = 12; +pub const Z_STMT : usize = 13; +pub const Z_TMP : usize = 14; +pub const Z_INTEST : usize = 15; +pub const Z_NNAMES : usize = 16; +pub const Z_NFILE : usize = 17; +pub const Z_ROUND : usize = 18; +pub const Z_RET : usize = 19; +pub const Z_GREW : usize = 20; +pub const Z_HIT : usize = 21; +pub const Z_NARGS : usize = 22; +pub const Z_ARG0 : usize = 23; +pub const Z_ARG1 : usize = 24; +pub const Z_ARG2 : usize = 25; +pub const Z_CLOSE : usize = 26; +pub const Z_LIMIT : usize = 27; +pub const Z_HASHON : usize = 28; +pub const Z_INCONST : usize = 29; +pub const Z_CRAMPED : usize = 31; +pub const Z_ONE : usize = 32; +pub const Z_OWNER : usize = 33; +pub const Z_CUR : usize = 34; +pub const Z_QUAL : usize = 35; +/// Slots below Z_FIXED are not the name table's: mine from 8, and rust_convert's own (its line cache sits at 51 and 52). +pub const Z_FIXED : usize = 128; +pub const Z_NAMES : usize = 128; +/// The tail of the state vector, when it is long enough, is a table of the hashes of the test names written so far. +pub const Z_HSLOTS : usize = 8192; +pub const Z_NAME_SLOTS : usize = 5; + +/// The byte values rust_convert has no name for. +pub const Z_B_AT : u8 = 64; +pub const Z_B_TILDE : u8 = 126; +pub const Z_B_P : u8 = 112; +pub const Z_B_I : u8 = 105; +pub const Z_B_G : u8 = 103; + +// ---------------------------------------------------------------- tokens + +/// The end of the Zig token at src[p]. rust_convert's tok_end reads what Zig shares with Rust; Zig adds `@name`, +/// the wrapping operators `+%` `-%` `*%` (and their `=` forms), `++`, `**`, `.?` and `.*`, and its `\\` line +/// string, which stays one byte so that nothing reads it as a literal. +pub fn z_tok_end(src: []const u8, p: usize) -> usize { + if (p >= src.len) { return p; } + const c : u8 = src[p]; + const d : u8 = head(src, p + 1); + if (c == Z_B_AT) { + var e : usize = p + 1; + while (e < src.len and is_word(src[e])) { e = e + 1; } + return e; + } + if ((c == B_PLUS or c == B_MINUS or c == B_STAR) and d == B_PCT) { + if (head(src, p + 2) == B_EQ) { return p + 3; } + return p + 2; + } + if ((c == B_PLUS and d == B_PLUS) or (c == B_STAR and d == B_STAR)) { return p + 2; } + if (c == B_DOT and (d == B_QMARK or d == B_STAR)) { return p + 2; } + if (c == B_BACKSLASH) { return p + 1; } + return tok_end(src, p); +} + +/// Is the token at src[p] the text s? +pub fn z_is(src: []const u8, p: usize, s: []const u8) -> bool { + return word_is(src, p, z_tok_end(src, p), s); +} + +/// Move to the next token. +pub fn z_step(src: []const u8, st: []usize) -> void { + st[P] = skip_ws(src, z_tok_end(src, st[P])); +} + +/// Step over the token s when it is next. +pub fn z_take(src: []const u8, st: []usize, s: []const u8) -> bool { + if (z_is(src, st[P], s)) { + z_step(src, st); + return true; + } + return false; +} + +/// Refuse at the current token. +pub fn z_refuse_here(src: []const u8, st: []usize, code: usize) -> void { + refuse(st, code, st[P], st[P], z_tok_end(src, st[P])); +} + +/// Is the next token s? Anything else is a syntax refusal. +pub fn z_want(src: []const u8, st: []usize, s: []const u8) -> void { + if (!z_take(src, st, s)) { z_refuse_here(src, st, Z_R_SYNTAX); } +} + + +// ---------------------------------------------------------------- refusals + +pub const Z_R_SYNTAX : usize = 1; +pub const Z_R_GENERIC : usize = 2; +pub const Z_R_POINTER : usize = 3; +pub const Z_R_OPTIONAL : usize = 4; +pub const Z_R_ERRORS : usize = 5; +pub const Z_R_TYPE : usize = 6; +pub const Z_R_BUILTIN : usize = 7; +pub const Z_R_STDCALL : usize = 8; +pub const Z_R_FIELD : usize = 9; +pub const Z_R_DEPENDS : usize = 10; +pub const Z_R_EXTERNAL : usize = 11; +pub const Z_R_UNDECL : usize = 12; +pub const Z_R_LITERAL : usize = 13; +pub const Z_R_LINKAGE : usize = 14; +pub const Z_R_UNREACH : usize = 15; +pub const Z_R_PAYLOAD : usize = 16; +pub const Z_R_LABEL : usize = 17; +pub const Z_R_LOOPFORM : usize = 18; +pub const Z_R_SWITCH : usize = 19; +pub const Z_R_ANON : usize = 20; +pub const Z_R_ARRAYLIT : usize = 21; +pub const Z_R_OPERATOR : usize = 22; +pub const Z_R_DEFER : usize = 23; +pub const Z_R_BRACES : usize = 24; +pub const Z_R_GLOBALVAR : usize = 25; +pub const Z_R_TYPEDECL : usize = 26; +pub const Z_R_TESTCALL : usize = 27; +pub const Z_R_VALUE : usize = 28; +pub const Z_R_INDEX : usize = 29; +pub const Z_R_FULL : usize = 30; +pub const Z_R_ITEM : usize = 31; +pub const Z_R_BOPERAND : usize = 32; +pub const Z_R_HOIST : usize = 33; + +/// The reason of a refusal code, in words: the kind a sweep ranks refusals by (rust_convert's reasons read the same way). +pub fn z_put_why(st: []usize, out: []u8, code: usize) -> void { + if (code == Z_R_GENERIC) { puts(st, out, "comptime or generic"); return; } + if (code == Z_R_POINTER) { puts(st, out, "pointer"); return; } + if (code == Z_R_OPTIONAL) { puts(st, out, "optional"); return; } + if (code == Z_R_ERRORS) { puts(st, out, "error handling"); return; } + if (code == Z_R_TYPE) { puts(st, out, "type"); return; } + if (code == Z_R_BUILTIN) { puts(st, out, "builtin"); return; } + if (code == Z_R_STDCALL) { puts(st, out, "std call"); return; } + if (code == Z_R_FIELD) { puts(st, out, "field or call through a value"); return; } + if (code == Z_R_DEPENDS) { puts(st, out, "uses refused"); return; } + if (code == Z_R_EXTERNAL) { puts(st, out, "uses external"); return; } + if (code == Z_R_UNDECL) { puts(st, out, "undeclared"); return; } + if (code == Z_R_LITERAL) { puts(st, out, "literal"); return; } + if (code == Z_R_LINKAGE) { puts(st, out, "export, extern or inline fn"); return; } + if (code == Z_R_UNREACH) { puts(st, out, "unreachable or panic"); return; } + if (code == Z_R_PAYLOAD) { puts(st, out, "payload capture"); return; } + if (code == Z_R_LABEL) { puts(st, out, "label"); return; } + if (code == Z_R_LOOPFORM) { puts(st, out, "loop form"); return; } + if (code == Z_R_SWITCH) { puts(st, out, "switch form"); return; } + if (code == Z_R_ANON) { puts(st, out, "anonymous literal"); return; } + if (code == Z_R_ARRAYLIT) { puts(st, out, "array literal"); return; } + if (code == Z_R_OPERATOR) { puts(st, out, "operator"); return; } + if (code == Z_R_DEFER) { puts(st, out, "defer"); return; } + if (code == Z_R_BRACES) { puts(st, out, "bare braces statement"); return; } + if (code == Z_R_GLOBALVAR) { puts(st, out, "mutable file-level var"); return; } + if (code == Z_R_TYPEDECL) { puts(st, out, "type declaration"); return; } + if (code == Z_R_TESTCALL) { puts(st, out, "testing call"); return; } + if (code == Z_R_VALUE) { puts(st, out, "if, switch or loop as a value"); return; } + if (code == Z_R_INDEX) { puts(st, out, "indexing a value that is no slice or array"); return; } + if (code == Z_R_FULL) { puts(st, out, "name table full"); return; } + if (code == Z_R_ITEM) { puts(st, out, "declaration"); return; } + if (code == Z_R_BOPERAND) { puts(st, out, "builtin operand t27b cannot run"); return; } + if (code == Z_R_HOIST) { puts(st, out, "array literal outside a statement"); return; } + puts(st, out, "syntax"); +} + +// ---------------------------------------------------------------- classes + +/// What t27o knows about the value of an expression, enough to refuse what t27b would refuse (an integer `@abs`, +/// an integer `@min` beside a literal) and to name the element of a slice. A slice or array adds its element's class. +pub const ZC_NONE : usize = 0; +pub const ZC_INT : usize = 1; +pub const ZC_F64 : usize = 2; +pub const ZC_BOOL : usize = 3; +pub const ZC_VOID : usize = 4; +pub const ZC_LITINT : usize = 5; +pub const ZC_LITFLOAT : usize = 6; +pub const ZC_F32 : usize = 7; +pub const ZC_SLICE : usize = 16; +pub const ZC_ARRAY : usize = 32; + +pub fn z_is_float(c: usize) -> bool { + return c == ZC_F64 or c == ZC_F32 or c == ZC_LITFLOAT; +} + +pub fn z_is_int(c: usize) -> bool { + return c == ZC_INT or c == ZC_LITINT; +} + +/// A slice or an array. +pub fn z_is_seq(c: usize) -> bool { + return c >= ZC_SLICE; +} + +pub fn z_elem(c: usize) -> usize { + if (c >= ZC_ARRAY) { return c - ZC_ARRAY; } + if (c >= ZC_SLICE) { return c - ZC_SLICE; } + return ZC_NONE; +} + +/// The class of a binary operation: a comparison or a boolean operator is a bool, a shift keeps its left +/// operand's class, arithmetic is a float when either side is (the typed one wins over a literal), else an integer. +pub fn z_combine(lv: usize, a: usize, b: usize) -> usize { + if (lv == L_OR or lv == L_AND or lv == L_CMP) { return ZC_BOOL; } + if (lv == L_SHIFT) { return a; } + if (a == ZC_F64 or b == ZC_F64) { return ZC_F64; } + if (a == ZC_F32 or b == ZC_F32) { return ZC_F32; } + if (a == ZC_LITFLOAT or b == ZC_LITFLOAT) { return ZC_LITFLOAT; } + if (a == ZC_INT or b == ZC_INT) { return ZC_INT; } + if (a == ZC_LITINT and b == ZC_LITINT) { return ZC_LITINT; } + return ZC_NONE; +} + +/// One of the scalar types t27 spells as Zig does: u8 .. u64, i8 .. i64, usize, isize, bool, f32, f64. (i128 and +/// u128 are not: t27c's `as` does not take them.) +pub fn z_scalar(src: []const u8, a: usize, b: usize) -> bool { + return word_is(src, a, b, "u8") or word_is(src, a, b, "u16") or word_is(src, a, b, "u32") or word_is(src, a, b, "u64") or word_is(src, a, b, "i8") or word_is(src, a, b, "i16") or word_is(src, a, b, "i32") or word_is(src, a, b, "i64") or word_is(src, a, b, "usize") or word_is(src, a, b, "isize") or word_is(src, a, b, "bool") or word_is(src, a, b, "f32") or word_is(src, a, b, "f64"); +} + +/// The class of a scalar type's value. +pub fn z_type_class(src: []const u8, a: usize, b: usize) -> usize { + if (word_is(src, a, b, "f64")) { return ZC_F64; } + if (word_is(src, a, b, "f32")) { return ZC_F32; } + if (word_is(src, a, b, "bool")) { return ZC_BOOL; } + if (word_is(src, a, b, "void")) { return ZC_VOID; } + if (z_scalar(src, a, b)) { return ZC_INT; } + return ZC_NONE; +} + +// ---------------------------------------------------------------- the name table + +/// What a name is. Entries are (lo, hi, kind, class): the name is src[lo .. hi]. The file's own names come first and +/// stay; the locals of a fn follow and are dropped at its end. +pub const Z_K_FN : usize = 1; +pub const Z_K_FNX : usize = 2; +pub const Z_K_CONST : usize = 3; +pub const Z_K_CONSTX : usize = 4; +pub const Z_K_NS : usize = 5; +pub const Z_K_EXT : usize = 6; +pub const Z_K_TYPEX : usize = 7; +pub const Z_K_LOCAL : usize = 8; +pub const Z_K_PARAM : usize = 9; +pub const Z_K_VARX : usize = 10; +pub const Z_K_HOIST : usize = 11; +pub const Z_K_NSPACE : usize = 12; + +/// The std namespaces a call can name (the class of a K_NS entry): std itself, and its math, mem, testing and ascii. +pub const Z_NS_STD : usize = 1; +pub const Z_NS_MATH : usize = 2; +pub const Z_NS_MEM : usize = 3; +pub const Z_NS_TESTING : usize = 4; +pub const Z_NS_ASCII : usize = 5; +pub const Z_NS_OTHER : usize = 6; +pub const Z_NS_DEBUG : usize = 7; + +/// The first slot of entry e (an index counted from 1, as z_find returns it). +pub fn z_slot(e: usize) -> usize { + return Z_NAMES + (e - 1) * Z_NAME_SLOTS; +} + +/// Add a name (owned by the struct st[Z_OWNER] is reading, none for a local or a top-level name); false when the table is full. +pub fn z_add_name(st: []usize, kind: usize, lo: usize, hi: usize, cls: usize) -> bool { + const k : usize = st[Z_NNAMES]; + const s : usize = Z_NAMES + k * Z_NAME_SLOTS; + if (s + Z_NAME_SLOTS > st[Z_LIMIT]) { return false; } + st[s] = lo; + st[s + 1] = hi; + st[s + 2] = kind; + st[s + 3] = cls; + st[s + 4] = st[Z_OWNER]; + st[Z_NNAMES] = k + 1; + return true; +} + +/// The entry named src[a .. b] a fn body sees, as its index counted from 1; 0 when there is none (the index also lands in +/// st[Z_HIT]). A local comes first, newest first, then a member of the struct the fn is in (st[Z_CUR]), then a name of the file. +pub fn z_find(src: []const u8, st: []usize, a: usize, b: usize) -> usize { + st[Z_HIT] = 0; + var k : usize = st[Z_NNAMES]; + while (k > st[Z_NFILE]) { + k = k - 1; + const s : usize = Z_NAMES + k * Z_NAME_SLOTS; + if (st[s + 2] != Z_K_HOIST and same_text(src, st[s], st[s + 1], a, b)) { + st[Z_HIT] = k + 1; + return k + 1; + } + } + var phase : usize = 0; + while (phase < 2) { + const want_owner : usize = st[Z_CUR] * (1 - phase); + if (phase == 1 or st[Z_CUR] != 0) { + k = st[Z_NFILE]; + while (k > 0) { + k = k - 1; + const s : usize = Z_NAMES + k * Z_NAME_SLOTS; + if (st[s + 4] == want_owner and same_text(src, st[s], st[s + 1], a, b)) { + st[Z_HIT] = k + 1; + return k + 1; + } + } + } + phase = phase + 1; + } + return 0; +} + +/// The member of the struct whose entry is `owner`, named src[a .. b]; 0 when it has none. +pub fn z_find_member(src: []const u8, st: []usize, owner: usize, a: usize, b: usize) -> usize { + var k : usize = 0; + st[Z_HIT] = 0; + while (k < st[Z_NFILE]) { + const s : usize = Z_NAMES + k * Z_NAME_SLOTS; + if (st[s + 4] == owner and same_text(src, st[s], st[s + 1], a, b)) { + st[Z_HIT] = k + 1; + return k + 1; + } + k = k + 1; + } + return 0; +} + +/// The file-level entry named src[a .. b] of the struct being read (st[Z_CUR], none at the top); 0 when there is none. +pub fn z_find_file(src: []const u8, st: []usize, a: usize, b: usize) -> usize { + return z_find_member(src, st, st[Z_CUR], a, b); +} + +/// A name written for the entry e that src[a .. b] resolved to: a member of a namespace struct is `Struct_member`. +pub fn z_put_entry(src: []const u8, st: []usize, out: []u8, e: usize, a: usize, b: usize) -> void { + const owner : usize = st[z_slot(e) + 4]; + if (owner != 0) { + put_ident(src, st, out, st[z_slot(owner)], st[z_slot(owner) + 1]); + puts(st, out, "_"); + } + put_ident(src, st, out, a, b); +} + +/// The name src[a .. b] of a declaration of the struct being read (st[Z_CUR], none at the top), written `Struct_name`. +pub fn z_put_qualified(src: []const u8, st: []usize, out: []u8, a: usize, b: usize) -> void { + const owner : usize = st[Z_CUR]; + if (owner != 0) { + put_ident(src, st, out, st[z_slot(owner)], st[z_slot(owner) + 1]); + puts(st, out, "_"); + } + put_ident(src, st, out, a, b); +} + +/// The refusal for a name a fn body cannot use: 0 when it can. Refused names are the cascade (uses refused), another +/// file's names are external, a type or a mutable file-level var is said as what it is, a name nobody declares is undeclared. +pub fn z_lookup(src: []const u8, st: []usize, a: usize, b: usize) -> usize { + const e : usize = z_find(src, st, a, b); + if (e == 0) { + refuse(st, Z_R_UNDECL, a, a, b); + return 0; + } + const k : usize = st[z_slot(e) + 2]; + if (k == Z_K_FNX or k == Z_K_CONSTX) { refuse(st, Z_R_DEPENDS, a, a, b); } + if (k == Z_K_TYPEX) { refuse(st, Z_R_TYPE, a, a, b); } + if (k == Z_K_VARX) { refuse(st, Z_R_GLOBALVAR, a, a, b); } + if (k == Z_K_EXT) { refuse(st, Z_R_EXTERNAL, a, a, b); } + if (bad(st[ERR])) { return 0; } + return e; +} + +// ---------------------------------------------------------------- literals + +pub fn z_lower(c: u8) -> u8 { + if (is_upper(c)) { return c + 32; } + return c; +} + +/// Is the string literal src[a .. b] (quotes included) one t27 reads the same? Printable ASCII and the escapes +/// \n \t \\ \" (t27 has no \r, \x or \u in a string). +pub fn z_str_ok(src: []const u8, a: usize, b: usize) -> bool { + var i : usize = a + 1; + while (i + 1 < b) { + const c : u8 = src[i]; + if (c < B_SPACE or c >= B_DEL) { return false; } + if (c == B_BACKSLASH) { + const d : u8 = src[i + 1]; + if (!(d == B_LOWER_N or d == B_LOWER_T or d == B_BACKSLASH or d == B_QUOTE)) { return false; } + i = i + 2; + } else { + i = i + 1; + } + } + return true; +} + +/// Is the char literal src[a .. b] one t27 reads the same? One printable ASCII byte, or \n \t \r \\ \'. +pub fn z_chr_ok(src: []const u8, a: usize, b: usize) -> bool { + if (b - a == 3) { + const c : u8 = src[a + 1]; + return c >= B_SPACE and c < B_DEL and c != B_APOS and c != B_BACKSLASH; + } + if (b - a == 4 and src[a + 1] == B_BACKSLASH) { + const d : u8 = src[a + 2]; + return d == B_LOWER_N or d == B_LOWER_T or d == B_LOWER_R or d == B_BACKSLASH or d == B_APOS; + } + return false; +} + +/// The value of the octal literal src[a .. b] (`0o17`, underscores allowed); 4294967296 * 4294967296 - 1 when a digit +/// is not octal or the value is too big to be sure of. +pub fn z_octal(src: []const u8, a: usize, b: usize) -> usize { + var v : usize = 0; + var i : usize = a + 2; + while (i < b) { + const c : u8 = src[i]; + if (c != B_UNDERSCORE) { + if (c < B_ZERO or c > B_ZERO + 7 or v > 1152921504606846975) { return 18446744073709551615; } + v = v * 8 + (c - B_ZERO) as usize; + } + i = i + 1; + } + return v; +} + +/// Write the number literal src[a .. b] and set its class. Decimal, hex, binary and float literals pass as they +/// are, an octal one is written in decimal (t27 has no `0o`), a hex float is refused. +pub fn z_number(src: []const u8, st: []usize, out: []u8, a: usize, b: usize) -> void { + st[Z_CLS] = ZC_LITINT; + if (b - a >= 2 and src[a] == B_ZERO and src[a + 1] == B_LOWER_O) { + const v : usize = z_octal(src, a, b); + if (v == 18446744073709551615) { + refuse(st, Z_R_LITERAL, a, a, b); + return; + } + put_num(st, out, v); + return; + } + if (b - a >= 2 and src[a] == B_ZERO and (src[a + 1] == B_LOWER_X or src[a + 1] == B_LOWER_B)) { + var i : usize = a + 2; + while (i < b) { + if (src[i] == B_DOT or src[i] == Z_B_P) { refuse(st, Z_R_LITERAL, a, a, b); return; } + i = i + 1; + } + if (head(src, b) == B_DOT and is_word(head(src, b + 1))) { + refuse(st, Z_R_LITERAL, a, a, b); + return; + } + put_src(src, st, out, a, b); + return; + } + var i : usize = a; + while (i < b) { + if (src[i] == B_DOT or src[i] == B_LOWER_E or src[i] == B_UPPER_E) { st[Z_CLS] = ZC_LITFLOAT; } + i = i + 1; + } + put_src(src, st, out, a, b); +} + +// ---------------------------------------------------------------- types + +/// Why the type token src[a .. b] is no scalar: a pointer, an optional, an error union, a comptime or generic +/// parameter, or a type of this file or another (a named type). +pub fn z_bad_type_code(src: []const u8, a: usize, b: usize) -> usize { + const c : u8 = head(src, a); + if (c == B_STAR) { return Z_R_POINTER; } + if (c == B_QMARK) { return Z_R_OPTIONAL; } + if (c == B_BANG) { return Z_R_ERRORS; } + if (word_is(src, a, b, "error") or word_is(src, a, b, "anyerror")) { return Z_R_ERRORS; } + if (word_is(src, a, b, "comptime") or word_is(src, a, b, "anytype") or word_is(src, a, b, "type") or word_is(src, a, b, "noalias") or word_is(src, a, b, "comptime_int") or word_is(src, a, b, "comptime_float")) { return Z_R_GENERIC; } + return Z_R_TYPE; +} + +/// One scalar type at st[P], written; Z_CLS is its class. Anything else is refused. +pub fn z_scalar_type(src: []const u8, st: []usize, out: []u8) -> void { + st[Z_CLS] = ZC_NONE; + const a : usize = st[P]; + const b : usize = z_tok_end(src, a); + if (z_scalar(src, a, b)) { + put_src(src, st, out, a, b); + st[Z_CLS] = z_type_class(src, a, b); + z_step(src, st); + return; + } + refuse(st, z_bad_type_code(src, a, b), a, a, b); +} + +/// A type: a scalar, `void`, a slice `[]T` or `[]const T`, or an array `[N]T`, T a scalar and N a number or a const +/// of this file. Z_CLS is the class (a slice or array adds its element's). +pub fn z_type(src: []const u8, st: []usize, out: []u8) -> void { + st[Z_CLS] = ZC_NONE; + if (bad(st[ERR])) { return; } + const a : usize = st[P]; + const b : usize = z_tok_end(src, a); + if (word_is(src, a, b, "void")) { + put_src(src, st, out, a, b); + st[Z_CLS] = ZC_VOID; + z_step(src, st); + return; + } + if (head(src, a) != B_LBRACKET) { + z_scalar_type(src, st, out); + return; + } + z_step(src, st); + if (z_take(src, st, "]")) { + puts(st, out, "[]"); + if (z_take(src, st, "const")) { puts(st, out, "const "); } + z_scalar_type(src, st, out); + if (st[Z_CLS] != ZC_NONE) { st[Z_CLS] = ZC_SLICE + st[Z_CLS]; } + return; + } + const n0 : usize = st[P]; + const n1 : usize = z_tok_end(src, n0); + if (head(src, n0) == B_STAR or head(src, n0) == B_COLON) { + refuse(st, Z_R_POINTER, n0, n0, n1); + return; + } + if (is_alpha(head(src, n0))) { + const e : usize = z_find(src, st, n0, n1); + if (e == 0 or st[z_slot(e) + 2] != Z_K_CONST) { + refuse(st, Z_R_SYNTAX, n0, n0, n1); + return; + } + } else if (!is_digit(head(src, n0))) { + refuse(st, Z_R_SYNTAX, n0, n0, n1); + return; + } + puts(st, out, "["); + put_src(src, st, out, n0, n1); + puts(st, out, "]"); + z_step(src, st); + z_want(src, st, "]"); + z_scalar_type(src, st, out); + if (st[Z_CLS] != ZC_NONE) { st[Z_CLS] = ZC_ARRAY + st[Z_CLS]; } +} + +test types_are_scalars_slices_and_arrays_of_scalars { + var out : [128]u8 = undefined; + var st : [256]usize = [0] ** 256; + z_type("[]const u8", &st, &out); + assert(out_is(st[W], &out, 0, "[]const u8")); + assert(st[Z_CLS] == ZC_SLICE + ZC_INT); + var o2 : [128]u8 = undefined; + var s2 : [256]usize = [0] ** 256; + z_type("[]f64", &s2, &o2); + assert(out_is(s2[W], &o2, 0, "[]f64")); + assert(s2[Z_CLS] == ZC_SLICE + ZC_F64); + var o3 : [128]u8 = undefined; + var s3 : [256]usize = [0] ** 256; + z_type("[5]i8", &s3, &o3); + assert(out_is(s3[W], &o3, 0, "[5]i8")); + assert(s3[Z_CLS] == ZC_ARRAY + ZC_INT); +} + +test types_that_are_not_scalars_are_refused_with_their_kind { + var out : [128]u8 = undefined; + var st : [256]usize = [0] ** 256; + z_type("*u8", &st, &out); + assert(st[ERR] == Z_R_POINTER); + var s2 : [256]usize = [0] ** 256; + z_type("?u8", &s2, &out); + assert(s2[ERR] == Z_R_OPTIONAL); + var s3 : [256]usize = [0] ** 256; + z_type("!u8", &s3, &out); + assert(s3[ERR] == Z_R_ERRORS); + var s4 : [256]usize = [0] ** 256; + z_type("Point", &s4, &out); + assert(s4[ERR] == Z_R_TYPE); + var s5 : [256]usize = [0] ** 256; + z_type("u128", &s5, &out); + assert(s5[ERR] == Z_R_TYPE); + var s6 : [256]usize = [0] ** 256; + z_type("anytype", &s6, &out); + assert(s6[ERR] == Z_R_GENERIC); + var s7 : [256]usize = [0] ** 256; + z_type("[]const []const u8", &s7, &out); + assert(s7[ERR] == Z_R_TYPE); +} + +// ---------------------------------------------------------------- expressions + +/// The operators Zig has that t27 has no spelling for (or that mean error handling): `++` `**` `||` orelse catch. +pub const Z_OP_BAD : usize = 100; + +/// The binary operator src[a .. b] as a rust_convert precedence level (Zig's table is the same for the operators +/// t27 shares: `*` over `+` over shifts over `&` over `^` over `|` over comparisons over `and` over `or`); 0 when +/// the token is none, Z_OP_BAD for one that is refused. +pub fn z_op_level(src: []const u8, a: usize, b: usize) -> usize { + if (b == a + 1) { + const c : u8 = src[a]; + if (c == B_LT or c == B_GT) { return L_CMP; } + if (c == B_PIPE) { return L_BOR; } + if (c == B_CARET) { return L_XOR; } + if (c == B_AMP) { return L_BAND; } + if (c == B_PLUS or c == B_MINUS) { return L_ADD; } + if (c == B_STAR or c == B_SLASH or c == B_PCT) { return L_MUL; } + return 0; + } + if (word_is(src, a, b, "or")) { return L_OR; } + if (word_is(src, a, b, "and")) { return L_AND; } + if (word_is(src, a, b, "==") or word_is(src, a, b, "!=") or word_is(src, a, b, "<=") or word_is(src, a, b, ">=")) { return L_CMP; } + if (word_is(src, a, b, "<<") or word_is(src, a, b, ">>")) { return L_SHIFT; } + if (word_is(src, a, b, "+%") or word_is(src, a, b, "-%")) { return L_ADD; } + if (word_is(src, a, b, "*%")) { return L_MUL; } + if (word_is(src, a, b, "++") or word_is(src, a, b, "**") or word_is(src, a, b, "||") or word_is(src, a, b, "orelse") or word_is(src, a, b, "catch")) { return Z_OP_BAD; } + return 0; +} + +/// Why the operator src[a .. b] that z_op_level refused is refused. +pub fn z_bad_op_code(src: []const u8, a: usize, b: usize) -> usize { + if (word_is(src, a, b, "orelse")) { return Z_R_OPTIONAL; } + if (word_is(src, a, b, "catch")) { return Z_R_ERRORS; } + return Z_R_OPERATOR; +} + +/// The level an `if` or `switch` used as a value has: below every operator, so an operand of one is parenthesized. +pub const Z_L_COND : usize = 1; + +/// An expression: operators of level `min` and above. The result is the level of its top operator; Z_CLS its class. +pub fn z_expr_bp(src: []const u8, st: []usize, out: []u8, min: usize) -> usize { + const start : usize = st[W]; + var lvl : usize = z_unary(src, st, out); + var cls : usize = st[Z_CLS]; + while (!bad(st[ERR])) { + const a : usize = st[P]; + const b : usize = z_tok_end(src, a); + const lv : usize = z_op_level(src, a, b); + if (lv == 0 or lv < min) { + st[Z_CLS] = cls; + return lvl; + } + if (lv == Z_OP_BAD) { + refuse(st, z_bad_op_code(src, a, b), a, a, b); + return lvl; + } + z_step(src, st); + if (!keep_bare(lvl, lv, true)) { wrap(st, out, start); } + puts(st, out, " "); + put_src(src, st, out, a, b); + puts(st, out, " "); + const rs : usize = st[W]; + const ra : usize = st[P]; + const rl : usize = z_expr_bp(src, st, out, lv + 1); + if (!keep_bare(rl, lv, false)) { wrap(st, out, rs); } + // t27b has no result type for an `@intCast` that is a shift count (Zig gives it the count's type) + if (lv == L_SHIFT and z_is(src, ra, "@intCast") and !bad(st[ERR])) { refuse(st, Z_R_BOPERAND, ra, ra, z_tok_end(src, ra)); } + cls = z_combine(lv, cls, st[Z_CLS]); + lvl = lv; + } + st[Z_CLS] = cls; + return lvl; +} + +pub fn z_expr(src: []const u8, st: []usize, out: []u8) -> usize { + return z_expr_bp(src, st, out, L_OR); +} + +/// The entry of the array literal behind the `&` at src[pos], declared ahead of its statement; 0 when it has none yet. +pub fn z_hoisted(st: []usize, pos: usize) -> usize { + var k : usize = st[Z_NNAMES]; + st[Z_HIT] = 0; + while (k > 0) { + k = k - 1; + const s : usize = Z_NAMES + k * Z_NAME_SLOTS; + if (st[s + 2] == Z_K_HOIST and st[s] == pos) { + st[Z_HIT] = k + 1; + return k + 1; + } + } + return 0; +} + +/// A prefix operator and its operand: `-x`, `!x`, `~x`, `&arr` (an array handed to a slice parameter). +pub fn z_unary(src: []const u8, st: []usize, out: []u8) -> usize { + if (bad(st[ERR])) { return L_ATOM; } + const a : usize = st[P]; + const b : usize = z_tok_end(src, a); + if (z_is(src, a, "-")) { + z_step(src, st); + puts(st, out, "-"); + z_unary(src, st, out); + return L_UNARY; + } + if (z_is(src, a, "!")) { + z_step(src, st); + puts(st, out, "!"); + z_unary(src, st, out); + st[Z_CLS] = ZC_BOOL; + return L_UNARY; + } + if (z_is(src, a, "~")) { + z_step(src, st); + puts(st, out, "~"); + z_unary(src, st, out); + return L_UNARY; + } + if (z_is(src, a, "&")) { + z_step(src, st); + const n0 : usize = st[P]; + const n1 : usize = z_tok_end(src, n0); + if (head(src, n0) == B_LBRACKET) { + // `&[_]T{ .. }`: the statement's line declares the array first (z_line), and this reads as its name + const h : usize = z_hoisted(st, a); + if (h == 0) { + refuse(st, Z_R_HOIST, a, a, n1); + return L_ATOM; + } + const brace : usize = find_at(src, n0, B_LBRACE); + puts(st, out, "&t27o_a"); + put_num(st, out, st[z_slot(h) + 1]); + st[P] = skip_ws(src, block_end(src, brace)); + st[Z_CLS] = ZC_SLICE + z_elem(st[z_slot(h) + 3]); + return L_UNARY; + } + if (is_alpha(head(src, n0))) { + const e : usize = z_find(src, st, n0, n1); + if (e != 0 and st[z_slot(e) + 3] >= ZC_ARRAY and st[z_slot(e) + 2] != Z_K_FNX and st[z_slot(e) + 2] != Z_K_CONSTX) { + puts(st, out, "&"); + z_put_entry(src, st, out, e, n0, n1); + st[Z_CLS] = ZC_SLICE + z_elem(st[z_slot(e) + 3]); + z_step(src, st); + return L_UNARY; + } + } + refuse(st, Z_R_POINTER, a, a, b); + return L_ATOM; + } + if (z_is(src, a, "-%")) { + refuse(st, Z_R_OPERATOR, a, a, b); + return L_ATOM; + } + if (z_is(src, a, "try")) { + refuse(st, Z_R_ERRORS, a, a, b); + return L_ATOM; + } + return z_postfix(src, st, out); +} + +/// A primary expression and what follows it: an index or slice `[..]`, `.len` of a slice or array. +pub fn z_postfix(src: []const u8, st: []usize, out: []u8) -> usize { + var lvl : usize = z_primary(src, st, out); + while (!bad(st[ERR])) { + const a : usize = st[P]; + const b : usize = z_tok_end(src, a); + const cls : usize = st[Z_CLS]; + if (z_is(src, a, "[")) { + if (!z_is_seq(cls)) { + refuse(st, Z_R_INDEX, a, a, b); + return lvl; + } + z_step(src, st); + puts(st, out, "["); + z_expr(src, st, out); + var res : usize = z_elem(cls); + if (z_take(src, st, "..")) { + puts(st, out, ".."); + if (!z_is(src, st[P], "]")) { z_expr(src, st, out); } + res = ZC_SLICE + z_elem(cls); + } + z_want(src, st, "]"); + puts(st, out, "]"); + st[Z_CLS] = res; + lvl = L_ATOM; + } else if (z_is(src, a, ".")) { + z_step(src, st); + const f0 : usize = st[P]; + const f1 : usize = z_tok_end(src, f0); + if (z_is_seq(cls) and word_is(src, f0, f1, "len")) { + puts(st, out, ".len"); + z_step(src, st); + st[Z_CLS] = ZC_INT; + lvl = L_ATOM; + } else { + refuse(st, Z_R_FIELD, f0, f0, f1); + return lvl; + } + } else if (z_is(src, a, ".?")) { + refuse(st, Z_R_OPTIONAL, a, a, b); + return lvl; + } else if (z_is(src, a, ".*")) { + refuse(st, Z_R_POINTER, a, a, b); + return lvl; + } else { + return lvl; + } + } + return lvl; +} + +/// Call arguments after their `(` has been read and written: expressions up to the `)`, which is read and +/// written too. The count and the classes of the first three land in Z_NARGS and Z_ARG0 .. Z_ARG2 (after the +/// arguments are read, so a call inside one cannot overwrite them). +pub fn z_args_rest(src: []const u8, st: []usize, out: []u8) -> void { + var n : usize = 0; + var c0 : usize = ZC_NONE; + var c1 : usize = ZC_NONE; + var c2 : usize = ZC_NONE; + while (!bad(st[ERR]) and st[P] < src.len and !z_is(src, st[P], ")")) { + if (n > 0) { puts(st, out, ", "); } + z_expr(src, st, out); + const c : usize = st[Z_CLS]; + if (n == 0) { c0 = c; } + if (n == 1) { c1 = c; } + if (n == 2) { c2 = c; } + n = n + 1; + if (!z_take(src, st, ",")) { break; } + } + st[Z_CLOSE] = z_tok_end(src, st[P]); + z_want(src, st, ")"); + puts(st, out, ")"); + st[Z_NARGS] = n; + st[Z_ARG0] = c0; + st[Z_ARG1] = c1; + st[Z_ARG2] = c2; +} + +/// A parenthesized argument list. +pub fn z_args(src: []const u8, st: []usize, out: []u8) -> void { + z_want(src, st, "("); + puts(st, out, "("); + z_args_rest(src, st, out); +} + +/// The namespace of std the member name src[a .. b] names. +pub fn z_ns_of(src: []const u8, a: usize, b: usize) -> usize { + if (word_is(src, a, b, "math")) { return Z_NS_MATH; } + if (word_is(src, a, b, "mem")) { return Z_NS_MEM; } + if (word_is(src, a, b, "testing")) { return Z_NS_TESTING; } + if (word_is(src, a, b, "ascii")) { return Z_NS_ASCII; } + if (word_is(src, a, b, "debug")) { return Z_NS_DEBUG; } + return Z_NS_OTHER; +} + +/// std.math.maxInt / minInt of the integer type src[a .. b], as the literal t27 reads; "" for a type that is no integer. +pub fn z_int_limit(src: []const u8, a: usize, b: usize, max: bool) -> []const u8 { + if (word_is(src, a, b, "u8")) { if (max) { return "255"; } return "0"; } + if (word_is(src, a, b, "u16")) { if (max) { return "65535"; } return "0"; } + if (word_is(src, a, b, "u32")) { if (max) { return "4294967295"; } return "0"; } + if (word_is(src, a, b, "u64") or word_is(src, a, b, "usize")) { if (max) { return "18446744073709551615"; } return "0"; } + if (word_is(src, a, b, "i8")) { if (max) { return "127"; } return "-128"; } + if (word_is(src, a, b, "i16")) { if (max) { return "32767"; } return "-32768"; } + if (word_is(src, a, b, "i32")) { if (max) { return "2147483647"; } return "-2147483648"; } + if (word_is(src, a, b, "i64") or word_is(src, a, b, "isize")) { if (max) { return "9223372036854775807"; } return "-9223372036854775808"; } + return ""; +} + +/// A float constant of std.math as the f64 literal Zig rounds it to; "" for a name that is none. +pub fn z_math_const(src: []const u8, a: usize, b: usize) -> []const u8 { + if (word_is(src, a, b, "tau")) { return "6.283185307179586"; } + if (word_is(src, a, b, "phi")) { return "1.618033988749895"; } + if (word_is(src, a, b, "sqrt2")) { return "1.4142135623730951"; } + if (word_is(src, a, b, "sqrt1_2")) { return "0.7071067811865476"; } + if (word_is(src, a, b, "ln2")) { return "0.6931471805599453"; } + if (word_is(src, a, b, "ln10")) { return "2.302585092994046"; } + if (word_is(src, a, b, "log2e")) { return "1.4426950408889634"; } + if (word_is(src, a, b, "log10e")) { return "0.4342944819032518"; } + if (word_is(src, a, b, "pi_2")) { return "1.5707963267948966"; } + if (word_is(src, a, b, "pi_4")) { return "0.7853981633974483"; } + return ""; +} + +/// A call of std.math or std.mem: only the members whose t27 form is measured to mean what Zig's does, under t27b and +/// t27c, are written; the rest is refused naming the call (the text a0 .. mb). +pub fn z_std_member(src: []const u8, st: []usize, out: []u8, a0: usize, ns: usize, ma: usize, mb: usize) -> usize { + st[Z_CLS] = ZC_NONE; + if (ns == Z_NS_MATH) { + if (word_is(src, ma, mb, "pi") or word_is(src, ma, mb, "e")) { + puts(st, out, "std.math."); + put_src(src, st, out, ma, mb); + st[Z_CLS] = ZC_LITFLOAT; + return L_ATOM; + } + const k : []const u8 = z_math_const(src, ma, mb); + if (k.len > 0) { + puts(st, out, k); + st[Z_CLS] = ZC_LITFLOAT; + return L_ATOM; + } + if (word_is(src, ma, mb, "sqrt") or word_is(src, ma, mb, "floor") or word_is(src, ma, mb, "ceil") or word_is(src, ma, mb, "exp") or word_is(src, ma, mb, "cos")) { + puts(st, out, "@"); + put_src(src, st, out, ma, mb); + z_args(src, st, out); + if (st[Z_NARGS] != 1) { refuse(st, Z_R_SYNTAX, a0, a0, mb); } + z_builtin_check(st, z_builtin_id(src, ma, mb), a0, st[Z_CLOSE]); + return L_ATOM; + } + if (word_is(src, ma, mb, "isNan") or word_is(src, ma, mb, "isPositiveInf") or word_is(src, ma, mb, "isNegativeInf")) { + puts(st, out, "std.math."); + put_src(src, st, out, ma, mb); + z_args(src, st, out); + if (st[Z_NARGS] != 1) { refuse(st, Z_R_SYNTAX, a0, a0, mb); } + st[Z_CLS] = ZC_BOOL; + return L_ATOM; + } + if (word_is(src, ma, mb, "inf") or word_is(src, ma, mb, "nan")) { + z_want(src, st, "("); + const fa : usize = st[P]; + const fb : usize = z_tok_end(src, fa); + if (!(word_is(src, fa, fb, "f64") or word_is(src, fa, fb, "f32"))) { + refuse(st, Z_R_STDCALL, a0, a0, fb); + return L_ATOM; + } + puts(st, out, "std.math."); + put_src(src, st, out, ma, mb); + puts(st, out, "("); + put_src(src, st, out, fa, fb); + puts(st, out, ")"); + st[Z_CLS] = z_type_class(src, fa, fb); + z_step(src, st); + z_want(src, st, ")"); + return L_ATOM; + } + if (word_is(src, ma, mb, "maxInt") or word_is(src, ma, mb, "minInt")) { + z_want(src, st, "("); + const ta : usize = st[P]; + const tb : usize = z_tok_end(src, ta); + const lit : []const u8 = z_int_limit(src, ta, tb, word_is(src, ma, mb, "maxInt")); + if (lit.len == 0) { + refuse(st, Z_R_STDCALL, a0, a0, tb); + return L_ATOM; + } + z_step(src, st); + z_want(src, st, ")"); + puts(st, out, lit); + st[Z_CLS] = ZC_LITINT; + if (lit[0] == B_MINUS) { return L_UNARY; } + return L_ATOM; + } + } + if (ns == Z_NS_DEBUG and word_is(src, ma, mb, "assert")) { + puts(st, out, "assert"); + z_args(src, st, out); + if (st[Z_NARGS] != 1) { refuse(st, Z_R_SYNTAX, a0, a0, mb); } + st[Z_CLS] = ZC_VOID; + return L_ATOM; + } + if (ns == Z_NS_MEM and word_is(src, ma, mb, "eql")) { + z_want(src, st, "("); + if (!z_take(src, st, "u8")) { + refuse(st, Z_R_STDCALL, a0, a0, mb); + return L_ATOM; + } + z_want(src, st, ","); + puts(st, out, "std.mem.eql(u8, "); + z_args_rest(src, st, out); + if (st[Z_NARGS] != 2) { refuse(st, Z_R_SYNTAX, a0, a0, mb); } + st[Z_CLS] = ZC_BOOL; + return L_ATOM; + } + refuse(st, Z_R_STDCALL, a0, a0, mb); + return L_ATOM; +} + +/// A name that is std or an alias of one of its namespaces, with the members that follow it (`std.math.pi`, +/// `math.sqrt(x)`). st[P] is at the name. +pub fn z_std_chain(src: []const u8, st: []usize, out: []u8, a0: usize, ns0: usize) -> usize { + z_step(src, st); + var ns : usize = ns0; + if (ns == Z_NS_STD) { + if (!z_take(src, st, ".")) { + refuse(st, Z_R_STDCALL, a0, a0, st[P]); + return L_ATOM; + } + const sa : usize = st[P]; + const sb : usize = z_tok_end(src, sa); + ns = z_ns_of(src, sa, sb); + z_step(src, st); + } + if (!z_take(src, st, ".")) { + refuse(st, Z_R_STDCALL, a0, a0, st[P]); + return L_ATOM; + } + const ma : usize = st[P]; + const mb : usize = z_tok_end(src, ma); + z_step(src, st); + return z_std_member(src, st, out, a0, ns, ma, mb); +} + +/// The class of `if (c) a else b` and of the prongs of a switch: the common class, a typed one over a literal. +pub fn z_merge(a: usize, b: usize) -> usize { + if (a == b) { return a; } + return z_combine(L_ADD, a, b); +} + +/// `if (c) a else b` as a value. +pub fn z_if_expr(src: []const u8, st: []usize, out: []u8) -> usize { + z_step(src, st); + puts(st, out, "if ("); + z_want(src, st, "("); + z_expr(src, st, out); + z_want(src, st, ")"); + puts(st, out, ") "); + if (z_is(src, st[P], "|")) { + z_refuse_here(src, st, Z_R_PAYLOAD); + return Z_L_COND; + } + z_expr(src, st, out); + const c1 : usize = st[Z_CLS]; + if (!z_take(src, st, "else")) { + z_refuse_here(src, st, Z_R_SYNTAX); + return Z_L_COND; + } + puts(st, out, " else "); + z_expr(src, st, out); + st[Z_CLS] = z_merge(c1, st[Z_CLS]); + return Z_L_COND; +} + +/// `switch (e) { 0 => a, 1 => b, else => c }` as a value: prongs of one literal or const each, values that are +/// expressions. A prong list, a range, a block or a capture is refused (a switch statement is an if chain instead). +pub fn z_switch_expr(src: []const u8, st: []usize, out: []u8) -> usize { + z_step(src, st); + puts(st, out, "switch ("); + z_want(src, st, "("); + z_expr(src, st, out); + z_want(src, st, ")"); + puts(st, out, ") {"); + z_want(src, st, "{"); + st[IND] = st[IND] + 1; + var cls : usize = ZC_NONE; + var n : usize = 0; + while (!bad(st[ERR]) and st[P] < src.len and !z_is(src, st[P], "}")) { + newline(st, out); + const pa : usize = st[P]; + if (z_is(src, pa, "else")) { + puts(st, out, "else"); + z_step(src, st); + } else if (is_digit(head(src, pa)) or is_alpha(head(src, pa))) { + z_primary(src, st, out); + if (z_is(src, st[P], ",") or z_is(src, st[P], "...")) { + z_refuse_here(src, st, Z_R_SWITCH); + return Z_L_COND; + } + } else { + z_refuse_here(src, st, Z_R_SWITCH); + return Z_L_COND; + } + z_want(src, st, "=>"); + puts(st, out, " => "); + if (z_is(src, st[P], "|")) { + z_refuse_here(src, st, Z_R_PAYLOAD); + return Z_L_COND; + } + z_expr(src, st, out); + if (n == 0) { cls = st[Z_CLS]; } else { cls = z_merge(cls, st[Z_CLS]); } + n = n + 1; + puts(st, out, ","); + if (!z_take(src, st, ",")) { break; } + } + z_want(src, st, "}"); + st[IND] = st[IND] - 1; + newline(st, out); + puts(st, out, "}"); + st[Z_CLS] = cls; + return Z_L_COND; +} + +/// The builtins (and the std.math fns that are one) t27o writes, by number. +pub const ZB_NONE : usize = 0; +pub const ZB_INTCAST : usize = 1; +pub const ZB_FLOATCAST : usize = 2; +pub const ZB_FLOATFROMINT : usize = 3; +pub const ZB_INTFROMFLOAT : usize = 4; +pub const ZB_INTFROMBOOL : usize = 5; +pub const ZB_SQRT : usize = 6; +pub const ZB_FLOOR : usize = 7; +pub const ZB_CEIL : usize = 8; +pub const ZB_ROUND : usize = 9; +pub const ZB_EXP : usize = 10; +pub const ZB_LOG : usize = 11; +pub const ZB_COS : usize = 12; +pub const ZB_ABS : usize = 13; +pub const ZB_MIN : usize = 14; +pub const ZB_MAX : usize = 15; +pub const ZB_DIVTRUNC : usize = 16; +pub const ZB_REM : usize = 17; +pub const ZB_MOD : usize = 18; + +/// Which builtin the name src[a .. b] is, without its `@`: ZB_NONE for one that is not written. +pub fn z_builtin_id(src: []const u8, a: usize, b: usize) -> usize { + if (word_is(src, a, b, "intCast")) { return ZB_INTCAST; } + if (word_is(src, a, b, "floatCast")) { return ZB_FLOATCAST; } + if (word_is(src, a, b, "floatFromInt")) { return ZB_FLOATFROMINT; } + if (word_is(src, a, b, "intFromFloat")) { return ZB_INTFROMFLOAT; } + if (word_is(src, a, b, "intFromBool")) { return ZB_INTFROMBOOL; } + if (word_is(src, a, b, "sqrt")) { return ZB_SQRT; } + if (word_is(src, a, b, "floor")) { return ZB_FLOOR; } + if (word_is(src, a, b, "ceil")) { return ZB_CEIL; } + if (word_is(src, a, b, "round")) { return ZB_ROUND; } + if (word_is(src, a, b, "exp")) { return ZB_EXP; } + if (word_is(src, a, b, "log")) { return ZB_LOG; } + if (word_is(src, a, b, "cos")) { return ZB_COS; } + if (word_is(src, a, b, "abs")) { return ZB_ABS; } + if (word_is(src, a, b, "min")) { return ZB_MIN; } + if (word_is(src, a, b, "max")) { return ZB_MAX; } + if (word_is(src, a, b, "divTrunc")) { return ZB_DIVTRUNC; } + if (word_is(src, a, b, "rem")) { return ZB_REM; } + if (word_is(src, a, b, "mod")) { return ZB_MOD; } + return ZB_NONE; +} + +pub fn z_builtin_arity(id: usize) -> usize { + if (id == ZB_NONE) { return 0; } + if (id >= ZB_MIN) { return 2; } + return 1; +} + +pub fn z_is_lit(c: usize) -> bool { + return c == ZC_LITINT or c == ZC_LITFLOAT; +} + +/// The checks after a builtin's arguments were read (their classes are in Z_ARG0, Z_ARG1): what t27b cannot run is +/// refused naming the call src[a .. end] (an integer `@abs`; an integer `@min` beside a literal; a float `@rem`; a literal +/// operand of the libm fns `@exp` `@floor` `@ceil` `@round` `@cos`, which Zig types from the result location; any +/// operand of a builtin that is not a literal where a file-level const is being folded), and the result class is set. +pub fn z_builtin_check(st: []usize, id: usize, a: usize, end: usize) -> void { + const c0 : usize = st[Z_ARG0]; + const c1 : usize = st[Z_ARG1]; + if (st[Z_INCONST] == 1 and !(z_is_lit(c0) and (id < ZB_MIN or z_is_lit(c1)))) { refuse(st, Z_R_BOPERAND, a, a, end); } + if (id == ZB_INTCAST or id == ZB_INTFROMFLOAT or id == ZB_INTFROMBOOL) { + st[Z_CLS] = ZC_INT; + } else if (id == ZB_FLOATCAST or id == ZB_FLOATFROMINT) { + st[Z_CLS] = ZC_F64; + } else if (id == ZB_ABS) { + if (!z_is_float(c0)) { refuse(st, Z_R_BOPERAND, a, a, end); } + st[Z_CLS] = c0; + } else if (id == ZB_MIN or id == ZB_MAX) { + if (!z_is_float(c0) and !z_is_float(c1) and (c0 == ZC_LITINT or c1 == ZC_LITINT)) { refuse(st, Z_R_BOPERAND, a, a, end); } + st[Z_CLS] = z_merge(c0, c1); + } else if (id == ZB_DIVTRUNC or id == ZB_REM or id == ZB_MOD) { + if (z_is_float(c0) or z_is_float(c1)) { refuse(st, Z_R_BOPERAND, a, a, end); } + if (id != ZB_DIVTRUNC and c0 == ZC_LITINT) { refuse(st, Z_R_BOPERAND, a, a, end); } + st[Z_CLS] = ZC_INT; + } else { + if ((id == ZB_EXP or id == ZB_FLOOR or id == ZB_CEIL or id == ZB_ROUND or id == ZB_COS) and z_is_lit(c0)) { refuse(st, Z_R_BOPERAND, a, a, end); } + st[Z_CLS] = c0; + } +} + +/// A builtin call: `@as(T, e)` and the builtins whose t27 form means what Zig's does under t27b and t27c. The rest is +/// refused naming the builtin, and so are the operands t27b cannot run (z_builtin_check). +pub fn z_builtin(src: []const u8, st: []usize, out: []u8) -> usize { + const a : usize = st[P]; + const b : usize = z_tok_end(src, a); + z_step(src, st); + if (word_is(src, a, b, "@as")) { + z_want(src, st, "("); + puts(st, out, "@as("); + z_scalar_type(src, st, out); + const tc : usize = st[Z_CLS]; + z_want(src, st, ","); + puts(st, out, ", "); + z_expr(src, st, out); + _ = z_take(src, st, ","); + z_want(src, st, ")"); + puts(st, out, ")"); + st[Z_CLS] = tc; + return L_ATOM; + } + const id : usize = z_builtin_id(src, a + 1, b); + if (id == ZB_NONE) { + refuse(st, Z_R_BUILTIN, a, a, b); + return L_ATOM; + } + put_src(src, st, out, a, b); + z_args(src, st, out); + if (bad(st[ERR])) { return L_ATOM; } + const end : usize = st[Z_CLOSE]; + if (st[Z_NARGS] != z_builtin_arity(id)) { + refuse(st, Z_R_SYNTAX, a, a, end); + return L_ATOM; + } + z_builtin_check(st, id, a, end); + return L_ATOM; +} + +/// `Struct.member` where the struct is a namespace of this file: a call of its fn or a read of its const, written +/// `Struct_member`. A member nobody declares, a refused one, or anything else about the struct is refused. +pub fn z_member_ref(src: []const u8, st: []usize, out: []u8, a: usize, b: usize, e: usize) -> usize { + z_step(src, st); + if (!z_take(src, st, ".")) { + refuse(st, Z_R_TYPE, a, a, b); + return L_ATOM; + } + const ma : usize = st[P]; + const mb : usize = z_tok_end(src, ma); + if (!is_alpha(head(src, ma))) { + z_refuse_here(src, st, Z_R_SYNTAX); + return L_ATOM; + } + z_step(src, st); + const m : usize = z_find_member(src, st, e, ma, mb); + if (m == 0) { + // a field, a variant or a decl nobody wrote: of a type when the container holds more than fns and consts + if (st[z_slot(e) + 3] == 1) { refuse(st, Z_R_TYPE, a, a, b); } else { refuse(st, Z_R_UNDECL, a, a, mb); } + return L_ATOM; + } + const kind : usize = st[z_slot(m) + 2]; + const cls : usize = st[z_slot(m) + 3]; + if (kind == Z_K_FNX or kind == Z_K_CONSTX) { + refuse(st, Z_R_DEPENDS, a, a, mb); + return L_ATOM; + } + if (kind != Z_K_FN and kind != Z_K_CONST) { + refuse(st, Z_R_FIELD, a, a, mb); + return L_ATOM; + } + z_put_entry(src, st, out, m, ma, mb); + if (kind == Z_K_FN) { + if (!z_is(src, st[P], "(")) { + refuse(st, Z_R_FIELD, a, a, mb); + return L_ATOM; + } + z_args(src, st, out); + } + st[Z_CLS] = cls; + return L_ATOM; +} + +/// A name in an expression: a value (a parameter, a local, a const of this file), a call of a fn of this file, or +/// std. Names that are refused, external, types or undeclared refuse the reader with their kind. +pub fn z_name(src: []const u8, st: []usize, out: []u8, a: usize, b: usize) -> usize { + if (word_is(src, a, b, "true") or word_is(src, a, b, "false")) { + put_src(src, st, out, a, b); + z_step(src, st); + st[Z_CLS] = ZC_BOOL; + return L_ATOM; + } + if (word_is(src, a, b, "if")) { return z_if_expr(src, st, out); } + if (word_is(src, a, b, "switch")) { return z_switch_expr(src, st, out); } + if (word_is(src, a, b, "null")) { refuse(st, Z_R_OPTIONAL, a, a, b); return L_ATOM; } + if (word_is(src, a, b, "unreachable")) { refuse(st, Z_R_UNREACH, a, a, b); return L_ATOM; } + if (word_is(src, a, b, "error") or word_is(src, a, b, "try") or word_is(src, a, b, "catch")) { refuse(st, Z_R_ERRORS, a, a, b); return L_ATOM; } + if (word_is(src, a, b, "comptime") or word_is(src, a, b, "inline") or word_is(src, a, b, "async") or word_is(src, a, b, "await") or word_is(src, a, b, "suspend") or word_is(src, a, b, "resume") or word_is(src, a, b, "nosuspend") or word_is(src, a, b, "anytype")) { refuse(st, Z_R_GENERIC, a, a, b); return L_ATOM; } + if (word_is(src, a, b, "struct") or word_is(src, a, b, "enum") or word_is(src, a, b, "union") or word_is(src, a, b, "opaque") or word_is(src, a, b, "fn") or word_is(src, a, b, "packed") or word_is(src, a, b, "extern")) { refuse(st, Z_R_TYPE, a, a, b); return L_ATOM; } + if (word_is(src, a, b, "while") or word_is(src, a, b, "for") or word_is(src, a, b, "return") or word_is(src, a, b, "break") or word_is(src, a, b, "continue") or word_is(src, a, b, "undefined")) { refuse(st, Z_R_VALUE, a, a, b); return L_ATOM; } + const e : usize = z_find(src, st, a, b); + if (e == 0 and word_is(src, a, b, "std")) { return z_std_chain(src, st, out, a, Z_NS_STD); } + if (e != 0 and st[z_slot(e) + 2] == Z_K_NS) { return z_std_chain(src, st, out, a, st[z_slot(e) + 3]); } + if (e != 0 and st[z_slot(e) + 2] == Z_K_NSPACE) { return z_member_ref(src, st, out, a, b, e); } + const k : usize = z_lookup(src, st, a, b); + if (k == 0) { return L_ATOM; } + const kind : usize = st[z_slot(k) + 2]; + const cls : usize = st[z_slot(k) + 3]; + z_put_entry(src, st, out, k, a, b); + z_step(src, st); + if (kind == Z_K_FN) { + if (!z_is(src, st[P], "(")) { + refuse(st, Z_R_FIELD, a, a, b); + return L_ATOM; + } + z_args(src, st, out); + st[Z_CLS] = cls; + return L_ATOM; + } + st[Z_CLS] = cls; + return L_ATOM; +} + +/// A primary expression: a literal, a parenthesized expression, a builtin, a name. +pub fn z_primary(src: []const u8, st: []usize, out: []u8) -> usize { + st[Z_CLS] = ZC_NONE; + if (bad(st[ERR])) { return L_ATOM; } + if (st[P] >= src.len) { + refuse(st, Z_R_SYNTAX, st[P], st[P], st[P]); + return L_ATOM; + } + const a : usize = st[P]; + const b : usize = z_tok_end(src, a); + const c : u8 = src[a]; + if (is_digit(c)) { + z_number(src, st, out, a, b); + z_step(src, st); + return L_ATOM; + } + if (c == B_QUOTE) { + if (!z_str_ok(src, a, b)) { + refuse(st, Z_R_LITERAL, a, a, b); + return L_ATOM; + } + put_src(src, st, out, a, b); + z_step(src, st); + st[Z_CLS] = ZC_SLICE + ZC_INT; + return L_ATOM; + } + if (c == B_APOS) { + if (!z_chr_ok(src, a, b)) { + refuse(st, Z_R_LITERAL, a, a, b); + return L_ATOM; + } + put_src(src, st, out, a, b); + z_step(src, st); + st[Z_CLS] = ZC_LITINT; + return L_ATOM; + } + if (c == B_LPAREN) { + z_step(src, st); + puts(st, out, "("); + z_expr(src, st, out); + z_want(src, st, ")"); + puts(st, out, ")"); + return L_ATOM; + } + if (c == Z_B_AT) { return z_builtin(src, st, out); } + if (c == B_DOT) { + refuse(st, Z_R_ANON, a, a, b); + return L_ATOM; + } + if (c == B_LBRACKET) { + refuse(st, Z_R_ARRAYLIT, a, a, b); + return L_ATOM; + } + if (is_alpha(c)) { return z_name(src, st, out, a, b); } + refuse(st, Z_R_SYNTAX, a, a, b); + return L_ATOM; +} + +// ---------------------------------------------------------------- statements + +/// Is the token after the one at src[p] the text s? +pub fn z_peek_is(src: []const u8, p: usize, s: []const u8) -> bool { + return z_is(src, skip_ws(src, z_tok_end(src, p)), s); +} + +/// Append out[lo .. hi) to the output (the left side of `x <<= 1` is written again as the left operand of `<<`). +pub fn z_copy_out(st: []usize, out: []u8, lo: usize, hi: usize) -> void { + var i : usize = lo; + while (i < hi) { + var c : u8 = 63; + if (i < out.len) { c = out[i]; } + put(st, out, c); + i = i + 1; + } +} + +/// What the assignment operator src[a .. b] is: 1 for `=`, 2 for one t27 reads (`+=` `-=` `*=` `/=` `%=` `&=` `|=` `^=`), +/// 3 for one it does not (`<<=` `>>=` `+%=` `-%=` `*%=`, written out as `x = x op e`), 0 for any other token. +pub fn z_asg_kind(src: []const u8, a: usize, b: usize) -> usize { + if (word_is(src, a, b, "=")) { return 1; } + if (word_is(src, a, b, "+=") or word_is(src, a, b, "-=") or word_is(src, a, b, "*=") or word_is(src, a, b, "/=") or word_is(src, a, b, "%=") or word_is(src, a, b, "&=") or word_is(src, a, b, "|=") or word_is(src, a, b, "^=")) { return 2; } + if (word_is(src, a, b, "<<=") or word_is(src, a, b, ">>=") or word_is(src, a, b, "+%=") or word_is(src, a, b, "-%=") or word_is(src, a, b, "*%=")) { return 3; } + return 0; +} + +/// The level of the operator behind a kind-3 assignment operator (its text without the `=`). +pub fn z_asg_level(src: []const u8, a: usize, b: usize) -> usize { + if (word_is(src, a, b, "<<=") or word_is(src, a, b, ">>=")) { return L_SHIFT; } + if (word_is(src, a, b, "*%=")) { return L_MUL; } + return L_ADD; +} + +/// The `;` that ends a statement. A statement that is the unbraced body of an `if` may be followed by `else` instead +/// (`if (c) x = 1 else x = 2;`): the `;` is written all the same, the braces make it one. +pub fn z_semi(src: []const u8, st: []usize, out: []u8) -> void { + if (z_take(src, st, ";")) { + puts(st, out, ";"); + return; + } + if (st[Z_ONE] == 1 and z_is(src, st[P], "else")) { + puts(st, out, ";"); + return; + } + z_refuse_here(src, st, Z_R_SYNTAX); +} + +/// An assignment (`x = e`, `a[i] += e`, `x <<= e`), a discard (`_ = e`) or a call used as a statement. `semi` is +/// whether the `;` is read and written (a while loop's step has none). +pub fn z_simple(src: []const u8, st: []usize, out: []u8, semi: bool) -> void { + const a : usize = st[P]; + const b : usize = z_tok_end(src, a); + if (word_is(src, a, b, "_") and z_peek_is(src, a, "=")) { + z_step(src, st); + z_step(src, st); + puts(st, out, "_ = "); + const ra : usize = st[P]; + const rb : usize = z_tok_end(src, ra); + z_expr(src, st, out); + const one : bool = is_alpha(head(src, ra)) and skip_ws(src, rb) == st[P]; + if (semi) { z_semi(src, st, out); } + // `_ = x;` only silences Zig's unused-name error and means nothing; t27b reads it for a parameter and refuses it + // for a local or a const, so it is kept for a parameter and dropped (the whole line) for anything else + if (semi and one and !bad(st[ERR])) { + const e : usize = z_find(src, st, ra, rb); + if (e != 0 and st[z_slot(e) + 2] != Z_K_PARAM) { st[W] = st[Z_STMT] - 1 - 4 * st[IND]; } + } + return; + } + const lw : usize = st[W]; + z_postfix(src, st, out); + if (bad(st[ERR])) { return; } + const rw : usize = st[W]; + const oa : usize = st[P]; + const ob : usize = z_tok_end(src, oa); + const kind : usize = z_asg_kind(src, oa, ob); + if (kind != 0) { + z_step(src, st); + if (kind == 3) { + const lv : usize = z_asg_level(src, oa, ob); + puts(st, out, " = "); + z_copy_out(st, out, lw, rw); + puts(st, out, " "); + put_src(src, st, out, oa, ob - 1); + puts(st, out, " "); + const rs : usize = st[W]; + const rl : usize = z_expr(src, st, out); + if (!keep_bare(rl, lv, false)) { wrap(st, out, rs); } + } else { + puts(st, out, " "); + put_src(src, st, out, oa, ob); + puts(st, out, " "); + z_expr(src, st, out); + } + } + if (semi) { z_semi(src, st, out); } +} + +/// How many elements the array literal whose `{` has just been read holds: the commas at depth 0 before the +/// matching `}`, plus one unless the last thing is a comma. 0 for `{}`. +pub fn z_count_elems(src: []const u8, p: usize) -> usize { + var n : usize = 0; + var depth : usize = 0; + var any : bool = false; + var q : usize = p; + while (q < src.len) { + const e : usize = z_tok_end(src, q); + if (e == q + 1 and is_close(src[q])) { + if (depth == 0) { + if (any) { n = n + 1; } + return n; + } + depth = depth - 1; + any = true; + } else if (e == q + 1 and is_open(src[q])) { + depth = depth + 1; + any = true; + } else if (e == q + 1 and src[q] == B_COMMA and depth == 0) { + n = n + 1; + any = false; + } else { + any = true; + } + q = skip_ws(src, e); + } + return n; +} + +/// An array literal `[_]T{ a, b }` or `[N]T{ a, b }` as a declaration's initializer, written ` : [K]T = [a, b]` +/// (K is the count of elements; an empty literal is refused). Z_CLS is the array's class. +pub fn z_array_lit(src: []const u8, st: []usize, out: []u8) -> void { + z_want(src, st, "["); + const la : usize = st[P]; + const lb : usize = z_tok_end(src, la); + const q1 : usize = skip_ws(src, lb); + const q2 : usize = skip_ws(src, z_tok_end(src, q1)); + const q3 : usize = skip_ws(src, z_tok_end(src, q2)); + if (!(word_is(src, la, lb, "_") or is_digit(head(src, la)))) { + z_refuse_here(src, st, Z_R_ARRAYLIT); + return; + } + if (head(src, q1) != B_RBRACKET or head(src, q3) != B_LBRACE) { + z_refuse_here(src, st, Z_R_ARRAYLIT); + return; + } + const k : usize = z_count_elems(src, skip_ws(src, z_tok_end(src, q3))); + if (k == 0) { + refuse(st, Z_R_ARRAYLIT, la, la, lb); + return; + } + z_step(src, st); + z_want(src, st, "]"); + puts(st, out, " : ["); + put_num(st, out, k); + puts(st, out, "]"); + z_scalar_type(src, st, out); + const elem : usize = st[Z_CLS]; + z_want(src, st, "{"); + puts(st, out, " = ["); + var n : usize = 0; + while (!bad(st[ERR]) and st[P] < src.len and !z_is(src, st[P], "}")) { + if (n > 0) { puts(st, out, ", "); } + z_expr(src, st, out); + n = n + 1; + if (!z_take(src, st, ",")) { break; } + } + z_want(src, st, "}"); + puts(st, out, "]"); + st[Z_CLS] = ZC_ARRAY + elem; +} + +/// `const x = e;`, `var x: T = e;`, `const a = [_]T{..};` -- a local, or a const of the file. The name is entered +/// in the table when the whole declaration was read. +pub fn z_decl(src: []const u8, st: []usize, out: []u8) -> void { + const ka : usize = st[P]; + const kb : usize = z_tok_end(src, ka); + const is_var : bool = word_is(src, ka, kb, "var"); + z_step(src, st); + const na : usize = st[P]; + const nb : usize = z_tok_end(src, na); + if (!is_alpha(head(src, na)) or word_is(src, na, nb, "_")) { + z_refuse_here(src, st, Z_R_SYNTAX); + return; + } + z_step(src, st); + if (is_var) { puts(st, out, "var "); } else { puts(st, out, "const "); } + if (st[Z_QUAL] == 1) { z_put_qualified(src, st, out, na, nb); } else { put_ident(src, st, out, na, nb); } + var cls : usize = ZC_NONE; + if (z_take(src, st, ":")) { + puts(st, out, " : "); + z_type(src, st, out); + cls = st[Z_CLS]; + z_want(src, st, "="); + puts(st, out, " = "); + if (z_is(src, st[P], "undefined")) { + puts(st, out, "undefined"); + z_step(src, st); + } else { + z_expr(src, st, out); + } + } else { + z_want(src, st, "="); + const ns : usize = z_ns_init(src, st, st[P]); + if (ns != 0 and !bad(st[ERR])) { + // `const math = std.math;` in a body: a name for a namespace, nothing is written (the line goes) + const semi : usize = find_at(src, st[P], B_SEMI); + st[P] = skip_ws(src, semi + 1); + st[W] = st[Z_STMT] - 1 - 4 * st[IND]; + if (!z_add_name(st, Z_K_NS, na, nb, ns)) { refuse(st, Z_R_FULL, na, na, nb); } + return; + } + if (head(src, st[P]) == B_LBRACKET) { + z_array_lit(src, st, out); + } else { + puts(st, out, " = "); + z_expr(src, st, out); + } + cls = st[Z_CLS]; + } + z_want(src, st, ";"); + puts(st, out, ";"); + if (!bad(st[ERR]) and !z_add_name(st, Z_K_LOCAL, na, nb, cls)) { refuse(st, Z_R_FULL, na, na, nb); } +} + +/// The body of an if, a while, a for or a prong: `{`, a block or one statement, `}`; the statements are one +/// level in. The names declared inside are dropped at the end. +pub fn z_body(src: []const u8, st: []usize, out: []u8) -> void { + if (bad(st[ERR])) { return; } + puts(st, out, "{"); + st[IND] = st[IND] + 1; + const saved : usize = st[Z_NNAMES]; + const one : usize = st[Z_ONE]; + if (z_take(src, st, "{")) { + st[Z_ONE] = 0; + z_stmts(src, st, out); + } else { + st[Z_ONE] = 1; + z_line(src, st, out); + } + st[Z_ONE] = one; + st[Z_NNAMES] = saved; + st[IND] = st[IND] - 1; + newline(st, out); + puts(st, out, "}"); +} + +/// The statements of a block whose `{` was read, up to and past its `}`. +pub fn z_stmts(src: []const u8, st: []usize, out: []u8) -> void { + while (!bad(st[ERR]) and st[P] < src.len and !z_is(src, st[P], "}")) { + z_line(src, st, out); + } + z_want(src, st, "}"); +} + +/// A statement on a line of its own, at the current indent. An array literal passed as `&[_]T{ .. }` cannot be written in +/// place (t27 has no such expression): the read stops there asking for it (Z_R_HOIST), the line declares the array first, +/// `const t27o_a1 : [3]T = [..];`, and the statement is read again with the literal standing for that name. +pub fn z_line(src: []const u8, st: []usize, out: []u8) -> void { + if (bad(st[ERR])) { return; } + newline(st, out); + var w0 : usize = st[W]; + const p0 : usize = st[P]; + var tries : usize = 0; + while (tries < 13) { + st[Z_STMT] = w0; + z_stmt(src, st, out); + if (st[ERR] != Z_R_HOIST or tries == 12) { return; } + const hpos : usize = st[AT]; + st[ERR] = 0; + st[W] = w0; + st[Z_TMP] = st[Z_TMP] + 1; + const tmp : usize = st[Z_TMP]; + st[P] = skip_ws(src, z_tok_end(src, hpos)); + puts(st, out, "const t27o_a"); + put_num(st, out, tmp); + z_array_lit(src, st, out); + puts(st, out, ";"); + if (bad(st[ERR])) { return; } + if (!z_add_name(st, Z_K_HOIST, hpos, tmp, st[Z_CLS])) { + refuse(st, Z_R_FULL, hpos, hpos, hpos + 1); + return; + } + newline(st, out); + w0 = st[W]; + st[P] = p0; + tries = tries + 1; + } +} + +/// `if (c) a else b` as a statement; an `else if` chain stays a chain. +pub fn z_if_stmt(src: []const u8, st: []usize, out: []u8) -> void { + z_step(src, st); + puts(st, out, "if ("); + z_want(src, st, "("); + z_expr(src, st, out); + z_want(src, st, ")"); + puts(st, out, ") "); + if (z_is(src, st[P], "|")) { + z_refuse_here(src, st, Z_R_PAYLOAD); + return; + } + z_body(src, st, out); + if (!bad(st[ERR]) and z_take(src, st, "else")) { + puts(st, out, " else "); + if (z_is(src, st[P], "if")) { + z_if_stmt(src, st, out); + } else { + z_body(src, st, out); + } + } +} + +/// `while (c) body`, `while (c) : (step) body`. t27 reads the continue expression as Zig does, so it is kept where it +/// is: moved to the end of the body it would be skipped by a `continue`. +pub fn z_while_stmt(src: []const u8, st: []usize, out: []u8) -> void { + z_step(src, st); + puts(st, out, "while ("); + z_want(src, st, "("); + z_expr(src, st, out); + z_want(src, st, ")"); + puts(st, out, ")"); + if (z_is(src, st[P], "|")) { + z_refuse_here(src, st, Z_R_PAYLOAD); + return; + } + if (z_take(src, st, ":")) { + z_want(src, st, "("); + puts(st, out, " : ("); + z_simple(src, st, out, false); + z_want(src, st, ")"); + puts(st, out, ")"); + } + puts(st, out, " "); + z_body(src, st, out); +} + +/// `return e;` `return;` `break;` `continue;` +pub fn z_jump(src: []const u8, st: []usize, out: []u8) -> void { + const a : usize = st[P]; + const b : usize = z_tok_end(src, a); + const is_ret : bool = word_is(src, a, b, "return"); + z_step(src, st); + put_src(src, st, out, a, b); + if (!z_is(src, st[P], ";")) { + if (is_ret) { + puts(st, out, " "); + z_expr(src, st, out); + } else { + z_refuse_here(src, st, Z_R_LABEL); + return; + } + } + z_semi(src, st, out); +} + +/// `for (s) |x|`, `for (a..b) |i|` stay as they are. A `for` over several objects, `for (s, 0..) |x, i|` or +/// `for (a, b) |x, y|`, is written as an index loop (`for (0..a.len) |i|` with `const x = a[i];` first): t27b has no +/// multi-object `for`. (Zig checks in Debug that the objects are equally long; the index loop does not.) +pub fn z_for_stmt(src: []const u8, st: []usize, out: []u8) -> void { + z_step(src, st); + z_want(src, st, "("); + const base : usize = st[Z_NNAMES]; + puts(st, out, "for ("); + const w0 : usize = st[W]; + var is_a : [4]usize = [0] ** 4; + var is_b : [4]usize = [0] ** 4; + var is_k : [4]usize = [0] ** 4; + var is_c : [4]usize = [0] ** 4; + const f0 : usize = st[P]; + const f1 : usize = z_tok_end(src, f0); + z_expr(src, st, out); + if (bad(st[ERR])) { return; } + const c1 : usize = st[Z_CLS]; + is_a[0] = f0; + is_b[0] = f1; + is_k[0] = 1; + is_c[0] = c1; + var n : usize = 1; + var range : bool = false; + if (z_take(src, st, "..")) { + range = true; + puts(st, out, ".."); + z_expr(src, st, out); + } else { + if (n == 1 and skip_ws(src, f1) != st[P]) { is_k[0] = 2; } + while (!bad(st[ERR]) and z_take(src, st, ",")) { + const g0 : usize = st[P]; + const g1 : usize = z_tok_end(src, g0); + if (n >= 4 or is_k[0] == 2) { + z_refuse_here(src, st, Z_R_LOOPFORM); + return; + } + if (z_is(src, g0, "0") and z_peek_is(src, g0, "..")) { + z_step(src, st); + z_step(src, st); + is_k[n] = 3; + } else { + const e : usize = z_find(src, st, g0, g1); + if (!is_alpha(head(src, g0)) or e == 0 or !z_is_seq(st[z_slot(e) + 3])) { + z_refuse_here(src, st, Z_R_LOOPFORM); + return; + } + is_a[n] = g0; + is_b[n] = g1; + is_k[n] = 1; + is_c[n] = st[z_slot(e) + 3]; + z_step(src, st); + } + n = n + 1; + } + if (n == 1 and !z_is_seq(c1)) { + refuse(st, Z_R_LOOPFORM, f0, f0, f1); + return; + } + } + z_want(src, st, ")"); + // the captures + var ca : [4]usize = [0] ** 4; + var cb : [4]usize = [0] ** 4; + var m : usize = 0; + z_want(src, st, "|"); + while (!bad(st[ERR]) and m < 4) { + const xa : usize = st[P]; + const xb : usize = z_tok_end(src, xa); + if (!is_alpha(head(src, xa))) { + refuse(st, Z_R_PAYLOAD, xa, xa, xb); + return; + } + ca[m] = xa; + cb[m] = xb; + m = m + 1; + z_step(src, st); + if (!z_take(src, st, ",")) { break; } + } + z_want(src, st, "|"); + if (bad(st[ERR])) { return; } + if (m != n) { + z_refuse_here(src, st, Z_R_LOOPFORM); + return; + } + // the loop header and the names the body sees + var k : usize = 0; + var seqs : usize = 0; + while (k < n) { + if (is_k[k] == 1) { seqs = seqs + 1; } + k = k + 1; + } + var lead : usize = 0; + var idx : usize = 0; + var have_idx : bool = false; + k = 0; + while (k < n) { + if (is_k[k] == 3) { + idx = k; + have_idx = true; + } + k = k + 1; + } + const several : bool = n > 1; + if (several and (seqs == 0 or !is_alpha(head(src, f0)))) { + z_refuse_here(src, st, Z_R_LOOPFORM); + return; + } + if (several) { + st[W] = w0; + puts(st, out, "0.."); + put_ident(src, st, out, is_a[0], is_b[0]); + puts(st, out, ".len) |"); + if (have_idx) { + put_ident(src, st, out, ca[idx], cb[idx]); + } else { + st[Z_TMP] = st[Z_TMP] + 1; + lead = st[Z_TMP]; + puts(st, out, "t27o_i"); + put_num(st, out, lead); + } + puts(st, out, "| {"); + } else { + puts(st, out, ") |"); + put_ident(src, st, out, ca[0], cb[0]); + puts(st, out, "| {"); + } + k = 0; + while (k < n) { + var cls : usize = ZC_INT; + if (is_k[k] == 1 or is_k[k] == 2) { + cls = z_elem(is_c[k]); + if (range) { cls = ZC_INT; } + } + if (!word_is(src, ca[k], cb[k], "_") and !z_add_name(st, Z_K_LOCAL, ca[k], cb[k], cls)) { + refuse(st, Z_R_FULL, ca[k], ca[k], cb[k]); + return; + } + k = k + 1; + } + st[IND] = st[IND] + 1; + if (several) { + k = 0; + while (k < n) { + if (is_k[k] == 1 and !word_is(src, ca[k], cb[k], "_")) { + newline(st, out); + puts(st, out, "const "); + put_ident(src, st, out, ca[k], cb[k]); + puts(st, out, " = "); + put_ident(src, st, out, is_a[k], is_b[k]); + puts(st, out, "["); + if (have_idx) { + put_ident(src, st, out, ca[idx], cb[idx]); + } else { + puts(st, out, "t27o_i"); + put_num(st, out, lead); + } + puts(st, out, "];"); + } + k = k + 1; + } + } + if (z_take(src, st, "{")) { + z_stmts(src, st, out); + } else { + z_line(src, st, out); + } + st[IND] = st[IND] - 1; + newline(st, out); + puts(st, out, "}"); + st[Z_NNAMES] = base; +} + +/// Is the switch item at src[p] a range `lo...hi`? (A negative literal is two tokens.) +pub fn z_item_is_range(src: []const u8, p: usize) -> bool { + var q : usize = p; + if (z_is(src, q, "-")) { q = skip_ws(src, z_tok_end(src, q)); } + return z_peek_is(src, q, "..."); +} + +/// The scrutinee of a switch statement: the name it is, or the temporary it was bound to. +pub fn z_put_scrutinee(src: []const u8, st: []usize, out: []u8, simple: bool, sa: usize, sb: usize, tmp: usize) -> void { + if (simple) { + put_ident(src, st, out, sa, sb); + } else { + puts(st, out, "t27o_s"); + put_num(st, out, tmp); + } +} + +/// One item of a prong as the condition it is: `s == 3`, or `(s >= 1 and s <= 9)` for `1...9`. +pub fn z_switch_item(src: []const u8, st: []usize, out: []u8, simple: bool, sa: usize, sb: usize, tmp: usize) -> void { + const range : bool = z_item_is_range(src, st[P]); + if (range) { puts(st, out, "("); } + z_put_scrutinee(src, st, out, simple, sa, sb, tmp); + if (range) { puts(st, out, " >= "); } else { puts(st, out, " == "); } + z_unary(src, st, out); + if (range) { + z_want(src, st, "..."); + puts(st, out, " and "); + z_put_scrutinee(src, st, out, simple, sa, sb, tmp); + puts(st, out, " <= "); + z_unary(src, st, out); + puts(st, out, ")"); + } +} + +/// The body of a prong: a block, or one statement that has no `;` of its own in a switch (`=> return x,`). +pub fn z_prong_body(src: []const u8, st: []usize, out: []u8) -> void { + if (z_is(src, st[P], "{")) { + z_body(src, st, out); + return; + } + puts(st, out, "{"); + st[IND] = st[IND] + 1; + newline(st, out); + st[Z_STMT] = st[W]; + const a : usize = st[P]; + const b : usize = z_tok_end(src, a); + if (word_is(src, a, b, "return") or word_is(src, a, b, "break") or word_is(src, a, b, "continue")) { + z_step(src, st); + put_src(src, st, out, a, b); + if (!z_is(src, st[P], ",") and !z_is(src, st[P], "}")) { + if (word_is(src, a, b, "return")) { + puts(st, out, " "); + z_expr(src, st, out); + } else { + z_refuse_here(src, st, Z_R_LABEL); + } + } + } else if (word_is(src, a, b, "unreachable")) { + refuse(st, Z_R_UNREACH, a, a, b); + } else { + z_simple(src, st, out, false); + } + puts(st, out, ";"); + st[IND] = st[IND] - 1; + newline(st, out); + puts(st, out, "}"); +} + +/// `switch (e) { 1 => a, 2, 3 => b, 4...9 => c, else => d }` as a statement: an if chain over the scrutinee, which is +/// bound once (`const t27o_s1 = e;`) unless it is a name. +pub fn z_switch_stmt(src: []const u8, st: []usize, out: []u8) -> void { + z_step(src, st); + z_want(src, st, "("); + const sa : usize = st[P]; + const sb : usize = z_tok_end(src, sa); + const simple : bool = is_alpha(head(src, sa)) and z_peek_is(src, sa, ")"); + var tmp : usize = 0; + if (simple) { + const known : usize = z_lookup(src, st, sa, sb); + if (known == 0) { return; } + z_step(src, st); + } else { + st[Z_TMP] = st[Z_TMP] + 1; + tmp = st[Z_TMP]; + puts(st, out, "const t27o_s"); + put_num(st, out, tmp); + puts(st, out, " = "); + z_expr(src, st, out); + puts(st, out, ";"); + newline(st, out); + } + z_want(src, st, ")"); + z_want(src, st, "{"); + var first : bool = true; + while (!bad(st[ERR]) and st[P] < src.len and !z_is(src, st[P], "}")) { + if (z_is(src, st[P], "else")) { + z_step(src, st); + z_want(src, st, "=>"); + if (first) { puts(st, out, "if (true) "); } else { puts(st, out, " else "); } + } else { + if (first) { puts(st, out, "if ("); } else { puts(st, out, " else if ("); } + var n : usize = 0; + while (!bad(st[ERR])) { + if (n > 0) { puts(st, out, " or "); } + z_switch_item(src, st, out, simple, sa, sb, tmp); + n = n + 1; + if (!z_take(src, st, ",")) { break; } + } + z_want(src, st, "=>"); + puts(st, out, ") "); + } + first = false; + z_prong_body(src, st, out); + _ = z_take(src, st, ","); + } + z_want(src, st, "}"); +} + +// ---------------------------------------------------------------- the file's own tests + +/// An operand written for an operator of level `lv`, parenthesized when its own level needs it. +pub fn z_operand(src: []const u8, st: []usize, out: []u8, lv: usize, left: bool) -> void { + const w : usize = st[W]; + const l : usize = z_expr(src, st, out); + if (!keep_bare(l, lv, left)) { wrap(st, out, w); } +} + +/// The comma that ends the argument starting at src[p], past which the next argument starts; the end of the source +/// when there is none. +pub fn z_next_arg(src: []const u8, p: usize) -> usize { + const cm : usize = find_at(src, p, B_COMMA); + if (cm >= src.len) { return src.len; } + return skip_ws(src, cm + 1); +} + +/// `try std.testing.expect...(..);` in a test. The calls and what they become: +/// expect(c) assert(c) +/// expectEqual(e, a) assert(a == e) +/// expectApproxEqAbs(e, a, t) assert(@abs(a - e) <= t) +/// expectEqualStrings(e, a) assert(std.mem.eql(u8, a, e)) +/// expectEqualSlices(u8, e, a) the same +/// Any other call of the library is refused naming it. +pub fn z_test_call(src: []const u8, st: []usize, out: []u8) -> void { + const ta : usize = st[P]; + z_step(src, st); + const a0 : usize = st[P]; + const b0 : usize = z_tok_end(src, a0); + var ns : usize = 0; + const e : usize = z_find(src, st, a0, b0); + if (e != 0 and st[z_slot(e) + 2] == Z_K_NS) { ns = st[z_slot(e) + 3]; } + if (e == 0 and word_is(src, a0, b0, "std")) { ns = Z_NS_STD; } + if (ns == 0) { + refuse(st, Z_R_ERRORS, ta, ta, b0); + return; + } + z_step(src, st); + if (ns == Z_NS_STD) { + z_want(src, st, "."); + const sa : usize = st[P]; + ns = z_ns_of(src, sa, z_tok_end(src, sa)); + z_step(src, st); + } + if (ns != Z_NS_TESTING) { + refuse(st, Z_R_ERRORS, ta, ta, b0); + return; + } + z_want(src, st, "."); + const ma : usize = st[P]; + const mb : usize = z_tok_end(src, ma); + z_step(src, st); + z_want(src, st, "("); + var after : usize = 0; + if (word_is(src, ma, mb, "expect")) { + puts(st, out, "assert("); + z_expr(src, st, out); + after = st[P]; + puts(st, out, ")"); + } else if (word_is(src, ma, mb, "expectEqual")) { + const pe : usize = st[P]; + const pa : usize = z_next_arg(src, pe); + puts(st, out, "assert("); + st[P] = pa; + z_operand(src, st, out, L_CMP, true); + after = st[P]; + puts(st, out, " == "); + st[P] = pe; + z_operand(src, st, out, L_CMP, false); + puts(st, out, ")"); + } else if (word_is(src, ma, mb, "expectApproxEqAbs")) { + const pe : usize = st[P]; + const pa : usize = z_next_arg(src, pe); + const pt : usize = z_next_arg(src, pa); + puts(st, out, "assert(@abs("); + st[P] = pa; + z_operand(src, st, out, L_ADD, true); + puts(st, out, " - "); + st[P] = pe; + z_operand(src, st, out, L_ADD, false); + puts(st, out, ") <= "); + st[P] = pt; + z_operand(src, st, out, L_CMP, false); + after = st[P]; + puts(st, out, ")"); + } else if (word_is(src, ma, mb, "expectApproxEqRel")) { + const pe : usize = st[P]; + const pa : usize = z_next_arg(src, pe); + const pt : usize = z_next_arg(src, pa); + puts(st, out, "assert(@abs("); + st[P] = pa; + z_operand(src, st, out, L_ADD, true); + puts(st, out, " - "); + st[P] = pe; + z_operand(src, st, out, L_ADD, false); + puts(st, out, ") <= @max(@abs("); + st[P] = pa; + z_expr(src, st, out); + puts(st, out, "), @abs("); + st[P] = pe; + z_expr(src, st, out); + puts(st, out, ")) * "); + st[P] = pt; + z_operand(src, st, out, L_MUL, false); + after = st[P]; + puts(st, out, ")"); + } else if (word_is(src, ma, mb, "expectEqualStrings") or word_is(src, ma, mb, "expectEqualSlices")) { + if (word_is(src, ma, mb, "expectEqualSlices")) { + if (!z_take(src, st, "u8")) { + refuse(st, Z_R_TESTCALL, ma, ma, mb); + return; + } + z_want(src, st, ","); + } + const pe : usize = st[P]; + const pa : usize = z_next_arg(src, pe); + puts(st, out, "assert(std.mem.eql(u8, "); + st[P] = pa; + z_expr(src, st, out); + after = st[P]; + puts(st, out, ", "); + st[P] = pe; + z_expr(src, st, out); + puts(st, out, "))"); + } else { + refuse(st, Z_R_TESTCALL, ma, ma, mb); + return; + } + if (bad(st[ERR])) { return; } + st[P] = after; + _ = z_take(src, st, ","); + z_want(src, st, ")"); + z_want(src, st, ";"); + puts(st, out, ";"); +} + +// ---------------------------------------------------------------- one statement + +/// One statement at st[P], through its `;` or its closing `}`. +pub fn z_stmt(src: []const u8, st: []usize, out: []u8) -> void { + if (bad(st[ERR])) { return; } + if (st[P] >= src.len) { + z_refuse_here(src, st, Z_R_SYNTAX); + return; + } + const a : usize = st[P]; + const b : usize = z_tok_end(src, a); + if (word_is(src, a, b, "const") or word_is(src, a, b, "var")) { + z_decl(src, st, out); + } else if (word_is(src, a, b, "if")) { + z_if_stmt(src, st, out); + } else if (word_is(src, a, b, "while")) { + z_while_stmt(src, st, out); + } else if (word_is(src, a, b, "for")) { + z_for_stmt(src, st, out); + } else if (word_is(src, a, b, "switch")) { + z_switch_stmt(src, st, out); + } else if (word_is(src, a, b, "return") or word_is(src, a, b, "break") or word_is(src, a, b, "continue")) { + z_jump(src, st, out); + } else if (word_is(src, a, b, "try")) { + if (st[Z_INTEST] == 1) { z_test_call(src, st, out); } else { refuse(st, Z_R_ERRORS, a, a, b); } + } else if (word_is(src, a, b, "defer") or word_is(src, a, b, "errdefer")) { + refuse(st, Z_R_DEFER, a, a, b); + } else if (word_is(src, a, b, "comptime") or word_is(src, a, b, "inline") or word_is(src, a, b, "async") or word_is(src, a, b, "suspend") or word_is(src, a, b, "nosuspend") or word_is(src, a, b, "resume") or word_is(src, a, b, "await")) { + refuse(st, Z_R_GENERIC, a, a, b); + } else if (word_is(src, a, b, "unreachable")) { + refuse(st, Z_R_UNREACH, a, a, b); + } else if (head(src, a) == B_LBRACE) { + refuse(st, Z_R_BRACES, a, a, b); + } else if (is_alpha(head(src, a)) and z_peek_is(src, a, ":")) { + refuse(st, Z_R_LABEL, a, a, b); + } else { + z_simple(src, st, out, true); + } +} + +// ---------------------------------------------------------------- items + +/// The first depth-0 `;` or `{` from src[from]: where a fn's signature ends. +pub fn z_stop(src: []const u8, from: usize) -> usize { + var depth : usize = 0; + var q : usize = from; + while (q < src.len) { + const e : usize = z_tok_end(src, q); + if (e == q + 1) { + const c : u8 = src[q]; + if (depth == 0 and (c == B_SEMI or c == B_LBRACE)) { return q; } + if (c == B_LPAREN or c == B_LBRACKET) { depth = depth + 1; } + if (c == B_RPAREN or c == B_RBRACKET) { + if (depth == 0) { return src.len; } + depth = depth - 1; + } + } + q = skip_ws(src, e); + } + return src.len; +} + +/// Where the item that starts at src[p] ends: past the `;` of a const or var, past the body of anything else. +pub fn z_item_end(src: []const u8, p: usize) -> usize { + var q : usize = p; + var guard : usize = 0; + while (guard < 6 and (z_is(src, q, "pub") or z_is(src, q, "export") or z_is(src, q, "extern") or z_is(src, q, "inline") or z_is(src, q, "noinline") or z_is(src, q, "threadlocal"))) { + q = skip_ws(src, z_tok_end(src, q)); + guard = guard + 1; + } + const e : usize = z_tok_end(src, q); + if (word_is(src, q, e, "const") or word_is(src, q, e, "var")) { + const semi : usize = find_at(src, q, B_SEMI); + if (semi < src.len) { return semi + 1; } + return src.len; + } + const s : usize = z_stop(src, q); + if (s < src.len and src[s] == B_LBRACE) { return block_end(src, s); } + if (s < src.len) { return s + 1; } + return src.len; +} + +/// Is the token at src[q] one that sits before `fn` in a declaration (pub, export, extern, inline, noinline)? +pub fn z_mod_word(src: []const u8, q: usize) -> bool { + return z_is(src, q, "pub") or z_is(src, q, "export") or z_is(src, q, "extern") or z_is(src, q, "inline") or z_is(src, q, "noinline"); +} + +/// The class of a fn's result, read from its signature: the type word after the parameter list. +pub fn z_ret_class(src: []const u8, q: usize) -> usize { + const open : usize = find_at(src, q, B_LPAREN); + if (open >= src.len) { return ZC_NONE; } + const t : usize = skip_ws(src, block_end(src, open)); + return z_type_class(src, t, z_tok_end(src, t)); +} + +/// The class of a const's initializer, from its first token only: a number, true or false, a string. +pub fn z_init_class(src: []const u8, p: usize) -> usize { + const b : usize = z_tok_end(src, p); + const c : u8 = head(src, p); + if (is_digit(c)) { + var i : usize = p; + while (i < b) { + if (src[i] == B_DOT or src[i] == B_LOWER_E) { return ZC_LITFLOAT; } + i = i + 1; + } + return ZC_LITINT; + } + if (c == B_QUOTE) { return ZC_SLICE + ZC_INT; } + if (word_is(src, p, b, "true") or word_is(src, p, b, "false")) { return ZC_BOOL; } + return ZC_NONE; +} + +/// Does a const's initializer, whose first token is src[p], declare a type? +pub fn z_type_init(src: []const u8, p: usize) -> bool { + const b : usize = z_tok_end(src, p); + return word_is(src, p, b, "struct") or word_is(src, p, b, "enum") or word_is(src, p, b, "union") or word_is(src, p, b, "opaque") or word_is(src, p, b, "error") or word_is(src, p, b, "packed") or word_is(src, p, b, "extern") or word_is(src, p, b, "fn") or word_is(src, p, b, "anyerror"); +} + +/// Enter a file-level name; a table too small to hold the file's names is remembered (z_passes refuses the file then). +pub fn z_enter(st: []usize, kind: usize, lo: usize, hi: usize, cls: usize) -> void { + if (!z_add_name(st, kind, lo, hi, cls)) { st[Z_CRAMPED] = 1; } +} + +/// Which namespace of std the initializer starting at src[p] names, when it names one and nothing else: `@import("std")`, +/// `std`, `std.math`, or an alias of those; 0 when it is anything else. +pub fn z_ns_init(src: []const u8, st: []usize, p: usize) -> usize { + const b : usize = z_tok_end(src, p); + if (word_is(src, p, b, "@import")) { + const arg : usize = skip_ws(src, z_tok_end(src, skip_ws(src, b))); + const close : usize = skip_ws(src, z_tok_end(src, arg)); + if (z_is(src, arg, "\"std\"") and z_is(src, close, ")") and z_peek_is(src, close, ";")) { return Z_NS_STD; } + return 0; + } + var root : usize = 0; + if (word_is(src, p, b, "std")) { + root = Z_NS_STD; + } else { + const e : usize = z_find(src, st, p, b); + if (e != 0 and st[z_slot(e) + 2] == Z_K_NS) { root = st[z_slot(e) + 3]; } + } + if (root == 0) { return 0; } + const q : usize = skip_ws(src, b); + if (head(src, q) == B_SEMI) { return root; } + if (root == Z_NS_STD and z_is(src, q, ".")) { + const m : usize = skip_ws(src, z_tok_end(src, q)); + const mb : usize = z_tok_end(src, m); + if (is_alpha(head(src, m)) and head(src, skip_ws(src, mb)) == B_SEMI) { return z_ns_of(src, m, mb); } + } + return 0; +} + +/// Enter the const or var whose keyword is src[q] in the name table: an import is std (a namespace) or external, a +/// type declaration or alias is a type, a var is a var, the rest a const with the class its type or first token says. +pub fn z_scan_const(src: []const u8, st: []usize, q: usize) -> void { + const na : usize = skip_ws(src, z_tok_end(src, q)); + const nb : usize = z_tok_end(src, na); + if (!is_alpha(head(src, na))) { return; } + if (z_is(src, q, "var")) { + z_enter(st, Z_K_VARX, na, nb, ZC_NONE); + return; + } + const eq : usize = find_at(src, nb, B_EQ); + if (eq >= src.len) { return; } + const ip : usize = skip_ws(src, eq + 1); + const ib : usize = z_tok_end(src, ip); + const ns : usize = z_ns_init(src, st, ip); + if (ns != 0) { + z_enter(st, Z_K_NS, na, nb, ns); + return; + } + if (word_is(src, ip, ib, "@import") or word_is(src, ip, ib, "std")) { + z_enter(st, Z_K_EXT, na, nb, ZC_NONE); + return; + } + const lb : usize = z_container_open(src, ip); + // a container inside a container is a type like any other (its fns are counted refused with it), not a namespace + if (lb < src.len and st[Z_OWNER] == 0) { + var pure : usize = 1; + if (!z_container_pure(src, lb)) { pure = 0; } + z_enter(st, Z_K_NSPACE, na, nb, 1 - pure); + const owner : usize = st[Z_NNAMES]; + const close : usize = block_end(src, lb); + st[Z_OWNER] = owner; + var p : usize = z_next_member(src, skip_ws(src, lb + 1), close); + while (p + 1 < close) { + z_scan_item(src, st, p); + p = z_next_member(src, skip_ws(src, z_item_end(src, p)), close); + } + st[Z_OWNER] = 0; + return; + } + if (z_type_init(src, ip) or (z_scalar(src, ip, ib) and z_peek_is(src, ip, ";"))) { + z_enter(st, Z_K_TYPEX, na, nb, ZC_NONE); + return; + } + var cls : usize = z_init_class(src, ip); + if (z_is(src, skip_ws(src, nb), ":")) { + const ta : usize = skip_ws(src, z_tok_end(src, skip_ws(src, nb))); + cls = z_type_class(src, ta, z_tok_end(src, ta)); + } + z_enter(st, Z_K_CONST, na, nb, cls); +} + +/// Where the `{` is of the container (struct, enum or union) the initializer starting at src[ip] declares; src.len when it +/// declares none (a packed or extern struct, an opaque, an error set, anything else). +pub fn z_container_open(src: []const u8, ip: usize) -> usize { + if (!(z_is(src, ip, "struct") or z_is(src, ip, "enum") or z_is(src, ip, "union"))) { return src.len; } + var q : usize = skip_ws(src, z_tok_end(src, ip)); + if (head(src, q) == B_LPAREN) { q = skip_ws(src, block_end(src, q)); } + if (head(src, q) != B_LBRACE) { return src.len; } + return q; +} + +/// Does a member of a container start at src[p]: a fn, const, var, test, usingnamespace or comptime block (a field does not)? +pub fn z_decl_start(src: []const u8, p: usize) -> bool { + var q : usize = p; + var guard : usize = 0; + while (guard < 6 and (z_mod_word(src, q) or z_is(src, q, "threadlocal"))) { + q = skip_ws(src, z_tok_end(src, q)); + guard = guard + 1; + } + if (z_is(src, q, "fn") or z_is(src, q, "const") or z_is(src, q, "var") or z_is(src, q, "test") or z_is(src, q, "usingnamespace")) { return true; } + return z_is(src, q, "comptime") and z_peek_is(src, q, "{"); +} + +/// Where the next member of the container whose body ends at src[close - 1] starts, from src[p]; a field is skipped +/// through its comma. close - 1 when there is none. +pub fn z_next_member(src: []const u8, p: usize, close: usize) -> usize { + var q : usize = p; + while (q + 1 < close and !z_decl_start(src, q)) { + const cm : usize = find_at(src, q, B_COMMA); + if (cm + 1 >= close) { return close - 1; } + q = skip_ws(src, cm + 1); + } + return q; +} + +/// Does the container whose `{` is src[lb] hold only fns and consts: a namespace, whose fns are free fns named +/// `Container_fn`? A field, a variant, a var, a test, a nested type makes it a type, which is refused (its static fns +/// are still read one by one). +pub fn z_container_pure(src: []const u8, lb: usize) -> bool { + const close : usize = block_end(src, lb); + var p : usize = skip_ws(src, lb + 1); + while (p + 1 < close) { + var q : usize = p; + var guard : usize = 0; + while (guard < 6 and z_mod_word(src, q)) { + q = skip_ws(src, z_tok_end(src, q)); + guard = guard + 1; + } + if (z_is(src, q, "const")) { + const eq : usize = find_at(src, q, B_EQ); + if (eq >= src.len or z_type_init(src, skip_ws(src, eq + 1))) { return false; } + } else if (!z_is(src, q, "fn")) { + return false; + } + const end : usize = z_item_end(src, p); + if (end <= p) { return false; } + p = skip_ws(src, end); + } + return true; +} + +/// Enter the names of the item that starts at src[p]: a fn, a const or var, and the members of a namespace struct. +pub fn z_scan_item(src: []const u8, st: []usize, p: usize) -> void { + var q : usize = p; + var guard : usize = 0; + while (guard < 6 and z_mod_word(src, q)) { + q = skip_ws(src, z_tok_end(src, q)); + guard = guard + 1; + } + if (z_is(src, q, "fn")) { + const na : usize = skip_ws(src, z_tok_end(src, q)); + const nb : usize = z_tok_end(src, na); + if (is_alpha(head(src, na)) and !z_add_name(st, Z_K_FN, na, nb, z_ret_class(src, nb))) { st[Z_CRAMPED] = 1; } + } else if (z_is(src, q, "const") or z_is(src, q, "var")) { + z_scan_const(src, st, q); + } +} + +/// Every name the file declares at its top level, entered before any item is read, so that a fn may use one declared +/// below it. The table stops growing here (Z_NFILE). +pub fn z_prescan(src: []const u8, st: []usize) -> void { + var p : usize = skip_ws(src, 0); + while (p < src.len) { + var end : usize = z_item_end(src, p); + if (end <= p) { end = p + 1; } + z_scan_item(src, st, p); + p = skip_ws(src, end); + } + st[Z_NFILE] = st[Z_NNAMES]; +} + +/// Mark the file-level name src[a .. b] refused, from the second round on (the first only learns classes), and +/// count that the round is not the last. +pub fn z_mark_refused(src: []const u8, st: []usize, a: usize, b: usize) -> void { + if (st[Z_ROUND] < 2) { return; } + const e : usize = z_find_file(src, st, a, b); + if (e == 0) { return; } + const s : usize = z_slot(e); + if (st[s + 2] == Z_K_FN) { + st[s + 2] = Z_K_FNX; + st[Z_GREW] = st[Z_GREW] + 1; + } else if (st[s + 2] == Z_K_CONST) { + st[s + 2] = Z_K_CONSTX; + st[Z_GREW] = st[Z_GREW] + 1; + } +} + +/// The comment that stands where a refused item was: what it was, its name, the line, the reason, the source text +/// the reason names and its line, and how many fns the item held. +pub fn z_put_refusal(src: []const u8, st: []usize, out: []u8, what: []const u8, na: usize, nb: usize, pos: usize, nf: usize) -> void { + puts(st, out, "// t27o: refused "); + puts(st, out, what); + puts(st, out, " "); + if (st[Z_CUR] != 0) { + put_text(src, st, out, st[z_slot(st[Z_CUR])], st[z_slot(st[Z_CUR]) + 1], 60); + puts(st, out, "."); + } + put_text(src, st, out, na, nb, 60); + puts(st, out, " (line "); + const ln : usize = line_at(src, st, pos); + put_num(st, out, ln); + puts(st, out, "): "); + z_put_why(st, out, st[ERR]); + if (st[NHI] > st[NLO]) { + puts(st, out, " `"); + put_text(src, st, out, st[NLO], st[NHI], 40); + puts(st, out, "` a"); + puts(st, out, "t line "); + const at_ln : usize = line_at(src, st, st[AT]); + put_num(st, out, at_ln); + } + if (nf > 0) { + puts(st, out, "; "); + put_num(st, out, nf); + puts(st, out, " fn inside"); + } + puts(st, out, "\n\n"); +} + +/// The parameters of a fn, `(a: u32, s: []const u8)`, written and entered as names. +pub fn z_params(src: []const u8, st: []usize, out: []u8) -> void { + z_want(src, st, "("); + puts(st, out, "("); + var n : usize = 0; + while (!bad(st[ERR]) and st[P] < src.len and !z_is(src, st[P], ")")) { + if (n > 0) { puts(st, out, ", "); } + const a : usize = st[P]; + const b : usize = z_tok_end(src, a); + if (word_is(src, a, b, "comptime") or word_is(src, a, b, "noalias") or word_is(src, a, b, "anytype")) { + refuse(st, Z_R_GENERIC, a, a, b); + return; + } + if (!is_alpha(head(src, a)) or word_is(src, a, b, "_")) { + refuse(st, Z_R_SYNTAX, a, a, b); + return; + } + z_step(src, st); + z_want(src, st, ":"); + put_ident(src, st, out, a, b); + puts(st, out, ": "); + z_type(src, st, out); + if (!bad(st[ERR]) and !z_add_name(st, Z_K_PARAM, a, b, st[Z_CLS])) { refuse(st, Z_R_FULL, a, a, b); } + n = n + 1; + if (!z_take(src, st, ",")) { break; } + } + z_want(src, st, ")"); + puts(st, out, ")"); +} + +/// A fn: signature, then the statements of its body. The item is refused whole when anything in it is. +pub fn z_fn_item(src: []const u8, st: []usize, out: []u8, p0: usize, q: usize, end: usize, is_pub: bool) -> void { + st[Z_NFN] = st[Z_NFN] + 1; + const w0 : usize = st[W]; + st[P] = skip_ws(src, z_tok_end(src, q)); + const na : usize = st[P]; + const nb : usize = z_tok_end(src, na); + if (!is_alpha(head(src, na))) { + refuse(st, Z_R_SYNTAX, na, na, nb); + } else { + z_step(src, st); + if (is_pub) { puts(st, out, "pub "); } + puts(st, out, "fn "); + z_put_qualified(src, st, out, na, nb); + z_params(src, st, out); + const la : usize = st[P]; + const lb : usize = z_tok_end(src, la); + if (word_is(src, la, lb, "align") or word_is(src, la, lb, "callconv") or word_is(src, la, lb, "linksection") or word_is(src, la, lb, "addrspace")) { + refuse(st, Z_R_LINKAGE, la, la, lb); + } + puts(st, out, " -> "); + z_type(src, st, out); + const rc : usize = st[Z_CLS]; + z_want(src, st, "{"); + puts(st, out, " {"); + st[IND] = 1; + z_stmts(src, st, out); + st[IND] = 0; + newline(st, out); + puts(st, out, "}"); + const e : usize = z_find_file(src, st, na, nb); + if (e != 0 and !bad(st[ERR])) { st[z_slot(e) + 3] = rc; } + } + st[Z_NNAMES] = st[Z_NFILE]; + if (bad(st[ERR])) { + st[W] = w0; + z_put_refusal(src, st, out, "fn", na, nb, p0, 0); + z_mark_refused(src, st, na, nb); + } else { + puts(st, out, "\n\n"); + st[Z_NOK] = st[Z_NOK] + 1; + } + st[ERR] = 0; +} + +/// An `export`, `extern` or `inline` fn, or one with a calling convention: refused as it is, with the fns it holds. +pub fn z_linkage_item(src: []const u8, st: []usize, out: []u8, p0: usize, q: usize, end: usize) -> void { + st[Z_NFN] = st[Z_NFN] + 1; + const na : usize = skip_ws(src, z_tok_end(src, q)); + const nb : usize = z_tok_end(src, na); + refuse(st, Z_R_LINKAGE, p0, p0, z_tok_end(src, p0)); + z_put_refusal(src, st, out, "fn", na, nb, p0, 0); + z_mark_refused(src, st, na, nb); + st[ERR] = 0; +} + +/// A const at the top of the file. Imports and type aliases of std leave no trace (the table has them); a type +/// declaration is refused with the fns inside it; a var is refused; a const is converted. +pub fn z_const_item(src: []const u8, st: []usize, out: []u8, p0: usize, q: usize, end: usize, is_pub: bool) -> void { + const na : usize = skip_ws(src, z_tok_end(src, q)); + const nb : usize = z_tok_end(src, na); + const w0 : usize = st[W]; + const eq : usize = find_at(src, nb, B_EQ); + var ip : usize = src.len; + if (eq < src.len) { ip = skip_ws(src, eq + 1); } + const ib : usize = z_tok_end(src, ip); + const kind : usize = z_find_file(src, st, na, nb); + if (z_is(src, q, "var")) { + refuse(st, Z_R_GLOBALVAR, na, na, nb); + } else if (ip >= src.len) { + refuse(st, Z_R_SYNTAX, na, na, nb); + } else if (word_is(src, ip, ib, "@import") or word_is(src, ip, ib, "std")) { + return; + } else if (kind != 0 and st[z_slot(kind) + 2] == Z_K_NSPACE) { + z_nspace_item(src, st, out, p0, na, nb, ip, kind); + return; + } else if (z_type_init(src, ip)) { + const nf : usize = count_fns(src, p0, end); + st[Z_NFN] = st[Z_NFN] + nf; + refuse(st, Z_R_TYPEDECL, ip, ip, ib); + z_put_refusal(src, st, out, "const", na, nb, p0, nf); + st[ERR] = 0; + return; + } else if (z_scalar(src, ip, ib) and z_peek_is(src, ip, ";")) { + return; + } else { + st[P] = q; + if (is_pub) { puts(st, out, "pub "); } + st[Z_INCONST] = 1; + st[Z_QUAL] = 1; + z_decl(src, st, out); + st[Z_QUAL] = 0; + st[Z_INCONST] = 0; + const last : usize = st[Z_NNAMES]; + if (kind != 0 and last > st[Z_NFILE] and !bad(st[ERR])) { st[z_slot(kind) + 3] = st[z_slot(last) + 3]; } + } + st[Z_NNAMES] = st[Z_NFILE]; + if (bad(st[ERR])) { + st[W] = w0; + z_put_refusal(src, st, out, "const", na, nb, p0, 0); + z_mark_refused(src, st, na, nb); + } else { + puts(st, out, "\n\n"); + } + st[ERR] = 0; +} + +/// A container (struct, enum, union) of the file: its members are read as items of their own, named `Container_member`, +/// and the calls `Container.member(..)` and the bare calls between its members are written the same way. A container that +/// holds anything but fns and consts is a type, refused as such; its static fns still convert, and its methods are refused +/// one by one for the type they take. +pub fn z_nspace_item(src: []const u8, st: []usize, out: []u8, p0: usize, na: usize, nb: usize, ip: usize, e: usize) -> void { + const lb : usize = z_container_open(src, ip); + const close : usize = block_end(src, lb); + if (st[z_slot(e) + 3] == 1) { + refuse(st, Z_R_TYPEDECL, ip, ip, z_tok_end(src, ip)); + z_put_refusal(src, st, out, "const", na, nb, p0, 0); + st[ERR] = 0; + } + st[Z_CUR] = e; + st[P] = z_next_member(src, skip_ws(src, lb + 1), close); + while (st[P] + 1 < close) { + z_item(src, st, out); + st[P] = z_next_member(src, st[P], close); + } + st[Z_CUR] = 0; +} + +/// Does out hold the text s at out[j]? +pub fn z_out_has(out: []const u8, j: usize, s: []const u8) -> bool { + if (j + s.len > out.len) { return false; } + var k : usize = 0; + while (k < s.len) { + if (out[j + k] != s[k]) { return false; } + k = k + 1; + } + return true; +} + +/// Is there a test named like out[w1 .. w2] (written `test {`) before w1? +pub fn z_test_taken(out: []const u8, w1: usize, w2: usize) -> bool { + if (w2 > out.len or w2 < w1) { return false; } + const n : usize = w2 - w1; + var j : usize = 0; + while (j + 5 + n + 2 <= w1) { + if ((j == 0 or out[j - 1] == B_NL) and z_out_has(out, j, "test ") and z_out_has(out, j + 5 + n, " {")) { + var k : usize = 0; + while (k < n and out[j + 5 + k] == out[w1 + k]) { k = k + 1; } + if (k == n) { return true; } + } + j = j + 1; + } + return false; +} + +/// FNV-1a over out[a .. b), 32 bits. +pub fn z_hash(out: []const u8, a: usize, b: usize) -> usize { + var h : usize = 2166136261; + var i : usize = a; + while (i < b and i < out.len) { + h = ((h ^ (out[i] as usize)) * 16777619) % 4294967296; + i = i + 1; + } + return h; +} + +/// Has a test of the name out[w1 .. w2] been written before? Every name's hash is entered in the table at the tail of +/// the state vector; only a hit is checked against the text (a scan of the output), so a file of 20000 tests is not +/// 20000 scans. A name that finds no free slot in 16 probes goes unchecked. +pub fn z_test_seen(st: []usize, out: []const u8, w1: usize, w2: usize) -> bool { + if (st[Z_HASHON] == 0) { return z_test_taken(out, w1, w2); } + const h : usize = z_hash(out, w1, w2); + const base : usize = st[Z_LIMIT]; + var k : usize = 0; + while (k < 16) { + const s : usize = base + (h + k) % Z_HSLOTS; + if (st[s] == 0) { + st[s] = h + 1; + return false; + } + if (st[s] == h + 1 and z_test_taken(out, w1, w2)) { return true; } + k = k + 1; + } + return false; +} + +/// The identifier a test name becomes: letters, digits and `_` kept, every other run of bytes one `_`, none at either +/// end; a name that starts with a digit gets `t_`, a name that is nothing `t27o_test`, a name an earlier test already +/// has gets its ordinal. A test named by an identifier (`test foo {}`) is that identifier. +pub fn z_put_test_name(src: []const u8, st: []usize, out: []u8, na: usize, nb: usize, ord: usize) -> void { + const w1 : usize = st[W]; + var lo : usize = na; + var hi : usize = nb; + if (head(src, na) == B_QUOTE and nb > na + 1) { + lo = na + 1; + hi = nb - 1; + } + var pending : bool = false; + var any : bool = false; + var i : usize = lo; + while (i < hi) { + const c : u8 = src[i]; + if (is_word(c)) { + if (pending) { put(st, out, B_UNDERSCORE); } + pending = false; + if (!any and is_digit(c)) { puts(st, out, "t_"); } + put(st, out, c); + any = true; + } else if (any) { + pending = true; + } + i = i + 1; + } + if (!any) { puts(st, out, "t27o_test"); } + if (any and reserved(src, lo, hi)) { puts(st, out, "_"); } + if (z_test_seen(st, out, w1, st[W])) { + puts(st, out, "_"); + put_num(st, out, ord); + } +} + +/// A `test "name" { .. }` block: a t27 `test` with the statements of the body, where `try std.testing.expect..` +/// are asserts. The test is refused whole when anything in it is. +pub fn z_test_item(src: []const u8, st: []usize, out: []u8, p0: usize, q: usize, end: usize) -> void { + st[Z_NTS] = st[Z_NTS] + 1; + const w0 : usize = st[W]; + st[P] = skip_ws(src, z_tok_end(src, q)); + const na : usize = st[P]; + const nb : usize = z_tok_end(src, na); + var labeled : bool = false; + if (head(src, na) == B_QUOTE or is_alpha(head(src, na))) { + labeled = true; + z_step(src, st); + } + puts(st, out, "test "); + z_put_test_name(src, st, out, na, nb, st[Z_NTS]); + puts(st, out, " {"); + z_want(src, st, "{"); + st[Z_INTEST] = 1; + st[IND] = 1; + z_stmts(src, st, out); + st[IND] = 0; + st[Z_INTEST] = 0; + newline(st, out); + puts(st, out, "}"); + st[Z_NNAMES] = st[Z_NFILE]; + if (bad(st[ERR])) { + st[W] = w0; + z_put_refusal(src, st, out, "test", na, nb, p0, 0); + } else { + puts(st, out, "\n\n"); + st[Z_NTEST] = st[Z_NTEST] + 1; + } + st[ERR] = 0; +} + +/// An item t27o does not read at all (comptime, usingnamespace, a stray token): refused, with the fns it holds. +pub fn z_other_item(src: []const u8, st: []usize, out: []u8, p0: usize, q: usize, end: usize) -> void { + const kb : usize = z_tok_end(src, q); + const nf : usize = count_fns(src, p0, end); + st[Z_NFN] = st[Z_NFN] + nf; + refuse(st, Z_R_ITEM, q, q, kb); + z_put_refusal(src, st, out, "top-level", q, kb, p0, nf); + st[ERR] = 0; +} + +/// One top-level item at st[P]. +pub fn z_item(src: []const u8, st: []usize, out: []u8) -> void { + const p0 : usize = st[P]; + var end : usize = z_item_end(src, p0); + if (end <= p0) { end = p0 + 1; } + var q : usize = p0; + var is_pub : bool = false; + var linkage : bool = false; + var guard : usize = 0; + while (guard < 6 and z_mod_word(src, q)) { + if (z_is(src, q, "pub")) { is_pub = true; } else { linkage = true; } + q = skip_ws(src, z_tok_end(src, q)); + if (head(src, q) == B_QUOTE) { q = skip_ws(src, z_tok_end(src, q)); } + guard = guard + 1; + } + st[ERR] = 0; + st[IND] = 0; + if (z_is(src, q, "fn")) { + if (linkage) { z_linkage_item(src, st, out, p0, q, end); } else { z_fn_item(src, st, out, p0, q, end, is_pub); } + } else if (z_is(src, q, "const") or z_is(src, q, "var")) { + z_const_item(src, st, out, p0, q, end, is_pub); + } else if (z_is(src, q, "test") and !is_pub) { + z_test_item(src, st, out, p0, q, end); + } else { + z_other_item(src, st, out, p0, q, end); + } + st[P] = skip_ws(src, end); +} + +// ---------------------------------------------------------------- the file + +/// Does a file name end in .zig? `tri t27o` sends such a file here. +pub fn is_zig_name(name: []const u8) -> bool { + if (name.len < 4) { return false; } + const f : usize = name.len - 4; + return name[f] == B_DOT and name[f + 1] == B_LOWER_Z and name[f + 2] == Z_B_I and name[f + 3] == Z_B_G; +} + +/// Is the text s at src[i], whatever the case? +pub fn z_ci_at(src: []const u8, i: usize, s: []const u8) -> bool { + if (i + s.len > src.len) { return false; } + var k : usize = 0; + while (k < s.len) { + if (z_lower(src[i + k]) != s[k]) { return false; } + k = k + 1; + } + return true; +} + +/// Does a generator mark the file: `.vibee`, "generated" or "do not edit", in any case, within its first 20 lines? +/// Such a file is not converted (its source spec is), the owner's rule of #10597. +pub fn z_generated(src: []const u8) -> bool { + var lines : usize = 0; + var i : usize = 0; + while (i < src.len and lines < 20) { + if (src[i] == B_NL) { lines = lines + 1; } + if (z_ci_at(src, i, ".vibee") or z_ci_at(src, i, "generated") or z_ci_at(src, i, "do not edit")) { return true; } + i = i + 1; + } + return false; +} + +/// The module name a file stem becomes: letters, digits, `_` and `-` kept, any other byte `_`; `Converted` for no name. +pub fn z_put_module(st: []usize, out: []u8, name: []const u8) -> void { + if (name.len == 0) { puts(st, out, "Converted"); } + var i : usize = 0; + while (i < name.len) { + const c : u8 = name[i]; + if (is_word(c) or c == B_MINUS) { put(st, out, c); } else { put(st, out, B_UNDERSCORE); } + i = i + 1; + } +} + +/// The header of a converted spec. +pub fn z_header(st: []usize, out: []u8, name: []const u8) -> void { + puts(st, out, "// SPDX-License-Identifier: Apache-2.0\n; "); + z_put_module(st, out, name); + puts(st, out, ".t27 -- converted from Zig by t27o (Ouroboros), specs/tools/t27o/zig_convert.t27; read it before it lands.\n; ASCII only (L3)\n\nmodule "); + z_put_module(st, out, name); + puts(st, out, ";\n\n"); +} + +/// The report line, as the Rust front end writes it: fns seen, converted (tests among them), refused, coverage. +pub fn z_report(st: []usize, out: []u8) -> void { + puts(st, out, "// t27o: "); + put_num(st, out, st[Z_NFN]); + puts(st, out, " fn seen, "); + put_num(st, out, st[Z_NOK]); + puts(st, out, " converted ("); + put_num(st, out, st[Z_NTEST]); + puts(st, out, " of "); + put_num(st, out, st[Z_NTS]); + puts(st, out, " tests), "); + put_num(st, out, st[Z_NFN] - st[Z_NOK]); + puts(st, out, " refused (0 kept as stubs); "); + if (st[Z_NFN] == 0) { + puts(st, out, "coverage unknown: no fn seen\n"); + return; + } + puts(st, out, "coverage "); + put_num(st, out, st[Z_NOK] * 100 / st[Z_NFN]); + puts(st, out, "%\n"); +} + +/// One pass over the items: the output starts over, the name table keeps its refusals. +pub fn z_pass(src: []const u8, name: []const u8, out: []u8, st: []usize, whole: bool) -> void { + st[W] = 0; + st[ERR] = 0; + st[IND] = 0; + st[Z_NFN] = 0; + st[Z_NOK] = 0; + st[Z_NTS] = 0; + st[Z_NTEST] = 0; + st[Z_TMP] = 0; + st[Z_INTEST] = 0; + st[Z_GREW] = 0; + st[Z_NNAMES] = st[Z_NFILE]; + st[Z_CUR] = 0; + st[Z_QUAL] = 0; + st[Z_INCONST] = 0; + st[Z_ONE] = 0; + st[Z_OWNER] = 0; + st[LNPOS] = 0; + st[LNNL] = 0; + if (st[Z_HASHON] == 1) { + var h : usize = st[Z_LIMIT]; + while (h < st.len) { + st[h] = 0; + h = h + 1; + } + } + if (whole) { z_header(st, out, name); } + st[P] = skip_ws(src, 0); + while (st[P] < src.len) { z_item(src, st, out); } + if (whole) { z_report(st, out); } +} + +/// Passes until no fn or const is newly refused: the first learns the classes of the file's names, the next ones +/// refuse what uses a refused name, in whatever order the file declares them. The table only grows, so this ends. +pub fn z_passes(src: []const u8, name: []const u8, out: []u8, st: []usize, whole: bool) -> usize { + if (st.len < Z_NAMES) { return 0; } + var i : usize = 0; + while (i < Z_FIXED) { + st[i] = 0; + i = i + 1; + } + st[Z_LIMIT] = st.len; + if (st.len >= Z_NAMES + 4096 + Z_HSLOTS) { + st[Z_LIMIT] = st.len - Z_HSLOTS; + st[Z_HASHON] = 1; + } + if (z_generated(src)) { + if (whole) { z_header(st, out, name); } + puts(st, out, "// t27o: refused file (line 1): generated; conv"); + puts(st, out, "ert its source spec instead\n\n"); + if (whole) { z_report(st, out); } + return st[W]; + } + z_prescan(src, st); + if (st[Z_CRAMPED] == 1) { + if (whole) { z_header(st, out, name); } + puts(st, out, "// t27o: refused file (line 1): name table full\n\n"); + if (whole) { z_report(st, out); } + return st[W]; + } + var round : usize = 1; + var again : bool = true; + while (again and round <= 64) { + st[Z_ROUND] = round; + z_pass(src, name, out, st, whole); + again = round < 2 or st[Z_GREW] > 0; + round = round + 1; + } + return st[W]; +} + +/// The items of a Zig file as t27, with no header and no report (what the tests below compare). +pub fn z_body_text(src: []const u8, out: []u8, st: []usize) -> usize { + return z_passes(src, "", out, st, false); +} + +/// The Zig source `src` as the t27 spec `module ;`. The state vector holds the fixed slots and the name table +/// (Z_NAME_SLOTS a name, as many as fit). The result is the size of the output; the buffer was too small when it is +/// above out.len. +pub fn to_t27(src: []const u8, name: []const u8, out: []u8, st: []usize) -> usize { + return z_passes(src, name, out, st, true); +} + +// ---------------------------------------------------------------- tests +// Each test converts a Zig snippet and compares the whole output text (out_is). + +test scalar_and_slice_fns_convert_and_braces_are_always_written { + var out : [512]u8 = undefined; + var st : [2048]usize = [0] ** 2048; + const n : usize = z_body_text("pub fn add(a: u32, b: u32) u32 {\n return a + b * 2;\n}\nfn count(s: []const u8, c: u8) usize {\n var k: usize = 0;\n for (s) |x| {\n if (x == c) k += 1;\n }\n return k;\n}\nfn noop() void {}\n", &out, &st); + assert(out_is(n, &out, 0, "pub fn add(a: u32, b: u32) -> u32 {\n return a + b * 2;\n}\n\nfn count(s: []const u8, c: u8) -> usize {\n var k : usize = 0;\n for (s) |x| {\n if (x == c) {\n k += 1;\n }\n }\n return k;\n}\n\nfn noop() -> void {\n}\n\n")); + assert(st[Z_NFN] == 3); + assert(st[Z_NOK] == 3); +} + +test if_else_while_and_the_continue_expression { + var out : [768]u8 = undefined; + var st : [2048]usize = [0] ** 2048; + const n : usize = z_body_text("fn f(n: u32) u32 {\n var i: u32 = 0;\n var t: u32 = 0;\n while (i < n) : (i += 1) {\n if (i % 2 == 0) continue;\n t += i;\n }\n if (t > 10) return 1 else if (t > 5) return 2 else return 3;\n}\nfn g(x: i32) i32 {\n if (x < 0) {\n return -x;\n } else {\n return x;\n }\n}\n", &out, &st); + assert(out_is(n, &out, 0, "fn f(n: u32) -> u32 {\n var i : u32 = 0;\n var t : u32 = 0;\n while (i < n) : (i += 1) {\n if (i % 2 == 0) {\n continue;\n }\n t += i;\n }\n if (t > 10) {\n return 1;\n } else if (t > 5) {\n return 2;\n } else {\n return 3;\n }\n}\n\nfn g(x: i32) -> i32 {\n if (x < 0) {\n return -x;\n } else {\n return x;\n }\n}\n\n")); +} + +test bitwise_and_shift_operands_keep_their_parentheses { + var out : [512]u8 = undefined; + var st : [2048]usize = [0] ** 2048; + const n : usize = z_body_text("fn f(a: u32, b: u32) bool {\n return a & 3 == b or !(a < b) and a << 1 > b;\n}\nfn g(a: u32, b: u32, c: u32) u32 {\n return a - (b - c) + a * (b + c) | b ^ c;\n}\n", &out, &st); + assert(out_is(n, &out, 0, "fn f(a: u32, b: u32) -> bool {\n return (a & 3) == b or !(a < b) and a << 1 > b;\n}\n\nfn g(a: u32, b: u32, c: u32) -> u32 {\n return (a - (b - c) + a * (b + c)) | (b ^ c);\n}\n\n")); +} + +test imports_leave_nothing_and_scalar_consts_convert { + var out : [512]u8 = undefined; + var st : [2048]usize = [0] ** 2048; + const n : usize = z_body_text("const std = @import(\"std\");\nconst math = std.math;\npub const A: u32 = 4;\npub const B = A * 2 + 1;\nconst PI2: f64 = math.pi * 2.0;\npub const TABLE = [_]u8{ 1, 2, 3 };\npub fn elem(i: usize) u8 {\n return TABLE[i];\n}\npub fn tau() f64 {\n return PI2;\n}\n", &out, &st); + assert(out_is(n, &out, 0, "pub const A : u32 = 4;\n\npub const B = A * 2 + 1;\n\nconst PI2 : f64 = std.math.pi * 2.0;\n\npub const TABLE : [3]u8 = [1, 2, 3];\n\npub fn elem(i: usize) -> u8 {\n return TABLE[i];\n}\n\npub fn tau() -> f64 {\n return PI2;\n}\n\n")); + assert(st[Z_NFN] == 2); + assert(st[Z_NOK] == 2); +} + +test compound_assignments_t27_cannot_read_are_written_out { + var out : [768]u8 = undefined; + var st : [2048]usize = [0] ** 2048; + const n : usize = z_body_text("fn f(x: u32, a: []u8) u32 {\n var y = x;\n y <<= 2;\n y >>= 1;\n y += 1;\n y -= 1;\n y *= 3;\n y /= 2;\n y %= 7;\n y &= 255;\n y |= 1;\n y ^= 2;\n a[0] <<= 1;\n a[1] +%= 3 + y;\n var z: u8 = 1;\n z -%= 2;\n z *%= 3;\n return y;\n}\n", &out, &st); + assert(out_is(n, &out, 0, "fn f(x: u32, a: []u8) -> u32 {\n var y = x;\n y = y << 2;\n y = y >> 1;\n y += 1;\n y -= 1;\n y *= 3;\n y /= 2;\n y %= 7;\n y &= 255;\n y |= 1;\n y ^= 2;\n a[0] = a[0] << 1;\n a[1] = a[1] +% (3 + y);\n var z : u8 = 1;\n z = z -% 2;\n z = z *% 3;\n return y;\n}\n\n")); +} + +test for_loops_over_a_slice_a_range_and_several_objects { + var out : [1024]u8 = undefined; + var st : [2048]usize = [0] ** 2048; + const n : usize = z_body_text("fn sum(s: []const u32) u32 {\n var t: u32 = 0;\n for (s) |x| t += x;\n return t;\n}\nfn tri(n: usize) usize {\n var t: usize = 0;\n for (0..n) |i| {\n t += i;\n }\n return t;\n}\nfn weighted(a: []const u32) u32 {\n var t: u32 = 0;\n for (a, 0..) |x, i| {\n t += x * @as(u32, @intCast(i));\n }\n return t;\n}\nfn dot(a: []const f64, b: []const f64) f64 {\n var t: f64 = 0;\n for (a, b) |x, y| {\n t += x * y;\n }\n return t;\n}\nfn bad_pointer(a: []u32) void {\n for (a) |*x| x.* += 1;\n}\nfn bad_object(n: u32) void {\n for (n) |x| _ = x;\n}\n", &out, &st); + assert(out_is(n, &out, 0, "fn sum(s: []const u32) -> u32 {\n var t : u32 = 0;\n for (s) |x| {\n t += x;\n }\n return t;\n}\n\nfn tri(n: usize) -> usize {\n var t : usize = 0;\n for (0..n) |i| {\n t += i;\n }\n return t;\n}\n\nfn weighted(a: []const u32) -> u32 {\n var t : u32 = 0;\n for (0..a.len) |i| {\n const x = a[i];\n t += x * @as(u32, @intCast(i));\n }\n return t;\n}\n\nfn dot(a: []const f64, b: []const f64) -> f64 {\n var t : f64 = 0;\n for (0..a.len) |t27o_i1| {\n const x = a[t27o_i1];\n const y = b[t27o_i1];\n t += x * y;\n }\n return t;\n}\n\n// t27o: refused fn bad_pointer (line 27): payload capture `*` at line 28\n\n// t27o: refused fn bad_object (line 30): loop form `n` at line 31\n\n")); +} + +test a_switch_statement_is_an_if_chain { + var out : [768]u8 = undefined; + var st : [2048]usize = [0] ** 2048; + const n : usize = z_body_text("fn classify(c: u8) u8 {\n switch (c) {\n '0'...'9' => return 1,\n 'a', 'b' => return 2,\n else => return 0,\n }\n}\nfn bump(n: u32, k: u32) u32 {\n var r: u32 = 0;\n switch (n + k) {\n 0 => r = 5,\n 1, 2 => {\n r = 6;\n r += 1;\n },\n else => {},\n }\n return r;\n}\n", &out, &st); + assert(out_is(n, &out, 0, "fn classify(c: u8) -> u8 {\n if ((c >= '0' and c <= '9')) {\n return 1;\n } else if (c == 'a' or c == 'b') {\n return 2;\n } else {\n return 0;\n }\n}\n\nfn bump(n: u32, k: u32) -> u32 {\n var r : u32 = 0;\n const t27o_s1 = n + k;\n if (t27o_s1 == 0) {\n r = 5;\n } else if (t27o_s1 == 1 or t27o_s1 == 2) {\n r = 6;\n r += 1;\n } else {\n }\n return r;\n}\n\n")); +} + +test a_switch_expression_stays_a_switch_when_each_prong_is_one_item { + var out : [768]u8 = undefined; + var st : [2048]usize = [0] ** 2048; + const n : usize = z_body_text("fn pick(n: u32) u32 {\n return switch (n) {\n 0 => 10,\n 1 => 20,\n else => 30,\n };\n}\nfn lists(n: u32) u32 {\n return switch (n) {\n 1, 2 => 10,\n else => 0,\n };\n}\nfn ranges(n: u32) u32 {\n return switch (n) {\n 1...3 => 10,\n else => 0,\n };\n}\nfn chars(c: u8) u32 {\n return switch (c) {\n 'a' => 1,\n else => 0,\n };\n}\n", &out, &st); + assert(out_is(n, &out, 0, "fn pick(n: u32) -> u32 {\n return switch (n) {\n 0 => 10,\n 1 => 20,\n else => 30,\n };\n}\n\n// t27o: refused fn lists (line 8): switch form `,` at line 10\n\n// t27o: refused fn ranges (line 14): switch form `...` at line 16\n\n// t27o: refused fn chars (line 20): switch form `'a'` at line 22\n\n")); +} + +test if_expressions_are_values_and_get_parentheses_as_operands { + var out : [768]u8 = undefined; + var st : [2048]usize = [0] ** 2048; + const n : usize = z_body_text("fn sign(x: i32) i32 {\n return if (x < 0) -1 else if (x > 0) 1 else 0;\n}\nfn clamp(x: i32, lo: i32, hi: i32) i32 {\n const m = if (x < lo) lo else x;\n return (if (m > hi) hi else m) + 0;\n}\nfn no_else(x: i32) i32 {\n return if (x < 0) 1;\n}\n", &out, &st); + assert(out_is(n, &out, 0, "fn sign(x: i32) -> i32 {\n return if (x < 0) -1 else if (x > 0) 1 else 0;\n}\n\nfn clamp(x: i32, lo: i32, hi: i32) -> i32 {\n const m = if (x < lo) lo else x;\n return (if (m > hi) hi else m) + 0;\n}\n\n// t27o: refused fn no_else (line 8): syntax `;` at line 9\n\n")); +} + +test builtins_with_a_t27_form_that_means_the_same { + var out : [768]u8 = undefined; + var st : [2048]usize = [0] ** 2048; + const n : usize = z_body_text("fn f(n: u32, x: f64, a: u8) f64 {\n const r = @as(f64, @floatFromInt(n)) / 2.0;\n const q: u8 = @intCast(n);\n const m = @min(n, n + 1) + @max(n, 3 * n);\n const b = @abs(x) + @sqrt(x) + @floor(x) + @ceil(x) + @round(x) + @exp(x) + @log(x) + @cos(x);\n const c = @intFromFloat(x) + @intFromBool(a > 1) + @divTrunc(n, 2) + @rem(n, 2) + @mod(n, 2);\n return r + b + @floatCast(x);\n}\n", &out, &st); + assert(out_is(n, &out, 0, "fn f(n: u32, x: f64, a: u8) -> f64 {\n const r = @as(f64, @floatFromInt(n)) / 2.0;\n const q : u8 = @intCast(n);\n const m = @min(n, n + 1) + @max(n, 3 * n);\n const b = @abs(x) + @sqrt(x) + @floor(x) + @ceil(x) + @round(x) + @exp(x) + @log(x) + @cos(x);\n const c = @intFromFloat(x) + @intFromBool(a > 1) + @divTrunc(n, 2) + @rem(n, 2) + @mod(n, 2);\n return r + b + @floatCast(x);\n}\n\n")); +} + +test builtins_t27b_cannot_run_are_refused_with_the_call { + var out : [1024]u8 = undefined; + var st : [2048]usize = [0] ** 2048; + const n : usize = z_body_text("fn a(n: i32) u32 { return @abs(n); }\nfn b(n: u32) u32 { return @min(n, 10); }\nfn c(x: f64) f64 { return @exp(1.0); }\nfn d(n: u32) u32 { return n >> @intCast(n); }\nfn e(x: f64) f64 { return @rem(x, 2.0); }\nfn f(x: f64) f64 { return @sin(x); }\nfn g(x: u32) u8 { return @truncate(x); }\nfn h(x: f64) f64 { return @abs(x) + @min(x, 1.0) + @exp(@as(f64, 1)); }\n", &out, &st); + assert(out_is(n, &out, 0, "// t27o: refused fn a (line 1): builtin operand t27b cannot run `@abs(n)` at line 1\n\n// t27o: refused fn b (line 2): builtin operand t27b cannot run `@min(n, 10)` at line 2\n\n// t27o: refused fn c (line 3): builtin operand t27b cannot run `@exp(1.0)` at line 3\n\n// t27o: refused fn d (line 4): builtin operand t27b cannot run `@intCast` at line 4\n\n// t27o: refused fn e (line 5): builtin operand t27b cannot run `@rem(x, 2.0)` at line 5\n\n// t27o: refused fn f (line 6): builtin `@sin` at line 6\n\n// t27o: refused fn g (line 7): builtin `@truncate` at line 7\n\nfn h(x: f64) -> f64 {\n return @abs(x) + @min(x, 1.0) + @exp(@as(f64, 1));\n}\n\n")); +} + +test std_calls_with_a_measured_t27_form_and_the_rest_refused_by_name { + var out : [1280]u8 = undefined; + var st : [2048]usize = [0] ** 2048; + const n : usize = z_body_text("const std = @import(\"std\");\nconst math = std.math;\nconst mem = std.mem;\nfn consts() f64 {\n return std.math.pi + math.e + math.tau + math.phi;\n}\nfn limits() i64 {\n return std.math.maxInt(u32) + math.minInt(i8) + std.math.maxInt(i64);\n}\nfn same(a: []const u8, b: []const u8) bool {\n return mem.eql(u8, a, b);\n}\nfn root(x: f64) f64 {\n return math.sqrt(x) + math.floor(x) + std.math.cos(x);\n}\nfn special() bool {\n return std.math.isNan(std.math.nan(f64)) or std.math.isPositiveInf(std.math.inf(f32));\n}\nfn power(x: f64) f64 {\n return std.math.pow(f64, x, 2.0);\n}\nfn prefix(a: []const u8, b: []const u8) bool {\n return mem.startsWith(u8, a, b);\n}\nfn other(a: []const u16, b: []const u16) bool {\n return std.mem.eql(u16, a, b);\n}\nfn out() void {\n std.debug.print(\"x\", .{});\n}\nfn lit_root() f64 {\n return math.floor(2.5);\n}\n", &out, &st); + assert(out_is(n, &out, 0, "fn consts() -> f64 {\n return std.math.pi + std.math.e + 6.283185307179586 + 1.618033988749895;\n}\n\nfn limits() -> i64 {\n return 4294967295 + -128 + 9223372036854775807;\n}\n\nfn same(a: []const u8, b: []const u8) -> bool {\n return std.mem.eql(u8, a, b);\n}\n\nfn root(x: f64) -> f64 {\n return @sqrt(x) + @floor(x) + @cos(x);\n}\n\nfn special() -> bool {\n return std.math.isNan(std.math.nan(f64)) or std.math.isPositiveInf(std.math.inf(f32));\n}\n\n// t27o: refused fn power (line 19): std call `std.math.pow` at line 20\n\n// t27o: refused fn prefix (line 22): std call `mem.startsWith` at line 23\n\n// t27o: refused fn other (line 25): std call `std.mem.eql` at line 26\n\n// t27o: refused fn out (line 28): std call `std.debug.print` at line 29\n\n// t27o: refused fn lit_root (line 31): builtin operand t27b cannot run `math.floor(2.5)` at line 32\n\n")); +} + +test test_blocks_become_tests_and_the_expectations_asserts { + var out : [1024]u8 = undefined; + var st : [2048]usize = [0] ** 2048; + const n : usize = z_body_text("const std = @import(\"std\");\nfn add(a: u32, b: u32) u32 {\n return a + b;\n}\nfn half(x: f64) f64 {\n return x / 2.0;\n}\nfn same(a: []const u8, b: []const u8) bool {\n return std.mem.eql(u8, a, b);\n}\ntest \"add\" {\n try std.testing.expectEqual(@as(u32, 5), add(2, 3));\n try std.testing.expectEqual(true, add(1, 1) < 3);\n try std.testing.expectEqual(add(1, 2) | 4, @as(u32, 7));\n try std.testing.expect(add(0, 0) == 0);\n try std.testing.expect(!same(\"a\", \"b\"));\n}\ntest \"approximate values\" {\n const h = half(3.0);\n try std.testing.expectApproxEqAbs(@as(f64, 1.5), h, 0.001);\n try std.testing.expectApproxEqRel(@as(f64, 1.5), h, 0.001);\n try std.testing.expectApproxEqAbs(1.0, h - 0.5, 1.0 - 0.9);\n}\ntest \"texts\" {\n try std.testing.expectEqualStrings(\"ab\", \"ab\");\n try std.testing.expectEqualSlices(u8, \"ab\", \"ab\");\n try std.testing.expectEqualSlices(u32, \"ab\", \"ab\");\n}\n", &out, &st); + assert(out_is(n, &out, 0, "fn add(a: u32, b: u32) -> u32 {\n return a + b;\n}\n\nfn half(x: f64) -> f64 {\n return x / 2.0;\n}\n\nfn same(a: []const u8, b: []const u8) -> bool {\n return std.mem.eql(u8, a, b);\n}\n\ntest add {\n assert(add(2, 3) == @as(u32, 5));\n assert((add(1, 1) < 3) == true);\n assert(@as(u32, 7) == (add(1, 2) | 4));\n assert(add(0, 0) == 0);\n assert(!same(\"a\", \"b\"));\n}\n\ntest approximate_values {\n const h = half(3.0);\n assert(@abs(h - @as(f64, 1.5)) <= 0.001);\n assert(@abs(h - @as(f64, 1.5)) <= @max(@abs(h), @abs(@as(f64, 1.5))) * 0.001);\n assert(@abs(h - 0.5 - 1.0) <= 1.0 - 0.9);\n}\n\n// t27o: refused test \"texts\" (line 24): testing call `expectEqualSlices` at line 27\n\n")); + assert(st[Z_NTS] == 3); +} + +test test_names_become_identifiers_and_stay_unique { + var out : [512]u8 = undefined; + var st : [2048]usize = [0] ** 2048; + const n : usize = z_body_text("test \"sums, products & more\" {}\ntest \"3 is a digit\" {}\ntest \"a-b\" {}\ntest \"a b\" {}\ntest \"a_b\" {}\ntest plain {}\ntest {}\ntest \"\" {}\ntest \"error\" {}\n", &out, &st); + assert(out_is(n, &out, 0, "test sums_products_more {\n}\n\ntest t_3_is_a_digit {\n}\n\ntest a_b {\n}\n\ntest a_b_4 {\n}\n\ntest a_b_5 {\n}\n\ntest plain {\n}\n\ntest t27o_test {\n}\n\ntest t27o_test_8 {\n}\n\ntest error_ {\n}\n\n")); + assert(st[Z_NTEST] == 9); +} + +test a_test_that_uses_a_refused_fn_is_refused_with_it { + var out : [768]u8 = undefined; + var st : [2048]usize = [0] ** 2048; + const n : usize = z_body_text("const std = @import(\"std\");\nfn good(x: u32) u32 {\n return x + 1;\n}\nfn bad(p: *u32) u32 {\n return p.*;\n}\ntest \"calls good\" {\n try std.testing.expectEqual(@as(u32, 2), good(1));\n}\ntest \"calls bad\" {\n var x: u32 = 1;\n try std.testing.expectEqual(@as(u32, 1), bad(&x));\n}\ntest \"errors\" {\n try std.testing.expectError(error.Oops, good(1));\n}\ntest \"allocator\" {\n const a = std.testing.allocator;\n _ = a;\n}\ntest \"try elsewhere\" {\n const x = try good(1);\n _ = x;\n}\n", &out, &st); + assert(out_is(n, &out, 0, "fn good(x: u32) -> u32 {\n return x + 1;\n}\n\n// t27o: refused fn bad (line 5): pointer `*` at line 5\n\ntest calls_good {\n assert(good(1) == @as(u32, 2));\n}\n\n// t27o: refused test \"calls bad\" (line 11): uses refused `bad` at line 13\n\n// t27o: refused test \"errors\" (line 15): testing call `expectError` at line 16\n\n// t27o: refused test \"allocator\" (line 18): std call `std.testing.allocator` at line 19\n\n// t27o: refused test \"try elsewhere\" (line 22): error handling `try` at line 23\n\n")); + assert(st[Z_NTEST] == 1); + assert(st[Z_NTS] == 5); +} + +test array_literal_arguments_are_declared_ahead_of_their_statement { + var out : [1024]u8 = undefined; + var st : [2048]usize = [0] ** 2048; + const n : usize = z_body_text("const std = @import(\"std\");\nfn sum(s: []const u32) u32 {\n var t: u32 = 0;\n for (s) |x| t += x;\n return t;\n}\nfn dot(a: []const u32, b: []const u32) u32 {\n return sum(a) * sum(b);\n}\ntest \"sums\" {\n try std.testing.expectEqual(@as(u32, 6), sum(&[_]u32{ 1, 2, 3 }));\n try std.testing.expectEqual(@as(u32, 21), dot(&[_]u32{ 1, 2 }, &[_]u32{ 3, 4 }));\n const data = [_]u32{ 5, 6 };\n try std.testing.expectEqual(@as(u32, 11), sum(&data));\n if (sum(&[_]u32{1}) == 1) {\n try std.testing.expect(true);\n }\n}\nfn bad() void {\n const t = [_][]const u8{ \"a\", \"b\" };\n _ = t;\n}\n", &out, &st); + assert(out_is(n, &out, 0, "fn sum(s: []const u32) -> u32 {\n var t : u32 = 0;\n for (s) |x| {\n t += x;\n }\n return t;\n}\n\nfn dot(a: []const u32, b: []const u32) -> u32 {\n return sum(a) * sum(b);\n}\n\ntest sums {\n const t27o_a1 : [3]u32 = [1, 2, 3];\n assert(sum(&t27o_a1) == @as(u32, 6));\n const t27o_a2 : [2]u32 = [1, 2];\n const t27o_a3 : [2]u32 = [3, 4];\n assert(dot(&t27o_a2, &t27o_a3) == @as(u32, 21));\n const data : [2]u32 = [5, 6];\n assert(sum(&data) == @as(u32, 11));\n const t27o_a4 : [1]u32 = [1];\n if (sum(&t27o_a4) == 1) {\n assert(true);\n }\n}\n\n// t27o: refused fn bad (line 19): array literal `_` at line 20\n\n")); +} + +test discards_of_a_local_are_dropped_and_of_a_parameter_kept { + var out : [512]u8 = undefined; + var st : [2048]usize = [0] ** 2048; + const n : usize = z_body_text("fn f(a: u32, b: u32) u32 {\n const k = a * 2;\n _ = k;\n _ = b;\n _ = a + 1;\n return a;\n}\n", &out, &st); + assert(out_is(n, &out, 0, "fn f(a: u32, b: u32) -> u32 {\n const k = a * 2;\n _ = b;\n _ = a + 1;\n return a;\n}\n\n")); +} + +test a_callee_refused_anywhere_refuses_its_callers_in_any_order { + var out : [768]u8 = undefined; + var st : [2048]usize = [0] ** 2048; + const n : usize = z_body_text("fn first(x: u32) u32 {\n return second(x) + 1;\n}\nfn second(x: u32) u32 {\n return third(x) + 1;\n}\nfn third(x: u32) u32 {\n return fourth(x) + 1;\n}\nfn fourth(x: *u32) u32 {\n return x.*;\n}\nfn fine(x: u32) u32 {\n return x;\n}\nconst BAD = other.value;\nfn uses_const() u32 {\n return BAD + 1;\n}\n", &out, &st); + assert(out_is(n, &out, 0, "// t27o: refused fn first (line 1): uses refused `second` at line 2\n\n// t27o: refused fn second (line 4): uses refused `third` at line 5\n\n// t27o: refused fn third (line 7): uses refused `fourth` at line 8\n\n// t27o: refused fn fourth (line 10): pointer `*` at line 10\n\nfn fine(x: u32) -> u32 {\n return x;\n}\n\n// t27o: refused const BAD (line 16): undeclared `other` at line 16\n\n// t27o: refused fn uses_const (line 17): uses refused `BAD` at line 18\n\n")); + assert(st[Z_NFN] == 6); + assert(st[Z_NOK] == 1); +} + +test every_refusal_names_its_kind_its_token_and_its_line { + var out : [2560]u8 = undefined; + var st : [2048]usize = [0] ** 2048; + const n : usize = z_body_text("const Point = struct {\n x: u32,\n pub fn len(self: Point) u32 { return self.x; }\n pub fn neg(self: Point) u32 { return 0 - self.x; }\n};\nconst Mode = enum { a, b };\nvar counter: u32 = 0;\nconst other = @import(\"other.zig\");\nfn ptr(p: *u32) void { _ = p; }\nfn opt(x: ?u32) u32 { return x orelse 0; }\nfn err(x: u32) !u32 { return x; }\nfn typed(p: Point) u32 { return p.x; }\nfn gen(comptime T: type, x: T) T { return x; }\nfn any(x: anytype) u32 { _ = x; return 0; }\nfn tryit(x: u32) u32 { return try err(x); }\nfn catches(x: u32) u32 { return err(x) catch 0; }\nfn defers(x: u32) u32 { defer counter += 1; return x; }\nfn unreach(x: u32) u32 { if (x > 1) unreachable; return x; }\nfn glob(x: u32) u32 { return x + counter; }\nfn ext(x: u32) u32 { return other.f(x); }\nfn field(x: u32) u32 { return x.y; }\nfn anon() u32 { const p = .{ .a = 1 }; _ = p; return 1; }\nfn lab(x: u32) u32 { blk: { _ = x; } return 0; }\nfn blockstmt(x: u32) u32 { { _ = x; } return 0; }\nfn payload(s: []const u8) u32 { if (s.len > 0) |x| { _ = x; } return 0; }\nfn wide(x: u128) u128 { return x; }\nfn catfn(a: u32) u32 { return a ++ a; }\nexport fn exported(x: u32) u32 { return x; }\nextern fn external(x: u32) u32;\ninline fn inl(x: u32) u32 { return x; }\nfn plain(x: u32) u32 { return x; }\ncomptime { }\n", &out, &st); + assert(out_is(n, &out, 0, "// t27o: refused const Point (line 1): type declaration `struct` at line 1\n\n// t27o: refused fn Point.len (line 3): type `Point` at line 3\n\n// t27o: refused fn Point.neg (line 4): type `Point` at line 4\n\n// t27o: refused const Mode (line 6): type declaration `enum` at line 6\n\n// t27o: refused const counter (line 7): mutable file-level var `counter` at line 7\n\n// t27o: refused fn ptr (line 9): pointer `*` at line 9\n\n// t27o: refused fn opt (line 10): optional `?` at line 10\n\n// t27o: refused fn err (line 11): error handling `!` at line 11\n\n// t27o: refused fn typed (line 12): type `Point` at line 12\n\n// t27o: refused fn gen (line 13): comptime or generic `comptime` at line 13\n\n// t27o: refused fn any (line 14): comptime or generic `anytype` at line 14\n\n// t27o: refused fn tryit (line 15): error handling `try` at line 15\n\n// t27o: refused fn catches (line 16): uses refused `err` at line 16\n\n// t27o: refused fn defers (line 17): defer `defer` at line 17\n\n// t27o: refused fn unreach (line 18): unreachable or panic `unreachable` at line 18\n\n// t27o: refused fn glob (line 19): mutable file-level var `counter` at line 19\n\n// t27o: refused fn ext (line 20): uses external `other` at line 20\n\n// t27o: refused fn field (line 21): field or call through a value `y` at line 21\n\n// t27o: refused fn anon (line 22): anonymous literal `.` at line 22\n\n// t27o: refused fn lab (line 23): label `blk` at line 23\n\n// t27o: refused fn blockstmt (line 24): bare braces statement `{` at line 24\n\n// t27o: refused fn payload (line 25): payload capture `|` at line 25\n\n// t27o: refused fn wide (line 26): type `u128` at line 26\n\n// t27o: refused fn catfn (line 27): operator `++` at line 27\n\n// t27o: refused fn exported (line 28): export, extern or inline fn `export` at line 28\n\n// t27o: refused fn external (line 29): export, extern or inline fn `extern` at line 29\n\n// t27o: refused fn inl (line 30): export, extern or inline fn `inline` at line 30\n\nfn plain(x: u32) -> u32 {\n return x;\n}\n\n// t27o: refused top-level comptime (line 32): declaration `comptime` at line 32\n\n")); +} + +test literals_t27_reads_the_same_pass_and_the_rest_are_refused { + var out : [1024]u8 = undefined; + var st : [2048]usize = [0] ** 2048; + const n : usize = z_body_text("fn s() []const u8 { return \"a\\n\\t\\\\\\\"b\"; }\nfn esc() []const u8 { return \"a\\x41\"; }\nfn cr() []const u8 { return \"a\\r\"; }\nfn c1(c: u8) bool { return c == 'a' or c == '\\n' or c == '\\'' or c == '\"' or c == '\\\\' or c == '\\t' or c == '\\r'; }\nfn c2(c: u8) bool { return c == '\\x41'; }\nfn n1() u32 { return 0xFF + 0b101 + 1_000 + 0o17; }\nfn n2() f64 { return 1.5e3 + 2e-2 + 3.0; }\nfn n3() f64 { return 0x1.8p3; }\nfn n4() u32 { return 0o99; }\n", &out, &st); + assert(out_is(n, &out, 0, "fn s() -> []const u8 {\n return \"a\\n\\t\\\\\\\"b\";\n}\n\n// t27o: refused fn esc (line 2): literal `\"a\\x41\"` at line 2\n\n// t27o: refused fn cr (line 3): literal `\"a\\r\"` at line 3\n\nfn c1(c: u8) -> bool {\n return c == 'a' or c == '\\n' or c == '\\'' or c == '\"' or c == '\\\\' or c == '\\t' or c == '\\r';\n}\n\n// t27o: refused fn c2 (line 5): literal `'\\x41'` at line 5\n\nfn n1() -> u32 {\n return 0xFF + 0b101 + 1_000 + 15;\n}\n\nfn n2() -> f64 {\n return 1.5e3 + 2e-2 + 3.0;\n}\n\n// t27o: refused fn n3 (line 8): literal `0x1` at line 8\n\n// t27o: refused fn n4 (line 9): literal `0o99` at line 9\n\n")); +} + +test a_generated_file_is_refused_whole_in_any_case { + var out : [768]u8 = undefined; + var st : [2048]usize = [0] ** 2048; + const n : usize = to_t27("// Generated from specs/x.tri -- DO NOT EDIT\nfn f(x: u32) u32 { return x; }\ntest \"f\" { }\n", "demo", &out, &st); + assert(out_is(n, &out, 0, "// SPDX-License-Identifier: Apache-2.0\n; demo.t27 -- converted from Zig by t27o (Ouroboros), specs/tools/t27o/zig_convert.t27; read it before it lands.\n; ASCII only (L3)\n\nmodule demo;\n\n// t27o: refused file (line 1): generated; convert its source spec instead\n\n// t27o: 0 fn seen, 0 converted (0 of 0 tests), 0 refused (0 kept as stubs); coverage unknown: no fn seen\n")); + assert(st[Z_NFN] == 0); +} + +test a_whole_file_has_a_header_the_items_and_a_count_line { + var out : [768]u8 = undefined; + var st : [2048]usize = [0] ** 2048; + const n : usize = to_t27("const std = @import(\"std\");\n/// Double.\npub fn twice(x: u32) u32 {\n return x * 2;\n}\nfn bad(p: *u32) u32 {\n return p.*;\n}\ntest \"twice\" {\n try std.testing.expectEqual(@as(u32, 4), twice(2));\n}\n", "demo", &out, &st); + assert(out_is(n, &out, 0, "// SPDX-License-Identifier: Apache-2.0\n; demo.t27 -- converted from Zig by t27o (Ouroboros), specs/tools/t27o/zig_convert.t27; read it before it lands.\n; ASCII only (L3)\n\nmodule demo;\n\npub fn twice(x: u32) -> u32 {\n return x * 2;\n}\n\n// t27o: refused fn bad (line 6): pointer `*` at line 6\n\ntest twice {\n assert(twice(2) == @as(u32, 4));\n}\n\n// t27o: 2 fn seen, 1 converted (1 of 1 tests), 1 refused (0 kept as stubs); coverage 50%\n")); +} + +test a_file_with_no_fn_reports_unknown_coverage_not_100 { + var out : [768]u8 = undefined; + var st : [2048]usize = [0] ** 2048; + const n : usize = to_t27("const A: u32 = 1;\n", "demo", &out, &st); + assert(out_is(n, &out, 0, "// SPDX-License-Identifier: Apache-2.0\n; demo.t27 -- converted from Zig by t27o (Ouroboros), specs/tools/t27o/zig_convert.t27; read it before it lands.\n; ASCII only (L3)\n\nmodule demo;\n\nconst A : u32 = 1;\n\n// t27o: 0 fn seen, 0 converted (0 of 0 tests), 0 refused (0 kept as stubs); coverage unknown: no fn seen\n")); +} + +test reserved_words_get_an_underscore_everywhere { + var out : [512]u8 = undefined; + var st : [2048]usize = [0] ** 2048; + const n : usize = z_body_text("fn f(spec: u32, module: u32) u32 {\n const bench = spec + module;\n return bench;\n}\n", &out, &st); + assert(out_is(n, &out, 0, "fn f(spec_: u32, module_: u32) -> u32 {\n const bench_ = spec_ + module_;\n return bench_;\n}\n\n")); +} + +test a_mutable_slice_parameter_can_be_written_through { + var out : [512]u8 = undefined; + var st : [2048]usize = [0] ** 2048; + const n : usize = z_body_text("fn fill(a: []u8, v: u8) void {\n for (0..a.len) |i| {\n a[i] = v;\n }\n}\nfn first_of(a: []const u8) u8 {\n return a[0];\n}\n", &out, &st); + assert(out_is(n, &out, 0, "fn fill(a: []u8, v: u8) -> void {\n for (0..a.len) |i| {\n a[i] = v;\n }\n}\n\nfn first_of(a: []const u8) -> u8 {\n return a[0];\n}\n\n")); +} + +test a_namespace_alias_inside_a_body_is_a_name_and_not_a_line { + var out : [512]u8 = undefined; + var st : [2048]usize = [0] ** 2048; + const n : usize = z_body_text("const std = @import(\"std\");\nfn area(r: f64) f64 {\n const m = std.math;\n return m.pi * r * r;\n}\ntest \"alias\" {\n const testing = std.testing;\n const s = @import(\"std\");\n try testing.expect(area(1.0) > 3.0);\n try s.testing.expect(true);\n}\n", &out, &st); + assert(out_is(n, &out, 0, "fn area(r: f64) -> f64 {\n return std.math.pi * r * r;\n}\n\ntest alias {\n assert(area(1.0) > 3.0);\n assert(true);\n}\n\n")); + assert(st[Z_NTEST] == 1); +} + +test an_unbraced_body_may_be_followed_by_else { + var out : [768]u8 = undefined; + var st : [2048]usize = [0] ** 2048; + const n : usize = z_body_text("fn sign(x: i32) i32 {\n var r: i32 = 0;\n if (x < 0) r = -1 else r = 1;\n return r;\n}\nfn nested(a: bool, b: bool) u32 {\n var r: u32 = 0;\n if (a) if (b) r = 1 else r = 2 else r = 3;\n return r;\n}\nfn needs_semicolon(x: i32) i32 {\n var r: i32 = 0;\n if (x < 0) r = -1\n return r;\n}\n", &out, &st); + assert(out_is(n, &out, 0, "fn sign(x: i32) -> i32 {\n var r : i32 = 0;\n if (x < 0) {\n r = -1;\n } else {\n r = 1;\n }\n return r;\n}\n\nfn nested(a: bool, b: bool) -> u32 {\n var r : u32 = 0;\n if (a) {\n if (b) {\n r = 1;\n } else {\n r = 2;\n }\n } else {\n r = 3;\n }\n return r;\n}\n\n// t27o: refused fn needs_semicolon (line 11): syntax `return` at line 14\n\n")); +} + +test a_while_loop_and_a_for_loop_nest_with_continue_and_break { + var out : [768]u8 = undefined; + var st : [2048]usize = [0] ** 2048; + const n : usize = z_body_text("fn first_pair(a: []const u32, b: []const u32) u32 {\n var found: u32 = 0;\n for (a) |x| {\n var i: usize = 0;\n while (i < b.len) : (i += 1) {\n if (b[i] < x) continue;\n if (b[i] == x) {\n found = x;\n break;\n }\n }\n if (found != 0) break;\n }\n return found;\n}\n", &out, &st); + assert(out_is(n, &out, 0, "fn first_pair(a: []const u32, b: []const u32) -> u32 {\n var found : u32 = 0;\n for (a) |x| {\n var i : usize = 0;\n while (i < b.len) : (i += 1) {\n if (b[i] < x) {\n continue;\n }\n if (b[i] == x) {\n found = x;\n break;\n }\n }\n if (found != 0) {\n break;\n }\n }\n return found;\n}\n\n")); +} + +test std_debug_assert_is_an_assert_in_a_body { + var out : [512]u8 = undefined; + var st : [2048]usize = [0] ** 2048; + const n : usize = z_body_text("const std = @import(\"std\");\nconst debug = std.debug;\nfn checked(x: u32) u32 {\n std.debug.assert(x > 0);\n debug.assert(x < 100);\n return x - 1;\n}\nfn printing(x: u32) u32 {\n std.debug.print(\"{}\", .{x});\n return x;\n}\n", &out, &st); + assert(out_is(n, &out, 0, "fn checked(x: u32) -> u32 {\n assert(x > 0);\n assert(x < 100);\n return x - 1;\n}\n\n// t27o: refused fn printing (line 8): std call `std.debug.print` at line 9\n\n")); +} + +test a_struct_of_fns_and_consts_is_read_as_free_fns_named_after_it { + var out : [1024]u8 = undefined; + var st : [2048]usize = [0] ** 2048; + const n : usize = z_body_text("const std = @import(\"std\");\npub const Optimizer = struct {\n pub const LIMIT: u32 = 16;\n const TABLE = [_]u8{ 3, 1, 4 };\n\n pub fn bitLength(n: u64) usize {\n var len: usize = 0;\n var v = n;\n while (v > 0) : (v >>= 1) len += 1;\n return len;\n }\n\n pub fn weight(i: usize) u32 {\n return TABLE[i] + LIMIT + @as(u32, @intCast(bitLength(i)));\n }\n};\nfn outside(n: u64) usize {\n return Optimizer.bitLength(n) + Optimizer.LIMIT;\n}\nfn missing() u32 {\n return Optimizer.nothing(1);\n}\ntest \"bits\" {\n try std.testing.expectEqual(@as(usize, 8), Optimizer.bitLength(255));\n try std.testing.expectEqual(@as(usize, 24), outside(255));\n}\n", &out, &st); + assert(out_is(n, &out, 0, "pub const Optimizer_LIMIT : u32 = 16;\n\nconst Optimizer_TABLE : [3]u8 = [3, 1, 4];\n\npub fn Optimizer_bitLength(n: u64) -> usize {\n var len : usize = 0;\n var v = n;\n while (v > 0) : (v = v >> 1) {\n len += 1;\n }\n return len;\n}\n\npub fn Optimizer_weight(i: usize) -> u32 {\n return Optimizer_TABLE[i] + Optimizer_LIMIT + @as(u32, @intCast(Optimizer_bitLength(i)));\n}\n\nfn outside(n: u64) -> usize {\n return Optimizer_bitLength(n) + Optimizer_LIMIT;\n}\n\n// t27o: refused fn missing (line 20): undeclared `Optimizer.nothing` at line 21\n\ntest bits {\n assert(Optimizer_bitLength(255) == @as(usize, 8));\n assert(outside(255) == @as(usize, 24));\n}\n\n")); + assert(st[Z_NFN] == 4); + assert(st[Z_NOK] == 3); +} + +test a_struct_with_fields_is_a_type_but_its_static_fns_still_convert { + var out : [1024]u8 = undefined; + var st : [2048]usize = [0] ** 2048; + const n : usize = z_body_text("const Point = struct {\n x: u32,\n y: u32 = 0,\n\n pub const ORIGIN_X: u32 = 0;\n\n pub fn dist(a: u32, b: u32) u32 {\n return if (a > b) a - b else b - a;\n }\n\n pub fn len(self: Point) u32 {\n return self.x + self.y;\n }\n\n pub fn flip(self: *Point) void {\n self.x = self.y;\n }\n};\nconst Mode = enum(u8) {\n on,\n off = 5,\n\n pub fn count() usize {\n return 2;\n }\n};\nfn use_it() u32 {\n return Point.dist(3, 5) + Point.ORIGIN_X;\n}\nfn variant() u8 {\n return @intFromEnum(Mode.off);\n}\nfn make() u32 {\n return Point.origin();\n}\n", &out, &st); + assert(out_is(n, &out, 0, "// t27o: refused const Point (line 1): type declaration `struct` at line 1\n\npub const Point_ORIGIN_X : u32 = 0;\n\npub fn Point_dist(a: u32, b: u32) -> u32 {\n return if (a > b) a - b else b - a;\n}\n\n// t27o: refused fn Point.len (line 11): type `Point` at line 11\n\n// t27o: refused fn Point.flip (line 15): pointer `*` at line 15\n\n// t27o: refused const Mode (line 19): type declaration `enum` at line 19\n\npub fn Mode_count() -> usize {\n return 2;\n}\n\nfn use_it() -> u32 {\n return Point_dist(3, 5) + Point_ORIGIN_X;\n}\n\n// t27o: refused fn variant (line 30): builtin `@intFromEnum` at line 31\n\n// t27o: refused fn make (line 33): type `Point` at line 34\n\n")); +} + +test a_nested_type_a_test_and_a_var_in_a_struct_are_read_for_what_they_are { + var out : [768]u8 = undefined; + var st : [2048]usize = [0] ** 2048; + const n : usize = z_body_text("const Outer = struct {\n var counter: u32 = 0;\n const Inner = struct {\n pub fn deep() u32 { return 1; }\n };\n pub fn plain(x: u32) u32 { return x + 1; }\n test \"plain\" {\n try @import(\"std\").testing.expectEqual(@as(u32, 2), plain(1));\n }\n};\n", &out, &st); + assert(out_is(n, &out, 0, "// t27o: refused const Outer (line 1): type declaration `struct` at line 1\n\n// t27o: refused const Outer.counter (line 2): mutable file-level var `counter` at line 2\n\n// t27o: refused const Outer.Inner (line 3): type declaration `struct` at line 3; 1 fn inside\n\npub fn Outer_plain(x: u32) -> u32 {\n return x + 1;\n}\n\n// t27o: refused test Outer.\"plain\" (line 7): error handling `try @import` at line 8\n\n")); +} + +test the_tokenizer_reads_zig_operators_whole { + assert(z_tok_end("@as(u8, x)", 0) == 3); + assert(z_tok_end("+% b", 0) == 2); + assert(z_tok_end("-%= b", 0) == 3); + assert(z_tok_end("*% b", 0) == 2); + assert(z_tok_end("x.? y", 1) == 3); + assert(z_tok_end("x.* y", 1) == 3); + assert(z_tok_end("a ** b", 2) == 4); + assert(z_tok_end("a + b", 2) == 3); + assert(z_tok_end("<<= 1", 0) == 3); + assert(z_tok_end("0..n", 0) == 1); + assert(z_tok_end("\\\\line", 0) == 1); +} + +test a_file_name_ending_in_dot_zig_is_a_zig_file { + assert(is_zig_name("a.zig")); + assert(is_zig_name("src/x/y.zig")); + assert(!is_zig_name("a.zig.rs")); + assert(!is_zig_name("zig")); + assert(!is_zig_name("a.rs")); + assert(!is_zig_name("a.cir")); +} + +test a_generator_mark_in_the_first_20_lines_marks_the_file { + assert(z_generated("// Generated from specs/x.tri\nfn f() void {}\n")); + assert(z_generated("// DO NOT EDIT\n")); + assert(z_generated("// do Not Edit by hand\n")); + assert(z_generated("// from specs/a.VIBEE\n")); + assert(z_generated("x\nx\nx\nx\nx\nx\nx\nx\nx\nx\nx\nx\nx\nx\nx\nx\nx\nx\nx\n// generated by hand\n")); + assert(!z_generated("x\nx\nx\nx\nx\nx\nx\nx\nx\nx\nx\nx\nx\nx\nx\nx\nx\nx\nx\nx\n// generated\n")); + assert(!z_generated("pub fn f() u32 { return 1; }\n")); + assert(!z_generated("")); +} + +test octal_literals_are_decimal_and_a_digit_that_is_no_octal_is_told { + assert(z_octal("0o17", 0, 4) == 15); + assert(z_octal("0o1_0", 0, 5) == 8); + assert(z_octal("0o9", 0, 3) == 18446744073709551615); +} + +test an_array_literal_is_counted_by_its_elements { + assert(z_count_elems("1, 2, 3 }", 0) == 3); + assert(z_count_elems("1, 2, }", 0) == 2); + assert(z_count_elems("}", 0) == 0); + assert(z_count_elems("f(1, 2), 3 }", 0) == 2); + assert(z_count_elems("{1, 2}, {3} }", 0) == 2); +} + +test a_big_state_vector_keeps_test_names_unique_by_hash { + var out : [512]u8 = undefined; + var st : [12500]usize = [0] ** 12500; + const n : usize = z_body_text("test \"a-b\" {}\ntest \"a b\" {}\ntest \"a_b\" {}\n", &out, &st); + assert(out_is(n, &out, 0, "test a_b {\n}\n\ntest a_b_2 {\n}\n\ntest a_b_3 {\n}\n\n")); + assert(st[Z_HASHON] == 1); +} + +test a_state_vector_too_small_for_the_names_refuses_the_file { + var out : [512]u8 = undefined; + var st : [130]usize = [0] ** 130; + const n : usize = to_t27("fn f() void {}\n", "demo", &out, &st); + assert(out_is(n, &out, 0, "// SPDX-License-Identifier: Apache-2.0\n; demo.t27 -- converted from Zig by t27o (Ouroboros), specs/tools/t27o/zig_convert.t27; read it before it lands.\n; ASCII only (L3)\n\nmodule demo;\n\n// t27o: refused file (line 1): name table full\n\n// t27o: 0 fn seen, 0 converted (0 of 0 tests), 0 refused (0 kept as stubs); coverage unknown: no fn seen\n")); +} + +test a_buffer_too_small_is_told_by_the_size_and_nothing_is_cut { + var out : [32]u8 = undefined; + var st : [2048]usize = [0] ** 2048; + const n : usize = z_body_text("pub fn add(a: u32, b: u32) u32 {\n return a + b;\n}\n", &out, &st); + assert(n == 57); + assert(out_is(32, &out, 0, "pub fn add(a: u32, b: u32) -> u3")); +} + +test a_module_name_keeps_letters_digits_and_dashes_and_hides_the_rest { + var out : [512]u8 = undefined; + var st : [2048]usize = [0] ** 2048; + const n : usize = to_t27("", ".!15390!self-improver.v2", &out, &st); + assert(out_is(n, &out, 0, "// SPDX-License-Identifier: Apache-2.0\n; __15390_self-improver_v2.t27 -- converted from Zig by t27o (Ouroboros), specs/tools/t27o/zig_convert.t27; read it before it lands.\n; ASCII only (L3)\n\nmodule __15390_self-improver_v2;\n\n// t27o: 0 fn seen, 0 converted (0 of 0 tests), 0 refused (0 kept as stubs); coverage unknown: no fn seen\n")); +} + +test a_state_vector_shorter_than_the_fixed_slots_gives_no_output_and_no_panic { + var out : [64]u8 = undefined; + var st : [100]usize = [0] ** 100; + assert(to_t27("fn f() void {}\n", "demo", &out, &st) == 0); +} + +test a_source_with_crlf_lines_reads_like_one_with_lf { + var src : [96]u8 = undefined; + const t : []const u8 = "fn f(x: u32) u32 {\n return x;\n}\ntest \"f\" {\n try std.testing.expect(f(1) == 1);\n}\n"; + var i : usize = 0; + var w : usize = 0; + while (i < t.len) { + if (t[i] == 10) { + src[w] = 13; + w = w + 1; + } + src[w] = t[i]; + w = w + 1; + i = i + 1; + } + var out : [256]u8 = undefined; + var st : [2048]usize = [0] ** 2048; + const n : usize = z_body_text(src[0..w], &out, &st); + assert(out_is(n, &out, 0, "fn f(x: u32) -> u32 {\n return x;\n}\n\ntest f {\n assert(f(1) == 1);\n}\n\n")); +}