metadata.tags = ["build a programming language", "rust"]
metadata.date = "2021-05-09 19:14:42 -0400"
metadata.shortDesc = ""
metadata.slug = "variable-declarations"
metadata.preamble = `<p style="font-style: italic;">This post is part of a <a href="/build-a-programming-language/" data-link="/build-a-programming-language/">series</a> about learning Rust and building a small programming language.</p><hr>`
```
Now that the parser can handle multiple statements and the usage of variables, let's add the ability to actually declare variables.
<!-- excerpt-end -->
First off, the lexer now lexes a couple new things: the `let` keyword and the equals sign.
When the parser tries to parse a statement and sees a let token, it knows it's looking at a variable declaration. After the let token it expects to find a identifier (the variable name), an equals sign, and then an expression for the initial value of the variable. The variable name and the initial value expression then make up a `Declare` AST node.
expect_token!(it, Equals, "expected equals after identifier after let");
let value = parse_expression(it).expect("initial value in let statement");
Some(Statement::Declare { name, value })
}
// ...
};
// ...
}
```
`expect_token!` is a simple macro I wrote to handle expecting to see a specific token in the stream and panicking if it's not there, since that's a pattern that was coming up frequently:
```rust
macro_rules! expect_token {
($stream:ident, $token:ident, $msg:expr) => {
if let Some(Token::$token) = $stream.peek() {
$stream.next();
} else {
panic!($msg);
}
};
}
```
Next, to actually evaluate variable declarations, the evaulator needs to have some concept of a context. Right now, every expression can be evaluated without any external state. But, when a variable is declared, we want it to be accessible later on in the code, so there needs to be somewhere to store that information.
For now, the only information we need is the map of variable names to their values.
```rust
struct Context {
variables: HashMap<String,Value>,
}
```
There are also a few methods for `Context`, one to construct a new context and one to declare a variable with an initial value.
Every `eval_` function has also changed to take a reference to the current context[^1] and the main `eval` function creates a context before evaluating each statement.
[^1]: For now a simple mutable reference is fine, because there's only ever one context: the global one. But, in the future, this will need to be something a bit more complicated.
With that, declaration statements can be evaluated just by calling the `declare_variable` method on the context: