Skip to main content

rhai/ast/
ast.rs

1//! Module defining the AST (abstract syntax tree).
2
3use super::{ASTFlags, Expr, FnAccess, Stmt};
4use crate::{expose_under_internals, Dynamic, FnNamespace, ImmutableString, Position, ThinVec};
5#[cfg(feature = "no_std")]
6use std::prelude::v1::*;
7use std::{
8    borrow::Borrow,
9    fmt,
10    hash::Hash,
11    ops::{Add, AddAssign},
12    ptr,
13};
14
15/// Compiled AST (abstract syntax tree) of a Rhai script.
16///
17/// # Thread Safety
18///
19/// Currently, [`AST`] is neither `Send` nor `Sync`. Turn on the `sync` feature to make it `Send + Sync`.
20#[derive(Clone)]
21pub struct AST {
22    /// Source of the [`AST`].
23    source: Option<ImmutableString>,
24    /// Global statements.
25    body: ThinVec<Stmt>,
26    /// Script-defined functions.
27    #[cfg(not(feature = "no_function"))]
28    lib: crate::SharedModule,
29    /// Embedded module resolver, if any.
30    #[cfg(not(feature = "no_module"))]
31    pub(crate) resolver: Option<crate::Shared<crate::module::resolvers::StaticModuleResolver>>,
32    /// [`AST`] documentation.
33    #[cfg(feature = "metadata")]
34    pub(crate) doc: crate::SmartString,
35}
36
37impl Default for AST {
38    #[inline(always)]
39    fn default() -> Self {
40        Self::empty()
41    }
42}
43
44impl fmt::Debug for AST {
45    #[cold]
46    #[inline(never)]
47    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
48        let mut fp = f.debug_struct("AST");
49
50        fp.field("source", &self.source);
51        #[cfg(feature = "metadata")]
52        fp.field("doc", &self.doc);
53        #[cfg(not(feature = "no_module"))]
54        fp.field("resolver", &self.resolver);
55
56        fp.field("body", &self.body);
57
58        #[cfg(not(feature = "no_function"))]
59        for (.., fn_def) in self.lib.iter_script_fn() {
60            let sig = fn_def.to_string();
61            fp.field(&sig, &fn_def.body.statements());
62        }
63
64        fp.finish()
65    }
66}
67
68impl AST {
69    /// _(internals)_ Create a new [`AST`].
70    /// Exported under the `internals` feature only.
71    #[expose_under_internals]
72    #[inline]
73    #[must_use]
74    fn new(
75        statements: impl IntoIterator<Item = Stmt>,
76        #[cfg(not(feature = "no_function"))] functions: impl Into<crate::SharedModule>,
77    ) -> Self {
78        Self {
79            source: None,
80            #[cfg(feature = "metadata")]
81            doc: crate::SmartString::new_const(),
82            body: statements.into_iter().collect(),
83            #[cfg(not(feature = "no_function"))]
84            lib: functions.into(),
85            #[cfg(not(feature = "no_module"))]
86            resolver: None,
87        }
88    }
89    /// _(internals)_ Create a new [`AST`] with a source name.
90    /// Exported under the `internals` feature only.
91    #[expose_under_internals]
92    #[inline]
93    #[must_use]
94    fn new_with_source(
95        statements: impl IntoIterator<Item = Stmt>,
96        #[cfg(not(feature = "no_function"))] functions: impl Into<crate::SharedModule>,
97        source: impl Into<ImmutableString>,
98    ) -> Self {
99        let mut ast = Self::new(
100            statements,
101            #[cfg(not(feature = "no_function"))]
102            functions,
103        );
104        ast.set_source(source);
105        ast
106    }
107    /// Create a new [`AST`] from a shared [`Module`][crate::Module].
108    #[cfg(not(feature = "no_function"))]
109    #[inline]
110    #[must_use]
111    pub fn new_from_module(module: impl Into<crate::SharedModule>) -> Self {
112        let module = module.into();
113
114        if let Some(source) = module.id() {
115            let source: crate::SmartString = source.into();
116            Self::new_with_source(std::iter::empty(), module, source)
117        } else {
118            Self::new(std::iter::empty(), module)
119        }
120    }
121    /// Create an empty [`AST`].
122    #[inline(always)]
123    #[must_use]
124    pub fn empty() -> Self {
125        Self {
126            source: None,
127            #[cfg(feature = "metadata")]
128            doc: crate::SmartString::new_const(),
129            body: <_>::default(),
130            #[cfg(not(feature = "no_function"))]
131            lib: crate::Module::new().into(),
132            #[cfg(not(feature = "no_module"))]
133            resolver: None,
134        }
135    }
136    /// Get the source, if any.
137    #[inline(always)]
138    #[must_use]
139    pub fn source(&self) -> Option<&str> {
140        self.source.as_deref()
141    }
142    /// Get a reference to the source.
143    #[inline(always)]
144    #[must_use]
145    pub(crate) const fn source_raw(&self) -> Option<&ImmutableString> {
146        self.source.as_ref()
147    }
148    /// Set the source.
149    #[inline]
150    pub fn set_source(&mut self, source: impl Into<ImmutableString>) -> &mut Self {
151        let source = source.into();
152
153        #[cfg(not(feature = "no_function"))]
154        crate::Shared::get_mut(&mut self.lib)
155            .as_mut()
156            .map(|m| m.set_id(source.clone()));
157
158        self.source = (!source.is_empty()).then_some(source);
159
160        self
161    }
162    /// Clear the source.
163    #[inline(always)]
164    pub fn clear_source(&mut self) -> &mut Self {
165        self.source = None;
166        self
167    }
168    /// Get the documentation (if any).
169    /// Exported under the `metadata` feature only.
170    ///
171    /// Documentation is a collection of all comment lines beginning with `//!`.
172    ///
173    /// Leading white-spaces are stripped, and each line always starts with `//!`.
174    #[cfg(feature = "metadata")]
175    #[inline(always)]
176    #[must_use]
177    pub fn doc(&self) -> &str {
178        &self.doc
179    }
180    /// _(internals)_ Get the statements.
181    /// Exported under the `internals` feature only.
182    #[expose_under_internals]
183    #[inline(always)]
184    #[must_use]
185    fn statements(&self) -> &[Stmt] {
186        &self.body
187    }
188    /// Get the statements.
189    #[inline(always)]
190    #[must_use]
191    #[allow(dead_code)]
192    pub(crate) fn statements_mut(&mut self) -> &mut ThinVec<Stmt> {
193        &mut self.body
194    }
195    /// Does this [`AST`] contain script-defined functions?
196    ///
197    /// Not available under `no_function`.
198    #[cfg(not(feature = "no_function"))]
199    #[inline(always)]
200    #[must_use]
201    pub fn has_functions(&self) -> bool {
202        !self.lib.is_empty()
203    }
204    /// _(internals)_ Get the internal shared [`Module`][crate::Module] containing all script-defined functions.
205    /// Exported under the `internals` feature only.
206    ///
207    /// Not available under `no_function`.
208    #[expose_under_internals]
209    #[cfg(not(feature = "no_function"))]
210    #[inline(always)]
211    #[must_use]
212    const fn shared_lib(&self) -> &crate::SharedModule {
213        &self.lib
214    }
215    /// _(internals)_ Get the embedded [module resolver][crate::ModuleResolver].
216    /// Exported under the `internals` feature only.
217    ///
218    /// Not available under `no_module`.
219    #[cfg(feature = "internals")]
220    #[cfg(not(feature = "no_module"))]
221    #[inline(always)]
222    #[must_use]
223    pub const fn resolver(
224        &self,
225    ) -> Option<&crate::Shared<crate::module::resolvers::StaticModuleResolver>> {
226        self.resolver.as_ref()
227    }
228    /// Clone the [`AST`]'s functions into a new [`AST`].
229    /// No statements are cloned.
230    ///
231    /// Not available under `no_function`.
232    ///
233    /// This operation is cheap because functions are shared.
234    #[cfg(not(feature = "no_function"))]
235    #[inline(always)]
236    #[must_use]
237    pub fn clone_functions_only(&self) -> Self {
238        self.clone_functions_only_filtered(|_, _, _, _, _| true)
239    }
240    /// Clone the [`AST`]'s functions into a new [`AST`] based on a filter predicate.
241    /// No statements are cloned.
242    ///
243    /// Not available under `no_function`.
244    ///
245    /// This operation is cheap because functions are shared.
246    #[cfg(not(feature = "no_function"))]
247    #[inline]
248    #[must_use]
249    pub fn clone_functions_only_filtered(
250        &self,
251        filter: impl Fn(FnNamespace, FnAccess, bool, &str, usize) -> bool,
252    ) -> Self {
253        let mut lib = crate::Module::new();
254        lib.merge_filtered(&self.lib, &filter);
255        Self {
256            source: self.source.clone(),
257            #[cfg(feature = "metadata")]
258            doc: self.doc.clone(),
259            body: <_>::default(),
260            lib: lib.into(),
261            #[cfg(not(feature = "no_module"))]
262            resolver: self.resolver.clone(),
263        }
264    }
265    /// Clone the [`AST`]'s script statements into a new [`AST`].
266    /// No functions are cloned.
267    #[inline(always)]
268    #[must_use]
269    pub fn clone_statements_only(&self) -> Self {
270        Self {
271            source: self.source.clone(),
272            #[cfg(feature = "metadata")]
273            doc: self.doc.clone(),
274            body: self.body.clone(),
275            #[cfg(not(feature = "no_function"))]
276            lib: crate::Module::new().into(),
277            #[cfg(not(feature = "no_module"))]
278            resolver: self.resolver.clone(),
279        }
280    }
281    /// Merge two [`AST`] into one.  Both [`AST`]'s are untouched and a new, merged,
282    /// version is returned.
283    ///
284    /// Statements in the second [`AST`] are simply appended to the end of the first _without any processing_.
285    /// Thus, the return value of the first [`AST`] (if using expression-statement syntax) is buried.
286    /// Of course, if the first [`AST`] uses a `return` statement at the end, then
287    /// the second [`AST`] will essentially be dead code.
288    ///
289    /// All script-defined functions in the second [`AST`] overwrite similarly-named functions
290    /// in the first [`AST`] with the same number of parameters.
291    ///
292    /// # Example
293    ///
294    /// ```
295    /// # fn main() -> Result<(), Box<rhai::EvalAltResult>> {
296    /// # #[cfg(not(feature = "no_function"))]
297    /// # {
298    /// use rhai::Engine;
299    ///
300    /// let engine = Engine::new();
301    ///
302    /// let ast1 = engine.compile("
303    ///     fn foo(x) { 42 + x }
304    ///     foo(1)
305    /// ")?;
306    ///
307    /// let ast2 = engine.compile(r#"
308    ///     fn foo(n) { `hello${n}` }
309    ///     foo("!")
310    /// "#)?;
311    ///
312    /// let ast = ast1.merge(&ast2);    // Merge 'ast2' into 'ast1'
313    ///
314    /// // Notice that using the '+' operator also works:
315    /// // let ast = &ast1 + &ast2;
316    ///
317    /// // 'ast' is essentially:
318    /// //
319    /// //    fn foo(n) { `hello${n}` } // <- definition of first 'foo' is overwritten
320    /// //    foo(1)                    // <- notice this will be "hello1" instead of 43,
321    /// //                              //    but it is no longer the return value
322    /// //    foo("!")                  // returns "hello!"
323    ///
324    /// // Evaluate it
325    /// assert_eq!(engine.eval_ast::<String>(&ast)?, "hello!");
326    /// # }
327    /// # Ok(())
328    /// # }
329    /// ```
330    #[inline(always)]
331    #[must_use]
332    pub fn merge(&self, other: &Self) -> Self {
333        self.merge_filtered_impl(other, |_, _, _, _, _| true)
334    }
335    /// Combine one [`AST`] with another.  The second [`AST`] is consumed.
336    ///
337    /// Statements in the second [`AST`] are simply appended to the end of the first _without any processing_.
338    /// Thus, the return value of the first [`AST`] (if using expression-statement syntax) is buried.
339    /// Of course, if the first [`AST`] uses a `return` statement at the end, then
340    /// the second [`AST`] will essentially be dead code.
341    ///
342    /// All script-defined functions in the second [`AST`] overwrite similarly-named functions
343    /// in the first [`AST`] with the same number of parameters.
344    ///
345    /// # Example
346    ///
347    /// ```
348    /// # fn main() -> Result<(), Box<rhai::EvalAltResult>> {
349    /// # #[cfg(not(feature = "no_function"))]
350    /// # {
351    /// use rhai::Engine;
352    ///
353    /// let engine = Engine::new();
354    ///
355    /// let mut ast1 = engine.compile("
356    ///     fn foo(x) { 42 + x }
357    ///     foo(1)
358    /// ")?;
359    ///
360    /// let ast2 = engine.compile(r#"
361    ///     fn foo(n) { `hello${n}` }
362    ///     foo("!")
363    /// "#)?;
364    ///
365    /// ast1.combine(ast2);    // Combine 'ast2' into 'ast1'
366    ///
367    /// // Notice that using the '+=' operator also works:
368    /// // ast1 += ast2;
369    ///
370    /// // 'ast1' is essentially:
371    /// //
372    /// //    fn foo(n) { `hello${n}` } // <- definition of first 'foo' is overwritten
373    /// //    foo(1)                    // <- notice this will be "hello1" instead of 43,
374    /// //                              //    but it is no longer the return value
375    /// //    foo("!")                  // returns "hello!"
376    ///
377    /// // Evaluate it
378    /// assert_eq!(engine.eval_ast::<String>(&ast1)?, "hello!");
379    /// # }
380    /// # Ok(())
381    /// # }
382    /// ```
383    #[inline(always)]
384    pub fn combine(&mut self, other: Self) -> &mut Self {
385        self.combine_filtered_impl(other, |_, _, _, _, _| true)
386    }
387    /// Merge two [`AST`] into one.  Both [`AST`]'s are untouched and a new, merged, version
388    /// is returned.
389    ///
390    /// Not available under `no_function`.
391    ///
392    /// Statements in the second [`AST`] are simply appended to the end of the first _without any processing_.
393    /// Thus, the return value of the first [`AST`] (if using expression-statement syntax) is buried.
394    /// Of course, if the first [`AST`] uses a `return` statement at the end, then
395    /// the second [`AST`] will essentially be dead code.
396    ///
397    /// All script-defined functions in the second [`AST`] are first selected based on a filter
398    /// predicate, then overwrite similarly-named functions in the first [`AST`] with the
399    /// same number of parameters.
400    ///
401    /// # Example
402    ///
403    /// ```
404    /// # fn main() -> Result<(), Box<rhai::EvalAltResult>> {
405    /// use rhai::Engine;
406    ///
407    /// let engine = Engine::new();
408    ///
409    /// let ast1 = engine.compile("
410    ///     fn foo(x) { 42 + x }
411    ///     foo(1)
412    /// ")?;
413    ///
414    /// let ast2 = engine.compile(r#"
415    ///     fn foo(n) { `hello${n}` }
416    ///     fn error() { 0 }
417    ///     foo("!")
418    /// "#)?;
419    ///
420    /// // Merge 'ast2', picking only 'error()' but not 'foo(..)', into 'ast1'
421    /// let ast = ast1.merge_filtered(&ast2, |_, _, script, name, params|
422    ///                                 script && name == "error" && params == 0);
423    ///
424    /// // 'ast' is essentially:
425    /// //
426    /// //    fn foo(n) { 42 + n }      // <- definition of 'ast1::foo' is not overwritten
427    /// //                              //    because 'ast2::foo' is filtered away
428    /// //    foo(1)                    // <- notice this will be 43 instead of "hello1",
429    /// //                              //    but it is no longer the return value
430    /// //    fn error() { 0 }          // <- this function passes the filter and is merged
431    /// //    foo("!")                  // <- returns "42!"
432    ///
433    /// // Evaluate it
434    /// assert_eq!(engine.eval_ast::<String>(&ast)?, "42!");
435    /// # Ok(())
436    /// # }
437    /// ```
438    #[cfg(not(feature = "no_function"))]
439    #[inline(always)]
440    #[must_use]
441    pub fn merge_filtered(
442        &self,
443        other: &Self,
444        filter: impl Fn(FnNamespace, FnAccess, bool, &str, usize) -> bool,
445    ) -> Self {
446        self.merge_filtered_impl(other, filter)
447    }
448    /// Merge two [`AST`] into one.  Both [`AST`]'s are untouched and a new, merged, version
449    /// is returned.
450    #[inline]
451    #[must_use]
452    fn merge_filtered_impl(
453        &self,
454        other: &Self,
455        _filter: impl Fn(FnNamespace, FnAccess, bool, &str, usize) -> bool,
456    ) -> Self {
457        let merged = match (self.body.as_ref(), other.body.as_ref()) {
458            ([], []) => <_>::default(),
459            (_, []) => self.body.to_vec(),
460            ([], _) => other.body.to_vec(),
461            (body, other) => {
462                let mut body = body.to_vec();
463                body.extend(other.iter().cloned());
464                body
465            }
466        };
467
468        #[cfg(not(feature = "no_function"))]
469        let lib = {
470            let mut lib = self.lib.as_ref().clone();
471            lib.merge_filtered(&other.lib, &_filter);
472            lib
473        };
474
475        let mut _ast = match other.source {
476            Some(ref source) => Self::new_with_source(
477                merged,
478                #[cfg(not(feature = "no_function"))]
479                lib,
480                source.clone(),
481            ),
482            None => Self::new(
483                merged,
484                #[cfg(not(feature = "no_function"))]
485                lib,
486            ),
487        };
488
489        #[cfg(not(feature = "no_module"))]
490        match (
491            self.resolver.as_deref().map_or(true, |r| r.is_empty()),
492            other.resolver.as_deref().map_or(true, |r| r.is_empty()),
493        ) {
494            (true, true) => (),
495            (false, true) => _ast.resolver.clone_from(&self.resolver),
496            (true, false) => _ast.resolver.clone_from(&other.resolver),
497            (false, false) => {
498                let mut resolver = self.resolver.as_deref().unwrap().clone();
499                for (k, v) in other.resolver.as_deref().unwrap() {
500                    resolver.insert(k.clone(), v.as_ref().clone());
501                }
502                _ast.resolver = Some(resolver.into());
503            }
504        }
505
506        #[cfg(feature = "metadata")]
507        match (other.doc.as_str(), _ast.doc.as_str()) {
508            ("", _) => (),
509            (_, "") => _ast.doc = other.doc.clone(),
510            (_, _) => {
511                _ast.doc.push_str("\n");
512                _ast.doc.push_str(&other.doc);
513            }
514        }
515
516        _ast
517    }
518    /// Combine one [`AST`] with another.  The second [`AST`] is consumed.
519    ///
520    /// Not available under `no_function`.
521    ///
522    /// Statements in the second [`AST`] are simply appended to the end of the first _without any processing_.
523    /// Thus, the return value of the first [`AST`] (if using expression-statement syntax) is buried.
524    /// Of course, if the first [`AST`] uses a `return` statement at the end, then
525    /// the second [`AST`] will essentially be dead code.
526    ///
527    /// All script-defined functions in the second [`AST`] are first selected based on a filter
528    /// predicate, then overwrite similarly-named functions in the first [`AST`] with the
529    /// same number of parameters.
530    ///
531    /// # Example
532    ///
533    /// ```
534    /// # fn main() -> Result<(), Box<rhai::EvalAltResult>> {
535    /// use rhai::Engine;
536    ///
537    /// let engine = Engine::new();
538    ///
539    /// let mut ast1 = engine.compile("
540    ///     fn foo(x) { 42 + x }
541    ///     foo(1)
542    /// ")?;
543    ///
544    /// let ast2 = engine.compile(r#"
545    ///     fn foo(n) { `hello${n}` }
546    ///     fn error() { 0 }
547    ///     foo("!")
548    /// "#)?;
549    ///
550    /// // Combine 'ast2', picking only 'error()' but not 'foo(..)', into 'ast1'
551    /// ast1.combine_filtered(ast2, |_, _, script, name, params|
552    ///                                 script && name == "error" && params == 0);
553    ///
554    /// // 'ast1' is essentially:
555    /// //
556    /// //    fn foo(n) { 42 + n }      // <- definition of 'ast1::foo' is not overwritten
557    /// //                              //    because 'ast2::foo' is filtered away
558    /// //    foo(1)                    // <- notice this will be 43 instead of "hello1",
559    /// //                              //    but it is no longer the return value
560    /// //    fn error() { 0 }          // <- this function passes the filter and is merged
561    /// //    foo("!")                  // <- returns "42!"
562    ///
563    /// // Evaluate it
564    /// assert_eq!(engine.eval_ast::<String>(&ast1)?, "42!");
565    /// # Ok(())
566    /// # }
567    /// ```
568    #[cfg(not(feature = "no_function"))]
569    #[inline(always)]
570    pub fn combine_filtered(
571        &mut self,
572        other: Self,
573        filter: impl Fn(FnNamespace, FnAccess, bool, &str, usize) -> bool,
574    ) -> &mut Self {
575        self.combine_filtered_impl(other, filter)
576    }
577    /// Combine one [`AST`] with another.  The second [`AST`] is consumed.
578    fn combine_filtered_impl(
579        &mut self,
580        other: Self,
581        _filter: impl Fn(FnNamespace, FnAccess, bool, &str, usize) -> bool,
582    ) -> &mut Self {
583        #[cfg(not(feature = "no_module"))]
584        match (
585            self.resolver.as_deref().map_or(true, |r| r.is_empty()),
586            other.resolver.as_deref().map_or(true, |r| r.is_empty()),
587        ) {
588            (_, true) => (),
589            (true, false) => self.resolver.clone_from(&other.resolver),
590            (false, false) => {
591                let resolver = crate::func::shared_make_mut(self.resolver.as_mut().unwrap());
592                let other_resolver = crate::func::shared_take_or_clone(other.resolver.unwrap());
593                for (k, v) in other_resolver {
594                    resolver.insert(k, crate::func::shared_take_or_clone(v));
595                }
596            }
597        }
598
599        match (self.body.as_ref(), other.body.as_ref()) {
600            (_, []) => (),
601            ([], _) => self.body = other.body,
602            (_, _) => self.body.extend(other.body),
603        }
604
605        #[cfg(not(feature = "no_function"))]
606        if !other.lib.is_empty() {
607            crate::func::shared_make_mut(&mut self.lib).merge_filtered(&other.lib, &_filter);
608        }
609
610        #[cfg(feature = "metadata")]
611        match (other.doc.as_str(), self.doc.as_str()) {
612            ("", _) => (),
613            (_, "") => self.doc = other.doc,
614            (_, _) => {
615                self.doc.push_str("\n");
616                self.doc.push_str(&other.doc);
617            }
618        }
619
620        self
621    }
622    /// Filter out the functions, retaining only some based on a filter predicate.
623    ///
624    /// Not available under `no_function`.
625    ///
626    /// # Example
627    ///
628    /// ```
629    /// # fn main() -> Result<(), Box<rhai::EvalAltResult>> {
630    /// # #[cfg(not(feature = "no_function"))]
631    /// # {
632    /// use rhai::Engine;
633    ///
634    /// let engine = Engine::new();
635    ///
636    /// let mut ast = engine.compile(r#"
637    ///     fn foo(n) { n + 1 }
638    ///     fn bar() { print("hello"); }
639    /// "#)?;
640    ///
641    /// // Remove all functions except 'foo(..)'
642    /// ast.retain_functions(|_, _, name, params| name == "foo" && params == 1);
643    /// # }
644    /// # Ok(())
645    /// # }
646    /// ```
647    #[cfg(not(feature = "no_function"))]
648    #[inline]
649    pub fn retain_functions(
650        &mut self,
651        filter: impl Fn(FnNamespace, FnAccess, &str, usize) -> bool,
652    ) -> &mut Self {
653        if self.has_functions() {
654            crate::func::shared_make_mut(&mut self.lib).retain_script_functions(filter);
655        }
656        self
657    }
658    /// _(internals)_ Iterate through all function definitions.
659    /// Exported under the `internals` feature only.
660    ///
661    /// Not available under `no_function`.
662    #[expose_under_internals]
663    #[cfg(not(feature = "no_function"))]
664    #[inline]
665    fn iter_fn_def(&self) -> impl Iterator<Item = &crate::Shared<super::ScriptFuncDef>> {
666        self.lib.iter_script_fn().map(|(.., fn_def)| fn_def)
667    }
668    /// Iterate through all function definitions.
669    ///
670    /// Not available under `no_function`.
671    #[cfg(not(feature = "no_function"))]
672    #[inline]
673    pub fn iter_functions(&self) -> impl Iterator<Item = super::ScriptFnMetadata<'_>> {
674        self.lib
675            .iter_script_fn()
676            .map(|(.., fn_def)| fn_def.as_ref().into())
677    }
678    /// Clear all function definitions in the [`AST`].
679    ///
680    /// Not available under `no_function`.
681    #[cfg(not(feature = "no_function"))]
682    #[inline(always)]
683    pub fn clear_functions(&mut self) -> &mut Self {
684        self.lib = crate::Module::new().into();
685        self
686    }
687    /// Clear all statements in the [`AST`], leaving only function definitions.
688    #[inline(always)]
689    pub fn clear_statements(&mut self) -> &mut Self {
690        self.body = <_>::default();
691        self
692    }
693    /// Extract all top-level literal constant and/or variable definitions.
694    /// This is useful for extracting all global constants from a script without actually running it.
695    ///
696    /// A literal constant/variable definition takes the form of:
697    /// `const VAR = `_value_`;` and `let VAR = `_value_`;`
698    /// where _value_ is a literal expression or will be optimized into a literal.
699    ///
700    /// # Example
701    ///
702    /// ```
703    /// # fn main() -> Result<(), Box<rhai::EvalAltResult>> {
704    /// use rhai::{Engine, Scope};
705    ///
706    /// let engine = Engine::new();
707    ///
708    /// let ast = engine.compile(
709    /// "
710    ///     const A = 40 + 2;   // constant that optimizes into a literal
711    ///     let b = 123;        // literal variable
712    ///     const B = b * A;    // non-literal constant
713    ///     const C = 999;      // literal constant
714    ///     b = A + C;          // expression
715    ///
716    ///     {                   // <- new block scope
717    ///         const Z = 0;    // <- literal constant not at top-level
718    ///
719    ///         print(Z);       // make sure the block is not optimized away
720    ///     }
721    /// ")?;
722    ///
723    /// let mut iter = ast.iter_literal_variables(true, false)
724    ///                   .map(|(name, is_const, value)| (name, is_const, value.as_int().unwrap()));
725    ///
726    /// # #[cfg(not(feature = "no_optimize"))]
727    /// assert_eq!(iter.next(), Some(("A", true, 42)));
728    /// assert_eq!(iter.next(), Some(("C", true, 999)));
729    /// assert_eq!(iter.next(), None);
730    ///
731    /// let mut iter = ast.iter_literal_variables(false, true)
732    ///                   .map(|(name, is_const, value)| (name, is_const, value.as_int().unwrap()));
733    ///
734    /// assert_eq!(iter.next(), Some(("b", false, 123)));
735    /// assert_eq!(iter.next(), None);
736    ///
737    /// let mut iter = ast.iter_literal_variables(true, true)
738    ///                   .map(|(name, is_const, value)| (name, is_const, value.as_int().unwrap()));
739    ///
740    /// # #[cfg(not(feature = "no_optimize"))]
741    /// assert_eq!(iter.next(), Some(("A", true, 42)));
742    /// assert_eq!(iter.next(), Some(("b", false, 123)));
743    /// assert_eq!(iter.next(), Some(("C", true, 999)));
744    /// assert_eq!(iter.next(), None);
745    ///
746    /// let scope: Scope = ast.iter_literal_variables(true, false).collect();
747    ///
748    /// # #[cfg(not(feature = "no_optimize"))]
749    /// assert_eq!(scope.len(), 2);
750    ///
751    /// Ok(())
752    /// # }
753    /// ```
754    pub fn iter_literal_variables(
755        &self,
756        include_constants: bool,
757        include_variables: bool,
758    ) -> impl Iterator<Item = (&str, bool, Dynamic)> {
759        self.statements().iter().filter_map(move |stmt| match stmt {
760            Stmt::Var(x, options, ..)
761                if options.intersects(ASTFlags::CONSTANT) && include_constants
762                    || !options.intersects(ASTFlags::CONSTANT) && include_variables =>
763            {
764                let (name, expr, ..) = &**x;
765                expr.get_literal_value(None)
766                    .map(|value| (name.as_str(), options.intersects(ASTFlags::CONSTANT), value))
767            }
768            _ => None,
769        })
770    }
771    /// _(internals)_ Recursively walk the [`AST`], including function bodies (if any).
772    /// Return `false` from the callback to terminate the walk.
773    /// Exported under the `internals` feature only.
774    #[cfg(feature = "internals")]
775    #[inline(always)]
776    pub fn walk(&self, on_node: &mut (impl FnMut(&[ASTNode]) -> bool + ?Sized)) -> bool {
777        self._walk(on_node)
778    }
779    /// Recursively walk the [`AST`], including function bodies (if any).
780    /// Return `false` from the callback to terminate the walk.
781    pub(crate) fn _walk(&self, on_node: &mut (impl FnMut(&[ASTNode]) -> bool + ?Sized)) -> bool {
782        let path = &mut Vec::new();
783
784        for stmt in self.statements() {
785            if !stmt.walk(path, on_node) {
786                return false;
787            }
788        }
789        #[cfg(not(feature = "no_function"))]
790        for stmt in self.iter_fn_def().flat_map(|f| f.body.iter()) {
791            if !stmt.walk(path, on_node) {
792                return false;
793            }
794        }
795
796        true
797    }
798}
799
800impl<A: AsRef<AST>> Add<A> for &AST {
801    type Output = AST;
802
803    #[inline(always)]
804    fn add(self, rhs: A) -> Self::Output {
805        self.merge(rhs.as_ref())
806    }
807}
808
809impl<A: Into<Self>> AddAssign<A> for AST {
810    #[inline(always)]
811    fn add_assign(&mut self, rhs: A) {
812        self.combine(rhs.into());
813    }
814}
815
816impl Borrow<[Stmt]> for AST {
817    #[inline(always)]
818    fn borrow(&self) -> &[Stmt] {
819        self.statements()
820    }
821}
822
823impl AsRef<[Stmt]> for AST {
824    #[inline(always)]
825    fn as_ref(&self) -> &[Stmt] {
826        self.statements()
827    }
828}
829
830#[cfg(not(feature = "no_function"))]
831impl Borrow<crate::Module> for AST {
832    #[inline(always)]
833    fn borrow(&self) -> &crate::Module {
834        self.shared_lib()
835    }
836}
837
838#[cfg(not(feature = "no_function"))]
839impl AsRef<crate::Module> for AST {
840    #[inline(always)]
841    fn as_ref(&self) -> &crate::Module {
842        self.shared_lib().as_ref()
843    }
844}
845
846#[cfg(not(feature = "no_function"))]
847impl Borrow<crate::SharedModule> for AST {
848    #[inline(always)]
849    fn borrow(&self) -> &crate::SharedModule {
850        self.shared_lib()
851    }
852}
853
854#[cfg(not(feature = "no_function"))]
855impl AsRef<crate::SharedModule> for AST {
856    #[inline(always)]
857    fn as_ref(&self) -> &crate::SharedModule {
858        self.shared_lib()
859    }
860}
861
862/// _(internals)_ An [`AST`] node, consisting of either an [`Expr`] or a [`Stmt`].
863/// Exported under the `internals` feature only.
864#[derive(Debug, Clone, Copy, Hash)]
865#[non_exhaustive]
866pub enum ASTNode<'a> {
867    /// A statement ([`Stmt`]).
868    Stmt(&'a Stmt),
869    /// An expression ([`Expr`]).
870    Expr(&'a Expr),
871}
872
873impl<'a> From<&'a Stmt> for ASTNode<'a> {
874    #[inline(always)]
875    fn from(stmt: &'a Stmt) -> Self {
876        Self::Stmt(stmt)
877    }
878}
879
880impl<'a> From<&'a Expr> for ASTNode<'a> {
881    #[inline(always)]
882    fn from(expr: &'a Expr) -> Self {
883        Self::Expr(expr)
884    }
885}
886
887impl PartialEq for ASTNode<'_> {
888    #[inline]
889    fn eq(&self, other: &Self) -> bool {
890        match (self, other) {
891            (Self::Stmt(x), Self::Stmt(y)) => ptr::eq(*x, *y),
892            (Self::Expr(x), Self::Expr(y)) => ptr::eq(*x, *y),
893            _ => false,
894        }
895    }
896}
897
898impl Eq for ASTNode<'_> {}
899
900impl ASTNode<'_> {
901    /// Is this [`ASTNode`] a [`Stmt`]?
902    #[inline(always)]
903    #[must_use]
904    pub const fn is_stmt(&self) -> bool {
905        matches!(self, Self::Stmt(..))
906    }
907    /// Is this [`ASTNode`] an [`Expr`]?
908    #[inline(always)]
909    #[must_use]
910    pub const fn is_expr(&self) -> bool {
911        matches!(self, Self::Expr(..))
912    }
913    /// Get the [`Position`] of this [`ASTNode`].
914    #[inline]
915    #[must_use]
916    pub fn position(&self) -> Position {
917        match self {
918            Self::Stmt(stmt) => stmt.position(),
919            Self::Expr(expr) => expr.position(),
920        }
921    }
922}
923
924/// _(internals)_ Encapsulated AST environment.
925/// Exported under the `internals` feature only.
926///
927/// 1) stack of scripted functions defined
928/// 2) the stack of imported [modules][crate::Module]
929/// 3) global constants
930#[derive(Debug, Clone)]
931pub struct EncapsulatedEnviron {
932    /// Stack of loaded [modules][crate::Module] containing script-defined functions.
933    #[cfg(not(feature = "no_function"))]
934    pub lib: crate::StaticVec<crate::SharedModule>,
935    /// Imported [modules][crate::Module].
936    #[cfg(not(feature = "no_module"))]
937    pub imports: crate::ThinVec<(ImmutableString, crate::SharedModule)>,
938    /// Globally-defined constants.
939    #[cfg(not(feature = "no_module"))]
940    #[cfg(not(feature = "no_function"))]
941    pub constants: Option<crate::eval::SharedGlobalConstants>,
942}
943
944#[cfg(not(feature = "no_function"))]
945impl From<&crate::eval::GlobalRuntimeState> for EncapsulatedEnviron {
946    fn from(value: &crate::eval::GlobalRuntimeState) -> Self {
947        Self {
948            lib: value.lib.clone(),
949            #[cfg(not(feature = "no_module"))]
950            imports: value
951                .iter_imports_raw()
952                .map(|(n, m)| (n.clone(), m.clone()))
953                .collect(),
954            #[cfg(not(feature = "no_module"))]
955            constants: value.constants.clone(),
956        }
957    }
958}