Kunci jawaban 12 Latihan Bahasa Egg. Dijalankan bersama kode interpreter Egg dari babnya.

12.1 Array

topScope.array = (...values) => values;
topScope.length = array => array.length;
topScope.element = (array, i) => array[i];

Kenapa: array cukup mengembalikan rest argument-nya, yang memang sudah berupa array JavaScript. length dan element tinggal meneruskan ke operasi array biasa. Ini contoh “menumpang” fitur JavaScript: fitur array Egg didukung langsung oleh array JavaScript di baliknya.

→ Soal: 12 Latihan Bahasa Egg > 12.1 Array

12.2 Closure

Jawaban: bentuk khusus diberi scope lokal tempat ia dievaluasi. Fungsi yang dikembalikan fun menyimpan referensi ke argumen scope itu, dan waktu fungsi dipanggil, ia membuat scope lokalnya dengan Object.create(scope). Artinya prototype scope lokal adalah scope tempat fungsi dibuat. Jadi waktu fun(b, ...) bagian dalam dipanggil, ia masih bisa mencari a — karena a ada di scope f yang jadi prototype (leluhur) scope-nya. Itulah closure: fungsi membawa lingkungan tempat ia lahir. Sama persis idenya dengan closure JavaScript di 03 Fungsi > Closure.

→ Soal: 12 Latihan Bahasa Egg > 12.2 Closure

12.3 Komentar

function skipSpace(string) {
  let lewat = string.match(/^(\s|#.*)*/);
  return string.slice(lewat[0].length);
}

Kenapa: pola (\s|#.*)* artinya “(whitespace atau # diikuti sisa baris), diulang berapa pun kali”. Karena . gak cocok baris baru, komentar #... berhenti di ujung barisnya, lalu \s menelan baris barunya dan pola bisa lanjut ke komentar berikutnya — jadi beberapa komentar beruntun tertangani. lewat[0] adalah seluruh rentangan whitespace-dan-komentar di awal, dan kita potong sepanjang itu.

→ Soal: 12 Latihan Bahasa Egg > 12.3 Komentar

12.4 Memperbaiki Scope

specialForms.set = (args, scope) => {
  if (args.length != 2 || args[0].type != "word") {
    throw new SyntaxError("Salah pakai set");
  }
  let nama = args[0].name;
  let nilai = evaluate(args[1], scope);
 
  for (let s = scope; s; s = Object.getPrototypeOf(s)) {
    if (Object.hasOwn(s, nama)) {
      s[nama] = nilai;
      return nilai;
    }
  }
  throw new ReferenceError(`Set variabel tak dikenal: ${nama}`);
};

Kenapa: loop for menaiki rantai prototype — s = scope mulai dari scope lokal, s = Object.getPrototypeOf(s) naik ke scope luar, berhenti saat s jadi null. Object.hasOwn (bukan in) penting: in juga melihat prototype, jadi gak bisa membedakan binding milik scope itu dengan yang diwarisi. Beda dari define yang selalu menulis ke scope lokal, set mencari scope yang benar-benar memiliki binding itu dan menulis di situ; kalau tak ada di mana pun, lempar ReferenceError.

→ Soal: 12 Latihan Bahasa Egg > 12.4 Memperbaiki Scope