xqvm 0.2.1

X-Quadratic Virtual Machine — bytecode interpreter for the XQuad Toolchain
Documentation
// Copyright (C) 2026 Postquant Labs Incorporated
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <https://www.gnu.org/licenses/>.
//
// SPDX-License-Identifier: AGPL-3.0-or-later

//! Compile-time parity check between the [`opcodes!`](crate::opcodes)
//! x-macro (the canonical Rust opcode table) and
//! `conformance/opcodes.yaml` (the canonical machine-readable table).
//!
//! The build script [`build.rs`](../../../build.rs) parses the YAML and
//! emits `YAML_OPCODES: &[(u8, &str)]` into `$OUT_DIR/opcodes_yaml.rs`.
//! The `opcodes!` macro is invoked here with a callback that constructs a
//! parallel `MACRO_OPCODES: &[(u8, &str)]`. A `const` assertion compares
//! the two tables element-wise; any disagreement fails the build.
//!
//! Keeping the check in a `const` context means every `cargo build` (not
//! just `cargo test`) enforces parity — drift cannot land quietly.

// Generated by build.rs. Defines `YAML_OPCODES: &[(u8, &str)]`.
include!(concat!(env!("OUT_DIR"), "/opcodes_yaml.rs"));

/// Build a `(code, mnemonic)` slice from the `opcodes!` macro expansion.
macro_rules! macro_opcodes_table {
    (
        $( ($code:literal, $variant:ident, $mnem:literal, $doc:literal, $_delta:expr, {$($f:tt)*}) ),*
        $(,)?
    ) => {
        const MACRO_OPCODES: &[(u8, &str)] = &[
            $( ($code, $mnem), )*
        ];
    };
}

crate::opcodes!(macro_opcodes_table);

// `<[T]>::get` is not yet const-fn, so the bounded `while` loops below must
// index directly. Each access sits under an explicit `i < len` guard.
#[expect(
    clippy::indexing_slicing,
    reason = "const fn cannot use slice::get; loop condition bounds the index"
)]
const fn str_eq(a: &str, b: &str) -> bool {
    let ab = a.as_bytes();
    let bb = b.as_bytes();
    if ab.len() != bb.len() {
        return false;
    }
    let mut i = 0;
    while i < ab.len() {
        if ab[i] != bb[i] {
            return false;
        }
        i += 1;
    }
    true
}

#[expect(
    clippy::indexing_slicing,
    reason = "const fn cannot use slice::get; loop condition bounds the index"
)]
const fn tables_match() -> bool {
    if MACRO_OPCODES.len() != YAML_OPCODES.len() {
        return false;
    }
    let mut i = 0;
    while i < MACRO_OPCODES.len() {
        let (mc, mn) = MACRO_OPCODES[i];
        let (yc, yn) = YAML_OPCODES[i];
        if mc != yc || !str_eq(mn, yn) {
            return false;
        }
        i += 1;
    }
    true
}

const _: () = assert!(
    tables_match(),
    "opcodes! x-macro disagrees with conformance/opcodes.yaml — \
     update the YAML or the macro so they match, or run `make opcode-parity` \
     for a detailed diff."
);

#[cfg(test)]
mod tests {
    #[test]
    fn tables_have_expected_size() {
        assert_eq!(super::MACRO_OPCODES.len(), 93);
        assert_eq!(super::YAML_OPCODES.len(), 93);
    }

    #[test]
    fn tables_agree() {
        assert_eq!(super::MACRO_OPCODES, super::YAML_OPCODES);
    }
}