Types
BitterASM has exactly one built-in type, int.
Every other type is declared in BitterASM code, usually in a library:
| Kind | Declared with | Example |
|---|---|---|
| Struct | struct | an instruction format, bits<N>, Array<T, N> |
| Enum | enum | Endian { Little, Big }, Option<T> |
| Type alias | type | type Reg = bits<5> |
Types can be generic: bits<8> and Array<int, 4>
are instances of generic structs.
Why types matter
Types exist only at compile time. The compiler uses them to:
- Check arguments. A macro that takes a
Regrejects anything that isn’t aReg. - Pick overloads.
mov rax, rbxandmov rax, 5call differentmovmacros becauserbxand5have different types. See Overloading. - Enforce rules. An invariant such as “fits in 8 bits” or “is even” is checked every time a value of the type is made.
- Describe output. A struct’s fields tell an evaluator like
bitterhow to lay out bits. See Packing bytes withbitter.
No automatic conversions
A value never changes type on its own. To turn one type into another, convert
it explicitly with as:
from std.binary import bits
macro emit_byte(b: bits<8>) {
@emit b
}
emit_byte 65 as bits<8>
bits<8> { value: 65 }
Equality
== and != work on any two values: structs compare field by field, and
enums compare variant and payload.
In this chapter
- Structs: declaring, constructing and reading them.
- Struct fields:
pubandskip: visibility and iteration. - Enums: a value that is one of several variants.
- Type aliases: new names for types, and new types with rules.
- Invariants: rules every value of a type must follow.
- Conversions:
as,toandfrom.