我正在使用 Parsec 构建一个简单的 Lisp 解析器。
对解析器类型使用自定义 ADT 与使用标准树(即Data.Tree
)相比有什么(缺点)优势?
在尝试了两种方式之后,我为自定义 ADT 提出了几点意见(即Parser ASTNode
):
- 似乎更加清晰和简单
- 其他人已经这样做了(包括Tiger,它与 Parsec 捆绑在一起)
一个反对(即Parser (Tree ASTNode)
:
Data.Tree
已经有Functor、Monad等实例,对语义分析、评估、计算代码统计会有很大帮助
例如:
自定义 ADT
import Text.ParserCombinators.Parsec data ASTNode = Application ASTNode [ASTNode] | Symbol String | Number Float deriving (Show) int :: Parser ASTNode int = many1 digit >>= (return . Number . read) symbol :: Parser ASTNode symbol = many1 (oneOf ['a'..'z']) >>= (return . Symbol) whitespace :: Parser String whitespace = many1 (oneOf " \t\n\r\f") app :: Parser ASTNode app = char '(' >> sepBy1 expr whitespace >>= (\(e:es) -> char ')' >> (return $ Application e es)) expr :: Parser ASTNode expr = symbol <|> int <|> app
示例使用:
ghci> parse expr "" "(a 12 (b 13))" Right (Application (Symbol "a") [Number 12.0, Application (Symbol "b") [Number 13.0]])
Data.Tree
import Text.ParserCombinators.Parsec import Data.Tree data ASTNode = Application (Tree ASTNode) | Symbol String | Number Float deriving (Show) int :: Parser (Tree ASTNode) int = many1 digit >>= (\x -> return $ Node (Number $ read x) []) symbol :: Parser (Tree ASTNode) symbol = many1 (oneOf ['a' .. 'z']) >>= (\x -> return $ Node (Symbol x) []) whitespace :: Parser String whitespace = many1 (oneOf " \t\n\r\f") app :: Parser (Tree ASTNode) app = char '(' >> sepBy1 expr whitespace >>= (\(e:es) -> char ')' >> (return $ Node (Application e) es)) expr :: Parser (Tree ASTNode) expr = symbol <|> int <|> app
和示例使用:
ghci> parse expr "" "(a 12 (b 13))" Right (Node (Application (Node (Symbol "a") [])) [Node (Number 12.0) [], Node (Application (Node (Symbol "b") [])) [Node (Number 13.0) []]])
(对不起格式 - 希望它很清楚)