简单回顾下。本项目是为了重现「木兰」编程语言编译器,刚开始原型搭建,与原始版本一样,用 Python3 和 RPly 实现,照例使用中文命名标识符。
之前实现了加法和打印输出。此文实现整数的減、乘、除,其中“除”的语义与 Python3 不同。
比如print(2+3*4/5)
在「木兰」中输出 4,而不是像 Python3 中输出的 4.4。
可以想见,「木兰」的除法中,如果俩数是整数,除法结果就仍是整数,而小数部分直接舍去。
而 Python3 无论是否相除的俩数是否整数,都会返回小数部分(如果有的话)。
在进入实现细节之前,想先尝试猜度一下这种设计的思路。刚搜了下 Python2 的除法语义和「木兰」是一致的。而 Python3 新加了一个//
达到舍去小数的目的。
估计「木兰」设计者也是参考了两种设计,最后选择了 Python2 的设计。从逆向工程看,「木兰」实现用的是 Python3。从后面的实现细节可以看出,是额外添加了一点代码才实现了 Python2 的除法。
相比之下,个人的理解是,Python3 添加一种运算符的一个好处是能让语义更直观,省得除法结果依赖于操作数是否是整数,因为这不一定那么好确定。而 Python2 的“整数除法得整数”这一层语义在实际应用中的用途感觉并不那么广泛。即使在需要实现这一功能时再自行实现也就是调用个floor
函数的事。
那么为何仍选择用 Python2 的除法设计,而不使用//
运算符呢?使用手册第一趴中可见,「木兰」选择了用//
和/* */
作为注释标志。相信这是为了与市面上占大头的 C 系列、Java、JS、PHP 等等编程语言的语法靠近。
语言设计时的各种取舍可见一斑。
比较简单,在之前的基础上,加减乘部分与最上面的 RPly 入门示例大致相同这里不重复。下面是除法运算的相关部分。
语法分析部分:
@分析器母机.production('二元表达式 : 表达式 除 表达式')
def 除法(片段):
return 语法树.调用(
函数=语法树.名称(
标识='__除__',
上下文=(ast.Load()),
行号=0,
列号=0),
参数=[片段[0], 片段[2]],
行号=0,
列号=0)
行号列号还是全 0,比较刺眼,会尽快研究。下面是除法内置函数:
def __内置_除(a, b):
if isinstance(a, int):
if isinstance(b, int):
return math.floor(a / b)
return a / b
的确就是用了floor
进行了特殊处理。
完整代码在此,语法分析部分已上升到 85 行,相比「木兰」的 1400 行,还有相当距离。
下面打算对错误处理和行列号进行研究。
更多「木兰」相关技术文章,欢迎关注木兰编程语言知乎专栏。