我正在使用ply(Lex和Yacc的流行python实现)为自定义语言创建一个简单的编译器。
目前我的lexer看起来如下:
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)
根据文档,我正在为保留关键字创建一个字典,然后将它们添加到tokens
列表中,而不是为它们添加单独的规则。文件还指出,优先权是根据以下两条规则决定的:
- 函数定义的所有标记都按照它们在lexer文件中出现的顺序添加
- 接下来,通过按正则表达式长度递减的顺序对字符串定义的令牌进行排序来添加它们(首先添加较长的表达式)
我遇到的问题是,当我使用这个测试字符串测试lexer时
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"
lexer返回以下错误:
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
这里的主要问题是没有忽略空白;所有的错误都是结果。在语法中添加t_ignore
定义将消除这些错误。
但是,即使修复了空白问题,语法也不会像预期的那样工作,因为您似乎缺少文档的一个重要方面,该方面告诉您如何实际使用字典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
中这一事实本身并不能定义该标记的模式。