ply的简化api(python lex&yacc)。

ox-parser的Python项目详细描述


ox是一个简单的“编译器的编译器”框架,基于优秀的PLY 图书馆。

为什么是牛?

ply是一个很好的库,它是一个相当有效的纯python 实施yacc/bison。不过,我们认为它的api有点尴尬 做了很多奇怪的魔术。牛包主要层功能 变成一个更加功能化和简单的api,目的是在 仍然更容易使用。

ply是为yacc/bison设计的python替代品,不提供 作为构建编译器的通用框架的任何功能。牛 是一个极简的框架,提供了一些额外的功能(但是 它永远不会是python的替代品,比如llvm)。

ox已经足够成熟,可以用于生产代码,但是就像ply一样,它是 作为编译器入门课程的工具创建的。一个明确的教学法 ox的目标是使不同编译阶段的边界非常 明确且易于相互插入。这种方法有利于 教学,但它不会导致最有效或最有力的 真正编译器的实现。作为大多数编译器生成器,ox对于 快速试验,但性能有限,而且 重要的是,ox解析器通常无法为 语法错误。

名字呢?

ply是yacc的pythonic实现/解释。最广泛的 yacc的实现当然是gnu-bison。我们决定养牛 活的主题并称之为ox。

概念

编译通常分为几个步骤:

  1. 标记化/词法分析:一个源代码串被分解成 令牌列表。ox lexers是任何接收源字符串的函数 编码并返回令牌列表(或任何iterable)。
  2. 解析:将标记列表转换为语法树。在ox中,解析器 从BNF形式的语法派生而来。它接收一个令牌列表 输出任意解析树。
  3. 语义分析:对解析树进行语义错误扫描(例如 无效的变量名、无效的类型签名等)。解析树可以 在此过程中转换为不同的表示。
  4. 代码优化:为了生成 有效的内部陈述。这在很大程度上取决于目标 语言和运行时,它往往是一个真正的编译器的最大部分。
  5. 代码生成:中间表示用于在 目标语言。目标语言通常是低级语言,如 装配或机器代码。没有什么能阻止我们对Python或 然而,javascript。

ox主要关注步骤1和2。图书馆的支持非常有限 步骤3继续,但一般来说,它们往往非常针对具体的应用程序 像ox这样的通用工具几乎帮不上什么忙。

用法

ox可以通过简单地提供令牌名称列表来构建lexer函数。 与相应的正则表达式关联:

importoxlexer=ox.make_lexer([('NUMBER',r'\d+(\.\d*)?'),('PLUS',r'\+'),('MINUS',r'\-'),('MUL',r'\*'),('DIV',r'\/'),])

它声明了一个令牌赋予器函数,该函数接收一个源代码字符串,并且 返回令牌列表:

>>> lexer('21 + 21')
[NUMBER('21'), PLUS('+'), NUMBER('21')]

当然,下一步是将这个令牌列表传递给解析器,以便 生成解析树。我们可以从映射中轻松地在ox中声明解析器 处理函数的语法规则。

每个处理程序函数从其对应的 语法规则并返回ast节点。在下面的示例中,我们返回元组 将我们的ast构建为类似lisp的s表达式。

binop=lambdax,op,y:(op,x,y)identity=lambdax:x

现在规则是:

parser=ox.make_parser([('expr : expr PLUS term',binop),('expr : expr MINUS term',binop),('expr : term',identity),('term : term MUL atom',binop),('term : term DIV atom',binop),('term : atom',identity),('atom : NUMBER',float),])

解析器获取一个令牌列表并将其转换为ast:

>>> parser(lexer('2 + 2 * 20'))
('+', 2.0, ('*', 2.0, 20.0))

ast使分析和计算表达式变得容易。我们可以 写一个简单的ev计算如下:

importoperatorasopoperations={'+':op.add,'-':op.sub,'*':op.mul,'/':op.truediv}defeval(node):ifisinstance(node,tuple):head,*tail=nodefunc=operations[head]args=(eval(x)forxintail)returnfunc(*args)else:returnnode

eval函数接收一个ast,但是我们可以很容易地将它与另一个 函数以接受字符串输入。(ox函数理解sidekick的 管道操作员。箭头运算符>>通过传递 将每个函数输出到箭头后面管道中的函数 方向)。

>>> eval_input = lexer >> parser >> eval
>>> eval_input('2 + 2 * 20')
42.0

我们可以在一个循环中调用这个函数,让一个漂亮的计算器只用 几行python代码!

defeval_loop():expr=input('expr: ')print('result:',eval_input(expr))

欢迎加入QQ群-->: 979659372 Python中文网_新手群

推荐PyPI第三方库


热门话题
实现接口方法时不允许java@Override   使用BuffereImage加载映像时java高ram使用率   java For循环混乱,为什么不是循环?   java Android网格视图字符串对齐问题   java如何将方法与比较类型的附加功能进行比较?   在Java Swing中放置JSepator后的间隙大小   java如何避免并发访问我的网站中的支付链接   java如何从现有的Unix服务器连接到FTP服务器?   Spring中的java用户相关bean定义   带有scribesjava库的wordpress Woocommerce REST API返回消费者密钥参数缺失错误消息   java我可以自动检测特定设备连接的串行端口吗?   Javafx棋盘游戏   java使用JTextPane显示HTML,支持SVG吗?   SpringBoot如何在java中将映射转换为实体对象?   如何使用java代码对xls文件进行密码保护   Java JPA(EclipseLink)如何在持久化实际实体之前接收下一个生成的值?   Javaservlet启动外部进程