我正在尝试编写一个非常简单的解析器。我在SO和互联网上阅读了类似的问题,但我能找到的只限于"算术类似于"的东西。
我有一个非常简单的DSL,例如:
ELEMENT TYPE<TYPE> elemName {
TYPE<TYPE> memberName;
}
<TYPE>
部分是可选的,仅对某些类型有效。
根据我的阅读,我尝试在Python中编写一个递归下降解析器,但有些事情我似乎无法理解:
<
或一个空格后面跟<
。我该如何解决这个问题?答案 0 :(得分:3)
所有问题归结为您在解析字符串之前没有对字符串进行标记。
解析过程实际上分为两个不同的部分: lexing 和解析。
您对解析的看法似乎缺少的是令牌化或lexing。它是将字符串转换为标记流(即单词)的过程。在询问如何分解不同部分的文本时,您正在寻找的是什么?
您可以通过使用re
检查字符串与正则表达式列表来自行完成,或者您可以使用一些众所周知的库,例如PLY。虽然如果你使用Python3,我会偏向于我写的一个lexing-parsing librairy,这是ComPyl。
所以继续使用ComPyl,您正在寻找的语法似乎如下。
from compyl.lexer import Lexer
rules = [
(r'\s+', None),
(r'\w+', 'ID'),
(r'< *\w+ *>', 'TYPE'), # Will match your <TYPE> token with inner whitespaces
(r'{', 'L_BRACKET'),
(r'}', 'R_BRACKET'),
]
lexer = Lexer(rules=rules, line_rule='\n')
# See ComPyl doc to figure how to proceed from here
请注意,第一条规则(r'\s+', None)
实际上解决了有关空白的问题。它基本上告诉词法分析器匹配任何空白字符并忽略它们。当然,如果您不想使用lexing工具,只需在自己的re
实现中添加类似的规则。
您似乎想编写自己的 LL(1)解析器,因此我将简要介绍该部分。只知道有很多工具可以为你做(PLY和ComPyl librairies提供LR(1)解析器,它更强大但更难手写,看看LL(1)和LR(1)之间的区别here)。
只需注意,既然您知道如何对字符串进行标记,那么如何查找超过1个字符的标记?的问题已经解决。您现在正在解析,而不是字符流,而是一个封装匹配的字的标记流。
答案 1 :(得分:2)
Olivier关于lexing / tokenizing然后解析的答案是有帮助的。
但是,对于相对简单的情况,一些解析工具能够处理您的需求,而无需单独的标记步骤。 parsy就是其中之一。您可以从较小的构建块构建解析器 - 有很好的文档可以提供帮助。
使用parsy完成语法类型的解析器示例如下:http://parsy.readthedocs.io/en/latest/howto/other_examples.html#proto-file-parser。
它比你的复杂得多,但展示了可能的东西。在允许空格(但不是必需的)的情况下,它使用lexeme
实用程序(在顶部定义)来使用可选的空格。
您可能需要加强对空白是否必要的位置以及可选位置的理解,以及您真正意味着什么样的空白。