Puff is an experimental Huff compiler implemented in Racket.
Pretty much everything not mentioned in the next section
- Custom constructors
- Functions (no plan on supporting these, functions in huff are kinda weird imo lol)
- Code/Jump Tables
- Racket
- Rust
# macOS with Homebrew
brew install racket
# Arch/Manjaro
sudo pacman -S racket
# Ubuntu/Debian
sudo apt install racket
# Fedora
sudo dnf install racketAfter installing Racket and Rust, run:
make installThis will run a custom install script which ensures the required Rust dependency is compiled and Racket libraries are installed. Then, it builds an executable and puts it in your PATH. The install script will prompt you for a directory where you want the puffc executable to live.
Compile one of the example contracts:
puffc -b examples/add_two.huff
The compile-src function illustrates the compilation pipeline
(define (compile-src src)
(~> src
lex ;; src -> tokens
parse ;; tokens -> syntax obj
syntax->datum ;; syntax obj -> AST
analyze-node ;; AST -> compiler data
compile-program-data ;; compiler data -> bytes
bytes->hex)) ;; bytes -> hex stringThis happens in puff/lexer.rkt. In it we define some patterns we want to capture in the source code as specific tokens. This lets us have a much simpler and more explicit grammar.
We use the brag language to define our grammar in huffparser.rkt. With this grammar definintion, we get a parse function anywhere just by importing huffparser.rkt.
We define various phases of analysis in analysis.rkt. In this file we do all the work required to go from an AST to a compiler-data struct, which is a struct meant to contain all the data required to actually compile a contract. The compiler-data object essentiallyu acts as an enhanced AST, all the same data is contained but with the ability to easily and quickly perform lookups on labels, constants, errors, events, etc.
This happens in puff.rkt, codegen.rkt, and all the phases under puff/phases. We coordinate the various steps in puff.rkt but most of the actual compilation logic is implemented in various handlers in codegen.rkt and the phases.
Puff depends on the keccak implementation from Alloy.
We need a tiny bit of Rust code to build a library we can call from Racket over FFI. This is all handled by the makefile.
This rust code is found under rust_src