Zinterpretuj swój język, ale nie siebie?

21

Istnieje wiele wyzwań, które mówią „interpretować X”, gdzie X jest prostym językiem. Moim zdaniem jest to zbyt nudne. Aby dać wszystkim zwlekającym ludziom w Internecie coś ciekawego do zrobienia, możesz spróbować wykonać to wyzwanie:

Wyzwanie

Wybierz język $LANG. $LANGmoże być dowolnym kompletnym językiem programowania Turinga lub kompletnym podzbiorem języka programowania Turinga. Pamiętaj, że jeśli pominiesz funkcję swojego języka w $LANGtłumaczeniu, nie możesz używać go również w swoim własnym programie, ponieważ twoje zgłoszenie musi być również zapisane $LANG.

Napisz kompilator / tłumacz dla $LANGnapisanych w $LANG. Możesz użyć wszystkich udogodnień (w tym evali znajomych) swojego języka, które są dostępne do napisania tego kompilatora. Aby zadanie było trudniejsze, istnieje jedno ograniczenie: Twój program powinien być w stanie interpretować / kompilować wszystkie prawidłowe programy z $LANGwyjątkiem samego interpretera / kompilatora. Jeśli okaże się, że program do interpretacji / kompilacji jest twoim interpreterem lub kompilatorem (niezależnie od nazwy pliku), twój program powinien zrobić coś zupełnie niezwiązanego z funkcjonalnością interpretera lub kompilatora (np. Wygładzanie lub drukowanie Hello, world!).

Aby uczynić to zadanie jeszcze bardziej złożonym, twój program nie może czytać własnego źródła podczas kompilacji lub interpretacji.

Dane techniczne

  • To zadanie polega na kodowaniu w golfa. Zgłoszenie zawierające najmniej poprawnych znaków wygrywa. W przypadku remisu rozwiązanie, które zostało przesłane jako pierwsze, wygrywa.
  • Twój program / skrypt powinien odczytać program do interpretacji z pliku. Możesz na stałe zakodować jego ścieżkę i nazwę. Po odczytaniu pliku możesz go skompilować do innego pliku (który musi być wykonywalny w systemie) lub uruchomić go bezpośrednio. Jeśli $LANGbrakuje możliwości odczytu plików, możesz wybrać inny sposób odczytu w odpowiednim kodzie $LANG. Nie możesz wybrać $LANGpodzbioru innego języka, ale z usuniętymi możliwościami odczytu plików.
  • Obowiązują zwykłe zasady gry w golfa. To znaczy: Twój osobisty język dla zwierząt domowych, który wymyśliłeś tylko po to, aby rozwiązać to wyzwanie, jest zabroniony, jeśli rozwiązanie stanie się trywialne przy jego użyciu (np. Zdefiniowanie programu jednoznakowego, który dokładnie implementuje rozwiązanie). Zachęca się do nadużywania zasad.
FUZxxl
źródło
Czy możemy zdefiniować dla tego język, o ile jest on w pełni ukończony?
Cruncher,
@Cruncher Tak, jesteś. Aby uzyskać więcej informacji, zobacz ostatni punkt specyfikacji.
FUZxxl,

Odpowiedzi:

8

Ruby, 63

b=$<.read
t="b=$<.read\nt=%p\nb!=t%%t&&eval(b)"
b!=t%t&&eval(b)
Lowjacker
źródło
Odpowiedź zaakceptowana, o ile nie ma mniejszego rozwiązania.
FUZxxl,
11

Perl, 89 znaków, bez oszukiwania

$_=q($_=q(Q);s/Q/$_/;($q=join"",<>)eq$_?die:eval$q);s/Q/$_/;($q=join"",<>)eq$_?die:eval$q

Zauważ, że ten kod jest bardzo wybredny w kwestii tego, co liczy się jako „samo”. W szczególności nie rozpoznaje siebie, jeśli na wejściu znajdują się znaki nowej linii lub inne dodatkowe białe znaki. Aby to przetestować, zapisz go w pliku o nazwie (na przykład) unquine.pli wykonaj następujące czynności:

$ perl unquine.pl unquine.pl
Died at unquine.pl line 1, <> line 1.

Pamiętaj, że unquine.plplik powinien mieć dokładnie 89 bajtów, nie więcej, nie mniej. Uruchomienie go z innym skryptem Perla jako wejściem powoduje wykonanie innego skryptu, tak jak powinien:

$ perl unquine.pl hello.pl
Hello, world!

Jak sama nazwa może sugerować, implementacja oparta jest na quine - a konkretnie na tej:

$_=q($_=q(Q);s/Q/$_/);s/Q/$_/

Ten kod ustawia się na $_równi; reszta programu (która oczywiście musi być zduplikowana w środku $_) po prostu porównuje $_dane wejściowe, umiera, jeśli pasują, i ocenia dane wejściowe inaczej.

Ilmari Karonen
źródło
Można zastąpić tę &&/ ;parę z potrójnego (jeden char off, podwojona przez quining). Świetny pomysł i wdrożenie!
JB
@JB: Dobry chwyt! Teraz do 89 znaków.
Ilmari Karonen,
5

GolfScript, 30 znaków

{`".~"+"#{$<.read}".@=!{~}*}.~

Ten program odczytuje zawartość pliku o nazwie podanej w wierszu poleceń i, jeśli nie jest dokładnie równy powyższemu kodowi, interpretuje go jako GolfScript. Jeśli dane wejściowe są dokładnie równe powyższemu kodowi, zostaną po prostu wydrukowane w niezmienionej formie (z wyjątkiem nowej linii dołączonej na końcu).

Jest to dość prosta adaptacja tego programu umożliwiającego identyfikację . Konkretnie:

  • { } to dosłowny blok kodu w GolfScript.
  • .~, zastosowane do bloku kodu, duplikuje blok i wykonuje kopię.

Wewnątrz bloku kodu:

  • ` uszeregowuje kopię bloku kodu.
  • ".~"+dołącza do niego znaki .~, uzyskując ciąg zawierający kod źródłowy programu.
  • "#{$<.read}"to udokumentowany hack, który pozwala na wykonanie kodu Ruby w GolfScript. W takim przypadku wykonuje instrukcję Ruby $<.read(bezwstydnie skradzioną z rozwiązania Ruby Lowjackera ), która odczytuje i zwraca zawartość wszystkich plików określonych w wierszu poleceń. Ten hack jest potrzebny, ponieważ sam GolfScript nie zapewnia jawnych możliwości we / wy pliku.
  • .@ duplikuje i tasuje elementy na górze stosu, tak że stos zawiera dwie kopie zawartości pliku, a następnie kod źródłowy tego programu.
  • =! porównuje dwa górne elementy na stosie (tj. zawartość pliku i źródło), zwracając 1, jeśli są różne i 0, jeśli są takie same.
  • {~}*ocenia pozostałą kopię zawartości pliku jako kod GolfScript, ale tylko wtedy, gdy wynikiem porównania jest 1. (Technicznie wykonuje blok kodu {~}tyle razy , ile wynika z liczby na stosie, tj. 0 lub 1 razy. blok, ~jest operatorem ewaluacji GolfScript).

Ps. Jeśli dozwolone jest czytanie kodu do wykonania ze standardowego wejścia, to wyzwanie można rozwiązać 21 znakami bez konieczności wysyłania wiadomości do Ruby:

{`".~"+1$=!{""\~}*}.~

Ten program odczyta ciąg wejściowy ze standardowego wejścia i jeśli nie pasuje do własnego źródła, wykonuje go (z pustym wejściem). Podobnie jak w powyższym programie, wejście pasujące do źródła jest po prostu powtarzane.

Ilmari Karonen
źródło
Wygląda ładnie, ale nie wygląda na to, że czytasz dane wejściowe z pliku.
FUZxxl,
Naprawiono, teraz czyta z pliku (dokładnie) takiego jak rozwiązanie Lowjackera.
Ilmari Karonen
5

Python, 167 130 118 bajtów

To moja pierwsza próba gry w golfa, więc proszę! Interpretuje każdy program oprócz siebie

Poprawiona wersja:

i=open(raw_input()).read();q='i=open(raw_input()).read();q=%s;i==q%%repr(q)and a;exec(i)\n';i==q%repr(q)and a;exec(i)

Jeśli się to dostanie, oznacza to, że:

Traceback (most recent call last):
  File "pygolf.py", line 1, in <module>
    i=open(raw_input()).read();q='i=open(raw_input()).read();q=%s;i==q%%repr(q)and a;exec(i)\n';i==q%repr(q)and a;exec(i)
NameError: name 'a' is not defined

Myślę, że to rozwiązanie działa prawie tak samo jak Ilmari Karonen, podstawową ideą jest coś takiego:

input = read_some_file()
if input == some_quine()
    barf()
interpret(input)

Quine, którego użyłem, było oparte na tym:

(lambda x: x + repr((x,)))('(lambda x: x + repr((x,)))',)

Ale od tamtej pory zdałem sobie sprawę, że znacznie krótszy quine to:

q='q=%s;q%%repr(q)';q%repr(q)

Może to być nawet krótsze, jeśli zezwolisz na interaktywną powłokę python, w którym to przypadku możesz:

'%s;_%%repr(_)';_%repr(_)

Ponieważ python nie ma krótkiego sposobu na uzyskanie argumentów wiersza poleceń, poszedłem z raw_input () (która wciąż jest dość długa, ale nie tak długa jak

import sys;sys.argv[1]

Zastosowanie to:

echo "foobar.py" | python quinterpretter.py

lub

python quinterpretter.py
<type filename and hit enter>

Znalazłem krótszy quine do użycia, ale oto moja stara wersja (dla potomności):

i=open(raw_input()).read();a if i==(lambda x,y:x+repr((x,y))+y)('i=open(raw_input()).read();a if i==(lambda x,y:x+repr((x,y))+y)', ' else 1;exec(i)\n') else 1;exec(i)
Gordon Bailey
źródło
Zamień% s na% r i usuń repr. % r oznacza surowe i to w zasadzie ta sama rzecz.
Loovjo,
4

Nie mogę dokładnie odczytać z pliku przy użyciu Javascript (ok, mógłbym, używając HTMLR FileReader, ale to sprawia, że ​​jest to o wiele bardziej skomplikowane niż potrzebuję). Jest to funkcja, która akceptuje program JavaScript jako ciąg znaków i uruchamia go.

To prawdopodobnie nie jest tak golfowe, jak mogłoby być, ale i tak jest:

JavaScript, 252

function c(p){q='\"';s='\\';a="function c(p){q='\"';s='\\';a=%;a=a.slice(0,17)+s+a.slice(17,24)+a[23]+a.slice(24);a=q+a.replace('%',q+a+q)+q;alert(a);}";a=a.slice(0,17)+s+a.slice(17,24)+a[23]+a.slice(24);a=a.replace('%',q+a+q);alert(a);if(p!=a)eval(p)}

Daj mi znać, jeśli ktoś zna lepszą technikę tworzenia quinu w Javascript.

Peter Olson
źródło
1
Poniżej umieściłem 135-znakowe rozwiązanie JS, oparte na twoim kodzie i moim rozwiązaniu Perl. +1 za inspirację!
Ilmari Karonen,
2
read p<p;read c<c;[ "$p" = "$c" ]||. ./c

45 znaków sh (powłoka POSIX). Kod do uruchomienia musi znajdować się w pliku ./c.

Kod samego interpretera musi znajdować się w pliku ./p, więc chyba trochę mnie oszukałem, chociaż wyzwanie nie wydaje się, aby go zabrać. Czy może to zdyskwalifikuje mój „język” z bycia „kompletnym językiem programowania”?

Za pomocą narzędzia, które zwykle jest zewnętrznym plikiem wykonywalnym, ale teoretycznie może być wbudowane w powłokę, kod można skrócić:

cmp -s p c||. ./c

To 18 znaków, a -sbit ma na celu jedynie pomijanie wiersza, który w innym przypadku zawsze byłby drukowany dla prawidłowych programów (innych niż ja).

I wtedy zawsze możesz zbudować wersję języka powłoki, która robi powyższe, z bardziej zwięzłą składnią.

I wtedy zawsze możesz zbudować program, który, gdy dane wejściowe składają się z pojedynczego „.” - lub do diabła, pusty ciąg - ocenia zawartość innego pliku jako normalny kod i nazywa to językiem programowania. Tak więc pusty ciąg znaków będzie Twoim rozwiązaniem dla wyzwania w języku, który zbudowałeś. Oto tłumacz dla takiego języka:

read code; if [ "$code" ]; then eval "$code"; else . ./othercode; fi

Używając języka, który interpretuje powyższy skrypt, rozwiązaniem jest pusty ciąg znaków. Lokalizacja kodu nie musi być już zakodowana.

Problem?

TaylanUB
źródło
2
Wyzwanie mówi, że „twój program nie może czytać własnego źródła”.
Ilmari Karonen,
Darnit, zmarnowałem trochę czasu. Widzę, że nawet mówi, że nie wolno używać funkcji, które zostaną pominięte. Byłoby to sprzeczne z funkcją pustych ciągów. Z drugiej strony, interpreter musi pomijać / zmieniać funkcjonalność, jeśli kod samego kompilatora / interpretera powoduje inne zachowanie w nowym języku. W każdym razie dobrze się bawiłem pisząc błąd.
TaylanUB,
@TaylanUB Cóż, właściwie musisz interpretować wszystkie prawidłowe programy $ lang z wyjątkiem samego tłumacza.
FUZxxl,
@FUZxxl Tak, język „sh + pusty ciąg” jest w przeciwnym razie równoważny z sh (jeśli kod nie jest pustym ciągiem), a napisany w nim program pusty ciąg również interpretuje kod sh (który należy wstawić ./othercode) i robi nic, gdy kod jest pustym ciągiem. Nie powinienem był nazywać pliku ./othercode, to wprowadza w błąd; to tylko kod, który interpretuje interpreter napisany w pustym języku łańcuchowym.
TaylanUB,
2

JavaScript, 135 znaków

function c(p){q='function c(p){q=%27Q%27;p!=unescape(q).replace(/Q/,q)?eval(p):alert()}';p!=unescape(q).replace(/Q/,q)?eval(p):alert()}

Rozwiązanie JavaScript Petera Olsona zainspirowało mnie do przeniesienia mojego rozwiązania Perla do JS. Podobnie jak jego rozwiązanie, ten kod definiuje funkcję, cktóra akceptuje ciąg znaków i sprawdza go, jeśli nie jest on równy powyższemu kodowi.

Zajęło mi trochę czasu, aby dowiedzieć się dobrym sposobem radzenia sobie z brakiem zrównoważonych ograniczników smyczkowych w JavaScript, aż znalazłem to, co z perspektywy czasu jest oczywiste rozwiązanie: unescape().

Dogodnie, mój kod nie zawiera żadnych ukośników odwrotnych ani podwójnych cudzysłowów, więc można go bezpiecznie przechowywać w ciągach podwójnych cudzysłowów. Ułatwia to testowanie:

e = "function c(p){q='function c(p){q=%27Q%27;p!=unescape(q).replace(/Q/,q)?eval(p):alert()}';p!=unescape(q).replace(/Q/,q)?eval(p):alert()}"
h = "alert('Hello, world!')"

eval(e)  // defines the function c()

c(h)     // evaluates h
c(e)     // does not evaluate e, alerts "undefined" instead
Ilmari Karonen
źródło
Można wymienić alert()ze 0aby nie zrobić niczego zamiast ostrzegania undefinedi zapisz 13 znaków.
Peter Olson,
@PeterOlson: Tak, ale zadanie mówi, że „twój program powinien zrobić coś zupełnie niezwiązanego”, jeśli sam się wykryje. Interpretuję to jako oznaczające, że powinno coś zrobić - przypuszczam, że najlepiej coś widocznego dla użytkownika. Poza tym bardziej podoba mi się to w ten sposób. :) (Ps. Tak, na zewnątrz pada śnieg! Nareszcie jest zima!)
nadeszła Ilmari Karonen,
1
@Ilmari Nie robienie niczego nie jest związane z interpretacją JavaScript IMHO.
FUZxxl
Możesz iść p=>...zamiastfunction c(p)
FireCubez
2

Common Lisp, 59

#+~ #.(#:a)(defun L(p)(compile-file p))(push :~ *features*)
  • W nowej wersji Lisp REPL skompiluj plik (np. sbcl --load)
  • Masz teraz funkcję L, która może kompilować pliki Common Lisp
  • Jeśli jednak zadzwonisz (L <your file>), podczas odczytu pliku sygnalizowany jest błąd .

Czemu?

Ponieważ po raz pierwszy włożyłeś :~słowo kluczowe do *features*. Teraz twoje środowisko wie o tej ~funkcji, a makro czytnika #+, po ocenie ~ wyrażenia cechy , odniesie sukces i przeczyta następujący formularz, zamiast pomijać go tak jak za pierwszym razem. W twoim pliku jest następujący formularz #.(#:a), który prosi o ocenę (#:a)w czasie odczytu i użycie wynikowej wartości jako odczytywanego kodu. Ale (#:a)wywołuje funkcję powiązaną z niewewnętrznym symbolem #:a. Ponieważ #:ajest niewzruszony, jest świeżym symbolem, który nie jest związany z żadną funkcją (tj. Niefboundp ). Błąd.

rdzeń rdzeniowy
źródło
1

Schemat, 48 lub 51 znaków

Schemat to język z wieloma różnymi implementacjami. Mimo że implementacje muszą być zgodne z najnowszym RnRS, najnowszy działający standard (R6RS) był niepopularny ze względu na brak minimalizmu. R7RS zostanie wkrótce wydany jako remedium, przy jednoczesnym podzieleniu języka na 2. Pierwszy język jest potężny i minimalistyczny, a drugi, nadzbiór pierwszego, który ma na celu zapewnienie rozszerzeń funkcji dla interoperacyjności między implementacjami. Do tego czasu polegamy na SRFI (Scheme Requests Implementation), które zapewniają (jeśli są implementowane w implementacji hosta lub ręcznie (jak to jest powszechne w schemacie)) środki do przenośnego wykonywania typowych zadań. Wszystko po to, by powiedzieć, że pierwszy fragment kodu (51 znaków), choć jest tak przenośny, jak to tylko możliwe, opiera się na SRFI-22 (wykonywanie skryptów schematu w systemie UNIX) w celu uzyskania dostępu do argumentów wiersza poleceń:

(define(main x y)(case y(x => error)(else => load)))

lub bardziej czytelnie:

(define (main current-file arg)
  (case arg
    [current-file => error]
    [else => load]))

Drugi (48 znaków) oznacza plik bez interpretacji, który nie może się ocenić (w środowisku zerowym):

(define(e)(write(eval(read)null-environment))(e))

lub bardziej czytelnie:

(define (interpret)
  (write (eval (read) null-environment))
  (interpret))
Samuel Duclos
źródło
Twój kod nie działa, jeśli skopiujesz tłumacza.
FUZxxl,
1
Pozostaw odpowiedź schematu zawierającą zagnieżdżone nawiasy w prozie.
Cyoce
1

Groovy, 13 bajtów

{Eval.me(it)}

To powinno interpretować podzbiór Groovy.

przypadki testowe:

p={Eval.me(it)}

p'''
    (0..37).each{println"1234567890JIHGFEDCBAKLMNOPQRST!?,.ZYXWVU"[it..it+2]}
'''

p'''
    {Eval.me(it)}
'''

Niestety, choć na pewno barfs, robi to w sposób całkowicie podobny do interpretera i robi to dla całkiem sporego wkładu.

Armand
źródło
W którym wierszu czytasz program do interpretacji? Twój kod jest interesujący, chociaż nie jest prawidłowym przesłaniem tego zadania.
FUZxxl,
Zakładam, że błąd to coś w rodzaju „przekroczony limit rekurencji”?
Ilmari Karonen,