Küçük Lisp, küçük tercüman


33

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.14ve -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)verir 1.

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), bir 1iş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ğerlendirmek a, isme bağlı değeri verir a, 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 ( 0veya 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ğildir d, 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 dtanı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 dve 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çerlidir i.

Kuyruk özyineleme doğrudan özyineleme (bir işlev kendisini çağıran) ve dolaylı özyineleme (işlev hem çalışmalıdır aaramalar 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.


“Tamamen rakamlardan oluşan herhangi bir simge bir tamsayı değişmezdir. (Lider sıfırlar tamam.) Rakam olmayan herhangi bir simge, bir sembol, hatta 123abc, 3.14 ve -10 gibi sayısal görünümlü örnekler.” "Tam anlamıyla en azından -2 ^ 31 - 2 ^ 31-1 arası desteklenmesi gerekiyor."
msh210

3
@ msh210 Gerçekten değil, çünkü eski belirteçlerden bahsediyor , ikincisi de değerlerden bahsediyor . Girmenin doğrudan bir yolu olmasa da -1, yaparak -1 değerini hala yapabilirim (s 0 1).
DLosc

1
@coredump İlgili Wikipedia makalesini okuduktan sonra , uygulamanın gerçekten dinamik olana daha yakın olduğu, ancak kapsam dahilinde olmadığı sonucuna vardım. Fonksiyonundaki Değişkenler Ffonksiyonu bulunmayan Geğer Fçağrılar G(dinamik kapsama alınması gibi), ancak aynı zamanda fonksiyonu bulunmayan Heğer Hiç 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.
DLosc

1
Başka bir deyişle: kapsam iç içe geçme eksikliği nedeniyle, bir uygulama dinamik veya sözlüksel bir kapsam belirleme stratejisini kullanabilir ve aynı sonuçları bulabilir. Herhangi bir zamanda kapsamdaki tek isimler 1) eğer mevcut durumda yürürlükte olan işlevin yerel isimleri, ve 2) global isimler. Kapaklar desteklenmiyor. (Referans uygulama çağrı yığını karşılık gelen isim bağları bir yığın tutar -. Dinamik tarzı bir yaklaşım, ben uygulamak için en kolay olacağını düşünüyorum)
DLosc

1
Zorunlu xkcd .
mınxomaτ

Yanıtlar:


11

Python 2, 685 675 660 657 646 642 640 bayt

import sys,re
E=[]
G=zip("chtsle",[eval("lambda x,y=0:"+f)for f
in"[x]+y (x+[E])[0] x[1:] x-y +(x<y) +(x==y)".split()])
def V(e,L=E):
 while 1:
    try:return e and int("0%s"%e)
    except:A=e[1:]
    if""<e:return dict(G+L).get(e,e)
    f=V(e[0],L)
    if""<f:
     if f in"iv":t=V(A[0],L);e=(e[~bool(t)],t)[f>"u"];continue
     if"e">f:G[:]+=(A[0],V(A[1],L)),
     return A[0]
    if[]>f or f[0]:A=[V(a,L)for a in A]
    if[]>f:return f(*A)
    P,e=f[-2:];L=([(P,A)],zip(P,A))[P<""]
F=lambda x:V<x<""and"(%s)"%" ".join(map(F,x))or"%s"%x
for t in re.sub("([()])"," \\1 ",sys.stdin.read()).split():
 if")"==t:t=E.pop()
 if"("==t:E+=[],
 elif E:E[-1]+=t,
 else:print F(V(t))

STDIN'den girişi okur ve çıktıyı STDOUT'a yazar.

Kesin olarak gerekli olmamakla birlikte, tercüman nullary fonksiyonlarını ve makroları destekler ve yapılan kuyruk çağrılarını optimize eder v.

açıklama

ayrıştırma

Giriş ayrıştırmak için, öncelikle her oluşumunu çevreleyen (ve )boşluklarla ve kelimelerle çıkan dize bölmek; bu bize belirteçlerin listesini verir. EBaşlangıçta boş olan bir ifade yığını tutuyoruz . Jetonları şu sırayla tarıyoruz:

  • a ile karşılaşırsak (, ifade yığınının üstündeki boş bir listeye basarız;
  • a ile karşılaşırsak ), ifade yığınının üstündeki değeri açarız ve bunu yığında daha önce bulunan listeye ekleriz;
  • Aksi halde, mevcut dizgiyi bir dize olarak ifade yığınının üstündeki listeye ekleriz (tamsayıları bu aşamada dize olarak tutar ve değerlendirme sırasında bunları ayrıştırırız.)

Sıradan bir simge işlerken ya da yığından gelen bir ifadeyi açtıktan sonra ), ifade yığını boşsa, üst düzey bir ifadedeyiz ve aksi takdirde eklediğimiz değeri, kullanarak V()ve sonucunu kullanarak, uygun şekilde biçimlendirilmiş şekilde yazdırabilir F().

Değerlendirme

Küresel kapsamı, Ganahtar / değer çiftlerinin bir listesi olarak koruyoruz . Başlangıçta, yalnızca yerleşik işlevler (ancak makro olarak değerlendirdiğimiz makrolar değil v), lambdalar olarak uygulanır.

Değerlendirme içinde olur V(), değerlendirmek için ifade alır, eve yerel alanı, L(bir üst düzey ifadesini değerlendirirken, yerel alanı boştur.) Bağırsaklar, anahtar / değer çiftleri listesini üstelik, V()canlı Sonsuz bir döngü içinde, daha sonra açıklanacağı gibi kuyruk çağrısı optimizasyonunu (TCO) nasıl yaptığımızdır.

Türüne egöre işlem yapıyoruz :

  • boş liste veya bir int'ye dönüştürülebilir bir dize ise, hemen geri döndürürüz (int'ye dönüştürüldükten sonra); aksi takdirde,

  • bir dizge ise, onu küresel ve yerel kapsamların birleştirilmesinden oluşturulmuş bir sözlükte ararız. İlişkili bir değer bulursak, onu döndürürüz; aksi halde, ebir yerleşik makro (yani adı olmalıdır q, i, dveya v) ve biz değişmeden geri. Aksi takdirde, ebir dize değilse,

  • e(boş olmayan) bir liste, yani bir işlev çağrısıdır. Listenin ilk öğesini, yani işlev ifadesini V()yinelemeli olarak çağırarak (mevcut yerel kapsamı kullanarak) değerlendiriyoruz; sonucu çağırırız f. Listenin geri kalanı A, argümanların listesidir. fyalnızca bir dize olabilir; bu durumda yerleşik bir makro (veya işlev v), bir lambda, bu durumda yerleşik bir işlev veya bir liste olabilir; bu durumda kullanıcı tanımlı bir işlev veya makrodur.

    Eğer faa dize, yani bir yerleşik makro, biz yerinde halledeceğim. Makro ise, iya vilk işleneni değerlendiriyoruz, ya ikinci ya da üçüncü işleneni buna göre seçiyoruz i, ya da ilk işlenenin sonucunu kullanıyorsanız v; TCO'yu yenecek seçili ifadeyi yinelemeli olarak değerlendirmek yerine, sadece eifade ile değiştiririz ve döngünün başlangıcına atlarız. Eğer fmakro olduğunu d, biz, küresel kapsamına, ilk elemanı ikinci eleman ikinci işlenen değerlendiren sonucudur ilk işlenen ve bir çift ekleme Gve ilk işlenen dönün. Aksi takdirde, fmakro q, bu durumda işlemcisini doğrudan döndürürüz.

    Kısacası, eğer fbir lambda ise veya ilk elemanı olmayan bir liste ise (), o zaman makro olmayan bir fonksiyondur, bu durumda argümanlarını, yani elemanlarını Ave Asonucunu değiştiririz .

    Eğer fbir lambda ise, onu paketlenmiş argümanları ileterek adlandırırız Ave sonucu veririz .

    Aksi takdirde, fbir liste, yani kullanıcı tanımlı bir işlev veya makro; parametre listesi, en son ikinci öğedir ve gövdesi son öğedir. Makrolarda olduğu gibi ive vTCO'yu yapmak için de vücudu özyinelemeli olarak değerlendirmiyoruz, aksine evücutla değiştiriyoruz ve bir sonraki yinelemeye devam ediyoruz. Aksine ive vancak, aynı zamanda yerel kapsamını değiştirin, Lfonksiyonun yeni yerel kapsamı ile. Parametre listesi, Paslında bir liste ise, yeni yerel kapsam parametre listesini sıkıştırmak suretiyle P, argüman listesiyle oluşturulur A; Aksi takdirde, yeni yerel kapsamın sadece bir elemanı olan çift değişkenli bir fonksiyonla uğraşıyoruz (P, A).

REPL

Onunla oynamak istiyorsanız, tercümanın bir REPL versiyonu. Sembollerin yeniden tanımlanmasını ve komut satırı argümanları veya (import <filename>)makro üzerinden dosyaları içe aktarmayı destekler . Tercümandan çıkmak için girişi sonlandırın (genellikle, Ctrl + D veya Ctrl + Z).

Ve işte birleştirme düzenini uygulayan örnek bir oturum:


Zlib kullanarak daha kısa sürede bir şeyler elde edebilirsiniz :) Kodunuzu bayt cinsinden dönüştürün ve aşağıdakilerle değiştirin:import zlib;exec(zlib.decompress(your_code_compressed_in_bytes))
Labo

Sen atayarak iki bayt kurtarabilecek A[0]sadece blok haricinde sonra biraz tek karakter değişkene
Hannes Karppila

@HannesKarppila Bu doğru, ancak bu (çünkü Abu durumda boş) nullary fonksiyonlarını kıracak ve ben "gerileme" istemiyorum.
Ell

4

C (GNU), 1095 bayt

Aksiyonun büyük kısmı dev vfonksiyonda gerçekleşiyor. Bunun yerine açıkça kuyruk Özyinelemeyi uygulanması, vgelen çağrıların çoğunun böylece yapılandırılmıştır viçin vgcc kuyruğu özyineleme optimizasyonu tarafından ele alınacaktır. Çöp toplama yok.

Bu, GCC uzantılarını yoğun şekilde kullanır, böylece yalnızca gcc ile derlenebilir (komutu kullanın gcc -w -Os tl.c). Ayrıca scanf, genellikle kullandığım Windows'ta bulunmayan bazı uzantıları da kullanıyor . Ayrıştırıcıyı standart scanfolarak yazma olasılığı o kadar berbattı ki, programı test etmek için Linux VM kullandım. scanfKarakter sınıfları olmadan ayrıştırma muhtemelen 100+ bayt eklerdi.

#define O(...)({o*_=malloc(32);*_=(o){__VA_ARGS__};_;})
#define P printf
#define F(I,B)({for(I;x->c;x=x->l)B;})
#define Z return
typedef struct o{struct o*n,*l,*c;int i,t;}o;E(o a,o b){Z
a.n?!strcmp(a.n,b.n):a.c?b.c&&E(*a.c,*b.c)&E(*a.l,*b.l):!b.c&a.i==b.i;}p(o*x){x->t?P("%d ",x->i):x->n?P("%s ",x->n):F(P("("),p(x->c);P(")"));}o*x,G,N;*C(o*h,o*t){Z
O(c:h,l:t);}o*v(o*x,o*e){o*W(o*l,o*e){Z
l->c?C(v(l->c,e),W(l->l,e)):&N;}o*y,*a,*f;int t;Z
x->c?y=v(x->c,e),x=x->l,t=y->i,t?9/t?a=v(x->c,e),t>7?(t>8?a->c:a->l)?:a:t>6?v(a,e):t<6?x=v(x->l->c,e),t>4?C(a,x):O(t:1,i:t>3?E(*a,*x):t>2?a->i<x->i:a->i-x->i):v((a-&N&&!a->t|a->i?x:x->l)->l->c,e):(t&1&&d(x->c->n,v(x->l->c,e)),x->c):(y->l->l->l?y=y->l:(x=W(x,e)),a=y->c,v(y->l->c,a->n?O(n:a->n,c:x,l:&G):F(f=&G,(f=O(n:a->c->n,c:x->c,l:f),a=a->l);f))):x->n?e->n?strcmp(x->n,e->n)?v(x,e->l):e->c:e:x;}d(o*n,o*x){*v(O(n:""),&G)=(o){n:n,c:x,l:O()};}*R(h){char*z,*q;Z
scanf(" %m[^ \n()]",&q)>0?h=strtoul(q,&z,10),C(*z?O(n:q):O(t:1,i:h),R()):~getchar()&1?q=R(),C(q,R()):&N;}main(i){for(;++i<12;)d(strndup("slecivthqd"+i-2,1),O(i:i));F(x=R(),p(v(x->c,&G)));}

Yarı ungolfed

typedef struct o o;
struct o {
    char* n;
    o* l, //next in this list
     * c; 
    int i,
        t;
} ;



#define O(...)({o*_=malloc(32);*_=(o){__VA_ARGS__};_;})

E(o a, o b) { //tests equality 
    return
        a.n ? !strcmp(a.n,b.n) :
        a.t ? a.i==b.i :
        a.c ? b.c && E(*a.c,*b.c)&E(*a.l,*b.l) :
        !b.c
    ;
}

#define P printf


p(o*x){
    x->t?P("%d ",x->i):x->n?P("%s ",x->n):({for(P("(");x->c;x=x->l)p(x->c);P(")");});
}


o*_,G,N; //N = nil



o*C(o*h,o*t){return O(c:h,l:t);}


/*
        2 3 4 5 6 7 8 9 10 11
        s l e c i v t h d  q
    */


o* v(o* x, o* e) { //takes list, int, or name
    o*W(o* l, o* e) { //eval each item in list
        return l->c ? C(v(l->c ,e), W(l->l, e)) : &N;
    }

    o*y,*a,*f;int t;
    return x->c ? //nonempty list = function/macro call
        y = v(x->c,e), //evals to function/macro
        x = x->l,   //list position of first arg (if it exists)
        (t=y->t)?   //builtin no., if any
             t>9 ?
              t&1 ? x->c // 11 = q
                : //10 = d
                (d(x->c,v(x->l->c,e)),x->c)
           : (a = v(x->c,e), //eval'd first arg
             t)>7 ? // t/h
                (t > 8 ? a->c : a->l) ?: a
           : t>6 ? //v
                v(a,e)
           : (x = x->l, //position of 2nd arg in list
             t)>5 ? //i
                v( (a->n||a->l||a->i|a->t>1 ? x : x->l)->c, e)
           : (x = v(x->c,e), //evaluated 2nd arg
             t)>4 ? // c
                C(a,x)
           : O(t:1,i:
                t>3 ? E(*a,*x) :  //e
                t>2 ? a->i<x->i : //l
                      a->i-x->i   //s
              )
        :
        (
            y->l->l->l ? //whether this is macro
                y = y->l :
                (x = W(x,e)),  //eval args
            a = y->c,  //a = arg list
            //a = a->n ? x=C(x, &N), C(a, &N) : a, //transform variadic style to normal
            v(y->l->c,
               a->n ? //variadic
                O(n:a->n,c:x,l:&G)
              : ({
                   for(f=&G; a->c; a=a->l,x=x->l)
                      f=O(n:a->c->n, c: x->c, l:f);
                   f;
                })
            )
        )
    :
    x->n ? // name
        e->n ?
            strcmp(x->n,e->n) ?
                v(x,e->l)
            : e->c
        : e
     : x; //int or nil
}

d(o*n,o*x){
    * v(O(n:""),&G) =
        (o){n:n->n,c:x,l:O()};
}


;
o*R(){
    char*z,*q;int h;
return scanf(" %m[^ \n()]",&q)>0?
    h=strtoul(q,&z,10),
    C(*z ? O(n:q) : O(t:1,i:h), R())
: getchar()&1?&N:(q=R(),C(q,R()));
}
main(i) {

    for(;++i<12;) d(O(n:strndup("slecivthdq"+i-2,1)),O(t:i));

    o *q;
    for(q=R(); q->c; q=q->l) p(v(q->c,&G));

}

Derlenmiş yürütülebilir dosyanın kullanımı nedir? REPL mi? Bir dosya adını girdi olarak alıyor mu?
ckjbgames

@ckjbgames Stdin'den bir program okur.
feersum

Tamam. Bence cevabınızı düzeltmeli ve not almalısınız.
ckjbgames

1

Seylan, 2422 bayt

(Sanırım bu şimdiye kadarki en uzun golf programım.)

import ceylon.language{sh=shared,va=variable,fo=formal,O=Object}import ceylon.language.meta.model{F=Function}interface X{sh fo V v(S t);sh fo G g;}class G(va Map<S,V>m)satisfies X{v(S t)=>m[t]else nV;g=>this;sh S d(S s,V v){assert(!s in m);m=map{s->v,*m};return s;}}V nV=>nothing;class LC(G c,Map<S,V>m)satisfies X{g=>c;v(S t)=>m[t]else g.v(t);}alias C=>V|Co;interface Co{sh fo C st();}interface V{sh fo C l(X c,V[]a);sh default Boolean b=>0<1;sh fo C vO(X c);sh default V vF(X c){va C v=vO(c);while(is Co n=v){v=n.st();}assert(is V r=v);return r;}}class L(sh V*i)satisfies V{vO(X c)=>if(nonempty i)then i[0].vF(c).l(c,i.rest)else this;equals(O o)=>if(is L o)then i==o.i else 1<0;b=>!i.empty;string=>"(``" ".join(i)``)";hash=>i.hash;sh actual C l(X c,V[]p){value[h,ns,x]=i.size<3then[f,i[0],i[1]]else[m,i[1],i[2]];value n=if(is L ns)then[*ns.i.narrow<S>()]else ns;assert(is S|S[]n,is V x);V[]a=h(c,p);LC lC=if(is S n)then LC(c.g,map{n->L(*a)})else LC(c.g,map(zipEntries(n,a)));return object satisfies Co{st()=>x.vO(lC);};}}class S(String n)satisfies V{vO(X c)=>c.v(this);l(X c,V[]a)=>nV;equals(O o)=>if(is S o)then n==o.n else 1<0;hash=>n.hash;string=>n;}class I(sh Integer i)satisfies V{vO(X c)=>this;l(X c,V[]a)=>nV;equals(O o)=>if(is I o)then i==o.i else 1<0;hash=>i;b=>!i.zero;string=>i.string;}V[]f(X c,V[]a)=>[for(v in a)v.vF(c)];V[]m(X c,V[]a)=>a;L c(X c,V h,L t)=>L(h,*t.i);V h(X c,L l)=>l.i[0]else L();V t(X c,L l)=>L(*l.i.rest);I s(X c,I f,I s)=>I(f.i-s.i);I l(X c,I f,I s)=>I(f.i<s.i then 1else 0);I e(X c,V v1,V v2)=>I(v1==v2then 1else 0);C v(X c,V v)=>v.vO(c);V q(X c,V a)=>a;C i(X c,V d,V t,V f)=>d.vF(c).b then t.vO(c)else f.vO(c);S d(X c,S s,V x)=>c.g.d(s,x.vF(c));class B<A>(F<C,A>nat,V[](X,V[])h=f)satisfies V given A satisfies[X,V+]{vO(X c)=>nV;string=>nat.declaration.name;l(X c,V[]a)=>nat.apply(c,*h(c,a));}{<S->V>*}b=>{S("c")->B(`c`),S("h")->B(`h`),S("t")->B(`t`),S("s")->B(`s`),S("l")->B(`l`),S("e")->B(`e`),S("v")->B(`v`),S("q")->B(`q`,m),S("i")->B(`i`,m),S("d")->B(`d`,m)};[V*]p(String inp){value ts=inp.split(" \n()".contains,1<0,1<0);va[[V*]*]s=[];va[V*]l=[];for(t in ts){if(t in" \n"){}else if(t=="("){s=[l,*s];l=[];}else if(t==")"){l=[L(*l.reversed),*(s[0]else[])];s=s.rest;}else if(exists i=parseInteger(t),i>=0){l=[I(i),*l];}else{l=[S(t),*l];}}return l.reversed;}sh void run(){va value u="";while(exists l=process.readLine()){u=u+l+"\n";}V[]e=p(u);X c=G(map(b));for(v in e){print(v.vF(c));}}

Bazı yerlerde bazı iki harfli tanımlayıcılar kullandığım için bazı baytları daha fazla golf oynayabilirdim, ancak bunlar için anlamlı anlamlı tekil harfler kullandım. Her ne kadar bu şekilde de Ceylon gibi görünmese de ...

Bu, nesne yönelimli bir uygulamadır.

Biz bir değer arayüzüne sahip Vuygulayan sınıfları ile L- (bir Seylan sıralı etrafında sarıcı listesi V,) S(sembol - bir dize etrafında sarıcı), I(tamsayı - Bir Seylan tamsayı etrafında sarıcı) ve B(fonksiyonu yerleşik veya makro, a etrafında sarıcı Seylan işlevi).

equalsYöntemi (ve ayrıca hashsadece semboller için gerçekten gerekli olan niteliği) ve ayrıca stringçıktı için standart niteliği uygulayarak standart Ceylon eşitliği gösterimini kullanıyorum .

Bir Boolean özniteliğine sahibiz b(varsayılan olarak doğrudur, geçersiz listelerde geçersiz kılınır Ive Lgeri döndürülürler) ve iki yöntem l(çağrı, yani bu nesneyi bir işlev olarak kullanın) ve vO(bir adımı değerlendiririz). Her ikisi de bir değer veya bir başka adım için değerlendirmeye olanak sağlayan bir Süreklilik nesnesi döndürür vFve sonuç artık bir devam etmeyene kadar döngüler (tam olarak değerlendir).

Bir bağlam arayüzü değişkenlere erişim sağlar. Genel Gbağlam için ( dyerleşik kullanarak değişken eklemeye izin verir ) ve LCyerel bir bağlam için, bir kullanıcı işlevinin ifadesini değerlendirirken etkin olan (genel içeriğe geri döner) iki uygulama vardır.

Sembol değerlendirmesi içeriğe erişir, (boş değilse) önce ilk öğelerini değerlendirerek ve sonra çağrı yöntemini çağırarak değerlendirmeyi listeler. Çağrı sadece listeler ve yerleşikler tarafından gerçekleştirilir - önce argümanı değerlendirir (eğer bir işlev varsa, eğer bir makro değilse) ve sonra asıl ilginç olanı yapar - sadece kodlanmış olan yapıdakiler için, listeler için yeni bir yerel içerik oluşturur ve bununla devam.

Yerleşimler için, kullandığım şeye benzer bir numara kullandım Shift Yorumlayıcımda , bu da onları ihtiyaç duydukları argüman tipleriyle tanımlamama izin veriyor, ancak onları yansıma kullanarak genel bir sıra ile çağırıyorlar (türler çağrı zamanında kontrol edilecek). Bu fonksiyonların / makroların içindeki dönüşüm / savsama sıkıntısını önler, ancak meta-model Functionnesnelerini alabilmem için üst seviye fonksiyonlara ihtiyaç duyar .

p(Ayrıştırma) işlevi belirteçleri üzerinde döngüler sonra, boşluklar, yeni satır ve parantez dizeyi böler ve bir yığın ve çalışan bir listesini kullanarak listeleri oluşturur.

Tercüman ( rungiriş noktası olan yöntemde) daha sonra bu ifadelerin listesini alır (sadece değerlerdir), her birini değerlendirir ve sonucu yazdırır.


Aşağıda yorumların yer aldığı ve bir biçimlendirici üzerinden geçen bir sürüm.

Golf oynamaya başlamadan önceki bir sürüm (ve hala liste değerlendirme ile ilgili bazı yanlış anlamalar var) Github depomda bulunuyor , yakında bunu oraya koyacağım (bu yüzden orijinali istiyorsanız ilk versiyona bakmayı unutmayın).

//  Tiny Lisp, tiny interpreter
//
// An interpreter for a tiny subset of Lisp, from which most of the
// rest of the language can be bootstrapped.
//
// Question:   https://codegolf.stackexchange.com/q/62886/2338
// My answer:  https://codegolf.stackexchange.com/a/63352/2338
//
import ceylon.language {
    sh=shared,
    va=variable,
    fo=formal,
    O=Object
}
import ceylon.language.meta.model {
    F=Function
}

// context

interface X {
    sh fo V v(S t);
    sh fo G g;
}
// global (mutable) context, with the buildins 
class G(va Map<S,V> m) satisfies X {
    // get entry throws error on undefined variables. 
    v(S t) => m[t] else nV;
    g => this;
    sh S d(S s, V v) {
        // error when already defined
        assert (!s in m);
        // building a new map is cheaper (code-golf wise) than having a mutable one.
        m = map { s->v, *m };
        return s;
    }
}

// This is simply a shorter way of writing "this is not an allowed operation".
// It will throw an exception when trying to access it.
// nV stands for "no value".
V nV => nothing;

// local context
class LC(G c, Map<S,V> m) satisfies X {
    g => c;
    v(S t) => m[t] else g.v(t);
    // sh actual String string => "[local: ``m``, global: ``g``]";
}

// continuation or value
alias C => V|Co;

// continuation
interface Co {
    sh fo C st();
}

// value
interface V {
    // use this as a function and call with arguments.
    // will not work for all types of stuff.
    sh fo C l(X c, V[] a);
    // check the truthiness. Defaults to true, as
    // only lists and integers can be falsy.
    sh default Boolean b => 0 < 1;
    // evaluate once (return either a value or a continuation).
    // will not work for all kinds of expression.
    sh fo C vO(X c);
    /// evaluate fully
    sh default V vF(X c) {
        va C v = vO(c);
        while (is Co n = v) {
            v = n.st();
        }
        assert (is V r = v);
        return r;
    }
}
class L(sh V* i) satisfies V {

    vO(X c) => if (nonempty i) then i[0].vF(c).l(c, i.rest) else this;
    equals(O o) => if (is L o) then i == o.i else 1 < 0;
    b => !i.empty;
    string => "(``" ".join(i)``)";
    hash => i.hash;

    sh actual C l(X c, V[] p) {
        value [h, ns, x] =
                i.size < 3
                then [f, i[0], i[1]]
                else [m, i[1], i[2]];
        // parameter names – either a single symbol, or a list of symbols.
        // If it is a list, we filter it to throw out any non-symbols.
        // Should throw an error if there are any, but this is harder.
        value n = if (is L ns) then [*ns.i.narrow<S>()] else ns;
        assert (is S|S[] n, is V x);
        V[] a = h(c, p);

        // local context
        LC lC = if (is S n) then
            LC(c.g, map { n -> L(*a) })
        else
            LC(c.g, map(zipEntries(n, a)));
        // return a continuation instead of actually
        // calling it here, to allow stack unwinding.
        return object satisfies Co {
            st() => x.vO(lC);
        };
    }
}

// symbol
class S(String n) satisfies V {
    // evaluate: resolve
    vO(X c) => c.v(this);
    // call is not allowed
    l(X c, V[] a) => nV;
    // equal if name is equal
    equals(O o) => if (is S o) then n == o.n else 1 < 0;
    hash => n.hash;
    string => n;
}

// integer
class I(sh Integer i) satisfies V {

    vO(X c) => this;
    l(X c, V[] a) => nV;
    equals(O o) => if (is I o) then i == o.i else 1 < 0;
    hash => i;
    b => !i.zero;
    string => i.string;
}

// argument handlers for functions or macros
V[] f(X c, V[] a) => [for (v in a) v.vF(c)];
V[] m(X c, V[] a) => a;

// build-in functions
// construct
L c(X c, V h, L t) => L(h, *t.i);
// head
V h(X c, L l) => l.i[0] else L();
// tail
V t(X c, L l) => L(*l.i.rest);
// subtract
I s(X c, I f, I s) => I(f.i - s.i);
// lessThan
I l(X c, I f, I s) => I(f.i < s.i then 1 else 0);
// equal
I e(X c, V v1, V v2) => I(v1 == v2 then 1 else 0);
// eval (returns potentially a continuation)
C v(X c, V v) => v.vO(c);

// build-in macros
// quote
V q(X c, V a) => a;
// if (also returns potentially a continuation)
C i(X c, V d, V t, V f) => d.vF(c).b then t.vO(c) else f.vO(c);
// define symbol in global context
S d(X c, S s, V x) => c.g.d(s, x.vF(c));

// buildin function or macro, made from a native function and an argument handler
class B<A>(F<C,A> nat, V[](X, V[]) h = f)
        satisfies V
        given A satisfies [X, V+] {
    vO(X c) => nV;
    string => nat.declaration.name;
    // this "apply" is a hack which breaks type safety ...
    // but it will be checked at runtime.
    l(X c, V[] a) => nat.apply(c, *h(c, a));
}

// define buildins
{<S->V>*} b => {
    S("c") -> B(`c`),
    S("h") -> B(`h`),
    S("t") -> B(`t`),
    S("s") -> B(`s`),
    S("l") -> B(`l`),
    S("e") -> B(`e`),
    S("v") -> B(`v`),
    S("q") -> B(`q`, m),
    S("i") -> B(`i`, m),
    S("d") -> B(`d`, m)
};

// parses a string into a list of expressions.
[V*] p(String inp) {
    // split string into tokens (retain separators, don't group them –
    // whitespace and empty strings will be sorted out later in the loop)
    value ts = inp.split(" \n()".contains, 1 < 0, 1 < 0);
    // stack of not yet finished nested lists, outer most at bottom
    va [[V*]*] s = [];
    // current list, in reverse order (because appending at the start is shorter)
    va [V*] l = [];
    for (t in ts) {
        if (t in " \n") {
            // do nothing for empty tokens
        } else if (t == "(") {
            // push the current list onto the stack, open a new list.
            s = [l, *s];
            l = [];
        } else if (t == ")") {
            // build a lisp list from the current list,
            // pop the latest list from the stack, append the created lisp list. 
            l = [L(*l.reversed), *(s[0] else [])];
            s = s.rest;
        } else if (exists i = parseInteger(t), i >= 0) {
            // append an integer to the current list.
            l = [I(i), *l];
        } else {
            // append a symbol to the current list.
            l = [S(t), *l];
        }
    }
    return l.reversed;
}

// Runs the interpreter.
// This handles input and output, calls the parser and evaluates the expressions.
sh void run() {
    va value u = "";
    while (exists l = process.readLine()) {
        u = u + l + "\n";
    }
    V[] e = p(u);
    // create global context
    X c = G(map(b));
    // iterate over the expressions, ...
    for (v in e) {
        // print("  '``v``' → ...");
        // ... evaluate each (fully) and print the result.
        print(v.vF(c));
    }
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.