Refine Scope API.
This commit is contained in:
164
src/scope.rs
164
src/scope.rs
@@ -10,32 +10,34 @@ use crate::stdlib::{
|
||||
vec::Vec,
|
||||
};
|
||||
|
||||
/// Type of a variable in the Scope.
|
||||
/// Type of an entry in the Scope.
|
||||
#[derive(Debug, Eq, PartialEq, Hash, Copy, Clone)]
|
||||
pub enum VariableType {
|
||||
/// Normal variable.
|
||||
pub enum EntryType {
|
||||
/// Normal value.
|
||||
Normal,
|
||||
/// Immutable constant value.
|
||||
Constant,
|
||||
}
|
||||
|
||||
/// An entry in the Scope.
|
||||
pub struct ScopeEntry<'a> {
|
||||
/// Name of the variable.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Entry<'a> {
|
||||
/// Name of the entry.
|
||||
pub name: Cow<'a, str>,
|
||||
/// Type of the variable.
|
||||
pub var_type: VariableType,
|
||||
/// Current value of the variable.
|
||||
/// Type of the entry.
|
||||
pub typ: EntryType,
|
||||
/// Current value of the entry.
|
||||
pub value: Dynamic,
|
||||
/// A constant expression if the initial value matches one of the recognized types.
|
||||
pub expr: Option<Expr>,
|
||||
}
|
||||
|
||||
/// Information about a particular entry in the Scope.
|
||||
pub(crate) struct ScopeSource<'a> {
|
||||
#[derive(Debug, Hash, Copy, Clone)]
|
||||
pub(crate) struct EntryRef<'a> {
|
||||
pub name: &'a str,
|
||||
pub idx: usize,
|
||||
pub var_type: VariableType,
|
||||
pub index: usize,
|
||||
pub typ: EntryType,
|
||||
}
|
||||
|
||||
/// A type containing information about the current scope.
|
||||
@@ -57,9 +59,9 @@ pub(crate) struct ScopeSource<'a> {
|
||||
/// # }
|
||||
/// ```
|
||||
///
|
||||
/// When searching for variables, newly-added variables are found before similarly-named but older variables,
|
||||
/// allowing for automatic _shadowing_ of variables.
|
||||
pub struct Scope<'a>(Vec<ScopeEntry<'a>>);
|
||||
/// When searching for entries, newly-added entries are found before similarly-named but older entries,
|
||||
/// allowing for automatic _shadowing_.
|
||||
pub struct Scope<'a>(Vec<Entry<'a>>);
|
||||
|
||||
impl<'a> Scope<'a> {
|
||||
/// Create a new Scope.
|
||||
@@ -72,7 +74,7 @@ impl<'a> Scope<'a> {
|
||||
self.0.clear();
|
||||
}
|
||||
|
||||
/// Get the number of variables inside the Scope.
|
||||
/// Get the number of entries inside the Scope.
|
||||
pub fn len(&self) -> usize {
|
||||
self.0.len()
|
||||
}
|
||||
@@ -82,19 +84,14 @@ impl<'a> Scope<'a> {
|
||||
self.0.len() == 0
|
||||
}
|
||||
|
||||
/// Add (push) a new variable to the Scope.
|
||||
/// Add (push) a new entry to the Scope.
|
||||
pub fn push<K: Into<Cow<'a, str>>, T: Any + Clone>(&mut self, name: K, value: T) {
|
||||
let value = value.into_dynamic();
|
||||
self.push_dynamic_value(name, EntryType::Normal, value.into_dynamic(), false);
|
||||
}
|
||||
|
||||
// Map into constant expressions
|
||||
//let (expr, value) = map_dynamic_to_expr(value, Position::none());
|
||||
|
||||
self.0.push(ScopeEntry {
|
||||
name: name.into(),
|
||||
var_type: VariableType::Normal,
|
||||
value,
|
||||
expr: None,
|
||||
});
|
||||
/// Add (push) a new `Dynamic` entry to the Scope.
|
||||
pub fn push_dynamic<K: Into<Cow<'a, str>>>(&mut self, name: K, value: Dynamic) {
|
||||
self.push_dynamic_value(name, EntryType::Normal, value, false);
|
||||
}
|
||||
|
||||
/// Add (push) a new constant to the Scope.
|
||||
@@ -104,84 +101,75 @@ impl<'a> Scope<'a> {
|
||||
/// However, in order to be used for optimization, constants must be in one of the recognized types:
|
||||
/// `INT` (default to `i64`, `i32` if `only_i32`), `f64`, `String`, `char` and `bool`.
|
||||
pub fn push_constant<K: Into<Cow<'a, str>>, T: Any + Clone>(&mut self, name: K, value: T) {
|
||||
let value = value.into_dynamic();
|
||||
|
||||
// Map into constant expressions
|
||||
let (expr, value) = map_dynamic_to_expr(value, Position::none());
|
||||
|
||||
self.0.push(ScopeEntry {
|
||||
name: name.into(),
|
||||
var_type: VariableType::Constant,
|
||||
value,
|
||||
expr,
|
||||
});
|
||||
self.push_dynamic_value(name, EntryType::Constant, value.into_dynamic(), true);
|
||||
}
|
||||
|
||||
/// Add (push) a new variable with a `Dynamic` value to the Scope.
|
||||
pub(crate) fn push_dynamic<K: Into<Cow<'a, str>>>(
|
||||
/// Add (push) a new constant with a `Dynamic` value to the Scope.
|
||||
///
|
||||
/// Constants are immutable and cannot be assigned to. Their values never change.
|
||||
/// Constants propagation is a technique used to optimize an AST.
|
||||
/// However, in order to be used for optimization, the `Dynamic` value must be in one of the
|
||||
/// recognized types:
|
||||
/// `INT` (default to `i64`, `i32` if `only_i32`), `f64`, `String`, `char` and `bool`.
|
||||
pub fn push_constant_dynamic<K: Into<Cow<'a, str>>>(&mut self, name: K, value: Dynamic) {
|
||||
self.push_dynamic_value(name, EntryType::Constant, value, true);
|
||||
}
|
||||
|
||||
/// Add (push) a new entry with a `Dynamic` value to the Scope.
|
||||
pub(crate) fn push_dynamic_value<K: Into<Cow<'a, str>>>(
|
||||
&mut self,
|
||||
name: K,
|
||||
var_type: VariableType,
|
||||
entry_type: EntryType,
|
||||
value: Dynamic,
|
||||
map_expr: bool,
|
||||
) {
|
||||
let (expr, value) = map_dynamic_to_expr(value, Position::none());
|
||||
let (expr, value) = if map_expr {
|
||||
map_dynamic_to_expr(value, Position::none())
|
||||
} else {
|
||||
(None, value)
|
||||
};
|
||||
|
||||
self.0.push(ScopeEntry {
|
||||
self.0.push(Entry {
|
||||
name: name.into(),
|
||||
var_type,
|
||||
typ: entry_type,
|
||||
value,
|
||||
expr,
|
||||
});
|
||||
}
|
||||
|
||||
/// Remove (pop) the last variable from the Scope.
|
||||
pub fn pop(&mut self) -> Option<(String, VariableType, Dynamic)> {
|
||||
self.0.pop().map(
|
||||
|ScopeEntry {
|
||||
name,
|
||||
var_type,
|
||||
value,
|
||||
..
|
||||
}| (name.to_string(), var_type, value),
|
||||
)
|
||||
}
|
||||
|
||||
/// Truncate (rewind) the Scope to a previous size.
|
||||
pub fn rewind(&mut self, size: usize) {
|
||||
self.0.truncate(size);
|
||||
}
|
||||
|
||||
/// Does the scope contain the variable?
|
||||
/// Does the scope contain the entry?
|
||||
pub fn contains(&self, key: &str) -> bool {
|
||||
self.0
|
||||
.iter()
|
||||
.enumerate()
|
||||
.rev() // Always search a Scope in reverse order
|
||||
.any(|(_, ScopeEntry { name, .. })| name == key)
|
||||
.any(|(_, Entry { name, .. })| name == key)
|
||||
}
|
||||
|
||||
/// Find a variable in the Scope, starting from the last.
|
||||
pub(crate) fn get(&self, key: &str) -> Option<(ScopeSource, Dynamic)> {
|
||||
/// Find an entry in the Scope, starting from the last.
|
||||
pub(crate) fn get(&self, key: &str) -> Option<(EntryRef, Dynamic)> {
|
||||
self.0
|
||||
.iter()
|
||||
.enumerate()
|
||||
.rev() // Always search a Scope in reverse order
|
||||
.find(|(_, ScopeEntry { name, .. })| name == key)
|
||||
.find(|(_, Entry { name, .. })| name == key)
|
||||
.map(
|
||||
|(
|
||||
i,
|
||||
ScopeEntry {
|
||||
name,
|
||||
var_type,
|
||||
value,
|
||||
..
|
||||
Entry {
|
||||
name, typ, value, ..
|
||||
},
|
||||
)| {
|
||||
(
|
||||
ScopeSource {
|
||||
EntryRef {
|
||||
name: name.as_ref(),
|
||||
idx: i,
|
||||
var_type: *var_type,
|
||||
index: i,
|
||||
typ: *typ,
|
||||
},
|
||||
value.clone(),
|
||||
)
|
||||
@@ -189,41 +177,41 @@ impl<'a> Scope<'a> {
|
||||
)
|
||||
}
|
||||
|
||||
/// Get the value of a variable in the Scope, starting from the last.
|
||||
/// Get the value of an entry in the Scope, starting from the last.
|
||||
pub fn get_value<T: Any + Clone>(&self, key: &str) -> Option<T> {
|
||||
self.0
|
||||
.iter()
|
||||
.enumerate()
|
||||
.rev() // Always search a Scope in reverse order
|
||||
.find(|(_, ScopeEntry { name, .. })| name == key)
|
||||
.and_then(|(_, ScopeEntry { value, .. })| value.downcast_ref::<T>())
|
||||
.find(|(_, Entry { name, .. })| name == key)
|
||||
.and_then(|(_, Entry { value, .. })| value.downcast_ref::<T>())
|
||||
.map(T::clone)
|
||||
}
|
||||
|
||||
/// Get a mutable reference to a variable in the Scope.
|
||||
pub(crate) fn get_mut(&mut self, name: &str, index: usize) -> &mut Dynamic {
|
||||
let entry = self.0.get_mut(index).expect("invalid index in Scope");
|
||||
/// Get a mutable reference to an entry in the Scope.
|
||||
pub(crate) fn get_mut(&mut self, key: EntryRef) -> &mut Dynamic {
|
||||
let entry = self.0.get_mut(key.index).expect("invalid index in Scope");
|
||||
assert_eq!(entry.typ, key.typ, "entry type not matched");
|
||||
|
||||
// assert_ne!(
|
||||
// entry.var_type,
|
||||
// VariableType::Constant,
|
||||
// "get mut of constant variable"
|
||||
// entry.typ,
|
||||
// EntryType::Constant,
|
||||
// "get mut of constant entry"
|
||||
// );
|
||||
assert_eq!(entry.name, name, "incorrect key at Scope entry");
|
||||
assert_eq!(entry.name, key.name, "incorrect key at Scope entry");
|
||||
|
||||
&mut entry.value
|
||||
}
|
||||
|
||||
/// Get a mutable reference to a variable in the Scope and downcast it to a specific type
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
pub(crate) fn get_mut_by_type<T: Any + Clone>(&mut self, name: &str, index: usize) -> &mut T {
|
||||
self.get_mut(name, index)
|
||||
/// Get a mutable reference to an entry in the Scope and downcast it to a specific type
|
||||
pub(crate) fn get_mut_by_type<T: Any + Clone>(&mut self, key: EntryRef) -> &mut T {
|
||||
self.get_mut(key)
|
||||
.downcast_mut::<T>()
|
||||
.expect("wrong type cast")
|
||||
}
|
||||
|
||||
/// Get an iterator to variables in the Scope.
|
||||
pub fn iter(&self) -> impl Iterator<Item = &ScopeEntry> {
|
||||
/// Get an iterator to entries in the Scope.
|
||||
pub fn iter(&self) -> impl Iterator<Item = &Entry> {
|
||||
self.0.iter().rev() // Always search a Scope in reverse order
|
||||
}
|
||||
}
|
||||
@@ -234,15 +222,15 @@ impl Default for Scope<'_> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, K> iter::Extend<(K, VariableType, Dynamic)> for Scope<'a>
|
||||
impl<'a, K> iter::Extend<(K, EntryType, Dynamic)> for Scope<'a>
|
||||
where
|
||||
K: Into<Cow<'a, str>>,
|
||||
{
|
||||
fn extend<T: IntoIterator<Item = (K, VariableType, Dynamic)>>(&mut self, iter: T) {
|
||||
fn extend<T: IntoIterator<Item = (K, EntryType, Dynamic)>>(&mut self, iter: T) {
|
||||
self.0
|
||||
.extend(iter.into_iter().map(|(name, var_type, value)| ScopeEntry {
|
||||
.extend(iter.into_iter().map(|(name, typ, value)| Entry {
|
||||
name: name.into(),
|
||||
var_type,
|
||||
typ,
|
||||
value,
|
||||
expr: None,
|
||||
}));
|
||||
|
Reference in New Issue
Block a user