JURNAL IT - Pemrograman modern sering kali dipandang sebagai disiplin ilmu yang berbasis pada logika dan matematika.
Namun, ketika kita menggali lebih dalam, pemrograman menghadapi tantangan yang sangat mirip dengan yang dihadapi bahasa manusia, menegaskan bahwa ia secara fundamental dapat dianggap sebagai masalah linguistik.
Sebagai programmer, kita berkomunikasi dari individu ke compiler (kompilator).
Kompilator, meskipun merupakan penemuan yang dibuat oleh manusia, tidak sempurna dalam hal interpretasi, sama seperti bahasa manusia yang sering menghadapi ambiguitas.
Menariknya, hampir setiap masalah khas dalam bahasa pemrograman dapat ditelusuri kembali ke masalah yang setara dalam bahasa manusia.
Ambiguitas dan Tiga Fase Kompilasi
Kompilator harus 100% benar sepanjang waktu, tetapi ia tidak memiliki keuntungan untuk dapat meminta konteks tambahan saat dibutuhkan, seperti yang bisa dilakukan manusia.
Untuk memproses sintaks suatu program, kompiler membaginya menjadi tiga tahap yang berbeda:
1. Analisis Leksikal (Lexical Analysis): Ini adalah tahap pertama di mana kompiler memecah program menjadi token yang berbeda, seperti variabel dan operator.
2. Tahap Parsing (Parsing Stage): Kompilator mengambil semua token tersebut dan mengubahnya menjadi Abstract Syntax Tree (AST). Pada tahap ini, kompiler menemukan kesalahan sintaksis yang umum, seperti lupa menambahkan titik koma (semicolon) dalam program C++.
3. Analisis Semantik (Semantic Analysis): Ini adalah tahap akhir di mana kompiler menggunakan AST untuk mencari kebenaran logis dan semantik dalam kode, seperti melakukan pemeriksaan tipe (type checking) atau resolusi cakupan (scope resolution).
Tantangan Ambiguitas Sintaksis
Ambiguitas sering terjadi, terutama pada bahasa yang lebih tua yang terus menerus ditambahkan fitur baru.
Dalam istilah linguistik, ambiguitas sintaksis disebut sebagai ambiguitas tata bahasa (grammar ambiguity).
1. The Most Vexing Parse (MVP)
MVP adalah ambiguitas sintaksis di C++ di mana token yang sama dapat masuk ke beberapa parse tree yang berbeda. Kompiler menjadi bingung apakah suatu deklarasi adalah definisi fungsi atau deklarasi variabel baru.
Konsep ini setara dengan ambiguitas dalam kalimat bahasa Inggris seperti, "visiting relatives can be boring," di mana kata 'visiting' dapat menjadi kata kerja (tindakan mengunjungi) atau kata sifat (menggambarkan kerabat yang sedang mengunjungi).
Untuk mengatasi MVP, C++ modern (sejak C++ 11) menyediakan opsi deklarasi yang tidak ambigu dengan menggunakan kurung kurawal ({}).
2. Dangling Else
Ini adalah contoh ambiguitas asosiasi. Ketika menggunakan struktur if-else tanpa kurung kurawal, kompiler mungkin tidak yakin apakah pernyataan else harus dikaitkan dengan pernyataan if yang terdekat atau pernyataan if yang lebih jauh.
Ambiguitas ini dapat menyebabkan hasil yang tidak terduga. Untuk memperbaiki masalah dangling else dan membuat kode jauh lebih mudah dibaca, disarankan untuk selalu menggunakan kurung kurawal ({}).
Ambiguity Leksikal dan Semantik
Parsing token bisa menjadi rumit jika kompiler tidak memiliki konteks yang tepat.
1. Nested Generics (Ambiguitas Leksikal)
Dalam C++ 98, ketika menyarangkan tipe data, seperti vector<vector<int>>, dua kurung sudut kanan (>>) secara keliru diakui sebagai operasi shift right.
Kompiler tidak dapat membedakan apakah ini adalah tipe bersarang (nested type) atau operasi pergeseran.
Solusi lama mengharuskan programmer untuk menambahkan spasi di antara dua kurung sudut tersebut agar kompiler tidak ambigu.
Untungnya, kompiler C++ modern (C++ 20) telah berhasil mem-parse sintaks ini dengan benar.
2. Dependent Type Names (Ambiguitas Semantik)
Ambiguitas semantik sangat bergantung pada konteks. Dalam template C++, ketika kompiler menemukan dua titik dua (::) untuk merujuk pada suatu tipe (misalnya, const_iterator), ia mungkin bingung apakah itu adalah tipe yang dimaksud atau variabel anggota statis.
Sama seperti kita harus eksplisit dalam bahasa Inggris (misalnya, menggunakan huruf kapital 'P' untuk membedakan antara "Polish" kebangsaan dan "polish" mengilapkan), kita harus eksplisit di C++.
Solusinya adalah dengan secara eksplisit menentukan bahwa yang kita harapkan adalah nama tipe dengan menggunakan kata kunci typename sebelum nama tipe dependen.
Kata kunci lain, seperti template, juga merupakan penanda yang sangat penting yang secara fundamental mengubah interpretasi kompiler C++.
Jika kompiler mengira simbol kurang dari (<) adalah operasi perbandingan alih-alih bagian dari deklarasi template, kita harus eksplisit dengan menambahkan kata kunci template.
Menuju Bahasa yang Kurang Ambiguitas
Meskipun C++ mungkin bahasa pemrograman favorit banyak orang, secara historis ia tidak hebat dalam menghindari kebingungan leksikal ini.
Terdapat upaya untuk menciptakan bahasa yang sangat mudah dipahami dan bebas ambiguitas. Contohnya, Simplified Technical English (STE), yang dibuat oleh industri penerbangan Eropa untuk manual teknis.
STE sangat sederhana, mudah dipahami oleh penutur non-Inggris, dan terbatas hanya pada sekitar 900 kata.
Dalam dunia pemrograman, LISP dan bahasa sejenis LISP dianggap sebagai "pemenang yang jelas" dalam hal mengurangi kompleksitas leksikal. Meskipun banyak yang tidak menyukai banyaknya tanda kurung dalam LISP, penggunaan tanda kurung tersebut membuatnya sangat jelas dan super tanpa ambiguitas bagi kompiler.
Selain LISP, ADA dan Haskell juga cukup baik dalam hal ini.(*)
0 Komentar