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編譯器