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
+1 -1
View File
@@ -92,4 +92,4 @@ impl Display for EvalError {
}
}
}
}
}
+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.
/// .equ directives must be able to be solved in order, that is, in terms of
/// 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 line_nums: BTreeMap<usize, i32> = BTreeMap::new();
for (line_idx, line) in lines.iter().enumerate() {
let line_num = line_idx + 1;
if let VASMLine::Equ(Label(name), expr) = line {
for (line_num, line) in lines.iter().enumerate() {
if let VASMLine::Equ(Label(name), expr) = &line.line {
let value = eval(expr, line_num, &line_nums, &scope)
.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
/// 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).
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 mut lengths = LineLengths::new();
for (line_idx, line) in lines.iter().enumerate() {
let line_num = line_idx + 1;
match line {
for (line_num, line) in lines.iter().enumerate() {
match &line.line {
VASMLine::Instruction(_, _, None) => {
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,
/// rather than the argument.
fn place_labels(
lines: &[VASMLine],
lines: &[Line],
scope: Scope,
lengths: &LineLengths,
) -> Result<(LineAddresses, Scope), AssembleError> {
let mut scope = scope;
let mut address = 0;
let mut addresses = LineAddresses::new();
for (line_idx, line) in lines.iter().enumerate() {
let line_num = line_idx + 1;
if let VASMLine::Org(_, expr) = line {
for (line_num, line) in lines.iter().enumerate() {
if let VASMLine::Org(_, expr) = &line.line {
address = eval(expr, line_num, &addresses, &scope)
.map_err(|err| AssembleError::OrgResolveError(line_num, err))?;
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) {
scope.insert(label.clone(), address as i32);
}
}
match line {
match &line.line {
VASMLine::Org(_, _) => {}
_ => {
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
fn code_bounds(
lines: &[VASMLine],
lines: &[Line],
line_addresses: &LineAddresses,
line_lengths: &LineLengths,
) -> Result<(usize, usize), AssembleError> {
let mut actual_lines = lines
.iter()
.enumerate()
.filter(|(_, line)| !line.zero_length());
.filter(|(_, line)| !(&line.line).zero_length());
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
.iter()
.enumerate()
.filter(|(_, line)| !line.zero_length());
.filter(|(_, line)| !(&line.line).zero_length());
let (last_idx, _) = actual_lines.last().unwrap();
let end = line_addresses[&(last_idx + 1)] as usize;
let end_length = line_lengths[&(last_idx + 1)];
let end = line_addresses[&(last_idx)] as usize;
let end_length = line_lengths[&(last_idx)];
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()) {
Err(error.clone())
} else {
generate_code(
line_results
.iter_mut()
.map(|line| line.clone().unwrap().line),
)
generate_code(line_results.iter_mut().map(|line| line.clone().unwrap()))
}
}
@@ -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
/// 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> {
let lines: Vec<VASMLine> = lines.into_iter().collect();
fn generate_code<T: IntoIterator<Item = Line>>(lines: T) -> Result<Vec<u8>, AssembleError> {
let lines: Vec<Line> = lines.into_iter().collect();
let scope = solve_equs(&lines)?;
let line_lengths = measure_instructions(&lines, &scope);
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 current_addr = start;
for (line_idx, line) in lines.iter().enumerate() {
let line_num = line_idx + 1;
match line {
for (line_num, line) in lines.iter().enumerate() {
match &line.line {
VASMLine::Instruction(_, opcode, None) => {
code[current_addr - start] = u8::from(*opcode) << 2;
current_addr += 1;
@@ -294,10 +285,10 @@ mod test {
use crate::parse_error::EvalError::*;
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
.into_iter()
.map(|line| parse_vasm_line(line).unwrap())
.map(|line| parse_vasm_line(line).unwrap().into())
.collect()
}
@@ -345,7 +336,7 @@ mod test {
assert_eq!(
solve_equs(&parse(["blah: .equ 5", "foo: .equ banana"])),
Err(EquResolveError(
2,
1,
"foo".into(),
MissingLabel("banana".into())
))
@@ -353,14 +344,14 @@ mod test {
assert_eq!(
solve_equs(&parse(["blah: .equ foo+3", "foo: .equ 7"])),
Err(EquResolveError(
1,
0,
"blah".into(),
MissingLabel("foo".into())
))
);
assert_eq!(
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"]),
&[].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!(
measure_instructions(&parse([".db 7", ".db \"hello\\0\""]), &[].into()),
[(1, 3), (2, 6)].into()
[(0, 3), (1, 6)].into()
);
assert_eq!(
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!(
measure_instructions(
&parse([".org 0x400", "push 3", "call blah", "hlt", "blah: mul 2"]),
&[].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!(
measure_instructions(
&parse(["add 2 + foo", "add 3 + blah", "jmpr @foo"]),
&[("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!(
place_labels_pass(["start: .org 256", "add", "dup"]),
Ok((
[(1, 256), (2, 256), (3, 257)].into(),
[(0, 256), (1, 256), (2, 257)].into(),
[("start".to_string(), 256)].into()
))
);
assert_eq!(
place_labels_pass(["push 70000", "dup"]),
Ok(([(1, 0), (2, 4)].into(), [].into()))
Ok(([(0, 0), (1, 4)].into(), [].into()))
);
assert_eq!(
place_labels_pass(["start: .equ 256", "blah: .org start + 4", "add"]),
Ok((
[(1, 0), (2, 260), (3, 260)].into(),
[(0, 0), (1, 260), (2, 260)].into(),
[("blah".to_string(), 260), ("start".to_string(), 256)].into()
))
);
assert_eq!(
place_labels_pass(["start: .org 256", "blah: .org start + 10", "add"]),
Ok((
[(1, 256), (2, 266), (3, 266)].into(),
[(0, 256), (1, 266), (2, 266)].into(),
[("blah".to_string(), 266), ("start".to_string(), 256)].into()
))
);
@@ -435,12 +426,12 @@ mod test {
]),
Ok((
[
(0, 1024),
(1, 1024),
(2, 1024),
(3, 1026),
(4, 1030),
(5, 1031),
(6, 1033)
(2, 1026),
(3, 1030),
(4, 1031),
(5, 1033)
]
.into(),
[("blah".to_string(), 1031)].into()
@@ -452,11 +443,11 @@ mod test {
fn test_unresolvable_orgs() {
assert_eq!(
place_labels_pass([".org 0xffffff - blah"]),
Err(OrgResolveError(1, MissingLabel("blah".into())))
Err(OrgResolveError(0, MissingLabel("blah".into())))
);
assert_eq!(
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)]
pub struct Line {
pub line: VASMLine,
line_num: usize,
file: String,
pub line_num: usize,
pub file: String,
}
#[derive(Debug, Clone, PartialEq)]
@@ -34,10 +34,7 @@ enum ControlStructure {
LoopDo(String, String),
}
pub struct LineSource<
T: IntoIterator<Item = String>,
F: Fn(String) -> Result<T, AssembleError>,
> {
pub struct LineSource<T: IntoIterator<Item = String>, F: Fn(String) -> Result<T, AssembleError>> {
generated_lines: VecDeque<Line>,
current_line: usize,
filename_stack: Vec<String>,
@@ -47,6 +44,16 @@ pub struct LineSource<
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
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
// line and location (file and line number) and then pass one of those tuples to create
// an error.
Ok(VASMLine::Blank) => { self.next() }
Ok(VASMLine::Blank) => self.next(),
Ok(normal_line) => Some(Ok(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>>
LineSource<T, F>
{
impl<T: IntoIterator<Item = String>, F: Fn(String) -> Result<T, AssembleError>> LineSource<T, F> {
// TODO: linesource should take a deref instead so it accepts either str or string.
pub fn new(file: &str, lines: T, include: F) -> Self {
LineSource {