diff --git a/src/compile/mod.rs b/src/compile/mod.rs index 20971e9..dcbfe91 100644 --- a/src/compile/mod.rs +++ b/src/compile/mod.rs @@ -19,7 +19,7 @@ impl std::fmt::Display for Operand { match self { Operand::Variable(name) => write!(f, "{}", name), Operand::String(string) => { - let escaped = string.replace('\\', r#"\\"#).replace('"', r#"\""#); + let escaped = string.replace('\\', r"\\").replace('"', r#"\""#); write!(f, "\"{}\"", escaped) } Operand::Integer(int) => write!(f, "{}", int), @@ -50,7 +50,7 @@ pub enum MindustryOperation { } impl MindustryOperation { - fn operands<'a>(&'a self) -> Box<[&'a Operand]> { + fn operands(&self) -> Box<[&Operand]> { match self { Self::JumpIf(_label, _operator, lhs, rhs) => Box::new([lhs, rhs]), Self::Operator(_target, _operator, lhs, rhs) => Box::new([lhs, rhs]), @@ -65,7 +65,7 @@ impl MindustryOperation { } } - fn operands_mut<'a>(&'a mut self) -> Vec<&'a mut Operand> { + fn operands_mut(&mut self) -> Vec<&mut Operand> { match self { Self::JumpIf(_label, _operator, lhs, rhs) => vec![lhs, rhs], Self::Operator(_target, _operator, lhs, rhs) => vec![lhs, rhs], @@ -91,7 +91,7 @@ impl MindustryOperation { } } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Default)] pub struct MindustryProgram(pub Vec); impl MindustryProgram { @@ -225,7 +225,7 @@ pub fn translate_ast( condition_name.clone(), )); - if false_branch.instructions.len() > 0 { + if !false_branch.instructions.is_empty() { let else_label = namer.label("else"); let end_label = namer.label("endif"); res.push(MindustryOperation::JumpIf( @@ -301,18 +301,12 @@ pub fn translate_ast( res.push(MindustryOperation::GenericMut( target_name.clone(), out_name, - argument_names - .into_iter() - .map(|name| Operand::Variable(name)) - .collect(), + argument_names.into_iter().map(Operand::Variable).collect(), )); } else { res.push(MindustryOperation::Generic( target_name.clone(), - argument_names - .into_iter() - .map(|name| Operand::Variable(name)) - .collect(), + argument_names.into_iter().map(Operand::Variable).collect(), )); } } @@ -403,7 +397,7 @@ impl std::fmt::Display for MindustryProgram { for operand in operands { write!(f, " {}", operand)?; } - write!(f, "\n")?; + writeln!(f)?; } MindustryOperation::GenericMut(name, out_name, operands) => { write!(f, "{}", name)?; @@ -411,7 +405,7 @@ impl std::fmt::Display for MindustryProgram { for operand in operands { write!(f, " {}", operand)?; } - write!(f, "\n")?; + writeln!(f)?; } } } diff --git a/src/compile/optimize/constant.rs b/src/compile/optimize/constant.rs index 7ef12a3..5bcdbb3 100644 --- a/src/compile/optimize/constant.rs +++ b/src/compile/optimize/constant.rs @@ -10,11 +10,7 @@ pub fn optimize_constant(program: MindustryProgram) -> MindustryProgram { return true; } - if name.starts_with('@') { - false - } else { - true - } + !name.starts_with('@') }; let res = replace_if(program, |instructions, instruction, use_index| { @@ -67,12 +63,12 @@ pub fn optimize_constant(program: MindustryProgram) -> MindustryProgram { .filter(|(_name, value, set_index)| { // Don't optimize operands that refer to a mutating variable (either mutable @-variables or instructions that get updated in-between) if let Operand::Variable(assigned_var) = value { - if !is_safe_variable(&assigned_var) { + if !is_safe_variable(assigned_var) { return false; } for instr_between in &instructions[*set_index..use_index] { - if instr_between.mutates(&assigned_var) { + if instr_between.mutates(assigned_var) { return false; } } @@ -85,8 +81,8 @@ pub fn optimize_constant(program: MindustryProgram) -> MindustryProgram { .map(|(name, value, _index)| (name, value)) .collect::>(); - if optimizable_operands.len() == 0 { - return None + if optimizable_operands.is_empty() { + return None; } let mut instruction = instruction.clone(); diff --git a/src/compile/optimize/dead.rs b/src/compile/optimize/dead.rs index 2ae558c..0db9236 100644 --- a/src/compile/optimize/dead.rs +++ b/src/compile/optimize/dead.rs @@ -6,11 +6,10 @@ pub(crate) fn optimize_dead_code(program: MindustryProgram) -> MindustryProgram let mut needed_vars = HashSet::new(); let mut needed_labels = HashSet::new(); - let mut push_var = |operand: &Operand| match operand { - Operand::Variable(name) => { + let mut push_var = |operand: &Operand| { + if let Operand::Variable(name) = operand { needed_vars.insert(name.clone()); } - _ => {} }; for instruction in program.0.iter() { @@ -44,31 +43,32 @@ pub(crate) fn optimize_dead_code(program: MindustryProgram) -> MindustryProgram } // Remove unneeded `set`s and `op`s - replace_if(program, |_instructions, instruction, _index| { - match instruction { + replace_if( + program, + |_instructions, instruction, _index| match instruction { MindustryOperation::Set(name, _) | MindustryOperation::Operator(name, _, _, _) => { if !tmp_regex.is_match(name) { - return None + return None; } if needed_vars.contains(name) { - return None + return None; } - return Some(vec![]) + Some(vec![]) } MindustryOperation::JumpLabel(label) => { if !label_regex.is_match(label) { - return None + return None; } if needed_labels.contains(label) { - return None + return None; } - return Some(vec![]) + Some(vec![]) } - _ => None - } - }) + _ => None, + }, + ) } diff --git a/src/compile/optimize/replace.rs b/src/compile/optimize/replace.rs index 6ee53fe..3c5c87a 100644 --- a/src/compile/optimize/replace.rs +++ b/src/compile/optimize/replace.rs @@ -1,9 +1,12 @@ use super::*; - pub(crate) fn replace_if(program: MindustryProgram, callback: F) -> MindustryProgram where - F: for<'c> Fn(&'c Vec, &'c MindustryOperation, usize) -> Option> + F: for<'c> Fn( + &'c Vec, + &'c MindustryOperation, + usize, + ) -> Option>, { let mut res = MindustryProgram::new(); diff --git a/src/cursor.rs b/src/cursor.rs index f14499a..382ad5f 100644 --- a/src/cursor.rs +++ b/src/cursor.rs @@ -103,6 +103,10 @@ impl<'a, T> Cursor<'a, T> { self.data.len().saturating_sub(self.offset) } + pub fn is_empty(&self) -> bool { + self.len() == 0 + } + pub fn range>(&self, range: R) -> Self { Self { data: &self.data[self.offset..][range], diff --git a/src/parse/ast.rs b/src/parse/ast.rs index 4755f5f..1cdc990 100644 --- a/src/parse/ast.rs +++ b/src/parse/ast.rs @@ -46,7 +46,7 @@ pub fn build_ast(tokens: &[BasicToken], config: &Config) -> Result, Context)> = vec![(Vec::new(), Context::Main)]; - while tokens.len() > 0 { + while !tokens.is_empty() { let Some((ref mut instructions, _context)) = context_stack.last_mut() else { unreachable!("Context stack got emptied!"); }; @@ -225,7 +225,7 @@ pub fn build_ast(tokens: &[BasicToken], config: &Config) -> Result 1 { match &context_stack.last().unwrap().1 { @@ -277,7 +277,7 @@ pub(crate) fn parse_expression( tokens: &mut Cursor<'_, BasicToken>, ) -> Result { /// Returns the first non-newline token in `tokens` - fn peek<'a>(tokens: &'a [BasicToken]) -> Option<&'a BasicToken> { + fn peek(tokens: &[BasicToken]) -> Option<&BasicToken> { tokens.iter().find(|t| !matches!(t, BasicToken::NewLine)) } diff --git a/src/parse/tokenize.rs b/src/parse/tokenize.rs index cc2e908..9753fe7 100644 --- a/src/parse/tokenize.rs +++ b/src/parse/tokenize.rs @@ -86,10 +86,10 @@ pub fn tokenize(raw: &str) -> Result, ParseError> { // TODO: handle labels for mut line in raw.lines() { - if line.len() > 0 { + if !line.is_empty() { res.push(BasicToken::NewLine); } - while line.len() > 0 { + while !line.is_empty() { // Main match clause for tokens match_token!(line, res; match_space => (), diff --git a/tests/examples.rs b/tests/examples.rs index 1964e87..1568138 100644 --- a/tests/examples.rs +++ b/tests/examples.rs @@ -3,30 +3,35 @@ use std::path::{Path, PathBuf}; use basic_to_mindustry::common::Config; -use basic_to_mindustry::compile::{optimize_constant, translate_ast, optimize_jump_op, optimize_jump_always}; +use basic_to_mindustry::compile::{ + optimize_constant, optimize_jump_always, optimize_jump_op, translate_ast, +}; use basic_to_mindustry::parse::{build_ast, tokenize}; -fn read_basic_examples() -> impl Iterator { - Path::new("./examples/").read_dir().unwrap().filter_map(|entry| { - let Ok(entry) = entry else { - return None; - }; - - if entry - .file_name() - .into_string() - .map(|name| name.ends_with(".basic")) - .unwrap_or(false) - { - let file_name = entry.file_name().into_string().unwrap(); - let file = std::fs::read_to_string(entry.path()).unwrap_or_else(|e| { - panic!("Error opening {:?}: {:?}", file_name, e); - }); - Some((file_name, file)) - } else { - None - } - }) +fn read_basic_examples() -> impl Iterator { + Path::new("./examples/") + .read_dir() + .unwrap() + .filter_map(|entry| { + let Ok(entry) = entry else { + return None; + }; + + if entry + .file_name() + .into_string() + .map(|name| name.ends_with(".basic")) + .unwrap_or(false) + { + let file_name = entry.file_name().into_string().unwrap(); + let file = std::fs::read_to_string(entry.path()).unwrap_or_else(|e| { + panic!("Error opening {:?}: {:?}", file_name, e); + }); + Some((file_name, file)) + } else { + None + } + }) } #[test] @@ -53,17 +58,27 @@ fn test_examples_opt() { let config = Config::default(); for (file_name, file) in read_basic_examples() { - let Some(program_name) = PathBuf::from(file_name.clone()).file_stem().and_then(|stem| stem.to_str()).map(|s| s.to_string()) else { - panic!("Basic program in examples/ has an invalid filename: {}", file_name); + let Some(program_name) = PathBuf::from(file_name.clone()) + .file_stem() + .and_then(|stem| stem.to_str()) + .map(|s| s.to_string()) + else { + panic!( + "Basic program in examples/ has an invalid filename: {}", + file_name + ); }; let opt_0 = format!("tests/examples/{}.0.mlog", program_name); if !std::fs::try_exists(&opt_0).unwrap() { - continue + continue; } let opt_0 = std::fs::read_to_string(opt_0).unwrap_or_else(|e| { - panic!("Couldn't open tests/examples/{}.0.mlog: {:?}", program_name, e); + panic!( + "Couldn't open tests/examples/{}.0.mlog: {:?}", + program_name, e + ); }); let tokenized = tokenize(&file).unwrap_or_else(|e| { @@ -74,14 +89,17 @@ fn test_examples_opt() { }); let translated = translate_ast(&parsed, &mut Default::default(), &config); - pretty_assertions::assert_eq!(opt_0.trim(), format!("{}", translated).trim()); let optimized = optimize_jump_always(optimize_jump_op(optimize_constant(translated))); - let opt_1 = std::fs::read_to_string(format!("tests/examples/{}.1.mlog", program_name)).unwrap_or_else(|e| { - panic!("Couldn't open tests/examples/{}.1.mlog: {:?}", program_name, e); - }); + let opt_1 = std::fs::read_to_string(format!("tests/examples/{}.1.mlog", program_name)) + .unwrap_or_else(|e| { + panic!( + "Couldn't open tests/examples/{}.1.mlog: {:?}", + program_name, e + ); + }); pretty_assertions::assert_eq!(opt_1.trim(), format!("{}", optimized).trim()); }