如何使用HughesPJ的漂亮打印库正确缩进块?

问题描述

我有以下编程语言语法:

data Expr = ...

data Stmt = SExpr Expr | SBlock Block | SLet Fundef | ...

data Block = Block [Stmt]

data Fundef = Fundef String [String] Block

data TopDef = TopFun Fundef

使用以下示例语法:

function long_function_name () = {
  let g() = {
    {
       h()
    };
    3
  } 
}

我正在尝试使用HughesPJ pretty库为该语言创建漂亮的打印机。到目前为止,我的尝试如下:

instance Pretty Stmt where
  pprint = \case
    SExpr e -> pprint e
    SBlock b -> pprint b
    SLet f -> text "let" <+> pprint f

instance Pretty Block where
  pprint (Block stmts) = lbrace $+$
    nest 2 (vcat (punctuate semi (map pprint stmts))) $+$ 
    rbrace

instance Pretty Fundef where
  pprint (Fundef name args body) = pprint name  <> parens (...) <+> text "=" <+> pprint body

instance Prettty TopDef where
  pprint (TopFun f) = text "function" <+> pprint f

问题是,我想在函数声明的同一行中包含{,但它总是使后面几行相对于括号列缩进,而不是绝对的。应该在上面的示例的漂亮字体中可见;

function long_function_name () = {
                                   let g() = {
                                               {
                                                 h()
                                               };
                                               3
                                             } 
}

为什么会发生这种情况,我该如何解决这个问题?我想尽可能避免重复代码

解决方法

您在正文之前写了<+>,因此$+$的垂直串联完全在function行的水平串联中,因此全部缩进了。我相信,pretty的用途是在块上进行显式匹配,因为它是垂直布局的一部分,即:

pPrint (Fundef name args (Block stmts)) = vcat
  [ pPrint name <> parens (...) <+> text "=" <+> lbrace,nest 2 (vcat (punctuate semi (map pPrint stmts))),rbrace
  ]

prettyprinter这样的更现代的漂亮打印库使此操作变得容易一些:nest(或indenthang)处理行缩进 跟随第一行以垂直布局排列,因此您可以将nest放在左括号和主体之间,并将右括号放在嵌套外部,如下所示:

"prefix" <+> vcat
  [ nest 4 $ vcat
    [ "{","body"
    ],"}"
  ]

prefix {
  body
}

(注意。您可以像这样使用OverloadedStrings而不是将文字包装在text中。)

但这不适用于prettyprettyprinter似乎是为了使一切杂乱无章。

我还建议您使用group的其他优点,例如,一个{{1}}函数可让您表达“如果合适,可将其放在一行上”,这对于使格式化稳固非常有用。并响应不同的渲染上下文。