Skip to main content

brooks_lib/mel/
serializer.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
18//! Serialize a MEL AST
19
20use std::fmt::Display;
21
22use crate::common::TextLocation;
23use crate::mel::ast::{
24    BinaryInfixOperator, ComparisonOperator, LogicOperator, MathOperator, MemberAccessOperator,
25    StringConcatOperator, TernaryOperator,
26};
27use crate::mel::{
28    analysis::{self, Analyzed},
29    ast::{
30        self, Argument, ArgumentList, AstVisitor, AstVisitorDriver, AstVisitorResult, BinaryExpr,
31        FunctionCall, Identifier,
32    },
33};
34
35impl Display for MemberAccessOperator {
36    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
37        write!(f, " . ")
38    }
39}
40
41impl Display for BinaryInfixOperator {
42    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
43        match self {
44            BinaryInfixOperator::Logic(logic_operator) => write!(f, "{logic_operator}"),
45            BinaryInfixOperator::Comparison(comparison_operator) => {
46                write!(f, "{comparison_operator}")
47            }
48            BinaryInfixOperator::Math(math_operator) => write!(f, "{math_operator}"),
49            BinaryInfixOperator::Concat(string_concat_operator) => {
50                write!(f, "{string_concat_operator}")
51            }
52            BinaryInfixOperator::MemberAccess(member_access_operator) => {
53                write!(f, "{member_access_operator}")
54            }
55        }
56    }
57}
58
59impl Display for ComparisonOperator {
60    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
61        match self {
62            ComparisonOperator::Eq => write!(f, "=="),
63            ComparisonOperator::Ne => write!(f, "!="),
64            ComparisonOperator::Lt => write!(f, "<"),
65            ComparisonOperator::Lte => write!(f, "<="),
66            ComparisonOperator::Gt => write!(f, ">"),
67            ComparisonOperator::Gte => write!(f, ">="),
68            ComparisonOperator::Re => write!(f, "~="),
69            ComparisonOperator::IP => write!(f, "ipmatch"),
70        }
71    }
72}
73
74impl Display for LogicOperator {
75    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
76        match self {
77            LogicOperator::And => write!(f, "and"),
78            LogicOperator::Or => write!(f, "or"),
79        }
80    }
81}
82
83impl Display for StringConcatOperator {
84    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
85        write!(f, "concat")
86    }
87}
88
89impl Display for MathOperator {
90    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
91        match self {
92            MathOperator::Plus => write!(f, "plus"),
93            MathOperator::Minus => write!(f, "minus"),
94            MathOperator::Multiply => write!(f, "multiply"),
95            MathOperator::Divide => write!(f, "divide"),
96            MathOperator::Modulo => write!(f, "modulo"),
97        }
98    }
99}
100
101impl Display for TernaryOperator {
102    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
103        match self {
104            TernaryOperator::Question => write!(f, "?"),
105            TernaryOperator::Colon => write!(f, ":"),
106        }
107    }
108}
109
110impl Display for ast::Literal {
111    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
112        match self {
113            ast::Literal::Boolean(bl) => write!(f, "{bl}"),
114            ast::Literal::Number(nl) => write!(f, "{nl}"),
115            ast::Literal::String(sl) => write!(f, "{sl}"),
116            ast::Literal::Regex(rl) => write!(f, "{rl}"),
117            ast::Literal::IPAddress(ip) => write!(f, "{ip}"),
118        }
119    }
120}
121
122impl Display for ast::BooleanLiteral {
123    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
124        match self {
125            ast::BooleanLiteral::True => write!(f, "true"),
126            ast::BooleanLiteral::False => write!(f, "false"),
127        }
128    }
129}
130
131impl Display for ast::NumberLiteral {
132    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
133        write!(f, "{}", self.literal)
134    }
135}
136
137impl Display for ast::StringLiteral {
138    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
139        write!(f, "{}", self.literal)
140    }
141}
142
143impl Display for ast::RegexLiteral {
144    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
145        write!(f, "{}", self.literal)
146    }
147}
148
149impl Display for ast::IPAddressLiteral {
150    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
151        write!(f, "{}", self.literal)
152    }
153}
154
155impl Display for analysis::CompiledConstant {
156    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
157        match self {
158            analysis::CompiledConstant::Integer(i) => write!(f, "{i}"),
159            analysis::CompiledConstant::String(s) => write!(f, "{s}"),
160            analysis::CompiledConstant::Boolean(b) => write!(f, "{b}"),
161            analysis::CompiledConstant::IPAddress(ip) => write!(f, "{ip}"),
162        }
163    }
164}
165
166#[derive(Debug, Clone, Default)]
167pub struct AstTextSerializerError {}
168
169pub struct AstTextSerializer {}
170
171pub struct AstTextSerializerContext {
172    pub serialized: String,
173    pub indent: usize,
174}
175
176impl AstTextSerializerContext {
177    fn indent(&self) -> Self {
178        AstTextSerializerContext {
179            serialized: self.serialized.clone(),
180            indent: self.indent + 1,
181        }
182    }
183    fn unindent(&self) -> Self {
184        AstTextSerializerContext {
185            serialized: self.serialized.clone(),
186            indent: self.indent - 1,
187        }
188    }
189    fn append(&self, addition: String) -> Self {
190        AstTextSerializerContext {
191            serialized: self.serialized.clone() + &addition,
192            indent: self.indent,
193        }
194    }
195}
196
197impl AstVisitor<AstTextSerializerContext, (), AstTextSerializerError> for AstTextSerializer {
198    fn visit_function_call(
199        &self,
200        ast: &FunctionCall<()>,
201        context: AstTextSerializerContext,
202        driver: &AstVisitorDriver,
203    ) -> AstVisitorResult<AstTextSerializerContext, AstTextSerializerError> {
204        let mut context =
205            context.append(("\t".repeat(context.indent) + "Function Call:\n").to_string());
206
207        context = context.indent();
208        context = context.append("\t".repeat(context.indent) + "Callee:\n");
209
210        context = driver.visit(&ast.callee, self, context.indent())?;
211        context = context.append("\n".into());
212        context = context.unindent();
213        context = context.unindent();
214
215        context = self.visit_argument_list(&ast.arguments, context.indent(), driver)?;
216        Ok(context.unindent())
217    }
218
219    fn visit_identifier(
220        &self,
221        ast: &Identifier<()>,
222        context: AstTextSerializerContext,
223        _driver: &AstVisitorDriver,
224    ) -> AstVisitorResult<AstTextSerializerContext, AstTextSerializerError> {
225        let output = "\t".repeat(context.indent) + "Identifier: " + &ast.identifier;
226        Ok(context.append(output))
227    }
228
229    fn visit_argument_list(
230        &self,
231        ast: &ArgumentList<()>,
232        context: AstTextSerializerContext,
233        driver: &AstVisitorDriver,
234    ) -> AstVisitorResult<AstTextSerializerContext, AstTextSerializerError> {
235        let mut context = context.append("\t".repeat(context.indent) + "Arguments:\n");
236        context = context.indent();
237        let mut first = true;
238        for arg in &ast.arguments {
239            if !first {
240                context = context.append("\n".into())
241            }
242            first = false;
243            context = self.visit_argument(arg, context, driver)?;
244        }
245        Ok(context.unindent())
246    }
247
248    fn visit_argument(
249        &self,
250        ast: &Argument<()>,
251        context: AstTextSerializerContext,
252        driver: &AstVisitorDriver,
253    ) -> AstVisitorResult<AstTextSerializerContext, AstTextSerializerError> {
254        driver.visit(&ast.expr, self, context)
255    }
256
257    fn visit_binary_expr(
258        &self,
259        ast: &BinaryExpr<()>,
260        context: AstTextSerializerContext,
261        driver: &AstVisitorDriver,
262    ) -> AstVisitorResult<AstTextSerializerContext, AstTextSerializerError> {
263        let mut context = context.append("\t".repeat(context.indent) + "Binary Expression:\n");
264
265        context = context.indent();
266
267        context = context.append("\t".repeat(context.indent) + "Left:\n");
268        context = driver.visit(&ast.left, self, context.indent())?;
269        context = context.unindent();
270
271        context = context.append("\n".into());
272
273        context = context.append("\t".repeat(context.indent) + &format!("Operation: {}\n", ast.op));
274
275        context = context.append("\t".repeat(context.indent) + "Right:\n");
276        context = driver.visit(&ast.right, self, context.indent())?;
277        context = context.unindent();
278
279        context = context.unindent();
280        Ok(context)
281    }
282
283    fn visit_literal(
284        &self,
285        ast: (&ast::Literal, &TextLocation, &()),
286        context: AstTextSerializerContext,
287        _driver: &AstVisitorDriver,
288    ) -> AstVisitorResult<AstTextSerializerContext, AstTextSerializerError> {
289        Ok(context.append("\t".repeat(context.indent) + "Literal: " + &ast.0.to_string()))
290    }
291
292    fn visit_ternary_expr(
293        &self,
294        ast: &ast::TernaryExpr<()>,
295        context: AstTextSerializerContext,
296        driver: &AstVisitorDriver,
297    ) -> AstVisitorResult<AstTextSerializerContext, AstTextSerializerError> {
298        let mut context = context.append("\t".repeat(context.indent) + "Ternary Expression:\n");
299
300        context = context.indent();
301
302        context = context.append("\t".repeat(context.indent) + "Condition:\n");
303        context = driver.visit(&ast.condition, self, context.indent())?;
304        context = context.unindent();
305
306        context = context.append("\n".into());
307
308        context = context.append("\t".repeat(context.indent) + "Yes:\n");
309        context = driver.visit(&ast.yes, self, context.indent())?;
310        context = context.unindent();
311
312        context = context.append("\n".into());
313
314        context = context.append("\t".repeat(context.indent) + "No:\n");
315        context = driver.visit(&ast.no, self, context.indent())?;
316        context = context.unindent();
317
318        context = context.unindent();
319        Ok(context)
320    }
321
322    fn visit_member_access_expr(
323        &self,
324        ast: &ast::MemberAccessExpression<()>,
325        context: AstTextSerializerContext,
326        driver: &AstVisitorDriver,
327    ) -> AstVisitorResult<AstTextSerializerContext, AstTextSerializerError> {
328        let mut context =
329            context.append("\t".repeat(context.indent) + "Member Access Expression:\n");
330
331        context = context.indent();
332        context = context.append("\t".repeat(context.indent) + "Base:\n");
333        context = driver.visit(&ast.base, self, context.indent())?;
334        context = context.append("\n".into());
335        context = context.unindent();
336
337        context = context.append("\t".repeat(context.indent) + "Member:\n");
338        context = self.visit_identifier(&ast.member, context.indent(), driver)?;
339        context = context.unindent();
340
341        context = context.unindent();
342        Ok(context)
343    }
344}
345
346#[cfg(test)]
347mod tests {
348    use crate::mel::{
349        compiler::compile,
350        serializer::{AstTextSerializer, AstTextSerializerContext, AstVisitorDriver},
351    };
352
353    #[test]
354    fn serialize_function_call() {
355        let code = "testing(one(hello),b)";
356        let expected = "Function Call:
357\tCallee:
358\t\tIdentifier: testing
359\tArguments:
360\t\tFunction Call:
361\t\t\tCallee:
362\t\t\t\tIdentifier: one
363\t\t\tArguments:
364\t\t\t\tIdentifier: hello
365\t\tIdentifier: b";
366
367        let compile_result = compile(code);
368        let ast = compile_result.expect("Compilation error");
369
370        let driver = AstVisitorDriver {};
371        let visitor = AstTextSerializer {};
372        let context = AstTextSerializerContext {
373            serialized: "".to_string(),
374            indent: 0,
375        };
376        let result = driver
377            .visit(&ast, &visitor, context)
378            .expect("Could not serialize");
379        pretty_assertions::assert_eq!(result.serialized, expected);
380    }
381
382    #[test]
383    fn serialize_binary_expr() {
384        let code = "a and b";
385        let expected = "Binary Expression:
386\tLeft:
387\t\tIdentifier: a
388\tOperation: and
389\tRight:
390\t\tIdentifier: b";
391
392        let compile_result = compile(code);
393        let ast = compile_result.expect("Compilation error");
394
395        let driver = AstVisitorDriver {};
396        let visitor = AstTextSerializer {};
397        let context = AstTextSerializerContext {
398            serialized: "".to_string(),
399            indent: 0,
400        };
401        let result = driver
402            .visit(&ast, &visitor, context)
403            .expect("Could not serialize");
404        pretty_assertions::assert_eq!(result.serialized, expected);
405    }
406
407    #[test]
408    fn serialize_binary_regex_expr() {
409        let code = "a ~= \"/[0-9]/\"";
410        let expected = "Binary Expression:
411\tLeft:
412\t\tIdentifier: a
413\tOperation: ~=
414\tRight:
415\t\tLiteral: /[0-9]/";
416
417        let compile_result = compile(code);
418        let ast = compile_result.expect("Compilation error");
419
420        let driver = AstVisitorDriver {};
421        let visitor = AstTextSerializer {};
422        let context = AstTextSerializerContext {
423            serialized: "".to_string(),
424            indent: 0,
425        };
426        let result = driver
427            .visit(&ast, &visitor, context)
428            .expect("Could not serialize");
429        pretty_assertions::assert_eq!(result.serialized, expected);
430    }
431
432    #[test]
433    fn serialize_binary_math_expr() {
434        let code = "a + b";
435        let expected = "Binary Expression:
436\tLeft:
437\t\tIdentifier: a
438\tOperation: plus
439\tRight:
440\t\tIdentifier: b";
441
442        let compile_result = compile(code);
443        let ast = compile_result.expect("Compilation error");
444
445        let driver = AstVisitorDriver {};
446        let visitor = AstTextSerializer {};
447        let context = AstTextSerializerContext {
448            serialized: "".to_string(),
449            indent: 0,
450        };
451        let result = driver
452            .visit(&ast, &visitor, context)
453            .expect("Could not serialize");
454        pretty_assertions::assert_eq!(result.serialized, expected);
455    }
456
457    #[test]
458    fn serialize_binary_math_expr_associative() {
459        let code = "a + b - c";
460        let expected = "Binary Expression:
461\tLeft:
462\t\tBinary Expression:
463\t\t\tLeft:
464\t\t\t\tIdentifier: a
465\t\t\tOperation: plus
466\t\t\tRight:
467\t\t\t\tIdentifier: b
468\tOperation: minus
469\tRight:
470\t\tIdentifier: c";
471
472        let compile_result = compile(code);
473        let ast = compile_result.expect("Compilation error");
474        let driver = AstVisitorDriver {};
475        let visitor = AstTextSerializer {};
476        let context = AstTextSerializerContext {
477            serialized: "".to_string(),
478            indent: 0,
479        };
480        let result = driver
481            .visit(&ast, &visitor, context)
482            .expect("Could not serialize");
483        pretty_assertions::assert_eq!(result.serialized, expected);
484    }
485
486    #[test]
487    fn serialize_binary_math_expr_grouped() {
488        let code = "(a + b) - c";
489        let expected = "Binary Expression:
490\tLeft:
491\t\tBinary Expression:
492\t\t\tLeft:
493\t\t\t\tIdentifier: a
494\t\t\tOperation: plus
495\t\t\tRight:
496\t\t\t\tIdentifier: b
497\tOperation: minus
498\tRight:
499\t\tIdentifier: c";
500
501        let compile_result = compile(code);
502        let ast = compile_result.expect("Compilation error");
503
504        let driver = AstVisitorDriver {};
505        let visitor = AstTextSerializer {};
506        let context = AstTextSerializerContext {
507            serialized: "".to_string(),
508            indent: 0,
509        };
510        let result = driver
511            .visit(&ast, &visitor, context)
512            .expect("Could not serialize");
513        pretty_assertions::assert_eq!(result.serialized, expected);
514    }
515
516    #[test]
517    fn serialize_binary_math_expr_grouped_right() {
518        let code = "a + (b - c)";
519        let expected = "Binary Expression:
520\tLeft:
521\t\tIdentifier: a
522\tOperation: plus
523\tRight:
524\t\tBinary Expression:
525\t\t\tLeft:
526\t\t\t\tIdentifier: b
527\t\t\tOperation: minus
528\t\t\tRight:
529\t\t\t\tIdentifier: c";
530
531        let compile_result = compile(code);
532        let ast = compile_result.expect("Compilation error");
533
534        let driver = AstVisitorDriver {};
535        let visitor = AstTextSerializer {};
536        let context = AstTextSerializerContext {
537            serialized: "".to_string(),
538            indent: 0,
539        };
540        let result = driver
541            .visit(&ast, &visitor, context)
542            .expect("Could not serialize");
543        pretty_assertions::assert_eq!(result.serialized, expected);
544    }
545
546    #[test]
547    fn serialize_binary_math_expr_grouped_right2() {
548        let code = "a * (b % c)";
549        let expected = "Binary Expression:
550\tLeft:
551\t\tIdentifier: a
552\tOperation: multiply
553\tRight:
554\t\tBinary Expression:
555\t\t\tLeft:
556\t\t\t\tIdentifier: b
557\t\t\tOperation: modulo
558\t\t\tRight:
559\t\t\t\tIdentifier: c";
560
561        let compile_result = compile(code);
562        let ast = compile_result.expect("Compilation error");
563
564        let driver = AstVisitorDriver {};
565        let visitor = AstTextSerializer {};
566        let context = AstTextSerializerContext {
567            serialized: "".to_string(),
568            indent: 0,
569        };
570        let result = driver
571            .visit(&ast, &visitor, context)
572            .expect("Could not serialize");
573        pretty_assertions::assert_eq!(result.serialized, expected);
574    }
575
576    #[test]
577    fn serialize_boolean_expression() {
578        let code = "true or false";
579        let expected = "Binary Expression:
580\tLeft:
581\t\tLiteral: true
582\tOperation: or
583\tRight:
584\t\tLiteral: false";
585
586        let compile_result = compile(code);
587        let ast = compile_result.expect("Compilation error");
588
589        let driver = AstVisitorDriver {};
590        let visitor = AstTextSerializer {};
591        let context = AstTextSerializerContext {
592            serialized: "".to_string(),
593            indent: 0,
594        };
595        let result = driver
596            .visit(&ast, &visitor, context)
597            .expect("Could not serialize");
598        pretty_assertions::assert_eq!(result.serialized, expected);
599    }
600
601    #[test]
602    fn serialize_boolean_expression2() {
603        let code = "true < false";
604        let expected = "Binary Expression:
605\tLeft:
606\t\tLiteral: true
607\tOperation: <
608\tRight:
609\t\tLiteral: false";
610
611        let compile_result = compile(code);
612        let ast = compile_result.expect("Compilation error");
613
614        let driver = AstVisitorDriver {};
615        let visitor = AstTextSerializer {};
616        let context = AstTextSerializerContext {
617            serialized: "".to_string(),
618            indent: 0,
619        };
620        let result = driver
621            .visit(&ast, &visitor, context)
622            .expect("Could not serialize");
623        pretty_assertions::assert_eq!(result.serialized, expected);
624    }
625
626    #[test]
627    fn serialize_boolean_expression3() {
628        let code = "(1 < 2) != true";
629        let expected = "Binary Expression:
630\tLeft:
631\t\tBinary Expression:
632\t\t\tLeft:
633\t\t\t\tLiteral: 1
634\t\t\tOperation: <
635\t\t\tRight:
636\t\t\t\tLiteral: 2
637\tOperation: !=
638\tRight:
639\t\tLiteral: true";
640        let compile_result = compile(code);
641        let ast = compile_result.expect("Compilation error");
642
643        let driver = AstVisitorDriver {};
644        let visitor = AstTextSerializer {};
645        let context = AstTextSerializerContext {
646            serialized: "".to_string(),
647            indent: 0,
648        };
649        let result = driver
650            .visit(&ast, &visitor, context)
651            .expect("Could not serialize");
652        pretty_assertions::assert_eq!(result.serialized, expected);
653    }
654
655    #[test]
656    fn serialize_number_expression() {
657        let code = "5 + 4";
658        let expected = "Binary Expression:
659\tLeft:
660\t\tLiteral: 5
661\tOperation: plus
662\tRight:
663\t\tLiteral: 4";
664
665        let compile_result = compile(code);
666        let ast = compile_result.expect("Compilation error");
667
668        let driver = AstVisitorDriver {};
669        let visitor = AstTextSerializer {};
670        let context = AstTextSerializerContext {
671            serialized: "".to_string(),
672            indent: 0,
673        };
674        let result = driver
675            .visit(&ast, &visitor, context)
676            .expect("Could not serialize");
677        pretty_assertions::assert_eq!(result.serialized, expected);
678    }
679
680    #[test]
681    fn serialize_string_expression() {
682        let code = "\"testing\" . \"one\"";
683        let expected = "Binary Expression:
684\tLeft:
685\t\tLiteral: testing
686\tOperation: concat
687\tRight:
688\t\tLiteral: one";
689
690        let compile_result = compile(code);
691        let ast = compile_result.expect("Compilation error");
692
693        let driver = AstVisitorDriver {};
694        let visitor = AstTextSerializer {};
695        let context = AstTextSerializerContext {
696            serialized: "".to_string(),
697            indent: 0,
698        };
699        let result = driver
700            .visit(&ast, &visitor, context)
701            .expect("Could not serialize");
702        pretty_assertions::assert_eq!(result.serialized, expected);
703    }
704
705    #[test]
706    fn serialize_ternary_expr() {
707        let code = "true ? 1 : 5";
708        let expected = "Ternary Expression:
709\tCondition:
710\t\tLiteral: true
711\tYes:
712\t\tLiteral: 1
713\tNo:
714\t\tLiteral: 5";
715
716        let compile_result = compile(code);
717        let ast = compile_result.expect("Compilation error");
718
719        let driver = AstVisitorDriver {};
720        let visitor = AstTextSerializer {};
721        let context = AstTextSerializerContext {
722            serialized: "".to_string(),
723            indent: 0,
724        };
725        let result = driver
726            .visit(&ast, &visitor, context)
727            .expect("Could not serialize");
728        pretty_assertions::assert_eq!(result.serialized, expected);
729    }
730
731    #[test]
732    fn serialize_ternary_expr_proper_associativity() {
733        let code = "true ? a : false ? \"b\" : 10";
734        let expected = "Ternary Expression:
735\tCondition:
736\t\tLiteral: true
737\tYes:
738\t\tIdentifier: a
739\tNo:
740\t\tTernary Expression:
741\t\t\tCondition:
742\t\t\t\tLiteral: false
743\t\t\tYes:
744\t\t\t\tLiteral: b
745\t\t\tNo:
746\t\t\t\tLiteral: 10";
747
748        let compile_result = compile(code);
749        let ast = compile_result.expect("Compilation error");
750
751        let driver = AstVisitorDriver {};
752        let visitor = AstTextSerializer {};
753        let context = AstTextSerializerContext {
754            serialized: "".to_string(),
755            indent: 0,
756        };
757        let result = driver
758            .visit(&ast, &visitor, context)
759            .expect("Could not serialize");
760        pretty_assertions::assert_eq!(result.serialized, expected);
761    }
762
763    #[test]
764    fn serialize_member_access_expression() {
765        let code = "a^b^c";
766        let expected = "Member Access Expression:
767\tBase:
768\t\tMember Access Expression:
769\t\t\tBase:
770\t\t\t\tIdentifier: a
771\t\t\tMember:
772\t\t\t\tIdentifier: b
773\tMember:
774\t\tIdentifier: c";
775
776        let compile_result = compile(code);
777        let ast = compile_result.expect("Compilation error");
778
779        let driver = AstVisitorDriver {};
780        let visitor = AstTextSerializer {};
781        let context = AstTextSerializerContext {
782            serialized: "".to_string(),
783            indent: 0,
784        };
785        let result = driver
786            .visit(&ast, &visitor, context)
787            .expect("Could not serialize");
788        pretty_assertions::assert_eq!(result.serialized, expected);
789    }
790}
791
792impl AstVisitor<AstTextSerializerContext, Analyzed, AstTextSerializerError> for AstTextSerializer {
793    fn visit_function_call(
794        &self,
795        ast: &FunctionCall<Analyzed>,
796        context: AstTextSerializerContext,
797        driver: &AstVisitorDriver,
798    ) -> AstVisitorResult<AstTextSerializerContext, AstTextSerializerError> {
799        let mut context =
800            context.append(("\t".repeat(context.indent) + "Function Call:\n").to_string());
801
802        context = context.indent();
803        context = context.append("\t".repeat(context.indent) + "Callee:\n");
804
805        context = driver.visit(&ast.callee, self, context.indent())?;
806        context = context.append("\n".into());
807        context = context.unindent();
808        context = context.unindent();
809
810        context = self.visit_argument_list(&ast.arguments, context.indent(), driver)?;
811
812        context = context.append("\n".into());
813        context = context.append("\t".repeat(context.indent) + &format!("Type: {}", ast.aug.tipe));
814
815        Ok(context.unindent())
816    }
817
818    fn visit_identifier(
819        &self,
820        ast: &Identifier<Analyzed>,
821        context: AstTextSerializerContext,
822        _driver: &AstVisitorDriver,
823    ) -> AstVisitorResult<AstTextSerializerContext, AstTextSerializerError> {
824        let output = "\t".repeat(context.indent) + "Identifier: " + &ast.identifier;
825        Ok(context.append(output))
826    }
827
828    fn visit_argument_list(
829        &self,
830        ast: &ArgumentList<Analyzed>,
831        context: AstTextSerializerContext,
832        driver: &AstVisitorDriver,
833    ) -> AstVisitorResult<AstTextSerializerContext, AstTextSerializerError> {
834        let mut context = context.append("\t".repeat(context.indent) + "Arguments:\n");
835        context = context.indent();
836        let mut first = true;
837        for arg in &ast.arguments {
838            if !first {
839                context = context.append("\n".into())
840            }
841            first = false;
842            context = self.visit_argument(arg, context, driver)?;
843        }
844        Ok(context.unindent())
845    }
846
847    fn visit_argument(
848        &self,
849        ast: &Argument<Analyzed>,
850        context: AstTextSerializerContext,
851        driver: &AstVisitorDriver,
852    ) -> AstVisitorResult<AstTextSerializerContext, AstTextSerializerError> {
853        driver.visit(&ast.expr, self, context)
854    }
855
856    fn visit_binary_expr(
857        &self,
858        ast: &BinaryExpr<Analyzed>,
859        context: AstTextSerializerContext,
860        driver: &AstVisitorDriver,
861    ) -> AstVisitorResult<AstTextSerializerContext, AstTextSerializerError> {
862        let mut context = context.append("\t".repeat(context.indent) + "Binary Expression:\n");
863
864        context = context.indent();
865
866        context = context.append("\t".repeat(context.indent) + "Left:\n");
867        context = driver.visit(&ast.left, self, context.indent())?;
868        context = context.unindent();
869
870        context = context.append("\n".into());
871
872        context = context.append("\t".repeat(context.indent) + &format!("Operation: {}\n", ast.op));
873
874        context = context.append("\t".repeat(context.indent) + "Right:\n");
875        context = driver.visit(&ast.right, self, context.indent())?;
876        context = context.unindent();
877
878        context = context.append("\n".into());
879        context = context.append("\t".repeat(context.indent) + &format!("Type: {}", ast.aug.tipe));
880        context = context.append("\n".into());
881
882        context = context.append(
883            "\t".repeat(context.indent)
884                + &ast
885                    .aug
886                    .constant
887                    .as_ref()
888                    .map(|c| format!("Constant value: {c}"))
889                    .unwrap_or("Not a constant".to_string()),
890        );
891
892        context = context.unindent();
893        Ok(context)
894    }
895
896    fn visit_literal(
897        &self,
898        ast: (&ast::Literal, &TextLocation, &Analyzed),
899        context: AstTextSerializerContext,
900        _driver: &AstVisitorDriver,
901    ) -> AstVisitorResult<AstTextSerializerContext, AstTextSerializerError> {
902        let mut context =
903            context.append("\t".repeat(context.indent) + "Literal: " + &ast.0.to_string());
904        context = context.append("\n".into());
905
906        context = context.indent();
907
908        context = context.append("\t".repeat(context.indent) + &format!("Type: {}", ast.2.tipe));
909        context = context.append("\n".into());
910
911        context = context.append(
912            "\t".repeat(context.indent)
913                + &ast
914                    .2
915                    .constant
916                    .as_ref()
917                    .map(|c| format!("Constant value: {c}"))
918                    .unwrap_or("Not a constant".to_string()),
919        );
920        Ok(context.unindent())
921    }
922
923    fn visit_ternary_expr(
924        &self,
925        ast: &ast::TernaryExpr<Analyzed>,
926        context: AstTextSerializerContext,
927        driver: &AstVisitorDriver,
928    ) -> AstVisitorResult<AstTextSerializerContext, AstTextSerializerError> {
929        let mut context = context.append("\t".repeat(context.indent) + "Ternary Expression:\n");
930
931        context = context.indent();
932
933        context = context.append("\t".repeat(context.indent) + "Condition:\n");
934        context = driver.visit(&ast.condition, self, context.indent())?;
935        context = context.unindent();
936
937        context = context.append("\n".into());
938
939        context = context.append("\t".repeat(context.indent) + "Yes:\n");
940        context = driver.visit(&ast.yes, self, context.indent())?;
941        context = context.unindent();
942
943        context = context.append("\n".into());
944
945        context = context.append("\t".repeat(context.indent) + "No:\n");
946        context = driver.visit(&ast.no, self, context.indent())?;
947        context = context.unindent();
948
949        context = context.append("\n".into());
950
951        context = context.append("\t".repeat(context.indent) + &format!("Type: {}", ast.aug.tipe));
952        context = context.append("\n".into());
953
954        context = context.append(
955            "\t".repeat(context.indent)
956                + &ast
957                    .aug
958                    .constant
959                    .as_ref()
960                    .map(|c| format!("Constant value: {c}"))
961                    .unwrap_or("Not a constant".to_string()),
962        );
963
964        context = context.unindent();
965        Ok(context)
966    }
967
968    fn visit_member_access_expr(
969        &self,
970        ast: &ast::MemberAccessExpression<Analyzed>,
971        context: AstTextSerializerContext,
972        driver: &AstVisitorDriver,
973    ) -> AstVisitorResult<AstTextSerializerContext, AstTextSerializerError> {
974        let mut context =
975            context.append("\t".repeat(context.indent) + "Member Access Expression:\n");
976
977        context = context.indent();
978        context = context.append("\t".repeat(context.indent) + "Base:\n");
979        context = driver.visit(&ast.base, self, context.indent())?;
980        context = context.append("\n".into());
981        context = context.unindent();
982
983        context = context.append("\t".repeat(context.indent) + "Member:\n");
984        context = self.visit_identifier(&ast.member, context.indent(), driver)?;
985        context = context.append("\n".into());
986        context = context.unindent();
987
988        context = context.append("\t".repeat(context.indent) + &format!("Type: {}", ast.aug.tipe));
989        context = context.append("\n".into());
990
991        context = context.append(
992            "\t".repeat(context.indent)
993                + &ast
994                    .aug
995                    .constant
996                    .as_ref()
997                    .map(|c| format!("Constant value: {c}"))
998                    .unwrap_or("Not a constant".to_string()),
999        );
1000
1001        context = context.unindent();
1002
1003        Ok(context)
1004    }
1005}
1006
1007#[cfg(test)]
1008mod analyzed_serializer_tests {
1009    use crate::mel::types::{
1010        self, SimpleParamTypeChecker, Struct,
1011        Type::{self, Function},
1012    };
1013    use crate::mel::{
1014        analysis::{MelAnalysisContext, MelOptimizer, MelTypeChecker},
1015        compiler::compile,
1016        serializer::{AstTextSerializer, AstTextSerializerContext, AstVisitorDriver},
1017    };
1018
1019    #[test]
1020    fn serialize_analyzed_string_expression() {
1021        let code = "\"testing\" . \"one\"";
1022        let expected = "Binary Expression:
1023\tLeft:
1024\t\tLiteral: testing
1025\t\t\tType: String
1026\t\t\tConstant value: testing
1027\tOperation: concat
1028\tRight:
1029\t\tLiteral: one
1030\t\t\tType: String
1031\t\t\tConstant value: one
1032\tType: String
1033\tConstant value: testingone";
1034
1035        let compile_result = compile(code);
1036        let ast = compile_result.expect("Compilation error");
1037
1038        let driver = AstVisitorDriver {};
1039        let visitor = MelTypeChecker {};
1040        let context = MelAnalysisContext::default();
1041        let result = driver
1042            .visit(&ast, &visitor, context)
1043            .expect("Could not analyze");
1044
1045        let driver = AstVisitorDriver {};
1046        let visitor = MelOptimizer {};
1047        let result = driver
1048            .visit(&ast, &visitor, result)
1049            .expect("Could not analyze");
1050
1051        let result = result.expr.expect("Could not get the analyzed expression");
1052
1053        let driver = AstVisitorDriver {};
1054        let visitor = AstTextSerializer {};
1055        let context = AstTextSerializerContext {
1056            serialized: "".to_string(),
1057            indent: 0,
1058        };
1059        let result = driver
1060            .visit(&result, &visitor, context)
1061            .expect("Could not serialize");
1062        pretty_assertions::assert_eq!(result.serialized, expected);
1063    }
1064
1065    #[test]
1066    fn serialize_analyzed_function_call() {
1067        let code = "use(5)";
1068        let expected = "Function Call:
1069\tCallee:
1070\t\tIdentifier: use
1071\tArguments:
1072\t\tLiteral: 5
1073\t\t\tType: Integer
1074\t\t\tNot a constant
1075\tType: Integer";
1076
1077        let compile_result = compile(code);
1078        let ast = compile_result.expect("Compilation error");
1079
1080        let driver = AstVisitorDriver {};
1081        let visitor = MelTypeChecker {};
1082        let mut context = MelAnalysisContext::default();
1083
1084        context = context.update_scopes(&context.scopes.insert(
1085            "use",
1086            Function(
1087                "use".to_string(),
1088                || Type::Integer,
1089                || {
1090                    Box::new(SimpleParamTypeChecker {
1091                        p: types::ParameterTypesList {
1092                            params: vec![Type::Integer],
1093                        },
1094                    })
1095                },
1096            ),
1097        ));
1098
1099        let result = driver
1100            .visit(&ast, &visitor, context)
1101            .expect("Could not analyze");
1102
1103        let driver = AstVisitorDriver {};
1104        let visitor = MelOptimizer {};
1105        let result = driver
1106            .visit(&ast, &visitor, result)
1107            .expect("Could not analyze");
1108
1109        let result = result.expr.expect("Could not get the analyzed expression");
1110
1111        let driver = AstVisitorDriver {};
1112        let visitor = AstTextSerializer {};
1113        let context = AstTextSerializerContext {
1114            serialized: "".to_string(),
1115            indent: 0,
1116        };
1117        let result = driver
1118            .visit(&result, &visitor, context)
1119            .expect("Could not serialize");
1120        pretty_assertions::assert_eq!(result.serialized, expected);
1121    }
1122
1123    #[test]
1124    fn serialize_analyzed_comparison_expr() {
1125        let code = "5 < 4";
1126        let expected = "Binary Expression:
1127\tLeft:
1128\t\tLiteral: 5
1129\t\t\tType: Integer
1130\t\t\tConstant value: 5
1131\tOperation: <
1132\tRight:
1133\t\tLiteral: 4
1134\t\t\tType: Integer
1135\t\t\tConstant value: 4
1136\tType: Bool
1137\tNot a constant";
1138
1139        let compile_result = compile(code);
1140        let ast = compile_result.expect("Compilation error");
1141
1142        let driver = AstVisitorDriver {};
1143        let visitor = MelTypeChecker {};
1144        let context = MelAnalysisContext::default();
1145
1146        let result = driver
1147            .visit(&ast, &visitor, context)
1148            .expect("Could not analyze");
1149
1150        let driver = AstVisitorDriver {};
1151        let visitor = MelOptimizer {};
1152        let result = driver
1153            .visit(&ast, &visitor, result)
1154            .expect("Could not analyze");
1155
1156        let result = result.expr.expect("Could not get the analyzed expression");
1157
1158        let driver = AstVisitorDriver {};
1159        let visitor = AstTextSerializer {};
1160        let context = AstTextSerializerContext {
1161            serialized: "".to_string(),
1162            indent: 0,
1163        };
1164        let result = driver
1165            .visit(&result, &visitor, context)
1166            .expect("Could not serialize");
1167        pretty_assertions::assert_eq!(result.serialized, expected);
1168    }
1169
1170    #[test]
1171    fn serialize_analyzed_ternary_expr() {
1172        let code = "true ? 1 : 5";
1173        let expected = "Ternary Expression:
1174\tCondition:
1175\t\tLiteral: true
1176\t\t\tType: Bool
1177\t\t\tConstant value: true
1178\tYes:
1179\t\tLiteral: 1
1180\t\t\tType: Integer
1181\t\t\tConstant value: 1
1182\tNo:
1183\t\tLiteral: 5
1184\t\t\tType: Integer
1185\t\t\tConstant value: 5
1186\tType: Integer
1187\tConstant value: 1";
1188
1189        let compile_result = compile(code);
1190        let ast = compile_result.expect("Compilation error");
1191
1192        let driver = AstVisitorDriver {};
1193        let visitor = MelTypeChecker {};
1194        let context = MelAnalysisContext::default();
1195
1196        let result = driver
1197            .visit(&ast, &visitor, context)
1198            .expect("Could not analyze");
1199
1200        let driver = AstVisitorDriver {};
1201        let visitor = MelOptimizer {};
1202        let result = driver
1203            .visit(&ast, &visitor, result)
1204            .expect("Could not analyze");
1205
1206        let result = result.expr.expect("Could not get the analyzed expression");
1207
1208        let driver = AstVisitorDriver {};
1209        let visitor = AstTextSerializer {};
1210        let context = AstTextSerializerContext {
1211            serialized: "".to_string(),
1212            indent: 0,
1213        };
1214        let result = driver
1215            .visit(&result, &visitor, context)
1216            .expect("Could not serialize");
1217        pretty_assertions::assert_eq!(result.serialized, expected);
1218    }
1219
1220    #[test]
1221    fn serialize_analyzed_ternary_expr2() {
1222        let code = "false ? 1 : (5 + 2 + 3)";
1223        let expected = "Ternary Expression:
1224\tCondition:
1225\t\tLiteral: false
1226\t\t\tType: Bool
1227\t\t\tConstant value: false
1228\tYes:
1229\t\tLiteral: 1
1230\t\t\tType: Integer
1231\t\t\tConstant value: 1
1232\tNo:
1233\t\tBinary Expression:
1234\t\t\tLeft:
1235\t\t\t\tBinary Expression:
1236\t\t\t\t\tLeft:
1237\t\t\t\t\t\tLiteral: 5
1238\t\t\t\t\t\t\tType: Integer
1239\t\t\t\t\t\t\tConstant value: 5
1240\t\t\t\t\tOperation: plus
1241\t\t\t\t\tRight:
1242\t\t\t\t\t\tLiteral: 2
1243\t\t\t\t\t\t\tType: Integer
1244\t\t\t\t\t\t\tConstant value: 2
1245\t\t\t\t\tType: Integer
1246\t\t\t\t\tConstant value: 7
1247\t\t\tOperation: plus
1248\t\t\tRight:
1249\t\t\t\tLiteral: 3
1250\t\t\t\t\tType: Integer
1251\t\t\t\t\tConstant value: 3
1252\t\t\tType: Integer
1253\t\t\tConstant value: 10
1254\tType: Integer
1255\tConstant value: 10";
1256
1257        let compile_result = compile(code);
1258        let ast = compile_result.expect("Compilation error");
1259
1260        let driver = AstVisitorDriver {};
1261        let visitor = MelTypeChecker {};
1262        let context = MelAnalysisContext::default();
1263
1264        let result = driver
1265            .visit(&ast, &visitor, context)
1266            .expect("Could not analyze");
1267
1268        let driver = AstVisitorDriver {};
1269        let visitor = MelOptimizer {};
1270        let result = driver
1271            .visit(&ast, &visitor, result)
1272            .expect("Could not analyze");
1273
1274        let result = result.expr.expect("Could not get the analyzed expression");
1275
1276        let driver = AstVisitorDriver {};
1277        let visitor = AstTextSerializer {};
1278        let context = AstTextSerializerContext {
1279            serialized: "".to_string(),
1280            indent: 0,
1281        };
1282        let result = driver
1283            .visit(&result, &visitor, context)
1284            .expect("Could not serialize");
1285        pretty_assertions::assert_eq!(result.serialized, expected);
1286    }
1287
1288    #[test]
1289    fn serialize_analyzed_member_access_binary_expr_nested() {
1290        let code = "req^incoming^headers . \"testing\"";
1291        let expected = "Binary Expression:
1292\tLeft:
1293\t\tMember Access Expression:
1294\t\t\tBase:
1295\t\t\t\tMember Access Expression:
1296\t\t\t\t\tBase:
1297\t\t\t\t\t\tIdentifier: req
1298\t\t\t\t\tMember:
1299\t\t\t\t\t\tIdentifier: incoming
1300\t\t\t\t\tType: Struct: Name: headers, Fields: headers
1301\t\t\t\t\tNot a constant
1302\t\t\tMember:
1303\t\t\t\tIdentifier: headers
1304\t\t\tType: String
1305\t\t\tNot a constant
1306\tOperation: concat
1307\tRight:
1308\t\tLiteral: testing
1309\t\t\tType: String
1310\t\t\tNot a constant
1311\tType: String
1312\tNot a constant";
1313
1314        let compile_result = compile(code);
1315        let ast = compile_result.expect("Compilation error");
1316
1317        let driver = AstVisitorDriver {};
1318        let visitor = MelTypeChecker {};
1319        let mut context = MelAnalysisContext::default();
1320
1321        let mut headers = Struct::new("headers");
1322        headers.insert_field("headers", Type::String);
1323
1324        let mut reqs = Struct::new("req");
1325        reqs.insert_field("incoming", Type::Struct(headers));
1326
1327        context = context.update_scopes(&context.scopes.insert("req", Type::Struct(reqs)));
1328
1329        let result = driver
1330            .visit(&ast, &visitor, context)
1331            .expect("Could not analyze");
1332
1333        let result = result.expr.expect("Could not get the analyzed expression");
1334
1335        let driver = AstVisitorDriver {};
1336        let visitor = AstTextSerializer {};
1337        let context = AstTextSerializerContext {
1338            serialized: "".to_string(),
1339            indent: 0,
1340        };
1341        let result = driver
1342            .visit(&result, &visitor, context)
1343            .expect("Could not serialize");
1344        pretty_assertions::assert_eq!(result.serialized, expected);
1345    }
1346
1347    #[test]
1348    fn serialize_analyzed_member_access_binary_expr() {
1349        let code = "req^incoming and true";
1350        let expected = "Binary Expression:
1351\tLeft:
1352\t\tMember Access Expression:
1353\t\t\tBase:
1354\t\t\t\tIdentifier: req
1355\t\t\tMember:
1356\t\t\t\tIdentifier: incoming
1357\t\t\tType: Bool
1358\t\t\tNot a constant
1359\tOperation: and
1360\tRight:
1361\t\tLiteral: true
1362\t\t\tType: Bool
1363\t\t\tNot a constant
1364\tType: Bool
1365\tNot a constant";
1366
1367        let compile_result = compile(code);
1368        let ast = compile_result.expect("Compilation error");
1369
1370        let driver = AstVisitorDriver {};
1371        let visitor = MelTypeChecker {};
1372        let mut context = MelAnalysisContext::default();
1373
1374        let mut reqs = Struct::new("req");
1375        reqs.insert_field("incoming", Type::Boolean);
1376
1377        context = context.update_scopes(&context.scopes.insert("req", Type::Struct(reqs)));
1378
1379        let result = driver
1380            .visit(&ast, &visitor, context)
1381            .expect("Could not analyze");
1382
1383        let result = result.expr.expect("Could not get the analyzed expression");
1384
1385        let driver = AstVisitorDriver {};
1386        let visitor = AstTextSerializer {};
1387        let context = AstTextSerializerContext {
1388            serialized: "".to_string(),
1389            indent: 0,
1390        };
1391        let result = driver
1392            .visit(&result, &visitor, context)
1393            .expect("Could not serialize");
1394        pretty_assertions::assert_eq!(result.serialized, expected);
1395    }
1396
1397    #[test]
1398    fn serialize_analyzed_member_access_function_call_expr() {
1399        let code = "req^callable(5)";
1400        let expected = "Function Call:
1401\tCallee:
1402\t\tMember Access Expression:
1403\t\t\tBase:
1404\t\t\t\tIdentifier: req
1405\t\t\tMember:
1406\t\t\t\tIdentifier: callable
1407\t\t\tType: Name: callable Return Type: Bool, Parameter Types: Integer
1408\t\t\tNot a constant
1409\tArguments:
1410\t\tLiteral: 5
1411\t\t\tType: Integer
1412\t\t\tNot a constant
1413\tType: Bool";
1414
1415        let compile_result = compile(code);
1416        let ast = compile_result.expect("Compilation error");
1417
1418        let driver = AstVisitorDriver {};
1419        let visitor = MelTypeChecker {};
1420        let mut context = MelAnalysisContext::default();
1421
1422        let mut reqs = Struct::new("req");
1423
1424        reqs.insert_field(
1425            "callable",
1426            Function(
1427                "callable".to_string(),
1428                || Type::Boolean,
1429                || {
1430                    Box::new(SimpleParamTypeChecker {
1431                        p: types::ParameterTypesList {
1432                            params: vec![Type::Integer],
1433                        },
1434                    })
1435                },
1436            ),
1437        );
1438        context = context.update_scopes(&context.scopes.insert("req", Type::Struct(reqs)));
1439
1440        let result = driver
1441            .visit(&ast, &visitor, context)
1442            .expect("Could not analyze");
1443
1444        let result = result.expr.expect("Could not get the analyzed expression");
1445
1446        let driver = AstVisitorDriver {};
1447        let visitor = AstTextSerializer {};
1448        let context = AstTextSerializerContext {
1449            serialized: "".to_string(),
1450            indent: 0,
1451        };
1452        let result = driver
1453            .visit(&result, &visitor, context)
1454            .expect("Could not serialize");
1455        pretty_assertions::assert_eq!(result.serialized, expected);
1456    }
1457}