#solidity #syn #ast #ethereum #evm

linera-alloy-syn-solidity

由syn驱动的Solidity解析器

1个不稳定版本

0.7.4 2024年5月30日

#19#solidity

Download history 83/week @ 2024-05-24 136/week @ 2024-05-31 94/week @ 2024-06-07 101/week @ 2024-06-14 33/week @ 2024-06-21 65/week @ 2024-06-28 36/week @ 2024-07-05 97/week @ 2024-07-12 63/week @ 2024-07-19 125/week @ 2024-07-26 68/week @ 2024-08-02 149/week @ 2024-08-09 139/week @ 2024-08-16

487 每月下载量
用于 21 个crate(直接使用2个)

MIT/Apache

315KB
9K SLoC

linera-alloy-syn-solidity

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

解析的根元素是 File,它包含一系列 ItemItem 还支持外部属性,如下所示。

⚠️ 进行中 ⚠️

设计

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

然而,与官方Solidity编译器和语法规范相比,linera-alloy-syn-solidity 更为宽容和宽松。以下是一些在 linera-alloy-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 linera_alloy_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 = linera_alloy_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(())

依赖项

~240–680KB
~16K SLoC