Defining Instructions With vihaco
Instruction types are the encoded operations in vihaco. Component runtime
products are declared with component!; source- or bytecode-facing enums can
derive Instruction when a single enum is the representation you want.
This guide shows:
- how to define a component-local instruction enum
- how opcodes are assigned by default
- how instruction width is chosen
For explicit opcode overrides, explicit widths, and machine-level wrapper instructions, see Advanced Instruction Usage.
If you are new to the component model, read Building Components With vihaco first.
A Small Instruction Enum
The smallest useful instruction type is just an enum with #[derive(Instruction)].
use vihaco::Instruction;
#[derive(Debug, Clone, Instruction)]
pub enum CounterInst {
Add(i64),
Print,
}
Each variant becomes an opcode in the encoded instruction stream. Tuple fields become payload bytes that follow the opcode.
Conceptually:
CounterInst::Add(5) => [opcode for Add][encoded i64 payload]
CounterInst::Print => [opcode for Print]
By default, #[derive(Instruction)] assigns opcodes in variant order starting at 0.
That means the first variant gets opcode 0, the second gets 1, and so on.
For the current component model, declare products directly:
use eyre::Result;
use vihaco::{Instruction, component};
#[derive(Debug, Clone, Instruction)]
pub enum LampInst {
On,
Off,
}
#[derive(Debug, Default)]
pub struct Lamp {
on: bool,
}
component! {
component Lamp { on: bool, }
instruction { On, Off, }
}
How Width Is Chosen
Every instruction type has an encoded width in bytes. That width includes:
- one opcode byte for the enum variant itself
- enough payload space for the largest variant in the enum
If you do not set a width explicitly, #[derive(Instruction)] computes it from the enum shape.
As a rule:
- a unit variant contributes only its opcode byte
- a tuple variant contributes its opcode byte plus the widths of its fields
- the enum width becomes the largest of those variant widths
For example:
use vihaco::Instruction;
#[derive(Debug, Clone, Instruction)]
pub enum InnerInst {
Ping,
Pong,
}
#[derive(Debug, Clone, Instruction)]
pub enum OuterInst {
Idle,
Inner(InnerInst),
}
InnerInst has width 1, because each variant is only an opcode.
OuterInst has width 2:
OuterInst::Idleneeds1byteOuterInst::Inner(...)needs1outer opcode byte plus the1byte used byInnerInst
So the final width is the maximum variant width, which is 2.
Practical Guidance
- Put bytecode-visible operations in an
#[derive(Instruction)]enum. - Let opcodes default to variant order unless you need specific encoded values.
- Start with inferred width unless you already need a fixed record size.
What Comes Next
For explicit opcode assignment, explicit widths, and machine-level wrapper instructions, see Advanced Instruction Usage.
#[derive(Instruction)] covers bytecode and runtime semantics; source-text parsing is owned by an orthogonal #[derive(vihaco_parser_derive::Parse)] on the same enum. See Pattern Parser Integration for Component Instructions for the parser-side workflow and Module Parsing and Resolution for section headers, typed function bodies, and module resolution.
After defining products, implement Execute<I> for each product as described in
Building Components.
See Building Components With vihaco for the execution side of that model.