Latihan buat 12 Proyek Bahasa Pemrograman. Semua latihan ini dikerjakan bersama kode interpreter Egg dari babnya (parser, evaluate, specialForms, topScope). Jawaban di 12 Kunci Jawaban Bahasa Egg.

12.1 Array

Tambahkan dukungan array ke Egg dengan tiga fungsi di topScope: array(...values) membuat array, length(array) panjangnya, element(array, n) mengambil elemen ke-n.

Kode awal:

topScope.array = "...";
topScope.length = "...";
topScope.element = "...";

Petunjuk: wakili array Egg dengan array JavaScript biasa. Ketiganya harus fungsi. Dengan rest argument, array bisa sangat singkat. Konsep di 12 Proyek Bahasa Pemrograman > Environment.

→ Jawaban: 12 Kunci Jawaban Bahasa Egg > 12.1 Array

12.2 Closure

Cara fun didefinisikan membuat fungsi Egg bisa mengakses scope sekitarnya (closure). Program ini membuktikannya:

run(`
do(define(f, fun(a, fun(b, +(a, b)))),
   print(f(4)(5)))
`);
// → 9

Jelaskan mekanisme apa yang membuat ini jalan. (Pertanyaan pemahaman, bukan menulis kode.)

Petunjuk: perhatikan bagaimana fungsi yang dikembalikan fun membuat scope lokalnya. Konsep di 12 Proyek Bahasa Pemrograman > Fungsi dan 03 Fungsi > Closure.

→ Jawaban: 12 Kunci Jawaban Bahasa Egg > 12.2 Closure

12.3 Komentar

Buat Egg mendukung komentar: tiap ketemu #, sisa baris diabaikan (seperti //). Ubah skipSpace supaya melewati komentar seperti whitespace.

Kode awal:

// ubah skipSpace ini...
function skipSpace(string) {
  let first = string.search(/\S/);
  if (first == -1) return "";
  return string.slice(first);
}
 
console.log(parse("# hello\nx"));
// → {type: "word", name: "x"}
console.log(parse("a # one\n   # two\n()"));
// → {type: "apply", operator: {type: "word", name: "a"}, args: []}

Petunjuk: pastikan menangani beberapa komentar berturut-turut dengan whitespace di antaranya. Pakai regex yang cocok “whitespace atau komentar, nol atau lebih kali”, lalu potong sepanjang yang cocok. Konsep di 12 Proyek Bahasa Pemrograman > Parsing.

→ Jawaban: 12 Kunci Jawaban Bahasa Egg > 12.3 Komentar

12.4 Memperbaiki Scope

Saat ini define selalu bikin binding di scope lokal, jadi gak bisa mengubah binding di scope luar. Tambahkan bentuk khusus set yang memberi nilai baru ke binding yang sudah ada (mencari ke scope luar kalau perlu), dan melempar ReferenceError kalau binding-nya tak ada sama sekali.

Kode awal:

specialForms.set = (args, scope) => {
  // tulis kodemu di sini
};
 
run(`
do(define(x, 4),
   define(setx, fun(val, set(x, val))),
   setx(50),
   print(x))
`);
// → 50
run(`set(quux, true)`);
// → semacam ReferenceError

Petunjuk: loop menaiki rantai prototype dengan Object.getPrototypeOf, dan tiap scope cek Object.hasOwn (bukan in, karena in juga melihat prototype). Kalau ketemu, set dan kembalikan nilainya; kalau sampai null belum ketemu, lempar error. Konsep di 12 Proyek Bahasa Pemrograman > Bentuk Khusus.

→ Jawaban: 12 Kunci Jawaban Bahasa Egg > 12.4 Memperbaiki Scope