y_lang/typechecker/
mod.rs

1//! Type checker for Y.
2//!
3//! This module provides type checking capabilities for ASt's.
4mod error;
5mod fn_extractor;
6mod info;
7mod typescope;
8mod variabletype;
9
10use crate::{
11    ast::{
12        Array, Assignment, Ast, BinaryExpr, BinaryOp, Block, Boolean, Call, Character,
13        CompilerDirective, Declaration, Definition, Expression, FnDef, Ident, If, Import, Indexing,
14        InlineAssembly, Integer, Intrinsic, Param, Position, PostfixExpr, PostfixOp, PrefixExpr,
15        PrefixOp, Statement, Str, Type, WhileLoop,
16    },
17    loader::Modules,
18};
19
20pub use self::fn_extractor::extract_exports;
21pub use self::info::TypeInfo;
22pub use self::typescope::TypeScope;
23pub use self::variabletype::VariableType;
24
25use self::{error::TypeError, typescope::setup_scope};
26
27/// Result of type checking a node within the AST.
28type TResult<T> = Result<T, TypeError>;
29
30/// Struct for type checking an AST.
31pub struct Typechecker {
32    ast: Ast<()>,
33    modules: Modules<()>,
34}
35
36impl Typechecker {
37    pub fn from_ast(ast: Ast<()>, modules: Modules<()>) -> Self {
38        Self { ast, modules }
39    }
40
41    /// Type check the contained AST and return the type correct AST with type information attached
42    /// to each node.
43    pub fn check(&self) -> Result<Ast<TypeInfo>, TypeError> {
44        let nodes = self.ast.nodes();
45
46        let mut scope = setup_scope();
47
48        let mut statements = vec![];
49
50        for node in nodes {
51            statements.push(self.check_statement(&node, &mut scope)?);
52        }
53
54        Ok(Ast::from_nodes(statements))
55    }
56
57    /// Extract the exports of a given AST. In particular, the exports are only the type
58    /// information of the defined functions.
59    /// Note: The exports are _not_ type checked.
60    pub fn extract_exports(ast: &Ast<()>) -> Result<TypeScope, TypeError> {
61        let nodes = ast.nodes();
62
63        let mut scope = setup_scope();
64
65        for intrinsic in nodes.iter() {
66            match intrinsic {
67                Statement::Intrinsic(Intrinsic::Definition(definition)) => {
68                    let Definition { value, ident, .. } = definition;
69
70                    let Expression::FnDef(FnDef { params, type_annotation , position, ..}) = value else {
71                        continue;
72                    };
73
74                    let mut param_types = vec![];
75
76                    for Param {
77                        type_annotation,
78                        position,
79                        ..
80                    } in params
81                    {
82                        param_types.push(Self::get_type_def(
83                            &type_annotation.value,
84                            position.clone(),
85                        )?);
86                    }
87
88                    scope.set(
89                        &ident.value,
90                        VariableType::Func {
91                            params: param_types,
92                            return_type: Box::new(Self::get_type_def(
93                                &type_annotation.value,
94                                position.clone(),
95                            )?),
96                            source: None,
97                        },
98                        false,
99                    )
100                }
101                Statement::Intrinsic(Intrinsic::Declaration(declaration)) => {
102                    let Declaration {
103                        ident,
104                        type_annotation,
105                        position,
106                        ..
107                    } = declaration;
108                    let type_annotation =
109                        Self::get_type_def(&type_annotation.value, position.clone())?;
110
111                    if let VariableType::Func { .. } = &type_annotation {
112                        scope.set(&ident.value, type_annotation, false);
113                    }
114                }
115                _ => {}
116            }
117        }
118        Ok(scope)
119    }
120
121    fn check_statement(
122        &self,
123        statement: &Statement<()>,
124        scope: &mut TypeScope,
125    ) -> TResult<Statement<TypeInfo>> {
126        Ok(match &statement {
127            Statement::Expression(expression) => {
128                Statement::Expression(self.check_expression(None, expression, scope)?)
129            }
130            Statement::Intrinsic(intrinsic) => {
131                Statement::Intrinsic(self.check_intrinsic(intrinsic, scope)?)
132            }
133            Statement::Import(import) => Statement::Import(self.check_import(import, scope)?),
134            Statement::CompilerDirective(compiler_directive) => Statement::CompilerDirective(
135                self.check_compiler_directive(compiler_directive, scope)?,
136            ),
137            Statement::InlineAssembly(inline_assembly) => {
138                Statement::InlineAssembly(self.check_inline_assembly(inline_assembly, scope)?)
139            }
140        })
141    }
142
143    fn check_inline_assembly(
144        &self,
145        InlineAssembly {
146            statements,
147            position,
148            ..
149        }: &InlineAssembly<()>,
150        _: &mut TypeScope,
151    ) -> TResult<InlineAssembly<TypeInfo>> {
152        Ok(InlineAssembly {
153            statements: statements.clone(),
154            position: position.clone(),
155            info: TypeInfo {
156                source: None,
157                _type: VariableType::Unknown,
158            },
159        })
160    }
161
162    fn check_compiler_directive(
163        &self,
164        CompilerDirective {
165            directive,
166            statement,
167            position,
168        }: &CompilerDirective<()>,
169        scope: &mut TypeScope,
170    ) -> TResult<CompilerDirective<TypeInfo>> {
171        let Expression::Binary(directive) = directive.clone() else {
172            unimplemented!("Currently only compiler directives in the form of binary expressions are supported!");
173        };
174        let Some(statement) = statement.clone() else {
175            return Ok(CompilerDirective {
176                directive: Expression::Binary(directive),
177                statement: None,
178                position: position.to_owned()
179            });
180        };
181
182        let is_valid = match (directive.lhs.as_ref(), directive.rhs.as_ref()) {
183            (Expression::Ident(ident), Expression::Str(rhs)) => match ident.value.as_str() {
184                "os" => std::env::consts::OS == rhs.value,
185                _ => false,
186            },
187            _ => unimplemented!(
188                "Currently only compiler directives in the form of 'ident == str' are supported!"
189            ),
190        };
191
192        if is_valid {
193            Ok(CompilerDirective {
194                directive: Expression::Binary(directive),
195                statement: Some(Box::new(self.check_statement(&statement, scope)?)),
196                position: position.clone(),
197            })
198        } else {
199            Ok(CompilerDirective {
200                directive: Expression::Binary(directive),
201                statement: None,
202                position: position.clone(),
203            })
204        }
205    }
206
207    fn check_import(&self, import: &Import, scope: &mut TypeScope) -> TResult<Import> {
208        let Import { position, path } = import;
209        let Some(module) = self.modules.get(path) else {
210           return Err(TypeError {
211               message: format!("Could not import module '{path}'"),
212               position: position.clone()
213           });
214        };
215
216        let imports = module.exports.flatten();
217
218        for (key, value) in imports {
219            if import.is_wildcard() {
220                scope.set(&key, value.variable_type.set_source(module.clone()), false);
221            } else {
222                scope.set(
223                    &format!("{path}::{key}"),
224                    value.variable_type.set_source(module.clone()),
225                    false,
226                );
227            }
228        }
229
230        Ok(import.clone())
231    }
232
233    fn check_intrinsic(
234        &self,
235        intrinsic: &Intrinsic<()>,
236        scope: &mut TypeScope,
237    ) -> TResult<Intrinsic<TypeInfo>> {
238        Ok(match &intrinsic {
239            Intrinsic::Definition(definition) => {
240                Intrinsic::Definition(self.check_definition(definition, scope)?)
241            }
242            Intrinsic::Assignment(assignment) => {
243                Intrinsic::Assignment(self.check_assignment(assignment, scope)?)
244            }
245            Intrinsic::Declaration(declaration) => {
246                Intrinsic::Declaration(self.check_declaration(declaration, scope)?)
247            }
248            Intrinsic::WhileLoop(while_loop) => {
249                Intrinsic::WhileLoop(self.check_while_loop(while_loop, scope)?)
250            }
251        })
252    }
253
254    fn check_while_loop(
255        &self,
256        WhileLoop {
257            condition,
258            block,
259            position,
260            ..
261        }: &WhileLoop<()>,
262        scope: &mut TypeScope,
263    ) -> TResult<WhileLoop<TypeInfo>> {
264        let condition = self.check_expression(None, condition, scope)?;
265        if condition.info()._type != VariableType::Bool {
266            return Err(TypeError {
267                message: format!("Invalid type of condition '{}'", condition.info()._type),
268                position: position.to_owned(),
269            });
270        }
271
272        let block = self.check_block(block, scope)?;
273
274        Ok(WhileLoop {
275            condition,
276            block,
277            position: position.to_owned(),
278            info: TypeInfo {
279                _type: VariableType::Void,
280                source: None,
281            },
282        })
283    }
284
285    fn check_declaration(
286        &self,
287        declaration: &Declaration,
288        scope: &mut TypeScope,
289    ) -> TResult<Declaration> {
290        let ident = &declaration.ident;
291        let type_annotation = &declaration.type_annotation;
292        let type_def =
293            Self::get_type_def(&type_annotation.value, type_annotation.position.clone())?;
294
295        scope.set(&ident.value, type_def, false);
296        Ok(declaration.clone())
297    }
298
299    fn check_if(&self, if_statement: &If<()>, scope: &mut TypeScope) -> TResult<If<TypeInfo>> {
300        let condition = self.check_expression(None, &if_statement.condition, scope)?;
301        let condition_info = condition.info();
302        let condition_type = condition_info._type;
303
304        if condition_type != VariableType::Bool {
305            return Err(TypeError {
306                message: format!("Invalid tye of condition '{condition_type:?}'"),
307                position: if_statement.condition.position(),
308            });
309        }
310
311        let if_block = self.check_block(&if_statement.if_block, scope)?;
312        let if_block_type = if_block.info._type.clone();
313
314        let mut new_if = If {
315            condition: Box::new(condition),
316            if_block,
317            else_block: None,
318            position: if_statement.position.clone(),
319            info: TypeInfo {
320                _type: if_block_type.clone(),
321                source: None,
322            },
323        };
324
325        if let Some(else_block) = &if_statement.else_block {
326            let else_block = self.check_block(else_block, scope)?;
327            let else_block_type = else_block.info._type.clone();
328
329            if if_block_type != else_block_type {
330                return Err(TypeError {
331                    message: format!(
332                        "Return type mismatch of if-else. Got '{if_block_type}' and '{else_block_type}'"
333                    ),
334                    position: if_statement.position.clone(),
335                });
336            }
337
338            new_if.else_block = Some(else_block);
339        }
340
341        Ok(new_if)
342    }
343
344    fn check_block(&self, block: &Block<()>, scope: &mut TypeScope) -> TResult<Block<TypeInfo>> {
345        scope.push();
346
347        let mut new_block = Block {
348            position: block.position.clone(),
349            block: vec![],
350            info: TypeInfo {
351                _type: VariableType::Void,
352                source: None,
353            },
354        };
355
356        for statement in &block.block {
357            let statement = self.check_statement(statement, scope)?;
358            new_block.info._type = statement.info()._type;
359            new_block.block.push(statement);
360        }
361
362        scope.pop();
363
364        Ok(new_block)
365    }
366
367    fn check_definition(
368        &self,
369        definition: &Definition<()>,
370        scope: &mut TypeScope,
371    ) -> TResult<Definition<TypeInfo>> {
372        let definition_rhs =
373            self.check_expression(Some(&definition.ident), &definition.value, scope)?;
374
375        if scope.contains_in_current_scope(&definition.ident.value) {
376            return Err(TypeError {
377                message: format!(
378                    "Variable '{}' has already been defined!",
379                    definition.ident.value
380                ),
381                position: definition.position.clone(),
382            });
383        }
384
385        scope.set(
386            &definition.ident.value,
387            definition_rhs.info()._type,
388            definition.is_mutable,
389        );
390
391        let ident = &definition.ident;
392
393        Ok(Definition {
394            ident: Ident {
395                position: ident.position.clone(),
396                value: ident.value.clone(),
397                info: definition_rhs.info(),
398            },
399            value: definition_rhs,
400            position: definition.position.clone(),
401            is_mutable: definition.is_mutable,
402            info: TypeInfo {
403                _type: VariableType::Void,
404                source: None,
405            },
406        })
407    }
408
409    fn check_assignment(
410        &self,
411        assignment: &Assignment<()>,
412        scope: &mut TypeScope,
413    ) -> TResult<Assignment<TypeInfo>> {
414        let lhs = &assignment.lhs;
415
416        match lhs {
417            Expression::Postfix(PostfixExpr {
418                op: PostfixOp::Indexing(indexing),
419                lhs: indexing_lhs,
420                position,
421                ..
422            }) => {
423                let indexing_lhs = self.check_expression(None, indexing_lhs, scope)?;
424                let indexing = self.check_indexing(&indexing_lhs, indexing, scope)?;
425
426                let assignment_rhs = self.check_expression(None, &assignment.value, scope)?;
427
428                if assignment_rhs
429                    .info()
430                    ._type
431                    .convert_to(&indexing.info._type)
432                    .is_err()
433                {
434                    return Err(TypeError {
435                        message: format!(
436                            "Can not assign value of type '{}' to indexed variable of type '{}'",
437                            assignment_rhs.info()._type,
438                            indexing.info._type
439                        ),
440                        position: assignment.position.clone(),
441                    });
442                }
443
444                Ok(Assignment {
445                    lhs: Expression::Postfix(PostfixExpr {
446                        op: PostfixOp::Indexing(indexing),
447                        lhs: Box::new(indexing_lhs),
448                        position: position.clone(),
449                        info: assignment_rhs.info(),
450                    }),
451                    value: assignment_rhs,
452                    position: assignment.position.clone(),
453                    info: TypeInfo {
454                        source: None,
455                        _type: VariableType::Void,
456                    },
457                })
458            }
459            Expression::Ident(lhs) => {
460                if !scope.contains(&lhs.value) {
461                    return Err(TypeError {
462                        message: format!("Undefined identifier '{}'", lhs.value),
463                        position: lhs.position.clone(),
464                    });
465                }
466
467                if !scope.is_mutable(&lhs.value) {
468                    return Err(TypeError {
469                        message: format!(
470                    "Variable '{}' can not be modified, because it is not defined in current scope",
471                    lhs.value
472                ),
473                        position: lhs.position.clone(),
474                    });
475                }
476
477                let assignment_rhs = self.check_expression(Some(lhs), &assignment.value, scope)?;
478
479                scope.update(
480                    &lhs.value,
481                    assignment_rhs.info()._type,
482                    &assignment.position,
483                )?;
484
485                Ok(Assignment {
486                    lhs: Expression::Ident(Ident {
487                        position: lhs.position.clone(),
488                        value: lhs.value.clone(),
489                        info: assignment_rhs.info(),
490                    }),
491                    value: assignment_rhs,
492                    position: assignment.position.clone(),
493                    info: TypeInfo {
494                        source: None,
495                        _type: VariableType::Void,
496                    },
497                })
498            }
499            _ => Err(TypeError {
500                message: format!("Invalid lvalue of assignment '{lhs:?}'"),
501                position: lhs.position(),
502            }),
503        }
504    }
505
506    fn check_expression(
507        &self,
508        identifier: Option<&Ident<()>>,
509        expression: &Expression<()>,
510        scope: &mut TypeScope,
511    ) -> TResult<Expression<TypeInfo>> {
512        Ok(match expression {
513            Expression::If(if_statement) => Expression::If(self.check_if(if_statement, scope)?),
514            Expression::Binary(binary_expr) => {
515                Expression::Binary(self.check_binary_expression(binary_expr, scope)?)
516            }
517            Expression::Integer(Integer {
518                value, position, ..
519            }) => Expression::Integer(Integer {
520                value: *value,
521                position: position.clone(),
522                info: TypeInfo {
523                    _type: VariableType::Int,
524                    source: None,
525                },
526            }),
527            Expression::Str(Str {
528                value, position, ..
529            }) => Expression::Str(Str {
530                value: value.to_owned(),
531                position: position.clone(),
532                info: TypeInfo {
533                    _type: VariableType::Str,
534                    source: None,
535                },
536            }),
537            Expression::Boolean(Boolean {
538                value, position, ..
539            }) => Expression::Boolean(Boolean {
540                value: *value,
541                position: position.clone(),
542                info: TypeInfo {
543                    _type: VariableType::Bool,
544                    source: None,
545                },
546            }),
547            Expression::Ident(ident) => Expression::Ident(self.check_identifier(ident, scope)?),
548            Expression::Prefix(prefix_expr) => {
549                Expression::Prefix(self.check_prefix_expression(prefix_expr, scope)?)
550            }
551            Expression::Postfix(postfix_expr) => {
552                Expression::Postfix(self.check_postfix_expression(postfix_expr, scope)?)
553            }
554            Expression::FnDef(fn_def) => {
555                Expression::FnDef(self.check_fn_def(identifier, fn_def, scope)?)
556            }
557            Expression::Block(block) => Expression::Block(self.check_block(block, scope)?),
558            Expression::Array(array) => Expression::Array(self.check_array(array, scope)?),
559            Expression::Character(Character {
560                value, position, ..
561            }) => Expression::Character(Character {
562                value: *value,
563                position: position.clone(),
564                info: TypeInfo {
565                    _type: VariableType::Char,
566                    source: None,
567                },
568            }),
569        })
570    }
571
572    fn check_array(
573        &self,
574        Array {
575            initializer,
576            size,
577            position,
578            ..
579        }: &Array<()>,
580        scope: &mut TypeScope,
581    ) -> TResult<Array<TypeInfo>> {
582        let initializer = self.check_expression(None, initializer, scope)?;
583
584        Ok(Array {
585            initializer: Box::new(initializer.clone()),
586            size: size.to_owned(),
587            position: position.to_owned(),
588            info: TypeInfo {
589                _type: VariableType::TupleArray {
590                    item_type: Box::new(initializer.info()._type),
591                    size: if size.value >= 0 {
592                        size.value as usize
593                    } else {
594                        return Err(TypeError {
595                            message: "Negative length arrays are not supported!".to_string(),
596                            position: position.clone(),
597                        });
598                    },
599                },
600                source: initializer.info()._type.get_source(),
601            },
602        })
603    }
604
605    fn check_identifier(
606        &self,
607        identifier: &Ident<()>,
608        scope: &mut TypeScope,
609    ) -> TResult<Ident<TypeInfo>> {
610        match scope.find(&identifier.value) {
611            Some(identifier_type) => Ok(Ident {
612                value: identifier.value.clone(),
613                position: identifier.position.clone(),
614                info: TypeInfo {
615                    _type: identifier_type,
616                    source: None,
617                },
618            }),
619            None => Err(TypeError {
620                message: format!("Undefined identifier '{}'", identifier.value),
621                position: identifier.position.clone(),
622            }),
623        }
624    }
625
626    fn get_type_def(type_: &Type, position: Position) -> Result<VariableType, TypeError> {
627        match type_ {
628            Type::Literal(literal) => literal.parse().map_err(|_| TypeError {
629                message: format!("Unexpected type annotation '{type_:?}'"),
630                position,
631            }),
632            Type::Function {
633                params,
634                return_type,
635            } => {
636                let mut fn_params = vec![];
637                for param in params {
638                    fn_params.push(Self::get_type_def(param, position.clone())?);
639                }
640
641                let return_type = Self::get_type_def(return_type, position)?;
642                Ok(VariableType::Func {
643                    return_type: Box::new(return_type),
644                    params: fn_params,
645                    source: None,
646                })
647            }
648            Type::ArraySlice(item_type) => {
649                let item_type = Self::get_type_def(item_type, position)?;
650
651                Ok(VariableType::ArraySlice(Box::new(item_type)))
652            }
653            Type::TupleArray { item_type, size } => {
654                let item_type = Self::get_type_def(item_type, position.clone())?;
655
656                Ok(VariableType::TupleArray {
657                    item_type: Box::new(item_type),
658                    size: if size.value >= 0 {
659                        size.value as usize
660                    } else {
661                        return Err(TypeError {
662                            message: "Negative length arrays are not supported!".to_string(),
663                            position,
664                        });
665                    },
666                })
667            }
668        }
669    }
670
671    fn check_fn_def(
672        &self,
673        identifier: Option<&Ident<()>>,
674        fn_def: &FnDef<()>,
675        scope: &mut TypeScope,
676    ) -> TResult<FnDef<TypeInfo>> {
677        let type_annotation = Self::get_type_def(
678            &fn_def.type_annotation.value,
679            fn_def.type_annotation.position.clone(),
680        )?;
681        scope.push();
682
683        let mut params = vec![];
684
685        for param in &fn_def.params {
686            let param_type = Self::get_type_def(
687                &param.type_annotation.value,
688                param.type_annotation.position.clone(),
689            )?;
690
691            scope.set(&param.ident.value, param_type.clone(), false);
692            params.push(param_type);
693        }
694
695        if let Some(ident) = identifier {
696            scope.set(
697                &ident.value,
698                VariableType::Func {
699                    params: params.clone(),
700                    return_type: Box::new(type_annotation.clone()),
701                    source: None,
702                },
703                // TODO: This should handle mutable definitions
704                false,
705            )
706        }
707
708        let block = self.check_block(&fn_def.block, scope)?;
709
710        let Ok(return_type) = block.info._type.convert_to(&type_annotation) else {
711            return Err(TypeError {
712                message: format!(
713                    "Expected return type of '{type_annotation}' but got '{}'", block.info._type
714                ),
715                position: fn_def.position.clone(),
716            });
717        };
718
719        scope.pop();
720
721        Ok(FnDef {
722            params: self.check_fn_params(&fn_def.params)?,
723            type_annotation: fn_def.type_annotation.clone(),
724            block,
725            position: fn_def.position.clone(),
726            info: TypeInfo {
727                _type: VariableType::Func {
728                    params,
729                    return_type: Box::new(return_type),
730                    source: None,
731                },
732                source: None,
733            },
734        })
735    }
736
737    fn check_fn_params(&self, params: &Vec<Param<()>>) -> TResult<Vec<Param<TypeInfo>>> {
738        let mut new_params = vec![];
739
740        for param in params {
741            let Ident {
742                value, position, ..
743            } = &param.ident;
744            let type_annotation = &param.type_annotation;
745            let param_type =
746                Self::get_type_def(&type_annotation.value, type_annotation.position.clone())?;
747
748            new_params.push(Param {
749                ident: Ident {
750                    value: value.clone(),
751                    position: position.clone(),
752                    info: TypeInfo {
753                        _type: param_type,
754                        source: None,
755                    },
756                },
757                position: param.position.clone(),
758                type_annotation: type_annotation.clone(),
759            });
760        }
761
762        Ok(new_params)
763    }
764
765    fn check_fn_call(
766        &self,
767        ident: &Ident<()>,
768        fn_call: &Call<()>,
769        scope: &mut TypeScope,
770    ) -> TResult<Call<TypeInfo>> {
771        scope.push();
772
773        let ident = &ident.value;
774
775        let Some(fn_def) = scope.find(ident) else {
776            return Err(TypeError {
777                message: format!("Call to undefined function '{ident}'"),
778                position: fn_call.position.clone(),
779            });
780        };
781
782        let VariableType::Func { params, return_type, .. } = fn_def.clone() else {
783            return Err(TypeError {
784                message: format!("Trying to call an invalid function '{ident}'"),
785                position: fn_call.position.clone(),
786            });
787        };
788
789        if params.len() != fn_call.params.len() {
790            return Err(TypeError {
791                message: format!(
792                    "Invalid amount of parameters! Expected {} but got {}",
793                    params.len(),
794                    fn_call.params.len()
795                ),
796                position: fn_call.position.clone(),
797            });
798        }
799
800        let mut new_params = vec![];
801
802        for (i, param) in params.iter().enumerate() {
803            let call_param = self.check_expression(None, &fn_call.params[i], scope)?;
804            let call_param_type = call_param.info()._type;
805
806            if call_param_type.convert_to(param).is_err() {
807                return Err(TypeError {
808                    message: format!(
809                        "Invalid type of parameter! Expected '{param}' but got '{call_param_type}'"
810                    ),
811                    position: fn_call.params[i].position(),
812                });
813            }
814
815            new_params.push(call_param);
816        }
817
818        scope.pop();
819
820        Ok(Call {
821            params: new_params,
822            position: fn_call.position.clone(),
823            info: TypeInfo {
824                _type: *return_type,
825                source: fn_def.get_source(),
826            },
827        })
828    }
829
830    fn check_binary_expression(
831        &self,
832        binary_expression: &BinaryExpr<()>,
833        scope: &mut TypeScope,
834    ) -> TResult<BinaryExpr<TypeInfo>> {
835        let position = binary_expression.position.clone();
836
837        let lhs = &binary_expression.lhs;
838        let rhs = &binary_expression.rhs;
839
840        let lhs = self.check_expression(None, lhs, scope)?;
841        let l_type = lhs.info()._type;
842
843        let rhs = self.check_expression(None, rhs, scope)?;
844        let r_type = rhs.info()._type;
845
846        match binary_expression.op {
847            BinaryOp::Equal => {
848                if l_type != r_type {
849                    return Err(TypeError {
850                        message: format!(
851                        "Left and right value of binary operation do not match! ('{l_type}' and '{r_type}')"
852                    ),
853                        position,
854                    });
855                }
856                Ok(BinaryExpr {
857                    op: binary_expression.op,
858                    lhs: Box::new(lhs),
859                    rhs: Box::new(rhs),
860                    position: binary_expression.position.clone(),
861                    info: TypeInfo {
862                        _type: VariableType::Bool,
863                        source: None,
864                    },
865                })
866            }
867            BinaryOp::LessThan | BinaryOp::GreaterThan => {
868                if l_type != VariableType::Int || r_type != VariableType::Int {
869                    return Err(TypeError {
870                        message: format!(
871                            "Invalid types for binary operation '{}'. Got '{}' and '{}'",
872                            binary_expression.op, l_type, r_type
873                        ),
874                        position,
875                    });
876                }
877                Ok(BinaryExpr {
878                    op: binary_expression.op,
879                    lhs: Box::new(lhs),
880                    rhs: Box::new(rhs),
881                    position: binary_expression.position.clone(),
882                    info: TypeInfo {
883                        _type: VariableType::Bool,
884                        source: None,
885                    },
886                })
887            }
888            BinaryOp::Plus | BinaryOp::Minus | BinaryOp::Times | BinaryOp::DividedBy => {
889                if l_type != VariableType::Int {
890                    return Err(TypeError {
891                        message: format!(
892                        "Left value of numeric binary operation has to be of type Int. Found '{l_type}'"
893                    ),
894                        position: lhs.position(),
895                    });
896                } else if r_type != VariableType::Int {
897                    return Err(TypeError {
898                        message: format!(
899                        "Right value of numeric binary operation has to be of type Int. Found '{r_type}'"
900                    ),
901                        position: rhs.position(),
902                    });
903                }
904
905                Ok(BinaryExpr {
906                    op: binary_expression.op,
907                    lhs: Box::new(lhs),
908                    rhs: Box::new(rhs),
909                    position: binary_expression.position.clone(),
910                    info: TypeInfo {
911                        _type: VariableType::Int,
912                        source: None,
913                    },
914                })
915            }
916        }
917    }
918
919    fn check_prefix_expression(
920        &self,
921        prefix_expression: &PrefixExpr<()>,
922        scope: &mut TypeScope,
923    ) -> TResult<PrefixExpr<TypeInfo>> {
924        let position = prefix_expression.position.clone();
925
926        let rhs = &prefix_expression.rhs;
927
928        let rhs = self.check_expression(None, rhs, scope)?;
929        let r_type = rhs.info()._type;
930
931        match prefix_expression.op {
932            PrefixOp::Not => {
933                if r_type != VariableType::Bool {
934                    return Err(TypeError {
935                        message: format!(
936                            "Invalid type for boolean prefix operation '{}'. Got '{}'",
937                            prefix_expression.op, r_type
938                        ),
939                        position,
940                    });
941                }
942                Ok(PrefixExpr {
943                    op: prefix_expression.op,
944                    rhs: Box::new(rhs),
945                    position,
946                    info: TypeInfo {
947                        _type: VariableType::Bool,
948                        source: None,
949                    },
950                })
951            }
952            PrefixOp::UnaryMinus => {
953                if r_type != VariableType::Int {
954                    return Err(TypeError {
955                        message: format!(
956                            "Invalid type for integral prefix operation '{}'. Got '{}'",
957                            prefix_expression.op, r_type
958                        ),
959                        position,
960                    });
961                }
962                Ok(PrefixExpr {
963                    op: prefix_expression.op,
964                    rhs: Box::new(rhs),
965                    position,
966                    info: TypeInfo {
967                        _type: VariableType::Int,
968                        source: None,
969                    },
970                })
971            }
972        }
973    }
974
975    fn check_postfix_expression(
976        &self,
977        postfix_expression: &PostfixExpr<()>,
978        scope: &mut TypeScope,
979    ) -> TResult<PostfixExpr<TypeInfo>> {
980        let postfix_expression = postfix_expression.clone();
981
982        let lhs = &postfix_expression.lhs;
983
984        let lhs = self.check_expression(None, lhs, scope)?;
985
986        match postfix_expression.op {
987            PostfixOp::Call(call) => {
988                let Expression::Ident(ident) = *postfix_expression.lhs else {
989                    unimplemented!("Calls on non-identifier-expressions are not implemented yet")
990                };
991                let call = self.check_fn_call(&ident, &call, scope)?;
992                let info = call.info.clone();
993                Ok(PostfixExpr {
994                    op: PostfixOp::Call(call),
995                    lhs: Box::new(lhs),
996                    position: postfix_expression.position,
997                    info,
998                })
999            }
1000            PostfixOp::Indexing(indexing) => {
1001                let lhs = self.check_expression(None, &postfix_expression.lhs, scope)?;
1002                let indexing = self.check_indexing(&lhs, &indexing, scope)?;
1003
1004                Ok(PostfixExpr {
1005                    op: PostfixOp::Indexing(indexing.clone()),
1006                    lhs: Box::new(lhs),
1007                    position: postfix_expression.position,
1008                    info: indexing.info,
1009                })
1010            }
1011        }
1012    }
1013
1014    fn check_indexing(
1015        &self,
1016        lhs: &Expression<TypeInfo>,
1017        Indexing {
1018            index, position, ..
1019        }: &Indexing<()>,
1020        scope: &mut TypeScope,
1021    ) -> TResult<Indexing<TypeInfo>> {
1022        let Expression::Integer(index) = self.check_expression(None, index, scope)? else {
1023            unimplemented!("Indexing with a non-numeric index is currently not supported")
1024        };
1025
1026        match lhs.info()._type {
1027            VariableType::ArraySlice(item_type) => Ok(Indexing {
1028                index: Box::new(Expression::Integer(index)),
1029                position: position.to_owned(),
1030                info: TypeInfo {
1031                    _type: *item_type.clone(),
1032                    source: item_type.get_source(),
1033                },
1034            }),
1035            VariableType::TupleArray { item_type, .. } => Ok(Indexing {
1036                index: Box::new(Expression::Integer(index)),
1037                position: position.to_owned(),
1038                info: TypeInfo {
1039                    _type: *item_type.clone(),
1040                    source: item_type.get_source(),
1041                },
1042            }),
1043            VariableType::Str => Ok(Indexing {
1044                index: Box::new(Expression::Integer(index)),
1045                position: position.to_owned(),
1046                info: TypeInfo {
1047                    _type: VariableType::Char,
1048                    source: lhs.info()._type.get_source(),
1049                },
1050            }),
1051            _ => unimplemented!("Indexing on non-array types is currently not supported"),
1052        }
1053    }
1054}