运算符优先级相关问题

larva运算符系统大体上抄python,不过也有一些区别,简单总结一下运算符和表达式解析相关的问题

问题一:
没有实现乘方运算**,原因也简单在编译器的注释里面写了,**用得不多,是唯一一个右结合的二元运算,即a**b**c等同于a**(b**c),不留心会出现问题
另外,**运算和单目运算关系较为复杂,比如-a**b等同于-(a**b),根据python的文档,**运算优先级比单目运算符高(+,-,~),但是,如果只规定这点,则表达式a**-b就会解析出错,所以后面又增加一个说明,当单目运算符出现在指数域的时候,乘方运算的优先级略低于单目运算,我不太清楚文档如此写是否有一些特殊含义,不过我觉得只要规定**和单目运算符优先级同级,且右结合就行了,好像也没什么问题
不过,在larva中还是去掉了**,代之以内建的pow函数

问题二:
也是单目运算符优先级的问题,python规定了最高优先级是列表[……]、字典{……}、带括号的tuple构造(……)、及repr表达式`……`,不过这些都可以看做是一个被括起来的大表达式,所以说它们优先级最高,实际和说括号优先级最高没啥区别
其次的优先级是下标、调用、取属性三种,且左结合,这也是没有什么问题的,因为流行的语言,比如C就是这么规定(C还有->运算)
由于去掉了**不讨论,因此接下来就是单目运算了,有三种单目运算,取正(+)、取负(-)、取反(~),问题就在于和C语言单目运算的一个差异,逻辑非运算在C中运算符是“!”,且优先级和+-~三种一样,但是python将其修改为not运算,且运算优先级降低到关系运算符之下,例如python中:
not a > b
在C中就必须写成:
!(a > b)
即需要强制加括号
这样做的原因,我猜是因为在大多数情况下,需要对一个表达式结果取反,而不是一个单独的变量,为了省掉不必要的括号,python将符号“!”改成关键字not,使得表达式看起来更稀疏(因not后不加括号的话需要一个空格),其实还有一个地方体现了这点,就是python将位运算优先级提高到了比较运算之上,例如:
a & 0x100 == 0
在C中就得写成:
(a & 0x100) == 0
C语言这个坑还是挺大的,很多人都掉过,我估计是历史原因

由于not是一个单目运算符,但优先级又比较低,所以问题就来了,如果not混在比它高的双目运算中,对于编译和表达式理解就会造成困扰,例如:
1 + not 2
编译器在解析这个表达式的时候,当not入运算符栈,发现里面已经有+了,就弹出,但此时运算分量栈里面只有一个1,结果编译器就出现内部错误了
这个表达式其实跟上面a**-b一个道理,不过python对not就没像对**和单目运算那么客气开小灶,直接报了个语法错误,写这个表达式的人大概认为这样没有歧义(的确是没有),但是在复杂一点的表达式里面就会比较乱,比如对上面的式子加点料:
1 + not 2 + 3
这里,not究竟对2做运算,还是对2+3,就是一个比较困扰的问题,若是前者,则相当于not在不同情况优先级会变化(幅度还比较大,因为几乎所有二元运算都比它高),而如果是后者,则相当于not的存在改变了同优先级二元运算的结合性,若3后面还有+4+5,表达式比较长的时候,可读性就很差了
所以,对于not夹在二元运算中间的情况,python统统认为是语法错误,若真有上面第一个式子这种需求,就请程序员自己加括号了
这个问题最后比较重要的一点是,如何检测这种情况,larva编译器的实现算法是:当not运算符在入栈时,若栈顶运算符比它优先级高,则直接报“'not'位置错误”的语法错误

P.S.在larva中,比not优先级低的运算只有and、or和逗号,不过逗号没有进入正常的栈解析器,而是用代码特殊处理了

问题三:
如果设计过一个特性比较多的现代语言,可能会对关键字或符号重用有一定感触,简单说就是键盘上的常用符号不够用,最典型的就是C++和java的泛型语法的尖括号,以及附带的反尖括号和右移运算符的分析问题
虽然这个问题其实也不算是问题,我们可以用双字符甚至三字符的符号来扩充,比方说,->连起来看着像一个箭头,可以用作C中的指针指向属性运算,而且还可以创造关键字,不过还是需要考虑习惯问题,比如我曾考虑过用这种语法:(x,y)->(x*y)在larva中实现lambda,但后面还是放弃,使用了lambda x,y:x*y这种,原因在于,->作为C语言中的一个经典运算符已经被很多人所熟知,新创造一个语法反而容易造成歧义,而且过分符号化也会让代码看着比较乱(想想perl),因此还是采用了python的lambda形式
扯这么多,其实larva在这方面只碰到了一个问题:就是in操作符的复用问题,in作为一个操作符,可以判断容器中是否包含一个元素,但同时在for语句中,它是一个关键字,而for语句中的in左右两边又都是表达式,所以一开始我的编译方式是这样(for语法是for LVALUE in EXPR:):
1 发现for关键字,启动for语句解析流程,先跳过for
2 解析一个表达式树e
3 检查并跳过符号“:”
4 检查e的操作符,必须是“in”
5 e的左右操作数则为上述LVALUE和EXPR
接着往后就是正常流程,比如LVALUE的左值检查,循环体的解析等
也就是说,对于for语句后面到“:”开始的内容,我将其视为一个in表达式来解析,但后面发现这样做虽然运行没有问题,若碰上如下代码:
for i in (1, 2, 3):
这个括号就比较烦人,若不写括号,则上述第四步就报错,因为in比逗号优先级高,for后面的表达式成了一个tuple
虽然只是一个小问题,不过我还是觉得不爽,在python中是可以直接写for i in 1,2,3:的,所以后面对这个流程做了修改,解析LVALUE的时候,碰到in就返回,然后跳过in,继续解析EXPR
P.S.不用担心for a[b in c] in d:这种情况,因为b in c在括号中,是递归调用解析过程解析普通表达式,而“碰到in返回”的规则只用于解析LVALUE当前的过程

不过,对于如下代码,具体实现还是和python有区别:
if i in 1, 2, 3:
这个代码,python会报语法错误,因为从语法上说,if后面的表达式是一个tuple,但上面的代码也非常可能是程序员想写if i in (1,2,3):,为了避免歧义可能造成的问题,python特殊照顾了一下,即这种情况强制要求程序员加括号,尽管从语法上说它没有任何问题
larva没做到这一点,上述代码会严格按照语法来解析,即if后面是一个tuple表达式,如果要照顾粗心的程序员们,就需要特殊处理

问题四:
和问题三类似,对于逗号这个符号,它既可以是一个组建tuple的运算符,也可以是语法的一部分(如函数参数分隔,print语句各参数分隔,构造列表和字典时的分隔等)
处理方法也和上述for ... in ...:类似,在解析特定表达式的时候,指定碰到逗号就返回。不过在逗号这个问题上,采用更严格的处理方式,比如构造列表的时候,解析key的表达式就需指定,否则碰上这种情况:
{1, 2 : 3}
会解析为{(1,2) : 3},因此需要在解析完1就返回,然后在检查“:”的时候报错(larva中{}只是dict,没有引入构造set的语法,set需要显式使用set(...)构造)
类似的,在解析下标时,也指定了碰到逗号返回,这点比python更严格些,比如:a[1,2]会报错,需要显式写为a[(1,2)](python中则不需要)
lambda表达式比逗号运算优先级高,所以lambda x : x, 1是一个tuple,lambda x : (x, 1)是一个lambda
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值