如何将两个标记定义为一个标记?

时间:2017-10-28 11:34:07

标签: python lexical-analysis ply

我试图在我的词法分析器中定义两个由空格分隔的单词作为一个标记 但当我传递in out之类的输入时,它会显示LexToken(KEYIN,'in',1,0)LexToken(KEYOUT,'out',1,3) 我需要它像LexToken(KEYINOUT,'in out',1,0)

PS:KEYINKEYOUT是两个不同的标记,因为语法的定义

以下是导致问题的测试:

import lex
reserved = {'in': 'KEYIN', 'out': 'KEYOUT', 'in\sout': 'KEYINOUT'} # the problem is in here

tokens = ['PLUS', 'MINUS', 'IDENTIFIER'] + list(reserved.values())

t_MINUS = r'-'
t_PLUS = r'\+'
t_ignore = ' \t'

def t_IDENTIFIER(t):
    r'[a-zA-Z]+([(a-zA-Z)*|(\d+)*|(_*)])*'
    t.type = reserved.get(t.value, 'IDENTIFIER')  # Check for reserved words
    return t


def t_error(t):
    print("Illegal character '%s'" % t.value[0], "at line", t.lexer.lineno, "at position", t.lexer.lexpos)
    t.lexer.skip(1)


lex.lex()
lex.input("in out inout + - ")
while True:
    tok = lex.token()
    print(tok)
    if not tok:
        break

输出:

LexToken(KEYIN,'in',1,0)
LexToken(KEYOUT,'out',1,3)
LexToken(IDENTIFIER,'inout',1,7)
LexToken(PLUS,'+',1,13)
LexToken(MINUS,'-',1,15)
None

1 个答案:

答案 0 :(得分:0)

这是您识别IDENTIFIER和关键字的功能:

def t_IDENTIFIER(t):
    r'[a-zA-Z]+([(a-zA-Z)*|(\d+)*|(_*)])*'
    t.type = reserved.get(t.value, 'IDENTIFIER')  # Check for reserved words
    return t

首先,很明显它能够识别的关键字正是字典reserved的关键字,它们是:

in
out
in\sout

由于in out不是该字典中的键(in\sout 是相同的字符串),因此无论{{1}是什么t.value都无法将其识别为关键字碰巧是。

t.value也不能in out,因为t.value将始终与控制t_IDENTIFIER的正则表达式匹配:

[a-zA-Z]+([(a-zA-Z)*|(\d+)*|(_*)])*

并且正则表达式永远不会匹配任何带空格字符的内容。 (该正则表达式存在各种问题;第二个字符类中的字符*()|+被视为普通字符。请参阅下面的正确的正则表达式。)

您可以在编辑之前以与原始问题中建议的方式类似的方式将in out作为令牌进行匹配。然而,

t_KEYINOUT = r'in\sout'

不起作用,因为Ply不使用常见的“最大munch”算法来决定接受哪个正则表达式模式。相反,它只是命令所有模式并选择匹配的第一个模式,其中顺序包含所有标记化函数(按照它们的定义顺序),然​​后是标记变量以正则表达式长度的相反顺序排序。由于t_IDENTIFIER是一个函数,因此将在变量t_KEYINOUT之前尝试。为确保首先尝试t_KEYINOUT,必须将其设置为一个函数,然后将放在 t_IDENTIFIER之前。

然而,这仍然不是你想要的,因为它会标记化

in outwards

as

LexToken(KEYINOUT,'in out',1,0)
LexToken(IDENTIFIER,'wards',1,6)

而不是

LexToken(KEYIN,'in',1,0)
LexToken(IDENTIFIER,'outwards',1,3)

要获得正确的分析,您需要确保in out仅在out为完整字词时才匹配;换句话说,如果在比赛结束时有一个单词边界。所以一个解决方案是:

reserved = {'in': 'KEYIN', 'out': 'KEYOUT'}

def t_KEYINOUT(t):
    r'in\sout\b'
    return t

def t_IDENTIFIER(t):
    r'[a-zA-Z][a-zA-Z0-9_]*'
    t.type = reserved.get(t.value, 'IDENTIFIER')  # Check for reserved words
    return t

但是,词法分析器将in out识别为单个标记几乎肯定不是必要。由于inout都是关键字,因此很容易将它留给解析器注意它们何时作为in out指示符一起使用:

parameter: KEYIN IDENTIFIER
         | KEYOUT IDENTIFIER
         | KEYIN KEYOUT IDENTIFIER