Aiko adalah compiler sederhana yang dikembangkan dengan JavaScript untuk mengubah kode sumber menjadi bahasa assembly x86. Proyek ini mencakup pembuatan Lexer, Parser (AST), hingga Code Generator.
Proyek ini dibuat untuk:
- Mempelajari cara kerja compiler dari dasar
- Mengimplementasikan pipeline kompilasi secara manual
- Memahami representasi AST dan transformasi kode
- Mengeksplorasi konsep memory management di level rendah
- Menghasilkan output assembly x86 tanpa dependency compiler eksternal
- Lexer: Memecah string input menjadi token (kata kunci, operator, literal).
- Parser: Menyusun token menjadi Abstract Syntax Tree (AST) menggunakan teknik Recursive Descent.
- Compiler: Mentranslasi AST menjadi instruksi x86 Assembly (NASM syntax).
| Kategori | Fitur / Sintaksis | Contoh Kode | Keterangan / Output | Detail Internal & Fitur Tersembunyi |
|---|---|---|---|---|
| Variabel | var (Mutable)val (Immutable) |
var x = 888;val newNull = null; |
var nilainya bisa diubah; val bersifat read-only/konstan. |
Variabel yang dideklarasikan tanpa inisialisasi awal (kosong) otomatis diisi nilai default 0 oleh compiler. |
| Tipe Data & Primitif | Int, String, Bool, null |
var z = "coba";var n = null; |
Mendukung pengecekan kesamaan pada tipe data primitif dan null. |
Representasi Runtime Type ID: • 0 = Integer• 1 = String• 2 = Boolean• null memiliki ID tipe khusus untuk validasi pointer kosong. |
| Operator Aritmatika | Unary & Binary Op | 10 * 4 / 2 + 1;print(!!!false); |
Mendukung presedensi operator, modulo (%), dan multi-unary (!, -). |
Mendukung operasi matematika bertingkat dengan pemrosesan langsung via register x86 (EDX, EBX, EAX) tanpa bantuan library luar. |
| Kontrol Alur | if, elif, else |
if x == 10 {} elif x == 9 {} else {} |
Percabangan kondisional (menggunakan keyword spesifik elif). |
Ditranslasikan langsung menjadi instruksi perbandingan assembly (cmp) dan lompatan bersyarat (je, jne, jg, dll.). |
| Perulangan (For) | Range Loop dengan Step | for i = 49 .. -10, 20 |
Loop dari 49 ke -10 dengan langkah (step) mundur sebesar 20. | Mendukung pencacah naik (increment) maupun turun (decrement) secara dinamis tergantung nilai batas yang diberikan. |
| For-In (Array Loop) | for item in arr3 |
Iterasi langsung untuk setiap elemen di dalam array. | Mengambil panjang array secara internal dan melakukan kalkulasi offset memori secara otomatis di setiap iterasi. | |
For-In + when |
for item in arr3 when item > 500 |
Fitur Unggulan: Iterasi array yang langsung difilter dengan kondisi tertentu. | Syntactic Sugar: Generator langsung menyisipkan blok kondisional di dalam perulangan sebelum instruksi pencetakan/proses dieksekusi. | |
| Kontrol Loop | Loop Control | break; & continue; |
Menghentikan perulangan atau melompati iterasi yang sedang berjalan. | Memanfaatkan label jump assembly unik (misal .loop_end dan .loop_start) yang melacak hierarki perulangan aktif (termasuk nested loop). |
| Fungsi (Function) | Tanpa Return | fun addParams(a, b) { print(a+b); } |
Prosedur standar, mendukung multi-parameter. | Argumen fungsi di-passing via stack assembly. Variabel lokal dialokasikan menggunakan offset dari Base Pointer (ebp). |
| Dengan Return | fun res(a) { return a + 1; } |
Mengembalikan nilai baik berupa literal, variabel, maupun hasil operasi. | Nilai kembalian (return value) selalu diletakkan di register utama eax sebelum instruksi ret dipanggil. |
|
| Rekursif | fun rec(n) { ... return n * rec(n-1); } |
Mampu menangani pemanggilan fungsi dirinya sendiri. | Manajemen call stack x86 yang aman untuk memastikan alamat kembali (return address) dipulihkan dengan benar saat rekursi selesai. | |
| Array & String | Inisialisasi Range | var arr = [9 .. 15, 97, 43]; |
Membuat array dari jangkauan jajaran angka digabung dengan elemen literal. | Array Flattening: Menggabungkan hasil ekspansi jangkauan (range) dan elemen individu menjadi satu kesatuan deret data linear di memori. |
| Inisialisasi Fixed | var arr = array(5); |
Alokasi array kosong dengan panjang statis di memori. | Alokasi memori berurutan (kontigu) sebesar ukuran elemen dikalikan panjang array yang diminta. | |
Fungsi len() |
len(arr) |
Mengembalikan jumlah panjang elemen dari sebuah array. | Membaca metadata ukuran array yang disimpan pada blok memori khusus di awal alokasi array tersebut. | |
| Indexing | arr[idx] = 777;kata[0] |
Akses/ubah elemen via index. String bisa diakses per karakter. | Fitur Keamanan Tersembunyi: Kode mencakup pemeriksaan batas indeks (out of bounds checking). Jika melebihi kapasitas, program akan memicu error runtime. | |
| Fitur Sistem | typeof() |
typeof(tipeInt) |
Mengembalikan runtime type indicator. | Membaca metadata tipe data yang tersimpan di dalam struktur internal data (Box Object) pada offset memori ke-4 ([eax + 4]). |
input() |
input("Angka: ") as int |
Menerima input user lewat konsol dengan mekanisme type casting. | Melakukan pemanggilan sistem (system call) atau fungsi eksternal untuk membaca buffer konsol, lalu mengonversi string input menjadi integer jika ditambahkan klausul as int. |
|
use (Modul Pustaka) |
use std.mathmath.abs(-79) |
Mekanisme penyertaan standar library internal. | Namespace Resolution: Menggabungkan kode atau fungsi eksternal dari pustaka standar ke dalam tabel simbol global compiler saat fase parsing berlangsung. | |
| Low-Level | view (Pointer/Memory) |
print(view x);print(view arr[i]); |
Menginspeksi alamat memori asli atau referensi variabel. | Direct Pointer Access: Melewati pembungkusan data (Unboxing) dan langsung mengambil nilai alamat memori mentah dari pointer Box* di arena_alloc hanya untuk dicetak atau diinspeksi. |
Lexer ini merupakan Lexical Analyzer untuk memecah input string menjadi token-token. Token-token ini digunakan oleh parser untuk analisis lebih lanjut. Kode ini dilengkapi dengan berbagai fungsi untuk memeriksa karakter dalam input dan mengidentifikasi berbagai elemen kode.
Lexer ini memiliki kemampuan untuk memeriksa dan mengenali: Angka: Integer dan float. String: Teks yang diapit oleh tanda kutip. Identifier: Variabel dan nama fungsi. Operator: Seperti +, -, *, /, dll. Pembanding dan Penugasan: Seperti =, ==, >=, !=, dll. Tanda baca: Seperti ;, ,, (), {}, dll.
Berikut adalah contoh cara menggunakan lexer untuk memeriksa kode input var a = 10; dan mengembalikan token yang sesuai.
var x = 10;Maka akan menghasilkan output:
[
{ type: 'VAR', value: 'var' },
{ type: 'IDENTIFIER', value: 'a' },
{ type: 'ASSIGN', value: '=' },
{ type: 'INT', value: 10 },
{ type: 'SEMICOLON', value: ';' },
{ type: 'EOF', value: null }
]Parser ini digunakan untuk mengonversi sekumpulan token yang dihasilkan oleh lexer menjadi struktur yang lebih kompleks, yaitu pohon sintaksis abstrak (AST). Parser menggunakan teknik recursive descent parsing, yang memungkinkan untuk menangani berbagai ekspresi dan pernyataan dengan lebih mudah dan terstruktur.
Parser ini memiliki kemampuan untuk:
- Memproses pernyataan dasar seperti deklarasi variabel, print, dan kontrol alur (if-else, for).
- Menangani ekspresi matematika, perbandingan, dan pemanggilan fungsi.
- Membuat dan mengelola fungsi, termasuk pengembalian nilai dan parameter.
- Menghasilkan AST yang mewakili struktur kode secara hierarkis.
Berikut adalah contoh cara menggunakan parser untuk memproses kode input dan menghasilkan AST.
var x = 10;
if x > 5 {
print(x);
}Hasil AST yang dihasilkan:
ProgramStmt {
type: 'Program',
statements: [
VarDeclStmt {
type: 'VarDecl',
name: 'x',
initializer: LiteralStmt { type: 'Literal', value: 10 }
},
IfStmt {
type: 'If',
condition: BinaryOpStmt {
type: 'BinaryOp',
left: IdentifierStmt { type: 'Identifier', name: 'x' },
op: '>',
right: LiteralStmt { type: 'Literal', value: 5 }
},
then_block: [
PrintStmt {
type: 'Print',
expression: IdentifierStmt { type: 'Identifier', name: 'x' }
}
],
elifs: [],
else_block: null
}
]
}Compiler ini bertanggung jawab untuk mengonversi Abstract Syntax Tree (AST) dari kode sumber menjadi kode assembler, serta menangani berbagai jenis pernyataan dan ekspresi dalam kode tersebut. Kode ini mendemonstrasikan cara-cara menangani deklarasi variabel, kontrol alur (seperti if dan for), serta fungsi (seperti deklarasi fungsi dan pernyataan return).
Compiler ini memiliki kemampuan untuk menangani:
- Deklarasi variabel (termasuk literal dan array).
- Ekspresi aritmatika dan logika (seperti penambahan, pengurangan, perbandingan, dll.).
- Pernyataan kontrol seperti
ifdanfor. - Deklarasi dan pemanggilan fungsi.
- Pencetakan hasil ekspresi (seperti nilai variabel atau hasil perhitungan).
Berikut adalah contoh cara compiler ini menangani kode sumber yang menggunakan deklarasi variabel, operasi aritmatika, dan perintah print.
Misalnya, kode sumber berikut:
var a = 10;
var b = 5;
var c = a + b;
print(c);Compiler akan menghasilkan kode assembler yang mirip dengan ini:
; ------------------------------ Start Literal ------------------------------
push 8 ; ------------------------------ alokasi untuk 1 element ------------------------------
call arena_alloc
add esp, 4
mov dword [eax], 10 ; alamat dalam register eax = 10
mov dword [eax + 4], 0 ; tipe data = angka sebagai 0
; ------------------------------ End Literal ------------------------------
; ------------------------------ Start Deklarasi variabel a ------------------------------
sub esp, 4
mov dword [ebp - 4], eax ; pindahkan alamat Box* ke dalam offset 4
; ------------------------------ End Deklarasi variabel a ------------------------------
; ------------------------------ Start Literal ------------------------------
push 8 ; ------------------------------ alokasi untuk 1 element ------------------------------
call arena_alloc
add esp, 4
mov dword [eax], 5 ; alamat dalam register eax = 5
mov dword [eax + 4], 0 ; tipe data = angka sebagai 0
; ------------------------------ End Literal ------------------------------
; ------------------------------ Start Deklarasi variabel b ------------------------------
sub esp, 4
mov dword [ebp - 8], eax ; pindahkan alamat Box* ke dalam offset 8
; ------------------------------ End Deklarasi variabel b ------------------------------
; ------------------------------ Start Binary Op (+) ------------------------------
; ------------------------------ Start Ambil offset variabel a ------------------------------
mov eax, [ebp - 4] ; eax = Box*
; ------------------------------ End Ambil offset variabel a ------------------------------
mov eax, eax
mov edx, [eax] ; Unbox left ke EDX
push edx ; simpan left value
; ------------------------------ Start Ambil offset variabel b ------------------------------
mov eax, [ebp - 8] ; eax = Box*
; ------------------------------ End Ambil offset variabel b ------------------------------
mov eax, eax
mov ebx, [eax] ; Unbox right ke EBX
pop edx ; restore left value ke edx
add edx, ebx
; ------------------------------ End Binary Op + ------------------------------
; simpan hasil di box
push edx
push 8 ; ------------------------------ alokasi untuk 1 element ------------------------------
call arena_alloc
add esp, 4
pop edx
mov [eax], edx
mov dword [eax + 4], 0
; ------------------------------ Start Deklarasi variabel c ------------------------------
sub esp, 4
mov dword [ebp - 12], eax ; pindahkan alamat Box* ke dalam offset 12
; ------------------------------ End Deklarasi variabel c ------------------------------Aiko menggunakan runtime Arena Allocator kustom untuk mengelola alokasi memori dinamis di dalam heap secara linear dan cepat.
Untuk mendukung fitur seperti dynamic typing dan keyword typeof(), setiap variabel atau literal dibungkus ke dalam struktur Box Object berukuran 8 byte:
- Offset 0 (
[eax]): Menyimpan nilai mentah (Value) atau pointer data. - Offset 4 (
[eax + 4]): Menyimpan Metadata Type ID (0= Int,1= String,2= Bool).
Ketika operasi aritmatika (seperti a + b) terjadi, compiler akan men-generate instruksi untuk melakukan Unboxing (mengambil nilai di offset 0 ke register), memprosesnya, lalu meletakkan kembali hasilnya ke objek sebelumnya.
Anda tidak perlu melakukan setup environment atau menginstal assembler secara lokal untuk mencoba Aiko. Jalankan, modifikasi, dan lihat hasil translasi assembly x86 secara langsung melalui web playground:
Aiko adalah proyek yang mengubah kode yang biasa kita tulis menjadi instruksi bahasa mesin (Assembly x86) yang sangat dasar. Melalui Aiko, kita bisa melihat bagaimana sebuah logika program diolah secara rapi di dalam memori komputer melalui sistem penyimpanan data yang cerdas.