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.
|
* `#[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.
|
* 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
|
Enhancements
|
||||||
------------
|
------------
|
||||||
|
98
src/ast.rs
98
src/ast.rs
@ -749,6 +749,86 @@ impl AST {
|
|||||||
self.body = StmtBlock::empty();
|
self.body = StmtBlock::empty();
|
||||||
self
|
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).
|
/// Recursively walk the [`AST`], including function bodies (if any).
|
||||||
/// Return `false` from the callback to terminate the walk.
|
/// Return `false` from the callback to terminate the walk.
|
||||||
#[cfg(not(feature = "internals"))]
|
#[cfg(not(feature = "internals"))]
|
||||||
@ -835,7 +915,7 @@ impl AsRef<Module> for AST {
|
|||||||
pub struct Ident {
|
pub struct Ident {
|
||||||
/// Identifier name.
|
/// Identifier name.
|
||||||
pub name: Identifier,
|
pub name: Identifier,
|
||||||
/// Declaration position.
|
/// Position.
|
||||||
pub pos: 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`].
|
/// _(internals)_ An [`AST`] node, consisting of either an [`Expr`] or a [`Stmt`].
|
||||||
/// Exported under the `internals` feature only.
|
/// 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.
|
/// 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.
|
/// statements are internally pure.
|
||||||
#[inline]
|
#[inline]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
|
39
src/scope.rs
39
src/scope.rs
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
use crate::dynamic::{AccessMode, Variant};
|
use crate::dynamic::{AccessMode, Variant};
|
||||||
use crate::{Dynamic, Identifier, StaticVec};
|
use crate::{Dynamic, Identifier, StaticVec};
|
||||||
|
use std::iter::FromIterator;
|
||||||
#[cfg(feature = "no_std")]
|
#[cfg(feature = "no_std")]
|
||||||
use std::prelude::v1::*;
|
use std::prelude::v1::*;
|
||||||
use std::{borrow::Cow, iter::Extend};
|
use std::{borrow::Cow, iter::Extend};
|
||||||
@ -592,8 +593,42 @@ impl<'a, K: Into<Cow<'a, str>>> Extend<(K, Dynamic)> for Scope<'a> {
|
|||||||
#[inline]
|
#[inline]
|
||||||
fn extend<T: IntoIterator<Item = (K, Dynamic)>>(&mut self, iter: T) {
|
fn extend<T: IntoIterator<Item = (K, Dynamic)>>(&mut self, iter: T) {
|
||||||
iter.into_iter().for_each(|(name, value)| {
|
iter.into_iter().for_each(|(name, value)| {
|
||||||
self.names.push((name.into(), None));
|
self.push_dynamic_value(name, AccessMode::ReadWrite, value);
|
||||||
self.values.push(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