solar-ast 0.2.0

Solidity and Yul AST type and visitor trait definitions
Documentation
//! Yul AST.

use crate::{AstPath, Box, BoxSlice, DocComments, Lit, StrLit};
use solar_interface::{Ident, Span};

/// A block of Yul statements: `{ ... }`.
///
/// Reference: <https://docs.soliditylang.org/en/latest/grammar.html#a4.SolidityParser.yulBlock>
#[derive(Debug)]
pub struct Block<'ast> {
    /// The span of the block, including the `{` and `}`.
    pub span: Span,
    /// The statements in the block.
    pub stmts: BoxSlice<'ast, Stmt<'ast>>,
}

impl<'ast> std::ops::Deref for Block<'ast> {
    type Target = [Stmt<'ast>];

    fn deref(&self) -> &Self::Target {
        self.stmts
    }
}

impl<'ast> std::ops::DerefMut for Block<'ast> {
    fn deref_mut(&mut self) -> &mut Self::Target {
        self.stmts
    }
}

/// A Yul object.
///
/// Reference: <https://docs.soliditylang.org/en/latest/yul.html#specification-of-yul-object>
#[derive(Debug)]
pub struct Object<'ast> {
    /// The doc-comments of the object.
    pub docs: DocComments<'ast>,
    /// The span of the object, including the `object` keyword, but excluding the doc-comments.
    pub span: Span,
    /// The name of the object.
    pub name: StrLit,
    /// The `code` block.
    pub code: CodeBlock<'ast>,
    /// Sub-objects, if any.
    pub children: BoxSlice<'ast, Self>,
    /// `data` segments, if any.
    pub data: BoxSlice<'ast, Data<'ast>>,
}

/// A Yul `code` block. See [`Object`].
#[derive(Debug)]
pub struct CodeBlock<'ast> {
    /// The span of the code block, including the `code` keyword.
    ///
    /// The `code` keyword may not be present in the source code if the object is parsed as a
    /// plain [`Block`].
    pub span: Span,
    /// The `code` block.
    pub code: Block<'ast>,
}

/// A Yul `data` segment. See [`Object`].
#[derive(Debug)]
pub struct Data<'ast> {
    /// The span of the code block, including the `data` keyword.
    pub span: Span,
    /// The name of the data segment.
    pub name: StrLit,
    /// The data. Can only be a `Str` or `HexStr` literal.
    pub data: Lit<'ast>,
}

/// A Yul statement.
///
/// Reference: <https://docs.soliditylang.org/en/latest/grammar.html#a4.SolidityParser.yulStatement>
#[derive(Debug)]
pub struct Stmt<'ast> {
    /// The doc-comments of the statement.
    pub docs: DocComments<'ast>,
    /// The span of the statement.
    pub span: Span,
    /// The kind of statement.
    pub kind: StmtKind<'ast>,
}

/// A kind of Yul statement.
#[derive(Debug)]
pub enum StmtKind<'ast> {
    /// A blocked scope: `{ ... }`.
    ///
    /// Reference: <https://docs.soliditylang.org/en/latest/grammar.html#a4.SolidityParser.yulBlock>
    Block(Block<'ast>),

    /// A single-variable assignment statement: `x := 1`.
    ///
    /// Reference: <https://docs.soliditylang.org/en/latest/grammar.html#a4.SolidityParser.yulAssignment>
    AssignSingle(AstPath<'ast>, Expr<'ast>),

    /// A multiple-variable assignment statement: `x, y, z := foo(1, 2)`.
    ///
    /// Multi-assignments require a function call on the right-hand side.
    ///
    /// Reference: <https://docs.soliditylang.org/en/latest/grammar.html#a4.SolidityParser.yulAssignment>
    AssignMulti(BoxSlice<'ast, AstPath<'ast>>, Expr<'ast>),

    /// An expression statement. This can only be a function call.
    Expr(Expr<'ast>),

    /// An if statement: `if lt(a, b) { ... }`.
    ///
    /// Reference: <https://docs.soliditylang.org/en/latest/grammar.html#a4.SolidityParser.yulIfStatement>
    If(Expr<'ast>, Block<'ast>),

    /// A for statement: `for {let i := 0} lt(i,10) {i := add(i,1)} { ... }`.
    ///
    /// Reference: <https://docs.soliditylang.org/en/latest/grammar.html#a4.SolidityParser.yulForStatement>
    ///
    /// Breakdown of parts: <https://docs.soliditylang.org/en/latest/yul.html#loops>
    For(Box<'ast, StmtFor<'ast>>),

    /// A switch statement: `switch expr case 0 { ... } default { ... }`.
    ///
    /// Reference: <https://docs.soliditylang.org/en/latest/grammar.html#a4.SolidityParser.yulSwitchStatement>
    Switch(StmtSwitch<'ast>),

    /// A leave statement: `leave`.
    Leave,

    /// A break statement: `break`.
    Break,

    /// A continue statement: `continue`.
    Continue,

    /// A function definition statement: `function f() { ... }`.
    FunctionDef(Function<'ast>),

    /// A variable declaration statement: `let x := 0`.
    ///
    /// Reference: <https://docs.soliditylang.org/en/latest/grammar.html#a4.SolidityParser.yulVariableDeclaration>
    VarDecl(BoxSlice<'ast, Ident>, Option<Expr<'ast>>),
}

/// A Yul for statement: `for {let i := 0} lt(i,10) {i := add(i,1)} { ... }`.
///
/// Reference: <https://docs.soliditylang.org/en/latest/grammar.html#a4.SolidityParser.yulForStatement>
///
/// Breakdown of parts: <https://docs.soliditylang.org/en/latest/yul.html#loops>
#[derive(Debug)]
pub struct StmtFor<'ast> {
    pub init: Block<'ast>,
    pub cond: Expr<'ast>,
    pub step: Block<'ast>,
    pub body: Block<'ast>,
}

/// A Yul switch statement can consist of only a default-case or one
/// or more non-default cases optionally followed by a default-case.
///
/// Example switch statement in Yul:
///
/// ```solidity
/// switch exponent
/// case 0 { result := 1 }
/// case 1 { result := base }
/// default { revert(0, 0) }
/// ```
///
/// Reference: <https://docs.soliditylang.org/en/latest/grammar.html#a4.SolidityParser.yulSwitchStatement>
#[derive(Debug)]
pub struct StmtSwitch<'ast> {
    pub selector: Expr<'ast>,
    /// The cases of the switch statement. Includes the default case in the last position, if any.
    pub cases: BoxSlice<'ast, StmtSwitchCase<'ast>>,
}

impl<'ast> StmtSwitch<'ast> {
    /// Returns the default case of the switch statement, if any.
    pub fn default_case(&self) -> Option<&StmtSwitchCase<'ast>> {
        self.cases.last().filter(|case| case.constant.is_none())
    }
}

/// Represents a non-default case of a Yul switch statement.
///
/// See [`StmtSwitch`] for more information.
#[derive(Debug)]
pub struct StmtSwitchCase<'ast> {
    pub span: Span,
    /// The constant of the case, if any. `None` for the default case.
    pub constant: Option<Lit<'ast>>,
    pub body: Block<'ast>,
}

/// Yul function definition: `function f() -> a, b { ... }`.
///
/// Reference: <https://docs.soliditylang.org/en/latest/grammar.html#a4.SolidityParser.yulFunctionDefinition>
#[derive(Debug)]
pub struct Function<'ast> {
    pub name: Ident,
    pub parameters: BoxSlice<'ast, Ident>,
    pub returns: BoxSlice<'ast, Ident>,
    pub body: Block<'ast>,
}

/// A Yul expression.
#[derive(Debug)]
pub struct Expr<'ast> {
    /// The span of the expression.
    pub span: Span,
    /// The kind of expression.
    pub kind: ExprKind<'ast>,
}

/// A kind of Yul expression.
#[derive(Debug)]
pub enum ExprKind<'ast> {
    /// A single path.
    Path(AstPath<'ast>),
    /// A function call: `foo(a, b)`.
    Call(ExprCall<'ast>),
    /// A literal.
    Lit(Box<'ast, Lit<'ast>>),
}

/// A Yul function call expression: `foo(a, b)`.
///
/// Reference: <https://docs.soliditylang.org/en/latest/grammar.html#a4.SolidityParser.yulFunctionCall>
#[derive(Debug)]
pub struct ExprCall<'ast> {
    pub name: Ident,
    pub arguments: BoxSlice<'ast, Expr<'ast>>,
}