【Python神器】使用lex进行规则解释

2021-10-28 14:49:22 浏览数 (2)

背景


在一个复杂的文章搜索匹配的需求里,匹配规则已经实现,但是原有的规则写法过于复杂,需要进行简化,例如原规则:

代码语言:javascript复制
("小鹏" >= 1) and ("P7" >= 1)

这个规则的意思实际上是:小鹏这个关键词的出现次数大于等于1,P7这个关键词出现次数也大于等于1。

但是这个语法显然很罗嗦,客户要求进行简化。客户希望可以简化成这样:

代码语言:javascript复制
小鹏 and P7

这是客户的习惯,实际上参考搜索引擎的查询语法是可以更加简洁的“ 小鹏 P7”,不过这暂时不再考虑范围。

上面这个只是一个简化的示例,实际客户写的匹配规则是可能很复杂的。

使用lex进行解释


同事们好像觉得这个功能实现很难,没什么信心,其实只要理解其中的逻辑,并不复杂,就算不借助工具也能实现,单单用正则和循环也能解决。不过,使用神器lex显然是更好的解决方案(lex经常和yacc搭配使用,不过我们的需求比较简单,并不需要用到yacc)。

下面是一个简单的示例:

代码语言:javascript复制
import ply.lex as lex

# List of token names.
tokens = (
    'KEYWORD',  # 关键词
    'LPAREN',   # 左括号
    'RPAREN',   # 右括号
    'LOGIT',    # 逻辑操作
    'CMP',      # 比较操作
)

# 分组
t_LPAREN  = r'('
t_RPAREN  = r')'
# 忽略空格及tab
t_ignore  = ' t'

# 关键词
def t_KEYWORD(t):
    # 双引号内的,或者不非空格组成的字符串(不含括号)
    r'("[^"] ")|([^s()] )'
    val_lower = t.value.lower()
    if val_lower in {'and', 'or', 'not'}:
        t.type = 'LOGIT'
        return t
    if val_lower in {'>=', '>', '==', '<=', '<'}:
        t.type = 'CMP'
        return t
    if t.value[0] == '"':
        t.value = t.value[1:-1]    # 关键词统一去掉双引号
    return t

 # Error handling rule
def t_error(t):
    print("Illegal character '%s'" % t.value[0])
    t.lexer.skip(1)
# Build the lexer
lexer = lex.lex()

测试代码:

代码语言:javascript复制
def parse_tokens(data):
    print('')
    lexer.input(data)
    while True:
        tok = lexer.token()
        if not tok: 
            break
        print(tok)

data = '小鹏 and P7'
parse_tokens(data)

# 带双引号的测试
data = '小鹏  and "P7 价格"'
parse_tokens(data)

# 带括号及比较操作的测试
data = '(小鹏 >= 2) and "P7 价格"'
parse_tokens(data)

测试对应的输出:

代码语言:javascript复制
LexToken(KEYWORD,'小鹏',1,0)
LexToken(LOGIT,'and',1,3)
LexToken(KEYWORD,'P7',1,7)

LexToken(KEYWORD,'小鹏',1,0)
LexToken(LOGIT,'and',1,4)
LexToken(KEYWORD,'P7 价格',1,8)

LexToken(LPAREN,'(',1,0)
LexToken(KEYWORD,'小鹏',1,1)
LexToken(CMP,'>=',1,4)
LexToken(KEYWORD,'2',1,7)
LexToken(RPAREN,')',1,8)
LexToken(LOGIT,'and',1,10)
LexToken(KEYWORD,'P7 价格',1,14)

有了这个结果,要处理成完整的表达式已经是很简单了。

lex与yacc


有了这两个神器,想实现一门简单的语言也是不难的。而且,理解了这两个工具,非常有助于理解编程语言本身,可谓大有益处。

程序员还是要保持好奇心。

备注:

ply是Python Lex Yacc的缩写,官方文档:http://www.dabeaz.com/ply/ply.html

0 人点赞