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}