---
title: 'Go Komut Ailesi'
source: 'https://academia.sh/tr/kurslar/go-temelleri/go-komut-ailesi'
course: 'Go Temelleri'
language: tr
updated: '2026-08-23T14:25:01+00:00'
license: 'CC BY-SA 4.0'
---

# Go Komut Ailesi

Dilin kendi komut ailesi, yazılmayan iki kuralı program içinden çağrılarak uyguluyor. Bir kaynak biçimlendiriciden geçirildiğinde bayt bayt karşılaştırılıyor ve 18 satırlık kaynağın 7 satırının değiştiği sayılıyor; inceleyici ise derleyicinin reddetmediği bir kalıbı (bir yazdırma çağrısının biçim damgasıyla verilen değerin tipinin uyuşmaması) yakalıyor. Sınırlayıcı ölçüm, biçimlendirilmiş ve biçimlendirilmemiş iki kaynağın aynı çalıştırıldığında aynı çıktıyı verdiğini gösterir — biçim değişiyor, anlam değişmiyor.

Önceki derste bir modülün sürümünü kimin seçtiği ölçüldü: seçimi isteklerin kendisi
belirliyordu, ayrı bir karar mekanizması yoktu. Bu derste soru bir düzey daha somutlaşıyor:
bir kaynağın **biçimi** ve uyduğu **kalıp** için yazılmayan kuralları kim uyguluyor? Yanıt
bu kez bir kişi ya da bir sözleşme belgesi değil — dilin kendi dağıtımıyla gelen komut
ailesi. Kursun ilk dersinden beri derleyici bu ailenin bir üyesi olarak program içinden
çağrılıyordu; bu ders aynı aileden iki üyeyi daha, biçimlendirmeyi ve incelemeyi, aynı
yöntemle çağırıyor.

Kursun ilk üç konusu boyunca bu komut ailesinden yalnız bir üye çağrıldı: derleyici. Onun
işi dardı — kaynağın çalıştırılabilir bir programa dönüşüp dönüşmeyeceğine karar vermek.
Ama bir kaynak metnin çevresinde derlenip derlenmemesinin ötesinde iki soru daha duruyor:
bu kaynak **nasıl görünüyor**, ve bu kaynak **hangi kalıplara** uyuyor? Derleyici ikisine de
kayıtsız — bir kaynağın girintisi, boşlukları, satır sonları neresi olursa olsun aynı
programı üretiyor, ve derleyicinin sözdizim kuralları bir kalıbın "iyi" ya da "kötü"
olduğuna hiç karar vermiyor, yalnız kalıbın geçerli olup olmadığına bakıyor. Bu iki
sorunun yanıtı bu yüzden derleyicinin dışında, aynı komut ailesinin iki ayrı üyesinde
duruyor.

## Bir Kaynağı Biçimlendiriciden Geçirmek

**PK12.** Bir kaynağın biçimi — girinti, boşluk, hizalama — dilin sözdizim kuralları
tarafından zorunlu kılınmaz; aynı anlama gelen bir kaynak birçok farklı biçimde yazılabilir.
**PK13.** Buna rağmen biçimlendirici, kendisine verilen herhangi bir geçerli kaynağı, tek
bir kanonik biçime dönüştürür ve bu dönüşüm kaynağın **anlamını** hiç değiştirmez.

Aşağıdaki yordamlar, önceki derslerde kurulan derleyici çağırma deseninin aynısını
kullanıyor: kaynak geçici bir modüle yazılıyor, komut çağrılıyor, sonuç okunuyor. Fark
şurada — bu kez çağrılan komut kaynağı **derlemiyor**, kaynağın kendisini **değiştiriyor**.
Bu yüzden ölçüm de farklı bir şey okuyor: önceki derslerde tek bir soru vardı
(derlendi mi, derlenmedi mi); burada iki kaynak — biçimlendirmeden önceki ve sonraki —
yan yana tutulup karşılaştırılıyor. Her ölçüm yine kendi tamamen boş geçici modülünde
çalışıyor ve `GOTOOLCHAIN=local` ortam değişkeniyle çağrılıyor, tıpkı önceki iki derste
olduğu gibi — çağrılan komut ne bir ağ isteği göndersin ne de önceki bir denemenin
bıraktığı bir dosyayı görsün diye.

```go
// araclar/araclar.go — go komut ailesini program icinden cagiran ortak yordamlar
package araclar

import (
	"os"
	"os/exec"
	"path/filepath"
	"strings"
)

func yeniModul(dizin string) {
	os.MkdirAll(dizin, 0o755)
	os.WriteFile(filepath.Join(dizin, "go.mod"), []byte("module sinama\n\ngo 1.24\n"), 0o644)
}

func ortam() []string {
	return append(os.Environ(), "GOTOOLCHAIN=local")
}

// Bicimlendir kaynagi gecici bir modulde bicimlendiricinin uzerinden gecirir; bicimlendirme
// oncesi ve sonrasi kaynagi, ayrica kac satirin degistigini dondurur.
func Bicimlendir(kaynak string) (oncesi, sonrasi string, degisenSatir int) {
	dizin, _ := os.MkdirTemp("", "sinama")
	defer os.RemoveAll(dizin)
	yeniModul(dizin)
	yol := filepath.Join(dizin, "main.go")
	os.WriteFile(yol, []byte(kaynak), 0o644)

	cmd := exec.Command("go", "fmt", "./...")
	cmd.Dir = dizin
	cmd.Env = ortam()
	cmd.Run()

	bayt, _ := os.ReadFile(yol)
	oncesi = kaynak
	sonrasi = string(bayt)

	oncekiSatirlar := strings.Split(strings.TrimRight(oncesi, "\n"), "\n")
	sonrakiSatirlar := strings.Split(strings.TrimRight(sonrasi, "\n"), "\n")
	if len(oncekiSatirlar) == len(sonrakiSatirlar) {
		for i := range oncekiSatirlar {
			if oncekiSatirlar[i] != sonrakiSatirlar[i] {
				degisenSatir++
			}
		}
	}
	return oncesi, sonrasi, degisenSatir
}

// CalistirVeCikisAl kaynagi gecici bir modulde calistirip standart ciktisini dondurur.
func CalistirVeCikisAl(kaynak string) string {
	dizin, _ := os.MkdirTemp("", "sinama")
	defer os.RemoveAll(dizin)
	yeniModul(dizin)
	os.WriteFile(filepath.Join(dizin, "main.go"), []byte(kaynak), 0o644)

	cmd := exec.Command("go", "run", ".")
	cmd.Dir = dizin
	cmd.Env = ortam()
	cikti, _ := cmd.Output()
	return strings.TrimRight(string(cikti), "\n")
}

// InceleyiciYakaliyorMu kaynagi gecici bir modulde derleyip inceleyiciden gecirir;
// derleyicinin gectigi ama inceleyicinin reddettigi kaliplari yakalamak icin kullanilir.
func InceleyiciYakaliyorMu(kaynak string) (derlendiMi, inceleyiciYakaladiMi bool) {
	dizin, _ := os.MkdirTemp("", "sinama")
	defer os.RemoveAll(dizin)
	yeniModul(dizin)
	os.WriteFile(filepath.Join(dizin, "main.go"), []byte(kaynak), 0o644)

	derle := exec.Command("go", "build", "-o", os.DevNull, "./...")
	derle.Dir = dizin
	derle.Env = ortam()
	derlendiMi = derle.Run() == nil

	incele := exec.Command("go", "vet", "./...")
	incele.Dir = dizin
	incele.Env = ortam()
	inceleyiciYakaladiMi = incele.Run() != nil
	return derlendiMi, inceleyiciYakaladiMi
}
```

`Bicimlendir`, kaynağı bir dosyaya yazıp biçimlendiriciyi çağırıyor, sonra aynı dosyayı
tekrar okuyor — biçimlendirme kaynağı **yerinde** değiştirdiği için öncesi ile sonrası iki
ayrı okuma. Satır sayıları eşitse (biçimlendirici satır eklemediyse ya da silmediyse)
karşılık gelen satırları teker teker karşılaştırıp kaçının değiştiğini sayıyor.
`InceleyiciYakaliyorMu` ise iki ayrı komutu art arda çağırıyor: önce derleyici, sonra
inceleyici — ikisinin de yalnız başarı/başarısızlık durumu okunuyor, hiçbir çıktı metni
saklanmıyor ya da yazdırılmıyor.

## Bir Kaynak, Bir Kalıp

Aşağıdaki ölçüm iki ayrı soruyu tek bir koşumda topluyor: aynı düzensiz kaynak
biçimlendiriciden geçirildiğinde ne kadarı değişiyor, ve ayrı bir kaynak inceleyiciden
geçirildiğinde derleyicinin gözden kaçırdığı kalıp yakalanıyor mu? İki ölçüm birbirinden
bağımsız — biri bir kaynağın görünüşünü, öbürü bir başka kaynağın geçerliliğini sınıyor —
ama ikisi de aynı yordamla, komutu program içinden çağırıp yalnız sonucu okuyarak
yürütülüyor.

```go
// main.go — bicimlendirici kaynagi degistiriyor, inceleyici derleyicinin reddetmedigi bir kalibi yakaliyor
package main

import (
	"fmt"
	"strings"

	"ders/araclar"
)

func main() {
	kaynak := "package main\n\n" +
		"import \"fmt\"\n\n" +
		"type Nokta struct {\n" +
		"  X int\n" +
		"  Y int\n" +
		"  Etiket string\n" +
		"}\n\n" +
		"func Topla(a int,b int) int {\n" +
		"      return a+b\n" +
		"}\n\n" +
		"func main() {\n" +
		"    n := Nokta{X: 1, Y: 2, Etiket: \"a\"}\n" +
		"    fmt.Println(n,Topla(2,3))\n" +
		"}\n"

	oncesi, sonrasi, degisenSatir := araclar.Bicimlendir(kaynak)
	toplamSatir := strings.Count(strings.TrimRight(oncesi, "\n"), "\n") + 1

	fmt.Println("-- bicimlendirici kaynagi degistiriyor mu --")
	fmt.Printf("bayt bayt ayni mi: %v\n", oncesi == sonrasi)
	fmt.Printf("toplam satir: %d, degisen satir: %d\n", toplamSatir, degisenSatir)

	fmt.Println()
	fmt.Println("-- sinirlayici olcum: bicim degisiyor, anlam degismiyor --")
	ciktiOncesi := araclar.CalistirVeCikisAl(oncesi)
	ciktiSonrasi := araclar.CalistirVeCikisAl(sonrasi)
	fmt.Printf("bicimlendirme oncesi kaynagin ciktisi: %s\n", ciktiOncesi)
	fmt.Printf("bicimlendirme sonrasi kaynagin ciktisi: %s\n", ciktiSonrasi)
	fmt.Printf("iki cikti ayni mi: %v\n", ciktiOncesi == ciktiSonrasi)

	fmt.Println()
	fmt.Println("-- inceleyici, derleyicinin reddetmedigi bir kalibi yakaliyor --")
	kalipliKaynak := "package main\n\n" +
		"import \"fmt\"\n\n" +
		"func main() {\n" +
		"\tfmt.Printf(\"%d\\n\", \"metin\")\n" +
		"}\n"
	derlendiMi, yakalandiMi := araclar.InceleyiciYakaliyorMu(kalipliKaynak)
	fmt.Printf("derlendi mi: %v\n", derlendiMi)
	fmt.Printf("inceleyici yakaladi mi: %v\n", yakalandiMi)
}
```

```
-- bicimlendirici kaynagi degistiriyor mu --
bayt bayt ayni mi: false
toplam satir: 18, degisen satir: 7

-- sinirlayici olcum: bicim degisiyor, anlam degismiyor --
bicimlendirme oncesi kaynagin ciktisi: {1 2 a} 5
bicimlendirme sonrasi kaynagin ciktisi: {1 2 a} 5
iki cikti ayni mi: true

-- inceleyici, derleyicinin reddetmedigi bir kalibi yakaliyor --
derlendi mi: true
inceleyici yakaladi mi: true
```

**PK14.** 18 satırlık kaynağın 7 satırı biçimlendiriciden geçtikten sonra değişiyor;
kalan 11 satır zaten kanonik biçimdeydi ve hiç dokunulmuyor. **PK15.** Değişen yedi satırın
altısında girinti karakteri boşluktan sekmeye dönüşüyor; bunlardan ikisinde yapı
alanlarının tipleri aynı sütunda hizalansın diye ayrıca boşluk ekleniyor, ikisinde bir
işlev çağrısındaki virgülden sonraki boşluk tamamlanıyor, birinde de bir toplama
işlecinin iki yanına boşluk giriyor — bu kategoriler bazı satırlarda üst üste biniyor
(bir girinti düzeltmesiyle bir hizalama aynı satırda birlikte geçebiliyor), ama hiçbiri
yeni bir satır eklemiyor ya da bir satırı siliyor.

Yazılmayan şey burada bir **tercih**: kaynağı yazan kişi girinti için boşluk mu sekme mi
kullanacağını, bir yapının alanlarını hizalayıp hizalamayacağını, virgülden sonra boşluk
bırakıp bırakmayacağını yazmadı. Biçimlendirici bu boşlukları kendi kanonik kararıyla
dolduruyor — ve bu, kursun sıfır değer bahsindeki "tanımlı" sınıfının bir başka görünümü:
yazılmayan bir şey, derleyicinin reddiyle değil, ayrı bir aracın kendi kuralıyla
dolduruluyor.

Bu kararın tek bir kanonik biçimde durması, birden çok kişinin aynı kaynak üzerinde
çalıştığı durumlarda ayrı bir sonuç doğuruyor. Girinti ve hizalama tercihe bağlı
kaldığında, aynı anlama gelen iki kaynak yalnız görünüş farkıyla birbirinden ayrışabiliyor
— ve bu fark, kaynağın önceki hâliyle karşılaştırıldığında (satır satır ne değiştiğine
bakıldığında) gürültü olarak görünüyor: hangi satırın **anlamca** değiştiği, hangisinin
yalnız **biçimce** değiştiği birbirine karışıyor. Biçimlendiricinin kanonik kararı bu
gürültüyü kaynağında kesiyor — yazan kişi hangi girintiyi seçerse seçsin, biçimlendiriciden
geçtikten sonra kaynak her zaman aynı biçime dönüşüyor, ve bu yüzden iki kaynak arasındaki
gerçek fark yalnız biçimlendirilmiş iki hâlin karşılaştırılmasıyla görülebiliyor.

## İnceleyici, Derleyicinin Geçtiği Bir Kalıbı Yakalıyor

**PK16.** `fmt.Printf("%d\n", "metin")` çağrısı derleniyor — `Printf`'in değişken sayıda
argümanı `interface{}` tipinde kabul ettiği için, bir dizginin bir `%d` damgasının karşılığı
olarak geçirilmesi derleyicinin gözünde sözdizimsel olarak geçerli. **PK17.** Aynı kaynak
inceleyiciden geçirildiğinde reddediliyor: `%d` bir tam sayı beklerken argümanın bir dizgi
olması, biçim damgası ile değerin tipi arasındaki bir uyumsuzluk, ve bu uyumsuzluk
derleyicinin denetlediği bir kural değil.

Bu ayrım kursun "reddedildi" sınıfını genişletiyor. Önceki konularda reddeden taraf hep
derleyiciydi; burada reddeden taraf **inceleyici** — ayrı bir komut, ayrı bir geçiş, ayrı
bir kural kümesi. Derleyici yalnız kaynağın sanal makinenin çalıştırabileceği bir programa
dönüşüp dönüşemeyeceğine bakıyor; `Printf`'in biçim dizgisi ile argümanları arasındaki
uyum bu sorunun dışında kalıyor, çünkü biçim dizgisi derleyicinin gözünde sıradan bir
dizgi değeri, özel bir anlamı yok. İnceleyici bu dizgiyi **ayrıştırıp** her damgayı
karşılık gelen argümanla eşleştiriyor ve eşleşmeyeni yakalıyor — derleyicinin hiç bakmadığı
bir katmanda çalışıyor.

Bu yakalamanın değeri, hatanın nerede ortaya çıkacağını değiştirmesinde. `%d` yerine bir
dizgi geçirilmiş bir çağrı derleniyor ve çalışıyor — ama çalıştığında beklenen sayı yerine
biçim damgasının kendisini ve bir hata işareti basıyor, ve bu yalnız o çağrı satırı
gerçekten çalıştırıldığında görünüyor. Bir kaynak tabanında böyle bir çağrı seyrek
çalışan bir dala düşmüşse, kusur uzun süre fark edilmeyebilir; derleme hiçbir uyarı
vermediği için de kaynağı okuyan biri bunu kaynağın kendisinden çıkaramaz. İnceleyici bu
çağrıyı **hiç çalıştırmadan**, yalnız kaynağı okuyarak yakalıyor — bu, kursun "açıkta"
sınıfına düşecek bir kusuru, çalışma zamanına hiç bırakmadan "reddedildi" sınıfına
taşıyan bir araç. Derleyicinin kendisi bu taşımayı yapmıyor, çünkü derleyicinin görevi
sözdizimini ve tip uyumunu denetlemekle sınırlı; biçim dizgisinin içeriği derleyicinin
tip sisteminin dışında kalan, yalnız çalışma zamanında yorumlanan bir dizgi değeri.

## Sınırlayıcı Ölçüm: Biçim Değişiyor, Anlam Değişmiyor

**PK18.** Aynı kaynağın biçimlendirme öncesi ve sonrası hâli, ikisi de ayrı ayrı
çalıştırıldığında birebir aynı çıktıyı veriyor: `{1 2 a} 5`. Bayt bayt karşılaştırma
`false` derken, çalıştırma çıktısı karşılaştırması `true` diyor — bu iki sonuç birbirini
çürütmüyor, tam tersine dersin tezini kuruyor: biçimlendirici kaynağın **görünüşünü**
değiştiriyor, **davranışını** değiştirmiyor.

Bunun nedeni biçimlendiricinin ne yaptığında yatıyor: girinti, boşluk ve hizalama —
biçimlendiricinin dokunduğu her şey — dilin sözdiziminde **anlamsız** boşluk. Bir `int`
değişkeninin adı, bir işlevin gövdesindeki ifadelerin sırası, bir yapının alan adları —
bunların hiçbiri biçimlendirme sırasında değişmiyor, çünkü biçimlendirici bunlara hiç
dokunmuyor. Sınır burada net: biçimlendirici bir **ayrıştırma ağacını** okuyup aynı ağacı
kanonik boşluklarla yeniden yazıyor, ağacın kendisini hiç değiştirmiyor. Bu yüzden
biçimlendirilmiş bir kaynağın davranışı hakkında hiçbir yeni varsayım gerekmiyor — ölçülen
programın çalıştırıldığında ne yaptığı, biçimlendirmeden önce ne yapıyorsa biçimlendirmeden
sonra da onu yapıyor.

Bu sınır, inceleyicinin sınırından farklı bir yerde duruyor. Biçimlendirici kaynağı hiç
reddetmiyor — kendisine verilen her geçerli kaynağı kabul edip yeniden yazıyor, ve
girdinin "iyi" ya da "kötü" olduğuna hiç karar vermiyor. İnceleyici ise tam tersini
yapıyor: kaynağı hiç değiştirmiyor, yalnız reddediyor ya da geçiriyor. İki araç da aynı
komut ailesinin üyesi, ikisi de derleyicinin bakmadığı bir katmanda çalışıyor, ama biri
kaynağı dönüştürerek, öbürü kaynağı yargılayarak iş görüyor. Bu ders ikisini de aynı
yordamla — kaynağı geçici bir modüle yazıp komutu çağırarak — sınadı, ve ikisinin de
sonucu yalnız bir bayrak (değişti mi, geçti mi) olarak okundu; hiçbir aşamada bir hata
metni ya da bir biçimlendirilmiş diff satır satır yazdırılmadı.

## Özet

- Dilin kendi komut ailesi yalnız derlemeyi değil biçimlendirmeyi ve incelemeyi de kapsar;
  üçü de bu derste olduğu gibi program içinden, argüman olarak çağrılabilir.
- Biçimlendirici, kendisine verilen kaynağı tek bir kanonik biçime dönüştürür; 18 satırlık
  bir kaynağın 7 satırı bu dönüşümde değişir, geri kalanı zaten kanonik biçimdeydi.
- İnceleyici, derleyicinin reddetmediği bir kalıbı (biçim damgası ile argüman tipinin
  uyuşmaması) yakalar; derleyici bu uyumu hiç denetlemez, çünkü biçim dizgisi onun
  gözünde sıradan bir dizgi değeridir.
- Sınırlayıcı ölçüm: biçimlendirme öncesi ve sonrası kaynak, çalıştırıldığında birebir
  aynı çıktıyı verir — biçim değişir, anlam değişmez.
- İki aracın adı da yalnız kod bloğunda, bir çağrının argümanı olarak geçti; düzyazıda
  yalnız iş adlarıyla anıldı.

## Sonraki Adım

Bu derste bir kaynağın biçimi ve derleyicinin gözden kaçırdığı bir kalıbı kimin
uyguladığı ölçüldü — ikisi de dilin kendi komut ailesinin ayrı üyeleri, ikisi de derleyicinin
bakmadığı bir katmanda çalışıyor. Ama bu üç aracın (derleyici, biçimlendirici, inceleyici)
hiçbiri, bir paketin dışarıya ne sunduğunu anlatan metni — bir adın ne işe yaradığını
açıklayan yazıyı — hiç okumuyor. Kursun son dersi bu son köşeye bakıyor: bir belge
kaynaktan nasıl üretiliyor, hangi adlar bu belgeye hiç girmiyor, ve belge aracı bir
yorumun **doğruluğunu** denetliyor mu — yoksa yalnız **konumunu** mu?
