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: add SourceLocation to Node #885

Merged
merged 2 commits into from
May 19, 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
33 changes: 17 additions & 16 deletions assembly/src/assembler/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -204,9 +204,9 @@ impl Assembler {
prologue: vec![Operation::Push(num_locals), Operation::FmpUpdate],
epilogue: vec![Operation::Push(-num_locals), Operation::FmpUpdate],
};
self.compile_body(proc.body.iter(), context, Some(wrapper))?
self.compile_body(proc.body.nodes().iter(), context, Some(wrapper))?
} else {
self.compile_body(proc.body.iter(), context, None)?
self.compile_body(proc.body.nodes().iter(), context, None)?
};

context.complete_proc(code_root);
Expand All @@ -233,47 +233,48 @@ impl Assembler {

for node in body {
match node.borrow() {
Node::Instruction(instruction) => {
if let Some(block) =
self.compile_instruction(instruction, &mut span, context)?
{
Node::Instruction(inner) => {
if let Some(block) = self.compile_instruction(inner, &mut span, context)? {
span.extract_span_into(&mut blocks);
blocks.push(block);
}
}

Node::IfElse(t, f) => {
Node::IfElse {
true_case,
false_case,
} => {
span.extract_span_into(&mut blocks);

let t = self.compile_body(t.iter(), context, None)?;
let true_case = self.compile_body(true_case.nodes().iter(), context, None)?;

// else is an exception because it is optional; hence, will have to be replaced
// by noop span
let f = if !f.is_empty() {
self.compile_body(f.iter(), context, None)?
let false_case = if !false_case.nodes().is_empty() {
self.compile_body(false_case.nodes().iter(), context, None)?
} else {
CodeBlock::new_span(vec![Operation::Noop])
};

let block = CodeBlock::new_split(t, f);
let block = CodeBlock::new_split(true_case, false_case);

blocks.push(block);
}

Node::Repeat(n, nodes) => {
Node::Repeat { times, body } => {
span.extract_span_into(&mut blocks);

let block = self.compile_body(nodes.iter(), context, None)?;
let block = self.compile_body(body.nodes().iter(), context, None)?;

for _ in 0..*n {
for _ in 0..*times {
blocks.push(block.clone());
}
}

Node::While(nodes) => {
Node::While { body } => {
span.extract_span_into(&mut blocks);

let block = self.compile_body(nodes.iter(), context, None)?;
let block = self.compile_body(body.nodes().iter(), context, None)?;
let block = CodeBlock::new_loop(block);

blocks.push(block);
Expand Down
32 changes: 29 additions & 3 deletions assembly/src/library/masl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ pub struct MaslLibrary {
namespace: LibraryNamespace,
/// Version of the library.
version: Version,
/// Flag defining if locations are serialized with the library.
has_source_locations: bool,
/// Available modules.
modules: Vec<Module>,
}
Expand Down Expand Up @@ -51,9 +53,10 @@ impl MaslLibrary {
/// # Errors
/// Returns an error if the provided `modules` vector is empty or contains more than
/// [u16::MAX] elements.
fn new(
pub(super) fn new(
namespace: LibraryNamespace,
version: Version,
has_source_locations: bool,
modules: Vec<Module>,
) -> Result<Self, LibraryError> {
if modules.is_empty() {
Expand All @@ -69,9 +72,18 @@ impl MaslLibrary {
Ok(Self {
namespace,
version,
has_source_locations,
modules,
})
}

// STATE MUTATORS
// --------------------------------------------------------------------------------------------

/// Clears the source locations from this bundle.
pub fn clear_locations(&mut self) {
self.modules.iter_mut().for_each(|m| m.clear_locations())
}
}

#[cfg(feature = "std")]
Expand All @@ -96,6 +108,7 @@ mod use_std {
pub fn read_from_dir<P>(
path: P,
namespace: LibraryNamespace,
with_source_locations: bool,
version: Version,
) -> io::Result<Self>
where
Expand All @@ -118,7 +131,7 @@ mod use_std {
.map(|(path, ast)| Module { path, ast })
.collect();

Self::new(namespace, version, modules)
Self::new(namespace, version, with_source_locations, modules)
.map_err(|err| io::Error::new(io::ErrorKind::Other, format!("{err}")))
}

Expand Down Expand Up @@ -229,6 +242,13 @@ impl Serializable for MaslLibrary {
.write_into(target);
module.ast.write_into(target);
});

// optionally write the locations into the target. given the modules count is already
// written, we can safely dump the locations structs
target.write_bool(self.has_source_locations);
if self.has_source_locations {
self.modules.iter().for_each(|m| m.write_source_locations(target));
}
}
}

Expand All @@ -247,7 +267,13 @@ impl Deserializable for MaslLibrary {
modules.push(Module { path, ast });
}

Self::new(namespace, version, modules)
// for each module, load its locations
let has_source_locations = source.read_bool()?;
if has_source_locations {
modules.iter_mut().try_for_each(|m| m.load_source_locations(source))?;
}

Self::new(namespace, version, has_source_locations, modules)
.map_err(|err| DeserializationError::InvalidValue(format!("{err}")))
}
}
27 changes: 27 additions & 0 deletions assembly/src/library/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ pub use masl::MaslLibrary;
mod path;
pub use path::LibraryPath;

#[cfg(test)]
mod tests;

// LIBRARY
// ================================================================================================

Expand Down Expand Up @@ -89,6 +92,30 @@ impl Module {
LibraryError::inconsistent_namespace(self.path.first(), namespace.as_str())
})
}

// STATE MUTATORS
// --------------------------------------------------------------------------------------------

/// Clears the source locations from this module.
pub fn clear_locations(&mut self) {
self.ast.clear_locations()
}

// SERIALIZATION / DESERIALIZATION
// --------------------------------------------------------------------------------------------

/// Loads the [SourceLocation] of the procedures via [ModuleAst::load_source_locations].
pub fn load_source_locations<R: ByteReader>(
&mut self,
source: &mut R,
) -> Result<(), DeserializationError> {
self.ast.load_source_locations(source)
}

/// Writes the [SourceLocation] of the procedures via [ModuleAst::write_source_locations].
pub fn write_source_locations<W: ByteWriter>(&self, target: &mut W) {
self.ast.write_source_locations(target)
}
}

impl PartialOrd for Module {
Expand Down
57 changes: 57 additions & 0 deletions assembly/src/library/tests.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
use super::{LibraryNamespace, LibraryPath, MaslLibrary, Module, ModuleAst, Version};
use vm_core::utils::{Deserializable, Serializable, SliceReader};

#[test]
fn masl_locations_serialization() {
// declare foo module
let foo = r#"
export.foo
add
end
export.foo_mul
mul
end
"#;
let path = LibraryPath::new("test::foo").unwrap();
let ast = ModuleAst::parse(foo).unwrap();
let foo = Module::new(path, ast);

// declare bar module
let bar = r#"
export.bar
mtree_get
end
export.bar_mul
mul
end
"#;
let path = LibraryPath::new("test::bar").unwrap();
let ast = ModuleAst::parse(bar).unwrap();
let bar = Module::new(path, ast);
let modules = [foo, bar].to_vec();

// create the bundle with locations
let namespace = LibraryNamespace::new("test").unwrap();
let version = Version::MIN;
let locations = true;
let bundle = MaslLibrary::new(namespace, version, locations, modules.clone()).unwrap();

// serialize/deserialize the bundle
let mut bytes = Vec::new();
bundle.write_into(&mut bytes);
let deserialized = MaslLibrary::read_from(&mut SliceReader::new(&bytes)).unwrap();
assert_eq!(bundle, deserialized);

// create the bundle without locations
let namespace = LibraryNamespace::new("test").unwrap();
let locations = false;
let mut bundle = MaslLibrary::new(namespace, version, locations, modules).unwrap();

// serialize/deserialize the bundle
let mut bytes = Vec::new();
bundle.write_into(&mut bytes);
let deserialized = MaslLibrary::read_from(&mut SliceReader::new(&bytes)).unwrap();
assert_ne!(bundle, deserialized, "sanity check");
bundle.clear_locations();
assert_eq!(bundle, deserialized);
}
Loading