我用Alex和Happy构建了一个解析器和词法分析器,它生成和抽象我正在解析的语言(Solidity)的语法树。我现在的问题是如何正确地遍历和匹配语言的某些方面。目的是为了 创建一个规则引擎,它将对生成的AST执行代码分析,检查特定问题,如不正确使用函数,危险调用或缺少某些元素。
这是我的数据的布局,其中输出为AST。 (这不是完整的AST,只是一个快照)
data SourceUnit = SourceUnit PragmaDirective
| ImportUnit ImportDirective
| ContractDef ContractDefinition
deriving (Show, Eq, Data, Typeable, Ord)
-- Version Information
data PragmaDirective = PragmaDirective PragmaName Version Int
deriving(Show, Eq, Data, Typeable, Ord)
data Version = Version String
deriving (Show, Eq, Data, Typeable, Ord)
data PragmaName = PragmaName Ident
deriving(Show, Eq, Typeable, Data, Ord)
data PragmaValue = PragmaValue Dnum
deriving(Show, Eq, Data, Typeable, Ord)
-- File imports/Contract Imports
data ImportDirective = ImportDir String
| ImportMulti Identifier Identifier Identifier String
deriving (Show, Eq, Data, Typeable, Ord)
-- The definition of an actual Contract Code Block
data ContractDefinition = Contract Identifier [InheritanceSpec] [ContractConts]
deriving (Show, Eq, Data, Typeable, Ord)
data ContractConts = StateVarDec StateVarDeclaration
| FunctionDefinition FunctionDef
| UsingFor UsingForDec
deriving (Show, Eq, Data, Typeable, Ord)
我目前的思路是通过将[SourceUnit]
传递给函数并匹配特定情况来使用模式匹配。例如,以下函数匹配代码并返回状态变量声明的数据类型。
getStateVar :: [SourceUnit] -> Maybe StateVarDeclaration
getStateVar [SourceUnit _ , ContractDef (Contract _ _ [StateVarDec x]) ] = Just x
getStateVar _ = Nothing
这会输出以下内容,这部分是我需要的。不幸的是,语言可能包含多个合同声明,并且有多个状态变量声明,因此我认为完全不可能以这种方式匹配它。
Main> getStateVar $ runTest "pragma solidity ^0.5.0; contract test { address owner = msg.send;}"
Just (StateVariableDeclaration (ElementaryTypeName (AddrType "address")) [] (Identifier "owner") [MemberAccess (IdentExpression "msg") "." (Identifier "send")])
我已经阅读了一些有关泛型编程和“废弃样板”的内容,但我并不完全了解它是如何工作的,或者是实现它的最佳方法。
问题是,我是否在这种模式匹配方面走上正轨,还是有更好的选择?