Transpile WordMath

25

2

We've all seen those online "maths hax" that look like this:

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

And, by magic, everyone ends up with the number 8!


Language

Let's define a programming language which uses the syntax of the text above, called "WordMath". WordMath scripts follow this template:

Think of a number, <commandlist>.

Which basically means: Take a number (as input from STDIN) as the initial accumulator, perform all commands on it, and output the result.

The commands are seperated by the delimiter , (comma + space). The valid commands are (note that # represents a non-negative integer:):

  • add # / subtract # - Add/subtract the value from the accumulator.
  • divide by # / multiply by # - floordiv / multiply the accumulator by the given value.
  • subtract from # - Similar to subtract, but does acc = # - acc instead of acc = acc - #
  • repeat - do last command again. This cannot be the 1st command, but you must support multiple consecutive repeats.

The Challenge

Your task is to create a program or function which takes a valid WordMath script as input and transpiles it into a valid full program - in the same language your code is in.

For example, if my code is in Python 2 and the script is:

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

The outputted program can be:

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

Or alternatively:

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

As long as it is a full program which takes input from STDIN and prints to STDOUT, or the language's nearest equivalents.


Rules

  • Your original program may assume that the input is always a valid WordMath script.
  • The transpiled programs do not have to handle mathematical errors such as division by 0.
  • The transpiled programs may assume that the input represents a valid signed integer, within your language's standard integer range.
  • This is , so the shortest solution (in bytes) wins.
  • Only the byte count of your original program matters - the outputted code can be as long as you want!

Example Scripts

Example 1:

Think of a number. 

Take input, do nothing, display it: WordMath's cat program.

Example 2:

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

Remember that "divide" is floor division, so for this program 6 -> 8, and 29 -> 4.

Example 3:

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

The extended cat program!

Example 4:

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

Takes a number and subtracts 3.

FlipTack

Posted 2016-12-05T19:28:21.640

Reputation: 13 242

Do we have to support consecutive repeats? – darrylyeo – 2016-12-05T21:14:46.130

1Can we use floats when that's the language's default type / if it doesn't support integers? – Rainer P. – 2016-12-05T21:57:25.037

@RainerP. only if the language doesn't support integers/integer division – FlipTack – 2016-12-05T22:12:45.630

@darrylyeo yes, I'll make that clear in the spec. – FlipTack – 2016-12-05T22:12:55.210

You might want a test case with repeat at the end, and more than one repeat in a row. – mbomb007 – 2016-12-06T21:14:30.110

@mbomb007 yeah, I'll do that now. – FlipTack – 2016-12-06T21:14:55.910

1What is the expected result of -5/3? Do we round towards 0 or towards negative infinity? – Martin Ender – 2016-12-07T21:09:11.060

1@MartinEnder I'd say round towards negative infinity as it's floor division, but if your language implements integer division towards 0 that's fine too. – FlipTack – 2016-12-07T21:10:40.110

The order of multiply and divide should be switched. I know that it's clear what you're saying. – Zacharý – 2016-12-27T19:05:40.283

Answers

6

05AB1E, 59 56 54 52 bytes

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

Try it online!

My brain hurts like hell after that... It outputs in 05AB1E code as follows:

  • Think of a Number is removed, due to implicit input.
  • Subtract From # coverts to #s- (swap a and b and perform operation).
  • Subtract # converts to #-.
  • Add # converts to #+.
  • Multiply by # converts to #*.
  • Divide by # converts to #/.
  • Repeat grabs whatever was last stored in the register and concatenates it.

Explained:

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

Example:

Input:

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

Output:

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

Try solution with input of 10:

Try it online!

See it on google:

Here's a link to the same equation typed into google.

Magic Octopus Urn

Posted 2016-12-05T19:28:21.640

Reputation: 19 422

13

C Preprocessor, 362 Bytes

I ALMOST got this working in JUST the C preprocessor, but the repeat command turns out to be far too difficult to implement. So instead I used the preprocessor to turn the input into an array that is then interpreted by some additional code.

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

The input must be provided in "input.wm" or just dumped in the source at that line. I included its bytes in my count because I figure its a bit hacky and slightly against the rules of the challenge, so it is only fitting.

Anyways, once you dump your WordMath source into input.wm where a compiler can find it, you should be able to just compile this, as is, with warnings to produce an executable that does what the WordMath source says.

LambdaBeta

Posted 2016-12-05T19:28:21.640

Reputation: 2 499

2Note: this unfortunately fails with some compilers when you end with repeat. This is because they toss a space after the 0 and then see a stray period and don't know what to do with it. – LambdaBeta – 2016-12-05T22:00:12.143

clever, i'm impressed. – cat – 2016-12-27T21:49:45.520

7

Retina, 170 bytes

Because who wouldn't want to see this?!

I thought of how awesome it'd be to see a Retina solution, and I decided to create it quick. It only took an hour. As usual, byte count assumes ISO 8859-1 encoding.

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

Try it online

Output has a trailing newline that should not be copied when testing the resulting program. The program does not support negatives, because Retina's standard integer range (in unary) does not.

Explanation:

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

Math programs:

Add:

Add the number of ones to the beginning. Add 5:

^
1111

Subtract:

Remove the number of ones from the beginning. Subtract 5:

^11111

Subtract from:

Replace input 1s with xs. Put next to the fixed number. Repeatedly remove x1. Subtract from 10:

1
x
$
1111111111
+`x1

Multiply by:

Replace every 1 with a certain number of them. Multiply by 3:

1
111

Divide by:

This uses my Retina program for Integer Division. Divide by 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

Posted 2016-12-05T19:28:21.640

Reputation: 21 944

I'm afraid I don't see how this can work. Whatever inputs I try for the subtraction commands, I get broken results (are there linefeeds missing in the output?). I also don't see how this handles negative inputs or negative intermediate results. – Martin Ender – 2016-12-07T21:15:19.660

@MartinEnder I can fix subtraction if you explain why this simplified program gives two ones in the output. http://retina.tryitonline.net/#code=JArCtjE&input=dGVzdAo

– mbomb007 – 2016-12-07T23:36:27.087

Because $ matches at the very end of the string or in front of a trailing linefeed. You need \z if you only want the former. – Martin Ender – 2016-12-08T07:35:39.777

4

Python 2, 263 258 260 221 bytes

This could probably still be much shorter.

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

Try it online

I use // instead of /, because the last instruction will have a . at the end, making any number a float. So in order to keep division consistent, I use integer division.

Test cases output:

print input()
print 9.-((input())//5)
print ((((((input())+5)+10)+10)-15)-15)//2.
print (((input())-1)-1)-1

mbomb007

Posted 2016-12-05T19:28:21.640

Reputation: 21 944

If you change that big block of ifs for o to the following (which I think should work): o=[[o+[['+-'['s'in c],'//']['v'in c],'*']['m'in c]+n,n+'-'+o]['f'in c],'input()']['T'in c], you can get it down to 224. – Kade – 2016-12-07T17:19:32.937

@Kade Yeah, it was still legible. Can't have that. – mbomb007 – 2016-12-07T17:26:14.533

@Cyoce No, the very act of calling the lambda would probably cost more than it saves. It'd have to save 4 or 5 bytes per call to pay off. – mbomb007 – 2016-12-08T22:17:05.270

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

Invocation:

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

Test cases:

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

Posted 2016-12-05T19:28:21.640

Reputation: 2 457

4

Haskell, 232 231 bytes

Of course a functional programmer would prefer to return a function rather than a string representing a program, but here we go:

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

Remarks: We always start with adding zero, otherwise the transpilation of the trivial WordMath program wouldn't give enough information to infer the type at which read is used. subtract from n could be implemented as (n-), but I use ((-)n) for more uniformity. In the case of subtract n I copy the subtract from the input so I don't have to write it, but I need to compensate for the missing space at the end. repeat is used as default operation; together with an empty initial previous operation this allows for easy ignoring of the first four words.

Usage example:

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

The other examples give the following results:

"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

Posted 2016-12-05T19:28:21.640

Reputation: 6 366

Just curious, how would you generate a function to return instead of a string? – Cyoce – 2016-12-08T16:14:03.327

In a functional programming language, creating and composing a function is no more difficult than creating and appending a string. h might look something like h s n r|x<-s.read.init$n=x%r.x and be called with first argument a function like h(+)n r (and there need to be some flip somewhere to get the correct operator order), base case is _%_=id. Main function can avoid all the boilerplate and just be t l=id%words l. - Thanks to currying, it might be seen as an interpreter, and that idea might lead to an easier and/or shorter solution. – Christian Sievers – 2016-12-08T23:05:17.303

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

Try it online!

Output

The code it generates starts with a & (input value) command, and finishes with . (output value) and @ (exit) commands. In between we have the various calculations in the form <number><operation>, where the operation can be + (add), - (subtract), / (divide by), * (multiply by), and \- (subtract from).

The number itself is a bit complicated, because Befunge only supports numeric literals in the range 0 to 9, so anything larger than that needs to be manually calculated. Since we are already reading the numbers in character by character, we simply build up the number as each digit is read, so for example, 123 becomes 155+*2+55+*3+, i.e. (((1 * 10) + 2) * 10) + 3.

Examples

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

Explanation

Befunge doesn't have the ability to manipulate strings as such, so most of the parsing is handled by counting characters. We start just by skipping the first 18 characters, which gets us past the Think of a number phrase (plus either a comma or period). Then if the next character is some form of newline or EOF we go straight to the output routine, otherwise we continue looking for a command list.

To parse a command, we just keep counting characters until we reach a digit or separator. If it's a separator, it must have been the repeat command which we handle as a special case. If it's a digit we add it to our output buffer, and continue looking for more digits. Each time a digit is output, we prefix it with 55+* (to multiply the total so far by 10) and suffix it with + (to add it to the total). Once the digits are finished we add the command character.

As for how the command is determined, we take the count of characters up to the first digit modulo 7. For add this is 4 (including the following space), for subtract it's 2, for divide by it's 3, for multiply by it's 5, and for subtract from it's 0. The subtract from requires a little additional handling since it needs the \- command combo, but the others just use their value to lookup the appropriate command character in a table.

This process is repeated for each command, building up the output into a preconstructed string on line 8. Each time an additional command is added, we also add a closing quote to the string to make sure it's always properly terminated. Then when we eventually reach the end of our input, we simply "execute" this string to push it onto the stack, then follow that with a standard output sequence to write it all out.

James Holderness

Posted 2016-12-05T19:28:21.640

Reputation: 8 298

3

JavaScript (ES6), 163 bytes

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

Try it:

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

Posted 2016-12-05T19:28:21.640

Reputation: 6 214

3

Vim 208 171 168 bytes

Added the ability to do multiple repeats in a row as per @Flp.Tkc but golfed off enough bytes that I could still lower the byte count.

: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

Unprintable characters:

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

Test cases output:

  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

Posted 2016-12-05T19:28:21.640

Reputation: 3 274

This doesn't seem to work for multiple consecutive repeats. – FlipTack – 2016-12-06T20:49:27.447

@Flp.Tkc fixed, thank you! I didn't notice that earlier. – nmjcman101 – 2016-12-07T15:46:39.730

2

Pyth, 69 67 bytes

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

A program that takes input of a "quoted string" and prints the result.

Test suite

How it works

Pyth has prefix operators, so the basic arithmetical operations are performed using (operator)(operand1)(operand2), while the pre-initialised variable Q gives the input. Hence, a transpiled WordMath program is constructed by starting with the string 'Q', and at each stage, prepending the operator, and then prepending or appending the operand as neccessary.

J\Q Set J, the transpiled program string, to the string 'Q'

tcQ\, Split the input on commas, and discard the first element (which is 'Think of a number')

V For N in that:

  • Iq@N1\r If the character at N[1] is 'r' (repeat):
    • =NZ Set Nto Z (previous value of N, set at end of for loop)
  • x"asdm"@N1 Find the index of N[1] in "asdm" (add, subtract, divide, multiply)
  • @"+-/*" Index with that into "+-/*", giving the required operator
  • ,J-eCN)\. Yield the two-element list [J, -eCN)\.], where the second element is the last element of N split on whitespace with any '.' characters removed (operand)
  • qh@cN)1\f If the first character of the second element of N split on whitespace is 'f' (subtract from):
    • .> Swap the elements of the two-element list
  • + Merge the operator and two-element list into one list
  • =Jjd Set J to that joined on spaces
  • =ZN Set Z to N

J Print J

TheBikingViking

Posted 2016-12-05T19:28:21.640

Reputation: 3 674

Nice answer man... Inspired me to try in 05AB1E, which... Was more intimidating than anticipated. – Magic Octopus Urn – 2016-12-07T23:23:51.813

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 targets to C, so a C compiler would need to compile it into something executable. The lexer library (ll) would also need to be linked. This may add a byte-penalty, but I'm not sure how many bytes if so.

The program outputs a lex program (per specification) that evaluates the transpiled wordmath expression. The code between %{ and %} is for the "transpiler" only:

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

Between the two %% lines is the regex/action portion. The first rule to be matched would be T ("Think...") which builds the preamble (lex programs must start contain the rule section at the least, and yytext is last matching text, so the rule essentially seeds the accumulator with the user's input).

The program discards all input except for that which is matched, and the other rules (ad, fr, up to re) handle the wordmath expression clauses with as minimal a match as possible to be unique. In most of these, it sets c to an expression infix, which gets concatenated between n and the last integer read when O gets called (so for example, reading "add 9" will set the infix to +=, v to 9, and the call to O will output n+=9;). (An interesting aside is that "subtract from 8" will cause both the s and the fr rules to get matched, but since O is called only at the number, the proper rule n=-n+8; is the only expression that gets output). The re rule for "repeat" just calls O again, which outputs the last created expression (and since subsequent matches will clobber yytext, supporting "repeat" is why the integer conversion in the [0-9]+ rule was required). Finally, a period causes the program trailer to be output, which just outputs the accumulator and closes with the %% pair denoting the end of the output lex program.

Note: Neither the main transpiler program or the output program will terminate. Piping input in would work, or providing EOF (ctrl-D). If termination is required after the first input, exit()s can be added.

To build/run:

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

Posted 2016-12-05T19:28:21.640

Reputation: 21

2

Pip, 58 bytes

Too bad I haven't implemented that reverse-subtraction operator yet.

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

The program takes a WordMath script from stdin and outputs Pip code to stdout. The code that is output, similarly, takes a number from stdin and outputs the result to stdout. Try it online!

Strategy

For input like this:

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

we want output like this:

YqYy*3Yy+1

which works as follows:

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 + explanation

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

The basic structure of the program is {...}Mq^k, which splits q (a line of stdin) on k (comma-space) and Maps a function to each element.

Inside the function, we start by handling the repeat case. The shortest test in Pip seems to be sNa (is there a space in the command). If so, we want to use a; if not, use p, which stores the previous command. Assign that value back to a and also to p (for next time).

For our return value, we use a list, which is fine because the default output format for lists is to concatenate everything together. The result always starts with Y. Next, we need a lookup table for the operations.

Observe that the lengths of add (4), subtract (9), divide by (10), multiply by (12), and subtract from (14) are all distinct. Further observe that they are still distinct when taken mod 7. Thus, we can use them to index into a seven-element list (containing five code snippets and two placeholders) to map each WordMath command to the appropriate Pip code (designed so the number can simply be concatenated to the end):

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

For the indices, we use regex to get the index of the first digit in the command: a@?`\d`. We also yank the regex into y for future use. The lookup table is generated by splitting the string "-y+ y- y// y+ y* " on s (space).

We still have to handle the first entry, which should translate into the code Yq. Since Think of a number does not contain any digits, the @? operator returns nil. Using nil as an index into the lookup table also returns nil. Nil is falsy, so all we need to do is add |'q to use q instead of an operation for this case.

The final element of the returned list is the number itself. We obtain this via a@y (find all matches in the command of the digit regex we yanked earlier). This returns a list of digits, but again, it's not a problem because all lists will be concatenated when output. For the first entry, a@y matches no digits and gives empty list, which doesn't add anything to the output.

For example

With input

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

the map expression gives the list

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

which, when concatenated, outputs

YqY-y+20Yy+2Yy+2

DLosc

Posted 2016-12-05T19:28:21.640

Reputation: 21 213

2

Python 2, 154 153 146 bytes

Fixed, and even saved several bytes in the process. ^__^

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"

Try it online!

Based on the same strategy as my Pip answer. Python-specific features:

  • Think of and the closing . are removed from the string before splitting (input()[9:-1]). The period was too pesky to handle in the main loop. Removing the first nine characters helps for a different reason (see below).
  • Instead of getting each command's length by regex-searching for a digit (expensive in Python because import re), we use rfind(" ") to find the last space in the command. We can also use this to check for the repeat case.
  • Python doesn't have Pip's cyclical indexing, so we have to take the index mod 7 explicitly. On the other hand, this means that we can remove the last dummy value in the lookup table, since the index mod 7 is never 6.
  • The "command" the first time through is a number, in which the index of the space is 1. This index conveniently fills the other hole in the lookup table. The other problem with processing the input stage in the main loop was the +c[s:] part, which would result in x=input() number. To resolve that problem, we string-multiply by c[0]<"a": 1 for all regular commands, in which c starts with a space, but 0 for the initial a number.

DLosc

Posted 2016-12-05T19:28:21.640

Reputation: 21 213

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 by defining alias for repeated code

Inspired by LambdaBeta's use of #define. This approach modifies the WordMath syntax slightly (, and . must be space-delimited like the other words, and , does not follow repeat), and creates alias such that the modified WordMath syntax is valid WinDbg code. The last line does what the question asks and transpiles by converting the input into the modified syntax.

Input is taken by setting a string at a memory address and setting the pseudo-register $t0 to that address. Note: this will overwrite the int at 0x2000000, so if you start your string there, it'll be partly overwritten. $t0 will also be overwritten.

Because it creates aliases, depending on whether this code has run before or after setting teh string, the output code will be different (either aliased or not). Unfortunately, I didn't find a way to get the aliases to properly expand without being whitespace delimited (meaning the WordMath script could not just be executed directly without being transformed first).

How it works:

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

Sample output, entering the string before running this code once (the resulting program resembles 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

Sample output, entering the string after after this code has run once (the aliases are expanded when entering the string so the resulting program is not as pretty):

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

Some more sample output, just using the slightly modified 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

milk

Posted 2016-12-05T19:28:21.640

Reputation: 3 043

0

Scala, 338 bytes

Try it yourself at 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)")}

Explanation:

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

Posted 2016-12-05T19:28:21.640

Reputation: 1 889