PyParsing を使用して、かなり複雑な文法のパーサーを実装しています。(付け加えるとすれば、これは使用するのが本当に楽しいことです!)
文法は、(さまざまな) アルファベットの定義を可能にするという点で、いくぶん「動的」であり、他の定義で許可される要素を定義します。例として:
alphabet: a b c
lists:
s1 = a b
s2 = b c x
ここで、alphabet
は定義で許可される要素を定義することを意味しますlists
。たとえば、有効ですが、無効なs1
がs2
含まれていますx
。
この種の検証を行わない単純な PyParsing パーサーは、次のようになります。
from pyparsing import Literal, lineEnd, Word, alphanums,\
OneOrMore, Group, Suppress, dictOf
def fixedToken(literal):
return Suppress(Literal(literal))
Element = Word(alphanums)
Alphabet = Group(OneOrMore(~lineEnd + Element))
AlphaDef = fixedToken("alphabet:") + Alphabet
ListLine = OneOrMore(~lineEnd + Element)
Lists = dictOf(Word(alphanums) + fixedToken("="), ListLine)
Start = AlphaDef + fixedToken("lists:") + Lists
if __name__ == "__main__":
data = """
alphabet: a b c
lists:
s1 = a b
s2 = b c x
"""
res = Start.parseString(data)
for k, v in sorted(res.items()):
print k, "=", v
これは解析して出力を与えます:
Alphabet= set(['a', 'c', 'b'])
s1 = ['a', 'b']
s2 = ['b', 'c', 'x']
ただし、s2
無効なx
. 理想的には、 の定義を次のように定義できるようにListLine
しOneOrMore(oneOf(Alphabet))
たいと考えていAlphabet
ます。
私が見つけた 1 つの解決策は、1. アルファベットを記憶し、2. 行を検証するために解析アクションを追加することでした。
# ...
Alphabet = Group(OneOrMore(~lineEnd + Element))
def alphaHold(toks):
alphaHold.alpha = set(*toks)
print "Alphabet=", alphaHold.alpha
Alphabet.addParseAction(alphaHold)
AlphaDef = fixedToken("alphabet:") + Alphabet
ListLine = OneOrMore(~lineEnd + Element)
def lineValidate(toks):
unknown = set(toks).difference(alphaHold.alpha)
if len(unknown):
msg= "Unknown element(s): {}".format(unknown)
print msg
raise ParseException(msg)
ListLine.addParseAction(lineValidate)
# ...
これにより、ほぼ目的の出力が得られます。
Alphabet= set(['a', 'c', 'b'])
Unknown element(s): set(['x'])
s1 = ['a', 'b']
しかし残念なことに、PyParsing は解析アクションからスローされた例外をキャッチするため、このアプローチは技術的に失敗します。私が見逃したかもしれないPyParsing内でこれを達成する別の方法はありますか?