mirror of
https://github.com/pezkuwichain/revive.git
synced 2026-06-09 20:01:05 +00:00
YUL tree visitor interface (#369)
- Implement the visitor interface. This simplifies working with the AST, for example transformations into other IRs or collecting and analyzing various statistics. - Switch the explorer to use the visitor interface. - Add the reciprocal function name conversion for function names. - Some drive-by cosmetic fixes. --------- Signed-off-by: xermicus <bigcyrill@hotmail.com>
This commit is contained in:
@@ -15,6 +15,8 @@ use crate::lexer::Lexer;
|
||||
use crate::parser::error::Error as ParserError;
|
||||
use crate::parser::identifier::Identifier;
|
||||
use crate::parser::statement::expression::Expression;
|
||||
use crate::visitor::AstNode;
|
||||
use crate::visitor::AstVisitor;
|
||||
|
||||
/// The Yul assignment expression statement.
|
||||
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
|
||||
@@ -184,3 +186,20 @@ where
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl AstNode for Assignment {
|
||||
fn accept(&self, ast_visitor: &mut impl AstVisitor) {
|
||||
ast_visitor.visit_assignment(self);
|
||||
}
|
||||
|
||||
fn visit_children(&self, ast_visitor: &mut impl AstVisitor) {
|
||||
for binding in &self.bindings {
|
||||
binding.accept(ast_visitor);
|
||||
}
|
||||
self.initializer.accept(ast_visitor);
|
||||
}
|
||||
|
||||
fn location(&self) -> Location {
|
||||
self.location
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,8 @@ use crate::parser::error::Error as ParserError;
|
||||
use crate::parser::statement::assignment::Assignment;
|
||||
use crate::parser::statement::expression::Expression;
|
||||
use crate::parser::statement::Statement;
|
||||
use crate::visitor::AstNode;
|
||||
use crate::visitor::AstVisitor;
|
||||
|
||||
/// The Yul source code block.
|
||||
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
|
||||
@@ -226,6 +228,22 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl AstNode for Block {
|
||||
fn accept(&self, ast_visitor: &mut impl AstVisitor) {
|
||||
ast_visitor.visit_block(self);
|
||||
}
|
||||
|
||||
fn visit_children(&self, ast_visitor: &mut impl AstVisitor) {
|
||||
for statement in &self.statements {
|
||||
statement.accept(ast_visitor);
|
||||
}
|
||||
}
|
||||
|
||||
fn location(&self) -> Location {
|
||||
self.location
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::lexer::token::location::Location;
|
||||
|
||||
@@ -13,6 +13,8 @@ use crate::lexer::token::Token;
|
||||
use crate::lexer::Lexer;
|
||||
use crate::parser::error::Error as ParserError;
|
||||
use crate::parser::statement::block::Block;
|
||||
use crate::visitor::AstNode;
|
||||
use crate::visitor::AstVisitor;
|
||||
|
||||
/// The YUL code entity, which is the first block of the object.
|
||||
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
|
||||
@@ -66,6 +68,20 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl AstNode for Code {
|
||||
fn accept(&self, ast_visitor: &mut impl AstVisitor) {
|
||||
ast_visitor.visit_code(self);
|
||||
}
|
||||
|
||||
fn visit_children(&self, ast_visitor: &mut impl AstVisitor) {
|
||||
self.block.accept(ast_visitor);
|
||||
}
|
||||
|
||||
fn location(&self) -> Location {
|
||||
self.location
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::lexer::token::location::Location;
|
||||
|
||||
@@ -19,6 +19,8 @@ use crate::lexer::Lexer;
|
||||
use crate::parser::error::Error as ParserError;
|
||||
use crate::parser::statement::expression::literal::Literal;
|
||||
use crate::parser::statement::expression::Expression;
|
||||
use crate::visitor::AstNode;
|
||||
use crate::visitor::AstVisitor;
|
||||
|
||||
use self::name::Name;
|
||||
|
||||
@@ -1021,3 +1023,19 @@ impl FunctionCall {
|
||||
Ok(arguments.try_into().expect("Always successful"))
|
||||
}
|
||||
}
|
||||
|
||||
impl AstNode for FunctionCall {
|
||||
fn accept(&self, ast_visitor: &mut impl AstVisitor) {
|
||||
ast_visitor.visit_function_call(self);
|
||||
}
|
||||
|
||||
fn visit_children(&self, ast_visitor: &mut impl AstVisitor) {
|
||||
for argument in &self.arguments {
|
||||
argument.accept(ast_visitor);
|
||||
}
|
||||
}
|
||||
|
||||
fn location(&self) -> Location {
|
||||
self.location
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
//! The function name.
|
||||
|
||||
use std::fmt;
|
||||
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
|
||||
/// The function name.
|
||||
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
pub enum Name {
|
||||
/// The user-defined function.
|
||||
UserDefined(String),
|
||||
@@ -356,3 +359,130 @@ impl From<&str> for Name {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Name {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
if let Self::Verbatim {
|
||||
input_size,
|
||||
output_size,
|
||||
} = self
|
||||
{
|
||||
return write!(f, "verbatim_{input_size}i_{output_size}o");
|
||||
}
|
||||
|
||||
let token = match self {
|
||||
Self::Add => "add",
|
||||
Self::Sub => "sub",
|
||||
Self::Mul => "mul",
|
||||
Self::Div => "div",
|
||||
Self::Mod => "mod",
|
||||
Self::Sdiv => "sdiv",
|
||||
Self::Smod => "smod",
|
||||
|
||||
Self::Lt => "lt",
|
||||
Self::Gt => "gt",
|
||||
Self::Eq => "eq",
|
||||
Self::IsZero => "iszero",
|
||||
Self::Slt => "slt",
|
||||
Self::Sgt => "sgt",
|
||||
|
||||
Self::Or => "or",
|
||||
Self::Xor => "xor",
|
||||
Self::Not => "not",
|
||||
Self::And => "and",
|
||||
Self::Shl => "shl",
|
||||
Self::Shr => "shr",
|
||||
Self::Sar => "sar",
|
||||
Self::Byte => "byte",
|
||||
Self::Pop => "pop",
|
||||
|
||||
Self::AddMod => "addmod",
|
||||
Self::MulMod => "mulmod",
|
||||
Self::Exp => "exp",
|
||||
Self::SignExtend => "signextend",
|
||||
|
||||
Self::Keccak256 => "keccak256",
|
||||
|
||||
Self::MLoad => "mload",
|
||||
Self::MStore => "mstore",
|
||||
Self::MStore8 => "mstore8",
|
||||
Self::MCopy => "mcopy",
|
||||
|
||||
Self::SLoad => "sload",
|
||||
Self::SStore => "sstore",
|
||||
Self::TLoad => "tload",
|
||||
Self::TStore => "tstore",
|
||||
Self::LoadImmutable => "loadimmutable",
|
||||
Self::SetImmutable => "setimmutable",
|
||||
|
||||
Self::CallDataLoad => "calldataload",
|
||||
Self::CallDataSize => "calldatasize",
|
||||
Self::CallDataCopy => "calldatacopy",
|
||||
Self::CodeSize => "codesize",
|
||||
Self::CodeCopy => "codecopy",
|
||||
Self::ReturnDataSize => "returndatasize",
|
||||
Self::ReturnDataCopy => "returndatacopy",
|
||||
Self::ExtCodeSize => "extcodesize",
|
||||
Self::ExtCodeHash => "extcodehash",
|
||||
|
||||
Self::Return => "return",
|
||||
Self::Revert => "revert",
|
||||
|
||||
Self::Log0 => "log0",
|
||||
Self::Log1 => "log1",
|
||||
Self::Log2 => "log2",
|
||||
Self::Log3 => "log3",
|
||||
Self::Log4 => "log4",
|
||||
|
||||
Self::Call => "call",
|
||||
Self::DelegateCall => "delegatecall",
|
||||
Self::StaticCall => "staticcall",
|
||||
Self::Create => "create",
|
||||
Self::Create2 => "create2",
|
||||
|
||||
Self::DataSize => "datasize",
|
||||
Self::DataOffset => "dataoffset",
|
||||
Self::DataCopy => "datacopy",
|
||||
|
||||
Self::Stop => "stop",
|
||||
Self::Invalid => "invalid",
|
||||
|
||||
Self::LinkerSymbol => "linkersymbol",
|
||||
Self::MemoryGuard => "memoryguard",
|
||||
|
||||
Self::Address => "address",
|
||||
Self::Caller => "caller",
|
||||
|
||||
Self::CallValue => "callvalue",
|
||||
Self::Gas => "gas",
|
||||
Self::Balance => "balance",
|
||||
Self::SelfBalance => "selfbalance",
|
||||
|
||||
Self::GasLimit => "gaslimit",
|
||||
Self::GasPrice => "gasprice",
|
||||
Self::Origin => "origin",
|
||||
Self::ChainId => "chainid",
|
||||
Self::Timestamp => "timestamp",
|
||||
Self::Number => "number",
|
||||
Self::BlockHash => "blockhash",
|
||||
Self::BlobHash => "blobhash",
|
||||
Self::Difficulty => "difficulty",
|
||||
Self::Prevrandao => "prevrandao",
|
||||
Self::CoinBase => "coinbase",
|
||||
Self::BaseFee => "basefee",
|
||||
Self::BlobBaseFee => "blobbasefee",
|
||||
Self::MSize => "msize",
|
||||
|
||||
Self::CallCode => "callcode",
|
||||
Self::Pc => "pc",
|
||||
Self::ExtCodeCopy => "extcodecopy",
|
||||
Self::SelfDestruct => "selfdestruct",
|
||||
|
||||
Self::UserDefined(s) => s.as_str(),
|
||||
|
||||
Self::Verbatim { .. } => unreachable!(),
|
||||
};
|
||||
|
||||
write!(f, "{token}")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,8 @@ use crate::lexer::token::Token;
|
||||
use crate::lexer::Lexer;
|
||||
use crate::parser::error::Error as ParserError;
|
||||
use crate::parser::r#type::Type;
|
||||
use crate::visitor::AstNode;
|
||||
use crate::visitor::AstVisitor;
|
||||
|
||||
/// Represents a literal in YUL without differentiating its type.
|
||||
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
|
||||
@@ -221,3 +223,15 @@ impl Literal {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AstNode for Literal {
|
||||
fn accept(&self, ast_visitor: &mut impl AstVisitor) {
|
||||
ast_visitor.visit_literal(self);
|
||||
}
|
||||
|
||||
fn visit_children(&self, _ast_visitor: &mut impl AstVisitor) {}
|
||||
|
||||
fn location(&self) -> Location {
|
||||
self.location
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,8 @@ use crate::lexer::token::Token;
|
||||
use crate::lexer::Lexer;
|
||||
use crate::parser::error::Error as ParserError;
|
||||
use crate::parser::identifier::Identifier;
|
||||
use crate::visitor::AstNode;
|
||||
use crate::visitor::AstVisitor;
|
||||
|
||||
use self::function_call::FunctionCall;
|
||||
use self::literal::Literal;
|
||||
@@ -144,3 +146,21 @@ impl Expression {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AstNode for Expression {
|
||||
fn accept(&self, ast_visitor: &mut impl AstVisitor) {
|
||||
ast_visitor.visit_expression(self);
|
||||
}
|
||||
|
||||
fn visit_children(&self, ast_visitor: &mut impl AstVisitor) {
|
||||
match self {
|
||||
Self::FunctionCall(inner) => inner.accept(ast_visitor),
|
||||
Self::Identifier(inner) => inner.accept(ast_visitor),
|
||||
Self::Literal(inner) => inner.accept(ast_visitor),
|
||||
}
|
||||
}
|
||||
|
||||
fn location(&self) -> Location {
|
||||
self.location()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,8 @@ use crate::lexer::token::Token;
|
||||
use crate::lexer::Lexer;
|
||||
use crate::parser::statement::block::Block;
|
||||
use crate::parser::statement::expression::Expression;
|
||||
use crate::visitor::AstNode;
|
||||
use crate::visitor::AstVisitor;
|
||||
|
||||
/// The Yul for-loop statement.
|
||||
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
|
||||
@@ -113,3 +115,20 @@ where
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl AstNode for ForLoop {
|
||||
fn accept(&self, ast_visitor: &mut impl AstVisitor) {
|
||||
ast_visitor.visit_for_loop(self);
|
||||
}
|
||||
|
||||
fn visit_children(&self, ast_visitor: &mut impl AstVisitor) {
|
||||
self.initializer.accept(ast_visitor);
|
||||
self.condition.accept(ast_visitor);
|
||||
self.finalizer.accept(ast_visitor);
|
||||
self.body.accept(ast_visitor);
|
||||
}
|
||||
|
||||
fn location(&self) -> Location {
|
||||
self.location
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,8 @@ use crate::parser::error::Error as ParserError;
|
||||
use crate::parser::identifier::Identifier;
|
||||
use crate::parser::statement::block::Block;
|
||||
use crate::parser::statement::expression::function_call::name::Name as FunctionName;
|
||||
use crate::visitor::AstNode;
|
||||
use crate::visitor::AstVisitor;
|
||||
|
||||
/// The function definition statement.
|
||||
/// All functions are translated in two steps:
|
||||
@@ -329,6 +331,28 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl AstNode for FunctionDefinition {
|
||||
fn accept(&self, ast_visitor: &mut impl AstVisitor) {
|
||||
ast_visitor.visit_function_definition(self);
|
||||
}
|
||||
|
||||
fn visit_children(&self, ast_visitor: &mut impl AstVisitor) {
|
||||
for argument in &self.arguments {
|
||||
argument.accept(ast_visitor);
|
||||
}
|
||||
|
||||
self.body.accept(ast_visitor);
|
||||
|
||||
for result in &self.result {
|
||||
result.accept(ast_visitor);
|
||||
}
|
||||
}
|
||||
|
||||
fn location(&self) -> Location {
|
||||
self.location
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::collections::BTreeSet;
|
||||
|
||||
@@ -11,6 +11,8 @@ use crate::lexer::token::Token;
|
||||
use crate::lexer::Lexer;
|
||||
use crate::parser::statement::block::Block;
|
||||
use crate::parser::statement::expression::Expression;
|
||||
use crate::visitor::AstNode;
|
||||
use crate::visitor::AstVisitor;
|
||||
|
||||
/// The Yul if-conditional statement.
|
||||
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
|
||||
@@ -82,3 +84,18 @@ where
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl AstNode for IfConditional {
|
||||
fn accept(&self, ast_visitor: &mut impl AstVisitor) {
|
||||
ast_visitor.visit_if_conditional(self);
|
||||
}
|
||||
|
||||
fn visit_children(&self, ast_visitor: &mut impl AstVisitor) {
|
||||
self.condition.accept(ast_visitor);
|
||||
self.block.accept(ast_visitor);
|
||||
}
|
||||
|
||||
fn location(&self) -> Location {
|
||||
self.location
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,6 +23,8 @@ use crate::lexer::token::location::Location;
|
||||
use crate::lexer::token::Token;
|
||||
use crate::lexer::Lexer;
|
||||
use crate::parser::error::Error as ParserError;
|
||||
use crate::visitor::AstNode;
|
||||
use crate::visitor::AstVisitor;
|
||||
|
||||
use self::assignment::Assignment;
|
||||
use self::block::Block;
|
||||
@@ -177,3 +179,31 @@ impl Statement {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AstNode for Statement {
|
||||
fn accept(&self, ast_visitor: &mut impl AstVisitor) {
|
||||
ast_visitor.visit_statement(self);
|
||||
}
|
||||
|
||||
fn visit_children(&self, ast_visitor: &mut impl AstVisitor) {
|
||||
match self {
|
||||
Self::Object(inner) => inner.accept(ast_visitor),
|
||||
Self::Code(inner) => inner.accept(ast_visitor),
|
||||
Self::Block(inner) => inner.accept(ast_visitor),
|
||||
Self::Expression(inner) => inner.accept(ast_visitor),
|
||||
Self::FunctionDefinition(inner) => inner.accept(ast_visitor),
|
||||
Self::VariableDeclaration(inner) => inner.accept(ast_visitor),
|
||||
Self::Assignment(inner) => inner.accept(ast_visitor),
|
||||
Self::IfConditional(inner) => inner.accept(ast_visitor),
|
||||
Self::Switch(inner) => inner.accept(ast_visitor),
|
||||
Self::ForLoop(inner) => inner.accept(ast_visitor),
|
||||
Self::Continue(_location) => {}
|
||||
Self::Break(_location) => {}
|
||||
Self::Leave(_location) => {}
|
||||
}
|
||||
}
|
||||
|
||||
fn location(&self) -> Location {
|
||||
self.location()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,8 @@ use crate::lexer::token::Token;
|
||||
use crate::lexer::Lexer;
|
||||
use crate::parser::error::Error as ParserError;
|
||||
use crate::parser::statement::code::Code;
|
||||
use crate::visitor::AstNode;
|
||||
use crate::visitor::AstVisitor;
|
||||
|
||||
/// The upper-level YUL object, representing the deploy code.
|
||||
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
|
||||
@@ -297,6 +299,24 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl AstNode for Object {
|
||||
fn accept(&self, ast_visitor: &mut impl AstVisitor) {
|
||||
ast_visitor.visit_object(self);
|
||||
}
|
||||
|
||||
fn visit_children(&self, ast_visitor: &mut impl AstVisitor) {
|
||||
self.code.accept(ast_visitor);
|
||||
|
||||
if let Some(inner_object) = &self.inner_object {
|
||||
inner_object.accept(ast_visitor);
|
||||
}
|
||||
}
|
||||
|
||||
fn location(&self) -> Location {
|
||||
self.location
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::lexer::token::location::Location;
|
||||
|
||||
@@ -13,6 +13,8 @@ use crate::lexer::Lexer;
|
||||
use crate::parser::error::Error as ParserError;
|
||||
use crate::parser::statement::block::Block;
|
||||
use crate::parser::statement::expression::literal::Literal;
|
||||
use crate::visitor::AstNode;
|
||||
use crate::visitor::AstVisitor;
|
||||
|
||||
/// The Yul switch statement case.
|
||||
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
|
||||
@@ -61,6 +63,21 @@ impl Case {
|
||||
}
|
||||
}
|
||||
|
||||
impl AstNode for Case {
|
||||
fn accept(&self, ast_visitor: &mut impl AstVisitor) {
|
||||
ast_visitor.visit_case(self);
|
||||
}
|
||||
|
||||
fn visit_children(&self, ast_visitor: &mut impl AstVisitor) {
|
||||
self.literal.accept(ast_visitor);
|
||||
self.block.accept(ast_visitor);
|
||||
}
|
||||
|
||||
fn location(&self) -> Location {
|
||||
self.location
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::lexer::token::location::Location;
|
||||
|
||||
@@ -16,6 +16,8 @@ use crate::lexer::Lexer;
|
||||
use crate::parser::error::Error as ParserError;
|
||||
use crate::parser::statement::block::Block;
|
||||
use crate::parser::statement::expression::Expression;
|
||||
use crate::visitor::AstNode;
|
||||
use crate::visitor::AstVisitor;
|
||||
|
||||
use self::case::Case;
|
||||
|
||||
@@ -179,6 +181,28 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl AstNode for Switch {
|
||||
fn accept(&self, ast_visitor: &mut impl AstVisitor) {
|
||||
ast_visitor.visit_switch(self);
|
||||
}
|
||||
|
||||
fn visit_children(&self, ast_visitor: &mut impl AstVisitor) {
|
||||
self.expression.accept(ast_visitor);
|
||||
|
||||
for case in &self.cases {
|
||||
case.accept(ast_visitor);
|
||||
}
|
||||
|
||||
if let Some(default) = self.default.as_ref() {
|
||||
default.accept(ast_visitor);
|
||||
}
|
||||
}
|
||||
|
||||
fn location(&self) -> Location {
|
||||
self.location
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::lexer::token::location::Location;
|
||||
|
||||
@@ -17,6 +17,8 @@ use crate::parser::error::Error as ParserError;
|
||||
use crate::parser::identifier::Identifier;
|
||||
use crate::parser::statement::expression::function_call::name::Name as FunctionName;
|
||||
use crate::parser::statement::expression::Expression;
|
||||
use crate::visitor::AstNode;
|
||||
use crate::visitor::AstVisitor;
|
||||
|
||||
/// The Yul variable declaration statement.
|
||||
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
|
||||
@@ -218,6 +220,26 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl AstNode for VariableDeclaration {
|
||||
fn accept(&self, ast_visitor: &mut impl AstVisitor) {
|
||||
ast_visitor.visit_variable_declaration(self);
|
||||
}
|
||||
|
||||
fn visit_children(&self, ast_visitor: &mut impl AstVisitor) {
|
||||
for binding in &self.bindings {
|
||||
binding.accept(ast_visitor);
|
||||
}
|
||||
|
||||
if let Some(initializer) = self.expression.as_ref() {
|
||||
initializer.accept(ast_visitor);
|
||||
}
|
||||
}
|
||||
|
||||
fn location(&self) -> Location {
|
||||
self.location
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::lexer::token::location::Location;
|
||||
|
||||
Reference in New Issue
Block a user