// Copyright 2017 Jeremy Wall // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. //! Bottom up parser for precedence parsing of expressions separated by binary //! operators. use std; use nom::{ErrorKind, IResult, InputIter, InputLength, Slice}; use super::{non_op_expression, NomResult, ParseResult}; use ast::*; use error; use tokenizer::TokenIter; /// Defines the intermediate stages of our bottom up parser for precedence parsing. #[derive(Debug, PartialEq, Clone)] pub enum Element { Expr(Expression), MathOp(BinaryExprType), CompareOp(CompareType), } named!(math_op_type, alt!( do_parse!(punct!("+") >> (Element::MathOp(BinaryExprType::Add))) | do_parse!(punct!("-") >> (Element::MathOp(BinaryExprType::Sub))) | do_parse!(punct!("*") >> (Element::MathOp(BinaryExprType::Mul))) | do_parse!(punct!("/") >> (Element::MathOp(BinaryExprType::Div))) ) ); fn parse_expression(i: OpListIter) -> IResult { let i_ = i.clone(); if i_.input_len() == 0 { return IResult::Error(ErrorKind::Custom(error::Error::new( format!("Expected Expression found End Of Input"), error::ErrorType::IncompleteParsing, // TODO(jwall): This position information is incorrect. Position { line: 0, column: 0 }, ))); } let el = &(i_[0]); if let &Element::Expr(ref expr) = el { return IResult::Done(i.slice(1..), expr.clone()); } return IResult::Error(ErrorKind::Custom(error::Error::new( format!( "Error while parsing Binary Expression Unexpected Operator {:?}", el ), error::ErrorType::ParseError, // TODO(jwall): This position information is incorrect. Position { line: 0, column: 0 }, ))); } fn parse_sum_operator(i: OpListIter) -> IResult { let i_ = i.clone(); if i_.input_len() == 0 { return IResult::Error(ErrorKind::Custom(error::Error::new( format!("Expected Expression found End Of Input"), error::ErrorType::IncompleteParsing, // TODO(jwall): This position information is incorrect. Position { line: 0, column: 0 }, ))); } let el = &(i_[0]); if let &Element::MathOp(ref op) = el { match op { &BinaryExprType::Add => { return IResult::Done(i.slice(1..), op.clone()); } &BinaryExprType::Sub => { return IResult::Done(i.slice(1..), op.clone()); } _other => { // noop } }; } return IResult::Error(ErrorKind::Custom(error::Error::new( format!( "Error while parsing Binary Expression Unexpected Operator {:?}", el ), error::ErrorType::ParseError, // TODO(jwall): This position information is incorrect. Position { line: 0, column: 0 }, ))); } fn tuple_to_binary_expression( tpl: (BinaryExprType, Expression, Expression), ) -> ParseResult { let pos = tpl.1.pos().clone(); Ok(Expression::Binary(BinaryOpDef { kind: tpl.0, left: Box::new(tpl.1), right: Box::new(tpl.2), pos: pos, })) } fn parse_product_operator(i: OpListIter) -> IResult { let i_ = i.clone(); if i_.input_len() == 0 { return IResult::Error(ErrorKind::Custom(error::Error::new( format!("Expected Expression found End Of Input"), error::ErrorType::IncompleteParsing, // TODO(jwall): This position information is incorrect. Position { line: 0, column: 0 }, ))); } let el = &(i_[0]); if let &Element::MathOp(ref op) = el { match op { &BinaryExprType::Mul => { return IResult::Done(i.slice(1..), op.clone()); } &BinaryExprType::Div => { return IResult::Done(i.slice(1..), op.clone()); } _other => { // noop } }; } return IResult::Error(ErrorKind::Custom(error::Error::new( format!( "Error while parsing Binary Expression Unexpected Operator {:?}", el ), error::ErrorType::ParseError, // TODO(jwall): This position information is incorrect. Position { line: 0, column: 0 }, ))); } /// do_binary_expr implements precedence based parsing where the more tightly bound /// parsers are passed in as lowerrule parsers. We default to any non_op_expression /// as the most tightly bound expressions. macro_rules! do_binary_expr { ($i:expr, $oprule:ident, $lowerrule:ident) => { do_binary_expr!($i, call!($oprule), $lowerrule) }; ($i:expr, $oprule:ident, $lowerrule:ident!( $($lowerargs:tt)* )) => { do_binary_expr!($i, call!($oprule), $lowerrule!($($lowerargs)*)) }; ($i:expr, $oprule:ident) => { do_binary_expr!($i, call!($oprule)) }; ($i:expr, $oprule:ident!( $($args:tt)* )) => { do_binary_expr!($i, $oprule!($($args)*), parse_expression) }; ($i:expr, $oprule:ident!( $($args:tt)* ), $lowerrule:ident) => { do_binary_expr!($i, $oprule!($($args)*), call!($lowerrule)) }; ($i:expr, $oprule:ident!( $($args:tt)* ), $lowerrule:ident!( $($lowerargs:tt)* )) => { map_res!($i, do_parse!( left: $lowerrule!($($lowerargs)*) >> typ: $oprule!($($args)*) >> right: $lowerrule!($($lowerargs)*) >> (typ, left, right) ), tuple_to_binary_expression ) }; } named!(sum_expression, do_binary_expr!( parse_sum_operator, alt!(trace_nom!(product_expression) | trace_nom!(parse_expression))) ); named!(product_expression, do_binary_expr!( parse_product_operator, trace_nom!(parse_expression)) ); named!(math_expression, alt!(trace_nom!(sum_expression) | trace_nom!(product_expression)) ); // TODO(jwall): Change comparison operators to use the do_binary_expr! with precedence? fn tuple_to_compare_expression( tpl: (CompareType, Expression, Expression), ) -> ParseResult { let pos = tpl.1.pos().clone(); Ok(Expression::Compare(ComparisonDef { kind: tpl.0, left: Box::new(tpl.1), right: Box::new(tpl.2), pos: pos, })) } named!(compare_op_type, alt!( do_parse!(punct!("==") >> (Element::CompareOp(CompareType::Equal))) | do_parse!(punct!("!=") >> (Element::CompareOp(CompareType::NotEqual))) | do_parse!(punct!("<=") >> (Element::CompareOp(CompareType::LTEqual))) | do_parse!(punct!(">=") >> (Element::CompareOp(CompareType::GTEqual))) | do_parse!(punct!("<") >> (Element::CompareOp(CompareType::LT))) | do_parse!(punct!(">") >> (Element::CompareOp(CompareType::GT))) ) ); fn parse_compare_operator(i: OpListIter) -> IResult { let i_ = i.clone(); if i_.input_len() == 0 { return IResult::Error(ErrorKind::Custom(error::Error::new( format!("Expected Expression found End Of Input"), error::ErrorType::IncompleteParsing, // TODO(jwall): This position information is incorrect. Position { line: 0, column: 0 }, ))); } let el = &(i_[0]); if let &Element::CompareOp(ref op) = el { return IResult::Done(i.slice(1..), op.clone()); } return IResult::Error(ErrorKind::Custom(error::Error::new( format!( "Error while parsing Binary Expression Unexpected Operator {:?}", el ), error::ErrorType::ParseError, // TODO(jwall): This position information is incorrect. Position { line: 0, column: 0 }, ))); } named!(compare_expression, map_res!( do_parse!( left: alt!(trace_nom!(math_expression) | trace_nom!(parse_expression)) >> typ: parse_compare_operator >> right: alt!(trace_nom!(math_expression) | trace_nom!(parse_expression)) >> (typ, left, right) ), tuple_to_compare_expression ) ); /// Parse a list of expressions separated by operators into a Vec. fn parse_operand_list(i: TokenIter) -> NomResult> { // 1. First try to parse a non_op_expression, let mut _i = i.clone(); let mut list = Vec::new(); // 1. loop let mut firstrun = true; loop { // 2. Parse a non_op_expression. match non_op_expression(_i.clone()) { IResult::Error(e) => { // A failure to parse an expression // is always an error. return IResult::Error(e); } IResult::Incomplete(i) => { return IResult::Incomplete(i); } IResult::Done(rest, expr) => { list.push(Element::Expr(expr)); _i = rest.clone(); } } // 3. Parse an operator. match alt!(_i, math_op_type | compare_op_type) { IResult::Error(e) => { if firstrun { // If we don't find an operator in our first // run then this is not an operand list. return IResult::Error(e); } // if we don't find one on subsequent runs then // that's the end of the operand list. break; } IResult::Incomplete(i) => { return IResult::Incomplete(i); } IResult::Done(rest, el) => { list.push(el); _i = rest.clone(); } } firstrun = false; } return IResult::Done(_i, list); } #[derive(Clone, Debug, PartialEq)] pub struct OpListIter<'a> { pub source: &'a [Element], } impl<'a> OpListIter<'a> { pub fn len(&self) -> usize { self.source.len() } } impl<'a> InputLength for OpListIter<'a> { fn input_len(&self) -> usize { self.source.input_len() } } macro_rules! impl_op_iter_slice { ($r:ty) => { impl<'a> Slice<$r> for OpListIter<'a> { fn slice(&self, range: $r) -> Self { OpListIter { source: self.source.slice(range), } } } }; } impl_op_iter_slice!(std::ops::Range); impl_op_iter_slice!(std::ops::RangeTo); impl_op_iter_slice!(std::ops::RangeFrom); impl_op_iter_slice!(std::ops::RangeFull); impl<'a> std::ops::Index for OpListIter<'a> { type Output = Element; fn index(&self, i: usize) -> &Self::Output { &self.source[i] } } impl<'a> InputIter for OpListIter<'a> { type Item = &'a Element; type RawItem = Element; type Iter = std::iter::Enumerate>; type IterElem = std::slice::Iter<'a, Self::RawItem>; fn iter_indices(&self) -> Self::Iter { self.source.iter().enumerate() } fn iter_elements(&self) -> Self::IterElem { self.source.iter() } fn position

(&self, predicate: P) -> Option where P: Fn(Self::RawItem) -> bool, { for (o, v) in self.iter_indices() { if predicate(v.clone()) { return Some(o); } } None } fn slice_index(&self, count: usize) -> Option { let mut cnt = 0; for (index, _) in self.iter_indices() { if cnt == count { return Some(index); } cnt += 1; } if cnt == count { return Some(self.len()); } None } } /// Parse a binary operator expression. pub fn op_expression(i: TokenIter) -> NomResult { let preparse = parse_operand_list(i.clone()); match preparse { IResult::Error(e) => IResult::Error(e), IResult::Incomplete(i) => IResult::Incomplete(i), IResult::Done(rest, oplist) => { // TODO run our binary parsing. let mut i_ = OpListIter { source: oplist.as_slice(), }; let parse_result = alt!( i_, trace_nom!(math_expression) | trace_nom!(compare_expression) ); match parse_result { IResult::Error(e) => IResult::Error(e), IResult::Incomplete(i) => IResult::Incomplete(i), IResult::Done(_, expr) => IResult::Done(rest.clone(), expr), } } } }