Lex似乎不遵循优先顺序

时间:2019-09-09 17:17:20

标签: parsing yacc lex ply

我正在使用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. 由函数定义的所有标记的添加顺序与它们在lexer文件中出现的顺序相同。
  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"

词法分析器返回以下错误:

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保留关键字。我还尝试过使用正则表达式在其余的令牌中添加保留关键字,但得到的结果是相同的(文档也建议不要这样做)。

有人知道如何解决此问题吗?我希望Lexer首先识别保留的关键字,然后尝试对其余输入进行标记。

谢谢!

编辑:

即使使用文档中示例的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

1 个答案:

答案 0 :(得分:1)

这里的主要问题是您没有忽略空格;所有错误都是后果。在语法中添加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中有一个单词这一事实本身并不能为该令牌定义模式。