gitql_parser/
type_checker.rs

1use std::collections::HashMap;
2
3use gitql_ast::expression::CastExpr;
4use gitql_ast::expression::Expr;
5use gitql_ast::statement::TableSelection;
6use gitql_ast::types::any::AnyType;
7use gitql_ast::types::dynamic::DynamicType;
8use gitql_ast::types::varargs::VarargsType;
9use gitql_ast::types::DataType;
10use gitql_core::environment::Environment;
11
12use crate::diagnostic::Diagnostic;
13use crate::token::SourceLocation;
14
15/// Checks if all values has the same type
16/// If they have the same type, return it or return None
17pub fn check_all_values_are_same_type(arguments: &[Box<dyn Expr>]) -> Option<Box<dyn DataType>> {
18    let arguments_count = arguments.len();
19    if arguments_count == 0 {
20        return Some(Box::new(AnyType));
21    }
22
23    let data_type = arguments[0].expr_type();
24    for argument in arguments.iter().take(arguments_count).skip(1) {
25        let expr_type = argument.expr_type();
26        if !data_type.equals(&expr_type) {
27            return None;
28        }
29    }
30
31    Some(data_type)
32}
33
34/// Check That function call arguments types are matches the parameter types
35/// Return a Diagnostic Error if anything is wrong
36pub fn check_function_call_arguments(
37    arguments: &mut [Box<dyn Expr>],
38    parameters: &[Box<dyn DataType>],
39    function_name: String,
40    location: SourceLocation,
41) -> Result<(), Box<Diagnostic>> {
42    let parameters_count = parameters.len();
43    let arguments_count = arguments.len();
44
45    let mut has_varargs_parameter = false;
46    let mut optional_parameters_count = 0;
47    if parameters_count != 0 {
48        let last_parameter = parameters.last().unwrap();
49        has_varargs_parameter = last_parameter.is_varargs();
50
51        // Count number of optional parameters
52        for parameter_type in parameters.iter().take(parameters_count) {
53            if parameter_type.is_optional() {
54                optional_parameters_count += 1;
55            }
56        }
57    }
58
59    let mut min_arguments_count = parameters_count - optional_parameters_count;
60    if has_varargs_parameter {
61        min_arguments_count -= 1;
62    }
63
64    if arguments_count < min_arguments_count {
65        return Err(Diagnostic::error(&format!(
66            "Function `{}` expects at least `{}` arguments but got `{}`",
67            function_name, min_arguments_count, arguments_count
68        ))
69        .with_location(location)
70        .as_boxed());
71    }
72
73    if !has_varargs_parameter && arguments_count > parameters_count {
74        return Err(Diagnostic::error(&format!(
75            "Function `{}` expects `{}` arguments but got `{}`",
76            function_name, parameters_count, arguments_count
77        ))
78        .with_location(location)
79        .as_boxed());
80    }
81
82    // Type check the min required arguments
83    for index in 0..min_arguments_count {
84        let parameter_type =
85            resolve_dynamic_data_type(parameters, arguments, parameters.get(index).unwrap());
86        let argument = arguments.get(index).unwrap();
87        let argument_type = argument.expr_type();
88
89        // Catch undefined arguments
90        if argument_type.is_undefined() {
91            return Err(Diagnostic::error(&format!(
92                "Function `{}` argument number {} has Undefined type",
93                function_name, index,
94            ))
95            .add_help("Make sure you used a correct field name")
96            .add_help("Check column names for each table from docs website")
97            .with_location(location)
98            .as_boxed());
99        }
100
101        // Both types are equals
102        if parameter_type.equals(&argument_type) {
103            continue;
104        }
105
106        // Argument exp can be implicit casted to Parameter type
107        if parameter_type.has_implicit_cast_from(argument) {
108            arguments[index] = Box::new(CastExpr {
109                value: argument.clone(),
110                result_type: parameter_type.clone(),
111            });
112            continue;
113        }
114
115        // Argument type is not equal and can't be casted to parameter type
116        return Err(Diagnostic::error(&format!(
117            "Function `{}` argument number {} with type `{}` don't match expected type `{}`",
118            function_name,
119            index,
120            argument_type.literal(),
121            parameter_type.literal()
122        ))
123        .with_location(location)
124        .as_boxed());
125    }
126
127    // Type check the optional parameters
128    let last_optional_param_index = min_arguments_count + optional_parameters_count;
129    for index in min_arguments_count..last_optional_param_index {
130        if index >= arguments_count {
131            return Ok(());
132        }
133
134        let parameter_type =
135            resolve_dynamic_data_type(parameters, arguments, parameters.get(index).unwrap());
136        let argument = arguments.get(index).unwrap();
137        let argument_type = argument.expr_type();
138
139        // Catch undefined arguments
140        if argument_type.is_undefined() {
141            return Err(Diagnostic::error(&format!(
142                "Function `{}` argument number {} has Undefined type",
143                function_name, index,
144            ))
145            .add_help("Make sure you used a correct field name")
146            .add_help("Check column names for each table from docs website")
147            .with_location(location)
148            .as_boxed());
149        }
150
151        // Both types are equals
152        if parameter_type.equals(&argument_type) {
153            continue;
154        }
155
156        // Argument exp can be implicit casted to Parameter type
157        if parameter_type.has_implicit_cast_from(argument) {
158            arguments[index] = Box::new(CastExpr {
159                value: argument.clone(),
160                result_type: parameter_type.clone(),
161            });
162            continue;
163        }
164
165        // Argument type is not equal and can't be casted to parameter type
166        return Err(Diagnostic::error(&format!(
167            "Function `{}` argument number {} with type `{}` don't match expected type `{}`",
168            function_name,
169            index,
170            argument_type.literal(),
171            parameter_type.literal()
172        ))
173        .with_location(location)
174        .as_boxed());
175    }
176
177    // Type check the variable parameters if exists
178    if has_varargs_parameter {
179        let varargs_type =
180            resolve_dynamic_data_type(parameters, arguments, parameters.last().unwrap());
181        for index in last_optional_param_index..arguments_count {
182            let argument = arguments.get(index).unwrap();
183            let argument_type = argument.expr_type();
184
185            // Catch undefined arguments
186            if argument_type.is_undefined() {
187                return Err(Diagnostic::error(&format!(
188                    "Function `{}` argument number {} has Undefined type",
189                    function_name, index,
190                ))
191                .add_help("Make sure you used a correct field name")
192                .add_help("Check column names for each table from docs website")
193                .with_location(location)
194                .as_boxed());
195            }
196
197            // Both types are equals
198            if varargs_type.equals(&argument_type) {
199                continue;
200            }
201
202            // Argument exp can be implicit casted to Parameter type
203            if varargs_type.has_implicit_cast_from(argument) {
204                arguments[index] = Box::new(CastExpr {
205                    value: argument.clone(),
206                    result_type: varargs_type.clone(),
207                });
208                continue;
209            }
210
211            return Err(Diagnostic::error(&format!(
212                "Function `{}` argument number {} with type `{}` don't match expected type `{}`",
213                function_name,
214                index,
215                &argument_type.literal(),
216                &varargs_type.literal()
217            ))
218            .with_location(location)
219            .as_boxed());
220        }
221    }
222
223    Ok(())
224}
225
226/// Check that all selected fields types are defined correctly in selected tables
227/// Return the columns classified for each table
228/// Return a Diagnostic Error if anything is wrong
229pub fn type_check_and_classify_selected_fields(
230    env: &mut Environment,
231    selected_tables: &Vec<String>,
232    selected_columns: &Vec<String>,
233    location: SourceLocation,
234) -> Result<Vec<TableSelection>, Box<Diagnostic>> {
235    let mut table_selections: Vec<TableSelection> = vec![];
236    let mut table_index: HashMap<String, usize> = HashMap::new();
237    for (index, table) in selected_tables.iter().enumerate() {
238        table_selections.push(TableSelection {
239            table_name: table.to_string(),
240            columns_names: vec![],
241        });
242        table_index.insert(table.to_string(), index);
243    }
244
245    for selected_column in selected_columns {
246        let mut is_column_resolved = false;
247        for table in selected_tables {
248            let table_columns = env.schema.tables_fields_names.get(table.as_str()).unwrap();
249
250            // Check if this column name exists in current table
251            if table_columns.contains(&selected_column.as_str()) {
252                is_column_resolved = true;
253                let table_selection_index = *table_index.get(table).unwrap();
254                let selection = &mut table_selections[table_selection_index];
255                selection.columns_names.push(selected_column.to_string());
256                continue;
257            }
258        }
259
260        if !is_column_resolved {
261            // This case for aggregated values or functions
262            if let Some(data_type) = env.resolve_type(selected_column) {
263                if !data_type.is_undefined() {
264                    if table_selections.is_empty() {
265                        table_selections.push(TableSelection {
266                            table_name: selected_tables
267                                .first()
268                                .unwrap_or(&"".to_string())
269                                .to_string(),
270                            columns_names: vec![selected_column.to_string()],
271                        });
272                    } else {
273                        table_selections[0]
274                            .columns_names
275                            .push(selected_column.to_string());
276                    }
277                    continue;
278                }
279            }
280
281            return Err(Diagnostic::error(&format!(
282                "Column `{}` not exists in any of the selected tables",
283                selected_column
284            ))
285            .add_help("Check the documentations to see available fields for each tables")
286            .with_location(location)
287            .as_boxed());
288        }
289    }
290
291    Ok(table_selections)
292}
293
294/// Check that all projection columns are valid for this table name
295/// Return a Diagnostic Error if anything is wrong
296pub fn type_check_projection_symbols(
297    env: &mut Environment,
298    selected_tables: &[String],
299    projection_names: &[String],
300    projection_locations: &[SourceLocation],
301) -> Result<(), Box<Diagnostic>> {
302    for (index, selected_column) in projection_names.iter().enumerate() {
303        let mut is_column_resolved = false;
304        for table in selected_tables {
305            let table_columns = env.schema.tables_fields_names.get(table.as_str()).unwrap();
306            if table_columns.contains(&selected_column.as_str()) {
307                is_column_resolved = true;
308                break;
309            }
310        }
311
312        if !is_column_resolved {
313            return Err(Diagnostic::error(&format!(
314                "Column `{}` not exists in any of the selected tables",
315                selected_column
316            ))
317            .add_help("Check the documentations to see available fields for each tables")
318            .with_location(projection_locations[index])
319            .as_boxed());
320        }
321    }
322
323    Ok(())
324}
325
326/// Resolve dynamic data type depending on the parameters and arguments types to actual DataType
327#[allow(clippy::borrowed_box)]
328pub fn resolve_dynamic_data_type(
329    parameters: &[Box<dyn DataType>],
330    arguments: &[Box<dyn Expr>],
331    data_type: &Box<dyn DataType>,
332) -> Box<dyn DataType> {
333    // Resolve Dynamic type
334    if let Some(dynamic_type) = data_type.as_any().downcast_ref::<DynamicType>() {
335        let mut resolved_data_type = (dynamic_type.function)(parameters);
336
337        // In Case that data type is Any or Variant [Type1 | Type2...] need to resolve it from arguments types
338        // To be able to use it with other expressions
339        if !arguments.is_empty() && (resolved_data_type.is_variant() || resolved_data_type.is_any())
340        {
341            let mut arguments_types: Vec<Box<dyn DataType>> = Vec::with_capacity(arguments.len());
342            for argument in arguments {
343                arguments_types.push(argument.expr_type());
344            }
345            resolved_data_type = (dynamic_type.function)(&arguments_types);
346        }
347
348        return resolved_data_type;
349    }
350
351    // Resolve ...Dynamic to ...<TYPE> recursively
352    if let Some(varargs) = data_type.as_any().downcast_ref::<VarargsType>() {
353        if varargs
354            .base
355            .as_any()
356            .downcast_ref::<DynamicType>()
357            .is_some()
358        {
359            let base = resolve_dynamic_data_type(parameters, arguments, &varargs.base);
360            return Box::new(VarargsType { base });
361        }
362    }
363
364    data_type.clone()
365}