Add parse_if and parse_map
This commit is contained in:
parent
c6e6e1dbee
commit
c5c9cd3458
40
src/ast.rs
40
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() {
|
||||||
let curr_token_prec = op.get_precedence();
|
op.get_precedence()
|
||||||
|
|
||||||
if curr_token_prec < expr_prec {
|
|
||||||
let _ = operator.insert(*op);
|
|
||||||
break; // Just return lhs
|
|
||||||
} else {
|
} 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 mut rhs = parse_primary_expression(stream)?;
|
let mut rhs = parse_primary_expression(stream)?;
|
||||||
|
|
||||||
if let Ok(next_op) = stream.parse::<BinaryOperator>() {
|
if let Ok(next_op) = stream.parse_peek::<BinaryOperator>() {
|
||||||
let next_prec = next_op.get_precedence();
|
let next_prec = next_op.get_precedence();
|
||||||
if curr_token_prec < next_prec {
|
if curr_token_prec < next_prec {
|
||||||
// Operator on the right of rhs has more precedence, turn
|
// Operator on the right of rhs has more precedence, turn
|
||||||
// rhs into lhs for new binop
|
// rhs into lhs for new binop
|
||||||
let op;
|
rhs = parse_binop_rhs(stream, rhs, Some(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 {
|
} else {
|
||||||
let _ = operator.insert(next_op);
|
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)]
|
#[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