clue_core 3.0.0

C/Rust like programming language that compiles into Lua code
Documentation
use std::fmt::Write;
use std::iter::{Iterator, Peekable};

use crate::env::Options;
use crate::{
	env::ContinueMode,
	format_clue,
	parser::{CodeBlock, ComplexToken, ComplexToken::*, Expression, FunctionArgs},
	scanner::TokenType::*,
};

pub struct Compiler<'a> {
	options: &'a Options,
}

impl<'a> Compiler<'a> {
	pub const fn new(options: &'a Options) -> Self {
		Self { options }
	}

	fn indentate(&self, scope: usize) -> String {
		let mut result = String::with_capacity(128);
		for _ in 0..scope {
			result += "\t";
		}
		result
	}

	fn indentate_if<T: Iterator>(&self, ctokens: &mut Peekable<T>, scope: usize) -> String {
		match ctokens.peek() {
			Some(_) => {
				format!("\n{}", self.indentate(scope))
			}
			None => String::with_capacity(4),
		}
	}

	fn compile_list<T>(
		&self,
		list: Vec<T>,
		separator: &str,
		tostring: &mut impl FnMut(T) -> String,
	) -> String {
		let mut result = String::new();
		let end = list.len();
		let mut start = 0usize;
		for element in list {
			result += &(tostring(element));
			start += 1;
			if start < end {
				result += separator
			}
		}
		result
	}

	fn compile_identifiers(&self, names: Vec<String>) -> String {
		self.compile_list(names, ", ", &mut |name| name)
	}

	fn compile_expressions(&self, scope: usize, values: Vec<Expression>) -> String {
		self.compile_list(values, ", ", &mut |expr| {
			self.compile_expression(scope, expr)
		})
	}

	fn compile_function(
		&self,
		scope: usize,
		args: FunctionArgs,
		code: CodeBlock,
	) -> (String, String) {
		let mut code = self.compile_code_block(scope, "", code);
		let args = self.compile_list(args, ", ", &mut |(arg, default)| {
			if let Some((default, line)) = default {
				let default = self.compile_expression(scope + 2, default);
				let pre = self.indentate(scope + 1);
				let debug = self.compile_debug_line(line, scope + 2);
				let line = self.compile_debug_comment(line);
				code = format_clue!(
					"\n",
					pre,
					"if ",
					arg,
					" == nil then\n",
					pre,
					"\t",
					debug,
					arg,
					" = ",
					default,
					line,
					"\n",
					pre,
					"end",
					code
				);
			}
			arg
		});
		(code, args)
	}

	fn compile_code_block(&self, scope: usize, start: &str, block: CodeBlock) -> String {
		let pre = self.indentate(scope);
		let code = self.compile_tokens(scope + 1, block.code);
		let debug = self.compile_debug_line(block.start, scope + 1);
		if self.options.env_debug {
			format!(
				"{}\n{}\t{}--{}->{}\n{}\n{}",
				start, pre, debug, block.start, block.end, code, pre
			)
		} else {
			format_clue!(start, "\n", code, "\n", pre)
		}
	}

	fn compile_debug_comment(&self, line: usize) -> String {
		if self.options.env_debug {
			format!(" --{line}")
		} else {
			String::new()
		}
	}

	fn compile_debug_line(&self, line: usize, scope: usize) -> String {
		if self.options.env_debug {
			format!("_clueline = {line};\n{}", self.indentate(scope))
		} else {
			String::new()
		}
	}

	fn compile_identifier(&self, scope: usize, expr: Expression) -> String {
		let mut result = String::with_capacity(32);
		let mut checked = String::with_capacity(32);
		let mut iter = expr.into_iter().peekable();
		while let Some(t) = iter.next() {
			match t {
				SYMBOL(lexeme) => {
					let lexeme = lexeme.as_str();
					match lexeme {
						"?." => {
							result += &(checked.clone() + " and ");
							checked += ".";
						}
						"?::" => {
							result += &(checked.clone() + " and ");
							checked += ":";
						}
						"?[" => {
							result += &(checked.clone() + " and ");
							checked += "["
						}
						"]" => checked += ")]",
						_ => checked += lexeme,
					}
				}
				EXPR(expr) => {
					let expr = self.compile_expression(scope, expr);
					checked += &format_clue!("(", expr);
				}
				CALL(args) => {
					let args =
						format_clue!("(", self.compile_expressions(scope, args.clone()), ")");
					if iter.peek().is_none() {
						return if result.is_empty() {
							checked + &args
						} else {
							format_clue!("(", result, checked, ")", args)
						};
					}
					checked += &args
				}
				_ => {}
			}
		}
		if result.is_empty() {
			checked
		} else {
			format_clue!(result, checked)
		}
	}

	fn compile_expression(&self, mut scope: usize, expr: Expression) -> String {
		let mut result = String::with_capacity(64);
		for t in expr {
			result += &match t {
				MACRO_CALL { expr, args } => {
					let _args = {
						let mut strings: Vec<String> = Vec::new();
						for arg in args {
							strings.push(self.compile_expression(scope, arg))
						}
						strings
					};
					let expr = self.compile_expression(scope, expr);
					format_clue!("(", expr, ")")
				}
				SYMBOL(lexeme) => lexeme,
				TABLE {
					values,
					metas,
					metatable,
				} => {
					scope += 1;
					let mut prevline = 0;
					let pre1 = self.indentate(scope);
					let values = if values.is_empty() {
						String::new()
					} else {
						self.compile_list(values, ", ", &mut |(name, value, line)| {
							let value = self.compile_expression(scope, value);
							let l = if prevline != 0 {
								self.compile_debug_comment(prevline)
							} else {
								String::new()
							};
							prevline = line;
							if let Some(name) = name {
								let name = self.compile_expression(scope, name);
								format_clue!(l, "\n", pre1, name, " = ", value)
							} else {
								format_clue!(l, "\n", pre1, value)
							}
						}) + &self.compile_debug_comment(prevline)
							+ "\n"
					};
					prevline = 0;
					let pre2 = self.indentate(scope - 1);
					if metas.is_empty() {
						scope -= 1;
						if let Some(metatable) = metatable {
							format!("setmetatable({{{values}{pre2}}}, {metatable})")
						} else {
							format!("{{{values}{pre2}}}")
						}
					} else {
						let metas = self.compile_list(metas, ", ", &mut |(name, value, line)| {
							let value = self.compile_expression(scope, value);
							let l = if prevline != 0 {
								self.compile_debug_comment(prevline)
							} else {
								String::new()
							};
							prevline = line;
							format_clue!(l, "\n", pre1, name, " = ", value)
						});
						scope -= 1;
						let line = self.compile_debug_comment(prevline);
						format!(
							"setmetatable({{{}{}}}, {{{}{}\n{}}})",
							values, pre2, metas, line, pre2
						)
					}
				}
				LAMBDA { args, code } => {
					let (code, args) = self.compile_function(scope, args, code);
					format_clue!("function(", args, ")", code, "end")
				}
				IDENT { expr, .. } => self.compile_identifier(scope, expr),
				CALL(args) => format!("({})", self.compile_expressions(scope, args)),
				EXPR(expr) => format!("({})", self.compile_expression(scope, expr)),
				_ => {
					unreachable!("Unexpected ComplexToken found")
				}
			}
		}
		result
	}

	fn compile_elseif_chain(
		&self,
		scope: usize,
		condition: Expression,
		code: CodeBlock,
		next: Option<Box<ComplexToken>>,
	) -> String {
		let condition = self.compile_expression(scope, condition);
		let code = self.compile_code_block(scope, "then", code);
		let next = if let Some(next) = next {
			String::from("else")
				+ &match *next {
					IF_STATEMENT {
						condition,
						code,
						next,
					} => self.compile_elseif_chain(scope, condition, code, next),
					DO_BLOCK(code) => self.compile_code_block(scope, "", code),
					_ => panic!("Unexpected ComplexToken found"),
				}
		} else {
			String::new()
		};
		format_clue!("if ", condition, " ", code, next)
	}

	pub fn compile_tokens(&self, scope: usize, ctokens: Expression) -> String {
		let mut result = self.indentate(scope);
		let ctokens = &mut ctokens.into_iter().peekable();
		while let Some(t) = ctokens.next() {
			result += &match t {
				SYMBOL(lexeme) => lexeme,
				VARIABLE {
					local,
					names,
					values,
					line,
				} => {
					let debug = self.compile_debug_line(line, scope);
					let line = self.compile_debug_comment(line);
					if !local && self.options.env_rawsetglobals {
						let mut result = debug;
						let mut valuesit = values.iter();
						let namesit = &mut names.iter().peekable();
						while let Some(name) = namesit.next() {
							let value = if let Some(value) = valuesit.next() {
								self.compile_expression(scope, value.clone())
							} else {
								String::from("nil")
							};
							let end = {
								let pend = self.indentate_if(namesit, scope);
								if !pend.is_empty() {
									pend
								} else {
									self.indentate_if(ctokens, scope)
								}
							};
							write!(
								result,
								"rawset(_G, \"{}\", {});{}{}",
								name, value, line, end
							)
							.expect("something really unexpected happened");
						}
						result
					} else {
						let end = self.indentate_if(ctokens, scope);
						let pre = if local { "local " } else { "" };
						if values.is_empty() {
							let ident = self.compile_identifiers(names);
							format_clue!(debug, pre, ident, ";", line, end)
						} else {
							let values = self.compile_expressions(scope, values);
							let names = self.compile_identifiers(names);
							format_clue!(debug, pre, names, " = ", values, ";", line, end)
						}
					}
				}
				ALTER {
					kind,
					names,
					values,
					line,
				} => {
					let iter = names.into_iter();
					let mut names: Vec<String> = Vec::new();
					for name in iter {
						names.push(self.compile_expression(scope, name))
					}
					let mut i = 0usize;
					let values = self.compile_list(values, ", ", &mut |expr| {
						let name = if let Some(name) = names.get(i) {
							name.clone()
						} else {
							String::from("nil")
						};
						i += 1;
						(if kind == DEFINE {
							String::new()
						} else {
							name + match kind {
								DEFINE_AND => " and ",
								DEFINE_OR => " or ",
								INCREASE => " + ",
								DECREASE => " - ",
								MULTIPLY => " * ",
								DIVIDE => " / ",
								EXPONENTIATE => " ^ ",
								CONCATENATE => " .. ",
								MODULATE => " % ",
								_ => panic!("Unexpected alter type found"),
							}
						}) + &self.compile_expression(scope, expr)
					});
					let names = self.compile_identifiers(names);
					let debug = self.compile_debug_line(line, scope);
					let line = self.compile_debug_comment(line);
					format_clue!(
						debug,
						names,
						" = ",
						values,
						";",
						line,
						self.indentate_if(ctokens, scope)
					)
				}
				FUNCTION {
					local,
					name,
					args,
					code,
				} => {
					let pre = if local { "local " } else { "" };
					let end = self.indentate_if(ctokens, scope);
					let name = self.compile_expression(scope, name);
					let (code, args) = self.compile_function(scope, args, code);
					format_clue!(pre, "function ", name, "(", args, ")", code, "end", end)
				}
				IF_STATEMENT {
					condition,
					code,
					next,
				} => {
					let code = self.compile_elseif_chain(scope, condition, code, next);
					format_clue!(code, "end", self.indentate_if(ctokens, scope))
				}
				MATCH_BLOCK {
					name,
					value,
					branches,
					line,
				} => {
					let value = self.compile_expression(scope, value);
					let debug = self.compile_debug_line(line, scope);
					let line = self.compile_debug_comment(line);
					let branches = {
						let mut result = self.indentate(scope);
						let mut branches = branches.into_iter().peekable();
						while let Some((conditions, extraif, code)) = branches.next() {
							let empty = conditions.is_empty();
							let default = empty && extraif.is_none();
							let pre = if default { "else" } else { "if" };
							let condition = {
								let mut condition =
									self.compile_list(conditions, "or ", &mut |expr| {
										let expr = self.compile_expression(scope, expr);
										format_clue!("(", name, " == ", expr, ") ")
									});
								if let Some(extraif) = extraif {
									condition.pop();
									let extraif = self.compile_expression(scope, extraif);
									if empty {
										extraif + " "
									} else {
										format_clue!("(", condition, ") and ", extraif, " ")
									}
								} else {
									condition
								}
							};
							let code = self.compile_code_block(
								scope,
								if default { "" } else { "then" },
								code,
							);
							let end = match branches.peek() {
								Some((conditions, extraif, _))
									if conditions.is_empty() && matches!(extraif, None) =>
								{
									""
								}
								Some(_) => "else",
								_ => "end",
							};
							write!(result, "{pre} {condition}{code}{end}")
								.expect("something really unexpected happened");
						}
						result
					};
					let end = self.indentate_if(ctokens, scope);
					format_clue!(
						debug, "local ", name, " = ", value, ";", line, "\n", branches, end
					)
				}
				WHILE_LOOP { condition, code } => {
					let condition = self.compile_expression(scope, condition);
					let code = self.compile_code_block(scope, "do", code);
					format_clue!(
						"while ",
						condition,
						" ",
						code,
						"end",
						self.indentate_if(ctokens, scope)
					)
				}
				LOOP_UNTIL { condition, code } => {
					let condition = self.compile_expression(scope, condition);
					let code = self.compile_code_block(scope, "", code);
					format_clue!(
						"repeat ",
						code,
						"until ",
						condition,
						self.indentate_if(ctokens, scope)
					)
				}
				FOR_LOOP {
					iterator,
					start,
					end,
					alter,
					code,
				} => {
					let start = self.compile_expression(scope, start);
					let endexpr = self.compile_expression(scope, end);
					let alter = self.compile_expression(scope, alter);
					let code = self.compile_code_block(scope, "do", code);
					let end = self.indentate_if(ctokens, scope);
					format_clue!(
						"for ", iterator, " = ", start, ", ", endexpr, ", ", alter, " ", code,
						"end", end
					)
				}
				FOR_FUNC_LOOP {
					iterators,
					expr,
					code,
				} => {
					let expr = self.compile_expression(scope, expr);
					let iterators = self.compile_identifiers(iterators);
					let code = self.compile_code_block(scope, "do", code);
					format_clue!(
						"for ",
						iterators,
						" in ",
						expr,
						" ",
						code,
						"end",
						self.indentate_if(ctokens, scope)
					)
				}
				TRY_CATCH {
					totry,
					error,
					catch,
				} => {
					let i = self.indentate_if(ctokens, scope);
					let totry = self.compile_code_block(scope, "function()", totry);
					if let Some(catch) = catch {
						let catch = self.compile_code_block(scope, "if not _check then", catch);
						let i2 = self.indentate(scope);
						if let Some(error) = error {
							format_clue!(
								"local _check, ",
								error,
								" = pcall(",
								totry,
								"end)\n",
								i2,
								catch,
								"end",
								i
							)
						} else {
							format_clue!(
								"local _check = pcall(",
								totry,
								"end)\n",
								i2,
								catch,
								"end",
								i
							)
						}
					} else {
						format_clue!("pcall(", totry, "end)", i)
					}
				}
				IDENT { expr, line } => {
					let expr = self.compile_identifier(scope, expr);
					let debug = self.compile_debug_line(line, scope);
					let line = self.compile_debug_comment(line);
					format_clue!(debug, expr, ";", line, self.indentate_if(ctokens, scope))
				}
				EXPR(expr) => {
					format!("({})", self.compile_expression(scope, expr))
				}
				DO_BLOCK(code) => {
					format!(
						"{}end{}",
						self.compile_code_block(scope, "do", code),
						self.indentate_if(ctokens, scope)
					)
				}
				RETURN_EXPR(exprs) => {
					if let Some(exprs) = exprs {
						format!("return {};", self.compile_expressions(scope, exprs))
					} else {
						String::from("return;")
					}
				}
				CONTINUE_LOOP => {
					let end = self.indentate_if(ctokens, scope);
					format!(
						"{};{}",
						if self.options.env_continue == ContinueMode::LUAJIT {
							"goto continue"
						} else {
							"continue"
						},
						end
					)
				}
				BREAK_LOOP => String::from("break;") + &self.indentate_if(ctokens, scope),
				_ => {
					panic!("Unexpected ComplexToken found")
				}
			}
		}
		result
	}
}