在你的词法分析器中,你可能想要维护一些状态。这可能包括模式设置,符号表和其他细节。例如,假设你想要跟踪`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
...
~~~
- 0 一些翻译约定
- 1 前言和预备
- 2 介绍
- 3 PLY概要
- 4 Lex
- 4.1 Lex的例子
- 4.2 标记列表
- 4.3 标记的规则
- 4.4 标记的值
- 4.5 丢弃标记
- 4.6 行号和位置信息
- 4.7 忽略字符
- 4.8 字面字符
- 4.9 错误处理
- 4.10 构建和使用lexer
- 4.11 @TOKEN装饰器
- 4.12 优化模式
- 4.13 调试
- 4.14 其他方式定义词法规则
- 4.15 额外状态维护
- 4.16 Lexer克隆
- 4.17 Lexer的内部状态
- 4.18 基于条件的扫描和启动条件
- 4.19 其他问题
- 5 语法分析基础
- 6 Yacc
- 6.1 一个例子
- 6.2 将语法规则合并
- 6.3 字面字符
- 6.4 空产生式
- 6.5 改变起始符号
- 6.6 处理二义文法
- 6.7 parser.out调试文件
- 6.8 处理语法错误
- 6.9 行号和位置的跟踪
- 6.10 构造抽象语法树
- 6.11 嵌入式动作
- 6.12 Yacc的其他
- 7 多个语法和词法分析器
- 8 使用Python的优化模式
- 9 高级调试
- 9.1 调试lex()和yacc()命令
- 9.2 运行时调试
- 10 如何继续