Skip to content

Commit

Permalink
Implement Between expression
Browse files Browse the repository at this point in the history
  • Loading branch information
AmrDeveloper committed Jun 29, 2023
1 parent 412e87b commit 7aeab10
Show file tree
Hide file tree
Showing 3 changed files with 124 additions and 6 deletions.
19 changes: 19 additions & 0 deletions src/expression.rs
Original file line number Diff line number Diff line change
Expand Up @@ -264,3 +264,22 @@ impl Expression for CallExpression {
return prototype.result.clone();
}
}

pub struct BetweenExpression {
pub value: Box<dyn Expression>,
pub range_start: Box<dyn Expression>,
pub range_end: Box<dyn Expression>,
}

impl Expression for BetweenExpression {
fn evaluate(&self, object: &GQLObject) -> String {
let value = self.value.evaluate(object).parse::<i64>().unwrap();
let range_start = self.range_start.evaluate(object).parse::<i64>().unwrap();
let range_end = self.range_end.evaluate(object).parse::<i64>().unwrap();
return (value >= range_start && value <= range_end).to_string();
}

fn expr_type(&self) -> DataType {
return DataType::Boolean;
}
}
88 changes: 86 additions & 2 deletions src/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use std::collections::HashSet;
use crate::aggregation::AGGREGATIONS;
use crate::aggregation::AGGREGATIONS_PROTOS;
use crate::diagnostic::GQLError;
use crate::expression::{ArithmeticExpression, CallExpression, Expression};
use crate::expression::{ArithmeticExpression, BetweenExpression, CallExpression, Expression};
use crate::expression::{ArithmeticOperator, CheckOperator, ComparisonOperator, LogicalOperator};
use crate::expression::{BooleanExpression, NumberExpression, StringExpression, SymbolExpression};
use crate::expression::{CheckExpression, ComparisonExpression, LogicalExpression, NotExpression};
Expand Down Expand Up @@ -590,7 +590,91 @@ fn parse_expression(
tokens: &Vec<Token>,
position: &mut usize,
) -> Result<Box<dyn Expression>, GQLError> {
return parse_logical_expression(tokens, position);
return parse_between_expression(tokens, position);
}

fn parse_between_expression(
tokens: &Vec<Token>,
position: &mut usize,
) -> Result<Box<dyn Expression>, GQLError> {
let expression = parse_logical_expression(tokens, position);
if expression.is_err() {
return expression;
}

if *position < tokens.len() && tokens[*position].kind == TokenKind::Between {
let between_location = tokens[*position].location;

// Consume Between keyword
*position += 1;

let value = expression.ok().unwrap();
if value.expr_type() != DataType::Number {
return Err(GQLError {
message: format!(
"BETWEEN value must to be Number type but got {}",
value.expr_type().literal()
),
location: between_location,
});
}

if *position >= tokens.len() {
return Err(GQLError {
message: "Between keyword expects two range after it".to_owned(),
location: between_location,
});
}

let range_start_result = parse_logical_expression(tokens, position);
if range_start_result.is_err() {
return range_start_result;
}

let range_start = range_start_result.ok().unwrap();
if range_start.expr_type() != DataType::Number {
return Err(GQLError {
message: format!(
"Expect range start to be Number type but got {}",
range_start.expr_type().literal()
),
location: between_location,
});
}

if *position >= tokens.len() || tokens[*position].kind != TokenKind::DotDot {
return Err(GQLError {
message: "Expect `..` after BETWEEN range start".to_owned(),
location: between_location,
});
}

// Consume AND keyword
*position += 1;

let range_end_result = parse_logical_expression(tokens, position);
if range_end_result.is_err() {
return range_end_result;
}

let range_end = range_end_result.ok().unwrap();
if range_end.expr_type() != DataType::Number {
return Err(GQLError {
message: format!(
"Expect range end to be Number type but got {}",
range_end.expr_type().literal()
),
location: between_location,
});
}

return Ok(Box::new(BetweenExpression {
value,
range_start,
range_end,
}));
}
return expression;
}

fn parse_logical_expression(
Expand Down
23 changes: 19 additions & 4 deletions src/tokenizer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ pub enum TokenKind {
Order,
By,

Between,
DotDot,

Greater,
GreaterEqual,
Less,
Expand Down Expand Up @@ -293,21 +296,31 @@ pub fn tokenize(script: String) -> Result<Vec<Token>, GQLError> {
continue;
}

// Dot
// Dot or Range (DotDot)
if char == '.' {
let location = Location {
start: column_start,
end: position,
};

position += 1;

let mut kind = TokenKind::Dot;
let mut literal = ".";

if position < len && characters[position] == '.' {
position += 1;
kind = TokenKind::DotDot;
literal = "..";
}

let token = Token {
location: location,
kind: TokenKind::Dot,
literal: ".".to_owned(),
kind: kind,
literal: literal.to_string(),
};

tokens.push(token);
position += 1;
continue;
}

Expand Down Expand Up @@ -470,6 +483,8 @@ fn resolve_symbol_kind(literal: String) -> TokenKind {
"order" => TokenKind::Order,
"by" => TokenKind::By,

"between" => TokenKind::Between,

// Logical Operators
"or" => TokenKind::Or,
"and" => TokenKind::And,
Expand Down

0 comments on commit 7aeab10

Please sign in to comment.