İçeriğe geç
academia.sh

Ders 06 / 27

Yapılandırma Dili

Kendi küçük yapılandırma dilimiz tanımlanır ve ayrıştırıcısı yazılır; dilin taşıdığı bildirim, ifade ve tip yapıları ayrı ayrı sayılır. Tip bildirimi olmayan kayıtta on bir kusurun kaçının ancak uygulamada göründüğü, tip eklenince kaçının ayrıştırma anında yakalandığı ve aynı hedefin üç yazımında satır sayısıyla okuma sıçramasının ters yönde hareket ettiği ölçülür.

İçindekiler

Bir önceki konu sapmayı sayılabilir kıldı; ölçülmeden kalan tek şey kaydın kendisiydi. Bu ders kendi küçük yapılandırma dilimizi tanımlar, node ile bir ayrıştırıcı yazar ve üç şeyi ölçer: dilin taşıdığı bildirim, ifade ve tip yapıları; tipsiz kayıt ile tipli kaydın aynı kusurları nerede yakaladığı; aynı hedefin üç yazımının bedeli. Ölçüm bölgesel ölçüm ağının batı ortamı üzerindedir ve kurgudur; dil, ayrıştırıcı ve hedef sistem node ile kurulmuş bir modeldir.

BS1. Kurgu kayıt batı ortamının iki kaynağını taşır: bir makine kümesi ve bir kuyruk. BS2. Tip bildirimi dilin yapısıdır, hedef sistemin değil; uygulamadan önce koşar. BS3. Tip denetimi ifadenin sonucunu hesaplamaz, yalnız sabit yazılmış değeri sınar. BS4. Hedef sistem modeli kayıttaki değeri olduğu gibi yazar, yalnız sayı beklenen yerde sayı olmayanı reddeder. BS5. Hedefe yazılan değer doğru kayıttakinden farklıysa ve hiçbir aşama bildirmiyorsa kusur sessiz sayılır. BS6. Sıçrama, bir alanın değerini bulmak için izlenen blok başvurusu sayısıdır.

Dilin Üç Yapısı

// dil.mjs — dilimizin sozcuk ayiricisi ve ayristiricisi.
const TUR = ["dize", "sayi", "ad", "imce", "yorum", "bilinmeyen"];
const IM = /"([^"]*)"|(-?\d+)|([a-z_][a-z0-9_]*)|([{}=,.()])|(#[^\n]*)|(\S)/g;

export function sozcukle(k) {
  const t = [];
  for (const m of k.matchAll(IM)) {
    const i = m.slice(1).findIndex((x) => x !== undefined);
    if (i === 5) throw new Error(`cozulemeyen im "${m[0]}"`);
    if (i !== 4) t.push({ tur: TUR[i], deger: m[i + 1] });
  }
  return t;
}

export function ayristir(kaynak) {
  const t = sozcukle(kaynak); let i = 0;
  const bak = () => t[i], al = () => t[i++];
  const bekle = (d) => { const x = al();
    if (!x || x.deger !== d) throw new Error(`"${d}" bekleniyordu, "${x ? x.deger : "son"}" bulundu`); };
  const govde = () => { const h = {};                // blok govdesi ve harita ayni yol
    while (bak().deger !== "}") { const a = al().deger; bekle("="); h[a] = ifade();
      if (bak().deger === ",") al(); }
    al(); return h; };
  function ifade() {
    const x = al();
    if (!x) throw new Error("ifade bitmeden kayit bitti");
    if (x.tur === "sayi") return { d: "sayi", deger: Number(x.deger) };
    if (x.tur === "dize") return { d: "dize", deger: x.deger };
    if (x.deger === "{") return { d: "harita", alanlar: govde() };
    if (x.tur !== "ad") throw new Error(`beklenmeyen "${x.deger}"`);
    if (bak() && bak().deger === "(") { al(); const arg = [];
      while (bak().deger !== ")") { arg.push(ifade()); if (bak().deger === ",") al(); }
      al(); return { d: "cagri", ad: x.deger, arg }; }
    const yol = [x.deger];
    while (bak() && bak().deger === ".") { al(); yol.push(al().deger); }
    return { d: "yol", yol };
  }
  const b = [];
  while (i < t.length) {
    const tur = al().deger, etiket = [];
    while (bak() && bak().tur !== "imce") etiket.push(al().deger);
    bekle("{");
    b.push({ tur, etiket, alanlar: govde(), anahtar: tur === "kaynak" ? etiket.join(".") : tur });
  }
  return b;
}
// kayit.mjs — bati ortaminin kurgu kaydi.
export const ORTAM = `ortam "bati" { bolge = "bati"  toplayici = 6  esik_ms = 900  saklama = 30 }`;
export const GOVDE = `
kaynak makine "toplayici" {
  ad        = birlestir(ortam.bolge, "-toplayici")
  sayi      = ortam.toplayici
  cekirdek  = 4
  bellek_gb = 8
  etiket    = { gorev = "sayac-okuma", bolge = ortam.bolge }
}
kaynak kuyruk "sayac" {
  ad         = birlestir(ortam.bolge, "-sayac-kuyrugu")
  bolme      = carp(ortam.toplayici, 2)
  saklama_gn = ortam.saklama
  bicim      = "ikili"
  esik_ms    = ortam.esik_ms
}`;
export const TIP = `
tip ortam  { bolge = dize  toplayici = sayi  esik_ms = sayi  saklama = sayi }
tip makine { ad = dize  sayi = aralik(1, 64)  cekirdek = aralik(1, 64)
             bellek_gb = aralik(1, 256)  etiket = harita }
tip kuyruk { ad = dize  bolme = aralik(1, 128)  saklama_gn = aralik(1, 365)
             bicim = kume("ikili", "metin")  esik_ms = sayi }`;

Dilde tek bildirim biçimi vardır: tür sözcüğü, etiketler, ayraç içinde alan atamaları. Sağ taraf sabit, başka bir bloğun alanına başvuru, bir çağrı ya da bir harita olabilir; işleç yoktur, çarpma da bir çağrıdır. Ölçüyü taşıyan en küçük biçim budur. Tip bildirimi de aynı söz dizimiyle yazılır.

Tipsiz Kayıt, Tipli Kayıt

// denet.mjs — ad cozumu, tip denetimi, uygulama modeli.
import { ayristir } from "./dil.mjs";

const ISLEV = { birlestir: (...a) => a.join(""), carp: (a, b) => a * b, sec: (h, k) => h[k] };
const alt = (e) => e.d === "cagri" ? e.arg : e.d === "harita" ? Object.values(e.alanlar) : [];
export const yaz = (g, ...s) => console.log(s.map((x) => String(x).padEnd(g)).join("").trimEnd());
export const dugum = (e) => [e, ...alt(e).flatMap(dugum)];
export const derin = (e) => 1 + Math.max(0, ...alt(e).map(derin));
export const sabit = (e) => e.d === "sayi" || e.d === "dize";
export const cins = (b) => b.tur === "kaynak" ? b.etiket[0] : b.tur;
export const dizinle = (b) => new Map(b.map((x) => [x.anahtar, x]));

export const semaCikar = (m) => Object.fromEntries(ayristir(m).map((b) => [b.etiket[0],
  Object.fromEntries(Object.entries(b.alanlar).map(([a, e]) => [a, e.d === "yol" ? { taban: e.yol[0] }
    : e.ad === "aralik" ? { taban: "sayi", aralik: e.arg.map((x) => x.deger) }
    : { taban: "dize", kume: e.arg.map((x) => x.deger) }]))]));

export function coz(yol, dizin) {
  for (let n = yol.length - 1; n >= 1; n--) {
    const b = dizin.get(yol.slice(0, n).join(".")), a = yol.slice(n).join(".");
    if (!b) continue;
    if (!(a in b.alanlar)) throw new Error(`${b.anahtar} icinde ${a} yok`);
    return { blok: b, alan: a };
  }
  throw new Error(`${yol.join(".")} bir bloga denk gelmiyor`);
}

export function tipDenet(bloklar, sema) {            // yalniz tipli kayitta kosar
  const h = [];
  for (const b of bloklar) {
    const t = sema[cins(b)];
    for (const a of Object.keys(t)) if (!(a in b.alanlar)) h.push(`${b.anahtar}.${a} eksik`);
    for (const [a, e] of Object.entries(b.alanlar)) {
      const k = t[a], y = `${b.anahtar}.${a}`;
      if (!k) h.push(`${y} tipsiz`);
      else if (!sabit(e)) continue;                  // hesaplanan deger kayittan okunamaz
      else if (e.d !== k.taban) h.push(`${y} ${k.taban} degil`);
      else if (k.aralik && (e.deger < k.aralik[0] || e.deger > k.aralik[1])) h.push(`${y} aralik disi`);
      else if (k.kume && !k.kume.includes(e.deger)) h.push(`${y} kume disi`);
    }
  }
  return h;
}

export function degerle(e, d) {
  if (sabit(e)) return e.deger;
  if (e.d === "harita") return Object.fromEntries(Object.entries(e.alanlar).map(([a, v]) => [a, degerle(v, d)]));
  if (e.d === "cagri") return ISLEV[e.ad](...e.arg.map((x) => degerle(x, d)));
  const c = coz(e.yol, d);
  return degerle(c.blok.alanlar[c.alan], d);
}

export function uygula(bloklar, sema) {              // hedef sistem modeli
  const d = dizinle(bloklar);
  return Object.fromEntries(bloklar.filter((x) => x.tur === "kaynak").flatMap((b) =>
    Object.entries(b.alanlar).map(([a, e]) => {
      const v = degerle(e, d), y = `${b.anahtar}.${a}`;
      if (sema[cins(b)][a]?.taban === "sayi" && !Number.isFinite(v)) throw new Error(`${y} sayi degil`);
      return [y, typeof v === "object" ? JSON.stringify(v) : v];
    })));
}
// hata.mjs — uc yapi ve on bir kusur sayilir.
import { ayristir } from "./dil.mjs";
import { ORTAM, GOVDE, TIP } from "./kayit.mjs";
import { dugum, sabit, dizinle, coz, tipDenet, uygula, semaCikar, yaz } from "./denet.mjs";

const SEMA = semaCikar(TIP), KAYIT = ORTAM + GOVDE;
const bloklar = ayristir(KAYIT), TABAN = uygula(bloklar, SEMA);
const atama = bloklar.flatMap((x) => Object.values(x.alanlar));
const ifade = atama.flatMap(dugum), tipAlan = Object.values(SEMA).flatMap(Object.values);
const say = (t) => `${ifade.filter((e) => e.d === t).length} ${t}`;
console.log(`bildirim ${bloklar.length} blok, ${atama.length} atama, ${atama.filter((e) => !sabit(e)).length} hesaplanan`);
console.log(`ifade    ${ifade.length} dugum = ` + ["sayi", "dize", "yol", "cagri", "harita"].map(say).join(" + "));
console.log(`tip      ${Object.keys(SEMA).length} cins, ${tipAlan.length} alan bildirimi, ` +
  `${tipAlan.filter((k) => k.aralik || k.kume).length} kisitli\n`);

const boz = (alan, yeni) => yeni === null            // tek satirlik kusur
  ? KAYIT.replace(new RegExp(`\n *${alan} *=[^\n]*`), "")
  : KAYIT.replace(new RegExp(`(\n *)${alan}( *=)[^\n]*`),
    yeni[0] === "@" ? `$1${yeni.slice(1)}` : `$1${alan}$2 ${yeni}`);
const KUSUR = [
  ["tur uyusmazligi", "cekirdek", '"4"'], ["alan eksik", "bellek_gb", null],
  ["alan adi yanlis", "saklama_gn", "@saklama = 30"],
  ["kume disi deger", "bicim", '"ham"'],
  ["aralik disi sabit", "cekirdek", "128"], ["basvuru yazimi", "sayi", "ortam.toplayic"],
  ["olmayan alan", "esik_ms", "makine.toplayici.cekirdekler"],
  ["soz dizimi", "bicim", 'ikili"'], ["yanlis alana basvuru", "sayi", "ortam.esik_ms"],
  ["hesaplanan asim", "saklama_gn", "carp(ortam.saklama, 20)"],
  ["hesaplanan sifir", "esik_ms", "carp(ortam.esik_ms, 0)"],
];

function asama(metin, tipVar) {                      // kusur hangi asamada goruluyor
  let b;
  try { b = ayristir(metin); const d = dizinle(b);
    for (const x of b) for (const e of Object.values(x.alanlar))
      dugum(e).filter((y) => y.d === "yol").forEach((y) => coz(y.yol, d));
  } catch (e) { return ["ayristirma", e.message]; }
  if (tipVar) { const t = tipDenet(b, SEMA); if (t.length) return ["tip", t[0]]; }
  let g; try { g = uygula(b, SEMA); } catch (e) { return ["uygulama: hata", e.message]; }
  const f = Object.keys(TABAN).filter((k) => TABAN[k] !== g[k]);
  return f.length ? ["uygulama: sessiz", `${f[0]} ${TABAN[f[0]]} yerine ${g[f[0]]}`] : ["gecti", "-"];
}

console.log(`taban kayit ${asama(KAYIT, true)[0]}, hedefe yazilan ${Object.keys(TABAN).length} alan`);
yaz(22, "kusur", "tipsiz kayit", "tipli kayit", "tipli kayitta uygulamadaki iz");
const sayac = { A: {}, B: {} };
for (const [ad, alan, yeni] of KUSUR) {
  const m = boz(alan, yeni), [a] = asama(m, false), [b, iz] = asama(m, true);
  sayac.A[a] = (sayac.A[a] || 0) + 1; sayac.B[b] = (sayac.B[b] || 0) + 1;
  yaz(22, ad, a, b, b[0] === "u" ? iz : "");
}
console.log();
yaz(22, "asama", "tipsiz", "tipli");
for (const a of ["ayristirma", "tip", "uygulama: hata", "uygulama: sessiz"])
  yaz(22, a, sayac.A[a] || 0, sayac.B[a] || 0);
bildirim 3 blok, 14 atama, 7 hesaplanan
ifade    22 dugum = 6 sayi + 5 dize + 7 yol + 3 cagri + 1 harita
tip      3 cins, 14 alan bildirimi, 6 kisitli

taban kayit gecti, hedefe yazilan 10 alan
kusur                 tipsiz kayit          tipli kayit           tipli kayitta uygulamadaki iz
tur uyusmazligi       uygulama: hata        tip
alan eksik            uygulama: sessiz      tip
alan adi yanlis       uygulama: sessiz      tip
kume disi deger       uygulama: sessiz      tip
aralik disi sabit     uygulama: sessiz      tip
basvuru yazimi        ayristirma            ayristirma
olmayan alan          ayristirma            ayristirma
soz dizimi            ayristirma            ayristirma
yanlis alana basvuru  uygulama: sessiz      uygulama: sessiz      makine.toplayici.sayi 6 yerine 900
hesaplanan asim       uygulama: sessiz      uygulama: sessiz      kuyruk.sayac.saklama_gn 30 yerine 600
hesaplanan sifir      uygulama: sessiz      uygulama: sessiz      kuyruk.sayac.esik_ms 900 yerine 0

asama                 tipsiz                tipli
ayristirma            3                     3
tip                   0                     5
uygulama: hata        1                     0
uygulama: sessiz      7                     3

Üç blok on dört atama taşır; yedisi hesaplanan ifadedir, yirmi iki ifade düğümünün on biri sabittir. Tip tarafı üç cins için on dört alan bildirimi yazar — aynı cinsten on kaynak yazılsaydı aynı on dört bildirim yüz kırk atamayı denetlerdi. Tipin bedeli cins başına ödenir, kaynak başına değil.

Üç kusur tipten bağımsız görünür: bozuk söz dizimi, hiçbir bloğa denk gelmeyen başvuru, var olan bloğun olmayan alanı; bunlar için ad çözümü yeter. Kalan sekiz kusur tipsiz kayıtta uygulamaya iner ve yedisi hata vermeden hedefe farklı bir değer yazar. Tip bildirimi beşini ayrıştırma anına çeker; uygulamaya inen kusur üçe, sessiz kusur üçe iner. Kazanç mesafedir: ayrıştırmada yakalanan kusur hiçbir kaynağa dokunmaz.

Sona kalan üçünde değer sabit değil ifadedir: altı yerine dokuz yüz makine, otuz gün yerine altı yüz gün, dokuz yüz milisaniyelik eşik yerine sıfır. Tip üçünün de tabanını doğru bulur, çünkü hepsi sayıdır; aralığı sınayamaz, çünkü değer kayıt okunurken henüz yoktur. Kaydın kapsamadığı şey budur.

İfade Gücü ile Okunabilirlik

// bicem.mjs — ayni hedefin uc yazimi olculur.
import { ayristir } from "./dil.mjs";
import { GOVDE, TIP } from "./kayit.mjs";
import { semaCikar, dugum, derin, sabit, coz, dizinle, cins, uygula, yaz } from "./denet.mjs";

const SEMA = semaCikar(TIP), O = ["bati", "dogu", "merkez"], ADET = { bati: 6, dogu: 6, merkez: 4 };
const ORTA = GOVDE.split("\n}\n")[0] + "\n}";        // kayittaki makine blogu, oldugu gibi
const ortaOrtam = (o) => `ortam "${o}" { bolge = "${o}"  toplayici = ${ADET[o]} }`;
const sikiOrtam = (o) => `ortam "${o}" { bolge = "${o}" }`;
const DUZ = `
kaynak makine "toplayici" {
  ad        = "bati-toplayici"
  sayi      = 6
  cekirdek  = 4
  bellek_gb = 8
  etiket    = { gorev = "sayac-okuma", bolge = "bati" }
}`;
const SIKI = `
yerel "olcek" {
  taban    = 2
  kat      = { bati = 3, dogu = 3, merkez = 2 }
  cekirdek = { bati = 4, dogu = 4, merkez = 4 }
}
kaynak makine "toplayici" {
  ad        = birlestir(ortam.bolge, "-toplayici")
  sayi      = carp(yerel.taban, sec(yerel.kat, ortam.bolge))
  cekirdek  = sec(yerel.cekirdek, ortam.bolge)
  bellek_gb = carp(yerel.taban, carp(sec(yerel.cekirdek, ortam.bolge), 1))
  etiket    = { gorev = "sayac-okuma", bolge = ortam.bolge }
}`;

const satir = (m) => m.split("\n").filter((s) => s.trim()).length;
const sicrama = (e, d) => dugum(e).filter((x) => x.d === "yol")
  .reduce((t, x) => { const c = coz(x.yol, d); return t + 1 + sicrama(c.blok.alanlar[c.alan], d); }, 0);
const es = (m) => JSON.stringify(uygula(ayristir(m), SEMA));

const olc = (ad, metinler, sat, per) => {
  const a = metinler.flatMap((m) => { const b = ayristir(m), d = dizinle(b);
    return b.filter((x) => x.tur === "kaynak").flatMap((k) => Object.entries(k.alanlar).map(([n, e]) =>
      [derin(e), sicrama(e, d), (SEMA[cins(k)][n].aralik || SEMA[cins(k)][n].kume) && !sabit(e) ? 1 : 0])); });
  const en = (i) => Math.max(...a.map((x) => x[i]));
  yaz(14, ad, sat, per, en(0), en(1), a.reduce((s, x) => s + x[2], 0));
};

console.log(`uc yazim ayni hedefi mi uretiyor: ${es(DUZ) === es(ortaOrtam("bati") + ORTA) &&
  O.every((o) => es(sikiOrtam(o) + SIKI) === es(ortaOrtam(o) + ORTA)) ? "evet" : "hayir"}\n`);
yaz(14, "yazim", "satir", "ortam basina", "en derin", "sicrama", "tip goremez");
olc("duz", [DUZ], satir(DUZ) * 3, satir(DUZ));
olc("orta", O.map((o) => ortaOrtam(o) + ORTA), satir(ORTA) + 3, 1);
olc("siki", O.map((o) => sikiOrtam(o) + SIKI), satir(SIKI) + 3, 1);
uc yazim ayni hedefi mi uretiyor: evet

yazim         satir         ortam basina  en derin      sicrama       tip goremez
duz           21            7             2             0             0
orta          10            1             2             1             3
siki          15            1             4             3             9

Üç yazım üç ortamda aynı on beş alanı aynı değerlerle üretir. Düz yazımda sıçrama yoktur ve tipin göremediği alan sıfırdır; bedeli üç ortam için yirmi bir satır ve her yeni ortam için yedi satırdır: on iki ortamda seksen dört.

Orta yazım aynı hedefi on satıra indirir ve yeni ortamın maliyetini bire düşürür; on iki ortamda on dokuz satır. Karşılığı bir sıçrama ve tipin göremediği üç alandır. Sıkı yazım ters yöne gider: satır sayısı on beşe çıkar, çünkü haritaları taşıyan ek blok da yazılmıştır; en derin ifade dörde, sıçrama üçe, tipin göremediği alan dokuza çıkar — on beş alanın dokuzu kayıt okunurken sınanamaz. İfade gücü tekrarı azaltır, satırı azaltmaz, denetlenebilirliği kesin olarak azaltır.

Özet

  • Kurgu kayıt 3 blok, 14 atama, 22 ifade düğümü taşıdı; 7 atama hesaplanandı ve tip 3 cins için 14 alan bildirimi yazdı.
  • Tipsiz kayıtta 11 kusurun 3’ü ayrıştırmada görüldü, 8’i uygulamaya indi; 7’si hata vermeden hedefe farklı değer yazdı.
  • Tip bildirimi o 8’in 5’ini ayrıştırma anına çekti; uygulamaya inen kusur 3’e, sessiz kusur 3’e indi.
  • Kalan 3 kusurun değeri ifadeydi; 6 yerine 900, 30 gün yerine 600 gün yazıldı, 900 ms eşik 0 oldu.
  • Üç yazım 21, 10 ve 15 satır tuttu; sıçrama 0, 1 ve 3, tipin göremediği alan 0, 3 ve 9 oldu; yeni ortamın maliyeti 7 satıra karşı 1 satır.

Sonraki Adım

Buradaki hedef sistem beş satırlık bir modeldi. Gerçekte bir kaynağın yaratılması, okunması, güncellenmesi ve silinmesi ayrı davranışlardır ve bunları kayda bağlayan katmanın adı sağlayıcıdır. Bir sonraki ders sağlayıcıyı bir arayüz olarak modeller: hangi alan değişince kaynak yerinde güncellenemeyip yeniden oluşturuluyor, sağlayıcı sürümü değiştiğinde kaç alanın varsayılanı kayıyor.

İlerlemeni kaydetmek ve not almak için Giriş yap

Notlarım

Not almak için giriş yapmalısın.

Aramak için yazmaya başlayın.

↑↓ Esc gezin · aç · kapat