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