Skip to main content

brooks_lib/mel/interpreter/
interpret.rs

1// brooks, Copyright 2026, Will Hawkins
2//
3// This file is part of brooks.
4
5// This file is free software: you can redistribute it and/or modify
6// it under the terms of the GNU General Public License as published by
7// the Free Software Foundation, either version 3 of the License, or
8// (at your option) any later version.
9//
10// This program is distributed in the hope that it will be useful,
11// but WITHOUT ANY WARRANTY; without even the implied warranty of
12// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13// GNU General Public License for more details.
14//
15// You should have received a copy of the GNU General Public License
16// along with this program. If not, see <https://www.gnu.org/licenses/>.
17
18use 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        // Make sure that the evaluated type and the analyzed type are the same.
919        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}