Transpile WordMath

25

Wir haben alle diese Online "Mathe-Hax" gesehen, die so aussehen:

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

Und durch Zauberei endet jeder mit der Nummer 8!


Sprache

Definieren wir eine Programmiersprache, die die Syntax des obigen Textes "WordMath" verwendet. WordMath-Skripte folgen dieser Vorlage:

Think of a number, <commandlist>.

Was im Grunde bedeutet: Nimm eine Zahl (als Eingabe von STDIN) als anfänglichen Akkumulator, führe alle Befehle darauf aus und gib das Ergebnis aus.

Die Befehle werden durch das Trennzeichen ,(Komma + Leerzeichen) getrennt. Die gültigen Befehle sind (Anmerkung, #die eine nicht negative Ganzzahl darstellt :) :

  • add #/ subtract #- Addiere / subtrahiere den Wert vom Akkumulator.
  • divide by #/ multiply by #- floordiv / multipliziere den Akku mit dem angegebenen Wert.
  • subtract from #- Ähnlich wie subtract, aber acc = # - accstattdessenacc = acc - #
  • repeat- Wiederholen Sie den letzten Befehl. Dies kann nicht der erste Befehl sein, Sie müssen jedoch mehrere aufeinanderfolgende Wiederholungen unterstützen.

Die Herausforderung

Ihre Aufgabe ist es, ein Programm oder eine Funktion zu erstellen , die eine gültige WordMath Skript als Eingabe und transpiles es in ein gültiges volles Programm - in der Sprache des Code ist in.

Wenn sich mein Code beispielsweise in Python 2 befindet und das Skript wie folgt lautet:

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

Das ausgegebene Programm kann sein:

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

Oder alternativ:

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

Solange es sich um ein vollständiges Programm handelt, das Eingaben von STDINoder Ausdrucke an STDOUTdie nächsten Entsprechungen der Sprache vornimmt.


Regeln

  • Ihr ursprüngliches Programm geht möglicherweise davon aus, dass die Eingabe immer ein gültiges WordMath-Skript ist.
  • Die transpilierten Programme müssen nicht mit mathematischen Fehlern wie der Division durch 0 umgehen.
  • Die transpilierten Programme können davon ausgehen, dass die Eingabe eine gültige Ganzzahl mit Vorzeichen innerhalb des Standard-Ganzzahlbereichs Ihrer Sprache darstellt.
  • Das ist , also gewinnt die kürzeste Lösung (in Bytes).
  • Nur die Anzahl der Bytes Ihres ursprünglichen Programms ist von Bedeutung - der ausgegebene Code kann so lang sein, wie Sie möchten!

Beispielskripte

Beispiel 1:

Think of a number. 

Nehmen Sie Eingaben, tun Sie nichts, zeigen Sie sie an: WordMaths Katzenprogramm.

Beispiel 2:

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

Denken Sie daran, dass "Teilen" Bodenteilung ist, also für dieses Programm 6 -> 8und 29 -> 4.

Beispiel 3:

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

Das erweiterte Katzenprogramm!

Beispiel 4:

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

Nimmt eine Zahl und subtrahiert 3.

FlipTack
quelle
Müssen wir aufeinanderfolgende Wiederholungen unterstützen?
Darrylyeo
1
Können wir Floats verwenden, wenn dies der Standardtyp der Sprache ist / wenn es keine Ganzzahlen unterstützt?
Rainer P.
@RainerP. Nur wenn die Sprache keine Ganzzahlen / Ganzzahl-Division unterstützt
FlipTack
1
Was ist das erwartete Ergebnis von -5/3? Runden wir gegen 0oder gegen die negative Unendlichkeit?
Martin Ender
1
@MartinEnder würde ich um sagen zu negativer Unendlichkeit , wie es des Bodens Teilung, aber wenn Sie Ihre Sprache implementiert Integer - Division auf 0 , das auch in Ordnung ist.
FlipTack

Antworten:

6

05AB1E , 59 56 54 52 Bytes

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

Probieren Sie es online!

Mein Gehirn tut danach höllisch weh ... Es wird im 05AB1E-Code wie folgt ausgegeben:

  • Think of a Number wird aufgrund impliziter Eingaben entfernt.
  • Subtract From #verdeckt zu #s-(tauschena und bund führen Sie die Operation).
  • Subtract # konvertiert zu #- .
  • Add # konvertiert zu #+ .
  • Multiply by # konvertiert zu #* .
  • Divide by # konvertiert zu #/ .
  • Repeat greift nach dem zuletzt im Register gespeicherten und verkettet es.

Erklärt:

„, ¡                                                 # 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.

Beispiel:

Eingang:

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

Ausgabe:

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

Probiere Lösung mit Eingabe von 10:

Probieren Sie es online!

Sehen Sie es auf Google:

Hier ist ein Link zu derselben Gleichung, die in Google eingegeben wurde.

Magische Kraken-Urne
quelle
13

C Präprozessor, 362 Bytes

Ich habe das fast nur im C-Präprozessor zum Laufen gebracht, aber der Wiederholungsbefehl erweist sich als viel zu schwierig zu implementieren. Stattdessen habe ich den Präprozessor verwendet, um die Eingabe in ein Array umzuwandeln, das dann von zusätzlichem Code interpretiert wird.

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",_);}

Die Eingabe muss in "input.wm" bereitgestellt oder einfach in der Quelle in dieser Zeile gespeichert werden. Ich habe seine Bytes in meine Zählung aufgenommen, weil ich finde, dass es ein bisschen hackig ist und leicht gegen die Regeln der Herausforderung verstößt, daher ist es nur passend.

Sobald Sie Ihre WordMath-Quelle in input.wm abgelegt haben, wo ein Compiler sie finden kann, sollten Sie dies mit Warnungen kompilieren können, um eine ausführbare Datei zu erstellen, die das tut, was die WordMath-Quelle sagt.

LambdaBeta
quelle
2
Hinweis: Bei einigen Compilern schlägt dies leider fehl, wenn Sie mit repeat enden. Dies liegt daran, dass sie ein Leerzeichen nach der 0 werfen und dann eine Streuperiode sehen und nicht wissen, was sie damit machen sollen.
LambdaBeta
klug, ich bin beeindruckt.
Katze
7

Netzhaut, 170 Bytes

Denn wer möchte das nicht sehen ?!

Ich dachte daran, wie großartig es wäre, eine Retina-Lösung zu sehen, und beschloss, sie schnell zu erstellen. Es dauerte nur eine Stunde. Die Anzahl der Bytes setzt wie üblich die Kodierung nach ISO 8859-1 voraus.

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

Probieren Sie es online aus

Die Ausgabe enthält eine nachgestellte Newline, die beim Testen des resultierenden Programms nicht kopiert werden sollte. Das Programm unterstützt keine Negative, weil Retina ganzzahliger Standardbereich (in Unary) dies nicht unterstützt.

Erläuterung:

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

Mathe-Programme:

Hinzufügen:

Fügen Sie die Anzahl der Einsen zum Anfang hinzu. 5 hinzufügen:

^
1111

Subtrahieren:

Entfernen Sie die Anzahl von Einsen vom Anfang. Subtrahiere 5:

^11111

Subtrahieren von:

Ersetzen Sie die Eingabe 1s durch xs. Neben die feste Zahl setzen. Mehrmals entfernen x1. Subtrahieren von 10:

1
x
$
1111111111
+`x1

Mal:

Ersetzen Sie jeden 1durch eine bestimmte Anzahl von ihnen. Mit 3 multiplizieren:

1
111

Teilen durch:

Dies verwendet mein Retina-Programm für die Ganzzahldivision . Teilen Sie durch 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
quelle
Ich fürchte, ich sehe nicht, wie das funktionieren kann. Unabhängig davon, welche Eingaben ich für die Subtraktionsbefehle versuche, erhalte ich fehlerhafte Ergebnisse (fehlen Zeilenvorschübe in der Ausgabe?). Ich verstehe auch nicht, wie dies mit negativen Eingaben oder negativen Zwischenergebnissen umgeht.
Martin Ender
@MartinEnder Ich kann die Subtraktion korrigieren, wenn Sie erklären, warum dieses vereinfachte Programm zwei in der Ausgabe enthält. retina.tryitonline.net/#code=JArCtjE&input=dGVzdAo
mbomb007
Da $passt ganz am Ende der Zeichenfolge oder vor einem nachgestellten Zeilenumbruch. Sie brauchen, \zwenn Sie nur die ersteren wollen.
Martin Ender
4

GNU awk, 139 Bytes

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

Aufruf:

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

Testfälle:

$> 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.
quelle
4

Haskell, 232 231 Bytes

Natürlich würde ein funktionaler Programmierer es vorziehen, eine Funktion anstelle eines Strings zurückzugeben, der ein Programm darstellt, aber hier ist es:

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

Anmerkungen: Wir beginnen immer mit dem Hinzufügen von Null, da sonst die Transpilation des trivialen WordMath-Programms nicht genügend Informationen liefert, um auf den verwendeten Typ schließen zu readkönnen. subtract from nkönnte als implementiert werden (n-), aber ich benutze ((-)n)für mehr einheitlichkeit. Wenn subtract nich das subtractvon der Eingabe kopiere , muss ich es nicht schreiben, sondern muss den fehlenden Platz am Ende ausgleichen.repeatwird als Standardoperation verwendet; zusammen mit einer leeren anfänglichen vorherigen Operation ermöglicht dies das einfache Ignorieren der ersten vier Wörter.

Anwendungsbeispiel:

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

Die anderen Beispiele ergeben die folgenden Ergebnisse:

"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
quelle
Nur neugierig, wie würden Sie eine Funktion generieren, die anstelle eines Strings zurückgegeben wird?
Cyoce
In einer funktionalen Programmiersprache ist das Erstellen und Komponieren einer Funktion nicht schwieriger als das Erstellen und Anhängen einer Zeichenfolge. hVielleicht sieht so etwas aus h s n r|x<-s.read.init$n=x%r.xund wird mit dem ersten Argument eine Funktion genannt wie h(+)n r(und es muss flipirgendwo welche geben, um die richtige Operatorreihenfolge zu erhalten), Basisfall ist _%_=id. Hauptfunktion kann das ganze Boilerplate vermeiden und gerade sein t l=id%words l. - Dank des Lehrens kann es als Dolmetscher gesehen werden, und diese Idee kann zu einer einfacheren und / oder kürzeren Lösung führen.
Christian Sievers
4

Python 2, 263 258 260 221 Bytes

Dies könnte wahrscheinlich noch viel kürzer sein.

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(','))

Probieren Sie es online aus

Ich benutze //stattdessen /, weil die letzte Anweisung ein .am Ende hat und jede Zahl zu einem float macht. Um die Division konsistent zu halten, verwende ich die Ganzzahldivision.

Testfallausgabe:

print input()
print 9.-((input())//5)
print ((((((input())+5)+10)+10)-15)-15)//2.
print (((input())-1)-1)-1
mbomb007
quelle
Wenn Sie diesen großen Block von ifs owie folgt ändern (was meiner Meinung nach funktionieren sollte): o=[[o+[['+-'['s'in c],'//']['v'in c],'*']['m'in c]+n,n+'-'+o]['f'in c],'input()']['T'in c]Sie können es auf 224 bringen.
Kade
@Kade Ja, es war immer noch lesbar. Kann das nicht haben.
mbomb007
@Cyoce Nein, der Anruf beim Lambda würde wahrscheinlich mehr kosten, als es spart. Es müssten 4 oder 5 Bytes pro Anruf eingespart werden, um sich auszuzahlen.
mbomb007
4

Befunge, 342 305 Bytes

>~$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

Probieren Sie es online!

Ausgabe

Der erzeugte Code beginnt mit einem &Befehl (Eingabewert) und endet mit Befehlen .(Ausgabewert) und @(Beenden). Dazwischen haben wir die verschiedenen Berechnungen in der Form <number><operation>, wo die Operation stattfindet sein kann +(add), -(subtrahieren), /(divide by), *(multiplizieren) und \-(subtrahieren).

Die Zahl selbst ist etwas kompliziert, da Befunge nur numerische Literale im Bereich von 0 bis 9 unterstützt, sodass alles, was größer ist, manuell berechnet werden muss. Da wir die Zahlen bereits zeichenweise lesen, bauen wir die Zahl einfach auf, während jede Ziffer gelesen wird, sodass beispielsweise 123 wird155+*2+55+*3+ , d (((1 * 10) + 2) * 10) + 3. H.

Beispiele

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-.@

Erläuterung

Befunge ist nicht in der Lage, Zeichenfolgen als solche zu manipulieren. Daher wird der größte Teil des Parsings durch das Zählen von Zeichen erledigt. Wir beginnen einfach mit dem Überspringen der ersten 18 Zeichen, wodurch wir den Gedanken an eine Zahlenphrase (plus Komma oder Punkt) hinter uns lassen . Wenn das nächste Zeichen eine Form von Newline oder EOF ist, gehen wir direkt zur Ausgaberoutine, ansonsten suchen wir weiter nach einer Befehlsliste.

Um einen Befehl zu analysieren, zählen wir nur die Zeichen, bis wir eine Ziffer oder ein Trennzeichen erreichen. Wenn es sich um ein Trennzeichen handelt, muss es sich um den Wiederholungsbefehl handeln, den wir als Sonderfall behandeln. Wenn es sich um eine Ziffer handelt, fügen wir sie unserem Ausgabepuffer hinzu und suchen weiter nach weiteren Ziffern. Jedes Mal, wenn eine Ziffer ausgegeben wird, wird ihr ein Präfix vorangestellt55+* (um die bisherige Summe mit 10 zu multiplizieren) und mit einem Suffix versehen+ (um sie zur Summe hinzuzufügen) versehen. Sobald die Ziffern fertig sind, fügen wir das Befehlszeichen hinzu.

Was die Bestimmung des Befehls betrifft, nehmen wir die Anzahl der Zeichen bis zur ersten Ziffer Modulo 7. Für Addition ist dies 4 (einschließlich des folgenden Leerzeichens), für Subtraktion ist es 2, für Division durch 3, für Multiplikation durch 5 und für subtrahieren von es 0. Das Subtrahieren von erfordert ein wenig zusätzliche Behandlung, da es das benötigt\- Befehlskombination , aber die anderen verwenden nur ihren Wert, um das entsprechende Befehlszeichen in einer Tabelle zu suchen.

Dieser Vorgang wird für jeden Befehl wiederholt, wobei die Ausgabe in einer vorkonstruierten Zeichenfolge in Zeile 8 aufgebaut wird. Jedes Mal, wenn ein zusätzlicher Befehl hinzugefügt wird, wird der Zeichenfolge ein schließendes Anführungszeichen hinzugefügt, um sicherzustellen, dass sie immer ordnungsgemäß terminiert ist. Wenn wir schließlich das Ende unserer Eingabe erreichen, "führen" wir einfach diesen String aus, um ihn auf den Stapel zu schieben, und folgen ihm mit einer Standardausgabesequenz, um alles auszuschreiben.

James Holderness
quelle
3

JavaScript (ES6), 163 Byte

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)`

Versuch es:

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
quelle
3

Vim 208 171 168 Bytes

Es wurde die Möglichkeit hinzugefügt, mehrere Wiederholungen nacheinander gemäß @ Flp.Tkc auszuführen, aber es wurden genügend Bytes abgelegt, sodass ich die Byteanzahl immer noch senken konnte.

: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

Nicht druckbare Zeichen:

: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^[^[

Testfallausgabe:

  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
quelle
Dies scheint nicht für mehrere aufeinanderfolgende Wiederholungen zu funktionieren.
FlipTack
@Flp.tkc behoben, danke! Das habe ich früher nicht bemerkt.
nmjcman101
2

Lex, 246 Bytes

%{
#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%%%%");
. ;
%%

Lex zielt auf C ab, daher müsste ein C-Compiler es in eine ausführbare Datei kompilieren. Die Lexer-Bibliothek (ll ) müsste ebenfalls verlinkt werden. Dies kann eine Byte-Strafe hinzufügen, aber ich bin nicht sicher, wie viele Bytes, wenn ja.

Das Programm gibt ein Lex-Programm (gemäß Spezifikation) aus, das den transpilierten Wordmath-Ausdruck auswertet. Der Code zwischen %{und %}ist nur für den "Transpiler":

#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 */

Zwischen den beiden %%Zeilen befindet sich der Regex / Action-Teil. Die erste Regel, die gefunden wird, ist T("Think ..."), die die Präambel erstellt (Lex-Programme müssen mindestens den Regelabschnitt enthalten, undyytext letzte übereinstimmende Text sein, sodass die Regel den Akkumulator im Wesentlichen mit den Eingaben des Benutzers verbindet ).

Das Programm verwirft alle Eingaben mit Ausnahme derjenigen , die abgestimmt ist, und die anderen Regeln ( ad, frbis zu re) behandeln die wordmath Ausdruck Klauseln als minimal ein Spiel wie möglich eindeutig zu sein. In den meisten diese, setzt sich cauf einen Ausdruck Infix, die zwischen konkateniert wird nund die letzte Ganzzahl gelesen , wenn Oaufgerufen wird (so beispielsweise das Lesen „add 9“ wird die Infix einzustellen +=, v 9, und der Anruf an OWillen Ausgang n+=9;) . (Interessanterweise führt "subtrahieren von 8" dazu, dass sowohl die Regeln sals auch die frRegeln übereinstimmen. Da Ojedoch nur die Zahl aufgerufen wird, ist die richtige Regel n=-n+8;der einzige Ausdruck, der ausgegeben wird.) Dasre Regel für "Wiederholen" nennt man einfachOAuch hier wird der zuletzt erstellte Ausdruck ausgegeben (und da nachfolgende Übereinstimmungen nicht mehr funktionieren yytext, war die Ganzzahlkonvertierung in der [0-9]+Regel erforderlich , da "repeat" unterstützt wird ). Schließlich bewirkt eine Periode die Ausgabe des Programm-Trailers, der nur den Akkumulator ausgibt und mit dem %%Paar schließt, das das Ende des Ausgabe-Lex-Programms bezeichnet.

Hinweis: Weder das Haupttranspilerprogramm noch das Ausgabeprogramm werden beendet. Das Leiten von Eingaben in würde funktionieren oder EOF (Strg-D) bereitstellen. Wenn nach der ersten Eingabe eine Beendigung erforderlich ist, können exit () s hinzugefügt werden.

Zum Bauen / Laufen:

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
quelle
2

Pyth, 69 67 Bytes

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

Ein Programm, das die Eingabe von a "quoted string" und das Ergebnis druckt.

Testsuite

Wie es funktioniert

Pyth hat Präfixoperatoren, daher werden die grundlegenden arithmetischen Operationen mit ausgeführt (operator)(operand1)(operand2), während die vorinitialisierte Variable Qdie Eingabe liefert. Daher wird ein transpiliertes WordMath-Programm erstellt, indem mit der Zeichenfolge begonnen 'Q'wird und in jeder Phase der Operator vorangestellt und dann der Operand nach Bedarf vorangestellt oder angehängt wird.

J\QSetzen Sie Jden transpilierten Programmstring auf den String'Q'

tcQ\, Teilen Sie die Eingabe durch Kommas und verwerfen Sie das erste Element (das ist ' Think of a number')

V Denn Ndarin:

  • Iq@N1\r Wenn das Zeichen an N[1]ist 'r'(Wiederholung):
    • =NZSetzen Nauf Z(vorheriger Wert von N, setzen am Ende der for-Schleife)
  • x"asdm"@N1 Finden Sie den Index von N[1]in"asdm" (addieren, subtrahieren, dividieren, multiplizieren)
  • @"+-/*" Indizieren Sie damit in "+-/*"und geben Sie den erforderlichen Operator an
  • ,J-eCN)\.Ergibt die Liste mit zwei Elementen [J, -eCN)\.], wobei das zweite Element das letzte Element der NAufteilung in Leerzeichen ist, wobei alle '.'Zeichen entfernt wurden (Operand).
  • qh@cN)1\f Wenn das erste Zeichen des zweiten Elements Nauf Leerzeichen aufgeteilt ist'f' (subtrahieren von):
    • .> Tauschen Sie die Elemente der Liste mit zwei Elementen aus
  • + Führen Sie den Operator und die Liste mit zwei Elementen in einer Liste zusammen
  • =Jjd einstellen J Sie zu , dass auf Räume verbunden
  • =ZN Stellen Sie ZaufN

J Drucken J

TheBikingViking
quelle
Nette Antwort Mann ... Hat mich dazu inspiriert, es in 05AB1E zu versuchen, was ... einschüchternder war als erwartet.
Magic Octopus Urn
2

Pip , 58 Bytes

Schade, dass ich diesen Rückwärtssubtraktionsoperator noch nicht implementiert habe.

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

Das Programm nimmt ein WordMath-Skript von stdin und gibt Pip-Code an stdout aus. Der ausgegebene Code nimmt auf ähnliche Weise eine Zahl von stdin und gibt das Ergebnis an stdout aus. Probieren Sie es online!

Strategie

Für Eingaben wie diese:

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

wir wollen folgendes ausgeben:

YqYy*3Yy+1

was wie folgt funktioniert:

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

Ungolfed + Erklärung

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

Die Grundstruktur des Programms ist {...}Mq^k, das sich q(eine Zeile von stdin) aufteiltkM jedes Element (Komma-Leerzeichen) aufgespalten wird und eine Funktion zugewiesen wird.

Innerhalb der Funktion beginnen wir mit der Behandlung des repeatFalls. Der kürzeste Test in Pip scheint zu sein sNa(gibt es ein Leerzeichen im Befehl). Wenn ja, möchten wir verwenden a; Wenn nicht, verwenden Sie p, um den vorherigen Befehl zu speichern. Weisen Sie diesen Wert wieder zua und auch zup (für das nächste Mal).

Für unseren Rückgabewert verwenden wir eine Liste, was in Ordnung ist, da das Standardausgabeformat für Listen darin besteht, alles miteinander zu verketten. Das Ergebnis beginnt immer mitY . Als nächstes benötigen wir eine Nachschlagetabelle für die Operationen.

Beachten Sie, dass die Längen von add (4), subtract (9), divide by (10), multiply by (12) undsubtract from (14) alle unterschiedlich sind. Beachten Sie weiterhin, dass sie in Mod 7 immer noch unterschiedlich sind. Daher können Sie sie verwenden, um eine Liste mit sieben Elementen zu indizieren (die fünf Codeausschnitte und zwei Platzhalter enthält), um jeden WordMath-Befehl dem entsprechenden Pip-Code zuzuordnen (entsprechend der Nummer) kann einfach bis zum Ende verkettet werden):

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

Für die Indizes verwenden regex wir den Index der ersten Ziffer in dem Befehl zu erhalten: a@?`\d`. Wir reißen den Regex auch yfür die zukünftige Verwendung ein. Die Nachschlagetabelle wird durch Aufteilen der Zeichenfolge "-y+ y- y// y+ y* "auf s(Leerzeichen) generiert .

Wir müssen noch den ersten Eintrag bearbeiten, der in den Code übersetzt werden soll Yq. Da Think of a numberkeine Ziffern enthalten sind, gibt der @?Operator nil zurück. Die Verwendung von nil als Index in der Nachschlagetabelle gibt ebenfalls nil zurück. Nil ist falsch, daher müssen wir für diesen Fall nur add |'qto use qanstelle einer Operation ausführen.

Das letzte Element der zurückgegebenen Liste ist die Nummer selbst. Wir erhalten dies über a@y(finden Sie alle Übereinstimmungen im Befehl der zuvor gezerrten Regex). Dies gibt eine Liste mit Ziffern zurück, aber auch dies ist kein Problem, da alle Listen bei der Ausgabe verkettet werden. Für den ersten Eintraga@y nicht mit Ziffern überein und gibt eine leere Liste aus, die der Ausgabe nichts hinzufügt.

Beispielsweise

Mit Eingabe

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

Der Kartenausdruck gibt die Liste an

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

was, wenn verkettet, ausgibt

YqY-y+20Yy+2Yy+2
DLosc
quelle
2

Python 2 , 154 153 146 Bytes

Behoben und dabei sogar mehrere Bytes gespeichert. ^ __ ^

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"

Probieren Sie es online!

Basiert auf der gleichen Strategie wie meine Pip-Antwort . Python-spezifische Funktionen:

  • Think of und das Schließen .werden vor dem Teilen von der Zeichenfolge entfernt (input()[9:-1] ) . Die Periode war zu lästig, um sie in der Hauptschleife zu verarbeiten. Das Entfernen der ersten neun Zeichen hilft aus einem anderen Grund (siehe unten).
  • Anstatt die Länge jedes Befehls durch eine reguläre Suche nach einer Ziffer zu ermitteln (in Python teuer, weil import re), wird rfind(" ")der letzte Platz im Befehl gesucht . Wir können dies auch verwenden, um nach dem zu suchenrepeat Fall .
  • Python hat keine zyklische Indizierung von Pip, daher müssen wir den Index Mod 7 explizit nehmen. Andererseits bedeutet dies, dass wir den letzten Dummy-Wert in der Nachschlagetabelle entfernen können, da der Index mod 7 niemals 6 ist.
  • Das "Kommando" ist das erste Mal durch a number, in dem sich der Index des Leerzeichens befindet 1. Dieser Index füllt bequemerweise die andere Lücke in der Nachschlagetabelle. Das andere Problem bei der Verarbeitung der Eingangsstufe in der Hauptschleife war der +c[s:]Teil, der dazu führen würde x=input() number. Um dieses Problem zu lösen, multiplizieren wir die Zeichenfolge mit c[0]<"a": 1für alle regulären Befehle, in denen cmit einem Leerzeichen begonnen wird, aber 0für die Initiale a number.
DLosc
quelle
1

WinDbg, 449 388 Bytes

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 Bytes durch Definieren eines Alias ​​für wiederholten Code

Inspiriert von LambdaBetas Einsatz von #define. Dieser Ansatz ändert die WordMath-Syntax geringfügig ( ,und .muss wie die anderen Wörter und durch Leerzeichen getrennt werden), folgt nichtrepeat ) und erstellt einen Alias, sodass die geänderte WordMath-Syntax gültiger WinDbg-Code ist. In der letzten Zeile wird die Frage beantwortet und transpiliert, indem die Eingabe in die geänderte Syntax konvertiert wird.

Die Eingabe erfolgt durch Setzen eines Strings an einer Speicheradresse und Setzen des Pseudoregisters $t0auf diese Adresse. Hinweis: Dadurch wird das intat 0x2000000überschrieben. Wenn Sie also dort mit der Eingabe beginnen, wird es teilweise überschrieben.$t0wird auch überschrieben.

Da Aliase erstellt werden, ist der Ausgabecode je nachdem, ob dieser Code vor oder nach dem Festlegen der Zeichenfolge ausgeführt wurde, unterschiedlich (entweder mit Alias ​​oder ohne Alias). Leider habe ich keine Möglichkeit gefunden, die Aliase ordnungsgemäß zu erweitern, ohne durch Leerzeichen getrennt zu sein.

Wie es funktioniert:

* $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 " ."

Beispielausgabe, Eingabe der Zeichenfolge, bevor dieser Code einmal ausgeführt wird (das resultierende Programm ähnelt 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

Beispielausgabe, Eingabe der Zeichenfolge nach einmaliger Ausführung dieses Codes (die Aliase werden bei der Eingabe der Zeichenfolge erweitert, damit das resultierende Programm nicht so schön ist):

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

Noch ein paar Beispiele, nur mit der leicht geänderten WordMath-Syntax:

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
Milch
quelle
0

Scala, 338 Bytes

Probieren Sie es selbst bei 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)")}

Erläuterung:

// 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
quelle