Add AST::iter_literal_variables.
This commit is contained in:
parent
e63cba9caf
commit
f6dc440601
@ -14,6 +14,7 @@ New features
|
||||
|
||||
* `#[cfg(...)]` attributes can now be put directly on plugin functions or function defined in a plugin module.
|
||||
* A custom syntax parser can now return a symbol starting with `$$` to inform the implementation function which syntax variant was actually parsed.
|
||||
* `AST::iter_literal_variables` extracts all top-level literal constant/variable definitions from a script without running it.
|
||||
|
||||
Enhancements
|
||||
------------
|
||||
|
98
src/ast.rs
98
src/ast.rs
@ -749,6 +749,86 @@ impl AST {
|
||||
self.body = StmtBlock::empty();
|
||||
self
|
||||
}
|
||||
/// Extract all top-level literal constant and/or variable definitions.
|
||||
/// This is useful for extracting all global constants from a script without actually running it.
|
||||
///
|
||||
/// A literal constant/variable definition takes the form of:
|
||||
/// `const VAR = `_value_`;` and `let VAR = `_value_`;`
|
||||
/// where _value_ is a literal expression or will be optimized into a literal.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// # fn main() -> Result<(), Box<rhai::EvalAltResult>> {
|
||||
/// use rhai::{Engine, Scope};
|
||||
///
|
||||
/// let engine = Engine::new();
|
||||
///
|
||||
/// let ast = engine.compile(
|
||||
/// "
|
||||
/// const A = 40 + 2; // constant that optimizes into a literal
|
||||
/// let b = 123; // literal variable
|
||||
/// const B = b * A; // non-literal constant
|
||||
/// const C = 999; // literal constant
|
||||
/// b = A + C; // expression
|
||||
///
|
||||
/// { // <- new block scope
|
||||
/// const Z = 0; // <- literal constant not at top-level
|
||||
/// }
|
||||
/// ")?;
|
||||
///
|
||||
/// let mut iter = ast.iter_literal_variables(true, false)
|
||||
/// .map(|(name, is_const, value)| (name, is_const, value.as_int().unwrap()));
|
||||
///
|
||||
/// assert_eq!(iter.next(), Some(("A", true, 42)));
|
||||
/// assert_eq!(iter.next(), Some(("C", true, 999)));
|
||||
/// assert_eq!(iter.next(), None);
|
||||
///
|
||||
/// let mut iter = ast.iter_literal_variables(false, true)
|
||||
/// .map(|(name, is_const, value)| (name, is_const, value.as_int().unwrap()));
|
||||
///
|
||||
/// assert_eq!(iter.next(), Some(("b", false, 123)));
|
||||
/// assert_eq!(iter.next(), None);
|
||||
///
|
||||
/// let mut iter = ast.iter_literal_variables(true, true)
|
||||
/// .map(|(name, is_const, value)| (name, is_const, value.as_int().unwrap()));
|
||||
///
|
||||
/// assert_eq!(iter.next(), Some(("A", true, 42)));
|
||||
/// assert_eq!(iter.next(), Some(("b", false, 123)));
|
||||
/// assert_eq!(iter.next(), Some(("C", true, 999)));
|
||||
/// assert_eq!(iter.next(), None);
|
||||
///
|
||||
/// let scope: Scope = ast.iter_literal_variables(true, false).collect();
|
||||
///
|
||||
/// assert_eq!(scope.len(), 2);
|
||||
///
|
||||
/// Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn iter_literal_variables(
|
||||
&self,
|
||||
include_constants: bool,
|
||||
include_variables: bool,
|
||||
) -> impl Iterator<Item = (&str, bool, Dynamic)> {
|
||||
self.statements().iter().filter_map(move |stmt| match stmt {
|
||||
Stmt::Var(expr, name, options, _)
|
||||
if options.contains(AST_OPTION_FLAGS::AST_OPTION_CONSTANT) && include_constants
|
||||
|| !options.contains(AST_OPTION_FLAGS::AST_OPTION_CONSTANT)
|
||||
&& include_variables =>
|
||||
{
|
||||
if let Some(value) = expr.get_literal_value() {
|
||||
Some((
|
||||
name.as_str(),
|
||||
options.contains(AST_OPTION_FLAGS::AST_OPTION_CONSTANT),
|
||||
value,
|
||||
))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
_ => None,
|
||||
})
|
||||
}
|
||||
/// Recursively walk the [`AST`], including function bodies (if any).
|
||||
/// Return `false` from the callback to terminate the walk.
|
||||
#[cfg(not(feature = "internals"))]
|
||||
@ -835,7 +915,7 @@ impl AsRef<Module> for AST {
|
||||
pub struct Ident {
|
||||
/// Identifier name.
|
||||
pub name: Identifier,
|
||||
/// Declaration position.
|
||||
/// Position.
|
||||
pub pos: Position,
|
||||
}
|
||||
|
||||
@ -846,6 +926,20 @@ impl fmt::Debug for Ident {
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<str> for Ident {
|
||||
#[inline(always)]
|
||||
fn as_ref(&self) -> &str {
|
||||
self.name.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl Ident {
|
||||
#[inline(always)]
|
||||
pub fn as_str(&self) -> &str {
|
||||
self.name.as_str()
|
||||
}
|
||||
}
|
||||
|
||||
/// _(internals)_ An [`AST`] node, consisting of either an [`Expr`] or a [`Stmt`].
|
||||
/// Exported under the `internals` feature only.
|
||||
///
|
||||
@ -1404,7 +1498,7 @@ impl Stmt {
|
||||
///
|
||||
/// An internally pure statement only has side effects that disappear outside the block.
|
||||
///
|
||||
/// Currently only variable declarations (i.e. `let` and `const`) and `import`/`export`
|
||||
/// Currently only variable definitions (i.e. `let` and `const`) and `import`/`export`
|
||||
/// statements are internally pure.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
|
39
src/scope.rs
39
src/scope.rs
@ -2,6 +2,7 @@
|
||||
|
||||
use crate::dynamic::{AccessMode, Variant};
|
||||
use crate::{Dynamic, Identifier, StaticVec};
|
||||
use std::iter::FromIterator;
|
||||
#[cfg(feature = "no_std")]
|
||||
use std::prelude::v1::*;
|
||||
use std::{borrow::Cow, iter::Extend};
|
||||
@ -592,8 +593,42 @@ impl<'a, K: Into<Cow<'a, str>>> Extend<(K, Dynamic)> for Scope<'a> {
|
||||
#[inline]
|
||||
fn extend<T: IntoIterator<Item = (K, Dynamic)>>(&mut self, iter: T) {
|
||||
iter.into_iter().for_each(|(name, value)| {
|
||||
self.names.push((name.into(), None));
|
||||
self.values.push(value);
|
||||
self.push_dynamic_value(name, AccessMode::ReadWrite, value);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, K: Into<Cow<'a, str>>> FromIterator<(K, Dynamic)> for Scope<'a> {
|
||||
#[inline]
|
||||
fn from_iter<T: IntoIterator<Item = (K, Dynamic)>>(iter: T) -> Self {
|
||||
let mut scope = Self::new();
|
||||
scope.extend(iter);
|
||||
scope
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, K: Into<Cow<'a, str>>> Extend<(K, bool, Dynamic)> for Scope<'a> {
|
||||
#[inline]
|
||||
fn extend<T: IntoIterator<Item = (K, bool, Dynamic)>>(&mut self, iter: T) {
|
||||
iter.into_iter().for_each(|(name, is_constant, value)| {
|
||||
self.push_dynamic_value(
|
||||
name,
|
||||
if is_constant {
|
||||
AccessMode::ReadOnly
|
||||
} else {
|
||||
AccessMode::ReadWrite
|
||||
},
|
||||
value,
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, K: Into<Cow<'a, str>>> FromIterator<(K, bool, Dynamic)> for Scope<'a> {
|
||||
#[inline]
|
||||
fn from_iter<T: IntoIterator<Item = (K, bool, Dynamic)>>(iter: T) -> Self {
|
||||
let mut scope = Self::new();
|
||||
scope.extend(iter);
|
||||
scope
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user