metasyn icon navigation icon
pastel rainbow stripes

Crafting Interpreters

below are some of the notes i am taking while reading through robert nystrom’s crafting interpreters book.

chapter 2: a map of the territory

the long route.

front end

intermediate representations

middle end

optimizations

back end

code generation

runtime

shortcuts

chapter 3: lox, the language

chapter 4: scanning

the scanner in the book is written in java, and, it uses mutable array that it pushes tokens to as they’re parsed. it also keeps track of where it is in the string with a simple pointer. however, this doesn’t work as easily with rust, where strings are utf-8 (which may be multiple bytes per grapheme/character). in order to index into the String - we need to either use a Chars iterator or use chars_indices on the String to figure out where one char maps to a particular byte. I could probably have gotten away with just using the exact same approach - because all the identifiers and keywords in the language are single byte utf-8 characters (e.g. ascii). However - this part of the book gave me a chance to dig more into the distinction between str/string/bytes/chars/graphemes in rust. i ended up implementing a version of the scanner that uses Peekable - and that was enough to keep track of the state of the iterator and advance when needed.

chapter 5: representing code

lox grammar:

chapter 6: parsing expressions

example lox grammar:

expression     → literal
               | unary
               | binary
               | grouping ;

literal        → NUMBER | STRING | "true" | "false" | "nil" ;
grouping       → "(" expression ")" ;
unary          → ( "-" | "!" ) expression ;
binary         → expression operator expression ;
operator       → "==" | "!=" | "<" | "<=" | ">" | ">="
               | "+"  | "-"  | "*" | "/" ;

recursive decent parsing:

grammar <------------->   precedence
------------------------------------
top       equality        lower
          comparison
          addition  
          multiplication
bottom    unary           higher

the parser in the book is written almost identically to the the scanner, however it needs a lot of conditional matching. this sort of works with Peekable again in rust, however, it ends up causing some problems when you need to check more than one character forward at a time. i think there was probably a way to do it while still satisfying the borrow cchecker, but the code started to look ugly and copying the code from the book was simple nough. one other thing that came up while i was doing this was noticing the lack of variadic arguments in rust. it seems simple enough to just pass a vector of the argument type in question, but, still found it surprising. another thing i learned about here was slightly more complex destructuring in rust:

match c {
  Token { token_type: Foo | Bar} => println!("match foo or bar field values")
  Token { token_type: Foo } => println!("match struct with field value"}
  Token { .. } => println!("match struct"}
}

in the book’s implementation, java’s lack of builtin pattern matching becomes really obvious, and having a ton of if branches that just try to make a match don’t really feel like very idiomatic rust. however, at the end of the day, its ok to do the simpler thing in this case, since i think the complexity can still be completely hidden from export, by putting the entire parser and its state into a static function.

chapter 7: evaluating expressions

chapter 8: statements & sate

chapter 9: control flow


last updated:

2025.02.05