Move constancy into Dynamic.

This commit is contained in:
Stephen Chung
2020-12-08 22:47:38 +08:00
parent 8e8069f819
commit f22a04fc74
14 changed files with 501 additions and 430 deletions

View File

@@ -1,29 +1,9 @@
//! Module that defines the [`Scope`] type representing a function call-stack scope.
use crate::dynamic::Variant;
use crate::dynamic::{AccessType, Variant};
use crate::stdlib::{borrow::Cow, boxed::Box, iter, string::String, vec::Vec};
use crate::{Dynamic, StaticVec};
/// Type of an entry in the Scope.
#[derive(Debug, Eq, PartialEq, Hash, Copy, Clone)]
pub enum EntryType {
/// Normal value.
Normal,
/// Immutable constant value.
Constant,
}
impl EntryType {
/// Is this entry constant?
#[inline(always)]
pub fn is_constant(&self) -> bool {
match self {
Self::Normal => false,
Self::Constant => true,
}
}
}
/// Type containing information about the current scope.
/// Useful for keeping state between [`Engine`][crate::Engine] evaluation runs.
///
@@ -58,19 +38,17 @@ impl EntryType {
//
// # Implementation Notes
//
// [`Scope`] is implemented as three [`Vec`]'s of exactly the same length. Variables data (name, type, etc.)
// [`Scope`] is implemented as two [`Vec`]'s of exactly the same length. Variables data (name, type, etc.)
// is manually split into three equal-length arrays. That's because variable names take up the most space,
// with [`Cow<str>`][Cow] being four words long, but in the vast majority of cases the name is NOT used to look up
// a variable's value. Variable lookup is usually via direct index, by-passing the name altogether.
//
// Since [`Dynamic`] is reasonably small, packing it tightly improves cache locality when variables are accessed.
// The variable type is packed separately into another array because it is even smaller.
#[derive(Debug, Clone)]
#[derive(Debug)]
pub struct Scope<'a> {
/// Current value of the entry.
values: Vec<Dynamic>,
/// Type of the entry.
types: Vec<EntryType>,
/// (Name, aliases) of the entry. The list of aliases is Boxed because it occurs rarely.
names: Vec<(Cow<'a, str>, Box<StaticVec<String>>)>,
}
@@ -79,7 +57,6 @@ impl Default for Scope<'_> {
fn default() -> Self {
Self {
values: Vec::with_capacity(16),
types: Vec::with_capacity(16),
names: Vec::with_capacity(16),
}
}
@@ -124,7 +101,6 @@ impl<'a> Scope<'a> {
#[inline(always)]
pub fn clear(&mut self) -> &mut Self {
self.names.clear();
self.types.clear();
self.values.clear();
self
}
@@ -180,7 +156,7 @@ impl<'a> Scope<'a> {
name: impl Into<Cow<'a, str>>,
value: impl Variant + Clone,
) -> &mut Self {
self.push_dynamic_value(name, EntryType::Normal, Dynamic::from(value))
self.push_dynamic_value(name, AccessType::Normal, Dynamic::from(value))
}
/// Add (push) a new [`Dynamic`] entry to the [`Scope`].
///
@@ -196,7 +172,7 @@ impl<'a> Scope<'a> {
/// ```
#[inline(always)]
pub fn push_dynamic(&mut self, name: impl Into<Cow<'a, str>>, value: Dynamic) -> &mut Self {
self.push_dynamic_value(name, EntryType::Normal, value)
self.push_dynamic_value(name, value.access_type(), value)
}
/// Add (push) a new constant to the [`Scope`].
///
@@ -219,7 +195,7 @@ impl<'a> Scope<'a> {
name: impl Into<Cow<'a, str>>,
value: impl Variant + Clone,
) -> &mut Self {
self.push_dynamic_value(name, EntryType::Constant, Dynamic::from(value))
self.push_dynamic_value(name, AccessType::Constant, Dynamic::from(value))
}
/// Add (push) a new constant with a [`Dynamic`] value to the Scope.
///
@@ -242,18 +218,18 @@ impl<'a> Scope<'a> {
name: impl Into<Cow<'a, str>>,
value: Dynamic,
) -> &mut Self {
self.push_dynamic_value(name, EntryType::Constant, value)
self.push_dynamic_value(name, AccessType::Constant, value)
}
/// Add (push) a new entry with a [`Dynamic`] value to the [`Scope`].
#[inline]
pub(crate) fn push_dynamic_value(
&mut self,
name: impl Into<Cow<'a, str>>,
entry_type: EntryType,
value: Dynamic,
access: AccessType,
mut value: Dynamic,
) -> &mut Self {
self.names.push((name.into(), Box::new(Default::default())));
self.types.push(entry_type);
value.set_access_type(access);
self.values.push(value.into());
self
}
@@ -286,7 +262,6 @@ impl<'a> Scope<'a> {
#[inline(always)]
pub fn rewind(&mut self, size: usize) -> &mut Self {
self.names.truncate(size);
self.types.truncate(size);
self.values.truncate(size);
self
}
@@ -312,14 +287,14 @@ impl<'a> Scope<'a> {
}
/// Find an entry in the [`Scope`], starting from the last.
#[inline(always)]
pub(crate) fn get_index(&self, name: &str) -> Option<(usize, EntryType)> {
pub(crate) fn get_index(&self, name: &str) -> Option<(usize, AccessType)> {
self.names
.iter()
.enumerate()
.rev() // Always search a Scope in reverse order
.find_map(|(index, (key, _))| {
if name == key.as_ref() {
Some((index, self.types[index]))
Some((index, self.values[index].access_type()))
} else {
None
}
@@ -374,8 +349,8 @@ impl<'a> Scope<'a> {
None => {
self.push(name, value);
}
Some((_, EntryType::Constant)) => panic!("variable {} is constant", name),
Some((index, EntryType::Normal)) => {
Some((_, AccessType::Constant)) => panic!("variable {} is constant", name),
Some((index, AccessType::Normal)) => {
*self.values.get_mut(index).unwrap() = Dynamic::from(value);
}
}
@@ -383,11 +358,8 @@ impl<'a> Scope<'a> {
}
/// Get a mutable reference to an entry in the [`Scope`].
#[inline(always)]
pub(crate) fn get_mut(&mut self, index: usize) -> (&mut Dynamic, EntryType) {
(
self.values.get_mut(index).expect("invalid index in Scope"),
self.types[index],
)
pub(crate) fn get_mut(&mut self, index: usize) -> &mut Dynamic {
self.values.get_mut(index).expect("invalid index in Scope")
}
/// Update the access type of an entry in the [`Scope`].
#[cfg(not(feature = "no_module"))]
@@ -412,7 +384,6 @@ impl<'a> Scope<'a> {
.for_each(|(index, (name, alias))| {
if !entries.names.iter().any(|(key, _)| key == name) {
entries.names.push((name.clone(), alias.clone()));
entries.types.push(self.types[index]);
entries.values.push(self.values[index].clone());
}
});
@@ -422,13 +393,11 @@ impl<'a> Scope<'a> {
/// Get an iterator to entries in the [`Scope`].
#[inline(always)]
#[allow(dead_code)]
pub(crate) fn into_iter(
self,
) -> impl Iterator<Item = (Cow<'a, str>, EntryType, Dynamic, Vec<String>)> {
pub(crate) fn into_iter(self) -> impl Iterator<Item = (Cow<'a, str>, Dynamic, Vec<String>)> {
self.names
.into_iter()
.zip(self.types.into_iter().zip(self.values.into_iter()))
.map(|((name, alias), (typ, value))| (name, typ, value, alias.to_vec()))
.zip(self.values.into_iter())
.map(|((name, alias), value)| (name, value, alias.to_vec()))
}
/// Get an iterator to entries in the [`Scope`].
/// Shared values are flatten-cloned.
@@ -466,17 +435,16 @@ impl<'a> Scope<'a> {
pub fn iter_raw<'x: 'a>(&'x self) -> impl Iterator<Item = (&'a str, bool, &'x Dynamic)> + 'x {
self.names
.iter()
.zip(self.types.iter().zip(self.values.iter()))
.map(|((name, _), (typ, value))| (name.as_ref(), typ.is_constant(), value))
.zip(self.values.iter())
.map(|((name, _), value)| (name.as_ref(), value.is_constant(), value))
}
}
impl<'a, K: Into<Cow<'a, str>>> iter::Extend<(K, EntryType, Dynamic)> for Scope<'a> {
impl<'a, K: Into<Cow<'a, str>>> iter::Extend<(K, Dynamic)> for Scope<'a> {
#[inline(always)]
fn extend<T: IntoIterator<Item = (K, EntryType, Dynamic)>>(&mut self, iter: T) {
iter.into_iter().for_each(|(name, typ, value)| {
fn extend<T: IntoIterator<Item = (K, Dynamic)>>(&mut self, iter: T) {
iter.into_iter().for_each(|(name, value)| {
self.names.push((name.into(), Box::new(Default::default())));
self.types.push(typ);
self.values.push(value);
});
}