diff --git a/src/rules/convert_luau_numbers.rs b/src/rules/convert_luau_numbers.rs new file mode 100644 index 00000000..35350018 --- /dev/null +++ b/src/rules/convert_luau_numbers.rs @@ -0,0 +1,106 @@ +use crate::{ + nodes::{Block, DecimalNumber, NumberExpression}, + process::{DefaultVisitor, NodeProcessor, NodeVisitor}, + rules::{Context, FlawlessRule, RuleConfiguration, RuleConfigurationError, RuleProperties}, +}; + +use super::verify_no_rule_properties; + +#[derive(Default)] +struct Processor<'a> { + code: &'a str, +} + +impl NodeProcessor for Processor<'_> { + fn process_number_expression(&mut self, num_exp: &mut NumberExpression) { + if let NumberExpression::Binary(binary) = num_exp { + let value = binary.compute_value(); + *num_exp = DecimalNumber::new(value).into(); + return; + } + if let Some(token) = num_exp.get_token() { + let content = token.read(self.code); + let mut underscore_removed = String::with_capacity(content.len()); + let mut changed = false; + + for c in content.chars() { + if c != '_' { + underscore_removed.push(c); + } else { + changed = true; + } + } + + if changed { + let mut new_token = token.clone(); + new_token.replace_with_content(underscore_removed); + num_exp.set_token(new_token); + } + } + } +} + +impl<'a> Processor<'a> { + fn new(code: &'a str) -> Self { + Self { code } + } +} + +pub const CONVERT_LUAU_NUMBERS_RULE_NAME: &str = "convert_luau_numbers"; + +/// A rule that converts Luau number literals to decimal numbers. +#[derive(Default, Debug, PartialEq, Eq)] +pub struct ConvertLuauNumbers {} + +impl FlawlessRule for ConvertLuauNumbers { + fn flawless_process(&self, block: &mut Block, context: &Context) { + let mut processor = Processor::new(context.original_code()); + DefaultVisitor::visit_block(block, &mut processor); + } +} + +impl RuleConfiguration for ConvertLuauNumbers { + fn configure(&mut self, properties: RuleProperties) -> Result<(), RuleConfigurationError> { + verify_no_rule_properties(&properties)?; + + Ok(()) + } + + fn get_name(&self) -> &'static str { + CONVERT_LUAU_NUMBERS_RULE_NAME + } + + fn serialize_to_properties(&self) -> RuleProperties { + RuleProperties::new() + } +} + +#[cfg(test)] +mod test { + use super::*; + use crate::rules::Rule; + + use insta::assert_json_snapshot; + + fn new_rule() -> ConvertLuauNumbers { + ConvertLuauNumbers::default() + } + + #[test] + fn serialize_default_rule() { + let rule: Box = Box::new(new_rule()); + + assert_json_snapshot!("default_convert_luau_numbers", rule); + } + + #[test] + fn configure_with_extra_field_error() { + let result = json5::from_str::>( + r#"{ + rule: 'convert_luau_numbers', + prop: "something", + }"#, + ); + pretty_assertions::assert_eq!(result.unwrap_err().to_string(), "unexpected field 'prop'"); + } +} diff --git a/src/rules/mod.rs b/src/rules/mod.rs index e0050181..0ea21db0 100644 --- a/src/rules/mod.rs +++ b/src/rules/mod.rs @@ -6,6 +6,7 @@ mod call_parens; mod compute_expression; mod configuration_error; mod convert_index_to_field; +mod convert_luau_numbers; mod convert_require; mod empty_do; mod filter_early_return; @@ -39,6 +40,7 @@ pub use call_parens::*; pub use compute_expression::*; pub use configuration_error::RuleConfigurationError; pub use convert_index_to_field::*; +pub use convert_luau_numbers::*; pub use convert_require::*; pub use empty_do::*; pub use filter_early_return::*; @@ -265,6 +267,7 @@ pub fn get_all_rule_names() -> Vec<&'static str> { RENAME_VARIABLES_RULE_NAME, REMOVE_IF_EXPRESSION_RULE_NAME, REMOVE_CONTINUE_RULE_NAME, + CONVERT_LUAU_NUMBERS_RULE_NAME, ] } @@ -301,6 +304,7 @@ impl FromStr for Box { RENAME_VARIABLES_RULE_NAME => Box::::default(), REMOVE_IF_EXPRESSION_RULE_NAME => Box::::default(), REMOVE_CONTINUE_RULE_NAME => Box::::default(), + CONVERT_LUAU_NUMBERS_RULE_NAME => Box::::default(), _ => return Err(format!("invalid rule name: {}", string)), }; diff --git a/src/rules/snapshots/darklua_core__rules__convert_luau_numbers__test__default_convert_luau_numbers.snap b/src/rules/snapshots/darklua_core__rules__convert_luau_numbers__test__default_convert_luau_numbers.snap new file mode 100644 index 00000000..43809221 --- /dev/null +++ b/src/rules/snapshots/darklua_core__rules__convert_luau_numbers__test__default_convert_luau_numbers.snap @@ -0,0 +1,5 @@ +--- +source: src/rules/convert_luau_numbers.rs +expression: rule +--- +"convert_luau_numbers" diff --git a/src/rules/snapshots/darklua_core__rules__remove_number_literals__test__default_remove_number_literals.snap b/src/rules/snapshots/darklua_core__rules__remove_number_literals__test__default_remove_number_literals.snap new file mode 100644 index 00000000..492fc9b0 --- /dev/null +++ b/src/rules/snapshots/darklua_core__rules__remove_number_literals__test__default_remove_number_literals.snap @@ -0,0 +1,5 @@ +--- +source: src/rules/remove_number_literals.rs +expression: rule +--- +"remove_number_literals" diff --git a/src/rules/snapshots/darklua_core__rules__test__all_rule_names.snap b/src/rules/snapshots/darklua_core__rules__test__all_rule_names.snap index b7cf2528..800aa4e4 100644 --- a/src/rules/snapshots/darklua_core__rules__test__all_rule_names.snap +++ b/src/rules/snapshots/darklua_core__rules__test__all_rule_names.snap @@ -27,5 +27,6 @@ expression: rule_names "remove_unused_while", "rename_variables", "remove_if_expression", - "remove_continue" + "remove_continue", + "convert_luau_numbers" ] diff --git a/tests/rule_tests/convert_luau_numbers.rs b/tests/rule_tests/convert_luau_numbers.rs new file mode 100644 index 00000000..d381d559 --- /dev/null +++ b/tests/rule_tests/convert_luau_numbers.rs @@ -0,0 +1,25 @@ +use darklua_core::rules::{ConvertLuauNumbers, Rule}; + +test_rule!( + convert_luau_numbers, + ConvertLuauNumbers::default(), + binary_integer_literals("local a = 0b01010101 local b = 0B01010101 local c = 0b_0101_0101 local d = 0B_0101_0101 local e = 0b__________0101_0101") + => "local a = 85 local b = 85 local c = 85 local d = 85 local e = 85", + decimal_separators("local a = 1_048_576 local a1 = 1___048__576__ local b = 0xFFFF_FFFF local c = 0b_0101_0101 local d = 0B_0101_0101") + => "local a = 1048576 local a1 = 1048576 local b = 0xFFFFFFFF local c = 85 local d = 85", +); + +#[test] +fn deserialize_from_object_notation() { + json5::from_str::>( + r#"{ + rule: 'convert_luau_numbers', + }"#, + ) + .unwrap(); +} + +#[test] +fn deserialize_from_string() { + json5::from_str::>("'convert_luau_numbers'").unwrap(); +} diff --git a/tests/rule_tests/mod.rs b/tests/rule_tests/mod.rs index f73e5580..674a760d 100644 --- a/tests/rule_tests/mod.rs +++ b/tests/rule_tests/mod.rs @@ -418,6 +418,7 @@ macro_rules! test_rule_snapshot { mod append_text_comment; mod compute_expression; mod convert_index_to_field; +mod convert_luau_numbers; mod convert_require; mod filter_early_return; mod group_local_assignment;