Python Parsley:解析简单源代码

0 投票
1 回答
780 浏览
提问于 2025-04-18 04:21

我有一些旧代码,现在还在用。看了Python的parsley和提供的教程,我发现没有好的例子来解析源代码。举个我代码的例子:

{ comment

still part of the comment above

oh ya more commenting

}

ACTION_1 [file_name.ex1]
ACTION_2 [file_name.ex2] ; some comment
{ some comment} ACTION_3 [file_name.ex3]
ACTION_4 {wow another comment } [file_name.ex4]
;ACTION_5 [file_name.ex5] <-- commented out line
ACTION_6 [file_name.ex6]

于是我开始创建语法,

x = parsley.grammar = r"""
text = (anything:x ?(x not in '{}') -> x)+:d -> tex.text("".join(d))
comment = ';' (anything:x ?(x not in '\n'))+ '\n' -> ''
file_name = (anything:x ?(x in '{}') -> x)+:d -> text.text("".join(d))
"""

我想用parsley来解析这个内容,格式是 dict ={'ACTION_x': 'file_name.exx', ... }。我该如何创建合适的语法来解析这个文件呢?

1 个回答

3

在创建语法时,如果想要得到某种抽象语法树(AST),我通常会经历三个步骤。第一步是找出主要的非终结符产生式,并构建它们。刚开始不要担心简化,只需先把基本的产生式写出来,并确保它们能匹配你的源文件。如果你的语言已经有现成的语法规范,就直接用它;这通常比你自己对语言结构的理解要准确得多。

这点值得强调:如果你的语言已经有现成的语法规范,就用它。我之前成功地把PEG和CFG(BNF)语言描述转化为Parsley语法。

multilineComment = '{' (~'}' anything)* '}'

这段代码应该能匹配你的多行注释语法。注意我使用了PEG风格的(负向)前瞻断言,而不是语义断言;这样通常会更简洁,并且更好地表达你想要的解析方式。大声读出来:“多行注释是一个开括号,后面跟着零个或多个不是闭括号的内容,最后是一个闭括号。”

单行注释就比较棘手,因为你的语言似乎对空白字符很敏感。这意味着每个消耗换行符的规则都必须一致,明确何时何地会消耗换行符。

lineComment = ';' (~'\n' anything)* '\n'

有趣的是,我实际上写了一个until规则来处理这种“做这个直到那个”的规则,但结果发现这样会让事情变得更复杂!活到老学到老。

第二步是编写测试。从你在IRC上的评论来看,我猜你知道如何为Parsley代码编写测试,所以我在这里就不详细讲了;简单来说,就是写一堆小代码段,然后通过Parsley运行它们,检查它们是成功还是失败。等你回来后,会把成功的测试改成检查代码片段是否解析成有效的树,这就是第三步。

第三步是给你的规则添加简化注释(绑定、简化)。这会把你的语法从仅仅识别目标语言变成一个解析器。

还记得之前的文件名规则吗?

fileName = '[' (~']' anything)+ ']'

我们来给它添加一个绑定和简化;我们想要捕获括号中的内容,并将其作为字符串返回。

fileName = '[' (~']' anything)+::cs ']' -> "".join(cs)

Parsley还有一种从输入可迭代对象中切片的方法,这在解析字符串并想要捕获字符串时非常方便。

fileName = '[' <(~']' anything)+>:s ']' -> s

我这里用的是单字母变量,因为我习惯于Haskell,但你可以随意为绑定使用任何你喜欢的名字。

希望这对你有帮助!~ C.

撰写回答