Lisp programcıları Lisp'in çok küçük ilkel işlemlerden oluşturulabilen güçlü bir dil olduğunu söylemektedir . Bu fikri, bir lehçeye çeviren bir tercümanı golf yaparak uygulamaya koyalım tinylisp
.
Dil özellikleri
Bu şartnamede, sonucu "tanımsız" olarak tanımlanan herhangi bir durum tercümanınızda her şeyi yapabilir: çarpışma, sessizce başarısızlık, rastgele gobbldegook üretme veya beklendiği gibi çalışma. Python 3'te referans uygulaması burada mevcuttur .
Sözdizimi
Tinylisp içinde Jetonlar olan (
, )
ya da parantez içinde veya uzay dışında bir veya birden fazla yazdırılabilir ASCII karakter dizesi. (Yani, aşağıdaki regex:. [()]|[^() ]+
) Tamamen rakamlardan oluşan herhangi bir belirteç bir tamsayıdır. (Baştaki sıfırlar gayet iyi.) Olmayan basamak içeren herhangi belirteç bile sayısal görünümlü örnekler gibi, bir semboldür 123abc
, 3.14
ve -10
. Belirteçleri ayırdığı sürece, tüm boşluklar (en azından ASCII karakterleri 32 ve 10 dahil) dikkate alınmaz.
Bir tinylisp programı bir dizi ifadeden oluşur. Her ifade bir tam sayı, sembol veya s ifadesidir (liste). Listeler parantez içine alınmış sıfır veya daha fazla ifadeden oluşur. Öğeler arasında ayırıcı kullanılmaz. İfadelere örnekler:
4
tinylisp!!
()
(c b a)
(q ((1 2)(3 4)))
İyi biçimlenmemiş ifadeler (özellikle, benzersiz parantezlere sahip) ifadeleri tanımsız davranışlar sağlar. (Başvuru uygulaması açık parenleri otomatik olarak kapatır ve benzersiz yakın parenlerde ayrıştırmayı durdurur.)
Veri tipleri
Tinylisp'in veri tipleri tamsayılar, semboller ve listelerdir. Çıktı formatları tanımsız olmasına rağmen yerleşik fonksiyonlar ve makrolar da bir tür olarak kabul edilebilir. Bir liste, herhangi bir türde herhangi bir sayıdaki değeri içerebilir ve keyfi olarak derinden yerleştirilebilir. Tamsayılar en az -2 ^ 31 ila 2 ^ 31-1 arasında desteklenmelidir.
Boş liste ()
- sıfır olarak da adlandırılır - ve tamsayı 0
, mantıksal olarak yanlış sayılan tek değerdir ; diğer tüm tamsayılar, boş olmayan listeler, yerleşikler ve tüm semboller mantıksal olarak doğrudur.
Değerlendirme
Bir programdaki ifadeler sırayla değerlendirilir ve her birinin sonuçları stdout'a gönderilir (daha sonra çıkış formatlaması hakkında daha fazla bilgi için).
- Bir tamsayı değişmezi kendini değerlendirir.
- Boş liste
()
kendi kendine değerlendirilir. - Bir veya daha fazla öğenin listesi, ilk öğesini değerlendirir ve geri kalan öğelerle argümanlar olarak çağırarak onu bir işlev veya makro olarak değerlendirir. Öğe bir işlev / makro değilse, davranış tanımsızdır.
- Bir sembol bir isim olarak değerlendirilir ve mevcut fonksiyonda o isme bağlı değeri verir. Eğer isim geçerli fonksiyonda tanımlanmamışsa, global kapsamda kendisine bağlı olan değeri değerlendirir. Ad geçerli veya global kapsamda tanımlanmadıysa, sonuç tanımlanmaz (başvuru uygulaması bir hata mesajı verir ve sıfır değerini döndürür).
Yerleşik işlevler ve makrolar
Tinylisp'te yedi yerleşik işlev vardır. Bir işlev kendilerine bir işlem yapmadan ve sonucu döndürmeden önce her bir argümanı değerlendirir.
c
- eksileri [ürün listesi]. Bir değer ve bir liste olmak üzere iki argüman alır ve listenin önüne değer ekleyerek elde edilen yeni bir liste döndürür.h
- baş ( araba , Lisp terminolojisinde). Bir liste alır ve içindeki ilk öğeyi döndürür, sıfır ise nil döndürür.t
- kuyruk ( cdr , Lisp terminolojisinde). Bir liste alır ve ilk öğeyi içeren tümünü içeren yeni bir liste döndürür, eğer nil verilirse nil.s
- çıkarma. İki tamsayı alır ve ilk eksi ikinciyi döndürür.l
- daha az. İki tamsayı alır; ilki ikinciden küçükse 1, aksi takdirde 0 döndürür.e
- eşit. Aynı türde iki değer alır (her iki tam sayı, her iki liste veya her iki simge); İkisi eşitse (veya her elementte aynıysa) 1, aksi takdirde 0 döndürür. Yerleşiklerin eşitlik için test edilmesi tanımlanmamıştır (referans uygulaması beklendiği gibi çalışır).v
- eval. Bir ifadeyi temsil eden bir liste, tam sayı veya simge alır ve onu değerlendirir. Örneğin, yapmak(v (q (c a b)))
aynıdır(c a b)
;(v 1)
verir1
.
Buradaki "Değer", aksi belirtilmedikçe, tüm listeleri, tam sayıları, simgeleri veya yerleşik yapıları içerir. Bir fonksiyon belirli tipler alıyorsa listeleniyorsa, farklı tiplerin geçirilmesi yanlış sayıda argümandan geçildiği gibi tanımsız bir davranıştır (referans uygulaması genellikle kilitlenir).
Tinylisp'te üç yerleşik makro vardır. Bir işlevden farklı olarak bir makro, işlem yapmadan önce argümanlarını değerlendirmez.
q
- alıntı. Bir ifade alır ve değerlenmemiş olarak döndürür. Örneğin değerlendirme(1 2 3)
, bir1
işlev veya makro olarak çağırmaya çalıştığı için bir hata verir , ancak(q (1 2 3))
listeyi döndürür(1 2 3)
. Değerlendirmeka
, isme bağlı değeri verira
, ancak(q a)
ismin kendisini verir.i
- Eğer. Üç ifade alır: bir koşul, bir çok ifade ve bir çok ifade. İlk önce durumu değerlendirir. Sonuç sahte ise (0
veya sıfır), iffalse ifadesini değerlendirir ve döndürür. Aksi takdirde, iftrue ifadesini değerlendirir ve döndürür. Döndürülmeyen ifadenin asla değerlendirilmediğine dikkat edin.d
- def. Bir sembol ve ifade alır. İfadeyi değerlendirir ve global kapsamda bir isim olarak kabul edilen verilen sembole bağlar , sonra sembolü döndürür. Bir ismi yeniden tanımlamaya çalışmak başarısız olmalıdır (sessizce, bir mesajla veya kilitlenerek; referans uygulaması bir hata mesajı görüntüler). Not: Adını iletmeden önce alıntı yapmak gerekli değildird
, ancak değerlendirilmesini istemediğiniz bir liste veya simge ise ifadeyi alıntılamak gerekir: örn(d x (q (1 2 3)))
.
Makroya yanlış sayıda argüman iletme tanımsız davranış (referans uygulaması çöküyor). İlk argüman olarak bir sembol olmayan bir şeyi geçmek d
tanımsız davranışlardır (referans uygulaması bir hata vermez, ancak daha sonra referans alınamaz).
Kullanıcı tanımlı fonksiyonlar ve makrolar
Bu on yerleşikten başlayarak, dil yeni işlevler ve makrolar oluşturularak genişletilebilir. Bunların tahsis edilmiş veri türü yoktur; bunlar sadece belli bir yapıya sahip listeler:
- Bir fonksiyon iki öğenin bir listesidir. Birincisi, bir veya daha fazla parametre adının listesi veya işleve iletilen herhangi bir argüman listesini alacak tek bir addır (bu nedenle değişken işlev işlevlerine izin verilir). İkincisi, işlev gövdesi olan bir ifadedir.
- Bir makro, parametre adlarından önce nil içermemesi dışında bir işlevle aynıdır, bu nedenle üç öğe listesi yapar. (Nil ile başlamayan üç maddeli listeleri çağırmaya çalışmak tanımsız davranışlardır; referans uygulaması ilk argümanı yok sayar ve bunları makro olarak da kabul eder.)
Örneğin, aşağıdaki ifade iki tam sayı ekleyen bir işlevdir:
(q List must be quoted to prevent evaluation
(
(x y) Parameter names
(s x (s 0 y)) Expression (in infix, x - (0 - y))
)
)
Ve herhangi bir sayıda argüman alan ve birincisini değerlendiren ve veren bir makro:
(q
(
()
args
(v (h args))
)
)
Fonksiyonlar ve makrolar doğrudan çağrılabilir, isimleri kullanarak bağlanabilir d
ve diğer fonksiyonlara veya makrolara aktarılabilir.
İşlev gövdeleri tanım zamanında çalıştırılmadığından özyinelemeli işlevler kolayca tanımlanabilir:
(d len
(q (
(list)
(i list If list is nonempty
(s 1 (s 0 (len (t list)))) 1 - (0 - len(tail(list)))
0 else 0
)
))
)
Bununla birlikte, yukarıdakilerin bir uzunluk fonksiyonu tanımlamak için iyi bir yol olmadığını unutmayın, çünkü kullanmaz ...
Kuyruk çağrısı özyinelemesi
Kuyruk çağrısı özyinmesi Lisp'te önemli bir kavramdır. Döngü olarak belirli özyineleme türlerini uygular, böylece çağrı yığınını küçük tutar. Tinylisp tercümanınız uygun kuyruk çağrısı özyinelemesi yapmalıdır !
- Kullanıcı tanımlı bir işlev veya makronun dönüş ifadesi, başka bir kullanıcı tanımlı işlev veya makro çağrısıysa, tercümanınız bu aramayı değerlendirmek için özyineleme kullanmamalıdır. Bunun yerine, geçerli işlev ve argümanları yeni işlev ve argümanlarla değiştirmeli ve çağrılar zinciri çözülene kadar döngülenmelidir.
- Kullanıcı tanımlı bir işlev veya makronun dönüş ifadesi bir çağrıysa
i
, seçilen dalı hemen değerlendirmeyin. Bunun yerine, kullanıcı tarafından tanımlanan başka bir işlev veya makro çağrısı olup olmadığını kontrol edin. Eğer öyleyse, işlevi ve argümanları yukarıdaki gibi değiştirin. Bu keyfi derinlemesine iç içe geçmiş oluşumları için de geçerlidiri
.
Kuyruk özyineleme doğrudan özyineleme (bir işlev kendisini çağıran) ve dolaylı özyineleme (işlev hem çalışmalıdır a
aramalar fonksiyon b
çağrıları [vb] işlevini çağırır ki a
).
Bir kuyruk özyinelemeli uzunluk fonksiyonu (bir yardımcı fonksiyonu ile len*
):
(d len*
(q (
(list accum)
(i list
(len*
(t list)
(s 1 (s 0 accum))
)
accum
)
))
)
(d len
(q (
(list)
(len* list 0)
))
)
Bu uygulama, yalnızca maksimum tamsayı boyutuyla sınırlı, isteğe bağlı olarak büyük listelerde çalışır.
kapsam
İşlev parametreleri yerel değişkenlerdir (aslında değiştirilemezler çünkü sabitler). Bu işlev çağrısının gövdesi yürütülürken kapsam dahilindedir ve daha derin çağrılar sırasında ve işlev geri döndükten sonra kapsam dışıdır. Genel olarak tanımlanmış adları "gölgeleyebilir", böylece genel adı geçici olarak kullanılamaz duruma getirebilir. Örneğin, aşağıdaki kod 41 değil 5 döndürür:
(d x 42)
(d f
(q (
(x)
(s x 1)
))
)
(f 6)
Bununla birlikte, aşağıdaki kod 41'i döndürür, çünkü x
çağrı seviyesinde 1, çağrı seviyesi 2'den erişilemez:
(d x 42)
(d f
(q (
(x)
(g 15)
))
)
(d g
(q (
(y)
(s x 1)
))
)
(f 6)
Herhangi bir zamanda kapsamdaki tek isimler 1) varsa, şu anda yürütülen fonksiyonun yerel isimleri ve 2) global isimlerdir.
Gönderim şartları
Giriş ve çıkış
Tercümanınız programı stdin'den veya stdin veya komut satırı argümanı ile belirtilen bir dosyadan okuyabilir. Her ifadeyi değerlendirdikten sonra, bu ifadenin sonucunu izleyen bir yeni satırla stdout'a çıkarmalıdır.
- Tamsayılar, uygulama dilinizin en doğal temsilinde gösterilmelidir. Negatif tamsayılar, eksi işaretleriyle birlikte çıkarılabilir.
- Semboller, çevreleyen tırnak işaretleri veya çıkışlar olmadan dizge olarak çıkarılmalıdır .
- Listeler, tüm öğeler boşlukla ayrılmış ve parantez içinde sarılmış olarak verilmelidir. Parantezler içindeki boşluk isteğe bağlıdır:
(1 2 3)
ve( 1 2 3 )
her ikisi de kabul edilebilir formatlardır. - Yerleşik işlevlerin ve makroların çıktısı tanımsız davranışlardır. (Referans yorumu onları olarak gösterir
<built-in function>
.)
Diğer
Referans yorumlayıcısı bir REPL ortamı ve tinylisp modüllerini diğer dosyalardan yükleme kabiliyetini içerir; bunlar kolaylık sağlamak için verilmiştir ve bu zorluk için gerekli değildir.
Test durumları
Test durumları birkaç gruba ayrılmıştır, böylece daha karmaşık olanları daha önce çalışarak daha basit olanları test edebilirsiniz. Bununla birlikte, hepsini bir dosyaya döktüğünüzde gayet iyi çalışırlar. Çalıştırmadan önce başlıkları ve beklenen çıktıyı çıkarmayı unutmayın.
Kuyruk çağrısı özyinelemesini uygun şekilde uyguladıysanız, son (çok parçalı) test durumu yığın taşmasına neden olmadan geri dönecektir. Referans uygulaması dizüstü bilgisayarımda yaklaşık altı saniye içinde hesaplar.
-1
, yaparak -1 değerini hala yapabilirim (s 0 1)
.
F
fonksiyonu bulunmayan G
eğer F
çağrılar G
(dinamik kapsama alınması gibi), ancak aynı zamanda fonksiyonu bulunmayan H
eğer H
iç içe işlev tanımlı içindedir F
(anlamsal kapsama alınması gibi) - bkz test durumu 5. çağırarak "sözcük Yani "yanıltıcı olabilir.