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, the component! 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::RuntimeInstruction (constants, arithmetic, branches, halt, …), a pattern-derived cpu::SurfaceInstruction, and the execution component. Use it directly, or as a reference for writing your own components.
vihaco-parser-derive parser
The #[derive(Parse)] proc-macro. It turns an instruction enum or struct into a chumsky parser from declarative #[syntax_class] and #[pattern] attributes.
vihaco-parser parser
The Parse<'src> and SurfaceInstruction traits, plus parser implementations for primitives, lexical types (Ident, BareToken, and QuotedString), vectors, and tuples.
vihaco-runtime-derive internal
The procedural macros behind component! and composite! (#[derive(Instruction)] lives in vihaco-abi-derive). Both are re-exported through vihaco — you rarely depend on them 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-derive.

§ 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-derive = { git = "https://github.com/QuEraComputing/vihaco" }
eyre                 = "0.6"   # vihaco APIs return eyre::Result

§ 4A first component

A component declares instruction products with component!. Each product gets its own Execute<I> implementation, so its message, effect, and fault types can be specific to that operation.

use eyre::Result;
use vihaco::{component, Effects, Execute, Execution, StepResult};

component! {
    component Counter {
        value: i64,
    }

    instruction {
        Add(i64),
        Read,
    }
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Value(pub i64);

impl Execute<counter::instruction::Add> for counter::Counter {
    type Message = ();
    type Effect = ();
    type Fault = eyre::Report;

    fn execute(&mut self, instruction: &counter::instruction::Add, _: ()) -> Result<StepResult<()>> {
        self.value += instruction.0;
        Ok(StepResult { effects: Effects::none(), execution: Execution::Complete })
    }
}

impl Execute<counter::instruction::Read> for counter::Counter {
    type Message = ();
    type Effect = Value;
    type Fault = eyre::Report;

    fn execute(&mut self, _: &counter::instruction::Read, _: ()) -> Result<StepResult<Value>> {
        Ok(StepResult { effects: Effects::one(Value(self.value)), execution: Execution::Complete })
    }
}

fn main() -> Result<()> {
    let mut counter = counter::Counter { value: 0 };
    Execute::execute(&mut counter, &counter::instruction::Add(5), ())?;
    let value = Execute::execute(&mut counter, &counter::instruction::Read, ())?
        .effects
        .into_iter()
        .next()
        .expect("Read emits one value");
    assert_eq!(value, Value(5));
    Ok(())
}

A composite's generated execute_generated method is the route-dispatch boundary; components themselves use Execute directly. 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. A user-declared surface enum derives vihaco_parser::Parse from its syntax class and patterns. When the source and runtime representations match, that enum can also derive Instruction:

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

// The same enum can derive both `Instruction` (bytecode + runtime) and
// `Parse` (SST). The two derives are orthogonal.
#[derive(Debug, Clone, PartialEq, Instruction, vihaco_parser_derive::Parse)]
#[syntax_class(instruction, head = "counter")]
pub enum CounterInst {
    #[pattern = "'add $0"]
    Add(i64),
    Print,
}

fn main() {
    // The syntax class supplies the `counter::` namespace. Patterns bind
    // source operands directly to Rust fields.
    let inst = CounterInst::parser()
        .parse("counter::add 5")
        .into_result()
        .unwrap();
    assert_eq!(inst, CounterInst::Add(5));
}

That is the whole instruction-level surface. Module-level parsing adds device headers, typed function bodies, and application-specific resolution, covered in Pattern Parser Integration, Pattern Parser, and Module Parsing and Resolution.

§ 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.