1use std::fmt::Debug;
19
20use crate::{
21 common::TextLocation,
22 environment::scope::Scopes,
23 mel::interpreter::{
24 error::{
25 InterpAssertion::{self, SuccessWithoutValue},
26 InterpError::{self, UnknownIdentifier},
27 InterpLocatableError,
28 },
29 values::{TypedValue, Value},
30 },
31};
32use crate::{
33 logging::{LogLevel, LogMsg, LogMsgs},
34 mel::{interpreter::builtins::BuiltinFunctionInterpreter, types::BuiltinFunctionType},
35};
36
37use crate::mel::{
38 analysis::Analyzed,
39 ast::{
40 self, Argument, ArgumentList, AstVisitor, AstVisitorDriver, AstVisitorResult, BinaryExpr,
41 BinaryInfixOperator, BooleanLiteral,
42 ComparisonOperator::{self, IP, Re},
43 FunctionCall, IPAddressLiteral, Identifier, NumberLiteral, RegexLiteral, StringLiteral,
44 TernaryExpr,
45 },
46 types::Type,
47};
48
49pub trait BuiltinFunction: BuiltinFunctionType + BuiltinFunctionInterpreter {}
50
51pub type MelInterpResult = Result<MelInterpContext, Box<InterpLocatableError>>;
52
53#[derive(Debug, Default)]
54pub struct MelInterpContext {
55 pub val: Option<TypedValue>,
56 pub scopes: Scopes<TypedValue>,
57 pub log: LogMsgs,
58}
59
60impl MelInterpContext {
61 pub fn update_val(self, new: Option<TypedValue>) -> Self {
62 MelInterpContext {
63 val: new,
64 scopes: self.scopes,
65 log: self.log,
66 }
67 }
68 pub fn update_scopes(self, new: &Scopes<TypedValue>) -> Self {
69 MelInterpContext {
70 val: self.val,
71 scopes: new.clone(),
72 log: self.log,
73 }
74 }
75
76 pub fn update_log(self, new: LogMsgs) -> Self {
77 MelInterpContext {
78 val: self.val,
79 scopes: self.scopes,
80 log: new,
81 }
82 }
83}
84
85pub struct MelInterp {}
86
87impl MelInterp {
88 pub fn interp_binary_expr(
89 op: &BinaryInfixOperator,
90 left: &TypedValue,
91 right: &TypedValue,
92 ) -> Result<TypedValue, Box<InterpError>> {
93 match op {
94 ast::BinaryInfixOperator::Logic(logic_operator) => {
95 let left = if let Value::Boolean(l) = left.value {
96 l
97 } else {
98 return Err(InterpError::Assertion(
99 InterpAssertion::TypeMismatch(Type::Boolean, left.tpe.clone()).into(),
100 )
101 .into());
102 };
103 let right = if let Value::Boolean(r) = right.value {
104 r
105 } else {
106 return Err(InterpError::Assertion(
107 InterpAssertion::TypeMismatch(Type::Boolean, right.tpe.clone()).into(),
108 )
109 .into());
110 };
111 match logic_operator {
112 ast::LogicOperator::And => Ok(TypedValue {
113 value: Value::Boolean(left && right),
114 tpe: Type::Boolean,
115 }),
116 ast::LogicOperator::Or => Ok(TypedValue {
117 value: Value::Boolean(left || right),
118 tpe: Type::Boolean,
119 }),
120 }
121 }
122 ast::BinaryInfixOperator::Math(math_operator) => {
123 let left = if let Value::Integer(l) = left.value {
124 l
125 } else {
126 return Err(InterpError::Assertion(
127 InterpAssertion::TypeMismatch(Type::Integer, left.tpe.clone()).into(),
128 )
129 .into());
130 };
131 let right = if let Value::Integer(r) = right.value {
132 r
133 } else {
134 return Err(InterpError::Assertion(
135 InterpAssertion::TypeMismatch(Type::Integer, right.tpe.clone()).into(),
136 )
137 .into());
138 };
139 match math_operator {
140 ast::MathOperator::Plus => Ok(TypedValue {
141 value: Value::Integer(left + right),
142 tpe: Type::Integer,
143 }),
144 ast::MathOperator::Minus => Ok(TypedValue {
145 value: Value::Integer(left - right),
146 tpe: Type::Integer,
147 }),
148 ast::MathOperator::Multiply => Ok(TypedValue {
149 value: Value::Integer(left * right),
150 tpe: Type::Integer,
151 }),
152 ast::MathOperator::Divide => Ok(TypedValue {
153 value: Value::Integer(left / right),
154 tpe: Type::Integer,
155 }),
156 ast::MathOperator::Modulo => Ok(TypedValue {
157 value: Value::Integer(left % right),
158 tpe: Type::Integer,
159 }),
160 }
161 }
162 ast::BinaryInfixOperator::Concat(_) => {
163 let left = if let Value::String(l) = &left.value {
164 l
165 } else {
166 return Err(InterpError::Assertion(
167 InterpAssertion::TypeMismatch(Type::String, left.tpe.clone()).into(),
168 )
169 .into());
170 };
171 let right = if let Value::String(r) = &right.value {
172 r
173 } else {
174 return Err(InterpError::Assertion(
175 InterpAssertion::TypeMismatch(Type::String, right.tpe.clone()).into(),
176 )
177 .into());
178 };
179 Ok(TypedValue {
180 value: Value::String(left.clone() + right),
181 tpe: Type::String,
182 })
183 }
184 ast::BinaryInfixOperator::Comparison(IP) => {
185 let left = if let Value::IPAddress(l) = left.value {
186 l
187 } else {
188 return Err(InterpError::Assertion(
189 InterpAssertion::TypeMismatch(Type::IPAddress, left.tpe.clone()).into(),
190 )
191 .into());
192 };
193 let right = if let Value::IPAddress(r) = right.value {
194 r
195 } else {
196 return Err(InterpError::Assertion(
197 InterpAssertion::TypeMismatch(Type::IPAddress, right.tpe.clone()).into(),
198 )
199 .into());
200 };
201 Ok(TypedValue {
202 value: Value::Boolean(left == right),
203 tpe: Type::Boolean,
204 })
205 }
206 ast::BinaryInfixOperator::Comparison(Re) => {
207 let left = if let Value::String(l) = &left.value {
208 l
209 } else {
210 return Err(InterpError::Assertion(
211 InterpAssertion::TypeMismatch(Type::String, left.tpe.clone()).into(),
212 )
213 .into());
214 };
215 let right = if let Value::Regex(r) = &right.value {
216 r
217 } else {
218 return Err(InterpError::Assertion(
219 InterpAssertion::TypeMismatch(Type::Regex, right.tpe.clone()).into(),
220 )
221 .into());
222 };
223 Ok(TypedValue {
224 value: Value::Boolean(right.is_match(left)),
225 tpe: Type::Boolean,
226 })
227 }
228 ast::BinaryInfixOperator::Comparison(cop) => match left.tpe {
229 Type::Boolean => {
230 let left = if let Value::Boolean(l) = left.value {
231 l
232 } else {
233 return Err(InterpError::Assertion(
234 InterpAssertion::TypeMismatch(Type::Boolean, left.tpe.clone()).into(),
235 )
236 .into());
237 };
238 let right = if let Value::Boolean(r) = right.value {
239 r
240 } else {
241 return Err(InterpError::Assertion(
242 InterpAssertion::TypeMismatch(Type::Boolean, right.tpe.clone()).into(),
243 )
244 .into());
245 };
246
247 match cop {
248 ComparisonOperator::Eq => Ok(TypedValue {
249 value: Value::Boolean(left == right),
250 tpe: Type::Boolean,
251 }),
252 #[allow(clippy::bool_comparison)]
253 ComparisonOperator::Lt => Ok(TypedValue {
254 value: Value::Boolean(left < right),
255 tpe: Type::Boolean,
256 }),
257 ComparisonOperator::Lte => Ok(TypedValue {
258 value: Value::Boolean(left <= right),
259 tpe: Type::Boolean,
260 }),
261 #[allow(clippy::bool_comparison)]
262 ComparisonOperator::Gt => Ok(TypedValue {
263 value: Value::Boolean(left > right),
264 tpe: Type::Boolean,
265 }),
266 ComparisonOperator::Gte => Ok(TypedValue {
267 value: Value::Boolean(left >= right),
268 tpe: Type::Boolean,
269 }),
270 _ => todo!(),
271 }
272 }
273 Type::Integer => {
274 let left = if let Value::Integer(l) = left.value {
275 l
276 } else {
277 return Err(InterpError::Assertion(
278 InterpAssertion::TypeMismatch(Type::Integer, left.tpe.clone()).into(),
279 )
280 .into());
281 };
282 let right = if let Value::Integer(r) = right.value {
283 r
284 } else {
285 return Err(InterpError::Assertion(
286 InterpAssertion::TypeMismatch(Type::Integer, right.tpe.clone()).into(),
287 )
288 .into());
289 };
290
291 match cop {
292 ComparisonOperator::Eq => Ok(TypedValue {
293 value: Value::Boolean(left == right),
294 tpe: Type::Boolean,
295 }),
296 ComparisonOperator::Lt => Ok(TypedValue {
297 value: Value::Boolean(left < right),
298 tpe: Type::Boolean,
299 }),
300 ComparisonOperator::Lte => Ok(TypedValue {
301 value: Value::Boolean(left <= right),
302 tpe: Type::Boolean,
303 }),
304 ComparisonOperator::Gt => Ok(TypedValue {
305 value: Value::Boolean(left > right),
306 tpe: Type::Boolean,
307 }),
308 ComparisonOperator::Gte => Ok(TypedValue {
309 value: Value::Boolean(left >= right),
310 tpe: Type::Boolean,
311 }),
312 _ => todo!(),
313 }
314 }
315 Type::String => {
316 let left = if let Value::String(l) = &left.value {
317 l
318 } else {
319 return Err(InterpError::Assertion(
320 InterpAssertion::TypeMismatch(Type::String, left.tpe.clone()).into(),
321 )
322 .into());
323 };
324 let right = if let Value::String(r) = &right.value {
325 r
326 } else {
327 return Err(InterpError::Assertion(
328 InterpAssertion::TypeMismatch(Type::String, right.tpe.clone()).into(),
329 )
330 .into());
331 };
332
333 match cop {
334 ComparisonOperator::Eq => Ok(TypedValue {
335 value: Value::Boolean(left == right),
336 tpe: Type::Boolean,
337 }),
338 ComparisonOperator::Lt => Ok(TypedValue {
339 value: Value::Boolean(left < right),
340 tpe: Type::Boolean,
341 }),
342 ComparisonOperator::Lte => Ok(TypedValue {
343 value: Value::Boolean(left <= right),
344 tpe: Type::Boolean,
345 }),
346 ComparisonOperator::Gt => Ok(TypedValue {
347 value: Value::Boolean(left > right),
348 tpe: Type::Boolean,
349 }),
350 ComparisonOperator::Gte => Ok(TypedValue {
351 value: Value::Boolean(left >= right),
352 tpe: Type::Boolean,
353 }),
354 _ => todo!(),
355 }
356 }
357 _ => todo!(),
358 },
359 ast::BinaryInfixOperator::MemberAccess(_) => Err(InterpError::Assertion(
360 InterpAssertion::UnexpectedOperator(op.clone()).into(),
361 )
362 .into()),
363 }
364 }
365}
366
367macro_rules! use_log {
368 ($context:ident, $logx:expr) => {
369 match $context {
370 MelInterpContext { val, scopes, log } => {
371 let logp = $logx(log);
372 MelInterpContext {
373 log: logp,
374 scopes,
375 val,
376 }
377 }
378 }
379 };
380}
381
382#[macro_export]
383macro_rules! use_constant {
384 ($const:expr, $loc:expr, $context:ident) => {
385 if let Some(constant) = &$const {
386 let $context = use_log!($context, |log: LogMsgs| {
387 trace_with_loc!(log, $loc.clone(), "Using constant")
388 });
389
390 return Ok($context.update_val(Some(constant.into())));
391 }
392 };
393}
394
395impl AstVisitor<MelInterpContext, Analyzed, Box<InterpLocatableError>> for MelInterp {
396 fn visit_function_call(
397 &self,
398 ast: &FunctionCall<Analyzed>,
399 context: MelInterpContext,
400 driver: &AstVisitorDriver,
401 ) -> AstVisitorResult<MelInterpContext, Box<InterpLocatableError>> {
402 let context = use_log!(context, |log: LogMsgs| {
403 trace_with_loc!(
404 log,
405 ast.location.clone(),
406 "Evaluating function call expression"
407 )
408 });
409
410 use_constant!(ast.aug.constant, ast.location, context);
411
412 let context = context.update_val(None);
413 let context = driver.visit(&ast.callee, self, context)?;
414
415 let callee_value = match &context.val {
416 Some(v) => v.clone(),
417 None => {
418 return Err(InterpLocatableError {
419 error: InterpError::Assertion(
420 SuccessWithoutValue(
421 "callee".to_string(),
422 "visit_function_call".to_string(),
423 )
424 .into(),
425 )
426 .into(),
427 location: ast.callee.location(),
428 context,
429 }
430 .into());
431 }
432 };
433
434 let callee_value = match callee_value {
435 TypedValue {
436 value: Value::Function(f),
437 tpe: _,
438 } => f,
439 TypedValue { value: _, tpe: t } => {
440 return Err(InterpLocatableError {
441 error: InterpError::Assertion(
442 InterpAssertion::TypeMismatch(ast.callee.tipe(), t.clone()).into(),
443 )
444 .into(),
445 location: ast.callee.location(),
446 context,
447 }
448 .into());
449 }
450 };
451
452 let context = context.update_val(None);
453 let context = self.visit_argument_list(&ast.arguments, context, driver)?;
454
455 let argument_list_values = match &context.val {
456 Some(v) => v.clone(),
457 None => {
458 return Err(InterpLocatableError {
459 error: InterpError::Assertion(
460 SuccessWithoutValue(
461 "arguments".to_string(),
462 "visit_function_call".to_string(),
463 )
464 .into(),
465 )
466 .into(),
467 location: ast.callee.location(),
468 context,
469 }
470 .into());
471 }
472 };
473
474 let argument_list_values = match argument_list_values {
475 TypedValue {
476 value: f @ Value::ArgumentList(_),
477 tpe: _,
478 } => f,
479 TypedValue { value: _, tpe: t } => {
480 return Err(InterpLocatableError {
481 error: InterpError::Assertion(
482 InterpAssertion::TypeMismatch(ast.callee.tipe(), t.clone()).into(),
483 )
484 .into(),
485 location: ast.callee.location(),
486 context,
487 }
488 .into());
489 }
490 };
491
492 let res = match (*callee_value).interpw(argument_list_values.clone()) {
493 Ok(res) => res,
494 Err(e) => {
495 return Err(InterpLocatableError {
496 error: InterpError::BuiltinError(e).into(),
497 location: ast.location.clone(),
498 context,
499 }
500 .into());
501 }
502 };
503 Ok(context.update_val(Some(res)))
504 }
505
506 fn visit_identifier(
507 &self,
508 ast: &Identifier<Analyzed>,
509 context: MelInterpContext,
510 _driver: &AstVisitorDriver,
511 ) -> AstVisitorResult<MelInterpContext, Box<InterpLocatableError>> {
512 let context = use_log!(context, |log: LogMsgs| {
513 trace_with_loc!(
514 log,
515 ast.location.clone(),
516 "Evaluating identifier expression"
517 )
518 });
519
520 let found_id = match context.scopes.lookup(&ast.identifier) {
521 Some(v) => v,
522 None => {
523 return Err(InterpLocatableError {
524 error: UnknownIdentifier(ast.identifier.clone()).into(),
525 location: ast.location.clone(),
526 context,
527 }
528 .into());
529 }
530 };
531
532 Ok(context.update_val(Some(found_id)))
533 }
534
535 fn visit_argument_list(
536 &self,
537 ast: &ArgumentList<Analyzed>,
538 context: MelInterpContext,
539 driver: &AstVisitorDriver,
540 ) -> AstVisitorResult<MelInterpContext, Box<InterpLocatableError>> {
541 let context = use_log!(context, |log: LogMsgs| {
542 trace_with_loc!(
543 log,
544 ast.location.clone(),
545 "Evaluating argument list expression"
546 )
547 });
548
549 use_constant!(ast.aug.constant, ast.location, context);
550
551 let mut context = context.update_val(None);
552
553 let mut arg_values: Vec<TypedValue> = vec![];
554 for arg in &ast.arguments {
555 context = self.visit_argument(arg, context, driver)?;
556
557 let arg_value = match &context.val {
558 Some(v) => v.clone(),
559 None => {
560 return Err(InterpLocatableError {
561 error: InterpError::Assertion(
562 SuccessWithoutValue(
563 "argument".to_string(),
564 "visit_function_call".to_string(),
565 )
566 .into(),
567 )
568 .into(),
569 location: arg.location.clone(),
570 context,
571 }
572 .into());
573 }
574 };
575
576 arg_values.push(arg_value.clone());
577 }
578
579 Ok(context.update_val(Some(TypedValue {
580 value: Value::ArgumentList(arg_values),
581 tpe: ast.aug.tipe.clone(),
582 })))
583 }
584
585 fn visit_argument(
586 &self,
587 ast: &Argument<Analyzed>,
588 context: MelInterpContext,
589 driver: &AstVisitorDriver,
590 ) -> AstVisitorResult<MelInterpContext, Box<InterpLocatableError>> {
591 let context = use_log!(context, |log: LogMsgs| {
592 trace_with_loc!(
593 log,
594 ast.location.clone(),
595 "Evaluating argument list expression"
596 )
597 });
598
599 use_constant!(ast.aug.constant, ast.location, context);
600
601 let context = context.update_val(None);
602 let context = driver.visit(&ast.expr, self, context)?;
603
604 let argument_value = match &context.val {
605 Some(v) => v.clone(),
606 None => {
607 return Err(InterpLocatableError {
608 error: InterpError::Assertion(
609 SuccessWithoutValue("argument".to_string(), "visit_argument".to_string())
610 .into(),
611 )
612 .into(),
613 location: ast.expr.location(),
614 context,
615 }
616 .into());
617 }
618 };
619
620 if argument_value.tpe != ast.aug.tipe {
621 return Err(InterpLocatableError {
622 error: InterpError::Assertion(
623 InterpAssertion::TypeMismatch(ast.aug.tipe.clone(), argument_value.tpe.clone())
624 .into(),
625 )
626 .into(),
627 location: ast.location.clone(),
628 context,
629 }
630 .into());
631 }
632
633 Ok(context.update_val(Some(argument_value.clone())))
634 }
635
636 fn visit_binary_expr(
637 &self,
638 ast: &BinaryExpr<Analyzed>,
639 context: MelInterpContext,
640 driver: &AstVisitorDriver,
641 ) -> AstVisitorResult<MelInterpContext, Box<InterpLocatableError>> {
642 let context = use_log!(context, |log: LogMsgs| {
643 trace_with_loc!(log, ast.location.clone(), "Evaluating binary expression")
644 });
645
646 use_constant!(ast.aug.constant, ast.location, context);
647
648 let context = context.update_val(None);
649 let context = driver.visit(&ast.left, self, context)?;
650
651 let left_value = match &context.val {
652 Some(v) => v.clone(),
653 None => {
654 return Err(InterpLocatableError {
655 error: InterpError::Assertion(
656 SuccessWithoutValue(
657 "left_operand".to_string(),
658 "visit_binary_expr".to_string(),
659 )
660 .into(),
661 )
662 .into(),
663 location: ast.left.location(),
664 context,
665 }
666 .into());
667 }
668 };
669
670 let context = context.update_val(None);
671 let context = driver.visit(&ast.right, self, context)?;
672
673 let right_value = match &context.val {
674 Some(v) => v.clone(),
675 None => {
676 return Err(InterpLocatableError {
677 error: InterpError::Assertion(
678 SuccessWithoutValue(
679 "right_operand".to_string(),
680 "visit_binary_expr".to_string(),
681 )
682 .into(),
683 )
684 .into(),
685 location: ast.right.location(),
686 context,
687 }
688 .into());
689 }
690 };
691
692 let result = match Self::interp_binary_expr(&ast.op, &left_value, &right_value) {
693 Ok(o) => o,
694 Err(e) => {
695 return Err(InterpLocatableError {
696 error: e,
697 location: ast.location.clone(),
698 context,
699 }
700 .into());
701 }
702 };
703
704 Ok(context.update_val(Some(result)))
705 }
706
707 fn visit_literal(
708 &self,
709 ast: (&ast::Literal, &TextLocation, &Analyzed),
710 context: MelInterpContext,
711 _driver: &AstVisitorDriver,
712 ) -> AstVisitorResult<MelInterpContext, Box<InterpLocatableError>> {
713 let context = use_log!(context, |log: LogMsgs| {
714 trace_with_loc!(log, ast.1.clone(), "Evaluating literal expression")
715 });
716
717 match ast {
718 (ast::Literal::Boolean(b), _, _) => Ok(context.update_val(Some(TypedValue {
719 value: Value::Boolean(*b == BooleanLiteral::True),
720 tpe: Type::Boolean,
721 }))),
722 (ast::Literal::Number(NumberLiteral { literal: l }), _, _) => {
723 Ok(context.update_val(Some(TypedValue {
724 value: Value::Integer(*l as i64),
725 tpe: Type::Integer,
726 })))
727 }
728 (ast::Literal::String(StringLiteral { literal: s }), _, _) => {
729 Ok(context.update_val(Some(TypedValue {
730 value: Value::String(s.clone()),
731 tpe: Type::String,
732 })))
733 }
734 (ast::Literal::Regex(RegexLiteral { literal: rl }), _, _) => {
735 Ok(context.update_val(Some(TypedValue {
736 value: Value::Regex(rl.clone()),
737 tpe: Type::Regex,
738 })))
739 }
740 (ast::Literal::IPAddress(IPAddressLiteral { literal: rl }), _, _) => Ok(context
741 .update_val(Some(TypedValue {
742 value: Value::IPAddress(*rl),
743 tpe: Type::IPAddress,
744 }))),
745 }
746 }
747
748 fn visit_ternary_expr(
749 &self,
750 ast: &TernaryExpr<Analyzed>,
751 context: MelInterpContext,
752 driver: &AstVisitorDriver,
753 ) -> AstVisitorResult<MelInterpContext, Box<InterpLocatableError>> {
754 let context = use_log!(context, |log: LogMsgs| {
755 trace_with_loc!(log, ast.location.clone(), "Evaluating ternary expression")
756 });
757
758 use_constant!(ast.aug.constant, ast.location, context);
759
760 let context = context.update_val(None);
761 let context = driver.visit(&ast.condition, self, context)?;
762
763 let condition_value = match &context.val {
764 Some(v) => v.clone(),
765 None => {
766 return Err(InterpLocatableError {
767 error: InterpError::Assertion(
768 SuccessWithoutValue(
769 "condition".to_string(),
770 "visit_ternary_expr".to_string(),
771 )
772 .into(),
773 )
774 .into(),
775 location: ast.condition.location(),
776 context,
777 }
778 .into());
779 }
780 };
781
782 let condition_value = match condition_value {
783 TypedValue {
784 value: Value::Boolean(b),
785 tpe: Type::Boolean,
786 } => b,
787 e => {
788 return Err(InterpLocatableError {
789 error: InterpError::Assertion(
790 InterpAssertion::TypeMismatch(Type::Boolean, e.tpe.clone()).into(),
791 )
792 .into(),
793 location: ast.location.clone(),
794 context,
795 }
796 .into());
797 }
798 };
799
800 let context = context.update_val(None);
801
802 let (context, result) = if condition_value {
803 let context = driver.visit(&ast.yes, self, context)?;
804
805 let val = match &context.val {
806 Some(v) => v.clone(),
807 None => {
808 return Err(InterpLocatableError {
809 error: InterpError::Assertion(
810 SuccessWithoutValue(
811 "true branch".to_string(),
812 "visit_ternary_expr".to_string(),
813 )
814 .into(),
815 )
816 .into(),
817 location: ast.condition.location(),
818 context,
819 }
820 .into());
821 }
822 };
823 (context, val)
824 } else {
825 let context = driver.visit(&ast.no, self, context)?;
826
827 let val = match &context.val {
828 Some(v) => v.clone(),
829 None => {
830 return Err(InterpLocatableError {
831 error: InterpError::Assertion(
832 SuccessWithoutValue(
833 "false branch".to_string(),
834 "visit_ternary_expr".to_string(),
835 )
836 .into(),
837 )
838 .into(),
839 location: ast.condition.location(),
840 context,
841 }
842 .into());
843 }
844 };
845 (context, val)
846 };
847
848 if result.tpe != ast.aug.tipe {
849 return Err(InterpLocatableError {
850 error: InterpError::Assertion(
851 InterpAssertion::TypeMismatch(ast.aug.tipe.clone(), result.tpe.clone()).into(),
852 )
853 .into(),
854 location: ast.location.clone(),
855 context,
856 }
857 .into());
858 }
859
860 Ok(context.update_val(Some(result.clone())))
861 }
862
863 fn visit_member_access_expr(
864 &self,
865 ast: &ast::MemberAccessExpression<Analyzed>,
866 context: MelInterpContext,
867 driver: &AstVisitorDriver,
868 ) -> AstVisitorResult<MelInterpContext, Box<InterpLocatableError>> {
869 let context = use_log!(context, |log: LogMsgs| {
870 trace_with_loc!(
871 log,
872 ast.location.clone(),
873 "Evaluating member access expression"
874 )
875 });
876
877 use_constant!(ast.aug.constant, ast.location, context);
878
879 let context = driver.visit(&ast.base, self, context)?;
880
881 let base = match &context.val {
882 Some(v) => v.clone(),
883 None => {
884 return Err(InterpLocatableError {
885 error: InterpError::Assertion(
886 SuccessWithoutValue(
887 "base".to_string(),
888 "visit_member_access_expr".to_string(),
889 )
890 .into(),
891 )
892 .into(),
893 location: ast.base.location(),
894 context,
895 }
896 .into());
897 }
898 };
899
900 let (base_value, base_type) = match base {
901 TypedValue {
902 value: Value::Struct(sv),
903 tpe: Type::Struct(st),
904 } => (sv, st),
905 t => {
906 return Err(InterpLocatableError {
907 error: InterpError::Assertion(
908 InterpAssertion::TypeMismatch(ast.base.tipe(), t.tpe).into(),
909 )
910 .into(),
911 location: ast.base.location(),
912 context,
913 }
914 .into());
915 }
916 };
917
918 if Type::Struct(base_type.clone()) != ast.base.tipe() {
920 return Err(InterpLocatableError {
921 error: InterpError::Assertion(
922 InterpAssertion::TypeMismatch(ast.base.tipe(), Type::Struct(base_type)).into(),
923 )
924 .into(),
925 location: ast.base.location(),
926 context,
927 }
928 .into());
929 }
930
931 let member_type = match base_type.get_field(&ast.member.identifier) {
932 Some(v) => v.clone(),
933 None => {
934 return Err(InterpLocatableError {
935 error: InterpError::UnknownField(ast.member.identifier.clone()).into(),
936 location: ast.member.location.clone(),
937 context,
938 })?;
939 }
940 };
941
942 let member_value = match base_value.fields.get(&ast.member.identifier) {
943 Some(v) => v.clone(),
944 None => {
945 return Err(InterpLocatableError {
946 error: InterpError::UnknownField(ast.member.identifier.clone()).into(),
947 location: ast.member.location.clone(),
948 context,
949 }
950 .into());
951 }
952 };
953
954 if member_type != member_value.tpe {
955 return Err(InterpLocatableError {
956 error: InterpError::Assertion(
957 InterpAssertion::TypeMismatch(member_type.clone(), member_value.tpe.clone())
958 .into(),
959 )
960 .into(),
961 location: ast.member.location.clone(),
962 context,
963 }
964 .into());
965 }
966
967 Ok(context.update_val(Some(member_value)))
968 }
969}