A compiler for silang which is a new language.
Compiler = Flex + Bison + llvm
Si 是拍脑袋的产物,试验性的产品,现在仅仅出于最初级阶段。随时会变化。 糅合了 go, Java, C++, Lua 等语言中我觉得“爽”的部分,作为个人兴趣爱好实现,不必太过期待。
它应该有以下特点:
它应该可以和 c 较好的融合 也就是方便的调用 c 语言编译出来的库,并且用它编译的库,也可以被 c 语言较好的调用。 在后端采用 LLVM 的情况下,这个目标似乎不难实现。站在巨人的肩上,也可能让它更好的发展。
它应该可以和 c++ 较好的融合
语法上会比较接近 Java,抛弃所有 C/C++ 中的复杂部分,但引入一些高级特性。
编译型语言,编译的代码应该有较高的效率。所有的类型都是“可决”(在编译期完全确定)的。
简洁&优雅,表现力强,行尾推荐不加 ; 但不强制
原生的支持委托(或者说函数对象?)和闭包
原生的支持协程,多线程
原生支持 utf-8, unicode
支持国际化
可选的 GC
谨慎的支持操作符重载
使用一个简化的异常系统来处理错误。
支持模板
设计思想:
支持的内置类型:
名称 描述 符号 位宽
-----------------
boolean 无 1 bit
byte 无 8 bit
char 有 8 bit
short 有 16 bit
ushort 无 16 bit
int 有 32 bit
uint 无 32 bit
long 有 64 bit
ulong 无 64 bit
float 有 32 bit
double 有 64 bit
注:内部类型在函数中使用传值方式传递,其他类型传引用
其他类型:
class
interface
singleton // 单例
TRUE 真类型(用于推导)
FALSE 假类型(用于推导)
语言级别支持的内部类
Tuple 元组
String 字符串
Any 可变类型
Func 函数对象
Delay 延迟对象
Delegate 委托(保留)
使用 const 来定义常量:
const pi=3.1415 // 注意:常量不需要类型定义
以及枚举:
enum MyEnum{ red=0, blue=2 } // 枚举和 int 可以无缝转换
枚举使用时为:
MyEnum.red
Si 语言中除了内置类型(整数和浮点),其他类型都保存在指针中。
定义变量可以用 var 来自动推导,用右值推断类型,甚至允许先定义,使用时再推导。
var i=10
var a
if(..){
a = 30.0f
}
不过变量的类型是确定不变的。因此以下代码是错误的
var name = "Hei" // 这里 name 自动成为 String 类型
name = 15 // 而这里会出错,因为 name 不能改变类型
另外,内部变量有初始值。
除 singleton 外都可以定义成数组,数组有固定长度,有个默认打开的编译选项,会让越界访问抛出异常。 数组下标从0开始
int[] a=[ 0, 1 ] // 数组可以直接初始化
var array2=int[10] // 初始化一个空的数组
int v=array2[1] // 数组取值
// 通过内置的 size 成员函数来获取数组的长度
var len = a.size()
当数组作为函数参数时,可以指定大小,也可以不指定:
func myFunc( int[20] array ){ ... }
func myFunc( int[] array ){ ... }
另外数组也支持切片
int[] s=arr[startIndex:length]
需要注意的是,切片是引用,因此如果源改变了,切片同样会改。
Any类型可以保存任何值,并且保存类型信息。
Any 类型允许在运行期保存任何对象,可以判断它内部是什么类型。
Any i=20.0f // 保存一个 float
然后你可以这样用
assert( i is Any )
when(i){ // 用 when 关键字进行运行期类型判断
int : print(i) // 在这个分支,i 被自动转换为 int 类型
float : {
// do float
}
default : {
printf("unknown Type")
}
}
Type type=i.type // 获取类型(参见**反射**)
float x=i // 或者 (float)i 强制获取 float 类型的值
但是,如果取值时,类型不一致,并且不能进行隐含转换,将抛出一个异常,这个过程是运行期的。
判断类型时,如果用 is,就是编译期的,用 when 就是运行期的。
if 采用 c 的语法, 增强 switch。
switch 语法如下,支持多种数据以及多种比较,只要是测试相等就可以。冒号后如果是多行,需要大括号。
switch vegetable {
"celery": vegetableComment = "Add some raisins and make ants on a log."
"cucumber", "watercress": {
vegetableComment = "That would make a good tea sandwich."
comm : "多行需要大括号"
}
default: vegetableComment = "Everything tastes good in soup."
也可以使用范围
switch intValue {
1..10:
v="1 to 10"
20..30:
v="20 to 30"
}
Si 语言中支持元组,替代 C++ 中的 pair, tuple。Si 里的元组用圆括号括起来,必须从明确的值创建,并且创建后 不可修改 元组必须在创建时赋值,并且值不可更改。可以通过 := 来解构,或者使用[]取值,元组可以参与编译期运算。
var a=(1, "second", x) // 直接创建
print( a[0] ) // 取第一个值(注:[] 操作是编译期的)
if( a[1] is int ) print("yes") // 通过索引取类型
for( var i : a ){ // 通过 for 循环,在编译期解开元组
print( i )
}
var b0, b1 := a // 元组的自动解构,注意复制的是引用,变量数量可以比实际的少,但不能多。
int c0, float c1 := (10, 10.0)
var c=(0) // 这不是元组,c是 int 类型
另外,元组的成员也可以命名:
var tuple=( key=1, value=2 )
print( tuple.key )
支持 while, for 循环,c 的语法。但不支持逗号多定义,多步进,不支持 do-while。 另外支持 for-each 形式
int[] arrays = {10, 20}
for( var i : arrays ){
i = 30 // 将改变 arrays 的内容
}
作为一个语法糖,在循环内使用 free 语句释放迭代器,编译器会尝试调用容器的 remove 方法(如果有),将对象从容器中移除。remove 的返回值赋值给 i, 重新开始循环体 (continue)
for( var i : list ){
free i
}
特别的,如果列表里保存的是元组(比如 map ),可以使用自动解构的语法
for( var k,v : map ){ // 同样,注意复制的是引用,因此改变 v 会改变 map 内的值
map.remove(k) // 这里不能用 free 了
}
在 Si 里的字符串类是个内部类型,String,字符串类内部维护字符串编码,一般情况下内部使用 UCS-4 存放
String eng="Hello world!" // 纯英文字符串,直接创建为 utf-8 编码
String str="中文" // 包含非 ascii, 使用 UCS-4 编码
ushort aChar=str[0] // a='中', 直接取出,为 UCS-4 编码
String str( bytes, Charsets.utf8) ) // 通过 bytes 指定编码来创建
str.bytes() // 获取 byte,不指定编码,会默认返回 utf-8
也支持多行字符串:
string str="""Hello
^ World ^
! """
print(str)
输出:
Hello
World
!
需要特别注意的是,多行字符串每行前后的空格将被忽略。 想加入前后空格,需要使用 ^ 标记起始位置。
另外,字符串也支持切片。
String s=str[0:1] // 注意,这里不会复制内部字符串
字符串允许进行 + 操作,并且可以和整数、浮点数加,结果是字符串。
String s="" + 16 // 结果是字符串 "16"
s=R("Hello", "zhCN“) // 从资源文件获取字符串
未来支持字符串模板
注意:字符串内容不能更改。 如果需要可变字符串,使用另一个兼容类 StringBuffer。
支持大部分 C++ 操作符,但是不支持前置 --, ++。 支持 >>> 运算符(无符号右移)
函数这样定义
func first( int a ) : int {
return a
}
调用时:
var x = first( 10 )
并且为了保证定义的清晰,如果省略返回值的定义,就是表示不返回。 参数允许可变参数,不过必须定义在函数的最后,也只允许一个可变参数.
另外允许多返回值,这时返回值会被包装为一个元组。 多返回值函数定义方式如下,返回值可以匿名也可以命名,命名的返回值在函数内可以直接作为变量操作.
func second( int a, var b, int ... args ) : int retval, int val {
// 这里 args 被视为数组 array
if( args.size > 0 )
val = args[0]
retval = a + b // 直接操作返回值
return // 可以省略
}
注意,返回值如果命名,那么在函数开始,就会调用无参数构造函数构造返回值,如果没有无参数构造函数, 编译会失败,可以使用 ? 定义为可空类型。
单行函数(语法糖)
func add(int a, int b) = a + b
单号函数使用推导获取返回值,必定有返回值,因此也不需要 return。
可变参数可以是同一个类型的,或者使用 var 让它成为可变模板函数
func varFunc( int a, var ... args ){
for( var i : args ){ // 这个 For 会在编译期被展开
run(i)
}
}
这个 for 循环会在编译时被展开成顺序的多个代码块。
函数可以使用返回元组的形式来返回多个变量,并且你可以用自动解构。
first, second := fun( 10, 20 )
返回值如果有非命名参数,那么必须写在列表的前面
func second( int a, var b ) : int, float, int {
return 0, 15.0, 2
}
Si 语言中,传入的参数(包括 int 等内部参数)都被视为指针,因此它的内容可以在函数中可以被更改。
比如:
string old="Hello"
cut2(old)
print(old) // 输出 "Hell"
另外,如果参数可以为 null, 需要使用 ? 明确指出
func third( int? a ) : int? {
if(&a){
return a+1;
}
return null;
}
参数中可以使用 var 关键字,这时候它同样被视为模板函数。 返回值也可以使用 var,这时通过 return 来推断返回值
func myFunc( var a, int b ) : var ret{
return a+b // 这时推断返回值为 int 类型
}
int x=myFunc( 10, 20 )
函数使用比较宽松的调用形式,可以是顺序的,比如:
first, second = fun( 10, 20 )
或者命名的形式:
var v = fun( name="myname", value=20 )
也可以2者组合
fun( 10, 20, name="myname" )
但是有个约束,包括,顺序形式的必须在函数调用的开头。并且所有的参数(除非有默认参数)都必须充满。
下面的写法是非法的(除非第一个参数就是 name):
var v= fun( name="myname", 10, 20 )
Si 在语言级别支持函数对象、匿名函数
var add=func(int a, int b) : int{ return a+b } // 匿名函数
int a=add(10, 20) // 执行
如果没返回值,没有参数,都可以省略
var my1=func(int a){ ... } // 无返回值
var my2=func{ ... } // 无参数、无返回值的函数
var my3=func{ return 10 } // 返回值类型自动推导
func(int):float itIsAFunctor // 明确的类型
特别的,操作符 -> 后面可以省略 func, 另外,string 也支持 -> 操作符,并且在定义时就会被调用, 这个特性可以用来写“函数块”
"块的注释"->{
int a=0
}
推荐这样写函数块,以便让 IDE 实现函数内的块缩进。
同时,匿名函数是闭包的:
int myVal = 10;
var x=func(int a){ return a+myVal; } // 注意这个 myVal 是引用,在匿名函数调用时取*当前*值
assert( x(20)==30 );
上面的代码演示了简单闭包,不过要注意的是,并行的情况下,myVal 可能被更改、互斥,这时候使用闭包需要特别小心。 另外,匿名函数里包含的是对象引用,因此如果在匿名函数里修改 myVal 的值,当匿名函数被调用时,myVal 就会被更改。 这点也需要注意。
Si 支持的异常。
/// 语言内部异常,所有异常的基类
class Exception(
string resource; // 字符串资源 ID,默认会直接使用类名
}
// 定义一个异常
class IOException : Exception("IOException") // 定义一个新异常
class HttpException : IOException{ // resource="HttpException"
int code;
}
// 如果函数会抛出异常,那么必须要加 throws 描述
func( int ) : int
throws CodeException, HttpException {
if( false )
throw HttpException{ code=404 }
return 10
}
try{
var a=func( 10 )
}catch( HttpException | IOException e ){ // catch 允许多个异常类型
print( e.message )
}finally{
// 最后会执行的代码
}
// 简化的异常处理格式,对函数的异常直接处理
var i=func( 10 ) catch (IOException e){
String m = exception(e)
} catch(Exception e){
print(e)
}
func(10) catch(Exception); //明确忽略这个异常, 注意:分号不能省略
如果函数会抛出异常,那么必须处理,或者放函数 throws 签名中再抛出,或者用 try (包括简化语法) 处理。
某个默认打开的编译参数可以在运行时让空指针抛出 NullPointerException 异常,当然,这会略微的降低执行效率。 对于线程、协程,默认的处理方式就是输出错误日志,并退出线程。
?: 操作符可以在指针值为空时提供默认值
var a = mayNullOrInt() ?: 10
每个文件(.sc)可以定义一个类。 si 中的类类似 Java。成员变量、方法只有公开和包内的,公开的可以被包外访问, 否则只允许同名包或者子包内的方法访问。
包 org.first.second 是 org.first 的子包
代码开始
/* File MyCls.sc 开始 *
package org.jadedrip // 定义包
class MyCls { // 定义类
init(){ // 无参数的构造函数
}
init(int a, int .value){ // 带参数的构造函数,参数前带.可以省略 this.value=value 的代码
}
finalize{ // 析构函数始终是无参数的,不需要括号
}
clone{ // 克隆函数
return MyCls(){
this.key = clone(key)
this.value = clone(value);
};
}
int key = 1 // 创建时初始化(先于构造函数)
int value // 创建时默认为0
func do_something(){
this.key++ // this 是自己
}
protected: // 类中有且仅能有一个 protected 分割线,分割线上部的是公开的变量、方法,下部是
// 保护的,只有在同一个类、子类或继承类内可以访问
int? privateValue // 可为 null 的变量
}
class MyDataClass( int a, int b ){ // 这里直接定义了主构造函数、内部变量为 a, b
init(){ // 主构造函数不包含函数体,因此无参数构造函数仍然会被调用(赋值后)
}
}
继承和重载的概念被简化,类可以单一继承,并且不允许重载方法,当你需要一个可以重载的类、或者虚函数时,需要把它定义成函数对象。
class Base{
func cantOverload(){ // 普通函数不可以重载
}
func( int v ) virtualFunction // 定义一个函数对象,可以实现纯虚函数的功能
var canOverload = func(int v){ // 以函数对象的语法定义函数,可以通过替换函数对象的方法达到重载的目的
print(v)
virtualFunction(v) // 调用虚函数
}
}
class Second : Base{ // 类可以继承,但只能单继承
func cantOverload(){ // 这会是个全新函数
}
virtualFunction=func(int v){ // 实例之
print(v);
}
canOverload = func(int v){
canOverload(v) // 这可以视为调用基类函数
}
}
对象构造使用构造函数的形式,括号不可省略。
var a=MyCls(10, 20) // 通过构造函数构造,参数表使用逗号分割,当然可以 var 推断
var c=MyCls(){ // 在构造时,后面直接接大括号,将在构造后,直接执行语句块
key=0 // 在构造函数执行后执行
val=20
}
对象不构造时,可以认为是一个指针:
MyCls? a; // 这时 a 可以认为是指针,? 不可省略,表示对象被初始化为 null
a.var = 10 // 编译错误,构造前赋值
对象的传递,都是浅copy,除非明确指明复制
MyCls a=clone(b) // clone 会调用复制函数进行深 copy。
对象可以被理解为都保存在智能指针中, 指针的赋值需要使用等号,并且指针不能进行任何运算,包括比较。 而操作符重载不能重载等号。 除了 &, = 等指针专用符号,其他一切符号,都是对对象的操作(操作符重载)。
if( &x ){ // 指针判断是否为存在
x=Cs() // 这里会改变 x 指向的对象
}
if( x == null ){ // 注意这种写法并非判断x是否为空,而是内部对象和 null 比对(或重载的 == 方法)
}
var a = Cs()
Cs b = a // b 指向 a
b.val = 1 // 这里同时会改变 a 指向的对象值
类的成员变量支持 Set & Get 方法。
class MyClass{
int a
set a( int nv ){ // set, a=x 时调用
print("New value:" + nv)
a=nv
}
get a{ // 使用 var a=myClass.a 的代码时被调用
return a // 返回
}
}
MyClass x
x.a = 10 // 这里就会调用 set a
两个类互相引用对方的情况是被禁止的(即使是间接引用)。但可以定义子类。 子类只允许在类内被构造,并且可以访问父类的变量。
class MyCls3{
int var=0
class SubCls{
int subVar=2
func incVar(){
var+=subVar // 子类可以访问父类的成员变量
super.print() // super 是父类的指针
}
}
SubCls cls=SubCls()
func print(){
Console.print(cls.subVar)
}
}
默认情况下,类通过垃圾收集器/引用计数来管理内存。类可以包含析构函数,这时会使用引用计数的管理方式。 对于没有析构函数的类,默认使用 GC 来管理。
另外,支持手工管理,使用 new 关键字,new 生成的对象,必须使用 free 来释放
MyClass a=new MyClass()
free a // 析构、删除对象,并清空指针
assert( !&a )
a.name = "Hello" // 这里编译就会报错(使用未初始化的对象)
通过在对象后直接挂接代码块,可以以这个对象为“基准”来执行代码。区块中的所有函数、变量会先在这个 对象内部查找。
var i=a{
doSomething()
name = "hello"
}
这相当于:
var i=a
a.doSomething()
a.name = "hello"
除了默认类型转换,还可以使用下面的显式转换.
var a=(MyClass)b // 尝试将 b 转换为 a
(MyClass)b.funcInMyClass() // 先转换在调用
(MyClass)b{ // 转换在调用对象作用区
funcInMyclass()
}
但显式转换也是有限制的,比如你并不能吧 int 转为一个不兼容对象。
单例受到语言级别的支持。它的声明类似 class, 仅仅是把关键字 class 改为 singleton
////// 文件开始 ////////
package org.silang;
singleton MySingleton{ // 单例的公开定义部分
void AstCallit(){}
}
单例会在首次被使用时,被线程安全的初始化,并且直到程序关闭时被析构。
MySingleton.AstCallit()
当一样参数为可空的,那么就可以通过 null 来初始化。
var? k = int // 可以理解为 var? k = (int)null
k = 10
k = null
k = 20
可以通过把类中的成员函数,定义为 var 来创建模板类,模板类必须在构造时,能推导所有模板成员的类型。
class MyMap(var? k, var? v){ // 允许使用空指针来初始化
func templateFunc( var a ){ // 函数直接使用 var 来定义模板函数
if( a is int ){ // 这里的 is 是编译期的操作符,因此这里的 if 也是编译期的
// 只有 a 的类型是 int 类型的,这个代码块才会编译
a++
}
typeof(a) b? // b 定义为 a 相同的类型
Type c=typeof(a) // Type 是种描述类型的特殊类型
when( a ){ // 用 when 判断类型
int : {
}
float : {
}
default : {
}
}
}
}
var my = MyMap("Hello",9) // 构造函数必须可以推导所有类型 var you = MyMap(1.0, 9) // 注意:typeof(my)!=typeof(you) var my2 = MyMap(int, string) // 使用类型空指针来初始化
可以使用 def 来复制类型,可以认为是继承了一个新类
def AFun = func(int, int):int
def MapClass = MyMap(int, string) // 定义模板函数(会虚拟的调用构造函数,以推断类中的模板参数)
var m = MyMap( 10, "Hi" )
def IntMap = m // 可以理解为 def IntMap typeof(m) 的语法糖
def StringList = List(string) // 其实是 def StringList typeof(List(string))
StringList myList // 注意,myList 是 StringList 类型,而不是 List
需要注意的是,def 定义的类型是一个新类型,可以视为从原类型继承的类。
如果常量传入模板函数,那么函数在编译器,就会被计算
func myTemp( var a ) : var c{
if( a == 1 ){
return a+10
}else if(a=="Hello"){ // 传入 常量整数时,这分支都不会被编译
return "World"
}
}
myTemp( 10 ) // 在编译时不会生成函数,直接替换为 a+10
var x = 10
myTemp( x ) // 编译错误,传入的是变量,参数固定为 int
const y = "Hello"
myTems( y ) // 传入的是常量,按模板编译
Si 将支持一些类型操作符,结合模板,可以实现编译期编程。因此特别定义了两个内置类型·TRUE·和·FALSE·。
在编译期判断类型可以使用 is,语法是
A is B
结果将是内置类型 TRUE 或 FALSE。A 可以是变量,或者模板参数,B 为类型或接口。需要注意的是,这里并非要求 A,B 是完全一致的类型,当 B 是从 A 继承,或A实现了接口B,也将返回 TRUE。 但相对的,如果 A 和 B 都是模板类型,那么模板参数不一致(即使他们有继承关系),那也返回 FALSE
在编译期判断类型是否相等
A === B
B 必须为类型或接口,A 可以是变量,或者模板参数,和 is 相近,但 === 要求类型完全相等。
当 if 后的括号内,是一个类型,那么它就成为一个编译期的类型操作符,仅当类型为 FALSE 时,编译 else 语句块, 其他任何类型都编译 then 语句块。
if(a == int){ // 当 a 类型为 int 时被编译
int b = a+10
}else{
a = "Hello" // 虽然类型错误,但这里没编译,因此不会报错
}
反引号将尝试从类型中按名称取出成员,如果成员不存在返回 FALSE,存在返回成员的类型。 这可以在编译期判断类是否包含某个成员。
if( a`name` ) ...
if( a`myfunc` && a`myfunc` is func(int, string):string ) ...
for 可以用来解开通过 ... 传入的多个参数等,也可以解开元组,这个过程是编译期的。
func my( var ... attrs ){
for( var i: attrs ){
// 这里 i 的类型会按输入变化
}
var x=( 10, 20, "Hello")
for( var i: x ){
print i
}
}
当两个常量进行布尔运算,他们也会在编译期运算为 TRUE 或 FALSE。
var a=(1==1) // TRUE
Si 可以通过 interface 关键字定义接口,接口所有的方法、变量都是公开的。 接口内的函数可以有默认实现。
接口可以直接用在函数、方法的参数上,它可以用来约束函数、方法的参数, 它实现了部分需要模板的功能,只要类实现了接口里的所有方法和变量,并且他们是公开的,那么就视为他实现了该接口,可以传送给接受该接口的方法。
比如
interface MyInterface{
int a
func getSome():int // 这是个函数定义
}
void aFunc( MyInterface inc ){ // aFunc 实际上是一个模板函数。
// 这样,任何对象,只要包含 int a 的成员变量,以及 getSome 这样一个方法,
// 就可以被传入这个函数
}
需要注意的是,这是编译期的动作,接口被视为有约束的模板参数。 另外,比较简单的接口可以直接在参数中定义(匿名接口)
void aFunc( {int a; func getSome():int} inc ){
}
接口也可以用来明确的强制一个类去符合某种契约:
class MyCalss : Base, MyInterface{
}
由于函数的返回值可以通过 return 来推导,因此可以通过写一个完全静态的函数,来实现计算类型的模板函数。
func TplFunc( var? a, var? b ) : var{
if( a is int && b is int)
return TRUE() // 真类型,if( TRUE ) 永远真,并且在编译期就处理
else
return FALSE()
}
var a = 10, b=10
if( TplFunc(a,b) ){ // 静态语句,在编译期展开
int c = 10
}
si 的函数参数,允许使用延迟生成的技术以优化效率。它让参数仅在被首次使用的时候,才会被生成它。 比如:
trans_data( get_data(), x)
而 trans_data 的代码如下:
func trans_data( var v, bool x ){
if(x) print(v)
}
这段代码里,v 通过 get_data() 获取值,但在 trans_data 中,如果 x=false,v根本不会被使用。这个时候 get_data() 的调用 是完全没有必要的。而通过延迟生成技术,只有在v参数实际被使用时才会尝试“构造”它,因此,如果 x=false,get_data() 会被直接 放弃,生成的代码类似下面的
void trans_data( bool x ){
if(x) print( get_data() )
}
要启用延迟优化,调用代码写成如下
trans_data( *[get_data()], x)
但需要注意,延迟优化只用于模板函数,因为编译器实际上是生成了一个 Delay 内部模板对象,传参如果类型是定的,Delay 对象会立刻解开,不会延迟。
其实也不一定用在函数里
Delay x=*[get_data()] // 这时 get_date() 其实没有被执行
var k=x // 这时才会执行,并且只会执行一次
var k2=x // 注意,这里不会重复执行
创建线程由线程库支持,类似 Java
Thread thread(daemon=true, level=3)
thread->{ // 运行线程
aFunc()
}
thread.join() // 等待结束
而同时,Si 包含一个小的语言库,直接支持并行任务,并且尽可能自动维护。 在一个函数、区块调用前加上 go 关键字,这次调用就会在一个新的并行任务中并发执行。 当被调用的函数返回时,这个并行任务也自动结束。需要注意的是,如果这个函数有返回值, 那么这个返回值会被包装为 Chan。
go{ // 通过go来创建一个并行任务
print( "go" )
}
go dosomthing() // 并行执行函数
Barrier()->{ // 通过一个内存栅栏,保证并行项都执行完毕后才进行下一步
for( int i; i<10; ++i ) go{ // 并行任务
dosomething(i) // 这个函数会被并行执行(OpenMP)
}
}
Si 通过库来支持管道(Chan),以便实现异步数据交换。 管道可以输入输出多次,并且可取消。
Chan s=Chan(int)
go{ // 并行执行语句块
s <- i // 写入管道,这里会阻塞,直到值被取出
}
go{
sleep(1000)
if( s.cancel() ){ // 交换器可以取消,取消成功返回 true, 而如果管道已经被使用,那么返回 false
// 超时代码
}
}
可以明确的“等待异步线程事件”完成
var a = *s // 从管道取出值,这里会被阻塞,直到有值
或者异步等待
s->int a{ // 异步输出,回调函数会在有输入时被调用。另外,这是缩写(语法糖),完整的写法是 s->func(int a)
print a;
}
使用 go 调用的函数,如果有返回值,会自动包装为 Chan,并且返回值自动输入
func inThread( int a, int b ) : int s {
...
return a; //
}
Chan x=go inThread(10, 20);
x->var a{ // 异步返回的值写入 a 并执行后面的代码块
... // 需要特别注意的是,管道输出后执行的代码块,
// 是在**异步线程**内执行的
}
另外,管道输出只能选择一种,同时使用同步和异步取,会导致编译错误。
如果需要锁,代码如下
Mutex myMutex=Mutex() // 定义一个锁
myMutex -> {
// 锁内
} // 这里解锁
原子操作由库来支持:
var a=Atomic(10) // 原子的 int
int v=a++ // 原子的 +1 并返回新值
int cur=a.compareAndSet( 11, newValue );
Si 通过 import 导入包
import org.silang.net import ( // 导入多个 org.silang.math.sin org.silang.io.print )
注意:import 只能写在文件头部,package 定义之下
可以引入 c 或其他语言写的库会被编译成包,以便被 Si 调用。Si 语言可以输出标准 c 函数,以便被其他语言调用。 lib 文件、需要对应的头文件及适配文件等需要的东西,会被某个处理软件打成一个包,并放在编译程序能找到的地方。 而 Si 可以编译成 c lib, 函数会按约定转换成 c 的格式。
交叉类型是将多个类型合并为一个类型。 这让我们可以把现有的多种类型叠加到一 起成为一种类型,它包含了所需的所有类型的特性。
var a = (MyClass & Second)() // 实际类型为 Intersection<MyClass, Second>
a 可以直接使用 MyClass 和 Second 内的属性,方法,特别的,如果 MyClass 和 Second 中有重复的属性或方法,可以这样使用:
(MyClass)a.funcInMyclass()
var b = (MyClass)a
Si 支持有限的操作符重载。对于类内的操作符,可以通过一个函数重载
class MyClass{
operator + ( int right ){ // 二元操作符的函数重载(默认的返回 MyClass 类型)
return this
}
operator ++{ // 自增在后
// return this 可省略
}
}
operator + ( int left, MyClass cls ) : MyClass{ // MyClass 在操作符的右边
}
si 支持注解及反射。(抄 Java)
class MyClass{
@ReflectName( name="value", idx=12 ) // 使用注解对象来进行注解
int a ;
void doFun(){
}
}
Package pkg=Reflect.packages["org.example"]; // 获取包
Type cls=typeof(MyClass); // Type 描述类型
Type cls=Reflect.classes["org.example.MyClass"]; // 通过全名获取
Type cls=pkg.classes["MyClass"]; // 通过包获取对象
for( string name, field v : cls.fields ){
var annotation=v.annotations; // 获取注解
}
注:Reflect 其实是一个语言支持的单例,因此如果程序中没有使用到它,那么它并不会初始化。
@Clang("my_c_object")
class MyCObject
{
func at( int idx ) : char; // int my_c_object_at( my_c_object* me, int idx );
@Clang("add")
operator + ( MyCObject other ) : MyCObject // my_c_object* my_c_object_add(my_c_object* me, my_c_object* other);
}
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。