Я не могу говорить о PLY-решении, но вот тот, который использует pyparsing. Иногда пример pyparsing может быть полезен, даже если вы в конечном итоге захотите реализовать свой парсер, используя какую-то другую библиотеку, в качестве быстрого и грязного прототипа / упражнения. К сожалению, в этом примере интенсивно используется метод operatorPrecedence
, который скрывает большую часть магии разбора инфикса, поэтому я не знаю, насколько легко вы сможете его перевести. Более традиционный пример синтаксического анализатора expr / term / factor можно найти на вики-странице pyparsing на странице примеров (http://pyparsing.wikispaces.com/Examples), под названием fourFn.py .
bnf = """
statement-list -> statement ',' statement-list
statement -> ident 'was' 'a' type |
ident 'became' expr |
'print' expr |
'if' conditional-expr statement
type -> 'number' | 'letter'
expr -> factor |
expr '+' factor |
expr '-' factor
factor -> number | letter | ident
"""
from pyparsing import (CaselessKeyword, Word, nums, alphas, alphanums, operatorPrecedence,
Forward, MatchFirst, opAssoc, oneOf, Group, delimitedList)
PRINT, WAS, A, BECAME, NUMBER, LETTER, IF, ELSE, TRUE, FALSE, AND, OR, NOT = map(
CaselessKeyword,
"print was a became number letter if else true false and or not".upper().split())
keyword = MatchFirst([PRINT, WAS, A, BECAME, NUMBER, LETTER, IF, ELSE, TRUE, FALSE, AND, OR, NOT])
typeSpecifier = NUMBER | LETTER
number = Word(nums)
ident = ~keyword + Word(alphas, alphanums+'_')
operand = number | ident
expr = operatorPrecedence(operand,
[
('-', 1, opAssoc.RIGHT),
(oneOf('* /'), 2, opAssoc.LEFT),
(oneOf('+ -'), 2, opAssoc.LEFT),
])
comparisonExpr = operatorPrecedence(expr,
[
("!", 1, opAssoc.RIGHT),
(oneOf("< > = <= >= !="), 2, opAssoc.LEFT),
])
booleanExpr = operatorPrecedence(TRUE | FALSE | comparisonExpr,
[
(NOT, 1, opAssoc.RIGHT),
(AND, 2, opAssoc.LEFT),
(OR, 2, opAssoc.LEFT),
])
statement = Forward()
printStmt = PRINT + expr
wasaStmt = ident + WAS + A + typeSpecifier
becameStmt = ident + BECAME + expr
ifStmt = IF + booleanExpr + statement
statement << Group(printStmt | wasaStmt | becameStmt | ifStmt)
statementList = delimitedList(statement)
tests = """\
x was a number
y became 2+5
print y
print 100*(5+2)
print 100*5+2
if 5 > y print 1000
if y < 10 y became y+1, print y
""".splitlines()[:-1]
for t in tests:
print t.strip()
for s in statementList.parseString(t).asList():
print(s)
print
Печать:
x was a number
['x', 'WAS', 'A', 'NUMBER']
y became 2+5
['y', 'BECAME', ['2', '+', '5']]
print y
['PRINT', 'y']
print 100*(5+2)
['PRINT', ['100', '*', ['5', '+', '2']]]
print 100*5+2
['PRINT', [['100', '*', '5'], '+', '2']]
if 5 > y print 1000
['IF', ['5', '>', 'y'], ['PRINT', '1000']]
if y < 10 y became y+1, print y
['IF', ['y', '<', '10'], ['y', 'BECAME', ['y', '+', '1']]
['PRINT', 'y']
Я позволил себе добавить print
в качестве типа оператора, чтобы он мог появляться в любом месте тела вашей программы. Кроме того, я попытался добавить оператор IF-THEN, и этот действительно показывает, как добавление такого оператора control-flow начинает вас по пути написания рекурсивной грамматики (рекурсия не нужна только для ' был ',' стал ', и' печать ').