diff --git a/site/posts/2021-04-15-evaluation.md b/site/posts/2021-04-15-evaluation.md
new file mode 100644
index 0000000..8a726a7
--- /dev/null
+++ b/site/posts/2021-04-15-evaluation.md
@@ -0,0 +1,76 @@
+```
+metadata.title = "Part 3: Basic Evaluation"
+metadata.tags = ["build a programming language", "rust"]
+metadata.date = "2021-04-15 17:00:42 -0400"
+metadata.shortDesc = "A bad calculator."
+metadata.slug = "evaluation"
+metadata.preamble = `
This post is part of a series about learning Rust and building a small programming language.
`
+```
+
+Last time I said operator precedence was going to be next. Well, if you've read the title, you know that's not the case. I decided I really wanted to see this actually run^[evaluate] some code^[a single expression], so let's do that.
+
+
+
+First, there needs to be something to actually store values during the evaluation process. For this, I used yet another enum. It only has one case for now because we can currently only lex and parse integer values and one arithmetic operator.
+
+```rust
+enum Value {
+ Integer(i64),
+}
+```
+
+There's also a helper function to extract the underlying integer from a value in places where we're certain it's going to be an integer:
+
+```rust
+impl Value {
+ fn expect_integer(&self, &msg) -> i64 {
+ match self {
+ Value::Integer(n) => *n,
+ _ => panic!("{}", msg),
+ }
+ }
+}
+```
+
+The compiler warns about the unreachable match arm, but it'll be useful once there are more types of values. (Once again, actual error reporting will wait.)
+
+The actual evaulation starts in the `eval` function which takes a reference to the node to evaluate and returns a `Value` representing its result.
+
+For integer nodes, the value of the AST node is wrapped in a Value and returned directly. For binary operator (i.e. addition) nodes the left- and right-hand values are extracted and another function is called to perform the operation.
+
+```rust
+fn eval(node: &Node) -> Value {
+ match node {
+ Node::Integer(n) => Value::Integer(*n),
+ Node::BinaryOp { left, right } => eval_binary_op(left, right),
+ }
+}
+```
+
+This `eval_binary_op` function takes each of the nodes and calls `eval` with it. By doing this, it recurses through the the AST evaluating each node in a depth-first manner. It then turns each value into an integer (panicking if either isn't what it expects) and returns a new Value with the values added together.
+
+```rust
+fn eval_binary_op(left: &Node, right: &Node) -> Value {
+ let left = eval(left).expect_integer("left hand side of binary operator must be an integer");
+ let right = eval(right).expect_integer("right hand side of binary operator must be an integer");
+ Value::Integer(left + right)
+}
+```
+
+And with that surpisingly small amount of code, I've got a very dumb calculator that can perform arbitrary additions:
+
+```rust
+fn main() {
+ let tokens = tokenize("1 + 2 + 3");
+ if let Some(node) = parse(tokens) {
+ println!("result: {:?}", eval(&node));
+ }
+}
+```
+
+```sh
+$ cargo run
+result: Integer(6)
+```
+
+Next time, I'll add some more operators and actually get around to operator precedence.