Transpile WordMath

25

Wszyscy widzieliśmy te „hax matematyki” online, które wyglądają tak:

Think of a number, divide by 2, multiply by 0, add 8.

I, magicznie, wszyscy kończą na cyfrze 8!


Język

Zdefiniujmy język programowania, który wykorzystuje składnię powyższego tekstu, zwany „WordMath”. Skrypty WordMath są zgodne z tym szablonem:

Think of a number, <commandlist>.

Co w zasadzie oznacza: weź liczbę (jako dane wejściowe ze STDIN) jako akumulator początkowy, wykonaj na nim wszystkie polecenia i wyślij wynik.

Polecenia są rozdzielane separatorem ,(przecinek + spacja). Prawidłowe polecenia to (uwaga, która #reprezentuje nieujemną liczbę całkowitą :) :

  • add #/ subtract #- Dodaj / odejmij wartość z akumulatora.
  • divide by #/ multiply by #- floordiv / pomnóż akumulator przez podaną wartość.
  • subtract from #- Podobne do subtract, ale robi acc = # - acczamiastacc = acc - #
  • repeat- ponownie wykonaj ostatnie polecenie. To nie może być pierwsze polecenie, ale musisz obsługiwać wiele kolejnych powtórzeń.

Wyzwanie

Twoim zadaniem jest utworzenie programu lub funkcji, która pobiera prawidłowy skrypt WordMath jako dane wejściowe i transponuje go do prawidłowego pełnego programu - w tym samym języku, w którym znajduje się kod.

Na przykład, jeśli mój kod znajduje się w języku Python 2, a skrypt to:

Think of a number, subtract from 10, add 10, multiply by 2.

Wyprowadzanym programem może być:

a = input()
a = 10 - a
a += 10
a *= 2
print(a)

Lub alternatywnie:

print(((10-input())+10)*2)

Tak długo, jak jest to pełny program, który pobiera dane wejściowe STDINi drukuje STDOUT, lub najbliższe odpowiedniki języka.


Zasady

  • Twój oryginalny program może założyć, że dane wejściowe są zawsze prawidłowym skryptem WordMath.
  • Transponowane programy nie muszą obsługiwać błędów matematycznych, takich jak dzielenie przez 0.
  • Transpilowane programy mogą zakładać, że dane wejściowe reprezentują prawidłową liczbę całkowitą ze znakiem, w standardowym zakresie liczb całkowitych w języku użytkownika.
  • To jest , więc wygrywa najkrótsze rozwiązanie (w bajtach).
  • Liczy się tylko liczba bajtów oryginalnego programu - kod wyjściowy może być tak długi, jak chcesz!

Przykładowe skrypty

Przykład 1:

Think of a number. 

Wejdź, nic nie rób, wyświetl: program cat WordMatha.

Przykład 2:

Think of a number, divide by 5, subtract from 9.

Pamiętaj, że „podział” to podział podłogi, więc w przypadku tego programu 6 -> 8i 29 -> 4.

Przykład 3:

Think of a number, add 5, add 10, multiply by 2, subtract 15, repeat, divide by 2.

Rozszerzony program dla kotów!

Przykład 4:

Think of a number, subtract 1, repeat, repeat.

Pobiera liczbę i odejmuje 3.

FlipTack
źródło
Czy musimy wspierać kolejne powtórzenia?
darrylyeo
1
Czy możemy używać liczb zmiennoprzecinkowych, gdy jest to domyślny typ języka / jeśli nie obsługuje liczb całkowitych?
Rainer P.
@RainerP. tylko jeśli język nie obsługuje liczb całkowitych / dzielenia liczb całkowitych
FlipTack
1
Jaki jest oczekiwany wynik -5/3? Czy zaokrąglamy w 0kierunku ujemnej nieskończoności?
Martin Ender,
1
@MartinEnder Powiedziałbym, że jest okrągłe w kierunku ujemnej nieskończoności jako podziału podłogi , ale jeśli twój język implementuje dzielenie liczb całkowitych w kierunku 0, to też jest w porządku.
FlipTack,

Odpowiedzi:

6

05AB1E , 59 56 54 52 bajtów

„, ¡¦vyDþs.AJá'bK"dmas""/*+-"‡„-f„s-.:«D'rQi®}©}J'rK

Wypróbuj online!

Po tym mój mózg boli jak diabli ... Wyprowadza kod 05AB1E w następujący sposób:

  • Think of a Number jest usuwany ze względu na domniemane dane wejściowe.
  • Subtract From #coverts do #s-(zamiana ai bi wykonywania operacji).
  • Subtract #konwertuje na #-.
  • Add #konwertuje na #+.
  • Multiply by #konwertuje na #*.
  • Divide by #konwertuje na #/.
  • Repeat pobiera to, co ostatnio zapisano w rejestrze i łączy je.

Wyjaśnił:

„, ¡                                                 # Split on ', '.
    ¦                                                # Remove the 'Think of a number'.
     vy                                        }     # Loop through chunks.
       Dþs                                           # Dupe, push only digits, swap.
          .AJá                                       # Acronymify without digits.
              'bK                                    # Remove the `b`.
                 "dmas""/*+-"‡                       # Replace letters with OPs.
                              „-f„s-.:               # Replace '-f' with 's-'.
                                      «              # Concat # with OP.
                                       D'rQ          # Dupe, push 1 if OP='r'.
                                           i®}       # If 'r', push last #OP.
                                              ©      # Store current OP.
                                                J'rK # Join, remove remaining r's.

Przykład:

Wkład:

Think of a number, divide by 2, multiply by 10, add 8, subtract 6, subtract from 9, repeat, repeat, subtract 41.

Wydajność:

2/10*8+6-9s-9s-9s-41-

Wypróbuj rozwiązanie z wejściem 10:

Wypróbuj online!

Zobacz w Google:

Oto link do tego samego równania wpisanego w google.

Urna Magicznej Ośmiornicy
źródło
13

Preprocesor C, 362 bajtów

PRAWIE mam to działa w JUST preprocesorze C, ale polecenie powtarzania okazuje się zbyt trudne do wdrożenia. Zamiast tego użyłem preprocesora do przekształcenia danych wejściowych w tablicę, która jest następnie interpretowana przez jakiś dodatkowy kod.

main(){int c[]={
#define Think 
#define of
#define a
#define number 0
#define add 1,
#define subtract 2,
#define from 0,3,
#define multiply 4,
#define divide 5,
#define by
#define repeat 6, 0
#include "input.wm"
,'$'};int _,l,v;scanf("%d", &_);for(int i=1;c[i]-'$';++i){c[i]!=6?l=c[i],v=c[++i]:++i;l==1?_+=v:l==2?_-=v:l==3?_=v-_:l==4?_*=v:_/=v;}printf("%d",_);}

Dane wejściowe należy podać w pliku „input.wm” lub po prostu zrzucić do źródła w tym wierszu. Uwzględniłem jego bajty, ponieważ uważam, że jest nieco zuchwały i nieco niezgodny z regułami wyzwania, więc pasuje tylko.

W każdym razie, gdy zrzucisz źródło WordMath do input.wm, gdzie kompilator może go znaleźć, powinieneś być w stanie po prostu skompilować to, tak jak jest, z ostrzeżeniami, aby stworzyć plik wykonywalny, który robi to, co mówi źródło WordMath.

LambdaBeta
źródło
2
Uwaga: to niestety nie działa w przypadku niektórych kompilatorów po zakończeniu powtarzania. Wynika to z tego, że rzucają spacją po 0, a następnie widzą okres bezdomny i nie wiedzą, co z tym zrobić.
LambdaBeta,
jestem sprytny, jestem pod wrażeniem.
kot
7

Siatkówka, 170 bajtów

Bo kto nie chciałby tego zobaczyć ?!

Pomyślałem o tym, jak wspaniale byłoby zobaczyć rozwiązanie Retina i postanowiłem go szybko stworzyć. Zajęło to tylko godzinę. Jak zwykle liczba bajtów zakłada kodowanie ISO 8859-1.

S`, 
\.

T.*
.*¶$$*
\;+`(.*)¶rep.*(¶?)
$1¶$1$2
\d+
$*
.*f.* (1*)
1¶x¶$¶$1¶+`x1¶
m.* 
1¶
di.* (1*)
^¶$1 ¶^(1+) (\1)+1*$¶x$$#+¶1+ 1*¶x0¶x\d+¶$$*
s.* (1*)
^$1¶
a.* 
^¶
\z
¶1

Wypróbuj online

Dane wyjściowe mają końcowy znak nowej linii, którego nie należy kopiować podczas testowania wynikowego programu. Program nie obsługuje negatywów, ponieważ standardowy zakres liczb całkowitych Retiny (w jedności) nie.

Wyjaśnienie:

S`,                 # Split input on ", " putting each command on its own line
\.                  # Remove the period

T.*                 # Think of a number -> .*\n$* (replaces input with unary)
.*¶$$*
\;+`(.*)¶rep.*(¶?)  # Loop, replacing "repeat" with the line before it
$1¶$1$2
\d+                 # Replace all numbers with their unary representation
$*
.*f.* (1*)          # Replace "subtract from " with a subtract from program
1¶x¶$¶$1¶+`x1¶
m.*                 # Replace "multiply by " with a multiply program
1¶
di.* (1*)           # Replace "divide by " by my integer division program
^¶$1 ¶^(1+) (\1)+1*$¶x$$#+¶1+ 1*¶x0¶x\d+¶$$*
s.* (1*)            # Replace "subtract " with a subtraction program
^$1¶
a.*                 # Replace "add " with an addition program
^¶
\z                  # At the end, add a stage to change unary into decimal
¶1

Programy matematyczne:

Dodaj:

Dodaj liczbę jedynek na początku. Dodaj 5:

^
1111

Odejmować:

Usuń liczbę jedynek od początku. Odejmij 5:

^11111

Odejmij od:

Zamień wejścia 1s na xs. Umieść obok stałego numeru. Wielokrotnie usuwaj x1. Odejmij od 10:

1
x
$
1111111111
+`x1

Pomnożyć przez:

Zastąp każdy 1określoną liczbą. Pomnóż przez 3:

1
111

Podzielić przez:

To używa mojego programu Retina dla Integer Division . Podziel przez 2:

^                   # Place the fixed divisor before the dividend
11 
^(.+) (\1)+.*$      # Match the divisor, followed by each occurrence in the dividend.
x$#+                # Replace with the number of matches. Trailing ones are dropped
.+ .*               # If there are still two numbers, the result is zero
x0
x\d+                # Replace result (marked with an 'x') with unary
$*
mbomb007
źródło
Obawiam się, że nie rozumiem, jak to może działać. Jakiekolwiek dane wejściowe próbuję dla poleceń odejmowania, otrzymuję niepoprawne wyniki (czy brakuje wyjściowych linii?). Nie widzę też, jak to radzi sobie z negatywnymi danymi wejściowymi lub negatywnymi wynikami pośrednimi.
Martin Ender,
@MartinEnder Mogę naprawić odejmowanie, jeśli wyjaśnisz, dlaczego ten uproszczony program daje dwa z nich w wyniku. retina.tryitonline.net/#code=JArCtjE&input=dGVzdAo
mbomb007
Ponieważ $dopasowuje się na samym końcu łańcucha lub przed końcowym podawaniem linii. Potrzebujesz, \zjeśli chcesz tylko tego pierwszego.
Martin Ender,
4

GNU awk, 139 bajtów

BEGIN{RS="[,.]";c="{}{";ORS=";"}
/ad/{c="+="$2}
/c/{c="-="$2}
/om/{c="="$3"-$0"}
/l/{c="*="$3}
/v/{c="=int($0/"$3")"}
!NF{c="}1"}
$0="$0"c

Wezwanie:

$> awk `awk -f wordmath <<< "Think of a number, add 1, repeat, repeat."` <<< 0
$> 3

Przypadki testowe:

$> awk -f wordmath <<< "Think of a number."  
$> $0{}{;$0}1;

$> awk -f wordmath <<< "Think of a number, divide by 5, subtract from 9."
$> $0{}{;$0=int($0/5);$0=9-$0;$0}1;

$> awk -f wordmath <<< "Think of a number, add 5, add 10, multiply by 2, subtract 15, repeat, divide by 2."
$> $0{}{;$0+=5;$0+=10;$0*=2;$0-=15;$0-=15;$0=int($0/2);$0}1;
Rainer P.
źródło
4

Haskell, 232 231 bajtów

Oczywiście funkcjonalny programista wolałby zwracać funkcję niż ciąg znaków reprezentujący program, ale proszę bardzo:

t l="main=getLine>>=print."++""%words l++"(0+).read"
x%((o:_):f:n:r)|o=='m'=h"*"n r|o=='d'=h"`div`"n r|f=="from"=h"(-)"n r
x%(s:n:r)|s=="add"=h"+"n r|s!!0=='s'=h s(' ':n)r
x%(_:r)=x%r++x
_%_=""
h s n r|x<-'(':s++init n++")."=x%r++x

Uwagi: Zawsze zaczynamy od dodania zera, w przeciwnym razie transpozycja trywialnego programu WordMath nie dałaby wystarczających informacji, aby wywnioskować, z jakiego typu readjest on używany. subtract from nmoże być wdrożony jako (n-), ale używam ((-)n)dla większej jednolitości. W przypadku subtract nkopiowania subtractz wejścia, więc nie muszę go pisać, ale muszę wyrównać brakujące miejsce na końcu. repeatjest używany jako operacja domyślna; wraz z pustą początkową poprzednią operacją pozwala to na łatwe zignorowanie pierwszych czterech słów.

Przykład użycia:

*Main> t "Think of a number. "
"main=getLine>>=print.(0+).read" 

Inne przykłady dają następujące wyniki:

"main=getLine>>=print.((-)9).(`div`5).(0+).read"
"main=getLine>>=print.(`div`2).(subtract 15).(subtract 15).(*2).(+10).(+5).(0+).read"  
"main=getLine>>=print.(subtract 1).(subtract 1).(subtract 1).(0+).read"
Christian Sievers
źródło
Ciekawe, jak wygenerowałbyś funkcję zwracającą zamiast łańcucha?
Cyoce,
W funkcjonalnym języku programowania tworzenie i komponowanie funkcji nie jest trudniejsze niż tworzenie i dołączanie łańcucha. hmoże wyglądać jak h s n r|x<-s.read.init$n=x%r.xi być wywoływana z pierwszym argumentem jak funkcja h(+)n r(i musi być flipgdzieś, aby uzyskać prawidłową kolejność operatorów), podstawową sprawą jest _%_=id. Główna funkcja pozwala uniknąć całej płyty kotła i po prostu być t l=id%words l. - Dzięki curry może to być interpretowane jako tłumacz, a ten pomysł może prowadzić do łatwiejszego i / lub krótszego rozwiązania.
Christian Sievers,
4

Python 2, 263 258 260 221 bajtów

Może to prawdopodobnie nadal być znacznie krótszy.

def r(s,o="",p=""):c=s.pop(0);c=[c,p]['re'in c];n=c.split()[-1];o=[[o+[['+-'['s'in c],'//']['v'in c],'*']['m'in c]+n,n+'-'+o]['f'in c],'input()']['T'in c];return s and r(s,"(%s)"%o,c)or o
lambda s:"print "+r(s.split(','))

Wypróbuj online

Używam //zamiast /, ponieważ ostatnia instrukcja będzie miała .na końcu, dzięki czemu dowolna liczba będzie liczbą zmiennoprzecinkową. Aby zachować spójność podziału, używam podziału na liczby całkowite.

Dane wyjściowe przypadków testowych:

print input()
print 9.-((input())//5)
print ((((((input())+5)+10)+10)-15)-15)//2.
print (((input())-1)-1)-1
mbomb007
źródło
Jeśli zmienisz ten wielki blok ifS odo następującego (co moim zdaniem powinno działać) o=[[o+[['+-'['s'in c],'//']['v'in c],'*']['m'in c]+n,n+'-'+o]['f'in c],'input()']['T'in c], można je dostać w dół do 224.
Kade
@Kade Tak, to wciąż było czytelne. Nie mogę tego mieć.
mbomb007,
@Cyoce Nie, sam akt wywołania lambdy prawdopodobnie kosztowałby więcej niż oszczędza. Musiałby zaoszczędzić 4 lub 5 bajtów na połączenie, aby się opłacić.
mbomb007,
4

Befunge, 342 305 bajtów

>~$1 +:89+`#v_801p
  v_^#`+85~$<
1+>~:5+88+/3-!#v_$
v`"/":~p8p10+1<>>:"/"`!|
 "+"\5+8p4+:1+^:p11:g10<  >:"+"\2+8p:"*"\3+8p:
_$7%:01g\!#v_\2-7g\:1+:01v^p8+1\"5":p8\"5":g10
#8\"\"$#:0#<^v<p8p8\<g80p<>\#+$#12#p
-/+* >:#,_@  #^p8g10<
".@"<^"&"
10<v0<\g8-\g11:<:p1>#1+g11:p10+g
-:^>1g\-8p1-::#^_$8g^  >$$01g11g

Wypróbuj online!

Wydajność

Generowany przez niego kod zaczyna się od polecenia &(wartość wejściowa), a kończy na poleceniu .(wartość wyjściowa) i @(wyjście). W międzyczasie mamy różne obliczenia w formie <number><operation>, w której operacją może być +(dodawanie), -(odejmowanie), /(dzielenie przez),* (mnożenie przez) i\- (odejmowanie od).

Sama liczba jest nieco skomplikowana, ponieważ Befunge obsługuje tylko literały liczbowe w zakresie od 0 do 9, więc wszystko większe niż to musi zostać obliczone ręcznie. Ponieważ czytamy już cyfry znak po znaku, po prostu budujemy liczbę, gdy każda cyfra jest odczytywana, więc na przykład 123 staje się 155+*2+55+*3+, tj (((1 * 10) + 2) * 10) + 3.

Przykłady

Input:  Think of a number.
Output: &.@

Input:  Think of a number, divide by 5, subtract from 9.
Output: &5/9\-.@

Input:  Think of a number, add 5, add 10, multiply by 2, subtract 15, repeat, divide by 2.
Output: &5+155+*0++2*155+*5+-155+*5+-2/.@

Input:  Think of a number, subtract 1, repeat, repeat.
Output: &1-1-1-.@

Wyjaśnienie

Befunge nie ma możliwości manipulowania ciągami jako takimi, więc większość parsowania jest obsługiwana przez liczenie znaków. Zaczynamy od pominięcia pierwszych 18 znaków, co pozwala nam przejść przez myśl o liczbie wyrażenia (plus przecinek lub kropka). Następnie, jeśli następny znak jest jakąś formą nowego wiersza lub EOF, przechodzimy od razu do procedury wyjściowej, w przeciwnym razie kontynuujemy wyszukiwanie listy poleceń.

Aby przeanalizować polecenie, po prostu zliczamy znaki, aż dojdziemy do cyfry lub separatora. Jeśli jest to separator, musi to być polecenie powtarzania, które traktujemy jako specjalny przypadek. Jeśli jest to cyfra, dodajemy ją do bufora wyjściowego i kontynuujemy wyszukiwanie kolejnych cyfr. Za każdym razem, gdy wypisywana jest cyfra, poprzedzamy ją znakiem 55+*(aby pomnożyć dotychczasową liczbę całkowitą przez 10) i +dodajemy przyrostek za pomocą (aby dodać ją do sumy). Po zakończeniu cyfr dodajemy znak polecenia.

Jeśli chodzi o sposób określania polecenia, bierzemy liczbę znaków do pierwszej cyfry modulo 7. Aby dodać to jest 4 (łącznie z następną spacją), do odejmowania to 2, do dzielenia przez to 3, do mnożenia przez to 5 , i dla odejmowania od jego 0. Odejmowanie od wymaga nieco dodatkowej obsługi, ponieważ wymaga \-kombinacji komend, ale inni po prostu używają ich wartości, aby wyszukać odpowiedni znak polecenia w tabeli.

Ten proces powtarza się dla każdego polecenia, tworząc wynik w postaci wcześniej utworzonego ciągu w linii 8. Za każdym razem, gdy dodawane jest dodatkowe polecenie, dodajemy również cudzysłów zamykający do łańcucha, aby upewnić się, że jest on zawsze poprawnie zakończony. Następnie, kiedy w końcu osiągamy koniec naszych danych wejściowych, po prostu „wykonujemy” ten ciąg, aby wypchnąć go na stos, a następnie postępujemy zgodnie ze standardową sekwencją wyjściową, aby zapisać to wszystko.

James Holderness
źródło
3

JavaScript (ES6), 163 bajty

w=>w.split`, `.map(l=>(o={a:'+',s:'-',m:'*',d:'/'}[a=l[0]])?p=(x=l.split` `.pop(),l[9]=='f'?x+`-n`:`n`+o+x+`|0`):a<'r'?'n=+prompt()':p).join`
n=`+`
console.log(n)`

Spróbuj:

f=w=>w.split`, `.map(l=>(o={a:'+',s:'-',m:'*',d:'/'}[a=l[0]])?p=(x=l.split` `.pop(),l[9]=='f'?x+`-n`:`n`+o+x+`|0`):a<'r'?'n=+prompt()':p).join`
n=`+`
console.log(n)`

const program = f('Think of a number, add 5, add 10, multiply by 2, subtract 15, repeat, divide by 2.')
document.write('<pre>' + program + '</pre>' )

eval(program)

/*
Outputs:

n=+prompt()
n=n+5|0
n=n+10|0
n=n*2|0
n=n-15|0
n=n-15|0
n=n/2.|0
console.log(n)
*/

darrylyeo
źródło
3

Vim 208 171 168 bajtów

Dodano możliwość wykonywania wielu powtórzeń z rzędu zgodnie z @ Flp.Tkc, ale grało w golfa wystarczającą liczbę bajtów, żebym mógł nadal zmniejszyć liczbę bajtów.

:map s :s;\v
c4wcw="s, s\w* \w+ (\d+); *-1+\1);g
s, ([adms]).{-}(\d+); \1\2);g
qws(\S+), r\w+;\1 \1
@wq@ws.*;\=tr(submatch(0),'adms','+/*-')
qq])T=i(@qq@qx$xA

TryItOnline

Znaki niedrukowalne:

:map s :s;\v
c4wcw^V^R=^V^R"^[s, s\w* \w+ (\d+); *-1+\1);g
s, ([adms]).{-}(\d+); \1\2);g
qws(\S+), r\w+;\1 \1
@wq@ws.*;\=tr(submatch(0),'adms','+/*-')
qq])T=i(^[@qq@qx$xA
^V^[^[

Dane wyjściowe przypadków testowych:

  1. cw^R=^R" ^[ TryItOnline
  2. cw^R=((^R" /5) *-1+9) ^[ TryItOnline
  3. cw^R=((((((^R" +5) +10) *2) -15) -15) /2) ^[ TryItOnline
nmjcman101
źródło
To nie wydaje się działać w przypadku wielu kolejnych powtórzeń.
FlipTack,
@ Flp.Tkc naprawione, dziękuję! Nie zauważyłem tego wcześniej.
nmjcman101
2

leksykon, 246 bajtów

%{
#define P printf
#define O P("n%s%d;",c,v);
int v;char*c;
%}
%%
T {P("%%%%\n.* {int n=atoi(yytext);");}
ad {c="+=";}
fr {c="=-n+";}
s {c="-=";}
v {c="/=";}
l {c="*=";}
re {O}
[0-9]+ {v=atoi(yytext);O}
\. P("printf(\"%%d\",n);}\n%%%%");
. ;
%%

leksykon celuje na C, więc kompilator C musiałby skompilować go w coś wykonywalnego. Biblioteka Lexer ( ll) również musiałaby zostać połączona. Może to dodać karę bajtową, ale nie jestem pewien, ile bajtów, jeśli tak.

Program generuje program leksykalny (według specyfikacji), który ocenia przełożone wyrażenie matematyczne. Kod pomiędzy „ %{i %}” dotyczy tylko „transpilatora”:

#define P printf              /* for brevity */
#define O P("n%s%d;",c,v)     /* expression builder, calls P = printf */
int v;char*c;                 /* v=most recent integer read */
                              /* c=the expression infix */

Pomiędzy dwiema %%liniami znajduje się część wyrażenia regularnego / akcji. Pierwszą zasadą, którą należy dopasować, byłabyT („Pomyśl ...”), która buduje preambułę (programy lex muszą zacząć zawierać przynajmniej sekcję reguł, iyytext jest ostatnim pasującym tekstem, więc reguła zasadniczo zapełnia akumulator danymi wejściowymi użytkownika ).

Odrzuty zaprogramować wszystkie wejścia z wyjątkiem tego, co jest dopasowany, i inne zasady ( ad, frmaksymalnie re) obsługują klauzule ekspresji wordmath z jako minimalny mecz, jak to możliwe, aby być unikatowa. W większości z nich ustawia csię na przyrostek wyrażenia, który jest konkatenowany między nostatnią liczbą całkowitą odczytaną po Owywołaniu (tak więc na przykład czytanie „dodaj 9” spowoduje ustawienie przyrostka na +=, v na 9i wywołanieO wyjście n+=9;) . (Ciekawostką jest to, że „odejmowanie od 8” spowoduje dopasowanie zarówno reguł, jak si frreguł, ale ponieważ Ojest wywoływane tylko pod numerem, odpowiednia reguła n=-n+8;jest jedynym wyrażeniem, które otrzymuje dane wyjściowe). ThereZasada „Repeat” połączeń tylkoOponownie, który wyprowadza ostatnio utworzone wyrażenie (a ponieważ kolejne dopasowania będą się zapychać yytext, obsługa „powtórz” jest powodem, dla której [0-9]+wymagana jest konwersja liczb całkowitych w regule). W końcu kropka powoduje wyświetlenie zwiastuna programu, który po prostu wysyła akumulator i zamyka się z %%parą oznaczającą koniec programu lex wyjścia.

Uwaga: Ani główny program transpilatora, ani program wyjściowy nie zostaną zakończone. Pipingowanie wejścia działałoby lub dostarczanie EOF (ctrl-D). Jeśli zakończenie jest wymagane po pierwszym wejściu, można dodać exit () s.

Aby zbudować / uruchomić:

Build the main program:
% lex -o wordmath.yy.c wordmath.l
% cc -o wordmath wordmath.yy.c -ll

Execute to create a specific transpiled program:
% echo "This is a number, add 8, subtract 5, repeat." | ./wordmath > program.l

Build the transpiled program:
% lex -o program.yy.c program.l
% cc -o program program.yy.c -ll

Execute the transpiled program (with input 3, called via a pipe or inline):
% echo 3 | ./program
1
% ./program
3
1
^D
%

Test 1:

%%
.* {int n=atoi(yytext);printf("%d",n);}
%%

Test 2:

%%
.* {int n=atoi(yytext);n/=5;n=-n+9;printf("%d",n);}
%%

Test 3:

%%
.* {int n=atoi(yytext);n+=5;n+=10;n*=2;n-=15;n-=15;n/=2;printf("%d",n);}
%%

Test 4:

%%
.* {int n=atoi(yytext);n-=1;n-=1;n-=1;printf("%d",n);}
%%
ryounce
źródło
2

Pyth, 69 67 bajtów

J\QVtcQ\,Iq@N1\r=NZ)=Jjd+@"+-/*"x"asdm"@N1.>,J-ecN)\.qh@cN)1\f=ZN)J

Program, który pobiera dane wejściowe "quoted string"i wypisuje wynik.

Zestaw testowy

Jak to działa

Pyth ma operatory przedrostkowe, więc podstawowe operacje arytmetyczne są wykonywane przy użyciu (operator)(operand1)(operand2), podczas gdy wstępnie zainicjalizowana zmienna Qpodaje dane wejściowe. Dlatego transpilowany program WordMath jest konstruowany, zaczynając od łańcucha 'Q'i na każdym etapie, przygotowując operatora, a następnie przygotowując lub dołączając operand, jeśli jest to konieczne.

J\Q Zestaw J transpilowany ciąg programu na ciąg'Q'

tcQ\, Podziel dane wejściowe przecinkami i odrzuć pierwszy element (czyli „Think of a number' )

V W Ntym:

  • Iq@N1\r Jeśli znak at N[1]to'r' (powtórz):
    • =NZUstaw Nna Z(poprzednia wartość N, ustawiona na końcu pętli for)
  • x"asdm"@N1 Znajdź indeks N[1]w"asdm" (dodawanie, odejmowanie, dzielenie, mnożenie)
  • @"+-/*" Zindeksuj to "+-/*", podając wymaganego operatora
  • ,J-eCN)\.Daje listę dwuelementową [J, -eCN)\.], w której drugi element jest ostatnim elementem Npodziału na białe znaki z '.'usuniętymi dowolnymi znakami (operand)
  • qh@cN)1\f Jeśli pierwszym znakiem drugiego elementu Npodziału na białych znakach jest 'f'(odejmij od):
    • .> Zamień elementy listy dwuelementowej
  • + Scal listę operatorów i listę dwóch elementów w jedną listę
  • =Jjd Ustaw Jto połączone na spacje
  • =ZN Ustaw ZnaN

J Wydrukować J

TheBikingViking
źródło
Fajna odpowiedź, człowieku ... Zainspirował mnie do spróbowania w 05AB1E, co ... Było bardziej zastraszające niż się spodziewałem.
Magic Octopus Urn
2

Pypeć , 58 bajtów

Szkoda, że ​​nie wdrożyłem jeszcze operatora odwrotnego odejmowania.

{p:a:sNa?ap['Y("-y+  y- y// y+ y* "^sa@?Y`\d`)|'qa@y]}Mq^k

Program pobiera skrypt WordMath ze standardowego wejścia i wysyła kod Pip na standardowe wyjście. Podobnie kod wyjściowy pobiera liczbę ze standardowego wejścia i przekazuje wynik na standardowe wyjście. Wypróbuj online!

Strategia

Dla takich danych wejściowych:

Think of a number, multiply by 3, add 1.

chcemy, aby dane wyjściowe wyglądały tak:

YqYy*3Yy+1

który działa w następujący sposób:

Yq    Yank a line of stdin into y
Yy*3  Compute y*3 and yank result into y
Yy+1  Compute y+1 and yank result into y
      Last expression is autoprinted

Niegolfowane + wyjaśnienie

{
 p : a : sNa ? a p
 [
  'Y
  ("-y+  y- y// y+ y* "^s a@?Y`\d`) | 'q
  a@y
 ]
} M q^k

Podstawową strukturą programu jest {...}Mq^kpodział q(linia standardowego) na k(przecinek-spacja) iM zastosowanie funkcji do każdego elementu.

Wewnątrz funkcji zaczynamy od obsługi repeatskrzynki. Najkrótszy test w Pipie wydaje się być sNa(czy w poleceniu jest spacja). Jeśli tak, chcemy użyć a; jeśli nie, użyj p, która przechowuje poprzednie polecenie. Przypisz tę wartość z powrotem do, aa także dop (na następny raz).

Do naszej wartości zwracanej używamy listy, co jest w porządku, ponieważ domyślnym formatem wyjściowym list jest konkatenacja wszystkiego razem. Wynik zawsze zaczyna się od Y. Następnie potrzebujemy tabeli przeglądowej dla operacji.

Zauważ, że długości add (4), subtract (9), divide by (10), multiply by (12) i subtract from (14) są różne. Ponadto zauważ, że nadal są one odrębne, gdy weźmiesz mod 7. Zatem możemy ich użyć do indeksowania do listy siedmiu elementów (zawierającej pięć fragmentów kodu i dwa symbole zastępcze), aby zamapować każdą komendę WordMath na odpowiedni kod Pip (zaprojektowany tak, aby liczba można po prostu połączyć do końca):

  • 0: -y+( subtract from)
  • 1: symbol zastępczy
  • 2: y-( subtract)
  • 3: y//( divide by)
  • 4: y+( add)
  • 5: y*( multiply by)
  • 6: symbol zastępczy

Dla indeksów, używamy regex, aby uzyskać indeks pierwszej cyfry polecenia: a@?`\d`. Wciągamy również wyrażenie regularne do ywykorzystania w przyszłości. W tabeli odnośników są generowane przez dzielenie ciąg "-y+ y- y// y+ y* "nas (spację).

Nadal musimy obsłużyć pierwszy wpis, który powinien przełożyć się na kod Yq. OdThink of a number nie zawiera żadnych cyfr, @?operator zwraca zero. Użycie wartości zero jako indeksu w tabeli odnośników również zwraca wartość zero. Brak jest fałszem, więc wszystko, co musimy zrobić, to dodać |'qdo użycia qzamiast operacji w tym przypadku.

Ostatnim elementem zwracanej listy jest sama liczba. Otrzymujemy to poprzez a@y(znajdź wszystkie dopasowania w poleceniu wyrażenia regularnego, który wcześniej szarpaliśmy). Zwraca to listę cyfr, ale znowu nie stanowi to problemu, ponieważ wszystkie listy zostaną połączone podczas wyjścia. Dla pierwszego wpisu a@ynie pasuje do żadnych cyfr i daje pustą listę, która nie dodaje niczego do wyniku.

Na przykład

Z wejściem

Think of a number, subtract from 20, add 2, repeat.

wyrażenie mapy daje listę

[["Y";"q";[]]; ["Y";"-y+";[2;0]]; ["Y";"y+";[2]]; ["Y";"y+";[2]]]

które po połączeniu zostają wydane

YqY-y+20Yy+2Yy+2
DLosc
źródło
2

Python 2 , 154 153 146 bajtów

Naprawiono, a nawet zapisano kilka bajtów. ^ __ ^

for c in input()[9:-1].split(","):s=c.rfind(" ");p=o=s and"x="+"-x+ input() x- x// x+ x*".split()[s%7]+c[s:]*(c[0]<"a")or p;print o
print"print x"

Wypróbuj online!

Oparty na tej samej strategii, co moja odpowiedź Pip . Funkcje specyficzne dla języka Python:

  • Think of a zamknięcie .jest usuwane z łańcucha przed podziałem ( input()[9:-1]). Okres był zbyt nieznośny, aby poradzić sobie w głównej pętli. Usunięcie pierwszych dziewięciu znaków pomaga z innego powodu (patrz poniżej).
  • Zamiast uzyskiwać długość każdego polecenia przez wyszukiwanie wyrażeń regularnych (drogie w Pythonie, ponieważ import re), używamy rfind(" ")do znalezienia ostatniej przestrzeni w poleceniu. Możemy również użyć tego do sprawdzenia repeatsprawy.
  • Python nie ma cyklicznego indeksowania Pip, więc musimy wyraźnie przyjąć indeks mod 7. Z drugiej strony oznacza to, że możemy usunąć ostatnią wartość fikcyjną z tabeli odnośników, ponieważ indeks mod 7 to nigdy 6.
  • „Poleceniem” po raz pierwszy jest a number, w którym znajduje się indeks przestrzeni 1. Ten indeks dogodnie wypełnia drugą dziurę w tabeli odnośników. Innym problemem związanym z przetwarzaniem stopnia wejściowego w głównej pętli była +c[s:]część, w wyniku której x=input() number. Aby rozwiązać ten problem, mnożymy ciąg znaków przez c[0]<"a": 1dla wszystkich zwykłych poleceń, w których czaczyna się spacją, ale 0dla początkowego a number.
DLosc
źródło
1

WinDbg, 449 388 bajtów

as, }@$t1
as. }0;?@$t0
asThink n10;ed8<<22;r$t0=dwo(8<<22);r$t1=0;.do{
aSa " "
asQ .printf";r$t1=1;.do{r$t1=@$t1-1;r$t0=@$t0
as/c add Q +"
aSby " "
as/c divide Q /"
asfrom 0;r$t0=-@$t0+
as/c multiply Q *"
aSnumber " "
aSof " "
asrepeat +1
as/c subtract Q -"
.for(r$t9=1;by(@$t0);r$t0=@$t0+1){j44!=by(@$t0) .printf"%c",by(@$t0);.if116!=by(@$t0-1){.printf" , "}};.printf"\b ."

-61 bajtów poprzez zdefiniowanie aliasu dla powtarzającego się kodu

Zainspirowany LambdaBeta za stosowania #define. To podejście nieznacznie modyfikuje składnię WordMath (, i .musi być oddzielone spacjami, jak inne słowa, i ,nie następujerepeat ), i tworzy alias taki, że zmodyfikowana składnia WordMath jest poprawnym kodem WinDbg. Ostatni wiersz robi to, o co pyta i transponuje, konwertując dane wejściowe na zmodyfikowaną składnię.

Wprowadzanie odbywa się poprzez ustawienie ciągu pod adresem pamięci i ustawienie pseudorejestru $t0na ten adres. Uwaga: spowoduje to zastąpienieint at 0x2000000, więc jeśli zaczniesz tam swój ciąg, zostanie on częściowo nadpisany. $t0zostaną również zastąpione.

Ponieważ tworzy aliasy, w zależności od tego, czy ten kod działał przed czy po ustawieniu ciągu znaków, kod wyjściowy będzie inny (alias lub nie). Niestety nie znalazłem sposobu, aby aliasy były poprawnie rozwijane bez ograniczania białych znaków (co oznacza, że ​​skrypt WordMath nie mógł zostać wykonany bezpośrednio bez uprzedniej transformacji).

Jak to działa:

* $t1 is used for repeating and $t0 is used to read the input and hold the accumulator
* Alias , to }@$t1 -- closing do-while loop and allowing repeat
as , }@$t1

* Alias . to }0;?@$t0 -- close do-while loop and evaluate $t0 (accumulator)
as . }0;?@$t0

* Alias Think to (note this is one line)
as Think n10;               * Set base 10
         ed 8<<22;          * Read ints to address 0x2000000. Enter nothing to exit input mode
         r$t0 = dwo(8<<22); * Set $t0 = first int
         r$t1=0;.do{        * Open do-while

* Alias a to nothing
aS a " "

* Alias add to (note one line):
as add ;                       * Close previous statement
       r$t1=1;.do{r$t1=@$t1-1; * Open do-while (once) loop
       r$t0=@$t0+              * Add number to $t0

* Alias by to nothing
aS by " "

* Alias divide to (note one line):
as divide ;                       * Close previous statement
          r$t1=1;.do{r$t1=@$t1-1; * Open do-while (once) loop
          r$t0=@$t0/              * Divide next number from $t0

* Alias from to (note one line):
as from 0;         * Preceding subtract statement subtracts 0
       r$t0=-@$t0+ * Subtract $t0 from next number

* Alias multiply to (note one line):
as multiply ;                       * Close previous statement
            r$t1=1;.do{r$t1=@$t1-1; * Open do-while (once) loop
            r$t0=@$t0*              * Multiply next number with $t0

* Alias number to nothing
aS number " "

* Alias of to nothing
aS of " "

* Alias repeat to +1 making do-while (once) loops into do-while (once)+1
as repeat +1

* Alias subtract to (note one line):
as subtract ;                       * Close previous statement
            r$t1=1;.do{r$t1=@$t1-1; * Open do-while (once) loop
            r$t0=@$t0-              * Subtract next number from $t0


.for (r$t9=1; by(@$t0); r$t0=@$t0+1) * Enumerate the string
{
    j 44!=by(@$t0)                   * If not comma
        .printf "%c",by(@$t0);       * Print the char
    * implicit else
        .if 116!=by(@$t0-1)          * Else if the previous char is not t
        {
          .printf " , "              * Print the comma with spaces around it
        }
};
.printf "\b ."                       * Replacing ending "." with " ."

Przykładowe dane wyjściowe, wprowadzanie ciągu znaków przed jednokrotnym uruchomieniem tego kodu (wynikowy program przypomina WordMath):

0:000> r$t0=8<<22
0:000> eza8<<22"Think of a number, add 5, add 10, multiply by 2, subtract 15, repeat, divide by 2."
0:000> as, }@$t1
0:000> as. }0;?@$t0
0:000> asThink n10;ed8<<22;r$t0=dwo(8<<22);r$t1=0;.do{
0:000> aSa " "
0:000> asadd ;r$t1=1;.do{r$t1=@$t1-1;r$t0=@$t0+
0:000> aSby " "
0:000> asdivide ;r$t1=1;.do{r$t1=@$t1-1;r$t0=@$t0/
0:000> asfrom 0;r$t0=-@$t0+
0:000> asmultiply ;r$t1=1;.do{r$t1=@$t1-1;r$t0=@$t0*
0:000> aSnumber " "
0:000> aSof " "
0:000> asrepeat +1
0:000> assubtract ;r$t1=1;.do{r$t1=@$t1-1;r$t0=@$t0-
0:000> .for(r$t9=1;by(@$t0);r$t0=@$t0+1){j44!=by(@$t0) .printf"%c",by(@$t0);.if116!=by(@$t0-1){.printf" , "}};.printf"\b ."
Think of a number ,  add 5 ,  add 10 ,  multiply by 2 ,  subtract 15 ,  repeat divide by 2 }0;?@$t0

0:000> Think of a number ,  add 5 ,  add 10 ,  multiply by 2 ,  subtract 15 ,  repeat divide by 2 }0;?@$t0
base is 10
02000000 6e696854 18
18
02000004 666f206b 

Evaluate expression: 18 = 00000012

Przykładowe dane wyjściowe, wprowadzanie ciągu po jednorazowym uruchomieniu tego kodu (aliasy są rozszerzane podczas wprowadzania ciągu, więc wynikowy program nie jest tak ładny):

0:000> r$t0=8<<22
0:000> eza8<<22"Think of a number, add 5, add 10, multiply by 2, subtract 15, repeat, divide by 2."
0:000> as, }@$t1
0:000> as. }0;?@$t0
0:000> asThink n10;ed8<<22;r$t0=dwo(8<<22);r$t1=0;.do{
0:000> aSa " "
0:000> asadd ;r$t1=1;.do{r$t1=@$t1-1;r$t0=@$t0+
0:000> aSby " "
0:000> asdivide ;r$t1=1;.do{r$t1=@$t1-1;r$t0=@$t0/
0:000> asfrom 0;r$t0=-@$t0+
0:000> asmultiply ;r$t1=1;.do{r$t1=@$t1-1;r$t0=@$t0*
0:000> aSnumber " "
0:000> aSof " "
0:000> asrepeat +1
0:000> assubtract ;r$t1=1;.do{r$t1=@$t1-1;r$t0=@$t0-
0:000> .for(r$t9=1;by(@$t0);r$t0=@$t0+1){j44!=by(@$t0) .printf"%c",by(@$t0);.if116!=by(@$t0-1){.printf" , "}};.printf"\b ."
n10;ed8<<22;r$t0=dwo(8<<22);r$t1=0;.do{     number ,  ;r$t1=1;.do{r$t1=@$t1-1;r$t0=@$t0+ 5 ,  ;r$t1=1;.do{r$t1=@$t1-1;r$t0=@$t0+ 10 ,  ;r$t1=1;.do{r$t1=@$t1-1;r$t0=@$t0*   2 ,  ;r$t1=1;.do{r$t1=@$t1-1;r$t0=@$t0- 15 ,  repeat ;r$t1=1;.do{r$t1=@$t1-1;r$t0=@$t0/   2 }0;?@$t0

0:000> n10;ed8<<22;r$t0=dwo(8<<22);r$t1=0;.do{     number ,  ;r$t1=1;.do{r$t1=@$t1-1;r$t0=@$t0+ 5 ,  ;r$t1=1;.do{r$t1=@$t1-1;r$t0=@$t0+ 10 ,  ;r$t1=1;.do{r$t1=@$t1-1;r$t0=@$t0*   2 ,  ;r$t1=1;.do{r$t1=@$t1-1;r$t0=@$t0- 15 ,  repeat ;r$t1=1;.do{r$t1=@$t1-1;r$t0=@$t0/   2 }0;?@$t0
base is 10
02000000 3b30316e 26
26
02000004 3c386465 

Evaluate expression: 26 = 0000001a

Jeszcze więcej przykładowych danych wyjściowych, używając tylko nieznacznie zmodyfikowanej składni WordMath:

0:000> Think of a number , add 1 , repeat repeat repeat divide by 3 .
base is 10
02000000 0000001a 3
3
02000004 3c386465 

Evaluate expression: 2 = 00000002


0:000> Think of a number , divide by 5 , subtract from 9 .
base is 10
02000000 00000003 29
29
02000004 3c386465 

Evaluate expression: 4 = 00000004
mleko
źródło
0

Scala, 338 bajtów

Wypróbuj sam w ideone

s=>{val n=(_:String)filter(_.isDigit)toInt;(Seq("").tail/:s.split(",").tail)((a,&)=> &match{case&if&contains "v"=>a:+"x/="+n(&)
case&if&contains "d"=>a:+"x+="+n(&)
case&if&contains "y"=>a:+"x*="+n(&)
case&if&contains "f"=>a:+"x="+n(&)+"-x"
case&if&contains "s"=>a:+"x-="+n(&)
case p=>a:+a.last})mkString("var x=readInt;",";",";print(x)")}

Wyjaśnienie:

// define a function with a parameter s
s => {
    // define a function with a String parameter to extract a number from a string
    val n =
        // filter out the chars that arent't digits
        (_: String) filter (_.isDigit)
        // and parse the number
        toInt;
    // take the tail of a list with an empty string,
    // which is the empty list of type Seq[String].
    // This is the start value for the fold...
    (Seq("").tail /:
        // ... of the tail of the sentence splitted at commas
        s.split(",").tail
    ) (
        // This is the function for the fold.
        // a is the accumulator (the list), and the current element is called &
        (a, &) => & match {
            // if & contains a "v", append "x/=" + n(&) to a.
            case & if & contains "v" => a :+ "x/=" + n(&)
            // the other cases are similar
            case & if & contains "d" => a :+ "x+=" + n(&)
            case & if & contains "y" => a :+ "x*=" + n(&)
            case & if & contains "f" => a :+ "x=" + n(&) + "-x"
            case & if & contains "s" => a :+ "x-=" + n(&)
            // for the repeat, apppend the last value of a to a
            case p                   => a :+ a.last
        }
     )
     // make a string out of the parts by joining them with semicolons,
     // prepending "var x=readInt;" and appending ";print(x)"
     mkString("var x=readInt;", ";", ";print(x)")
}
corvus_192
źródło