Skip to content

Commit 0156a95

Browse files
authored
Chapter 5: Representing Code (#2)
This implements the [code representation in an AST](http://craftinginterpreters.com/representing-code.html). The visitor pattern is slightly different from the original Java. I had to box the enum values. This should be suffice for now since I am not aiming at performance yet.
1 parent 9fef15e commit 0156a95

File tree

4 files changed

+108
-2
lines changed

4 files changed

+108
-2
lines changed

README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,6 @@
33
A [Lox](http://craftinginterpreters.com/the-lox-language.html) Interpreter in Rust based on the
44
[Crafting Interpreters](http://craftinginterpreters.com) book.
55

6-
Each commit corresponds to one chapter in the book.
6+
Each commit corresponds to one chapter in the book:
7+
8+
* [Chapter 4](https://github.com/jeschkies/lox-rs/commit/9fef15e73fdf57a3e428bb074059c7e144e257f7)

src/main.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
mod error;
22
mod scanner;
3+
mod syntax;
34
mod token;
45

56
use std::io::{self, BufRead};

src/syntax.rs

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
use crate::token::Token;
2+
3+
pub enum Expr {
4+
Binary {
5+
left: Box<Expr>,
6+
operator: Token,
7+
right: Box<Expr>,
8+
},
9+
Grouping {
10+
expression: Box<Expr>,
11+
},
12+
Literal {
13+
value: String,
14+
}, // Object in chapter 5
15+
Unary {
16+
operator: Token,
17+
right: Box<Expr>,
18+
},
19+
}
20+
21+
pub trait Visitor<R> {
22+
fn visit_binary_expr(&self, left: &Expr, operator: &Token, right: &Expr) -> R;
23+
fn visit_grouping_expr(&self, expression: &Expr) -> R;
24+
fn visit_literal_expr(&self, value: String) -> R;
25+
fn visit_unary_expr(&self, operator: &Token, right: &Expr) -> R;
26+
}
27+
28+
impl Expr {
29+
pub fn accept<R>(&self, visitor: &Visitor<R>) -> R {
30+
match self {
31+
Expr::Binary {left, operator, right} => visitor.visit_binary_expr(left, operator, right),
32+
Expr::Grouping { expression } => visitor.visit_grouping_expr(expression),
33+
Expr::Literal { value } => visitor.visit_literal_expr(value.to_string()),
34+
Expr::Unary {operator, right } => visitor.visit_unary_expr(operator, right),
35+
}
36+
}
37+
}
38+
39+
pub struct AstPrinter;
40+
41+
impl AstPrinter {
42+
pub fn print(&self, expr: Expr) -> String {
43+
expr.accept(self)
44+
}
45+
46+
fn parenthesize(&self, name: String, exprs: Vec<&Expr>) -> String {
47+
let mut r = String::new();
48+
r.push_str("(");
49+
r.push_str(&name);
50+
for e in &exprs {
51+
r.push_str(" ");
52+
r.push_str(&e.accept(self));
53+
}
54+
r.push_str(")");
55+
r
56+
}
57+
}
58+
59+
impl Visitor<String> for AstPrinter {
60+
fn visit_binary_expr(&self, left: &Expr, operator: &Token, right: &Expr) -> String {
61+
self.parenthesize(operator.lexeme.clone(), vec![left, right])
62+
}
63+
64+
fn visit_grouping_expr(&self, expr: &Expr) -> String {
65+
self.parenthesize("group".to_string(), vec![expr])
66+
}
67+
68+
fn visit_literal_expr(&self, value: String) -> String {
69+
value // check for null
70+
}
71+
72+
fn visit_unary_expr(&self, operator: &Token, right: &Expr) -> String {
73+
self.parenthesize(operator.lexeme.clone(), vec![right])
74+
}
75+
}
76+
77+
78+
#[cfg(test)]
79+
mod tests {
80+
use super::*;
81+
use crate::token::{Token, TokenType};
82+
83+
#[test]
84+
fn test_printer() {
85+
let expression = Expr::Binary {
86+
left: Box::new(Expr::Unary {
87+
operator: Token::new(TokenType::Minus,"-", 1),
88+
right: Box::new(Expr::Literal {
89+
value: "123".to_string(),
90+
}),
91+
}),
92+
operator: Token::new(TokenType::Star, "*", 1),
93+
right: Box::new(Expr::Grouping {
94+
expression: Box::new(Expr::Literal {
95+
value: "45.67".to_string(),
96+
}),
97+
}),
98+
};
99+
let printer = AstPrinter;
100+
101+
assert_eq!(printer.print(expression), "(* (- 123) (group 45.67))");
102+
}
103+
}

src/token.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ include!(concat!(env!("OUT_DIR"), "/keywords.rs"));
5858

5959
pub struct Token {
6060
tpe: TokenType,
61-
lexeme: String,
61+
pub lexeme: String,
6262
line: i32,
6363
}
6464

0 commit comments

Comments
 (0)