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}