Skip to content

Commit

Permalink
feat: Improve the type checker to resolve dynamic types in arguments
Browse files Browse the repository at this point in the history
  • Loading branch information
AmrDeveloper committed Aug 31, 2024
1 parent f8e1586 commit 8a18423
Show file tree
Hide file tree
Showing 6 changed files with 65 additions and 34 deletions.
15 changes: 15 additions & 0 deletions crates/gitql-core/src/dynamic_types.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
use crate::types::DataType;

/// Return a clone of the first type from the list
pub fn type_of_first_element(elements: &[DataType]) -> DataType {
elements[0].clone()
}

/// Return a clone of the first array element type of first type
pub fn array_element_type_of_first_element(elements: &[DataType]) -> DataType {
let first_element_type = &elements[0];
match first_element_type {
DataType::Array(element_type) => *element_type.clone(),
_ => panic!("First element type must be an Array"),
}
}
1 change: 1 addition & 0 deletions crates/gitql-core/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
pub mod dynamic_types;
pub mod environment;
pub mod object;
pub mod schema;
Expand Down
22 changes: 17 additions & 5 deletions crates/gitql-parser/src/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ use crate::type_checker::check_all_values_are_same_type;
use crate::type_checker::check_function_call_arguments;
use crate::type_checker::is_expression_type_equals;
use crate::type_checker::prefix_unary_expected_type;
use crate::type_checker::resolve_call_expression_return_type;
use crate::type_checker::resolve_dynamic_data_type;
use crate::type_checker::type_check_and_classify_selected_fields;
use crate::type_checker::type_check_prefix_unary;
use crate::type_checker::type_check_projection_symbols;
Expand Down Expand Up @@ -2472,8 +2472,14 @@ fn parse_function_call_expression(
function_name_location,
)?;

// Register function name with return type
let return_type = resolve_call_expression_return_type(env, signature, &arguments);
let return_type = resolve_dynamic_data_type(
env,
&signature.parameters,
&arguments,
&signature.return_type,
);

// Register function name with return type after resolving it
env.define(function_name.to_string(), return_type.clone());

return Ok(Box::new(CallExpression {
Expand Down Expand Up @@ -2512,8 +2518,14 @@ fn parse_function_call_expression(
let column_name = context.generate_column_name();
context.hidden_selections.push(column_name.to_string());

// Register aggregation generated name with return type
let return_type = resolve_call_expression_return_type(env, signature, &arguments);
let return_type = resolve_dynamic_data_type(
env,
&signature.parameters,
&arguments,
&signature.return_type,
);

// Register aggregation generated name with return type after resolving it
env.define(column_name.to_string(), return_type);

context.aggregations.insert(
Expand Down
42 changes: 22 additions & 20 deletions crates/gitql-parser/src/type_checker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ use gitql_ast::expression::StringValueType;
use gitql_ast::operator::PrefixUnaryOperator;
use gitql_ast::statement::TableSelection;
use gitql_core::environment::Environment;
use gitql_core::signature::Signature;
use gitql_core::types::DataType;

use crate::diagnostic::Diagnostic;
Expand Down Expand Up @@ -272,7 +271,8 @@ pub fn check_function_call_arguments(

// Type check the min required arguments
for index in 0..min_arguments_count {
let parameter_type = parameters.get(index).unwrap();
let parameter_type =
resolve_dynamic_data_type(env, parameters, arguments, parameters.get(index).unwrap());
let argument = arguments.get(index).unwrap();

// Catch undefined arguments
Expand All @@ -287,7 +287,7 @@ pub fn check_function_call_arguments(
.as_boxed());
}

match is_expression_type_equals(env, argument, parameter_type) {
match is_expression_type_equals(env, argument, &parameter_type) {
ExprTypeCheckResult::ImplicitCasted(new_expr) => {
arguments[index] = new_expr;
}
Expand All @@ -311,7 +311,8 @@ pub fn check_function_call_arguments(
return Ok(());
}

let parameter_type = parameters.get(index).unwrap();
let parameter_type =
resolve_dynamic_data_type(env, parameters, arguments, parameters.get(index).unwrap());
let argument = arguments.get(index).unwrap();

// Catch undefined arguments
Expand All @@ -326,7 +327,7 @@ pub fn check_function_call_arguments(
.as_boxed());
}

match is_expression_type_equals(env, argument, parameter_type) {
match is_expression_type_equals(env, argument, &parameter_type) {
ExprTypeCheckResult::ImplicitCasted(new_expr) => {
arguments[index] = new_expr;
}
Expand All @@ -345,7 +346,8 @@ pub fn check_function_call_arguments(

// Type check the variable parameters if exists
if has_varargs_parameter {
let varargs_type = parameters.last().unwrap();
let varargs_type =
resolve_dynamic_data_type(env, parameters, arguments, parameters.last().unwrap());
for index in last_optional_param_index..arguments_count {
let argument = arguments.get(index).unwrap();

Expand All @@ -361,7 +363,7 @@ pub fn check_function_call_arguments(
.as_boxed());
}

match is_expression_type_equals(env, argument, varargs_type) {
match is_expression_type_equals(env, argument, &varargs_type) {
ExprTypeCheckResult::ImplicitCasted(new_expr) => {
arguments[index] = new_expr;
}
Expand Down Expand Up @@ -533,29 +535,29 @@ pub fn prefix_unary_expected_type(op: &PrefixUnaryOperator) -> DataType {
}
}

/// Resolve the return type of Std or Aggregation function and re resolve it if it variant or dynamic
pub fn resolve_call_expression_return_type(
/// Resolve dynamic data type depending on the parameters and arguments types
pub fn resolve_dynamic_data_type(
env: &Environment,
signature: &Signature,
arguments: &Vec<Box<dyn Expression>>,
parameters: &[DataType],
arguments: &[Box<dyn Expression>],
data_type: &DataType,
) -> DataType {
let mut return_type = signature.return_type.clone();
let mut resolved_data_type = data_type.clone();
if let DataType::Dynamic(calculate_type) = resolved_data_type {
resolved_data_type = calculate_type(parameters);

if let DataType::Dynamic(calculate_type) = return_type {
return_type = calculate_type(&signature.parameters);

// In Case that return type is variant for example [Int | Float] need to resolve it from arguments types
// In Case that data type is Any or Variant [Type1 | Type2...] need to resolve it from arguments types
// To be able to use it with other expressions
if !arguments.is_empty() && return_type.is_variant() {
if !arguments.is_empty() && (resolved_data_type.is_variant() || resolved_data_type.is_any())
{
let mut arguments_types = Vec::with_capacity(arguments.len());
for argument in arguments {
arguments_types.push(argument.expr_type(env));
}
return_type = calculate_type(&arguments_types);
resolved_data_type = calculate_type(&arguments_types);
}
}

return_type
resolved_data_type
}

/// Return a [Diagnostic] with common type mismatch error message
Expand Down
6 changes: 3 additions & 3 deletions crates/gitql-std/src/aggregation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ use std::cmp::Ordering;
use std::collections::HashMap;
use std::sync::OnceLock;

use gitql_core::dynamic_types::type_of_first_element;
use gitql_core::signature::Aggregation;
use gitql_core::signature::Signature;
use gitql_core::types::same_type_as_first_parameter;
use gitql_core::types::DataType;
use gitql_core::value::Value;

Expand Down Expand Up @@ -41,7 +41,7 @@ pub fn aggregation_function_signatures() -> &'static HashMap<&'static str, Signa
DataType::Time,
DataType::DateTime,
])],
return_type: DataType::Dynamic(same_type_as_first_parameter),
return_type: DataType::Dynamic(type_of_first_element),
},
);
map.insert(
Expand All @@ -55,7 +55,7 @@ pub fn aggregation_function_signatures() -> &'static HashMap<&'static str, Signa
DataType::Time,
DataType::DateTime,
])],
return_type: DataType::Dynamic(same_type_as_first_parameter),
return_type: DataType::Dynamic(type_of_first_element),
},
);
map.insert(
Expand Down
13 changes: 7 additions & 6 deletions crates/gitql-std/src/function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@ use crate::number::*;
use crate::regex::*;
use crate::text::*;

use gitql_core::dynamic_types::array_element_type_of_first_element;
use gitql_core::dynamic_types::type_of_first_element;
use gitql_core::signature::Function;
use gitql_core::signature::Signature;
use gitql_core::types::same_type_as_first_parameter;
use gitql_core::types::DataType;
use std::collections::HashMap;
use std::sync::OnceLock;
Expand Down Expand Up @@ -507,7 +508,7 @@ pub fn standard_function_signatures() -> &'static HashMap<&'static str, Signatur
"abs",
Signature {
parameters: vec![DataType::Variant(vec![DataType::Integer, DataType::Float])],
return_type: DataType::Dynamic(same_type_as_first_parameter),
return_type: DataType::Dynamic(type_of_first_element),
},
);
map.insert(
Expand Down Expand Up @@ -705,7 +706,7 @@ pub fn standard_function_signatures() -> &'static HashMap<&'static str, Signatur
"array_shuffle",
Signature {
parameters: vec![DataType::Array(Box::new(DataType::Any))],
return_type: DataType::Dynamic(same_type_as_first_parameter),
return_type: DataType::Dynamic(type_of_first_element),
},
);
map.insert(
Expand All @@ -727,10 +728,10 @@ pub fn standard_function_signatures() -> &'static HashMap<&'static str, Signatur
Signature {
parameters: vec![
DataType::Array(Box::new(DataType::Any)),
DataType::Any,
DataType::Any,
DataType::Dynamic(array_element_type_of_first_element),
DataType::Dynamic(array_element_type_of_first_element),
],
return_type: DataType::Dynamic(same_type_as_first_parameter),
return_type: DataType::Dynamic(type_of_first_element),
},
);
map
Expand Down

0 comments on commit 8a18423

Please sign in to comment.