4.3 标记的规则

最后更新于:2022-04-01 01:09:34

每种标记用一个正则表达式规则来表示,每个规则需要以”t_“开头声明,表示该声明是对标记的规则定义。对于简单的标记,可以定义成这样(在Python中使用raw string能比较方便的书写正则表达式): ~~~ t_PLUS = r'\+' ~~~ 这里,紧跟在t_后面的单词,必须跟标记列表中的某个标记名称对应。如果需要执行动作的话,规则可以写成一个方法。例如,下面的规则匹配数字字串,并且将匹配的字符串转化成Python的整型: ~~~ def t_NUMBER(t): r'\d+' t.value = int(t.value) return t ~~~ 如果使用方法的话,正则表达式写成方法的文档字符串。方法总是需要接受一个LexToken实例的参数,该实例有一个t.type的属性(字符串表示)来表示标记的类型名称,t.value是标记值(匹配的实际的字符串),t.lineno表示当前在源输入串中的作业行,t.lexpos表示标记相对于输入串起始位置的偏移。默认情况下,t.type是以t_开头的变量或方法的后面部分。方法可以在方法体里面修改这些属性。但是,如果这样做,应该返回结果token,否则,标记将被丢弃。 在lex内部,lex.py用`re`模块处理模式匹配,在构造最终的完整的正则式的时候,用户提供的规则按照下面的顺序加入: 1. 所有由方法定义的标记规则,按照他们的出现顺序依次加入 2. 由字符串变量定义的标记规则按照其正则式长度倒序后,依次加入(长的先入) 3. 顺序的约定对于精确匹配是必要的。比如,如果你想区分‘=’和‘==’,你需要确保‘==’优先检查。如果用字符串来定义这样的表达式的话,通过将较长的正则式先加入,可以帮助解决这个问题。用方法定义标记,可以显示地控制哪个规则优先检查。 为了处理保留字,你应该写一个单一的规则来匹配这些标识,并在方法里面作特殊的查询: ~~~ 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 ~~~ 这样做可以大大减少正则式的个数,并稍稍加快处理速度。注意:你应该避免为保留字编写单独的规则,例如,如果你像下面这样写: ~~~ t_FOR = r'for' t_PRINT = r'print' ~~~ 但是,这些规则照样也能够匹配以这些字符开头的单词,比如’forget’或者’printed’,这通常不是你想要的。
';