Skip to content

Commit

Permalink
Merge branch 'expression' into 'master'
Browse files Browse the repository at this point in the history
Parse 12. Expression

See merge request ricos/truck/ruststep!6
  • Loading branch information
termoshtt committed Nov 13, 2020
2 parents d3325d7 + a1f3347 commit a71470a
Show file tree
Hide file tree
Showing 4 changed files with 358 additions and 2 deletions.
1 change: 1 addition & 0 deletions exp2rs/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,4 @@ regex = "1.4.2"
lazy_static = "1.4.0"
trybuild = "1.0.35"
nom = "6.0.0"
derive_more = "0.99.11"
339 changes: 339 additions & 0 deletions exp2rs/src/parser/expression.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,339 @@
use super::*;
use derive_more::From;
use nom::{branch::*, bytes::complete::*, combinator::*, IResult, Parser};

/// Unary expresion, e.g. `x` or binary expression `x + y`
#[derive(Debug, Clone, PartialEq)]
pub enum Expr<Base, Op> {
Unary(Base),
Binary { op: Op, arg1: Base, arg2: Base },
}

impl<Base, Op> From<Base> for Expr<Base, Op> {
fn from(base: Base) -> Self {
Expr::Unary(base)
}
}

pub type Expression = Expr<SimpleExpression, RelOpExtended>;
pub type SimpleExpression = Expr<Term, AddLikeOp>;
pub type Term = Expr<Factor, MultiplicationLikeOp>;
pub type Factor = Expr<SimpleFactor, PowerOp>;

fn expr<Base, Op>(
input: &str,
base_parse: impl Fn(&str) -> IResult<&str, Base> + Copy,
op_parser: impl Fn(&str) -> IResult<&str, Op>,
) -> IResult<&str, Expr<Base, Op>> {
tuple((
base_parse,
opt(tuple((multispace0, op_parser, multispace0, base_parse))),
))
.map(|(base, opt)| {
if let Some((_, op, _, arg2)) = opt {
Expr::Binary {
op,
arg1: base,
arg2,
}
} else {
Expr::Unary(base)
}
})
.parse(input)
}

/// 216 expression = simple_expression [ rel_op_extended simple_expression ] .
pub fn expression(input: &str) -> IResult<&str, Expression> {
expr(input, simple_expression, rel_op_extended)
}

/// 305 simple_expression = term { add_like_op term } .
pub fn simple_expression(input: &str) -> IResult<&str, SimpleExpression> {
expr(input, term, add_like_op)
}

/// 325 term = factor { multiplication_like_op factor } .
pub fn term(input: &str) -> IResult<&str, Term> {
expr(input, factor, multiplication_like_op)
}

/// 217 factor = simple_factor [ `**` simple_factor ] .
pub fn factor(input: &str) -> IResult<&str, Factor> {
expr(input, simple_factor, power_op)
}

#[derive(Debug, Clone, PartialEq)]
pub enum PrimaryOrExpression {
Primary(Primary),
Expression(Box<Expression>), // to avoid recusive definition
}

#[derive(Debug, Clone, PartialEq)]
pub enum SimpleFactor {
PrimaryOrExpression {
unary_op: Option<UnaryOp>,
primary_or_expression: PrimaryOrExpression,
},
}

impl From<Primary> for SimpleFactor {
fn from(primary: Primary) -> Self {
SimpleFactor::PrimaryOrExpression {
unary_op: None,
primary_or_expression: PrimaryOrExpression::Primary(primary),
}
}
}

impl From<Expression> for SimpleFactor {
fn from(expression: Expression) -> Self {
SimpleFactor::PrimaryOrExpression {
unary_op: None,
primary_or_expression: PrimaryOrExpression::Expression(Box::new(expression)),
}
}
}

/// 306 simple_factor = aggregate_initializer
/// | entity_constructor
/// | enumeration_reference
/// | interval
/// | query_expression
/// | ( [ unary_op ] ( `(` expression `)` | primary ) ) .
pub fn simple_factor(input: &str) -> IResult<&str, SimpleFactor> {
// FIXME Add aggregate_initializer
// FIXME Add entity_constructor
// FIXME Add enumeration_reference
// FIXME Add interval
// FIXME Add query_expression
tuple((
opt(tuple((unary_op, multispace0)).map(|(op, _)| op)),
alt((
primary.map(|primary| PrimaryOrExpression::Primary(primary)),
tuple((tag("("), multispace0, expression, multispace0, tag(")"))).map(
|(_, _, expression, _, _)| PrimaryOrExpression::Expression(Box::new(expression)),
),
)),
))
.map(
|(unary_op, primary_or_expression)| SimpleFactor::PrimaryOrExpression {
unary_op,
primary_or_expression,
},
)
.parse(input)
}

#[derive(Debug, Clone, PartialEq, From)]
pub enum Primary {
Literal(Literal),
}

/// 269 primary = literal | ( qualifiable_factor { qualifier } ) .
pub fn primary(input: &str) -> IResult<&str, Primary> {
// FIXME add qualifiable_factor branch
literal
.map(|literal| Primary::Literal(literal))
.parse(input)
}

#[derive(Debug, Clone, PartialEq, From)]
pub enum RelOpExtended {
RelOp(RelOp),
/// `IN`
In,
/// `LIKE`
Like,
}

/// 283 rel_op_extended = rel_op | `IN` | `LIKE` .
pub fn rel_op_extended(input: &str) -> IResult<&str, RelOpExtended> {
use RelOpExtended::*;
alt((
rel_op.map(|op| RelOp(op)),
alt((value(In, tag("IN")), value(Like, tag("LIKE")))),
))
.parse(input)
}

#[derive(Debug, Clone, PartialEq)]
pub enum RelOp {
/// `=`
Equal,
/// `<>`
NotEqual,
/// `<`
LT,
/// `>`
GT,
/// `<=`
LEQ,
/// `>=`
GEQ,
/// `:=:`
InstanceEqual,
/// `:<>:`
InstanceNotEqual,
}

/// 282 rel_op = `<` | `>` | `<=` | `>=` | `<>` | `=` | `:<>:` | `:=:` .
pub fn rel_op(input: &str) -> IResult<&str, RelOp> {
use RelOp::*;
alt((
value(Equal, tag("=")),
value(NotEqual, tag("<>")),
value(LT, tag("<")),
value(GT, tag(">")),
value(LEQ, tag("<=")),
value(GEQ, tag(">=")),
value(InstanceEqual, tag(":=:")),
value(InstanceNotEqual, tag(":<>:")),
))
.parse(input)
}

#[derive(Debug, Clone, PartialEq)]
pub enum UnaryOp {
/// `+`
Plus,
/// `-`
Minus,
/// `NOT`
Not,
}

/// 331 unary_op = `+` | `-` | `NOT` .
pub fn unary_op(input: &str) -> IResult<&str, UnaryOp> {
use UnaryOp::*;
alt((
value(Plus, tag("+")),
value(Minus, tag("-")),
value(Not, tag("NOT")),
))
.parse(input)
}

#[derive(Debug, Clone, PartialEq)]
pub enum MultiplicationLikeOp {
/// `*`
Mul,
/// `/`
RealDiv,
/// `DIV`
IntegerDiv,
/// `MOD`
Mod,
/// `AND`
And,
/// `||`, Complex entity instance construction operator (12.10)
ComplexEntityInstanceConstruction,
}

/// 257 multiplication_like_op = `*` | `/` | `DIV` | `MOD` | `AND` | `||` .
pub fn multiplication_like_op(input: &str) -> IResult<&str, MultiplicationLikeOp> {
use MultiplicationLikeOp::*;
alt((
value(Mul, tag("*")),
value(RealDiv, tag("/")),
value(IntegerDiv, tag("DIV")),
value(Mod, tag("MOD")),
value(And, tag("AND")),
value(ComplexEntityInstanceConstruction, tag("||")),
))
.parse(input)
}

#[derive(Debug, Clone, PartialEq)]
pub enum AddLikeOp {
/// `+`
Add,
/// `-`
Sub,
/// `OR`
Or,
/// `XOR`
Xor,
}

/// 168 add_like_op = `+` | `-` | `OR` | `XOR` .
pub fn add_like_op(input: &str) -> IResult<&str, AddLikeOp> {
use AddLikeOp::*;
alt((
value(Add, tag("+")),
value(Sub, tag("-")),
value(Or, tag("OR")),
value(Xor, tag("XOR")),
))
.parse(input)
}

#[derive(Debug, Clone, PartialEq)]
pub enum PowerOp {
/// `**`
Power,
}

/// 999 power_op = `**`
///
/// Additional trivial rule for managing operators uniformly
pub fn power_op(input: &str) -> IResult<&str, PowerOp> {
value(PowerOp::Power, tag("**")).parse(input)
}

#[cfg(test)]
mod tests {
use super::*;
use nom::Finish;

#[test]
fn expr() {
let (residual, e) = super::expression("1 - 2").finish().unwrap();
dbg!(e);
assert_eq!(residual, "");
}

#[test]
fn simple_factor() {
let (residual, p) = super::simple_factor("123").finish().unwrap();
assert_eq!(p, Primary::Literal(Literal::Real(123.0)).into());
assert_eq!(residual, "");

let (residual, p) = super::simple_factor("-123").finish().unwrap();
assert_eq!(
p,
SimpleFactor::PrimaryOrExpression {
unary_op: Some(UnaryOp::Minus),
primary_or_expression: PrimaryOrExpression::Primary(Primary::Literal(
Literal::Real(123.0)
)),
}
);
assert_eq!(residual, "");
}

#[test]
fn simple_factor_expression() {
let (residual, expr) = super::expression("1 + 2").finish().unwrap();
assert_eq!(residual, "");

let (residual, sf) = super::simple_factor("(1 + 2)").finish().unwrap();
assert_eq!(residual, "");
match sf {
SimpleFactor::PrimaryOrExpression {
unary_op: _,
primary_or_expression,
} => match primary_or_expression {
PrimaryOrExpression::Expression(e) => assert_eq!(*e, expr),
_ => panic!("Must be expression"),
},
}
}

#[test]
fn primary() {
let (residual, p) = super::primary("123").finish().unwrap();
assert_eq!(p, Literal::Real(123.0).into());
assert_eq!(residual, "");
}
}
18 changes: 16 additions & 2 deletions exp2rs/src/parser/literal.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use derive_more::From;
use nom::{
branch::*, bytes::complete::*, character::complete::*, combinator::*, number::complete::double,
IResult, Parser,
Expand All @@ -10,14 +11,27 @@ pub enum Logical {
Unknown,
}

#[derive(Debug, Clone, PartialEq)]
#[derive(Debug, Clone, PartialEq, From)]
pub enum Literal {
Integer(u64),
Real(f64),
Logial(Logical),
}

/// 251 literal = binary_literal | logical_literal | real_literal | string_literal .
///
/// Integer value, e.g. `23` will be recognized as a real number `23.0`
///
/// Example
/// --------
///
/// ```
/// use exp2rs::parser::*;
/// use nom::Finish;
///
/// let (residual, l) = literal("23").finish().unwrap();
/// assert_eq!(l, Literal::Real(23.0));
/// assert_eq!(residual, "");
/// ```
pub fn literal(input: &str) -> IResult<&str, Literal> {
alt((
logical_literal.map(|val| Literal::Logial(val)),
Expand Down
2 changes: 2 additions & 0 deletions exp2rs/src/parser/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@
//! This module is based on [nom](https://github.com/Geal/nom) parser combinater.
mod entity;
mod expression;
mod literal;
mod schema;
mod simple_data_type;

pub use entity::*;
pub use expression::*;
pub use literal::*;
pub use schema::*;
pub use simple_data_type::*;
Expand Down

0 comments on commit a71470a

Please sign in to comment.