C编程语言的无限便携式替代方案。

对于那些记住“免费”的人来说,橡木本质上是该项目的更强大,更高的版本。橡木的目的是在前端尽可能高,但在后端中尽可能小和低水平。
我是大学毕业的高中毕业生,并在大学里寻找工作。如果您喜欢我的项目,请考虑通过给我买咖啡来支持我!
橡树疯狂可移植性的关键是它非常紧凑的后端实现。橡木后端的代码可以在1个以下的1行中表达。仅由于中间表示的微小指令集,因此可以实现这一较小的实现。 Oak的IR仅由17种不同的说明组成。那与Brainfuck相当!
橡木的后端功能非常简单。每个说明都在存储胶带上运行。该胶带本质上是双精度浮子的静态阵列。
let x : num = 5.25 ; ... let p : & num = & x ; `beginning of heap`
| | |
v v v
[ 0 , 0 , 0 , 5.25 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 3 , 0 , 0 , 0 , 0 , 0 , 0 , ... ]
^
|
`current location of the stack pointer`当一个函数中定义变量时,相对于虚拟机当前的基本指针,它给出了静态位置。因此,当调用函数时,将在堆栈上分配函数变量的空间,并且基本指针会增加以使用此新空间。然后,编译器仅将变量替换为其在代码其余部分中的基本指针偏移量中添加到基本指针偏移!
此外,内存胶带可作为堆栈和堆的功能。为所有程序变量分配了空间后,用于堆栈的内存开始。堆栈在整个程序中随着数据而增长和收缩:当将两个数字求和时,将它们从堆栈中弹出并替换为结果。同样,堆在整个程序中都会增长和收缩。但是,堆用于动态分配的数据:在编译时未知的内存足迹的信息。
现在,您了解了Oak的后端从根本上运作,这是完整的指令集!
| 操作说明 | 副作用 |
|---|---|
push(n: f64); | 将数字推到堆栈上。 |
add(); | 从堆栈中弹出两个数字,然后推出他们的总和。 |
subtract(); | 将两个数字从堆栈中弹出。从第二个减去第一个,然后推动结果。 |
multiply(); | 从堆栈中弹出两个数字,然后推出他们的产品。 |
divide(); | 将两个数字从堆栈中弹出。将第二个除以第一个,然后推动结果。 |
sign(); | 弹出堆栈的数字。如果它更大或等于零,请按1 ,否则按-1 。 |
allocate(); | 弹出堆栈的数字,然后将指针返回到堆上的该数量的自由单元。 |
free(); | 弹出堆栈的数字,然后转到此数字在内存中指向的位置。将另一个数字从堆栈中弹出,并在此位置中释放许多单元格。 |
store(size: i32); | 弹出堆栈的数字,然后转到此数字在内存中指向的位置。然后,堆栈中的流行size数量从堆栈中删除。以相反顺序存储在此位置中的内存中。 |
load(size: i32); | 弹出堆栈的数字,然后转到此数字在内存中指向的位置。然后,将连续存储单元的size推到堆栈上。 |
call(fn: i32); | 通过编译器分配的ID调用用户定义的函数。 |
call_foreign_fn(name: String); | 在源中以其名称调用外国功能。 |
begin_while(); | 开始一个时循环。对于每次迭代,请弹出堆栈的数字。如果数字不是零,请继续循环。 |
end_while(); | 标记一个时循环的末端。 |
load_base_ptr(); | 加载已建立的堆栈框架的基本指针,该指针始终小于或等于堆栈指针。变量相对于每个函数的基本指针存储。因此,定义x: num和y: num , x的函数可能存储在base_ptr + 1中, y可能存储在base_ptr + 2中。这允许函数动态和根据需要将变量存储在内存中,而不是使用静态内存位置。 |
establish_stack_frame(arg_size: i32, local_scope_size: i32); | 从堆栈中弹出arg_size数量的细胞数量,然后将它们存储起来。然后,在此功能结束时调用load_base_ptr恢复父堆栈框架。将local_scope_size零数量的零数量推到堆栈中,以腾出空间来为函数的变量腾出空间。最后,将存储的参数单元重新推回了堆栈中,因为它们最初被订购。 |
end_stack_frame(return_size: i32, local_scope_size: i32); | 从堆栈中弹出return_size数量的单元格数,然后将它们存储起来。然后,pop local_scope_size size clack of stack of stack of stack of stack框架的内存。从堆栈中弹出一个值,然后将其存储在基本指针中以恢复父堆栈框架。最后,将存储的返回值单元推回最初订购的堆栈中。 |
仅使用这些说明,Oak能够实施比C所能提供的更高的抽象!!!这听起来可能并不多,但是对于一种很小的语言来说,它非常强大。
橡木的语法受到生锈的编程语言的启发。
函数用fn关键字声明,并且在句法上与RUST函数相同,但return语义除外。此外,用户定义的类型和常数分别用type和const关键字声明。
与Rust的外部属性类似,Oak引入了许多编译时标志。其中一些与其他橡木功能一起进行了证明。

那么橡木编译器如何工作?
将结构弄平到它们的功能
putnumln(*bday.day)变成putnumln(*Date::day(&bday)) 。这是一个非常简单的过程。计算每个操作类型的大小
// `3` is the size of the structure on the stack
fn Date :: new ( month : 1 , day : 1 , year : 1 ) -> 3 {
month ; day ; year
}
// self is a pointer to an item of size `3`
fn Date :: day ( self : & 3 ) -> & 1 { self + 1 }
fn main ( ) -> 0 {
let bday : 3 = Date :: new ( 5 , 14 , 2002 ) ;
}静态计算程序的内存足迹
将橡木表达式和语句转换为同等的IR指令
在许多不同的情况下,方法调用有效。方法总是将指针作为参数。但是,不需要调用方法的对象是指针。例如,以下代码有效: let bday: Date = Date::new(); bday.print(); 。变量bday不是指针,但仍然可以使用方法.print() 。这就是原因。
当编译器看到一个扁平的方法调用时,它需要找到一种将“实例表达式”转换为指针的方法。对于变量,这很容易:只需添加参考!例如,已经是指示的表达式,这甚至更容易:什么也不做!但是,对于任何其他类型的表达方式,它都有更多的详细信息。编译器在隐藏的变量中偷偷摸摸地存储表达式,然后使用变量作为实例表达式再次编译方法调用。很酷,对吧?
组装IR指令
Target的特征。如果您使用Target特征实现IR对您的语言的每个说明,则OAK可以自动编译到您的新编程或组装语言!是的,听起来很简单! 为了允许用户阅读库和文件的文档,而无需访问Internet,Oak提供了doc命令。这使作者可以将文档属性添加到其代码中,以帮助其他用户了解其代码或API,而无需筛选源并阅读评论。
这是一些示例代码。
# [ std ]
# [ header ( "This file tests Oak's doc subcommand." ) ]
# [ doc ( "This constant is a constant." ) ]
const CONSTANT = 3 ;
// No doc attribute
const TEST = CONSTANT + 5 ;
# [ doc ( "This structure represents a given date in time.
A Date object has three members:
|Member|Value|
|-|-|
|`month: num` | The month component of the date |
|`day: num` | The day component of the date |
|`year: num` | The year component of the date |" ) ]
struct Date {
let month : num , day : num , year : num ;
# [ doc ( "The constructor used to create a date." ) ]
fn new ( month : num , day : num , year : num ) -> Date {
return [ month , day , year ] ;
}
# [ doc ( "Print the date object to STDOUT" ) ]
fn print ( self : & Date ) {
putnum ( self ->month ) ; putchar ( '/' ) ;
putnum ( self ->day ) ; putchar ( '/' ) ;
putnumln ( self ->year ) ;
}
}
# [ doc ( "This function takes a number `n` and returns `n * n`, or `n` squared." ) ]
fn square ( n : num ) -> num {
return n * n
}
fn main ( ) {
let d = Date :: new ( 5 , 14 , 2002 ) ;
d . print ( ) ;
}这是doc命令的示例用法,以将格式的文档打印到终端。

要获得当前的开发构建,请克隆存储库并安装它。
git clone https://github.com/adam-mcdaniel/oakc
cd oakc
cargo install -f --path . 要获取当前版本构建,请从Crates.io安装。
# Also works for updating oakc
cargo install -f oakc然后,可以使用OAKC二进制文件编译橡木文件。
oak c examples/hello_world.ok -c
main.exeC后端- 任何支持C99的海湾合作委员会编译器
后端- Golang 1.14编译器
打字稿后端- 打字稿3.9编译器