1use super::{ASTFlags, ASTNode, Ident, Stmt, StmtBlock};
4use crate::engine::KEYWORD_FN_PTR;
5use crate::eval::GlobalRuntimeState;
6use crate::tokenizer::Token;
7use crate::types::dynamic::Union;
8use crate::{
9 calc_fn_hash, Dynamic, FnArgsVec, FnPtr, Identifier, ImmutableString, Position, SmartString,
10 StaticVec, ThinVec, INT,
11};
12#[cfg(feature = "no_std")]
13use std::prelude::v1::*;
14use std::{
15 collections::BTreeMap,
16 fmt,
17 fmt::Write,
18 hash::Hash,
19 iter::once,
20 mem,
21 num::{NonZeroU8, NonZeroUsize},
22};
23
24#[derive(Debug, Clone, Hash, Default)]
27pub struct BinaryExpr {
28 pub lhs: Expr,
30 pub rhs: Expr,
32}
33
34#[cfg(not(feature = "no_custom_syntax"))]
39#[derive(Debug, Clone, Hash)]
40pub struct CustomExpr {
41 pub inputs: FnArgsVec<Expr>,
43 pub tokens: FnArgsVec<ImmutableString>,
45 pub state: Dynamic,
47 pub scope_may_be_changed: bool,
50 pub self_terminated: bool,
52}
53
54#[cfg(not(feature = "no_custom_syntax"))]
55impl CustomExpr {
56 #[inline(always)]
60 #[must_use]
61 pub const fn is_self_terminated(&self) -> bool {
62 self.self_terminated
63 }
64}
65
66#[derive(Clone, Copy, Eq, PartialEq, Hash)]
94pub struct FnCallHashes {
95 #[cfg(not(feature = "no_function"))]
97 script: Option<u64>,
98 native: u64,
100}
101
102impl fmt::Debug for FnCallHashes {
103 #[cold]
104 #[inline(never)]
105 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
106 #[cfg(not(feature = "no_function"))]
107 return match self.script {
108 Some(script) if script == self.native => fmt::Debug::fmt(&self.native, f),
109 Some(script) => write!(f, "({script}, {})", self.native),
110 None => write!(f, "{} (native only)", self.native),
111 };
112
113 #[cfg(feature = "no_function")]
114 return write!(f, "{}", self.native);
115 }
116}
117
118impl FnCallHashes {
119 #[inline]
121 #[must_use]
122 pub const fn from_hash(hash: u64) -> Self {
123 Self {
124 #[cfg(not(feature = "no_function"))]
125 script: Some(hash),
126 native: hash,
127 }
128 }
129 #[inline]
131 #[must_use]
132 pub const fn from_native_only(hash: u64) -> Self {
133 Self {
134 #[cfg(not(feature = "no_function"))]
135 script: None,
136 native: hash,
137 }
138 }
139 #[cfg(not(feature = "no_function"))]
143 #[inline]
144 #[must_use]
145 pub const fn from_script_and_native(script: u64, native: u64) -> Self {
146 Self {
147 script: Some(script),
148 native,
149 }
150 }
151 #[inline(always)]
153 #[must_use]
154 pub const fn is_native_only(&self) -> bool {
155 #[cfg(not(feature = "no_function"))]
156 return self.script.is_none();
157 #[cfg(feature = "no_function")]
158 return true;
159 }
160 #[inline(always)]
164 #[must_use]
165 pub const fn native(&self) -> u64 {
166 self.native
167 }
168 #[cfg(not(feature = "no_function"))]
176 #[inline(always)]
177 #[must_use]
178 pub fn script(&self) -> u64 {
179 self.script.expect("native-only hash")
180 }
181}
182
183#[derive(Clone, Hash)]
186pub struct FnCallExpr {
187 #[cfg(not(feature = "no_module"))]
189 pub namespace: super::Namespace,
190 pub name: ImmutableString,
192 pub hashes: FnCallHashes,
194 pub args: FnArgsVec<Expr>,
196 pub capture_parent_scope: bool,
198 pub op_token: Option<Token>,
200}
201
202impl fmt::Debug for FnCallExpr {
203 #[cold]
204 #[inline(never)]
205 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
206 let mut ff = f.debug_struct("FnCallExpr");
207 #[cfg(not(feature = "no_module"))]
208 if !self.namespace.is_empty() {
209 ff.field("namespace", &self.namespace);
210 }
211 ff.field("hash", &self.hashes)
212 .field("name", &self.name)
213 .field("args", &self.args);
214 if self.is_operator_call() {
215 ff.field("op_token", &self.op_token);
216 }
217 if self.capture_parent_scope {
218 ff.field("capture_parent_scope", &self.capture_parent_scope);
219 }
220 ff.finish()
221 }
222}
223
224impl FnCallExpr {
225 #[cfg(not(feature = "no_module"))]
229 #[inline(always)]
230 #[must_use]
231 pub fn is_qualified(&self) -> bool {
232 !self.namespace.is_empty()
233 }
234 #[inline(always)]
236 #[must_use]
237 pub const fn is_operator_call(&self) -> bool {
238 self.op_token.is_some()
239 }
240 #[inline(always)]
242 #[must_use]
243 pub fn into_fn_call_expr(self, pos: Position) -> Expr {
244 Expr::FnCall(self.into(), pos)
245 }
246 #[inline]
248 #[must_use]
249 pub fn constant_args(&self) -> bool {
250 self.args.is_empty() || self.args.iter().all(Expr::is_constant)
251 }
252}
253
254#[derive(Clone, Hash)]
257#[non_exhaustive]
258#[allow(clippy::type_complexity)]
259pub enum Expr {
260 DynamicConstant(Box<Dynamic>, Position),
267 BoolConstant(bool, Position),
269 IntegerConstant(INT, Position),
271 #[cfg(not(feature = "no_float"))]
273 FloatConstant(crate::types::FloatWrapper<crate::FLOAT>, Position),
274 CharConstant(char, Position),
276 StringConstant(ImmutableString, Position),
278 InterpolatedString(ThinVec<Self>, Position),
280 Array(ThinVec<Self>, Position),
282 Map(
284 Box<(StaticVec<(Ident, Self)>, BTreeMap<Identifier, Dynamic>)>,
285 Position,
286 ),
287 Unit(Position),
289 Variable(
295 #[cfg(not(feature = "no_module"))]
296 Box<(Option<NonZeroUsize>, ImmutableString, super::Namespace, u64)>,
297 #[cfg(feature = "no_module")] Box<(Option<NonZeroUsize>, ImmutableString)>,
298 Option<NonZeroU8>,
299 Position,
300 ),
301 ThisPtr(Position),
303 Property(
305 Box<(
306 (ImmutableString, u64),
307 (ImmutableString, u64),
308 ImmutableString,
309 )>,
310 Position,
311 ),
312 MethodCall(Box<FnCallExpr>, Position),
314 Stmt(Box<StmtBlock>),
316 FnCall(Box<FnCallExpr>, Position),
318 Dot(Box<BinaryExpr>, ASTFlags, Position),
325 Index(Box<BinaryExpr>, ASTFlags, Position),
332 And(Box<StaticVec<Self>>, Position),
334 Or(Box<StaticVec<Self>>, Position),
336 Coalesce(Box<StaticVec<Self>>, Position),
338 #[cfg(not(feature = "no_custom_syntax"))]
340 Custom(Box<CustomExpr>, Position),
341}
342
343impl Default for Expr {
344 #[inline(always)]
345 fn default() -> Self {
346 Self::Unit(Position::NONE)
347 }
348}
349
350impl fmt::Debug for Expr {
351 #[cold]
352 #[inline(never)]
353 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
354 let mut display_pos = self.start_position();
355
356 match self {
357 Self::DynamicConstant(value, ..) => write!(f, "{value:?}"),
358 Self::BoolConstant(value, ..) => write!(f, "{value:?}"),
359 Self::IntegerConstant(value, ..) => write!(f, "{value:?}"),
360 #[cfg(not(feature = "no_float"))]
361 Self::FloatConstant(value, ..) => write!(f, "{value:?}"),
362 Self::CharConstant(value, ..) => write!(f, "{value:?}"),
363 Self::StringConstant(value, ..) => write!(f, "{value:?}"),
364 Self::Unit(..) => f.write_str("()"),
365
366 Self::InterpolatedString(x, ..) => {
367 f.write_str("InterpolatedString")?;
368 return f.debug_list().entries(x.iter()).finish();
369 }
370 Self::Array(x, ..) => {
371 f.write_str("Array")?;
372 f.debug_list().entries(x.iter()).finish()
373 }
374 Self::Map(x, ..) => {
375 f.write_str("Map")?;
376 f.debug_map()
377 .entries(x.0.iter().map(|(k, v)| (k, v)))
378 .finish()
379 }
380 Self::ThisPtr(..) => f.debug_struct("ThisPtr").finish(),
381 Self::Variable(x, i, ..) => {
382 f.write_str("Variable(")?;
383
384 #[cfg(not(feature = "no_module"))]
385 if !x.2.is_empty() {
386 write!(f, "{}{}", x.1, crate::engine::NAMESPACE_SEPARATOR)?;
387 let pos = x.2.position();
388 if !pos.is_none() {
389 display_pos = pos;
390 }
391 }
392 f.write_str(&x.1)?;
393 #[cfg(not(feature = "no_module"))]
394 if let Some(n) = x.2.index {
395 write!(f, " #{n}")?;
396 }
397 if let Some(n) = i.map_or_else(|| x.0, |n| NonZeroUsize::new(n.get() as usize)) {
398 write!(f, " #{n}")?;
399 }
400 f.write_str(")")
401 }
402 Self::Property(x, ..) => write!(f, "Property({})", x.2),
403 Self::MethodCall(x, ..) => f.debug_tuple("MethodCall").field(x).finish(),
404 Self::Stmt(x) => {
405 let pos = x.span();
406 if !pos.is_none() {
407 display_pos = pos.start();
408 }
409 f.write_str("ExprStmtBlock")?;
410 f.debug_list().entries(x.iter()).finish()
411 }
412 Self::FnCall(x, ..) => fmt::Debug::fmt(x, f),
413 Self::Index(x, options, pos) => {
414 if !pos.is_none() {
415 display_pos = *pos;
416 }
417
418 let mut f = f.debug_struct("Index");
419
420 f.field("lhs", &x.lhs).field("rhs", &x.rhs);
421 if !options.is_empty() {
422 f.field("options", options);
423 }
424 f.finish()
425 }
426 Self::Dot(x, options, pos) => {
427 if !pos.is_none() {
428 display_pos = *pos;
429 }
430
431 let mut f = f.debug_struct("Dot");
432
433 f.field("lhs", &x.lhs).field("rhs", &x.rhs);
434 if !options.is_empty() {
435 f.field("options", options);
436 }
437 f.finish()
438 }
439 Self::And(x, pos) | Self::Or(x, pos) | Self::Coalesce(x, pos) => {
440 let op_name = match self {
441 Self::And(..) => "And",
442 Self::Or(..) => "Or",
443 Self::Coalesce(..) => "Coalesce",
444 expr => unreachable!("`And`, `Or` or `Coalesce` expected but gets {:?}", expr),
445 };
446
447 if !pos.is_none() {
448 display_pos = *pos;
449 }
450
451 let mut f = f.debug_tuple(op_name);
452 x.iter().for_each(|expr| {
453 f.field(expr);
454 });
455 f.finish()
456 }
457 #[cfg(not(feature = "no_custom_syntax"))]
458 Self::Custom(x, ..) => f.debug_tuple("Custom").field(x).finish(),
459 }?;
460
461 write!(f, " @ {display_pos:?}")
462 }
463}
464
465impl Expr {
466 #[inline]
470 #[must_use]
471 pub fn get_literal_value(&self, global: Option<&GlobalRuntimeState>) -> Option<Dynamic> {
472 Some(match self {
473 Self::DynamicConstant(x, ..) => {
474 let mut _value = x.as_ref().clone();
475
476 #[cfg(not(feature = "no_function"))]
477 if let Some(global) = global {
478 if let Some(mut fn_ptr) = _value.write_lock::<FnPtr>() {
479 fn_ptr.env = Some(crate::Shared::new(global.into()));
481 }
482 }
483
484 _value
485 }
486
487 Self::IntegerConstant(x, ..) => (*x).into(),
488 #[cfg(not(feature = "no_float"))]
489 Self::FloatConstant(x, ..) => (*x).into(),
490 Self::CharConstant(x, ..) => (*x).into(),
491 Self::StringConstant(x, ..) => x.clone().into(),
492 Self::BoolConstant(x, ..) => (*x).into(),
493 Self::Unit(..) => Dynamic::UNIT,
494
495 #[cfg(not(feature = "no_index"))]
496 Self::Array(x, ..) if self.is_constant() => {
497 let mut arr = crate::Array::with_capacity(x.len());
498 arr.extend(x.iter().map(|v| v.get_literal_value(global).unwrap()));
499 Dynamic::from_array(arr)
500 }
501
502 #[cfg(not(feature = "no_object"))]
503 Self::Map(x, ..) if self.is_constant() => {
504 let mut map = x.1.clone();
505
506 for (k, v) in &x.0 {
507 *map.get_mut(k.as_str()).unwrap() = v.get_literal_value(global).unwrap();
508 }
509
510 Dynamic::from_map(map)
511 }
512
513 Self::InterpolatedString(x, ..) if self.is_constant() => {
515 let mut s = SmartString::new_const();
516 for segment in x {
517 let v = segment.get_literal_value(global).unwrap();
518 write!(&mut s, "{v}").unwrap();
519 }
520 s.into()
521 }
522
523 #[cfg(not(feature = "no_module"))]
525 Self::FnCall(x, ..) if x.is_qualified() => return None,
526
527 Self::FnCall(x, ..) if x.args.len() == 1 && x.name == KEYWORD_FN_PTR => {
529 match x.args[0] {
530 Self::StringConstant(ref s, ..) => FnPtr::new(s.clone()).ok()?.into(),
531 _ => return None,
532 }
533 }
534
535 Self::FnCall(x, ..) if x.args.len() == 2 => {
537 pub const OP_EXCLUSIVE_RANGE: &str = Token::ExclusiveRange.literal_syntax();
538 pub const OP_INCLUSIVE_RANGE: &str = Token::InclusiveRange.literal_syntax();
539
540 match x.name.as_str() {
541 OP_EXCLUSIVE_RANGE => match (&x.args[0], &x.args[1]) {
543 (
544 Self::IntegerConstant(ref start, ..),
545 Self::IntegerConstant(ref end, ..),
546 ) => (*start..*end).into(),
547 (Self::IntegerConstant(ref start, ..), Self::Unit(..)) => {
548 (*start..INT::MAX).into()
549 }
550 (Self::Unit(..), Self::IntegerConstant(ref start, ..)) => {
551 (0..*start).into()
552 }
553 _ => return None,
554 },
555 OP_INCLUSIVE_RANGE => match (&x.args[0], &x.args[1]) {
557 (
558 Self::IntegerConstant(ref start, ..),
559 Self::IntegerConstant(ref end, ..),
560 ) => (*start..=*end).into(),
561 (Self::IntegerConstant(ref start, ..), Self::Unit(..)) => {
562 (*start..=INT::MAX).into()
563 }
564 (Self::Unit(..), Self::IntegerConstant(ref start, ..)) => {
565 (0..=*start).into()
566 }
567 _ => return None,
568 },
569 _ => return None,
570 }
571 }
572
573 _ => return None,
574 })
575 }
576 #[inline]
578 #[must_use]
579 pub fn from_dynamic(value: Dynamic, pos: Position) -> Self {
580 match value.0 {
581 Union::Unit(..) => Self::Unit(pos),
582 Union::Bool(b, ..) => Self::BoolConstant(b, pos),
583 Union::Str(s, ..) => Self::StringConstant(s, pos),
584 Union::Char(c, ..) => Self::CharConstant(c, pos),
585 Union::Int(i, ..) => Self::IntegerConstant(i, pos),
586
587 #[cfg(feature = "decimal")]
588 Union::Decimal(value, ..) => Self::DynamicConstant(Box::new((*value).into()), pos),
589
590 #[cfg(not(feature = "no_float"))]
591 Union::Float(f, ..) => Self::FloatConstant(f, pos),
592
593 #[cfg(not(feature = "no_index"))]
594 Union::Array(a, ..) => Self::DynamicConstant(Box::new((*a).into()), pos),
595
596 #[cfg(not(feature = "no_object"))]
597 Union::Map(m, ..) => Self::DynamicConstant(Box::new((*m).into()), pos),
598
599 Union::FnPtr(f, ..) if !f.is_curried() => Self::FnCall(
600 FnCallExpr {
601 #[cfg(not(feature = "no_module"))]
602 namespace: super::Namespace::NONE,
603 name: KEYWORD_FN_PTR.into(),
604 hashes: FnCallHashes::from_hash(calc_fn_hash(None, f.fn_name(), 1)),
605 args: once(Self::StringConstant(f.fn_name().into(), pos)).collect(),
606 capture_parent_scope: false,
607 op_token: None,
608 }
609 .into(),
610 pos,
611 ),
612
613 _ => Self::DynamicConstant(value.into(), pos),
614 }
615 }
616 #[inline]
620 #[must_use]
621 pub(crate) fn get_variable_name(&self, _non_qualified: bool) -> Option<&str> {
622 match self {
623 #[cfg(not(feature = "no_module"))]
624 Self::Variable(x, ..) if _non_qualified && !x.2.is_empty() => None,
625 Self::Variable(x, ..) => Some(&x.1),
626 _ => None,
627 }
628 }
629 #[inline]
631 #[must_use]
632 pub const fn options(&self) -> ASTFlags {
633 match self {
634 Self::Index(_, options, _) | Self::Dot(_, options, _) => *options,
635
636 #[cfg(not(feature = "no_float"))]
637 Self::FloatConstant(..) => ASTFlags::empty(),
638
639 Self::DynamicConstant(..)
640 | Self::BoolConstant(..)
641 | Self::IntegerConstant(..)
642 | Self::CharConstant(..)
643 | Self::Unit(..)
644 | Self::StringConstant(..)
645 | Self::Array(..)
646 | Self::Map(..)
647 | Self::Variable(..)
648 | Self::ThisPtr(..)
649 | Self::And(..)
650 | Self::Or(..)
651 | Self::Coalesce(..)
652 | Self::FnCall(..)
653 | Self::MethodCall(..)
654 | Self::InterpolatedString(..)
655 | Self::Property(..)
656 | Self::Stmt(..) => ASTFlags::empty(),
657
658 #[cfg(not(feature = "no_custom_syntax"))]
659 Self::Custom(..) => ASTFlags::empty(),
660 }
661 }
662 #[inline]
664 #[must_use]
665 pub const fn position(&self) -> Position {
666 match self {
667 #[cfg(not(feature = "no_float"))]
668 Self::FloatConstant(.., pos) => *pos,
669
670 Self::DynamicConstant(.., pos)
671 | Self::BoolConstant(.., pos)
672 | Self::IntegerConstant(.., pos)
673 | Self::CharConstant(.., pos)
674 | Self::Unit(pos)
675 | Self::StringConstant(.., pos)
676 | Self::Array(.., pos)
677 | Self::Map(.., pos)
678 | Self::Variable(.., pos)
679 | Self::ThisPtr(pos)
680 | Self::And(.., pos)
681 | Self::Or(.., pos)
682 | Self::Coalesce(.., pos)
683 | Self::FnCall(.., pos)
684 | Self::MethodCall(.., pos)
685 | Self::Index(.., pos)
686 | Self::Dot(.., pos)
687 | Self::InterpolatedString(.., pos)
688 | Self::Property(.., pos) => *pos,
689
690 #[cfg(not(feature = "no_custom_syntax"))]
691 Self::Custom(.., pos) => *pos,
692
693 Self::Stmt(x) => x.position(),
694 }
695 }
696 #[inline]
699 #[must_use]
700 pub fn start_position(&self) -> Position {
701 match self {
702 #[cfg(not(feature = "no_module"))]
703 Self::Variable(x, ..) => {
704 if x.2.is_empty() {
705 self.position()
706 } else {
707 x.2.position()
708 }
709 }
710
711 Self::And(x, ..) | Self::Or(x, ..) | Self::Coalesce(x, ..) => x[0].start_position(),
712
713 Self::Index(x, ..) | Self::Dot(x, ..) => x.lhs.start_position(),
714
715 Self::FnCall(.., pos) => *pos,
716
717 _ => self.position(),
718 }
719 }
720 #[inline]
722 pub fn set_position(&mut self, new_pos: Position) -> &mut Self {
723 match self {
724 #[cfg(not(feature = "no_float"))]
725 Self::FloatConstant(.., pos) => *pos = new_pos,
726
727 Self::DynamicConstant(.., pos)
728 | Self::BoolConstant(.., pos)
729 | Self::IntegerConstant(.., pos)
730 | Self::CharConstant(.., pos)
731 | Self::Unit(pos)
732 | Self::StringConstant(.., pos)
733 | Self::Array(.., pos)
734 | Self::Map(.., pos)
735 | Self::And(.., pos)
736 | Self::Or(.., pos)
737 | Self::Coalesce(.., pos)
738 | Self::Dot(.., pos)
739 | Self::Index(.., pos)
740 | Self::Variable(.., pos)
741 | Self::ThisPtr(pos)
742 | Self::FnCall(.., pos)
743 | Self::MethodCall(.., pos)
744 | Self::InterpolatedString(.., pos)
745 | Self::Property(.., pos) => *pos = new_pos,
746
747 #[cfg(not(feature = "no_custom_syntax"))]
748 Self::Custom(.., pos) => *pos = new_pos,
749
750 Self::Stmt(x) => x.set_position(new_pos, Position::NONE),
751 }
752
753 self
754 }
755 #[inline]
759 #[must_use]
760 pub fn is_pure(&self) -> bool {
761 match self {
762 Self::InterpolatedString(x, ..) | Self::Array(x, ..) => x.iter().all(Self::is_pure),
763
764 Self::Map(x, ..) => x.0.iter().map(|(.., v)| v).all(Self::is_pure),
765
766 Self::And(x, ..) | Self::Or(x, ..) | Self::Coalesce(x, ..) => {
767 x.iter().all(Self::is_pure)
768 }
769
770 Self::Stmt(x) => x.iter().all(Stmt::is_pure),
771
772 Self::Variable(..) => true,
773
774 _ => self.is_constant(),
775 }
776 }
777 #[inline(always)]
779 #[must_use]
780 pub const fn is_unit(&self) -> bool {
781 matches!(self, Self::Unit(..))
782 }
783 #[inline]
785 #[must_use]
786 pub fn is_constant(&self) -> bool {
787 match self {
788 #[cfg(not(feature = "no_float"))]
789 Self::FloatConstant(..) => true,
790
791 Self::DynamicConstant(..)
792 | Self::BoolConstant(..)
793 | Self::IntegerConstant(..)
794 | Self::CharConstant(..)
795 | Self::StringConstant(..)
796 | Self::Unit(..) => true,
797
798 Self::InterpolatedString(x, ..) | Self::Array(x, ..) => x.iter().all(Self::is_constant),
799
800 Self::Map(x, ..) => x.0.iter().map(|(.., expr)| expr).all(Self::is_constant),
801
802 _ => false,
803 }
804 }
805 #[inline]
807 #[must_use]
808 pub const fn is_valid_postfix(&self, token: &Token) -> bool {
809 match token {
810 #[cfg(not(feature = "no_object"))]
811 Token::Period | Token::Elvis => return true,
812 #[cfg(not(feature = "no_index"))]
813 Token::LeftBracket | Token::QuestionBracket => return true,
814 _ => (),
815 }
816
817 match self {
818 #[cfg(not(feature = "no_float"))]
819 Self::FloatConstant(..) => false,
820
821 Self::DynamicConstant(..)
822 | Self::BoolConstant(..)
823 | Self::CharConstant(..)
824 | Self::And(..)
825 | Self::Or(..)
826 | Self::Coalesce(..)
827 | Self::Unit(..) => false,
828
829 Self::IntegerConstant(..)
830 | Self::StringConstant(..)
831 | Self::InterpolatedString(..)
832 | Self::FnCall(..)
833 | Self::ThisPtr(..)
834 | Self::MethodCall(..)
835 | Self::Stmt(..)
836 | Self::Dot(..)
837 | Self::Index(..)
838 | Self::Array(..)
839 | Self::Map(..) => false,
840
841 #[cfg(not(feature = "no_custom_syntax"))]
842 Self::Custom(..) => false,
843
844 Self::Variable(..) => matches!(
845 token,
846 Token::LeftParen | Token::Unit | Token::Bang | Token::DoubleColon
847 ),
848
849 Self::Property(..) => matches!(token, Token::LeftParen),
850 }
851 }
852 #[inline(always)]
854 #[must_use]
855 pub fn take(&mut self) -> Self {
856 mem::take(self)
857 }
858 pub fn walk<'a>(
861 &'a self,
862 path: &mut Vec<ASTNode<'a>>,
863 on_node: &mut (impl FnMut(&[ASTNode]) -> bool + ?Sized),
864 ) -> bool {
865 path.push(self.into());
867
868 if !on_node(path) {
869 return false;
870 }
871
872 match self {
873 Self::Stmt(x) => {
874 for s in &**x {
875 if !s.walk(path, on_node) {
876 return false;
877 }
878 }
879 }
880 Self::InterpolatedString(x, ..) | Self::Array(x, ..) => {
881 for e in &**x {
882 if !e.walk(path, on_node) {
883 return false;
884 }
885 }
886 }
887 Self::Map(x, ..) => {
888 for (.., e) in &x.0 {
889 if !e.walk(path, on_node) {
890 return false;
891 }
892 }
893 }
894 Self::Index(x, ..) | Self::Dot(x, ..) => {
895 if !x.lhs.walk(path, on_node) {
896 return false;
897 }
898 if !x.rhs.walk(path, on_node) {
899 return false;
900 }
901 }
902 Self::And(x, ..) | Self::Or(x, ..) | Self::Coalesce(x, ..) => {
903 for expr in &***x {
904 if !expr.walk(path, on_node) {
905 return false;
906 }
907 }
908 }
909 Self::FnCall(x, ..) => {
910 for e in &*x.args {
911 if !e.walk(path, on_node) {
912 return false;
913 }
914 }
915 }
916 #[cfg(not(feature = "no_custom_syntax"))]
917 Self::Custom(x, ..) => {
918 for e in &*x.inputs {
919 if !e.walk(path, on_node) {
920 return false;
921 }
922 }
923 }
924 _ => (),
925 }
926
927 path.pop().unwrap();
928
929 true
930 }
931}