Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Constants

const gives a value a name:

const WIDTH = 8
const MASK = (1 << WIDTH) - 1

macro show(value: int) {
    @emit value
}

show MASK
255

A constant can hold any value: an integer, a struct, an enum variant, or a string.

Syntax

const NAME = value
const NAME: Type = value
pub const NAME = value
  • : Type converts the value to Type, and checks it, before binding it. See Conversions.
  • pub lets other files import the constant. See Visibility.
from std.binary import bits

const BYTE: bits<8> = 200

macro emit_byte(b: bits<8>) {
    @emit b
}

emit_byte BYTE
bits<8> { value: 200 }

A value that doesn’t fit the type is a compile error. bits<8> checks that its value fits in eight bits:

from std.binary import bits

const BYTE: bits<8> = 300
invariant `fits_inside_width(width, value)` was violated for `bits`

Constants never change

There are no variables in BitterASM. A constant is bound once and never reassigned, and nothing in the language can be mutated. To compute a value step by step, use recursion or @fold.

Constants inside macros

Inside a macro body, const names an intermediate value. It’s visible for the rest of that body:

macro hypot_squared(a: int, b: int) {
    const aa = a * a
    const bb = b * b
    @emit aa + bb
}

hypot_squared 3, 4
25

A pub const inside a macro is different: it declares a new top-level constant when the macro is called. See Generating declarations.

Registers are constants

Architecture packages use constants for registers. For example, std.riscv.impl declares each register as a constant of type Reg, a five-bit number:

pub type Reg = bits<5>

@for i in 0..32 {
    pub const x`i` = Reg(i)
}

pub const zero = x0
pub const ra = x1

The x`i` builds the names x0 to x31. See Spliced names.