[] build-status [] coverage-status [] pypi-version [] pypi-downloads
A pure-Python lexer and parser. Together they provide an experience reminiscent of yacc
or bison
, but of course in a more Pythonic way.
NOTE: As of the parser rewrite, only "small" grammars are supported. In the future I hope to improve this by using LALR(1) instead of LR(1) for the parsing table generation.
This project started out as a way to avoid writing C++ for a compilers class. Since I didn't know much about parsing algorithms at the time, it was simply intended to provide a nicer interface to PLY. After the class was over I took some time to tidy it up and add the textbook (basically straight out of the Dragon Book) LR(1) parsing algorithm.
I use purplex myself for one-off scripts here and there. However to date, the only project using purplex for anything big is hangups.
- Python 2.7 or 3+
- requirements.txt
python setup.py test
from purplex import Lexer, TokenDef
from purplex import Parser, attach
from purplex import LEFT, RIGHT
class MyLexer(Lexer):
INTEGER = TokenDef(r'\d+')
LPAREN = TokenDef(r'\(')
RPAREN = TokenDef(r'\)')
TIMES = TokenDef(r'\*')
DIVIDE = TokenDef(r'/')
PLUS = TokenDef(r'\+')
MINUS = TokenDef(r'-')
WHITESPACE = TokenDef(r'[\s\n]+', ignore=True)
class MyParser(Parser):
LEXER = MyLexer
START = 'e'
PRECEDENCE = (
(RIGHT, 'UMINUS'),
(LEFT, 'TIMES', 'DIVIDE'),
(LEFT, 'PLUS', 'MINUS'),
)
@attach('e : LPAREN e RPAREN')
def brackets(self, lparen, expr, rparen):
return expr
@attach('e : e PLUS e')
def addition(self, left, op, right):
return left + right
@attach('e : e MINUS e')
def subtract(self, left, op, right):
return left - right
@attach('e : e TIMES e')
def multiply(self, left, op, right):
return left * right
@attach('e : e DIVIDE e')
def division(self, left, op, right):
return left / right
@attach('e : MINUS e', prec_symbol='UMINUS')
def negate(self, minus, expr):
return -expr
@attach('e : INTEGER')
def number(self, num):
return int(num)
if __name__ == '__main__':
parser = MyParser()
problems = [
('2 + 3 * 4 - 4', 10),
('-4', -4),
('-4 * 2', -8),
('-2 * - (1 + 1)', 4),
('6 / 2 * 4 - 8 * 1', 4),
]
for problem, answer in problems:
result = parser.parse(problem)
print(problem, '=', result, ';', result == answer)
$ python example.py
2 + 3 * 4 - 4 = 10 ; True
-4 = -4 ; True
-4 * 2 = -8 ; True
-2 * - (1 + 1) = 4 ; True
6 / 2 * 4 - 8 * 1 = 4.0 ; True