本当にパーサーを実行したい場合は、コードを記述せずに、文法がどのように機能するかを理解することから始めます。Backus-Naur FormatまたはBNFは、文法を定義するために使用される一般的な表記法です。中置記法は一般的なソフトウェアエンジニアリングの解析トピックであり、中置記法の基本的なBNF構造は次のようになります。
letter ::= 'a'..'z'
operand ::= letter+
term ::= operand | '(' expr ')'
expr ::= term ( '+' term )*
重要なのはterm
、アルファベットのオペランドまたは()でラップされた部分式全体のいずれかを含むことです。その部分式は式全体とまったく同じであるため、この再帰的定義はすべての括弧の入れ子を処理します。この場合、式は、2項の「+」演算子を使用して追加された、0個以上の用語が続く用語です。(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)
以前と同じ結果が得られます。
より詳細な例は、pyparsingwikiでオンラインで見つけることができます-fourFn.pyでの明示的な実装とsimpleArith.pyでのoperatorPrecedenceの実装。