vihaco / virtual ISA & machine framework
On this page
  1. § 1 Workspace at a glance
  2. § 2 Pick crates by use case
  3. § 3 Setup
  4. § 4 A first component
  5. § 5 Parsing source text
  6. § 6 Next steps
Rust · edition 2024

rsQuick Start

vihaco is a framework for building small virtual machines: you define the instruction set, the components that execute it, the effects they emit, and (optionally) a parser for source text — all as ordinary Rust.

§ 1Workspace at a glance

vihaco is a Cargo workspace of five focused crates. vihaco is the foundation; everything else builds on it. Depend on what your workload actually needs — there is no umbrella crate.

vihaco foundation
The framework. The Instruction / Message / Effects types and their derives, the #[component], #[observe], and #[composite] macros, the module / syntax / runtime layers, and the Value / Type value model. Re-exports the macros, so most projects depend only on this crate.
vihaco-cpu component
A ready-made CPU/host component — a small stack machine with cpu::Instruction (constants, arithmetic, branches, halt, …) and the StepOutcome control-flow effect. Use it directly, or as a reference for writing your own components.
vihaco-parser parser
The #[derive(Parse)] proc-macro. It turns an instruction enum into a chumsky parser, driven by #[head] / #[token] / #[delimiters] / #[parse_with] attributes.
vihaco-parser-core parser
The Parse<'src> trait and blanket impls for the primitives (i64, u64, f64, bool, String, …), plus the ident() helper. Every parser in the workspace is a Parse impl.
vihaco-derive internal
The procedural macros behind #[derive(Instruction)], #[derive(Message)], #[component], #[observe], and #[composite]. Re-exported through vihaco — you rarely depend on it directly.

§ 2Pick crates by use case

  • Define instructions, components, and effects: vihaco.
  • Reuse a CPU / stack machine as one of your components: vihaco + vihaco-cpu.
  • Parse source text into instructions: vihaco + vihaco-parser + vihaco-parser-core.

§ 3Setup

Requires Rust edition 2024. There is no published release yet, so depend on the git repo. The git dependency form is identical for each crate:

[dependencies]
vihaco             = { git = "https://github.com/QuEraComputing/vihaco" }
vihaco-cpu         = { git = "https://github.com/QuEraComputing/vihaco" }
vihaco-parser      = { git = "https://github.com/QuEraComputing/vihaco" }
vihaco-parser-core = { git = "https://github.com/QuEraComputing/vihaco" }
eyre               = "0.6"   # vihaco APIs return eyre::Result

§ 4A first component

A component bundles three things: an Instruction enum (the operations), an optional Message (resolved execution input), and an optional effect type (what execution returns). The #[component(...)] attribute generates the runtime glue from a single execute method.

use eyre::Result;
use vihaco::{
    Effects, GeneratedComponent, Instruction, Message, component, expect_exactly_one_effect,
};

#[derive(Debug, Clone, Instruction)]
pub enum CounterInst {
    Add(i64),
    Print,
}

#[derive(Debug, Clone, Message)]
pub struct Prefix(pub String);

#[derive(Debug, Clone, PartialEq)]
pub struct Line(pub String);

#[derive(Debug, Default)]
pub struct Counter {
    value: i64,
}

#[component(instruction = CounterInst, message = Prefix, effect = Line)]
impl Counter {
    fn execute(&mut self, inst: CounterInst, msg: Prefix) -> Result<Effects<Line>> {
        match inst {
            CounterInst::Add(v) => {
                self.value += v;
                Ok(Effects::none())
            }
            CounterInst::Print => Ok(Effects::one(Line(format!("{}{}", msg.0, self.value)))),
        }
    }
}

fn main() -> Result<()> {
    let mut counter = Counter::default();

    // `Add` ignores its message and returns no effects.
    counter.execute_generated(CounterInst::Add(2), Prefix(String::new()))?;
    counter.execute_generated(CounterInst::Add(3), Prefix(String::new()))?;

    // `Print` returns exactly one `Line` effect.
    let effects = counter.execute_generated(CounterInst::Print, Prefix("total = ".into()))?;
    let line = expect_exactly_one_effect(effects)?;
    assert_eq!(line, Line("total = 5".into()));
    Ok(())
}

execute_generated is the runtime entry point the macro generates (via the GeneratedComponent trait); expect_exactly_one_effect is a helper for the common single-effect case. For the full data-flow model — when to use a Message, how effects are delivered to observers — read Building Components.

§ 5Parsing source text

Source-text parsing is orthogonal to bytecode. The same enum can derive vihaco_parser::Parse alongside Instruction; calling parser() gives you a chumsky parser:

use chumsky::Parser as _;
use vihaco::Instruction;
use vihaco_parser_core::Parse;

// The same enum can derive both `Instruction` (bytecode + runtime) and
// `Parse` (source text). The two derives are orthogonal.
#[derive(Debug, Clone, PartialEq, Instruction, vihaco_parser::Parse)]
pub enum CounterInst {
    Add(i64),
    Print,
}

fn main() {
    // The default token is the lowercase variant name; tuple fields are
    // wrapped in `( )` by default. (Bare forms like `add 5` are opt-in via
    // `#[delimiters(open = "", close = "", separator = "")]`.)
    let inst = CounterInst::parser()
        .parse("add(5)")
        .into_result()
        .unwrap();
    assert_eq!(inst, CounterInst::Add(5));
}

That is the whole instruction-level surface. Module-level orchestration — device headers, function bodies, sugar expansion, string interning, label resolution — is a two-pass parse → resolve pipeline covered in Parser Integration and Advanced Parser Customization.

§ 6Next steps

  • Work through the Guides in order — they build the full authoring model from instructions up to composites.
  • Browse the API Reference (generated rustdoc).
  • Read vihaco-cpu as a worked example of a non-trivial component.