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