Skip to content

Commit

Permalink
Comparison operators implemented
Browse files Browse the repository at this point in the history
  • Loading branch information
tailhook committed Sep 24, 2017
1 parent 871e0b1 commit 0028b3f
Show file tree
Hide file tree
Showing 5 changed files with 172 additions and 1 deletion.
99 changes: 99 additions & 0 deletions src/compare.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
use grammar::CmpOperator;
use std::i64;


/// An internal representation of something that can be compared
///
/// Only numbers and strings can be compared for now.
Expand All @@ -8,13 +12,20 @@ pub struct Comparable<'a>(ComparableInner<'a>);

#[derive(Debug)] // TODO(tailhook) make normal debug
enum ComparableInner<'a> {
Bool(bool),
I64(i64),
U64(u64),
F64(f64),
Str(&'a str),
String(String),
}

impl<'a> From<bool> for Comparable<'a> {
fn from(x: bool) -> Comparable<'a> {
Comparable(ComparableInner::Bool(x))
}
}

impl<'a> From<i8> for Comparable<'a> {
fn from(x: i8) -> Comparable<'a> {
Comparable(ComparableInner::I64(x as i64))
Expand Down Expand Up @@ -86,3 +97,91 @@ impl<'a> From<String> for Comparable<'a> {
Comparable(ComparableInner::String(x))
}
}

fn _compare<T: PartialEq + PartialOrd>(a: &T, b: &T, oper: CmpOperator) -> bool
{
use grammar::CmpOperator::*;
match oper {
Eq => a == b,
Neq => a != b,
Less => a < b,
Greater => a > b,
LessEq => a <= b,
GreaterEq => a >= b,
}
}

fn _less(oper: CmpOperator) -> bool {
use grammar::CmpOperator::*;
match oper {
Eq => false,
Neq => true,
Less => true,
Greater => false,
LessEq => true,
GreaterEq => false,
}
}

pub fn compare(a: &Comparable, b: &Comparable, oper: CmpOperator)
-> Result<bool, ()>
{
use self::ComparableInner::*;

match (&a.0, &b.0) {
// Same type
(&Bool(x), &Bool(y)) => Ok(_compare(&x, &y, oper)),
(&I64(x), &I64(y)) => Ok(_compare(&x, &y, oper)),
(&U64(x), &U64(y)) => Ok(_compare(&x, &y, oper)),
(&F64(x), &F64(y)) => Ok(_compare(&x, &y, oper)),
(&Str(x), &Str(y)) => Ok(_compare(&x, &y, oper)),
(&String(ref x), &String(ref y)) => Ok(_compare(x, y, oper)),
(&Str(x), &String(ref y)) => Ok(_compare(&x, &&y[..], oper)),
(&String(ref x), &Str(y)) => Ok(_compare(&&x[..], &y, oper)),

// Cast to float
(&I64(x), &F64(y)) => Ok(_compare(&(x as f64), &y, oper)),
(&F64(x), &I64(y)) => Ok(_compare(&x, &(y as f64), oper)),
(&U64(x), &F64(y)) => Ok(_compare(&(x as f64), &y, oper)),
(&F64(x), &U64(y)) => Ok(_compare(&x, &(y as f64), oper)),
// Two ints
(&I64(x), &U64(_)) if x < 0 => Ok(_less(oper)),
(&I64(x), &U64(y)) if y < i64::MAX as u64
=> Ok(_compare(&x, &(y as i64), oper)),
(&I64(_), &U64(_)) => Ok(_less(oper)),
(&U64(_), &I64(y)) if y < 0 => Ok(!_less(oper)),
(&U64(x), &I64(y)) if x < i64::MAX as u64
=> Ok(_compare(&(x as i64), &y, oper)),
(&U64(_), &I64(_)) => Ok(!_less(oper)),

// Disabled
(&Str(_), &Bool(_)) => Err(()),
(&String(_), &Bool(_)) => Err(()),
(&Bool(_), &Str(_)) => Err(()),
(&Bool(_), &String(_)) => Err(()),
(&Str(_), &I64(_)) => Err(()),
(&String(_), &I64(_)) => Err(()),
(&I64(_), &Str(_)) => Err(()),
(&I64(_), &String(_)) => Err(()),
(&Str(_), &U64(_)) => Err(()),
(&String(_), &U64(_)) => Err(()),
(&U64(_), &Str(_)) => Err(()),
(&U64(_), &String(_)) => Err(()),
(&Str(_), &F64(_)) => Err(()),
(&String(_), &F64(_)) => Err(()),
(&F64(_), &Str(_)) => Err(()),
(&F64(_), &String(_)) => Err(()),
(&Bool(_), &I64(_)) => Err(()),
(&I64(_), &Bool(_)) => Err(()),
(&Bool(_), &U64(_)) => Err(()),
(&U64(_), &Bool(_)) => Err(()),
(&Bool(_), &F64(_)) => Err(()),
(&F64(_), &Bool(_)) => Err(()),
}
}

#[test]
#[cfg(target_arch="x64_64")]
fn size() {
assert_eq!(size_of::<Comparable>(), 32);
}
2 changes: 1 addition & 1 deletion src/grammar.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use tokenizer::{Tokenizer, TokenStream, Token, Kind};
use {Options, Pos};


#[derive(Debug, PartialEq)]
#[derive(Debug, PartialEq, Clone, Copy)]
pub enum CmpOperator {
Eq,
Neq,
Expand Down
51 changes: 51 additions & 0 deletions src/render.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ use grammar::OutputMode::{self, Preserve, Strip, Space};
use grammar::{self, Statement, Expr, AssignTarget, Template as Tpl};
use number::{self, Number};
use owning::{Own, ExprCode};
use compare::{compare};
use preparser::Syntax::Oneline;
use render_error::{RenderError, DataError};
use varmap::{Context, SubContext, set, get};
Expand Down Expand Up @@ -223,6 +224,56 @@ fn eval_expr<'x, 'render: 'x>(r: &mut Renderer, root: &SubContext<'x, 'render>,
}
}
},
ExprCode::Comparison(ref left, ref vec) => {
assert!(vec.len() > 0);
let mut cur_exp = eval_expr(r, root, left);
for i in 0..vec.len() {
cur_exp = {
let left = match cur_exp.as_comparable() {
Ok(c) => c,
Err(e) => {
r.errors.push((expr.position.0, e));
return OwningRef::new(nothing(&r.nothing, root))
.map(|_| UNDEFINED as &Variable)
}
};
let right = expr.clone().map(|e| match e.code {
grammar::ExprCode::Comparison(_, ref vec) => {
&vec[i].1
}
_ => unreachable!(),
});
let oper = vec[i].0;
let rexpr = eval_expr(r, root, &right);
let next = match rexpr.as_comparable() {
Ok(c) => c,
Err(e) => {
r.errors.push((expr.position.0, e));
return OwningRef::new(nothing(&r.nothing, root))
.map(|_| UNDEFINED as &Variable)
}
};
match compare(&left, &next, oper) {
Ok(false) => {
return OwningRef::new(nothing(&r.nothing, root))
.map(|_| FALSE as &Variable);
}
Ok(true) => {}
Err(()) => {
r.errors.push((expr.position.0,
Incomparable(cur_exp.typename(),
rexpr.typename())
));
return OwningRef::new(nothing(&r.nothing, root))
.map(|_| UNDEFINED as &Variable)
}
}
rexpr.clone()
};
}
return OwningRef::new(nothing(&r.nothing, root))
.map(|_| TRUE as &Variable);
}
x => panic!("Unimplemented oper {:?}", x),
}
}
Expand Down
6 changes: 6 additions & 0 deletions src/render_error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,12 @@ quick_error! {
description("variable or attribute not found")
display("variable or attribute {:?} not found", name)
}
/// Incomparable types
Incomparable(left_type: &'static str, right_type: &'static str) {
description("two types can't be compared")
display("Can't compare object of type {:?} to {:?}",
left_type, right_type)
}
/// Custom error
Custom(err: Box<Error>) {
description(err.description())
Expand Down
15 changes: 15 additions & 0 deletions src/std_vars.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ impl<'a, 'render: 'a> Variable<'render> for &'a str {
fn as_bool(&self) -> Result<bool, DataError> {
Ok(self.len() > 0)
}
fn as_comparable(&self) -> Result<Comparable, DataError> {
Ok((*self).into())
}
}

impl<'x> Variable<'x> for String {
Expand All @@ -37,6 +40,9 @@ impl<'x> Variable<'x> for String {
fn as_bool(&self) -> Result<bool, DataError> {
Ok(self.len() > 0)
}
fn as_comparable(&self) -> Result<Comparable, DataError> {
Ok(self[..].into())
}
}

impl<'x> Variable<'x> for IpAddr {
Expand Down Expand Up @@ -100,6 +106,12 @@ impl<'x> Variable<'x> for Option<&'x str> {
fn as_bool(&self) -> Result<bool, DataError> {
Ok(true)
}
fn as_comparable(&self) -> Result<Comparable, DataError> {
match *self {
Some(ref x) => Ok(x[..].into()),
None => Ok("".into()),
}
}
}

macro_rules! impl_number {
Expand Down Expand Up @@ -246,4 +258,7 @@ impl<'x> Variable<'x> for bool {
fn as_bool(&self) -> Result<bool, DataError> {
Ok(*self)
}
fn as_comparable(&self) -> Result<Comparable, DataError> {
Ok((*self).into())
}
}

0 comments on commit 0028b3f

Please sign in to comment.