使用Python美化Lisp代码

4
有没有一种在Python中漂亮地打印Lisp风格的代码字符串的方式(换句话说,一堆平衡的括号和其中的文本),而无需重新发明轮子?

这段代码字符串是常量还是计算得出的?请告诉我们更多相关信息... - Basile Starynkevitch
这是一个解析树,因此它是被计算的。 - Max Malysh
1
你尝试过使用 pprint 模块吗?文档中的示例似乎表明它可能正好符合你的要求。(我假设你已经将其作为S表达式而不是树,正如你的评论所说。) - saulspatz
3个回答

2

简短回答

我认为,如果可以的话,生成Python列表或自定义对象而不是字符串,并使用@saulspatz建议的pprint模块是一个合理的方法。

详细回答

整个问题看起来像是一个XY问题的示例。为什么?因为你正在使用Python(为什么不用Lisp?)来操作表示生成的Lisp风格代码的字符串(为什么不用数据结构?),其中Lisp风格被定义为“一堆括号和文本”。 对于“如何漂亮地打印?”,我的回答是“我不会从这里开始!”。 在你的情况下,除了使用现有的工具之外,避免重复发明轮子的最佳方法是坚持使用简单的输出格式。

但首先,为什么需要漂亮地打印?谁会查看结果代码?

根据你使用的确切Lisp方言和代码的预期用途,你可以非常不同地格式化代码。例如,考虑换行符、缩进和文本的最大宽度。特别是Common Lisp pretty-printer已经非常复杂,我怀疑你不想拥有相同级别的可配置性。 如果你使用Lisp,简单调用pprint就可以解决你的问题,但你正在使用Python,所以暂时坚持最合理的输出,因为漂亮打印是一个麻烦的问题。

如果你的代码是给人类读者看的,请:

  • 不要将右括号放在自己的行上
  • 不要垂直对齐开放和关闭括号
  • 不要在开放括号之间添加空格

这很丑陋:

 ( * ( + 3 x )
     (f 
        x
        y
     )
 )

这更好:

(* (+ 3 x)
   (f x y))

或者简单点说:
(* (+ 3 x) (f x y))

点击此处获取更多详细信息。

但在打印之前,您需要解析输入字符串并确保其格式正确。也许由于您生成表单的方式,您确定它是格式良好的,但我认为打印机应该忽略这一点并且不要做太多假设。如果您将 Python 对象表示的 AST 传递给漂亮的打印机而不仅仅是字符串,那么这将更容易,如评论中所建议的。您可以构建数据结构或自定义类,并使用 pprint (python) 模块。如果您可以更改生成 Lisp 风格代码的方式,则如上所述,这似乎是您的选择。

对于字符串,您应该处理任何可能的输入并拒绝无效的输入。这意味着检查括号和引号是否平衡(注意转义字符等)。实际上,您不需要为打印真正构建一个中间树(尽管它可能有助于程序的其他部分),因为 Lisp 风格代码由易于嵌套并使用前缀符号的形式组成:您可以从左到右扫描输入字符串,并在看到括号时按需要打印(左括号:递归;右括号:从递归返回)。当您首次遇到未转义的双引号 " 时,读取直到下一个 ",... 这个方法与简单的打印方法结合使用,可能已足够满足您的需求。


1
OP说:“这是一棵解析树,因此它是可计算的”,这可能意味着它是来自ANTLR4的树形转储,字面上是许多括号、规则名称和标记值,全部在一行中。所以他们可能不是Lisp用户,只是被一个类似S-表达式的字符串卡住了,需要帮助使其变得更加易读。这正是我来到这里的原因 :-) - Ross Patterson

0

我认为最简单的方法是使用三引号。如果你这样说:

print """
    (((This is some lisp code))) """

应该可以工作。 您可以在三引号内以任何方式格式化代码,它将以您想要的方式呈现。

祝您好运,编码愉快!


1
谢谢您的回复,但问题是关于运行时生成的字符串。 - Max Malysh
@MaxMalysh 当我回答时,你评论区的讨论还没有发生。我以为你只是在打印格式化的Lisp代码。 - Joseph Farah

0

我曾经为美化基于Lisp的CLIPS编写了一个简单的漂亮打印机。或许可以帮到你:

def clips_pprint(clips_str: str) -> str:
    """Pretty-prints a CLIPS string.

    Indents a CLIPS string for easier visual confirmation during development
    and verification.
    Assumes the CLIPS string is valid CLIPS, i.e. braces are paired.
    """
    LB = "("
    RB = ")"
    TAB = " " * 4
    formatted_clips_str = ""
    tab_count = 0
    for c in clips_str:
        if c == LB:
            formatted_clips_str += os.linesep
            for _i in range(tab_count):
                formatted_clips_str += TAB

            tab_count += 1
        elif c == RB:
            tab_count -= 1
        formatted_clips_str += c

    return formatted_clips_str.strip()

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接