牛骨文教育服务平台(让学习变的简单)

4.15 额外状态维护

在你的词法分析器中,你可能想要维护一些状态。这可能包括模式设置,符号表和其他细节。例如,假设你想要跟踪NUMBER标记的出现个数。

一种方法是维护一个全局变量:

num_count = 0
def t_NUMBER(t):
    r"d+"
    global num_count
    num_count += 1
    t.value = int(t.value)    
    return t

如果你不喜欢全局变量,另一个记录信息的地方是lexer对象内部。可以通过当前标记的lexer属性访问:

def t_NUMBER(t):
    r"d+"
    t.lexer.num_count += 1     # Note use of lexer attribute
    t.value = int(t.value)    
    return t

lexer = lex.lex()
lexer.num_count = 0            # Set the initial count

上面这样做的优点是当同时存在多个lexer实例的情况下,简单易行。不过这看上去似乎是严重违反了面向对象的封装原则。lexer的内部属性(除了lineno)都是以lex开头命名的(lexdata、lexpos)。因此,只要不以lex开头来命名属性就很安全的。

如果你不喜欢给lexer对象赋值,你可以自定义你的lexer类型,就像前面看到的那样:

class MyLexer:
    ...
    def t_NUMBER(self,t):
        r"d+"
        self.num_count += 1
        t.value = int(t.value)    
        return t

    def build(self, **kwargs):
        self.lexer = lex.lex(object=self,**kwargs)

    def __init__(self):
        self.num_count = 0

如果你的应用会创建很多lexer的实例,并且需要维护很多状态,上面的类可能是最容易管理的。

状态也可以用闭包来管理,比如,在Python3中:

def MyLexer():
    num_count = 0
    ...
    def t_NUMBER(t):
        r"d+"
        nonlocal num_count
        num_count += 1
        t.value = int(t.value)    
        return t
    ...