convert low fid ast to typed

rebase
This commit is contained in:
franfran 2022-12-29 17:23:13 +01:00 committed by francois
parent 29510ed8e4
commit 6acd2735c1
2 changed files with 230 additions and 131 deletions

136
Cargo.lock generated
View File

@ -1420,7 +1420,6 @@ dependencies = [
"hex", "hex",
"hex-literal", "hex-literal",
"k256", "k256",
"num_enum",
"once_cell", "once_cell",
"open-fastrlp", "open-fastrlp",
"proc-macro2", "proc-macro2",
@ -1627,111 +1626,6 @@ dependencies = [
"wee_alloc", "wee_alloc",
] ]
[[package]]
name = "examples-anvil"
version = "1.0.2"
dependencies = [
"ethers",
"eyre",
"tokio",
]
[[package]]
name = "examples-big-numbers"
version = "1.0.2"
dependencies = [
"ethers",
]
[[package]]
name = "examples-contracts"
version = "1.0.2"
dependencies = [
"ethers",
"eyre",
"serde",
"serde_json",
"tokio",
]
[[package]]
name = "examples-events"
version = "1.0.2"
dependencies = [
"ethers",
"eyre",
"serde",
"serde_json",
"tokio",
]
[[package]]
name = "examples-middleware"
version = "1.0.2"
dependencies = [
"async-trait",
"ethers",
"eyre",
"serde_json",
"thiserror",
"tokio",
]
[[package]]
name = "examples-providers"
version = "1.0.2"
dependencies = [
"ethers",
"eyre",
"serde",
"serde_json",
"tokio",
]
[[package]]
name = "examples-queries"
version = "1.0.2"
dependencies = [
"ethers",
"eyre",
"serde",
"serde_json",
"tokio",
]
[[package]]
name = "examples-subscriptions"
version = "1.0.2"
dependencies = [
"ethers",
"eyre",
"serde",
"serde_json",
"tokio",
]
[[package]]
name = "examples-transactions"
version = "1.0.2"
dependencies = [
"ethers",
"eyre",
"serde",
"serde_json",
"tokio",
]
[[package]]
name = "examples-wallets"
version = "1.0.2"
dependencies = [
"ethers",
"eyre",
"serde",
"serde_json",
"tokio",
]
[[package]] [[package]]
name = "eyre" name = "eyre"
version = "0.6.8" version = "0.6.8"
@ -2683,27 +2577,6 @@ dependencies = [
"libc", "libc",
] ]
[[package]]
name = "num_enum"
version = "0.5.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf5395665662ef45796a4ff5486c5d41d29e0c09640af4c5f17fd94ee2c119c9"
dependencies = [
"num_enum_derive",
]
[[package]]
name = "num_enum_derive"
version = "0.5.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3b0498641e53dd6ac1a4f22547548caa6864cc4933784319cd1775271c5a46ce"
dependencies = [
"proc-macro-crate",
"proc-macro2",
"quote",
"syn",
]
[[package]] [[package]]
name = "num_threads" name = "num_threads"
version = "0.1.3" version = "0.1.3"
@ -3877,9 +3750,9 @@ dependencies = [
[[package]] [[package]]
name = "serial_test" name = "serial_test"
version = "0.10.0" version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1c789ec87f4687d022a2405cf46e0cd6284889f1839de292cadeb6c6019506f2" checksum = "92761393ee4dc3ff8f4af487bd58f4307c9329bbedea02cac0089ad9c411e153"
dependencies = [ dependencies = [
"dashmap", "dashmap",
"futures", "futures",
@ -3891,10 +3764,11 @@ dependencies = [
[[package]] [[package]]
name = "serial_test_derive" name = "serial_test_derive"
version = "0.10.0" version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b64f9e531ce97c88b4778aad0ceee079216071cffec6ac9b904277f8f92e7fe3" checksum = "4b6f5d1c3087fb119617cff2966fe3808a80e5eb59a8c1601d5994d66f4346a5"
dependencies = [ dependencies = [
"proc-macro-error",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn",

View File

@ -0,0 +1,225 @@
//! Bindings for solc's `ast` output field
use crate::artifacts::{serde_helpers, SourceUnit};
use serde::{de::DeserializeOwned, Deserialize, Serialize};
use std::{collections::BTreeMap, fmt, fmt::Write, str::FromStr};
/// Represents the AST field in the solc output
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct Ast {
#[serde(rename = "absolutePath")]
pub absolute_path: String,
pub id: usize,
#[serde(default, rename = "exportedSymbols")]
pub exported_symbols: BTreeMap<String, Vec<usize>>,
#[serde(rename = "nodeType")]
pub node_type: NodeType,
#[serde(with = "serde_helpers::display_from_str")]
pub src: SourceLocation,
#[serde(default)]
pub nodes: Vec<Node>,
/// Node attributes that were not deserialized.
#[serde(flatten)]
pub other: BTreeMap<String, serde_json::Value>,
}
impl Ast {
// TODO: find a better name
pub fn to_typed(self) -> SourceUnit {
let data = serde_json::to_string(&self).unwrap();
serde_json::from_str(&data).unwrap()
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct Node {
/// The node ID.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub id: Option<usize>,
/// The node type.
#[serde(rename = "nodeType")]
pub node_type: NodeType,
/// The location of the node in the source file.
#[serde(with = "serde_helpers::display_from_str")]
pub src: SourceLocation,
/// Child nodes for some node types.
#[serde(default)]
pub nodes: Vec<Node>,
/// Body node for some node types.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub body: Option<Box<Node>>,
/// Node attributes that were not deserialized.
#[serde(flatten)]
pub other: BTreeMap<String, serde_json::Value>,
}
impl Node {
/// Deserialize a serialized node attribute.
pub fn attribute<D: DeserializeOwned>(&self, key: impl AsRef<str>) -> Option<D> {
// TODO: Can we avoid this clone?
self.other.get(key.as_ref()).and_then(|v| serde_json::from_value(v.clone()).ok())
}
}
/// Represents the source location of a node: `<start byte>:<length>:<source index>`.
///
/// The `length` and `index` can be -1 which is represented as `None`
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct SourceLocation {
pub start: usize,
pub length: Option<usize>,
pub index: Option<usize>,
}
impl FromStr for SourceLocation {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let invalid_location = move || format!("{s} invalid source location");
let mut split = s.split(':');
let start = split
.next()
.ok_or_else(invalid_location)?
.parse::<usize>()
.map_err(|_| invalid_location())?;
let length = split
.next()
.ok_or_else(invalid_location)?
.parse::<isize>()
.map_err(|_| invalid_location())?;
let index = split
.next()
.ok_or_else(invalid_location)?
.parse::<isize>()
.map_err(|_| invalid_location())?;
let length = if length < 0 { None } else { Some(length as usize) };
let index = if index < 0 { None } else { Some(index as usize) };
Ok(Self { start, length, index })
}
}
impl fmt::Display for SourceLocation {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.start.fmt(f)?;
f.write_char(':')?;
if let Some(length) = self.length {
length.fmt(f)?;
} else {
f.write_str("-1")?;
}
f.write_char(':')?;
if let Some(index) = self.index {
index.fmt(f)?;
} else {
f.write_str("-1")?;
}
Ok(())
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum NodeType {
// Expressions
Assignment,
BinaryOperation,
Conditional,
ElementaryTypeNameExpression,
FunctionCall,
FunctionCallOptions,
Identifier,
IndexAccess,
IndexRangeAccess,
Literal,
MemberAccess,
NewExpression,
TupleExpression,
UnaryOperation,
// Statements
Block,
Break,
Continue,
DoWhileStatement,
EmitStatement,
ExpressionStatement,
ForStatement,
IfStatement,
InlineAssembly,
PlaceholderStatement,
Return,
RevertStatement,
TryStatement,
UncheckedBlock,
VariableDeclarationStatement,
VariableDeclaration,
WhileStatement,
// Yul statements
YulAssignment,
YulBlock,
YulBreak,
YulContinue,
YulExpressionStatement,
YulLeave,
YulForLoop,
YulFunctionDefinition,
YulIf,
YulSwitch,
YulVariableDeclaration,
// Yul expressions
YulFunctionCall,
YulIdentifier,
YulLiteral,
// Yul literals
YulLiteralValue,
YulHexValue,
// Definitions
ContractDefinition,
FunctionDefinition,
EventDefinition,
ErrorDefinition,
ModifierDefinition,
StructDefinition,
EnumDefinition,
UserDefinedValueTypeDefinition,
// Directives
PragmaDirective,
ImportDirective,
UsingForDirective,
// Misc
SourceUnit,
InheritanceSpecifier,
ElementaryTypeName,
FunctionTypeName,
ParameterList,
TryCatchClause,
ModifierInvocation,
/// An unknown AST node type.
Other(String),
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn can_parse_ast() {
let ast = include_str!("../../../test-data/ast/ast-erc4626.json");
let _ast: Ast = serde_json::from_str(ast).unwrap();
}
}