vasm now supports files
This commit is contained in:
@@ -10,3 +10,4 @@ mod vasm_parser;
|
||||
mod vasm_preprocessor;
|
||||
|
||||
pub use vasm_assembler::assemble_snippet;
|
||||
pub use vasm_assembler::assemble_file;
|
||||
|
||||
@@ -33,6 +33,7 @@ pub enum AssembleError {
|
||||
ArgError(usize, EvalError),
|
||||
NoCode,
|
||||
IncludeError(usize, String),
|
||||
FileError(String),
|
||||
MacroError(usize),
|
||||
}
|
||||
|
||||
@@ -63,6 +64,9 @@ impl Display for AssembleError {
|
||||
AssembleError::MacroError(line) => {
|
||||
write!(f, "Malformed macro control structure on line {}", line)
|
||||
}
|
||||
AssembleError::FileError(file) => {
|
||||
write!(f, "File read error in {}", file)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -88,4 +92,4 @@ impl Display for EvalError {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@ use crate::parse_error::AssembleError;
|
||||
use crate::vasm_evaluator::eval;
|
||||
use crate::vasm_preprocessor::{Line, LineSource};
|
||||
use std::collections::BTreeMap;
|
||||
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
|
||||
@@ -162,14 +163,14 @@ fn code_bounds(
|
||||
/// bytes long and index 0 will represent 0x400.
|
||||
/// ```
|
||||
/// assert_eq!(
|
||||
/// vasm::assemble_snippet(vec![".org 0x400", "push 5", "add 7"]),
|
||||
/// vasm::assemble_snippet(".org 0x400 \n push 5 \n add 7".lines().map(String::from)),
|
||||
/// Ok(vec![0x01, 0x05, 0x05, 0x07])
|
||||
/// )
|
||||
/// ```
|
||||
pub fn assemble_snippet<'a, T: IntoIterator<Item = &'a str>>(
|
||||
pub fn assemble_snippet<T: IntoIterator<Item = String>>(
|
||||
lines: T,
|
||||
) -> Result<Vec<u8>, AssembleError> {
|
||||
let mut line_results: Vec<Result<Line, AssembleError>> =
|
||||
let line_results: Vec<Result<Line, AssembleError>> =
|
||||
LineSource::new("_snippet", lines, |_file| {
|
||||
Err(AssembleError::IncludeError(
|
||||
0,
|
||||
@@ -178,6 +179,19 @@ pub fn assemble_snippet<'a, T: IntoIterator<Item = &'a str>>(
|
||||
})
|
||||
.collect();
|
||||
|
||||
assemble_line_results(line_results)
|
||||
}
|
||||
|
||||
pub fn assemble_file(filename: &str) -> Result<Vec<u8>, AssembleError> {
|
||||
let lines = lines_from_file(filename)?;
|
||||
let line_results: Vec<Result<Line, AssembleError>> = LineSource::new(filename, lines, |file| {
|
||||
lines_from_file(file.as_str())
|
||||
}).collect();
|
||||
|
||||
assemble_line_results(line_results)
|
||||
}
|
||||
|
||||
fn assemble_line_results(mut line_results: Vec<Result<Line, AssembleError>>) -> Result<Vec<u8>, AssembleError> {
|
||||
if let Some(Err(error)) = line_results.iter().find(|line| line.is_err()) {
|
||||
Err(error.clone())
|
||||
} else {
|
||||
@@ -189,6 +203,11 @@ pub fn assemble_snippet<'a, T: IntoIterator<Item = &'a str>>(
|
||||
}
|
||||
}
|
||||
|
||||
fn lines_from_file(filename: &str) -> Result<Vec<String>, AssembleError> {
|
||||
let file = fs::read_to_string(filename).map_err(|_e| AssembleError::FileError(filename.into()))?;
|
||||
Ok(file.lines().map(String::from).collect())
|
||||
}
|
||||
|
||||
/// At this point all lines have addresses and lengths, and all arguments are reduced to
|
||||
/// numeric constants. It's time to generate code.
|
||||
///
|
||||
@@ -474,9 +493,9 @@ mod test {
|
||||
|
||||
#[test]
|
||||
fn test_assemble_snippet() {
|
||||
assert_eq!(assemble_snippet(["add"]), Ok(vec![4]));
|
||||
assert_eq!(assemble_snippet(["add"].map(String::from)), Ok(vec![4]));
|
||||
assert_eq!(
|
||||
assemble_snippet(["apple"]),
|
||||
assemble_snippet(["apple"].map(String::from)),
|
||||
Err(ParseError(
|
||||
1,
|
||||
parse_error::ParseError::InvalidInstruction("apple".into())
|
||||
@@ -491,7 +510,7 @@ mod test {
|
||||
hlt
|
||||
blah: mul 2
|
||||
ret"
|
||||
.lines()
|
||||
.lines().map(String::from)
|
||||
),
|
||||
Ok(vec![
|
||||
0x01, 0x03, // nop 3
|
||||
@@ -509,7 +528,7 @@ mod test {
|
||||
nop $+2
|
||||
|
||||
nop 0x111111
|
||||
nop 0x222222".lines()),
|
||||
nop 0x222222".lines().map(String::from)),
|
||||
Ok(vec![
|
||||
0x03, 0x08, 0x04, 0x00,
|
||||
0x03, 0x11, 0x11, 0x11,
|
||||
|
||||
@@ -35,8 +35,7 @@ enum ControlStructure {
|
||||
}
|
||||
|
||||
pub struct LineSource<
|
||||
'a,
|
||||
T: IntoIterator<Item = &'a str>,
|
||||
T: IntoIterator<Item = String>,
|
||||
F: Fn(String) -> Result<T, AssembleError>,
|
||||
> {
|
||||
generated_lines: VecDeque<Line>,
|
||||
@@ -48,8 +47,8 @@ pub struct LineSource<
|
||||
include: F,
|
||||
}
|
||||
|
||||
impl<'a, T: IntoIterator<Item = &'a str>, F: Fn(String) -> Result<T, AssembleError>> Iterator
|
||||
for LineSource<'a, T, F>
|
||||
impl<T: IntoIterator<Item = String>, F: Fn(String) -> Result<T, AssembleError>> Iterator
|
||||
for LineSource<T, F>
|
||||
{
|
||||
type Item = Result<Line, AssembleError>;
|
||||
|
||||
@@ -68,7 +67,7 @@ impl<'a, T: IntoIterator<Item = &'a str>, F: Fn(String) -> Result<T, AssembleErr
|
||||
if let Some((line_idx, line)) = self.iter_stack.last_mut().unwrap().next() {
|
||||
self.current_line = line_idx + 1;
|
||||
// Try and parse it
|
||||
return match parse_vasm_line(line) {
|
||||
return match parse_vasm_line(line.as_str()) {
|
||||
// We failed to parse it
|
||||
Err(err) => Some(Err(AssembleError::ParseError(self.current_line, err))),
|
||||
|
||||
@@ -99,9 +98,10 @@ impl<'a, T: IntoIterator<Item = &'a str>, F: Fn(String) -> Result<T, AssembleErr
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: IntoIterator<Item = &'a str>, F: Fn(String) -> Result<T, AssembleError>>
|
||||
LineSource<'a, 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 {
|
||||
generated_lines: VecDeque::new(),
|
||||
@@ -206,16 +206,20 @@ mod tests {
|
||||
use crate::ast::{Label, Node};
|
||||
use vcore::opcodes::Opcode::*;
|
||||
|
||||
fn lines_for(source: Vec<&str>) -> Vec<VASMLine> {
|
||||
fn lines_for(source: Vec<String>) -> Vec<VASMLine> {
|
||||
let include = |_name: String| panic!();
|
||||
let src = LineSource::new("blah", source, include);
|
||||
src.map(|line| line.unwrap().line).collect()
|
||||
}
|
||||
|
||||
fn stringify(a: Vec<&str>) -> Vec<String> {
|
||||
a.into_iter().map(String::from).collect()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_preprocess() {
|
||||
let include = |_name: String| panic!();
|
||||
let lines = vec!["add"];
|
||||
let lines = stringify(vec!["add"]);
|
||||
let mut src = LineSource::new("blah", lines, include);
|
||||
assert_eq!(
|
||||
src.next(),
|
||||
@@ -230,8 +234,8 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_preprocess_include() {
|
||||
let include = |_name: String| Ok(vec!["sub"]);
|
||||
let lines = vec!["#include \"foo\"", "add"];
|
||||
let include = |_name: String| Ok(stringify(vec!["sub"]));
|
||||
let lines = stringify(vec!["#include \"foo\"", "add"]);
|
||||
let src = LineSource::new("blah", lines, include);
|
||||
assert_eq!(
|
||||
src.collect::<Vec<Result<Line, AssembleError>>>(),
|
||||
@@ -253,7 +257,7 @@ mod tests {
|
||||
#[test]
|
||||
fn test_preprocess_if_end() {
|
||||
assert_eq!(
|
||||
lines_for(vec!["#if", "#end"]),
|
||||
lines_for(stringify(vec!["#if", "#end"])),
|
||||
vec![
|
||||
VASMLine::Instruction(None, Brz, Some(Node::relative_label("__gensym_1"))),
|
||||
VASMLine::LabelDef(Label::from("__gensym_1"))
|
||||
@@ -264,7 +268,7 @@ mod tests {
|
||||
#[test]
|
||||
fn test_preprocess_else() {
|
||||
assert_eq!(
|
||||
lines_for(vec!["#if", "add", "#else", "sub", "#end"]),
|
||||
lines_for(stringify(vec!["#if", "add", "#else", "sub", "#end"])),
|
||||
vec![
|
||||
VASMLine::Instruction(None, Brz, Some(Node::relative_label("__gensym_1"))),
|
||||
VASMLine::Instruction(None, Add, None),
|
||||
@@ -279,7 +283,7 @@ mod tests {
|
||||
#[test]
|
||||
fn test_preprocess_do_end() {
|
||||
assert_eq!(
|
||||
lines_for(vec!["#while", "#do", "#end"]),
|
||||
lines_for(stringify(vec!["#while", "#do", "#end"])),
|
||||
vec![
|
||||
VASMLine::LabelDef(Label("__gensym_1".to_string())),
|
||||
VASMLine::Instruction(
|
||||
|
||||
Reference in New Issue
Block a user