Add binop expression parsing

This commit is contained in:
Sofia 2023-07-27 22:25:13 +03:00
parent 40f3738719
commit ee14e18787
4 changed files with 86 additions and 28 deletions

View File

@ -3,8 +3,9 @@
import std::print;
let test = 5;
let simpleAdd = 2 + 2;
let arithmetic = 3 + 2 * 5 + 1 * 2;
let multiplier = 5 * 2;
let result = arithmetic * multiplier + arithmetic;
let result = arithmetic + multiplier * arithmetic;
print(result);

View File

@ -93,22 +93,6 @@ pub fn tokenize<T: Into<String>>(to_tokenize: T) -> Result<Vec<FullToken>, Strin
Ok(tokens)
}
pub struct FullToken {
pub token: Token,
position: Position,
}
impl Debug for FullToken {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_fmt(format_args!(
"{:?} (Ln {}, Col {})",
self.token, self.position.1, self.position.0
))
}
}
pub type Position = (u32, u32);
#[derive(Debug, Eq, PartialEq, Clone)]
pub enum Token {
// Values
@ -132,6 +116,32 @@ pub enum Token {
Eof,
}
impl Token {
pub fn get_token_prec(&self) -> i8 {
match &self {
Token::Plus => 10,
Token::Times => 20,
_ => -1,
}
}
}
pub struct FullToken {
pub token: Token,
position: Position,
}
impl Debug for FullToken {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_fmt(format_args!(
"{:?} (Ln {}, Col {})",
self.token, self.position.1, self.position.0
))
}
}
pub type Position = (u32, u32);
pub struct Cursor<'a> {
pub position: Position,
char_stream: Peekable<Chars<'a>>,

View File

@ -7,24 +7,71 @@ where
fn parse(stream: TokenStream) -> Result<Self, ()>;
}
#[derive(Debug)]
#[derive(Debug, Clone)]
pub enum Expression {
VariableName(String),
ContantI32(i32),
BinopAdd(Box<Expression>, Box<Expression>),
BinopMult(Box<Expression>, Box<Expression>),
}
impl Parseable for Expression {
fn parse(mut stream: TokenStream) -> Result<Expression, ()> {
if let Some(token) = stream.next() {
Ok(match &token {
Token::Identifier(v) => Expression::VariableName(v.clone()),
Token::DecimalValue(v) => Expression::ContantI32(v.parse().unwrap()),
_ => Err(())?,
})
let lhs = parse_primary_expression(&mut stream)?;
parse_binop_rhs(&mut stream, lhs, 0)
}
}
fn parse_primary_expression(stream: &mut TokenStream) -> Result<Expression, ()> {
if let Some(token) = stream.next() {
Ok(match &token {
Token::Identifier(v) => Expression::VariableName(v.clone()),
Token::DecimalValue(v) => Expression::ContantI32(v.parse().unwrap()),
_ => Err(())?,
})
} else {
Err(())
}
}
/// This algorithm seems somewhat like magic to me. I understand it if I read
/// carefully, but it is difficult to read every single time.
///
/// Reference for how the algorithm is formed:
/// https://llvm.org/docs/tutorial/MyFirstLanguageFrontend/LangImpl02.html#binary-expression-parsing
fn parse_binop_rhs(
stream: &mut TokenStream,
mut lhs: Expression,
expr_prec: i8,
) -> Result<Expression, ()> {
while let Some(token) = stream.peek() {
let curr_token_prec = token.get_token_prec();
if curr_token_prec < expr_prec {
break; // Just return lhs
} else {
Err(())
// token has to be an operator
stream.next(); // Eat token
let mut rhs = parse_primary_expression(stream)?;
if let Some(next_op) = stream.peek() {
let next_prec = next_op.get_token_prec();
if curr_token_prec < next_prec {
// Operator on the right of rhs has more precedence, turn
// rhs into lhs for new binop
rhs = parse_binop_rhs(stream, rhs, curr_token_prec + 1)?;
}
}
lhs = match &token {
Token::Plus => Expression::BinopAdd(Box::new(lhs), Box::new(rhs)),
Token::Times => Expression::BinopMult(Box::new(lhs), Box::new(rhs)),
_ => Err(())?,
};
}
}
Ok(lhs)
}
#[derive(Debug)]

View File

@ -20,7 +20,7 @@ impl<'a, 'b> TokenStream<'a, 'b> {
pub fn expect(&mut self, token: Token) -> Result<(), ()> {
if let Some(peeked) = self.peek() {
if token == *peeked {
if token == peeked {
self.position += 1;
Ok(())
} else {
@ -41,11 +41,11 @@ impl<'a, 'b> TokenStream<'a, 'b> {
value
}
pub fn peek(&mut self) -> Option<&Token> {
pub fn peek(&mut self) -> Option<Token> {
if self.tokens.len() < self.position {
None
} else {
Some(&self.tokens[self.position].token)
Some(self.tokens[self.position].token.clone())
}
}