Если вас устраивает синтаксический анализ выражений , написание собственных анализаторов может быть невероятно коротким. Вот простой парсер Packrat, который принимает разумное подмножество PEG:
import functools
class peg_parse:
def __init__(self, grammar):
self.grammar = {k:[tuple(l) for l in rules] for k,rules in grammar.items()}
@functools.lru_cache(maxsize=None)
def unify_key(self, key, text, at=0):
if key not in self.grammar:
return (at + len(key), (key, [])) if text[at:].startswith(key) \
else (at, None)
rules = self.grammar[key]
for rule in rules:
l, res = self.unify_rule(rule, text, at)
if res is not None: return l, (key, res)
return (0, None)
def unify_line(self, parts, text, tfrom):
results = []
for part in parts:
tfrom, res = self.unify_key(part, text, tfrom)
if res is None: return tfrom, None
results.append(res)
return tfrom, results
Он принимает грамматики в форме словаря Python с нетерминалами в качестве ключей и альтернатив в качестве элементов массива, и каждая альтернатива представляет собой последовательность выражений. Ниже приведен пример грамматики.
term_grammar = {
'expr': [
['term', 'add_op', 'expr'],
['term']],
'term': [
['fact', 'mul_op', 'term'],
['fact']],
'fact': [
['digits'],
['(','expr',')']],
'digits': [
['digit','digits'],
['digit']],
'digit': [[str(i)] for i in list(range(10))],
'add_op': [['+'], ['-']],
'mul_op': [['*'], ['/']]
}
Вот драйвер:
import sys
def main(to_parse):
result = peg_parse(term_grammar).unify_key('expr', to_parse)
assert (len(to_parse) - result[0]) == 0
print(result[1])
if __name__ == '__main__': main(sys.argv[1])
Который может быть вызван так:
python3 parser.py '1+2'
('expr',
[('term',
[('fact',
[('digits', [('digit', [('1', [])])])])]),
('add_op', [('+', [])]),
('expr',
[('term', [('fact', [('digits', [('digit', [('2', [])])])])])])])
При разборе грамматик выражений нужно позаботиться о том, чтобы написать: порядок альтернатив важен (в отличие от грамматики без контекста, альтернативы являются упорядоченным выбором, при котором первый выбор пробуется первым, а второй - только если первый матч). Однако они могут представлять все известные контекстно-свободные грамматики.
Если, с другой стороны, вы решите использовать контекстно-свободную грамматику, Earley Parser является одним из самых простых.