如果你真的想做一个解析器,首先不要编写任何代码,而是要了解你的语法应该如何工作。Backus-Naur 格式或 BNF 是用于定义语法的典型符号。中缀表示法是一个常见的软件工程解析主题,中缀表示法的基本 BNF 结构如下:
letter ::= 'a'..'z'
operand ::= letter+
term ::= operand | '(' expr ')'
expr ::= term ( '+' term )*
关键是它term
包含您的字母操作数或包含在 () 中的整个子表达式。该子表达式与整个表达式相同,因此此递归定义负责所有括号嵌套。然后,表达式是一个术语,后跟零个或多个术语,使用二进制“+”运算符添加。(您也可以扩展term
以处理减法和乘法/除法,但我不会让这个答案变得过于复杂。)
Pyparsing 是一个包,它可以使用 Python 对象轻松地将 BNF 转换为工作解析器(Ply、spark 和 yapps 是其他解析器,它们遵循更传统的 lex/yacc 解析器创建模型)。这是直接使用pyparsing实现的BNF:
from pyparsing import Suppress, Word, alphas, Forward, Group, ZeroOrMore
LPAR, RPAR, PLUS = map(Suppress, "()+")
operand = Word(alphas)
# forward declare our overall expression, necessary when defining a recursive grammar
expr = Forward()
# each term is either an alpha operand, or an expr in ()'s
term = operand | Group(LPAR + expr + RPAR)
# define expr as a term, with optional '+ term's
expr << term + ZeroOrMore(PLUS + term)
# try it out
s = "(((a+b)+c)+(d+e))"
print expr.parseString(s)
给予:
[[[['a', 'b'], 'c'], ['d', 'e']]]
识别操作优先级的中缀表示法是一个非常常见的解析器,或者是更大解析器的一部分,因此 pyparsing 包含一个帮助器内置调用operatorPrecedence
来处理所有嵌套/分组/递归等。这是使用 编写的同一个解析器operatorPrecedence
:
from pyparsing import operatorPrecedence, opAssoc, Word, alphas, Suppress
# define an infix notation with precedence of operations
# you only define one operation '+', so this is a simple case
operand = Word(alphas)
expr = operatorPrecedence(operand,
[
('+', 2, opAssoc.LEFT),
])
print expr.parseString(s)
给出与以前相同的结果。
可以在 pyparsing wiki 上在线找到更详细的示例——fourFn.py 中的显式实现和simpleArith.py中的 operatorPrecedence 实现。