1use super::{ASTFlags, ASTNode, BinaryExpr, Expr, FnCallExpr, Ident};
4use crate::engine::{KEYWORD_EVAL, OP_EQUALS};
5use crate::func::StraightHashMap;
6use crate::tokenizer::Token;
7use crate::types::dynamic::Union;
8use crate::types::Span;
9use crate::{calc_fn_hash, Dynamic, FnArgsVec, Position, StaticVec, INT};
10#[cfg(feature = "no_std")]
11use std::prelude::v1::*;
12use std::{
13 borrow::Borrow,
14 fmt,
15 hash::{Hash, Hasher},
16 mem,
17 num::NonZeroUsize,
18 ops::{Range, RangeInclusive},
19};
20
21#[derive(Clone, PartialEq, Hash)]
26pub struct OpAssignment {
27 hash_op_assign: u64,
29 hash_op: u64,
31 op_assign: Token,
33 op_assign_syntax: &'static str,
35 op: Token,
37 op_syntax: &'static str,
39 pos: Position,
41}
42
43impl OpAssignment {
44 #[must_use]
46 #[inline(always)]
47 pub const fn new_assignment(pos: Position) -> Self {
48 Self {
49 hash_op_assign: 0,
50 hash_op: 0,
51 op_assign: Token::Equals,
52 op_assign_syntax: OP_EQUALS,
53 op: Token::Equals,
54 op_syntax: OP_EQUALS,
55 pos,
56 }
57 }
58 #[must_use]
60 #[inline(always)]
61 pub const fn is_op_assignment(&self) -> bool {
62 !matches!(self.op, Token::Equals)
63 }
64 #[must_use]
75 #[inline]
76 pub const fn get_op_assignment_info(
77 &self,
78 ) -> Option<(u64, u64, &Token, &'static str, &Token, &'static str)> {
79 if self.is_op_assignment() {
80 Some((
81 self.hash_op_assign,
82 self.hash_op,
83 &self.op_assign,
84 self.op_assign_syntax,
85 &self.op,
86 self.op_syntax,
87 ))
88 } else {
89 None
90 }
91 }
92 #[must_use]
94 #[inline(always)]
95 pub const fn position(&self) -> Position {
96 self.pos
97 }
98 #[must_use]
104 #[inline(always)]
105 pub fn new_op_assignment(name: &str, pos: Position) -> Self {
106 let op = Token::lookup_symbol_from_syntax(name)
107 .unwrap_or_else(|| panic!("{} is not an op-assignment operator", name));
108 Self::new_op_assignment_from_token(op, pos)
109 }
110 #[must_use]
116 pub fn new_op_assignment_from_token(op_assign: Token, pos: Position) -> Self {
117 let op = op_assign
118 .get_base_op_from_assignment()
119 .unwrap_or_else(|| panic!("{:?} is not an op-assignment operator", op_assign));
120
121 let op_assign_syntax = op_assign.literal_syntax();
122 let op_syntax = op.literal_syntax();
123
124 Self {
125 hash_op_assign: calc_fn_hash(None, op_assign_syntax, 2),
126 hash_op: calc_fn_hash(None, op_syntax, 2),
127 op_assign,
128 op_assign_syntax,
129 op,
130 op_syntax,
131 pos,
132 }
133 }
134 #[must_use]
140 #[inline(always)]
141 pub fn new_op_assignment_from_base(name: &str, pos: Position) -> Self {
142 let op = Token::lookup_symbol_from_syntax(name)
143 .unwrap_or_else(|| panic!("{} cannot be converted into an op-operator", name));
144 Self::new_op_assignment_from_base_token(&op, pos)
145 }
146 #[inline(always)]
152 #[must_use]
153 pub fn new_op_assignment_from_base_token(op: &Token, pos: Position) -> Self {
154 Self::new_op_assignment_from_token(
155 op.convert_to_op_assignment()
156 .unwrap_or_else(|| panic!("{:?} cannot be converted into an op-operator", op)),
157 pos,
158 )
159 }
160}
161
162impl fmt::Debug for OpAssignment {
163 #[cold]
164 #[inline(never)]
165 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
166 if self.is_op_assignment() {
167 f.debug_struct("OpAssignment")
168 .field("hash_op_assign", &self.hash_op_assign)
169 .field("hash_op", &self.hash_op)
170 .field("op_assign", &self.op_assign)
171 .field("op_assign_syntax", &self.op_assign_syntax)
172 .field("op", &self.op)
173 .field("op_syntax", &self.op_syntax)
174 .field("pos", &self.pos)
175 .finish()
176 } else {
177 write!(f, "{} @ {:?}", Token::Equals, self.pos)
178 }
179 }
180}
181
182#[derive(Clone, Hash)]
185pub enum RangeCase {
186 ExclusiveInt(Range<INT>, usize),
188 InclusiveInt(RangeInclusive<INT>, usize),
190}
191
192impl fmt::Debug for RangeCase {
193 #[cold]
194 #[inline(never)]
195 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
196 match self {
197 Self::ExclusiveInt(r, n) => write!(f, "{}..{} => {n}", r.start, r.end),
198 Self::InclusiveInt(r, n) => write!(f, "{}..={} => {n}", *r.start(), *r.end()),
199 }
200 }
201}
202
203impl From<Range<INT>> for RangeCase {
204 #[inline(always)]
205 fn from(value: Range<INT>) -> Self {
206 Self::ExclusiveInt(value, usize::MAX)
207 }
208}
209
210impl From<RangeInclusive<INT>> for RangeCase {
211 #[inline(always)]
212 fn from(value: RangeInclusive<INT>) -> Self {
213 Self::InclusiveInt(value, usize::MAX)
214 }
215}
216
217impl IntoIterator for RangeCase {
218 type Item = INT;
219 type IntoIter = Box<dyn Iterator<Item = Self::Item>>;
220
221 #[inline]
222 #[must_use]
223 fn into_iter(self) -> Self::IntoIter {
224 match self {
225 Self::ExclusiveInt(r, ..) => Box::new(r),
226 Self::InclusiveInt(r, ..) => Box::new(r),
227 }
228 }
229}
230
231impl RangeCase {
232 #[inline]
234 #[must_use]
235 pub fn is_empty(&self) -> bool {
236 match self {
237 Self::ExclusiveInt(r, ..) => r.is_empty(),
238 Self::InclusiveInt(r, ..) => r.is_empty(),
239 }
240 }
241 #[inline]
243 #[must_use]
244 pub fn len(&self) -> INT {
245 match self {
246 Self::ExclusiveInt(r, ..) if r.is_empty() => 0,
247 Self::ExclusiveInt(r, ..) => r.end - r.start,
248 Self::InclusiveInt(r, ..) if r.is_empty() => 0,
249 Self::InclusiveInt(r, ..) => *r.end() - *r.start() + 1,
250 }
251 }
252 #[inline]
254 #[must_use]
255 pub fn contains(&self, value: &Dynamic) -> bool {
256 match value {
257 Dynamic(Union::Int(v, ..)) => self.contains_int(*v),
258 #[cfg(not(feature = "no_float"))]
259 Dynamic(Union::Float(v, ..)) => self.contains_float(**v),
260 #[cfg(feature = "decimal")]
261 Dynamic(Union::Decimal(v, ..)) => self.contains_decimal(**v),
262 _ => false,
263 }
264 }
265 #[inline]
267 #[must_use]
268 pub fn contains_int(&self, n: INT) -> bool {
269 match self {
270 Self::ExclusiveInt(r, ..) => r.contains(&n),
271 Self::InclusiveInt(r, ..) => r.contains(&n),
272 }
273 }
274 #[cfg(not(feature = "no_float"))]
276 #[inline]
277 #[must_use]
278 pub fn contains_float(&self, n: crate::FLOAT) -> bool {
279 use crate::FLOAT;
280
281 match self {
282 Self::ExclusiveInt(r, ..) => ((r.start as FLOAT)..(r.end as FLOAT)).contains(&n),
283 Self::InclusiveInt(r, ..) => ((*r.start() as FLOAT)..=(*r.end() as FLOAT)).contains(&n),
284 }
285 }
286 #[cfg(feature = "decimal")]
288 #[inline]
289 #[must_use]
290 pub fn contains_decimal(&self, n: rust_decimal::Decimal) -> bool {
291 use rust_decimal::Decimal;
292
293 match self {
294 Self::ExclusiveInt(r, ..) => {
295 (Into::<Decimal>::into(r.start)..Into::<Decimal>::into(r.end)).contains(&n)
296 }
297 Self::InclusiveInt(r, ..) => {
298 (Into::<Decimal>::into(*r.start())..=Into::<Decimal>::into(*r.end())).contains(&n)
299 }
300 }
301 }
302 #[inline(always)]
304 #[must_use]
305 pub const fn is_inclusive(&self) -> bool {
306 match self {
307 Self::ExclusiveInt(..) => false,
308 Self::InclusiveInt(..) => true,
309 }
310 }
311 #[inline(always)]
313 #[must_use]
314 pub const fn index(&self) -> usize {
315 match self {
316 Self::ExclusiveInt(.., n) | Self::InclusiveInt(.., n) => *n,
317 }
318 }
319 #[inline(always)]
321 pub fn set_index(&mut self, index: usize) {
322 match self {
323 Self::ExclusiveInt(.., n) | Self::InclusiveInt(.., n) => *n = index,
324 }
325 }
326}
327
328pub type CaseBlocksList = smallvec::SmallVec<[usize; 2]>;
329
330#[derive(Debug, Clone)]
333pub struct SwitchCasesCollection {
334 pub expressions: FnArgsVec<BinaryExpr>,
336 pub cases: StraightHashMap<CaseBlocksList>,
338 pub ranges: StaticVec<RangeCase>,
340 pub def_case: Option<usize>,
342}
343
344impl Hash for SwitchCasesCollection {
345 #[inline(always)]
346 fn hash<H: Hasher>(&self, state: &mut H) {
347 self.expressions.hash(state);
348
349 self.cases.len().hash(state);
350 self.cases.iter().for_each(|kv| kv.hash(state));
351
352 self.ranges.hash(state);
353 self.def_case.hash(state);
354 }
355}
356
357#[cfg(not(feature = "no_std"))]
359const STMT_BLOCK_INLINE_SIZE: usize = 8;
360
361#[cfg(not(feature = "no_std"))]
368pub type StmtBlockContainer = smallvec::SmallVec<[Stmt; STMT_BLOCK_INLINE_SIZE]>;
369
370#[cfg(feature = "no_std")]
373pub type StmtBlockContainer = crate::StaticVec<Stmt>;
374
375#[derive(Clone, Hash, Default)]
378pub struct StmtBlock {
379 block: StmtBlockContainer,
381 span: Span,
383}
384
385impl StmtBlock {
386 pub const NONE: Self = Self::empty(Position::NONE);
388
389 #[inline(always)]
391 #[must_use]
392 pub fn new(
393 statements: impl IntoIterator<Item = Stmt>,
394 start_pos: Position,
395 end_pos: Position,
396 ) -> Self {
397 Self::new_with_span(statements, Span::new(start_pos, end_pos))
398 }
399 #[must_use]
401 pub fn new_with_span(statements: impl IntoIterator<Item = Stmt>, span: Span) -> Self {
402 let mut statements: smallvec::SmallVec<_> = statements.into_iter().collect();
403 statements.shrink_to_fit();
404 Self {
405 block: statements,
406 span,
407 }
408 }
409 #[inline(always)]
411 #[must_use]
412 pub const fn empty(pos: Position) -> Self {
413 Self {
414 block: StmtBlockContainer::new_const(),
415 span: Span::new(pos, pos),
416 }
417 }
418 #[inline(always)]
420 #[must_use]
421 pub fn is_empty(&self) -> bool {
422 self.block.is_empty()
423 }
424 #[inline(always)]
426 #[must_use]
427 pub fn len(&self) -> usize {
428 self.block.len()
429 }
430 #[inline(always)]
432 #[must_use]
433 pub fn statements(&self) -> &[Stmt] {
434 &self.block
435 }
436 #[inline(always)]
438 #[must_use]
439 pub fn statements_mut(&mut self) -> &mut StmtBlockContainer {
440 &mut self.block
441 }
442 #[inline(always)]
444 pub fn iter(&self) -> impl Iterator<Item = &Stmt> {
445 self.block.iter()
446 }
447 #[inline(always)]
449 #[must_use]
450 pub const fn position(&self) -> Position {
451 (self.span).start()
452 }
453 #[inline(always)]
455 #[must_use]
456 pub const fn end_position(&self) -> Position {
457 (self.span).end()
458 }
459 #[inline(always)]
461 #[must_use]
462 pub const fn span(&self) -> Span {
463 self.span
464 }
465 #[inline(always)]
468 #[must_use]
469 pub const fn span_or_else(&self, def_start_pos: Position, def_end_pos: Position) -> Span {
470 Span::new(
471 (self.span).start().or_else(def_start_pos),
472 (self.span).end().or_else(def_end_pos),
473 )
474 }
475 #[inline(always)]
477 pub fn set_position(&mut self, start_pos: Position, end_pos: Position) {
478 self.span = Span::new(start_pos, end_pos);
479 }
480}
481
482impl Borrow<[Stmt]> for StmtBlock {
483 #[inline(always)]
484 #[must_use]
485 fn borrow(&self) -> &[Stmt] {
486 &self.block
487 }
488}
489
490impl AsRef<[Stmt]> for StmtBlock {
491 #[inline(always)]
492 #[must_use]
493 fn as_ref(&self) -> &[Stmt] {
494 &self.block
495 }
496}
497
498impl AsMut<[Stmt]> for StmtBlock {
499 #[inline(always)]
500 #[must_use]
501 fn as_mut(&mut self) -> &mut [Stmt] {
502 &mut self.block
503 }
504}
505
506impl fmt::Debug for StmtBlock {
507 #[cold]
508 #[inline(never)]
509 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
510 f.write_str("Block")?;
511 fmt::Debug::fmt(&self.block, f)?;
512 if !self.span.is_none() {
513 write!(f, " @ {:?}", self.span())?;
514 }
515 Ok(())
516 }
517}
518
519impl From<Stmt> for StmtBlock {
520 #[inline]
521 fn from(stmt: Stmt) -> Self {
522 match stmt {
523 Stmt::Block(block) => *block,
524 Stmt::Noop(pos) => Self {
525 block: StmtBlockContainer::new_const(),
526 span: Span::new(pos, pos),
527 },
528 _ => {
529 let pos = stmt.position();
530 Self {
531 block: vec![stmt].into(),
532 span: Span::new(pos, Position::NONE),
533 }
534 }
535 }
536 }
537}
538
539impl IntoIterator for StmtBlock {
540 type Item = Stmt;
541 #[cfg(not(feature = "no_std"))]
542 type IntoIter = smallvec::IntoIter<[Stmt; STMT_BLOCK_INLINE_SIZE]>;
543 #[cfg(feature = "no_std")]
544 type IntoIter = smallvec::IntoIter<[Stmt; crate::STATIC_VEC_INLINE_SIZE]>;
545
546 #[inline(always)]
547 fn into_iter(self) -> Self::IntoIter {
548 self.block.into_iter()
549 }
550}
551
552impl<'a> IntoIterator for &'a StmtBlock {
553 type Item = &'a Stmt;
554 type IntoIter = std::slice::Iter<'a, Stmt>;
555
556 #[inline(always)]
557 fn into_iter(self) -> Self::IntoIter {
558 self.block.iter()
559 }
560}
561
562impl Extend<Stmt> for StmtBlock {
563 #[inline(always)]
564 fn extend<T: IntoIterator<Item = Stmt>>(&mut self, iter: T) {
565 self.block.extend(iter);
566 }
567}
568
569#[derive(Debug, Clone, Hash)]
576pub struct FlowControl {
577 pub expr: Expr,
579 pub body: StmtBlock,
581 pub branch: StmtBlock,
583}
584
585#[derive(Debug, Clone, Hash)]
588#[non_exhaustive]
589#[allow(clippy::type_complexity)]
590pub enum Stmt {
591 Noop(Position),
593 If(Box<FlowControl>, Position),
595 Switch(Box<(Expr, SwitchCasesCollection)>, Position),
603 While(Box<FlowControl>, Position),
607 Do(Box<FlowControl>, ASTFlags, Position),
614 For(Box<(Ident, Option<Ident>, FlowControl)>, Position),
616 Var(Box<(Ident, Expr, Option<NonZeroUsize>)>, ASTFlags, Position),
623 Assignment(Box<(OpAssignment, BinaryExpr)>),
625 FnCall(Box<FnCallExpr>, Position),
630 Block(Box<StmtBlock>),
632 TryCatch(Box<FlowControl>, Position),
634 Expr(Box<Expr>),
636 BreakLoop(Option<Box<Expr>>, ASTFlags, Position),
643 Return(Option<Box<Expr>>, ASTFlags, Position),
650 #[cfg(not(feature = "no_module"))]
654 Import(Box<(Expr, Ident)>, Position),
655 #[cfg(not(feature = "no_module"))]
659 Export(Box<(Ident, Ident)>, Position),
660 #[cfg(not(feature = "no_closure"))]
669 Share(Box<crate::FnArgsVec<(Ident, Option<NonZeroUsize>)>>),
670}
671
672impl Default for Stmt {
673 #[inline(always)]
674 #[must_use]
675 fn default() -> Self {
676 Self::Noop(Position::NONE)
677 }
678}
679
680impl Stmt {
681 #[inline(always)]
683 #[must_use]
684 pub const fn is_noop(&self) -> bool {
685 matches!(self, Self::Noop(..))
686 }
687 #[inline]
689 #[must_use]
690 pub const fn options(&self) -> ASTFlags {
691 match self {
692 Self::Do(_, options, _)
693 | Self::Var(_, options, _)
694 | Self::BreakLoop(_, options, _)
695 | Self::Return(_, options, _) => *options,
696
697 Self::Noop(..)
698 | Self::If(..)
699 | Self::Switch(..)
700 | Self::Block(..)
701 | Self::Expr(..)
702 | Self::FnCall(..)
703 | Self::While(..)
704 | Self::For(..)
705 | Self::TryCatch(..)
706 | Self::Assignment(..) => ASTFlags::empty(),
707
708 #[cfg(not(feature = "no_module"))]
709 Self::Import(..) | Self::Export(..) => ASTFlags::empty(),
710
711 #[cfg(not(feature = "no_closure"))]
712 Self::Share(..) => ASTFlags::empty(),
713 }
714 }
715 #[must_use]
717 pub fn position(&self) -> Position {
718 match self {
719 Self::Noop(pos)
720 | Self::BreakLoop(.., pos)
721 | Self::FnCall(.., pos)
722 | Self::If(.., pos)
723 | Self::Switch(.., pos)
724 | Self::While(.., pos)
725 | Self::Do(.., pos)
726 | Self::For(.., pos)
727 | Self::Return(.., pos)
728 | Self::Var(.., pos)
729 | Self::TryCatch(.., pos) => *pos,
730
731 Self::Assignment(x) => x.0.pos,
732
733 Self::Block(x) => x.position(),
734
735 Self::Expr(x) => x.start_position(),
736
737 #[cfg(not(feature = "no_module"))]
738 Self::Import(.., pos) => *pos,
739 #[cfg(not(feature = "no_module"))]
740 Self::Export(.., pos) => *pos,
741
742 #[cfg(not(feature = "no_closure"))]
743 Self::Share(x) => x[0].0.pos,
744 }
745 }
746 pub fn set_position(&mut self, new_pos: Position) -> &mut Self {
748 match self {
749 Self::Noop(pos)
750 | Self::BreakLoop(.., pos)
751 | Self::FnCall(.., pos)
752 | Self::If(.., pos)
753 | Self::Switch(.., pos)
754 | Self::While(.., pos)
755 | Self::Do(.., pos)
756 | Self::For(.., pos)
757 | Self::Return(.., pos)
758 | Self::Var(.., pos)
759 | Self::TryCatch(.., pos) => *pos = new_pos,
760
761 Self::Assignment(x) => x.0.pos = new_pos,
762
763 Self::Block(x) => x.set_position(new_pos, x.end_position()),
764
765 Self::Expr(x) => {
766 x.set_position(new_pos);
767 }
768
769 #[cfg(not(feature = "no_module"))]
770 Self::Import(.., pos) => *pos = new_pos,
771 #[cfg(not(feature = "no_module"))]
772 Self::Export(.., pos) => *pos = new_pos,
773
774 #[cfg(not(feature = "no_closure"))]
775 Self::Share(x) => x.iter_mut().for_each(|(x, _)| x.pos = new_pos),
776 }
777
778 self
779 }
780 #[must_use]
782 pub const fn returns_value(&self) -> bool {
783 match self {
784 Self::If(..)
785 | Self::Switch(..)
786 | Self::Block(..)
787 | Self::Expr(..)
788 | Self::FnCall(..) => true,
789
790 Self::Noop(..)
791 | Self::While(..)
792 | Self::Do(..)
793 | Self::For(..)
794 | Self::TryCatch(..) => false,
795
796 Self::Var(..) | Self::Assignment(..) | Self::BreakLoop(..) | Self::Return(..) => false,
797
798 #[cfg(not(feature = "no_module"))]
799 Self::Import(..) | Self::Export(..) => false,
800
801 #[cfg(not(feature = "no_closure"))]
802 Self::Share(..) => false,
803 }
804 }
805 #[must_use]
807 pub const fn is_self_terminated(&self) -> bool {
808 match self {
809 Self::If(..)
810 | Self::Switch(..)
811 | Self::While(..)
812 | Self::For(..)
813 | Self::Block(..)
814 | Self::TryCatch(..) => true,
815
816 Self::Noop(..) => false,
818
819 Self::Expr(e) => match &**e {
820 #[cfg(not(feature = "no_custom_syntax"))]
821 Expr::Custom(x, ..) if x.is_self_terminated() => true,
822 _ => false,
823 },
824
825 Self::Var(..)
826 | Self::Assignment(..)
827 | Self::FnCall(..)
828 | Self::Do(..)
829 | Self::BreakLoop(..)
830 | Self::Return(..) => false,
831
832 #[cfg(not(feature = "no_module"))]
833 Self::Import(..) | Self::Export(..) => false,
834
835 #[cfg(not(feature = "no_closure"))]
836 Self::Share(..) => false,
837 }
838 }
839 #[must_use]
843 pub fn is_pure(&self) -> bool {
844 match self {
845 Self::Noop(..) => true,
846 Self::Expr(expr) => expr.is_pure(),
847 Self::If(x, ..) => {
848 x.expr.is_pure()
849 && x.body.iter().all(Self::is_pure)
850 && x.branch.iter().all(Self::is_pure)
851 }
852 Self::Switch(x, ..) => {
853 let (expr, sw) = &**x;
854 expr.is_pure()
855 && sw.cases.values().flat_map(|cases| cases.iter()).all(|&c| {
856 let block = &sw.expressions[c];
857 block.lhs.is_pure() && block.rhs.is_pure()
858 })
859 && sw.ranges.iter().all(|r| {
860 let block = &sw.expressions[r.index()];
861 block.lhs.is_pure() && block.rhs.is_pure()
862 })
863 && sw.def_case.is_some()
864 && sw.expressions[sw.def_case.unwrap()].rhs.is_pure()
865 }
866
867 Self::While(x, ..) if matches!(x.expr, Expr::BoolConstant(false, ..)) => true,
869 Self::Do(x, options, ..) if matches!(x.expr, Expr::BoolConstant(..)) => match x.expr {
870 Expr::BoolConstant(cond, ..) if cond == options.intersects(ASTFlags::NEGATED) => {
871 x.body.iter().all(Self::is_pure)
872 }
873 _ => false,
874 },
875
876 Self::While(..) | Self::Do(..) => false,
878
879 Self::For(x, ..) => x.2.expr.is_pure() && x.2.body.iter().all(Self::is_pure),
882
883 Self::Var(..) | Self::Assignment(..) | Self::FnCall(..) => false,
884 Self::Block(block, ..) => block.iter().all(Self::is_pure),
885 Self::BreakLoop(..) | Self::Return(..) => false,
886 Self::TryCatch(x, ..) => {
887 x.expr.is_pure()
888 && x.body.iter().all(Self::is_pure)
889 && x.branch.iter().all(Self::is_pure)
890 }
891
892 #[cfg(not(feature = "no_module"))]
893 Self::Import(..) => false,
894 #[cfg(not(feature = "no_module"))]
895 Self::Export(..) => false,
896
897 #[cfg(not(feature = "no_closure"))]
898 Self::Share(..) => false,
899 }
900 }
901 #[inline]
909 #[must_use]
910 pub fn is_block_dependent(&self) -> bool {
911 match self {
912 Self::Var(..) => true,
913
914 Self::Expr(e) => match &**e {
915 Expr::Stmt(s) => s.iter().all(Self::is_block_dependent),
916 Expr::FnCall(x, ..) => !x.is_qualified() && x.name == KEYWORD_EVAL,
917 _ => false,
918 },
919
920 Self::FnCall(x, ..) => !x.is_qualified() && x.name == KEYWORD_EVAL,
921
922 #[cfg(not(feature = "no_module"))]
923 Self::Import(..) | Self::Export(..) => true,
924
925 _ => false,
926 }
927 }
928 #[inline]
935 #[must_use]
936 pub fn is_internally_pure(&self) -> bool {
937 match self {
938 Self::Var(x, ..) => x.1.is_pure(),
939
940 Self::Expr(e) => match &**e {
941 Expr::Stmt(s) => s.iter().all(Self::is_internally_pure),
942 _ => self.is_pure(),
943 },
944
945 #[cfg(not(feature = "no_module"))]
946 Self::Import(x, ..) => x.0.is_pure(),
947 #[cfg(not(feature = "no_module"))]
948 Self::Export(..) => true,
949
950 _ => self.is_pure(),
951 }
952 }
953 #[inline]
959 #[must_use]
960 pub const fn is_control_flow_break(&self) -> bool {
961 matches!(self, Self::Return(..) | Self::BreakLoop(..))
962 }
963 #[inline(always)]
965 #[must_use]
966 pub fn take(&mut self) -> Self {
967 mem::take(self)
968 }
969 pub fn walk<'a>(
972 &'a self,
973 path: &mut Vec<ASTNode<'a>>,
974 on_node: &mut (impl FnMut(&[ASTNode]) -> bool + ?Sized),
975 ) -> bool {
976 path.push(self.into());
978
979 if !on_node(path) {
980 return false;
981 }
982
983 match self {
984 Self::Var(x, ..) => {
985 if !x.1.walk(path, on_node) {
986 return false;
987 }
988 }
989 Self::If(x, ..) => {
990 if !x.expr.walk(path, on_node) {
991 return false;
992 }
993 for s in &x.body {
994 if !s.walk(path, on_node) {
995 return false;
996 }
997 }
998 for s in &x.branch {
999 if !s.walk(path, on_node) {
1000 return false;
1001 }
1002 }
1003 }
1004 Self::Switch(x, ..) => {
1005 let (expr, sw) = &**x;
1006
1007 if !expr.walk(path, on_node) {
1008 return false;
1009 }
1010 for (.., blocks) in &sw.cases {
1011 for &b in blocks {
1012 let block = &sw.expressions[b];
1013
1014 if !block.lhs.walk(path, on_node) {
1015 return false;
1016 }
1017 if !block.rhs.walk(path, on_node) {
1018 return false;
1019 }
1020 }
1021 }
1022 for r in &sw.ranges {
1023 let block = &sw.expressions[r.index()];
1024
1025 if !block.lhs.walk(path, on_node) {
1026 return false;
1027 }
1028 if !block.rhs.walk(path, on_node) {
1029 return false;
1030 }
1031 }
1032 if let Some(index) = sw.def_case {
1033 if !sw.expressions[index].lhs.walk(path, on_node) {
1034 return false;
1035 }
1036 }
1037 }
1038 Self::While(x, ..) | Self::Do(x, ..) => {
1039 if !x.expr.walk(path, on_node) {
1040 return false;
1041 }
1042 for s in x.body.statements() {
1043 if !s.walk(path, on_node) {
1044 return false;
1045 }
1046 }
1047 }
1048 Self::For(x, ..) => {
1049 if !x.2.expr.walk(path, on_node) {
1050 return false;
1051 }
1052 for s in &x.2.body {
1053 if !s.walk(path, on_node) {
1054 return false;
1055 }
1056 }
1057 }
1058 Self::Assignment(x, ..) => {
1059 if !x.1.lhs.walk(path, on_node) {
1060 return false;
1061 }
1062 if !x.1.rhs.walk(path, on_node) {
1063 return false;
1064 }
1065 }
1066 Self::FnCall(x, ..) => {
1067 for s in &*x.args {
1068 if !s.walk(path, on_node) {
1069 return false;
1070 }
1071 }
1072 }
1073 Self::Block(x, ..) => {
1074 for s in x.statements() {
1075 if !s.walk(path, on_node) {
1076 return false;
1077 }
1078 }
1079 }
1080 Self::TryCatch(x, ..) => {
1081 for s in &x.body {
1082 if !s.walk(path, on_node) {
1083 return false;
1084 }
1085 }
1086 for s in &x.branch {
1087 if !s.walk(path, on_node) {
1088 return false;
1089 }
1090 }
1091 }
1092 Self::Expr(e) => {
1093 if !e.walk(path, on_node) {
1094 return false;
1095 }
1096 }
1097 Self::Return(Some(e), ..) => {
1098 if !e.walk(path, on_node) {
1099 return false;
1100 }
1101 }
1102 #[cfg(not(feature = "no_module"))]
1103 Self::Import(x, ..) => {
1104 if !x.0.walk(path, on_node) {
1105 return false;
1106 }
1107 }
1108 _ => (),
1109 }
1110
1111 path.pop().unwrap();
1112
1113 true
1114 }
1115}