仓库源文_一探特别的除.markdown)

简单回顾下。本项目是为了重现「木兰」编程语言编译器,刚开始原型搭建,与原始版本一样,用 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 行,还有相当距离。

下面打算对错误处理和行列号进行研究。

更多「木兰」相关技术文章,欢迎关注木兰编程语言知乎专栏。