Now using Line instead of VASMLine everywhere

This commit is contained in:
2022-06-07 00:16:27 -05:00
parent f9902e39cf
commit 4049ca072e
3 changed files with 59 additions and 63 deletions
+43 -52
View File
@@ -8,12 +8,11 @@ use std::fs;
/// This will solve all the .equ directives and return a symbol table of them. /// This will solve all the .equ directives and return a symbol table of them.
/// .equ directives must be able to be solved in order, that is, in terms of /// .equ directives must be able to be solved in order, that is, in terms of
/// only preceding .equ directives. Anything else is an error. /// only preceding .equ directives. Anything else is an error.
fn solve_equs(lines: &[VASMLine]) -> Result<Scope, AssembleError> { fn solve_equs(lines: &[Line]) -> Result<Scope, AssembleError> {
let mut scope: Scope = Scope::new(); let mut scope: Scope = Scope::new();
let line_nums: BTreeMap<usize, i32> = BTreeMap::new(); let line_nums: BTreeMap<usize, i32> = BTreeMap::new();
for (line_idx, line) in lines.iter().enumerate() { for (line_num, line) in lines.iter().enumerate() {
let line_num = line_idx + 1; if let VASMLine::Equ(Label(name), expr) = &line.line {
if let VASMLine::Equ(Label(name), expr) = line {
let value = eval(expr, line_num, &line_nums, &scope) let value = eval(expr, line_num, &line_nums, &scope)
.map_err(|e| AssembleError::EquResolveError(line_num, name.to_string(), e))?; .map_err(|e| AssembleError::EquResolveError(line_num, name.to_string(), e))?;
@@ -54,12 +53,11 @@ fn arg_length(val: i32) -> usize {
/// what we know right now (.equs), are however long that argument is. If we don't /// what we know right now (.equs), are however long that argument is. If we don't
/// know right now (based on a label, say) then we'll set aside the full 3 bytes (so it's /// know right now (based on a label, say) then we'll set aside the full 3 bytes (so it's
/// 4 bytes long, with the instruction byte). /// 4 bytes long, with the instruction byte).
fn measure_instructions(lines: &[VASMLine], scope: &Scope) -> LineLengths { fn measure_instructions(lines: &[Line], scope: &Scope) -> LineLengths {
let line_nums: BTreeMap<usize, i32> = BTreeMap::new(); let line_nums: BTreeMap<usize, i32> = BTreeMap::new();
let mut lengths = LineLengths::new(); let mut lengths = LineLengths::new();
for (line_idx, line) in lines.iter().enumerate() { for (line_num, line) in lines.iter().enumerate() {
let line_num = line_idx + 1; match &line.line {
match line {
VASMLine::Instruction(_, _, None) => { VASMLine::Instruction(_, _, None) => {
lengths.insert(line_num, 1); lengths.insert(line_num, 1);
} }
@@ -93,29 +91,27 @@ fn measure_instructions(lines: &[VASMLine], scope: &Scope) -> LineLengths {
/// But, we'll skip labels that come before .equs: that would make every .equ set to its address, /// But, we'll skip labels that come before .equs: that would make every .equ set to its address,
/// rather than the argument. /// rather than the argument.
fn place_labels( fn place_labels(
lines: &[VASMLine], lines: &[Line],
scope: Scope, scope: Scope,
lengths: &LineLengths, lengths: &LineLengths,
) -> Result<(LineAddresses, Scope), AssembleError> { ) -> Result<(LineAddresses, Scope), AssembleError> {
let mut scope = scope; let mut scope = scope;
let mut address = 0; let mut address = 0;
let mut addresses = LineAddresses::new(); let mut addresses = LineAddresses::new();
for (line_idx, line) in lines.iter().enumerate() { for (line_num, line) in lines.iter().enumerate() {
let line_num = line_idx + 1; if let VASMLine::Org(_, expr) = &line.line {
if let VASMLine::Org(_, expr) = line {
address = eval(expr, line_num, &addresses, &scope) address = eval(expr, line_num, &addresses, &scope)
.map_err(|err| AssembleError::OrgResolveError(line_num, err))?; .map_err(|err| AssembleError::OrgResolveError(line_num, err))?;
addresses.insert(line_num, address); addresses.insert(line_num, address);
} }
if let Some(Label(label)) = line.label() { if let Some(Label(label)) = &line.line.label() {
if !scope.contains_key(label) { if !scope.contains_key(label) {
scope.insert(label.clone(), address as i32); scope.insert(label.clone(), address as i32);
} }
} }
match line { match &line.line {
VASMLine::Org(_, _) => {} VASMLine::Org(_, _) => {}
_ => { _ => {
addresses.insert(line_num, address); addresses.insert(line_num, address);
@@ -135,24 +131,24 @@ fn poke_word(code: &mut Vec<u8>, at: usize, word: i32) {
/// Find the lower and upper bounds where this program will place memory /// Find the lower and upper bounds where this program will place memory
fn code_bounds( fn code_bounds(
lines: &[VASMLine], lines: &[Line],
line_addresses: &LineAddresses, line_addresses: &LineAddresses,
line_lengths: &LineLengths, line_lengths: &LineLengths,
) -> Result<(usize, usize), AssembleError> { ) -> Result<(usize, usize), AssembleError> {
let mut actual_lines = lines let mut actual_lines = lines
.iter() .iter()
.enumerate() .enumerate()
.filter(|(_, line)| !line.zero_length()); .filter(|(_, line)| !(&line.line).zero_length());
let (first_idx, _) = actual_lines.next().ok_or(AssembleError::NoCode)?; let (first_idx, _) = actual_lines.next().ok_or(AssembleError::NoCode)?;
let start = line_addresses[&(first_idx + 1)] as usize; let start = line_addresses[&(first_idx)] as usize;
let actual_lines = lines let actual_lines = lines
.iter() .iter()
.enumerate() .enumerate()
.filter(|(_, line)| !line.zero_length()); .filter(|(_, line)| !(&line.line).zero_length());
let (last_idx, _) = actual_lines.last().unwrap(); let (last_idx, _) = actual_lines.last().unwrap();
let end = line_addresses[&(last_idx + 1)] as usize; let end = line_addresses[&(last_idx)] as usize;
let end_length = line_lengths[&(last_idx + 1)]; let end_length = line_lengths[&(last_idx)];
Ok((start, end + end_length - 1)) Ok((start, end + end_length - 1))
} }
@@ -200,11 +196,7 @@ fn assemble_line_results(
if let Some(Err(error)) = line_results.iter().find(|line| line.is_err()) { if let Some(Err(error)) = line_results.iter().find(|line| line.is_err()) {
Err(error.clone()) Err(error.clone())
} else { } else {
generate_code( generate_code(line_results.iter_mut().map(|line| line.clone().unwrap()))
line_results
.iter_mut()
.map(|line| line.clone().unwrap().line),
)
} }
} }
@@ -229,8 +221,8 @@ fn lines_from_file(filename: &str) -> Result<Vec<String>, AssembleError> {
/// ///
/// Vulcan is a little-endian architecture: multi-byte arguments / .dbs will store the /// Vulcan is a little-endian architecture: multi-byte arguments / .dbs will store the
/// least-significant byte at the lowest address, then the more significant bytes following. /// least-significant byte at the lowest address, then the more significant bytes following.
fn generate_code<T: IntoIterator<Item = VASMLine>>(lines: T) -> Result<Vec<u8>, AssembleError> { fn generate_code<T: IntoIterator<Item = Line>>(lines: T) -> Result<Vec<u8>, AssembleError> {
let lines: Vec<VASMLine> = lines.into_iter().collect(); let lines: Vec<Line> = lines.into_iter().collect();
let scope = solve_equs(&lines)?; let scope = solve_equs(&lines)?;
let line_lengths = measure_instructions(&lines, &scope); let line_lengths = measure_instructions(&lines, &scope);
let (line_addresses, scope) = place_labels(&lines, scope, &line_lengths)?; let (line_addresses, scope) = place_labels(&lines, scope, &line_lengths)?;
@@ -239,9 +231,8 @@ fn generate_code<T: IntoIterator<Item = VASMLine>>(lines: T) -> Result<Vec<u8>,
let mut code = vec![0u8; end - start + 1]; let mut code = vec![0u8; end - start + 1];
let mut current_addr = start; let mut current_addr = start;
for (line_idx, line) in lines.iter().enumerate() { for (line_num, line) in lines.iter().enumerate() {
let line_num = line_idx + 1; match &line.line {
match line {
VASMLine::Instruction(_, opcode, None) => { VASMLine::Instruction(_, opcode, None) => {
code[current_addr - start] = u8::from(*opcode) << 2; code[current_addr - start] = u8::from(*opcode) << 2;
current_addr += 1; current_addr += 1;
@@ -294,10 +285,10 @@ mod test {
use crate::parse_error::EvalError::*; use crate::parse_error::EvalError::*;
use crate::vasm_parser::parse_vasm_line; use crate::vasm_parser::parse_vasm_line;
fn parse<'a, T: IntoIterator<Item = &'a str>>(lines: T) -> Vec<VASMLine> { fn parse<'a, T: IntoIterator<Item = &'a str>>(lines: T) -> Vec<Line> {
lines lines
.into_iter() .into_iter()
.map(|line| parse_vasm_line(line).unwrap()) .map(|line| parse_vasm_line(line).unwrap().into())
.collect() .collect()
} }
@@ -345,7 +336,7 @@ mod test {
assert_eq!( assert_eq!(
solve_equs(&parse(["blah: .equ 5", "foo: .equ banana"])), solve_equs(&parse(["blah: .equ 5", "foo: .equ banana"])),
Err(EquResolveError( Err(EquResolveError(
2, 1,
"foo".into(), "foo".into(),
MissingLabel("banana".into()) MissingLabel("banana".into())
)) ))
@@ -353,14 +344,14 @@ mod test {
assert_eq!( assert_eq!(
solve_equs(&parse(["blah: .equ foo+3", "foo: .equ 7"])), solve_equs(&parse(["blah: .equ foo+3", "foo: .equ 7"])),
Err(EquResolveError( Err(EquResolveError(
1, 0,
"blah".into(), "blah".into(),
MissingLabel("foo".into()) MissingLabel("foo".into())
)) ))
); );
assert_eq!( assert_eq!(
solve_equs(&parse(["blah: .equ 3", "blah: .equ 7"])), solve_equs(&parse(["blah: .equ 3", "blah: .equ 7"])),
Err(EquDuplicateError(2, "blah".into())) Err(EquDuplicateError(1, "blah".into()))
); );
} }
@@ -371,29 +362,29 @@ mod test {
&parse(["add", "add 1", "add 500", "add 70000", "add -7"]), &parse(["add", "add 1", "add 500", "add 70000", "add -7"]),
&[].into() &[].into()
), ),
[(1, 1), (2, 2), (3, 3), (4, 4), (5, 4)].into() [(0, 1), (1, 2), (2, 3), (3, 4), (4, 4)].into()
); );
assert_eq!( assert_eq!(
measure_instructions(&parse([".db 7", ".db \"hello\\0\""]), &[].into()), measure_instructions(&parse([".db 7", ".db \"hello\\0\""]), &[].into()),
[(1, 3), (2, 6)].into() [(0, 3), (1, 6)].into()
); );
assert_eq!( assert_eq!(
measure_instructions(&parse([".org 256", "blah:", "foo: .equ 7"]), &[].into()), measure_instructions(&parse([".org 256", "blah:", "foo: .equ 7"]), &[].into()),
[(1, 0), (2, 0), (3, 0)].into() [(0, 0), (1, 0), (2, 0)].into()
); );
assert_eq!( assert_eq!(
measure_instructions( measure_instructions(
&parse([".org 0x400", "push 3", "call blah", "hlt", "blah: mul 2"]), &parse([".org 0x400", "push 3", "call blah", "hlt", "blah: mul 2"]),
&[].into() &[].into()
), ),
[(1, 0), (2, 2), (3, 4), (4, 1), (5, 2)].into() [(0, 0), (1, 2), (2, 4), (3, 1), (4, 2)].into()
); );
assert_eq!( assert_eq!(
measure_instructions( measure_instructions(
&parse(["add 2 + foo", "add 3 + blah", "jmpr @foo"]), &parse(["add 2 + foo", "add 3 + blah", "jmpr @foo"]),
&[("blah".to_string(), 300)].into() &[("blah".to_string(), 300)].into()
), ),
[(1, 4), (2, 3), (3, 4)].into() [(0, 4), (1, 3), (2, 4)].into()
); );
} }
@@ -402,25 +393,25 @@ mod test {
assert_eq!( assert_eq!(
place_labels_pass(["start: .org 256", "add", "dup"]), place_labels_pass(["start: .org 256", "add", "dup"]),
Ok(( Ok((
[(1, 256), (2, 256), (3, 257)].into(), [(0, 256), (1, 256), (2, 257)].into(),
[("start".to_string(), 256)].into() [("start".to_string(), 256)].into()
)) ))
); );
assert_eq!( assert_eq!(
place_labels_pass(["push 70000", "dup"]), place_labels_pass(["push 70000", "dup"]),
Ok(([(1, 0), (2, 4)].into(), [].into())) Ok(([(0, 0), (1, 4)].into(), [].into()))
); );
assert_eq!( assert_eq!(
place_labels_pass(["start: .equ 256", "blah: .org start + 4", "add"]), place_labels_pass(["start: .equ 256", "blah: .org start + 4", "add"]),
Ok(( Ok((
[(1, 0), (2, 260), (3, 260)].into(), [(0, 0), (1, 260), (2, 260)].into(),
[("blah".to_string(), 260), ("start".to_string(), 256)].into() [("blah".to_string(), 260), ("start".to_string(), 256)].into()
)) ))
); );
assert_eq!( assert_eq!(
place_labels_pass(["start: .org 256", "blah: .org start + 10", "add"]), place_labels_pass(["start: .org 256", "blah: .org start + 10", "add"]),
Ok(( Ok((
[(1, 256), (2, 266), (3, 266)].into(), [(0, 256), (1, 266), (2, 266)].into(),
[("blah".to_string(), 266), ("start".to_string(), 256)].into() [("blah".to_string(), 266), ("start".to_string(), 256)].into()
)) ))
); );
@@ -435,12 +426,12 @@ mod test {
]), ]),
Ok(( Ok((
[ [
(0, 1024),
(1, 1024), (1, 1024),
(2, 1024), (2, 1026),
(3, 1026), (3, 1030),
(4, 1030), (4, 1031),
(5, 1031), (5, 1033)
(6, 1033)
] ]
.into(), .into(),
[("blah".to_string(), 1031)].into() [("blah".to_string(), 1031)].into()
@@ -452,11 +443,11 @@ mod test {
fn test_unresolvable_orgs() { fn test_unresolvable_orgs() {
assert_eq!( assert_eq!(
place_labels_pass([".org 0xffffff - blah"]), place_labels_pass([".org 0xffffff - blah"]),
Err(OrgResolveError(1, MissingLabel("blah".into()))) Err(OrgResolveError(0, MissingLabel("blah".into())))
); );
assert_eq!( assert_eq!(
place_labels_pass(["blah: .org blah"]), place_labels_pass(["blah: .org blah"]),
Err(OrgResolveError(1, MissingLabel("blah".into()))) Err(OrgResolveError(0, MissingLabel("blah".into())))
); );
} }
+15 -10
View File
@@ -7,8 +7,8 @@ use std::iter::Enumerate;
#[derive(Debug, PartialEq, Clone)] #[derive(Debug, PartialEq, Clone)]
pub struct Line { pub struct Line {
pub line: VASMLine, pub line: VASMLine,
line_num: usize, pub line_num: usize,
file: String, pub file: String,
} }
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
@@ -34,10 +34,7 @@ enum ControlStructure {
LoopDo(String, String), LoopDo(String, String),
} }
pub struct LineSource< pub struct LineSource<T: IntoIterator<Item = String>, F: Fn(String) -> Result<T, AssembleError>> {
T: IntoIterator<Item = String>,
F: Fn(String) -> Result<T, AssembleError>,
> {
generated_lines: VecDeque<Line>, generated_lines: VecDeque<Line>,
current_line: usize, current_line: usize,
filename_stack: Vec<String>, filename_stack: Vec<String>,
@@ -47,6 +44,16 @@ pub struct LineSource<
include: F, include: F,
} }
impl From<VASMLine> for Line {
fn from(other: VASMLine) -> Self {
Line {
line: other,
line_num: 0,
file: "<none>".to_string(),
}
}
}
impl<T: IntoIterator<Item = String>, F: Fn(String) -> Result<T, AssembleError>> Iterator impl<T: IntoIterator<Item = String>, F: Fn(String) -> Result<T, AssembleError>> Iterator
for LineSource<T, F> for LineSource<T, F>
{ {
@@ -81,7 +88,7 @@ impl<T: IntoIterator<Item = String>, F: Fn(String) -> Result<T, AssembleError>>
// to rip out and redo the whole error system. Parse lines initially to tuples of their // to rip out and redo the whole error system. Parse lines initially to tuples of their
// line and location (file and line number) and then pass one of those tuples to create // line and location (file and line number) and then pass one of those tuples to create
// an error. // an error.
Ok(VASMLine::Blank) => { self.next() } Ok(VASMLine::Blank) => self.next(),
Ok(normal_line) => Some(Ok(Line { Ok(normal_line) => Some(Ok(Line {
line: normal_line, line: normal_line,
@@ -98,9 +105,7 @@ impl<T: IntoIterator<Item = String>, F: Fn(String) -> Result<T, AssembleError>>
} }
} }
impl<T: IntoIterator<Item = String>, F: Fn(String) -> Result<T, AssembleError>> impl<T: IntoIterator<Item = String>, F: Fn(String) -> Result<T, AssembleError>> LineSource<T, F> {
LineSource<T, F>
{
// TODO: linesource should take a deref instead so it accepts either str or string. // TODO: linesource should take a deref instead so it accepts either str or string.
pub fn new(file: &str, lines: T, include: F) -> Self { pub fn new(file: &str, lines: T, include: F) -> Self {
LineSource { LineSource {