Обход и сопоставление с образцом абстрактного синтаксического дерева
Я создал парсер и лексер с 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")])
Я немного читал об общем программировании и о том, как "отменить свой пример", но я не совсем понимаю, как это работает, и каким будет лучший способ его реализации.
Вопрос в том, нахожусь ли я на правильном пути с точки зрения соответствия шаблонов или есть лучшая альтернатива?