Drukuj, Przyrost, Zmniejszenie, Alias ​​- Interpretacja Prindeal

30

Prindeal (wymawiane prin-dee-al ) to nowy ezoteryczny język programowania, który ma tylko cztery polecenia: pr int , in crement , de crement i al ias . Pomimo minimalizmu skomplikowane operacje matematyczne można wykonywać w Prindeal, sprytnie łącząc cztery polecenia.

Twoim zadaniem w tym wyzwaniu golfowym jest napisanie najkrótszego programu, który może uruchomić kod Prindeal.

Specyfikacja jest długa, ale starałem się, aby była jak najbardziej klarowna i wierzę, że jeśli spróbujesz nauczyć się Prindeal, przekonasz się, że jest dość elegancki!


Interpretowanie Prindeal

Przetwarzanie wstępne

Przed interpretacją programu Prindeal należy usunąć z niego następujące rzeczy w następującej kolejności:

  1. Wszystko po #znaku do końca linii jest włączone, plus #sam. (To są komentarze.)
  2. Końcowe białe znaki w dowolnej linii.
  3. Całkowicie puste linie.

Na przykład program Prindeal

p cat #The next line has 7 trailing spaces.
p dog       

#p mouse

zostanie wstępnie przetworzony w

p cat
p dog

Odtąd założymy, że ten krok przetwarzania wstępnego został wykonany.

Zmienne

Musimy szybko zdefiniować zmienne, zanim pokażemy, jak są używane.

Zmienne (i odniesienia do zmiennych) są przekazywane do argumentów poleceń Prindeal. Zmienne są zawsze globalne , więc modyfikacje zmiennej, bez względu na to, gdzie występują, są odzwierciedlane wszędzie.

Każda zmienna zawiera nieujemną liczbę całkowitą o dowolnej dokładności (0, 1, 2, 3, ...). Zmienne nie muszą być inicjowane wstępnie - zawsze zaczynają się od wartości 0 przy pierwszym użyciu lub wywołaniu.

Nazwą zmiennej może być dowolny niepusty ciąg znaków alfanumerycznych i znaków podkreślenia, który nie zaczyna się od cyfry - [a-zA-Z_][0-9a-zA-Z_]*w wyrażeniu regularnym . Rozróżniają małe spiny_lumpsuck3ri wielkie litery, a więc Spiny_lumpsuck3rsą różnymi zmiennymi.

Wykonanie

Prindeal to imperatywny język programowania. Po uruchomieniu programu Prindeal jego instrukcje są wykonywane od góry do dołu w kolejności, a następnie program się kończy.

Każda linia bez wcięcia w programie Prindeal jest instrukcją, która wymaga wykonania pojedynczej komendy, która może przyjmować lub nie argumenty.

Wcięte linie występują tylko po poleceniach aliasu . W szczególności dokładnie trzy wiersze wcięte pojedynczymi spacjami występują po każdym poleceniu aliasu i są uważane za jego część. Zatem instrukcje aliasów mają tak naprawdę cztery linie. (Mogą to być jedna linia, cztery są po prostu bardziej czytelne).

Oświadczenia inne niż alias

Z wyjątkiem aliasu każda instrukcja w programie Prindeal ma postać:

[command name] [argument 1] [argument 2] [argument 3] ...

Może istnieć dowolna liczba argumentów (w tym żadna). Każdy argument jest zawsze zmienną lub (jak zobaczymy podczas omawiania aliasu ) odniesieniem do zmiennej .

Po zakończeniu wykonywania każda instrukcja jest oznaczana jako błąd lub sukces w zależności od tego, czy wystąpiły błędy, czy nie. (To naprawdę ma znaczenie tylko wtedy, gdy przejdziemy do używania aliasu ).

Wbudowane opcje drukowania , zwiększania i zmniejszania są instrukcjami o powyższej formie. Oto, co robią:

  1. print ma nazwę polecenia pi przyjmuje jeden argument. Wyświetla nazwę przekazywanej zmiennej i jej wartość (w systemie dziesiętnym) oddzieloną „=”, a następnie znakiem nowej linii. Zawsze jest oznaczany jako sukces .

    Na przykład program Prindeal

    p _MyVariable_321
    p screaming_hairy_armadillo
    

    wyjdzie

    _MyVariable_321 = 0
    screaming_hairy_armadillo = 0
    

    ponieważ wszystkie zmienne zaczynają się od 0. (Wymagane są spacje przed i po znaku równości).

  2. increment ma nazwę polecenia ii przyjmuje jeden argument. Zwiększa wartość zmiennej przekazywanej o 1. Zawsze jest oznaczana jako sukces .

    Na przykład program

    i alpaca
    p alpaca
    i alpaca
    p alpaca
    

    wyjdzie

    alpaca = 1
    alpaca = 2
    

    Zwróć uwagę, w jaki sposób alpacazwiększano od 0 do 1, mimo że nigdy wcześniej nie był dostępny.

  3. dekrement ma nazwę polecenia di przyjmuje jeden argument. Jeśli przekazywana zmienna jest niezerowa, jej wartość jest zmniejszana o 1, a instrukcja jest oznaczana jako sukces . Jeśli przekazana zmienna ma wartość 0, nic się nie dzieje, a instrukcja jest oznaczana jako błąd .

    Na przykład program

    i malamute
    p malamute
    d malamute    #success
    p malamute
    d malamute    #failure
    p malamute
    d akita       #failure
    p akita
    

    wyjdzie

    malamute = 1
    malamute = 0
    malamute = 0
    akita = 0
    

    Zauważ, że zmniejszenie wartości zmiennej o wartości 0 jest jedynym sposobem na wywołanie błędu .

Instrukcja alias i polecenia z aliasem

Komenda alias ma specjalną składnię i jest najsilniejsza, ponieważ może być używana do definiowania nowych komend. Nazwa polecenia alias to, aa instrukcja alias ma postać:

a [name of new command]
 [statement A]
 [statement B]
 [statement C]

Gdzie każdy [statement X]reprezentuje dowolną nie- aliasową instrukcję, tj. Coś w formie [command name] [argument 1] [argument 2] [argument 3] ....

Aliasowana nazwa polecenia [name of new command]może być dowolnym niepustym ciągiem znaków alfanumerycznych i znaków podkreślenia, który nie zaczyna się od cyfry - [a-zA-Z_][0-9a-zA-Z_]*w wyrażeniu regularnym.

(Jest to ten sam zestaw nazw co zmienne, ale polecenia i zmienne aliasy są różnymi rzeczami używanymi w różnych miejscach . Zmienna może być nazwana tak samo jak polecenie bez negatywnych konsekwencji).

Po wykonaniu instrukcji aliasu nowe polecenie jest dodawane obok czterech oryginalnych p i d apoleceń. Nowa komenda może być używana jako instrukcja [command name]in i wywoływana z argumentami, tak jak każda inna komenda inna niż alias .

Kiedy wykonywana jest instrukcja z aliasowaną nazwą polecenia, uruchamiane są dokładnie dwie kolejne instrukcje z oryginalnej instrukcji alias :

  • [statement A] jest zawsze uruchamiany
  • [statement B]jest uruchamiany, jeśli [statement A]był sukcesem
  • [statement C]jest uruchamiany, jeśli [statement A]była awaria

Instrukcje A, B i C są zawsze uruchamiane leniwie , tzn. Są oceniane w locie w czasie ich uruchamiania.

Po zakończeniu wykonywania komenda aliasowana jest oznaczana z tą samą flagą sukcesu lub niepowodzenia co instrukcja B lub C, w zależności od tego, która z nich została wykonana . ( same instrukcje aliasów nie muszą być oflagowane, ponieważ nie mogą występować wewnątrz siebie).

Alias ​​Przykład 1

Powiedzmy, że chcemy nowego polecenia, które zwiększy zmienną frogdwukrotnie. To oświadczenie aliasu osiąga to:

a increment_frog_twice
 i frog
 i frog
 d frog

Instrukcja A ( i frog) jest zawsze uruchamiana i zawsze oznaczana jako sukces, więc instrukcja B ( i frog) jest również zawsze uruchamiana, a zatem zmienna frog jest zwiększana o 2. increment_frog_twicePolecenie jest zawsze oznaczane jako sukces, ponieważ instrukcja B jest zawsze uruchamiana, a B jest zawsze sukces . Instrukcja C ( d frog) nigdy nie jest uruchamiana.

Więc wyjście do

a increment_frog_twice
 i frog
 i frog
 d frog
p frog
increment_frog_twice
p frog

byłoby

frog = 0
frog = 2

Możemy uogólnić ten przykład, tak aby dowolną zmienną można zwiększyć dwukrotnie, podając argument aliasowanemu poleceniu.

W instrukcji aliasu dodatnie liczby całkowite 1, 2, 3 itd. Reprezentują argumenty 1, 2, 3 itd. Przekazane do polecenia aliasu. (Te argumenty mogą być zwykłymi zmiennymi lub odwołaniami do samych zmiennych.) Liczby te mogą pojawiać się tylko w argumentach instrukcji A, B i C w instrukcji aliasowej . Nie ma sensu, aby pojawiały się gdzie indziej.

Alias ​​Przykład 2

Uogólnia to ostatni przykład - każda przekazana zmienna increment_twicezostanie zwiększona o 2, ponieważ 1jest to odwołanie do pierwszego przekazanego argumentu:

a increment_twice
 i 1
 i 1
 d 1 #never reached
p toad
increment_twice toad
p toad

Wyjście tego programu byłoby

toad = 0
toad = 2

Możemy wtedy dokonać aliasu innego polecenia, które przyjmuje dwa argumenty i wywołuje increment_twiceoba z nich:

a increment_twice
 i 1
 i 1
 d 1 #never reached
a increment_both_twice
 increment_twice 1
 increment_twice 2
 d 1 #never reached
increment_both_twice platypus duck
p platypus
p duck

Wynik byłby tutaj

platypus = 2
duck = 2

Ważne jest, aby zdawać sobie sprawę, że polecenia aliasy mogą być rekurencyjne, ponieważ na tym polega ich prawdziwa moc. Na przykład możemy wykonać polecenie, które ustawia dowolną zmienną przekazaną na 0:

Alias ​​Przykład 3

set_to_zeroPolecenie przyjmuje jeden argument i ustawia zmienną na 0 i jest oznaczona jako sukces po zakończeniu:

a set_to_zero
 d 1
 set_to_zero 1
 i _dummy_
i oryx
i oryx
i oryx
p oryx
set_to_zero oryx
p oryx

Wyjście tego programu byłoby

oryx = 3
oryx = 0

To, co się dzieje, polega na tym, że po set_to_zero oryxuruchomieniu d 1pomyślnie zmniejsza się oryxz 3 do 2, a następnie set_to_zero 1jest wywoływany, co jest równoznaczne z set_to_zero oryxponownym wywołaniem . Tak więc proces powtarza się, dopóki nie zakończyd 1 się niepowodzeniem , zatrzymując rekurencję i zwiększając wartość _dummy_zmiennej, aby osiągnąć sukces .


Wyzwanie

Napisz program, który może wykonać kod Prindeal dokładnie tak, jak opisano powyżej. Pobierz kod Prindeal przez stdin, wiersz poleceń lub jako plik tekstowy. Wydrukuj wyniki programu Prindeal na standardowe wyjście lub najbliższą alternatywę dla twojego języka.

Alternatywnie możesz napisać funkcję, która pobiera kod jako ciąg znaków i wypisuje lub zwraca ciąg wyjściowy.

Dodatkowo możesz założyć, że:

  • Wejściowy kod Prindeal będzie zawierał tylko znaki nowej linii i drukowalny kod ASCII oraz (opcjonalnie), że kończy się pustą linią.
  • Kod wejściowy będzie prawidłowy Prindeal - dobrze uformowany i poprawny składniowo.
  • Uruchomienie kodu nie wygeneruje nieskończonych pętli ani żadnych nieprawidłowych odwołań do poleceń, które nie zostały zdefiniowane lub argumentów, które nie zostały podane.
  • Nazwy poleceń p, i, d, i anigdy nie będzie aliasem skończona. (Być może nie przyjąć, że zmienne nie będą miały te nazwy).

Nie ma również znaczenia, czy wartości zmiennych nie są liczbami całkowitymi o dokładności arbitralnej, ponieważ testowane będą tylko liczby mniejsze niż około 1000. Jest również w porządku, jeśli Twój język ma ograniczenia rekurencyjne (takie jak Python ), które mogą napotkać bardziej złożone programy Prindeal, o ile działa poniższy program testowy.

Program testowy

Oto duży program Prindeal, który buduje operacje dodawania, mnożenia i potęgowania za pomocą zmiennych zastępczych (zaczynając _od konwencji) i wielu aliasów pomocniczych:

#Command Definitions:
a s             #flag as a success
 i _
 d _
 d _
a f             #flag as a failure
 d _
 d _
 d _
a z             #1 = zero
 d 1
 z 1
 s
a n             #1 = one
 z 1
 i 1
 s
a move          #2 += 1, 1 = zero
 moveH 1 2
 move 1 2
 s
a moveH         #move helper
 d 1
 i 2
 f
a dupe          #2 += 1, 3 += 1, 1 = zero
 dupeH1 1 2 3
 dupe 1 2 3
 s
a dupeH1        #dupe helper
 d 1
 dupeH2 2 3
 f
a dupeH2        #dupe helper
 i 1
 i 2
 s
a copy          #2 = 1
 z 2
 copyH 1 2
 s
a copyH         #copy helper
 dupe 1 2 _copy
 move _copy 1
 s
a addTo         #1 += 2
 copy 2 _add
 #testing comments #
 move _add 1#in weird places # just because #
 s
#it's a g##d idea
###
a add           #1 = 2 + 3
 #its a good idea
 z 1
 addH 1 2 3
 s
##

#
a addH          #add helper
#this is a comment
 addTo 1 2 #as is this
 addTo 1 3
 s
a mul           #1 = 2 * 3
 mulH1 1 2
 mulH2 1 3
 s
a mulH1         #mul helper
 z 1
 copy 2 _mul
 s
a mulH2         #mul helper
 mulH3 1 2
 mulH2 1 2
 s
a mulH3         #mul helper
 d _mul
 addTo 1 2
 f
a mulBy         #1 *= 2
 mul _mulBy 1 2
 copy _mulBy 1
 s
a pow           #1 = 2^3
 powH1 1 3
 powH2 1 2
 s
a powH1         #pow helper
 n 1
 copy 2 _pow
 s
a powH2         #pow helper
 powH3 1 2
 powH2 1 2
 s
a powH3         #pow helper
 d _pow
 mulBy 1 2
 f

#Running Tests:
p A
p B
p C
n A         #A = 1
n B         #B = 1
add C A B   #C = A + B = 1 + 1 = 2
p ____
p A
p B
p C
add B A C   #B = A + C = 1 + 2 = 3
p ____
p A
p B
p C
mul d B C   #d = B * C = 3 * 2 = 6
p ____
p d
mulBy d B   #d = d * B = 6 * 3 = 18
p ____
p d
d A         #A = A - 1 = 1 - 1 = 0
mulBy d A   #d = d * A = 18 * 0 = 0
p ____
p d
pow A C B   #A = C ^ B = 2 ^ 3 = 8
p ____
p A
p B
p C
pow A B C   #A = B ^ C = 3 ^ 2 = 9
p ____
p A
p B
p C
pow C A B   #C = A ^ B = 9 ^ 3 = 729
p ____
p A
p B
p C

(Jeśli bawisz się tym kodem, pamiętaj, że wiele poleceń nie powiedzie się, jeśli ta sama zmienna zostanie podana wielokrotnie jako argument. Można to łatwo naprawić, ale wynikowy kod jest dłuższy.)

Twój tłumacz Prindeal powinien być w stanie wygenerować dokładny wynik:

A = 0
B = 0
C = 0
____ = 0
A = 1
B = 1
C = 2
____ = 0
A = 1
B = 3
C = 2
____ = 0
d = 6
____ = 0
d = 18
____ = 0
d = 0
____ = 0
A = 8
B = 3
C = 2
____ = 0
A = 9
B = 3
C = 2
____ = 0
A = 9
B = 3
C = 729

Punktacja

Najkrótszy kod w bajtach wygrywa. Tiebreaker przechodzi do wcześniejszego zgłoszenia.

Brownie Bonus: Napisz fajny program w Prindeal. Zaimplementowałem dodawanie i mnożenie, czy możesz odejmować lub dzielić?

Hobby Calvina
źródło
O rany, chyba raz zostawię Pytha w spokoju i wyciągnę Lisp! Jedno pytanie - funkcje i zmienne żyją w zupełnie innych przestrzeniach, prawda? Więc mogę zwiększyć p, a następnie p p, co by wydrukowało 1, prawda?
orlp
@orlp Prawidłowo. (Jest tam kilka uwag na ten temat.)
Hobby Calvina,
2
Nie mogę być jedynym, który myśli PRNDL, kiedy widzę nazwę języka.
Downgoat,
Czy istnieje maksymalna liczba argumentów, które zostaną przekazane do polecenia aliasu?
Zach Gates,
@ZachGates Nope
Calvin's Hobbies

Odpowiedzi:

9

Pyth, 162 136 bajtów

JfTmchcd\#).zKHW<ZlJI!e=T@J~+Z1=@Tk)=k0 .x=J+]h=Nm.xL@Tskd@K=NhT+]+tN0>J~Z0,=Y.x@H=eT0?qN\pps[Td\=dYb)?xGN?qN\iXHThY?YXTH_1=k1XKT:JZ=+Z3

Demonstracja.

Grałem z 26 znaków przez inline zmienne i zmienia się od Ia Eprzepływ sterowania opiera się ?i .xprzepływ sterowania oparty.

Po raz pierwszy zabrakło mi zmiennych w Pyth. Każda pojedyncza zmienna w Pyth ( bdkGHNTYi JK) była używana i chciałem użyć jej bjako nowej linii. Na szczęście mogłem używać Ndwóch zupełnie różnych rzeczy w różnych częściach programu, więc nadal działa.

Ungolfed (bieg z -m):

JfTmchcd\#).z
KH
W<ZlJ
  I!e=T@J~+Z1
    =@Tk)
  =k0
     .x
      =J+]h=Nm.xL@Tskd@K=NhT+]+tN0>J~Z0
      ,
        =Y.x@H=eT0
        ?qN\p
          ps[Td\=dYb)
          ?xGN
            ?qN\i
              XHThY
              ?Y
                XTH_1
                =k1
            XKT:JZ=+Z3
isaacg
źródło
3
Uwielbiam to, że wciąż nie mogę powiedzieć, co to robi, nawet gdy nie jest golfistą ...
Jerry Jeremiah
Cóż, to potwierdza, że ​​Pyth jest niekompletny ...
Erik the Outgolfer
8

Python 2, 600 584 397 373 bajtów

To jest moje własne referencyjne rozwiązanie w golfa. Każdy może go ulepszyć lub zastosować się do jego logiki we własnej odpowiedzi, pod warunkiem podania źródła.

Najciekawsze jest to, że nie jest wykonywana żadna rekurencja, więc nigdy nie będzie mieć problemów z limitem rekurencji Pythona. Na przykład program Countup Prindeal firmy Sp może działać bez końca.

p=filter(len,[l.split('#')[0].split()for l in input().split('\n')]);m={};v={};i=0
while i<len(p):
 s=p[i]
 if'('in`s`:s=s[f]
 n,f=s[0],0
 if n in m:a,b,c=([s[int(g)]if g.isdigit()else g for g in t]for t in m[n]);p=[a,(b,c)]+p[i+1:];i=0;continue
 s=s[1]
 q=v.get(s,0)
 if'd'>n:m[s]=p[i+1:i+4];i+=3
 elif'i'<n:print s,'=',q
 elif'd'<n:v[s]=q+1
 elif q:v[s]-=1
 else:f=1
 i+=1

Jest to program, który pobiera cytowany ciąg programu ze znakami nowej linii, np
'p _MyVariable_321\np screaming_hairy_armadillo'.

Wziąłem różne wskazówki gry w golfa z odpowiedzi Sp i Pietu . Dzięki chłopaki :)

Hobby Calvina
źródło
6

Python 3, 345 336 335 328 bajtów

a=0
A={}
V={}
def f(l):
 if l[0]in"d p i":c,u=l;U=V[u]=V.get(u,0)+"pi".find(c);S=U<0;V[u]+=S;c<"p"or print(u,"=",U)
 else:d=lambda q:[w.isdigit()and l[int(w)]or w for w in A[l[0]][q]];S=f(d(1+f(d(0))))
 return S
for z in open("P"):
 l=z.split("#")[0].split()
 if"a "==z[:2]:a,s,*x=3,l[1]
 elif l*a:x+=l,;a-=1;A[s]=x
 elif l:f(l)

(-6 bajtów dzięki @orlp)

Wciąż gra w golfa. Zakłada, że ​​program jest zapisany w pliku o nazwie P.

Wpisanie wywołań do fwnętrza lambda dpozwoliłoby zaoszczędzić kilka bajtów, ale spowodowałoby, że ostatni przypadek testowy osiągnąłby maksymalną głębokość rekurencji.

Niektóre programy Prindeal

Bezużyteczny program odejmowania

Oto bezużyteczny program odejmowania . Jest bezużyteczny, ponieważ mimo prawidłowego odejmowania nie zwraca odpowiednio sukcesu / porażki.

Dane wyjściowe powinny być:

a = 15
b = 6
__________ = 0
a = 9
b = 6

Countup

a helper
 p 1
 countup 1
 i success

a countup
 i 1
 helper 1
 d failure

countup n

Liczy w górę i drukuje na nzawsze. Może działać jako test szybkości tłumacza (uwaga na długie śledzenie przerwania klawiatury).

Sp3000
źródło
2
Wszyscy w tym pytaniu przegapili ten golf, nie rozumiem dlaczego. l[:(l+"#").find("#")]a wszystkie jego odmiany można zastąpić prostym l.split('#')[0].
orlp
@orlp Byłem tak skupiony na findtym, że zapomniałem, że możesz to zrobić, splitnawet gdyby #ich nie było. Dzięki :)
Sp3000,
6

JavaScript (ES6), 273 258

Edytuj Naprawiono błędy i dodano prawdziwy zestaw testów.

Nie licząc wiodących spacji i nowych linii.

Na pewno można trochę bardziej grać w golfa.

Zbyt zmęczony, aby napisać teraz wyjaśnienie, myślę, że to dobry przykład użycia zamknięć, aby utrzymać przy życiu wartości tymczasowe (parametry).

Przetestuj uruchomienie fragmentu kodu w dowolnej przeglądarce zgodnej z EcmaScript 6 (w szczególności nie Chrome, a nie MSIE. Testowałem na Firefoxie, Safari 9 mogłaby działać)

F=p=>(
  p=p.match(/^[^#\n]+/gm).filter(r=>r.trim(o='',v=[])),
  s={
    '':_=>1,
    p:a=>o+=a+` = ${v[a]||0}\n`,
    i:a=>v[a]=-~v[a],
    d:a=>v[a]&&v[a]--,
    a:(n,j)=>s[n]=(u,t,a)=>x(p[!x(p[j+1],0,a,1)+j+2],0,a,1)
  },
  p.map(x=(r,i,w,l,a=r.split(/ +/).slice(l).map(x=>-x?w[x]:x))=>s[a[0]](a[1],i,a)),
  o
)

// TEST

$('#O tr').each(function() {
  var $cells = $(this).find('td')
  var prg = $cells.eq(0).text()
  console.log(prg)
  var output = F(prg)
  $cells.eq(1).text(output)
})
#O td { vertical-align:top; white-space: pre; border: 1px solid #888; font-family:monospace }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<table>
<tr><th>Program</th><th>Outpout</th></tr>
<tbody id=O>  
<tr><td>p _MyVariable_321
p screaming_hairy_armadillo</td><td></td></tr>
<tr><td>i alpaca
p alpaca
i alpaca
p alpaca</td><td></td></tr>
<tr><td>i malamute
p malamute
d malamute    #success
p malamute
d malamute    #failure
p malamute
d akita       #failure
p akita</td><td></td></tr>
<tr><td>a increment_frog_twice
 i frog
 i frog
 d frog
p frog
increment_frog_twice
p frog</td><td></td></tr>
<tr><td>a increment_twice
 i 1
 i 1
 d 1 #never reached
a increment_both_twice
 increment_twice 1
 increment_twice 2
 d 1 #never reached
increment_both_twice platypus duck
p platypus
p duck</td><td></td></tr>
<tr><td>a set_to_zero
 d 1
 set_to_zero 1
 i _dummy_
i oryx
i oryx
i oryx
p oryx
set_to_zero oryx
p oryx</td><td></td></tr>
<tr><td>#Command Definitions:
a s             #flag as a success
 i _
 d _
 d _
a f             #flag as a failure
 d _
 d _
 d _
a z             #1 = zero
 d 1
 z 1
 s
a n             #1 = one
 z 1
 i 1
 s
a move          #2 += 1, 1 = zero
 moveH 1 2
 move 1 2
 s
a moveH         #move helper
 d 1
 i 2
 f
a dupe          #2 += 1, 3 += 1, 1 = zero
 dupeH1 1 2 3
 dupe 1 2 3
 s
a dupeH1        #dupe helper
 d 1
 dupeH2 2 3
 f
a dupeH2        #dupe helper
 i 1
 i 2
 s
a copy          #2 = 1
 z 2
 copyH 1 2
 s
a copyH         #copy helper
 dupe 1 2 _copy
 move _copy 1
 s
a addTo         #1 += 2
 copy 2 _add
 #testing comments #
 move _add 1#in weird places # just because #
 s
#it's a g##d idea
###
a add           #1 = 2 + 3
 #its a good idea
 z 1
 addH 1 2 3
 s
##

#
a addH          #add helper
#this is a comment
 addTo 1 2 #as is this
 addTo 1 3
 s
a mul           #1 = 2 * 3
 mulH1 1 2
 mulH2 1 3
 s
a mulH1         #mul helper
 z 1
 copy 2 _mul
 s
a mulH2         #mul helper
 mulH3 1 2
 mulH2 1 2
 s
a mulH3         #mul helper
 d _mul
 addTo 1 2
 f
a mulBy         #1 *= 2
 mul _mulBy 1 2
 copy _mulBy 1
 s
a pow           #1 = 2^3
 powH1 1 3
 powH2 1 2
 s
a powH1         #pow helper
 n 1
 copy 2 _pow
 s
a powH2         #pow helper
 powH3 1 2
 powH2 1 2
 s
a powH3         #pow helper
 d _pow
 mulBy 1 2
 f

#Running Tests:
p A
p B
p C
n A         #A = 1
n B         #B = 1
add C A B   #C = A + B = 1 + 1 = 2
p ____
p A
p B
p C
add B A C   #B = A + C = 1 + 2 = 3
p ____
p A
p B
p C
mul d B C   #d = B * C = 3 * 2 = 6
p ____
p d
mulBy d B   #d = d * B = 6 * 3 = 18
p ____
p d
d A         #A = A - 1 = 1 - 1 = 0
mulBy d A   #d = d * A = 18 * 0 = 0
p ____
p d
pow A C B   #A = C ^ B = 2 ^ 3 = 8
p ____
p A
p B
p C
pow A B C   #A = B ^ C = 3 ^ 2 = 9
p ____
p A
p B
p C
pow C A B   #C = A ^ B = 9 ^ 3 = 729
p ____
p A
p B
p C  
</td><td></td></tr>
</tbody>
</table>

edc65
źródło
Dodałem więcej komentarzy do programu testowego i wygląda na to, że spowodowały one, że Twój kod nie działa.
Calvin's Hobbies
@ Calvin'sHobbies pierwsza szybka łatka
edc65,
3

C # 6, 653 bajtów

Oto mój wpis, pośród morza Python ...

class P{string[]l;string r="";Dictionary<string,int>v=new Dictionary<string,int>();Dictionary<string,int>s=new Dictionary<string,int>();public string R(string t){l=t.Split('\n');for(int i=0;i<l.Length;i++){var z=l[i].Split(' ');if(z[0]=="a"){s.Add(z[1],i);i+=3;}else E(i, null);}return r;}bool E(int n,string[]p){var z=l[n].Split(' ');var a=z.Skip(1).Select(x=>Char.IsDigit(x[0])?p[int.Parse(x)-1]:x).ToArray();if(a.Length>0&&!v.ContainsKey(a[0]))v[a[0]]=0;if (z[0]=="p")r+=$"{a[0]} = {v[a[0]]}\n";else if(z[0]=="i")v[a[0]]++;else if(z[0]=="d")if(v[a[0]]>0)v[a[0]]--;else return false;else{var y=s[z[0]];return E(y+1,a)?E(y+2,a):E(y+3,a);}return true;}}

Rozszerzony i skomentowany:

class Prindeal
{
    string[] lines;
    string result = "";
    Dictionary<string, int> variables = new Dictionary<string, int>();
    Dictionary<string, int> statements = new Dictionary<string, int>();

    public string Run(string text)
    {
        lines = text.Split('\n');

        for (int i = 0; i < lines.Length; i++)
        {
            // Split on spaces to get the statement and any arguments
            var z = lines[i].Split(' ');

            // Are we defining a new statement?
            if (z[0] == "a")
            {
                // Add to the statements dictionary, step over definition statements
                statements.Add(z[1], i);
                i += 3;
            }
            else
            {
                // Execute the statement
                Execute(i, null);
            }
        }

        return result;
    }

    bool Execute(int lineNumber, string[] parameters)
    {
        // Split on spaces to get the statement and any arguments
        var z = lines[lineNumber].Split(' ');

        // Parse the arguments - if it's a number, get the corresponding 
        // parameter from the calling statement
        var arguments = z.Skip(1).Select(
            x => Char.IsDigit(x[0]) ? 
            parameters[int.Parse(x) - 1] : 
            x)
            .ToArray();

        // If the first argument isn't already in the variables dict, add it
        if (arguments.Length > 0 && !variables.ContainsKey(arguments[0])) variables[arguments[0]] = 0;

        // Print statement, using string interpolation
        if (z[0] == "p")
            result += $"{arguments[0]} = {variables[arguments[0]]}\n";
        // Increment statement
        else if (z[0] == "i")
            variables[arguments[0]]++;
        // Decrement statement
        else if (z[0] == "d")
            if (variables[arguments[0]] > 0)
                variables[arguments[0]]--;
            else
                return false;
        else
        {
            // Get the line number to jump to
            var y = statements[z[0]];

            // Execute A ? B : C
            return Execute(y + 1, arguments) ? Execute(y + 2, arguments) : Execute(y + 3, arguments);
        }

        // If we reach this point, it's from a 'p', 'i' or 'd' statement which has succeeded
        return true;
    }
}

Aby go użyć, po prostu utwórz instancję klasy i wywołaj R()metodę, na przykład:

string prindealText = new StreamReader("prindeal.txt").ReadToEnd();
Console.WriteLine(new P().R(prindealText));
Sok
źródło
3

Common Lisp, 758 646 619

(progn(set-macro-character #\#(get-macro-character #\;))(setf(readtable-case *readtable*):invert)(#3=defun v(s)(if(boundp s)(eval s)0))(#3# i(s)(set s(1+ (v s))))(#3# d(s)(and(plusp(v s))(set s(1-(v s)))))(#3# p(s)(format t"~A = ~A~%"s(v s)))(defmacro a(n . p)`(#3#,(cadr n)(&rest g)(if,@p)))(#3# k(s)(typecase s(integer`(nth,(1- s)g))(symbol `',s)(t(list*(car s)(mapcar 'k(cdr s))))))(#3# r()(prog(l p q)$(setf p()l(make-string-input-stream(or(read-line()()())(return))))@(when(setf p(read l()()))(push p q)(go @))(if q(return(k(reverse q)))(go $))))(do ((x(r)(r)))((not x))(eval(if(eq(car x)'a)`(,@x,(r),(r),(r))x))))

Włóż to file.lispi zadzwoń na przykład sbcl --script file.lisp; wejście jest odczytywane ze standardowego strumienia wejściowego.

Wersja ta analizuje rozszerzeniem o Prindeal: bez nie wiele trudności, można uzyskać dostęp do wszystkich Common Lisp z Prindeal źródła. Uważam to za cechę interpretera.

Skomentowana wersja

;; copy-readtable is only used during development, so that I do not 
;; mess with my running environment. The real code starts with the
;; progn below, which is superfluous of course inside a let.
(let ((*readtable* (copy-readtable)))

  ;; I use PROGN in the golfed version so that I can have the whole
  ;; program as a unique tree. This allows me to define reader 
  ;; variables like #3=defun in order to gain a few bytes by writing
  ;; #3# instead of defun. Reader variables are removed in
  ;; this human-friendly version.
  (progn
    ;; Let # point to the same reader function as ;
    ;; Of course, ; is still usable as a comment delimiter
    (set-macro-character #\#
                         (get-macro-character #\;))

    ;; :invert does what is necessary to enable case-sensitive reading
    ;; and printing of symbols
    (setf (readtable-case *readtable*) :invert)

    ;; value of symbol, or zero
    (defun v(s)(if(boundp s)(eval s)0))

    ;; increment
    (defun i(s)(set s(1+ (v s))))

    ;; decrement
    (defun d(s)(and(plusp(v s))(set s(1-(v s)))))

    ;; print
    (defun p(s)(format t"~A = ~A~%"s(v s)))

    ;; alias: wrap an "if" inside a "defun".
    ;; YES, that means you can redefine ANY lisp function with "a" !
    ;; A safer version would properly intern symbols in a dedicated package.
    ;;
    ;; Notice the G variable.  We take advantage of the "unhygienic"
    ;; (what a bad adjective) nature of macros to create a context
    ;; where G is bound to the argument list. The same G is referenced
    ;; implicitely later.
    (defmacro a(n . p)`(defun,(cadr n)(&rest g)(if,@p)))

    ;; Canonicalize expressions:
    ;;
    ;; - if s is a symbol, return s quoted. All functions manipulate
    ;; symbols in order to allow the undeclared use of variables. With
    ;; symbols, we can check for boundness.
    ;;
    ;; - if s is an integer, then we are inside an alias definition. The
    ;; integer is replaced by an access to the s'th element of the
    ;; implicit argument list G using (nth (1- s) g). G will be bound
    ;; when the expressions is injected in the defun corresponding to
    ;; the alias, or else an error will be signaled: either because G
    ;; is unbound, or because you defined a variable named G which is
    ;; by construction not a list. Since we do not sanitize properly
    ;; the input, you could bind G globally to a list, but that would be
    ;; nasty.
    ;; 
    ;; - Finally, if s is a list, apply k to all but the first
    ;; elements of s.  The first element is a symbol but we do not
    ;; need to quote it because we want to call the function
    ;; associated with the symbol. Due to the Lisp-2-ness
    ;; of Common Lisp, functions and variables can coexist
    ;; with the same name.
    ;;
    (defun k(s)(typecase s
                 (integer`(nth,(1- s)g))
                 (symbol`',s)
                 (t(list*(car s)(mapcar #'k(cdr s))))))

    ;; Reader function
    (defun r()
      (prog (l ; current line, as an input-stream reading a string
             p ; current read form
             q ; whole line and return value, as a list
             )

         ;; PROG includes an implicit TAGBODY. Below, $ and @ are
         ;; labels for GO statements (gotos).

       $ (setf
          ;; emtpy p
          p ()

          ;; Read a whole line and if we do not fail, build an input
          ;; stream to read from it.
          l (make-string-input-stream
             (or (read-line()()()) ;; try to read a line,
                 (return)          ;; but return from prog if we reach
                                   ;; the end of file.
                 )))
       @ (when (setf p (read l()()))
           ;; Read a lisp expression, put it in p and if p is not nil
           ;; push it into q.  A nil could happen at the end of the
           ;; line or if someone (you know who) inserted an empty list
           ;; in the file being read.
           ;; 
           ;; Thanks to the readtable which now handles comments
           ;; and spaces for us, nothing needs to be done here to
           ;; preprocess the input.

           (push p q) (go @))

         ;; If we read an empty line, q can be nil. In this case, go
         ;; back to $ and read another line. If q is not nil, reverse
         ;; it (we pushed, remember), canonicalize it and return the
         ;; result.
         (if q (return(k(reverse q))) (go $)))
      )

    ;; Read/eval loop.  When reading "(a name)", we read the three
    ;; next lines and append them to the first so that it builds a
    ;; call the the alias definition macro a. Otherwise, just eval x.
    (do((x(r)(r))((not x))
      (eval (if (eq(car x'a))
                `(,@x,(r),(r),(r))
                x)))))

Przykład

~$ sbcl --script file.lisp < testfile

A = 0
B = 0
C = 0
____ = 0
A = 1
B = 1
C = 2
____ = 0
A = 1
B = 3
C = 2
____ = 0
d = 6
____ = 0
d = 18
____ = 0
d = 0
____ = 0
A = 8
B = 3
C = 2
____ = 0
A = 9
B = 3
C = 2
____ = 0
A = 9
B = 3
C = 729

Jeśli zamienimy evalna printw pętli read / eval, możemy zobaczyć, co jest oceniane:

(a 's (i '_) (d '_) (d '_)) 
(a 'f (d '_) (d '_) (d '_)) 
(a 'z (d (nth 0 g)) (z (nth 0 g)) (s)) 
(a 'n (z (nth 0 g)) (i (nth 0 g)) (s)) 
(a 'move (moveH (nth 0 g) (nth 1 g)) (move (nth 0 g) (nth 1 g)) (s)) 
(a 'moveH (d (nth 0 g)) (i (nth 1 g)) (f)) 
(a 'dupe (dupeH1 (nth 0 g) (nth 1 g) (nth 2 g))
   (dupe (nth 0 g) (nth 1 g) (nth 2 g)) (s)) 
(a 'dupeH1 (d (nth 0 g)) (dupeH2 (nth 1 g) (nth 2 g)) (f)) 
(a 'dupeH2 (i (nth 0 g)) (i (nth 1 g)) (s)) 
(a 'copy (z (nth 1 g)) (copyH (nth 0 g) (nth 1 g)) (s)) 
(a 'copyH (dupe (nth 0 g) (nth 1 g) '_copy) (move '_copy (nth 0 g)) (s)) 
(a 'addTo (copy (nth 1 g) '_add) (move '_add (nth 0 g)) (s)) 
(a 'add (z (nth 0 g)) (addH (nth 0 g) (nth 1 g) (nth 2 g)) (s)) 
(a 'addH (addTo (nth 0 g) (nth 1 g)) (addTo (nth 0 g) (nth 2 g)) (s)) 
(a 'mul (mulH1 (nth 0 g) (nth 1 g)) (mulH2 (nth 0 g) (nth 2 g)) (s)) 
(a 'mulH1 (z (nth 0 g)) (copy (nth 1 g) '_mul) (s)) 
(a 'mulH2 (mulH3 (nth 0 g) (nth 1 g)) (mulH2 (nth 0 g) (nth 1 g)) (s)) 
(a 'mulH3 (d '_mul) (addTo (nth 0 g) (nth 1 g)) (f)) 
(a 'mulBy (mul '_mulBy (nth 0 g) (nth 1 g)) (copy '_mulBy (nth 0 g)) (s)) 
(a 'pow (powH1 (nth 0 g) (nth 2 g)) (powH2 (nth 0 g) (nth 1 g)) (s)) 
(a 'powH1 (n (nth 0 g)) (copy (nth 1 g) '_pow) (s)) 
(a 'powH2 (powH3 (nth 0 g) (nth 1 g)) (powH2 (nth 0 g) (nth 1 g)) (s)) 
(a 'powH3 (d '_pow) (mulBy (nth 0 g) (nth 1 g)) (f)) 
(p 'A) 
(p 'B) 
(p 'C) 
(n 'A) 
(n 'B) 
(add 'C 'A 'B) 
(p '____) 
(p 'A) 
(p 'B) 
(p 'C) 
(add 'B 'A 'C) 
(p '____) 
(p 'A) 
(p 'B) 
(p 'C) 
(mul 'd 'B 'C) 
(p '____) 
(p 'd) 
(mulBy 'd 'B) 
(p '____) 
(p 'd) 
(d 'A) 
(mulBy 'd 'A) 
(p '____) 
(p 'd) 
(pow 'A 'C 'B) 
(p '____) 
(p 'A) 
(p 'B) 
(p 'C) 
(pow 'A 'B 'C) 
(p '____) 
(p 'A) 
(p 'B) 
(p 'C) 
(pow 'C 'A 'B) 
(p '____) 
(p 'A) 
(p 'B) 
(p 'C)

Makroekspansja

Jeśli wybierzemy następującą definicję aliasu:

(a 'powH2 (powH3 (nth 0 g) (nth 1 g)) (powH2 (nth 0 g) (nth 1 g)) (s))

... widzimy odniesienia do zmiennej o nazwie, gktórej nigdzie nie ma w zakresie leksykalnym. Ale po makroekspansji sprawdzany jest rzeczywisty kod:

(defun powH2 (&rest g)
  (if (powH3 (nth 0 g) (nth 1 g))
      (powH2 (nth 0 g) (nth 1 g))
      (s))) 

Teraz godnosi się do listy argumentów definiowanej funkcji.

rdzeń rdzeniowy
źródło
2

Python 2, 486 bajtów

Jest to rozwiązanie referencyjne, które bardziej grałem w golfa (obecnie -98 bajtów).

import sys;sys.setrecursionlimit(2000)
def r(s):
 n=s[0]
 if n in A:f=lambda i:r([s[int(t)]if'0'<t[0]<':'else t for t in A[n][i]]);return f(1+(f(0)or 0))
 k=s[1]
 if'i'<n:print k,'=',V.get(k,0)
 elif'd'<n:V[k]=-~V[k]if k in V else 1
 elif'a'<n:
    if~-(k in V)or V[k]<1:return 1
    V[k]-=1
 else:A[k]=s[2:]
A={};V={};c=filter(bool,([l,l[:l.find('#')]]['#'in l]for l in input().split('\n')))
while c:
 s=c[0].split();c=c[1:]
 if'a'!=s[0]:r(s)
 else:r(['a',s[1]]+map(str.split,c[:3]));c=c[3:]

Zmiany (które pamiętam):

  • automatyczna konwersja liczb całkowitych logicznych ( [l,l[:l.find('#')]]['#'in l]).
  • ustaw lub zwiększ w jednej instrukcji ( V[k]=-~V[k]if k in V else 1)
  • więcej aliasów do dłuższych wyrażeń ( k=s[1])
  • brak licznika w głównej pętli, zamiast tego czyszczona jest lista wejściowa
  • printautomatyczne dodawanie spacji ( print k,'=',V.get(k,0))
  • sprawdzanie cyfr 1-9 ( '0'<t[0]<':')
  • odwracanie zwracanych wartości w rcelu zapisania returns
  • usuwanie powtórzeń krojenia i dzielenia ( map(str.split,c[:3])))
PurkkaKoodari
źródło
1

Python 3, 1322 bajty

Gra w golfa:

import re,sys;sys.setrecursionlimit(2000);F,L=filter,list
class P:
 N,O,F=0,{},{}
 def __init__(S,c):
  S.B,S.E={"p":S.P,"i":S.I,"d":S.D,"a":S.L},dict(enumerate(F(None,[i.split('#')[0].rstrip()for i in c.splitlines()])))
  while S.N in S.E:S.X(S.E[S.N])
 def V(S, v, y, z=0):
  if re.match("[\w_][\d\w_]*",v):
   if not v in y:
    if z is not None:y[v]=z
    else:return False
   return True
  return False
 def A(S):S.N+=1
 def P(S,v):
  if S.V(v,S.O):print("{0} = {1}".format(v, S.O[v]));return True
  return False
 def I(S,v):
  if S.V(v, S.O):S.O[v]+=1;return True
  return False
 def D(S,v):
  if S.V(v,S.O)and S.O[v]>0:S.O[v]-=1;return True
  return False
 def L(S,v):
  e=[]
  if S.V(v,S.F,e):
   for i in range(3):S.A();e.append(S.E[S.N].lstrip())
   return True
  return False
 def C(S,c,v):
  def R(Z,v):
   for i in re.findall("\s(\d+)", Z):Z=Z.replace(" %s"%i," %s"%v[int(i)-1])
   return Z
  Q,m,f=map(lambda l:R(l,v),S.F[c])
  if S.X(Q,False):return S.X(m,False)
  return S.X(f,False)
 def X(S,Z,C=True):
  u=re.match("\s?([\w_][\d\w_]*)\s?([\w_][\d\w ]*)?",Z)
  if u:
   c,v=map(lambda i:''if i is None else i,u.groups());v=L(F(None,v.split(' ')))
   if S.V(c,S.F,None):
    T=S.C(c, v)
    if C:S.A()
   elif S.V(c,S.B,None):
    T=S.B[c](*v)
    if C:S.A()
   else:return False
   return T
  return False

Nie golfowany:

import re

class Prindeal:
    iline = 0
    local = {}
    udef = {}
    content  = {}

    def __init__(self, c):
        self.built = {
            "p": self.print,
            "i": self.increment,
            "d": self.decrement,
            "a": self.alias,
        }
        self.content = dict(enumerate(filter(None, [i.split('#')[0].rstrip()for i in c.splitlines()])))
        while self.iline in self.content:
            self.execute_line(self.content[self.iline])

    def validate_name(self, varname, stack, default=0):
        if re.match("[\w_][\d\w_]*", varname):
            if not varname in stack:
                if default is not None:
                    stack[varname] = default
                else:
                    return False
            return True
        return False

    def advance_stack(self):
        self.iline += 1

    def print(self, varname):
        if self.validate_name(varname, self.local):
            print("{0} = {1}".format(varname, self.local[varname]))
            return True
        return False

    def increment(self, varname):
        if self.validate_name(varname, self.local):
            self.local[varname] += 1
            return True
        return False

    def decrement(self, varname):
        if self.validate_name(varname, self.local) and self.local[varname] > 0:
            self.local[varname] -= 1
            return True
        return False

    def alias(self, aliasname):
        indexed_lines = []
        if self.validate_name(aliasname, self.udef, indexed_lines):
            for i in range(3):
                self.advance_stack()
                indexed_lines.append(self.content[self.iline].lstrip())
            return True
        return False

    def execute_alias(self, cmd, variables):
        def parse_args(line, variables):
            for i in re.findall("\s(\d+)", line):
                line = line.replace(" %s" % i, " %s" % variables[int(i) - 1])
            return line
        init, success, failure = map(lambda l: parse_args(l, variables), self.udef[cmd])
        if self.execute_line(init, False):
            return self.execute_line(success, False)
        return self.execute_line(failure, False)

    def execute_line(self, line, cont=True):
        valid_execution = re.match("\s?([\w_][\d\w_]*)\s?([\w_][\d\w ]*)?", line)
        if valid_execution:
            cmd, variables = map(lambda i: '' if i is None else i, valid_execution.groups())
            variables = list(filter(None, variables.split(' ')))
            if self.validate_name(cmd, self.udef, None):
                temp = self.execute_alias(cmd, variables)
                if cont:
                    self.advance_stack()
            elif self.validate_name(cmd, self.built, None):
                temp = self.built[cmd](*variables)
                if cont:
                    self.advance_stack()
            else:
                return False
            return temp
        return False

Stosowanie:

P(c)

Gdzie cjest treść tekstu?

Przykłady:

Akceptowane są ciągi jednowierszowe:

  • P("p cat")
  • P("p dog\ni dog\np dog")

Akceptowane są również sznurki z podszewką:

P("""
p dog
i dog
p dog
""")

Lub:

P("""p dog
i dog
p dog""")

Itp.

Uwagi:

Działa to poprawnie dla wszystkich przypadków testowych, ale osiąga limit rekurencji:

pow C A B   #C = A ^ B = 9 ^ 3 = 729

Stąd sys.setrecursionlimit(2000).

Zach Gates
źródło
1
Zużyje trochę bajtów, ale czy nie możesz użyć sys.setrecursionlimit (), aby działało to poprawnie z aliasem pow?
Corwin,
Mógłbym, ale OP stwierdził, że języki takie jak Python (które mają limity rekurencji) są akceptowane w obecnej postaci. Dodam jednak poprawkę, jeśli zażąda tego OP. @Corwin
Zach Gates,
Słusznie. Brakowało tego w specyfikacji. @ZachGates
Corwin,
1

Python - 695 688 bajtów

def p(v):print v,"=",w.get(v,0)
def i(v):w[v]=w.get(v,0)+1
def d(v):
 if v in w:
<TAB>w[v]-=1
<TAB>if not w[v]:del w[v]
 else:return 1
def a(n,b,d,h):
 def g(*a):
<TAB>i=1;f=b;s=d;t=h
<TAB>for v in a:v=q+v+q;k=q+j(i)+q;f=c(f,k,v);s=c(s,k,v);t=c(t,k,v);i+=1
<TAB>y=u(t,e)if u(f,e)else u(s,e);i=1;return y
 e[n]=g
q="'";w=x={};u=eval;e={'a':a,'d':d,'i':i,'p':p};import sys;l=sys.stdin.readlines();r="";j=str;c=j.replace;sys.setrecursionlimit(2000)
for h in l:
 h = h.strip()
 if not h:continue
 l = h.split();f=l[0];n=f+"("
 if "#" in f:continue
 for g in l[1:]:
<TAB>b=g.find("#")+1
<TAB>if b:g=g[:b-1]
<TAB>if g:n+="'%s',"%g
<TAB>if b:break
 if x:x-=1;d+='"%s)",'%n
 else:x=(f=="a")*3;d=n
 if not x:d+=")\n";r+=d
exec r in e

<TAB> to dosłowny znak tabulacji.

pppery
źródło
1

C ++, 1111 bajtów

Ten jest w C ++ - tak idiomatyczny, jak tylko mogłem.
Oznacza to, że będzie bardziej C ++ - i mniej C - Ish.
Oznacza to również, że jest większy niż równoważny program C.
Myślę, że C ++ rywalizuje z Javą o pełną bibliotekę standardową.
Kompiluje się z VS2013 ig ++ 4.9.2 (z -std = c ++ 11)

#include<array>
#include<iostream>
#include<map>
#include<regex>
#include<sstream>
#include<stack>
#define B std::
#define a first
#define b second
#define c(s);else if(x.a==s)
#define d(n)B getline(B cin,r##n)
#define e(n)r##n=B regex_replace(r##n,q,"$1");
#define f(n)do{d(n);e(n)}while(r##n.empty());
#define g B string
#define h B istream_iterator<g>
#define i p.top().a
#define j p.empty()
#define k B pair
#define u continue;
#define w B back_inserter
typedef B vector<g>s;typedef B array<g,3>A;typedef k<k<long,A>,s>t;B map<g,A>m;B map<g,long>n;B stack<t>p;B regex q("^ *(.*?) *(#.*)?$");int main(){g r0,r1,r2,r3;while(d(0)){e(0)if(r0.empty())u p.push(t{{0,{{r0,"",""}}},{}});bool z;while(!j){k<g,s>x;B istringstream ss(i.b[i.a]);ss>>x.a;B copy(h(ss),h(),w(x.b));s o;B transform(B begin(x.b),B end(x.b),w(o),[](g y){int v=atoi(y.c_str());return v>0?p.top().b[v-1]:y;});z=true;if(0)c("")c("p")B cout<<o[0]<<" = "<<n[o[0]]<<B endl c("i")n[o[0]]++c("d")n[o[0]]-=(z=n[o[0]])c("a"){f(1)f(2)f(3)m.insert(B make_pair(o[0],A{{r1,r2,r3}}));}else{p.push(t{{0,m[x.a]},o});u}while(!j&&i.a)p.pop();if(!j)i.a+=1+!z;}}}

Poniżej znajduje się oryginał. Jeśli ktoś może wymyślić sposób, aby uczynić go bardziej idiotycznym i krótszym w tym samym czasie, proszę dać mi znać.

#include <array>
#include <iostream>
#include <map>
#include <regex>
#include <sstream>
#include <stack>

typedef std::vector<std::string> List;
typedef std::pair<std::string, List> Statement;
typedef std::array<std::string, 3> Alias;
typedef std::pair<long, Alias> IndexedAlias;
typedef std::pair<IndexedAlias, List> Item;

std::map<std::string, Alias> aliases;
std::map<std::string, long> variables;
std::stack<Item> stack;
std::regex re("^ *(.*?) *(#.*)?$");

int main()
{
    std::string line, line1, line2, line3;
    while (std::getline(std::cin, line)) // control-Z to exit
    {
        line = std::regex_replace(line, re, "$1");
        if (line.empty()) continue;
        stack.push(Item{ { 0, { { line, "", "" } } }, {} });

        bool flag;
        while (!stack.empty())
        {
            Statement statement;
            std::istringstream ss(stack.top().first.second[stack.top().first.first]);
            ss >> statement.first;
            std::copy(std::istream_iterator<std::string>(ss), std::istream_iterator<std::string>(), std::back_inserter(statement.second));

            List arguments;
            std::transform(std::begin(statement.second), std::end(statement.second), std::back_inserter(arguments),
                [](std::string arg){ int i = atoi(arg.c_str()); return i > 0 ? stack.top().second[i - 1] : arg; });

            flag = true;
            if (statement.first == "")
                ;
            else if (statement.first == "p")
                std::cout << arguments[0] << " = " << variables[arguments[0]] << std::endl;
            else if (statement.first == "i")
                variables[arguments[0]]++;
            else if (statement.first == "d")
                variables[arguments[0]] -= (flag = variables[arguments[0]]);
            else if (statement.first == "a")
            {
                do { std::getline(std::cin, line1); line1 = std::regex_replace(line1, re, "$1"); } while (line1.empty());
                do { std::getline(std::cin, line2); line2 = std::regex_replace(line2, re, "$1"); } while (line2.empty());
                do { std::getline(std::cin, line3); line3 = std::regex_replace(line3, re, "$1"); } while (line3.empty());
                aliases.insert(std::make_pair(arguments[0], Alias{ { line1, line2, line3 } }));
            }
            else
            {
                stack.push(Item{ { 0, aliases[statement.first] }, arguments });
                continue;
            }

            while (!stack.empty() && stack.top().first.first) stack.pop();
            if (!stack.empty()) stack.top().first.first += 1 + !flag;
        }
    }

    std::cout << "-- Variables --" << std::endl;
    std::transform(std::begin(variables), std::end(variables), std::ostream_iterator<std::string>(std::cout, "\n"),
        [](std::map<std::string, long>::value_type pair){ std::ostringstream ss; ss << pair.first << " = " << pair.second; return ss.str(); });
    std::cout << "-- Aliases --" << std::endl;
    std::transform(std::begin(aliases), std::end(aliases), std::ostream_iterator<std::string>(std::cout, "\n"),
        [](std::map<std::string, Alias>::value_type pair){ std::ostringstream ss; ss << pair.first << " = [1]:" << pair.second[0] << " [2]:" << pair.second[1] << " [3]:" << pair.second[1]; return ss.str(); });
    std::cout << "---------------" << std::endl;

    return 0;
}
Jerry Jeremiasz
źródło
0

Haskell, 1009

Zrobiłem co w mojej mocy, aby zagrać w golfa; mój nieoznakowany kod składał się z ponad 3000 znaków. W tym momencie nie pamiętam, co robią wszystkie funkcje, więc gra w golfa oznacza zgadywanie, co go złamie, a co nie.

import qualified Data.Map as M
import Control.Monad.State.Lazy
import Data.List
type A=M.Map String
data P=P(A Int)(A([String]->StateT P IO Int))
a f=evalStateT f(P M.empty$M.fromList[("i",\(b:_)->(+1)%b),("d",\(b:_)->pred%b),("p",\(b:_)->i b>>= \v->liftIO(putStrLn$b++"="++show v)>>q 1)])
e(k:l)=do{(P v a)<-get;put.P v$M.insert k(m l)a;q 1}
g t s f= \a->t a>>= \b->if b>0then s a else f a
f%k=f<$>i k>>= \v->if v<0then k#0>>q 0else k#v>>q 1
i k=get>>= \(P v _)->q$M.findWithDefault 0 k v
k#v=get>>= \(P b a)->put$P(M.insert k v b)a
l k=get>>= \(P _ a)->q$a M.!k
f s=let(f:a)=r s in($a)<$>l f>>=id
m(t:s:f:_)=g(k t)(k s)(k f)
k s=let(f:b)=r s in\a->($(map((\y z->if all(\c->c>'/'&&c<':')z then y!!(read z-1)else z)a)b))<$>l f>>=id
n=dropWhileEnd(==' ').takeWhile(not.(=='#')).dropWhile(==' ')
o[]=[]
o(l:ls)|(head.r$l)=="a"=(l:take 3 ls):(o$drop 3 ls)|1>0=[l]:o ls
p s|length s>1=e$(n.tail.head$s):tail s|1>0=f.head$s
q=return
main=join$a.(\s->mapM_ p(o.filter(not.null).map n.lines$s))<$>getContents
r=words
ankh-morpork
źródło