Compare commits

..

3 Commits

Author SHA1 Message Date
9f903556f9 feat: implement #3 challenge for ch6
i kinda cheated, i was checking the answers and then stumbled upon the
grammar. Sorry :(
2025-01-05 06:26:31 +04:00
977fd9a8a8 fix: actually finish challeng #2(ch.6)
I didn't implement the scanner and the printer lol
2025-01-05 05:12:20 +04:00
e54e1b1b27 feat: finish challenge #2 from the book
Implementing ternary conditional
2025-01-05 04:57:56 +04:00
5 changed files with 83 additions and 2 deletions

View File

@ -2,6 +2,13 @@ use crate::token::{LiteralType, Token};
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub enum Expr { pub enum Expr {
Ternary {
first: Box<Expr>,
first_op: Token,
second: Box<Expr>,
second_op: Token,
third: Box<Expr>,
},
Binary { Binary {
left: Box<Expr>, left: Box<Expr>,
op: Token, op: Token,

View File

@ -28,10 +28,32 @@ impl Parser<'_> {
} }
// Challenge #1. We're writing comma before equality, because it has the lowest precedence // Challenge #1. We're writing comma before equality, because it has the lowest precedence
// comma -> equality ((,) equality)* ; // expression grammar // comma -> equality ("," equality)* ; // expression grammar
fn comma(&mut self) -> Result<Expr, ParseError> { fn comma(&mut self) -> Result<Expr, ParseError> {
use TokenType::*; use TokenType::*;
self.left_association_binary(&[Comma], Parser::equality) self.left_association_binary(&[Comma], Self::ternary)
}
// ternary -> equality ("?" expression : ternary)? // expression grammar
fn ternary(&mut self) -> Result<Expr, ParseError> {
use TokenType::*;
let expr = self.equality()?;
if self.match_token(&[Question]) {
let first_op = self.previous().clone();
let second = self.expression()?;
let second_op = self.consume(Colon, "Expected : after ternary operator ?")?;
let third = self.ternary()?;
return Ok(Expr::Ternary {
first: Box::new(expr),
first_op,
second: Box::new(second),
second_op,
third: Box::new(third),
});
}
Ok(expr)
} }
fn equality(&mut self) -> Result<Expr, ParseError> { fn equality(&mut self) -> Result<Expr, ParseError> {
@ -68,6 +90,11 @@ impl Parser<'_> {
self.primary() self.primary()
} }
/* error boundaries:
("!=" | "==") equality
| (">" | ">=" | "<" | "<=") comparison
| ("+") term
| ("/" | "*") factor ; */
fn primary(&mut self) -> Result<Expr, ParseError> { fn primary(&mut self) -> Result<Expr, ParseError> {
use LiteralType::*; use LiteralType::*;
use TokenType::*; use TokenType::*;
@ -101,6 +128,38 @@ impl Parser<'_> {
}); });
} }
if self.match_token(&[Equal, BangEqual]) {
let _ = self.equality();
return Err(ParseError {
token: self.previous().clone(),
msg: "Missing left-hand operand.".to_string(),
});
}
if self.match_token(&[Greater, GreaterEqual, Less, LessEqual]) {
let _ = self.comparison();
return Err(ParseError {
token: self.previous().clone(),
msg: "Missing left-hand operand.".to_string(),
});
}
if self.match_token(&[Plus]) {
let _ = self.term();
return Err(ParseError {
token: self.previous().clone(),
msg: "Missing left-hand operand.".to_string(),
});
}
if self.match_token(&[Star, Slash]) {
let _ = self.factor();
return Err(ParseError {
token: self.previous().clone(),
msg: "Missing left-hand operand.".to_string(),
});
}
Err(ParseError { Err(ParseError {
token: self.peek().clone(), token: self.peek().clone(),
msg: "Expect expression.".to_string(), msg: "Expect expression.".to_string(),
@ -119,6 +178,7 @@ impl Parser<'_> {
} }
// will not be used for the time being (per the book) // will not be used for the time being (per the book)
// used for error recovery
fn synchronize(&mut self) { fn synchronize(&mut self) {
use TokenType::*; use TokenType::*;
self.advance(); self.advance();

View File

@ -12,6 +12,16 @@ pub fn pretty_print(expr: &Expr) -> String {
None => "None".to_string(), None => "None".to_string(),
}, },
Expr::Unary { op, right } => parenthesize(&op.lexeme, &[right]), Expr::Unary { op, right } => parenthesize(&op.lexeme, &[right]),
Expr::Ternary {
first,
first_op,
second,
second_op,
third,
} => parenthesize(
&format!("{}{}", first_op.lexeme, second_op.lexeme),
&[first, second, third],
),
} }
} }

View File

@ -99,6 +99,8 @@ impl Scanner {
'<' => self.add_token(TokenType::Less), '<' => self.add_token(TokenType::Less),
'>' if self.peek_and_match('=') => self.add_token(TokenType::GreaterEqual), '>' if self.peek_and_match('=') => self.add_token(TokenType::GreaterEqual),
'>' => self.add_token(TokenType::Greater), '>' => self.add_token(TokenType::Greater),
'?' => self.add_token(TokenType::Question),
':' => self.add_token(TokenType::Colon),
// checking for comments and just advance the iterator if it's a comment // checking for comments and just advance the iterator if it's a comment
'/' if self.peek_and_match('/') => { '/' if self.peek_and_match('/') => {
while self.peek().is_some_and(|x| x != '\n') { while self.peek().is_some_and(|x| x != '\n') {

View File

@ -22,6 +22,8 @@ pub enum TokenType {
GreaterEqual, GreaterEqual,
Less, Less,
LessEqual, LessEqual,
Question,
Colon,
Identifier, Identifier,
String, String,