🎨 Make cargo clippy happy

main
Shad Amethyst 9 months ago
parent 790da1870b
commit 5949174cdb

@ -19,7 +19,7 @@ impl std::fmt::Display for Operand {
match self { match self {
Operand::Variable(name) => write!(f, "{}", name), Operand::Variable(name) => write!(f, "{}", name),
Operand::String(string) => { Operand::String(string) => {
let escaped = string.replace('\\', r#"\\"#).replace('"', r#"\""#); let escaped = string.replace('\\', r"\\").replace('"', r#"\""#);
write!(f, "\"{}\"", escaped) write!(f, "\"{}\"", escaped)
} }
Operand::Integer(int) => write!(f, "{}", int), Operand::Integer(int) => write!(f, "{}", int),
@ -50,7 +50,7 @@ pub enum MindustryOperation {
} }
impl MindustryOperation { impl MindustryOperation {
fn operands<'a>(&'a self) -> Box<[&'a Operand]> { fn operands(&self) -> Box<[&Operand]> {
match self { match self {
Self::JumpIf(_label, _operator, lhs, rhs) => Box::new([lhs, rhs]), Self::JumpIf(_label, _operator, lhs, rhs) => Box::new([lhs, rhs]),
Self::Operator(_target, _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 { match self {
Self::JumpIf(_label, _operator, lhs, rhs) => vec![lhs, rhs], Self::JumpIf(_label, _operator, lhs, rhs) => vec![lhs, rhs],
Self::Operator(_target, _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<MindustryOperation>); pub struct MindustryProgram(pub Vec<MindustryOperation>);
impl MindustryProgram { impl MindustryProgram {
@ -225,7 +225,7 @@ pub fn translate_ast(
condition_name.clone(), condition_name.clone(),
)); ));
if false_branch.instructions.len() > 0 { if !false_branch.instructions.is_empty() {
let else_label = namer.label("else"); let else_label = namer.label("else");
let end_label = namer.label("endif"); let end_label = namer.label("endif");
res.push(MindustryOperation::JumpIf( res.push(MindustryOperation::JumpIf(
@ -301,18 +301,12 @@ pub fn translate_ast(
res.push(MindustryOperation::GenericMut( res.push(MindustryOperation::GenericMut(
target_name.clone(), target_name.clone(),
out_name, out_name,
argument_names argument_names.into_iter().map(Operand::Variable).collect(),
.into_iter()
.map(|name| Operand::Variable(name))
.collect(),
)); ));
} else { } else {
res.push(MindustryOperation::Generic( res.push(MindustryOperation::Generic(
target_name.clone(), target_name.clone(),
argument_names argument_names.into_iter().map(Operand::Variable).collect(),
.into_iter()
.map(|name| Operand::Variable(name))
.collect(),
)); ));
} }
} }
@ -403,7 +397,7 @@ impl std::fmt::Display for MindustryProgram {
for operand in operands { for operand in operands {
write!(f, " {}", operand)?; write!(f, " {}", operand)?;
} }
write!(f, "\n")?; writeln!(f)?;
} }
MindustryOperation::GenericMut(name, out_name, operands) => { MindustryOperation::GenericMut(name, out_name, operands) => {
write!(f, "{}", name)?; write!(f, "{}", name)?;
@ -411,7 +405,7 @@ impl std::fmt::Display for MindustryProgram {
for operand in operands { for operand in operands {
write!(f, " {}", operand)?; write!(f, " {}", operand)?;
} }
write!(f, "\n")?; writeln!(f)?;
} }
} }
} }

@ -10,11 +10,7 @@ pub fn optimize_constant(program: MindustryProgram) -> MindustryProgram {
return true; return true;
} }
if name.starts_with('@') { !name.starts_with('@')
false
} else {
true
}
}; };
let res = replace_if(program, |instructions, instruction, use_index| { 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)| { .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) // 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 let Operand::Variable(assigned_var) = value {
if !is_safe_variable(&assigned_var) { if !is_safe_variable(assigned_var) {
return false; return false;
} }
for instr_between in &instructions[*set_index..use_index] { for instr_between in &instructions[*set_index..use_index] {
if instr_between.mutates(&assigned_var) { if instr_between.mutates(assigned_var) {
return false; return false;
} }
} }
@ -85,8 +81,8 @@ pub fn optimize_constant(program: MindustryProgram) -> MindustryProgram {
.map(|(name, value, _index)| (name, value)) .map(|(name, value, _index)| (name, value))
.collect::<Vec<_>>(); .collect::<Vec<_>>();
if optimizable_operands.len() == 0 { if optimizable_operands.is_empty() {
return None return None;
} }
let mut instruction = instruction.clone(); let mut instruction = instruction.clone();

@ -6,11 +6,10 @@ pub(crate) fn optimize_dead_code(program: MindustryProgram) -> MindustryProgram
let mut needed_vars = HashSet::new(); let mut needed_vars = HashSet::new();
let mut needed_labels = HashSet::new(); let mut needed_labels = HashSet::new();
let mut push_var = |operand: &Operand| match operand { let mut push_var = |operand: &Operand| {
Operand::Variable(name) => { if let Operand::Variable(name) = operand {
needed_vars.insert(name.clone()); needed_vars.insert(name.clone());
} }
_ => {}
}; };
for instruction in program.0.iter() { 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 // Remove unneeded `set`s and `op`s
replace_if(program, |_instructions, instruction, _index| { replace_if(
match instruction { program,
|_instructions, instruction, _index| match instruction {
MindustryOperation::Set(name, _) | MindustryOperation::Operator(name, _, _, _) => { MindustryOperation::Set(name, _) | MindustryOperation::Operator(name, _, _, _) => {
if !tmp_regex.is_match(name) { if !tmp_regex.is_match(name) {
return None return None;
} }
if needed_vars.contains(name) { if needed_vars.contains(name) {
return None return None;
} }
return Some(vec![]) Some(vec![])
} }
MindustryOperation::JumpLabel(label) => { MindustryOperation::JumpLabel(label) => {
if !label_regex.is_match(label) { if !label_regex.is_match(label) {
return None return None;
} }
if needed_labels.contains(label) { if needed_labels.contains(label) {
return None return None;
} }
return Some(vec![]) Some(vec![])
} }
_ => None _ => None,
} },
}) )
} }

@ -1,9 +1,12 @@
use super::*; use super::*;
pub(crate) fn replace_if<F>(program: MindustryProgram, callback: F) -> MindustryProgram pub(crate) fn replace_if<F>(program: MindustryProgram, callback: F) -> MindustryProgram
where where
F: for<'c> Fn(&'c Vec<MindustryOperation>, &'c MindustryOperation, usize) -> Option<Vec<MindustryOperation>> F: for<'c> Fn(
&'c Vec<MindustryOperation>,
&'c MindustryOperation,
usize,
) -> Option<Vec<MindustryOperation>>,
{ {
let mut res = MindustryProgram::new(); let mut res = MindustryProgram::new();

@ -103,6 +103,10 @@ impl<'a, T> Cursor<'a, T> {
self.data.len().saturating_sub(self.offset) self.data.len().saturating_sub(self.offset)
} }
pub fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn range<R: std::slice::SliceIndex<[T], Output = [T]>>(&self, range: R) -> Self { pub fn range<R: std::slice::SliceIndex<[T], Output = [T]>>(&self, range: R) -> Self {
Self { Self {
data: &self.data[self.offset..][range], data: &self.data[self.offset..][range],

@ -46,7 +46,7 @@ pub fn build_ast(tokens: &[BasicToken], config: &Config) -> Result<BasicAstBlock
let mut context_stack: Vec<(Vec<BasicAstInstruction>, Context)> = let mut context_stack: Vec<(Vec<BasicAstInstruction>, Context)> =
vec![(Vec::new(), Context::Main)]; vec![(Vec::new(), Context::Main)];
while tokens.len() > 0 { while !tokens.is_empty() {
let Some((ref mut instructions, _context)) = context_stack.last_mut() else { let Some((ref mut instructions, _context)) = context_stack.last_mut() else {
unreachable!("Context stack got emptied!"); unreachable!("Context stack got emptied!");
}; };
@ -225,7 +225,7 @@ pub fn build_ast(tokens: &[BasicToken], config: &Config) -> Result<BasicAstBlock
} }
} }
if context_stack.len() == 0 { if context_stack.is_empty() {
unreachable!("Empty context stack"); unreachable!("Empty context stack");
} else if context_stack.len() > 1 { } else if context_stack.len() > 1 {
match &context_stack.last().unwrap().1 { match &context_stack.last().unwrap().1 {
@ -277,7 +277,7 @@ pub(crate) fn parse_expression(
tokens: &mut Cursor<'_, BasicToken>, tokens: &mut Cursor<'_, BasicToken>,
) -> Result<BasicAstExpression, ParseError> { ) -> Result<BasicAstExpression, ParseError> {
/// Returns the first non-newline token in `tokens` /// 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)) tokens.iter().find(|t| !matches!(t, BasicToken::NewLine))
} }

@ -86,10 +86,10 @@ pub fn tokenize(raw: &str) -> Result<Vec<BasicToken>, ParseError> {
// TODO: handle labels // TODO: handle labels
for mut line in raw.lines() { for mut line in raw.lines() {
if line.len() > 0 { if !line.is_empty() {
res.push(BasicToken::NewLine); res.push(BasicToken::NewLine);
} }
while line.len() > 0 { while !line.is_empty() {
// Main match clause for tokens // Main match clause for tokens
match_token!(line, res; match_token!(line, res;
match_space => (), match_space => (),

@ -3,30 +3,35 @@
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use basic_to_mindustry::common::Config; 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}; use basic_to_mindustry::parse::{build_ast, tokenize};
fn read_basic_examples() -> impl Iterator<Item=(String, String)> { fn read_basic_examples() -> impl Iterator<Item = (String, String)> {
Path::new("./examples/").read_dir().unwrap().filter_map(|entry| { Path::new("./examples/")
let Ok(entry) = entry else { .read_dir()
return None; .unwrap()
}; .filter_map(|entry| {
let Ok(entry) = entry else {
if entry return None;
.file_name() };
.into_string()
.map(|name| name.ends_with(".basic")) if entry
.unwrap_or(false) .file_name()
{ .into_string()
let file_name = entry.file_name().into_string().unwrap(); .map(|name| name.ends_with(".basic"))
let file = std::fs::read_to_string(entry.path()).unwrap_or_else(|e| { .unwrap_or(false)
panic!("Error opening {:?}: {:?}", file_name, e); {
}); let file_name = entry.file_name().into_string().unwrap();
Some((file_name, file)) let file = std::fs::read_to_string(entry.path()).unwrap_or_else(|e| {
} else { panic!("Error opening {:?}: {:?}", file_name, e);
None });
} Some((file_name, file))
}) } else {
None
}
})
} }
#[test] #[test]
@ -53,17 +58,27 @@ fn test_examples_opt() {
let config = Config::default(); let config = Config::default();
for (file_name, file) in read_basic_examples() { 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 { let Some(program_name) = PathBuf::from(file_name.clone())
panic!("Basic program in examples/ has an invalid filename: {}", file_name); .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); let opt_0 = format!("tests/examples/{}.0.mlog", program_name);
if !std::fs::try_exists(&opt_0).unwrap() { if !std::fs::try_exists(&opt_0).unwrap() {
continue continue;
} }
let opt_0 = std::fs::read_to_string(opt_0).unwrap_or_else(|e| { 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| { 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); let translated = translate_ast(&parsed, &mut Default::default(), &config);
pretty_assertions::assert_eq!(opt_0.trim(), format!("{}", translated).trim()); pretty_assertions::assert_eq!(opt_0.trim(), format!("{}", translated).trim());
let optimized = optimize_jump_always(optimize_jump_op(optimize_constant(translated))); 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| { let opt_1 = std::fs::read_to_string(format!("tests/examples/{}.1.mlog", program_name))
panic!("Couldn't open tests/examples/{}.1.mlog: {:?}", program_name, e); .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()); pretty_assertions::assert_eq!(opt_1.trim(), format!("{}", optimized).trim());
} }

Loading…
Cancel
Save