From c5c9cd345848566d7b03ee88091c84036349e371 Mon Sep 17 00:00:00 2001 From: sofia Date: Wed, 17 Jul 2024 19:28:06 +0300 Subject: [PATCH] Add parse_if and parse_map --- src/ast.rs | 54 +++++++++++++++++++------------------- src/token_stream.rs | 64 +++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 89 insertions(+), 29 deletions(-) diff --git a/src/ast.rs b/src/ast.rs index 46371d6..e74f06c 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -46,7 +46,7 @@ pub enum Expression { impl Parse for Expression { fn parse(mut stream: TokenStream) -> Result { let lhs = parse_primary_expression(&mut stream)?; - parse_binop_rhs(&mut stream, lhs, 0, None).map(|(rhs, _)| rhs) + parse_binop_rhs(&mut stream, lhs, None) } } @@ -81,38 +81,38 @@ fn parse_primary_expression(stream: &mut TokenStream) -> Result, -) -> Result<(Expression, Option), Error> { - while let Some(op) = operator.take().as_ref().or(stream.parse().as_ref().ok()) { + mut prev_operator: Option, +) -> Result { + // Expression precedence = LHS precedence so far. + let expr_precedence = if let Some(op) = prev_operator.take() { + op.get_precedence() + } else { + 0 + }; + + while let Ok(op) = + // If next operator precedence is lower than expression precedence, we + // need to climb back up the recursion. + stream.parse_if::(|b| b.get_precedence() >= expr_precedence) + { let curr_token_prec = op.get_precedence(); + let mut rhs = parse_primary_expression(stream)?; - if curr_token_prec < expr_prec { - let _ = operator.insert(*op); - break; // Just return lhs - } else { - let mut rhs = parse_primary_expression(stream)?; - - if let Ok(next_op) = stream.parse::() { - let next_prec = next_op.get_precedence(); - if curr_token_prec < next_prec { - // Operator on the right of rhs has more precedence, turn - // rhs into lhs for new binop - let op; - (rhs, op) = parse_binop_rhs(stream, rhs, curr_token_prec + 1, Some(next_op))?; - if let Some(upcoming_op) = op { - let _ = operator.insert(upcoming_op); - } - } else { - let _ = operator.insert(next_op); - } + if let Ok(next_op) = stream.parse_peek::() { + let next_prec = next_op.get_precedence(); + 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, Some(op))?; + } else { + let _ = prev_operator.insert(next_op); } - - lhs = Expression::Binop(*op, Box::new(lhs), Box::new(rhs)); } + + lhs = Expression::Binop(op, Box::new(lhs), Box::new(rhs)); } - Ok((lhs, operator)) + Ok(lhs) } #[derive(Debug, Clone, Copy)] diff --git a/src/token_stream.rs b/src/token_stream.rs index 395ed10..0cd31fc 100644 --- a/src/token_stream.rs +++ b/src/token_stream.rs @@ -57,7 +57,61 @@ impl<'a, 'b> TokenStream<'a, 'b> { } } + /// Parse the next value of trait Parse. If the parse succeeded, the related + /// tokens are consumed, otherwise token stream does not advance. + /// + /// Parsetime-error is returned on failure. pub fn parse(&mut self) -> Result { + let (res, pos) = self.parse_meta()?; + self.position = pos; + Ok(res) + } + + /// Parse the next item with Parse-trait (Same as [`TokenStream::parse`]) + /// without consuming the related tokens, essentially only peeking. + pub fn parse_peek(&mut self) -> Result { + self.parse_meta().map(|(res, _)| res) + } + + /// Parse the next item with Parse-trait, also mapping it with the given + /// function. The token-stream is only consumed, if the inner function + /// retuns an Ok. + pub fn parse_map(&mut self, inner: F) -> Result + where + F: Fn(T) -> Result, + { + let (res, pos) = self.parse_meta::()?; + match inner(res) { + Ok(mapped) => { + self.position = pos; + Ok(mapped) + } + Err(e) => Err(e), + } + } + + /// Parses the item with Parse if the condition specified by the + /// lambda-function is passed. Errors returned from this should not be + /// passed to the end-user. + pub fn parse_if(&mut self, inner: F) -> Result + where + F: Fn(&T) -> bool, + { + let (res, pos) = self.parse_meta::()?; + if inner(&res) { + self.position = pos; + Ok(res) + } else { + Err(Error::IfFailed) + } + } + + /// Parse the next item with Parse-trait. If successful, returning the + /// parsed item and the new position of the TokenStream. Failing, returning + /// parse-error. + /// + /// Used for [`TokenStream::parse`] and [`TokenStream::parse_peek`] + fn parse_meta(&mut self) -> Result<(T, usize), Error> { let mut ref_pos = self.position; let position = self.position; @@ -69,9 +123,9 @@ impl<'a, 'b> TokenStream<'a, 'b> { match T::parse(clone) { Ok(res) => { - self.position = ref_pos.max(self.position); dbg!(&res); - Ok(res) + let new_pos = ref_pos.max(self.position); + Ok((res, new_pos)) } Err(e) => Err(e), } @@ -101,4 +155,10 @@ pub enum Error { Expected(String, Token, Position), #[error("Source file contains no tokens")] FileEmpty, + /// Only use this error in situations where the error never ends up for the end-user! + #[error("Undefined error, should only be used in situations where the error is not emitted!")] + Undefined, + /// Condition failed for the parse-if + #[error("Condition failed for parse-if. Should never be returned to end-user.")] + IfFailed, }