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}