1use std::{
21 fmt::{Debug, Display},
22 net::IpAddr,
23 sync::Arc,
24};
25
26use crate::{
27 common::TextLocation,
28 environment::scope::Scopes,
29 mel::{
30 error::{AnalysisAssertions, AnalysisError, AnalysisLocatableError, AnalysisPreconditions},
31 types::{ArgumentTypeList, ParamsTypeCheckerError, ParamsTypeCheckerGenerator},
32 },
33};
34
35use crate::mel::{
36 analysis::AnalysisAssertions::{
37 ContextMissingExpr, ContextMissingParams, ContextWrongExprType,
38 },
39 ast::{
40 self, Argument, ArgumentList, AstVisitor, AstVisitorDriver, AstVisitorResult, BinaryExpr,
41 BinaryInfixOperator, BooleanLiteral,
42 ComparisonOperator::{IP, Re},
43 Expr, FunctionCall, IPAddressLiteral, Identifier, MemberAccessExpression, NumberLiteral,
44 StringLiteral, TernaryExpr,
45 },
46 error::AnalysisError::{AssertionFailure, InvalidType, Mismatch, UnknownIdentifier},
47 types::{
48 self,
49 Type::{self, Struct},
50 },
51};
52
53#[derive(Debug, Clone)]
54pub enum CompiledConstant {
55 Integer(i64),
56 String(String),
57 Boolean(bool),
58 IPAddress(IpAddr),
59}
60
61#[derive(Debug, Clone)]
62pub struct Analyzed {
63 pub tipe: Type,
64 pub constant: Option<CompiledConstant>,
65}
66
67impl Expr<Analyzed> {
68 pub fn tipe(&self) -> Type {
69 match self {
70 Expr::FunctionCall(function_call) => function_call.aug.tipe.clone(),
71 Expr::BinaryExpr(binary_expr) => binary_expr.aug.tipe.clone(),
72 Expr::TernaryExpr(ternary_expr) => ternary_expr.aug.tipe.clone(),
73 Expr::Identifier(identifier) => identifier.aug.tipe.clone(),
74 Expr::ArgumentList(argument_list) => argument_list.aug.tipe.clone(),
75 Expr::Argument(argument) => argument.aug.tipe.clone(),
76 Expr::MemberAccess(member) => member.aug.tipe.clone(),
77 Expr::Literal(_, _, aug) => aug.tipe.clone(),
78 }
79 }
80
81 pub fn constant(&self) -> Option<CompiledConstant> {
82 let analyzed = match self {
83 Expr::FunctionCall(function_call) => &function_call.aug,
84 Expr::BinaryExpr(binary_expr) => &binary_expr.aug,
85 Expr::TernaryExpr(ternary_expr) => &ternary_expr.aug,
86 Expr::Identifier(identifier) => &identifier.aug,
87 Expr::ArgumentList(argument_list) => &argument_list.aug,
88 Expr::Argument(argument) => &argument.aug,
89 Expr::MemberAccess(member) => &member.aug,
90 Expr::Literal(_, _, aug) => aug,
91 };
92 analyzed.constant.clone()
93 }
94}
95
96impl<A: Clone + Debug> Display for Expr<A> {
97 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
98 match self {
99 Expr::FunctionCall(_) => write!(f, "FunctionCall"),
100 Expr::BinaryExpr(_) => write!(f, "BinaryExpr"),
101 Expr::TernaryExpr(_) => write!(f, "TernaryExpr"),
102 Expr::Identifier(_) => write!(f, "Identifier"),
103 Expr::ArgumentList(_) => write!(f, "ArgumentList"),
104 Expr::Argument(_) => write!(f, "Argument"),
105 Expr::MemberAccess(_) => write!(f, "MemberAccess"),
106 Expr::Literal(_, _, _) => write!(f, "Literal"),
107 }
108 }
109}
110
111#[derive(Debug, Clone, Default)]
112pub struct MelAnalysisContext {
113 pub expr: Option<Expr<Analyzed>>,
114 pub ptcg: Option<ParamsTypeCheckerGenerator>,
115 pub scopes: Scopes<Type>,
116}
117
118impl MelAnalysisContext {
119 pub fn update_expr(&self, new: Expr<Analyzed>) -> Self {
120 MelAnalysisContext {
121 expr: Some(new),
122 ptcg: self.ptcg,
123 scopes: self.scopes.clone(),
124 }
125 }
126 pub fn update_scopes(&self, new: &Scopes<Type>) -> Self {
127 MelAnalysisContext {
128 expr: self.expr.clone(),
129 ptcg: self.ptcg,
130 scopes: new.clone(),
131 }
132 }
133 pub fn update_param_type_checker_generator(&self, ptcg: ParamsTypeCheckerGenerator) -> Self {
134 MelAnalysisContext {
135 expr: self.expr.clone(),
136 ptcg: Some(ptcg),
137 scopes: self.scopes.clone(),
138 }
139 }
140}
141
142impl From<Vec<Argument<Analyzed>>> for ArgumentTypeList {
143 fn from(value: Vec<Argument<Analyzed>>) -> Self {
144 ArgumentTypeList {
145 args: value.iter().map(|a| a.expr.tipe()).collect(),
146 }
147 }
148}
149
150pub struct MelTypeChecker {}
151
152impl AstVisitor<MelAnalysisContext, (), AnalysisLocatableError> for MelTypeChecker {
153 fn visit_function_call(
154 &self,
155 ast: &FunctionCall<()>,
156 context: MelAnalysisContext,
157 driver: &AstVisitorDriver,
158 ) -> AstVisitorResult<MelAnalysisContext, AnalysisLocatableError> {
159 let callee = driver.visit(&ast.callee, self, context.clone())?;
160
161 let callee = if let Some(callee) = callee.expr {
162 callee
163 } else {
164 return Err(AnalysisLocatableError {
165 error: AssertionFailure(AnalysisAssertions::ContextMissingExpr(
166 "visit_function_call".to_string(),
167 ))
168 .into(),
169 location: ast.location.clone(),
170 });
171 };
172
173 let fn_params = match callee.tipe() {
174 Type::Function(_, return_type_generator, param_type_checker_generator) => {
175 let return_type = return_type_generator();
176 (return_type, param_type_checker_generator)
177 }
178 _ => unreachable!(), };
180
181 let context_with_params = context.update_param_type_checker_generator(fn_params.1);
182 let args = self.visit_argument_list(&ast.arguments, context_with_params, driver)?;
183 let args = match args.expr.unwrap() {
184 Expr::ArgumentList(argument_list) => Ok(argument_list),
185 e => Err(AssertionFailure(ContextWrongExprType(
186 "ArgumentList".to_string(),
187 e.to_string(),
188 "visit_function_call".to_string(),
189 ))
190 .into()),
191 }
192 .map_err(|e| AnalysisLocatableError {
193 error: e,
194 location: ast.location.clone(),
195 })?;
196
197 Ok(
198 context.update_expr(Expr::FunctionCall(Arc::new(FunctionCall {
199 callee: callee.clone(),
200 location: ast.location.clone(),
201 arguments: (*args).clone(),
202 aug: Analyzed {
203 tipe: fn_params.0.clone(),
204 constant: None,
205 },
206 }))),
207 )
208 }
209
210 fn visit_identifier(
211 &self,
212 ast: &Identifier<()>,
213 context: MelAnalysisContext,
214 _driver: &AstVisitorDriver,
215 ) -> AstVisitorResult<MelAnalysisContext, AnalysisLocatableError> {
216 let found_id = context
217 .scopes
218 .lookup(&ast.identifier)
219 .ok_or(AnalysisLocatableError {
220 error: UnknownIdentifier(ast.identifier.clone()).into(),
221 location: ast.location.clone(),
222 })?;
223
224 Ok(context.update_expr(Expr::Identifier(Arc::new(Identifier {
225 identifier: ast.identifier.clone(),
226 location: ast.location.clone(),
227 aug: Analyzed {
228 tipe: found_id,
229 constant: None,
230 },
231 }))))
232 }
233
234 fn visit_argument_list(
235 &self,
236 ast: &ArgumentList<()>,
237 context: MelAnalysisContext,
238 driver: &AstVisitorDriver,
239 ) -> AstVisitorResult<MelAnalysisContext, AnalysisLocatableError> {
240 let ptcg = context.ptcg.ok_or(AnalysisLocatableError {
241 error: AssertionFailure(ContextMissingParams).into(),
242 location: ast.location.clone(),
243 })?;
244
245 let ptc = ptcg();
246
247 let mut arg_types: Vec<Argument<Analyzed>> = vec![];
248 for arg in &ast.arguments {
249 let arg = self
250 .visit_argument(arg, context.clone(), driver)?
251 .expr
252 .ok_or(AnalysisLocatableError {
253 error: AnalysisError::AssertionFailure(ContextMissingExpr(
254 "visit_argument_list".into(),
255 ))
256 .into(),
257 location: arg.location.clone(),
258 })?;
259 let arg = match arg {
260 Expr::Argument(argument) => Ok(argument),
261 e => Err(AssertionFailure(ContextWrongExprType(
262 "Argument".to_string(),
263 e.to_string(),
264 "visit_argument_list".to_string(),
265 ))),
266 }
267 .map_err(|e| AnalysisLocatableError {
268 error: e.into(),
269 location: ast.location.clone(),
270 })?;
271 arg_types.push((*arg).clone());
272 }
273
274 ptc.check(arg_types.clone().into()).map_err(|e| {
275 let error = match e {
276 ParamsTypeCheckerError::Miscount(expected, actual) => {
277 AnalysisError::Miscount(expected, actual)
278 }
279 ParamsTypeCheckerError::Mismatch(expected, actual) => {
280 AnalysisError::Mismatch(expected, actual)
281 }
282 };
283 AnalysisLocatableError {
284 error: error.into(),
285 location: ast.location.clone(),
286 }
287 })?;
288
289 Ok(
290 context.update_expr(Expr::ArgumentList(Arc::new(ArgumentList {
291 arguments: arg_types,
292 location: ast.location.clone(),
293 aug: Analyzed {
294 tipe: Type::Nil,
295 constant: None,
296 },
297 }))),
298 )
299 }
300
301 fn visit_argument(
302 &self,
303 ast: &Argument<()>,
304 context: MelAnalysisContext,
305 driver: &AstVisitorDriver,
306 ) -> AstVisitorResult<MelAnalysisContext, AnalysisLocatableError> {
307 let arg =
308 driver
309 .visit(&ast.expr, self, context.clone())?
310 .expr
311 .ok_or(AnalysisLocatableError {
312 error: AnalysisError::AssertionFailure(ContextMissingExpr(
313 "visit_argument".into(),
314 ))
315 .into(),
316 location: ast.location.clone(),
317 })?;
318
319 let arg_type = arg.tipe();
320 Ok(context.update_expr(Expr::Argument(Arc::new(Argument {
321 expr: arg,
322 location: ast.location.clone(),
323 aug: Analyzed {
324 tipe: arg_type,
325 constant: None,
326 },
327 }))))
328 }
329
330 fn visit_binary_expr(
331 &self,
332 ast: &BinaryExpr<()>,
333 context: MelAnalysisContext,
334 driver: &AstVisitorDriver,
335 ) -> AstVisitorResult<MelAnalysisContext, AnalysisLocatableError> {
336 let left =
337 driver
338 .visit(&ast.left, self, context.clone())?
339 .expr
340 .ok_or(AnalysisLocatableError {
341 error: AnalysisError::AssertionFailure(ContextMissingExpr(
342 "visit_binary_expr".into(),
343 ))
344 .into(),
345 location: ast.left.location(),
346 })?;
347 let right = driver
348 .visit(&ast.right, self, context.clone())?
349 .expr
350 .ok_or(AnalysisLocatableError {
351 error: AnalysisError::AssertionFailure(ContextMissingExpr(
352 "visit_binary_expr".into(),
353 ))
354 .into(),
355 location: ast.right.location(),
356 })?;
357
358 let left_type = left.tipe();
359 let right_type = right.tipe();
360
361 let (valid_types, result_type) = match &ast.op {
362 BinaryInfixOperator::Logic(_) => (vec![Type::Boolean], Type::Boolean),
363 BinaryInfixOperator::Comparison(Re) => (vec![Type::Regex, Type::String], Type::Boolean),
364 BinaryInfixOperator::Comparison(IP) => (vec![Type::IPAddress], Type::Boolean),
365 BinaryInfixOperator::Comparison(_) => (
366 vec![Type::Boolean, Type::String, Type::Integer],
367 Type::Boolean,
368 ),
369 BinaryInfixOperator::Math(_) => (vec![Type::Integer], Type::Integer),
370 BinaryInfixOperator::Concat(_) => (vec![Type::String], Type::String),
371 BinaryInfixOperator::MemberAccess(e) => {
372 return Err(AnalysisLocatableError {
373 error: AnalysisError::AssertionFailure(AnalysisAssertions::InvalidOperator(
374 e.to_string(),
375 "visit_binary_expr".to_string(),
376 ))
377 .into(),
378 location: ast.right.location(),
379 });
380 }
381 };
382
383 if !valid_types.contains(&left_type) {
384 return Err(AnalysisLocatableError {
385 error: InvalidType(valid_types, left_type).into(),
386 location: left.location(),
387 });
388 }
389 if !valid_types.contains(&right_type) {
390 return Err(AnalysisLocatableError {
391 error: InvalidType(valid_types, right_type).into(),
392 location: right.location(),
393 });
394 }
395
396 if let BinaryInfixOperator::Comparison(Re) = ast.op {
398 if left_type != Type::String {
399 return Err(AnalysisLocatableError {
400 error: Mismatch(Type::String, left_type).into(),
401 location: left.location(),
402 });
403 }
404 if right_type != Type::Regex {
405 return Err(AnalysisLocatableError {
406 error: Mismatch(Type::Regex, right_type).into(),
407 location: right.location(),
408 });
409 }
410 return Ok(context.update_expr(Expr::BinaryExpr(Arc::new(BinaryExpr {
411 left,
412 right,
413 op: ast.op.clone(),
414 location: ast.location.clone(),
415 aug: Analyzed {
416 tipe: result_type,
417 constant: None,
418 },
419 }))));
420 }
421
422 if left_type != right_type {
424 return Err(AnalysisLocatableError {
425 error: Mismatch(left_type, right_type).into(),
426 location: right.location(),
427 });
428 }
429
430 Ok(context.update_expr(Expr::BinaryExpr(Arc::new(BinaryExpr {
431 left,
432 right,
433 op: ast.op.clone(),
434 location: ast.location.clone(),
435 aug: Analyzed {
436 tipe: result_type,
437 constant: None,
438 },
439 }))))
440 }
441
442 fn visit_literal(
443 &self,
444 ast: (&ast::Literal, &TextLocation, &()),
445 context: MelAnalysisContext,
446 _driver: &AstVisitorDriver,
447 ) -> AstVisitorResult<MelAnalysisContext, AnalysisLocatableError> {
448 match ast {
449 (b @ ast::Literal::Boolean(_), location, _) => Ok(context.update_expr(Expr::Literal(
450 b.clone(),
451 location.clone(),
452 Analyzed {
453 tipe: Type::Boolean,
454 constant: None,
455 },
456 ))),
457 (n @ ast::Literal::Number(_), location, _) => Ok(context.update_expr(Expr::Literal(
458 n.clone(),
459 location.clone(),
460 Analyzed {
461 tipe: Type::Integer,
462 constant: None,
463 },
464 ))),
465 (s @ ast::Literal::String(_), location, _) => Ok(context.update_expr(Expr::Literal(
466 s.clone(),
467 location.clone(),
468 Analyzed {
469 tipe: Type::String,
470 constant: None,
471 },
472 ))),
473 (r @ ast::Literal::Regex(_), location, _) => Ok(context.update_expr(Expr::Literal(
474 r.clone(),
475 location.clone(),
476 Analyzed {
477 tipe: Type::Regex,
478 constant: None,
479 },
480 ))),
481 (ip @ ast::Literal::IPAddress(_), location, _) => {
482 Ok(context.update_expr(Expr::Literal(
483 ip.clone(),
484 location.clone(),
485 Analyzed {
486 tipe: Type::IPAddress,
487 constant: None,
488 },
489 )))
490 }
491 }
492 }
493
494 fn visit_ternary_expr(
495 &self,
496 ast: &TernaryExpr<()>,
497 context: MelAnalysisContext,
498 driver: &AstVisitorDriver,
499 ) -> AstVisitorResult<MelAnalysisContext, AnalysisLocatableError> {
500 let condition = driver
501 .visit(&ast.condition, self, context.clone())?
502 .expr
503 .ok_or(AnalysisLocatableError {
504 error: AnalysisError::AssertionFailure(ContextMissingExpr(
505 "visit_ternary_expr".into(),
506 ))
507 .into(),
508 location: ast.condition.location(),
509 })?;
510 let yes =
511 driver
512 .visit(&ast.yes, self, context.clone())?
513 .expr
514 .ok_or(AnalysisLocatableError {
515 error: AnalysisError::AssertionFailure(ContextMissingExpr(
516 "visit_ternary_expr".into(),
517 ))
518 .into(),
519 location: ast.yes.location(),
520 })?;
521
522 let no =
523 driver
524 .visit(&ast.no, self, context.clone())?
525 .expr
526 .ok_or(AnalysisLocatableError {
527 error: AnalysisError::AssertionFailure(ContextMissingExpr(
528 "visit_ternary_expr".into(),
529 ))
530 .into(),
531 location: ast.no.location(),
532 })?;
533
534 let condition_type = condition.tipe();
535 let yes_type = yes.tipe();
536 let no_type = no.tipe();
537
538 if condition_type != Type::Boolean {
539 return Err(AnalysisLocatableError {
540 error: Mismatch(Type::Boolean, condition_type).into(),
541 location: ast.location.clone(),
542 });
543 }
544
545 if yes_type != no_type {
546 return Err(AnalysisLocatableError {
547 error: Mismatch(yes_type, no_type).into(),
548 location: ast.location.clone(),
549 });
550 }
551
552 Ok(context.update_expr(Expr::TernaryExpr(Arc::new(TernaryExpr {
553 condition,
554 yes,
555 no,
556 location: ast.location.clone(),
557 aug: Analyzed {
558 tipe: yes_type,
559 constant: None,
560 },
561 }))))
562 }
563
564 fn visit_member_access_expr(
565 &self,
566 ast: &ast::MemberAccessExpression<()>,
567 context: MelAnalysisContext,
568 driver: &AstVisitorDriver,
569 ) -> AstVisitorResult<MelAnalysisContext, AnalysisLocatableError> {
570 let base =
571 driver
572 .visit(&ast.base, self, context.clone())?
573 .expr
574 .ok_or(AnalysisLocatableError {
575 error: AnalysisError::AssertionFailure(ContextMissingExpr(
576 "visit_member_access_expr".into(),
577 ))
578 .into(),
579 location: ast.base.location(),
580 })?;
581
582 let struct_type = match base.tipe() {
583 Type::Struct(struct_type) => struct_type,
584 t => {
585 return Err(AnalysisLocatableError {
586 error: AnalysisError::Mismatch(Struct(types::Struct::new("TODO")), t).into(),
587 location: ast.base.location(),
588 });
589 }
590 };
591
592 let member_type =
593 struct_type
594 .type_for_field(&ast.member.identifier)
595 .ok_or(AnalysisLocatableError {
596 error: AnalysisError::UnknownField(
597 struct_type.name,
598 ast.member.identifier.clone(),
599 )
600 .into(),
601 location: ast.base.location(),
602 })?;
603
604 Ok(
605 context.update_expr(Expr::MemberAccess(Arc::new(MemberAccessExpression {
606 base,
607 oper: ast.oper.clone(),
608 member: Identifier {
609 identifier: ast.member.identifier.clone(),
610 aug: Analyzed {
611 tipe: types::Type::Nil,
612 constant: None,
613 },
614 location: ast.location.clone(),
615 },
616 location: ast.location.clone(),
617 aug: Analyzed {
618 tipe: member_type,
619 constant: None,
620 },
621 }))),
622 )
623 }
624}
625
626#[cfg(test)]
627mod type_check_tests {
628 use crate::common::TextLocation;
629 use crate::mel::MEL_USER_SET_VARIABLE_STRUCT_NAME;
630 use crate::mel::types::Type::IPAddress;
631 use crate::mel::types::{
632 BuiltinFunctionType, IntegerBuiltin, RealBuiltin, SimpleParamTypeChecker, StringBuiltin,
633 minimal_core_variable_types,
634 };
635 use crate::mel::{
636 analysis::{AnalysisError, Analyzed, MelAnalysisContext, MelTypeChecker},
637 ast::{AstVisitorDriver, BinaryExpr, Expr, FunctionCall, Identifier},
638 compiler::compile,
639 types::{
640 self,
641 Type::{self, Boolean, Function, Integer},
642 },
643 };
644 use std::assert_matches;
645
646 #[test]
647 fn test_type_check_literal() {
648 let expr = "5";
649
650 let compile_result = compile(expr);
651 let ast = compile_result.expect("Compilation error");
652
653 let driver = AstVisitorDriver {};
654 let visitor = MelTypeChecker {};
655 let context = MelAnalysisContext::default();
656 let result = driver
657 .visit(&ast, &visitor, context)
658 .expect("Could not analyze");
659
660 let result = result.expr.expect("Could not get the analyzed expression");
661 let aug = match result {
662 Expr::Literal(_, _, c) => c,
663 _ => todo!(),
664 };
665
666 assert_matches!(
667 aug,
668 Analyzed {
669 tipe: Type::Integer,
670 constant: None,
671 }
672 )
673 }
674
675 #[test]
676 fn test_type_check_invalid_regex_expr() {
677 let expr = "\"testing\" ~= /[tsting/";
678
679 let compile_result = compile(expr);
680 let _ =
681 compile_result.expect_err("Could compile expression with invalid regular expression.");
682 }
683
684 #[test]
685 fn test_type_check_binary_regex_expr() {
686 let expr = "\"testing\" ~= /testing/";
687
688 let compile_result = compile(expr);
689 let ast = compile_result.expect("Compilation error");
690
691 let driver = AstVisitorDriver {};
692 let visitor = MelTypeChecker {};
693 let context = MelAnalysisContext::default();
694 let result = driver
695 .visit(&ast, &visitor, context)
696 .expect("Could not analyze");
697
698 let result = result.expr.expect("Could not get the analyzed expression");
699 let binary_expr = match result {
700 Expr::BinaryExpr(binary_expr) => binary_expr,
701 _ => todo!(),
702 };
703
704 assert_matches!(
705 *binary_expr,
706 BinaryExpr::<Analyzed> {
707 left: _,
708 right: _,
709 op: _,
710 location: _,
711 aug: Analyzed {
712 tipe: Type::Boolean,
713 constant: _
714 }
715 }
716 )
717 }
718
719 #[test]
720 fn test_type_check_binary_regex_expr_error() {
721 let expr = "5 ~= /testing/";
722
723 let compile_result = compile(expr);
724 let ast = compile_result.expect("Compilation error");
725
726 let driver = AstVisitorDriver {};
727 let visitor = MelTypeChecker {};
728 let context = MelAnalysisContext::default();
729 let result = driver
730 .visit(&ast, &visitor, context)
731 .expect_err("Could analyze expression with type error");
732
733 assert_eq!(
734 result.location,
735 TextLocation {
736 start: 0,
737 extent: 1
738 }
739 );
740 assert_matches!(
741 *result.error,
742 AnalysisError::InvalidType(i, Integer)
743 if i == vec![Type::Regex, Type::String]
744 )
745 }
746
747 #[test]
748 fn test_type_check_binary_regex_expr_error_reverse() {
749 let expr = "\"/testing/\" ~= false";
750
751 let compile_result = compile(expr);
752 let ast = compile_result.expect("Compilation error");
753
754 let driver = AstVisitorDriver {};
755 let visitor = MelTypeChecker {};
756 let context = MelAnalysisContext::default();
757 let result = driver
758 .visit(&ast, &visitor, context)
759 .expect_err("Could analyze expression with type error");
760
761 assert_eq!(
762 result.location,
763 TextLocation {
764 start: 15,
765 extent: 5
766 }
767 );
768 assert_matches!(*result.error,
769 AnalysisError::InvalidType(i, Boolean)
770 if i == vec![Type::Regex, Type::String]
771 )
772 }
773
774 #[test]
775 fn test_type_check_binary_regex_expr_error_two_res() {
776 let expr = "/testing/ ~= /[\\W]/";
777
778 let compile_result = compile(expr);
779 let ast = compile_result.expect("Compilation error");
780
781 let driver = AstVisitorDriver {};
782 let visitor = MelTypeChecker {};
783 let context = MelAnalysisContext::default();
784 let result = driver
785 .visit(&ast, &visitor, context)
786 .expect_err("Could analyze expression with type error");
787 assert_eq!(
788 result.location,
789 TextLocation {
790 start: 0,
791 extent: 9
792 }
793 );
794 assert_matches!(
795 *result.error,
796 AnalysisError::Mismatch(Type::String, Type::Regex)
797 )
798 }
799
800 #[test]
801 fn test_type_check_binary_regex_expr_error_two_strings() {
802 let expr = "\"testing\" ~= \"hello\"";
803
804 let compile_result = compile(expr);
805 let ast = compile_result.expect("Compilation error");
806
807 let driver = AstVisitorDriver {};
808 let visitor = MelTypeChecker {};
809 let context = MelAnalysisContext::default();
810 let result = driver
811 .visit(&ast, &visitor, context)
812 .expect_err("Could analyze expression with type error");
813
814 assert_eq!(
815 result.location,
816 TextLocation {
817 start: 13,
818 extent: 7
819 }
820 );
821 assert_matches!(
822 *result.error,
823 AnalysisError::Mismatch(Type::Regex, Type::String)
824 )
825 }
826
827 #[test]
828 fn test_type_check_binary_expr() {
829 let expr = "5 + 3";
830
831 let compile_result = compile(expr);
832 let ast = compile_result.expect("Compilation error");
833
834 let driver = AstVisitorDriver {};
835 let visitor = MelTypeChecker {};
836 let context = MelAnalysisContext::default();
837 let result = driver
838 .visit(&ast, &visitor, context)
839 .expect("Could not analyze");
840
841 let result = result.expr.expect("Could not get the analyzed expression");
842 let binary_expr = match result {
843 Expr::BinaryExpr(binary_expr) => binary_expr,
844 _ => todo!(),
845 };
846
847 assert_matches!(
848 *binary_expr,
849 BinaryExpr::<Analyzed> {
850 left: _,
851 right: _,
852 op: _,
853 location: _,
854 aug: Analyzed {
855 tipe: Type::Integer,
856 constant: _
857 }
858 }
859 )
860 }
861
862 #[test]
863 fn test_type_check_binary_expr_nested() {
864 let expr = "5 + (3 * 8)";
865
866 let compile_result = compile(expr);
867 let ast = compile_result.expect("Compilation error");
868
869 let driver = AstVisitorDriver {};
870 let visitor = MelTypeChecker {};
871 let context = MelAnalysisContext::default();
872 let result = driver
873 .visit(&ast, &visitor, context)
874 .expect("Could not analyze");
875
876 let result = result.expr.expect("Could not get the analyzed expression");
877 let binary_expr = match result {
878 Expr::BinaryExpr(binary_expr) => binary_expr,
879 _ => todo!(),
880 };
881
882 assert_matches!(
883 *binary_expr,
884 BinaryExpr::<Analyzed> {
885 left: _,
886 right: _,
887 op: _,
888 location: _,
889 aug: Analyzed {
890 tipe: Type::Integer,
891 constant: _
892 }
893 }
894 );
895 }
896
897 #[test]
898 fn test_type_check_binary_expr_error() {
899 let expr = "5 + \"testing\"";
900
901 let compile_result = compile(expr);
902 let ast = compile_result.expect("Compilation error");
903
904 let driver = AstVisitorDriver {};
905 let visitor = MelTypeChecker {};
906 let context = MelAnalysisContext::default();
907 let result = driver
908 .visit(&ast, &visitor, context)
909 .expect_err("Could analyze expression with invalid types");
910
911 assert_eq!(
912 result.location,
913 TextLocation {
914 start: 4,
915 extent: 9
916 }
917 );
918 assert_matches!(
919 *result.error,
920 AnalysisError::InvalidType(x, types::Type::String)
921 if x == vec![Integer]
922 )
923 }
924
925 #[test]
926 fn test_type_check_identifier_missing() {
927 let expr = "a";
928
929 let compile_result = compile(expr);
930 let ast = compile_result.expect("Compilation error");
931
932 let driver = AstVisitorDriver {};
933 let visitor = MelTypeChecker {};
934 let context = MelAnalysisContext::default();
935 let result = driver
936 .visit(&ast, &visitor, context)
937 .expect_err("Could analyze expression with missing identifier");
938
939 assert_eq!(
940 result.location,
941 TextLocation {
942 start: 0,
943 extent: 1
944 }
945 );
946 assert_matches!(
947 *result.error,
948 AnalysisError::UnknownIdentifier(a)
949 if a == "a"
950 )
951 }
952
953 #[test]
954 fn test_type_check_identifier_in_expression() {
955 let expr = "5 + a";
956
957 let compile_result = compile(expr);
958 let ast = compile_result.expect("Compilation error");
959
960 let driver = AstVisitorDriver {};
961 let visitor = MelTypeChecker {};
962 let mut context = MelAnalysisContext::default();
963
964 context = context.update_scopes(&context.scopes.insert("a", Integer));
965
966 let result = driver
967 .visit(&ast, &visitor, context)
968 .expect("Could not analyze expression with missing identifier");
969
970 let result = result.expr.expect("Could not get the analyzed expression");
971 let binary_expr = match result {
972 Expr::BinaryExpr(binary_expr) => binary_expr,
973 _ => todo!(),
974 };
975
976 assert_matches!(
977 *binary_expr,
978 BinaryExpr::<Analyzed> {
979 left: _,
980 right: _,
981 op: _,
982 location: _,
983 aug: Analyzed {
984 tipe: Type::Integer,
985 constant: _
986 }
987 }
988 );
989 }
990
991 #[test]
992 fn test_type_check_bool_to_math_operator() {
993 let expr = "false + true";
994
995 let compile_result = compile(expr);
996 let ast = compile_result.expect("Compilation error");
997
998 let driver = AstVisitorDriver {};
999 let visitor = MelTypeChecker {};
1000 let mut context = MelAnalysisContext::default();
1001
1002 context = context.update_scopes(&context.scopes.insert("a", Integer));
1003
1004 let result = driver
1005 .visit(&ast, &visitor, context)
1006 .expect_err("Could analyze expression with type error");
1007
1008 assert_eq!(
1009 result.location,
1010 TextLocation {
1011 start: 0,
1012 extent: 5
1013 }
1014 );
1015 assert_matches!(
1016 *result.error,
1017 AnalysisError::InvalidType(a, Boolean)
1018 if a == vec![Integer]
1019 )
1020 }
1021
1022 #[test]
1023 fn test_type_check_bool_to_math_operator2() {
1024 let expr = "5 + true";
1025
1026 let compile_result = compile(expr);
1027 let ast = compile_result.expect("Compilation error");
1028
1029 let driver = AstVisitorDriver {};
1030 let visitor = MelTypeChecker {};
1031 let mut context = MelAnalysisContext::default();
1032
1033 context = context.update_scopes(&context.scopes.insert("a", Integer));
1034
1035 let result = driver
1036 .visit(&ast, &visitor, context)
1037 .expect_err("Could analyze expression with type error");
1038
1039 assert_eq!(
1040 result.location,
1041 TextLocation {
1042 start: 4,
1043 extent: 4
1044 }
1045 );
1046 assert_matches!(
1047 *result.error,
1048 AnalysisError::InvalidType(a, Boolean)
1049 if a == vec![Integer]
1050 )
1051 }
1052
1053 #[test]
1054 fn test_type_check_identifier() {
1055 let expr = "a";
1056
1057 let compile_result = compile(expr);
1058 let ast = compile_result.expect("Compilation error");
1059
1060 let driver = AstVisitorDriver {};
1061 let visitor = MelTypeChecker {};
1062 let mut context = MelAnalysisContext::default();
1063
1064 context = context.update_scopes(&context.scopes.insert("a", Integer));
1065
1066 let result = driver
1067 .visit(&ast, &visitor, context)
1068 .expect("Could not analyze expression with missing identifier");
1069
1070 let result = result.expr.expect("Could not get the analyzed expression");
1071 let binary_expr = match result {
1072 Expr::Identifier(id) => id,
1073 _ => todo!(),
1074 };
1075
1076 assert_matches!(
1077 *binary_expr,
1078 Identifier::<Analyzed> {
1079 identifier: _,
1080 location: _,
1081 aug: Analyzed {
1082 tipe: Type::Integer,
1083 constant: _
1084 }
1085 }
1086 );
1087 }
1088
1089 #[test]
1090 fn test_type_check_function_call_integer_error() {
1091 let expr = "integer(0, 1)";
1092
1093 let compile_result = compile(expr);
1094 let ast = compile_result.expect("Compilation error");
1095
1096 let driver = AstVisitorDriver {};
1097 let visitor = MelTypeChecker {};
1098 let mut context = MelAnalysisContext::default();
1099
1100 let b = IntegerBuiltin {};
1101
1102 context = context.update_scopes(&context.scopes.insert(
1103 &b.name(),
1104 Function(
1105 b.name(),
1106 b.return_type_calculator(),
1107 b.params_type_checker(),
1108 ),
1109 ));
1110
1111 let result = driver.visit(&ast, &visitor, context).expect_err(
1112 "Could analyze call of builtin function with incorrect number of parameters",
1113 );
1114
1115 assert_eq!(
1116 result.location,
1117 TextLocation {
1118 start: 7,
1119 extent: 6
1120 }
1121 );
1122 assert_matches!(*result.error, AnalysisError::Miscount(1, 2))
1123 }
1124
1125 #[test]
1126 fn test_type_check_function_call_real_error() {
1127 let expr = "real(0, 1)";
1128
1129 let compile_result = compile(expr);
1130 let ast = compile_result.expect("Compilation error");
1131
1132 let driver = AstVisitorDriver {};
1133 let visitor = MelTypeChecker {};
1134 let mut context = MelAnalysisContext::default();
1135
1136 let b = RealBuiltin {};
1137
1138 context = context.update_scopes(&context.scopes.insert(
1139 &b.name(),
1140 Function(
1141 b.name(),
1142 b.return_type_calculator(),
1143 b.params_type_checker(),
1144 ),
1145 ));
1146
1147 let result = driver.visit(&ast, &visitor, context).expect_err(
1148 "Could analyze call of builtin function with incorrect number of parameters",
1149 );
1150
1151 assert_eq!(
1152 result.location,
1153 TextLocation {
1154 start: 4,
1155 extent: 6
1156 }
1157 );
1158 assert_matches!(*result.error, AnalysisError::Miscount(1, 2))
1159 }
1160
1161 #[test]
1162 fn test_type_check_function_call_string_error() {
1163 let expr = "string(0, 1)";
1164
1165 let compile_result = compile(expr);
1166 let ast = compile_result.expect("Compilation error");
1167
1168 let driver = AstVisitorDriver {};
1169 let visitor = MelTypeChecker {};
1170 let mut context = MelAnalysisContext::default();
1171
1172 let b = StringBuiltin {};
1173
1174 context = context.update_scopes(&context.scopes.insert(
1175 &b.name(),
1176 Function(
1177 b.name(),
1178 b.return_type_calculator(),
1179 b.params_type_checker(),
1180 ),
1181 ));
1182
1183 let result = driver.visit(&ast, &visitor, context).expect_err(
1184 "Could analyze call of builtin function with incorrect number of parameters",
1185 );
1186
1187 assert_eq!(
1188 result.location,
1189 TextLocation {
1190 start: 6,
1191 extent: 6
1192 }
1193 );
1194 assert_matches!(*result.error, AnalysisError::Miscount(1, 2))
1195 }
1196
1197 #[test]
1198 fn test_type_check_function_call() {
1199 let expr = "testing(5)";
1200
1201 let compile_result = compile(expr);
1202 let ast = compile_result.expect("Compilation error");
1203
1204 let driver = AstVisitorDriver {};
1205 let visitor = MelTypeChecker {};
1206 let mut context = MelAnalysisContext::default();
1207
1208 context = context.update_scopes(&context.scopes.insert(
1209 "testing",
1210 Function(
1211 "testing".to_string(),
1212 || Type::Integer,
1213 || {
1214 Box::new(SimpleParamTypeChecker {
1215 p: types::ParameterTypesList {
1216 params: vec![Type::Integer],
1217 },
1218 })
1219 },
1220 ),
1221 ));
1222
1223 let result = driver
1224 .visit(&ast, &visitor, context)
1225 .expect("Could not analyze expression with missing identifier");
1226
1227 let result = result.expr.expect("Could not get the analyzed expression");
1228 let binary_expr = match result {
1229 Expr::FunctionCall(id) => id,
1230 _ => todo!(),
1231 };
1232
1233 assert_matches!(
1234 *binary_expr,
1235 FunctionCall::<Analyzed> {
1236 callee: _,
1237 location: _,
1238 arguments: _,
1239 aug: Analyzed {
1240 tipe: Integer,
1241 constant: _
1242 }
1243 }
1244 );
1245 }
1246
1247 #[test]
1248 fn test_type_check_function_call_error() {
1249 let expr = "use_me(5)";
1250
1251 let compile_result = compile(expr);
1252 let ast = compile_result.expect("Compilation error");
1253
1254 let driver = AstVisitorDriver {};
1255 let visitor = MelTypeChecker {};
1256 let mut context = MelAnalysisContext::default();
1257
1258 context = context.update_scopes(&context.scopes.insert(
1259 "use",
1260 Function(
1261 "testing".to_string(),
1262 || Type::Integer,
1263 || {
1264 Box::new(SimpleParamTypeChecker {
1265 p: types::ParameterTypesList {
1266 params: vec![Type::Integer],
1267 },
1268 })
1269 },
1270 ),
1271 ));
1272
1273 let result = driver
1274 .visit(&ast, &visitor, context)
1275 .expect_err("Could analyze expression with missing identifier");
1276
1277 assert_matches!(
1278 *result.error, AnalysisError::UnknownIdentifier(a)
1279 if a == "use_me"
1280 )
1281 }
1282
1283 #[test]
1284 fn test_type_check_function_call_error_wrong_argument_count() {
1285 let expr = "use(5)";
1286
1287 let compile_result = compile(expr);
1288 let ast = compile_result.expect("Compilation error");
1289
1290 let driver = AstVisitorDriver {};
1291 let visitor = MelTypeChecker {};
1292 let mut context = MelAnalysisContext::default();
1293
1294 context = context.update_scopes(&context.scopes.insert(
1295 "use",
1296 Function(
1297 "use".to_string(),
1298 || Type::Integer,
1299 || {
1300 Box::new(SimpleParamTypeChecker {
1301 p: types::ParameterTypesList {
1302 params: vec![Type::Integer, Type::String],
1303 },
1304 })
1305 },
1306 ),
1307 ));
1308
1309 let result = driver
1310 .visit(&ast, &visitor, context)
1311 .expect_err("Could analyze expression with miscounted params/args");
1312
1313 assert_matches!(*result.error, AnalysisError::Miscount(2, 1))
1314 }
1315
1316 #[test]
1317 fn test_type_check_function_call_in_expression() {
1318 let expr = "use_me(5)";
1319
1320 let compile_result = compile(expr);
1321 let ast = compile_result.expect("Compilation error");
1322
1323 let driver = AstVisitorDriver {};
1324 let visitor = MelTypeChecker {};
1325 let mut context = MelAnalysisContext::default();
1326
1327 context = context.update_scopes(&context.scopes.insert(
1328 "use_me",
1329 Function(
1330 "use_me".to_string(),
1331 || Type::Integer,
1332 || {
1333 Box::new(SimpleParamTypeChecker {
1334 p: types::ParameterTypesList {
1335 params: vec![Type::Integer],
1336 },
1337 })
1338 },
1339 ),
1340 ));
1341
1342 let result = driver
1343 .visit(&ast, &visitor, context)
1344 .expect("Could not analyze expression");
1345
1346 let result = result.expr.expect("Could not get the analyzed expression");
1347 let binary_expr = match result {
1348 Expr::FunctionCall(id) => id,
1349 _ => todo!(),
1350 };
1351
1352 assert_matches!(
1353 *binary_expr,
1354 FunctionCall::<Analyzed> {
1355 callee: _,
1356 arguments: _,
1357 location: _,
1358 aug: Analyzed {
1359 tipe: Integer,
1360 constant: _
1361 }
1362 }
1363 );
1364 }
1365
1366 #[test]
1367 fn test_type_check_function_call_in_expression2() {
1368 let expr = "use_me(5, \"testing\")";
1369
1370 let compile_result = compile(expr);
1371 let ast = compile_result.expect("Compilation error");
1372
1373 let driver = AstVisitorDriver {};
1374 let visitor = MelTypeChecker {};
1375 let mut context = MelAnalysisContext::default();
1376
1377 context = context.update_scopes(&context.scopes.insert(
1378 "use_me",
1379 Function(
1380 "use_me".to_string(),
1381 || Type::Integer,
1382 || {
1383 Box::new(SimpleParamTypeChecker {
1384 p: types::ParameterTypesList {
1385 params: vec![Type::Integer, Type::String],
1386 },
1387 })
1388 },
1389 ),
1390 ));
1391
1392 let result = driver
1393 .visit(&ast, &visitor, context)
1394 .expect("Could not analyze expression");
1395
1396 let result = result.expr.expect("Could not get the analyzed expression");
1397 let binary_expr = match result {
1398 Expr::FunctionCall(id) => id,
1399 _ => todo!(),
1400 };
1401
1402 assert_matches!(
1403 *binary_expr,
1404 FunctionCall::<Analyzed> {
1405 callee: _,
1406 arguments: _,
1407 location: _,
1408 aug: Analyzed {
1409 tipe: Integer,
1410 constant: _
1411 }
1412 }
1413 );
1414 }
1415
1416 #[test]
1417 fn test_type_check_function_call_in_expression3() {
1418 let expr = "use_me(5, \"testing\")";
1419
1420 let compile_result = compile(expr);
1421 let ast = compile_result.expect("Compilation error");
1422
1423 let driver = AstVisitorDriver {};
1424 let visitor = MelTypeChecker {};
1425 let mut context = MelAnalysisContext::default();
1426
1427 context = context.update_scopes(&context.scopes.insert(
1428 "use_me",
1429 Function(
1430 "use_me".to_string(),
1431 || Type::Integer,
1432 || {
1433 Box::new(SimpleParamTypeChecker {
1434 p: types::ParameterTypesList {
1435 params: vec![Type::Integer, Type::Integer],
1436 },
1437 })
1438 },
1439 ),
1440 ));
1441
1442 let result = driver
1443 .visit(&ast, &visitor, context)
1444 .expect_err("Could analyze expression with type error");
1445
1446 assert_eq!(
1447 result.location,
1448 TextLocation {
1449 start: 6,
1450 extent: 14
1451 }
1452 );
1453 assert_matches!(
1454 *result.error,
1455 AnalysisError::Mismatch(Integer, Type::String),
1456 )
1457 }
1458
1459 #[test]
1460 fn test_type_check_function_call_in_binary_expression() {
1461 let expr = "use_me(5) + 10";
1462
1463 let compile_result = compile(expr);
1464 let ast = compile_result.expect("Compilation error");
1465
1466 let driver = AstVisitorDriver {};
1467 let visitor = MelTypeChecker {};
1468 let mut context = MelAnalysisContext::default();
1469
1470 context = context.update_scopes(&context.scopes.insert(
1471 "use_me",
1472 Function(
1473 "use_me".to_string(),
1474 || Type::Integer,
1475 || {
1476 Box::new(SimpleParamTypeChecker {
1477 p: types::ParameterTypesList {
1478 params: vec![Type::Integer],
1479 },
1480 })
1481 },
1482 ),
1483 ));
1484
1485 let result = driver
1486 .visit(&ast, &visitor, context)
1487 .expect("Could not analyze expression");
1488
1489 let result = result.expr.expect("Could not get the analyzed expression");
1490 let binary_expr = match result {
1491 Expr::BinaryExpr(bin) => bin,
1492 _ => todo!(),
1493 };
1494
1495 assert_matches!(
1496 *binary_expr,
1497 BinaryExpr::<Analyzed> {
1498 left: _,
1499 right: _,
1500 op: _,
1501 location: _,
1502
1503 aug: Analyzed {
1504 tipe: Integer,
1505 constant: _
1506 }
1507 }
1508 );
1509 }
1510
1511 #[test]
1512 fn test_type_check_function_call_in_expression_mismatch() {
1513 let expr = "5 + (use_me(5) + 10)";
1514
1515 let compile_result = compile(expr);
1516 let ast = compile_result.expect("Compilation error");
1517
1518 let driver = AstVisitorDriver {};
1519 let visitor = MelTypeChecker {};
1520 let mut context = MelAnalysisContext::default();
1521
1522 context = context.update_scopes(&context.scopes.insert(
1523 "use_me",
1524 Function(
1525 "use_me".to_string(),
1526 || Type::Boolean,
1527 || {
1528 Box::new(SimpleParamTypeChecker {
1529 p: types::ParameterTypesList {
1530 params: vec![Type::Integer],
1531 },
1532 })
1533 },
1534 ),
1535 ));
1536
1537 let result = driver
1538 .visit(&ast, &visitor, context)
1539 .expect_err("Could analyze expression with type error");
1540
1541 assert_eq!(
1542 result.location,
1543 TextLocation {
1544 start: 5,
1545 extent: 9
1546 }
1547 );
1548 assert_matches!(
1549 *result.error, AnalysisError::InvalidType(i, Boolean)
1550 if i == vec![Integer]
1551 )
1552 }
1553
1554 #[test]
1555 fn test_type_check_comparison_expr() {
1556 let expr = "5 < 4";
1557
1558 let compile_result = compile(expr);
1559 let ast = compile_result.expect("Compilation error");
1560
1561 let driver = AstVisitorDriver {};
1562 let visitor = MelTypeChecker {};
1563 let context = MelAnalysisContext::default();
1564 let result = driver
1565 .visit(&ast, &visitor, context)
1566 .expect("Could not analyze");
1567
1568 let result = result.expr.expect("Could not get the analyzed expression");
1569 let binary_expr = match result {
1570 Expr::BinaryExpr(id) => id,
1571 _ => todo!(),
1572 };
1573
1574 assert_matches!(
1575 *binary_expr,
1576 BinaryExpr::<Analyzed> {
1577 left: _,
1578 right: _,
1579 op: _,
1580 location: _,
1581 aug: Analyzed {
1582 tipe: Type::Boolean,
1583 constant: None,
1584 }
1585 }
1586 );
1587 }
1588
1589 #[test]
1590 fn test_type_check_comparison_expr_mismatch() {
1591 let expr = "(5 < 4) + 5";
1592
1593 let compile_result = compile(expr);
1594 let ast = compile_result.expect("Compilation error");
1595
1596 let driver = AstVisitorDriver {};
1597 let visitor = MelTypeChecker {};
1598 let context = MelAnalysisContext::default();
1599
1600 let result = driver
1601 .visit(&ast, &visitor, context)
1602 .expect_err("Could analyze expression with type error");
1603
1604 assert_eq!(
1605 result.location,
1606 TextLocation {
1607 start: 1,
1608 extent: 5
1609 }
1610 );
1611 assert_matches!(
1612 *result.error,
1613 AnalysisError::InvalidType(i, Boolean) if i == vec![Integer]
1614 )
1615 }
1616
1617 #[test]
1618 fn test_type_check_comparison_expr_ipliteral() {
1619 let expr = "8.8.8.8 ipmatch 10.1.9.2";
1620
1621 let compile_result = compile(expr);
1622 let ast = compile_result.expect("Compilation error");
1623
1624 let driver = AstVisitorDriver {};
1625 let visitor = MelTypeChecker {};
1626 let context = MelAnalysisContext::default();
1627
1628 let result = driver
1629 .visit(&ast, &visitor, context)
1630 .expect("Could not analyze");
1631
1632 let result = result.expr.expect("Could not get the analyzed expression");
1633 let binary_expr = match result {
1634 Expr::BinaryExpr(id) => id,
1635 _ => todo!(),
1636 };
1637
1638 assert_matches!(
1639 *binary_expr,
1640 BinaryExpr::<Analyzed> {
1641 left: _,
1642 right: _,
1643 op: _,
1644 location: _,
1645 aug: Analyzed {
1646 tipe: Type::Boolean,
1647 constant: None,
1648 }
1649 }
1650 );
1651 }
1652
1653 #[test]
1654 fn test_type_check_comparison_expr_ipliteral2() {
1655 let expr = "2001:0db8:85a3:0000:0000:8a2e:0370:7334 ipmatch 2001:0db8:85a3:0000:0000:8a2e:0370:7334";
1656
1657 let compile_result = compile(expr);
1658 let ast = compile_result.expect("Compilation error");
1659
1660 let driver = AstVisitorDriver {};
1661 let visitor = MelTypeChecker {};
1662 let context = MelAnalysisContext::default();
1663
1664 let result = driver
1665 .visit(&ast, &visitor, context)
1666 .expect("Could not analyze");
1667
1668 let result = result.expr.expect("Could not get the analyzed expression");
1669 let binary_expr = match result {
1670 Expr::BinaryExpr(id) => id,
1671 _ => todo!(),
1672 };
1673
1674 assert_matches!(
1675 *binary_expr,
1676 BinaryExpr::<Analyzed> {
1677 left: _,
1678 right: _,
1679 op: _,
1680 location: _,
1681 aug: Analyzed {
1682 tipe: Type::Boolean,
1683 constant: None,
1684 }
1685 }
1686 );
1687 }
1688
1689 #[test]
1690 fn test_type_check_comparison_expr_ipliteral3() {
1691 let expr = "2001:0db8:85a3:0000:0000:8a2e:0370:7334 ipmatch 8.8.8.8";
1692
1693 let compile_result = compile(expr);
1694 let ast = compile_result.expect("Compilation error");
1695
1696 let driver = AstVisitorDriver {};
1697 let visitor = MelTypeChecker {};
1698 let context = MelAnalysisContext::default();
1699
1700 let result = driver
1701 .visit(&ast, &visitor, context)
1702 .expect("Could not analyze");
1703
1704 let result = result.expr.expect("Could not get the analyzed expression");
1705 let binary_expr = match result {
1706 Expr::BinaryExpr(id) => id,
1707 _ => todo!(),
1708 };
1709
1710 assert_matches!(
1711 *binary_expr,
1712 BinaryExpr::<Analyzed> {
1713 left: _,
1714 right: _,
1715 op: _,
1716 location: _,
1717 aug: Analyzed {
1718 tipe: Type::Boolean,
1719 constant: None,
1720 }
1721 }
1722 );
1723 }
1724
1725 #[test]
1726 fn test_type_check_comparison_expr_ipliteral4() {
1727 let expr = "2001:0db8:85a3:0000:0000:8a2e:0370:7334 ipmatch a";
1728
1729 let compile_result = compile(expr);
1730 let ast = compile_result.expect("Compilation error");
1731
1732 let driver = AstVisitorDriver {};
1733 let visitor = MelTypeChecker {};
1734 let mut context = MelAnalysisContext::default();
1735
1736 context = context.update_scopes(&context.scopes.insert("a", IPAddress));
1737
1738 let result = driver
1739 .visit(&ast, &visitor, context)
1740 .expect("Could not analyze");
1741
1742 let result = result.expr.expect("Could not get the analyzed expression");
1743 let binary_expr = match result {
1744 Expr::BinaryExpr(id) => id,
1745 _ => todo!(),
1746 };
1747
1748 assert_matches!(
1749 *binary_expr,
1750 BinaryExpr::<Analyzed> {
1751 left: _,
1752 right: _,
1753 op: _,
1754 location: _,
1755 aug: Analyzed {
1756 tipe: Type::Boolean,
1757 constant: None,
1758 }
1759 }
1760 );
1761 }
1762
1763 #[test]
1764 fn test_type_check_comparison_expr_ip() {
1765 let expr = "a ipmatch b";
1766
1767 let compile_result = compile(expr);
1768 let ast = compile_result.expect("Compilation error");
1769
1770 let driver = AstVisitorDriver {};
1771 let visitor = MelTypeChecker {};
1772 let mut context = MelAnalysisContext::default();
1773
1774 context = context.update_scopes(&context.scopes.insert("a", IPAddress));
1775
1776 context = context.update_scopes(&context.scopes.insert("b", IPAddress));
1777
1778 let result = driver
1779 .visit(&ast, &visitor, context)
1780 .expect("Could not analyze");
1781
1782 let result = result.expr.expect("Could not get the analyzed expression");
1783 let binary_expr = match result {
1784 Expr::BinaryExpr(id) => id,
1785 _ => todo!(),
1786 };
1787
1788 assert_matches!(
1789 *binary_expr,
1790 BinaryExpr::<Analyzed> {
1791 left: _,
1792 right: _,
1793 op: _,
1794 location: _,
1795 aug: Analyzed {
1796 tipe: Type::Boolean,
1797 constant: None,
1798 }
1799 }
1800 );
1801 }
1802
1803 #[test]
1804 fn test_type_check_comparison_expr_ipliteral_mismatch() {
1805 let expr = "2001:0db8:85a3:0000:0000:8a2e:0370:7334 ipmatch 8";
1806
1807 let compile_result = compile(expr);
1808 let ast = compile_result.expect("Compilation error");
1809
1810 let driver = AstVisitorDriver {};
1811 let visitor = MelTypeChecker {};
1812 let context = MelAnalysisContext::default();
1813
1814 let result = driver
1815 .visit(&ast, &visitor, context)
1816 .expect_err("Could analyze expression with type error");
1817
1818 assert_eq!(
1819 result.location,
1820 TextLocation {
1821 start: 48,
1822 extent: 1
1823 }
1824 );
1825 assert_matches!(
1826 *result.error,
1827 AnalysisError::InvalidType(i, Integer)
1828 if i == vec![IPAddress]
1829 )
1830 }
1831
1832 #[test]
1833 fn test_type_check_comparison_expr_ipliteral_mismatch2() {
1834 let expr = "2001:0db8:85a3:0000:0000:8a2e:0370:7334 ipmatch a";
1835
1836 let compile_result = compile(expr);
1837 let ast = compile_result.expect("Compilation error");
1838
1839 let driver = AstVisitorDriver {};
1840 let visitor = MelTypeChecker {};
1841 let mut context = MelAnalysisContext::default();
1842
1843 context = context.update_scopes(&context.scopes.insert("a", Type::String));
1844
1845 let result = driver
1846 .visit(&ast, &visitor, context)
1847 .expect_err("Could analyze expression with type error");
1848
1849 assert_eq!(
1850 result.location,
1851 TextLocation {
1852 start: 48,
1853 extent: 1
1854 }
1855 );
1856 assert_matches!(
1857 *result.error,
1858 AnalysisError::InvalidType(i, Type::String)
1859 if i == vec![IPAddress]
1860 )
1861 }
1862
1863 #[test]
1864 fn test_type_check_comparison_expr_ip_mismatch() {
1865 let expr = "a ipmatch b";
1866
1867 let compile_result = compile(expr);
1868 let ast = compile_result.expect("Compilation error");
1869
1870 let driver = AstVisitorDriver {};
1871 let visitor = MelTypeChecker {};
1872 let mut context = MelAnalysisContext::default();
1873
1874 context = context.update_scopes(&context.scopes.insert("a", IPAddress));
1875
1876 context = context.update_scopes(&context.scopes.insert("b", Type::String));
1877
1878 let result = driver
1879 .visit(&ast, &visitor, context)
1880 .expect_err("Could analyze expression with type error");
1881
1882 assert_eq!(
1883 result.location,
1884 TextLocation {
1885 start: 10,
1886 extent: 1
1887 }
1888 );
1889 assert_matches!(
1890 *result.error,
1891 AnalysisError::InvalidType(i, Type::String)
1892 if i == vec![IPAddress]
1893 )
1894 }
1895
1896 #[test]
1897 fn test_basic_scope_of_types_for_var_struct() {
1898 let sc = minimal_core_variable_types();
1899 let var = sc
1900 .lookup(MEL_USER_SET_VARIABLE_STRUCT_NAME)
1901 .expect("Could not get type of var from scope that should contain it.");
1902 assert_matches!(var, Type::Struct(s) if s.fields().count() == 0);
1903 }
1904
1905 #[test]
1906 fn test_basic_scope_of_types_for_var_struct_updated() {
1907 let sc = minimal_core_variable_types();
1908 let var = sc
1909 .lookup(MEL_USER_SET_VARIABLE_STRUCT_NAME)
1910 .expect("Could not get type of var from scope that should contain it.");
1911
1912 let new_var = match var {
1913 Type::Struct(mut s) => {
1914 s.insert_field("new_user_variable", Type::String);
1915 s
1916 }
1917 _ => panic!(),
1918 };
1919
1920 let sc = sc.insert(MEL_USER_SET_VARIABLE_STRUCT_NAME, Type::Struct(new_var));
1921 let var = sc
1922 .lookup(MEL_USER_SET_VARIABLE_STRUCT_NAME)
1923 .expect("Could not get type of var from scope that should contain it.");
1924
1925 assert_matches!(var, Type::Struct(s) if s.fields().count() == 1);
1926 }
1927
1928 #[test]
1929 fn test_basic_scope_of_types_for_req_struct() {
1930 let sc = minimal_core_variable_types();
1931 let req = sc
1932 .lookup("req")
1933 .expect("Could not get type of req from scope that should contain it.");
1934 assert_matches!(req, Type::Struct(_));
1935 }
1936}
1937
1938pub struct ConstEvaluator {}
1939
1940impl ConstEvaluator {
1941 pub fn const_evaluatable(op: &ast::BinaryInfixOperator) -> bool {
1942 matches!(op, ast::BinaryInfixOperator::Comparison(IP))
1943 || !matches!(op, ast::BinaryInfixOperator::Comparison(_))
1944 }
1945
1946 fn evaluate_binary_expr(
1947 left: &CompiledConstant,
1948 left_type: Type,
1949 right: &CompiledConstant,
1950 right_type: Type,
1951 op: ast::BinaryInfixOperator,
1952 ) -> Result<CompiledConstant, Box<AnalysisError>> {
1953 match op {
1954 ast::BinaryInfixOperator::Comparison(IP) => {
1955 let left = if let CompiledConstant::IPAddress(l) = left {
1956 l
1957 } else {
1958 return Err(AnalysisError::Mismatch(Type::IPAddress, left_type).into());
1959 };
1960 let right = if let CompiledConstant::IPAddress(r) = right {
1961 r
1962 } else {
1963 return Err(AnalysisError::Mismatch(Type::IPAddress, right_type).into());
1964 };
1965 Ok(CompiledConstant::Boolean(left == right))
1966 }
1967 ast::BinaryInfixOperator::Comparison(_) => {
1968 Err(AnalysisError::OptimizationNotSupported("Comparison operator".into()).into())
1969 }
1970 ast::BinaryInfixOperator::Logic(logic_operator) => {
1971 let left = if let CompiledConstant::Boolean(l) = left {
1972 l
1973 } else {
1974 return Err(AnalysisError::Mismatch(Type::Boolean, left_type).into());
1975 };
1976 let right = if let CompiledConstant::Boolean(r) = right {
1977 r
1978 } else {
1979 return Err(AnalysisError::Mismatch(Type::Boolean, right_type).into());
1980 };
1981 match logic_operator {
1982 ast::LogicOperator::And => Ok(CompiledConstant::Boolean(*left && *right)),
1983 ast::LogicOperator::Or => Ok(CompiledConstant::Boolean(*left || *right)),
1984 }
1985 }
1986 ast::BinaryInfixOperator::Math(math_operator) => {
1987 let left = if let CompiledConstant::Integer(l) = left {
1988 l
1989 } else {
1990 return Err(AnalysisError::Mismatch(Type::Integer, left_type).into());
1991 };
1992 let right = if let CompiledConstant::Integer(r) = right {
1993 r
1994 } else {
1995 return Err(AnalysisError::Mismatch(Type::Integer, right_type).into());
1996 };
1997 match math_operator {
1998 ast::MathOperator::Plus => Ok(CompiledConstant::Integer(left + right)),
1999 ast::MathOperator::Minus => Ok(CompiledConstant::Integer(left - right)),
2000 ast::MathOperator::Multiply => Ok(CompiledConstant::Integer(left * right)),
2001 ast::MathOperator::Divide => Ok(CompiledConstant::Integer(left / right)),
2002 ast::MathOperator::Modulo => Ok(CompiledConstant::Integer(left % right)),
2003 }
2004 }
2005 ast::BinaryInfixOperator::Concat(_) => {
2006 let left = if let CompiledConstant::String(l) = left {
2007 l
2008 } else {
2009 return Err(AnalysisError::Mismatch(Type::String, left_type).into());
2010 };
2011 let right = if let CompiledConstant::String(r) = right {
2012 r
2013 } else {
2014 return Err(AnalysisError::Mismatch(Type::String, right_type).into());
2015 };
2016 Ok(CompiledConstant::String(left.to_owned() + right))
2017 }
2018 ast::BinaryInfixOperator::MemberAccess(c) => Err(AnalysisError::AssertionFailure(
2019 AnalysisAssertions::InvalidOperator(
2020 c.to_string(),
2021 "evaluate_binary_expr".to_string(),
2022 ),
2023 )
2024 .into()),
2025 }
2026 }
2027}
2028pub struct MelOptimizer {}
2029
2030impl AstVisitor<MelAnalysisContext, (), AnalysisLocatableError> for MelOptimizer {
2031 fn visit_function_call(
2032 &self,
2033 _ast: &FunctionCall<()>,
2034 context: MelAnalysisContext,
2035 _driver: &AstVisitorDriver,
2036 ) -> AstVisitorResult<MelAnalysisContext, AnalysisLocatableError> {
2037 Ok(context.clone())
2038 }
2039
2040 fn visit_identifier(
2041 &self,
2042 _ast: &Identifier<()>,
2043 context: MelAnalysisContext,
2044 _driver: &AstVisitorDriver,
2045 ) -> AstVisitorResult<MelAnalysisContext, AnalysisLocatableError> {
2046 Ok(context.clone())
2047 }
2048
2049 fn visit_argument_list(
2050 &self,
2051 _ast: &ArgumentList<()>,
2052 context: MelAnalysisContext,
2053 _driver: &AstVisitorDriver,
2054 ) -> AstVisitorResult<MelAnalysisContext, AnalysisLocatableError> {
2055 Ok(context.clone())
2056 }
2057
2058 fn visit_argument(
2059 &self,
2060 _: &Argument<()>,
2061 context: MelAnalysisContext,
2062 _: &AstVisitorDriver,
2063 ) -> AstVisitorResult<MelAnalysisContext, AnalysisLocatableError> {
2064 Ok(context.clone())
2065 }
2066
2067 fn visit_binary_expr(
2068 &self,
2069 ast: &BinaryExpr<()>,
2070 context: MelAnalysisContext,
2071 driver: &AstVisitorDriver,
2072 ) -> AstVisitorResult<MelAnalysisContext, AnalysisLocatableError> {
2073 let expr = context.expr.as_ref().ok_or(AnalysisLocatableError {
2074 error: AnalysisError::PreconditionFailure(AnalysisPreconditions::ContextMissingExpr(
2075 "visit_binary_expr".into(),
2076 ))
2077 .into(),
2078 location: ast.location.clone(),
2079 })?;
2080
2081 let analyzed_binary_expr = match expr {
2082 Expr::BinaryExpr(binary_expr) => (**binary_expr).clone(),
2083 _ => todo!(),
2084 };
2085
2086 let left = driver
2087 .visit(
2088 &ast.left,
2089 self,
2090 context.update_expr(analyzed_binary_expr.left),
2091 )?
2092 .expr
2093 .ok_or(AnalysisLocatableError {
2094 error: AnalysisError::AssertionFailure(ContextMissingExpr(
2095 "visit_binary_expr".into(),
2096 ))
2097 .into(),
2098 location: ast.location.clone(),
2099 })?;
2100
2101 let right = driver
2102 .visit(
2103 &ast.right,
2104 self,
2105 context.update_expr(analyzed_binary_expr.right),
2106 )?
2107 .expr
2108 .ok_or(AnalysisLocatableError {
2109 error: AnalysisError::AssertionFailure(ContextMissingExpr(
2110 "visit_binary_expr".into(),
2111 ))
2112 .into(),
2113 location: ast.location.clone(),
2114 })?;
2115
2116 let constant = match (left.constant(), right.constant()) {
2117 (Some(cl), Some(cr)) => {
2118 if ConstEvaluator::const_evaluatable(&ast.op) {
2119 Some(
2120 ConstEvaluator::evaluate_binary_expr(
2121 &cl,
2122 left.tipe(),
2123 &cr,
2124 right.tipe(),
2125 ast.op.clone(),
2126 )
2127 .map_err(|e| AnalysisLocatableError {
2128 error: e,
2129 location: ast.location.clone(),
2130 })?,
2131 )
2132 } else {
2133 None
2134 }
2135 }
2136 _ => None,
2137 };
2138
2139 Ok(
2140 context.update_expr(Expr::BinaryExpr(Arc::new(ast::BinaryExpr {
2141 left,
2142 right,
2143 op: ast.op.clone(),
2144 location: ast.location.clone(),
2145 aug: Analyzed {
2146 tipe: expr.tipe(),
2147 constant,
2148 },
2149 }))),
2150 )
2151 }
2152
2153 fn visit_literal(
2154 &self,
2155 _ast: (&ast::Literal, &TextLocation, &()),
2156 context: MelAnalysisContext,
2157 _driver: &AstVisitorDriver,
2158 ) -> AstVisitorResult<MelAnalysisContext, AnalysisLocatableError> {
2159 match _ast {
2160 (b @ ast::Literal::Boolean(v), location, _) => Ok(context.update_expr(Expr::Literal(
2161 b.clone(),
2162 location.clone(),
2163 Analyzed {
2164 tipe: Type::Boolean,
2165 constant: Some(CompiledConstant::Boolean(*v == BooleanLiteral::True)),
2166 },
2167 ))),
2168 (n @ ast::Literal::Number(NumberLiteral { literal: v }), location, _) => Ok(context
2169 .update_expr(Expr::Literal(
2170 n.clone(),
2171 location.clone(),
2172 Analyzed {
2173 tipe: Type::Integer,
2174 constant: Some(CompiledConstant::Integer(*v as i64)),
2175 },
2176 ))),
2177 (s @ ast::Literal::String(StringLiteral { literal: v }), location, _) => Ok(context
2178 .update_expr(Expr::Literal(
2179 s.clone(),
2180 location.clone(),
2181 Analyzed {
2182 tipe: Type::String,
2183 constant: Some(CompiledConstant::String(v.clone())),
2184 },
2185 ))),
2186 (r @ ast::Literal::Regex(_), location, _) => Ok(context.update_expr(Expr::Literal(
2187 r.clone(),
2188 location.clone(),
2189 Analyzed {
2190 tipe: Type::Regex,
2191 constant: None,
2192 },
2193 ))),
2194 (ip @ ast::Literal::IPAddress(IPAddressLiteral { literal: ipl }), location, _) => {
2195 Ok(context.update_expr(Expr::Literal(
2196 ip.clone(),
2197 location.clone(),
2198 Analyzed {
2199 tipe: Type::IPAddress,
2200 constant: Some(CompiledConstant::IPAddress(*ipl)),
2201 },
2202 )))
2203 }
2204 }
2205 }
2206
2207 fn visit_ternary_expr(
2208 &self,
2209 ast: &ast::TernaryExpr<()>,
2210 context: MelAnalysisContext,
2211 driver: &AstVisitorDriver,
2212 ) -> AstVisitorResult<MelAnalysisContext, AnalysisLocatableError> {
2213 let expr = context.expr.as_ref().ok_or(AnalysisLocatableError {
2214 error: AnalysisError::PreconditionFailure(AnalysisPreconditions::ContextMissingExpr(
2215 "visit_ternary_expr".into(),
2216 ))
2217 .into(),
2218 location: ast.location.clone(),
2219 })?;
2220
2221 let analyzed_binary_expr = match expr {
2222 Expr::TernaryExpr(ternary_expr) => (**ternary_expr).clone(),
2223 _ => todo!(),
2224 };
2225
2226 let condition = driver
2227 .visit(
2228 &ast.condition,
2229 self,
2230 context.update_expr(analyzed_binary_expr.condition),
2231 )?
2232 .expr
2233 .ok_or(AnalysisLocatableError {
2234 error: AnalysisError::AssertionFailure(ContextMissingExpr(
2235 "visit_ternary_expr".into(),
2236 ))
2237 .into(),
2238 location: ast.location.clone(),
2239 })?;
2240 let yes = driver
2241 .visit(
2242 &ast.yes,
2243 self,
2244 context.update_expr(analyzed_binary_expr.yes),
2245 )?
2246 .expr
2247 .ok_or(AnalysisLocatableError {
2248 error: AnalysisError::AssertionFailure(ContextMissingExpr(
2249 "visit_ternary_expr".into(),
2250 ))
2251 .into(),
2252 location: ast.location.clone(),
2253 })?;
2254
2255 let no = driver
2256 .visit(&ast.no, self, context.update_expr(analyzed_binary_expr.no))?
2257 .expr
2258 .ok_or(AnalysisLocatableError {
2259 error: AnalysisError::AssertionFailure(ContextMissingExpr(
2260 "visit_ternary_expr".into(),
2261 ))
2262 .into(),
2263 location: ast.location.clone(),
2264 })?;
2265
2266 let condition_c = match condition.constant() {
2267 Some(condition_c) => condition_c,
2268 None => {
2269 return Ok(
2270 context.update_expr(Expr::TernaryExpr(Arc::new(ast::TernaryExpr {
2271 condition,
2272 yes,
2273 no,
2274 location: ast.location.clone(),
2275 aug: Analyzed {
2276 tipe: expr.tipe(),
2277 constant: None,
2278 },
2279 }))),
2280 );
2281 }
2282 };
2283
2284 let condition_c = match condition_c {
2285 CompiledConstant::Boolean(b) => b,
2286 e => {
2287 return Err(AnalysisLocatableError {
2288 error: AnalysisError::AssertionFailure(
2289 AnalysisAssertions::ContextWrongExprType(
2290 "Boolean".to_string(),
2291 e.to_string(),
2292 "visit_ternary_expr".to_string(),
2293 ),
2294 )
2295 .into(),
2296 location: ast.location.clone(),
2297 });
2298 }
2299 };
2300
2301 let constant_result = if condition_c { &yes } else { &no }.constant();
2302
2303 Ok(
2304 context.update_expr(Expr::TernaryExpr(Arc::new(ast::TernaryExpr {
2305 condition,
2306 yes,
2307 no,
2308 location: ast.location.clone(),
2309 aug: Analyzed {
2310 tipe: expr.tipe(),
2311 constant: constant_result,
2312 },
2313 }))),
2314 )
2315 }
2316
2317 fn visit_member_access_expr(
2318 &self,
2319 _ast: &ast::MemberAccessExpression<()>,
2320 context: MelAnalysisContext,
2321 _driver: &AstVisitorDriver,
2322 ) -> AstVisitorResult<MelAnalysisContext, AnalysisLocatableError> {
2323 Ok(context.clone())
2324 }
2325}
2326
2327#[cfg(test)]
2328mod optimizer_tests {
2329 use crate::mel::{
2330 analysis::{
2331 Analyzed, CompiledConstant, MelAnalysisContext, MelOptimizer, MelTypeChecker, ast::Expr,
2332 },
2333 ast::{AstVisitorDriver, BinaryExpr},
2334 compiler::compile,
2335 types::Type,
2336 };
2337 use std::assert_matches;
2338
2339 #[test]
2340 fn test_optimize_binary_expr() {
2341 let expr = "5 + 3";
2342
2343 let compile_result = compile(expr);
2344 let ast = compile_result.expect("Compilation error");
2345
2346 let driver = AstVisitorDriver {};
2347 let visitor = MelTypeChecker {};
2348 let context = MelAnalysisContext::default();
2349 let result = driver
2350 .visit(&ast, &visitor, context)
2351 .expect("Could not analyze");
2352
2353 let driver = AstVisitorDriver {};
2354 let visitor = MelOptimizer {};
2355 let result = driver
2356 .visit(&ast, &visitor, result)
2357 .expect("Could not analyze");
2358
2359 let result = result.expr.expect("Could not get the analyzed expression");
2360 let binary_expr = match result {
2361 Expr::BinaryExpr(id) => id,
2362 _ => todo!(),
2363 };
2364
2365 assert_matches!(
2366 *binary_expr,
2367 BinaryExpr::<Analyzed> {
2368 left: _,
2369 right: _,
2370 op: _,
2371 location: _,
2372 aug: Analyzed {
2373 tipe: Type::Integer,
2374 constant: Some(CompiledConstant::Integer(8)),
2375 }
2376 }
2377 );
2378 }
2379
2380 #[test]
2381 fn test_optimize_nested_binary_expr() {
2382 let expr = "5 + 3 + (8 * 11)";
2383
2384 let compile_result = compile(expr);
2385 let ast = compile_result.expect("Compilation error");
2386
2387 let driver = AstVisitorDriver {};
2388 let visitor = MelTypeChecker {};
2389 let context = MelAnalysisContext::default();
2390 let result = driver
2391 .visit(&ast, &visitor, context)
2392 .expect("Could not analyze");
2393
2394 let driver = AstVisitorDriver {};
2395 let visitor = MelOptimizer {};
2396 let result = driver
2397 .visit(&ast, &visitor, result)
2398 .expect("Could not analyze");
2399
2400 let result = result.expr.expect("Could not get the analyzed expression");
2401 let binary_expr = match result {
2402 Expr::BinaryExpr(id) => id,
2403 _ => todo!(),
2404 };
2405
2406 assert_matches!(
2407 *binary_expr,
2408 BinaryExpr::<Analyzed> {
2409 left: _,
2410 right: _,
2411 op: _,
2412 location: _,
2413 aug: Analyzed {
2414 tipe: Type::Integer,
2415 constant: Some(CompiledConstant::Integer(96)),
2416 }
2417 }
2418 );
2419 }
2420
2421 #[test]
2422 fn test_optimize_nested_binary_expr2() {
2423 let expr = "(8 * 11) / 8";
2424
2425 let compile_result = compile(expr);
2426 let ast = compile_result.expect("Compilation error");
2427
2428 let driver = AstVisitorDriver {};
2429 let visitor = MelTypeChecker {};
2430 let context = MelAnalysisContext::default();
2431 let result = driver
2432 .visit(&ast, &visitor, context)
2433 .expect("Could not analyze");
2434
2435 let driver = AstVisitorDriver {};
2436 let visitor = MelOptimizer {};
2437 let result = driver
2438 .visit(&ast, &visitor, result)
2439 .expect("Could not analyze");
2440
2441 let result = result.expr.expect("Could not get the analyzed expression");
2442 let binary_expr = match result {
2443 Expr::BinaryExpr(id) => id,
2444 _ => todo!(),
2445 };
2446
2447 assert_matches!(
2448 *binary_expr,
2449 BinaryExpr::<Analyzed> {
2450 left: _,
2451 right: _,
2452 op: _,
2453 location: _,
2454 aug: Analyzed {
2455 tipe: Type::Integer,
2456 constant: Some(CompiledConstant::Integer(11)),
2457 }
2458 }
2459 );
2460 }
2461
2462 #[test]
2463 fn test_optimize_nested_binary_expr3() {
2464 let expr = "(8 * 11) % 2";
2465
2466 let compile_result = compile(expr);
2467 let ast = compile_result.expect("Compilation error");
2468
2469 let driver = AstVisitorDriver {};
2470 let visitor = MelTypeChecker {};
2471 let context = MelAnalysisContext::default();
2472 let result = driver
2473 .visit(&ast, &visitor, context)
2474 .expect("Could not analyze");
2475
2476 let driver = AstVisitorDriver {};
2477 let visitor = MelOptimizer {};
2478 let result = driver
2479 .visit(&ast, &visitor, result)
2480 .expect("Could not analyze");
2481
2482 let result = result.expr.expect("Could not get the analyzed expression");
2483 let binary_expr = match result {
2484 Expr::BinaryExpr(id) => id,
2485 _ => todo!(),
2486 };
2487
2488 assert_matches!(
2489 *binary_expr,
2490 BinaryExpr::<Analyzed> {
2491 left: _,
2492 right: _,
2493 op: _,
2494 location: _,
2495 aug: Analyzed {
2496 tipe: Type::Integer,
2497 constant: Some(CompiledConstant::Integer(0)),
2498 }
2499 }
2500 );
2501 }
2502
2503 #[test]
2504 fn test_optimize_nested_binary_expr4() {
2505 let expr = "\"testing\" . \"one\"";
2506
2507 let compile_result = compile(expr);
2508 let ast = compile_result.expect("Compilation error");
2509
2510 let driver = AstVisitorDriver {};
2511 let visitor = MelTypeChecker {};
2512 let context = MelAnalysisContext::default();
2513 let result = driver
2514 .visit(&ast, &visitor, context)
2515 .expect("Could not analyze");
2516
2517 let driver = AstVisitorDriver {};
2518 let visitor = MelOptimizer {};
2519 let result = driver
2520 .visit(&ast, &visitor, result)
2521 .expect("Could not analyze");
2522
2523 let result = result.expr.expect("Could not get the analyzed expression");
2524 let binary_expr = match result {
2525 Expr::BinaryExpr(id) => id,
2526 _ => todo!(),
2527 };
2528
2529 assert_matches!(
2530 &*binary_expr,
2531 BinaryExpr::<Analyzed> {
2532 left: _,
2533 right: _,
2534 op: _,
2535 location: _,
2536 aug: Analyzed {
2537 tipe: Type::String,
2538 constant: Some(CompiledConstant::String(a)),
2539 }
2540 } if a == "testingone"
2541 );
2542 }
2543
2544 #[test]
2545 fn test_optimize_nested_binary_expr5() {
2546 let expr = "(\"testing\" . \"one\") . \"two\"";
2547
2548 let compile_result = compile(expr);
2549 let ast = compile_result.expect("Compilation error");
2550
2551 let driver = AstVisitorDriver {};
2552 let visitor = MelTypeChecker {};
2553 let context = MelAnalysisContext::default();
2554 let result = driver
2555 .visit(&ast, &visitor, context)
2556 .expect("Could not analyze");
2557
2558 let driver = AstVisitorDriver {};
2559 let visitor = MelOptimizer {};
2560 let result = driver
2561 .visit(&ast, &visitor, result)
2562 .expect("Could not analyze");
2563
2564 let result = result.expr.expect("Could not get the analyzed expression");
2565 let binary_expr = match result {
2566 Expr::BinaryExpr(id) => id,
2567 _ => todo!(),
2568 };
2569
2570 assert_matches!(
2571 &*binary_expr,
2572 BinaryExpr::<Analyzed> {
2573 left: _,
2574 right: _,
2575 op: _,
2576 location: _,
2577 aug: Analyzed {
2578 tipe: Type::String,
2579 constant: Some(CompiledConstant::String(a)),
2580 }
2581 } if a == "testingonetwo"
2582 );
2583 }
2584
2585 #[test]
2586 fn test_optimize_logical_binary_expr() {
2587 let expr = "true or false";
2588
2589 let compile_result = compile(expr);
2590 let ast = compile_result.expect("Compilation error");
2591
2592 let driver = AstVisitorDriver {};
2593 let visitor = MelTypeChecker {};
2594 let context = MelAnalysisContext::default();
2595 let result = driver
2596 .visit(&ast, &visitor, context)
2597 .expect("Could not analyze");
2598
2599 let driver = AstVisitorDriver {};
2600 let visitor = MelOptimizer {};
2601 let result = driver
2602 .visit(&ast, &visitor, result)
2603 .expect("Could not analyze");
2604
2605 let result = result.expr.expect("Could not get the analyzed expression");
2606 let binary_expr = match result {
2607 Expr::BinaryExpr(id) => id,
2608 _ => todo!(),
2609 };
2610
2611 assert_matches!(
2612 *binary_expr,
2613 BinaryExpr::<Analyzed> {
2614 left: _,
2615 right: _,
2616 op: _,
2617 location: _,
2618 aug: Analyzed {
2619 tipe: Type::Boolean,
2620 constant: Some(CompiledConstant::Boolean(true)),
2621 }
2622 }
2623 );
2624 }
2625
2626 #[test]
2627 fn test_optimize_logical_binary_expr2() {
2628 let expr = "true and false";
2629
2630 let compile_result = compile(expr);
2631 let ast = compile_result.expect("Compilation error");
2632
2633 let driver = AstVisitorDriver {};
2634 let visitor = MelTypeChecker {};
2635 let context = MelAnalysisContext::default();
2636 let result = driver
2637 .visit(&ast, &visitor, context)
2638 .expect("Could not analyze");
2639
2640 let driver = AstVisitorDriver {};
2641 let visitor = MelOptimizer {};
2642 let result = driver
2643 .visit(&ast, &visitor, result)
2644 .expect("Could not analyze");
2645
2646 let result = result.expr.expect("Could not get the analyzed expression");
2647 let binary_expr = match result {
2648 Expr::BinaryExpr(id) => id,
2649 _ => todo!(),
2650 };
2651
2652 assert_matches!(
2653 *binary_expr,
2654 BinaryExpr::<Analyzed> {
2655 left: _,
2656 right: _,
2657 op: _,
2658 location: _,
2659 aug: Analyzed {
2660 tipe: Type::Boolean,
2661 constant: Some(CompiledConstant::Boolean(false)),
2662 }
2663 }
2664 );
2665 }
2666
2667 #[test]
2668 fn test_optimize_comparison_expr() {
2669 let expr = "5 < 4";
2670
2671 let compile_result = compile(expr);
2672 let ast = compile_result.expect("Compilation error");
2673
2674 let driver = AstVisitorDriver {};
2675 let visitor = MelTypeChecker {};
2676 let context = MelAnalysisContext::default();
2677 let result = driver
2678 .visit(&ast, &visitor, context)
2679 .expect("Could not analyze");
2680
2681 let driver = AstVisitorDriver {};
2682 let visitor = MelOptimizer {};
2683 let result = driver
2684 .visit(&ast, &visitor, result)
2685 .expect("Could not analyze");
2686
2687 let result = result.expr.expect("Could not get the analyzed expression");
2688 let binary_expr = match result {
2689 Expr::BinaryExpr(id) => id,
2690 _ => todo!(),
2691 };
2692
2693 assert_matches!(
2694 *binary_expr,
2695 BinaryExpr::<Analyzed> {
2696 left: _,
2697 right: _,
2698 op: _,
2699 location: _,
2700 aug: Analyzed {
2701 tipe: Type::Boolean,
2702 constant: None,
2703 }
2704 }
2705 );
2706 }
2707
2708 #[test]
2709 fn test_optimize_ipmatch_comparison_expr() {
2710 let expr = "8.8.8.8 ipmatch 5.5.5.5";
2711
2712 let compile_result = compile(expr);
2713 let ast = compile_result.expect("Compilation error");
2714
2715 let driver = AstVisitorDriver {};
2716 let visitor = MelTypeChecker {};
2717 let context = MelAnalysisContext::default();
2718 let result = driver
2719 .visit(&ast, &visitor, context)
2720 .expect("Could not analyze");
2721
2722 let driver = AstVisitorDriver {};
2723 let visitor = MelOptimizer {};
2724 let result = driver
2725 .visit(&ast, &visitor, result)
2726 .expect("Could not analyze");
2727
2728 let result = result.expr.expect("Could not get the analyzed expression");
2729 let binary_expr = match result {
2730 Expr::BinaryExpr(id) => id,
2731 _ => todo!(),
2732 };
2733
2734 assert_matches!(
2735 *binary_expr,
2736 BinaryExpr::<Analyzed> {
2737 left: _,
2738 right: _,
2739 op: _,
2740 location: _,
2741 aug: Analyzed {
2742 tipe: Type::Boolean,
2743 constant: Some(CompiledConstant::Boolean(false)),
2744 }
2745 }
2746 );
2747 }
2748
2749 #[test]
2750 fn test_optimize_ipmatch_comparison_expr2() {
2751 let expr = "5.5.5.5 ipmatch 5.5.5.5";
2752
2753 let compile_result = compile(expr);
2754 let ast = compile_result.expect("Compilation error");
2755
2756 let driver = AstVisitorDriver {};
2757 let visitor = MelTypeChecker {};
2758 let context = MelAnalysisContext::default();
2759 let result = driver
2760 .visit(&ast, &visitor, context)
2761 .expect("Could not analyze");
2762
2763 let driver = AstVisitorDriver {};
2764 let visitor = MelOptimizer {};
2765 let result = driver
2766 .visit(&ast, &visitor, result)
2767 .expect("Could not analyze");
2768
2769 let result = result.expr.expect("Could not get the analyzed expression");
2770 let binary_expr = match result {
2771 Expr::BinaryExpr(id) => id,
2772 _ => todo!(),
2773 };
2774
2775 assert_matches!(
2776 *binary_expr,
2777 BinaryExpr::<Analyzed> {
2778 left: _,
2779 right: _,
2780 op: _,
2781 location: _,
2782 aug: Analyzed {
2783 tipe: Type::Boolean,
2784 constant: Some(CompiledConstant::Boolean(true)),
2785 }
2786 }
2787 );
2788 }
2789
2790 #[test]
2791 fn test_optimize_ipmatch_comparison_expr3() {
2792 let expr = "2001:0db8:85a3:0000:0000:8a2e:0370:7334 ipmatch 5.5.5.5";
2793
2794 let compile_result = compile(expr);
2795 let ast = compile_result.expect("Compilation error");
2796
2797 let driver = AstVisitorDriver {};
2798 let visitor = MelTypeChecker {};
2799 let context = MelAnalysisContext::default();
2800 let result = driver
2801 .visit(&ast, &visitor, context)
2802 .expect("Could not analyze");
2803
2804 let driver = AstVisitorDriver {};
2805 let visitor = MelOptimizer {};
2806 let result = driver
2807 .visit(&ast, &visitor, result)
2808 .expect("Could not analyze");
2809
2810 let result = result.expr.expect("Could not get the analyzed expression");
2811 let binary_expr = match result {
2812 Expr::BinaryExpr(id) => id,
2813 _ => todo!(),
2814 };
2815
2816 assert_matches!(
2817 *binary_expr,
2818 BinaryExpr::<Analyzed> {
2819 left: _,
2820 right: _,
2821 op: _,
2822 location: _,
2823 aug: Analyzed {
2824 tipe: Type::Boolean,
2825 constant: Some(CompiledConstant::Boolean(false)),
2826 }
2827 }
2828 );
2829 }
2830
2831 #[test]
2832 fn test_optimize_ipmatch_comparison_expr4() {
2833 let expr = "2001:0db8:85a3:0000:0000:8a2e:0370:7334 ipmatch 2001:0db8:85a3:0000:0000:8a2e:0370:7334";
2834
2835 let compile_result = compile(expr);
2836 let ast = compile_result.expect("Compilation error");
2837
2838 let driver = AstVisitorDriver {};
2839 let visitor = MelTypeChecker {};
2840 let context = MelAnalysisContext::default();
2841 let result = driver
2842 .visit(&ast, &visitor, context)
2843 .expect("Could not analyze");
2844
2845 let driver = AstVisitorDriver {};
2846 let visitor = MelOptimizer {};
2847 let result = driver
2848 .visit(&ast, &visitor, result)
2849 .expect("Could not analyze");
2850
2851 let result = result.expr.expect("Could not get the analyzed expression");
2852 let binary_expr = match result {
2853 Expr::BinaryExpr(id) => id,
2854 _ => todo!(),
2855 };
2856
2857 assert_matches!(
2858 *binary_expr,
2859 BinaryExpr::<Analyzed> {
2860 left: _,
2861 right: _,
2862 op: _,
2863 location: _,
2864 aug: Analyzed {
2865 tipe: Type::Boolean,
2866 constant: Some(CompiledConstant::Boolean(true)),
2867 }
2868 }
2869 );
2870 }
2871}
2872
2873#[cfg(test)]
2874mod analysis_error_tests {
2875 use crate::mel::{
2876 analysis::{AnalysisError, AnalysisPreconditions, MelAnalysisContext, MelOptimizer},
2877 ast::AstVisitorDriver,
2878 compiler::compile,
2879 };
2880 use std::assert_matches;
2881
2882 #[test]
2883 fn test_invalid_context_error_binary_expr() {
2884 let expr = "5 < 4";
2885
2886 let compile_result = compile(expr);
2887 let ast = compile_result.expect("Compilation error");
2888
2889 let context = MelAnalysisContext::default();
2890 let driver = AstVisitorDriver {};
2891 let visitor = MelOptimizer {};
2892
2893 let result = driver
2894 .visit(&ast, &visitor, context)
2895 .expect_err("Could analyze expression with no context");
2896 assert_matches!(
2897 *result.error,
2898 AnalysisError::PreconditionFailure(AnalysisPreconditions::ContextMissingExpr(_))
2899 );
2900 }
2901}
2902
2903pub type MelAnalysisResult = Result<Expr<Analyzed>, Box<AnalysisLocatableError>>;
2904
2905pub fn analyze(expr: &Expr<()>, scopes: &Scopes<Type>) -> MelAnalysisResult {
2907 let driver = AstVisitorDriver {};
2908 let visitor = MelTypeChecker {};
2909 let mut context = MelAnalysisContext::default();
2910
2911 context = context.update_scopes(scopes);
2912 let result = driver.visit(expr, &visitor, context)?;
2913
2914 let driver = AstVisitorDriver {};
2915 let visitor = MelOptimizer {};
2916 let result = driver.visit(expr, &visitor, result)?;
2917
2918 let result = result.expr.ok_or(AnalysisLocatableError {
2919 error: AnalysisError::Incalculable.into(),
2920 location: TextLocation {
2921 start: 0,
2922 extent: 0,
2923 },
2924 })?;
2925
2926 Ok(result)
2927}