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