Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Validate whether a file defined a label multiple times during assembly #25

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion mvn-assembler/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "mvn-assembler"
version = "0.1.1"
version = "0.1.2"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
Expand Down
76 changes: 69 additions & 7 deletions mvn-assembler/src/processor/validator.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
use std::collections::BTreeSet;

use crate::types::{mneumonic, Instruction, Line, Operand};

use crate::processor::address::{Address, AddressedProgram, LabelMap};
use crate::processor::address::{Address, AddressedLine, AddressedProgram, LabelMap};

use super::MvnReportError;

Expand All @@ -10,19 +12,61 @@ pub fn validate<'a, 'b>(
program: &'a AddressedProgram<'b>,
label_map: &'a LabelMap<'b>,
) -> ValidatorResult<'b> {
let validator = ProgramValidator::new(program, label_map);
validator.validate()?;
for line in program.lines.iter() {
let validator = LineValidator::new(&line.line, &line.address, label_map);
validator.validate()?;
}
Ok(())
}

// `label_map` is not required right now but it might be in future tests,
// so keep it in the struct despite being dead code
#[allow(dead_code)]
struct ProgramValidator<'a, 'b> {
program: &'a AddressedProgram<'b>,
label_map: &'a LabelMap<'b>,
}

struct LineValidator<'a, 'b> {
line: &'a Line<'b>,
address: &'a Address,
label_map: &'a LabelMap<'b>,
}

/* Every validator function's name should
* answer the question: "Does the program
* contain {name}?"
*/

impl<'b> ProgramValidator<'_, 'b> {
pub fn validate(self) -> ValidatorResult<'b> {
self.labels_defined_more_than_once()?;
Ok(())
}

fn labels_defined_more_than_once(&self) -> ValidatorResult<'b> {
let mut label_set = BTreeSet::new();
for AddressedLine { address: _, line } in self
.program
.lines
.iter()
.filter(|addressed_line| addressed_line.line.label.is_some())
{
if let Some(label) = &line.label {
if !label_set.insert(label.value.clone()) {
return Err(MvnReportError::new(
label.position,
Some("label was already defined".to_string()),
));
}
}
}
Ok(())
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@guissalustiano você acha que isso fica melhor com iterator? Eu tentei fazer mas acabei me enrolando porque tem efeitos colaterais no meio, e porque mesmo o método mais próximo do que eu queria fazer (try_fold) não suportada mencionar o elemento anterior ao erro.

}
}

impl<'b> LineValidator<'_, 'b> {
pub fn validate(self) -> ValidatorResult<'b> {
self.numeric_operand_on_import_export()?;
Expand All @@ -33,11 +77,6 @@ impl<'b> LineValidator<'_, 'b> {
Ok(())
}

/* Every validator function's name should
* answer the question: "Does the program
* contain {name}?"
*/

fn numeric_operand_on_import_export(&self) -> ValidatorResult<'b> {
match &self.line.operation.instruction.value {
Instruction::Relational(_) => match &self.line.operation.operand.value {
Expand Down Expand Up @@ -133,6 +172,12 @@ impl<'b> LineValidator<'_, 'b> {
}
}

impl<'a, 'b> ProgramValidator<'a, 'b> {
fn new(program: &'a AddressedProgram<'b>, label_map: &'a LabelMap<'b>) -> Self {
Self { program, label_map }
}
}

impl<'a, 'b> LineValidator<'a, 'b> {
fn new(line: &'a Line<'b>, address: &'a Address, label_map: &'a LabelMap<'b>) -> Self {
Self {
Expand Down Expand Up @@ -169,6 +214,7 @@ mod tests {
*/

struct TestProgram {
repeated_label: bool,
import: Operand<'static>,
constant: u32,
position: Operand<'static>,
Expand All @@ -179,6 +225,7 @@ mod tests {
impl Default for TestProgram {
fn default() -> Self {
Self {
repeated_label: false,
import: "IMPORT".into(),
constant: 0x1,
position: 0x100.into(),
Expand All @@ -195,6 +242,11 @@ mod tests {
}

fn render(self) -> (AddressedProgram<'static>, LabelMap<'static>) {
let main_label: Label = if self.repeated_label {
"ONE".into()
} else {
"MAIN".into()
};
let main_position = if let Operand::Numeric(immediate) = self.position {
immediate
} else {
Expand Down Expand Up @@ -256,7 +308,7 @@ mod tests {
..Default::default()
},
Line::new(
Some(Token::new(Position::new(8, 1), "MAIN".into())),
Some(Token::new(Position::new(8, 1), main_label)),
Operation::new(
Token::new(
Position::new(8, 9),
Expand Down Expand Up @@ -318,10 +370,20 @@ mod tests {
assert!(test_program.validate().is_ok());
}

#[test]
fn repeated_labels_should_fail() {
let test_program = TestProgram {
repeated_label: true,
..Default::default()
};
assert!(test_program.validate().is_err());
}

// #[test]
// fn symbolic_operand_on_import_export_should_pass() {

// }

#[test]
fn numeric_operand_on_import_export_should_fail() {
let test_program = TestProgram {
Expand Down
9 changes: 9 additions & 0 deletions mvn-utils/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,3 +64,12 @@ impl<T: fmt::UpperHex> fmt::UpperHex for Token<T> {
write!(f, "{:X}", &self.value)
}
}

impl<T: Clone> Clone for Token<T> {
fn clone(&self) -> Self {
Self {
value: self.value.clone(),
position: self.position,
}
}
}