From 99ebf6c727d15c1660f495ab4ce33f420b20e523 Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Sun, 1 Dec 2024 20:43:31 +0400 Subject: [PATCH 01/11] wip --- Cargo.lock | 7 + Cargo.toml | 1 + crates/ast-pretty/Cargo.toml | 26 ++++ crates/ast-pretty/README.md | 3 + crates/ast-pretty/src/lib.rs | 293 +++++++++++++++++++++++++++++++++++ 5 files changed, 330 insertions(+) create mode 100644 crates/ast-pretty/Cargo.toml create mode 100644 crates/ast-pretty/README.md create mode 100644 crates/ast-pretty/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index ac671046..8914f9a9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2541,6 +2541,13 @@ dependencies = [ "typed-arena", ] +[[package]] +name = "solar-ast-pretty" +version = "0.1.0" +dependencies = [ + "solar-ast", +] + [[package]] name = "solar-bench" version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml index f1d8cd5b..669abc1d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -81,6 +81,7 @@ codegen-units = 1 # compiler crates solar = { version = "0.1.0", path = "crates/solar", package = "solar-compiler" } solar-ast = { version = "0.1.0", path = "crates/ast" } +solar-ast-pretty = { version = "0.1.0", path = "crates/ast-pretty" } solar-cli = { version = "0.1.0", path = "crates/cli" } solar-config = { version = "0.1.0", path = "crates/config" } solar-data-structures = { version = "0.1.0", path = "crates/data-structures" } diff --git a/crates/ast-pretty/Cargo.toml b/crates/ast-pretty/Cargo.toml new file mode 100644 index 00000000..758707e5 --- /dev/null +++ b/crates/ast-pretty/Cargo.toml @@ -0,0 +1,26 @@ +[package] +name = "solar-ast-pretty" +description = "AST pretty-printing" +homepage = "https://github.com/paradigmxyz/solar/tree/main/crates/ast-pretty" + +version.workspace = true +authors.workspace = true +edition.workspace = true +rust-version.workspace = true +license.workspace = true +repository.workspace = true +keywords.workspace = true +categories.workspace = true + +[package.metadata.docs.rs] +all-features = true +rustdoc-args = ["--cfg", "docsrs"] + +[lints] +workspace = true + +[dependencies] +solar-ast.workspace = true + +[features] +nightly = ["solar-ast/nightly"] diff --git a/crates/ast-pretty/README.md b/crates/ast-pretty/README.md new file mode 100644 index 00000000..5d41da09 --- /dev/null +++ b/crates/ast-pretty/README.md @@ -0,0 +1,3 @@ +# solar-ast-pretty + +Solidity AST pretty-printing. diff --git a/crates/ast-pretty/src/lib.rs b/crates/ast-pretty/src/lib.rs new file mode 100644 index 00000000..eac09dce --- /dev/null +++ b/crates/ast-pretty/src/lib.rs @@ -0,0 +1,293 @@ +#![doc = include_str!("../README.md")] +#![doc( + html_logo_url = "https://raw.githubusercontent.com/paradigmxyz/solar/main/assets/logo.png", + html_favicon_url = "https://raw.githubusercontent.com/paradigmxyz/solar/main/assets/favicon.ico" +)] +#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] + +use core::fmt::{self, Write}; +use solar_ast as ast; + +/// AST pretty-printer. +#[derive(Debug)] +pub struct Printer { + writer: W, + ident: usize, +} + +impl Printer { + pub fn new(writer: W) -> Self { + Self { writer, ident: 0 } + } +} + +impl Printer { + fn write_indent(&mut self) -> fmt::Result { + write!(self.writer, "{}", " ".repeat(self.ident)) + } + + fn write_block(&mut self, f: impl FnOnce(&mut Self) -> fmt::Result) -> fmt::Result { + write!(self.writer, " {{\n")?; + self.ident += 1; + f(self)?; + self.ident -= 1; + write!(self.writer, "}}") + } + + fn write_comma_separated( + &mut self, + iter: &[I], + f: impl Fn(&mut Self, &I) -> fmt::Result, + ) -> fmt::Result { + let mut iter = iter.iter(); + if let Some(first) = iter.next() { + f(self, first)?; + for item in iter { + self.writer.write_str(", ")?; + f(self, item)?; + } + } + + Ok(()) + } + + pub fn write_source_unit(&mut self, source_unit: &ast::SourceUnit<'_>) -> fmt::Result { + for item in source_unit.items.iter() { + self.write_item(item)?; + } + + Ok(()) + } + + fn write_item(&mut self, item: &ast::Item<'_>) -> fmt::Result { + match &item.kind { + ast::ItemKind::Pragma(item) => self.write_pragma_directive(item)?, + ast::ItemKind::Contract(item) => self.write_item_contract(item)?, + ast::ItemKind::Struct(item) => self.write_item_struct(item)?, + ast::ItemKind::Variable(item) => { + self.write_variable_definition(item)?; + self.writer.write_str(";\n")?; + } + _ => todo!(), + }; + Ok(()) + } + + fn write_pragma_directive(&mut self, pragma: &ast::PragmaDirective<'_>) -> fmt::Result { + let ast::PragmaDirective { tokens } = pragma; + self.writer.write_str("pragma ")?; + match tokens { + ast::PragmaTokens::Custom(name, value) => { + write!(self.writer, "{}", name.as_str())?; + if let Some(value) = value { + write!(self.writer, " {}", value.as_str())?; + } + } + ast::PragmaTokens::Verbatim(tokens) => { + for token in tokens.iter() { + write!(self.writer, "{}", token.kind.as_str())?; + } + } + ast::PragmaTokens::Version(ident, req) => { + write!(self.writer, "{ident}")?; + write!(self.writer, " {req}")?; + } + } + self.writer.write_str(";\n")?; + Ok(()) + } + + fn write_item_contract(&mut self, contract: &ast::ItemContract<'_>) -> fmt::Result { + let ast::ItemContract { kind, name, bases, body } = contract; + + self.write_indent()?; + write!(self.writer, "{kind} {name}")?; + + if !bases.is_empty() { + self.writer.write_str(" is ")?; + self.write_comma_separated(bases, |this, base| this.write_modifier(base))?; + } + + self.write_block(|this| { + for item in body.iter() { + this.write_indent()?; + this.write_item(item)?; + } + Ok(()) + })?; + + Ok(()) + } + + fn write_item_struct(&mut self, struct_: &ast::ItemStruct<'_>) -> fmt::Result { + let ast::ItemStruct { name, fields } = struct_; + write!(self.writer, "struct {name}")?; + + self.write_block(|this| { + for field in fields.iter() { + this.write_indent()?; + this.write_variable_definition(field)?; + this.writer.write_str(";\n")?; + } + Ok(()) + }) + } + + fn write_variable_definition(&mut self, var_def: &ast::VariableDefinition<'_>) -> fmt::Result { + let ast::VariableDefinition { + name, + ty, + initializer, + span: _, + visibility, + mutability, + data_location, + override_, + indexed, + } = var_def; + + self.write_ty(ty)?; + + if let Some(visibility) = visibility { + write!(self.writer, " {visibility}")?; + } + if let Some(mutability) = mutability { + write!(self.writer, " {mutability}")?; + } + if let Some(data_location) = data_location { + write!(self.writer, " {data_location}")?; + } + if let Some(override_) = override_ { + self.write_override(override_)?; + } + if *indexed { + self.writer.write_str(" indexed")?; + } + + if let Some(name) = name { + write!(self.writer, " {name}")?; + } + + if let Some(init) = initializer { + self.writer.write_str(" = ")?; + self.write_expr(init)?; + } + + Ok(()) + } + + fn write_ty(&mut self, ty: &ast::Type<'_>) -> fmt::Result { + let ast::Type { span: _, kind } = ty; + match &kind { + ast::TypeKind::Elementary(ty) => ty.write_abi_str(&mut self.writer)?, + ast::TypeKind::Array(ty) => { + let ast::TypeArray { size, element } = ty; + self.write_ty(&element)?; + self.writer.write_str("[")?; + if let Some(size) = size { + self.write_expr(size)?; + } + self.writer.write_str("]")?; + } + ast::TypeKind::Function(ty) => { + let ast::TypeFunction { parameters, returns, visibility, state_mutability } = ty; + self.writer.write_str("function(")?; + self.write_comma_separated(parameters, |this, param| { + this.write_variable_definition(param) + })?; + self.writer.write_str(")")?; + if let Some(visibility) = visibility { + write!(self.writer, " {visibility}")?; + } + write!(self.writer, " {state_mutability}")?; + if !returns.is_empty() { + self.writer.write_str(" returns(")?; + self.write_comma_separated(returns, |this, ret| { + this.write_variable_definition(ret) + })?; + self.writer.write_str(")")?; + } + } + ast::TypeKind::Mapping(ty) => { + let ast::TypeMapping { key, value, key_name, value_name } = ty; + self.writer.write_str("mapping(")?; + self.write_ty(key)?; + if let Some(key_name) = key_name { + write!(self.writer, " {key_name}")?; + } + self.writer.write_str(" => ")?; + self.write_ty(value)?; + if let Some(value_name) = value_name { + write!(self.writer, " {value_name}")?; + } + self.writer.write_str(")")?; + } + ast::TypeKind::Custom(ty) => { + write!(self.writer, "{ty}")?; + } + }; + + Ok(()) + } + + fn write_override(&mut self, override_: &ast::Override<'_>) -> fmt::Result { + let ast::Override { paths, span: _ } = override_; + self.writer.write_str("override(")?; + self.write_comma_separated(paths, |this, path| write!(this.writer, "{path}"))?; + self.writer.write_str(")") + } + + fn write_modifier(&mut self, modifier: &ast::Modifier<'_>) -> fmt::Result { + let ast::Modifier { name, arguments } = modifier; + write!(self.writer, "{name}")?; + self.write_call_args(arguments) + } + + fn write_call_args(&mut self, args: &ast::CallArgs<'_>) -> fmt::Result { + self.writer.write_char('(')?; + match args { + ast::CallArgs::Unnamed(args) => { + self.write_comma_separated(args, |this, expr| this.write_expr(expr))?; + } + ast::CallArgs::Named(args) => { + self.writer.write_str("{{")?; + self.write_comma_separated(args, |this, arg| { + let ast::NamedArg { name, value } = arg; + write!(this.writer, "{name}: ")?; + this.write_expr(value) + })?; + self.writer.write_str("}}")?; + } + } + self.writer.write_char(')') + } + + fn write_expr(&mut self, expr: &ast::Expr<'_>) -> fmt::Result { + match &expr.kind { + ast::ExprKind::Array(exprs) => { + self.write_comma_separated(exprs, |this, expr| this.write_expr(expr))?; + } + ast::ExprKind::Assign(lhs, op, rhs) => { + self.write_expr(lhs)?; + self.writer.write_char(' ')?; + if let Some(op) = op { + write!(self.writer, "{op}")?; + } + self.writer.write_str("= ")?; + self.write_expr(rhs)?; + } + ast::ExprKind::Binary(lhs, op, rhs) => { + self.write_expr(lhs)?; + write!(self.writer, " {op} ")?; + self.write_expr(rhs)?; + } + ast::ExprKind::Call(expr, args) => { + self.write_expr(expr)?; + self.write_call_args(args)?; + } + _ => todo!(), + } + + Ok(()) + } +} From 69069b8f9917dc6c6f89f6f4596c12951ba3722b Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Sun, 8 Dec 2024 18:00:36 +0400 Subject: [PATCH 02/11] fixes --- Cargo.lock | 9 +- Cargo.toml | 1 - crates/ast-pretty/Cargo.toml | 26 ---- crates/ast-pretty/README.md | 3 - crates/ast-pretty/src/lib.rs | 293 ----------------------------------- crates/ast/src/lib.rs | 1 + 6 files changed, 2 insertions(+), 331 deletions(-) delete mode 100644 crates/ast-pretty/Cargo.toml delete mode 100644 crates/ast-pretty/README.md delete mode 100644 crates/ast-pretty/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index 8914f9a9..8228a72b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,6 +1,6 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 [[package]] name = "addr2line" @@ -2541,13 +2541,6 @@ dependencies = [ "typed-arena", ] -[[package]] -name = "solar-ast-pretty" -version = "0.1.0" -dependencies = [ - "solar-ast", -] - [[package]] name = "solar-bench" version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml index 669abc1d..f1d8cd5b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -81,7 +81,6 @@ codegen-units = 1 # compiler crates solar = { version = "0.1.0", path = "crates/solar", package = "solar-compiler" } solar-ast = { version = "0.1.0", path = "crates/ast" } -solar-ast-pretty = { version = "0.1.0", path = "crates/ast-pretty" } solar-cli = { version = "0.1.0", path = "crates/cli" } solar-config = { version = "0.1.0", path = "crates/config" } solar-data-structures = { version = "0.1.0", path = "crates/data-structures" } diff --git a/crates/ast-pretty/Cargo.toml b/crates/ast-pretty/Cargo.toml deleted file mode 100644 index 758707e5..00000000 --- a/crates/ast-pretty/Cargo.toml +++ /dev/null @@ -1,26 +0,0 @@ -[package] -name = "solar-ast-pretty" -description = "AST pretty-printing" -homepage = "https://github.com/paradigmxyz/solar/tree/main/crates/ast-pretty" - -version.workspace = true -authors.workspace = true -edition.workspace = true -rust-version.workspace = true -license.workspace = true -repository.workspace = true -keywords.workspace = true -categories.workspace = true - -[package.metadata.docs.rs] -all-features = true -rustdoc-args = ["--cfg", "docsrs"] - -[lints] -workspace = true - -[dependencies] -solar-ast.workspace = true - -[features] -nightly = ["solar-ast/nightly"] diff --git a/crates/ast-pretty/README.md b/crates/ast-pretty/README.md deleted file mode 100644 index 5d41da09..00000000 --- a/crates/ast-pretty/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# solar-ast-pretty - -Solidity AST pretty-printing. diff --git a/crates/ast-pretty/src/lib.rs b/crates/ast-pretty/src/lib.rs deleted file mode 100644 index eac09dce..00000000 --- a/crates/ast-pretty/src/lib.rs +++ /dev/null @@ -1,293 +0,0 @@ -#![doc = include_str!("../README.md")] -#![doc( - html_logo_url = "https://raw.githubusercontent.com/paradigmxyz/solar/main/assets/logo.png", - html_favicon_url = "https://raw.githubusercontent.com/paradigmxyz/solar/main/assets/favicon.ico" -)] -#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] - -use core::fmt::{self, Write}; -use solar_ast as ast; - -/// AST pretty-printer. -#[derive(Debug)] -pub struct Printer { - writer: W, - ident: usize, -} - -impl Printer { - pub fn new(writer: W) -> Self { - Self { writer, ident: 0 } - } -} - -impl Printer { - fn write_indent(&mut self) -> fmt::Result { - write!(self.writer, "{}", " ".repeat(self.ident)) - } - - fn write_block(&mut self, f: impl FnOnce(&mut Self) -> fmt::Result) -> fmt::Result { - write!(self.writer, " {{\n")?; - self.ident += 1; - f(self)?; - self.ident -= 1; - write!(self.writer, "}}") - } - - fn write_comma_separated( - &mut self, - iter: &[I], - f: impl Fn(&mut Self, &I) -> fmt::Result, - ) -> fmt::Result { - let mut iter = iter.iter(); - if let Some(first) = iter.next() { - f(self, first)?; - for item in iter { - self.writer.write_str(", ")?; - f(self, item)?; - } - } - - Ok(()) - } - - pub fn write_source_unit(&mut self, source_unit: &ast::SourceUnit<'_>) -> fmt::Result { - for item in source_unit.items.iter() { - self.write_item(item)?; - } - - Ok(()) - } - - fn write_item(&mut self, item: &ast::Item<'_>) -> fmt::Result { - match &item.kind { - ast::ItemKind::Pragma(item) => self.write_pragma_directive(item)?, - ast::ItemKind::Contract(item) => self.write_item_contract(item)?, - ast::ItemKind::Struct(item) => self.write_item_struct(item)?, - ast::ItemKind::Variable(item) => { - self.write_variable_definition(item)?; - self.writer.write_str(";\n")?; - } - _ => todo!(), - }; - Ok(()) - } - - fn write_pragma_directive(&mut self, pragma: &ast::PragmaDirective<'_>) -> fmt::Result { - let ast::PragmaDirective { tokens } = pragma; - self.writer.write_str("pragma ")?; - match tokens { - ast::PragmaTokens::Custom(name, value) => { - write!(self.writer, "{}", name.as_str())?; - if let Some(value) = value { - write!(self.writer, " {}", value.as_str())?; - } - } - ast::PragmaTokens::Verbatim(tokens) => { - for token in tokens.iter() { - write!(self.writer, "{}", token.kind.as_str())?; - } - } - ast::PragmaTokens::Version(ident, req) => { - write!(self.writer, "{ident}")?; - write!(self.writer, " {req}")?; - } - } - self.writer.write_str(";\n")?; - Ok(()) - } - - fn write_item_contract(&mut self, contract: &ast::ItemContract<'_>) -> fmt::Result { - let ast::ItemContract { kind, name, bases, body } = contract; - - self.write_indent()?; - write!(self.writer, "{kind} {name}")?; - - if !bases.is_empty() { - self.writer.write_str(" is ")?; - self.write_comma_separated(bases, |this, base| this.write_modifier(base))?; - } - - self.write_block(|this| { - for item in body.iter() { - this.write_indent()?; - this.write_item(item)?; - } - Ok(()) - })?; - - Ok(()) - } - - fn write_item_struct(&mut self, struct_: &ast::ItemStruct<'_>) -> fmt::Result { - let ast::ItemStruct { name, fields } = struct_; - write!(self.writer, "struct {name}")?; - - self.write_block(|this| { - for field in fields.iter() { - this.write_indent()?; - this.write_variable_definition(field)?; - this.writer.write_str(";\n")?; - } - Ok(()) - }) - } - - fn write_variable_definition(&mut self, var_def: &ast::VariableDefinition<'_>) -> fmt::Result { - let ast::VariableDefinition { - name, - ty, - initializer, - span: _, - visibility, - mutability, - data_location, - override_, - indexed, - } = var_def; - - self.write_ty(ty)?; - - if let Some(visibility) = visibility { - write!(self.writer, " {visibility}")?; - } - if let Some(mutability) = mutability { - write!(self.writer, " {mutability}")?; - } - if let Some(data_location) = data_location { - write!(self.writer, " {data_location}")?; - } - if let Some(override_) = override_ { - self.write_override(override_)?; - } - if *indexed { - self.writer.write_str(" indexed")?; - } - - if let Some(name) = name { - write!(self.writer, " {name}")?; - } - - if let Some(init) = initializer { - self.writer.write_str(" = ")?; - self.write_expr(init)?; - } - - Ok(()) - } - - fn write_ty(&mut self, ty: &ast::Type<'_>) -> fmt::Result { - let ast::Type { span: _, kind } = ty; - match &kind { - ast::TypeKind::Elementary(ty) => ty.write_abi_str(&mut self.writer)?, - ast::TypeKind::Array(ty) => { - let ast::TypeArray { size, element } = ty; - self.write_ty(&element)?; - self.writer.write_str("[")?; - if let Some(size) = size { - self.write_expr(size)?; - } - self.writer.write_str("]")?; - } - ast::TypeKind::Function(ty) => { - let ast::TypeFunction { parameters, returns, visibility, state_mutability } = ty; - self.writer.write_str("function(")?; - self.write_comma_separated(parameters, |this, param| { - this.write_variable_definition(param) - })?; - self.writer.write_str(")")?; - if let Some(visibility) = visibility { - write!(self.writer, " {visibility}")?; - } - write!(self.writer, " {state_mutability}")?; - if !returns.is_empty() { - self.writer.write_str(" returns(")?; - self.write_comma_separated(returns, |this, ret| { - this.write_variable_definition(ret) - })?; - self.writer.write_str(")")?; - } - } - ast::TypeKind::Mapping(ty) => { - let ast::TypeMapping { key, value, key_name, value_name } = ty; - self.writer.write_str("mapping(")?; - self.write_ty(key)?; - if let Some(key_name) = key_name { - write!(self.writer, " {key_name}")?; - } - self.writer.write_str(" => ")?; - self.write_ty(value)?; - if let Some(value_name) = value_name { - write!(self.writer, " {value_name}")?; - } - self.writer.write_str(")")?; - } - ast::TypeKind::Custom(ty) => { - write!(self.writer, "{ty}")?; - } - }; - - Ok(()) - } - - fn write_override(&mut self, override_: &ast::Override<'_>) -> fmt::Result { - let ast::Override { paths, span: _ } = override_; - self.writer.write_str("override(")?; - self.write_comma_separated(paths, |this, path| write!(this.writer, "{path}"))?; - self.writer.write_str(")") - } - - fn write_modifier(&mut self, modifier: &ast::Modifier<'_>) -> fmt::Result { - let ast::Modifier { name, arguments } = modifier; - write!(self.writer, "{name}")?; - self.write_call_args(arguments) - } - - fn write_call_args(&mut self, args: &ast::CallArgs<'_>) -> fmt::Result { - self.writer.write_char('(')?; - match args { - ast::CallArgs::Unnamed(args) => { - self.write_comma_separated(args, |this, expr| this.write_expr(expr))?; - } - ast::CallArgs::Named(args) => { - self.writer.write_str("{{")?; - self.write_comma_separated(args, |this, arg| { - let ast::NamedArg { name, value } = arg; - write!(this.writer, "{name}: ")?; - this.write_expr(value) - })?; - self.writer.write_str("}}")?; - } - } - self.writer.write_char(')') - } - - fn write_expr(&mut self, expr: &ast::Expr<'_>) -> fmt::Result { - match &expr.kind { - ast::ExprKind::Array(exprs) => { - self.write_comma_separated(exprs, |this, expr| this.write_expr(expr))?; - } - ast::ExprKind::Assign(lhs, op, rhs) => { - self.write_expr(lhs)?; - self.writer.write_char(' ')?; - if let Some(op) = op { - write!(self.writer, "{op}")?; - } - self.writer.write_str("= ")?; - self.write_expr(rhs)?; - } - ast::ExprKind::Binary(lhs, op, rhs) => { - self.write_expr(lhs)?; - write!(self.writer, " {op} ")?; - self.write_expr(rhs)?; - } - ast::ExprKind::Call(expr, args) => { - self.write_expr(expr)?; - self.write_call_args(args)?; - } - _ => todo!(), - } - - Ok(()) - } -} diff --git a/crates/ast/src/lib.rs b/crates/ast/src/lib.rs index 42fc60fb..d5f0f735 100644 --- a/crates/ast/src/lib.rs +++ b/crates/ast/src/lib.rs @@ -12,5 +12,6 @@ pub use solar_interface as interface; mod ast; pub use ast::*; +pub mod pretty; pub mod token; pub mod visit; From 2058231814e16c713b5cec4dcb5979a38de06979 Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Sun, 8 Dec 2024 18:34:47 +0400 Subject: [PATCH 03/11] wip --- crates/ast/src/pretty.rs | 376 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 376 insertions(+) create mode 100644 crates/ast/src/pretty.rs diff --git a/crates/ast/src/pretty.rs b/crates/ast/src/pretty.rs new file mode 100644 index 00000000..4e7bd9ec --- /dev/null +++ b/crates/ast/src/pretty.rs @@ -0,0 +1,376 @@ +//! AST pretty-printing. + +use crate::ast; +use core::fmt::{self, Write}; + +/// AST pretty-printer. +#[derive(Debug)] +pub struct Printer { + writer: W, + indent: usize, +} + +impl Printer { + pub fn new(writer: W) -> Self { + Self { writer, indent: 0 } + } +} + +impl Printer { + fn print_indent(&mut self) -> fmt::Result { + write!(self.writer, "{}", " ".repeat(self.indent)) + } + + fn print_block(&mut self, f: impl FnOnce(&mut Self) -> fmt::Result) -> fmt::Result { + self.writer.write_str("{\n")?; + self.indent += 1; + f(self)?; + self.indent -= 1; + self.writer.write_str("}") + } + + fn print_comma_separated( + &mut self, + iter: &[I], + f: impl Fn(&mut Self, &I) -> fmt::Result, + ) -> fmt::Result { + let mut iter = iter.iter(); + if let Some(first) = iter.next() { + f(self, first)?; + for item in iter { + self.writer.write_str(", ")?; + f(self, item)?; + } + } + + Ok(()) + } + + pub fn print_soure_unit(&mut self, source_unit: &ast::SourceUnit<'_>) -> fmt::Result { + for item in source_unit.items.iter() { + self.print_item(item)?; + } + + Ok(()) + } + + fn print_item(&mut self, item: &ast::Item<'_>) -> fmt::Result { + match &item.kind { + ast::ItemKind::Pragma(item) => self.print_pragma_directive(item)?, + ast::ItemKind::Contract(item) => self.print_item_contract(item)?, + ast::ItemKind::Struct(item) => self.print_item_struct(item)?, + ast::ItemKind::Variable(item) => { + self.print_variable_definition(item)?; + self.writer.write_str(";\n")?; + } + ast::ItemKind::Function(item) => self.print_item_function(item)?, + _ => todo!(), + }; + Ok(()) + } + + fn print_pragma_directive(&mut self, pragma: &ast::PragmaDirective<'_>) -> fmt::Result { + let ast::PragmaDirective { tokens } = pragma; + self.writer.write_str("pragma ")?; + match tokens { + ast::PragmaTokens::Custom(name, value) => { + write!(self.writer, "{}", name.as_str())?; + if let Some(value) = value { + write!(self.writer, " {}", value.as_str())?; + } + } + ast::PragmaTokens::Verbatim(tokens) => { + for token in tokens.iter() { + write!(self.writer, "{}", token.kind.as_str())?; + } + } + ast::PragmaTokens::Version(ident, req) => { + write!(self.writer, "{ident}")?; + write!(self.writer, " {req}")?; + } + } + self.writer.write_str(";\n")?; + Ok(()) + } + + fn print_item_contract(&mut self, contract: &ast::ItemContract<'_>) -> fmt::Result { + let ast::ItemContract { kind, name, bases, body } = contract; + + self.print_indent()?; + write!(self.writer, "{kind} {name}")?; + + if !bases.is_empty() { + self.writer.write_str(" is ")?; + self.print_comma_separated(bases, |this, base| this.print_modifier(base))?; + } + + self.print_block(|this| { + for item in body.iter() { + this.print_indent()?; + this.print_item(item)?; + } + Ok(()) + })?; + + Ok(()) + } + + fn print_item_struct(&mut self, struct_: &ast::ItemStruct<'_>) -> fmt::Result { + let ast::ItemStruct { name, fields } = struct_; + write!(self.writer, "struct {name}")?; + + self.print_block(|this| { + for field in fields.iter() { + this.print_indent()?; + this.print_variable_definition(field)?; + this.writer.write_str(";\n")?; + } + Ok(()) + }) + } + + fn print_item_function(&mut self, function: &ast::ItemFunction<'_>) -> fmt::Result { + let ast::ItemFunction { kind, header, body } = function; + let ast::FunctionHeader { + name, + parameters, + visibility, + state_mutability, + modifiers, + virtual_, + override_, + returns, + } = header; + + write!(self.writer, "{kind}")?; + if let Some(name) = name { + write!(self.writer, " {name}")?; + } + + self.writer.write_str("(")?; + self.print_comma_separated(parameters, |this, param| { + this.print_variable_definition(param) + })?; + self.writer.write_str(")")?; + + if let Some(visibility) = visibility { + write!(self.writer, " {visibility}")?; + } + + write!(self.writer, " {state_mutability}")?; + + for modifier in modifiers.iter() { + self.writer.write_char(' ')?; + self.print_modifier(modifier)?; + } + + if *virtual_ { + write!(self.writer, " virtual")?; + } + + if let Some(override_) = override_ { + self.print_override(override_)?; + } + + if !returns.is_empty() { + write!(self.writer, " returns (")?; + self.print_comma_separated(returns, |this, val| this.print_variable_definition(val))?; + self.writer.write_str(")")?; + } + + if let Some(body) = body { + self.print_block(|this| { + for stmt in body.iter() { + this.print_stmt(stmt)?; + } + + Ok(()) + })?; + } else { + self.writer.write_char(';')?; + } + + Ok(()) + } + + fn print_variable_definition(&mut self, var_def: &ast::VariableDefinition<'_>) -> fmt::Result { + let ast::VariableDefinition { + name, + ty, + initializer, + span: _, + visibility, + mutability, + data_location, + override_, + indexed, + } = var_def; + + self.print_ty(ty)?; + + if let Some(visibility) = visibility { + write!(self.writer, " {visibility}")?; + } + if let Some(mutability) = mutability { + write!(self.writer, " {mutability}")?; + } + if let Some(data_location) = data_location { + write!(self.writer, " {data_location}")?; + } + if let Some(override_) = override_ { + self.print_override(override_)?; + } + if *indexed { + self.writer.write_str(" indexed")?; + } + + if let Some(name) = name { + write!(self.writer, " {name}")?; + } + + if let Some(init) = initializer { + self.writer.write_str(" = ")?; + self.print_expr(init)?; + } + + Ok(()) + } + + fn print_ty(&mut self, ty: &ast::Type<'_>) -> fmt::Result { + let ast::Type { span: _, kind } = ty; + match &kind { + ast::TypeKind::Elementary(ty) => ty.write_abi_str(&mut self.writer)?, + ast::TypeKind::Array(ty) => { + let ast::TypeArray { size, element } = ty; + self.print_ty(element)?; + self.writer.write_str("[")?; + if let Some(size) = size { + self.print_expr(size)?; + } + self.writer.write_str("]")?; + } + ast::TypeKind::Function(ty) => { + let ast::TypeFunction { parameters, returns, visibility, state_mutability } = ty; + self.writer.write_str("function(")?; + self.print_comma_separated(parameters, |this, param| { + this.print_variable_definition(param) + })?; + self.writer.write_str(")")?; + if let Some(visibility) = visibility { + write!(self.writer, " {visibility}")?; + } + write!(self.writer, " {state_mutability}")?; + if !returns.is_empty() { + self.writer.write_str(" returns(")?; + self.print_comma_separated(returns, |this, ret| { + this.print_variable_definition(ret) + })?; + self.writer.write_str(")")?; + } + } + ast::TypeKind::Mapping(ty) => { + let ast::TypeMapping { key, value, key_name, value_name } = ty; + self.writer.write_str("mapping(")?; + self.print_ty(key)?; + if let Some(key_name) = key_name { + write!(self.writer, " {key_name}")?; + } + self.writer.write_str(" => ")?; + self.print_ty(value)?; + if let Some(value_name) = value_name { + write!(self.writer, " {value_name}")?; + } + self.writer.write_str(")")?; + } + ast::TypeKind::Custom(ty) => { + write!(self.writer, "{ty}")?; + } + }; + + Ok(()) + } + + fn print_override(&mut self, override_: &ast::Override<'_>) -> fmt::Result { + let ast::Override { paths, span: _ } = override_; + self.writer.write_str("override(")?; + self.print_comma_separated(paths, |this, path| write!(this.writer, "{path}"))?; + self.writer.write_str(")") + } + + fn print_modifier(&mut self, modifier: &ast::Modifier<'_>) -> fmt::Result { + let ast::Modifier { name, arguments } = modifier; + write!(self.writer, "{name}")?; + self.print_call_args(arguments) + } + + fn print_call_args(&mut self, args: &ast::CallArgs<'_>) -> fmt::Result { + self.writer.write_char('(')?; + match args { + ast::CallArgs::Unnamed(args) => { + self.print_comma_separated(args, |this, expr| this.print_expr(expr))?; + } + ast::CallArgs::Named(args) => { + self.writer.write_str("{{")?; + self.print_comma_separated(args, |this, arg| { + let ast::NamedArg { name, value } = arg; + write!(this.writer, "{name}: ")?; + this.print_expr(value) + })?; + self.writer.write_str("}}")?; + } + } + self.writer.write_char(')') + } + + fn print_expr(&mut self, expr: &ast::Expr<'_>) -> fmt::Result { + match &expr.kind { + ast::ExprKind::Array(exprs) => { + self.print_comma_separated(exprs, |this, expr| this.print_expr(expr))?; + } + ast::ExprKind::Assign(lhs, op, rhs) => { + self.print_expr(lhs)?; + self.writer.write_char(' ')?; + if let Some(op) = op { + write!(self.writer, "{op}")?; + } + self.writer.write_str("= ")?; + self.print_expr(rhs)?; + } + ast::ExprKind::Binary(lhs, op, rhs) => { + self.print_expr(lhs)?; + write!(self.writer, " {op} ")?; + self.print_expr(rhs)?; + } + ast::ExprKind::Call(expr, args) => { + self.print_expr(expr)?; + self.print_call_args(args)?; + } + _ => todo!(), + } + + Ok(()) + } + + fn print_stmt(&mut self, stmt: &ast::Stmt<'_>) -> fmt::Result { + let ast::Stmt { docs, span, kind } = stmt; + match kind { + ast::StmtKind::Block(block) => { + self.writer.write_str("{\n")?; + for stmt in block.iter() { + self.print_indent()?; + self.print_stmt(stmt)?; + self.writer.write_str(";\n")?; + } + self.writer.write_str("}\n")?; + } + ast::StmtKind::Break => { + self.writer.write_str("break;\n")?; + } + ast::StmtKind::Continue => { + self.writer.write_str("continue;\n")?; + } + _ => todo!() + } + Ok(()) + } +} From 1865f8e14bdfb09bbbffcd1b16445a5c4e749077 Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Mon, 9 Dec 2024 00:58:26 +0400 Subject: [PATCH 04/11] wip --- crates/ast/src/ast/item.rs | 9 + crates/ast/src/ast/lit.rs | 18 ++ crates/ast/src/pretty.rs | 467 ++++++++++++++++++++++++++++++++----- crates/ast/src/token.rs | 2 +- 4 files changed, 438 insertions(+), 58 deletions(-) diff --git a/crates/ast/src/ast/item.rs b/crates/ast/src/ast/item.rs index d31768c3..1759d122 100644 --- a/crates/ast/src/ast/item.rs +++ b/crates/ast/src/ast/item.rs @@ -333,6 +333,15 @@ impl UserDefinableOperator { } } +impl fmt::Display for UserDefinableOperator { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self.to_op() { + Either::Left(op) => f.write_str(op.to_str()), + Either::Right(op) => f.write_str(op.to_str()), + } + } +} + /// A contract, abstract contract, interface, or library definition: /// `contract Foo is Bar("foo"), Baz { ... }`. /// diff --git a/crates/ast/src/ast/lit.rs b/crates/ast/src/ast/lit.rs index bea72af2..817fc28c 100644 --- a/crates/ast/src/ast/lit.rs +++ b/crates/ast/src/ast/lit.rs @@ -16,6 +16,24 @@ pub struct Lit { pub kind: LitKind, } +impl fmt::Display for Lit { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let Self { kind, symbol, span: _ } = self; + match kind { + LitKind::Str(StrKind::Str, _) => write!(f, "\"{symbol}\""), + LitKind::Str(StrKind::Unicode, _) => write!(f, "unicode\"{symbol}\""), + LitKind::Str(StrKind::Hex, _) => write!(f, "hex\"{symbol}\""), + LitKind::Number(_) + | LitKind::Rational(_) + | LitKind::Err(_) + | LitKind::Address(_) + | LitKind::Bool(_) => { + write!(f, "{symbol}") + } + } + } +} + /// A kind of literal. #[derive(Clone, Debug)] pub enum LitKind { diff --git a/crates/ast/src/pretty.rs b/crates/ast/src/pretty.rs index 4e7bd9ec..734ed427 100644 --- a/crates/ast/src/pretty.rs +++ b/crates/ast/src/pretty.rs @@ -21,20 +21,34 @@ impl Printer { write!(self.writer, "{}", " ".repeat(self.indent)) } - fn print_block(&mut self, f: impl FnOnce(&mut Self) -> fmt::Result) -> fmt::Result { - self.writer.write_str("{\n")?; + fn print_block_lines( + &mut self, + items: &[I], + f: impl Fn(&mut Self, &I) -> fmt::Result, + ) -> fmt::Result { + if items.is_empty() { + return self.writer.write_str("{}"); + } + + self.writer.write_char('{')?; self.indent += 1; - f(self)?; + for item in items { + self.writer.write_char('\n')?; + self.print_indent()?; + f(self, item)?; + } self.indent -= 1; + self.writer.write_char('\n')?; + self.print_indent()?; self.writer.write_str("}") } fn print_comma_separated( &mut self, - iter: &[I], + items: &[I], f: impl Fn(&mut Self, &I) -> fmt::Result, ) -> fmt::Result { - let mut iter = iter.iter(); + let mut iter = items.iter(); if let Some(first) = iter.next() { f(self, first)?; for item in iter { @@ -49,28 +63,38 @@ impl Printer { pub fn print_soure_unit(&mut self, source_unit: &ast::SourceUnit<'_>) -> fmt::Result { for item in source_unit.items.iter() { self.print_item(item)?; + self.writer.write_char('\n')?; } Ok(()) } fn print_item(&mut self, item: &ast::Item<'_>) -> fmt::Result { - match &item.kind { + let ast::Item { docs, span: _, kind } = item; + + match kind { ast::ItemKind::Pragma(item) => self.print_pragma_directive(item)?, + ast::ItemKind::Import(item) => self.print_import_directive(item)?, + ast::ItemKind::Using(item) => self.print_using_directive(item)?, ast::ItemKind::Contract(item) => self.print_item_contract(item)?, ast::ItemKind::Struct(item) => self.print_item_struct(item)?, ast::ItemKind::Variable(item) => { self.print_variable_definition(item)?; - self.writer.write_str(";\n")?; + self.writer.write_char(';')?; } ast::ItemKind::Function(item) => self.print_item_function(item)?, - _ => todo!(), + ast::ItemKind::Error(item) => self.print_item_error(item)?, + ast::ItemKind::Event(item) => self.print_item_event(item)?, + ast::ItemKind::Enum(item) => self.print_item_enum(item)?, + ast::ItemKind::Udvt(item) => self.print_item_udvt(item)?, }; + Ok(()) } fn print_pragma_directive(&mut self, pragma: &ast::PragmaDirective<'_>) -> fmt::Result { let ast::PragmaDirective { tokens } = pragma; + self.writer.write_str("pragma ")?; match tokens { ast::PragmaTokens::Custom(name, value) => { @@ -89,7 +113,81 @@ impl Printer { write!(self.writer, " {req}")?; } } - self.writer.write_str(";\n")?; + self.writer.write_str(";")?; + Ok(()) + } + + fn print_import_directive(&mut self, import: &ast::ImportDirective<'_>) -> fmt::Result { + let ast::ImportDirective { path, items } = import; + + self.writer.write_str("import ")?; + match items { + ast::ImportItems::Plain(alias) => { + write!(self.writer, "\"{}\"", path.value)?; + if let Some(alias) = alias { + write!(self.writer, " as {alias}")?; + } + self.writer.write_str(";")?; + } + ast::ImportItems::Aliases(items) => { + self.writer.write_str("{")?; + self.print_comma_separated(items, |this, (item, alias)| { + write!(this.writer, "{item}")?; + if let Some(alias) = alias { + write!(this.writer, " as {alias}")?; + } + + Ok(()) + })?; + write!(self.writer, "}} from \"{}\";", path.value)?; + } + ast::ImportItems::Glob(alias) => { + self.writer.write_char('*')?; + if let Some(alias) = alias { + write!(self.writer, " as {alias}")?; + } + write!(self.writer, " from \"{}\";", path.value)?; + } + }; + + Ok(()) + } + + fn print_using_directive(&mut self, using: &ast::UsingDirective<'_>) -> fmt::Result { + let ast::UsingDirective { list, ty, global } = using; + + self.writer.write_str("using ")?; + + match list { + ast::UsingList::Single(path) => write!(self.writer, "{path}")?, + ast::UsingList::Multiple(paths) => { + self.writer.write_char('{')?; + self.print_comma_separated(paths, |this, (path, op)| { + write!(this.writer, "{path}")?; + if let Some(op) = op { + write!(this.writer, " as {op}")?; + } + + Ok(()) + })?; + self.writer.write_char('}')?; + } + }; + + self.writer.write_str(" for ")?; + + if let Some(ty) = ty { + self.print_ty(ty)?; + } else { + self.writer.write_char('*')?; + } + + if *global { + self.writer.write_str(" global")?; + } + + self.writer.write_char(';')?; + Ok(()) } @@ -104,28 +202,58 @@ impl Printer { self.print_comma_separated(bases, |this, base| this.print_modifier(base))?; } - self.print_block(|this| { - for item in body.iter() { - this.print_indent()?; - this.print_item(item)?; - } - Ok(()) + self.writer.write_char(' ')?; + self.print_block_lines(body, |this, item| this.print_item(item)) + } + + fn print_item_error(&mut self, error: &ast::ItemError<'_>) -> fmt::Result { + let ast::ItemError { name, parameters } = error; + + write!(self.writer, "error {name}(")?; + self.print_comma_separated(parameters, |this, param| { + this.print_variable_definition(param) })?; - Ok(()) + self.writer.write_str(");") + } + + fn print_item_event(&mut self, error: &ast::ItemEvent<'_>) -> fmt::Result { + let ast::ItemEvent { name, parameters, anonymous } = error; + + write!(self.writer, "event {name}(")?; + self.print_comma_separated(parameters, |this, param| { + this.print_variable_definition(param) + })?; + + if *anonymous { + self.writer.write_str(" anonymous")?; + } + + self.writer.write_str(");") + } + + fn print_item_enum(&mut self, enum_: &ast::ItemEnum<'_>) -> fmt::Result { + let ast::ItemEnum { name, variants } = enum_; + + write!(self.writer, "enum {name} ")?; + self.print_block_lines(variants, |this, variant| write!(this.writer, "{variant},")) + } + + fn print_item_udvt(&mut self, udvt: &ast::ItemUdvt<'_>) -> fmt::Result { + let ast::ItemUdvt { name, ty } = udvt; + + write!(self.writer, "type {name} is ")?; + self.print_ty(ty)?; + self.writer.write_char(';') } fn print_item_struct(&mut self, struct_: &ast::ItemStruct<'_>) -> fmt::Result { let ast::ItemStruct { name, fields } = struct_; - write!(self.writer, "struct {name}")?; - self.print_block(|this| { - for field in fields.iter() { - this.print_indent()?; - this.print_variable_definition(field)?; - this.writer.write_str(";\n")?; - } - Ok(()) + write!(self.writer, "struct {name} ")?; + self.print_block_lines(fields, |this, field| { + this.print_variable_definition(field)?; + this.writer.write_char(';') }) } @@ -157,7 +285,10 @@ impl Printer { write!(self.writer, " {visibility}")?; } - write!(self.writer, " {state_mutability}")?; + // Skip writing default state mutability. + if !state_mutability.is_non_payable() { + write!(self.writer, " {state_mutability}")?; + } for modifier in modifiers.iter() { self.writer.write_char(' ')?; @@ -169,6 +300,7 @@ impl Printer { } if let Some(override_) = override_ { + self.writer.write_char(' ')?; self.print_override(override_)?; } @@ -179,13 +311,8 @@ impl Printer { } if let Some(body) = body { - self.print_block(|this| { - for stmt in body.iter() { - this.print_stmt(stmt)?; - } - - Ok(()) - })?; + self.writer.write_char(' ')?; + self.print_block_lines(body, |this, stmt| this.print_stmt(stmt))?; } else { self.writer.write_char(';')?; } @@ -218,6 +345,7 @@ impl Printer { write!(self.writer, " {data_location}")?; } if let Some(override_) = override_ { + self.writer.write_char(' ')?; self.print_override(override_)?; } if *indexed { @@ -238,6 +366,7 @@ impl Printer { fn print_ty(&mut self, ty: &ast::Type<'_>) -> fmt::Result { let ast::Type { span: _, kind } = ty; + match &kind { ast::TypeKind::Elementary(ty) => ty.write_abi_str(&mut self.writer)?, ast::TypeKind::Array(ty) => { @@ -292,15 +421,26 @@ impl Printer { fn print_override(&mut self, override_: &ast::Override<'_>) -> fmt::Result { let ast::Override { paths, span: _ } = override_; - self.writer.write_str("override(")?; - self.print_comma_separated(paths, |this, path| write!(this.writer, "{path}"))?; - self.writer.write_str(")") + + self.writer.write_str("override")?; + if !paths.is_empty() { + self.writer.write_char('(')?; + self.print_comma_separated(paths, |this, path| write!(this.writer, "{path}"))?; + self.writer.write_char(')')?; + } + + Ok(()) } fn print_modifier(&mut self, modifier: &ast::Modifier<'_>) -> fmt::Result { let ast::Modifier { name, arguments } = modifier; + write!(self.writer, "{name}")?; - self.print_call_args(arguments) + if !arguments.is_empty() { + self.print_call_args(arguments)?; + } + + Ok(()) } fn print_call_args(&mut self, args: &ast::CallArgs<'_>) -> fmt::Result { @@ -309,19 +449,21 @@ impl Printer { ast::CallArgs::Unnamed(args) => { self.print_comma_separated(args, |this, expr| this.print_expr(expr))?; } - ast::CallArgs::Named(args) => { - self.writer.write_str("{{")?; - self.print_comma_separated(args, |this, arg| { - let ast::NamedArg { name, value } = arg; - write!(this.writer, "{name}: ")?; - this.print_expr(value) - })?; - self.writer.write_str("}}")?; - } + ast::CallArgs::Named(args) => self.print_named_args(args)?, } self.writer.write_char(')') } + fn print_named_args(&mut self, args: &ast::NamedArgList<'_>) -> fmt::Result { + self.writer.write_char('{')?; + self.print_comma_separated(args, |this, arg| { + let ast::NamedArg { name, value } = arg; + write!(this.writer, "{name}: ")?; + this.print_expr(value) + })?; + self.writer.write_char('}') + } + fn print_expr(&mut self, expr: &ast::Expr<'_>) -> fmt::Result { match &expr.kind { ast::ExprKind::Array(exprs) => { @@ -345,31 +487,242 @@ impl Printer { self.print_expr(expr)?; self.print_call_args(args)?; } - _ => todo!(), + ast::ExprKind::Lit(lit, denom) => { + write!(self.writer, "{lit}")?; + if let Some(denom) = denom { + write!(self.writer, " {denom}")?; + } + } + ast::ExprKind::Ident(ident) => write!(self.writer, "{ident}")?, + ast::ExprKind::Member(item, member) => { + self.print_expr(item)?; + write!(self.writer, ".{member}")?; + } + ast::ExprKind::Type(ty) => self.print_ty(ty)?, + ast::ExprKind::Tuple(exprs) => { + self.writer.write_str("(")?; + self.print_comma_separated(exprs, |this, expr| { + if let Some(expr) = expr { + this.print_expr(expr)?; + } + + Ok(()) + })?; + self.writer.write_str(")")?; + } + ast::ExprKind::New(ty) => { + self.writer.write_str("new ")?; + self.print_ty(ty)?; + } + ast::ExprKind::Unary(op, expr) => { + if op.kind.is_prefix() { + write!(self.writer, "{op}")?; + self.print_expr(expr)?; + } else { + self.print_expr(expr)?; + write!(self.writer, "{op}")?; + } + } + ast::ExprKind::TypeCall(ty) => { + self.writer.write_str("type(")?; + self.print_ty(ty)?; + self.writer.write_char(')')?; + } + ast::ExprKind::Ternary(cond, first, second) => { + self.print_expr(cond)?; + self.writer.write_str(" ? ")?; + self.print_expr(first)?; + self.writer.write_str(" : ")?; + self.print_expr(second)?; + } + ast::ExprKind::Index(item, index) => { + self.print_expr(item)?; + self.writer.write_char('[')?; + match index { + ast::IndexKind::Index(expr) => { + if let Some(expr) = expr { + self.print_expr(expr)?; + } + } + ast::IndexKind::Range(start, end) => { + if let Some(start) = start { + self.print_expr(start)?; + } + self.writer.write_char(':')?; + if let Some(end) = end { + self.print_expr(end)?; + } + } + } + self.writer.write_char(']')?; + } + ast::ExprKind::Delete(expr) => { + self.writer.write_str("delete ")?; + self.print_expr(expr)?; + } + ast::ExprKind::CallOptions(item, opts) => { + self.print_expr(item)?; + self.print_named_args(opts)?; + } + ast::ExprKind::Payable(opts) => { + self.writer.write_str("payable")?; + self.print_call_args(opts)?; + } } Ok(()) } fn print_stmt(&mut self, stmt: &ast::Stmt<'_>) -> fmt::Result { - let ast::Stmt { docs, span, kind } = stmt; + let ast::Stmt { docs, span: _, kind } = stmt; + match kind { ast::StmtKind::Block(block) => { - self.writer.write_str("{\n")?; - for stmt in block.iter() { - self.print_indent()?; - self.print_stmt(stmt)?; - self.writer.write_str(";\n")?; - } - self.writer.write_str("}\n")?; + self.print_block_lines(block, |this, stmt| this.print_stmt(stmt))? } ast::StmtKind::Break => { - self.writer.write_str("break;\n")?; + self.writer.write_str("break;")?; } ast::StmtKind::Continue => { - self.writer.write_str("continue;\n")?; + self.writer.write_str("continue;")?; + } + ast::StmtKind::DeclSingle(decl) => { + self.print_variable_definition(decl)?; + self.writer.write_char(';')?; + } + ast::StmtKind::DeclMulti(vars, def) => { + self.writer.write_char('(')?; + self.print_comma_separated(vars, |this, var| { + if let Some(var) = var { + this.print_variable_definition(var)?; + } + + Ok(()) + })?; + self.writer.write_str(") = ")?; + self.print_expr(def)?; + self.writer.write_char(';')?; + } + ast::StmtKind::Expr(expr) => { + self.print_expr(expr)?; + self.writer.write_char(';')?; + } + ast::StmtKind::Return(expr) => { + self.writer.write_str("return")?; + if let Some(expr) = expr { + self.writer.write_char(' ')?; + self.print_expr(expr)?; + } + self.writer.write_char(';')?; + } + ast::StmtKind::If(cond, body, else_) => { + self.writer.write_str("if (")?; + self.print_expr(cond)?; + self.writer.write_str(") ")?; + self.print_stmt(body)?; + if let Some(else_) = else_ { + self.writer.write_str(" else ")?; + self.print_stmt(else_)?; + } + } + ast::StmtKind::Emit(event, args) => { + write!(self.writer, "emit {event}")?; + self.print_call_args(args)?; + self.writer.write_char(';')?; + } + ast::StmtKind::Revert(error, args) => { + write!(self.writer, "revert {error}")?; + self.print_call_args(args)?; + self.writer.write_char(';')?; + } + ast::StmtKind::Placeholder => { + self.writer.write_str("_;")?; + } + ast::StmtKind::Assembly(ast::StmtAssembly { dialect, flags, block }) => { + self.writer.write_str("assembly")?; + if let Some(dialect) = dialect { + write!(self.writer, " \"{}\"", dialect.value.as_str())?; + } + + if !flags.is_empty() { + self.writer.write_str(" (")?; + self.print_comma_separated(flags, |this, flag| { + write!(this.writer, "\"{}\"", flag.value.as_str()) + })?; + self.writer.write_str(")")?; + } + + self.writer.write_str("{}")?; + } + ast::StmtKind::For { init, cond, next, body } => { + self.writer.write_str("for (")?; + + if let Some(init) = init { + self.print_stmt(init)?; + } else { + self.writer.write_char(';')?; + } + + if let Some(cond) = cond { + self.writer.write_char(' ')?; + self.print_expr(cond)?; + } + + self.writer.write_char(';')?; + + if let Some(next) = next { + self.writer.write_char(' ')?; + self.print_expr(next)?; + } + + self.writer.write_str(") ")?; + self.print_stmt(body)?; + } + ast::StmtKind::While(cond, body) => { + self.writer.write_str("while (")?; + self.print_expr(cond)?; + self.writer.write_str(") ")?; + self.print_stmt(body)?; + } + ast::StmtKind::UncheckedBlock(block) => { + self.writer.write_str("unchecked ")?; + self.print_block_lines(block, |this, stmt| this.print_stmt(stmt))?; + } + ast::StmtKind::DoWhile(body, cond) => { + self.writer.write_str("do ")?; + self.print_stmt(body)?; + self.writer.write_str(" while (")?; + self.print_expr(cond)?; + self.writer.write_str(");")?; + } + ast::StmtKind::Try(ast::StmtTry { expr, returns, block, catch }) => { + self.writer.write_str("try ")?; + self.print_expr(expr)?; + if !returns.is_empty() { + self.writer.write_str(" returns (")?; + self.print_comma_separated(returns, |this, ret| { + this.print_variable_definition(ret) + })?; + self.writer.write_str(")")?; + } + + self.print_block_lines(block, |this, stmt| this.print_stmt(stmt))?; + + for catch in catch.iter() { + let ast::CatchClause { name, args, block } = catch; + + self.writer.write_str(" catch ")?; + if let Some(name) = name { + write!(self.writer, "{name}")?; + } + self.writer.write_char('(')?; + self.print_comma_separated(args, |this, arg| { + this.print_variable_definition(arg) + })?; + self.writer.write_str(") ")?; + self.print_block_lines(block, |this, stmt| this.print_stmt(stmt))?; + } } - _ => todo!() } Ok(()) } diff --git a/crates/ast/src/token.rs b/crates/ast/src/token.rs index 30fb9c5c..9cff150b 100644 --- a/crates/ast/src/token.rs +++ b/crates/ast/src/token.rs @@ -96,7 +96,7 @@ impl BinOpToken { Self::Star => BinOpKind::Mul, Self::Slash => BinOpKind::Div, Self::Percent => BinOpKind::Rem, - Self::Caret => BinOpKind::Pow, + Self::Caret => BinOpKind::BitXor, Self::And => BinOpKind::BitAnd, Self::Or => BinOpKind::BitOr, Self::Shl => BinOpKind::Shl, From ee0b2e3a12f4ba56e3a625bce20bc250856c4611 Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Mon, 9 Dec 2024 01:05:43 +0400 Subject: [PATCH 05/11] fix --- crates/ast/src/ast/item.rs | 10 +++------- crates/ast/src/pretty.rs | 2 +- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/crates/ast/src/ast/item.rs b/crates/ast/src/ast/item.rs index 1759d122..387b093f 100644 --- a/crates/ast/src/ast/item.rs +++ b/crates/ast/src/ast/item.rs @@ -331,14 +331,10 @@ impl UserDefinableOperator { Self::Ne => Either::Right(BinOpKind::Ne), } } -} -impl fmt::Display for UserDefinableOperator { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self.to_op() { - Either::Left(op) => f.write_str(op.to_str()), - Either::Right(op) => f.write_str(op.to_str()), - } + /// Returns the string representation of the operator. + pub const fn to_str(&self) -> &'static str { + either::for_both!(self.to_op(), op => op.to_str()) } } diff --git a/crates/ast/src/pretty.rs b/crates/ast/src/pretty.rs index 734ed427..c6fa14e1 100644 --- a/crates/ast/src/pretty.rs +++ b/crates/ast/src/pretty.rs @@ -165,7 +165,7 @@ impl Printer { self.print_comma_separated(paths, |this, (path, op)| { write!(this.writer, "{path}")?; if let Some(op) = op { - write!(this.writer, " as {op}")?; + write!(this.writer, " as {}", op.to_str())?; } Ok(()) From 27c6bfccd4d38344fdaba6d6f21790a7f90e5daf Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Mon, 9 Dec 2024 01:26:49 +0400 Subject: [PATCH 06/11] wip --- crates/ast/src/pretty.rs | 673 ++++++++++++++++++++------------------- 1 file changed, 349 insertions(+), 324 deletions(-) diff --git a/crates/ast/src/pretty.rs b/crates/ast/src/pretty.rs index c6fa14e1..38935853 100644 --- a/crates/ast/src/pretty.rs +++ b/crates/ast/src/pretty.rs @@ -40,7 +40,7 @@ impl Printer { self.indent -= 1; self.writer.write_char('\n')?; self.print_indent()?; - self.writer.write_str("}") + self.writer.write_char('}') } fn print_comma_separated( @@ -61,7 +61,9 @@ impl Printer { } pub fn print_soure_unit(&mut self, source_unit: &ast::SourceUnit<'_>) -> fmt::Result { - for item in source_unit.items.iter() { + let ast::SourceUnit { items } = source_unit; + + for item in items.iter() { self.print_item(item)?; self.writer.write_char('\n')?; } @@ -69,29 +71,294 @@ impl Printer { Ok(()) } - fn print_item(&mut self, item: &ast::Item<'_>) -> fmt::Result { + pub fn print_item(&mut self, item: &ast::Item<'_>) -> fmt::Result { let ast::Item { docs, span: _, kind } = item; + self.print_doc_comments(docs)?; match kind { ast::ItemKind::Pragma(item) => self.print_pragma_directive(item)?, ast::ItemKind::Import(item) => self.print_import_directive(item)?, ast::ItemKind::Using(item) => self.print_using_directive(item)?, ast::ItemKind::Contract(item) => self.print_item_contract(item)?, - ast::ItemKind::Struct(item) => self.print_item_struct(item)?, + ast::ItemKind::Function(item) => self.print_item_function(item)?, ast::ItemKind::Variable(item) => { self.print_variable_definition(item)?; self.writer.write_char(';')?; } - ast::ItemKind::Function(item) => self.print_item_function(item)?, - ast::ItemKind::Error(item) => self.print_item_error(item)?, - ast::ItemKind::Event(item) => self.print_item_event(item)?, + ast::ItemKind::Struct(item) => self.print_item_struct(item)?, ast::ItemKind::Enum(item) => self.print_item_enum(item)?, ast::ItemKind::Udvt(item) => self.print_item_udvt(item)?, + ast::ItemKind::Error(item) => self.print_item_error(item)?, + ast::ItemKind::Event(item) => self.print_item_event(item)?, }; Ok(()) } + pub fn print_stmt(&mut self, stmt: &ast::Stmt<'_>) -> fmt::Result { + let ast::Stmt { docs, span: _, kind } = stmt; + + self.print_doc_comments(docs)?; + match kind { + ast::StmtKind::Assembly(ast::StmtAssembly { dialect, flags, block }) => { + self.writer.write_str("assembly")?; + if let Some(dialect) = dialect { + write!(self.writer, " \"{}\"", dialect.value.as_str())?; + } + + if !flags.is_empty() { + self.writer.write_str(" (")?; + self.print_comma_separated(flags, |this, flag| { + write!(this.writer, "\"{}\"", flag.value.as_str()) + })?; + self.writer.write_char(')')?; + } + + self.writer.write_str("{}")?; + } + ast::StmtKind::DeclSingle(decl) => { + self.print_variable_definition(decl)?; + self.writer.write_char(';')?; + } + ast::StmtKind::DeclMulti(vars, def) => { + self.writer.write_char('(')?; + self.print_comma_separated(vars, |this, var| { + if let Some(var) = var { + this.print_variable_definition(var)?; + } + + Ok(()) + })?; + self.writer.write_str(") = ")?; + self.print_expr(def)?; + self.writer.write_char(';')?; + } + ast::StmtKind::Block(block) => { + self.print_block_lines(block, |this, stmt| this.print_stmt(stmt))? + } + ast::StmtKind::Break => { + self.writer.write_str("break;")?; + } + ast::StmtKind::Continue => { + self.writer.write_str("continue;")?; + } + ast::StmtKind::DoWhile(body, cond) => { + self.writer.write_str("do ")?; + self.print_stmt(body)?; + self.writer.write_str(" while (")?; + self.print_expr(cond)?; + self.writer.write_str(");")?; + } + ast::StmtKind::Emit(event, args) => { + write!(self.writer, "emit {event}")?; + self.print_call_args(args)?; + self.writer.write_char(';')?; + } + ast::StmtKind::Expr(expr) => { + self.print_expr(expr)?; + self.writer.write_char(';')?; + } + ast::StmtKind::For { init, cond, next, body } => { + self.writer.write_str("for (")?; + + if let Some(init) = init { + self.print_stmt(init)?; + } else { + self.writer.write_char(';')?; + } + + if let Some(cond) = cond { + self.writer.write_char(' ')?; + self.print_expr(cond)?; + } + + self.writer.write_char(';')?; + + if let Some(next) = next { + self.writer.write_char(' ')?; + self.print_expr(next)?; + } + + self.writer.write_str(") ")?; + self.print_stmt(body)?; + } + ast::StmtKind::If(cond, body, else_) => { + self.writer.write_str("if (")?; + self.print_expr(cond)?; + self.writer.write_str(") ")?; + self.print_stmt(body)?; + if let Some(else_) = else_ { + self.writer.write_str(" else ")?; + self.print_stmt(else_)?; + } + } + ast::StmtKind::Return(expr) => { + self.writer.write_str("return")?; + if let Some(expr) = expr { + self.writer.write_char(' ')?; + self.print_expr(expr)?; + } + self.writer.write_char(';')?; + } + ast::StmtKind::Revert(error, args) => { + write!(self.writer, "revert {error}")?; + self.print_call_args(args)?; + self.writer.write_char(';')?; + } + ast::StmtKind::Try(ast::StmtTry { expr, returns, block, catch }) => { + self.writer.write_str("try ")?; + self.print_expr(expr)?; + if !returns.is_empty() { + self.writer.write_str(" returns (")?; + self.print_comma_separated(returns, |this, ret| { + this.print_variable_definition(ret) + })?; + self.writer.write_char(')')?; + } + + self.print_block_lines(block, |this, stmt| this.print_stmt(stmt))?; + + for catch in catch.iter() { + let ast::CatchClause { name, args, block } = catch; + + self.writer.write_str(" catch ")?; + if let Some(name) = name { + write!(self.writer, "{name}")?; + } + self.writer.write_char('(')?; + self.print_comma_separated(args, |this, arg| { + this.print_variable_definition(arg) + })?; + self.writer.write_str(") ")?; + self.print_block_lines(block, |this, stmt| this.print_stmt(stmt))?; + } + } + ast::StmtKind::UncheckedBlock(block) => { + self.writer.write_str("unchecked ")?; + self.print_block_lines(block, |this, stmt| this.print_stmt(stmt))?; + } + ast::StmtKind::While(cond, body) => { + self.writer.write_str("while (")?; + self.print_expr(cond)?; + self.writer.write_str(") ")?; + self.print_stmt(body)?; + } + ast::StmtKind::Placeholder => { + self.writer.write_str("_;")?; + } + } + Ok(()) + } + + pub fn print_expr(&mut self, expr: &ast::Expr<'_>) -> fmt::Result { + match &expr.kind { + ast::ExprKind::Array(exprs) => { + self.print_comma_separated(exprs, |this, expr| this.print_expr(expr))?; + } + ast::ExprKind::Assign(lhs, op, rhs) => { + self.print_expr(lhs)?; + self.writer.write_char(' ')?; + if let Some(op) = op { + write!(self.writer, "{op}")?; + } + self.writer.write_str("= ")?; + self.print_expr(rhs)?; + } + ast::ExprKind::Binary(lhs, op, rhs) => { + self.print_expr(lhs)?; + write!(self.writer, " {op} ")?; + self.print_expr(rhs)?; + } + ast::ExprKind::Call(expr, args) => { + self.print_expr(expr)?; + self.print_call_args(args)?; + } + ast::ExprKind::CallOptions(item, opts) => { + self.print_expr(item)?; + self.print_named_args(opts)?; + } + ast::ExprKind::Delete(expr) => { + self.writer.write_str("delete ")?; + self.print_expr(expr)?; + } + ast::ExprKind::Ident(ident) => write!(self.writer, "{ident}")?, + ast::ExprKind::Index(item, index) => { + self.print_expr(item)?; + self.writer.write_char('[')?; + match index { + ast::IndexKind::Index(expr) => { + if let Some(expr) = expr { + self.print_expr(expr)?; + } + } + ast::IndexKind::Range(start, end) => { + if let Some(start) = start { + self.print_expr(start)?; + } + self.writer.write_char(':')?; + if let Some(end) = end { + self.print_expr(end)?; + } + } + } + self.writer.write_char(']')?; + } + ast::ExprKind::Lit(lit, denom) => { + write!(self.writer, "{lit}")?; + if let Some(denom) = denom { + write!(self.writer, " {denom}")?; + } + } + ast::ExprKind::Member(item, member) => { + self.print_expr(item)?; + write!(self.writer, ".{member}")?; + } + ast::ExprKind::New(ty) => { + self.writer.write_str("new ")?; + self.print_ty(ty)?; + } + ast::ExprKind::Payable(opts) => { + self.writer.write_str("payable")?; + self.print_call_args(opts)?; + } + ast::ExprKind::Ternary(cond, first, second) => { + self.print_expr(cond)?; + self.writer.write_str(" ? ")?; + self.print_expr(first)?; + self.writer.write_str(" : ")?; + self.print_expr(second)?; + } + ast::ExprKind::Tuple(exprs) => { + self.writer.write_char('(')?; + self.print_comma_separated(exprs, |this, expr| { + if let Some(expr) = expr { + this.print_expr(expr)?; + } + + Ok(()) + })?; + self.writer.write_char(')')?; + } + ast::ExprKind::TypeCall(ty) => { + self.writer.write_str("type(")?; + self.print_ty(ty)?; + self.writer.write_char(')')?; + } + ast::ExprKind::Type(ty) => self.print_ty(ty)?, + ast::ExprKind::Unary(op, expr) => { + if op.kind.is_prefix() { + write!(self.writer, "{op}")?; + self.print_expr(expr)?; + } else { + self.print_expr(expr)?; + write!(self.writer, "{op}")?; + } + } + } + + Ok(()) + } + fn print_pragma_directive(&mut self, pragma: &ast::PragmaDirective<'_>) -> fmt::Result { let ast::PragmaDirective { tokens } = pragma; @@ -113,7 +380,7 @@ impl Printer { write!(self.writer, " {req}")?; } } - self.writer.write_str(";")?; + self.writer.write_char(';')?; Ok(()) } @@ -127,10 +394,10 @@ impl Printer { if let Some(alias) = alias { write!(self.writer, " as {alias}")?; } - self.writer.write_str(";")?; + self.writer.write_char(';')?; } ast::ImportItems::Aliases(items) => { - self.writer.write_str("{")?; + self.writer.write_char('{')?; self.print_comma_separated(items, |this, (item, alias)| { write!(this.writer, "{item}")?; if let Some(alias) = alias { @@ -206,57 +473,6 @@ impl Printer { self.print_block_lines(body, |this, item| this.print_item(item)) } - fn print_item_error(&mut self, error: &ast::ItemError<'_>) -> fmt::Result { - let ast::ItemError { name, parameters } = error; - - write!(self.writer, "error {name}(")?; - self.print_comma_separated(parameters, |this, param| { - this.print_variable_definition(param) - })?; - - self.writer.write_str(");") - } - - fn print_item_event(&mut self, error: &ast::ItemEvent<'_>) -> fmt::Result { - let ast::ItemEvent { name, parameters, anonymous } = error; - - write!(self.writer, "event {name}(")?; - self.print_comma_separated(parameters, |this, param| { - this.print_variable_definition(param) - })?; - - if *anonymous { - self.writer.write_str(" anonymous")?; - } - - self.writer.write_str(");") - } - - fn print_item_enum(&mut self, enum_: &ast::ItemEnum<'_>) -> fmt::Result { - let ast::ItemEnum { name, variants } = enum_; - - write!(self.writer, "enum {name} ")?; - self.print_block_lines(variants, |this, variant| write!(this.writer, "{variant},")) - } - - fn print_item_udvt(&mut self, udvt: &ast::ItemUdvt<'_>) -> fmt::Result { - let ast::ItemUdvt { name, ty } = udvt; - - write!(self.writer, "type {name} is ")?; - self.print_ty(ty)?; - self.writer.write_char(';') - } - - fn print_item_struct(&mut self, struct_: &ast::ItemStruct<'_>) -> fmt::Result { - let ast::ItemStruct { name, fields } = struct_; - - write!(self.writer, "struct {name} ")?; - self.print_block_lines(fields, |this, field| { - this.print_variable_definition(field)?; - this.writer.write_char(';') - }) - } - fn print_item_function(&mut self, function: &ast::ItemFunction<'_>) -> fmt::Result { let ast::ItemFunction { kind, header, body } = function; let ast::FunctionHeader { @@ -275,11 +491,11 @@ impl Printer { write!(self.writer, " {name}")?; } - self.writer.write_str("(")?; + self.writer.write_char('(')?; self.print_comma_separated(parameters, |this, param| { this.print_variable_definition(param) })?; - self.writer.write_str(")")?; + self.writer.write_char(')')?; if let Some(visibility) = visibility { write!(self.writer, " {visibility}")?; @@ -307,7 +523,7 @@ impl Printer { if !returns.is_empty() { write!(self.writer, " returns (")?; self.print_comma_separated(returns, |this, val| this.print_variable_definition(val))?; - self.writer.write_str(")")?; + self.writer.write_char(')')?; } if let Some(body) = body { @@ -364,6 +580,57 @@ impl Printer { Ok(()) } + fn print_item_struct(&mut self, struct_: &ast::ItemStruct<'_>) -> fmt::Result { + let ast::ItemStruct { name, fields } = struct_; + + write!(self.writer, "struct {name} ")?; + self.print_block_lines(fields, |this, field| { + this.print_variable_definition(field)?; + this.writer.write_char(';') + }) + } + + fn print_item_enum(&mut self, enum_: &ast::ItemEnum<'_>) -> fmt::Result { + let ast::ItemEnum { name, variants } = enum_; + + write!(self.writer, "enum {name} ")?; + self.print_block_lines(variants, |this, variant| write!(this.writer, "{variant},")) + } + + fn print_item_udvt(&mut self, udvt: &ast::ItemUdvt<'_>) -> fmt::Result { + let ast::ItemUdvt { name, ty } = udvt; + + write!(self.writer, "type {name} is ")?; + self.print_ty(ty)?; + self.writer.write_char(';') + } + + fn print_item_error(&mut self, error: &ast::ItemError<'_>) -> fmt::Result { + let ast::ItemError { name, parameters } = error; + + write!(self.writer, "error {name}(")?; + self.print_comma_separated(parameters, |this, param| { + this.print_variable_definition(param) + })?; + + self.writer.write_str(");") + } + + fn print_item_event(&mut self, error: &ast::ItemEvent<'_>) -> fmt::Result { + let ast::ItemEvent { name, parameters, anonymous } = error; + + write!(self.writer, "event {name}(")?; + self.print_comma_separated(parameters, |this, param| { + this.print_variable_definition(param) + })?; + + if *anonymous { + self.writer.write_str(" anonymous")?; + } + + self.writer.write_str(");") + } + fn print_ty(&mut self, ty: &ast::Type<'_>) -> fmt::Result { let ast::Type { span: _, kind } = ty; @@ -372,11 +639,11 @@ impl Printer { ast::TypeKind::Array(ty) => { let ast::TypeArray { size, element } = ty; self.print_ty(element)?; - self.writer.write_str("[")?; + self.writer.write_char('[')?; if let Some(size) = size { self.print_expr(size)?; } - self.writer.write_str("]")?; + self.writer.write_char(']')?; } ast::TypeKind::Function(ty) => { let ast::TypeFunction { parameters, returns, visibility, state_mutability } = ty; @@ -384,7 +651,7 @@ impl Printer { self.print_comma_separated(parameters, |this, param| { this.print_variable_definition(param) })?; - self.writer.write_str(")")?; + self.writer.write_char(')')?; if let Some(visibility) = visibility { write!(self.writer, " {visibility}")?; } @@ -394,7 +661,7 @@ impl Printer { self.print_comma_separated(returns, |this, ret| { this.print_variable_definition(ret) })?; - self.writer.write_str(")")?; + self.writer.write_char(')')?; } } ast::TypeKind::Mapping(ty) => { @@ -409,7 +676,7 @@ impl Printer { if let Some(value_name) = value_name { write!(self.writer, " {value_name}")?; } - self.writer.write_str(")")?; + self.writer.write_char(')')?; } ast::TypeKind::Custom(ty) => { write!(self.writer, "{ty}")?; @@ -464,265 +731,23 @@ impl Printer { self.writer.write_char('}') } - fn print_expr(&mut self, expr: &ast::Expr<'_>) -> fmt::Result { - match &expr.kind { - ast::ExprKind::Array(exprs) => { - self.print_comma_separated(exprs, |this, expr| this.print_expr(expr))?; - } - ast::ExprKind::Assign(lhs, op, rhs) => { - self.print_expr(lhs)?; - self.writer.write_char(' ')?; - if let Some(op) = op { - write!(self.writer, "{op}")?; - } - self.writer.write_str("= ")?; - self.print_expr(rhs)?; - } - ast::ExprKind::Binary(lhs, op, rhs) => { - self.print_expr(lhs)?; - write!(self.writer, " {op} ")?; - self.print_expr(rhs)?; - } - ast::ExprKind::Call(expr, args) => { - self.print_expr(expr)?; - self.print_call_args(args)?; - } - ast::ExprKind::Lit(lit, denom) => { - write!(self.writer, "{lit}")?; - if let Some(denom) = denom { - write!(self.writer, " {denom}")?; - } - } - ast::ExprKind::Ident(ident) => write!(self.writer, "{ident}")?, - ast::ExprKind::Member(item, member) => { - self.print_expr(item)?; - write!(self.writer, ".{member}")?; - } - ast::ExprKind::Type(ty) => self.print_ty(ty)?, - ast::ExprKind::Tuple(exprs) => { - self.writer.write_str("(")?; - self.print_comma_separated(exprs, |this, expr| { - if let Some(expr) = expr { - this.print_expr(expr)?; - } - - Ok(()) - })?; - self.writer.write_str(")")?; - } - ast::ExprKind::New(ty) => { - self.writer.write_str("new ")?; - self.print_ty(ty)?; - } - ast::ExprKind::Unary(op, expr) => { - if op.kind.is_prefix() { - write!(self.writer, "{op}")?; - self.print_expr(expr)?; - } else { - self.print_expr(expr)?; - write!(self.writer, "{op}")?; - } - } - ast::ExprKind::TypeCall(ty) => { - self.writer.write_str("type(")?; - self.print_ty(ty)?; - self.writer.write_char(')')?; - } - ast::ExprKind::Ternary(cond, first, second) => { - self.print_expr(cond)?; - self.writer.write_str(" ? ")?; - self.print_expr(first)?; - self.writer.write_str(" : ")?; - self.print_expr(second)?; - } - ast::ExprKind::Index(item, index) => { - self.print_expr(item)?; - self.writer.write_char('[')?; - match index { - ast::IndexKind::Index(expr) => { - if let Some(expr) = expr { - self.print_expr(expr)?; - } - } - ast::IndexKind::Range(start, end) => { - if let Some(start) = start { - self.print_expr(start)?; - } - self.writer.write_char(':')?; - if let Some(end) = end { - self.print_expr(end)?; - } - } - } - self.writer.write_char(']')?; - } - ast::ExprKind::Delete(expr) => { - self.writer.write_str("delete ")?; - self.print_expr(expr)?; - } - ast::ExprKind::CallOptions(item, opts) => { - self.print_expr(item)?; - self.print_named_args(opts)?; - } - ast::ExprKind::Payable(opts) => { - self.writer.write_str("payable")?; - self.print_call_args(opts)?; - } - } - - Ok(()) - } - - fn print_stmt(&mut self, stmt: &ast::Stmt<'_>) -> fmt::Result { - let ast::Stmt { docs, span: _, kind } = stmt; - - match kind { - ast::StmtKind::Block(block) => { - self.print_block_lines(block, |this, stmt| this.print_stmt(stmt))? - } - ast::StmtKind::Break => { - self.writer.write_str("break;")?; - } - ast::StmtKind::Continue => { - self.writer.write_str("continue;")?; - } - ast::StmtKind::DeclSingle(decl) => { - self.print_variable_definition(decl)?; - self.writer.write_char(';')?; - } - ast::StmtKind::DeclMulti(vars, def) => { - self.writer.write_char('(')?; - self.print_comma_separated(vars, |this, var| { - if let Some(var) = var { - this.print_variable_definition(var)?; - } - - Ok(()) - })?; - self.writer.write_str(") = ")?; - self.print_expr(def)?; - self.writer.write_char(';')?; - } - ast::StmtKind::Expr(expr) => { - self.print_expr(expr)?; - self.writer.write_char(';')?; - } - ast::StmtKind::Return(expr) => { - self.writer.write_str("return")?; - if let Some(expr) = expr { - self.writer.write_char(' ')?; - self.print_expr(expr)?; - } - self.writer.write_char(';')?; - } - ast::StmtKind::If(cond, body, else_) => { - self.writer.write_str("if (")?; - self.print_expr(cond)?; - self.writer.write_str(") ")?; - self.print_stmt(body)?; - if let Some(else_) = else_ { - self.writer.write_str(" else ")?; - self.print_stmt(else_)?; - } - } - ast::StmtKind::Emit(event, args) => { - write!(self.writer, "emit {event}")?; - self.print_call_args(args)?; - self.writer.write_char(';')?; - } - ast::StmtKind::Revert(error, args) => { - write!(self.writer, "revert {error}")?; - self.print_call_args(args)?; - self.writer.write_char(';')?; - } - ast::StmtKind::Placeholder => { - self.writer.write_str("_;")?; - } - ast::StmtKind::Assembly(ast::StmtAssembly { dialect, flags, block }) => { - self.writer.write_str("assembly")?; - if let Some(dialect) = dialect { - write!(self.writer, " \"{}\"", dialect.value.as_str())?; - } - - if !flags.is_empty() { - self.writer.write_str(" (")?; - self.print_comma_separated(flags, |this, flag| { - write!(this.writer, "\"{}\"", flag.value.as_str()) - })?; - self.writer.write_str(")")?; - } - - self.writer.write_str("{}")?; - } - ast::StmtKind::For { init, cond, next, body } => { - self.writer.write_str("for (")?; - - if let Some(init) = init { - self.print_stmt(init)?; - } else { - self.writer.write_char(';')?; - } - - if let Some(cond) = cond { - self.writer.write_char(' ')?; - self.print_expr(cond)?; + fn print_doc_comments(&mut self, items: &ast::DocComments<'_>) -> fmt::Result { + for item in items.iter() { + self.print_indent()?; + let ast::DocComment { span: _, kind, symbol } = item; + match kind { + ast::CommentKind::Line => { + self.writer.write_str("// ")?; + self.writer.write_str(symbol.as_str())?; } - - self.writer.write_char(';')?; - - if let Some(next) = next { - self.writer.write_char(' ')?; - self.print_expr(next)?; + ast::CommentKind::Block => { + self.writer.write_str("/* ")?; + self.writer.write_str(symbol.as_str())?; + self.writer.write_str(" */")?; } - - self.writer.write_str(") ")?; - self.print_stmt(body)?; - } - ast::StmtKind::While(cond, body) => { - self.writer.write_str("while (")?; - self.print_expr(cond)?; - self.writer.write_str(") ")?; - self.print_stmt(body)?; - } - ast::StmtKind::UncheckedBlock(block) => { - self.writer.write_str("unchecked ")?; - self.print_block_lines(block, |this, stmt| this.print_stmt(stmt))?; - } - ast::StmtKind::DoWhile(body, cond) => { - self.writer.write_str("do ")?; - self.print_stmt(body)?; - self.writer.write_str(" while (")?; - self.print_expr(cond)?; - self.writer.write_str(");")?; } - ast::StmtKind::Try(ast::StmtTry { expr, returns, block, catch }) => { - self.writer.write_str("try ")?; - self.print_expr(expr)?; - if !returns.is_empty() { - self.writer.write_str(" returns (")?; - self.print_comma_separated(returns, |this, ret| { - this.print_variable_definition(ret) - })?; - self.writer.write_str(")")?; - } - - self.print_block_lines(block, |this, stmt| this.print_stmt(stmt))?; - for catch in catch.iter() { - let ast::CatchClause { name, args, block } = catch; - - self.writer.write_str(" catch ")?; - if let Some(name) = name { - write!(self.writer, "{name}")?; - } - self.writer.write_char('(')?; - self.print_comma_separated(args, |this, arg| { - this.print_variable_definition(arg) - })?; - self.writer.write_str(") ")?; - self.print_block_lines(block, |this, stmt| this.print_stmt(stmt))?; - } - } + self.writer.write_char('\n')?; } Ok(()) } From 6a4b39e72debb3ac7f89ab7dd4709c133a6556e5 Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Mon, 9 Dec 2024 01:34:19 +0400 Subject: [PATCH 07/11] fix --- crates/ast/src/pretty.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/crates/ast/src/pretty.rs b/crates/ast/src/pretty.rs index 38935853..6d8c80db 100644 --- a/crates/ast/src/pretty.rs +++ b/crates/ast/src/pretty.rs @@ -655,7 +655,9 @@ impl Printer { if let Some(visibility) = visibility { write!(self.writer, " {visibility}")?; } - write!(self.writer, " {state_mutability}")?; + if !state_mutability.is_non_payable() { + write!(self.writer, " {state_mutability}")?; + } if !returns.is_empty() { self.writer.write_str(" returns(")?; self.print_comma_separated(returns, |this, ret| { @@ -733,11 +735,10 @@ impl Printer { fn print_doc_comments(&mut self, items: &ast::DocComments<'_>) -> fmt::Result { for item in items.iter() { - self.print_indent()?; let ast::DocComment { span: _, kind, symbol } = item; match kind { ast::CommentKind::Line => { - self.writer.write_str("// ")?; + self.writer.write_str("/// ")?; self.writer.write_str(symbol.as_str())?; } ast::CommentKind::Block => { @@ -748,6 +749,7 @@ impl Printer { } self.writer.write_char('\n')?; + self.print_indent()?; } Ok(()) } From 81769334fab434919dbe25730ce0536dc78c974b Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Mon, 9 Dec 2024 02:03:32 +0400 Subject: [PATCH 08/11] yul --- crates/ast/src/pretty.rs | 200 +++++++++++++++++++++++++++++---------- 1 file changed, 152 insertions(+), 48 deletions(-) diff --git a/crates/ast/src/pretty.rs b/crates/ast/src/pretty.rs index 6d8c80db..50c6701f 100644 --- a/crates/ast/src/pretty.rs +++ b/crates/ast/src/pretty.rs @@ -1,6 +1,6 @@ //! AST pretty-printing. -use crate::ast; +use crate::ast::{self, yul}; use core::fmt::{self, Write}; /// AST pretty-printer. @@ -11,55 +11,14 @@ pub struct Printer { } impl Printer { + /// Creates a new printer with the given writer. pub fn new(writer: W) -> Self { Self { writer, indent: 0 } } } impl Printer { - fn print_indent(&mut self) -> fmt::Result { - write!(self.writer, "{}", " ".repeat(self.indent)) - } - - fn print_block_lines( - &mut self, - items: &[I], - f: impl Fn(&mut Self, &I) -> fmt::Result, - ) -> fmt::Result { - if items.is_empty() { - return self.writer.write_str("{}"); - } - - self.writer.write_char('{')?; - self.indent += 1; - for item in items { - self.writer.write_char('\n')?; - self.print_indent()?; - f(self, item)?; - } - self.indent -= 1; - self.writer.write_char('\n')?; - self.print_indent()?; - self.writer.write_char('}') - } - - fn print_comma_separated( - &mut self, - items: &[I], - f: impl Fn(&mut Self, &I) -> fmt::Result, - ) -> fmt::Result { - let mut iter = items.iter(); - if let Some(first) = iter.next() { - f(self, first)?; - for item in iter { - self.writer.write_str(", ")?; - f(self, item)?; - } - } - - Ok(()) - } - + /// Prints a single [Solidity source file](`ast::SourceUnit`). pub fn print_soure_unit(&mut self, source_unit: &ast::SourceUnit<'_>) -> fmt::Result { let ast::SourceUnit { items } = source_unit; @@ -71,10 +30,11 @@ impl Printer { Ok(()) } + /// Prints a single [item](`ast::Item`). pub fn print_item(&mut self, item: &ast::Item<'_>) -> fmt::Result { let ast::Item { docs, span: _, kind } = item; - self.print_doc_comments(docs)?; + self.print_docs(docs)?; match kind { ast::ItemKind::Pragma(item) => self.print_pragma_directive(item)?, ast::ItemKind::Import(item) => self.print_import_directive(item)?, @@ -95,10 +55,11 @@ impl Printer { Ok(()) } + /// Prints a single [statement](`ast::Stmt`). pub fn print_stmt(&mut self, stmt: &ast::Stmt<'_>) -> fmt::Result { let ast::Stmt { docs, span: _, kind } = stmt; - self.print_doc_comments(docs)?; + self.print_docs(docs)?; match kind { ast::StmtKind::Assembly(ast::StmtAssembly { dialect, flags, block }) => { self.writer.write_str("assembly")?; @@ -114,7 +75,8 @@ impl Printer { self.writer.write_char(')')?; } - self.writer.write_str("{}")?; + self.writer.write_char(' ')?; + self.print_block_lines(block, |this, stmt| this.print_yul_stmt(stmt))? } ast::StmtKind::DeclSingle(decl) => { self.print_variable_definition(decl)?; @@ -250,6 +212,84 @@ impl Printer { Ok(()) } + /// Prints a single [Yul statement](`yul::Stmt`). + pub fn print_yul_stmt(&mut self, stmt: &yul::Stmt<'_>) -> fmt::Result { + let yul::Stmt { docs, span: _, kind } = stmt; + + self.print_docs(docs)?; + match kind { + yul::StmtKind::Block(block) => { + self.print_block_lines(block, |this, stmt| this.print_yul_stmt(stmt))? + } + yul::StmtKind::AssignSingle(path, expr) => { + write!(self.writer, "{path} := ")?; + self.print_yul_expr(expr)?; + } + yul::StmtKind::AssignMulti(paths, call) => { + self.print_comma_separated(paths, |this, path| write!(this.writer, "{path}"))?; + self.writer.write_str(" := ")?; + self.print_yul_expr_call(call)?; + } + yul::StmtKind::Expr(call) => self.print_yul_expr_call(call)?, + yul::StmtKind::If(cond, body) => { + self.writer.write_str("if ")?; + self.print_yul_expr(cond)?; + self.writer.write_char(' ')?; + self.print_block_lines(body, |this, stmt| this.print_yul_stmt(stmt))?; + } + yul::StmtKind::For { init, cond, step, body } => { + self.writer.write_str("for ")?; + self.print_block_lines(init, |this, stmt| this.print_yul_stmt(stmt))?; + self.writer.write_char(' ')?; + self.print_yul_expr(cond)?; + self.writer.write_char(' ')?; + self.print_block_lines(step, |this, stmt| this.print_yul_stmt(stmt))?; + self.writer.write_char(' ')?; + self.print_block_lines(body, |this, stmt| this.print_yul_stmt(stmt))?; + } + yul::StmtKind::Switch(yul::StmtSwitch { selector, branches, default_case }) => { + self.writer.write_str("switch ")?; + self.print_yul_expr(selector)?; + for yul::StmtSwitchCase { constant, body } in branches.iter() { + self.writer.write_char('\n')?; + self.print_indent()?; + write!(self.writer, "case {constant} ")?; + self.print_block_lines(body, |this, stmt| this.print_yul_stmt(stmt))?; + } + if let Some(default_case) = default_case { + self.writer.write_char('\n')?; + self.print_indent()?; + self.writer.write_str("default ")?; + self.print_block_lines(default_case, |this, stmt| this.print_yul_stmt(stmt))?; + } + } + yul::StmtKind::Leave => self.writer.write_str("leave")?, + yul::StmtKind::Break => self.writer.write_str("break")?, + yul::StmtKind::Continue => self.writer.write_str("continue")?, + yul::StmtKind::FunctionDef(yul::Function { name, parameters, returns, body }) => { + write!(self.writer, "function {name}(")?; + self.print_comma_separated(parameters, |this, param| { + write!(this.writer, "{param}") + })?; + self.writer.write_str(") -> (")?; + self.print_comma_separated(returns, |this, ret| write!(this.writer, "{ret}"))?; + self.writer.write_str(") ")?; + self.print_block_lines(body, |this, stmt| this.print_yul_stmt(stmt))? + } + yul::StmtKind::VarDecl(vars, init) => { + self.writer.write_str("let ")?; + self.print_comma_separated(vars, |this, var| write!(this.writer, "{var}"))?; + if let Some(init) = init { + self.writer.write_str(" := ")?; + self.print_yul_expr(init)?; + } + } + }; + + Ok(()) + } + + /// Prints a single [Solidity expression](`ast::Expr`). pub fn print_expr(&mut self, expr: &ast::Expr<'_>) -> fmt::Result { match &expr.kind { ast::ExprKind::Array(exprs) => { @@ -359,6 +399,62 @@ impl Printer { Ok(()) } + /// Prints a single [Yul expression](`yul::Expr`). + pub fn print_yul_expr(&mut self, expr: &yul::Expr<'_>) -> fmt::Result { + let yul::Expr { span: _, kind } = expr; + + match kind { + yul::ExprKind::Path(path) => write!(self.writer, "{path}")?, + yul::ExprKind::Call(call) => self.print_yul_expr_call(call)?, + yul::ExprKind::Lit(lit) => write!(self.writer, "{lit}")?, + }; + + Ok(()) + } + + fn print_indent(&mut self) -> fmt::Result { + write!(self.writer, "{}", " ".repeat(self.indent)) + } + + fn print_block_lines( + &mut self, + items: &[I], + f: impl Fn(&mut Self, &I) -> fmt::Result, + ) -> fmt::Result { + if items.is_empty() { + return self.writer.write_str("{}"); + } + + self.writer.write_char('{')?; + self.indent += 1; + for item in items { + self.writer.write_char('\n')?; + self.print_indent()?; + f(self, item)?; + } + self.indent -= 1; + self.writer.write_char('\n')?; + self.print_indent()?; + self.writer.write_char('}') + } + + fn print_comma_separated( + &mut self, + items: &[I], + f: impl Fn(&mut Self, &I) -> fmt::Result, + ) -> fmt::Result { + let mut iter = items.iter(); + if let Some(first) = iter.next() { + f(self, first)?; + for item in iter { + self.writer.write_str(", ")?; + f(self, item)?; + } + } + + Ok(()) + } + fn print_pragma_directive(&mut self, pragma: &ast::PragmaDirective<'_>) -> fmt::Result { let ast::PragmaDirective { tokens } = pragma; @@ -733,7 +829,7 @@ impl Printer { self.writer.write_char('}') } - fn print_doc_comments(&mut self, items: &ast::DocComments<'_>) -> fmt::Result { + fn print_docs(&mut self, items: &ast::DocComments<'_>) -> fmt::Result { for item in items.iter() { let ast::DocComment { span: _, kind, symbol } = item; match kind { @@ -753,4 +849,12 @@ impl Printer { } Ok(()) } + + fn print_yul_expr_call(&mut self, call: &yul::ExprCall<'_>) -> fmt::Result { + let yul::ExprCall { name, arguments } = call; + + write!(self.writer, "{name}(")?; + self.print_comma_separated(arguments, |this, expr| this.print_yul_expr(expr))?; + self.writer.write_char(')') + } } From 6289b6130cf1410018b656ea36013bc3afb9b3f4 Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Mon, 9 Dec 2024 02:04:39 +0400 Subject: [PATCH 09/11] fix --- Cargo.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index 8228a72b..ac671046 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,6 +1,6 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 4 +version = 3 [[package]] name = "addr2line" From 6d46b65e5f75d83c789210eb3d70efe4e99e1ee6 Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Mon, 9 Dec 2024 02:06:15 +0400 Subject: [PATCH 10/11] fix doc --- crates/ast/src/pretty.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/ast/src/pretty.rs b/crates/ast/src/pretty.rs index 50c6701f..ec3bb567 100644 --- a/crates/ast/src/pretty.rs +++ b/crates/ast/src/pretty.rs @@ -55,7 +55,7 @@ impl Printer { Ok(()) } - /// Prints a single [statement](`ast::Stmt`). + /// Prints a single [Solidity statement](`ast::Stmt`). pub fn print_stmt(&mut self, stmt: &ast::Stmt<'_>) -> fmt::Result { let ast::Stmt { docs, span: _, kind } = stmt; From 7565d62eb034f913528f8a708798e0e5da399c72 Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Mon, 9 Dec 2024 02:26:27 +0400 Subject: [PATCH 11/11] fix --- crates/ast/src/pretty.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/crates/ast/src/pretty.rs b/crates/ast/src/pretty.rs index ec3bb567..66d971a0 100644 --- a/crates/ast/src/pretty.rs +++ b/crates/ast/src/pretty.rs @@ -178,6 +178,7 @@ impl Printer { self.writer.write_char(')')?; } + self.writer.write_char(' ')?; self.print_block_lines(block, |this, stmt| this.print_stmt(stmt))?; for catch in catch.iter() { @@ -421,10 +422,6 @@ impl Printer { items: &[I], f: impl Fn(&mut Self, &I) -> fmt::Result, ) -> fmt::Result { - if items.is_empty() { - return self.writer.write_str("{}"); - } - self.writer.write_char('{')?; self.indent += 1; for item in items {