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(self.try_into_element_access()?))
477        }
478    }
479
480    fn try_into_element_access(self) -> Result<ElementAccess, Expr> {
481        match self {
482            Expr::Path(path_expr) => match path_expr.try_into_ident() {
483                Ok(name) => Ok(ElementAccess::Var(name)),
484                Err(path_expr) => Err(Expr::Path(path_expr)),
485            },
486            Expr::Index { target, arg } => match target.try_into_element_access() {
487                Ok(target) => Ok(ElementAccess::Index {
488                    target: Box::new(target),
489                    arg,
490                }),
491                error => error,
492            },
493            Expr::FieldProjection {
494                target,
495                dot_token,
496                name,
497            } => match target.try_into_element_access() {
498                Ok(target) => Ok(ElementAccess::FieldProjection {
499                    target: Box::new(target),
500                    dot_token,
501                    name,
502                }),
503                error => error,
504            },
505            Expr::TupleFieldProjection {
506                target,
507                dot_token,
508                field,
509                field_span,
510            } => match target.try_into_element_access() {
511                Ok(target) => Ok(ElementAccess::TupleFieldProjection {
512                    target: Box::new(target),
513                    dot_token,
514                    field,
515                    field_span,
516                }),
517                error => error,
518            },
519            expr => Err(expr),
520        }
521    }
522
523    pub fn is_control_flow(&self) -> bool {
524        match self {
525            Expr::Block(..)
526            | Expr::Asm(..)
527            | Expr::If(..)
528            | Expr::Match { .. }
529            | Expr::While { .. }
530            | Expr::For { .. } => true,
531            Expr::Error(..)
532            | Expr::Path(..)
533            | Expr::Literal(..)
534            | Expr::AbiCast { .. }
535            | Expr::Struct { .. }
536            | Expr::Tuple(..)
537            | Expr::Parens(..)
538            | Expr::Array(..)
539            | Expr::Return { .. }
540            | Expr::FuncApp { .. }
541            | Expr::Index { .. }
542            | Expr::MethodCall { .. }
543            | Expr::FieldProjection { .. }
544            | Expr::TupleFieldProjection { .. }
545            | Expr::Ref { .. }
546            | Expr::Deref { .. }
547            | Expr::Not { .. }
548            | Expr::Mul { .. }
549            | Expr::Div { .. }
550            | Expr::Pow { .. }
551            | Expr::Modulo { .. }
552            | Expr::Add { .. }
553            | Expr::Sub { .. }
554            | Expr::Shl { .. }
555            | Expr::Shr { .. }
556            | Expr::BitAnd { .. }
557            | Expr::BitXor { .. }
558            | Expr::BitOr { .. }
559            | Expr::Equal { .. }
560            | Expr::NotEqual { .. }
561            | Expr::LessThan { .. }
562            | Expr::GreaterThan { .. }
563            | Expr::LessThanEq { .. }
564            | Expr::GreaterThanEq { .. }
565            | Expr::LogicalAnd { .. }
566            | Expr::LogicalOr { .. }
567            | Expr::Reassignment { .. }
568            | Expr::Break { .. }
569            | Expr::Continue { .. } => false,
570        }
571    }
572
573    /// Friendly [Expr] name string used for error reporting,
574    pub fn friendly_name(&self) -> &'static str {
575        match self {
576            Expr::Error(_, _) => "error",
577            Expr::Path(_) => "path",
578            Expr::Literal(_) => "literal",
579            Expr::AbiCast { .. } => "ABI cast",
580            Expr::Struct { .. } => "struct instantiation",
581            Expr::Tuple(_) => "tuple",
582            Expr::Parens(_) => "parentheses", // Note the plural!
583            Expr::Block(_) => "block",
584            Expr::Array(_) => "array",
585            Expr::Asm(_) => "assembly block",
586            Expr::Return { .. } => "return",
587            Expr::If(_) => "if expression",
588            Expr::Match { .. } => "match expression",
589            Expr::While { .. } => "while loop",
590            Expr::For { .. } => "for loop",
591            Expr::FuncApp { .. } => "function call",
592            Expr::Index { .. } => "array element access",
593            Expr::MethodCall { .. } => "method call",
594            Expr::FieldProjection { .. } => "struct field access",
595            Expr::TupleFieldProjection { .. } => "tuple element access",
596            Expr::Ref { .. } => "referencing",
597            Expr::Deref { .. } => "dereferencing",
598            Expr::Not { .. } => "negation",
599            Expr::Mul { .. } => "multiplication",
600            Expr::Div { .. } => "division",
601            Expr::Pow { .. } => "power operation",
602            Expr::Modulo { .. } => "modulo operation",
603            Expr::Add { .. } => "addition",
604            Expr::Sub { .. } => "subtraction",
605            Expr::Shl { .. } => "left shift",
606            Expr::Shr { .. } => "right shift",
607            Expr::BitAnd { .. } => "bitwise and",
608            Expr::BitXor { .. } => "bitwise xor",
609            Expr::BitOr { .. } => "bitwise or",
610            Expr::Equal { .. } => "equality",
611            Expr::NotEqual { .. } => "non equality",
612            Expr::LessThan { .. } => "less than operation",
613            Expr::GreaterThan { .. } => "greater than operation",
614            Expr::LessThanEq { .. } => "less than or equal operation",
615            Expr::GreaterThanEq { .. } => "greater than or equal operation",
616            Expr::LogicalAnd { .. } => "logical and",
617            Expr::LogicalOr { .. } => "logical or",
618            Expr::Reassignment { .. } => "reassignment",
619            Expr::Break { .. } => "break",
620            Expr::Continue { .. } => "continue",
621        }
622    }
623}