首页 > 编程语言 >手写编程语言-实现运算符重载

手写编程语言-实现运算符重载

时间:2022-09-19 07:55:38浏览次数:102  
标签:函数 编程语言 int age 运算符 Person 重载

前言

先带来日常的 GScript 更新:新增了可变参数的特性,语法如下:

int add(string s, int ...num){
	println(s);
	int sum = 0;
	for(int i=0;i<len(num);i++){
		int v = num[i];
		sum = sum+v;
	}
	return sum;
}
int x = add("abc", 1,2,3,4);
println(x);
assertEqual(x, 10);

得益于可变参数,所以新增了格式化字符串的内置函数:

//formats according to a format specifier and writes to standard output.
printf(string format, any ...a){}

//formats according to a format specifier and returns the resulting string.
string sprintf(string format, any ...a){}

下面重点看看 GScript 所支持的运算符重载是如何实现的。

使用

运算符重载其实也是多态的一种表现形式,我们可以重写运算符的重载函数,从而改变他们的计算规则。

println(100+2*2);

以这段代码的运算符为例,输出的结果自然是:104.

但如果我们是对两个对象进行计算呢,举个例子:

class Person{
	int age;
	Person(int a){
		age = a;
	}
}
Person p1 = Person(10);
Person p2 = Person(20);
Person p3 = p1+p2;

这样的写法在 Java/Go 中都会报编译错误,这是因为他们两者都不支持运算符重载;

Python/C# 是支持的,相比之下我觉得 C# 的实现方式更符合 GScript 语法,所以参考 C# 实现了以下的语法规则。

Person operator + (Person p1, Person p2){
	Person pp = Person(p1.age+p2.age);
	return pp;
}
Person p3 = p1+p2;
println("p3.age="+p3.age);
assertEqual(p3.age, 30);

有几个硬性条件:

  • 函数名必须是 operator
  • 名称后跟上运算符即可。

目前支持的运算符有:+-*/ == != < <= > >=

实现

以前在使用 Python 运算符重载时就有想过它是如何实现的?但没有深究,这次借着自己实现相关功能从而需要深入理解。

其中重点就为两步:

  1. 编译期间:记录所有的重载函数和运算符的关系。
  2. 运行期:根据当前的运算找到声明的函数,直接运行即可。

第一步的重点是扫描所有的重载函数,将重载函数与运算符存放起来,需要关注的是函数的返回值与运算符类型。

// OpOverload 重载符
type OpOverload struct {
	function  *Func
	tokenType int
}

// 运算符重载自定义函数
opOverloads []*symbol.OpOverload

在编译器中使用一个切片存放。

而在运行期中当两个入参类型相同时,则需要查找重载函数。

// GetOpFunction 获取运算符重载函数
// 通过返回值以及运算符号(+-*/) 匹配重载函数
func (a *AnnotatedTree) GetOpFunction(returnType symbol.Type, tokenType int) *symbol.Func {
	for _, overload := range a.opOverloads {
		isType := overload.GetFunc().GetReturnType().IsType(returnType)
		if isType && overload.GetTokenType() == tokenType {
			return overload.GetFunc()
		}
	}
	return nil
}

查找方式就是通过编译期存放的数据进行匹配,拿到重载函数后自动调用便实现了重载。

感兴趣的朋友可以查看相关代码:

总结

运算符重载其实并不是一个常用的功能;因为会改变运算符的语义,比如明明是加法却在重载函数中写为减法。

这会使得代码阅读起来困难,但在某些情况下我们又非常希望语言本身能支持运算符重载。

比如在 Go 中常用的一个第三方精度库decimal.Decimal,进行运算时只能使用 d1.Add(d2) 这样的函数,当运算复杂时:

a5 = (a1.Add(a2).Add(a3)).Mul(a4);
a5 = (a1+a2+a3)*a4;

就不如下面这种直观,所以有利有弊吧,多一个选项总不是坏事。

GScript 源码:
https://github.com/crossoverJie/gscript

标签:函数,编程语言,int,age,运算符,Person,重载
From: https://www.cnblogs.com/crossoverJie/p/16706477.html

相关文章

  • Unicode 与编程语言
    编程语言中的Unicode因为Unicode可以给世界上大部分字符编码,因此大部分编程语言内部,都是使用Unicode来处理字符的。例如在Java中定义一个字符charc='中',这个字......
  • JAVA 方法(函数)的重载
    所谓方法重载,就是在同一个作用域内,方法名相同但参数个数或者参数类型不同的方法。publicclasstest1{publicstaticvoidmain(String[]args){//方法......
  • Java 的运算符
    Java语言支持如下运算符:算术运算符:+(加),-(减),*(乘),/(除),%(取余),++(自增),--(自减)操作符描述例子+加法-相加运算符两侧的值A+B等于30-减法-左操作数减去右操......
  • 终于实现了一门属于自己的编程语言
    前言都说程序员的三大浪漫是:操作系统、编译原理、图形学;最后的图形学确实是特定的专业领域,我们几乎接触不到,所以对我来说换成网络更合适一些,最后再加上一个数据库。这四......
  • 运算符
    基本运算符##算数运算符+-*/%++--//加减乘除取余自增自减##赋值运算符=##关系运算符><>=<===!=......
  • 肖sir__java运算符__06
    1.1算术运算符假设:intA=10,intB=20例子:+   加法   相加运算符两侧的值         A+B等于30-   减法   左操作数减去右......
  • 《跟着星仔学C语言》第二章 类型、运算符与表达式
    02_01002.1变量名2.2数据类型及长度2.1变量名/函数名  1.字母(含_)与数字组成的序列  2.不建议把_作为变量或者函数开头(可能会和编译器冲突,以微软的_strlwr函......
  • 4:运算符
    运算符运算符的分类算术运算符关系运算符逻辑运算符位运算符赋值运算符杂项运算符算术运算符#include<stdio.h>intmain(void){//算术运算符in......
  • 运算符优先级
    运算符优先级一元运算符里面的逻辑非优先级很高逻辑与比逻辑或优先级高......
  • 连接和三元运算符
    字符串连接符的运用:""+后面的连接,前面的运算;inta=10;intb=20;System.out.println(a+b""+);System.out.println(""+a+b);System.out.println(a+b+""+a+b);*/三元运算符:x......