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 racket
After installing Racket and Rust, run:
make install
This 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 string
This 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