26个版本 (6个重大更新)

0.8.0 2024年8月21日
0.7.7 2024年7月8日
0.7.6 2024年6月10日
0.7.0 2024年3月30日
0.3.1 2023年7月30日

#1 in 魔法豆

Download history 38325/week @ 2024-05-01 34769/week @ 2024-05-08 40713/week @ 2024-05-15 45843/week @ 2024-05-22 44287/week @ 2024-05-29 40343/week @ 2024-06-05 45009/week @ 2024-06-12 47935/week @ 2024-06-19 44801/week @ 2024-06-26 47337/week @ 2024-07-03 48012/week @ 2024-07-10 59055/week @ 2024-07-17 57608/week @ 2024-07-24 53347/week @ 2024-07-31 72914/week @ 2024-08-07 55388/week @ 2024-08-14

每月下载量 250,638
115 个crate中使用 (4个直接使用)

MIT/Apache

315KB
9K SLoC

syn-solidity

syn 驱动的用于类似 TokenStream 的 Solidity 解析器。

解析的根元素是 File,其中包含一个 Item 列表。 Item 也支持外部属性,如下所示。

设计

此解析器专门为Rust过程宏设计。它的目的是在解析有效的Solidity代码时模仿官方Solidity编译器(Solc)的行为。这意味着所有由Solc v0.5.*[^1]及更高版本识别的有效Solidity代码也将被 syn-solidity 正确识别和解析。

然而,与官方Solidity编译器和语法规范相比,syn-solidity 更加宽容和宽松。以下是一些在 syn-solidity 中有效但不在官方编译器中有效的代码模式示例

  • 标识符是Rust标识符(syn::Ident),因此不能包含美元符号($),但可以包含Unicode字符
  • 尾随标点符号,如函数参数或枚举定义中的逗号(,
  • 在特定上下文中的一些变量和函数属性,例如 internal 函数或接口中的实现函数({ ... }
  • 项目定义中的参数存储位置,例如在结构体或错误定义中的 uint256[] memory
  • 元组类型 (T, U, ..) 在需要类型的地方是允许的,并且可以可选地用 tuple 关键字开头。这与ethers.js 的人性化ABI 相同

这种宽松的行为是故意设计的,以方便在过程宏中使用,并减少解析器和AST中的代码复杂性。

[^1]: 更早的版本可能仍然可以成功解析,但这不能保证。

已知限制

此解析器仅限于有效的Rust标记,这意味着某些Solidity构造不受支持。以下是一些示例,但不仅限于

  • 标识符内的美元符号($
  • 单引号字符串
  • hexunicode 字符串字面量前缀。字面量前缀在Rust 2021版及更高版本中是保留的
  • "\uXXXX" Unicode转义。Rust使用 "\u{XXXX}" 表示Unicode代码点
  • 无效嵌套的块注释。例如,/*/*/ 无法解析。

基本上,您可以复制粘贴Solidity代码,并期望它在大多数情况下可以正确解析。您可以在测试目录中看到一些正确解析的Solidity代码示例(经过一些非常轻微的修补)。

示例

基本用法

use quote::quote;
use syn_solidity::{Expr, File, Item, Lit, Stmt};

// Create a Solidity `TokenStream`
let tokens = quote! {
    /// @name HelloWorld
    /// @notice A hello world example in Solidity.
    contract HelloWorld {
        /// @notice Returns the string "Hello, World!".
        function helloWorld() external pure returns (string memory) {
            return "Hello, World!";
        }
    }
};

// Parse the tokens into a `File`
let ast: File = syn_solidity::parse2(tokens)?;

let items: &[Item] = &ast.items;
let Some(Item::Contract(contract)) = items.first() else {
    unreachable!()
};
assert_eq!(contract.name, "HelloWorld");
assert_eq!(contract.attrs.len(), 2); // doc comments

let body: &[Item] = &contract.body;
let Some(Item::Function(function)) = body.first() else {
    unreachable!()
};
assert_eq!(function.attrs.len(), 1); // doc comment
assert_eq!(function.name.as_ref().unwrap(), "helloWorld");
assert!(function.parameters.is_empty()); // ()
assert_eq!(function.attributes.len(), 2); // external pure
assert!(function.returns.is_some());

let Some([Stmt::Return(ret)]) = function.body() else {
    unreachable!()
};
let Some(Expr::Lit(Lit::Str(s))) = &ret.expr else {
    unreachable!()
};
assert_eq!(s.value(), "Hello, World!");
# syn::Result::Ok(())

依赖项

~230–670KB
~16K SLoC