我正在为一种语言编写一个解释器,其中函数可以用作运算符.但是,函数内容只能在运行时知道.
为此,我考虑了两个解决方案:
>使用函数的运行时信息在运行时完成解析
>所有用户定义的运算符都使用默认值来表示优先级和关联性.
我选择了后者,因为我看到了分别解析到执行的许多优点.
现在谈到实现,我有兴趣看看有哪些选项.我最初的想法是一个shift reduce parser,但我在构造解析器方面没什么经验.
例:
LHS op RHS : LHS * RHS /* define a binary operator 'op' */
var : 3 /* define a variable */
print 5 op var /* should print 15 */
LHS op RHS : LHS / RHS /* Re-define op */
print var op var /* Should print 1 */
在最后一种情况下,解析器将从词法分析器获取:“id id id id”.只有在运行时我才知道’op’id是一个运算符.
最佳答案 (根据要求发布评论结果.)
解决方案#1绝对是丑陋的,实现起来很复杂,而且不需要,我同意.解决方案#2到目前为止更容易实现和理解.您也可以允许运算符的自定义关联性和优先级,只要这些是静态已知的.主要的是这些事实在解析时是已知的.
至于实际解析,大多数解析器都可以正常工作,因为围绕id的任何两个表达式都是自定义中缀运算符的应用程序(如果允许自定义优先级和关联性,则不太正确,在这种情况下,您需要一个允许确定的算法在解析时基于每个操作符的那些).无论哪种情况,我个人最喜欢的是“Top Down Operator Precedence Parser”或Pratt解析器.我发现以下资源(按照对我有用的顺序,YMMV)描述得很好:
> Simple Top-Down Parsing in Python
> Pratt Parsers: Expression Parsing Made Easy
> Top Down Operator Precedence
算法的两个属性使它非常适合这个问题:
>对每个标记动态发生关联性(“绑定能力”)的查找(允许解析器允许用户为其运算符定义优先级).
>手写[*]非常简单,你可能不得不这样做,因为这种程度的动态超出了大多数(至少我所知道的)解析器生成器的范围.
[*]我亲自为一个非常大的(只缺少案例,多维数组,也许是一些模糊的细微之处)编写了一个解析器,它包含了500行Python中的Pascal和2-3天的工作,剩下的只是因为其他它当时使用的软件部分更有趣,我没有理由实现其余部分.