diff --git a/src/main.rs b/src/main.rs index 7922937..9308029 100644 --- a/src/main.rs +++ b/src/main.rs @@ -107,13 +107,11 @@ fn get_writer(path: impl AsRef) -> Result> { fn main() -> Result<()> { use vm::{ state::State, - obj::assemble::AsmSession, + obj::assemble, }; let opt = Options::from_args(); - let mut asm_session = AsmSession::default(); - asm_session.include(&opt.input)?; - let object = asm_session.assemble()?; + let object = assemble::assemble_path(&opt.input)?; if opt.compile_only { let outfile = opt.out.clone().unwrap_or_else(|| { diff --git a/src/vm/obj/assemble.rs b/src/vm/obj/assemble.rs index 941bdae..1a62f47 100644 --- a/src/vm/obj/assemble.rs +++ b/src/vm/obj/assemble.rs @@ -1,184 +1,34 @@ +pub mod error; +pub mod session; +mod includes; +mod names; + +use self::{error::*, session::AsmSession}; use crate::vm::{ - addr::*, + addr::Addr, inst, obj::{ - obj, - syn::{ast::*, lexer, parser}, - }, + obj::{self, Object}, + syn::ast::*, + } }; use byteorder::{WriteBytesExt, LE}; -use snafu::Snafu; -use std::{ - collections::{BTreeSet, HashMap}, - path::{Path, PathBuf}, -}; +use std::{collections::HashMap, path::Path}; -pub type LexError = lrpar::LexError; -pub type ParseError = lrpar::ParseError; -pub type LexParseError = lrpar::LexParseError; - -pub trait Assemble { +pub trait Asm { type Out; - fn assemble(&self, asm: &mut Asm) -> Result; + fn assemble(&self, asm: &mut AsmSession) -> Result; } -/// A shared session for the assembler. -#[derive(Debug, Default)] -pub struct AsmSession { - include_paths: Vec, - included_files: BTreeSet, - include_stack: Vec, - directives: Vec, +pub fn assemble_path(path: impl AsRef) -> Result { + let mut session = AsmSession::default(); + session.include_path(path)?; + session.assemble() } -impl AsmSession { - pub fn assemble(self) -> Result { - let mut asm = Asm::default(); - self.directives.assemble(&mut asm) - } - - pub fn include(&mut self, path: impl AsRef) -> Result<()> { - let path = path.as_ref(); - let path = path.canonicalize() - .map_err(|_| AssembleError::BadPath { path: path.to_path_buf() })?; - if self.include_paths.contains(&path) { - return Ok(()); - } - - let text = std::fs::read_to_string(&path) - .map_err(|_| AssembleError::BadPath { path: path.to_path_buf() })?; - - // Add file to included paths and the path stack - self.included_files.insert(path.clone()); - self.include_stack.push(path); - - self.include_text(&text)?; - - self.include_stack.pop(); - - Ok(()) - } - - pub fn include_stack(&self) -> &Vec { - &self.include_stack - } - - pub fn current_include_path(&self) -> Option<&Path> { - self.include_stack.last().map(PathBuf::as_path) - } - - fn include_text(&mut self, text: &str) -> Result<()> { - let lexerdef = lexer::lexerdef(); - let lexer = lexerdef.lexer(&text); - let (res, errors) = parser::parse(&lexer); - - if !errors.is_empty() { - return Err(AssembleError::Syntax { - source: errors.into(), - }); - } - - let ast = res.unwrap(); - self.include_ast(ast) - } - - fn include_ast(&mut self, ast: Vec) -> Result<()> { - for dir in ast.iter() { - if let Directive::Include(path) = dir { - let path = self.resolve_include_path(path) - .ok_or_else(|| AssembleError::BadPath { path: path.into() })?; - self.include(path)?; - } - } - self.directives.extend(ast); - Ok(()) - } - - fn resolve_include_path(&self, path: impl AsRef) -> Option { - let path = path.as_ref(); - self.current_include_path() - .and_then(|last_path| last_path.parent()) - .map(|last_dir| last_dir.join(path)) - .or_else(|| self.include_paths - .iter() - .filter_map(|include| include.join(path).canonicalize().ok()) - .next()) - } -} - -#[derive(Debug, Default)] -pub struct Asm { - names: Vec>, - pos: Addr, -} - -impl Asm { - /// Gets an address value from a name, if it exists. Searches local -> global. - fn lookup_name(&self, name: &str) -> Result { - self.names - .iter() - .rev() - .filter_map(|names| names.get(name).copied()) - .next() - .ok_or_else(|| AssembleError::UnknownName { - name: name.to_string(), - }) - } - - /// Gets all names defined in a data section, their positions, and puts them into a hashmap. - fn gather_names(&self, section: &DataSection) -> Result> { - let mut names = HashMap::new(); - let mut addr = Addr(section.org.start()); - for line in section.lines.iter() { - match line { - DataLine::ValueDef(v) => addr += v.len(), - DataLine::Inst(inst) => addr += inst.len(), - DataLine::Export(_) => {} - DataLine::Label(label) => { - if let Some(_) = names.insert(label.to_string(), addr) { - return Err(AssembleError::DuplicateLabel { - name: label.to_string(), - }); - } - } - } - } - assert_eq!(addr, Addr(section.org.start() + (section.len() as u64))); - Ok(names) - } -} - -impl Assemble for Vec { +impl Asm for Vec<&'_ Directive> { type Out = obj::Object; - fn assemble(&self, asm: &mut Asm) -> Result { - // collect globals - let mut globals = HashMap::new(); - for section in self.iter() { - let section = if let Directive::Data(d) = section { - d - } else { - continue; - }; - let names = asm.gather_names(section)?; - for export in section.exports() { - let addr = *names - .get(export) - .ok_or_else(|| AssembleError::UnknownExport { - name: export.to_string(), - })?; - if globals.contains_key(export) { - return Err(AssembleError::DuplicateExport { - name: export.to_string(), - })?; - } - globals.insert(export.to_string(), addr); - } - } - // TODO : detect section overlap - // TODO : single meta section - asm.names.clear(); - asm.names.push(globals); - + fn assemble(&self, asm: &mut AsmSession) -> Result { let sections = self .iter() .filter_map(|section| section.assemble(asm).transpose()) @@ -190,10 +40,10 @@ impl Assemble for Vec { } } -impl Assemble for Directive { +impl Asm for Directive { type Out = Option; - fn assemble(&self, asm: &mut Asm) -> Result { + fn assemble(&self, asm: &mut AsmSession) -> Result { match self { Directive::Data(section) => Ok(Some(obj::Section::Data(section.assemble(asm)?))), Directive::Meta(section) => section.assemble(asm).map(Some), @@ -202,24 +52,24 @@ impl Assemble for Directive { } } -impl Assemble for DataSection { +impl Asm for DataSection { type Out = obj::DataSection; - fn assemble(&self, asm: &mut Asm) -> Result { - let names = asm.gather_names(self)?; - asm.names.push(names); + fn assemble(&self, session: &mut AsmSession) -> Result { + let names = names::get_section_names(self)?; + session.name_stack.push(names); let section_len = self.len() as u64; let (start, end) = match self.org { SectionOrg::Start(start) => (start, start + (section_len as u64)), SectionOrg::StartEnd(start, end) => (start, end), }; - asm.pos = Addr(start); + session.pos = Addr(start); if start > end { - return Err(AssembleError::StartGreaterThanEnd { start, end }); + return Err(AsmError::StartGreaterThanEnd { start, end }); } let len = end - start - 1; if len > section_len { - return Err(AssembleError::SectionTooShort { + return Err(AsmError::SectionTooShort { section_end: end, section_size: start + section_len, }); @@ -227,8 +77,8 @@ impl Assemble for DataSection { let mut contents = Vec::with_capacity(section_len as usize); for line in self.lines.iter() { - contents.extend(line.assemble(asm)?); - asm.pos += line.len(); + contents.extend(line.assemble(session)?); + session.pos += line.len(); } assert_eq!( contents.len() as u64, @@ -236,7 +86,7 @@ impl Assemble for DataSection { "in section {}", self.name ); - asm.names.pop(); + session.name_stack.pop(); Ok(obj::DataSection { name: self.name.clone(), start, @@ -246,22 +96,23 @@ impl Assemble for DataSection { } } -impl Assemble for MetaSection { +impl Asm for MetaSection { type Out = obj::Section; - fn assemble(&self, asm: &mut Asm) -> Result { + fn assemble(&self, session: &mut AsmSession) -> Result { let mut entries = HashMap::new(); for line in self.lines.iter() { if entries.contains_key(&line.name) { - return Err(AssembleError::DuplicateMetaName { + return Err(AsmError::DuplicateMetaName { name: line.name.to_string(), }); } let value = match &line.value { Value::Int(i) => *i, - Value::Name(s) => asm.lookup_name(s.as_str())?.0, + Value::Name(s) => session.lookup_name(s.as_str()) + .ok_or_else(|| AsmError::UnknownName { name: s.to_string() })?.addr.0, Value::Reg(_) | Value::Here | Value::Addr(_, _) => { - return Err(AssembleError::IllegalMetaValue { + return Err(AsmError::IllegalMetaValue { name: line.name.to_string(), value: line.value.clone(), }) @@ -275,22 +126,22 @@ impl Assemble for MetaSection { } } -impl Assemble for DataLine { +impl Asm for DataLine { type Out = Vec; - fn assemble(&self, asm: &mut Asm) -> Result { + fn assemble(&self, session: &mut AsmSession) -> Result { match self { - DataLine::ValueDef(v) => v.assemble(asm), - DataLine::Inst(i) => i.assemble(asm), + DataLine::ValueDef(v) => v.assemble(session), + DataLine::Inst(i) => i.assemble(session), DataLine::Export(_) | DataLine::Label(_) => Ok(Vec::new()), } } } -impl Assemble for ValueDef { +impl Asm for ValueDef { type Out = Vec; - fn assemble(&self, _: &mut Asm) -> Result { + fn assemble(&self, _: &mut AsmSession) -> Result { match self { ValueDef::Int(x, s) => Ok(x.to_le_bytes().iter().copied().take(s.len()).collect()), ValueDef::String(s) => { @@ -309,10 +160,10 @@ impl Assemble for ValueDef { } } -impl Assemble for Inst { +impl Asm for Inst { type Out = Vec; - fn assemble(&self, asm: &mut Asm) -> Result { + fn assemble(&self, session: &mut AsmSession) -> Result { let len = self.len(); macro_rules! map_inst { @@ -322,7 +173,7 @@ impl Assemble for Inst { let dest = $dest; let dest_encoding = dest.dest_encoding() - .ok_or_else(|| AssembleError::IllegalDestValue { + .ok_or_else(|| AsmError::IllegalDestValue { value: dest.clone(), })?; let source = $source; @@ -330,8 +181,8 @@ impl Assemble for Inst { bytes .write_u8((dest_encoding << 4) | source_encoding) .unwrap(); - bytes.extend(dest.assemble(asm)?); - bytes.extend(source.assemble(asm)?); + bytes.extend(dest.assemble(session)?); + bytes.extend(source.assemble(session)?); assert_eq!( self.len(), bytes.len(), @@ -349,7 +200,7 @@ impl Assemble for Inst { let source = $source; let source_encoding = source.source_encoding() << 4; bytes.write_u8(source_encoding).unwrap(); - bytes.extend(source.assemble(asm)?); + bytes.extend(source.assemble(session)?); assert_eq!( self.len(), bytes.len(), @@ -404,7 +255,7 @@ impl Assemble for Inst { bytes.write_u16::(inst::POP).unwrap(); let dest_encoding = dest.source_encoding() << 4; bytes.write_u8(dest_encoding).unwrap(); - bytes.extend(dest.assemble(asm)?); + bytes.extend(dest.assemble(session)?); assert_eq!( self.len(), bytes.len(), @@ -422,149 +273,45 @@ impl Assemble for Inst { } } -impl Assemble for Value { +impl Asm for Value { type Out = Vec; - fn assemble(&self, asm: &mut Asm) -> Result { + fn assemble(&self, session: &mut AsmSession) -> Result { match self { Value::Int(i) => Ok(i.to_le_bytes().to_vec()), Value::Reg(r) => Ok(vec![*r]), Value::Name(name) => { - let value = asm.lookup_name(name.as_str())?; - Ok(value.0.to_le_bytes().to_vec()) + let value = session.lookup_name(name.as_str()) + .ok_or_else(|| AsmError::UnknownName { name: name.to_string() })?; + Ok(value.addr.0.to_le_bytes().to_vec()) } - Value::Here => Ok(asm.pos.0.to_le_bytes().to_vec()), + Value::Here => Ok(session.pos.0.to_le_bytes().to_vec()), Value::Addr(v, _) => { if let Value::Addr(_, _) = &**v { // double deref is not allowed todo!() } else { - v.assemble(asm) + v.assemble(session) } } } } } -#[derive(Debug, Snafu)] -pub enum AssembleError { - #[snafu(display("unknown name: {}", name))] - UnknownName { - name: String, - }, - - #[snafu(display("unknown export name: {}", name))] - UnknownExport { - name: String, - }, - - #[snafu(display("duplicate label definition: {}", name))] - DuplicateLabel { - name: String, - }, - - #[snafu(display("duplicate meta entry name: {}", name))] - DuplicateMetaName { - name: String, - }, - - #[snafu(display("illegal meta value for entry name {}: {:?}", name, value))] - IllegalMetaValue { - name: String, - value: Value, - }, - - #[snafu(display("duplicate exported name: {}", name))] - DuplicateExport { - name: String, - }, - - #[snafu(display("section start ({:#x}) is greater than end ({:#x})", start, end))] - StartGreaterThanEnd { - start: u64, - end: u64, - }, - - #[snafu(display( - "section end ({:#x}) too short for section content size ({:#x})", - section_end, - section_size - ))] - SectionTooShort { - section_end: u64, - section_size: u64, - }, - - #[snafu(display("illegal instruction destination value: {:?}", value))] - IllegalDestValue { - value: Value, - }, - - #[snafu(display("deref of a deref value is not allowed"))] - DoubleDeref { - value: Value, - }, - - #[snafu(display("could not read path: {}", path.display()))] - BadPath { - path: PathBuf, - }, - - Syntax { - source: SyntaxError, - }, -} - -#[derive(Debug, Snafu)] -pub enum SyntaxError { - Lex { source: LexError }, - Parse { source: ParseError }, - Multi { errors: Vec }, -} - -impl From for SyntaxError { - fn from(source: LexError) -> Self { - SyntaxError::Lex { source } - } -} - -impl From for SyntaxError { - fn from(source: ParseError) -> Self { - SyntaxError::Parse { source } - } -} - -impl From> for SyntaxError { - fn from(errors: Vec) -> Self { - SyntaxError::Multi { errors } - } -} - -impl From for SyntaxError { - fn from(source: LexParseError) -> Self { - match source { - LexParseError::LexError(e) => SyntaxError::Lex { source: e }, - LexParseError::ParseError(e) => SyntaxError::Parse { source: e }, - } - } -} - -pub type Result = std::result::Result; - #[cfg(test)] mod test { use super::*; #[test] fn test_inst_len() { - let mut asm = Asm::default(); - asm.names - .push(vec![("test".to_string(), Addr(0u64))].into_iter().collect()); + let mut session = AsmSession::default(); + //asm.names + //.push(vec![("test".to_string(), Addr(0u64))].into_iter().collect()); macro_rules! assert_len { ($inst:expr) => {{ let inst = $inst; - let asm_size = $inst.assemble(&mut asm).unwrap().len(); + let asm_size = $inst.assemble(&mut session).unwrap().len(); assert_eq!(inst.len(), asm_size, "Instruction {:?}.len() indicates it should be {} bytes long but was assembled as {} bytes", inst, inst.len(), asm_size); }} } @@ -575,18 +322,18 @@ mod test { Value::Reg(0), Value::Addr(Box::new(Value::Reg(0)), IntSize::U8), Value::Addr(Box::new(Value::Here), IntSize::U16), - Value::Addr(Box::new(Value::Name("test".to_string())), IntSize::U32), + //Value::Addr(Box::new(Value::Name("test".to_string())), IntSize::U32), Value::Addr(Box::new(Value::Int(0)), IntSize::U64), ]; let dummy_sources = &[ Value::Int(0), Value::Reg(0), - Value::Name("test".to_string()), + //Value::Name("test".to_string()), Value::Here, Value::Addr(Box::new(Value::Reg(0)), IntSize::U8), Value::Addr(Box::new(Value::Here), IntSize::U16), - Value::Addr(Box::new(Value::Name("test".to_string())), IntSize::U32), + //Value::Addr(Box::new(Value::Name("test".to_string())), IntSize::U32), Value::Addr(Box::new(Value::Int(0)), IntSize::U32), ]; diff --git a/src/vm/obj/assemble/error.rs b/src/vm/obj/assemble/error.rs new file mode 100644 index 0000000..3a5ad91 --- /dev/null +++ b/src/vm/obj/assemble/error.rs @@ -0,0 +1,114 @@ +use crate::vm::obj::syn::ast::*; +use snafu::Snafu; +use std::path::PathBuf; + +pub type LexError = lrpar::LexError; +pub type ParseError = lrpar::ParseError; +pub type LexParseError = lrpar::LexParseError; + +#[derive(Debug, Snafu)] +pub enum AsmError { + #[snafu(display("unknown name: {}", name))] + UnknownName { + name: String, + }, + + #[snafu(display("unknown export name: {}", name))] + UnknownExport { + name: String, + }, + + #[snafu(display("duplicate label definition: {}", name))] + DuplicateLabel { + name: String, + }, + + #[snafu(display("duplicate meta entry name: {}", name))] + DuplicateMetaName { + name: String, + }, + + #[snafu(display("illegal meta value for entry name {}: {:?}", name, value))] + IllegalMetaValue { + name: String, + value: Value, + }, + + #[snafu(display("duplicate exported name: {}", name))] + DuplicateExport { + name: String, + }, + + #[snafu(display("section start ({:#x}) is greater than end ({:#x})", start, end))] + StartGreaterThanEnd { + start: u64, + end: u64, + }, + + #[snafu(display( + "section end ({:#x}) too short for section content size ({:#x})", + section_end, + section_size + ))] + SectionTooShort { + section_end: u64, + section_size: u64, + }, + + #[snafu(display("illegal instruction destination value: {:?}", value))] + IllegalDestValue { + value: Value, + }, + + #[snafu(display("deref of a deref value is not allowed"))] + DoubleDeref { + value: Value, + }, + + // TODO(asm) : Path error wrapper for assembling things + #[snafu(display("could not read path: {}", path.display()))] + BadPath { + path: PathBuf, + }, + + Syntax { + source: SyntaxError, + }, +} + +#[derive(Debug, Snafu)] +pub enum SyntaxError { + Lex { source: LexError }, + Parse { source: ParseError }, + Multi { errors: Vec }, +} + +impl From for SyntaxError { + fn from(source: LexError) -> Self { + SyntaxError::Lex { source } + } +} + +impl From for SyntaxError { + fn from(source: ParseError) -> Self { + SyntaxError::Parse { source } + } +} + +impl From> for SyntaxError { + fn from(errors: Vec) -> Self { + SyntaxError::Multi { errors } + } +} + +impl From for SyntaxError { + fn from(source: LexParseError) -> Self { + match source { + LexParseError::LexError(e) => SyntaxError::Lex { source: e }, + LexParseError::ParseError(e) => SyntaxError::Parse { source: e }, + } + } +} + +pub type Result = std::result::Result; + diff --git a/src/vm/obj/assemble/includes.rs b/src/vm/obj/assemble/includes.rs new file mode 100644 index 0000000..c67a807 --- /dev/null +++ b/src/vm/obj/assemble/includes.rs @@ -0,0 +1,68 @@ +use crate::vm::obj::{ + assemble::{error::*, session::AsmSession, Asm}, + syn::{ast::Directive, lexer, parser}, +}; +use std::{path::PathBuf, rc::Rc}; + +#[derive(Debug, Clone)] +pub struct GetIncludes { + path: PathBuf, +} + +impl GetIncludes { + pub fn assemble_from_path(path: PathBuf, session: &mut AsmSession) -> Result<()> { + let path = path + .canonicalize() + .map_err(|_| AsmError::BadPath { path })?; + GetIncludes { path }.assemble(session) + } +} + +impl Asm for GetIncludes { + type Out = (); + + fn assemble(&self, session: &mut AsmSession) -> Result { + assert!(self.path.is_absolute()); + if session.includes.contains_key(&self.path) { + return Ok(()); + } + + let text = std::fs::read_to_string(&self.path).map_err(|_| AsmError::BadPath { + path: self.path.clone(), + })?; + + let lexerdef = lexer::lexerdef(); + let lexer = lexerdef.lexer(&text); + let (res, errors) = parser::parse(&lexer); + + if !errors.is_empty() { + return Err(AsmError::Syntax { + source: errors.into(), + }); + } + + // insert a dummy AST in the includes and replace it when we're done with the actual AST + Rc::get_mut(&mut session.includes) + .unwrap() + .insert(self.path.clone(), Default::default()); + session.include_stack.push(self.path.clone()); + let ast = res.unwrap(); + for directive in ast.iter() { + if let Directive::Include(include_path) = directive { + GetIncludes::assemble_from_path(PathBuf::from(include_path), session)?; + } + } + session.include_stack.pop(); + + let dummy = Rc::get_mut(&mut session.includes) + .unwrap() + .insert(self.path.clone(), ast) + .expect("ast was removed after its inclusion (why did you do that?)"); + assert!( + dummy.is_empty(), + "ast was modified after its inclusion (why did you do that?)" + ); + + Ok(()) + } +} diff --git a/src/vm/obj/assemble/names.rs b/src/vm/obj/assemble/names.rs new file mode 100644 index 0000000..8f75052 --- /dev/null +++ b/src/vm/obj/assemble/names.rs @@ -0,0 +1,104 @@ +use crate::vm::{ + addr::Addr, + obj::{ + assemble::{ + session::AsmSession, + error::*, + }, + syn::ast::{DataSection, DataLine, Directive}, + }, +}; +use std::{collections::{HashMap, HashSet}, rc::Rc}; + +// TODO(asm) make custom Names type that has "merge" that will catch errors with duplicate names +pub type Names = HashMap; + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Name { + pub name: String, + pub addr: Addr, + pub export: bool, +} + +pub fn get_section_names(section: &DataSection) -> Result { + let mut pos = Addr(section.org.start()); + let mut names = HashMap::new(); + let mut exports = HashSet::new(); + + // This isn't immediately straightforward in code, so this is what's happening. + // + // A name may be specified exactly once. It is added to the "names" mapping. If the name is + // specified again, it is a duplicate error and handled as such. A name may be flagged as + // being exported. + // + // Exports are gathered at the beginning into a set. Afterwards, as name definitions + // are gathered, exports are removed from their set if they exist. Since duplicate names + // cause an error, export names will only be removed once. + // + // At the end of name gathering, if any exports have not been removed, then we know those + // are exports whose names are undefined and we can return an UnknownExport error. + + // get exported names + for line in section.lines.iter() { + if let DataLine::Export(name) = line { + exports.insert(name); + } + } + + // get names + for line in section.lines.iter() { + if let DataLine::Label(name) = line { + if names.contains_key(name) { + return Err(AsmError::DuplicateLabel { name: name.clone() }); + } + let export = exports.remove(name); + + names.insert(name.clone(), Name { + name: name.clone(), + addr: pos, + export, + }); + } + pos += line.len(); + } + + // all exports map 1:1 with names + if exports.is_empty() { + Ok(names) + } else { + Err(AsmError::UnknownExport { name: exports.iter().next().unwrap().to_string() }) + } +} + +pub fn get_exports(session: &mut AsmSession) -> Result { + let includes = Rc::clone(&session.includes); + let mut all_exports = Vec::new(); + + // get *all* exports first + for (_, ast) in includes.iter() { + for directive in ast.iter() { + if let Directive::Data(section) = directive { + let names: HashMap<_, _> = get_section_names(section)? + .into_iter() + .filter(|(_, name)| name.export) + .collect(); + all_exports.push(names); + } + } + } + + let mut exports = HashSet::new(); + + // make a set of all export names, erroring on duplicate export + for name_map in all_exports.iter() { + let names_set: HashSet<_> = name_map.keys().collect(); + if let Some(dupe) = exports.intersection(&names_set).next() { + return Err(AsmError::DuplicateExport { name: dupe.to_string() }); + } + exports.extend(names_set); + } + + // NOTE: this can probably be done with a fancy combinator chain + Ok(all_exports.into_iter() + .fold(Names::new(), |mut acc, val| { acc.extend(val); acc })) +} diff --git a/src/vm/obj/assemble/session.rs b/src/vm/obj/assemble/session.rs new file mode 100644 index 0000000..80e8d16 --- /dev/null +++ b/src/vm/obj/assemble/session.rs @@ -0,0 +1,72 @@ +use crate::vm::{ + addr::Addr, + obj::{ + assemble::{ + Asm, + includes::GetIncludes, + names::{self, Name, Names}, + error::*, + }, + obj::Object, + syn::ast::Ast, + }, +}; +use std::{ + collections::BTreeMap, + path::{Path, PathBuf}, + rc::Rc, +}; + +/// A shared session for the assembler. +#[derive(Debug, Default)] +pub struct AsmSession { + pub (in super) includes: Rc>, + pub (in super) include_search_paths: Vec, + pub (in super) include_stack: Vec, + pub (in super) name_stack: Vec, + pub (in super) pos: Addr, +} + +impl AsmSession { + pub fn assemble(&mut self) -> Result { + let includes = Rc::clone(&self.includes); + let sections: Vec<_> = includes.iter() + .flat_map(|(_, ast)| ast) + .collect(); + sections.assemble(self) + } + + pub fn include_path(&mut self, path: impl AsRef) -> Result<()> { + self.include_stack.clear(); + let path = path.as_ref().to_path_buf(); + + GetIncludes::assemble_from_path(path, self)?; + let exports = names::get_exports(self)?; + // TODO(asm) - merge exports + self.name_stack.push(exports); + Ok(()) + } + + pub (in super) fn current_include_path(&self) -> Option<&Path> { + self.include_stack.last().map(PathBuf::as_path) + } + + pub (in super) fn resolve_include_path(&self, path: impl AsRef) -> Option { + let path = path.as_ref(); + self.current_include_path() + .and_then(|last_path| last_path.parent()) + .map(|last_dir| last_dir.join(path)) + .or_else(|| self.include_search_paths + .iter() + .filter_map(|include| include.join(path).canonicalize().ok()) + .next()) + } + + pub fn lookup_name(&self, name: &str) -> Option<&Name> { + self.name_stack + .iter() + .rev() + .filter_map(|names| names.get(name)) + .next() + } +} diff --git a/src/vm/obj/syn/ast.rs b/src/vm/obj/syn/ast.rs index 3617c95..2280b64 100644 --- a/src/vm/obj/syn/ast.rs +++ b/src/vm/obj/syn/ast.rs @@ -3,6 +3,8 @@ use crate::vm::{ reg::Reg, }; +pub type Ast = Vec; + #[derive(Debug, Clone)] pub enum Directive { Meta(MetaSection),