The new huff compiler, with improved codegen, label semantics & error messages.
Rust
159
70 commits
updated Mar 15, 2026
Huff2 is the successor of the huff-rs compiler written in Rust. It comes with:
PUSH1 instead of PUSH2 if referencing a label with PC < 256)__codeoffset(macro_name: MacroIdentifier)__tablestart, __tablesize)__EVEN_HASH, __ERROR)#include statements)huff-rs?The huff-rs compiler was a passion project by pioneers
in the Huff community who aimed to create a better version of Zac's
original typescript implementation.
The initial developers of huff-rs were relatively new to compilers, choosing to write some of the
components like the lexer, parser & assembler themselves while adding novel compiler features like
the Huff testing framework that unfortunately didn't see a lot of usage.
Combined with a lot of the tech debt that accrued from the early days made us decide that it was best to start fresh, using existing libraries to do as much of the heavy lifting as possible:
chumsky for lexing & parsingariadne for pretty errorsevm-glue for EVM assemblyalloy for ABI types & parsingThis new foundation will allow bugs to be fixed more easily as well as allowing us to experiment with our own novel compiler features. π
huff-rsThe -b, --bytecode, -r, --bin-runtime, -m, --alt-main, -t, --alt-constructor have
been replaced in favor of a required positional argument indicating what macro to compile and
optional -f / --default-constructor flags to wrap the compiled result with a minimal default
constructor.
This was done to make the CLI simpler and clearer, you will always get a single output, the output you ask for and nothing extra will be added without you asking for it.
A lot of other flags were not reimplemented either because they were not widely used or because we just haven't gotten around to it. Raise an issue if you'd like to suggest a feature.
The compiler will not validatate certain things that were not checked/simply allowed in the previous compiler:
These errors were serious footguns that could easily go unnoticed when using the previous compiler.
Examples:
References resolve to the labels defined within the same macro:
#define macro INNER() = {
target β
Resolves ββ
β
0x1 0x1 β
add β
0x2 β
eq β
β
target: <βββββββββββββ
}
Falls back to resolving to invoker's label:
#define macro MAIN() = takes(0) returns(0) {
INNER() βββββ
target: <ββββ
}
#define macro INNER() = {
^
β
target β
Resolves
0x1 0x1
add
0x2
eq
}
Resolution does not go down into invoked macros:
#define macro MAIN() = takes(0) returns(0) {
INNER()
target β Fails to Resolve
}
#define macro INNER() = {
target:
0x1 0x1
add
0x2
eq
}
As you go down an invocation chain label definitions are added to a stack where the highest most definition is resolved by references.
#define macro MAIN() = takes(0) returns(0) {
INNER()
target: π‘ Shadowed by βββββ
} β
β
β
#define macro INNER() = { β
target β
Resolves ββ β
β β
0x1 0x1 β β
add β β
0x2 β β
eq β β
β β
target: <βββββββββββββ <ββββ
}
Rust
100.0%
The new huff compiler, with improved codegen, label semantics & error messages.
Rust
159
70 commits
updated Mar 15, 2026
Huff2 is the successor of the huff-rs compiler written in Rust. It comes with:
PUSH1 instead of PUSH2 if referencing a label with PC < 256)__codeoffset(macro_name: MacroIdentifier)__tablestart, __tablesize)__EVEN_HASH, __ERROR)#include statements)huff-rs?The huff-rs compiler was a passion project by pioneers
in the Huff community who aimed to create a better version of Zac's
original typescript implementation.
The initial developers of huff-rs were relatively new to compilers, choosing to write some of the
components like the lexer, parser & assembler themselves while adding novel compiler features like
the Huff testing framework that unfortunately didn't see a lot of usage.
Combined with a lot of the tech debt that accrued from the early days made us decide that it was best to start fresh, using existing libraries to do as much of the heavy lifting as possible:
chumsky for lexing & parsingariadne for pretty errorsevm-glue for EVM assemblyalloy for ABI types & parsingThis new foundation will allow bugs to be fixed more easily as well as allowing us to experiment with our own novel compiler features. π
huff-rsThe -b, --bytecode, -r, --bin-runtime, -m, --alt-main, -t, --alt-constructor have
been replaced in favor of a required positional argument indicating what macro to compile and
optional -f / --default-constructor flags to wrap the compiled result with a minimal default
constructor.
This was done to make the CLI simpler and clearer, you will always get a single output, the output you ask for and nothing extra will be added without you asking for it.
A lot of other flags were not reimplemented either because they were not widely used or because we just haven't gotten around to it. Raise an issue if you'd like to suggest a feature.
The compiler will not validatate certain things that were not checked/simply allowed in the previous compiler:
These errors were serious footguns that could easily go unnoticed when using the previous compiler.
Examples:
References resolve to the labels defined within the same macro:
#define macro INNER() = {
target β
Resolves ββ
β
0x1 0x1 β
add β
0x2 β
eq β
β
target: <βββββββββββββ
}
Falls back to resolving to invoker's label:
#define macro MAIN() = takes(0) returns(0) {
INNER() βββββ
target: <ββββ
}
#define macro INNER() = {
^
β
target β
Resolves
0x1 0x1
add
0x2
eq
}
Resolution does not go down into invoked macros:
#define macro MAIN() = takes(0) returns(0) {
INNER()
target β Fails to Resolve
}
#define macro INNER() = {
target:
0x1 0x1
add
0x2
eq
}
As you go down an invocation chain label definitions are added to a stack where the highest most definition is resolved by references.
#define macro MAIN() = takes(0) returns(0) {
INNER()
target: π‘ Shadowed by βββββ
} β
β
β
#define macro INNER() = { β
target β
Resolves ββ β
β β
0x1 0x1 β β
add β β
0x2 β β
eq β β
β β
target: <βββββββββββββ <ββββ
}
Rust
100.0%