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