ast/
tostring.rs

1//! This module contains the `Display` trait implementations for the AST nodes.
2//!
3//! The `Display` trait is used to convert an AST node to a string representation. This
4//! is useful for debugging and testing the code.
5use std::fmt::{Display, Formatter, Result};
6
7use lexer::token::TokenKind;
8
9use crate::expression::*;
10use crate::literal::Literal;
11use crate::statement::{
12    FunctionDeclaration, RepeatForever, RepeatNTimes, RepeatUntil, Return, Set, Statement,
13};
14use crate::{Program, StatementList};
15
16impl Display for Program {
17    fn fmt(&self, f: &mut Formatter<'_>) -> Result {
18        write!(f, "{}", format_semi_seperated(&self.statements))
19    }
20}
21
22impl Display for StatementList {
23    fn fmt(&self, f: &mut Formatter<'_>) -> Result {
24        write!(f, "{}", format_semi_seperated(&self.statements))
25    }
26}
27
28impl Display for Statement {
29    fn fmt(&self, f: &mut Formatter<'_>) -> Result {
30        match self {
31            Statement::Set(Set { ident, expr, .. }) => {
32                write!(f, "{} {} = {}", TokenKind::Set, ident.name, expr)
33            }
34            Statement::Return(Return { value, .. }) => {
35                write!(f, "{} {}", TokenKind::Return, value)
36            }
37            Statement::Expression(expr) => write!(f, "{}", expr),
38            Statement::FunctionDeclaration(FunctionDeclaration {
39                name,
40                parameters,
41                body,
42                ..
43            }) => {
44                write!(
45                    f,
46                    "{} {name}({}){}{}",
47                    TokenKind::DefineFunction,
48                    format_comma_seperated(&parameters),
49                    format_stmt_list(body),
50                    TokenKind::End
51                )
52            }
53            Statement::RepeatNTimes(RepeatNTimes { n, body, .. }) => {
54                write!(
55                    f,
56                    "{} {n} {}{}{}",
57                    TokenKind::Repeat,
58                    TokenKind::Times,
59                    format_stmt_list(body),
60                    TokenKind::End
61                )
62            }
63            Statement::RepeatUntil(RepeatUntil {
64                condition, body, ..
65            }) => {
66                write!(
67                    f,
68                    "{} {} {condition}{}{}",
69                    TokenKind::Repeat,
70                    TokenKind::Until,
71                    format_stmt_list(body),
72                    TokenKind::End
73                )
74            }
75            Statement::RepeatForever(RepeatForever { body, .. }) => {
76                write!(
77                    f,
78                    "{} {}{}{}",
79                    TokenKind::Repeat,
80                    TokenKind::Forever,
81                    format_stmt_list(body),
82                    TokenKind::End
83                )
84            }
85            Statement::Display(display) => {
86                write!(
87                    f,
88                    "{} {}",
89                    TokenKind::Display,
90                    format_comma_seperated(&display.expressions)
91                )
92            }
93        }
94    }
95}
96
97impl Display for Expression {
98    fn fmt(&self, f: &mut Formatter<'_>) -> Result {
99        match self {
100            Expression::Identifier(ident) => write!(f, "{}", ident),
101            Expression::Literal(l) => write!(f, "{}", l),
102            Expression::Unary(Unary {
103                operator, operand, ..
104            }) => match operator {
105                TokenKind::Not => write!(f, "({} {})", operator, operand),
106                _ => write!(f, "({}{})", operator, operand),
107            },
108            Expression::Binary(Binary {
109                operator,
110                left,
111                right,
112                ..
113            }) => write!(f, "({} {} {})", left, operator, right),
114            Expression::FunctionCall(FunctionCall {
115                callee, arguments, ..
116            }) => write!(f, "{}({})", callee, format_comma_seperated(arguments)),
117            Expression::Index(Index { object, index, .. }) => write!(f, "{}[{}]", object, index),
118            Expression::Selection(Selection {
119                condition,
120                conditional,
121                else_conditional,
122                ..
123            }) => {
124                let formatted_conditional = match conditional.statements.is_empty() {
125                    true => "".to_string(),
126                    false => format!(" {}", conditional),
127                };
128
129                let formatted_else_conditional = match else_conditional {
130                    Some(else_conditional) => match else_conditional.statements.is_empty() {
131                        true => format!(" {}", TokenKind::Otherwise),
132                        false => format!(" {} {else_conditional}", TokenKind::Otherwise),
133                    },
134                    None => "".to_string(),
135                };
136
137                write!(
138                    f,
139                    "{} {condition} {}{formatted_conditional}{formatted_else_conditional} {}",
140                    TokenKind::If,
141                    TokenKind::Then,
142                    TokenKind::End,
143                )
144            }
145            Expression::Array(Array { elements, .. }) => {
146                write!(f, "[{}]", format_comma_seperated(&elements))
147            }
148        }
149    }
150}
151
152impl Display for Identifier {
153    fn fmt(&self, f: &mut Formatter<'_>) -> Result {
154        write!(f, "{}", self.name)
155    }
156}
157
158impl Display for Literal {
159    fn fmt(&self, f: &mut Formatter<'_>) -> Result {
160        match self {
161            Literal::Integer { value, .. } => write!(f, "{}", value),
162            Literal::Boolean { value, .. } => write!(f, "{}", value),
163            Literal::Float { value, .. } => write!(f, "{}", value),
164            Literal::String { value, .. } => write!(f, "\"{}\"", value),
165        }
166    }
167}
168
169/// Formats a list of statements into a string representation.
170fn format_stmt_list(list: &StatementList) -> String {
171    // Some formatting to make the output more readable.
172    // If the body is empty, insert a single space between in place of the body.
173    // Otherwise, insert the body with a single space before and after it.
174    match list.statements.is_empty() {
175        true => " ".to_string(),
176        false => format!(" {} ", list),
177    }
178}
179
180/// Formats a list of items into a semi-colon separated string representation.
181fn format_semi_seperated<T: ToString>(items: &Vec<T>) -> String {
182    return items
183        .iter()
184        .map(|item| item.to_string())
185        .collect::<Vec<String>>()
186        .join("; ");
187}
188
189/// Formats a list of items into a comma separated string representation.
190fn format_comma_seperated<T: ToString>(items: &Vec<T>) -> String {
191    return items
192        .iter()
193        .map(|item| item.to_string())
194        .collect::<Vec<String>>()
195        .join(", ");
196}