Mengenal Bahasa Pemrograman Forth

Di posting ini saya ingin memperkenalkan bahasa pemrograman Forth. Bahasa Forth sudah ada selama 50 tahun dan cukup menarik walaupun saat ini sudah cukup jarang dipakai. Forth memakai notasi postfix, berbasis stack, dan merupakan contoh dari concatenative programming language.

Forth merupakan bahasa yang sangat sederhana dan dapat diimplementasikan di sistem dengan spesifikasi sangat rendah, bahkan juga di microcontroller. Biasanya Forth bisa diprogram secara interaktif (analoginya seperti berada di shell Python/Ruby), tapi bisa juga dikompilasi.

Sifat interaktif Forth ini seperti berbagai bahasa tingkat tinggi. Di sisi lain Forth bisa mengakses mesin secara low level dan tidak memiliki struktur data yang rumit (misalnya hash atau list),hanya dapat mengatur blok memori secara low level (seperti C). Jadi Forth berada antara bahasa tingkat tinggi dan tingkat rendah.

Di mana Forth dipakai?

Dari puluhan ribu bahasa pemrograman di dunia ini, tidak banyak bahasa pemrograman yang memiliki standard ISO, Forth merupakan salah satu bahasa yang memiliki standard ISO. Beberapa contoh bahasa dengan standar ISO adalah: JavaScript, Ruby, SQL, Pascal, C, dan C++, sementara Python, Go, Dart dsb tidak/belum punya standar ISO. Adanya standar sebuah bahasa biasanya membuat bahasa tersebut dapat dipakai di berbagai proyek yang memiliki spesifikasi sangat ketat (misalnya proyek NASA).

Di masa kejayaannya ada banyak sekali program yang ditulis dalam Forth untuk berbagai aplikasi ruang angkasa, misalnya pesawat ruang angkasa Philae yang mendarat di komet. Banyak proyek Forth yang bisa dilihat di sini.  Sebelum Apple beralih ke Intel, komputer Apple memakai Open firmware (seperti BIOS di PC) yang menggunakan Forth.

Saat ini saya belum menemukan daftar terbaru di mana Forth masih dipakai secara komersial atau dalam skala besar. Namun demikian, saya melihat masih banyak proyek open source yang aktif untuk microcontroller dan proyek IOT.

Interpreter/Compiler Forth

Saat ini ada banyak implementasi Forth untuk berbagai sistem baik komersial maupun open source. Untuk implementasi open source, daftar yang cukup lengkap bisa dilihat di Github. Awalnya Forth dirancang untuk berjalan di komputer tanpa operating system, jadi mungkin ada beberapa konsep yang agak membingungkan atau agak aneh jika dilihat dari kacamata sistem operasi saat ini.

Salah satu demo Gforth di Android

Pada artikel ini saya akan menggunakan Gforth yang dapat berjalan di Windows/Linux/OS X dan juga di Android. Saya juga akan membahas sedikit Forth untuk embedded system.

Jika Anda ingin mencoba versi Android, setelah instalasi, pergi ke settings untuk mengaktifkan permision “Storage”. Tanpa permission ini GForth tidak bisa mengekstrak contoh program ke /sdcard/gforth.

Aktifkan permission storage

Hello World

Bentuk hello world sederhana dengan gforth seperti ini.

." Hello World" cr

Perhatikan bawah tidak ada spasi antara . (titik) dan ” (petik), lalu ada spasi sebelum Hello (ini sangat penting ketika mencoba contoh tersebut). Contoh Hello World kurang menunjukkan keunikan Forth, jadi untuk contoh berikut, saya menggunakan mode interaktif Gforth untuk operasi matematika.

Untuk menambahkan dua buah angka dan mencetak hasilnya:

3 4 + .

Dalam contoh di atas, yang terjadi adalah: 3 akan dipush ke stack, 4 akan dipush ke stack, + (plus) akan mengambil 2 angka di stack dan menjumlahkan keduanya dan menaruh hasilnya di stack, . (titik) akan mengambil angka dari stack dan mencetak di layar.

Agar outputnya terlihat terpisah dari baris lain, kita bisa menambahkan cr, seperti ini (akhiri ini dengan menekan ENTER):

cr 3 4 + .

Forth memakai istilah “word” untuk semua simbol dan bilangan yang diparse oleh interpreter/compiler. Di posting ini saya tidak akan menerjemahkan “word” sebagai “kata”, supaya tidak bingung ketika membaca dokumentasi Forth.

Secara umum interpreter Forth bekerja seperti ini: interpreter akan memecah input menjadi word dan menjalankan aksi dari tiap word. Semua simbol dan bilangan yang dipisahkan spasi/enter dianggap sebagai satu word (jadi “123” adalah 1 word, “.” adalah satu word).

Aksi untuk word yang bisa dikonversi mernjadi bilangan adalah memasukkan (push) bilangan ke stack, aksi untuk operasi matematika adalah mengambil isi 2 elemen stack teratas, melakukan operasinya dan menaruh hasilnya di stack. Implementasi Forth ada yang case sensitive dan ada yang tidak. GForth merupakan contoh yang tidak, jadi besar kecil dianggap sama. Dalam Standard Forth, semua word standar harus dalam UPPERCASE.

Operasi matematika ini: (2 + 3) * 5 bisa dituliskan di forth seperti ini:

2 3 + 5 *

Sebagian orang sangat menyukai notasi postfix ini dan dulu ada banyak kalkulator yang memakai Reverse Polish Notation (RPN), bahkan semua kalkulator dari HP versi awal memakai notasi RPN.  Meskipun aneh, tapi notasi seperti ini hemat menekan tombol tidak butuh kurung untuk presedensi operator (seperti pada contoh di atas).

Bagaimana kita tahu apa saja yang ada di stack? kita bisa menginspeksi stack dengan word “.S”, ini akan memprint isi stack, contohnya

1 2 3 .S \ ada 3 isi stack; 1 2 3

Perhatikan backslash  adalah cara untuk membuat komentar (perlu diikuti spasi karena backslash adalah sebuah word) dan saya pakai untuk menjelaskan jadi jika ingin mengikuti contohnya di interpreter abaikan \ dan seterusnya. Untuk contoh di atas, cukup ketik:

1 2 3 .S

Jika kita jumlahkan dua isi stack teratas (2 dan 3) dengan +, lalu kita print lagi isi stacknya:

1 2 3 .S \ isi stack: 1 2 3
+ .S \ isi stack: 1 5

Top of stack menjadi 5.

Di interpreter Forth yang lain ada yang langsung mencetak isi stack di prompt (misalnya PunyForth), jadi kita tidak perlu melakukan apapun.

Ada banyak word yang mengoperasikan stack. Dalam dokumentasi  biasanya operasi dituliskan dalam bentuk komentar kurung buka ‘(‘ isi komentar lalu kurung tutup ‘)’.Komentar akan berisi stack sebelum dan sesudah word tersebut. Contohnya DUP untuk menduplikasi top of stack. Jika tadinya isi stack adalah A maka akan menjadi 2 yaitu A A. Di dokumentasi akan ditulis seperti ini:

DUP ( a -- a a)

Contoh lain, dokumentasi SWAP adalah seperti ini:

SWAP ( a b -- b a )

Dan OVER seperti ini:

OVER ( a b -- a b a )

Ada beberapa word di Forth yang sifatnya khusus, karena tidak mengoperasikan stack atau langsung dieksekusi, tapi akan melihat pada word berikutnya sampai batas tertentu. Kita mulai dari word pertama yang sifatnya khusus yaitu : (titik dua) untuk mendefinisikan word baru sampai ;

Contohnya kita ingin membuat word baru: tambahtujuh yang akan menambahkan 7 pada sebuah bilangan:

: tambahtujuh 7 + ;

Sekarang kita bisa melakukan ini

5 tambahtujuh .

dan hasilnya adalah 12. Untuk Anda yang penasaran dengan implementasi low level: Forth memiliki beberapa stack. Stack untuk data berbeda dengan stack yang dipakai untuk pemanggilan word (subrutin).

Di Forth, programmer mendefinisikan banyak word baru. Ini seperti subrutine/prosedur di sebuah bahasa. Setiap word hanya melakukan sedikit aksi. Dengan membuat word baru berdasarkan word sebelumnya, maka kita bisa membuat program yang terstruktur. Bisa dilihat bahwa di sini pendekatannya adalah bottom up.

Word : (titik dua) merupakan “defining word” dan kadang disebut juga “parsing word”, karena tidak seperti word biasa, word ini akan melihat pada input berikutnya, tidak hanya melihat stack dan mengeksekusi berdasarkan stack. Contoh defining word yang lain adalah adalah VARIABLE.

VARIABLE tanggal \ tanggal adalah variabel
12 tanggal ! \ set nilai tanggal menjadi 12
tanggal @ . \ akses variabel tanggal dan masukkan isinya ke stack, lalu print isi stack
tanggal ? \ langsung print isi variabel tanggal
7 tanggal +! \ tambahkan 7 ke nilai tanggal
tanggal ? \ sekarang tanggal menjadi 19

Konstanta juga bisa didefinisikan dengan CONSTANT.

144 CONSTANT LIMIT \ LIMIT nilainya 144

Loop yang diketahui batasnya bisa dibuat dengan

   FORMULA:
           limit index DO ... LOOP

Contoh untuk memprint kata “Bebek” 10 kali

: 10BEBEK 10 0 DO ." Bebek " CR LOOP ;
10BEBEK

Ada word bernama I (huruf i kapital) yang dipakai untuk menyalin isi stack untuk loop ke stack saat ini.

: 10BEBEK 10 0 DO I . ." Bebek " CR LOOP ; ok
10BEBEK

Jika kita mendefinisikan ulang word yang sudah ada, maka definisi terkhir yang dipakai, tapi definisi awal tidak dibuang.

Tentunya tidak semua hal bisa dikerjakan hanya dengan operasi stack. Forth juga memiliki control flow seperti IF dan LOOP. Bentuk IF di FORTH sintaksnya agak aneh dan tidak seperti di bahasa lain. Bentuknya adalah KONDISI IF AKSI THEN. Perlu diperhatikan bahwa menurut standar Forth semua control flow harus dimasukkan ke dalam definisi word

200 CONSTANT LIMIT
: lewatbatas LIMIT > IF cr ." Lewat batas" cr THEN ;

Untuk conditional loop kita bisa memakai BEGIN UNTIL jika pemeriksaan dilakukan di akhir aksi (seperti do while{}), atau BEGIN WHILE REPEAT jika pemeriksaan dilakukan di awal (seperti while () {} ). Jika kita ingin menghentikan loop di tengah (break), kita bisa menggunakan  LEAVE.

Berikut ini contoh sederhana dalam satu baris

: bebek 0 counter ! BEGIN counter ? ." Bebek" cr  1 counter +! counter @ 10 > UNTIL ; 

Tapi itu sulit dibaca, jadi biasanya ini akan dituliskan dengan banyak word supaya lebih jelas. Word pertama “reset” untuk mengeset counter menjadi 0, word “full?” akan mengecek apakah counter sudah bernilai lebih dari 10, word “next” akan menambah counter dengan 1.

VARIABLE counter
: reset 0 counter ! ;
: full? counter @ 10 > ;
: next 1 counter +! ;
: bebek reset BEGIN counter ? ." Bebek" cr next full? UNTIL ;

Literal string  didefinisikan dengan S”, seperti ini S" hello world" dan dicetak dengan type. Sebagai shortcut, kita bisa memakai ." string" untuk mencetak string langsung ke layar.

Kita bisa melihat definisi suatu word dengan “SEE”.

SEE lewatbatas
: lewatbatas
200 >
IF cr .\" Lewat batas" cr
THEN ; ok

Yang menarik, andaikan kodenya diimplementasikan dalam bahasa mesin, maka bahasa assemblynya akan ditampilkan.

Struktur Data

Saya tidak akan membahas panjang mengenai struktur data di Forth, karena Forth hanya memiliki primitif semacam malloc di C, dan sisanya adalah implementasi oleh programmer.

Forth bekerja dalam satuan CELL. Ukuran satu sel tergantung implementasi, tapi di Standar Forth ditentukan minimal 16 bit. Masalahnya tidak semua tipe data muat dalam 1 sel (misalnya tipe data double butuh 8 byte sedangkan integer 32 bit hanya butuh 4 byte), jadi kadang di Forth kita perlu mengoperasikan sepasang nilai di stack. Segala operasi ini dilakukan dengan Word yang berawalan dengan angka 2, misalnya 2DUP.

Word yang dipakai untuk mengalokasikan memori adalah ALLOT. Setelah memori dialokasikan, maka isinya bisa diakses dengan operasi aritmatika terhadap pointer (seperti di C). Contohnya untuk mengalokasikan Array 4 sel, lalu mengisi sel kedua dengan nilai 5:

VARIABLE a 4 CELLS ALLOT
5 a 1 CELLS + !

Ini mirip di C jika kita menggunakan aritmatika pointer.

a = (int*)malloc(4);
*(a+1) = 5;

Forth untuk embedded system

Karena Forth bisa berjalan langsung di microcontroller — termasuk juga mode interaktifnya — maka Forth memudahkan prototyping. Jika menggunakan C maka kita harus menulis program di desktop, mengcompile programnya (bisa beberapa detik hingga puluhan detik), mentransfer ke microcontroller dan melihat hasilnya. Dengan interpreter interaktif di microcontroller, maka kita bisa langsung menggunakan serial port dan langsung memprogram.

Sebagai catatan, kenyamanan ini dirasakan banyak orang sehingga sekarang ini sudah ada beberapa proyek untuk menjalankan bahasa yang lebih “modern” seperti Lua, Python dan bahkan JavaScript di berbagai microcontroller yang memiliki RAM besar. Tapi dari pengalaman saya, berbagai implementasi yang saya coba cukup lambat karena tidak dirancang dari awal untuk berjalan di embedded system. Bahasa-bahasa tersebut juga tidak dapat dijalankan di sistem yang RAM-nya sangat kecil (misalnya Forth untuk STM8S bisa berjalan dengan RAM satu kilobyte).

Wemos D1 Mini

Jika ingin mencoba Forth di embedded system, mungkin saat ini yang paling mudah dan murah adalah ESP8266, versi Wemos D1 hanya 2.75 USD dan hanya perlu kabel micro USB untuk koneksi ke komputer. Proses memasukkan interpreter Forth dan melakukan koneksi serial tidak perlu hardware lain.

Board STM8S

Board yang paling murah dan bisa menjalankan Forth setahu saya adalah STM8S yang harganya kurang dari 1 USD (termurah saat ini 0.86 USD, harga satuan, sudah termasuk ongkir dari China, bisa lebih murah jika beli banyak). Meskipun paling murah, tapi untuk memasukkan interpreter Forth kali pertama perlu memakai ST-LINK (Sekitar 2 USD) dan untuk memprogram secara interaktif butuh USB to Serial (1 USD). Setelah program berjalan, kedua hardware tersebut tidak dibutuhkan lagi. STM8 eForth bahkan mendukung background task di microcontroller ini.

Penutup

Artikel ini hanya memperkenalkan “kulitnya” Forth saja, banyak hal yang tidak saya jelaskan. Jika Anda ingin membaca lebih lanjut, ada banyak resource di Internet, misalnya bisa mulai dari ini:

Starting FORTH

 
Bahkan standard Forth juga bisa dibaca. Versi Draftnya (yang katanya 99.9% sama dengan versi finalnya) bisa dibaca di sini. Isi standarnya cukup ringkas, hanya penjelasan word yang termasuk dalam standar yang membuat dokumennya menjadi panjang.

Sebagai tambahan juga, saya menemukan ada juga seseorang di Indonesia yang memakai Forth untuk produknya: https://telinks.wordpress.com/.