解析器返回值的自定义ADT与树

时间:2012-08-09 14:41:30

标签: parsing haskell parsec

我正在使用Parsec构建一个简单的Lisp解析器。

使用自定义ADT作为解析器类型与使用标准树(即Data.Tree)有什么(dis)优势?

尝试了两种方法之后,我想出了几个 自定义ADT点(即Parser ASTNode):

  • 似乎更清晰,更简单
  • 其他人这样做了(包括Tiger,与Parsec捆绑在一起)

和一个反对(即Parser (Tree ASTNode)

  • Data.Tree已经有Functor,Monad等实例,这对语义分析,评估,计算代码统计非常有帮助

例如:

  1. 自定义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]])
    
  2. 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) []]])
    

    (抱歉格式化 - 希望很清楚)

1 个答案:

答案 0 :(得分:4)

我绝对会选择AST,因为一般来说,解释/编译/语言分析很大程度上取决于你的语言的结构。 AST将简单而自然地表示并尊重该结构,而Tree则不会。

例如,一种常见的语言实现技术形式是通过翻译实现一些复杂的功能:将涉及这些功能或构造的程序翻译成不使用它们的语言子集中的程序(例如Lisp宏) ,都是这个)。例如,如果使用AST,类型系统通常会禁止您将非法翻译作为输出。而不了解您的程序的Tree类型将无法帮助那里。

您的AST看起来并不复杂,因此为它编写实用程序功能应该不难。以此为例:

foldASTNode :: (r -> [r] -> r) -> (String -> r) -> (Float -> r) -> r
foldASTNode app sym num node = 
    case node of
      Application f args -> app (subfold f) (map subfold args)
      Symbol str         -> sym str
      Number n           -> num n
    where subfold = foldASTNode app sym num

在任何情况下,您希望在AST上使用哪种Functor?它上面没有类型参数......