1mod 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
27type TResult<T> = Result<T, TypeError>;
29
30pub 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 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 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 ¶m.type_annotation.value,
688 param.type_annotation.position.clone(),
689 )?;
690
691 scope.set(¶m.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 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 } = ¶m.ident;
744 let type_annotation = ¶m.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}