Skip to content

Commit

Permalink
feat: support assembling with compiled libraries
Browse files Browse the repository at this point in the history
This commit refactors `CompiledLibrary` a bit, to remove some
unnecessary restrictions leftover from the old MASL libraries:

* A `CompiledLibrary` no longer has a name, but it has a content digest
  obtained by lexicographically ordering the exported MAST roots of the
  library, and merging the hashes in order.
* As a consequence of being unnamed/having no namespace, a
  `CompiledLibrary` can now consist of procedures from many modules
  _and_ many namespaces. Any limitation we impose on top of that can be
  done via wrapper types, like how `KernelLibrary` is implemented.
* Support for re-exported procedures in a `CompiledLibrary` is
  implemented. It is assumed that all required libraries will be
  provided to the `Host` when executing a program.
* Some ergonomic improvements to APIs which accept libraries or sets of
  modules, to allow a greater variety of ways you can pass them.
  • Loading branch information
bitwalker committed Aug 3, 2024
1 parent 7d1e58b commit e7df6d9
Show file tree
Hide file tree
Showing 23 changed files with 314 additions and 222 deletions.
36 changes: 24 additions & 12 deletions assembly/src/assembler/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -138,15 +138,21 @@ impl Assembler {
}

/// Adds the compiled library to provide modules for the compilation.
pub fn add_compiled_library(&mut self, library: CompiledLibrary) -> Result<(), Report> {
pub fn add_compiled_library(
&mut self,
library: impl AsRef<CompiledLibrary>,
) -> Result<(), Report> {
self.module_graph
.add_compiled_modules(library.into_module_infos())
.add_compiled_modules(library.as_ref().module_infos())
.map_err(Report::from)?;
Ok(())
}

/// Adds the compiled library to provide modules for the compilation.
pub fn with_compiled_library(mut self, library: CompiledLibrary) -> Result<Self, Report> {
pub fn with_compiled_library(
mut self,
library: impl AsRef<CompiledLibrary>,
) -> Result<Self, Report> {
self.add_compiled_library(library)?;
Ok(self)
}
Expand Down Expand Up @@ -189,15 +195,21 @@ impl Assembler {
/// Returns an error if parsing or compilation of the specified modules fails.
pub fn assemble_library(
mut self,
modules: impl Iterator<Item = impl Compile>,
modules: impl IntoIterator<Item = impl Compile>,
) -> Result<CompiledLibrary, Report> {
let ast_module_indices: Vec<ModuleIndex> = modules
.map(|module| {
let module = module.compile_with_options(CompileOptions::for_library())?;
let ast_module_indices =
modules.into_iter().try_fold(Vec::default(), |mut acc, module| {
module
.compile_with_options(CompileOptions::for_library())
.and_then(|module| {
self.module_graph.add_ast_module(module).map_err(Report::from)
})
.map(move |module_id| {
acc.push(module_id);
acc
})
})?;

Ok(self.module_graph.add_ast_module(module)?)
})
.collect::<Result<_, Report>>()?;
self.module_graph.recompute()?;

let mut mast_forest_builder = MastForestBuilder::default();
Expand All @@ -206,8 +218,8 @@ impl Assembler {
let mut exports = BTreeMap::new();

for module_idx in ast_module_indices {
// Note: it is safe to use `unwrap_ast()` here, since all modules looped over are
// AST (we just added them to the module graph)
// Note: it is safe to use `unwrap_ast()` here, since all of the modules contained
// in `ast_module_indices` are in AST form by definition.
let ast_module = self.module_graph[module_idx].unwrap_ast().clone();

for (proc_idx, fqn) in ast_module.exported_procedures() {
Expand Down
10 changes: 4 additions & 6 deletions assembly/src/assembler/module_graph/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,7 @@ use self::{analysis::MaybeRewriteCheck, name_resolver::NameResolver, rewrites::M
use super::{GlobalProcedureIndex, ModuleIndex};
use crate::ast::InvokeKind;
use crate::{
ast::{
Export, InvocationTarget, Module, ProcedureIndex, ProcedureName, QualifiedProcedureName,
ResolvedProcedure,
},
ast::{Export, InvocationTarget, Module, ProcedureIndex, ProcedureName, ResolvedProcedure},
library::{ModuleInfo, ProcedureInfo},
AssemblyError, LibraryNamespace, LibraryPath, RpoDigest, Spanned,
};
Expand Down Expand Up @@ -171,9 +168,10 @@ impl ModuleGraph {
/// Adds all module infos to the graph.
pub fn add_compiled_modules(
&mut self,
module_infos: impl Iterator<Item = ModuleInfo>,
module_infos: impl IntoIterator<Item = ModuleInfo>,
) -> Result<Vec<ModuleIndex>, AssemblyError> {
let module_indices: Vec<ModuleIndex> = module_infos
.into_iter()
.map(|module| self.add_module(PendingWrappedModule::Info(module)))
.collect::<Result<_, _>>()?;

Expand Down Expand Up @@ -255,7 +253,7 @@ impl ModuleGraph {
// TODO: simplify this to avoid using Self::add_compiled_modules()
let mut graph = Self::default();
let module_indexes = graph
.add_compiled_modules([kernel_module].into_iter())
.add_compiled_modules([kernel_module])
.expect("failed to add kernel module to the module graph");
assert_eq!(module_indexes[0], ModuleIndex::new(0), "kernel should be the first module");

Expand Down
8 changes: 4 additions & 4 deletions assembly/src/assembler/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -250,9 +250,9 @@ fn explicit_fully_qualified_procedure_references() {

let bar = Module::parse_str(BAR_NAME.parse().unwrap(), ModuleKind::Library, BAR).unwrap();
let baz = Module::parse_str(BAZ_NAME.parse().unwrap(), ModuleKind::Library, BAZ).unwrap();
let library = Assembler::default().assemble_library(vec![bar, baz].into_iter()).unwrap();
let library = Assembler::default().assemble_library([bar, baz]).unwrap();

let assembler = Assembler::default().with_compiled_library(library).unwrap();
let assembler = Assembler::default().with_compiled_library(&library).unwrap();

let program = r#"
begin
Expand Down Expand Up @@ -282,9 +282,9 @@ fn re_exports() {

let bar = Module::parse_str(BAR_NAME.parse().unwrap(), ModuleKind::Library, BAR).unwrap();
let baz = Module::parse_str(BAZ_NAME.parse().unwrap(), ModuleKind::Library, BAZ).unwrap();
let library = Assembler::default().assemble_library(vec![bar, baz].into_iter()).unwrap();
let library = Assembler::default().assemble_library([bar, baz]).unwrap();

let assembler = Assembler::default().with_compiled_library(library).unwrap();
let assembler = Assembler::default().with_compiled_library(&library).unwrap();

let program = r#"
use.foo::baz
Expand Down
8 changes: 8 additions & 0 deletions assembly/src/ast/procedure/name.rs
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,14 @@ impl fmt::Debug for QualifiedProcedureName {
}
}

impl crate::prettier::PrettyPrint for QualifiedProcedureName {
fn render(&self) -> vm_core::prettier::Document {
use crate::prettier::*;

display(self)
}
}

impl fmt::Display for QualifiedProcedureName {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}::{}", &self.module, &self.name)
Expand Down
3 changes: 2 additions & 1 deletion assembly/src/library/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use crate::{
ast::QualifiedProcedureName,
diagnostics::Diagnostic,
library::{LibraryNamespaceError, VersionError},
prettier::pretty_print_csv,
DeserializationError, LibraryNamespace, LibraryPath, PathError,
};

Expand Down Expand Up @@ -76,7 +77,7 @@ pub enum CompiledLibraryError {
},
#[error(transparent)]
Kernel(#[from] KernelError),
#[error("no MAST roots for the following exports: {missing_exports:?}")]
#[error("no MAST roots for the following exports: {}", pretty_print_csv(missing_exports.as_slice()))]
MissingExports {
missing_exports: Vec<QualifiedProcedureName>,
},
Expand Down
Loading

0 comments on commit e7df6d9

Please sign in to comment.