Oak:一种由脑力激发的技术提供动力的无限便携语言

2020-07-26 18:34:22

对于你们中那些还记得免费的人来说,Oak本质上是该项目的一个更健壮、更高水平的版本。Oak的目标是在前端尽可能高级别,但在后端尽可能小和低级别。

我是一名应届高中毕业生,也是大学一年级学生,正在找工作。如果你喜欢我的项目,可以考虑给我买杯咖啡来支持我!

橡树令人疯狂的可移植性的关键是其令人难以置信的紧凑的后端实现。Oak后端的代码可以用不到100行的C语言表示,这样的小实现是可能的,因为中间表示的指令集很小。橡树的IR只由13个不同的指令组成。那和脑筋急转弯不相上下!

Oak的后端功能非常简单。每条指令都在存储磁带上运行。该磁带本质上是双精度浮点数的静态阵列。

设x:num=5.25;…。让p:&;num=&;x;`堆开始`|v[0,0,0,5.25,0,0,0,0,3,0,0,0,0,...]^|`堆栈指针的当前位置`。

定义变量时,它会在内存磁带上指定一个静态位置。然后,编译器只需在其余代码中将变量替换为其地址!

另外,存储带起堆栈和堆的作用。在为程序的所有变量分配空间后,用于堆栈的内存开始。堆栈随着整个程序中的数据增长和缩小:例如,当两个数字相加时,它们将从堆栈中弹出并替换为结果。类似地,堆在整个程序中都会增长和缩小。但是,堆用于动态分配的数据:在编译时内存占用量未知的信息。

从堆栈中弹出两个数字。从第二个中减去第一个,然后推送结果。

从堆栈中弹出两个数字。将第二个除以第一个,然后推送结果。

从堆栈中弹出一个数字,并返回指向堆上该数量的空闲单元的指针。

从堆栈中弹出一个数字,然后转到该数字在内存中指向的位置。从堆栈中弹出另一个数字,并释放内存中此位置的那么多单元格。

从堆栈中弹出一个数字,然后转到该数字在内存中指向的位置。然后,将弹出大小数字从堆栈中取出。将这些数字以相反的顺序存储在内存中的此位置。

从堆栈中弹出一个数字,然后转到该数字在内存中指向的位置。然后,将大小数目的连续存储单元压入堆栈。

开始一个While循环。对于每个迭代,从堆栈中弹出一个数字。如果数字不是零,则继续循环。

仅使用这些指令,Oak就能够实现比C语言所能提供的更高级别的抽象!这听起来可能不是很多,但对于这么小的一门语言来说,它是非常强大的。

橡树中的结构与其他语言中的结构不同。对象本身只是内存单元的数组:它们没有任何成员或属性。结构通过使用返回其";成员";地址的方法以独占方式检索它们的数据。然后,这些方法被展平为简单的函数。因此,putnumln(*bday.day)变成putnumln(*date::day(&;bday))。这是一个相当简单的过程。

由于Oak';的中间表示的结构,必须知道每个表达式的类型才能继续编译。编译器梳理每个表达式,找出其类型的大小。从这里开始,代码的表示形式如下所示:

//`3`是堆栈fn date::new(月:1,日:1,年:1)->;3{月;日;年}//self是指向大小为`3`fn date::day(self:&;3)->;&;1{self+1}fn main()->;0{let bday:3=date::new(5,14,2002);}的指针。

在汇总所有静态分配的数据(如变量和字符串的总内存大小)之后,程序会抢先在堆栈上预留适当的内存量。这实质上意味着立即移动堆栈指针,以便为程序开始时的所有数据腾出空间。

大多数表达式都非常简单:函数调用只是以相反的顺序将它们的参数推到堆栈上,并根据它的ID调用函数,对变量的引用只是将它们在堆栈上分配的位置作为一个数字推入,等等。然而,方法调用有点棘手。

在许多不同的情况下,方法调用是有效的。方法始终将指向结构的指针作为参数。但是,调用方法的对象不一定是指针。例如,下面的代码是有效的:让bday:date=date::new();bday.print();。变量bday不是指针,但是仍然可以使用方法.print()。这就是为什么。

当编译器看到扁平化的方法调用时,它需要找到一种方法将实例表达式转换为指针。对于变量,这很简单:只需添加一个引用!比如已经是指针的表达式,那就更简单了:什么都不要做!不过,对于任何其他类型的表达来说,它都要稍微冗长一些。编译器隐藏一个隐藏变量来存储表达式,然后使用该变量作为实例表达式再次编译方法调用。很酷,对吧?

由于橡树的红外辐射非常小,它可以支持多个目标。更好的是,添加目标非常容易。在橡树的板条箱里,有一种叫塔吉特的特性。如果您使用Target特征为您的语言实现IR的每一条指令,那么Oak可以自动编译到您的新编程或汇编语言!是的,它和听起来一样简单!

//在此目录中包含另一个文件的标志//如果此文件包含";main";方法,它将被覆盖。#[include(";str.ok";)]//设置用于堆的确切内存单元数的可选标志。//这使得Oak成为一种非常适合嵌入式开发的语言!#[heap(128)]//这里的`1`是堆栈类型bool(1){fn true()->;bool{1}fn false()->;bool{0}fn val(self:&;bool)-&;&;num{self}fn not(self:&;bool)->;bool{self}fn not(self:&;bool)->;bool{1}fn not(self:&;bool)->;bool{0}fn val(self:&;bool)->;Bool{if*self{bool::false()}Else{bool::true()}}fn main(){putnumln(Square(5));let b:bool=bool::false();putboolln(B);//赋值给b';s";val";属性b->;val=1;putboolln(B);b=bool::true()。//分配32个单元格let addr:&;char=alloc(Size);//释放这32个单元格空闲addr:size;}fn putbool(b:bool){if b{putstr(";true";)}Else{putstr(";false";)}}fn putboolln(b:bool){putbool(B);putchar(';\n&。}//函数可以独立排序fn square(x:num)->;num{putstr(";平方数字);Putnum(X);putcharln(';\';';);//主体中的最后一条语句不需要方括号x*x}

Fn Fact(n:num)->;num{if n-1{n*Fact(n-1)}Else{1}}fn main(){prn!(Fact(5))}。

Void fn0(MACHINE*VM);void fn1(MACHINE*VM);void fn0(MACHINE*VM){MACHINE_PUSH(VM,0);MACHINE_STORE(VM,1);MACHINE_PUSH(VM,0);MACHINE_LOAD(VM,1);MACHINE_PUSH(VM,1);MACHINE_SUBRACT(VM,1);MACHINE_PUSH(VM,2);MACHINE_STORE(VM,1);MACHINE_PUSH(VM,1);MACHINE_LOAD(VM,1);WHILE(MACHINE_POP(VM)){MACHINE_PUSH(VM,0);MACHINE_LOAD(VM,1);MACHINE_PUSH(VM,0);MACHINE_LOAD(VM,1);MACHINE_SUBTRACT(VM);fn0(VM);MACHINE_Multiply(VM);MACHINE_PUSH(VM,0);MACHINE_PUSH(VM,1);MACHINE_STORE(VM,1);MACHINE_PUSH(VM,0);MACHINE_PUSH(VM,2);MACHINE_STORE(VM,1);MACHINE_PUSH(VM,1);MACHINE_LOAD(VM,1);}MACHINE_PUSH(VM,2)。WHILE(MACHINE_POP(VM)){MACHINE_PUSH(VM,1);MACHINE_PUSH(VM,0);MACHINE_PUSH(VM,1);MACHINE_STORE(VM,1);MACHINE_PUSH(VM,0);MACHINE_PUSH(VM,2);MACHINE_STORE(VM,1);MACHINE_PUSH(VM,2)。}}void fn1(MACHINE*VM){MACHINE_PUSH(VM,5);fn0(VM);prn(VM);}int main(){MACHINE*VM=MACHINE_NEW(3,515);fn1(VM);MACHINE_DROP(VM);return 0;}。

对于这样一个小程序来说,这是相当多的输出代码。我们的代码是怎么变成这样的?首先,我们的事实函数被重命名为fn0。

//`N`存储在地址0//存储地址0的MACHINE_PUSH(VM,0);MACHINE_STORE(VM,1);

然后,通过加载值n、按下值1并执行减去函数来计算n-1。

//`N`存放在地址0//从地址0开始加载一个数字:MACHINE_PUSH(VM,0);MACHINE_LOAD(VM,1);MACHINE_PUSH(VM,1);MACHINE_SUBTRACT(VM);

将n-1的结果存储在一个变量中,以便在";If Else&34;语句代码中使用,并将1存储在另一个变量中,以确定";Else&34;分支是否将运行。

//将`n-1`存放在地址1的MACHINE_PUSH(VM,1);MACHINE_STORE(VM,1);//将`1`存放在地址2的MACHINE_PUSH(VM,1);MACHINE_PUSH(VM,2);MACHINE_STORE(VM,1);

//以MACHINE_PUSH(VM,1);MACHINE_LOAD(VM,1);//开始WHILE循环WHILE(MACHINE_POP(VM)){//加载`N`MACHINE_PUSH(VM,0);MACHINE_LOAD(VM,1);//将`n-1`推送到堆栈MACHINE_PUSH(VM,0);MACHINE_LOAD(VM,1);MACHINE_PUSH(VM,1);MACHINE_SUTRACT(VM);//用`n-1`fn0(VM)调用`fact`;//将`act(n-1)`的结果乘以`N`MACHINE_MULPLY(VM);//在WHILE循环的条件变量中存储零//停止if语句的Body循环MACHINE_PUSH(VM,0);MACHINE_PUSH(VM,1);MACHINE_STORE(VM,1);//在";ELSE";分支条件//因此ELSE分支将不执行MACHINE_PUSH(VM,0);MACHINE_PUSH(VM,2);MACHINE_STORE(VM,1);//这将加载WHILE循环的条件,//该条件已设置为零。MACHINE_PUSH(VM,1);MACHINE_LOAD(VM,1);}//结束IF大小写。

//加载";Else";案例条件变量。//如果执行";IF";案例,则这是零MACHINE_PUSH(VM,2);MACHINE_LOAD(VM,1);//BEGIN ELSE CASE条件分支WHILE(MACHINE_POP(VM)){//将1推入堆栈MACHINE_PUSH(VM,1);//将零存储在IF案例条件变量MACHINE_PUSH(VM,0);MACHINE_PUSH(VM,1);MACHINE_STORE(VM,1);//在ELSE CASE条件变量MACHINE_PUSH(VM,0);MACHINE_PUSH(VM,2);MACHINE_STORE(VM,1)中存储零;//这将加载WHILE循环的条件,//该条件已设置为零。MACHINE_PUSH(VM,2);MACHINE_LOAD(VM,1);}