rlox: A Rust Implementation of “Crafting Interpreters” – Parsing and Evaluating Expressions
In this post, I briefly describe the implementation of the code in Chapter 6: Parsing Expressions, and Chapter 7: Evaluating Expressions.
🦀 Index of the Complete Series.
![]() |
---|
rlox: A Rust Implementation of “Crafting Interpreters” – Parsing and Evaluating Expressions |
🚀 Please note, code for this post can be downloaded from GitHub with:
git clone -b v0.2.0 https://github.com/behai-nguyen/rlox.git
To run the CLI application, use the following command:
$ cargo run --release
Enter a valid expression such as ((4.5 / 2) * 2) == 4.50
.
This should produce the following output:
Expression: (== (group (* (group (/ 4.5 2.0)) 2.0)) 4.5)
Evaluated to: true
- Expression: Output of the src/parser.rs module.
- Evaluated to: Output of the src/interpreter.rs module.
🚀 At this point, the CLI application is capable as a basic arithmetic calculator which supports addition, subtraction, multiplication and division.
Please also refer to
this section of the
README.md
for more information.
Legend: ★ = updated, ☆ = new.
💥 Unmodified files are omitted for brevity.
.
├── docs
│ └── RLoxGuide.md ☆
├── README.md
├── src
│ ├── ast_printer.rs ★
│ ├── expr.rs ★
│ ├── interpreter.rs ☆
│ ├── lib.rs ★
│ ├── main.rs ★
│ ├── parser.rs ☆
│ └── stmt.rs ★
├── tests
│ ├── data
│ │ └── expressions ☆
│ │ ├── evaluate.lox
│ │ ├── parse-02.lox
│ │ ├── parse-03.lox
│ │ ├── parse.lox
│ │ └── README.md
│ ├── test_common.rs ★
│ ├── test_interpreter.rs ☆
│ └── test_parser.rs ☆
└── tool
└── generate_ast
└── src
└── main.rs ★
As the
src/interpreter.rs
module was being developed, I have found that it would be more efficient
to propagate the error up until the main()
function. This is
not unexpected, as stated toward the end of
this section in a
previous post. Let’s cover those first.
❸ Expressions and Statements Refactoring
In both modules
expr.rs; and
stmt.rs, all
Visitor<T>
trait’s visit_()*
and enum’s
accept()
methods now return Result<T, LoxError>
.
This led to the following changes:
-
As discussed, both
expr.rs
andstmt.rs
are generated by a standalone tool. The relevant parts of this tool have been updated to produce the desired output. Reference. - Most of the methods in the src/ast_printer.rs module had to be refactored in response to the above changes.
This is Chapter 6. I have completed the parsing code in this chapter only:
- src/parser.rs: The parser code.
- tests/test_parser.rs: Tests for the parser module.
The parser has been wired in the src/main.rs module, it takes in a list of tokens, which is produced by the scanner. If parsing succeeds, we first call src/ast_printer.rs to display the resulting expression. Then passing the expression to the src/interpreter.rs module to evaluate.
And this is Chapter 7. The following
expressions are implemented (evaluated): Expr::Binary
,
Expr::Grouping
, Expr::Literal
and Expr::Unary
.
🦀 Please recall that the Lox language supports only the following data types:
-
Number
:f64
in Rust. E.g.1.05
. -
String
:String
in Rust. E.g.abcd
. -
Boolean
:bool
in Rust. E.g.true
orfalse
. -
Nil
: Expressed asnil
without quotations.
Expressions are evaluated to values of one of the above data types.
And I have also completed only the code for this chapter:
- src/interpreter.rs: The interpreter code.
- tests/test_interpreter.rs: Exhaustive tests for the interpreter module.
For the Expr::Grouping
enum, the corresponding
visit_grouping_expr() method doesn’t compute
anything itself, it traverses the AST to perform recursive evaluation of grouped
sub-expressions: I have not written any test for this expression.
With regard to the Expr::Literal
enum, whose corresponding
visit_literal_expr() method, does not have to implement
any operator, it just returns the underlying literal value. Whereas:
-
Expr::Binary
implements the following operator>
,>=
,<
,<=
,!=
,==
,+
,-
,*
, and/
via method visit_binary_expr(). -
Expr::Unary
implements!
and-
via method visit_unary_expr().
Please refer to the RLox Language Guide’s Expressions and Operators for more detail.
The
interpreter test module
includes exhaustive tests for all Expr::Literal
data types, all
Expr::Binary
and Expr::Unary
operators.
There is still a long journey ahead… I’m studying each chapter in turn, implementing the code and documenting my progress as I go.
There are still warnings about dead code—these, I’m happy to ignore them for now.
Thank you for reading! I hope this post helps others following the same journey. As always—stay curious, stay safe 🦊
✿✿✿
Feature image sources:
- https://www.omgubuntu.co.uk/2024/03/ubuntu-24-04-wallpaper
- https://in.pinterest.com/pin/337277459600111737/
- https://www.rust-lang.org/
- https://www.pngitem.com/download/ibmJoR_rust-language-hd-png-download/
- https://craftinginterpreters.com/