Zagraj w golfa jako Purple Interpreter

13

Zagraj w golfa jako Purple Interpreter

Fioletowy to esolang, który został zaprojektowany w dwóch głównych celach:

  • Być minimalizacją bakłażana , ponieważ po prostu nie ma wystarczającej ilości samomodyfikujących się języków z jedną instrukcją.
  • Przyznać możliwość przerażająco małych golfistów. Moje pierwsze przejście w rozsądnie w pełni funkcjonalnym interpreterze Python 2 to tylko 702 bajty i jestem pewien, że bardziej doświadczony golfista mógłby się z tego trochę zgolić.

Twoim celem jest napisanie tłumacza dla tego języka.

Informacje na temat Purple:

Program Purple to sekwencja znaków umieszczonych w nieskończonej, adresowalnej tablicy pamięci, tak że pierwszy znak programu jest umieszczony pod adresem zero. Reszta tablicy (zarówno przed, jak i po zapisaniu programu Purple) jest inicjowana do zera.

Istnieją trzy rejestry w fioletowy, zwane i b , a ja , z których każdy może posiadać podpisane całkowitą i jest inicjowany do zera. i jest również wskaźnikiem instrukcji i zawsze wskazuje na aktualnie wykonywaną instrukcję Purple.

W każdym cyklu tłumacz interpretuje sekwencję trzech ciągłych znaków rozpoczynających się od miejsca w pamięci wskazanego wskaźnikiem instrukcji i próbuje wykonać tę sekwencję jako instrukcję fioletową. Następnie wskaźnik instrukcji jest zawsze zwiększany o 3.

Składniowo instrukcja Purple składa się z trzech znaków (lub ich kodowań) z rzędu, takich jak „ xyz ”.

Pierwszy znak x może być dowolnym z poniższych:

abABio

Symbole te mają następujące znaczenie:

a - Place the result in register a.
b - Place the result in register b.
A - Place the result in the location in memory referred to by register a.
B - Place the result in the location in memory referred to by register b.
i - Set the instruction pointer to the result.
o - Output the result to stdout.

Pozostałe dwa bajty y i z mogą być dowolnymi z poniższych:

abABio1

Każdy z tych symboli ma następujące znaczenie:

a - Return the contents of register a.
b - Return the contents of register b.
A - Return the contents of the memory array at the address stored in register a.
B - Return the contents of the memory array at the address stored in register b.
i - Return the contents of register i (the instruction pointer).
o - Return the value of a single character read from stdin.
1 - Return the literal numeric value 1.

Po pobraniu instrukcji interpreter Fioletowy oceni y, a następnie z , odejmie wynik z od wyniku y , a następnie wykona akcję wskazaną przez x na różnicy.

Jeśli sekwencja trzech znaków (lub ich kodowania) nie jest prawidłową instrukcją w kolorze fioletowym, interpreter zatrzymuje się natychmiast bez żadnych błędów.

Twój tłumacz musi:

  • Bądź kompletnym programem, a nie funkcją.
  • Nigdy nie wysyłaj do stderr, chyba że EOF zostanie odczytany .
  • Zachowuj się identycznie jak implementacja referencyjna na wszystkich dobrze sformułowanych danych wejściowych, które nie obejmują bardzo dużych liczb, w tym programów testowych podanych poniżej. (Cóż, identycznie do czasu - może działać wolniej, ale nie za bardzo!)

Możesz dostarczyć program tłumaczowi w dowolnej formie: odczytać go z pliku, osadzić w programie jako ciąg znaków lub odczytać ze standardowego wejścia.

Przypadki testowe:

Program

ooo

po uruchomieniu z wejściem

z!

powinien ustąpić

Y

Program

bbboobiii

po uruchomieniu z wejściem

It's a cat program.

(lub jakikolwiek inny wkład) powinien dać

It's a cat program.

(lub cokolwiek, co otrzymał), a następnie zacznij od nowa i zrób to samo .


Program

Aoab11bi1bABoAaiba

po uruchomieniu z wejściem

0

powinien ustąpić

0

a następnie zatrzymaj, ale po uruchomieniu z wejściem

1

powinien kontynuować wysyłanie

1

na zawsze.


Program

b1bbb1oAbabaa1ab1Ab1Bi1b

powinien ustąpić

b1bbb1oAbabaa1ab1Ab1Bi1b

Program

aA1aa1bb1oAbbi1bb1bbAb1Bi1b Purple is the awesomest! Why haven't you tried it yet?
!dlroW ,olleG

powinien ustąpić

Hello, World!

Punktacja:

To jest , więc wygrywa najkrótsze źródło w bajtach, potencjalnie zmodyfikowane przez następującą premię.

Premia:

  • -10%, jeśli interpreter odczyta nazwę pliku ze standardowego lub argumentu wiersza poleceń i załaduje program z pliku.
kwintopia
źródło
1
Jaki jest rozmiar komórek pamięci? bajty, znaki (Unicode?), (dowolne) duże liczby całkowite? Wygląda na to, że używasz „znak” i „bajt” o tym samym znaczeniu.
Paŭlo Ebermann,
@ PaŭloEbermann zgaduję, że jest to specyficzne dla implementacji; na przykład muszę użyć uint32znaków i MAXINT dla ints
cat
2
@sysreq Czy to naprawdę bloker? Twoja implementacja może mieć po prostu dwie taśmy, jedną dla negatywnych, a drugą dla pozytywnych wskaźników. (Tak, to zajmie trochę więcej kodu, ale chyba nie tyle.)
Paŭlo Ebermann
1
@sysreq w zasadzie, fioletowy interpreter byłby programem, który odczytuje fioletowy program ze standardowego interfejsu, a następnie robi wszystko, co ten program by zrobił. Pierwszy program Purple (tłumacz) może działać w dowolnym tłumaczu, który ci się podoba. Program, który całkowicie nadpisuje najniższe adresy pamięci danymi wejściowymi, a następnie usuwa się, zanim jakoś przeskoczy do odczytanego kodu się kwalifikuje (choć nie sądzę, że jest to faktycznie możliwe).
kwintopia
2
Byłem tak blisko posiadania środowiska wykonawczego zdolnego do samodzielnej interpretacji, ale spóźniłem się.
kot

Odpowiedzi:

7

Pyth, 148 128 121 bajtów (lub 124 * .9 = 111,6, patrz dół)

J,00=kjb.z .eXHkCbz#=b-Fm?=zx"oabABi1"C@H+Zd@s[0Jm.x@Hk0JZ1H)zCh~tkS2 ?hKx"abAB"=YC@HZ?PKXH@JKbXJKb?qY\i=Zb?qY\opCbvN=+Z3

Zestaw testowy

Kod podany w pierwszym wierszu STDIN, wejście do programu Purple w pozostałej części STDIN. Aby użyć kodu z nowymi wierszami, użyj alternatywnej wersji u dołu.

Rozsądnie grał w golfa. Oto przełamania linii i wcięcia dla jasności:

J,00
=kjb.z
 .eXHkCbz
#
  =b-Fm
    ?=zx"oabABi1"C@H+Zd
      @
        s[0Jm.x@Hk0JZ1H)
        z
      Ch~tk
    S2
   ?hKx"abAB"=YC@HZ
    ?PK
      XH@JKb
      XJKb
  ?qY\i=Zb
  ?qY\opCb
  vN
  =+Z3

Zasadniczo #pętla wykonuje wykonywanie i zatrzymuje się poprzez przerwanie błędów.

ai bsą połączone w jeden zmiennej J. Zjest wskaźnikiem instrukcji. kjest wprowadzany do programu Purple. Hto taśma reprezentowana jako słownik. bjest bieżącym wynikiem. Yjest bieżącym pierwszym bajtem instrukcji.

Odczytywanie z pliku:

J,00=kjb.z .eXHkCbjb'z#=b-Fm?q\o=zC@H+ZdCh~tk@s[Jm.x@Hk0JZ1H)x"abABi1"zS2 ?hKx"abAB"=YC@HZ?PKXH@JKbXJKb?qY\i=Zb?qY\opCbvN=+Z3

Podaj nazwę pliku jako pierwszy wiersz STDIN. Testowe uruchomienie:

$ cat purple-final.pyth 
J,00=kjb.z .eXHkCbjb'z#=b-Fm?=zx"oabABi1"C@H+Zd@s[0Jm.x@Hk0JZ1H)zCh~tkS2 ?hKx"abAB"=YC@HZ?PKXH@JKbXJKb?qY\i=Zb?qY\opCbvN=+Z3
$ cat purple-code.txt 
aA1aa1bb1oAbbi1bb1bbAb1Bi1b Purple is the awesomest! Why haven't you tried it yet?
!dlroW ,olleG
$ pyth purple-final.pyth <<< 'purple-code.txt' 
Hello, World!
isaacg
źródło
5

JavaScript (ES6), 292 bajty

eval(`a=b=i=d=0;v=n=>(x=m[i+n])==97?a_98?b_65?m[a]_66?m[b]_105?i_111?p()[c]()_49?1:d=1;for(m=[...(p=prompt)()].map(b=>b[c="charCodeAt"]());!d;i+=3)(y=v(1),d)||(z=v(2),d)?1:(x=m[r=y-z,i])==97?a=r_98?b=r_65?m[a]=r_66?m[b]=r_105?i=r-3_111?alert(String.fromCharCode(r)):d=1`.replace(/_/g,":x=="))

Wyjaśnienie

Odpowiedzi JavaScript zawsze są dziwne, kiedy STDINi STDOUTwymagane są ...

Pierwszy monit to dane wejściowe dla ciągu programu. Każdy monit wynikający z oinstrukcji czyta tylko pierwszy znak.

evalsłuży do zastąpienia zwykłej frazy, która oszczędza kilka bajtów. Bez golfa i bez evalprogramu wygląda tak:

// Initialisation
a=b=i=                            // initialise registers to 0
  d=0;                            // d is set to true when the program should die

// Gets the result of Y or Z
v=n=>                             // n = offset from i
  (x=m[i+n])==97?a:               // x = value of instruction
  x==98?b:
  x==65?m[a]:
  x==66?m[b]:
  x==105?i:
  x==111?p()[c]():
  x==49?1:
  d=1;                            // if it was none of the valid values, die

// Execution loop
for(
  m=                              // m = memory array
    [...(p=prompt)()]             // receive the program
    .map(b=>b[c="charCodeAt"]()); // initialise m to the ASCII values of the program
  !d;                             // finish if an error occured
  i+=3                            // increment i
)
  (y=v(1),d)||                    // get the value of Y and check for errors
  (z=v(2),d)?1:                   // get the value of Z and check for errors

    // Get the result of X
    (x=m[r=y-z,i])==97?a=r:       // r = result of y - z
    x==98?b=r:
    x==65?m[a]=r:
    x==66?m[b]=r:
    x==105?i=r-3:
    x==111?alert(String.fromCharCode(r)):
    d=1
użytkownik 81655
źródło
2
Czy drugi c="charCodeAt"można zastąpić just c?
Dendrobium,
Czy dostęp do tablicy z ujemnymi indeksami działa w JavaScript?
nimi
@Dendrobium Wow, nie wiem, jak tęskniłem za haha! Dzięki.
user81655,
2
@nimi Działa. Same tablice nie obsługują indeksów ujemnych, ale wykorzystuje to fakt, że zachowują się również jak obiekty. array[-1] = 1jest taki sam jak array = { "-1": 1 }. Oba są dostępne za pomocą array[-1].
user81655,
@ user81655: Ach miło, nie wiedziałem o tym.
nimi
3

Cejlon, 827 792 671 bajtów

import ceylon.language{l=variable,I=Integer,x=nothing,p=process,m=map}shared void run(){try{if(exists d=p.arguments[0]){l value t=m{*d*.hash.indexed};l I a=0;l I b=0;l I i=0;I g(I j)=>t[j]else 0;l{I*}c=[];I o{if(c==[]){if(exists e=p.readLine()){c=e*.hash.chain{10};}else{c={-1}.cycled;}}assert(is I r=c.first);c=c.rest;return r;}value f=m{97->{a},98->{b},65->{g(a)},66->{g(b)},105->{i},111->{o},49->{1}};value s=m{97->((I v)=>a=v),98->((I v)=>b=v),65->((I v)=>t=m{a->v,*t}),66->((I v)=>t=m{b->v,*t}),105->((I v)=>i=v),111->((I v)=>p.write("``v.character``"))};I h(I v)=>f[v]?.first else x;while(0<1){(s[g(i)]else x)(h(g(i+1))-h(g(i+2)));i+=3;}}}catch(AssertionError e){}}

Zachowuje się nieco inaczej niż implementacja referencyjna, gdy program próbuje odczytać dane wejściowe w EOF - implementacja referencyjna ulega awarii z błędem TypeError, który jest zbyt kosztowny, aby odtworzyć tutaj (a także prawdopodobnie błąd), więc zwróci -1 zamiast tego ( wielokrotnie, jeśli to konieczne).

(Podczas próby zapisania tego -1 na standardowe wyjście, interpreter zakończy się przepełnieniem błędu. Podobnie będzie, jeśli zostanie wypisana liczba całkowita spoza zakresu Unicode.)

Tłumacz interpretuje program jako pierwszy argument wiersza poleceń (pamiętaj, aby zacytować go dla swojej powłoki, gdy zawiera ona spacje lub inne interesujące rzeczy).

Na Cejlonie możemy tylko łatwo odczytać dane wejściowe liniowo (myślę, że zmieni się to w jednej z następnych wersji), więc kiedy ojest używany do czytania, czytam całą linię i buforuję części do przyszłych zastosowań. Wydaje mi się, że działa podobnie w implementacji Pythona po podłączeniu do terminala.


Podczas próby wykonania polecenia (części), która nie jest jednym z poprawnych znaków, nothingspowoduje wygenerowanie błędu AssertionError, który następnie przechwytuje się w bloku catch wokół głównej pętli.

Myślę, że najlepiej powinien to być niestandardowy typ wyjątku (ponieważ błąd może również wystąpić w innych miejscach, jeśli mam błąd), ale zajęłoby to znacznie więcej miejsca, pochłaniając większość ulepszeń, które wprowadziłem od pierwszej wersji.

Niektóre sztuczki używane do gry w golfa:

  • Poprzednie wersje używały ceylon.collection.HashMap - zamiast tego używamy niezmiennej mapy utworzonej przez mapfunkcję i tworzymy nową za każdym razem Alub Bjest ona używana jako x .
  • Używam importu aliasów do wszystkich identyfikatorów z ceylon.language, które są używane więcej niż jeden raz (w tym variableadnotację, która jest teraz l).
  • Pozbyłem się klasy E(dla środowiska) i s(krokowej) - wszystko dzieje się teraz wewnątrz runfunkcji.
  • Zamiast używać .integerdo uzyskania punktu kodowego znaku, .hashdaje ten sam wynik. Zatem string*.hashjest to samo co string.map(Character.integer)(daje iterowalne punkty kodowe z łańcucha).
  • Gdy typ jest importowany do aliasu, is I ...jest krótszy niż exists ....
  • Podczas przekształcania czegoś (np. x) W ciąg znaków "``t``"jest on krótszy niż t.string(lub, co użyłem dla znaku, String{t}).
  • funkcje użyte tylko raz można często wprowadzić.

Oto sformatowana (i skomentowana) wersja:

// Purple – a self-modifying, "one-instruction" language.
//
// Question:  http://codegolf.stackexchange.com/q/65411/2338
// My answer: http://codegolf.stackexchange.com/a/65492/2338

import ceylon.language {
    l=variable,
    I=Integer,
    x=nothing,
    p=process,
    m=map
}

shared void run() {
    try {
        // Reading code from file certainly takes more than 73 characters,
        // this isn't worth the 10% bonus.
        if (exists d = p.arguments[0]) {

            // The memory tape, as a Map<Integer, Integer>.
            // We can't modify the map itself, but we
            // can replace it by a new map when update is needed.
            l value t = m {
                // It is initialized with the code converted to Integers.
                // We use `.hash` instead of `.integer` because it is shorter.
                *d*.hash.indexed };

            // three registers
            l I a = 0;
            l I b = 0;
            l I i = 0;

            // get value from memory
            I g(I j) =>
                    t[j] else 0;

            // cached input which is still to be read
            l {I*} c = [];

            // get value from stdin.
            // we can only comfortably access stdin by line, so we read a whole line
            // and cache the rest for later.
            I o {
                if (c == []) {
                    if (exists e = p.readLine()) {
                        c = e*.hash.chain { 10 }; // convert string into ints, append \n
                    } else {
                        // EOF – return just -1 from now on.
                        c = { -1 }.cycled;
                    }
                }
                assert (is I r = c.first);
                c = c.rest;
                return r;
            }


            // Map of "functions" for fetching values.
            // We wrap the values in iterable constructors for lazy evaluation
            //  – this is shorter than using (() => ...).
            // The keys are the (Unicode/ASCII) code points of the mapped
            // source code characters.
            value f = m {
                // a
                97 -> { a },
                // b
                98 -> { b },
                // A
                65 -> { g(a) },
                // B
                66 -> { g(b) },
                // i
                105 -> { i },
                // o
                111 -> { o },
                // 1
                49 -> { 1 }
            };

            // Map of functions for "storing" results.
            // The values are void functions taking an Integer,
            // the keys are the ASCII/Unicode code points of the corresponding
            // source code characters.
            value s = m {
                // a
                97 -> ((I v) => a = v),
                // b
                98 -> ((I v) => b = v),
                // Modification of the memory works by replacing the map with a new one.
                // This is certainly not runtime-efficient, but shorter than importing
                // ceylon.collections.HashMap.
                // A
                65 -> ((I v) => t = m { a->v, *t }),
                // B
                66 -> ((I v) => t = m { b->v, *t }),
                // i
                105 -> ((I v) => i = v),
                // o – output as a character.
                111 -> ((I v) => p.write("``v.character``"))
            };

            // accessor function for the f map
            I h(I v) =>
                    f[v]?.first else x;

            // the main loop, can only be left by exception
            while (0 < 1) {
                (s[g(i)] else x)(h(g(i + 1)) - h(g(i + 2)));
                i += 3;
            }
        }
    } catch (AssertionError e) {
        // abort silently
    }
}
Paŭlo Ebermann
źródło
Ponownie wykorzystałem część tego kodu dla „równoległego tłumacza” próbującego znaleźć wszystkie programy zatrzymujące. (Jest ich wiele.) (Tam użyłem wersji Purple i innych niż I / O, ponieważ I / O zajmuje dużo miejsca i nie jest używane w tym zadaniu.)
Paŭlo Ebermann