# Topik 1. Analisis Leksikal Tujuan : 1. Memahami pengertian dan model dari translator, compiler, interpreter & assembler 2. Memahami konsep dasar kompilasi dan tahapan-tahapannya, seperti analisis leksikal, sintaks, semantik, pembangkit kode, optimasi kode, dan Object code generation. 3. Mampu mengimplementasikan lexical analyzer sederhana menggunakan Python untuk memproses kode sumber menjadi token. --- ## Pengenalan Teknik Kompilasi Arti kata teknik kompilasi: - Teknik adalah suatu metode atau cara - Kompilasi suatu proses menggabungkan serta menerjemahkan sesuatu (source program) menjadi bentuk lain. - Compile – to translate a program written in a high-level programming language. Sejarah perkembangan suatu penerjemah sudah dimulai sejak lama, yaitu pada saat mulai ditemukannya komputer pada awal 1950-an. Sejak waktu tersebut teknik dan cara pembentukan suatu penerjemah telah berkembang dengan sangat pesat. Demikian pula program bantu (tools) untuk membuat suatu penerjamah <div style="text-align: justify"> Sejarah perkembangan suatu penerjemah sudah dimulai sejak lama, yaitu pada saat mulai ditemukannya komputer pada awal 1950-an. Sejak waktu tersebut teknik dan cara pembentukan suatu penerjemah telah berkembang dengan sangat pesat. Demikian pula program bantu (tools) untuk membuat suatu penerjamah. ### Translator Dunia seperti yang kita kenal bergantung pada bahasa pemrograman, karena semua perangkat lunak yang berjalan di seluruh komputer ditulis dalam suatu bahasa pemrograman. Namun sebelum sebuah program dapat dijalankan, terlebih dahulu program tersebut harus diterjemahkan menjadi bentuk yang dapat dieksekusi oleh komputer sehingga komputer memerlukan translator. Translator (penerjemah) adalah sebuah program yang menerjemahkan sebuah program sumber (source program) menjadi program sasaran (target program). Bahasa mesin adalah bentuk bahasa terendah komputer karena berhubungan langsung dengan bagian bagian komputer seperti bits, register & sangat primitive. Hal ini akan membingungkan bagi programmer yang membuat program dengan bahasa mesin. Oleh karena itu dibutuhkan translator untuk menerjemahkan bahasa mesin menjadi bahasa yang dapat dipahami oleh manusia. Contoh : Jika source language adalah high level language, seperti cobol, pascal, fortran maka object language adalah low-level language atau mesin language. Translator seperti ini disebut COMPILER ![image](https://hackmd.io/_uploads/B1MgR5KDxx.png) Pengertian dan Model dari Compiler, Interpreter & Assembler **A. Pengertian Compiler** Kompilator adalah suatu program yang menerjemahkan bahasa program kedalam bahasa objek. Kompilator memerlukan waktu untuk membuat suatu program dapat dieksekusi oleh komputer, program yang dieksekusi oleh compiler dapat berjalan lebih cepat dibanding program yang diproduksi oleh interpreter, disamping itu juga bersifat independen. Istilah Compiler biasa digunakan untuk program komputer yang menerjemahkan program yang ditulis dalam bahasa pemrograman tingkat tinggi (Contoh bahasa C++, Pascal, FORTRAN, Visual C#, COBOL, Visual Basic, Java, atau xBase) diterjemahkan menjadi bahasa mesin, biasanya menggunakan bahasa Assembly sebagai perantara. salah satu contoh Compiler dalam Bahasa C++ adalah Visual C++, GNU Compiler Collection (GCC) dan Borland C++. ![image](https://hackmd.io/_uploads/rk4IRqKvxe.png) Tahap kompilasi berdasarkan bagan tersebut adalah: - Pertama source code (program yang ditulis) dibaca ke memori komputer. - Source code tersebut diubah menjadi object code (bahasa assembly). - Object code dihubungkan dengan library yang dibutuhkan untuk membentuk file yang bisa di eksekusi. **B. Pengertian Interpreter** *Interpreter* adalah perangkat lunak yang berfungsi untuk mengeksekusi kode program yang ditulis oleh programmer secara langsung dengan menerjemahkannya ke dalam bahasa mesin. Proses eksekusi ini dilakukan secara baris demi baris, mengikuti logika yang terdapat dalam kode sumber. Dalam konteks ini, interpreter tidak menghasilkan output dalam bentuk file eksekusi yang lengkap sebelum menjalankan program, melainkan menerjemahkan dan menjalankan kode pada saat yang bersamaan. ![image](https://hackmd.io/_uploads/H1yJysYvle.png) Berbeda dengan compiler, yang mengubah keseluruhan kode sumber menjadi bahasa mesin dalam bentuk file eksekusi terpisah sebelum eksekusi, interpreter menjalankan kode secara langsung tanpa menghasilkan file eksekusi terpisah. Proses penerjemahan pada compiler dilakukan sebelumnya dan hasilnya berupa satu kesatuan perintah dalam bahasa mesin yang siap untuk dijalankan. Di sisi lain, dalam terminologi penerjemahan bahasa, interpreter (atau dalam bahasa Indonesia dikenal sebagai "juru bahasa") berbeda dari translator. Interpreter dalam konteks bahasa manusia menerjemahkan bahasa sumber ke bahasa sasaran secara lisan atau secara langsung (oral), sedangkan translator menerjemahkan teks dari bahasa sumber ke bahasa sasaran secara tertulis. Meskipun keduanya memiliki fungsi menerjemahkan, metode dan media yang digunakan dalam proses penerjemahan ini berbeda. **C. Pengertian Assembler** Bahasa assembly adalah bahasa pemrograman yang menggunakan instruksi-instruksi dengan "mnemonic" untuk menggantikan kode biner yang rumit dari bahasa mesin. Misalnya, sebuah instruksi penambahan dalam bahasa mesin dengan kode “10110011” yang dalam bahasa assembly dapat dibuat dalam instruksi mnemonik ADD, sehingga mudah diingat dibandingkan dengan angka 0 dan 1, dalam setiap instruksi membutuhkan suatu operand baik berupa data langsung maupun suatu lokasi memori yang menyimpan data yang bersangkutan. ![image](https://hackmd.io/_uploads/S15yE9pDel.png) Bahasa assembly, sering disebut sebagai kode sumber atau kode simbolik, dapat dijalankan oleh prosesor setelah diterjemahkan oleh program assembler. Assembler adalah perangkat lunak yang mengkonversi program dalam bahasa assembly menjadi bahasa mesin. Bahasa mesin itu sendiri adalah sekumpulan kode biner yang dapat diproses langsung oleh komputer dan sering disebut sebagai kode objek. **Tahap–Tahap Kompilasi** Proses kompilasi dikelompokan ke dalam dua kelompok besar: 1. Tahap Analisa (Front-end): Menganalisis source code dan memecahnya menjadi bagian-bagian dasarnya. Menghasilkan kode level menengah dari source code input yang ada. Tahap-tahap yang harus dilalui pada saat mengkompilasi program, yaitu: a. Analisa Leksikal b. Analisa Sintaks c. Analisa Semantik d. Pembangkit Kode Antara 2. Tahap Sintesa (Back-end): membangun program sasaran yang diinginkan dari bentuk antara. Tahap-tahap yang harus dilalui pada tahap sintesa yaitu : a. Code optimization b. Object code generation ![image](https://hackmd.io/_uploads/SJQQiOeugg.png) Keterangan : * **Analisa Leksikal (scanner)** Berfungsi memecah teks program sumber menjadi bagian-bagian kecil yang mempunyai satu arti yang disebut token, seperti : konstanta, nama variabel, keyword, operator. Lexer adalah suatu komponen yang berfungsi untuk mendeteksi bagian terkecil (token) dari suatu bahasa. Proses pendeteksian ini disebut lexing. Biasanya regular expressions digunakan untuk melakukan proses lexing karena bentuk token biasanya sangat sederhana * **Analisa Sintaks (parser)** Parser berfungsi sebagai komponen yang mendeteksi struktur dari beberapa token berdasarkan aturan sintaksnya dan memeriksa kebenaran dan urutan kemunculan token. Proses pendeteksian ini disebut parsing. Proses parsing lebih rumit daripada lexing. Sederetan token yang tidak mengikuti aturan sintaks akan dilaporkan sebagai kesalahan sintaks (sintax error). Secara logika deretan token yang bersesuaian dengan sintaks tertentu akan dinyatakan sebagai pohon parsing (parse tree). * **Analisis Semantik** Berfungsi memeriksa token dan ekspresi dari batasan-batasan yang ditetapkan. Batasan-batasan tersebut misalnya : 1. Panjang maksimum token identifier adalah 8 karakter, 2. Panjang maksimum ekspresi tunggal adalah 80 karakter, 3. Nilai bilangan bulat adalah -32768 s/d 32767, 4. Operasi aritmatika harus melibatkan operan-operan yang bertipe sama. * **Pembangkit Kode Antara** Berfungsi membangkitkan kode antara (intermediate code) berdasarkan pohon parsing. Pohon parse selanjutnya diterjemahkan oleh suatu penerjemah yang dinamakan penerjemah berdasarkan sintak (syntax-directed translator). * **Symbol Table management** Berfungsi mengelola tabel simbol selama proses kompilasi. Tabel simbol adalah struktur data yang memuat record untuk tiap identifier dengan atribut-atribut identifier itu. * **Penangan Kesalahan (Error handler)** Berfungsi menangani kesalahan yang berlangsung selama proses kompilasi. --- ## Analisis leksikal Analisis leksikal (bahasa Inggris: lexical analysis) adalah sebuah proses yang mendahului parsing sebuah rangkaian karakter. Ia menerima masukan serangkaian karakter (seperti dalam dokumen plain-text atau source code) dan menghasilkan deretan simbol yang masing-masing dinamakan token; Kelompok karakter yang membentuk sebuah token dinamakan lexeme untuk token tersebut. Analisis leksikal membuat pekerjaan parser jadi lebih mudah; terlepas dari efisiensi pemrograman yang dapat dicapai dengan penggunaannya, proses kerja analisis leksikal yang membaca lebih dari sekali setiap karakter dari input yang diberikan menjadikan penganalisis leksikal sebagai sub-sistem yang paling intensif melakukan komputasi, terutama bila digunakan dalam sebuah kompilator. Analisis leksikal merupakan tahap pertama proses kompilasi. Tujuannya Mengubah kode sumber (stream karakter) menjadi token, yaitu unit leksikal seperti keyword, identifier, literal, operator, delimiter, dan indentasi. Token tersebut nantinya akan diproses oleh parser ke tahap selanjutnya. ## Implementasi Analisis Leksikal **Contoh Analisa Leksikal** Dalam contoh ini, kita akan membuat analisis leksikal sederhana yang dapat mengenali angka, kata kunci, operator, dan nama variabel dalam sebuah kode sumber menggunakan bahasa Python. Langkah 1: Definisikan Token dan Kategori Token ```python! class BahasaKu: def __init__(self, code): self.code = code self.line_nr = 0 self.returned_token = None self.token_feed = self.tokens() self.vars = {} # track assigned variables self.KEYWORDS = ['cetak', 'if', 'elif', 'else', 'end'] self.OPERATORS = ['=', '+', '-', '*', '/', '==', '!=', '<', '>', '<=', '>='] self.SYMBOLS = ['(', ')', ':'] def raise_error(self, msg): raise ValueError(f"Baris {self.line_nr}: {msg}") ``` Langkah 2: Buat Method untuk reset tokenizer ```python # ... def reset_tokenizer(self): """Reset tokenizer agar bisa dipanggil ulang""" self.line_nr = 0 self.returned_token = None self.token_feed = self.tokens() ``` Langkah 3: Buat Fungsi Lexer Sekarang, buat fungsi yang akan membaca kode sumber dan memecahnya menjadi token token. ```python! # ... def tokens(self): for line in self.code.splitlines(): self.line_nr += 1 line = line.strip() if line == '': yield ('NEWLINE',) continue i = 0 tokens = [] while i < len(line): ch = line[i] if ch.isspace(): i += 1 continue if ch in '"\'': quote = ch i += 1 start = i while i < len(line) and line[i] != quote: i += 1 if i >= len(line): self.raise_error("String tidak ditutup") tokens.append(('STR', line[start:i])) i += 1 continue if i + 1 < len(line) and line[i:i+2] in ['==','!=','<=','>=']: tokens.append(line[i:i+2]) i += 2 continue if ch in "=+-*/()<>:": tokens.append(ch) i += 1 continue if ch.isdigit() or (ch == '.' and i+1 < len(line) and line[i+1].isdigit()): start = i has_dot = False if ch == '.': has_dot = True i += 1 while i < len(line) and line[i].isdigit(): i += 1 if i < len(line) and line[i] == '.': if has_dot: self.raise_error("Angka tidak valid: terlalu banyak titik desimal") has_dot = True i += 1 if i < len(line) and line[i].isdigit(): while i < len(line) and line[i].isdigit(): i += 1 tokens.append(line[start:i]) continue if ch.isalpha() or ch == '_': start = i while i < len(line) and (line[i].isalnum() or line[i] == '_'): i += 1 tokens.append(line[start:i]) continue self.raise_error(f"Token tidak sah: {ch}") for t in tokens: if isinstance(t, tuple): yield t continue if t in self.KEYWORDS: yield ('KEY', t) elif t in self.OPERATORS: yield ('OP', t) elif t in self.SYMBOLS: yield ('SYM', t) elif t.replace('.', '', 1).isdigit(): yield ('NUM', float(t) if '.' in t else int(t)) else: yield ('IDF', t) yield ('NEWLINE',) ``` Langkah 4: Definisikan fungsi `next_token()` ```python # ... def next_token(self): if self.returned_token is not None: tok = self.returned_token self.returned_token = None return tok ``` Langkah 5: Definisikan fungsi `return_token()` ```python # ... try: return next(self.token_feed) except StopIteration: return None def return_token(self, tok): if tok: self.returned_token = tok ``` Berikut full program untuk analisis leksikal ```python class BahasaKu: def __init__(self, code): self.code = code self.line_nr = 0 self.returned_token = None self.token_feed = self.tokens() self.vars = {} self.KEYWORDS = ['cetak', 'if', 'elif', 'else', 'end'] self.OPERATORS = ['=', '+', '-', '*', '/', '==', '!=', '<', '>', '<=', '>='] self.SYMBOLS = ['(', ')', ':'] def raise_error(self, msg): raise ValueError(f"Baris {self.line_nr}: {msg}") def reset_tokenizer(self): """Reset tokenizer agar bisa dipanggil ulang""" self.line_nr = 0 self.returned_token = None self.token_feed = self.tokens() def tokens(self): for line in self.code.splitlines(): self.line_nr += 1 line = line.strip() if line == '': yield ('NEWLINE',) continue i = 0 tokens = [] while i < len(line): ch = line[i] if ch.isspace(): i += 1 continue if ch in '"\'': quote = ch i += 1 start = i while i < len(line) and line[i] != quote: i += 1 if i >= len(line): self.raise_error("String tidak ditutup") tokens.append(('STR', line[start:i])) i += 1 continue if i + 1 < len(line) and line[i:i+2] in ['==','!=','<=','>=']: tokens.append(line[i:i+2]) i += 2 continue if ch in "=+-*/()<>:": tokens.append(ch) i += 1 continue if ch.isdigit() or (ch == '.' and i+1 < len(line) and line[i+1].isdigit()): start = i has_dot = False if ch == '.': has_dot = True i += 1 while i < len(line) and line[i].isdigit(): i += 1 if i < len(line) and line[i] == '.': if has_dot: self.raise_error("Angka tidak valid: terlalu banyak titik desimal") has_dot = True i += 1 if i < len(line) and line[i].isdigit(): while i < len(line) and line[i].isdigit(): i += 1 tokens.append(line[start:i]) continue if ch.isalpha() or ch == '_': start = i while i < len(line) and (line[i].isalnum() or line[i] == '_'): i += 1 tokens.append(line[start:i]) continue self.raise_error(f"Token tidak sah: {ch}") for t in tokens: if isinstance(t, tuple): yield t continue if t in self.KEYWORDS: yield ('KEY', t) elif t in self.OPERATORS: yield ('OP', t) elif t in self.SYMBOLS: yield ('SYM', t) elif t.replace('.', '', 1).isdigit(): yield ('NUM', float(t) if '.' in t else int(t)) else: yield ('IDF', t) yield ('NEWLINE',) def next_token(self): if self.returned_token is not None: tok = self.returned_token self.returned_token = None return tok try: return next(self.token_feed) except StopIteration: return None def return_token(self, tok): if tok: self.returned_token = tok ``` Langkah 5: Lakukan pengujian, simpan program diatas lalu jalankan program, pada Python shell tulis kode berikut ```python! coba = BahasaKu(''' cetak ( 2.4 * 3 ) ''') list(coba.tokens()) ``` Output ``` [('NEWLINE',), ('KEY', 'cetak'), ('SYM', '('), ('FLOAT', 2.4), ('OP', '*'), ('NUM', 3), ('SYM', ')'), ('NEWLINE',)] ``` **Penjelasan:** Program di atas adalah sebuah lexer yang bertugas mengubah sebuah input string (seperti kode sumber atau ekspresi matematika) menjadi daftar token. Token adalah representasi dari elemen-elemen penting dalam string seperti angka, operator, atau kata kunci. 1. Token Types Program mendefinisikan beberapa jenis token Keywords, Operators, Symbols, Numerik, Identifier - **NUMBER**: Karakter angka berupa integer atau float, pada program tersebut menggunakan isdigit() untuk memeriksa apakah karakter merupakan digit (0–9). Namun, karena isdigit() hanya mengenali bilangan bulat (integer), maka untuk mendeteksi bilangan desimal (float) biasanya dilakukan pengecekan tambahan terhadap tanda titik (.). - **OPERATOR**: Operator aritmatika atau penugasan seperti `+`, `*`, `=`, `==`, `!=`, `<`, `>`,`<=`,`>=`. - **SYMBOL** : Simbol atau tanda baca yang digunakan dalam struktur sintaks. - `(` = Left Parenthesis atau tanda buka kurung - `)` = Right Parenthesis atau tanda kurung tutup - `:` = Tanda titik dua. - **IDENTIFIER**: pada program Nama variabel sesuai pola (contoh: `[a-zA-Z_][a-zA-Z0-9_]*`) - **KEYWORD**: Merupakan kata kunci yang memiliki makna khusus dalam bahasa pemrograman dan tidak dapat digunakan sebagai nama variabel atau identifier lain. Pada program ini, kata kunci yang dikenali meliputi: `cetak`, `if`, `elif`, `else`dan `end`. - **STRING**: Teks diapit tanda kutip tunggal atau ganda. - **NEWLINE**: Pada program digunakan untuk menandakan akhir baris kode. 2. Proses Lexer Program BahasaKu bekerja dengan cara membaca string kode sumber karakter demi karakter. Saat lexer menemukan spasi atau operator (seperti `+`, `*`, `=`), elemen tersebut dianggap sebagai batas (boundary) antar token, sehingga lexer dapat memisahkan dan mengidentifikasi token sebelumnya. Sebagai contoh, ketika lexer membaca angka 3, token tersebut akan dikenali sebagai INT, lalu ketika bertemu operator +, lexer akan menganggapnya sebagai OP. Proses ini terus berulang hingga seluruh karakter dalam input selesai diproses dan dikonversi menjadi token-token yang siap dikirim ke tahap parser untuk dianalisis lebih lanjut. 3. Tokenisasi Setelah string kode sumber dibaca dan diproses, lexer pada program BahasaKu akan mengubah setiap bagian kode menjadi token. Setiap token memiliki dua komponen utama, yaitu jenis token (seperti KEY, OP, INT, FLOAT, STR, atau ID) dan nilai token (misalnya if, +, 3, 3.14, atau nama). Hasil tokenisasi ini berupa daftar token yang merepresentasikan isi program dalam bentuk yang lebih terstruktur. Daftar token tersebut kemudian dapat digunakan oleh parser untuk memahami struktur logika dan aliran perintah dalam kode sumber. 4. Fungsi `next_token()` dan `return_token()` Setelah proses tokenisasi selesai dan seluruh token berhasil dibentuk, lexer menyediakan dua fungsi tambahan untuk mengatur aliran pembacaan token. Fungsi `next_token()` digunakan untuk mengambil token berikutnya dari hasil tokenisasi. Jika sebelumnya ada token yang dikembalikan, lexer akan mengembalikannya terlebih dahulu sebelum membaca token baru. Sementara itu, fungsi `return_token()` berfungsi untuk mengembalikan token terakhir yang sudah diambil, agar token tersebut dapat dibaca ulang saat dibutuhkan dalam proses analisis berikutnya. Output dari program lexer di atas berupa daftar token yang dihasilkan dari input string, seperti ekspresi matematika 2.4 * 3. Setiap token terdiri dari dua elemen: jenis token dan nilai token. Dalam contoh input ini, output token yang dihasilkan adalah: 1. **('NEWLINE', )**: Token ini menandakan akhir dari suatu baris kode. Digunakan lexer untuk memisahkan instruksi antarbaris. 2. **('SYM', ')')**:Token ini merupakan simbol penutup kurung, yang dikenali lexer sebagai tipe token SYMBOL. 3. **('KEY', 'cetak')**: Token ini adalah kata kunci (keyword) cetak, yang digunakan untuk menampilkan keluaran ke layar. 4. **('FLOAT', '2.4')**: Token ini adalah bilangan desimal dengan nilai 2.4, yang dikenali sebagai tipe token FLOAT. 5. **('OP', '*')**: Token ini merupakan operator aritmetika perkalian, yang dikenali sebagai tipe token OPERATOR. 6. **('NUM', '3')**: Token ini adalah bilangan bulat (integer) dengan nilai 3, dikenali sebagai tipe token INT. 7. **('SYM', ')')**: Token ini kembali merupakan simbol penutup kurung, dikenali sebagai tipe token SYMBOL 8. **('NEWLINE', )**: Token ini sekali lagi menandakan akhir dari baris kode, yang membantu lexer memisahkan baris satu dengan baris berikutnya. Setiap token ini memberi tahu program lebih lanjut mengenai bagian-bagian dari input dan memisahkan elemen-elemen penting dalam ekspresi, seperti angka dan operator. Dengan daftar token tersebut, program dapat melanjutkan ke tahap berikutnya, seperti parsing atau evaluasi ekspresi. </div> Sumber: 1. Modul Tulis Teknik Kompilasi Laboratorium Teknik Informatika Universitas Gunadarma 2. {%preview https://blog.miguelgrinberg.com/post/building-a-toy-programming-language-in-python %} 3. Shaikhsurab, A. (2025, Juni 22). Build Your Own Programming Language: Master Python Programming — A Simple Python-Based Guide for Students to Master Crafting Interpreters, Abstraction, and the Future of Coding [E-book]