1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171
use super::{
ByteReader, ByteWriter, Deserializable, DeserializationError, Node, Serializable,
SourceLocation, MAX_BODY_LEN,
};
use alloc::vec::Vec;
use core::{iter, slice};
// CODE BODY
// ================================================================================================
/// A contiguous sequence of [Node]s with optional [SourceLocation] specified for each node.
///
/// When present, the number of locations is equal to the number of nodes + 1. This is because the
/// last location tracks the `end` token of a body which does not have its own node.
#[derive(Clone, Default, Eq, Debug)]
pub struct CodeBody {
nodes: Vec<Node>,
locations: Vec<SourceLocation>,
}
impl CodeBody {
// CONSTRUCTOR
// --------------------------------------------------------------------------------------------
/// Creates a new instance of [CodeBody] populated with the provided `nodes`.
///
/// # Panics
/// Assumes that the number of nodes is smaller than 2^16 and panics otherwise.
pub fn new<N>(nodes: N) -> Self
where
N: IntoIterator<Item = Node>,
{
let nodes: Vec<_> = nodes.into_iter().collect();
assert!(nodes.len() <= MAX_BODY_LEN, "too many nodes");
Self {
nodes,
locations: Vec::new(),
}
}
/// Binds [SourceLocation]s to their respective [Node].
///
/// It is expected that `locations` have the length one greater than the length of `self.nodes`.
pub fn with_source_locations<L>(mut self, locations: L) -> Self
where
L: IntoIterator<Item = SourceLocation>,
{
self.locations = locations.into_iter().collect();
// TODO: add an assert to check that locations.len() == nodes.len() + 1; this is currently
// not possible because the true branch of an IfElse block when there is a false branch
// will not have location for the end token appended at construction time. this location
// is appended via `add_final_location()` method.
self
}
// STATE MUTATORS
// --------------------------------------------------------------------------------------------
/// Adds the provided location to the end of location list.
///
/// It is expected that prior to calling this method the number of nodes and locations
/// contained in this code body is the same. Thus, after calling this method there will be one
/// more location than node. This is because locations should map `1:1` to their nodes, except
/// for the block termination that is always the last location.
///
/// # Panics
/// Panics if the final location has been added previously.
pub fn add_final_location(&mut self, location: SourceLocation) {
assert_eq!(self.locations.len(), self.nodes.len());
self.locations.push(location);
}
/// Removes source location information from this code body.
pub fn clear_locations(&mut self) {
self.locations.clear();
}
// SERIALIZATION / DESERIALIZATION
// --------------------------------------------------------------------------------------------
/// Loads the [SourceLocation] from the `source`.
///
/// The `source` is expected to provide a locations count equal to the block nodes count + 1,
/// having the last element reserved for its `end` node. This way, the locations count is not
/// expected to be read, as opposed to common vector serialization strategies.
///
/// This implementation intentionally diverges from [Deserializable] so locations can be
/// optionally stored.
pub fn load_source_locations<R: ByteReader>(
&mut self,
source: &mut R,
) -> Result<(), DeserializationError> {
self.locations = (0..=self.nodes.len())
.map(|_| SourceLocation::read_from(source))
.collect::<Result<_, _>>()?;
Ok(())
}
/// Writes the [SourceLocation] into `target`.
///
/// The locations will be written directly, without storing the locations count. This is the
/// counterpart of [CodeBody::load_source_locations].
///
/// This implementation intentionally diverges from [Serializable] so locations can be
/// optionally stored.
pub fn write_source_locations<W: ByteWriter>(&self, target: &mut W) {
self.locations.iter().for_each(|l| l.write_into(target));
}
// PUBLIC ACCESSORS
// --------------------------------------------------------------------------------------------
/// Returns the [Node] sequence.
pub fn nodes(&self) -> &[Node] {
&self.nodes
}
/// Returns the [SourceLocations] bound to the nodes of this body structure.
pub fn source_locations(&self) -> &[SourceLocation] {
&self.locations
}
/// Returns true if this code body contain source location information.
pub fn has_locations(&self) -> bool {
!self.locations.is_empty()
}
// DESTRUCTURING
// --------------------------------------------------------------------------------------------
/// Returns the internal parts of this code body.
pub fn into_parts(self) -> (Vec<Node>, Vec<SourceLocation>) {
(self.nodes, self.locations)
}
}
impl<'a> IntoIterator for &'a CodeBody {
type Item = (&'a Node, &'a SourceLocation);
type IntoIter = iter::Zip<slice::Iter<'a, Node>, slice::Iter<'a, SourceLocation>>;
fn into_iter(self) -> Self::IntoIter {
self.nodes.iter().zip(self.locations.iter())
}
}
impl FromIterator<Node> for CodeBody {
fn from_iter<T: IntoIterator<Item = Node>>(nodes: T) -> Self {
Self {
nodes: nodes.into_iter().collect(),
locations: Vec::new(),
}
}
}
impl FromIterator<(Node, SourceLocation)> for CodeBody {
fn from_iter<T: IntoIterator<Item = (Node, SourceLocation)>>(nodes: T) -> Self {
let (nodes, locations) = nodes.into_iter().unzip();
Self { nodes, locations }
}
}
impl PartialEq for CodeBody {
fn eq(&self, other: &Self) -> bool {
// TODO deserialized node will not restore location, but equality must hold
let nodes = self.nodes == other.nodes;
let locations = self.locations == other.locations;
let left_empty = self.locations.is_empty();
let right_empty = other.locations.is_empty();
nodes && (locations || left_empty || right_empty)
}
}