sway_ast/expr/
mod.rs

1use sway_error::handler::ErrorEmitted;
2
3use crate::{assignable::ElementAccess, priv_prelude::*, PathExprSegment};
4
5pub mod asm;
6pub mod op_code;
7
8#[derive(Clone, Debug, Serialize)]
9pub enum Expr {
10    /// A malformed expression.
11    ///
12    /// Used for parser recovery when we cannot form a more specific node.
13    Error(Box<[Span]>, #[serde(skip_serializing)] ErrorEmitted),
14    Path(PathExpr),
15    Literal(Literal),
16    AbiCast {
17        abi_token: AbiToken,
18        args: Parens<AbiCastArgs>,
19    },
20    Struct {
21        path: PathExpr,
22        fields: Braces<Punctuated<ExprStructField, CommaToken>>,
23    },
24    Tuple(Parens<ExprTupleDescriptor>),
25    Parens(Parens<Box<Expr>>),
26    Block(Braces<CodeBlockContents>),
27    Array(SquareBrackets<ExprArrayDescriptor>),
28    Asm(AsmBlock),
29    Return {
30        return_token: ReturnToken,
31        expr_opt: Option<Box<Expr>>,
32    },
33    Panic {
34        panic_token: PanicToken,
35        expr_opt: Option<Box<Expr>>,
36    },
37    If(IfExpr),
38    Match {
39        match_token: MatchToken,
40        value: Box<Expr>,
41        branches: Braces<Vec<MatchBranch>>,
42    },
43    While {
44        while_token: WhileToken,
45        condition: Box<Expr>,
46        block: Braces<CodeBlockContents>,
47    },
48    For {
49        for_token: ForToken,
50        in_token: InToken,
51        value_pattern: Pattern,
52        iterator: Box<Expr>,
53        block: Braces<CodeBlockContents>,
54    },
55    FuncApp {
56        func: Box<Expr>,
57        args: Parens<Punctuated<Expr, CommaToken>>,
58    },
59    Index {
60        target: Box<Expr>,
61        arg: SquareBrackets<Box<Expr>>,
62    },
63    MethodCall {
64        target: Box<Expr>,
65        dot_token: DotToken,
66        path_seg: PathExprSegment,
67        contract_args_opt: Option<Braces<Punctuated<ExprStructField, CommaToken>>>,
68        args: Parens<Punctuated<Expr, CommaToken>>,
69    },
70    FieldProjection {
71        target: Box<Expr>,
72        dot_token: DotToken,
73        name: Ident,
74    },
75    TupleFieldProjection {
76        target: Box<Expr>,
77        dot_token: DotToken,
78        field: BigUint,
79        field_span: Span,
80    },
81    Ref {
82        ampersand_token: AmpersandToken,
83        mut_token: Option<MutToken>,
84        expr: Box<Expr>,
85    },
86    Deref {
87        star_token: StarToken,
88        expr: Box<Expr>,
89    },
90    Not {
91        bang_token: BangToken,
92        expr: Box<Expr>,
93    },
94    Mul {
95        lhs: Box<Expr>,
96        star_token: StarToken,
97        rhs: Box<Expr>,
98    },
99    Div {
100        lhs: Box<Expr>,
101        forward_slash_token: ForwardSlashToken,
102        rhs: Box<Expr>,
103    },
104    Pow {
105        lhs: Box<Expr>,
106        double_star_token: DoubleStarToken,
107        rhs: Box<Expr>,
108    },
109    Modulo {
110        lhs: Box<Expr>,
111        percent_token: PercentToken,
112        rhs: Box<Expr>,
113    },
114    Add {
115        lhs: Box<Expr>,
116        add_token: AddToken,
117        rhs: Box<Expr>,
118    },
119    Sub {
120        lhs: Box<Expr>,
121        sub_token: SubToken,
122        rhs: Box<Expr>,
123    },
124    Shl {
125        lhs: Box<Expr>,
126        shl_token: ShlToken,
127        rhs: Box<Expr>,
128    },
129    Shr {
130        lhs: Box<Expr>,
131        shr_token: ShrToken,
132        rhs: Box<Expr>,
133    },
134    BitAnd {
135        lhs: Box<Expr>,
136        ampersand_token: AmpersandToken,
137        rhs: Box<Expr>,
138    },
139    BitXor {
140        lhs: Box<Expr>,
141        caret_token: CaretToken,
142        rhs: Box<Expr>,
143    },
144    BitOr {
145        lhs: Box<Expr>,
146        pipe_token: PipeToken,
147        rhs: Box<Expr>,
148    },
149    Equal {
150        lhs: Box<Expr>,
151        double_eq_token: DoubleEqToken,
152        rhs: Box<Expr>,
153    },
154    NotEqual {
155        lhs: Box<Expr>,
156        bang_eq_token: BangEqToken,
157        rhs: Box<Expr>,
158    },
159    LessThan {
160        lhs: Box<Expr>,
161        less_than_token: LessThanToken,
162        rhs: Box<Expr>,
163    },
164    GreaterThan {
165        lhs: Box<Expr>,
166        greater_than_token: GreaterThanToken,
167        rhs: Box<Expr>,
168    },
169    LessThanEq {
170        lhs: Box<Expr>,
171        less_than_eq_token: LessThanEqToken,
172        rhs: Box<Expr>,
173    },
174    GreaterThanEq {
175        lhs: Box<Expr>,
176        greater_than_eq_token: GreaterThanEqToken,
177        rhs: Box<Expr>,
178    },
179    LogicalAnd {
180        lhs: Box<Expr>,
181        double_ampersand_token: DoubleAmpersandToken,
182        rhs: Box<Expr>,
183    },
184    LogicalOr {
185        lhs: Box<Expr>,
186        double_pipe_token: DoublePipeToken,
187        rhs: Box<Expr>,
188    },
189    Reassignment {
190        assignable: Assignable,
191        reassignment_op: ReassignmentOp,
192        expr: Box<Expr>,
193    },
194    Break {
195        break_token: BreakToken,
196    },
197    Continue {
198        continue_token: ContinueToken,
199    },
200}
201
202impl Spanned for Expr {
203    fn span(&self) -> Span {
204        match self {
205            Expr::Error(spans, _) => spans
206                .iter()
207                .cloned()
208                .reduce(|s1: Span, s2: Span| Span::join(s1, &s2))
209                .unwrap(),
210            Expr::Path(path_expr) => path_expr.span(),
211            Expr::Literal(literal) => literal.span(),
212            Expr::AbiCast { abi_token, args } => Span::join(abi_token.span(), &args.span()),
213            Expr::Struct { path, fields } => Span::join(path.span(), &fields.span()),
214            Expr::Tuple(tuple_expr) => tuple_expr.span(),
215            Expr::Parens(parens) => parens.span(),
216            Expr::Block(block_expr) => block_expr.span(),
217            Expr::Array(array_expr) => array_expr.span(),
218            Expr::Asm(asm_block) => asm_block.span(),
219            Expr::Return {
220                return_token,
221                expr_opt,
222            } => {
223                let start = return_token.span();
224                let end = match expr_opt {
225                    Some(expr) => expr.span(),
226                    None => return_token.span(),
227                };
228                Span::join(start, &end)
229            }
230            Expr::Panic {
231                panic_token,
232                expr_opt,
233            } => {
234                let start = panic_token.span();
235                let end = match expr_opt {
236                    Some(expr) => expr.span(),
237                    None => panic_token.span(),
238                };
239                Span::join(start, &end)
240            }
241            Expr::If(if_expr) => if_expr.span(),
242            Expr::Match {
243                match_token,
244                branches,
245                ..
246            } => Span::join(match_token.span(), &branches.span()),
247            Expr::While {
248                while_token, block, ..
249            } => Span::join(while_token.span(), &block.span()),
250            Expr::For {
251                for_token, block, ..
252            } => Span::join(for_token.span(), &block.span()),
253            Expr::FuncApp { func, args } => Span::join(func.span(), &args.span()),
254            Expr::Index { target, arg } => Span::join(target.span(), &arg.span()),
255            Expr::MethodCall { target, args, .. } => Span::join(target.span(), &args.span()),
256            Expr::FieldProjection { target, name, .. } => Span::join(target.span(), &name.span()),
257            Expr::TupleFieldProjection {
258                target, field_span, ..
259            } => Span::join(target.span(), field_span),
260            Expr::Ref {
261                ampersand_token,
262                expr,
263                ..
264            } => Span::join(ampersand_token.span(), &expr.span()),
265            Expr::Deref { star_token, expr } => Span::join(star_token.span(), &expr.span()),
266            Expr::Not { bang_token, expr } => Span::join(bang_token.span(), &expr.span()),
267            Expr::Pow { lhs, rhs, .. } => Span::join(lhs.span(), &rhs.span()),
268            Expr::Mul { lhs, rhs, .. } => Span::join(lhs.span(), &rhs.span()),
269            Expr::Div { lhs, rhs, .. } => Span::join(lhs.span(), &rhs.span()),
270            Expr::Modulo { lhs, rhs, .. } => Span::join(lhs.span(), &rhs.span()),
271            Expr::Add { lhs, rhs, .. } => Span::join(lhs.span(), &rhs.span()),
272            Expr::Sub { lhs, rhs, .. } => Span::join(lhs.span(), &rhs.span()),
273            Expr::Shl { lhs, rhs, .. } => Span::join(lhs.span(), &rhs.span()),
274            Expr::Shr { lhs, rhs, .. } => Span::join(lhs.span(), &rhs.span()),
275            Expr::BitAnd { lhs, rhs, .. } => Span::join(lhs.span(), &rhs.span()),
276            Expr::BitXor { lhs, rhs, .. } => Span::join(lhs.span(), &rhs.span()),
277            Expr::BitOr { lhs, rhs, .. } => Span::join(lhs.span(), &rhs.span()),
278            Expr::Equal { lhs, rhs, .. } => Span::join(lhs.span(), &rhs.span()),
279            Expr::NotEqual { lhs, rhs, .. } => Span::join(lhs.span(), &rhs.span()),
280            Expr::LessThan { lhs, rhs, .. } => Span::join(lhs.span(), &rhs.span()),
281            Expr::GreaterThan { lhs, rhs, .. } => Span::join(lhs.span(), &rhs.span()),
282            Expr::LessThanEq { lhs, rhs, .. } => Span::join(lhs.span(), &rhs.span()),
283            Expr::GreaterThanEq { lhs, rhs, .. } => Span::join(lhs.span(), &rhs.span()),
284            Expr::LogicalAnd { lhs, rhs, .. } => Span::join(lhs.span(), &rhs.span()),
285            Expr::LogicalOr { lhs, rhs, .. } => Span::join(lhs.span(), &rhs.span()),
286            Expr::Reassignment {
287                assignable, expr, ..
288            } => Span::join(assignable.span(), &expr.span()),
289            Expr::Break { break_token } => break_token.span(),
290            Expr::Continue { continue_token } => continue_token.span(),
291        }
292    }
293}
294
295#[derive(Clone, Debug, Serialize)]
296pub struct ReassignmentOp {
297    pub variant: ReassignmentOpVariant,
298    pub span: Span,
299}
300
301#[derive(Clone, Debug, Serialize)]
302pub enum ReassignmentOpVariant {
303    Equals,
304    AddEquals,
305    SubEquals,
306    MulEquals,
307    DivEquals,
308    ShlEquals,
309    ShrEquals,
310}
311
312impl ReassignmentOpVariant {
313    pub fn std_name(&self) -> &'static str {
314        match self {
315            ReassignmentOpVariant::Equals => "eq",
316            ReassignmentOpVariant::AddEquals => "add",
317            ReassignmentOpVariant::SubEquals => "subtract",
318            ReassignmentOpVariant::MulEquals => "multiply",
319            ReassignmentOpVariant::DivEquals => "divide",
320            ReassignmentOpVariant::ShlEquals => "lsh",
321            ReassignmentOpVariant::ShrEquals => "rsh",
322        }
323    }
324
325    pub fn as_str(&self) -> &'static str {
326        match self {
327            ReassignmentOpVariant::Equals => EqToken::AS_STR,
328            ReassignmentOpVariant::AddEquals => AddEqToken::AS_STR,
329            ReassignmentOpVariant::SubEquals => SubEqToken::AS_STR,
330            ReassignmentOpVariant::MulEquals => StarEqToken::AS_STR,
331            ReassignmentOpVariant::DivEquals => DivEqToken::AS_STR,
332            ReassignmentOpVariant::ShlEquals => ShlEqToken::AS_STR,
333            ReassignmentOpVariant::ShrEquals => ShrEqToken::AS_STR,
334        }
335    }
336}
337
338#[derive(Clone, Debug, Serialize)]
339pub struct AbiCastArgs {
340    pub name: PathType,
341    pub comma_token: CommaToken,
342    pub address: Box<Expr>,
343}
344
345#[allow(clippy::type_complexity)]
346#[derive(Clone, Debug, Serialize)]
347pub struct IfExpr {
348    pub if_token: IfToken,
349    pub condition: IfCondition,
350    pub then_block: Braces<CodeBlockContents>,
351    pub else_opt: Option<(
352        ElseToken,
353        LoopControlFlow<Braces<CodeBlockContents>, Box<IfExpr>>,
354    )>,
355}
356
357#[derive(Clone, Debug, Serialize)]
358pub enum IfCondition {
359    Expr(Box<Expr>),
360    Let {
361        let_token: LetToken,
362        lhs: Box<Pattern>,
363        eq_token: EqToken,
364        rhs: Box<Expr>,
365    },
366}
367
368#[derive(Clone, Debug, Serialize)]
369pub enum LoopControlFlow<B, C = ()> {
370    Continue(C),
371    Break(B),
372}
373
374impl Spanned for IfExpr {
375    fn span(&self) -> Span {
376        let start = self.if_token.span();
377        let end = match &self.else_opt {
378            Some((_else_token, tail)) => match tail {
379                LoopControlFlow::Break(block) => block.span(),
380                LoopControlFlow::Continue(if_expr) => if_expr.span(),
381            },
382            None => self.then_block.span(),
383        };
384        Span::join(start, &end)
385    }
386}
387
388#[derive(Clone, Debug, Serialize)]
389pub enum ExprTupleDescriptor {
390    Nil,
391    Cons {
392        head: Box<Expr>,
393        comma_token: CommaToken,
394        tail: Punctuated<Expr, CommaToken>,
395    },
396}
397
398#[derive(Clone, Debug, Serialize)]
399pub enum ExprArrayDescriptor {
400    Sequence(Punctuated<Expr, CommaToken>),
401    Repeat {
402        value: Box<Expr>,
403        semicolon_token: SemicolonToken,
404        length: Box<Expr>,
405    },
406}
407
408#[derive(Clone, Debug, Serialize)]
409pub struct MatchBranch {
410    pub pattern: Pattern,
411    pub fat_right_arrow_token: FatRightArrowToken,
412    pub kind: MatchBranchKind,
413}
414
415impl Spanned for MatchBranch {
416    fn span(&self) -> Span {
417        Span::join(self.pattern.span(), &self.kind.span())
418    }
419}
420
421#[allow(clippy::large_enum_variant)]
422#[derive(Debug, Clone, Serialize)]
423pub enum MatchBranchKind {
424    Block {
425        block: Braces<CodeBlockContents>,
426        comma_token_opt: Option<CommaToken>,
427    },
428    Expr {
429        expr: Expr,
430        comma_token: CommaToken,
431    },
432}
433
434impl Spanned for MatchBranchKind {
435    fn span(&self) -> Span {
436        match self {
437            MatchBranchKind::Block {
438                block,
439                comma_token_opt,
440            } => match comma_token_opt {
441                Some(comma_token) => Span::join(block.span(), &comma_token.span()),
442                None => block.span(),
443            },
444            MatchBranchKind::Expr { expr, comma_token } => {
445                Span::join(expr.span(), &comma_token.span())
446            }
447        }
448    }
449}
450
451#[derive(Clone, Debug, Serialize)]
452pub struct CodeBlockContents {
453    pub statements: Vec<Statement>,
454    pub final_expr_opt: Option<Box<Expr>>,
455    pub span: Span,
456}
457
458impl Spanned for CodeBlockContents {
459    fn span(&self) -> Span {
460        self.span.clone()
461    }
462}
463
464#[derive(Clone, Debug, Serialize)]
465pub struct ExprStructField {
466    pub field_name: Ident,
467    pub expr_opt: Option<(ColonToken, Box<Expr>)>,
468}
469
470impl Spanned for ExprStructField {
471    fn span(&self) -> Span {
472        match &self.expr_opt {
473            None => self.field_name.span(),
474            Some((_colon_token, expr)) => Span::join(self.field_name.span(), &expr.span()),
475        }
476    }
477}
478
479impl Expr {
480    /// Returns the resulting [Assignable] if the `self` is a
481    /// valid [Assignable], or an error containing the [Expr]
482    /// which causes the `self` to be an invalid [Assignable].
483    ///
484    /// In case of an error, the returned [Expr] can be `self`
485    /// or any subexpression of `self` that is not allowed
486    /// in assignment targets.
487    pub fn try_into_assignable(self) -> Result<Assignable, Expr> {
488        if let Expr::Deref { star_token, expr } = self {
489            Ok(Assignable::Deref { star_token, expr })
490        } else {
491            Ok(Assignable::ElementAccess(
492                self.try_into_element_access(false)?,
493            ))
494        }
495    }
496
497    fn try_into_element_access(
498        self,
499        accept_deref_without_parens: bool,
500    ) -> Result<ElementAccess, Expr> {
501        match self.clone() {
502            Expr::Path(path_expr) => match path_expr.try_into_ident() {
503                Ok(name) => Ok(ElementAccess::Var(name)),
504                Err(path_expr) => Err(Expr::Path(path_expr)),
505            },
506            Expr::Index { target, arg } => match target.try_into_element_access(false) {
507                Ok(target) => Ok(ElementAccess::Index {
508                    target: Box::new(target),
509                    arg,
510                }),
511                error => error,
512            },
513            Expr::FieldProjection {
514                target,
515                dot_token,
516                name,
517            } => match target.try_into_element_access(false) {
518                Ok(target) => Ok(ElementAccess::FieldProjection {
519                    target: Box::new(target),
520                    dot_token,
521                    name,
522                }),
523                error => error,
524            },
525            Expr::TupleFieldProjection {
526                target,
527                dot_token,
528                field,
529                field_span,
530            } => match target.try_into_element_access(false) {
531                Ok(target) => Ok(ElementAccess::TupleFieldProjection {
532                    target: Box::new(target),
533                    dot_token,
534                    field,
535                    field_span,
536                }),
537                error => error,
538            },
539            Expr::Parens(Parens { inner, .. }) => {
540                if let Expr::Deref { expr, star_token } = *inner {
541                    match expr.try_into_element_access(true) {
542                        Ok(target) => Ok(ElementAccess::Deref {
543                            target: Box::new(target),
544                            star_token,
545                            is_root_element: true,
546                        }),
547                        error => error,
548                    }
549                } else {
550                    Err(self)
551                }
552            }
553            Expr::Deref { expr, star_token } if accept_deref_without_parens => {
554                match expr.try_into_element_access(true) {
555                    Ok(target) => Ok(ElementAccess::Deref {
556                        target: Box::new(target),
557                        star_token,
558                        is_root_element: false,
559                    }),
560                    error => error,
561                }
562            }
563            expr => Err(expr),
564        }
565    }
566
567    pub fn is_control_flow(&self) -> bool {
568        match self {
569            Expr::Block(..)
570            | Expr::Asm(..)
571            | Expr::If(..)
572            | Expr::Match { .. }
573            | Expr::While { .. }
574            | Expr::For { .. } => true,
575            Expr::Error(..)
576            | Expr::Path(..)
577            | Expr::Literal(..)
578            | Expr::AbiCast { .. }
579            | Expr::Struct { .. }
580            | Expr::Tuple(..)
581            | Expr::Parens(..)
582            | Expr::Array(..)
583            | Expr::Return { .. }
584            | Expr::Panic { .. }
585            | Expr::FuncApp { .. }
586            | Expr::Index { .. }
587            | Expr::MethodCall { .. }
588            | Expr::FieldProjection { .. }
589            | Expr::TupleFieldProjection { .. }
590            | Expr::Ref { .. }
591            | Expr::Deref { .. }
592            | Expr::Not { .. }
593            | Expr::Mul { .. }
594            | Expr::Div { .. }
595            | Expr::Pow { .. }
596            | Expr::Modulo { .. }
597            | Expr::Add { .. }
598            | Expr::Sub { .. }
599            | Expr::Shl { .. }
600            | Expr::Shr { .. }
601            | Expr::BitAnd { .. }
602            | Expr::BitXor { .. }
603            | Expr::BitOr { .. }
604            | Expr::Equal { .. }
605            | Expr::NotEqual { .. }
606            | Expr::LessThan { .. }
607            | Expr::GreaterThan { .. }
608            | Expr::LessThanEq { .. }
609            | Expr::GreaterThanEq { .. }
610            | Expr::LogicalAnd { .. }
611            | Expr::LogicalOr { .. }
612            | Expr::Reassignment { .. }
613            | Expr::Break { .. }
614            | Expr::Continue { .. } => false,
615        }
616    }
617
618    /// Friendly [Expr] name string used for error reporting,
619    pub fn friendly_name(&self) -> &'static str {
620        match self {
621            Expr::Error(_, _) => "error",
622            Expr::Path(_) => "path",
623            Expr::Literal(_) => "literal",
624            Expr::AbiCast { .. } => "ABI cast",
625            Expr::Struct { .. } => "struct instantiation",
626            Expr::Tuple(_) => "tuple",
627            Expr::Parens(_) => "parentheses", // Note the plural!
628            Expr::Block(_) => "block",
629            Expr::Array(_) => "array",
630            Expr::Asm(_) => "assembly block",
631            Expr::Return { .. } => "return",
632            Expr::Panic { .. } => "panic",
633            Expr::If(_) => "if expression",
634            Expr::Match { .. } => "match expression",
635            Expr::While { .. } => "while loop",
636            Expr::For { .. } => "for loop",
637            Expr::FuncApp { .. } => "function call",
638            Expr::Index { .. } => "array element access",
639            Expr::MethodCall { .. } => "method call",
640            Expr::FieldProjection { .. } => "struct field access",
641            Expr::TupleFieldProjection { .. } => "tuple element access",
642            Expr::Ref { .. } => "referencing",
643            Expr::Deref { .. } => "dereferencing",
644            Expr::Not { .. } => "negation",
645            Expr::Mul { .. } => "multiplication",
646            Expr::Div { .. } => "division",
647            Expr::Pow { .. } => "power operation",
648            Expr::Modulo { .. } => "modulo operation",
649            Expr::Add { .. } => "addition",
650            Expr::Sub { .. } => "subtraction",
651            Expr::Shl { .. } => "left shift",
652            Expr::Shr { .. } => "right shift",
653            Expr::BitAnd { .. } => "bitwise and",
654            Expr::BitXor { .. } => "bitwise xor",
655            Expr::BitOr { .. } => "bitwise or",
656            Expr::Equal { .. } => "equality",
657            Expr::NotEqual { .. } => "non equality",
658            Expr::LessThan { .. } => "less than operation",
659            Expr::GreaterThan { .. } => "greater than operation",
660            Expr::LessThanEq { .. } => "less than or equal operation",
661            Expr::GreaterThanEq { .. } => "greater than or equal operation",
662            Expr::LogicalAnd { .. } => "logical and",
663            Expr::LogicalOr { .. } => "logical or",
664            Expr::Reassignment { .. } => "reassignment",
665            Expr::Break { .. } => "break",
666            Expr::Continue { .. } => "continue",
667        }
668    }
669}