İlk program
main adlı bir fonksiyonla başlar. Program
çalıştığında ilk olarak buranın içi yürütülür.
print ekrana bir şey yazar.fn main() {
print("Merhaba dünya!")
}Merhaba dünya!Nar dil rehberi · v0.6.0
Kurulum yok: deneme alanı derleyiciyi tarayıcında çalıştırır. Yazdığın kodun bağlantısını paylaşabilirsin.
struct Dil {
ad: String
hedefler: [String]
}
fn tanit(d: Dil) -> String =
"${d.ad}: tek kaynaktan ${d.hedefler.len()} hedef"
fn main() {
let nar = Dil {
ad: "Nar",
hedefler: ["web", "masaüstü", "mobil"]
}
print(tanit(nar))
print(nar.hedefler.join(" · "))
let sayilar = [3, 1, 4, 1, 5, 9, 2, 6]
print("çiftler:", sayilar.filter(|n| n % 2 == 0))
print("toplam:", sayilar.toplam())
}
Nar: tek kaynaktan 3 hedef
web · masaüstü · mobil
çiftler: [4, 2, 6]
toplam: 31Bilgisayarında Python ve Node.js kurulu olması yeterli.
Programını bir dosyaya yaz (örneğin deneme.nar) ve çalıştır:
cd C:\Users\mstfa\Desktop\yazılım_dili
.\nar.cmd run deneme.nar
Kodunu çalıştırmadan sadece kontrol ettirmek istersen
.\nar.cmd check deneme.nar yeter. Web sayfası üretmek için
.\nar.cmd build deneme.nar --target web.
Aşağıdaki örneklerin çoğu fn main() { ... } içine yazılır; kısa tutmak için o sarmalayıcı gösterilmiyor. Üst düzey bildirim (fn, struct, enum) içeren örnekler tam gösterilir.
İlk programı yaz, çalıştır, ekrana bir şey yazdır.
main adlı bir fonksiyonla başlar. Program
çalıştığında ilk olarak buranın içi yürütülür.
print ekrana bir şey yazar.fn main() {
print("Merhaba dünya!")
}Merhaba dünya!// Bu satır bir yorum, çalıştırılmaz.
/* Birden çok satırı
kaplayan yorum da yazılabilir. */
print("yorumlar çıktıyı etkilemez")yorumlar çıktıyı etkilemezprint virgülle ayrılmış birden çok değer alabilir; aralarına
boşluk koyar. Her türlü değeri yazdırabilir — sayı, metin, liste, kendi
tanımladığın tipler.print("tek değer")
print("ad:", "Ayşe", "yaş:", 30)
print([1, 2, 3])
print(3.5, true, none)tek değer
ad: Ayşe yaş: 30
[1, 2, 3]
3.5 true noneDeğer tutmak: let ve var, sayılar, metinler, doğru/yanlış.
let ile tanımlanan değer bir daha değişmez.
Değiştirmen gerekiyorsa var kullan.
Varsayılan olarak let kullan: neyin değiştiğini görmek kodu
okumayı kolaylaştırır. Değiştirmeye çalışırsan derleyici uyarır.
let ad = "Mustafa" // değişmez
var sayac = 0 // değişebilir
sayac = sayac + 1
sayac += 5 // aynı şeyin kısa yazımı
print(ad, sayac)Mustafa 6Temel tipler: Int (tam sayı), Float (ondalıklı),
String (metin), Bool (doğru/yanlış).
let a = 42 // Int (kendisi anladı)
let b = 3.14 // Float
let c = "merhaba" // String
let d = true // Bool
let e: Float = 10 // istersen açıkça yaz
let f: String = "elma"
print(a, b, c, d, e, f)42 3.14 merhaba true 10.0 elmaTek dikkat edilecek yer bölme: iki tam sayıyı bölersen sonuç tam sayıdır (ondalık kısım atılır). Ondalıklı sonuç istiyorsan taraflardan biri ondalıklı olmalı.
print(2 + 3) // 5
print(10 - 4) // 6
print(3 * 4) // 12
print(7 / 2) // 3 <- tam bölme
print(7 / 2.0) // 3.5 <- ondalıklı
print(7 % 3) // 1 <- kalan
print(1 + 2.5) // 3.5 <- karışık kullanılabilir
print(sqrt(16), abs(-5), min(3, 8), max(3, 8))
print(floor(3.7), ceil(3.2), round(3.5))5
6
12
3
3.5
1
3.5
4.0 5 3 8
3 4 4+ ile birleştirebilirsin. Diğer taraf sayı ya da liste
olsa bile çalışır — Nar onu otomatik yazıya çevirir.
Daha okunaklı yol: ${...} ile metnin içine doğrudan değer
gömmek.
let ad = "Ayşe"
let yas = 30
print("Merhaba " + ad)
print("Yaş: " + yas) // sayı otomatik yazıya döner
print("${ad} ${yas} yaşında") // gömme — en okunaklısı
print("Toplam: ${yas + 5}") // içinde işlem de yapılırMerhaba Ayşe
Yaş: 30
Ayşe 30 yaşında
Toplam: 35upperTr() ve lowerTr() Türkçeye özeldir:
i harfini İ yapar, I harfini
ı yapar. Normal upper()/lower() bunu
bilmez.
let s = " Merhaba Dünya "
print(s.trim()) // baştaki/sondaki boşlukları atar
print(s.trim().len()) // harf sayısı
print(s.trim().lower())
print("istanbul".upperTr()) // Türkçe: İSTANBUL
print("IĞDIR".lowerTr()) // Türkçe: ığdır
print("a-b-c".split("-"))
print("a-b-c".replace("-", "+"))
print("merhaba".contains("hab"))
print("merhaba".slice(0, 3)) // ilk 3 harf
print("ab".repeat(3))Merhaba Dünya
13
merhaba dünya
İSTANBUL
ığdır
["a", "b", "c"]
a+b+c
true
mer
abababkodu() karakterden sayıya, koddan(...) sayıdan
karaktere çevirir.
Şifreleme, sıralama, kaçış dizisi çözme gibi işlerde gerekir. Emoji gibi geniş karakterler de tek parça sayılır.
print("A".kodu()) // 65
print("a".kodu()) // 97
print(koddan(65)) // A
// Türkçe harfler de aynı şekilde
print("ı".kodu())
print(koddan(305))
// Sezar şifresi: her harfi üç ileri kaydır
let harf = "d"
print(koddan(harf.kodu() + 3))65
97
A
305
ı
gBool tipinin iki değeri vardır: true (doğru) ve
false (yanlış). Karşılaştırmalar hep Bool üretir.print(5 > 3) // true
print(5 == 5) // eşit mi
print(5 != 3) // eşit değil mi
print(5 >= 5, 2 <= 1)
let a = true
let b = false
print(a && b) // ve — ikisi de doğruysa
print(a || b) // veya — biri doğruysa
print(!a) // değiltrue
true
true
true false
false
true
falseKoşula göre farklı iş yapmak; if'i değer üretmek için kullanmak.
else if eklenir.let not = 85
if not >= 90 {
print("A aldın")
} else if not >= 80 {
print("B aldın")
} else {
print("Daha çok çalış")
}B aldınif yalnızca bir şey yapmakla kalmaz, değer de
üretebilir. Bir değişkene doğrudan atayabilirsin.
Bu biçimde else yazmak zorunludur — çünkü her durumda bir
değer çıkması gerekir.
let yas = 20
let durum = if yas >= 18 { "yetişkin" } else { "çocuk" }
print(durum)
// zincirlenebilir
let not = 85
let harf = if not >= 90 { "A" } else if not >= 80 { "B" } else { "C" }
print("harf:", harf)
// başka bir ifadenin içinde de kullanılabilir
print("ücret: " + if yas < 12 { 0 } else { 50 })yetişkin
harf: B
ücret: 50if ve match dallarında tek satır
yetmiyorsa blok yazabilirsin. Bloğun son satırı dalın
değeridir.
Ara adımları ayrı bir fonksiyona çıkarmak zorunda değilsin; hesabı olduğu yerde yapabilirsin.
Son satır bir değer olmalı — atama ya da döngüyle biten bir dal hata verir; derleyici bunu söyler.
enum Hava {
Gunesli
Yagmurlu
Karli
}
fn oneri(h: Hava) -> String = match h {
Gunesli -> "şapka al"
Yagmurlu -> {
let arac = "şemsiye"
let sebep = "yağmur"
"${arac} al (${sebep})"
}
Karli -> {
let katman = 3
"${katman} kat giy"
}
}
fn sinif(not: Int) -> String = if not >= 90 {
"A"
} else if not >= 80 {
let fark = 90 - not
"B (A'ya ${fark} puan)"
} else {
"C"
}
fn main() {
print(oneri(Yagmurlu))
print(oneri(Karli))
print(sinif(85))
}şemsiye al (yağmur)
3 kat giy
B (A'ya 5 puan)Aynı işi tekrarlamak: for, while, break ve continue.
0..5 son sayıyı içermez (0,1,2,3,4).
İçermesini istersen 0..=5 yaz.
for i in 1..4 {
print("sayı", i)
}
for i in 1..=3 {
print("dahil", i)
}
for meyve in ["elma", "armut", "kiraz"] {
print(meyve)
}
for harf in "abc" {
print(harf)
}sayı 1
sayı 2
sayı 3
dahil 1
dahil 2
dahil 3
elma
armut
kiraz
a
b
cMetinde de çalışır ve indeks karakter sayar: “ç” tek karakterdir, iki değil.
Eşlemede iki değişken zaten anahtar ve değerdir. Aralıkta ikinci değişken yok — aralığın kendisi zaten sayı üretiyor.
let sehirler = ["İstanbul", "Ankara", "İzmir"]
for (sira, sehir) in sehirler {
print("${sira + 1}. ${sehir}")
}
for (i, harf) in "açık" {
print(i, harf)
}
let yaslar = {"Ayşe": 30, "Mehmet": 24}
for (ad, yas) in yaslar {
print(ad, yas)
}1. İstanbul
2. Ankara
3. İzmir
0 a
1 ç
2 ı
3 k
Ayşe 30
Mehmet 24var sayi = 1
while sayi < 100 {
sayi = sayi * 2
}
print("100'ü aşan ilk ikinin kuvveti:", sayi)100'ü aşan ilk ikinin kuvveti: 128break döngüyü tamamen bitirir. continue o turu
atlayıp bir sonrakine geçer.for i in 1..=10 {
if i % 2 == 0 {
continue // çiftleri atla
}
if i > 7 {
break // 7'yi geçince dur
}
print(i)
}1
3
5
7Sırayla duran veri ve anahtar–değer eşlemeleri.
Olmayan bir dizine erişirsen program net bir hata mesajıyla durur — sessizce yanlış sonuç vermez.
var sayilar = [5, 3, 8, 1]
print(sayilar)
print("ilk eleman:", sayilar[0])
print("kaç eleman:", sayilar.len())
sayilar.push(10) // sona ekle
print(sayilar)
print("sıralı:", sayilar.sort())
print("ters:", sayilar.reverse())
print("içinde 8 var mı:", sayilar.contains(8))
print("bir kısmı:", sayilar.slice(1, 3))[5, 3, 8, 1]
ilk eleman: 5
kaç eleman: 4
[5, 3, 8, 1, 10]
sıralı: [1, 3, 5, 8, 10]
ters: [10, 1, 8, 3, 5]
içinde 8 var mı: true
bir kısmı: [3, 8]Toplama, ortalama, filtreleme gibi şeyler için önce buraya bak; aradığın
yoksa bir sonraki başlıktaki map/filter yöntemini
kullanırsın.
let sayilar = [5, 3, 8, 1, 3]
print("toplam:", sayilar.toplam())
print("ortalama:", sayilar.ortalama())
print("çarpım:", sayilar.carpim())
print("en büyük:", sayilar.enBuyuk() ?? 0)
print("en küçük:", sayilar.enKucuk() ?? 0)
print("iki katları:", sayilar.kat(2))
print("10 artırılmış:", sayilar.artir(10))
print("3'ten büyükler:", sayilar.buyukler(3))
print("3'ten küçükler:", sayilar.kucukler(3))
print("çiftler:", sayilar.ciftler())
print("tekler:", sayilar.tekler())
print("benzersiz:", sayilar.benzersiz())
print("kaç tane 3 var:", sayilar.say(3))
let kelimeler = ["nar", "elma", "kiraz"]
print("büyük harf:", kelimeler.buyukHarf())
print("'a' içerenler:", kelimeler.icerenler("a"))toplam: 20
ortalama: 4.0
çarpım: 360
en büyük: 8
en küçük: 1
iki katları: [10, 6, 16, 2, 6]
10 artırılmış: [15, 13, 18, 11, 13]
3'ten büyükler: [5, 8]
3'ten küçükler: [1]
çiftler: [8]
tekler: [5, 3, 1, 3]
benzersiz: [5, 3, 8, 1]
kaç tane 3 var: 2
büyük harf: ["NAR", "ELMA", "KİRAZ"]
'a' içerenler: ["nar", "elma", "kiraz"]|x| ... yazımı "her eleman için" demektir; x
sırayla her elemanın yerini alır.
map — her elemanı dönüştürürfilter — koşula uyanları seçerreduce — hepsini tek değere indirgerlet sayilar = [1, 2, 3, 4, 5]
print(sayilar.map(|x| x * x)) // her birinin karesi
print(sayilar.filter(|x| x % 2 == 0)) // sadece çiftler
print(sayilar.reduce(|a, b| a + b, 0)) // toplamı
// zincirlenebilir
print(sayilar.filter(|x| x > 2).map(|x| x * 10))
let kelimeler = ["nar", "elma", "kiraz"]
print(kelimeler.map(|k| k.upperTr()).join(", "))[1, 4, 9, 16, 25]
[2, 4]
15
[30, 40, 50]
NAR, ELMA, KİRAZget her zaman "olmayabilir" bir sonuç verir; bu yüzden
?? varsayılan ile bir yedek değer belirtirsin. Bu, olmayan
anahtarı unutmanı engeller.
var stok = {"elma": 12, "armut": 3}
stok.set("kiraz", 7)
print(stok)
print("kaç çeşit:", stok.len())
print("elma:", stok.get("elma") ?? 0)
print("muz:", stok.get("muz") ?? 0) // yok -> 0
print("armut var mı:", stok.has("armut"))
for (urun, adet) in stok {
print("${urun}: ${adet} adet")
}{"elma": 12, "armut": 3, "kiraz": 7}
kaç çeşit: 3
elma: 12
muz: 0
armut var mı: true
elma: 12 adet
armut: 3 adet
kiraz: 7 adetİşi ada bağlamak, parçalara ayırmak, isimsiz fonksiyonlar.
Parametrelerin ve döndürdüğü değerin tipini yazman gerekir. Bu fazladan yazı gibi görünür ama karşılığında derleyici seni yanlış kullanımdan korur ve hata mesajları çok daha anlaşılır olur.
fn selamla(ad: String) {
print("Merhaba " + ad)
}
fn topla(a: Int, b: Int) -> Int {
return a + b
}
fn main() {
selamla("Nar")
print(topla(3, 4))
}Merhaba Nar
7=
kullanabilirsin. Daha kısa ve okunaklı olur.fn kare(x: Int) -> Int = x * x
fn selam(ad: String) -> String = "Merhaba " + ad
fn buyuk_mu(n: Int) -> Bool = n > 100
fn main() {
print(kare(9))
print(selam("Nar"))
print(buyuk_mu(150))
}81
Merhaba Nar
truefn faktoriyel(n: Int) -> Int {
if n <= 1 {
return 1 // durma noktası
}
return n * faktoriyel(n - 1)
}
fn fib(n: Int) -> Int = if n < 2 { n } else { fib(n - 1) + fib(n - 2) }
fn main() {
print("6! =", faktoriyel(6))
print("fib(20) =", fib(20))
}6! = 720
fib(20) = 6765map ve filter içinde gördüğün |x| ...
tam olarak budur.
Bir değişkene atarsan parametrenin tipini yazman gerekir — çünkü ortada tipi anlamayı sağlayacak bir bağlam yoktur.
let iki_kat = |x: Int| x * 2
print(iki_kat(21))
// birden çok satır gerekiyorsa
let mesafe = |a: Int, b: Int| {
let fark = a - b
return abs(fark)
}
print(mesafe(3, 10))
// bir listeye uygularken tip yazmaya gerek yok
print([1, 2, 3].map(|x| x + 100))42
7
[101, 102, 103]Kendi veri biçimlerini kurmak: struct, enum, metot, match.
Alanlar varsayılan olarak değişmezdir. Değişebilmesi için başına
var yaz.
struct Kisi {
ad: String
var yas: Int
}
fn main() {
let k = Kisi { ad: "Ayşe", yas: 30 }
print(k)
print(k.ad, "->", k.yas)
k.yas = 31 // 'var' olduğu için değiştirilebilir
print("doğum günü sonrası:", k.yas)
}Kisi { ad: "Ayşe", yas: 30 }
Ayşe -> 30
doğum günü sonrası: 31self, üzerinde çalıştığın nesneyi gösterir.struct Dikdortgen {
en: Float
boy: Float
fn alan() -> Float = self.en * self.boy
fn cevre() -> Float = 2.0 * (self.en + self.boy)
fn kare_mi() -> Bool = self.en == self.boy
fn buyut(kat: Float) -> Dikdortgen {
return Dikdortgen { en: self.en * kat, boy: self.boy * kat }
}
}
fn main() {
let d = Dikdortgen { en: 3.0, boy: 4.0 }
print("alan:", d.alan())
print("çevre:", d.cevre())
print("kare mi:", d.kare_mi())
print("iki katı:", d.buyut(2.0))
}alan: 12.0
çevre: 14.0
kare mi: false
iki katı: Dikdortgen { en: 6.0, boy: 8.0 }enum kullanılır:
hava durumu, sipariş durumu, ödeme tipi gibi.
Metin kullanmaya göre avantajı: yazım hatası yapamazsın ve derleyici seçeneklerin hepsini ele aldığından emin olur.
enum Hava {
Gunesli
Yagmurlu
Karli
}
fn main() {
let bugun = Hava.Yagmurlu
print(bugun)
}Hava.YagmurluHava.Yagmurlu yerine kısaca Yagmurlu yazabilirsin.
Seçenek adı programda tek bir enum'a aitse derleyici hangisini
kastettiğini bilir.
Aynı ad iki ayrı enum'da geçiyorsa derleyici sana söyler ve
hangisi olduğunu yazmanı ister — sessizce yanlış olanı seçmez.
enum Hava {
Gunesli
Yagmurlu
Karli
}
fn oneri(h: Hava) -> String = match h {
Gunesli -> "şapka al"
Yagmurlu -> "şemsiye al"
Karli -> "atkı al"
}
fn main() {
print(oneri(Yagmurlu))
print(oneri(Hava.Karli)) // uzun yazım da geçerli
}şemsiye al
atkı alDilim a[1..3] listenin ya da metnin bir
parçasını alır; ..= üst sınırı da içine katar. Sınırlar
uçları aşarsa kırpılır, ters verilirse boş döner — yani dilim almak
programı durdurmaz.
Yayma [...a, 3] bir listenin öğelerini
yenisinin içine serer. Kaynak liste değişmez.
Boru x |> f(a) değeri ilk argüman
yaparak f(x, a) çağırır. İç içe çağrıları soldan sağa
okunan bir zincire çevirir: sar(buyut(temizle(s))) yerine
s |> temizle |> buyut |> sar.
fn temizle(s: String) -> String = s.trim()
fn buyut(s: String) -> String = s.upperTr()
fn sar(s: String, k: String) -> String = k + s + k
fn main() {
let sayilar = [1, 2, 3, 4, 5]
print(sayilar[1..3]) // [2, 3]
print(sayilar[1..=3]) // [2, 3, 4]
print("merhaba"[0..3]) // mer
let bas = [0]
print([...bas, ...sayilar[0..2], 9])
print(" merhaba " |> temizle |> buyut |> sar("*"))
}[2, 3]
[2, 3, 4]
mer
[0, 1, 2, 9]
*MERHABA*struct açmak gerekirdi. Tuple
tam bu boşluğu doldurur: parantez içinde birkaç değer, alan adı yok,
sırayla okunur.
let (a, b) = ... ile tuple doğrudan adlarına açılır;
öğeye tek tek .0, .1 ile de erişilebilir.
Ne zaman struct'a geçmeli: alanların adı anlam
taşımaya başladığında. (Int, Int) okunurken hangisinin
bölüm hangisinin kalan olduğu belli değilse, orası artık bir struct
yeridir — dört öğeyi geçen tuple için derleyici de uyarır.
fn bolVeKalan(a: Int, b: Int) -> (Int, Int) = (a / b, a % b)
fn enUzunKelime(cumle: String) -> (String, Int) {
var enUzun = ""
for k in cumle.split(" ") {
if k.len() > enUzun.len() {
enUzun = k
}
}
return (enUzun, enUzun.len())
}
fn main() {
let (bolum, kalan) = bolVeKalan(17, 5)
print(str(bolum) + " kalan " + str(kalan))
let (kelime, uzunluk) = enUzunKelime("bugün hava çok güzel")
print(kelime + " — " + str(uzunluk) + " harf")
// Tek tek de okunur:
let ikili = bolVeKalan(9, 2)
print(str(ikili.0) + "/" + str(ikili.1))
}3 kalan 2
bugün — 5 harf
4/1match kolu yalnız desene değil, bir koşula da bakabilir:
desen if koşul ->. Desenin bağladığı ad koşulun içinde
kullanılabilir.
Sayı ve metin aralıkları da desen olur: 1..=9 ikisini de
içine alır, 0..3 üst ucu dışarıda bırakır.
Neden önemli: peş peşe if yazmak yerine
bütün durumlar tek yerde, alt alta durur. Derleyici de bir durumu
atladığını söyleyebilir — koşullu kol tek başına match'i tamamlamış
saymaz, çünkü koşul tutmayabilir.
fn siniflandir(n: Int) -> String = match n {
x if x < 0 -> "eksi"
0 -> "sıfır"
1..=9 -> "tek haneli"
10..=99 -> "iki haneli"
_ -> "daha büyük"
}
fn main() {
for n in [-3, 0, 7, 42, 1000] {
print(str(n) + " -> " + siniflandir(n))
}
}-3 -> eksi
0 -> sıfır
7 -> tek haneli
42 -> iki haneli
1000 -> daha büyükAynısı struct alanları için de geçerli: varsayılanı olan
alan kurulurken yazılmayabilir. Sekiz alanlı bir yapıyı kurmak için
sekizini birden yazmak gerekmez.
Kural: varsayılanı olmayan parametre, varsayılanı olanlardan sonra gelemez — gelirse çağrıda ona ulaşılamazdı.
struct Kutu {
var yazi: String
var genislik: Int = 20
var cerceve: Bool = true
}
fn bicim(metin: String, once: String = "[", sonra: String = "]") -> String =
once + metin + sonra
fn main() {
print(bicim("selam"))
print(bicim("selam", sonra: ">"))
print(bicim(sonra: ">", once: "<", metin: "selam"))
let k = Kutu { yazi: "not" }
print(k.yazi + " " + str(k.genislik) + " " + str(k.cerceve))
}[selam]
[selam>
<selam>
not 20 truematch, bir değerin hangi durumda olduğuna bakıp ona göre iş
yapar.
En değerli özelliği: bir seçeneği ele almayı unutursan
program çalışmaz, derleyici sana hangisini unuttuğunu söyler. Sonradan
enum'a yeni bir seçenek eklediğinde, düzeltmen gereken bütün
yerleri derleyici gösterir.
enum Hava {
Gunesli
Yagmurlu
Karli
}
fn tavsiye(h: Hava) -> String = match h {
Hava.Gunesli -> "Şapka al."
Hava.Yagmurlu -> "Şemsiye al."
Hava.Karli -> "Mont al."
}
fn main() {
for h in [Hava.Gunesli, Hava.Yagmurlu, Hava.Karli] {
print(tavsiye(h))
}
// sayı ya da metin üzerinde de çalışır
let n = 3
print(match n {
1 -> "bir"
2 -> "iki"
_ -> "çok" // _ = geri kalan her şey
})
}Şapka al.
Şemsiye al.
Mont al.
çokNar'da hatalar bu şekilde yönetilir. Hatayı görmezden gelemezsin, çünkü
sonucu almak için match yazmak zorundasın.
enum Sonuc {
Tamam(Int)
Hata(String)
}
fn bol(a: Int, b: Int) -> Sonuc {
if b == 0 {
return Sonuc.Hata("sıfıra bölünemez")
}
return Sonuc.Tamam(a / b)
}
fn main() {
for bolen in [4, 0, 5] {
match bol(100, bolen) {
Sonuc.Tamam(deger) -> print("100 / ${bolen} = ${deger}")
Sonuc.Hata(mesaj) -> print("olmadı: ${mesaj}")
}
}
}100 / 4 = 25
olmadı: sıfıra bölünemez
100 / 5 = 20Aynı kodu her tiple çalıştırmak.
Böyle durumlarda tipe bir parametre verilir. Aşağıdaki
T "herhangi bir tip" demektir; kullanırken hangisi olduğu
belli olur.
Faydası: aynı kodu her tip için yeniden yazmazsın, ama tip güvenliği
kaybolmaz — Kutu<Int> içine metin koyamazsın.
struct Kutu<T> {
deger: T
fn al() -> T = self.deger
}
fn main() {
let sayiKutusu = Kutu { deger: 5 }
let metinKutusu = Kutu { deger: "merhaba" }
print(sayiKutusu.al())
print(metinKutusu.al())
// Tipi açıkça da yazabilirsin
let acik = Kutu<Float> { deger: 2.5 }
print(acik.deger)
}5
merhaba
2.5fn ilk<T>(liste: [T]) -> T? {
if liste.len() == 0 {
return none
}
return liste[0]
}
fn ikiKat<T>(x: T) -> [T] = [x, x]
fn main() {
print(ilk([3, 4, 5]) ?? 0)
print(ilk(["a", "b"]) ?? "yok")
let bos: [Int] = []
print(ilk(bos) ?? -1)
print(ikiKat(7))
print(ikiKat("x"))
}3
a
-1
[7, 7]
["x", "x"]try/catch) yoktur. Bir işlem
başarısız olabiliyorsa bunu dönüş tipinde söyler.
Çağıran match yazmak zorunda kaldığı için hatayı görmezden
gelemez. Unutursan program çalışmaz — derleyici hangi durumu ele almadığını
söyler.
Hazır tanım araclar/sonuc.nar dosyasındadır; kendin de
yazabilirsin, çünkü sıradan bir enum'dur.
enum Sonuc<T, H> {
Tamam(T)
Hata(H)
}
fn bol(a: Int, b: Int) -> Sonuc<Int, String> {
if b == 0 {
return Sonuc.Hata("sıfıra bölünemez")
}
return Sonuc.Tamam(a / b)
}
fn main() {
for bolen in [4, 0] {
match bol(100, bolen) {
Sonuc.Tamam(deger) -> print("100 / ${bolen} = ${deger}")
Sonuc.Hata(mesaj) -> print("olmadı: ${mesaj}")
}
}
}100 / 4 = 25
olmadı: sıfıra bölünemezenum Agac<T> {
Yaprak(T)
Dal([Agac<T>])
}
fn topla(a: Agac<Int>) -> Int = match a {
Agac.Yaprak(n) -> n
Agac.Dal(dallar) -> dallar.map(|d| topla(d)).toplam()
}
fn main() {
let agac = Agac.Dal([
Agac.Yaprak(1),
Agac.Dal([Agac.Yaprak(2), Agac.Yaprak(3)])
])
print("yaprakların toplamı:", topla(agac))
}yaprakların toplamı: 6Farklı tiplere ortak davranış tanımlamak.
interface yalnızca hangi metotların bulunması
gerektiğini söyler; nasıl yapılacağını her tip kendi bilir.
Bir tip arayüzü bildiriminde üstlenir (struct Nokta:
Yazdirilabilir). Bu bir sözdür: metotlardan biri eksikse ya da
imzası tutmuyorsa program derlenmez.
interface Yazdirilabilir {
fn yaz() -> String
}
struct Nokta: Yazdirilabilir {
x: Float
y: Float
fn yaz() -> String = "(${self.x}, ${self.y})"
}
struct Kisi: Yazdirilabilir {
ad: String
fn yaz() -> String = "kişi: " + self.ad
}
fn goster(sey: Yazdirilabilir) {
print(sey.yaz())
}
fn main() {
goster(Nokta { x: 1.0, y: 2.0 })
goster(Kisi { ad: "Ayşe" })
}(1.0, 2.0)
kişi: Ayşeenum da arayüz üstlenebilir.interface Yazdirilabilir {
fn yaz() -> String
}
struct Kisi: Yazdirilabilir {
ad: String
fn yaz() -> String = "kişi: " + self.ad
}
enum Durum: Yazdirilabilir {
Acik
Kapali
fn yaz() -> String = match self {
Durum.Acik -> "açık"
Durum.Kapali -> "kapalı"
}
}
fn main() {
let hepsi: [Yazdirilabilir] = [
Kisi { ad: "Ayşe" },
Durum.Acik,
Durum.Kapali
]
for e in hepsi {
print("- " + e.yaz())
}
}- kişi: Ayşe
- açık
- kapalı<T: Olculebilir> "T her tip olabilir, yeter ki
olcu metodu olsun" demektir.
Böylece fonksiyon içinde o metodu çağırabilirsin; sınır koymadığın metotları kullanmana derleyici izin vermez.
interface Olculebilir {
fn olcu() -> Float
}
struct Nokta: Olculebilir {
x: Float
y: Float
fn olcu() -> Float = sqrt(self.x * self.x + self.y * self.y)
}
fn enBuyuk<T: Olculebilir>(liste: [T]) -> Float {
var en = 0.0
for e in liste {
if e.olcu() > en {
en = e.olcu()
}
}
return en
}
fn main() {
print(enBuyuk([
Nokta { x: 3.0, y: 4.0 },
Nokta { x: 6.0, y: 8.0 }
]))
}10.0Olmayabilen değeri tipin kendisinde taşımak.
? koyarsın.
Bu, birçok dilde programların en sık çöktüğü hatayı ("null" hatası) baştan engeller: olmayabilecek bir değeri kontrol etmeden kullanamazsın.
let takma: String? = none
let gercek: String? = "Nar"
print(takma)
print(gercek)
// ?? ile yedek değer ver
print(takma ?? "takma adı yok")
print(gercek ?? "takma adı yok")none
Nar
takma adı yok
Nar?? yedek — yoksa şunu kullanif x != none { ... } — kontrol et, içeride normal kullanx! — "kesinlikle var" de; yanılırsan program net bir hatayla dururlet sayilar = [10, 20, 30]
let ilk = sayilar.first() // İlk eleman olmayabilir (liste boşsa)
print("ilk:", ilk ?? 0)
if ilk != none {
print("var, iki katı:", ilk * 2) // burada artık kesin var
}
var bos: [Int] = []
print("boş listenin ilki:", bos.first() ?? -1)ilk: 10
var, iki katı: 20
boş listenin ilki: -1if let.
Değer none değilse ada bağlanır ve o dalda açılmış
hâliyle görünür; ! yazmak gerekmez.
Bu olmadan önce bir değişkene almak, sonra sorup sonra açmak gerekirdi:
let e = bul("#uygulama")
if e != none {
e!.ekle(baslik)
}
else dalında ad görünmez: orada değer zaten yoktur.
else if let ile zincirlenebilir.
fn yas(ad: String) -> Int? {
let defter = {"Ayşe": 30, "Mehmet": 24}
return defter.get(ad)
}
fn main() {
if let y = yas("Ayşe") {
print("Ayşe ${y} yaşında")
} else {
print("Ayşe defterde yok")
}
if let y = yas("Zeynep") {
print("Zeynep ${y} yaşında")
} else if let y = yas("Mehmet") {
print("Zeynep yok ama Mehmet ${y} yaşında")
} else {
print("ikisi de yok")
}
}Ayşe 30 yaşında
Zeynep yok ama Mehmet 24 yaşındaNar zaten daraltma yapar: if x != none içinde x açılmış sayılır. if let bunu bir adım öteye taşır — değer bir çağrıdan geliyorsa önce değişkene almak gerekmez.
? tam bunu yazar —
değer none ise fonksiyondan hemen none döner,
değilse açılmış değeri verir.
Bu olmadan her adım dört satırdı:
let satir = satirlar.first()
if satir == none {
return none
}
let sayi = int(satir!.trim())
if sayi == none {
return none
}
return sayi
? fonksiyondan erken çıkar, bu yüzden iki
koşulu var: değerin opsiyonel olması ve bulunduğu fonksiyonun opsiyonel
döndürmesi. İkisi de sağlanmıyorsa derleyici söyler.
Erken çıkışın konabileceği bir yer olmayan yerlerde ?
kullanılamaz: ??, && ve || işleçlerinin
sağında, değer üreten ifin dallarında, match
kollarında, while koşulunda ve tek ifadelik lambda gövdesinde.
Bu yerlerde sessizce yanlış çalışmak yerine açık bir hata alırsın;
değeri önce bir değişkene al. Gövdeli lambdada serbesttir — orada ?
lambdadan çıkar.
// Satırların ilkindeki sayıyı okur.
// Herhangi bir adım tutmazsa sonuç none olur.
fn ilkSayi(satirlar: [String]) -> Int? {
let satir = satirlar.first()?
return int(satir.trim())?
}
fn main() {
print(ilkSayi([" 42 ", "başka satır"]) ?? -1)
print(ilkSayi(["sayı değil"]) ?? -1)
print(ilkSayi([]) ?? -1)
}42
-1
-1Değer bir kez hesaplanır: f()? çağrıyı iki kez yapmaz. Üretilen JavaScript de elle yazılmış gibi durur — geçici bir değişken ve bir if.
?. kullanılır.
Değer yoksa zincir durur ve sonuç none olur — program çökmez.struct Adres {
sehir: String
}
struct Kisi {
ad: String
adres: Adres?
}
fn main() {
let a = Kisi { ad: "Ayşe", adres: Adres { sehir: "Ankara" } }
let b = Kisi { ad: "Veli", adres: none }
print(a.adres?.sehir ?? "adres yok")
print(b.adres?.sehir ?? "adres yok")
}Ankara
adres yokTarayıcıda çalışan sayfa üretmek.
bul(...) bir öğeyi seçer. Öğe olmayabileceği için sonuç
her zaman olabilir tipindedir — kontrol etmen gerekir.
Bu, "öğe yok" hatasını baştan engeller.
Programı tarayıcıda çalışacak biçimde derlemek için:
nar build program.nar --target web
fn main() {
let baslik = bul("#baslik")
if baslik != none {
baslik.metinYaz("Merhaba Nar")
baslik.stil("color", "#a32a2f")
baslik.sinifEkle("vurgulu")
}
}olustur(...) yeni bir öğe yapar, ekle(...) onu
sayfaya koyar, dinle(...) ise tıklama gibi olayları yakalar.
Aşağıdaki program çalışan bir sayaçtır; tam hâli
ornekler/sayac_web.nar dosyasında.
var sayi = 0
fn main() {
let alan = bul("#uygulama")
if alan == none {
print("bu program tarayıcıda çalışmalı")
return
}
let ekran = olustur("div")
ekran.ozellikYaz("id", "ekran")
ekran.metinYaz("0")
alan!.ekle(ekran)
let dugme = olustur("button")
dugme.metinYaz("Artır")
dugme.dinle("click", || {
sayi += 1
bul("#ekran")?.metinYaz(str(sayi))
})
alan!.ekle(dugme)
}Element tipinin üzerindedir.
Not: Bu işlemler yalnızca tarayıcıda anlamlıdır.
Aynı programı nar run ile çalıştırırsan bul(...)
her zaman none döner ve program çökmez — bu sayede aynı kodu
her iki ortamda da güvenle çalıştırabilirsin.
fn ornekler(e: Element) {
e.metinYaz("yazı") // içindeki yazıyı değiştir
print(e.metin()) // içindeki yazıyı oku
e.htmlYaz("<b>kalın</b>") // HTML olarak yaz
e.degerYaz("giriş") // input/textarea değeri
print(e.deger())
e.sinifEkle("acik")
e.sinifSil("kapali")
print(e.sinifVarMi("acik"))
e.ozellikYaz("id", "kutu")
print(e.ozellik("id") ?? "yok")
e.stil("color", "red")
e.dinle("click", || { print("tıklandı") })
e.ekle(olustur("span")) // çocuk ekle
e.temizle() // içini boşalt
e.odaklan()
print(e.bul(".ic") != none) // içinde ara
}Durumdan görünüm üreten bildirimsel arayüz katmanı.
araclar/arayuz.nar kütüphanesi bunu tersine çevirir:
ekranı nasıl kuracağını değil, neye benzemesi
gerektiğini yazarsın. Durum değişince kütüphane ekranı kendisi
yeniler.
Bu kütüphane Nar'ın kendisiyle yazılmıştır — dilin bir parçası değil, dille yazılmış sıradan bir dosyadır. İstersen kopyalayıp kendine göre değiştirebilirsin.
import "araclar/arayuz.nar"
// Ekran bir Gorunum ağacıdır: Sutun, Satir, Kutu içinde
// Metin, Baslik, Dugme, Giris, Bosluk, Cizgi.
fn ciz(sayi: Int) -> Gorunum = Kutu([
Baslik("Sayaç"),
Metin("değer: ${sayi}"),
Satir([
Dugme("azalt", || { azalt() }),
Dugme("artır", || { artir() })
])
])uygulamaBaslat üç şey ister: çizimin yapılacağı yer, başlangıç
durumu ve durumu görünüme çeviren fonksiyon.
Durumu degistir ile güncellersin; ekranı yenilemek senin
işin değildir. Durum her tip olabilir — sayı, metin, kendi
struct'ın ya da bunların listesi.
import "araclar/arayuz.nar"
var uygulama: Uygulama<Int>? = none
fn artir() {
if uygulama != none {
uygulama!.degistir(uygulama!.durum + 1)
}
}
fn ciz(sayi: Int) -> Gorunum = Sutun([
Baslik("Sayaç"),
Metin("değer: ${sayi}"),
Dugme("artır", || { artir() })
])
fn main() {
// Sayfadaki <div id="uygulama"></div> içine çizer.
uygulama = uygulamaBaslat("#uygulama", 0, ciz)
}if, for,
liste işlemleri hepsi kullanılabilir. Aşağıdaki parça, listeyi görünüme
çeviren kısımdır.
Tamamı ornekler/yapilacaklar_uygulamasi.nar dosyasındadır.
Masaüstü uygulaması olarak paketlemek için:
nar build ornekler/yapilacaklar_uygulamasi.nar --target masaustu
fn gorevSatiri(sira: Int, g: Gorev) -> Gorunum {
var kutu = "[ ]"
if g.bitti {
kutu = "[x]"
}
return Satir([
Dugme(kutu, || { gorevDegistir(sira) }),
Metin(g.baslik)
])
}
fn gorevListesi(gorevler: [Gorev]) -> Gorunum {
if gorevler.len() == 0 {
return Metin("Henüz görev yok.")
}
var satirlar: [Gorunum] = []
var sira = 0
for g in gorevler {
satirlar.push(gorevSatiri(sira, g))
sira += 1
}
return Sutun(satirlar)
}Şu an iki deri var: varsayılan ve yumusak. Yumuşak deri
düğmeleri hap biçimine sokar, kartları yuvarlatıp yükseltir, girdileri
kenarlık yerine yumuşak bir yuvaya oturtur ve boşlukları açar.
Deri paleti değiştirmez. Kenarları zeminle aynı renge çeken kabartma yaklaşımı yumuşak görünür ama kontrast eşiğini geçemez; buradaki yumuşaklık renkten değil biçimden gelir, o yüzden okunabilirlik her deride aynı kalır.
import "../araclar/arayuz.nar"
fn ciz(n: Int) -> Gorunum = Kart("Deri denemesi", [
Metin("Sayaç: ${n}"),
Satir([
Dugme("Artır", || { }),
IkincilDugme("Sıfırla", || { })
])
])
fn main() {
deriSec("yumusak") // "" verirsen varsayılana döner
uygulamaBaslat("#uygulama", 0, ciz)
}Deri ayrı bir stil öğesinde durur; durum değişip ekran yeniden çizildiğinde bozulmaz. Seçili deriyi deri() ile okuyabilirsin.
Her şey CSS ile yapılır; Nar yalnızca sınıf ekler. Bunun üç sonucu var:
hareket birleştirici üzerinde yürür, kullanıcı araya girdiğinde takılı kalan
durum olmaz (doğruluk hiçbir zaman animationend olayına bağlı
değildir), ve prefers-reduced-motion açıkken hareket tamamen
kapanıp öğe nihai okunabilir durumunda görünür — yalnız süre
kısaltılmaz.
Giriş hareketleri öğe ilk kez göründüğünde bir kez oynar.
Durum her değiştiğinde ağaç baştan çizilir; hareket her çizimde oynasaydı bir
giriş alanına yazan kişi her tuşta bütün ekranın yeniden süzülmesini izlerdi.
Sekme ya da sayfa değiştirirken hareketDefteriniSil() çağırırsan
yeni içerik yeniden girer.
Süreklilik yalnızca yükleme göstergelerinde var: Iskelet ve
Donuyor. Dekoratif öğede sonsuz döngü yok.
Yoğunluk üç kademede: KADEME_SAKIN, KADEME_OLCULU
(varsayılan) ve KADEME_BELIRGIN. Kademeyi bir kez seç, sonra o
kademede kal — tek bir süreyi her geçişe kopyalamak da, her geçişe ayrı süre
uydurmak da yanlış.
import "../araclar/arayuz.nar"
fn ciz(yukleniyor: Bool) -> Gorunum {
if yukleniyor {
return Kart("Kayıtlar", [
Iskelet(3),
Bosluk(8),
Donuyor("Veri geliyor…")
])
}
return Kart("Kayıtlar", [
Sirali([
Metin("Ayşe"),
Metin("Mehmet"),
Metin("Zeynep")
]),
Cizgi,
Hareketli(NABIZ, Rozet("kaydedildi", TON_BASARI))
])
}
fn main() {
kademeSec(KADEME_SAKIN)
uygulamaBaslat("#uygulama", true, ciz)
}Yalnız transform ve opacity hareket eder. width, height, top ve left hareket ettirmek her karede yerleşimi yeniden hesaplatır.
.mobildeSutun() — dar ekranda satır sütuna döner.mobildeGizle() / .masaustundeGizle() — yalnız bir ekranda görünür.mobil(g) — dar ekranda yerine bambaşka bir görünüm gelirİlk üçü saf CSS sınıfıdır; hiçbir JavaScript çalışmaz. .mobil()
ise kırılma noktası değişince uygulamayı yeniden çizer — dar görünüm durum
gibi ele alınır. Tek DOM ağacı vardır; iki dalı birden çizip CSS ile birini
gizlemek olay dinleyicilerini ve odağı ikiye katlardı.
Metotlar zincirlenir ve üst üste sarmaz: Satir([...]).mobildeSutun().mobildeGizle() tek katmandır.
Sınır 600px; telefon dikey ve küçük tabletler dar sayılır. Kendi sorgun
gerekiyorsa dildeki medyaEslesir(sorgu) ve
medyaDinle(sorgu, islev) yerleşikleri elinde.
import "../araclar/arayuz.nar"
fn ciz(n: Int) -> Gorunum = Kart("Sipariş", [
// Geniş ekranda yan yana, telefonda alt alta.
Satir([
Kutu([Metin("Ürün: Nar")]),
Kutu([Metin("Adet: 3")]),
Kutu([Metin("Tutar: 120₺")])
]).mobildeSutun(),
// Geniş ekranda tablo, telefonda liste.
Tablo(["Ad", "Adet"], [["Nar", "3"]]).mobil(Liste(["Nar × 3"])),
Rozet("klavye kısayolu: Ctrl+K", TON_BILGI).mobildeGizle(),
Dugme("Öde", || { }).masaustundeGizle()
])
fn main() {
uygulamaBaslat("#uygulama", 0, ciz)
}Bootstrap buna navbar, Foundation top bar, MUI app bar der; hepsi aynı
işi yapar. Nar'da dördü de birer görünüm: Gezinti,
Kahraman, Bolum, AltBilgi. Hepsi
durumsuzdur — hangi bağlantının etkin olduğunu uygulama söyler, bileşen
kendi içinde saklamaz.
Aralarına boşluk girmesin diye Yigin kullanılır:
Sutun öğeler arasına boşluk koyar, Yigin
koymaz.
Dar ekranda üst şerit kendiliğinden menüye iner. Bunun için ayrı bir kod yazmazsın: iki hâl de her zaman çizilir, hangisinin görüneceğine CSS karar verir.
import "araclar/arayuz.nar"
var uygulama: Uygulama<Int>? = none
fn git(i: Int) {
if uygulama != none {
uygulama!.degistir(i)
}
}
fn ciz(sayfa: Int) -> Gorunum = Yigin([
Gezinti("Kavurma", ["Menü", "Hakkında", "İletişim"], sayfa,
|i| { git(i) },
Dugme("Sipariş ver", || { })),
Kahraman("Günlük kavurma", "Sabah taze kavrulur",
"Siparişin aynı gün yola çıkar.",
[Dugme("Menüye bak", || { git(0) }),
IkincilDugme("Nasıl çalışır?", || { git(1) })]),
Bolum("Neden biz?", "Üç sebep.",
Izgara(3, [
Kart("Taze", [Metin("Kavurma günü kargolanır.")]),
Kart("Şeffaf", [Metin("Çekirdeğin geldiği yeri yazıyoruz.")]),
Kart("Esnek", [Metin("Öğütümü sen seçersin.")])
])),
AltBilgi(["Kavurma", "Kurum"], [
[Bag("Menü", "#"), Bag("Abonelik", "#")],
[Bag("Hakkında", "#"), Bag("İletişim", "#")]
], "© 2026 Kavurma · Mardin")
])
fn main() {
uygulama = uygulamaBaslat("#uygulama", 0, ciz)
}Aynı kaynaktan web, masaüstü ve mobil çıktı almak.
--target seçer.
nar run program.nar | Terminalde çalıştırır (Node.js) |
nar build program.nar --target web | Tek dosyalık HTML sayfası |
nar build program.nar --target masaustu | Kendi penceresinde açılan uygulama |
nar build program.nar --target mobil | Android ve iOS projesi |
Sayfayla konuşan işlemler (bul, olustur) terminalde
sessizce boş döner; yani aynı kod her hedefte çöküp kalmaz.
// Aynı program dört hedefte de çalışır.
fn main() {
let kutu = bul("#uygulama")
if kutu == none {
print("terminaldeyiz")
return
}
kutu!.metinYaz("tarayıcıdayız")
}--target masaustu kendi başına çalışan bir klasör üretir:
programın HTML'i, onu kendi penceresinde açan bir başlatıcı ve Windows
için bir .cmd dosyası.
Kullanıcının bilgisayarında yalnızca Python olması yeter.
pip install pywebview yapılmışsa gerçek bir uygulama penceresi
açılır; yoksa tarayıcının adres çubuksuz uygulama penceresi kullanılır.
nar build ornekler/yapilacaklar_uygulamasi.nar --target masaustu
# üretilen klasör:
# index.html programın kendisi
# baslat.py pencereyi açan başlatıcı
# baslat.cmd Windows kısayolu
# BENIOKU.md--target mobil bir Capacitor projesi üretir.
Capacitor, uygulamanı gerçek bir mobil uygulamanın içine koyar; mağazaya
yüklenebilir.
Üretilen sayfa telefon için hazırlanmıştır: çentikli ekranlarda güvenli alan boşluğu bırakır, dokunma hedefleri parmakla basılacak boyuttadır (44 piksel) ve çift dokunmayla yakınlaştırma kapalıdır.
Önce telefon olmadan dene: üretilen
www/index.html dosyası tarayıcıda doğrudan açılır.
Dürüst not: Android paketi için Android Studio, iOS için macOS + Xcode gerekir. Bu paketleyici projeyi hazır eder ama derlemez; üretilen proje bir cihazda denenmemiştir.
nar build ornekler/yapilacaklar_uygulamasi.nar --target mobil
cd cikti/yapilacaklar_uygulamasi-mobil
npm install
npx cap add android
npx cap open android # Android Studio açılır, ▶ ile çalıştırDerleyicinin Nar ile yazılan parçaları.
Nar bu yolda ilerliyor. Şu ana kadar Nar diliyle yazılanlar:
derleyici/lexer.nar | Sözcüksel çözümleyici — kaynağı token'lara böler |
derleyici/ast.nar | Soyut sözdizim ağacı, konumlar ve S-ifadesi yazıcı |
derleyici/parser.nar | Sözdizim çözümleyici — token'lardan ağaç kurar |
derleyici/tipler.nar | Tip sistemi — atanabilirlik, ortak tip, tip çıkarımı |
derleyici/denetleyici.nar | Tip denetleyici (bildirim aşaması) |
derleyici/denetle.nar | Giriş noktası; içe aktarmaları izler |
araclar/renklendirici.nar | Sözdizimi renklendirici (bu sayfadaki renkler) |
araclar/bicimlendirici.nar | Kod biçimlendirici (nar fmt) |
araclar/arayuz.nar | Bildirimsel arayüz kütüphanesi |
araclar/json.nar | JSON okuyucu ve yazıcı |
araclar/ide_uygulamasi.nar | Nar IDE'nin tüm davranışı |
Sırada gövde denetimi (ifadeler ve deyimler) ve kod üreteci var.
# Nar ile yazılmış derleyiciyle bir dosyayı denetle
nar ozdenetim ornekler/yapilacaklar_uygulamasi.nar
# Derleyici kendi kaynağını da okuyabiliyor
nar ozdenetim derleyici/parser.nar --kutuphanenar ozdenetim önce o derleyiciyi (Nar kaynağından) JavaScript'e
çevirir, sonra Node ile çalıştırıp verdiğin dosyayı denetler.
İçe aktarmaları da izler: import edilen dosyaların
bildirimleri okunur, böylece başka dosyadaki tipler bilinir.
Kapsam: sözcüksel çözümleme, sözdizim çözümleme ve tip
denetiminin bildirim aşaması — tipler, imzalar, arayüz uyumu.
Gövde denetimi (ifadeler, deyimler) henüz Nar'a taşınmadı; tam denetim için
nar check kullanılır.
$ nar ozdenetim derleyici/denetleyici.nar --kutuphane
tamam — hata yok
$ nar ozdenetim bozuk.nar
hata: 2:1 — 'A' tipi zaten tanımlı
hata: 1:15 — bilinmeyen tip: 'Yok'
Ağaçları karşılaştırabilmek için ortak bir yazım gerekir. Nar bunun için S-ifadesi kullanır: ağacın parantezli metin hâli. Konum bilgisi yazılmaz — iki çözümleyicinin satır/sütun hesabı birebir aynı olmak zorunda değil, ağacın yapısı aynı olmak zorunda.
Karşılaştırma yapay örneklerle sınırlı değil: projedeki
bütün .nar dosyaları iki çözümleyiciden de
geçirilir. Ayrı bir modda konumlar (satır, sütun) da karşılaştırılır —
hata mesajlarının nereyi gösterdiği buna bağlı.
Tip sistemi için aynı yöntem: iki taraf aynı 40 tipi kurar, atanabilirlik, ortak tip ve tip çıkarımı 1600 çift üzerinde karşılaştırılır. Denetleyicide ise hata listeleri — mesaj, satır ve sütunla birlikte.
Bu yöntem gerçek hatalar buldu: Türk alfabesinde olmadığı için harf
listesinden düşen q harfi; metin literalindeki
${'{...}'} gömmesinin içeriğinin token'da saklanmaması;
desende birden çok _ kullanıldığında üretilen JavaScript'in
çökmesi.
// Kaynak
fn kare(x: Int) -> Int = x * x
// İki çözümleyicinin de ürettiği S-ifadesi
(modul [(fn "kare" [] [] [(parametre "x" (tad "Int"))] (tad "Int")
(blok [(donus (ikili "*" (ad "x") (ad "x")))]))])Dosyalara bölmek, içe aktarmak, biçimlendirmek, test etmek.
import yazman yeterli.
Örneğin araclar.nar dosyasında yardımcı fonksiyonlar,
program.nar dosyasında ana akış olabilir:
// araclar.nar dosyası:
fn selamla(ad: String) -> String = "Merhaba " + ad
// program.nar dosyası:
import "araclar.nar"
fn main() {
print(selamla("Nar"))
}Not: Bunlar yalnızca nar run ile (Node
üzerinde) anlamlıdır. Tarayıcıda dosya okuma none, yazma
false döner — program çökmez, sadece iş yapmaz.
Okuma her zaman "olabilir" sonuç verir: dosya yoksa none
gelir, kontrol etmen gerekir.
fn main() {
// Dosya yazma ve okuma
dosyaYaz("not.txt", "birinci satır\n")
dosyaEkle("not.txt", "ikinci satır\n")
let icerik = dosyaOku("not.txt")
if icerik != none {
print("satır sayısı:", icerik!.trim().split("\n").len())
}
print("var mı:", dosyaVarMi("not.txt"))
dosyaSil("not.txt")
// Klasördeki dosyalar
let dosyalar = klasorListele("ornekler")
print("kaç dosya:", dosyalar.len())
// Komut satırı argümanları
print("argümanlar:", argumanlar())
// Zaman
print("şu an:", zamanMetni())
let basla = simdi()
var toplam = 0
for i in 1..=1000 { toplam += i }
print("geçen süre (ms):", simdi() - basla)
// Kullanıcıdan girdi (terminalden):
// let satir = satirOku()
// if satir != none { print("girdin: " + satir!) }
}Hata mesajını okumak zaman kaybı değil, en hızlı çözüm yoludur. Örneğin değişmez bir değeri değiştirmeye çalışırsan:
let sayi = 5
sayi = 6hata-mesajlari.nar:3:3 — 'sayi' değişmez, değeri değiştirilemez
|
3 | sayi = 6
| ^^^^
ipucu: bildirimde 'let' yerine 'var' kullanenum seçeneklerinden birini ele almayı unutursan derleyici
tam olarak hangisini unuttuğunu söyler. Bu özellik, sonradan yeni seçenek
eklediğinde çok işe yarar.enum Durum {
Acik
Kapali
Bekliyor
}
fn main() {
match Durum.Acik {
Durum.Acik -> print("açık")
Durum.Kapali -> print("kapalı")
}
}hata-eksik-dal.nar:8:3 — match tam değil; kapsanmayan varyantlar: Bekliyor
|
8 | match Durum.Acik {
| ^^^^^
ipucu: eksik dalları ekle ya da '_ -> ...' dalı koyArarken bakılacak tablolar: hazır fonksiyonlar, metotlar, işleçler.
Her yerde kullanabileceğin, hiçbir şey yazmadan gelen fonksiyonlar.
print(a, b, ...) | Ekrana yazar, aralara boşluk koyar |
str(x) | Herhangi bir değeri metne çevirir |
len(x) | Metin, liste ya da sözlüğün uzunluğu |
int(x) | Metni ya da ondalıklıyı tam sayıya çevirir |
float(x) | Metni ya da tam sayıyı ondalıklıya çevirir |
abs(x) | Mutlak değer (işareti atar) |
min(a, b) · max(a, b) | Küçük olan · büyük olan |
sqrt(x) | Karekök |
pow(a, b) | Üs alma: a üzeri b |
floor(x) · ceil(x) · round(x) | Aşağı · yukarı · en yakına yuvarlar |
random() | 0 ile 1 arasında rastgele ondalıklı sayı |
panic(mesaj) | Programı verilen mesajla durdurur |
assert(kosul, mesaj) | Koşul yanlışsa programı durdurur |
Bir metnin sonuna nokta koyup çağırılır: "abc".upper()
len() | Harf sayısı |
upper() · lower() | Büyük · küçük harfe çevirir |
upperTr() · lowerTr() | Türkçe kurallarıyla: i↔İ, I↔ı |
trim() | Baştaki ve sondaki boşlukları atar |
split(ayrac) | Ayraca göre parçalayıp liste verir |
contains(parca) | İçinde geçiyor mu |
replace(eski, yeni) | Tüm geçtiği yerleri değiştirir |
startsWith(x) · endsWith(x) | Şununla başlıyor mu · bitiyor mu |
slice(bas, son) | Belirtilen aralığı keser |
charAt(i) | i numaralı harf |
indexOf(parca) | Kaçıncı sırada geçiyor (yoksa -1) |
repeat(n) | n kez tekrarlar |
ters() | Ters çevirir |
solaDoldur(n, dolgu) | Sabit genişliğe getirir: "7" → "007" |
sagaDoldur(n, dolgu) | Sağdan doldurarak genişletir |
Bir listenin sonuna nokta koyup çağırılır: [1,2].len()
len() | Eleman sayısı |
push(x) | Sona ekler |
pop() | Sondakini alır ve çıkarır (boşsa none) |
first() · last() | İlk · son eleman (boşsa none) |
contains(x) | İçinde var mı |
indexOf(x) | Kaçıncı sırada (yoksa -1) |
slice(bas, son) | Belirtilen aralığı alır |
reverse() | Ters çevrilmiş kopyasını verir |
sort() | Sıralanmış kopyasını verir |
join(ayrac) | Metin listesini birleştirir |
map(f) | Her elemanı dönüştürür |
filter(f) | Koşula uyanları seçer |
reduce(f, baslangic) | Hepsini tek değere indirger |
benzersiz() | Tekrar edenleri atar |
say(x) | x kaç kez geçiyor |
sirala(|a, b| a - b) | Kendi ölçütünle sıralar |
ters() | Ters çevrilmiş kopyası |
duzlestir() | İç içe listeyi bir kat açar |
eslestir(b) | İki listeyi çiftler: [(a, b), …] |
Fonksiyon yazmadan kullanılır; yalnızca sayı listelerinde geçerlidir.
toplam() | Hepsinin toplamı |
carpim() | Hepsinin çarpımı |
ortalama() | Ortalaması (her zaman ondalıklı) |
enBuyuk() · enKucuk() | En büyük · en küçük eleman (boşsa none) |
kat(n) | Her elemanı n ile çarpar |
artir(n) | Her elemana n ekler |
buyukler(n) · kucukler(n) | n'den büyük · küçük olanlar |
ciftler() · tekler() | Çift · tek sayılar (yalnızca Int) |
Yalnızca metin listelerinde geçerlidir.
buyukHarf() · kucukHarf() | Hepsini Türkçe kurallarıyla büyütür · küçültür |
icerenler(parca) | İçinde o parça geçenleri seçer |
Bir sözlüğün sonuna nokta koyup çağırılır.
len() | Kaç kayıt var |
get(anahtar) | Değeri verir (yoksa none) |
set(anahtar, deger) | Ekler ya da günceller |
has(anahtar) | Bu anahtar var mı |
remove(anahtar) | Kaydı siler |
keys() · values() | Anahtarların · değerlerin listesi |
Yalnızca tarayıcıda anlamlıdır; Node'da bul none döner.
bul(secici) | Bir öğe seçer (yoksa none) |
bulHepsi(secici) | Eşleşen tüm öğeler |
olustur(etiket) | Yeni öğe yapar |
govde() | Sayfanın gövdesi |
zamanla(ms, islev) | Belirtilen süre sonra çalıştırır |
istek(yontem, url, govde, islev) | Ağ isteği; sonuç islev'e gelir |
e.metin() · e.metinYaz(s) | İçindeki yazıyı okur · değiştirir |
e.html() · e.htmlYaz(s) | İçeriği HTML olarak okur · yazar |
e.deger() · e.degerYaz(s) | Giriş kutusunun değeri |
e.sinifEkle(s) · sinifSil(s) · sinifVarMi(s) | CSS sınıfı işlemleri |
e.ozellik(ad) · ozellikYaz(ad, d) | Öznitelik okur · yazar |
e.stil(ad, deger) | Tek bir stil kuralı verir |
e.dinle(olay, islev) | Olay dinler (click, input…) |
e.ekle(cocuk) · e.cikar() | Çocuk ekler · kendini kaldırır |
e.temizle() · e.odaklan() | İçini boşaltır · odağı verir |
e.bul(secici) · e.bulHepsi(secici) | Kendi içinde arar |
Yalnızca terminalde (Node) anlamlıdır; tarayıcıda iş yapmazlar.
dosyaOku(yol) | Dosyayı okur (yoksa none) |
dosyaYaz(yol, icerik) | Dosyayı yazar (üzerine yazar) |
dosyaEkle(yol, icerik) | Dosyanın sonuna ekler |
dosyaVarMi(yol) · dosyaSil(yol) | Var mı · siler |
klasorListele(yol) | Klasördeki dosya adları |
satirOku() | Girdiden bir satır okur (bitince none) |
tumGirdi() | Girdinin tamamını okur |
argumanlar() | Komut satırı argümanları |
komutCalistir(program, argumanlar) | Başka bir programı çalıştırır; .cikis() .cikti() .hata() |
cik(kod) | Programı verilen çıkış koduyla bitirir |
simdi() | 1970'ten beri geçen milisaniye |
zamanMetni() | Okunabilir tarih-saat |
Yukarıdakiler aşağıdakilerden önce hesaplanır.
a.b · a() · a[i] · a! | Erişim, çağrı, dizin, zorla aç |
-a · !a | Eksi işaret, değil |
* / % | Çarpma, bölme, kalan |
+ - | Toplama (metin birleştirme), çıkarma |
.. ..= | Aralık (son hariç · son dahil) |
< <= > >= | Karşılaştırma |
== != | Eşit mi, eşit değil mi |
&& | Ve |
|| | Veya |
?? | Yoksa şunu kullan |