ply.lex 是否只解析一次相同的标记?

0 投票
1 回答
733 浏览
提问于 2025-04-18 13:09

我在阅读一份关于词法解析的文档,目的是为了能解析一些参数。我完全按照文档的指导创建了一个解析器。这是我的完整代码:

#!/usr/bin/env python
#-*- coding: utf-8 -*-

import ply.lex as lex

args = ['[watashi]', '[anata]>500', '[kare]>400&&[kare]<800']

tokens = ('NUMBER', 'EXPRESSION', 'AND', 'LESS', 'MORE')

t_EXPRESSION = r'\[.*\]'
t_AND = r'&&'
t_LESS = r'<'
t_MORE = r'>'
t_ignore = '\t'

def t_NUMBER(t):
    r'\d+'
    t.value = int(t.value)
    return t

def t_newline(t):
    r'\n+'
    t.lexer.lineno += len(t.value)

def t_error(t):
    print 'Illegal character "%s"' % t.value[0]
    t.lexer.skip(1)

lexer = lex.lex()

for i in args:
    lexer.input(i)
    while True:
        tok = lexer.token()
        if not tok: break
        print tok
    print '#############'

我简单地创建了一个样本参数列表,得到了这个输出:

LexToken(EXPRESSION,'[watashi]',1,0)
#############
LexToken(EXPRESSION,'[anata]',1,0)
LexToken(MORE,'>',1,7)
LexToken(NUMBER,500,1,8)
#############
LexToken(EXPRESSION,'[kare]>400&&[kare]',1,0)
LexToken(LESS,'<',1,18)
LexToken(NUMBER,800,1,19)
#############

前两个样本参数解析得很正确,但第三个却不对。第三个样本参数是 EXPRESSION+LESS+NUMBER,而它应该是 EXPRESSION+MORE+NUMBER+AND+EXPRESSION+LESS+NUMBER。所以我想可能有以下几个问题:

  • ply.lex 只解析了一个标记:在上面的代码中,ply.lex 不能解析两个独立的表达式,它只返回最新的标记作为类型。"[kare]>400&&[kare]" 被解析为 EXPRESSION,因为它以最新的 EXPRESSION 标记结束,而这个标记是第二个 [kare],而 800 被解析为 NUMBER,因为它是最新的 NUMBER 标记。

    !!! 或者 !!!

  • t_EXPRESSION 变量有错误:我把这个变量定义为 "[.*]",目的是为了获取这两个括号([])中的所有字符。第三个样本参数的第一个标记是 "[kare]>400&&[kare]",因为它简单地以这些括号开始和结束,并且里面包含 .*(每一个字符),但我以为解释器会在第一个(])字符处停止。

所以我找不到解决办法,只好在这里询问。

总的来说,这就是我正在挣扎的内容

lexer.input("[kare]>400&&[kare]<800")
while True:
    tok = lexer.token()
    if not tok: break
    print tok

我得到的结果是

LexToken(EXPRESSION,'[kare]>400&&[kare]',1,0)
LexToken(LESS,'<',1,18)
LexToken(NUMBER,800,1,19)

但我期待的结果更像是

LexToken(EXPRESSION,'[kare]',1.0)
LexToken(LESS,'>',?)
LexToken(NUMBER,400,?)
LexToken(AND,'&&',?)
LexToken(EXPRESSION,'[kare]',1,0)
LexToken(LESS,'<',1,18)
LexToken(NUMBER,800,1,19)

1 个回答

2

我想我明白你的问题了。

t_EXPRESSION = r'\[.*\]'

这个匹配方式是贪婪的,它会尽量匹配到最大的内容,比如说 '[kare]>400&&[kare]'

你可以试试下面这个:

t_EXPRESSION = r'\[[^\]]*\]'

这个方法只会匹配一组,因为它寻找的是没有打开的括号的内容([^\]),而不是任何东西(.)。

你也可以使用不贪婪的匹配方式。

t_EXPRESSION = r'\[.*?\]'

这里的 ? 会让它尽量匹配最少的字符,而不是最多的。

撰写回答