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")])
私はジェネリックプログラミングと「ボイラープレートを破棄する」についていくらか読んだことがありますが、それがどのように機能するか、またはそれを実装するための最良の方法が何であるかを正確には理解していません。
問題は、この方法でパターン マッチングに関して正しい軌道に乗っているか、それともより良い代替手段があるかということです。