Home

Awesome

Huff 2

Huff2 is the successor of the huff-rs compiler written in Rust. It comes with:

Missing Features / TODOs

Why rewrite 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:

This new foundation will allow bugs to be fixed more easily as well as allowing us to experiment with our own novel compiler features. šŸ˜

Differences vs. huff-rs

CLI Changes

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

Stricter Code Validation

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.

New Label & Jump Table Semantics

  1. Up Only: Macros can only reference labels defined within them or the parents invoking them
  2. Shadowing: Label definitions deeper down in a chain of invocations shadows previous definitions

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:  <ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜ <ā”€ā”€ā”€ā”˜
}