Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: improve ergonomics of/access to module import info apis #1110

Merged
merged 2 commits into from
Oct 18, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions assembly/src/assembler/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,8 @@ impl AssemblyContext {
/// by the program, and thus, will be able to determine names of imported procedures for error
/// reporting purposes.
pub fn for_program(program: Option<&ProgramAst>) -> Self {
let program_imports = program.map(|p| p.get_imported_procedures_map()).unwrap_or_default();
let program_imports =
program.map(|p| p.import_info().get_imported_procedures()).unwrap_or_default();
Self {
module_stack: vec![ModuleContext::for_program(program_imports)],
is_kernel: false,
Expand Down Expand Up @@ -118,7 +119,7 @@ impl AssemblyContext {
}

// get the imported procedures map
let proc_map = module_ast.get_imported_procedures_map();
let proc_map = module_ast.import_info().get_imported_procedures();

// push a new module context onto the module stack and return
self.module_stack.push(ModuleContext::for_module(module_path, proc_map));
Expand Down
43 changes: 41 additions & 2 deletions assembly/src/ast/imports.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,18 +77,51 @@ impl ModuleImports {
// PUBLIC ACCESSORS
// --------------------------------------------------------------------------------------------

/// Returns true if there are no imports in the containing module
pub fn is_empty(&self) -> bool {
self.imports.is_empty()
}

/// Returns the number of imports contained in this table
pub fn len(&self) -> usize {
self.imports.len()
}

/// Look up the path of the imported module with the given name.
pub fn get_module_path(&self, module_name: &str) -> Option<&LibraryPath> {
self.imports.get(&module_name.to_string())
}

/// Look up the actual procedure name and module path associated with the given [ProcedureId],
/// if that procedure was imported and invoked in the current module.
pub fn get_procedure_info(&self, id: &ProcedureId) -> Option<(&ProcedureName, &LibraryPath)> {
self.invoked_procs.get(id).map(|(name, path)| (name, path))
}

/// Look up the procedure name associated with the given [ProcedureId],
/// if that procedure was imported and invoked in the current module.
pub fn get_procedure_name(&self, id: &ProcedureId) -> Option<&ProcedureName> {
self.invoked_procs.get(id).map(|(name, _)| name)
}

/// Look up the [LibraryPath] associated with the given [ProcedureId],
/// if that procedure was imported and invoked in the current module.
pub fn get_procedure_path(&self, id: &ProcedureId) -> Option<&LibraryPath> {
self.invoked_procs.get(id).map(|(_, path)| path)
}

/// Return the paths of all imported module
pub fn import_paths(&self) -> Vec<&LibraryPath> {
self.imports.values().collect()
}

/// Returns a reference to the invoked procedure map which maps procedure IDs to their names.
pub fn invoked_procs(&self) -> &InvokedProcsMap {
/// Returns a map containing IDs and names of imported procedures.
pub fn get_imported_procedures(&self) -> BTreeMap<ProcedureId, ProcedureName> {
self.invoked_procs.iter().map(|(id, (name, _))| (*id, name.clone())).collect()
}

/// Returns a reference to the internal invoked procedure map which maps procedure IDs to their names and paths.
pub(super) fn invoked_procs(&self) -> &InvokedProcsMap {
&self.invoked_procs
}

Expand Down Expand Up @@ -123,6 +156,12 @@ impl ModuleImports {
}
Ok(proc_id)
}

/// Clears all stored information about imported modules and invoked procedures
pub fn clear(&mut self) {
self.imports.clear();
self.invoked_procs.clear();
}
}

impl Serializable for ModuleImports {
Expand Down
128 changes: 44 additions & 84 deletions assembly/src/ast/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ type InvokedProcsMap = BTreeMap<ProcedureId, (ProcedureName, LibraryPath)>;
pub struct ProgramAst {
body: CodeBody,
local_procs: Vec<ProcedureAst>,
import_info: Option<ModuleImports>,
import_info: ModuleImports,
start: SourceLocation,
}

Expand All @@ -105,7 +105,7 @@ impl ProgramAst {
Ok(Self {
body,
local_procs,
import_info: None,
import_info: Default::default(),
start,
})
}
Expand All @@ -115,8 +115,8 @@ impl ProgramAst {
/// # Panics
/// Panics if import information has already been added.
pub fn with_import_info(mut self, import_info: ModuleImports) -> Self {
assert!(self.import_info.is_none(), "module imports have already been added");
self.import_info = Some(import_info);
assert!(self.import_info.is_empty(), "module imports have already been added");
self.import_info = import_info;
self
}

Expand Down Expand Up @@ -154,13 +154,9 @@ impl ProgramAst {
&self.body
}

/// Returns a map containing IDs and names of imported procedures.
pub fn get_imported_procedures_map(&self) -> BTreeMap<ProcedureId, ProcedureName> {
if let Some(info) = &self.import_info {
info.invoked_procs().iter().map(|(&id, (name, _))| (id, name.clone())).collect()
} else {
BTreeMap::new()
}
/// Returns a reference to the import info for this program
pub fn import_info(&self) -> &ModuleImports {
&self.import_info
}

// PARSER
Expand Down Expand Up @@ -249,10 +245,7 @@ impl ProgramAst {

// serialize imports if required
if options.serialize_imports {
match &self.import_info {
Some(imports) => imports.write_into(&mut target),
None => panic!("imports not initialized"),
}
self.import_info.write_into(&mut target);
}

// serialize procedures
Expand All @@ -279,10 +272,11 @@ impl ProgramAst {
let options = AstSerdeOptions::read_from(&mut source)?;

// deserialize imports if required
let mut import_info = None;
if options.serialize_imports {
import_info = Some(ModuleImports::read_from(&mut source)?);
}
let import_info = if options.serialize_imports {
ModuleImports::read_from(&mut source)?
} else {
ModuleImports::default()
};

// deserialize local procs
let num_local_procs = source.read_u16()?;
Expand All @@ -294,10 +288,7 @@ impl ProgramAst {

match Self::new(nodes, local_procs) {
Err(err) => Err(DeserializationError::UnknownError(err.message().clone())),
Ok(res) => match import_info {
Some(info) => Ok(res.with_import_info(info)),
None => Ok(res),
},
Ok(res) => Ok(res.with_import_info(import_info)),
}
}

Expand Down Expand Up @@ -336,7 +327,7 @@ impl ProgramAst {

/// Clear import info from the program
pub fn clear_imports(&mut self) {
self.import_info = None;
self.import_info.clear();
}

// WRITE TO FILE
Expand Down Expand Up @@ -369,23 +360,16 @@ impl fmt::Display for ProgramAst {
/// # Panics
/// Panics if import info is not associated with this program.
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
assert!(self.import_info.is_some(), "Program imports not instantiated");

// Imports
if let Some(ref info) = self.import_info {
let paths = info.import_paths();
for path in paths.iter() {
writeln!(f, "use.{path}")?;
}
if !paths.is_empty() {
writeln!(f)?;
}
let paths = self.import_info.import_paths();
for path in paths.iter() {
writeln!(f, "use.{path}")?;
}
if !paths.is_empty() {
writeln!(f)?;
}

let tmp_procs = InvokedProcsMap::new();
let invoked_procs =
self.import_info.as_ref().map(|info| info.invoked_procs()).unwrap_or(&tmp_procs);

let invoked_procs = self.import_info.invoked_procs();
let context = AstFormatterContext::new(&self.local_procs, invoked_procs);

// Local procedures
Expand All @@ -411,7 +395,7 @@ impl fmt::Display for ProgramAst {
pub struct ModuleAst {
local_procs: Vec<ProcedureAst>,
reexported_procs: Vec<ProcReExport>,
import_info: Option<ModuleImports>,
import_info: ModuleImports,
docs: Option<String>,
}

Expand Down Expand Up @@ -443,7 +427,7 @@ impl ModuleAst {
Ok(Self {
local_procs,
reexported_procs,
import_info: None,
import_info: Default::default(),
docs,
})
}
Expand All @@ -453,8 +437,8 @@ impl ModuleAst {
/// # Panics
/// Panics if import information has already been added.
pub fn with_import_info(mut self, import_info: ModuleImports) -> Self {
assert!(self.import_info.is_none(), "module imports have already been added");
self.import_info = Some(import_info);
assert!(self.import_info.is_empty(), "module imports have already been added");
self.import_info = import_info;
self
}

Expand Down Expand Up @@ -514,21 +498,9 @@ impl ModuleAst {
self.docs.as_ref()
}

/// Returns a map of imported modules in this module.
pub fn import_paths(&self) -> Vec<&LibraryPath> {
match &self.import_info {
Some(info) => info.import_paths(),
None => Vec::<&LibraryPath>::new(),
}
}

/// Returns a map containing IDs and names of imported procedures.
pub fn get_imported_procedures_map(&self) -> BTreeMap<ProcedureId, ProcedureName> {
if let Some(info) = &self.import_info {
info.invoked_procs().iter().map(|(&id, (name, _))| (id, name.clone())).collect()
} else {
BTreeMap::new()
}
/// Returns a reference to the import information for this module
pub fn import_info(&self) -> &ModuleImports {
&self.import_info
}

// STATE MUTATORS
Expand Down Expand Up @@ -564,10 +536,7 @@ impl ModuleAst {

// serialize imports if required
if options.serialize_imports {
match &self.import_info {
Some(imports) => imports.write_into(target),
None => panic!("imports not initialized"),
}
self.import_info.write_into(target);
}

// serialize procedures
Expand Down Expand Up @@ -601,10 +570,11 @@ impl ModuleAst {
};

// deserialize imports if required
let mut import_info = None;
if options.serialize_imports {
import_info = Some(ModuleImports::read_from(source)?);
}
let import_info = if options.serialize_imports {
ModuleImports::read_from(source)?
} else {
ModuleImports::default()
};

// deserialize re-exports
let num_reexported_procs = source.read_u16()? as usize;
Expand All @@ -616,10 +586,7 @@ impl ModuleAst {

match Self::new(local_procs, reexported_procs, docs) {
Err(err) => Err(DeserializationError::UnknownError(err.message().clone())),
Ok(res) => match import_info {
Some(info) => Ok(res.with_import_info(info)),
None => Ok(res),
},
Ok(res) => Ok(res.with_import_info(import_info)),
}
}

Expand Down Expand Up @@ -673,7 +640,7 @@ impl ModuleAst {

/// Clear import info from the module
pub fn clear_imports(&mut self) {
self.import_info = None;
self.import_info.clear();
}
}

Expand All @@ -686,23 +653,19 @@ impl fmt::Display for ModuleAst {
/// # Panics
/// Panics if import info is not associated with this module.
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
assert!(self.import_info.is_some(), "Program imports not instantiated");

// Docs
if let Some(ref doc) = self.docs {
writeln!(f, "#! {doc}")?;
writeln!(f)?;
}

// Imports
if let Some(ref info) = self.import_info {
let paths = info.import_paths();
for path in paths.iter() {
writeln!(f, "use.{path}")?;
}
if !paths.is_empty() {
writeln!(f)?;
}
let paths = self.import_info.import_paths();
for path in paths.iter() {
writeln!(f, "use.{path}")?;
}
if !paths.is_empty() {
writeln!(f)?;
}

// Re-exports
Expand All @@ -712,10 +675,7 @@ impl fmt::Display for ModuleAst {
}

// Local procedures
let tmp_procs = InvokedProcsMap::new();
let invoked_procs =
self.import_info.as_ref().map(|info| info.invoked_procs()).unwrap_or(&tmp_procs);

let invoked_procs = self.import_info.invoked_procs();
let context = AstFormatterContext::new(&self.local_procs, invoked_procs);

for proc in self.local_procs.iter() {
Expand Down
2 changes: 1 addition & 1 deletion assembly/src/library/masl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -272,7 +272,7 @@ mod use_std {
let ast = ModuleAst::parse(&contents)?;

// add dependencies of this module to the dependencies of this library
for path in ast.import_paths() {
for path in ast.import_info().import_paths() {
let ns = LibraryNamespace::new(path.first())?;
deps.insert(ns);
}
Expand Down
Loading