如何将两个令牌定义为一个令牌?

how to define two tokens as one token?

我试图在我的词法分析器中将由 space 分隔的两个词定义为一个标记 但是当我传递像 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

这是您识别 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不是那个字典的key(in\sout不是同一个字符串),无论怎样都不能识别为关键字t.value 恰好是什么。

但是t.value也不能是in out,因为t.value总是匹配控制t_IDENTIFIER:

的正则表达式
[a-zA-Z]+([(a-zA-Z)*|(\d+)*|(_*)])*

并且该正则表达式从不匹配具有 space 字符的任何内容。 (该正则表达式有各种问题;第二个字符 class 中的字符 *()|+ 被处理作为普通字符。请参阅下面的正确正则表达式。)

在编辑之前,您当然可以按照与原始问题中建议的方式类似的方式将 in out 匹配为标记。然而,

t_KEYINOUT = r'in\sout'

将不起作用,因为 Ply 不使用常见的 "maximum munch" 算法来决定接受哪个正则表达式模式。相反,它只是对所有模式进行排序并选择第一个匹配的模式,其中顺序包括所有标记化 函数 (按照它们定义的顺序),然​​后是标记变量按正则表达式长度的相反顺序排序。由于t_IDENTIFIER是一个函数,它会在变量t_KEYINOUT之前被尝试。为保证t_KEYINOUT先试,必须做成函数放在 t_IDENTIFIER.

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

in outwards

作为

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