cluna/
compiler.rs

1use std::iter::Peekable;
2
3use crate::parser::{CodeBlock, ComplexToken, Expression};
4
5fn indent(scope: usize) -> String {
6    let mut result = String::new();
7    for _ in 0..scope {
8        result += "    ";
9    }
10    result
11}
12
13fn indent_if<T: Iterator>(ctokens: &mut Peekable<T>, scope: usize) -> String {
14    match ctokens.peek() {
15        Some(_) => String::from('\n') + &indent(scope),
16        None => String::with_capacity(4),
17    }
18}
19
20fn compile_multiline_string(string: String) -> String {
21    let mut start = 1;
22    let mut equals_count = 0;
23    let chars = string.chars().skip(1);
24
25    for c in chars {
26        start += 1;
27        if c == '=' {
28            equals_count += 1;
29        } else {
30            break;
31        }
32    }
33
34    let end = string.len() - equals_count - 2;
35
36    String::from('`') + &string[start..end] + "`"
37}
38
39fn compile_list<T>(
40    list: Vec<T>,
41    separator: &str,
42    tostring: &mut impl FnMut(T) -> String,
43) -> String {
44    let mut result = String::new();
45    let end = list.len().saturating_sub(1);
46
47    for (i, element) in list.into_iter().enumerate() {
48        result += &(tostring(element));
49
50        if i != end {
51            result += separator
52        }
53    }
54    result
55}
56fn compile_expressions(scope: usize, exprs: Vec<Expression>) -> String {
57    compile_list(exprs, ", ", &mut |expr| compile_expression(scope, expr))
58}
59
60fn compile_symbol(lexeme: String) -> String {
61    match &*lexeme {
62        ":" => "::".to_owned(),
63        _ => lexeme,
64    }
65}
66
67fn compile_operator(lexeme: String, is_binop: bool) -> String {
68    match &*lexeme {
69        "and" => "&&".to_owned(),
70        "or" => "||".to_owned(),
71        "not" => "!".to_owned(),
72        "~=" => "!=".to_owned(),
73        "~" if is_binop => "^^".to_owned(),
74        "//" => "/_".to_owned(),
75        _ => lexeme,
76    }
77}
78
79fn compile_identifier(scope: usize, ident: ComplexToken) -> String {
80    use crate::parser::ComplexToken::*;
81
82    let mut result = String::new();
83    let Ident {expr, ..} = ident else {unreachable!()};
84
85    for ctoken in expr {
86        match ctoken {
87            Symbol(lexeme) => result += &compile_symbol(lexeme),
88            Expr(expr) => {
89                result.push('(');
90                result += &compile_expression(scope, expr);
91                result.push(')');
92            }
93            Call(args) => {
94                result.push('(');
95                result += &compile_expressions(scope, args);
96                result.push(')');
97            }
98            _ => unreachable!(),
99        }
100    }
101
102    result
103}
104
105fn compile_code_block(body: CodeBlock, scope: usize) -> String {
106    let code = compile_ast_helper(body.code, scope + 1);
107
108    String::from('\n') + &code + "\n" + &indent(scope)
109}
110
111fn compile_if_else_chain(
112    scope: usize,
113    condition: Expression,
114    code: CodeBlock,
115    next: Option<Box<ComplexToken>>,
116) -> String {
117    use crate::parser::ComplexToken::*;
118    let mut result = String::new();
119
120    let condition = compile_expression(scope, condition);
121    let body = compile_code_block(code, scope);
122
123    let next = if let Some(next) = next {
124        String::from(" else")
125            + &match *next {
126                IfStatement {
127                    condition,
128                    body,
129                    next,
130                    ..
131                } => {
132                    if condition.is_empty() {
133                        format!(" {{{}}}", compile_code_block(body, scope))
134                    } else {
135                        format!("if {}", compile_if_else_chain(scope, condition, body, next))
136                    }
137                }
138                _ => unreachable!(),
139            }
140    } else {
141        String::new()
142    };
143
144    result += "if ";
145    result += &condition;
146    result += " {";
147    result += &body;
148    result.push('}');
149    result += &next;
150
151    result
152}
153
154fn compile_expression(mut scope: usize, expr: Expression) -> String {
155    use crate::parser::ComplexToken::*;
156
157    let mut result = String::new();
158
159    for ctoken in expr {
160        match ctoken {
161            Symbol(lexeme) => result += &compile_symbol(lexeme),
162            Operator((op, is_binop)) => {
163                if is_binop {
164                    result += " ";
165                    result += &compile_operator(op, is_binop);
166                    result += " ";
167                } else {
168                    result += &compile_operator(op, is_binop);
169                }
170            }
171            MultilineString(string) => result += &compile_multiline_string(string),
172            Table { data, .. } => {
173                scope += 1;
174                let pre = indent(scope);
175                result.push('{');
176                if !data.is_empty() {
177                    result += &compile_list(data, ", ", &mut |(key, value)| {
178                        if let Some(key) = key {
179                            format!(
180                                "\n{}{} = {}",
181                                pre,
182                                compile_expression(scope, key),
183                                compile_expression(scope, value),
184                            )
185                        } else {
186                            String::from('\n') + &pre + &compile_expression(scope, value)
187                        }
188                    });
189                    result.push('\n');
190                    result += &indent(scope - 1);
191                }
192                result.push('}');
193            }
194            Lambda { args, body, .. } => {
195                result += "fn ";
196                result.push('(');
197                result += &compile_list(args, ", ", &mut |arg| arg);
198                result += ") ";
199                result += "{";
200                result += &compile_code_block(body, scope);
201                result.push('}');
202            }
203            ident @ Ident { .. } => {
204                result += &compile_identifier(scope, ident);
205            }
206            Call(args) => {
207                result.push('(');
208                result += &compile_expressions(scope, args);
209                result.push(')');
210            }
211            Expr(expr) => {
212                result.push('(');
213                result += &compile_expression(scope, expr);
214                result.push(')');
215            }
216            _ => unreachable!(),
217        }
218    }
219
220    result
221}
222
223fn compile_ast_helper(tree: Expression, scope: usize) -> String {
224    use crate::parser::ComplexToken::*;
225
226    let mut end = vec![];
227    let mut result = indent(scope);
228    let tree = &mut tree.into_iter().peekable();
229
230    while let Some(ctoken) = tree.next() {
231        match ctoken {
232            Variable { names, values, .. } => {
233                result += "local ";
234                result += &compile_list(names, ", ", &mut |(name, close)| {
235                    if close {
236                        end.push(name.clone());
237                    }
238
239                    name
240                });
241                if !values.is_empty() {
242                    result += " = ";
243                    result += &compile_expressions(scope, values);
244                }
245                result += &indent_if(tree, scope);
246            }
247            Alter { names, values, .. } => {
248                result += &compile_list(names, ", ", &mut |name| compile_identifier(scope, name));
249                result += " = ";
250                result += &compile_expressions(scope, values);
251                result += &indent_if(tree, scope);
252            }
253            Function {
254                local,
255                name,
256                args,
257                body,
258                ..
259            } => {
260                let end = indent_if(tree, scope);
261
262                if name.len() == 1 {
263                    if local {
264                        result += "local ";
265                    } else {
266                        result += "global ";
267                    }
268                    result += "fn ";
269                } else {
270                    result += "method ";
271                }
272
273                result += &compile_expression(scope, name);
274                result.push('(');
275                result += &compile_list(args, ", ", &mut |arg| arg);
276                result += ") ";
277                result += "{";
278                result += &compile_code_block(body, scope);
279                result.push('}');
280                result += &end;
281            }
282            IfStatement {
283                condition,
284                body,
285                next,
286                ..
287            } => {
288                let code = compile_if_else_chain(scope, condition, body, next);
289                result += &code;
290                result += &indent_if(tree, scope);
291            }
292            WhileLoop {
293                condition, body, ..
294            } => {
295                let condition = compile_expression(scope, condition);
296                let body = compile_code_block(body, scope);
297
298                result += "while ";
299                result += &condition;
300                result += " {";
301                result += &body;
302                result.push('}');
303                result += &indent_if(tree, scope);
304            }
305            ForLoop {
306                iter,
307                start,
308                end,
309                step,
310                code,
311                ..
312            } => {
313                result += "for ";
314                result += &iter;
315                result += " = ";
316                result += &compile_expression(scope, start);
317                result += ", ";
318                result += &compile_expression(scope, end);
319                if let Some(step) = step {
320                    result += ", ";
321                    result += &compile_expression(scope, step);
322                }
323                result += " {";
324                result += &compile_code_block(code, scope);
325                result.push('}');
326                result += &indent_if(tree, scope);
327            }
328            ForFuncLoop {
329                iters,
330                expr,
331                stop,
332                initial,
333                code,
334                ..
335            } => {
336                if stop.is_some() || initial.is_some() {
337                    let s = scope;
338                    let iters_compiled = &compile_list(iters.clone(), ", ", &mut |iter| iter);
339
340                    result += "{\n";
341                    let scope = scope + 1;
342                    result += &indent(scope);
343                    result += &format!(
344                        "local _internal_expr_{0}, _internal_stop_{0}, _internal_acc_{0} = ",
345                        s
346                    );
347                    result += &compile_expression(scope, expr);
348                    result += ", ";
349                    result += &compile_expression(scope, stop.unwrap());
350                    result += ", ";
351                    result += &initial.map_or("nil".to_owned(), |initial| {
352                        compile_expression(scope, initial)
353                    });
354                    result += ";\n";
355                    result += &indent(scope);
356                    result += "while true {\n";
357                    let scope = scope + 1;
358                    result += &indent(scope);
359                    result += "local ";
360                    result += iters_compiled;
361                    result += " = ";
362                    result += &format!(
363                        "_internal_expr_{0}(_internal_stop_{0}, _internal_acc_{0});\n",
364                        s
365                    );
366                    result += &indent(scope);
367                    result += &format!("_internal_acc_{s} = ");
368                    result += &iters[0];
369                    result += ";\n";
370                    result += &indent(scope);
371                    result += "if ";
372                    result += &format!("_internal_acc_{s}");
373                    result += " == nil";
374                    result += " { break; }";
375
376                    let scope = scope - 1;
377                    result += &compile_code_block(code, scope);
378                    result += "}\n";
379                    result += &indent(scope - 1);
380                    result += "}\n";
381                } else {
382                    result += "for ";
383                    result += &compile_list(iters, ", ", &mut |iter| iter);
384                    result += " with ";
385                    result += &compile_expression(scope, expr);
386                    result += " {";
387                    result += &compile_code_block(code, scope);
388                    result.push('}');
389                    result += &indent_if(tree, scope);
390                }
391            }
392            RepeatLoop {
393                condition, body, ..
394            } => {
395                let condition = compile_expression(scope, condition);
396                let body = compile_code_block(body, scope);
397
398                result += "loop ";
399                result += " {";
400                result += &body;
401                result.push('}');
402                result += " until ";
403                result += &condition;
404                result += &indent_if(tree, scope);
405            }
406            ctoken @ Ident { .. } => {
407                result += &compile_identifier(scope, ctoken);
408                result.push(';');
409                result += &indent_if(tree, scope);
410            }
411            Call(args) => {
412                result.push('(');
413                result += &compile_expressions(scope, args);
414                result.push(')');
415            }
416            Expr(expr) => {
417                result.push('(');
418                result += &compile_expression(scope, expr);
419                result.push(')');
420            }
421            DoBlock(body) => {
422                result += "{";
423                result += &compile_code_block(body, scope);
424                result += "}";
425                result += &indent_if(tree, scope);
426            }
427            Return(exprs) => {
428                result += "return";
429                if let Some(exprs) = exprs {
430                    result.push(' ');
431                    result +=
432                        &compile_list(exprs, ", ", &mut |expr| compile_expression(scope, expr));
433                }
434            }
435            Break => {
436                result += "break";
437                result += &indent_if(tree, scope);
438            }
439
440            _ => unreachable!(),
441        }
442    }
443
444    for name in end.iter().rev(){
445        result.push('\n');
446        result += &indent(scope);
447        result += "getmetatable(";
448        result += name;
449        result += ").__close(";
450        result += name;
451        result += ");";
452    }
453
454    result
455}
456
457pub fn compile_ast(tree: Expression) -> String {
458    compile_ast_helper(tree, 0)
459}