共计 2132 个字符,预计需要花费 6 分钟才能阅读完成。
前言
先带来日常的 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
运算符重载时就有想过它是如何实现的?但没有深究,这次借着本人实现相干性能从而须要深刻了解。
其中重点就为两步:
- 编译期间:记录所有的重载函数和运算符的关系。
- 运行期:依据以后的运算找到申明的函数,间接运行即可。
第一步的重点是扫描所有的重载函数,将重载函数与运算符寄存起来,须要关注的是函数的返回值与运算符类型。
// 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
}
查找形式就是通过编译期寄存的数据进行匹配,拿到重载函数后主动调用便实现了重载。
感兴趣的敌人能够查看相干代码:
- 编译期:https://github.com/crossoverJie/gscript/blob/ae729ce7d4cf39fe115121993fcd2222716755e5/resolver/type_scope_resolver.go#L127
- 运行期:https://github.com/crossoverJie/gscript/blob/499236af549be47ff827c6d55de1fc8e5600b9b3/visitor.go#L387
总结
运算符重载其实并不是一个罕用的性能;因为会扭转运算符的语义,比方明明是加法却在重载函数中写为减法。
这会使得代码浏览起来艰难,但在某些状况下咱们又十分心愿语言自身能反对运算符重载。
比方在 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