python rply 反向解析器
python rply reverse parser
我正在使用 rply
和 Python3.6 为一个小的私有项目创建词法分析器和解析器。
但我注意到解析器似乎翻转了 lexerstream 的顺序。
这是我正在解析的文件:
let test:string = "test";
print(test);
词法分析器输出:
Token('LET', 'let')
Token('NAME', 'test')
Token('COLON', ':')
Token('NAME', 'string')
Token('EQUALS', '=')
Token('STRING', '"test"')
Token('SEMI_COLON', ';')
Token('PRINT', 'print')
Token('OPEN_PARENS', '(')
Token('STRING', '"test"')
Token('CLOSE_PARENS', ')')
Token('SEMI_COLON', ';')
如您所见,它是按照脚本的顺序排列的。
我使用解析器创建了一个变量,名称为 test
,类型为 string
,值为 test
。然后我想打印变量。
它确实创建了变量,但是当我想打印出来时,什么也没有。
但是当我这样翻剧本的时候
print(test);
let test:string = "test";
它能够正确打印值。
两个解析器 'rules' 看起来像这样:
打印:
@self.pg.production('expression : PRINT OPEN_PARENS expression CLOSE_PARENS SEMI_COLON expression')
def print_s(p):
...
创建变量:
@self.pg.production('expression : LET expression COLON expression EQUALS expression SEMI_COLON expression')
def create_var(p):
...
所以我的问题是:如何翻转内容解析的顺序?
编辑:我在文档中查找过类似的问题或问题,但没有找到任何内容。
这里有一个更简单的例子;希望您能看到规律。
关键的见解是,当产生式的匹配项已被完全解析时,会执行缩减操作(即解析器函数)。这意味着如果产生式包含非终结符,那么针对这些非终结符的操作将在针对整个产生式的操作之前执行。
应该很清楚为什么会这样。每个生产动作都取决于所有组件的语义值,在非终端的情况下,这些值由 运行 相应的动作产生。
现在,考虑这两种非常相似的方法来解析 list
of thing
。在这两种情况下,我们假设有一个基础生产识别空 list
(list :
) 并且什么都不做。
右递归:
list : thing list
左递归:
list : list thing
在这两种情况下,操作都会打印 thing
,在右递归情况下为 p[0]
,在左递归情况下为 p[1]
。
右递归生产将导致 thing
以相反的顺序打印,因为打印 thing
直到内部 list
被解析后才会发生(并且打印了它的组件)。
但是左递归产生式将以从左到右的顺序打印 thing
s,原因相同。不同之处在于左递归情况下的 tgat,内部(递归)list
包含初始的 thing
,而在右递归情况下,list
包含最终的 [=13] =]s.
如果您只是构建 thing
的 Python 列表,这可能无关紧要,因为执行顺序并不重要。它仅在此示例中可见,因为该操作具有 副作用(打印值),这使得执行顺序可见。
在极少数确实有必要的情况下,还有其他技术可以命令操作。但最佳实践是在语法上可行时始终使用左递归。左递归解析器效率更高,因为解析器不需要累积一堆不完整的产生式。左递归通常也更适合您的操作。
这里,例如,左递归操作可以附加新值(p[0].append(p[1]); return p[0]
),而右递归操作需要创建一个新列表(return [p[0] + p[1]
)。由于重复附加是平均线性时间,而重复连接是二次的,因此左递归解析器对于大型列表更具可扩展性。
我正在使用 rply
和 Python3.6 为一个小的私有项目创建词法分析器和解析器。
但我注意到解析器似乎翻转了 lexerstream 的顺序。
这是我正在解析的文件:
let test:string = "test";
print(test);
词法分析器输出:
Token('LET', 'let')
Token('NAME', 'test')
Token('COLON', ':')
Token('NAME', 'string')
Token('EQUALS', '=')
Token('STRING', '"test"')
Token('SEMI_COLON', ';')
Token('PRINT', 'print')
Token('OPEN_PARENS', '(')
Token('STRING', '"test"')
Token('CLOSE_PARENS', ')')
Token('SEMI_COLON', ';')
如您所见,它是按照脚本的顺序排列的。
我使用解析器创建了一个变量,名称为 test
,类型为 string
,值为 test
。然后我想打印变量。
它确实创建了变量,但是当我想打印出来时,什么也没有。
但是当我这样翻剧本的时候
print(test);
let test:string = "test";
它能够正确打印值。
两个解析器 'rules' 看起来像这样: 打印:
@self.pg.production('expression : PRINT OPEN_PARENS expression CLOSE_PARENS SEMI_COLON expression')
def print_s(p):
...
创建变量:
@self.pg.production('expression : LET expression COLON expression EQUALS expression SEMI_COLON expression')
def create_var(p):
...
所以我的问题是:如何翻转内容解析的顺序?
编辑:我在文档中查找过类似的问题或问题,但没有找到任何内容。
这里有一个更简单的例子;希望您能看到规律。
关键的见解是,当产生式的匹配项已被完全解析时,会执行缩减操作(即解析器函数)。这意味着如果产生式包含非终结符,那么针对这些非终结符的操作将在针对整个产生式的操作之前执行。
应该很清楚为什么会这样。每个生产动作都取决于所有组件的语义值,在非终端的情况下,这些值由 运行 相应的动作产生。
现在,考虑这两种非常相似的方法来解析 list
of thing
。在这两种情况下,我们假设有一个基础生产识别空 list
(list :
) 并且什么都不做。
右递归:
list : thing list
左递归:
list : list thing
在这两种情况下,操作都会打印 thing
,在右递归情况下为 p[0]
,在左递归情况下为 p[1]
。
右递归生产将导致 thing
以相反的顺序打印,因为打印 thing
直到内部 list
被解析后才会发生(并且打印了它的组件)。
但是左递归产生式将以从左到右的顺序打印 thing
s,原因相同。不同之处在于左递归情况下的 tgat,内部(递归)list
包含初始的 thing
,而在右递归情况下,list
包含最终的 [=13] =]s.
如果您只是构建 thing
的 Python 列表,这可能无关紧要,因为执行顺序并不重要。它仅在此示例中可见,因为该操作具有 副作用(打印值),这使得执行顺序可见。
在极少数确实有必要的情况下,还有其他技术可以命令操作。但最佳实践是在语法上可行时始终使用左递归。左递归解析器效率更高,因为解析器不需要累积一堆不完整的产生式。左递归通常也更适合您的操作。
这里,例如,左递归操作可以附加新值(p[0].append(p[1]); return p[0]
),而右递归操作需要创建一个新列表(return [p[0] + p[1]
)。由于重复附加是平均线性时间,而重复连接是二次的,因此左递归解析器对于大型列表更具可扩展性。