Skip to content

Commit 9508c9d

Browse files
authored
Chapter 6: Parsing Expressions (#3)
This implements the [parsing](http://www.craftinginterpreters.com/parsing-expressions.htmll). A few differences: * `matches!` is a macro to allow varargs. It stands in for `match` from the Java version. * The literals are typed via `LiteralValue`. * `primary` does not use `matches!` but the Rust pattern matching to extract values. * We use `Result` instead of exceptions.
1 parent 0156a95 commit 9508c9d

File tree

6 files changed

+304
-13
lines changed

6 files changed

+304
-13
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,4 @@ A [Lox](http://craftinginterpreters.com/the-lox-language.html) Interpreter in Ru
66
Each commit corresponds to one chapter in the book:
77

88
* [Chapter 4](https://github.com/jeschkies/lox-rs/commit/9fef15e73fdf57a3e428bb074059c7e144e257f7)
9+
* [Chapter 5](https://github.com/jeschkies/lox-rs/commit/0156a95b4bf448dbff9cb4341a2339b741a163ca)

src/error.rs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
use std::fmt;
2+
3+
use crate::token::{Token, TokenType};
4+
15
pub fn error(line: i32, message: &str) {
26
report(line, "", message);
37
}
@@ -6,3 +10,24 @@ pub fn report(line: i32, where_: &str, message: &str) {
610
eprintln!("[line {}] Error{}: {}", line, where_, message);
711
// had_error = true; TODO: Use custom Error type
812
}
13+
14+
pub fn parser_error(token: &Token, message: &str) {
15+
if token.tpe == TokenType::EOF {
16+
report(token.line, " at end", message);
17+
} else {
18+
report(token.line, &format!(" at '{}'", token.lexeme), message);
19+
}
20+
}
21+
22+
#[derive(Debug)]
23+
pub enum Error {
24+
Parse,
25+
}
26+
27+
impl fmt::Display for Error {
28+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
29+
match self {
30+
Error::Parse => write!(f, "ParseError"),
31+
}
32+
}
33+
}

src/main.rs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
mod error;
2+
mod parser;
23
mod scanner;
34
mod syntax;
45
mod token;
@@ -7,7 +8,9 @@ use std::io::{self, BufRead};
78
use std::process::exit;
89
use std::{env, fs};
910

11+
use parser::Parser;
1012
use scanner::Scanner;
13+
use syntax::AstPrinter;
1114

1215
fn main() -> Result<(), Box<dyn std::error::Error + 'static>> {
1316
let args: Vec<String> = env::args().collect();
@@ -40,8 +43,10 @@ fn run(source: String) -> io::Result<()> {
4043
let mut scanner = Scanner::new(source);
4144
let tokens = scanner.scan_tokens();
4245

43-
for token in tokens {
44-
println!("{}", token);
46+
let mut parser = Parser::new(tokens);
47+
if let Some(expression) = parser.parse() {
48+
let printer = AstPrinter;
49+
println!("{}", printer.print(expression));
4550
}
4651
Ok(())
4752
}

src/parser.rs

Lines changed: 237 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,237 @@
1+
use crate::error::{parser_error, Error};
2+
use crate::syntax::{Expr, LiteralValue};
3+
use crate::token::{Token, TokenType};
4+
5+
pub struct Parser<'t> {
6+
tokens: &'t Vec<Token>,
7+
current: usize,
8+
}
9+
10+
/// AKA match in Chapter 6.
11+
macro_rules! matches {
12+
( $sel:ident, $( $x:expr ),* ) => {
13+
{
14+
if $( $sel.check($x) )||* {
15+
$sel.advance();
16+
true
17+
} else {
18+
false
19+
}
20+
}
21+
};
22+
}
23+
24+
impl<'t> Parser<'t> {
25+
pub fn new(tokens: &'t Vec<Token>) -> Self {
26+
Parser { tokens, current: 0 }
27+
}
28+
29+
pub fn parse(&mut self) -> Option<Expr> {
30+
self.expression().ok()
31+
}
32+
33+
fn expression(&mut self) -> Result<Expr, Error> {
34+
self.equality()
35+
}
36+
37+
fn equality(&mut self) -> Result<Expr, Error> {
38+
let mut expr = self.comparison()?;
39+
40+
while matches!(self, TokenType::BangEqual, TokenType::EqualEqual) {
41+
let operator: Token = (*self.previous()).clone();
42+
let right: Expr = self.comparison()?;
43+
expr = Expr::Binary {
44+
left: Box::new(expr),
45+
operator,
46+
right: Box::new(right),
47+
};
48+
}
49+
50+
Ok(expr)
51+
}
52+
53+
fn check(&self, token_type: TokenType) -> bool {
54+
if self.is_at_end() {
55+
return false;
56+
}
57+
58+
token_type == self.peek().tpe
59+
}
60+
61+
fn consume(&mut self, tpe: TokenType, message: &str) -> Result<Token, Error> {
62+
if self.check(tpe) {
63+
Ok(self.advance().clone())
64+
} else {
65+
Err(self.error(self.peek(), message))
66+
}
67+
}
68+
69+
fn advance(&mut self) -> &Token {
70+
if !self.is_at_end() {
71+
self.current += 1;
72+
}
73+
self.previous()
74+
}
75+
76+
fn previous(&self) -> &Token {
77+
self.tokens
78+
.get(self.current - 1)
79+
.expect("Previous was empty.")
80+
}
81+
82+
fn error(&self, token: &Token, message: &str) -> Error {
83+
parser_error(token, message);
84+
Error::Parse
85+
}
86+
87+
fn synchronize(&mut self) {
88+
self.advance();
89+
90+
while !self.is_at_end() {
91+
if self.previous().tpe == TokenType::Semicolon {
92+
return;
93+
}
94+
95+
match self.peek().tpe {
96+
TokenType::Class
97+
| TokenType::Fun
98+
| TokenType::Var
99+
| TokenType::For
100+
| TokenType::If
101+
| TokenType::While
102+
| TokenType::Print
103+
| TokenType::Return => return,
104+
_ => self.advance(),
105+
};
106+
}
107+
}
108+
109+
fn peek(&self) -> &Token {
110+
self.tokens
111+
.get(self.current)
112+
.expect("Peek into end of token stream.")
113+
}
114+
115+
fn is_at_end(&self) -> bool {
116+
self.peek().tpe == TokenType::EOF
117+
}
118+
119+
fn comparison(&mut self) -> Result<Expr, Error> {
120+
let mut expr = self.addition()?;
121+
122+
while matches!(
123+
self,
124+
TokenType::Greater,
125+
TokenType::GreaterEqual,
126+
TokenType::Less,
127+
TokenType::LessEqual
128+
) {
129+
let operator: Token = self.previous().clone();
130+
let right = self.addition()?;
131+
expr = Expr::Binary {
132+
left: Box::new(expr),
133+
operator,
134+
right: Box::new(right),
135+
}
136+
}
137+
138+
Ok(expr)
139+
}
140+
141+
fn addition(&mut self) -> Result<Expr, Error> {
142+
let mut expr = self.multiplication()?;
143+
144+
while matches!(self, TokenType::Minus, TokenType::Plus) {
145+
let operator: Token = self.previous().clone();
146+
let right = self.multiplication()?;
147+
expr = Expr::Binary {
148+
left: Box::new(expr),
149+
operator,
150+
right: Box::new(right),
151+
}
152+
}
153+
154+
Ok(expr)
155+
}
156+
157+
fn multiplication(&mut self) -> Result<Expr, Error> {
158+
let mut expr = self.unary()?;
159+
160+
while matches!(self, TokenType::Slash, TokenType::Star) {
161+
let operator: Token = self.previous().clone();
162+
let right = self.unary()?;
163+
expr = Expr::Binary {
164+
left: Box::new(expr),
165+
operator,
166+
right: Box::new(right),
167+
}
168+
}
169+
170+
Ok(expr)
171+
}
172+
173+
fn unary(&mut self) -> Result<Expr, Error> {
174+
if matches!(self, TokenType::Bang, TokenType::Minus) {
175+
let operator: Token = self.previous().clone();
176+
let right = self.unary()?;
177+
Ok(Expr::Unary {
178+
operator,
179+
right: Box::new(right),
180+
})
181+
} else {
182+
self.primary()
183+
}
184+
}
185+
186+
fn primary(&mut self) -> Result<Expr, Error> {
187+
// We don't use matches!() here since we want to extract the literals.
188+
let expr = match &self.peek().tpe {
189+
TokenType::False => Expr::Literal {
190+
value: LiteralValue::Boolean(false),
191+
},
192+
TokenType::True => Expr::Literal {
193+
value: LiteralValue::Boolean(true),
194+
},
195+
TokenType::Nil => Expr::Literal {
196+
value: LiteralValue::Null,
197+
},
198+
TokenType::String { literal } => Expr::Literal {
199+
value: LiteralValue::String(literal.clone()),
200+
},
201+
TokenType::Number { literal } => Expr::Literal {
202+
value: LiteralValue::Number(literal.clone()),
203+
},
204+
TokenType::LeftParen => {
205+
let expr = self.expression()?;
206+
self.consume(TokenType::RightParen, "Expected ')' after expression.")?;
207+
Expr::Grouping {
208+
expression: Box::new(expr),
209+
}
210+
}
211+
_ => return Err(self.error(self.peek(), "Expect expression.")),
212+
};
213+
214+
self.advance();
215+
216+
Ok(expr)
217+
}
218+
}
219+
220+
#[cfg(test)]
221+
mod tests {
222+
use super::*;
223+
use crate::scanner::Scanner;
224+
use crate::syntax::AstPrinter;
225+
226+
#[test]
227+
fn test_parser() {
228+
let mut scanner = Scanner::new("-123 * 45.67".to_string());
229+
let tokens = scanner.scan_tokens();
230+
231+
let mut parser = Parser::new(tokens);
232+
let expression = parser.parse().expect("Could not parse sample code.");
233+
let printer = AstPrinter;
234+
235+
assert_eq!(printer.print(expression), "(* (- 123) 45.67)");
236+
}
237+
}

src/syntax.rs

Lines changed: 30 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use crate::token::Token;
2+
use std::fmt;
23

34
pub enum Expr {
45
Binary {
@@ -10,14 +11,32 @@ pub enum Expr {
1011
expression: Box<Expr>,
1112
},
1213
Literal {
13-
value: String,
14-
}, // Object in chapter 5
14+
value: LiteralValue,
15+
},
1516
Unary {
1617
operator: Token,
1718
right: Box<Expr>,
1819
},
1920
}
2021

22+
pub enum LiteralValue {
23+
Boolean(bool),
24+
Null,
25+
Number(f64),
26+
String(String),
27+
}
28+
29+
impl fmt::Display for LiteralValue {
30+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
31+
match self {
32+
LiteralValue::Boolean(b) => write!(f, "{}", b),
33+
LiteralValue::Null => write!(f, "null"),
34+
LiteralValue::Number(n) => write!(f, "{}", n),
35+
LiteralValue::String(s) => write!(f, "{}", s),
36+
}
37+
}
38+
}
39+
2140
pub trait Visitor<R> {
2241
fn visit_binary_expr(&self, left: &Expr, operator: &Token, right: &Expr) -> R;
2342
fn visit_grouping_expr(&self, expression: &Expr) -> R;
@@ -28,10 +47,14 @@ pub trait Visitor<R> {
2847
impl Expr {
2948
pub fn accept<R>(&self, visitor: &Visitor<R>) -> R {
3049
match self {
31-
Expr::Binary {left, operator, right} => visitor.visit_binary_expr(left, operator, right),
50+
Expr::Binary {
51+
left,
52+
operator,
53+
right,
54+
} => visitor.visit_binary_expr(left, operator, right),
3255
Expr::Grouping { expression } => visitor.visit_grouping_expr(expression),
3356
Expr::Literal { value } => visitor.visit_literal_expr(value.to_string()),
34-
Expr::Unary {operator, right } => visitor.visit_unary_expr(operator, right),
57+
Expr::Unary { operator, right } => visitor.visit_unary_expr(operator, right),
3558
}
3659
}
3760
}
@@ -74,7 +97,6 @@ impl Visitor<String> for AstPrinter {
7497
}
7598
}
7699

77-
78100
#[cfg(test)]
79101
mod tests {
80102
use super::*;
@@ -84,15 +106,15 @@ mod tests {
84106
fn test_printer() {
85107
let expression = Expr::Binary {
86108
left: Box::new(Expr::Unary {
87-
operator: Token::new(TokenType::Minus,"-", 1),
109+
operator: Token::new(TokenType::Minus, "-", 1),
88110
right: Box::new(Expr::Literal {
89-
value: "123".to_string(),
111+
value: LiteralValue::Number(123f64),
90112
}),
91113
}),
92114
operator: Token::new(TokenType::Star, "*", 1),
93115
right: Box::new(Expr::Grouping {
94116
expression: Box::new(Expr::Literal {
95-
value: "45.67".to_string(),
117+
value: LiteralValue::Number(45.67f64),
96118
}),
97119
}),
98120
};

0 commit comments

Comments
 (0)