Lex 似乎不遵循优先顺序

Lex doesn't seem to follow precedence order

我正在使用 ply(Lex 和 Yacc 的流行 python 实现)为自定义语言创建一个简单的编译器。

目前我的词法分析器如下所示:

reserved = {
    'begin': 'BEGIN',
    'end': 'END',
    'DECLARE': 'DECL',
    'IMPORT': 'IMP',
    'Dow': 'DOW',
    'Enddo': 'ENDW',
    'For': 'FOR',
    'FEnd': 'ENDF',
    'CASE': 'CASE',
    'WHEN': 'WHN',
    'Call': 'CALL',
    'THEN': 'THN',
    'ENDC': 'ENDC',
    'Object': 'OBJ',
    'Move': 'MOV',
    'INCLUDE': 'INC',
    'Dec': 'DEC',
    'Vibration': 'VIB',
    'Inclination': 'INCLI',
    'Temperature': 'TEMP',
    'Brightness': 'BRI',
    'Sound': 'SOU',
    'Time': 'TIM',
    'Procedure': 'PROC'
}

tokens = ["INT", "COM", "SEMI", "PARO", "PARC", "EQ", "NAME"] + list(reserved.values())

t_COM = r'//'
t_SEMI = r";"
t_PARO = r'\('
t_PARC = r'\)'
t_EQ = r'='
t_NAME = r'[a-z][a-zA-Z_&!0-9]{0,9}'

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

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

Per the documentation,我正在为保留关键字创建字典,然后将它们添加到 tokens 列表中,而不是为它们添加单独的规则。该文档还指出优先级是根据以下 2 条规则决定的:

  1. 函数定义的所有标记都按照它们在词法分析器文件中出现的顺序添加。
  2. 接下来添加由字符串定义的标记,方法是按照正则表达式长度递减的顺序对它们进行排序(首先添加较长的表达式)。

我遇到的问题是,当我使用这个测试字符串测试词法分析器时

testInput = "// ; begin end DECLARE IMPORT Dow Enddo For FEnd CASE WHEN Call THEN ENDC (asdf) = Object Move INCLUDE Dec Vibration Inclination Temperature Brightness Sound Time Procedure 985568asdfLYBasdf ; Alol"

词法分析器returns出现以下错误:

LexToken(COM,'//',1,0)
LexToken(SEMI,';',1,2)
LexToken(NAME,'begin',1,3)
Syntax error: Illegal character ' '
LexToken(NAME,'end',1,9)
Syntax error: Illegal character ' '
Syntax error: Illegal character 'D'
Syntax error: Illegal character 'E'
Syntax error: Illegal character 'C'
Syntax error: Illegal character 'L'
Syntax error: Illegal character 'A'
Syntax error: Illegal character 'R'
Syntax error: Illegal character 'E'

(这不是全部错误,但足以了解发生了什么)

出于某种原因,Lex 在解析关键字之前先解析 NAME 个标记。即使在解析完 NAME 标记后,它也无法识别 DECLARE 保留关键字。我还尝试使用正则表达式为其余标记添加保留关键字,但我得到了相同的结果(文档也建议不要这样做)。

有谁知道如何解决这个问题?我希望词法分析器首先识别保留关键字,然后尝试标记化输入的其余部分。

谢谢!

编辑:

即使使用文档中举例说明的 t_ID 函数,我也会得到相同的结果:

def t_NAME(t):
    r'[a-z][a-zA-Z_&!0-9]{0,9}'
    t.type = reserved.get(t.value,'NAME')
    return t

这里的主要问题是你没有忽略空格;所有的错误都是结果。在语法中添加 t_ignore definition 将消除这些错误。

但是即使您修复了空白问题,语法也不会按预期工作,因为您似乎遗漏了文档的一个重要方面,它告诉您如何实际使用词典 reserved

要处理保留字,您应该编写一条规则来匹配标识符,并在函数中执行特殊名称查找,如下所示:

 reserved = {
    'if' : 'IF',
    'then' : 'THEN',
    'else' : 'ELSE',
    'while' : 'WHILE',
    ...
 }

 tokens = ['LPAREN','RPAREN',...,'ID'] + list(reserved.values())

 def t_ID(t):
     r'[a-zA-Z_][a-zA-Z_0-9]*'
     t.type = reserved.get(t.value,'ID')    # Check for reserved words
     return t

(在你的情况下,它将是 NAME 而不是 ID。)

Ply 对字典 reserved 一无所知,它也不知道您如何生成 tokens 中列举的标记名称。 tokens 唯一的一点就是让 Ply 知道文法中哪些符号代表记号,哪些代表非终结符。 tokens 中有某个单词这一事实并不能用来定义该标记的模式。