use std::iter;
use crate::{
generator::{utils, LuaGenerator},
nodes::*,
};
#[derive(Debug, Clone)]
pub struct TokenBasedLuaGenerator<'a> {
original_code: &'a str,
output: String,
currently_commenting: bool,
current_line: usize,
}
impl<'a> TokenBasedLuaGenerator<'a> {
pub fn new(original_code: &'a str) -> Self {
Self {
original_code,
output: String::new(),
currently_commenting: false,
current_line: 1,
}
}
fn push_str(&mut self, string: &str) {
self.current_line += utils::count_new_lines(string);
self.output.push_str(string);
}
fn write_trivia(&mut self, trivia: &Trivia) {
let content = trivia.read(self.original_code);
self.push_str(content);
match trivia.kind() {
TriviaKind::Comment => {
if is_single_line_comment(content) {
self.currently_commenting = true;
}
}
TriviaKind::Whitespace => {
if self.currently_commenting && content.contains('\n') {
self.currently_commenting = false
}
}
}
}
#[inline]
fn write_token(&mut self, token: &Token) {
self.write_token_options(token, true)
}
fn write_token_options(&mut self, token: &Token, space_check: bool) {
for trivia in token.iter_leading_trivia() {
self.write_trivia(trivia);
}
let content = token.read(self.original_code);
if !content.is_empty() {
if self.currently_commenting {
self.uncomment();
}
if let Some(line_number) = token.get_line_number() {
while line_number > self.current_line {
self.output.push('\n');
self.current_line += 1;
}
}
if space_check {
if let Some(next_character) = content.chars().next() {
if self.needs_space(next_character) {
self.output.push(' ');
}
}
}
self.push_str(content);
}
for trivia in token.iter_trailing_trivia() {
self.write_trivia(trivia);
}
}
fn write_block_with_tokens(&mut self, block: &Block, tokens: &BlockTokens) {
let mut iterator = block.iter_statements().enumerate().peekable();
while let Some((index, statement)) = iterator.next() {
self.write_statement(statement);
if let Some(semicolon) = tokens.semicolons.get(index).unwrap_or(&None) {
self.write_token(semicolon);
} else if let Some((_, next_statement)) = iterator.peek() {
if utils::starts_with_parenthese(next_statement)
&& utils::ends_with_prefix(statement)
{
self.write_symbol(";");
}
};
}
if let Some(statement) = block.get_last_statement() {
self.write_last_statement(statement);
}
if let Some(token) = &tokens.final_token {
self.write_token(token);
}
}
fn write_return_with_tokens(&mut self, statement: &ReturnStatement, tokens: &ReturnTokens) {
self.write_token(&tokens.r#return);
let last_index = statement.len().saturating_sub(1);
statement
.iter_expressions()
.enumerate()
.for_each(|(i, expression)| {
self.write_expression(expression);
if i < last_index {
if let Some(comma) = tokens.commas.get(i) {
self.write_token(comma);
} else {
self.write_symbol(",");
}
}
});
}
fn write_assign_with_tokens(&mut self, assign: &AssignStatement, tokens: &AssignTokens) {
let last_variable_index = assign.variables_len().saturating_sub(1);
assign
.iter_variables()
.enumerate()
.for_each(|(i, variable)| {
self.write_variable(variable);
if i < last_variable_index {
if let Some(comma) = tokens.variable_commas.get(i) {
self.write_token(comma);
} else {
self.write_symbol(",");
}
}
});
self.write_token(&tokens.equal);
let last_value_index = assign.values_len().saturating_sub(1);
assign.iter_values().enumerate().for_each(|(i, value)| {
self.write_expression(value);
if i < last_value_index {
if let Some(comma) = tokens.value_commas.get(i) {
self.write_token(comma);
} else {
self.write_symbol(",");
}
}
});
}
fn write_do_with_tokens(&mut self, do_statement: &DoStatement, tokens: &DoTokens) {
self.write_token(&tokens.r#do);
self.write_block(do_statement.get_block());
self.write_token(&tokens.end);
}
fn write_function_call_with_tokens(
&mut self,
call: &FunctionCall,
tokens: &FunctionCallTokens,
) {
self.write_prefix(call.get_prefix());
if let Some(method) = call.get_method() {
if let Some(colon) = &tokens.colon {
self.write_token(colon);
} else {
self.write_symbol(":");
}
self.write_identifier(method);
}
self.write_arguments(call.get_arguments());
}
fn write_parenthese_with_tokens(
&mut self,
parenthese: &ParentheseExpression,
tokens: &ParentheseTokens,
) {
self.write_token(&tokens.left_parenthese);
self.write_expression(parenthese.inner_expression());
self.write_token(&tokens.right_parenthese);
}
fn write_type_cast_with_tokens(&mut self, type_cast: &TypeCastExpression, token: &Token) {
let inner_expression = type_cast.get_expression();
if TypeCastExpression::needs_parentheses(inner_expression) {
self.write_symbol("(");
self.write_expression(inner_expression);
self.write_symbol(")");
} else {
self.write_expression(inner_expression);
}
self.write_token(token);
self.write_type(type_cast.get_type());
}
fn write_tuple_arguments_with_tokens(
&mut self,
arguments: &TupleArguments,
tokens: &TupleArgumentsTokens,
) {
self.write_token(&tokens.opening_parenthese);
let last_value_index = arguments.len().saturating_sub(1);
arguments.iter_values().enumerate().for_each(|(i, value)| {
self.write_expression(value);
if i < last_value_index {
if let Some(comma) = tokens.commas.get(i) {
self.write_token(comma);
} else {
self.write_symbol(",");
}
}
});
self.write_token(&tokens.closing_parenthese);
}
fn write_table_with_tokens(&mut self, table: &TableExpression, tokens: &TableTokens) {
self.write_token(&tokens.opening_brace);
let last_index = table.len().saturating_sub(1);
table.iter_entries().enumerate().for_each(|(i, entry)| {
self.write_table_entry(entry);
if let Some(separator) = tokens.separators.get(i) {
self.write_token(separator);
} else if i < last_index {
self.write_symbol(",");
}
});
self.write_token(&tokens.closing_brace);
}
fn write_table_field_with_tokens(&mut self, entry: &TableFieldEntry, token: &Token) {
self.write_identifier(entry.get_field());
self.write_token(token);
self.write_expression(entry.get_value());
}
fn write_table_index_with_tokens(
&mut self,
entry: &TableIndexEntry,
tokens: &TableIndexEntryTokens,
) {
self.write_token(&tokens.opening_bracket);
self.write_expression(entry.get_key());
self.write_token(&tokens.closing_bracket);
self.write_token(&tokens.equal);
self.write_expression(entry.get_value());
}
fn write_field_with_token(&mut self, field: &FieldExpression, token: &Token) {
self.write_prefix(field.get_prefix());
self.write_token_options(token, false);
self.write_identifier(field.get_field());
}
fn write_index_with_tokens(&mut self, index: &IndexExpression, tokens: &IndexExpressionTokens) {
self.write_prefix(index.get_prefix());
self.write_token(&tokens.opening_bracket);
self.write_expression(index.get_index());
self.write_token(&tokens.closing_bracket);
}
fn write_if_expression_with_token(
&mut self,
if_expression: &IfExpression,
tokens: &IfExpressionTokens,
) {
self.write_token(&tokens.r#if);
self.write_expression(if_expression.get_condition());
self.write_token(&tokens.then);
self.write_expression(if_expression.get_result());
for branch in if_expression.iter_branches() {
if let Some(tokens) = branch.get_tokens() {
self.write_if_expression_branch_with_tokens(branch, tokens);
} else {
self.write_if_expression_branch_with_tokens(
branch,
&self.generate_if_expression_branch_tokens(branch),
);
}
}
self.write_token(&tokens.r#else);
self.write_expression(if_expression.get_else_result());
}
fn write_if_expression_branch_with_tokens(
&mut self,
branch: &ElseIfExpressionBranch,
tokens: &ElseIfExpressionBranchTokens,
) {
self.write_token(&tokens.elseif);
self.write_expression(branch.get_condition());
self.write_token(&tokens.then);
self.write_expression(branch.get_result());
}
fn write_compound_assign_with_tokens(
&mut self,
assign: &CompoundAssignStatement,
tokens: &CompoundAssignTokens,
) {
self.write_variable(assign.get_variable());
self.write_token(&tokens.operator);
self.write_expression(assign.get_value());
}
#[allow(clippy::too_many_arguments)]
fn write_function_attributes<'b>(
&mut self,
tokens: &FunctionBodyTokens,
generic_parameters: Option<&GenericParameters>,
parameter_count: usize,
parameters: impl Iterator<Item = &'b TypedIdentifier>,
is_variadic: bool,
variadic_type: Option<&FunctionVariadicType>,
return_type: Option<&FunctionReturnType>,
block: &Block,
) {
if let Some(generics) = generic_parameters {
self.write_function_generics(generics);
}
self.write_token(&tokens.opening_parenthese);
let last_parameter_index = parameter_count.saturating_sub(1);
parameters.enumerate().for_each(|(i, param)| {
self.write_typed_identifier(param);
if i < last_parameter_index {
if let Some(comma) = tokens.parameter_commas.get(i) {
self.write_token(comma);
} else {
self.write_symbol(",");
}
}
});
if is_variadic {
if parameter_count > 0 {
if let Some(comma) = tokens.parameter_commas.get(last_parameter_index) {
self.write_token(comma);
} else {
self.write_symbol(",");
}
}
if let Some(token) = &tokens.variable_arguments {
self.write_token(token);
} else {
self.write_symbol("...");
}
if let Some(variadic_type) = variadic_type {
if let Some(colon) = &tokens.variable_arguments_colon {
self.write_token(colon);
} else {
self.write_symbol(":")
}
self.write_function_variadic_type(variadic_type);
}
}
self.write_token(&tokens.closing_parenthese);
if let Some(return_type) = return_type {
if let Some(colon) = &tokens.return_type_colon {
self.write_token(colon);
} else {
self.write_symbol(":")
}
self.write_function_return_type(return_type);
}
self.write_block(block);
self.write_token(&tokens.end);
}
fn write_function_statement_with_tokens(
&mut self,
function: &FunctionStatement,
tokens: &FunctionBodyTokens,
) {
self.write_token(&tokens.function);
let name = function.get_name();
if let Some(tokens) = name.get_tokens() {
self.write_function_name_with_tokens(name, tokens);
} else {
self.write_function_name_with_tokens(name, &self.generate_function_name_tokens(name));
}
self.write_function_attributes(
tokens,
function.get_generic_parameters(),
function.parameters_count(),
function.iter_parameters(),
function.is_variadic(),
function.get_variadic_type(),
function.get_return_type(),
function.get_block(),
);
}
fn write_function_name_with_tokens(
&mut self,
name: &FunctionName,
tokens: &FunctionNameTokens,
) {
self.write_identifier(name.get_name());
name.get_field_names()
.iter()
.enumerate()
.for_each(|(i, field)| {
if let Some(period) = tokens.periods.get(i) {
self.write_token_options(period, false);
} else {
self.write_symbol_without_space_check(".");
}
self.write_identifier(field);
});
if let Some(method) = name.get_method() {
if let Some(colon) = &tokens.colon {
self.write_token(colon);
} else {
self.write_symbol(":");
}
self.write_identifier(method);
}
}
fn write_generic_for_with_tokens(
&mut self,
generic_for: &GenericForStatement,
tokens: &GenericForTokens,
) {
self.write_token(&tokens.r#for);
let last_identifier_index = generic_for.identifiers_len().saturating_sub(1);
generic_for
.iter_identifiers()
.enumerate()
.for_each(|(i, identifier)| {
self.write_typed_identifier(identifier);
if i < last_identifier_index {
if let Some(comma) = tokens.identifier_commas.get(i) {
self.write_token(comma);
} else {
self.write_symbol(",");
}
}
});
self.write_token(&tokens.r#in);
let last_expression_index = generic_for.expressions_len().saturating_sub(1);
generic_for
.iter_expressions()
.enumerate()
.for_each(|(i, expression)| {
self.write_expression(expression);
if i < last_expression_index {
if let Some(comma) = tokens.value_commas.get(i) {
self.write_token(comma);
} else {
self.write_symbol(",");
}
}
});
self.write_token(&tokens.r#do);
self.write_block(generic_for.get_block());
self.write_token(&tokens.end);
}
fn write_if_statement_with_tokens(
&mut self,
if_statement: &IfStatement,
tokens: &IfStatementTokens,
) {
let mut branches = if_statement.iter_branches();
if let Some(branch) = branches.next() {
self.write_token(&tokens.r#if);
self.write_expression(branch.get_condition());
self.write_token(&tokens.then);
self.write_block(branch.get_block());
for branch in branches {
if let Some(tokens) = branch.get_tokens() {
self.write_if_branch_with_tokens(branch, tokens);
} else {
self.write_if_branch_with_tokens(
branch,
&self.generate_if_branch_tokens(branch),
);
}
}
if let Some(else_block) = if_statement.get_else_block() {
if let Some(token) = &tokens.r#else {
self.write_token(token);
} else {
self.write_symbol("else");
}
self.write_block(else_block);
}
self.write_token(&tokens.end);
}
}
fn write_if_branch_with_tokens(&mut self, branch: &IfBranch, tokens: &IfBranchTokens) {
self.write_token(&tokens.elseif);
self.write_expression(branch.get_condition());
self.write_token(&tokens.then);
self.write_block(branch.get_block());
}
fn write_local_assign_with_tokens(
&mut self,
assign: &LocalAssignStatement,
tokens: &LocalAssignTokens,
) {
self.write_token(&tokens.local);
let last_variable_index = assign.variables_len().saturating_sub(1);
assign
.iter_variables()
.enumerate()
.for_each(|(i, identifier)| {
self.write_typed_identifier(identifier);
if i < last_variable_index {
if let Some(comma) = tokens.variable_commas.get(i) {
self.write_token(comma);
} else {
self.write_symbol(",");
}
}
});
if assign.has_values() {
if let Some(token) = &tokens.equal {
self.write_token(token);
} else {
self.write_symbol("=");
}
let last_value_index = assign.values_len().saturating_sub(1);
assign.iter_values().enumerate().for_each(|(i, value)| {
self.write_expression(value);
if i < last_value_index {
if let Some(comma) = tokens.value_commas.get(i) {
self.write_token(comma);
} else {
self.write_symbol(",");
}
}
});
}
}
fn write_local_function_with_tokens(
&mut self,
function: &LocalFunctionStatement,
tokens: &LocalFunctionTokens,
) {
self.write_token(&tokens.local);
self.write_token(&tokens.function);
self.write_identifier(function.get_identifier());
self.write_function_attributes(
tokens,
function.get_generic_parameters(),
function.parameters_count(),
function.iter_parameters(),
function.is_variadic(),
function.get_variadic_type(),
function.get_return_type(),
function.get_block(),
);
}
fn write_numeric_for_with_tokens(
&mut self,
numeric_for: &NumericForStatement,
tokens: &NumericForTokens,
) {
self.write_token(&tokens.r#for);
self.write_typed_identifier(numeric_for.get_identifier());
self.write_token(&tokens.equal);
self.write_expression(numeric_for.get_start());
self.write_token(&tokens.end_comma);
self.write_expression(numeric_for.get_end());
if let Some(step) = numeric_for.get_step() {
if let Some(comma) = &tokens.step_comma {
self.write_token(comma);
} else {
self.write_symbol(",");
}
self.write_expression(step);
}
self.write_token(&tokens.r#do);
self.write_block(numeric_for.get_block());
self.write_token(&tokens.end);
}
fn write_repeat_with_tokens(&mut self, repeat: &RepeatStatement, tokens: &RepeatTokens) {
self.write_token(&tokens.repeat);
self.write_block(repeat.get_block());
self.write_token(&tokens.until);
self.write_expression(repeat.get_condition());
}
fn write_while_with_tokens(&mut self, while_statement: &WhileStatement, tokens: &WhileTokens) {
self.write_token(&tokens.r#while);
self.write_expression(while_statement.get_condition());
self.write_token(&tokens.r#do);
self.write_block(while_statement.get_block());
self.write_token(&tokens.end);
}
fn write_type_declaration_with_tokens(
&mut self,
statement: &TypeDeclarationStatement,
tokens: &TypeDeclarationTokens,
) {
if statement.is_exported() {
if let Some(export_token) = &tokens.export {
self.write_token(export_token);
} else {
self.write_symbol("export");
}
}
self.write_token(&tokens.r#type);
self.write_identifier(statement.get_name());
if let Some(generic_parameters) = statement
.get_generic_parameters()
.filter(|generic_parameters| !generic_parameters.is_empty())
{
if let Some(tokens) = generic_parameters.get_tokens() {
self.write_generic_parameters_with_default_with_tokens(generic_parameters, tokens);
} else {
self.write_generic_parameters_with_default_with_tokens(
generic_parameters,
&self.generate_generic_parameters_with_defaults_tokens(generic_parameters),
);
}
}
self.write_token(&tokens.equal);
self.write_type(statement.get_type());
}
fn write_generic_parameters_with_default_with_tokens(
&mut self,
generic_parameters: &GenericParametersWithDefaults,
tokens: &GenericParametersTokens,
) {
self.write_token(&tokens.opening_list);
let last_index = generic_parameters.len().saturating_sub(1);
for (i, parameter) in generic_parameters.iter().enumerate() {
match parameter {
GenericParameterRef::TypeVariable(identifier) => {
self.write_identifier(identifier);
}
GenericParameterRef::TypeVariableWithDefault(identifier_with_default) => {
self.write_identifier(identifier_with_default.get_type_variable());
if let Some(token) = identifier_with_default.get_token() {
self.write_token(token);
} else {
self.write_symbol("=");
}
self.write_type(identifier_with_default.get_default_type());
}
GenericParameterRef::GenericTypePack(generic_type_pack) => {
self.write_generic_type_pack(generic_type_pack);
}
GenericParameterRef::GenericTypePackWithDefault(generic_pack_with_default) => {
self.write_generic_type_pack(generic_pack_with_default.get_generic_type_pack());
if let Some(token) = generic_pack_with_default.get_token() {
self.write_token(token);
} else {
self.write_symbol("=");
}
self.write_generic_type_pack_default(
generic_pack_with_default.get_default_type(),
);
}
}
if i < last_index {
if let Some(comma) = tokens.commas.get(i) {
self.write_token(comma);
} else {
self.write_symbol(",");
}
}
}
self.write_token(&tokens.closing_list);
}
fn write_function_with_tokens(
&mut self,
function: &FunctionExpression,
tokens: &FunctionBodyTokens,
) {
self.write_token(&tokens.function);
self.write_function_attributes(
tokens,
function.get_generic_parameters(),
function.parameters_count(),
function.iter_parameters(),
function.is_variadic(),
function.get_variadic_type(),
function.get_return_type(),
function.get_block(),
);
}
fn write_type_parameters_with_tokens(
&mut self,
parameters: &TypeParameters,
tokens: &TypeParametersTokens,
) {
self.write_token(&tokens.opening_list);
let last_index = parameters.len().saturating_sub(1);
for (i, parameter) in parameters.iter().enumerate() {
self.write_type_parameter(parameter);
if i < last_index {
if let Some(comma) = tokens.commas.get(i) {
self.write_token(comma);
} else {
self.write_symbol(",");
}
}
}
self.write_token(&tokens.closing_list);
}
fn write_type_field_with_token(&mut self, type_field: &TypeField, token: &Token) {
self.write_identifier(type_field.get_namespace());
self.write_token_options(token, false);
self.write_type_name(type_field.get_type_name());
}
fn write_array_type_with_tokens(&mut self, array_type: &ArrayType, tokens: &ArrayTypeTokens) {
self.write_token(&tokens.opening_brace);
self.write_type(array_type.get_element_type());
self.write_token(&tokens.closing_brace);
}
fn write_table_type_with_tokens(&mut self, table_type: &TableType, tokens: &TableTypeTokens) {
self.write_token(&tokens.opening_brace);
let last_index = table_type.len().saturating_sub(1);
for (i, property) in table_type.iter_entries().enumerate() {
match property {
TableEntryType::Property(property) => {
self.write_identifier(property.get_identifier());
if let Some(colon) = property.get_token() {
self.write_token(colon);
} else {
self.write_symbol(":");
}
self.write_type(property.get_type());
}
TableEntryType::Literal(property) => {
if let Some(tokens) = property.get_tokens() {
self.write_table_literal_property_type_with_tokens(property, tokens);
} else {
self.write_table_literal_property_type_with_tokens(
property,
&self.generate_table_indexer_type_tokens(),
);
}
}
TableEntryType::Indexer(indexer) => {
if let Some(tokens) = indexer.get_tokens() {
self.write_table_indexer_type_with_tokens(indexer, tokens);
} else {
self.write_table_indexer_type_with_tokens(
indexer,
&self.generate_table_indexer_type_tokens(),
);
}
}
}
if i < last_index {
if let Some(comma) = tokens.separators.get(i) {
self.write_token(comma);
} else {
self.write_symbol(",");
}
}
}
self.write_token(&tokens.closing_brace);
}
fn write_table_indexer_type_with_tokens(
&mut self,
indexer_type: &TableIndexerType,
tokens: &TableIndexTypeTokens,
) {
self.write_token(&tokens.opening_bracket);
let key_type = indexer_type.get_key_type();
let need_parentheses = matches!(
key_type,
Type::Optional(_) | Type::Intersection(_) | Type::Union(_)
);
if need_parentheses {
self.write_symbol("(");
self.write_type(key_type);
self.write_symbol(")");
} else {
self.write_type(key_type);
}
self.write_token(&tokens.closing_bracket);
self.write_token(&tokens.colon);
self.write_type(indexer_type.get_value_type());
}
fn write_table_literal_property_type_with_tokens(
&mut self,
property: &TableLiteralPropertyType,
tokens: &TableIndexTypeTokens,
) {
self.write_token(&tokens.opening_bracket);
self.write_string_type(property.get_string());
self.write_token(&tokens.closing_bracket);
self.write_token(&tokens.colon);
self.write_type(property.get_type());
}
fn write_expression_type_with_tokens(
&mut self,
expression_type: &ExpressionType,
tokens: &ExpressionTypeTokens,
) {
self.write_token(&tokens.r#typeof);
self.write_token(&tokens.opening_parenthese);
self.write_expression(expression_type.get_expression());
self.write_token(&tokens.closing_parenthese);
}
fn write_parenthese_type_with_tokens(
&mut self,
parenthese_type: &ParentheseType,
tokens: &ParentheseTypeTokens,
) {
self.write_token(&tokens.left_parenthese);
self.write_type(parenthese_type.get_inner_type());
self.write_token(&tokens.right_parenthese);
}
fn write_function_type_with_tokens(
&mut self,
function_type: &FunctionType,
tokens: &FunctionTypeTokens,
) {
if let Some(generic_parameters) = function_type.get_generic_parameters() {
self.write_function_generics(generic_parameters);
}
self.write_token(&tokens.opening_parenthese);
let argument_len = function_type.argument_len();
let last_index = argument_len.saturating_sub(1);
for (i, argument) in function_type.iter_arguments().enumerate() {
if let Some(name) = argument.get_name() {
self.write_identifier(name);
if let Some(token) = argument.get_token() {
self.write_token(token);
} else {
self.write_symbol(":");
}
}
self.write_type(argument.get_type());
if i < last_index {
if let Some(comma) = tokens.commas.get(i) {
self.write_token(comma);
} else {
self.write_symbol(",");
}
}
}
if let Some(variadic_argument_type) = function_type.get_variadic_argument_type() {
if argument_len > 0 {
if let Some(comma) = tokens.commas.get(argument_len) {
self.write_token(comma);
} else {
self.write_symbol(",");
}
}
self.write_variadic_argument_type(variadic_argument_type);
}
self.write_token(&tokens.closing_parenthese);
self.write_token(&tokens.arrow);
self.write_function_return_type(function_type.get_return_type());
}
fn write_function_generics(&mut self, generic_parameters: &GenericParameters) {
if generic_parameters.is_empty() {
return;
}
if let Some(generic_tokens) = generic_parameters.get_tokens() {
self.write_generic_parameters_with_tokens(generic_parameters, generic_tokens);
} else {
self.write_generic_parameters_with_tokens(
generic_parameters,
&self.generate_generic_parameters_tokens(generic_parameters),
);
}
}
fn write_generic_parameters_with_tokens(
&mut self,
generic_parameters: &GenericParameters,
tokens: &GenericParametersTokens,
) {
self.write_token(&tokens.opening_list);
let last_index = generic_parameters.len().saturating_sub(1);
for (i, type_variable) in generic_parameters.iter_type_variable().enumerate() {
self.write_identifier(type_variable);
if i < last_index {
if let Some(comma) = tokens.commas.get(i) {
self.write_token(comma);
} else {
self.write_symbol(",");
}
}
}
let type_variables_len = generic_parameters.type_variables_len();
for (i, generic_type_pack) in generic_parameters.iter_generic_type_pack().enumerate() {
self.write_generic_type_pack(generic_type_pack);
if (i + type_variables_len) < last_index {
if let Some(comma) = tokens.commas.get(i) {
self.write_token(comma);
} else {
self.write_symbol(",");
}
}
}
self.write_token(&tokens.closing_list);
}
fn write_type_pack_with_tokens(&mut self, type_pack: &TypePack, tokens: &TypePackTokens) {
self.write_token(&tokens.left_parenthese);
let last_index = type_pack
.len()
.saturating_sub(if type_pack.has_variadic_type() { 0 } else { 1 });
for (i, r#type) in type_pack.iter().enumerate() {
self.write_type(r#type);
if i < last_index {
if let Some(comma) = tokens.commas.get(i) {
self.write_token(comma);
} else {
self.write_symbol(",");
}
}
}
if let Some(variadic_argument_type) = type_pack.get_variadic_type() {
self.write_variadic_argument_type(variadic_argument_type);
}
self.write_token(&tokens.right_parenthese);
}
fn write_optional_type_with_token(&mut self, optional: &OptionalType, token: &Token) {
let inner_type = optional.get_inner_type();
if OptionalType::needs_parentheses(inner_type) {
self.write_symbol("(");
self.write_type(inner_type);
self.write_symbol(")");
} else {
self.write_type(inner_type);
}
self.write_token(token);
}
fn write_intersection_type_with_token(
&mut self,
intersection: &IntersectionType,
tokens: &IntersectionTypeTokens,
) {
let length = intersection.len();
let last_index = length.saturating_sub(1);
for (i, r#type) in intersection.iter_types().enumerate() {
if i == 0 {
if let Some(leading) = &tokens.leading_token {
self.write_token(leading);
}
} else if let Some(token) = tokens.separators.get(i.saturating_sub(1)) {
self.write_token(token);
} else {
self.write_symbol("&");
}
let need_parentheses = if i == last_index {
IntersectionType::last_needs_parentheses(r#type)
} else {
IntersectionType::intermediate_needs_parentheses(r#type)
};
if need_parentheses {
self.write_symbol("(");
self.write_type(r#type);
self.write_symbol(")");
} else {
self.write_type(r#type);
}
}
}
fn write_union_type_with_token(&mut self, union: &UnionType, tokens: &UnionTypeTokens) {
let length = union.len();
let last_index = length.saturating_sub(1);
for (i, r#type) in union.iter_types().enumerate() {
if i == 0 {
if let Some(leading) = &tokens.leading_token {
self.write_token(leading);
}
} else if let Some(token) = tokens.separators.get(i.saturating_sub(1)) {
self.write_token(token);
} else {
self.write_symbol("|");
}
let need_parentheses = if i == last_index {
UnionType::last_needs_parentheses(r#type)
} else {
UnionType::intermediate_needs_parentheses(r#type)
};
if need_parentheses {
self.write_symbol("(");
self.write_type(r#type);
self.write_symbol(")");
} else {
self.write_type(r#type);
}
}
}
fn write_interpolated_string_with_tokens(
&mut self,
interpolated_string: &InterpolatedStringExpression,
tokens: &InterpolatedStringTokens,
) {
self.write_token(&tokens.opening_tick);
for segment in interpolated_string.iter_segments() {
match segment {
InterpolationSegment::String(string_segment) => {
if let Some(token) = string_segment.get_token() {
self.write_token(token);
} else {
self.write_symbol(&utils::write_interpolated_string_segment(string_segment))
}
}
InterpolationSegment::Value(value) => {
if let Some(tokens) = value.get_tokens() {
self.write_string_value_segment_with_tokens(value, tokens);
} else {
self.write_string_value_segment_with_tokens(
value,
&self.generate_string_value_segment_tokens(value),
);
}
}
}
}
self.write_token(&tokens.closing_tick);
}
fn write_string_value_segment_with_tokens(
&mut self,
value: &ValueSegment,
tokens: &ValueSegmentTokens,
) {
self.write_token(&tokens.opening_brace);
let expression = value.get_expression();
if self.output.ends_with('{') {
if let Some(table) = utils::starts_with_table(expression) {
if table
.get_tokens()
.and_then(|tokens| {
tokens
.opening_brace
.iter_leading_trivia()
.next()
.filter(|trivia| !trivia.read(self.original_code).is_empty())
})
.is_none()
{
self.output.push(' ');
}
}
}
self.write_expression(expression);
self.write_token(&tokens.closing_brace);
}
fn generate_block_tokens(&self, _block: &Block) -> BlockTokens {
BlockTokens {
semicolons: Vec::new(),
last_semicolon: None,
final_token: None,
}
}
fn generate_assign_tokens(&self, assign: &AssignStatement) -> AssignTokens {
AssignTokens {
equal: Token::from_content("="),
variable_commas: intersect_with_token(comma_token(), assign.variables_len()),
value_commas: intersect_with_token(comma_token(), assign.values_len()),
}
}
fn generate_do_tokens(&self, _do_statement: &DoStatement) -> DoTokens {
DoTokens {
r#do: Token::from_content("do"),
end: Token::from_content("end"),
}
}
fn generate_compound_assign_tokens(
&self,
assign: &CompoundAssignStatement,
) -> CompoundAssignTokens {
CompoundAssignTokens {
operator: Token::from_content(assign.get_operator().to_str()),
}
}
fn generate_generic_for_tokens(&self, generic_for: &GenericForStatement) -> GenericForTokens {
GenericForTokens {
r#for: Token::from_content("for"),
r#in: Token::from_content("in"),
r#do: Token::from_content("do"),
end: Token::from_content("end"),
identifier_commas: intersect_with_token(comma_token(), generic_for.identifiers_len()),
value_commas: intersect_with_token(comma_token(), generic_for.expressions_len()),
}
}
fn generate_if_statement_tokens(&self, if_statement: &IfStatement) -> IfStatementTokens {
IfStatementTokens {
r#if: Token::from_content("if"),
then: Token::from_content("then"),
end: Token::from_content("end"),
r#else: if_statement
.get_else_block()
.map(|_| Token::from_content("else")),
}
}
fn generate_if_branch_tokens(&self, _branch: &IfBranch) -> IfBranchTokens {
IfBranchTokens {
elseif: Token::from_content("elseif"),
then: Token::from_content("then"),
}
}
fn generate_function_statement_tokens(
&self,
function: &FunctionStatement,
) -> FunctionBodyTokens {
FunctionBodyTokens {
function: Token::from_content("function"),
opening_parenthese: Token::from_content("("),
closing_parenthese: Token::from_content(")"),
end: Token::from_content("end"),
parameter_commas: intersect_with_token(
comma_token(),
function.parameters_count() + usize::from(function.is_variadic()),
),
variable_arguments: if function.is_variadic() {
Some(Token::from_content("..."))
} else {
None
},
variable_arguments_colon: if function.has_variadic_type() {
Some(Token::from_content(":"))
} else {
None
},
return_type_colon: if function.has_return_type() {
Some(Token::from_content(":"))
} else {
None
},
}
}
fn generate_function_name_tokens(&self, name: &FunctionName) -> FunctionNameTokens {
FunctionNameTokens {
periods: iter::repeat_with(|| Token::from_content("."))
.take(name.get_field_names().len())
.collect(),
colon: name.get_method().map(|_| Token::from_content(":")),
}
}
fn generate_return_tokens(&self, return_statement: &ReturnStatement) -> ReturnTokens {
ReturnTokens {
r#return: Token::from_content("return")
.with_trailing_trivia(TriviaKind::Whitespace.with_content(" ")),
commas: intersect_with_token(comma_token(), return_statement.len()),
}
}
fn generate_local_assign_tokens(&self, assign: &LocalAssignStatement) -> LocalAssignTokens {
LocalAssignTokens {
local: Token::from_content("local"),
equal: if assign.has_values() {
Some(Token::from_content("="))
} else {
None
},
variable_commas: intersect_with_token(comma_token(), assign.variables_len()),
value_commas: intersect_with_token(comma_token(), assign.values_len()),
}
}
fn generate_local_function_tokens(
&self,
function: &LocalFunctionStatement,
) -> LocalFunctionTokens {
LocalFunctionTokens {
local: Token::from_content("local"),
function_body: FunctionBodyTokens {
function: Token::from_content("function"),
opening_parenthese: Token::from_content("("),
closing_parenthese: Token::from_content(")"),
end: Token::from_content("end"),
parameter_commas: intersect_with_token(
comma_token(),
function.parameters_count() + usize::from(function.is_variadic()),
),
variable_arguments: if function.is_variadic() {
Some(Token::from_content("..."))
} else {
None
},
variable_arguments_colon: if function.has_variadic_type() {
Some(Token::from_content(":"))
} else {
None
},
return_type_colon: if function.has_return_type() {
Some(Token::from_content(":"))
} else {
None
},
},
}
}
fn generate_numeric_for_tokens(&self, numeric_for: &NumericForStatement) -> NumericForTokens {
NumericForTokens {
r#for: Token::from_content("for"),
equal: Token::from_content("="),
r#do: Token::from_content("do"),
end: Token::from_content("end"),
end_comma: Token::from_content(","),
step_comma: numeric_for.get_step().map(|_| Token::from_content(",")),
}
}
fn generate_repeat_tokens(&self, _repeat: &RepeatStatement) -> RepeatTokens {
RepeatTokens {
repeat: Token::from_content("repeat"),
until: Token::from_content("until"),
}
}
fn generate_while_tokens(&self, _while_statement: &WhileStatement) -> WhileTokens {
WhileTokens {
r#while: Token::from_content("while"),
r#do: Token::from_content("do"),
end: Token::from_content("end"),
}
}
fn generate_type_declaration_tokens(
&self,
statement: &TypeDeclarationStatement,
) -> TypeDeclarationTokens {
TypeDeclarationTokens {
r#type: Token::from_content("type"),
equal: Token::from_content("="),
export: if statement.is_exported() {
Some(Token::from_content("export"))
} else {
None
},
}
}
fn generate_function_tokens(&self, function: &FunctionExpression) -> FunctionBodyTokens {
FunctionBodyTokens {
function: Token::from_content("function"),
opening_parenthese: Token::from_content("("),
closing_parenthese: Token::from_content(")"),
end: Token::from_content("end"),
parameter_commas: intersect_with_token(
comma_token(),
function.parameters_count() + usize::from(function.is_variadic()),
),
variable_arguments: if function.is_variadic() {
Some(Token::from_content("..."))
} else {
None
},
variable_arguments_colon: if function.has_variadic_type() {
Some(Token::from_content(":"))
} else {
None
},
return_type_colon: if function.has_return_type() {
Some(Token::from_content(":"))
} else {
None
},
}
}
fn generate_function_call_tokens(&self, call: &FunctionCall) -> FunctionCallTokens {
FunctionCallTokens {
colon: call.get_method().map(|_| Token::from_content(":")),
}
}
fn generate_field_token(&self, _field: &FieldExpression) -> Token {
Token::from_content(".")
}
fn generate_index_tokens(&self, _index: &IndexExpression) -> IndexExpressionTokens {
IndexExpressionTokens {
opening_bracket: Token::from_content("["),
closing_bracket: Token::from_content("]"),
}
}
fn generate_if_tokens(&self, _if_expression: &IfExpression) -> IfExpressionTokens {
IfExpressionTokens {
r#if: Token::from_content("if"),
then: Token::from_content("then"),
r#else: Token::from_content("else"),
}
}
fn generate_if_expression_branch_tokens(
&self,
_branch: &ElseIfExpressionBranch,
) -> ElseIfExpressionBranchTokens {
ElseIfExpressionBranchTokens {
elseif: Token::from_content("elseif"),
then: Token::from_content("then"),
}
}
fn generate_table_tokens(&self, table: &TableExpression) -> TableTokens {
TableTokens {
opening_brace: Token::from_content("{"),
closing_brace: Token::from_content("}"),
separators: intersect_with_token(comma_token(), table.len()),
}
}
fn generate_table_field_tokens(&self, _entry: &TableFieldEntry) -> Token {
Token::from_content("=")
}
fn generate_table_index_tokens(&self, _entry: &TableIndexEntry) -> TableIndexEntryTokens {
TableIndexEntryTokens {
opening_bracket: Token::from_content("["),
closing_bracket: Token::from_content("]"),
equal: Token::from_content("="),
}
}
fn generate_tuple_arguments_tokens(&self, arguments: &TupleArguments) -> TupleArgumentsTokens {
TupleArgumentsTokens {
opening_parenthese: Token::from_content("("),
closing_parenthese: Token::from_content(")"),
commas: intersect_with_token(comma_token(), arguments.len()),
}
}
fn generate_parenthese_tokens(&self, _parenthese: &ParentheseExpression) -> ParentheseTokens {
ParentheseTokens {
left_parenthese: Token::from_content("("),
right_parenthese: Token::from_content(")"),
}
}
fn generate_type_cast_token(&self, _type_cast: &TypeCastExpression) -> Token {
Token::from_content("::")
}
fn generate_type_parameters_tokens(&self, parameters: &TypeParameters) -> TypeParametersTokens {
TypeParametersTokens {
opening_list: Token::from_content("<"),
closing_list: Token::from_content(">"),
commas: intersect_with_token(comma_token(), parameters.len()),
}
}
fn generate_type_field_token(&self, _type_field: &TypeField) -> Token {
Token::from_content(".")
}
fn generate_array_type_tokens(&self, _array: &ArrayType) -> ArrayTypeTokens {
ArrayTypeTokens {
opening_brace: Token::from_content("{"),
closing_brace: Token::from_content("}"),
}
}
fn generate_table_type_tokens(&self, table_type: &TableType) -> TableTypeTokens {
TableTypeTokens {
opening_brace: Token::from_content("{"),
closing_brace: Token::from_content("}"),
separators: intersect_with_token(comma_token(), table_type.len()),
}
}
fn generate_table_indexer_type_tokens(&self) -> TableIndexTypeTokens {
TableIndexTypeTokens {
opening_bracket: Token::from_content("["),
closing_bracket: Token::from_content("]"),
colon: Token::from_content(":"),
}
}
fn generate_expression_type_tokens(
&self,
_expression_type: &ExpressionType,
) -> ExpressionTypeTokens {
ExpressionTypeTokens {
r#typeof: Token::from_content("typeof"),
opening_parenthese: Token::from_content("("),
closing_parenthese: Token::from_content(")"),
}
}
fn generate_parenthese_type_tokens(
&self,
_parenthese_type: &ParentheseType,
) -> ParentheseTypeTokens {
ParentheseTypeTokens {
left_parenthese: Token::from_content("("),
right_parenthese: Token::from_content(")"),
}
}
fn generate_function_type_tokens(&self, function_type: &FunctionType) -> FunctionTypeTokens {
FunctionTypeTokens {
opening_parenthese: Token::from_content("("),
closing_parenthese: Token::from_content(")"),
arrow: Token::from_content("->"),
commas: intersect_with_token(
comma_token(),
function_type.argument_len()
+ usize::from(function_type.has_variadic_argument_type()),
),
}
}
fn generate_optional_type_token(&self, _optional: &OptionalType) -> Token {
Token::from_content("?")
}
fn generate_intersection_type_token(
&self,
intersection: &IntersectionType,
) -> IntersectionTypeTokens {
IntersectionTypeTokens {
leading_token: intersection
.has_leading_token()
.then(|| Token::from_content("&")),
separators: intersect_with_token(Token::from_content("&"), intersection.len()),
}
}
fn generate_union_type_token(&self, union: &UnionType) -> UnionTypeTokens {
UnionTypeTokens {
leading_token: union.has_leading_token().then(|| Token::from_content("|")),
separators: intersect_with_token(Token::from_content("|"), union.len()),
}
}
fn generate_type_pack_tokens(&self, type_pack: &TypePack) -> TypePackTokens {
TypePackTokens {
left_parenthese: Token::from_content("("),
right_parenthese: Token::from_content(")"),
commas: intersect_with_token(
comma_token(),
type_pack.len() + usize::from(type_pack.has_variadic_type()),
),
}
}
fn generate_generic_parameters_tokens(
&self,
generic_parameters: &GenericParameters,
) -> GenericParametersTokens {
GenericParametersTokens {
opening_list: Token::from_content("<"),
closing_list: Token::from_content(">"),
commas: intersect_with_token(comma_token(), generic_parameters.len()),
}
}
fn generate_generic_parameters_with_defaults_tokens(
&self,
generic_parameters: &GenericParametersWithDefaults,
) -> GenericParametersTokens {
GenericParametersTokens {
opening_list: Token::from_content("<"),
closing_list: Token::from_content(">"),
commas: intersect_with_token(comma_token(), generic_parameters.len()),
}
}
fn generate_interpolated_string_tokens(
&self,
_interpolated_string: &InterpolatedStringExpression,
) -> InterpolatedStringTokens {
InterpolatedStringTokens {
opening_tick: Token::from_content("`"),
closing_tick: Token::from_content("`"),
}
}
fn generate_string_value_segment_tokens(
&self,
_value_segment: &ValueSegment,
) -> ValueSegmentTokens {
ValueSegmentTokens {
opening_brace: Token::from_content("{"),
closing_brace: Token::from_content("}"),
}
}
fn write_symbol(&mut self, symbol: &str) {
if self.currently_commenting {
self.uncomment();
} else if self.needs_space(symbol.chars().next().expect("symbol cannot be empty")) {
self.output.push(' ');
}
self.push_str(symbol);
}
fn write_symbol_without_space_check(&mut self, symbol: &str) {
if self.currently_commenting {
self.uncomment();
}
self.push_str(symbol);
}
fn write_typed_identifier(&mut self, typed_identifier: &TypedIdentifier) {
if let Some(token) = typed_identifier.get_token() {
let name_in_token = token.read(self.original_code);
if name_in_token == typed_identifier.get_name() {
self.write_token(token);
} else {
let mut new_token = token.clone();
new_token.replace_with_content(typed_identifier.get_name().clone());
self.write_token(&new_token);
}
} else {
let name = typed_identifier.get_name();
self.write_symbol(name);
}
if let Some(r#type) = typed_identifier.get_type() {
if let Some(colon) = typed_identifier.get_colon_token() {
self.write_token(colon);
} else {
self.write_symbol(":");
}
self.write_type(r#type);
}
}
#[inline]
fn needs_space(&self, next_character: char) -> bool {
if let Some(last) = self.output.chars().last() {
utils::should_break_with_space(last, next_character)
} else {
false
}
}
#[inline]
fn uncomment(&mut self) {
self.output.push('\n');
self.current_line += 1;
self.currently_commenting = false;
}
}
fn is_single_line_comment(content: &str) -> bool {
let is_multiline_comment = content.starts_with("--[") && {
if let Some((closing_bracket_index, _)) =
content.chars().skip(3).enumerate().find(|(_, c)| *c == '[')
{
content
.get(3..closing_bracket_index)
.map(|substring| substring.chars().all(|c| c == '='))
.unwrap_or(true)
} else {
false
}
};
!is_multiline_comment
}
#[inline]
fn comma_token() -> Token {
Token::from_content(",").with_trailing_trivia(TriviaKind::Whitespace.with_content(" "))
}
impl<'a> LuaGenerator for TokenBasedLuaGenerator<'a> {
fn into_string(self) -> String {
self.output
}
fn write_block(&mut self, block: &Block) {
if let Some(tokens) = block.get_tokens() {
self.write_block_with_tokens(block, tokens);
} else {
self.write_block_with_tokens(block, &self.generate_block_tokens(block));
}
}
fn write_assign_statement(&mut self, assign: &AssignStatement) {
if let Some(tokens) = assign.get_tokens() {
self.write_assign_with_tokens(assign, tokens);
} else {
self.write_assign_with_tokens(assign, &self.generate_assign_tokens(assign));
}
}
fn write_do_statement(&mut self, do_statement: &DoStatement) {
if let Some(tokens) = do_statement.get_tokens() {
self.write_do_with_tokens(do_statement, tokens);
} else {
self.write_do_with_tokens(do_statement, &self.generate_do_tokens(do_statement));
}
}
fn write_compound_assign(&mut self, assign: &CompoundAssignStatement) {
if let Some(tokens) = assign.get_tokens() {
self.write_compound_assign_with_tokens(assign, tokens);
} else {
self.write_compound_assign_with_tokens(
assign,
&self.generate_compound_assign_tokens(assign),
);
}
}
fn write_generic_for(&mut self, generic_for: &GenericForStatement) {
if let Some(tokens) = generic_for.get_tokens() {
self.write_generic_for_with_tokens(generic_for, tokens);
} else {
self.write_generic_for_with_tokens(
generic_for,
&self.generate_generic_for_tokens(generic_for),
);
}
}
fn write_if_statement(&mut self, if_statement: &IfStatement) {
if let Some(tokens) = if_statement.get_tokens() {
self.write_if_statement_with_tokens(if_statement, tokens);
} else {
self.write_if_statement_with_tokens(
if_statement,
&self.generate_if_statement_tokens(if_statement),
);
}
}
fn write_function_statement(&mut self, function: &FunctionStatement) {
if let Some(tokens) = function.get_tokens() {
self.write_function_statement_with_tokens(function, tokens);
} else {
self.write_function_statement_with_tokens(
function,
&self.generate_function_statement_tokens(function),
);
}
}
fn write_last_statement(&mut self, statement: &LastStatement) {
match statement {
LastStatement::Break(token) => {
if let Some(token) = token {
self.write_token(token);
} else {
self.write_symbol("break");
}
}
LastStatement::Continue(token) => {
if let Some(token) = token {
self.write_token(token);
} else {
self.write_symbol("continue");
}
}
LastStatement::Return(return_statement) => {
if let Some(tokens) = return_statement.get_tokens() {
self.write_return_with_tokens(return_statement, tokens);
} else {
self.write_return_with_tokens(
return_statement,
&self.generate_return_tokens(return_statement),
);
}
}
}
}
fn write_local_assign(&mut self, assign: &LocalAssignStatement) {
if let Some(tokens) = assign.get_tokens() {
self.write_local_assign_with_tokens(assign, tokens);
} else {
self.write_local_assign_with_tokens(assign, &self.generate_local_assign_tokens(assign));
}
}
fn write_local_function(&mut self, function: &LocalFunctionStatement) {
if let Some(tokens) = function.get_tokens() {
self.write_local_function_with_tokens(function, tokens);
} else {
self.write_local_function_with_tokens(
function,
&self.generate_local_function_tokens(function),
);
}
}
fn write_numeric_for(&mut self, numeric_for: &NumericForStatement) {
if let Some(tokens) = numeric_for.get_tokens() {
self.write_numeric_for_with_tokens(numeric_for, tokens);
} else {
self.write_numeric_for_with_tokens(
numeric_for,
&self.generate_numeric_for_tokens(numeric_for),
);
}
}
fn write_repeat_statement(&mut self, repeat: &RepeatStatement) {
if let Some(tokens) = repeat.get_tokens() {
self.write_repeat_with_tokens(repeat, tokens);
} else {
self.write_repeat_with_tokens(repeat, &self.generate_repeat_tokens(repeat));
}
}
fn write_while_statement(&mut self, while_statement: &WhileStatement) {
if let Some(tokens) = while_statement.get_tokens() {
self.write_while_with_tokens(while_statement, tokens);
} else {
self.write_while_with_tokens(
while_statement,
&self.generate_while_tokens(while_statement),
);
}
}
fn write_type_declaration_statement(&mut self, statement: &TypeDeclarationStatement) {
if let Some(tokens) = statement.get_tokens() {
self.write_type_declaration_with_tokens(statement, tokens);
} else {
self.write_type_declaration_with_tokens(
statement,
&self.generate_type_declaration_tokens(statement),
);
}
}
fn write_false_expression(&mut self, token: &Option<Token>) {
if let Some(token) = token {
self.write_token(token);
} else {
self.write_symbol("false");
}
}
fn write_true_expression(&mut self, token: &Option<Token>) {
if let Some(token) = token {
self.write_token(token);
} else {
self.write_symbol("true");
}
}
fn write_nil_expression(&mut self, token: &Option<Token>) {
if let Some(token) = token {
self.write_token(token);
} else {
self.write_symbol("nil");
}
}
fn write_variable_arguments_expression(&mut self, token: &Option<Token>) {
if let Some(token) = token {
self.write_token(token);
} else {
self.write_symbol("...");
}
}
fn write_binary_expression(&mut self, binary: &BinaryExpression) {
let operator = binary.operator();
let left = binary.left();
let right = binary.right();
if operator.left_needs_parentheses(left) {
self.write_symbol("(");
self.write_expression(left);
self.write_symbol(")");
} else {
self.write_expression(left);
}
if let Some(operator) = binary.get_token() {
self.write_token(operator);
} else {
self.write_token(&Token::from_content(binary.operator().to_str()));
}
if operator.right_needs_parentheses(right) {
self.write_symbol("(");
self.write_expression(right);
self.write_symbol(")");
} else {
self.write_expression(right);
}
}
fn write_unary_expression(&mut self, unary: &UnaryExpression) {
if let Some(operator) = unary.get_token() {
self.write_token(operator);
} else {
self.write_token(&Token::from_content(unary.operator().to_str()));
}
let expression = unary.get_expression();
match expression {
Expression::Binary(binary) if !binary.operator().precedes_unary_expression() => {
self.write_symbol("(");
self.write_expression(expression);
self.write_symbol(")");
}
_ => self.write_expression(expression),
}
}
fn write_function(&mut self, function: &FunctionExpression) {
if let Some(tokens) = function.get_tokens() {
self.write_function_with_tokens(function, tokens);
} else {
self.write_function_with_tokens(function, &self.generate_function_tokens(function));
}
}
fn write_function_call(&mut self, call: &FunctionCall) {
if let Some(tokens) = call.get_tokens() {
self.write_function_call_with_tokens(call, tokens);
} else {
self.write_function_call_with_tokens(call, &self.generate_function_call_tokens(call));
}
}
fn write_field(&mut self, field: &FieldExpression) {
if let Some(token) = field.get_token() {
self.write_field_with_token(field, token);
} else {
self.write_field_with_token(field, &self.generate_field_token(field));
}
}
fn write_index(&mut self, index: &IndexExpression) {
if let Some(tokens) = index.get_tokens() {
self.write_index_with_tokens(index, tokens);
} else {
self.write_index_with_tokens(index, &self.generate_index_tokens(index));
}
}
fn write_if_expression(&mut self, if_expression: &IfExpression) {
if let Some(token) = if_expression.get_tokens() {
self.write_if_expression_with_token(if_expression, token);
} else {
self.write_if_expression_with_token(
if_expression,
&self.generate_if_tokens(if_expression),
);
}
}
fn write_table(&mut self, table: &TableExpression) {
if let Some(tokens) = table.get_tokens() {
self.write_table_with_tokens(table, tokens);
} else {
self.write_table_with_tokens(table, &self.generate_table_tokens(table));
}
}
fn write_table_entry(&mut self, entry: &TableEntry) {
match entry {
TableEntry::Field(entry) => {
if let Some(tokens) = entry.get_token() {
self.write_table_field_with_tokens(entry, tokens);
} else {
self.write_table_field_with_tokens(
entry,
&self.generate_table_field_tokens(entry),
);
}
}
TableEntry::Index(entry) => {
if let Some(tokens) = entry.get_tokens() {
self.write_table_index_with_tokens(entry, tokens);
} else {
self.write_table_index_with_tokens(
entry,
&self.generate_table_index_tokens(entry),
);
}
}
TableEntry::Value(expression) => self.write_expression(expression),
}
}
fn write_number(&mut self, number: &NumberExpression) {
if let Some(token) = number.get_token() {
self.write_token(token);
} else {
self.write_token(&Token::from_content(utils::write_number(number)));
}
}
fn write_tuple_arguments(&mut self, arguments: &TupleArguments) {
if let Some(tokens) = arguments.get_tokens() {
self.write_tuple_arguments_with_tokens(arguments, tokens);
} else {
self.write_tuple_arguments_with_tokens(
arguments,
&self.generate_tuple_arguments_tokens(arguments),
);
}
}
fn write_string(&mut self, string: &StringExpression) {
if let Some(token) = string.get_token() {
self.write_token(token);
} else {
self.write_symbol(&utils::write_string(string.get_value()));
}
}
fn write_interpolated_string(&mut self, interpolated_string: &InterpolatedStringExpression) {
if let Some(tokens) = interpolated_string.get_tokens() {
self.write_interpolated_string_with_tokens(interpolated_string, tokens);
} else {
self.write_interpolated_string_with_tokens(
interpolated_string,
&self.generate_interpolated_string_tokens(interpolated_string),
);
}
}
fn write_identifier(&mut self, identifier: &Identifier) {
if let Some(token) = identifier.get_token() {
let name_in_token = token.read(self.original_code);
if name_in_token == identifier.get_name() {
self.write_token(token);
} else {
let mut new_token = token.clone();
new_token.replace_with_content(identifier.get_name().clone());
self.write_token(&new_token);
}
} else {
let name = identifier.get_name();
self.write_symbol(name);
}
}
fn write_parenthese(&mut self, parenthese: &ParentheseExpression) {
if let Some(tokens) = parenthese.get_tokens() {
self.write_parenthese_with_tokens(parenthese, tokens);
} else {
self.write_parenthese_with_tokens(
parenthese,
&self.generate_parenthese_tokens(parenthese),
);
}
}
fn write_type_cast(&mut self, type_cast: &TypeCastExpression) {
if let Some(token) = type_cast.get_token() {
self.write_type_cast_with_tokens(type_cast, token);
} else {
self.write_type_cast_with_tokens(type_cast, &self.generate_type_cast_token(type_cast));
}
}
fn write_type_name(&mut self, type_name: &TypeName) {
self.write_identifier(type_name.get_type_name());
if let Some(parameters) = type_name.get_type_parameters() {
if let Some(tokens) = parameters.get_tokens() {
self.write_type_parameters_with_tokens(parameters, tokens);
} else {
self.write_type_parameters_with_tokens(
parameters,
&self.generate_type_parameters_tokens(parameters),
);
}
}
}
fn write_type_field(&mut self, type_field: &TypeField) {
if let Some(tokens) = type_field.get_token() {
self.write_type_field_with_token(type_field, tokens);
} else {
self.write_type_field_with_token(
type_field,
&self.generate_type_field_token(type_field),
);
}
}
fn write_true_type(&mut self, token: &Option<Token>) {
if let Some(token) = token {
self.write_token(token);
} else {
self.write_symbol("true");
}
}
fn write_false_type(&mut self, token: &Option<Token>) {
if let Some(token) = token {
self.write_token(token);
} else {
self.write_symbol("false");
}
}
fn write_nil_type(&mut self, token: &Option<Token>) {
if let Some(token) = token {
self.write_token(token);
} else {
self.write_symbol("nil");
}
}
fn write_string_type(&mut self, string_type: &StringType) {
if let Some(token) = string_type.get_token() {
self.write_token(token);
} else {
self.write_symbol(&utils::write_string(string_type.get_value()));
}
}
fn write_array_type(&mut self, array: &ArrayType) {
if let Some(tokens) = array.get_tokens() {
self.write_array_type_with_tokens(array, tokens);
} else {
self.write_array_type_with_tokens(array, &self.generate_array_type_tokens(array));
}
}
fn write_table_type(&mut self, table_type: &TableType) {
if let Some(tokens) = table_type.get_tokens() {
self.write_table_type_with_tokens(table_type, tokens);
} else {
self.write_table_type_with_tokens(
table_type,
&self.generate_table_type_tokens(table_type),
);
}
}
fn write_expression_type(&mut self, expression_type: &ExpressionType) {
if let Some(tokens) = expression_type.get_tokens() {
self.write_expression_type_with_tokens(expression_type, tokens);
} else {
self.write_expression_type_with_tokens(
expression_type,
&self.generate_expression_type_tokens(expression_type),
);
}
}
fn write_parenthese_type(&mut self, parenthese_type: &ParentheseType) {
if let Some(tokens) = parenthese_type.get_tokens() {
self.write_parenthese_type_with_tokens(parenthese_type, tokens);
} else {
self.write_parenthese_type_with_tokens(
parenthese_type,
&self.generate_parenthese_type_tokens(parenthese_type),
);
}
}
fn write_function_type(&mut self, function_type: &FunctionType) {
if let Some(tokens) = function_type.get_tokens() {
self.write_function_type_with_tokens(function_type, tokens);
} else {
self.write_function_type_with_tokens(
function_type,
&self.generate_function_type_tokens(function_type),
);
}
}
fn write_optional_type(&mut self, optional: &OptionalType) {
if let Some(token) = optional.get_token() {
self.write_optional_type_with_token(optional, token);
} else {
self.write_optional_type_with_token(
optional,
&self.generate_optional_type_token(optional),
);
}
}
fn write_intersection_type(&mut self, intersection: &IntersectionType) {
if let Some(token) = intersection.get_token() {
self.write_intersection_type_with_token(intersection, token);
} else {
self.write_intersection_type_with_token(
intersection,
&self.generate_intersection_type_token(intersection),
);
}
}
fn write_union_type(&mut self, union: &UnionType) {
if let Some(token) = union.get_token() {
self.write_union_type_with_token(union, token);
} else {
self.write_union_type_with_token(union, &self.generate_union_type_token(union));
}
}
fn write_type_pack(&mut self, type_pack: &TypePack) {
if let Some(tokens) = type_pack.get_tokens() {
self.write_type_pack_with_tokens(type_pack, tokens);
} else {
self.write_type_pack_with_tokens(type_pack, &self.generate_type_pack_tokens(type_pack));
}
}
fn write_variadic_type_pack(&mut self, variadic_type_pack: &VariadicTypePack) {
self.push_str("...");
self.write_type(variadic_type_pack.get_type());
}
fn write_generic_type_pack(&mut self, generic_type_pack: &GenericTypePack) {
self.write_identifier(generic_type_pack.get_name());
self.push_str("...");
}
}
fn intersect_with_token(token: Token, list_length: usize) -> Vec<Token> {
iter::repeat_with(|| token.clone())
.take(list_length.saturating_sub(1))
.collect()
}
#[cfg(test)]
mod test {
use super::*;
macro_rules! test_output {
($($name:ident => $code:literal),* $(,)?) => {
$(
#[test]
fn $name() {
let parser = crate::Parser::default().preserve_tokens();
let block = parser.parse($code)
.expect(&format!("failed to parse `{}`", $code));
let mut generator = TokenBasedLuaGenerator::new($code);
generator.write_block(&block);
let output = generator.into_string();
assert_eq!($code, &output);
}
)*
mod without_tokens {
use super::*;
$(
#[test]
fn $name() {
let parser = crate::Parser::default();
let block = parser.parse($code)
.expect(&format!("failed to parse `{}`", $code));
let mut generator = TokenBasedLuaGenerator::new($code);
generator.write_block(&block);
let output = generator.into_string();
let parsed_output_block = parser.parse(&output)
.expect(&format!("failed to parse generated code `{}`", &output));
pretty_assertions::assert_eq!(block, parsed_output_block);
}
)*
}
};
}
test_output!(
assign => "var = true",
assign_multiple => "var, var2 =\n\ttrue,\tfalse\n",
empty_do => "do end\n",
nested_do => "do\n do end\nend\n",
call_without_arguments => "call()",
call_print => "print('hi')",
call_print_with_string_argument => "print 'hi' -- no parentheses",
call_function_with_table_multiline => "process {\n\targ = true,\n\tflag = false,\n}\n",
call_method_without_arguments => "foo:bar()",
call_method_with_arguments => "foo:bar(true, false)",
call_string_format => "('foo'):rep(3)",
call_math_floor => "math.floor(value)",
call_with_index => "object[ key ](i)",
compound_increment => "i += 1\n",
empty_function_declaration => "function process()\nend",
empty_static_function_declaration => "function Class .new()\nend",
empty_method_function_declaration => "function Class : process()\nend",
empty_nested_method_function_declaration => "function Class . foo.bar : help ()\nend",
empty_function_declaration_with_params => "function process(a, b --[[ optional ]]) end",
empty_variadic_function_declaration => "function process (...) end",
empty_variadic_function_declaration_with_one_param => "function format (str, ... --[[ optional strings ]]) end",
variadic_function_returns => "function identity(...)\n\treturn ...\nend\n",
empty_generic_for => "for key, value in pairs(result) do\n\t-- help\nend",
empty_generic_for_key_only => "for key in pairs(dict) do end",
generic_for_with_next => "for key,value in next, dict do\n\tprint(key, value)\nend\n",
empty_if => "if true then\nend",
if_condition_return => "if condition then\n\treturn\nend",
empty_if_with_empty_elseif => "if true then\nelseif false then\nend\n",
empty_if_with_two_empty_elseif => "if a then\nelseif b then\nelseif c then\n\tprint(c)\nend\n",
empty_if_with_empty_else => "if true then\nelse\nend\n",
empty_if_with_else_block => "if true then\n\t-- help\nelse\n\treturn\nend\n",
declare_one_variable => "local var\n",
declare_two_variables => "local var, var2\n",
local_assign_one_variable => "local var = true",
local_assign_two_variables => "local var, var2 = true, false",
local_empty_function => "local function process()\nend",
local_empty_variadic_function => "local function process(...)\nend",
local_empty_function_with_one_argument => "local function process( a )\nend",
local_empty_function_with_two_arguments => "local function process(a, b)\nend",
local_empty_variadic_function_with_two_arguments => "local function process(a, b, ...)\nend",
local_identity_function => "local function identity(...)\n\treturn ...\nend",
empty_numeric_for => "for i = 1, final do\nend\n",
empty_numeric_for_with_step => "for i = 1, final, step do\nend\n",
numeric_for => "for i = 1, #list do\n\tprocess(list[i])\nend",
empty_repeat => "repeat until false",
repeat_break_immediately => "repeat break until false",
empty_while => "while true do end",
while_break_immediately => "while true do\n\tbreak\nend",
break_with_comment => "break -- exit loop",
continue_with_comment => "continue -- skip to next iteration",
empty_return => "return\n",
return_true => "return true",
return_false => "return false",
return_nil => "return nil",
return_single_quote_string => "return 'ok'",
return_double_quote_string => "return \"ok\"",
return_identifier => "return var",
return_bracket_string => "return [[ [ok] ]]",
return_empty_interpolated_string => "return ``",
return_interpolated_string_escape_curly_brace => "return `Open: \\{`",
return_interpolated_string_followed_by_comment => "return `ok` -- comment",
return_interpolated_string_with_true_value => "return `{ true }`",
return_interpolated_string_with_true_value_and_prefix => "return `Result = { true }`",
return_interpolated_string_with_true_value_and_suffix => "return `{ variable } !`",
return_interpolated_string_with_various_segments => "return `Variable = { variable } ({ --[[len]] #variable })` -- display",
return_empty_table => "return { }",
return_table_with_field => "return { field = {} }",
return_table_with_index => "return { [field] = {} }",
return_list_of_one_element => "return { true, }",
return_list_of_two_elements => "return { true, false }",
return_mixed_table => "return { true, field = false, [\"hello\"] = true }",
return_parenthese_call => "return ( call() )",
return_variable_arguments => "return ...",
return_unary_minus => "return - number",
return_unary_length => "return #list\n",
return_unary_not => "return not condition\n",
return_binary_and => "return a and b",
return_binary_or => "return a or b",
return_binary_plus => "return 10 + 15",
return_empty_function => "return function() end",
return_empty_variadic_function => "return function(...)\nend",
return_empty_function_with_one_argument => "return function( a )\nend",
return_empty_function_with_two_arguments => "return function(a, b)\nend",
return_empty_variadic_function_with_two_arguments => "return function(a, b, ...)\nend",
return_identity_function => "return function(...)\n\treturn ...\nend",
return_field => "return math.huge",
return_field_ending_with_number => "return UDim2.new",
return_field_split_on_lines => "return value.\n\tproperty\n\t.name",
);
#[test]
fn inserts_a_new_line_after_a_comment_for_a_token() {
let statement = RepeatStatement::new(Block::default(), true).with_tokens(RepeatTokens {
repeat: Token::from_content("repeat")
.with_trailing_trivia(TriviaKind::Comment.with_content("-- hello")),
until: Token::from_content("until"),
});
let mut generator = TokenBasedLuaGenerator::new("");
generator.write_repeat_statement(&statement);
let output = generator.into_string();
crate::Parser::default()
.parse(&output)
.unwrap_or_else(|_| panic!("failed to parse generated code `{}`", &output));
}
#[test]
fn inserts_a_new_line_after_custom_added_comments() {
let code = "call(a--comment\n\t,b\n)";
let mut block = crate::Parser::default()
.preserve_tokens()
.parse(code)
.unwrap();
let call = match block.iter_mut_statements().last().unwrap() {
Statement::Call(call) => call,
_ => panic!("unexpected statement"),
};
let tuple = match call.mutate_arguments() {
Arguments::Tuple(tuple) => tuple,
_ => panic!("unexpected arguments"),
};
let mut tokens = tuple.get_tokens().unwrap().clone();
tuple.iter_mut_values().for_each(|value| match value {
Expression::Identifier(identifier) => {
let name = identifier.mutate_name();
let new_token = {
Token::from_content(name.to_owned())
.with_trailing_trivia(TriviaKind::Comment.with_content("--new comment"))
};
identifier.set_token(new_token);
}
_ => panic!("unexpected expression"),
});
tokens.commas.pop();
tuple.set_tokens(tokens);
let mut generator = TokenBasedLuaGenerator::new(code);
generator.write_block(&block);
let output = generator.into_string();
crate::Parser::default()
.parse(&output)
.unwrap_or_else(|_| panic!("failed to parse generated code `{}`", &output));
insta::assert_snapshot!("inserts_a_new_line_after_custom_added_comments", output);
}
}