spo_rhai/ast/
ast.rs

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