use gitql_ast::expression::ArithmeticExpr;
use gitql_ast::expression::ArrayExpr;
use gitql_ast::expression::AssignmentExpr;
use gitql_ast::expression::BenchmarkCallExpr;
use gitql_ast::expression::BetweenExpr;
use gitql_ast::expression::BetweenKind;
use gitql_ast::expression::BitwiseExpr;
use gitql_ast::expression::BooleanExpr;
use gitql_ast::expression::CallExpr;
use gitql_ast::expression::CaseExpr;
use gitql_ast::expression::CastExpr;
use gitql_ast::expression::ComparisonExpr;
use gitql_ast::expression::ContainedByExpr;
use gitql_ast::expression::ContainsExpr;
use gitql_ast::expression::Expr;
use gitql_ast::expression::ExprKind::*;
use gitql_ast::expression::GlobExpr;
use gitql_ast::expression::GlobalVariableExpr;
use gitql_ast::expression::GroupExpr;
use gitql_ast::expression::InExpr;
use gitql_ast::expression::IndexExpr;
use gitql_ast::expression::IsNullExpr;
use gitql_ast::expression::LikeExpr;
use gitql_ast::expression::LogicalExpr;
use gitql_ast::expression::MemberAccessExpr;
use gitql_ast::expression::Number;
use gitql_ast::expression::NumberExpr;
use gitql_ast::expression::RegexExpr;
use gitql_ast::expression::SliceExpr;
use gitql_ast::expression::StringExpr;
use gitql_ast::expression::SymbolExpr;
use gitql_ast::expression::UnaryExpr;
use gitql_ast::operator::ArithmeticOperator;
use gitql_ast::operator::BinaryBitwiseOperator;
use gitql_ast::operator::BinaryLogicalOperator;
use gitql_ast::operator::ComparisonOperator;
use gitql_ast::operator::PrefixUnaryOperator;
use gitql_core::environment::Environment;
use gitql_core::values::array::ArrayValue;
use gitql_core::values::base::Value;
use gitql_core::values::boolean::BoolValue;
use gitql_core::values::composite::CompositeValue;
use gitql_core::values::float::FloatValue;
use gitql_core::values::integer::IntValue;
use gitql_core::values::null::NullValue;
use gitql_core::values::text::TextValue;
use regex::Regex;
use regex::RegexBuilder;
use std::cmp::Ordering;
use std::string::String;
#[allow(clippy::borrowed_box)]
pub fn evaluate_expression(
env: &mut Environment,
expression: &Box<dyn Expr>,
titles: &[String],
object: &Vec<Box<dyn Value>>,
) -> Result<Box<dyn Value>, String> {
match expression.kind() {
Assignment => {
let expr = expression
.as_any()
.downcast_ref::<AssignmentExpr>()
.unwrap();
evaluate_assignment(env, expr, titles, object)
}
String => {
let expr = expression.as_any().downcast_ref::<StringExpr>().unwrap();
evaluate_string(expr)
}
Symbol => {
let expr = expression.as_any().downcast_ref::<SymbolExpr>().unwrap();
evaluate_symbol(expr, titles, object)
}
Array => {
let expr = expression.as_any().downcast_ref::<ArrayExpr>().unwrap();
evaluate_array(env, expr, titles, object)
}
GlobalVariable => {
let expr = expression
.as_any()
.downcast_ref::<GlobalVariableExpr>()
.unwrap();
evaluate_global_variable(env, expr)
}
Number => {
let expr = expression.as_any().downcast_ref::<NumberExpr>().unwrap();
evaluate_number(expr)
}
Boolean => {
let expr = expression.as_any().downcast_ref::<BooleanExpr>().unwrap();
evaluate_boolean(expr)
}
PrefixUnary => {
let expr = expression.as_any().downcast_ref::<UnaryExpr>().unwrap();
evaluate_prefix_unary(env, expr, titles, object)
}
Index => {
let expr = expression.as_any().downcast_ref::<IndexExpr>().unwrap();
evaluate_collection_index(env, expr, titles, object)
}
Slice => {
let expr = expression.as_any().downcast_ref::<SliceExpr>().unwrap();
evaluate_collection_slice(env, expr, titles, object)
}
Arithmetic => {
let expr = expression
.as_any()
.downcast_ref::<ArithmeticExpr>()
.unwrap();
evaluate_arithmetic(env, expr, titles, object)
}
Comparison => {
let expr = expression
.as_any()
.downcast_ref::<ComparisonExpr>()
.unwrap();
evaluate_comparison(env, expr, titles, object)
}
Contains => {
let expr = expression.as_any().downcast_ref::<ContainsExpr>().unwrap();
evaluate_contains(env, expr, titles, object)
}
ContainedBy => {
let expr = expression
.as_any()
.downcast_ref::<ContainedByExpr>()
.unwrap();
evaluate_contained_by(env, expr, titles, object)
}
Like => {
let expr = expression.as_any().downcast_ref::<LikeExpr>().unwrap();
evaluate_like(env, expr, titles, object)
}
Regex => {
let expr = expression.as_any().downcast_ref::<RegexExpr>().unwrap();
evaluate_regex(env, expr, titles, object)
}
Glob => {
let expr = expression.as_any().downcast_ref::<GlobExpr>().unwrap();
evaluate_glob(env, expr, titles, object)
}
Logical => {
let expr = expression.as_any().downcast_ref::<LogicalExpr>().unwrap();
evaluate_logical(env, expr, titles, object)
}
Bitwise => {
let expr = expression.as_any().downcast_ref::<BitwiseExpr>().unwrap();
evaluate_bitwise(env, expr, titles, object)
}
Call => {
let expr = expression.as_any().downcast_ref::<CallExpr>().unwrap();
evaluate_call(env, expr, titles, object)
}
BenchmarkCall => {
let expr = expression
.as_any()
.downcast_ref::<BenchmarkCallExpr>()
.unwrap();
evaluate_benchmark_call(env, expr, titles, object)
}
Between => {
let expr = expression.as_any().downcast_ref::<BetweenExpr>().unwrap();
evaluate_between(env, expr, titles, object)
}
Case => {
let expr = expression.as_any().downcast_ref::<CaseExpr>().unwrap();
evaluate_case(env, expr, titles, object)
}
In => {
let expr = expression.as_any().downcast_ref::<InExpr>().unwrap();
evaluate_in(env, expr, titles, object)
}
IsNull => {
let expr = expression.as_any().downcast_ref::<IsNullExpr>().unwrap();
evaluate_is_null(env, expr, titles, object)
}
Cast => {
let expr = expression.as_any().downcast_ref::<CastExpr>().unwrap();
evaluate_cast(env, expr, titles, object)
}
Grouping => {
let expr = expression.as_any().downcast_ref::<GroupExpr>().unwrap();
evaluate_grouping(env, expr, titles, object)
}
MemberAccess => {
let expr = expression
.as_any()
.downcast_ref::<MemberAccessExpr>()
.unwrap();
evaluate_member_access(env, expr, titles, object)
}
Null => Ok(Box::new(NullValue)),
}
}
fn evaluate_assignment(
env: &mut Environment,
expr: &AssignmentExpr,
titles: &[String],
object: &Vec<Box<dyn Value>>,
) -> Result<Box<dyn Value>, String> {
let value = evaluate_expression(env, &expr.value, titles, object)?;
env.globals.insert(expr.symbol.to_string(), value.clone());
Ok(value)
}
fn evaluate_string(expr: &StringExpr) -> Result<Box<dyn Value>, String> {
Ok(Box::new(TextValue {
value: expr.value.to_owned(),
}))
}
fn evaluate_symbol(
expr: &SymbolExpr,
titles: &[String],
object: &[Box<dyn Value>],
) -> Result<Box<dyn Value>, String> {
for (index, title) in titles.iter().enumerate() {
if expr.value.eq(title) {
return Ok(object[index].clone());
}
}
Err(format!("Invalid column name `{}`", &expr.value))
}
fn evaluate_array(
env: &mut Environment,
expr: &ArrayExpr,
titles: &[String],
object: &Vec<Box<dyn Value>>,
) -> Result<Box<dyn Value>, String> {
let mut values: Vec<Box<dyn Value>> = Vec::with_capacity(expr.values.len());
for value in &expr.values {
values.push(evaluate_expression(env, value, titles, object)?);
}
Ok(Box::new(ArrayValue {
values,
base_type: expr.element_type.clone(),
}))
}
fn evaluate_global_variable(
env: &mut Environment,
expr: &GlobalVariableExpr,
) -> Result<Box<dyn Value>, String> {
let name = &expr.name;
if env.globals.contains_key(name) {
return Ok(env.globals[name].clone());
}
Err(format!(
"The value of `{}` may be not exists or calculated yet",
name
))
}
fn evaluate_number(expr: &NumberExpr) -> Result<Box<dyn Value>, String> {
Ok(match expr.value {
Number::Int(integer) => Box::new(IntValue { value: integer }),
Number::Float(float) => Box::new(FloatValue { value: float }),
})
}
fn evaluate_boolean(expr: &BooleanExpr) -> Result<Box<dyn Value>, String> {
let value = expr.is_true;
Ok(Box::new(BoolValue { value }))
}
fn evaluate_collection_index(
env: &mut Environment,
expr: &IndexExpr,
titles: &[String],
object: &Vec<Box<dyn Value>>,
) -> Result<Box<dyn Value>, String> {
let array = evaluate_expression(env, &expr.collection, titles, object)?;
let index = evaluate_expression(env, &expr.index, titles, object)?;
array.index_op(&index)
}
fn evaluate_collection_slice(
env: &mut Environment,
expr: &SliceExpr,
titles: &[String],
object: &Vec<Box<dyn Value>>,
) -> Result<Box<dyn Value>, String> {
let array = evaluate_expression(env, &expr.collection, titles, object)?;
let start = if let Some(start_expr) = &expr.start {
Some(evaluate_expression(env, start_expr, titles, object)?)
} else {
None
};
let end = if let Some(end_expr) = &expr.end {
Some(evaluate_expression(env, end_expr, titles, object)?)
} else {
None
};
array.slice_op(&start, &end)
}
fn evaluate_prefix_unary(
env: &mut Environment,
expr: &UnaryExpr,
titles: &[String],
object: &Vec<Box<dyn Value>>,
) -> Result<Box<dyn Value>, String> {
let rhs = evaluate_expression(env, &expr.right, titles, object)?;
match expr.operator {
PrefixUnaryOperator::Negative => rhs.neg_op(),
PrefixUnaryOperator::Bang => rhs.bang_op(),
PrefixUnaryOperator::Not => rhs.not_op(),
}
}
fn evaluate_arithmetic(
env: &mut Environment,
expr: &ArithmeticExpr,
titles: &[String],
object: &Vec<Box<dyn Value>>,
) -> Result<Box<dyn Value>, String> {
let lhs = evaluate_expression(env, &expr.left, titles, object)?;
let rhs = evaluate_expression(env, &expr.right, titles, object)?;
match expr.operator {
ArithmeticOperator::Plus => lhs.add_op(&rhs),
ArithmeticOperator::Minus => lhs.sub_op(&rhs),
ArithmeticOperator::Star => lhs.mul_op(&rhs),
ArithmeticOperator::Slash => lhs.div_op(&rhs),
ArithmeticOperator::Modulus => lhs.rem_op(&rhs),
ArithmeticOperator::Exponentiation => lhs.caret_op(&rhs),
}
}
fn evaluate_comparison(
env: &mut Environment,
expr: &ComparisonExpr,
titles: &[String],
object: &Vec<Box<dyn Value>>,
) -> Result<Box<dyn Value>, String> {
let lhs = evaluate_expression(env, &expr.left, titles, object)?;
let rhs = evaluate_expression(env, &expr.right, titles, object)?;
match expr.operator {
ComparisonOperator::Greater => lhs.gt_op(&rhs),
ComparisonOperator::GreaterEqual => lhs.gte_op(&rhs),
ComparisonOperator::Less => lhs.lt_op(&rhs),
ComparisonOperator::LessEqual => lhs.lte_op(&rhs),
ComparisonOperator::Equal => lhs.eq_op(&rhs),
ComparisonOperator::NotEqual => lhs.bang_eq_op(&rhs),
ComparisonOperator::NullSafeEqual => lhs.null_safe_eq_op(&rhs),
}
}
fn evaluate_contains(
env: &mut Environment,
expr: &ContainsExpr,
titles: &[String],
object: &Vec<Box<dyn Value>>,
) -> Result<Box<dyn Value>, String> {
let lhs = evaluate_expression(env, &expr.left, titles, object)?;
let rhs = evaluate_expression(env, &expr.right, titles, object)?;
lhs.contains_op(&rhs)
}
fn evaluate_contained_by(
env: &mut Environment,
expr: &ContainedByExpr,
titles: &[String],
object: &Vec<Box<dyn Value>>,
) -> Result<Box<dyn Value>, String> {
let lhs = evaluate_expression(env, &expr.left, titles, object)?;
let rhs = evaluate_expression(env, &expr.right, titles, object)?;
rhs.contains_op(&lhs)
}
fn evaluate_like(
env: &mut Environment,
expr: &LikeExpr,
titles: &[String],
object: &Vec<Box<dyn Value>>,
) -> Result<Box<dyn Value>, String> {
let pattern = evaluate_expression(env, &expr.pattern, titles, object)?;
let input = evaluate_expression(env, &expr.input, titles, object)?;
if let Some(pattern_text) = pattern.as_any().downcast_ref::<TextValue>() {
if let Some(input_text) = input.as_any().downcast_ref::<TextValue>() {
let pattern = &format!(
"^{}$",
pattern_text
.value
.to_lowercase()
.replace('%', ".*")
.replace('_', ".")
);
let regex_result = RegexBuilder::new(pattern)
.multi_line(true)
.unicode(true)
.build();
if regex_result.is_err() {
return Err(regex_result.err().unwrap().to_string());
}
let regex = regex_result.ok().unwrap();
let is_match = regex.is_match(&input_text.value.to_lowercase());
return Ok(Box::new(BoolValue { value: is_match }));
}
}
Err("Invalid Arguments for LIKE expression".to_string())
}
fn evaluate_regex(
env: &mut Environment,
expr: &RegexExpr,
titles: &[String],
object: &Vec<Box<dyn Value>>,
) -> Result<Box<dyn Value>, String> {
let pattern = evaluate_expression(env, &expr.pattern, titles, object)?;
let input = evaluate_expression(env, &expr.input, titles, object)?;
if let Some(pattern_text) = pattern.as_any().downcast_ref::<TextValue>() {
if let Some(input_text) = input.as_any().downcast_ref::<TextValue>() {
let pattern = &format!(
"^{}$",
pattern_text
.value
.to_lowercase()
.replace('%', ".*")
.replace('_', ".")
);
let regex_result = RegexBuilder::new(pattern)
.multi_line(true)
.unicode(true)
.build();
if regex_result.is_err() {
return Err(regex_result.err().unwrap().to_string());
}
let regex = regex_result.ok().unwrap();
let is_match = regex.is_match(&input_text.value.to_lowercase());
return Ok(Box::new(BoolValue { value: is_match }));
}
}
Err("Invalid Arguments for REGEX expression".to_string())
}
fn evaluate_glob(
env: &mut Environment,
expr: &GlobExpr,
titles: &[String],
object: &Vec<Box<dyn Value>>,
) -> Result<Box<dyn Value>, String> {
let rhs = evaluate_expression(env, &expr.pattern, titles, object)?;
if let Some(rhs_text) = rhs.as_any().downcast_ref::<TextValue>() {
let text = rhs_text.literal();
let pattern = &format!(
"^{}$",
text.replace('.', "\\.")
.replace('*', ".*")
.replace('?', ".")
);
let regex_result = Regex::new(pattern);
if regex_result.is_err() {
return Err(regex_result.err().unwrap().to_string());
}
let regex = regex_result.ok().unwrap();
let lhs = evaluate_expression(env, &expr.input, titles, object)?;
if let Some(lhs_text) = lhs.as_any().downcast_ref::<TextValue>() {
let is_match = regex.is_match(&lhs_text.value);
return Ok(Box::new(BoolValue { value: is_match }));
}
}
Err("Invalid Arguments for GLOB expression".to_string())
}
fn evaluate_logical(
env: &mut Environment,
expr: &LogicalExpr,
titles: &[String],
object: &Vec<Box<dyn Value>>,
) -> Result<Box<dyn Value>, String> {
let lhs = evaluate_expression(env, &expr.left, titles, object)?;
let rhs = evaluate_expression(env, &expr.right, titles, object)?;
match expr.operator {
BinaryLogicalOperator::And => lhs.logical_and_op(&rhs),
BinaryLogicalOperator::Or => lhs.logical_or_op(&rhs),
BinaryLogicalOperator::Xor => lhs.logical_xor_op(&rhs),
}
}
fn evaluate_bitwise(
env: &mut Environment,
expr: &BitwiseExpr,
titles: &[String],
object: &Vec<Box<dyn Value>>,
) -> Result<Box<dyn Value>, String> {
let lhs = evaluate_expression(env, &expr.left, titles, object)?;
let rhs = evaluate_expression(env, &expr.right, titles, object)?;
match expr.operator {
BinaryBitwiseOperator::Or => lhs.or_op(&rhs),
BinaryBitwiseOperator::And => lhs.and_op(&rhs),
BinaryBitwiseOperator::Xor => lhs.xor_op(&rhs),
BinaryBitwiseOperator::RightShift => lhs.shr_op(&rhs),
BinaryBitwiseOperator::LeftShift => lhs.shl_op(&rhs),
}
}
fn evaluate_call(
env: &mut Environment,
expr: &CallExpr,
titles: &[String],
object: &Vec<Box<dyn Value>>,
) -> Result<Box<dyn Value>, String> {
let function_name = expr.function_name.as_str();
let mut arguments = Vec::with_capacity(expr.arguments.len());
for arg in expr.arguments.iter() {
arguments.push(evaluate_expression(env, arg, titles, object)?);
}
let function = env.std_function(function_name).unwrap();
Ok(function(&arguments))
}
fn evaluate_benchmark_call(
env: &mut Environment,
expr: &BenchmarkCallExpr,
titles: &[String],
object: &Vec<Box<dyn Value>>,
) -> Result<Box<dyn Value>, String> {
let number_of_execution = evaluate_expression(env, &expr.count, titles, object)?;
if let Some(number) = number_of_execution.as_any().downcast_ref::<IntValue>() {
for _ in 0..number.value {
evaluate_expression(env, &expr.expression, titles, object)?;
}
}
Ok(Box::new(IntValue { value: 0 }))
}
fn evaluate_between(
env: &mut Environment,
expr: &BetweenExpr,
titles: &[String],
object: &Vec<Box<dyn Value>>,
) -> Result<Box<dyn Value>, String> {
let value = evaluate_expression(env, &expr.value, titles, object)?;
let range_start = evaluate_expression(env, &expr.range_start, titles, object)?;
let range_end = evaluate_expression(env, &expr.range_end, titles, object)?;
let comparing_result = match expr.kind {
BetweenKind::Symmetric => {
let (start, end) = if let Some(order) = range_start.compare(&range_end) {
if Ordering::is_gt(order) {
(range_end, range_start)
} else {
(range_start, range_end)
}
} else {
(range_start, range_end)
};
value.compare(&start).unwrap().is_ge() && value.compare(&end).unwrap().is_le()
}
BetweenKind::Asymmetric => {
value.compare(&range_start).unwrap().is_ge()
&& value.compare(&range_end).unwrap().is_le()
}
};
Ok(Box::new(BoolValue {
value: comparing_result,
}))
}
fn evaluate_case(
env: &mut Environment,
expr: &CaseExpr,
titles: &[String],
object: &Vec<Box<dyn Value>>,
) -> Result<Box<dyn Value>, String> {
let conditions = &expr.conditions;
let values = &expr.values;
for i in 0..conditions.len() {
let condition = evaluate_expression(env, &conditions[i], titles, object)?;
if let Some(bool_value) = condition.as_any().downcast_ref::<BoolValue>() {
if bool_value.value {
return evaluate_expression(env, &values[i], titles, object);
}
}
}
match &expr.default_value {
Some(default_value) => evaluate_expression(env, default_value, titles, object),
_ => Err("Invalid case statement".to_owned()),
}
}
fn evaluate_in(
env: &mut Environment,
expr: &InExpr,
titles: &[String],
object: &Vec<Box<dyn Value>>,
) -> Result<Box<dyn Value>, String> {
let argument = evaluate_expression(env, &expr.argument, titles, object)?;
for value_expr in &expr.values {
let value = evaluate_expression(env, value_expr, titles, object)?;
if argument.equals(&value) {
return Ok(Box::new(BoolValue {
value: !expr.has_not_keyword,
}));
}
}
Ok(Box::new(BoolValue {
value: expr.has_not_keyword,
}))
}
fn evaluate_is_null(
env: &mut Environment,
expr: &IsNullExpr,
titles: &[String],
object: &Vec<Box<dyn Value>>,
) -> Result<Box<dyn Value>, String> {
let argument = evaluate_expression(env, &expr.argument, titles, object)?;
let is_null = argument.as_any().downcast_ref::<NullValue>().is_some();
Ok(Box::new(BoolValue {
value: if expr.has_not { !is_null } else { is_null },
}))
}
fn evaluate_cast(
env: &mut Environment,
expr: &CastExpr,
titles: &[String],
object: &Vec<Box<dyn Value>>,
) -> Result<Box<dyn Value>, String> {
let value = evaluate_expression(env, &expr.value, titles, object)?;
value.cast_op(&expr.result_type)
}
fn evaluate_grouping(
env: &mut Environment,
expr: &GroupExpr,
titles: &[String],
object: &Vec<Box<dyn Value>>,
) -> Result<Box<dyn Value>, String> {
let value = evaluate_expression(env, &expr.expr, titles, object)?;
Ok(value)
}
fn evaluate_member_access(
env: &mut Environment,
expr: &MemberAccessExpr,
titles: &[String],
object: &Vec<Box<dyn Value>>,
) -> Result<Box<dyn Value>, String> {
let value = evaluate_expression(env, &expr.composite, titles, object)?;
if let Some(composite_value) = value.as_any().downcast_ref::<CompositeValue>() {
let member_name = &expr.member_name;
return Ok(composite_value.members.get(member_name).unwrap().clone());
}
Err("Invalid value for Member access expression".to_owned())
}