Package: rply-ulang
在 rply 0.7.8 的基础上，作了如下修改：为减小工作量，暂时放弃对 pypy、Python 2 等版本的支持，仅支持 Python 3.7。各方面中文化：异常信息与标识符命名等；添加中文 API，同时仍然支持原英文 API。改进：添加 shift/reduce 歧义的调试信息，详见此文。修复: 分词错误时列号定位有误【试验】通过根据语法规则分词，支持中文无空格语法，演示在此。### 测试pytest运行所有测试用例，或运行单个用例，如pytest tests/test_both.py以下为 rply 原始文档。Welcome to RPLY! A pure Python parser generator, that also works with RPython.
It is a more-or-less direct port of David Beazley’s awesome PLY, with a new
public API, and RPython support.You can find the documentationonline.Basic API:fromrplyimportParserGenerator,LexerGeneratorfromrply.tokenimportBaseBoxlg=LexerGenerator()# Add takes a rule name, and a regular expression that defines the rule.lg.add("PLUS",r"\+")lg.add("MINUS",r"-")lg.add("NUMBER",r"\d+")lg.ignore(r"\s+")# This is a list of the token names. precedence is an optional list of# tuples which specifies order of operation for avoiding ambiguity.# precedence must be one of "left", "right", "nonassoc".# cache_id is an optional string which specifies an ID to use for# caching. It should *always* be safe to use caching,# RPly will automatically detect when your grammar is# changed and refresh the cache for you.pg=ParserGenerator(["NUMBER","PLUS","MINUS"],precedence=[("left",['PLUS','MINUS'])],cache_id="myparser")@pg.production("main : expr")defmain(p):# p is a list, of each of the pieces on the right hand side of the# grammar rulereturnp[0]@pg.production("expr : expr PLUS expr")@pg.production("expr : expr MINUS expr")defexpr_op(p):lhs=p[0].getint()rhs=p[2].getint()ifp[1].gettokentype()=="PLUS":returnBoxInt(lhs+rhs)elifp[1].gettokentype()=="MINUS":returnBoxInt(lhs-rhs)else:raiseAssertionError("This is impossible, abort the time machine!")@pg.production("expr : NUMBER")defexpr_num(p):returnBoxInt(int(p[0].getstr()))lexer=lg.build()parser=pg.build()classBoxInt(BaseBox):def__init__(self,value):self.value=valuedefgetint(self):returnself.valueThen you can do:parser.parse(lexer.lex("1 + 3 - 2+12-32"))You can also substitute your own lexer. A lexer is an object with anext()method that returns either the next token in sequence, orNoneif the token
stream has been exhausted.Why do we have the boxes?In RPython, like other statically typed languages, a variable must have a
specific type, we take advantage of polymorphism to keep values in a box so
that everything is statically typed. You can write whatever boxes you need for
your project.If you don’t intend to use your parser from RPython, and just want a cool pure
Python parser you can ignore all the box stuff and just return whatever you
like from each production method.Error handlingBy default, when a parsing error is encountered, anrply.ParsingErroris
raised, it has a methodgetsourcepos(), which returns anrply.token.SourcePositionobject.You may also provide an error handler, which, at the moment, must raise an
exception. It receives theTokenobject that the parser errored on.pg=ParserGenerator(...)@pg.errordeferror_handler(token):raiseValueError("Ran into a%swhere it wasn't expected"%token.gettokentype())Python compatibilityRPly is tested and known to work under Python 2.7, 3.4+, and PyPy. It is
also valid RPython for PyPy checkouts from6c642ae7a0eaonwards.LinksSource code and issue trackerPyPI releasesTalk at PyCon US 2013: So you want to write an interpreter?