2021-12-17 09:07:13 +01:00
|
|
|
//! Module defining the AST (abstract syntax tree).
|
|
|
|
|
2022-02-25 04:42:59 +01:00
|
|
|
use super::{ASTFlags, Expr, FnAccess, Stmt, StmtBlock, StmtBlockContainer};
|
2022-02-16 05:57:26 +01:00
|
|
|
use crate::{Dynamic, FnNamespace, Identifier, Position};
|
2021-12-17 09:07:13 +01:00
|
|
|
#[cfg(feature = "no_std")]
|
|
|
|
use std::prelude::v1::*;
|
|
|
|
use std::{
|
2022-01-31 06:38:27 +01:00
|
|
|
fmt,
|
2021-12-17 09:07:13 +01:00
|
|
|
hash::Hash,
|
|
|
|
ops::{Add, AddAssign},
|
2022-04-13 04:35:10 +02:00
|
|
|
ptr,
|
2021-12-17 09:07:13 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
/// Compiled AST (abstract syntax tree) of a Rhai script.
|
|
|
|
///
|
|
|
|
/// # Thread Safety
|
|
|
|
///
|
|
|
|
/// Currently, [`AST`] is neither `Send` nor `Sync`. Turn on the `sync` feature to make it `Send + Sync`.
|
2022-01-31 06:38:27 +01:00
|
|
|
#[derive(Clone)]
|
2021-12-17 09:07:13 +01:00
|
|
|
pub struct AST {
|
|
|
|
/// Source of the [`AST`].
|
2022-01-01 10:20:00 +01:00
|
|
|
/// No source if string is empty.
|
|
|
|
source: Identifier,
|
2021-12-17 09:07:13 +01:00
|
|
|
/// Global statements.
|
|
|
|
body: StmtBlock,
|
|
|
|
/// Script-defined functions.
|
|
|
|
#[cfg(not(feature = "no_function"))]
|
2022-01-31 06:38:27 +01:00
|
|
|
lib: crate::Shared<crate::Module>,
|
2021-12-17 09:07:13 +01:00
|
|
|
/// Embedded module resolver, if any.
|
|
|
|
#[cfg(not(feature = "no_module"))]
|
|
|
|
resolver: Option<crate::Shared<crate::module::resolvers::StaticModuleResolver>>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Default for AST {
|
|
|
|
#[inline(always)]
|
|
|
|
fn default() -> Self {
|
|
|
|
Self::empty()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-01-31 06:38:27 +01:00
|
|
|
impl fmt::Debug for AST {
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
|
|
let mut fp = f.debug_struct("AST");
|
|
|
|
|
|
|
|
if !self.source.is_empty() {
|
|
|
|
fp.field("source: ", &self.source);
|
|
|
|
}
|
|
|
|
#[cfg(not(feature = "no_module"))]
|
|
|
|
if let Some(ref resolver) = self.resolver {
|
|
|
|
fp.field("resolver: ", resolver);
|
|
|
|
}
|
|
|
|
|
|
|
|
fp.field("body", &self.body.as_slice());
|
|
|
|
|
|
|
|
#[cfg(not(feature = "no_function"))]
|
|
|
|
if !self.lib.is_empty() {
|
2022-02-08 02:02:15 +01:00
|
|
|
for (.., ref fn_def) in self.lib.iter_script_fn() {
|
2022-01-31 06:38:27 +01:00
|
|
|
let sig = fn_def.to_string();
|
|
|
|
fp.field(&sig, &fn_def.body.as_slice());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
fp.finish()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-12-17 09:07:13 +01:00
|
|
|
impl AST {
|
|
|
|
/// Create a new [`AST`].
|
|
|
|
#[cfg(not(feature = "internals"))]
|
|
|
|
#[inline(always)]
|
|
|
|
#[must_use]
|
|
|
|
pub(crate) fn new(
|
|
|
|
statements: impl IntoIterator<Item = Stmt>,
|
|
|
|
#[cfg(not(feature = "no_function"))] functions: impl Into<crate::Shared<crate::Module>>,
|
|
|
|
) -> Self {
|
|
|
|
Self {
|
2022-01-01 10:20:00 +01:00
|
|
|
source: Identifier::new_const(),
|
2022-02-04 06:31:33 +01:00
|
|
|
body: StmtBlock::new(statements, Position::NONE, Position::NONE),
|
2021-12-17 09:07:13 +01:00
|
|
|
#[cfg(not(feature = "no_function"))]
|
2022-01-31 06:38:27 +01:00
|
|
|
lib: functions.into(),
|
2021-12-17 09:07:13 +01:00
|
|
|
#[cfg(not(feature = "no_module"))]
|
|
|
|
resolver: None,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/// _(internals)_ Create a new [`AST`].
|
|
|
|
/// Exported under the `internals` feature only.
|
|
|
|
#[cfg(feature = "internals")]
|
|
|
|
#[inline(always)]
|
|
|
|
#[must_use]
|
|
|
|
pub fn new(
|
|
|
|
statements: impl IntoIterator<Item = Stmt>,
|
|
|
|
#[cfg(not(feature = "no_function"))] functions: impl Into<crate::Shared<crate::Module>>,
|
|
|
|
) -> Self {
|
|
|
|
Self {
|
2022-01-01 10:20:00 +01:00
|
|
|
source: Identifier::new_const(),
|
2022-02-04 05:04:33 +01:00
|
|
|
body: StmtBlock::new(statements, Position::NONE, Position::NONE),
|
2021-12-17 09:07:13 +01:00
|
|
|
#[cfg(not(feature = "no_function"))]
|
2022-01-31 06:38:27 +01:00
|
|
|
lib: functions.into(),
|
2021-12-17 09:07:13 +01:00
|
|
|
#[cfg(not(feature = "no_module"))]
|
|
|
|
resolver: None,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/// Create a new [`AST`] with a source name.
|
|
|
|
#[cfg(not(feature = "internals"))]
|
|
|
|
#[inline(always)]
|
|
|
|
#[must_use]
|
|
|
|
pub(crate) fn new_with_source(
|
|
|
|
statements: impl IntoIterator<Item = Stmt>,
|
|
|
|
#[cfg(not(feature = "no_function"))] functions: impl Into<crate::Shared<crate::Module>>,
|
|
|
|
source: impl Into<Identifier>,
|
|
|
|
) -> Self {
|
|
|
|
let mut ast = Self::new(
|
|
|
|
statements,
|
|
|
|
#[cfg(not(feature = "no_function"))]
|
|
|
|
functions,
|
|
|
|
);
|
|
|
|
ast.set_source(source);
|
|
|
|
ast
|
|
|
|
}
|
|
|
|
/// _(internals)_ Create a new [`AST`] with a source name.
|
|
|
|
/// Exported under the `internals` feature only.
|
|
|
|
#[cfg(feature = "internals")]
|
|
|
|
#[inline(always)]
|
|
|
|
#[must_use]
|
|
|
|
pub fn new_with_source(
|
|
|
|
statements: impl IntoIterator<Item = Stmt>,
|
|
|
|
#[cfg(not(feature = "no_function"))] functions: impl Into<crate::Shared<crate::Module>>,
|
|
|
|
source: impl Into<Identifier>,
|
|
|
|
) -> Self {
|
|
|
|
let mut ast = Self::new(
|
|
|
|
statements,
|
|
|
|
#[cfg(not(feature = "no_function"))]
|
|
|
|
functions,
|
|
|
|
);
|
|
|
|
ast.set_source(source);
|
|
|
|
ast
|
|
|
|
}
|
|
|
|
/// Create an empty [`AST`].
|
|
|
|
#[inline]
|
|
|
|
#[must_use]
|
|
|
|
pub fn empty() -> Self {
|
|
|
|
Self {
|
2022-01-01 10:20:00 +01:00
|
|
|
source: Identifier::new_const(),
|
2021-12-17 09:07:13 +01:00
|
|
|
body: StmtBlock::NONE,
|
|
|
|
#[cfg(not(feature = "no_function"))]
|
2022-01-31 06:38:27 +01:00
|
|
|
lib: crate::Module::new().into(),
|
2021-12-17 09:07:13 +01:00
|
|
|
#[cfg(not(feature = "no_module"))]
|
|
|
|
resolver: None,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/// Get the source, if any.
|
|
|
|
#[inline(always)]
|
|
|
|
#[must_use]
|
|
|
|
pub fn source(&self) -> Option<&str> {
|
2022-03-20 14:58:43 +01:00
|
|
|
if self.source.is_empty() {
|
|
|
|
None
|
|
|
|
} else {
|
|
|
|
Some(self.source.as_str())
|
2022-01-01 10:20:00 +01:00
|
|
|
}
|
2021-12-17 09:07:13 +01:00
|
|
|
}
|
|
|
|
/// Get a reference to the source.
|
|
|
|
#[inline(always)]
|
|
|
|
#[must_use]
|
2022-01-01 10:20:00 +01:00
|
|
|
pub(crate) fn source_raw(&self) -> &Identifier {
|
|
|
|
&self.source
|
2021-12-17 09:07:13 +01:00
|
|
|
}
|
|
|
|
/// Set the source.
|
|
|
|
#[inline]
|
|
|
|
pub fn set_source(&mut self, source: impl Into<Identifier>) -> &mut Self {
|
|
|
|
let source = source.into();
|
|
|
|
#[cfg(not(feature = "no_function"))]
|
2022-01-31 06:38:27 +01:00
|
|
|
crate::Shared::get_mut(&mut self.lib)
|
2021-12-17 09:07:13 +01:00
|
|
|
.as_mut()
|
|
|
|
.map(|m| m.set_id(source.clone()));
|
2022-01-01 10:20:00 +01:00
|
|
|
self.source = source;
|
2021-12-17 09:07:13 +01:00
|
|
|
self
|
|
|
|
}
|
|
|
|
/// Clear the source.
|
|
|
|
#[inline(always)]
|
|
|
|
pub fn clear_source(&mut self) -> &mut Self {
|
2022-01-01 10:20:00 +01:00
|
|
|
self.source.clear();
|
2021-12-17 09:07:13 +01:00
|
|
|
self
|
|
|
|
}
|
|
|
|
/// Get the statements.
|
|
|
|
#[cfg(not(feature = "internals"))]
|
|
|
|
#[inline(always)]
|
|
|
|
#[must_use]
|
|
|
|
pub(crate) fn statements(&self) -> &[Stmt] {
|
|
|
|
self.body.statements()
|
|
|
|
}
|
|
|
|
/// _(internals)_ Get the statements.
|
|
|
|
/// Exported under the `internals` feature only.
|
|
|
|
#[cfg(feature = "internals")]
|
|
|
|
#[inline(always)]
|
|
|
|
#[must_use]
|
|
|
|
pub fn statements(&self) -> &[Stmt] {
|
|
|
|
self.body.statements()
|
|
|
|
}
|
|
|
|
/// Extract the statements.
|
|
|
|
#[allow(dead_code)]
|
|
|
|
#[inline(always)]
|
|
|
|
#[must_use]
|
2022-02-16 05:57:26 +01:00
|
|
|
pub(crate) fn take_statements(&mut self) -> StmtBlockContainer {
|
2021-12-17 09:07:13 +01:00
|
|
|
self.body.take_statements()
|
|
|
|
}
|
|
|
|
/// Does this [`AST`] contain script-defined functions?
|
|
|
|
///
|
|
|
|
/// Not available under `no_function`.
|
|
|
|
#[cfg(not(feature = "no_function"))]
|
|
|
|
#[inline(always)]
|
|
|
|
#[must_use]
|
|
|
|
pub fn has_functions(&self) -> bool {
|
2022-01-31 06:38:27 +01:00
|
|
|
!self.lib.is_empty()
|
2021-12-17 09:07:13 +01:00
|
|
|
}
|
|
|
|
/// Get the internal shared [`Module`][crate::Module] containing all script-defined functions.
|
|
|
|
#[cfg(not(feature = "internals"))]
|
|
|
|
#[cfg(not(feature = "no_function"))]
|
|
|
|
#[inline(always)]
|
|
|
|
#[must_use]
|
|
|
|
pub(crate) fn shared_lib(&self) -> &crate::Shared<crate::Module> {
|
2022-01-31 06:38:27 +01:00
|
|
|
&self.lib
|
2021-12-17 09:07:13 +01:00
|
|
|
}
|
|
|
|
/// _(internals)_ Get the internal shared [`Module`][crate::Module] containing all script-defined functions.
|
|
|
|
/// Exported under the `internals` feature only.
|
|
|
|
///
|
|
|
|
/// Not available under `no_function`.
|
|
|
|
#[cfg(feature = "internals")]
|
|
|
|
#[cfg(not(feature = "no_function"))]
|
|
|
|
#[inline(always)]
|
|
|
|
#[must_use]
|
|
|
|
pub fn shared_lib(&self) -> &crate::Shared<crate::Module> {
|
2022-01-31 06:38:27 +01:00
|
|
|
&self.lib
|
2021-12-17 09:07:13 +01:00
|
|
|
}
|
|
|
|
/// Get the embedded [module resolver][`ModuleResolver`].
|
|
|
|
#[cfg(not(feature = "internals"))]
|
|
|
|
#[cfg(not(feature = "no_module"))]
|
|
|
|
#[inline(always)]
|
|
|
|
#[must_use]
|
|
|
|
pub(crate) fn resolver(
|
|
|
|
&self,
|
|
|
|
) -> Option<&crate::Shared<crate::module::resolvers::StaticModuleResolver>> {
|
|
|
|
self.resolver.as_ref()
|
|
|
|
}
|
|
|
|
/// _(internals)_ Get the embedded [module resolver][crate::ModuleResolver].
|
|
|
|
/// Exported under the `internals` feature only.
|
|
|
|
///
|
|
|
|
/// Not available under `no_module`.
|
|
|
|
#[cfg(feature = "internals")]
|
|
|
|
#[cfg(not(feature = "no_module"))]
|
|
|
|
#[inline(always)]
|
|
|
|
#[must_use]
|
|
|
|
pub fn resolver(
|
|
|
|
&self,
|
|
|
|
) -> Option<&crate::Shared<crate::module::resolvers::StaticModuleResolver>> {
|
|
|
|
self.resolver.as_ref()
|
|
|
|
}
|
|
|
|
/// Set the embedded [module resolver][`ModuleResolver`].
|
|
|
|
#[cfg(not(feature = "no_module"))]
|
|
|
|
#[inline(always)]
|
|
|
|
pub(crate) fn set_resolver(
|
|
|
|
&mut self,
|
|
|
|
resolver: impl Into<crate::Shared<crate::module::resolvers::StaticModuleResolver>>,
|
|
|
|
) -> &mut Self {
|
|
|
|
self.resolver = Some(resolver.into());
|
|
|
|
self
|
|
|
|
}
|
|
|
|
/// Clone the [`AST`]'s functions into a new [`AST`].
|
|
|
|
/// No statements are cloned.
|
|
|
|
///
|
|
|
|
/// Not available under `no_function`.
|
|
|
|
///
|
|
|
|
/// This operation is cheap because functions are shared.
|
|
|
|
#[cfg(not(feature = "no_function"))]
|
|
|
|
#[inline(always)]
|
|
|
|
#[must_use]
|
|
|
|
pub fn clone_functions_only(&self) -> Self {
|
|
|
|
self.clone_functions_only_filtered(|_, _, _, _, _| true)
|
|
|
|
}
|
|
|
|
/// Clone the [`AST`]'s functions into a new [`AST`] based on a filter predicate.
|
|
|
|
/// No statements are cloned.
|
|
|
|
///
|
|
|
|
/// Not available under `no_function`.
|
|
|
|
///
|
|
|
|
/// This operation is cheap because functions are shared.
|
|
|
|
#[cfg(not(feature = "no_function"))]
|
|
|
|
#[inline]
|
|
|
|
#[must_use]
|
|
|
|
pub fn clone_functions_only_filtered(
|
|
|
|
&self,
|
|
|
|
filter: impl Fn(FnNamespace, FnAccess, bool, &str, usize) -> bool,
|
|
|
|
) -> Self {
|
2022-01-31 06:38:27 +01:00
|
|
|
let mut lib = crate::Module::new();
|
|
|
|
lib.merge_filtered(&self.lib, &filter);
|
2021-12-17 09:07:13 +01:00
|
|
|
Self {
|
|
|
|
source: self.source.clone(),
|
|
|
|
body: StmtBlock::NONE,
|
2022-01-31 06:38:27 +01:00
|
|
|
lib: lib.into(),
|
2021-12-17 09:07:13 +01:00
|
|
|
#[cfg(not(feature = "no_module"))]
|
|
|
|
resolver: self.resolver.clone(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/// Clone the [`AST`]'s script statements into a new [`AST`].
|
|
|
|
/// No functions are cloned.
|
|
|
|
#[inline(always)]
|
|
|
|
#[must_use]
|
|
|
|
pub fn clone_statements_only(&self) -> Self {
|
|
|
|
Self {
|
|
|
|
source: self.source.clone(),
|
|
|
|
body: self.body.clone(),
|
|
|
|
#[cfg(not(feature = "no_function"))]
|
2022-01-31 06:38:27 +01:00
|
|
|
lib: crate::Module::new().into(),
|
2021-12-17 09:07:13 +01:00
|
|
|
#[cfg(not(feature = "no_module"))]
|
|
|
|
resolver: self.resolver.clone(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/// Merge two [`AST`] into one. Both [`AST`]'s are untouched and a new, merged,
|
|
|
|
/// version is returned.
|
|
|
|
///
|
|
|
|
/// Statements in the second [`AST`] are simply appended to the end of the first _without any processing_.
|
|
|
|
/// Thus, the return value of the first [`AST`] (if using expression-statement syntax) is buried.
|
|
|
|
/// Of course, if the first [`AST`] uses a `return` statement at the end, then
|
|
|
|
/// the second [`AST`] will essentially be dead code.
|
|
|
|
///
|
|
|
|
/// All script-defined functions in the second [`AST`] overwrite similarly-named functions
|
|
|
|
/// in the first [`AST`] with the same number of parameters.
|
|
|
|
///
|
|
|
|
/// # Example
|
|
|
|
///
|
|
|
|
/// ```
|
|
|
|
/// # fn main() -> Result<(), Box<rhai::EvalAltResult>> {
|
|
|
|
/// # #[cfg(not(feature = "no_function"))]
|
|
|
|
/// # {
|
|
|
|
/// use rhai::Engine;
|
|
|
|
///
|
|
|
|
/// let engine = Engine::new();
|
|
|
|
///
|
|
|
|
/// let ast1 = engine.compile("
|
|
|
|
/// fn foo(x) { 42 + x }
|
|
|
|
/// foo(1)
|
|
|
|
/// ")?;
|
|
|
|
///
|
|
|
|
/// let ast2 = engine.compile(r#"
|
|
|
|
/// fn foo(n) { `hello${n}` }
|
|
|
|
/// foo("!")
|
|
|
|
/// "#)?;
|
|
|
|
///
|
|
|
|
/// let ast = ast1.merge(&ast2); // Merge 'ast2' into 'ast1'
|
|
|
|
///
|
|
|
|
/// // Notice that using the '+' operator also works:
|
|
|
|
/// // let ast = &ast1 + &ast2;
|
|
|
|
///
|
|
|
|
/// // 'ast' is essentially:
|
|
|
|
/// //
|
|
|
|
/// // fn foo(n) { `hello${n}` } // <- definition of first 'foo' is overwritten
|
|
|
|
/// // foo(1) // <- notice this will be "hello1" instead of 43,
|
|
|
|
/// // // but it is no longer the return value
|
|
|
|
/// // foo("!") // returns "hello!"
|
|
|
|
///
|
|
|
|
/// // Evaluate it
|
|
|
|
/// assert_eq!(engine.eval_ast::<String>(&ast)?, "hello!");
|
|
|
|
/// # }
|
|
|
|
/// # Ok(())
|
|
|
|
/// # }
|
|
|
|
/// ```
|
|
|
|
#[inline(always)]
|
|
|
|
#[must_use]
|
|
|
|
pub fn merge(&self, other: &Self) -> Self {
|
|
|
|
self.merge_filtered_impl(other, |_, _, _, _, _| true)
|
|
|
|
}
|
|
|
|
/// Combine one [`AST`] with another. The second [`AST`] is consumed.
|
|
|
|
///
|
|
|
|
/// Statements in the second [`AST`] are simply appended to the end of the first _without any processing_.
|
|
|
|
/// Thus, the return value of the first [`AST`] (if using expression-statement syntax) is buried.
|
|
|
|
/// Of course, if the first [`AST`] uses a `return` statement at the end, then
|
|
|
|
/// the second [`AST`] will essentially be dead code.
|
|
|
|
///
|
|
|
|
/// All script-defined functions in the second [`AST`] overwrite similarly-named functions
|
|
|
|
/// in the first [`AST`] with the same number of parameters.
|
|
|
|
///
|
|
|
|
/// # Example
|
|
|
|
///
|
|
|
|
/// ```
|
|
|
|
/// # fn main() -> Result<(), Box<rhai::EvalAltResult>> {
|
|
|
|
/// # #[cfg(not(feature = "no_function"))]
|
|
|
|
/// # {
|
|
|
|
/// use rhai::Engine;
|
|
|
|
///
|
|
|
|
/// let engine = Engine::new();
|
|
|
|
///
|
|
|
|
/// let mut ast1 = engine.compile("
|
|
|
|
/// fn foo(x) { 42 + x }
|
|
|
|
/// foo(1)
|
|
|
|
/// ")?;
|
|
|
|
///
|
|
|
|
/// let ast2 = engine.compile(r#"
|
|
|
|
/// fn foo(n) { `hello${n}` }
|
|
|
|
/// foo("!")
|
|
|
|
/// "#)?;
|
|
|
|
///
|
|
|
|
/// ast1.combine(ast2); // Combine 'ast2' into 'ast1'
|
|
|
|
///
|
|
|
|
/// // Notice that using the '+=' operator also works:
|
|
|
|
/// // ast1 += ast2;
|
|
|
|
///
|
|
|
|
/// // 'ast1' is essentially:
|
|
|
|
/// //
|
|
|
|
/// // fn foo(n) { `hello${n}` } // <- definition of first 'foo' is overwritten
|
|
|
|
/// // foo(1) // <- notice this will be "hello1" instead of 43,
|
|
|
|
/// // // but it is no longer the return value
|
|
|
|
/// // foo("!") // returns "hello!"
|
|
|
|
///
|
|
|
|
/// // Evaluate it
|
|
|
|
/// assert_eq!(engine.eval_ast::<String>(&ast1)?, "hello!");
|
|
|
|
/// # }
|
|
|
|
/// # Ok(())
|
|
|
|
/// # }
|
|
|
|
/// ```
|
|
|
|
#[inline(always)]
|
|
|
|
pub fn combine(&mut self, other: Self) -> &mut Self {
|
|
|
|
self.combine_filtered_impl(other, |_, _, _, _, _| true)
|
|
|
|
}
|
|
|
|
/// Merge two [`AST`] into one. Both [`AST`]'s are untouched and a new, merged, version
|
|
|
|
/// is returned.
|
|
|
|
///
|
|
|
|
/// Not available under `no_function`.
|
|
|
|
///
|
|
|
|
/// Statements in the second [`AST`] are simply appended to the end of the first _without any processing_.
|
|
|
|
/// Thus, the return value of the first [`AST`] (if using expression-statement syntax) is buried.
|
|
|
|
/// Of course, if the first [`AST`] uses a `return` statement at the end, then
|
|
|
|
/// the second [`AST`] will essentially be dead code.
|
|
|
|
///
|
|
|
|
/// All script-defined functions in the second [`AST`] are first selected based on a filter
|
|
|
|
/// predicate, then overwrite similarly-named functions in the first [`AST`] with the
|
|
|
|
/// same number of parameters.
|
|
|
|
///
|
|
|
|
/// # Example
|
|
|
|
///
|
|
|
|
/// ```
|
|
|
|
/// # fn main() -> Result<(), Box<rhai::EvalAltResult>> {
|
|
|
|
/// use rhai::Engine;
|
|
|
|
///
|
|
|
|
/// let engine = Engine::new();
|
|
|
|
///
|
|
|
|
/// let ast1 = engine.compile("
|
|
|
|
/// fn foo(x) { 42 + x }
|
|
|
|
/// foo(1)
|
|
|
|
/// ")?;
|
|
|
|
///
|
|
|
|
/// let ast2 = engine.compile(r#"
|
|
|
|
/// fn foo(n) { `hello${n}` }
|
|
|
|
/// fn error() { 0 }
|
|
|
|
/// foo("!")
|
|
|
|
/// "#)?;
|
|
|
|
///
|
2022-02-08 02:46:14 +01:00
|
|
|
/// // Merge 'ast2', picking only 'error()' but not 'foo(..)', into 'ast1'
|
2021-12-17 09:07:13 +01:00
|
|
|
/// let ast = ast1.merge_filtered(&ast2, |_, _, script, name, params|
|
|
|
|
/// script && name == "error" && params == 0);
|
|
|
|
///
|
|
|
|
/// // 'ast' is essentially:
|
|
|
|
/// //
|
|
|
|
/// // fn foo(n) { 42 + n } // <- definition of 'ast1::foo' is not overwritten
|
|
|
|
/// // // because 'ast2::foo' is filtered away
|
|
|
|
/// // foo(1) // <- notice this will be 43 instead of "hello1",
|
|
|
|
/// // // but it is no longer the return value
|
|
|
|
/// // fn error() { 0 } // <- this function passes the filter and is merged
|
|
|
|
/// // foo("!") // <- returns "42!"
|
|
|
|
///
|
|
|
|
/// // Evaluate it
|
|
|
|
/// assert_eq!(engine.eval_ast::<String>(&ast)?, "42!");
|
|
|
|
/// # Ok(())
|
|
|
|
/// # }
|
|
|
|
/// ```
|
|
|
|
#[cfg(not(feature = "no_function"))]
|
|
|
|
#[inline(always)]
|
|
|
|
#[must_use]
|
|
|
|
pub fn merge_filtered(
|
|
|
|
&self,
|
|
|
|
other: &Self,
|
|
|
|
filter: impl Fn(FnNamespace, FnAccess, bool, &str, usize) -> bool,
|
|
|
|
) -> Self {
|
|
|
|
self.merge_filtered_impl(other, filter)
|
|
|
|
}
|
|
|
|
/// Merge two [`AST`] into one. Both [`AST`]'s are untouched and a new, merged, version
|
|
|
|
/// is returned.
|
|
|
|
#[inline]
|
|
|
|
#[must_use]
|
|
|
|
fn merge_filtered_impl(
|
|
|
|
&self,
|
|
|
|
other: &Self,
|
|
|
|
_filter: impl Fn(FnNamespace, FnAccess, bool, &str, usize) -> bool,
|
|
|
|
) -> Self {
|
|
|
|
let merged = match (self.body.is_empty(), other.body.is_empty()) {
|
|
|
|
(false, false) => {
|
|
|
|
let mut body = self.body.clone();
|
|
|
|
body.extend(other.body.iter().cloned());
|
|
|
|
body
|
|
|
|
}
|
|
|
|
(false, true) => self.body.clone(),
|
|
|
|
(true, false) => other.body.clone(),
|
|
|
|
(true, true) => StmtBlock::NONE,
|
|
|
|
};
|
|
|
|
|
|
|
|
#[cfg(not(feature = "no_function"))]
|
2022-01-31 06:38:27 +01:00
|
|
|
let lib = {
|
|
|
|
let mut lib = self.lib.as_ref().clone();
|
|
|
|
lib.merge_filtered(&other.lib, &_filter);
|
|
|
|
lib
|
2021-12-17 09:07:13 +01:00
|
|
|
};
|
|
|
|
|
2022-01-01 10:20:00 +01:00
|
|
|
if !other.source.is_empty() {
|
2021-12-17 09:07:13 +01:00
|
|
|
Self::new_with_source(
|
|
|
|
merged,
|
|
|
|
#[cfg(not(feature = "no_function"))]
|
2022-01-31 06:38:27 +01:00
|
|
|
lib,
|
2022-01-01 10:20:00 +01:00
|
|
|
other.source.clone(),
|
2021-12-17 09:07:13 +01:00
|
|
|
)
|
|
|
|
} else {
|
|
|
|
Self::new(
|
|
|
|
merged,
|
|
|
|
#[cfg(not(feature = "no_function"))]
|
2022-01-31 06:38:27 +01:00
|
|
|
lib,
|
2021-12-17 09:07:13 +01:00
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/// Combine one [`AST`] with another. The second [`AST`] is consumed.
|
|
|
|
///
|
|
|
|
/// Not available under `no_function`.
|
|
|
|
///
|
|
|
|
/// Statements in the second [`AST`] are simply appended to the end of the first _without any processing_.
|
|
|
|
/// Thus, the return value of the first [`AST`] (if using expression-statement syntax) is buried.
|
|
|
|
/// Of course, if the first [`AST`] uses a `return` statement at the end, then
|
|
|
|
/// the second [`AST`] will essentially be dead code.
|
|
|
|
///
|
|
|
|
/// All script-defined functions in the second [`AST`] are first selected based on a filter
|
|
|
|
/// predicate, then overwrite similarly-named functions in the first [`AST`] with the
|
|
|
|
/// same number of parameters.
|
|
|
|
///
|
|
|
|
/// # Example
|
|
|
|
///
|
|
|
|
/// ```
|
|
|
|
/// # fn main() -> Result<(), Box<rhai::EvalAltResult>> {
|
|
|
|
/// use rhai::Engine;
|
|
|
|
///
|
|
|
|
/// let engine = Engine::new();
|
|
|
|
///
|
|
|
|
/// let mut ast1 = engine.compile("
|
|
|
|
/// fn foo(x) { 42 + x }
|
|
|
|
/// foo(1)
|
|
|
|
/// ")?;
|
|
|
|
///
|
|
|
|
/// let ast2 = engine.compile(r#"
|
|
|
|
/// fn foo(n) { `hello${n}` }
|
|
|
|
/// fn error() { 0 }
|
|
|
|
/// foo("!")
|
|
|
|
/// "#)?;
|
|
|
|
///
|
2022-02-08 02:46:14 +01:00
|
|
|
/// // Combine 'ast2', picking only 'error()' but not 'foo(..)', into 'ast1'
|
2021-12-17 09:07:13 +01:00
|
|
|
/// ast1.combine_filtered(ast2, |_, _, script, name, params|
|
|
|
|
/// script && name == "error" && params == 0);
|
|
|
|
///
|
|
|
|
/// // 'ast1' is essentially:
|
|
|
|
/// //
|
|
|
|
/// // fn foo(n) { 42 + n } // <- definition of 'ast1::foo' is not overwritten
|
|
|
|
/// // // because 'ast2::foo' is filtered away
|
|
|
|
/// // foo(1) // <- notice this will be 43 instead of "hello1",
|
|
|
|
/// // // but it is no longer the return value
|
|
|
|
/// // fn error() { 0 } // <- this function passes the filter and is merged
|
|
|
|
/// // foo("!") // <- returns "42!"
|
|
|
|
///
|
|
|
|
/// // Evaluate it
|
|
|
|
/// assert_eq!(engine.eval_ast::<String>(&ast1)?, "42!");
|
|
|
|
/// # Ok(())
|
|
|
|
/// # }
|
|
|
|
/// ```
|
|
|
|
#[cfg(not(feature = "no_function"))]
|
|
|
|
#[inline(always)]
|
|
|
|
pub fn combine_filtered(
|
|
|
|
&mut self,
|
|
|
|
other: Self,
|
|
|
|
filter: impl Fn(FnNamespace, FnAccess, bool, &str, usize) -> bool,
|
|
|
|
) -> &mut Self {
|
|
|
|
self.combine_filtered_impl(other, filter)
|
|
|
|
}
|
|
|
|
/// Combine one [`AST`] with another. The second [`AST`] is consumed.
|
|
|
|
#[inline]
|
|
|
|
fn combine_filtered_impl(
|
|
|
|
&mut self,
|
|
|
|
other: Self,
|
|
|
|
_filter: impl Fn(FnNamespace, FnAccess, bool, &str, usize) -> bool,
|
|
|
|
) -> &mut Self {
|
|
|
|
self.body.extend(other.body.into_iter());
|
|
|
|
|
|
|
|
#[cfg(not(feature = "no_function"))]
|
2022-01-31 06:38:27 +01:00
|
|
|
if !other.lib.is_empty() {
|
|
|
|
crate::func::native::shared_make_mut(&mut self.lib)
|
|
|
|
.merge_filtered(&other.lib, &_filter);
|
2021-12-17 09:07:13 +01:00
|
|
|
}
|
|
|
|
self
|
|
|
|
}
|
|
|
|
/// Filter out the functions, retaining only some based on a filter predicate.
|
|
|
|
///
|
|
|
|
/// Not available under `no_function`.
|
|
|
|
///
|
|
|
|
/// # Example
|
|
|
|
///
|
|
|
|
/// ```
|
|
|
|
/// # fn main() -> Result<(), Box<rhai::EvalAltResult>> {
|
|
|
|
/// # #[cfg(not(feature = "no_function"))]
|
|
|
|
/// # {
|
|
|
|
/// use rhai::Engine;
|
|
|
|
///
|
|
|
|
/// let engine = Engine::new();
|
|
|
|
///
|
|
|
|
/// let mut ast = engine.compile(r#"
|
|
|
|
/// fn foo(n) { n + 1 }
|
|
|
|
/// fn bar() { print("hello"); }
|
|
|
|
/// "#)?;
|
|
|
|
///
|
2022-02-08 02:46:14 +01:00
|
|
|
/// // Remove all functions except 'foo(..)'
|
2021-12-17 09:07:13 +01:00
|
|
|
/// ast.retain_functions(|_, _, name, params| name == "foo" && params == 1);
|
|
|
|
/// # }
|
|
|
|
/// # Ok(())
|
|
|
|
/// # }
|
|
|
|
/// ```
|
|
|
|
#[cfg(not(feature = "no_function"))]
|
|
|
|
#[inline]
|
|
|
|
pub fn retain_functions(
|
|
|
|
&mut self,
|
|
|
|
filter: impl Fn(FnNamespace, FnAccess, &str, usize) -> bool,
|
|
|
|
) -> &mut Self {
|
2022-01-31 06:38:27 +01:00
|
|
|
if !self.lib.is_empty() {
|
|
|
|
crate::func::native::shared_make_mut(&mut self.lib).retain_script_functions(filter);
|
2021-12-17 09:07:13 +01:00
|
|
|
}
|
|
|
|
self
|
|
|
|
}
|
2022-01-31 06:38:27 +01:00
|
|
|
/// _(internals)_ Iterate through all function definitions.
|
|
|
|
/// Exported under the `internals` feature only.
|
|
|
|
///
|
|
|
|
/// Not available under `no_function`.
|
|
|
|
#[cfg(feature = "internals")]
|
|
|
|
#[cfg(not(feature = "no_function"))]
|
|
|
|
#[inline]
|
|
|
|
pub fn iter_fn_def(&self) -> impl Iterator<Item = &super::ScriptFnDef> {
|
|
|
|
self.lib
|
|
|
|
.iter_script_fn()
|
2022-02-08 02:02:15 +01:00
|
|
|
.map(|(.., fn_def)| fn_def.as_ref())
|
2022-01-31 06:38:27 +01:00
|
|
|
}
|
2021-12-17 09:07:13 +01:00
|
|
|
/// Iterate through all function definitions.
|
|
|
|
///
|
|
|
|
/// Not available under `no_function`.
|
2022-01-31 06:38:27 +01:00
|
|
|
#[cfg(not(feature = "internals"))]
|
2021-12-17 09:07:13 +01:00
|
|
|
#[cfg(not(feature = "no_function"))]
|
|
|
|
#[allow(dead_code)]
|
|
|
|
#[inline]
|
|
|
|
pub(crate) fn iter_fn_def(&self) -> impl Iterator<Item = &super::ScriptFnDef> {
|
2022-01-31 06:38:27 +01:00
|
|
|
self.lib
|
2021-12-17 09:07:13 +01:00
|
|
|
.iter_script_fn()
|
2022-02-08 02:02:15 +01:00
|
|
|
.map(|(.., fn_def)| fn_def.as_ref())
|
2021-12-17 09:07:13 +01:00
|
|
|
}
|
|
|
|
/// Iterate through all function definitions.
|
|
|
|
///
|
|
|
|
/// Not available under `no_function`.
|
|
|
|
#[cfg(not(feature = "no_function"))]
|
|
|
|
#[inline]
|
|
|
|
pub fn iter_functions<'a>(&'a self) -> impl Iterator<Item = super::ScriptFnMetadata> + 'a {
|
2022-01-31 06:38:27 +01:00
|
|
|
self.lib
|
2021-12-17 09:07:13 +01:00
|
|
|
.iter_script_fn()
|
2022-02-08 02:02:15 +01:00
|
|
|
.map(|(.., fn_def)| fn_def.as_ref().into())
|
2021-12-17 09:07:13 +01:00
|
|
|
}
|
|
|
|
/// Clear all function definitions in the [`AST`].
|
|
|
|
///
|
|
|
|
/// Not available under `no_function`.
|
|
|
|
#[cfg(not(feature = "no_function"))]
|
|
|
|
#[inline(always)]
|
|
|
|
pub fn clear_functions(&mut self) -> &mut Self {
|
2022-01-31 06:38:27 +01:00
|
|
|
self.lib = crate::Module::new().into();
|
2021-12-17 09:07:13 +01:00
|
|
|
self
|
|
|
|
}
|
|
|
|
/// Clear all statements in the [`AST`], leaving only function definitions.
|
|
|
|
#[inline(always)]
|
|
|
|
pub fn clear_statements(&mut self) -> &mut Self {
|
|
|
|
self.body = StmtBlock::NONE;
|
|
|
|
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()));
|
|
|
|
///
|
|
|
|
/// # #[cfg(not(feature = "no_optimize"))]
|
|
|
|
/// 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()));
|
|
|
|
///
|
|
|
|
/// # #[cfg(not(feature = "no_optimize"))]
|
|
|
|
/// 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();
|
|
|
|
///
|
|
|
|
/// # #[cfg(not(feature = "no_optimize"))]
|
|
|
|
/// 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 {
|
2022-02-16 10:51:14 +01:00
|
|
|
Stmt::Var(x, options, ..)
|
2022-02-25 04:42:59 +01:00
|
|
|
if options.contains(ASTFlags::CONSTANT) && include_constants
|
|
|
|
|| !options.contains(ASTFlags::CONSTANT) && include_variables =>
|
2021-12-17 09:07:13 +01:00
|
|
|
{
|
2022-02-18 08:04:46 +01:00
|
|
|
let (name, expr, ..) = x.as_ref();
|
2021-12-17 09:07:13 +01:00
|
|
|
if let Some(value) = expr.get_literal_value() {
|
2022-02-25 04:42:59 +01:00
|
|
|
Some((name.as_str(), options.contains(ASTFlags::CONSTANT), value))
|
2021-12-17 09:07:13 +01:00
|
|
|
} 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"))]
|
|
|
|
#[cfg(not(feature = "no_module"))]
|
|
|
|
#[inline]
|
|
|
|
pub(crate) fn walk(&self, on_node: &mut impl FnMut(&[ASTNode]) -> bool) -> bool {
|
|
|
|
let path = &mut Vec::new();
|
|
|
|
|
|
|
|
for stmt in self.statements() {
|
|
|
|
if !stmt.walk(path, on_node) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#[cfg(not(feature = "no_function"))]
|
|
|
|
for stmt in self.iter_fn_def().flat_map(|f| f.body.iter()) {
|
|
|
|
if !stmt.walk(path, on_node) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
true
|
|
|
|
}
|
|
|
|
/// _(internals)_ Recursively walk the [`AST`], including function bodies (if any).
|
|
|
|
/// Return `false` from the callback to terminate the walk.
|
|
|
|
/// Exported under the `internals` feature only.
|
|
|
|
#[cfg(feature = "internals")]
|
|
|
|
#[inline]
|
|
|
|
pub fn walk(&self, on_node: &mut impl FnMut(&[ASTNode]) -> bool) -> bool {
|
|
|
|
let path = &mut Vec::new();
|
|
|
|
|
|
|
|
for stmt in self.statements() {
|
|
|
|
if !stmt.walk(path, on_node) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#[cfg(not(feature = "no_function"))]
|
|
|
|
for stmt in self.iter_fn_def().flat_map(|f| f.body.iter()) {
|
|
|
|
if !stmt.walk(path, on_node) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<A: AsRef<AST>> Add<A> for &AST {
|
|
|
|
type Output = AST;
|
|
|
|
|
|
|
|
#[inline(always)]
|
|
|
|
fn add(self, rhs: A) -> Self::Output {
|
|
|
|
self.merge(rhs.as_ref())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<A: Into<AST>> AddAssign<A> for AST {
|
|
|
|
#[inline(always)]
|
|
|
|
fn add_assign(&mut self, rhs: A) {
|
|
|
|
self.combine(rhs.into());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl AsRef<[Stmt]> for AST {
|
|
|
|
#[inline(always)]
|
|
|
|
fn as_ref(&self) -> &[Stmt] {
|
|
|
|
self.statements()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(not(feature = "no_function"))]
|
|
|
|
impl AsRef<crate::Module> for AST {
|
|
|
|
#[inline(always)]
|
|
|
|
fn as_ref(&self) -> &crate::Module {
|
|
|
|
self.shared_lib().as_ref()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(not(feature = "no_function"))]
|
|
|
|
impl AsRef<crate::Shared<crate::Module>> for AST {
|
|
|
|
#[inline(always)]
|
|
|
|
fn as_ref(&self) -> &crate::Shared<crate::Module> {
|
|
|
|
self.shared_lib()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// _(internals)_ An [`AST`] node, consisting of either an [`Expr`] or a [`Stmt`].
|
|
|
|
/// Exported under the `internals` feature only.
|
2022-01-24 10:04:40 +01:00
|
|
|
#[derive(Debug, Clone, Copy, Hash)]
|
2022-04-26 10:36:24 +02:00
|
|
|
#[non_exhaustive]
|
2021-12-17 09:07:13 +01:00
|
|
|
pub enum ASTNode<'a> {
|
|
|
|
/// A statement ([`Stmt`]).
|
|
|
|
Stmt(&'a Stmt),
|
|
|
|
/// An expression ([`Expr`]).
|
|
|
|
Expr(&'a Expr),
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a> From<&'a Stmt> for ASTNode<'a> {
|
|
|
|
fn from(stmt: &'a Stmt) -> Self {
|
|
|
|
Self::Stmt(stmt)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a> From<&'a Expr> for ASTNode<'a> {
|
|
|
|
fn from(expr: &'a Expr) -> Self {
|
|
|
|
Self::Expr(expr)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-01-24 10:04:40 +01:00
|
|
|
impl PartialEq for ASTNode<'_> {
|
|
|
|
#[inline(always)]
|
|
|
|
fn eq(&self, other: &Self) -> bool {
|
|
|
|
match (self, other) {
|
2022-04-13 04:35:10 +02:00
|
|
|
(Self::Stmt(x), Self::Stmt(y)) => ptr::eq(*x, *y),
|
|
|
|
(Self::Expr(x), Self::Expr(y)) => ptr::eq(*x, *y),
|
2022-01-24 10:04:40 +01:00
|
|
|
_ => false,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Eq for ASTNode<'_> {}
|
|
|
|
|
2021-12-17 09:07:13 +01:00
|
|
|
impl ASTNode<'_> {
|
|
|
|
/// Get the [`Position`] of this [`ASTNode`].
|
2022-02-10 10:55:32 +01:00
|
|
|
pub fn position(&self) -> Position {
|
2021-12-17 09:07:13 +01:00
|
|
|
match self {
|
|
|
|
ASTNode::Stmt(stmt) => stmt.position(),
|
|
|
|
ASTNode::Expr(expr) => expr.position(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl AST {
|
|
|
|
/// _(internals)_ Get the internal [`Module`][crate::Module] containing all script-defined functions.
|
|
|
|
/// Exported under the `internals` feature only.
|
|
|
|
///
|
|
|
|
/// Not available under `no_function`.
|
|
|
|
///
|
|
|
|
/// # Deprecated
|
|
|
|
///
|
|
|
|
/// This method is deprecated. Use [`shared_lib`][AST::shared_lib] instead.
|
|
|
|
///
|
|
|
|
/// This method will be removed in the next major version.
|
|
|
|
#[deprecated(since = "1.3.0", note = "use `shared_lib` instead")]
|
|
|
|
#[cfg(feature = "internals")]
|
|
|
|
#[cfg(not(feature = "no_function"))]
|
|
|
|
#[inline(always)]
|
|
|
|
#[must_use]
|
|
|
|
pub fn lib(&self) -> &crate::Module {
|
2022-01-31 06:38:27 +01:00
|
|
|
&self.lib
|
2021-12-17 09:07:13 +01:00
|
|
|
}
|
|
|
|
}
|