Skip to content

Commit d2cee8b

Browse files
Allow unresolved or resolved version of a core
1 parent a973065 commit d2cee8b

File tree

4 files changed

+66
-54
lines changed

4 files changed

+66
-54
lines changed

src/cli.rs

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,20 +14,27 @@ struct CliOptions {
1414
/// Output file; defaults to stdout
1515
#[structopt(long, short, parse(from_os_str))]
1616
output_file: Option<PathBuf>,
17+
18+
/// Whether or not labels, expressions, etc. should be resolved in the output
19+
#[structopt(long, short)]
20+
resolve: bool,
1721
}
1822

1923
pub fn run() -> Result<(), Box<dyn Error>> {
2024
let cli_options = CliOptions::from_args();
2125

2226
let input_program = fs::read_to_string(cli_options.input_file)?;
2327

24-
let parsed_input = parser::parse(input_program.as_str())?;
25-
let parse_output = parsed_input.dump();
28+
let mut parsed_core = parser::parse(input_program.as_str())?;
29+
30+
if cli_options.resolve {
31+
parsed_core.resolve()?;
32+
}
2633

2734
if let Some(output_path) = cli_options.output_file {
28-
fs::write(output_path, parse_output)?;
35+
fs::write(output_path, format!("{}", parsed_core))?;
2936
} else {
30-
println!("{}", parse_output);
37+
println!("{}", parsed_core);
3138
};
3239

3340
Ok(())

src/load_file.rs

Lines changed: 43 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ pub use types::{AddressMode, Modifier, Opcode, Value};
88

99
pub const DEFAULT_CORE_SIZE: usize = 8000;
1010

11-
type InstructionSet = Box<[Option<Instruction>]>;
11+
type Instructions = Box<[Option<Instruction>]>;
1212
type LabelMap = HashMap<String, usize>;
1313

1414
#[derive(Clone, Debug, PartialEq)]
@@ -109,14 +109,15 @@ impl Instruction {
109109
}
110110

111111
pub struct Core {
112-
instructions: InstructionSet,
112+
instructions: Instructions,
113+
resolved: Option<Instructions>,
113114
labels: LabelMap,
114115
}
115116

116117
impl PartialEq for Core {
117-
// TODO: should this impl resolve the instructions? Depends on the use case
118118
fn eq(&self, other: &Self) -> bool {
119-
self.instructions == other.instructions
119+
(self.resolved.is_some() && self.resolved == other.resolved)
120+
|| self.instructions == other.instructions
120121
}
121122
}
122123

@@ -128,35 +129,35 @@ impl Default for Core {
128129

129130
impl fmt::Debug for Core {
130131
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
131-
write!(
132-
formatter,
133-
"Labels: {:?}\nCore:\n{}",
134-
self.labels,
135-
self.dump()
136-
)
132+
if self.resolved.is_some() {
133+
write!(formatter, "{}", self)
134+
} else {
135+
write!(formatter, "<unresolved core>")
136+
}
137137
}
138138
}
139139

140140
impl Core {
141141
pub fn new(core_size: usize) -> Self {
142142
Core {
143143
instructions: vec![None; core_size].into_boxed_slice(),
144-
labels: HashMap::new(),
144+
resolved: None,
145+
labels: LabelMap::new(),
145146
}
146147
}
147148

148149
pub fn get(&self, index: usize) -> Option<Instruction> {
149-
self.instructions.get(index)?.clone()
150-
}
151-
152-
pub fn get_resolved(&self, index: usize) -> Result<Instruction, String> {
153-
self.get(index)
154-
.unwrap_or_default()
155-
.resolve(index, &self.labels)
150+
match &self.resolved {
151+
Some(instructions) => instructions,
152+
None => &self.instructions,
153+
}
154+
.get(index)?
155+
.clone()
156156
}
157157

158158
pub fn set(&mut self, index: usize, value: Instruction) {
159159
self.instructions[index] = Some(value);
160+
// TODO need to re-resolve here? Or make caller do it
160161
}
161162

162163
pub fn add_label(&mut self, index: usize, label: String) -> Result<(), String> {
@@ -181,8 +182,8 @@ impl Core {
181182
self.labels.get(label).copied()
182183
}
183184

184-
pub fn resolve(&self) -> Result<Self, String> {
185-
let instructions = self
185+
pub fn resolve(&mut self) -> Result<&mut Self, String> {
186+
let resolved = self
186187
.instructions
187188
.iter()
188189
.enumerate()
@@ -192,29 +193,29 @@ impl Core {
192193
.map(|instruction| instruction.resolve(i, &self.labels))
193194
.transpose()
194195
})
195-
.collect::<Result<InstructionSet, String>>()?;
196+
.collect::<Result<_, String>>()?;
196197

197-
Ok(Self {
198-
instructions,
199-
..Default::default()
200-
})
198+
self.resolved = Some(resolved);
199+
200+
Ok(self)
201201
}
202+
}
202203

203-
pub fn dump(&self) -> String {
204-
// TODO: convert to fmt::Display - this will require some upfront
205-
// validation that all labels are valid, etc
206-
// It may be desirable to have Debug be a dump() of the load file and
207-
// Display show the original parsed document (or something like that)
208-
match self.resolve() {
209-
Err(msg) => msg,
210-
Ok(core) => core
211-
.instructions
212-
.iter()
213-
.filter_map(Option::as_ref)
214-
.fold(String::new(), |result, instruction| {
215-
result + &instruction.to_string() + "\n"
216-
}),
217-
}
204+
impl fmt::Display for Core {
205+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
206+
write!(
207+
f,
208+
"{}",
209+
match &self.resolved {
210+
None => &self.instructions,
211+
Some(instructions) => instructions,
212+
}
213+
.iter()
214+
.filter_map(Option::as_ref)
215+
.fold(String::new(), |result, instruction| {
216+
result + &instruction.to_string() + "\n"
217+
})
218+
)
218219
}
219220
}
220221

@@ -303,12 +304,10 @@ mod tests {
303304
},
304305
);
305306

306-
let resolved_core = core.resolve().expect("Should resolve all labels in core");
307+
core.resolve().expect("Should resolve all labels in core");
307308

308309
assert_eq!(
309-
resolved_core
310-
.get(3)
311-
.expect("Should have instruction at pos 5"),
310+
core.get(3).expect("Should have instruction at pos 5"),
312311
Instruction {
313312
field_a: Field {
314313
value: Value::Literal(4),

src/parser.rs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -92,9 +92,7 @@ pub fn parse(file_contents: &str) -> Result<Core, Error> {
9292
}
9393
}
9494

95-
// TODO: keep the original core or use the resolved one?
96-
// Probably it should keep a resolved copy in itself
97-
Ok(core.resolve()?)
95+
Ok(core)
9896
}
9997

10098
fn parse_instruction(mut instruction_pairs: Pairs<grammar::Rule>) -> Instruction {
@@ -246,7 +244,12 @@ mod tests {
246244
Instruction::new(Opcode::Jmp, Field::direct(-1), Field::immediate(0)),
247245
);
248246

249-
let parsed = parse(simple_input).expect("Should parse simple file");
247+
expected_core
248+
.resolve()
249+
.expect("Should resolve a core with no labels");
250+
251+
let mut parsed = parse(simple_input).expect("Should parse simple file");
252+
parsed.resolve().expect("Parsed file should resolve");
250253

251254
assert_eq!(parsed, expected_core);
252255
}

tests/dump_test.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,15 @@ fn run_test(input: &str, expected_output: &str) {
44
eprintln!("Parsing warrior:");
55
eprintln!("{}", input);
66

7-
let core = corewa_rs::parse(input).expect("Failed to parse input");
7+
let mut core = corewa_rs::parse(input).expect("Failed to parse input");
8+
core.resolve().expect("Failed to resolve parsed input");
9+
10+
// TODO: dump and check output of pre-resolved core
811

912
eprintln!("Loaded core:");
1013
eprintln!("{:?}", core);
1114

12-
assert_eq!(core.dump(), expected_output);
15+
assert_eq!(format!("{}", core), expected_output);
1316
}
1417

1518
#[test]

0 commit comments

Comments
 (0)