Skip to content

Latest commit

 

History

39 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Aiko

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.

Goals

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

Alur Kerja

  • 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).

Fitur Bahasa

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.math
math.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

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.

Fitur

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.

Contoh Penggunaan

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

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.

Fitur

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.

Contoh Penggunaan

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

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).

Fitur

Compiler ini memiliki kemampuan untuk menangani:

  • Deklarasi variabel (termasuk literal dan array).
  • Ekspresi aritmatika dan logika (seperti penambahan, pengurangan, perbandingan, dll.).
  • Pernyataan kontrol seperti if dan for.
  • Deklarasi dan pemanggilan fungsi.
  • Pencetakan hasil ekspresi (seperti nilai variabel atau hasil perhitungan).

Contoh Penggunaan

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 ------------------------------

Arsitektur Memori & Manajemen Sistem Boxing

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.

Coba Online (Playground)

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:


Web Playground Buka aikoes.dev ➔



Penutup

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.

About

Merupakan proyek bahasa pemrograman saya yang dibangun dengan javascript, semua hal dari lexer, token, parser, ast node, dan code generator di buat dengan javascript. Hasil akhirnya adalah program assembly yang akan di compile dan linking sehingga bisa dijalankan seperti biasa. Disini menggunakan shell script untuk kemudahan dalam proses compile.

Topics

Resources

Stars

4 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages