打印 AST 以显示缩进

Printing an AST to show indentation

我想在 Haskell 中打印抽象语法树。目前我可以逐行打印树,但我更喜欢为解析代码所在的每个块缩进它。

例如

要解析的代码:

module test
foo(x,y):
    foo x y
    x + y 

这是我创建和打印 AST 的函数。

Returns树.

parse :: String -> Either ParseError [Expr]
parse = runParser (many expr <* eof) () "[input]" . indentConfig

打印树。

printTree :: String -> IO ()
printTree line = do
    let res = parse line
    case res of
        Left err -> print err
        Right ex -> mapM_ print ex

当前输出:

Function "foo" ["x","y"] (FunctionCall "foo" [Variable "x",Variable "y"])
BinOp Plus (FunctionCall "x" []) (FunctionCall "y" [])

期望输出:

Function "foo" ["x","y"] 
    (FunctionCall "foo" [Variable "x",Variable "y"])
    BinOp Plus (FunctionCall "x" []) (FunctionCall "y" [])

实现此目标的最佳方法是什么?

Hackage 上有几个软件包可以让您以 "indented" 方式显示此类树结构。以前,我使用 Iavor Diatchki 的 pretty-show 包,它确实做得很好。您可能想尝试一下:https://hackage.haskell.org/package/pretty-show

您可以创建自己的 prettyPrint 函数,该函数接受 AST 和缩进级别,然后递归打印 AST 中的节点,并根据需要增加缩进级别。

在此函数中,您需要专门处理增加缩进级别的 AST 节点。

下面是此类函数的示例。

data AST =
    Function String [String] [AST]
  | BinOp AST AST AST
  | Plus
  | FunctionCall String [AST]
  | Variable String
  deriving (Show)

prettyPrint :: Int -> AST -> String
prettyPrint n (Function a b c) = "Function " ++ show a ++ " " ++ show b ++
    foldl (++) "" (map (\x -> "\n" ++ take (n + 1) (repeat '\t') ++
    prettyPrint (n + 1) x) c)
prettyPrint n a = show a

请注意,可能有更简洁的方法来编写此函数,但此版本展示了实际的想法。

如果我们 运行 在您提供的示例 AST 上执行此操作,我们将得到以下内容。

λ ~ let a = (Function "foo" ["x","y"] [FunctionCall "foo" [Variable "x",Variable "y"], BinOp Plus (FunctionCall "x" []) (FunctionCall "y" [])])
λ ~ putStrLn (prettyPrint 0 a)
Function "foo" ["x","y"]
    FunctionCall "foo" [Variable "x",Variable "y"]
    BinOp Plus (FunctionCall "x" []) (FunctionCall "y" [])

这也适用于多级缩进。

λ ~ let b = (Function "foo" ["x","y"] [FunctionCall "foo" [Variable "x",Variable "y"], BinOp Plus (FunctionCall "x" []) (FunctionCall "y" []), Function "bar" ["x"] [BinOp Plus (FunctionCall "x" []) (FunctionCall "y" [])]])
b :: AST
λ ~ putStrLn (prettyPrint 0 b)
Function "foo" ["x","y"]
    FunctionCall "foo" [Variable "x",Variable "y"]
    BinOp Plus (FunctionCall "x" []) (FunctionCall "y" [])
    Function "bar" ["x"]
        BinOp Plus (FunctionCall "x" []) (FunctionCall "y" [])

请注意,这仅适用于您想自己实现功能的情况。从长远来看,这可能是一个更好的举措 运行 使用一个漂亮的打印库来为你处理这个问题。