Add parse_if and parse_map
This commit is contained in:
		
							parent
							
								
									c6e6e1dbee
								
							
						
					
					
						commit
						c5c9cd3458
					
				
							
								
								
									
										54
									
								
								src/ast.rs
									
									
									
									
									
								
							
							
						
						
									
										54
									
								
								src/ast.rs
									
									
									
									
									
								
							| @ -46,7 +46,7 @@ pub enum Expression { | |||||||
| impl Parse for Expression { | impl Parse for Expression { | ||||||
|     fn parse(mut stream: TokenStream) -> Result<Expression, Error> { |     fn parse(mut stream: TokenStream) -> Result<Expression, Error> { | ||||||
|         let lhs = parse_primary_expression(&mut stream)?; |         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<Expression, Erro | |||||||
| fn parse_binop_rhs( | fn parse_binop_rhs( | ||||||
|     stream: &mut TokenStream, |     stream: &mut TokenStream, | ||||||
|     mut lhs: Expression, |     mut lhs: Expression, | ||||||
|     expr_prec: i8, |     mut prev_operator: Option<BinaryOperator>, | ||||||
|     mut operator: Option<BinaryOperator>, | ) -> Result<Expression, Error> { | ||||||
| ) -> Result<(Expression, Option<BinaryOperator>), Error> { |     // Expression precedence = LHS precedence so far.
 | ||||||
|     while let Some(op) = operator.take().as_ref().or(stream.parse().as_ref().ok()) { |     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::<BinaryOperator, _>(|b| b.get_precedence() >= expr_precedence) | ||||||
|  |     { | ||||||
|         let curr_token_prec = op.get_precedence(); |         let curr_token_prec = op.get_precedence(); | ||||||
|  |         let mut rhs = parse_primary_expression(stream)?; | ||||||
| 
 | 
 | ||||||
|         if curr_token_prec < expr_prec { |         if let Ok(next_op) = stream.parse_peek::<BinaryOperator>() { | ||||||
|             let _ = operator.insert(*op); |             let next_prec = next_op.get_precedence(); | ||||||
|             break; // Just return lhs
 |             if curr_token_prec < next_prec { | ||||||
|         } else { |                 // Operator on the right of rhs has more precedence, turn
 | ||||||
|             let mut rhs = parse_primary_expression(stream)?; |                 // rhs into lhs for new binop
 | ||||||
| 
 |                 rhs = parse_binop_rhs(stream, rhs, Some(op))?; | ||||||
|             if let Ok(next_op) = stream.parse::<BinaryOperator>() { |             } else { | ||||||
|                 let next_prec = next_op.get_precedence(); |                 let _ = prev_operator.insert(next_op); | ||||||
|                 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); |  | ||||||
|                 } |  | ||||||
|             } |             } | ||||||
| 
 |  | ||||||
|             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)] | #[derive(Debug, Clone, Copy)] | ||||||
|  | |||||||
| @ -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<T: Parse + std::fmt::Debug>(&mut self) -> Result<T, Error> { |     pub fn parse<T: Parse + std::fmt::Debug>(&mut self) -> Result<T, Error> { | ||||||
|  |         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<T: Parse + std::fmt::Debug>(&mut self) -> Result<T, Error> { | ||||||
|  |         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<T: Parse + std::fmt::Debug, F, O>(&mut self, inner: F) -> Result<O, Error> | ||||||
|  |     where | ||||||
|  |         F: Fn(T) -> Result<O, Error>, | ||||||
|  |     { | ||||||
|  |         let (res, pos) = self.parse_meta::<T>()?; | ||||||
|  |         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<T: Parse + std::fmt::Debug, F>(&mut self, inner: F) -> Result<T, Error> | ||||||
|  |     where | ||||||
|  |         F: Fn(&T) -> bool, | ||||||
|  |     { | ||||||
|  |         let (res, pos) = self.parse_meta::<T>()?; | ||||||
|  |         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<T: Parse + std::fmt::Debug>(&mut self) -> Result<(T, usize), Error> { | ||||||
|         let mut ref_pos = self.position; |         let mut ref_pos = self.position; | ||||||
| 
 | 
 | ||||||
|         let position = self.position; |         let position = self.position; | ||||||
| @ -69,9 +123,9 @@ impl<'a, 'b> TokenStream<'a, 'b> { | |||||||
| 
 | 
 | ||||||
|         match T::parse(clone) { |         match T::parse(clone) { | ||||||
|             Ok(res) => { |             Ok(res) => { | ||||||
|                 self.position = ref_pos.max(self.position); |  | ||||||
|                 dbg!(&res); |                 dbg!(&res); | ||||||
|                 Ok(res) |                 let new_pos = ref_pos.max(self.position); | ||||||
|  |                 Ok((res, new_pos)) | ||||||
|             } |             } | ||||||
|             Err(e) => Err(e), |             Err(e) => Err(e), | ||||||
|         } |         } | ||||||
| @ -101,4 +155,10 @@ pub enum Error { | |||||||
|     Expected(String, Token, Position), |     Expected(String, Token, Position), | ||||||
|     #[error("Source file contains no tokens")] |     #[error("Source file contains no tokens")] | ||||||
|     FileEmpty, |     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, | ||||||
| } | } | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user