25
\$\begingroup\$

There are quite a few accumulator-based programming languages featured in challenges across the site, but it's a little tiring to have to write almost identical code for each one. So in this challenge you will write a program which can generate interpreters from an input.

How does this work? Let us take this example:

[a ** 3, b * 2, c + 15, d = 0, e / 8]

This is not the only input format allowed. You are allowed to take input as an array of directives, an array of strings or even an entire string composed of these directives. Each directive looks similar to this:

[a, *, 3]

In this case, the a command multiplies (+ means add, - means subtract, * means multiply, / means divide, ** means exponentiate and = means assignment) the accumulator by 3 (the third element). So with the aforementioned input, we end up with this schema:

the command `a` cubes the accumulator
the command `b` doubles the accumulator
the command `c` adds 15 to the accumulator
the command `d` assigns the accumulator to 0
the command `e` divides the accumulator by 8

This means that typing the command cabdcbbe will output 7.5, because: (these rules apply to all generated "languages")

  • the accumulator is automatically initialized to 0 at the beginning of a program
  • the accumulator is implicitly outputted at the end of a command
  • (0 + 15)**3*2 is disregarded because d reassigns the accumulator to zero
  • (0 + 15)*2*2/8 = 7.5 When given an array of directives, your program should output a complete program in your favorite language (might be the one you write your answer in, but not necessarily) which takes a valid program in the newly generated language and outputs the accumulator at the end of it. If I were using JavaScript and were passed the array we discussed, my program might output

const program = prompt();
let acc = 0;
for (const command of program) {
  if (command === 'a') {
    acc = acc ** 3;
  } else if (command === 'b') {
    acc = acc * 2;
  } else if (command === 'c') {
    acc = acc + 15;
  } else if (command === 'd') {
    acc = 0;
  } else if (command === 'e') {
    acc = acc / 8;
  }
}
alert(acc);

now, for some more rules:

  • the input will always be valid. No destructive operators will be entered (such as a / 0 which divides the accumulator by zero.
  • each command will perform only one operation on the accumulator, so there will be no command which multiplies the accumulator by 3, squares it and adds 5 to it all in one go.
  • each command is one letter long.
  • the shortest answer in bytes wins. [code-golf rules]
  • the interpreter should read the program from STDIN or from a file.
  • on TIO, you may pre-define the input array in the header section, but your program should obviously be able to handle any valid input.
  • you only need to support the six operators mentioned previously.
  • commands can involve floating point numbers and not only integers
  • you may replace the operators but you must then state which ones you changed
  • you may output a float even if all operators yield integers. You may not truncate outputs however
\$\endgroup\$
20
  • \$\begingroup\$ Sandbox: codegolf.meta.stackexchange.com/questions/2140/… \$\endgroup\$
    – user100690
    Commented Mar 23, 2021 at 11:21
  • 1
    \$\begingroup\$ are +, -, *, /, ** and = the only operators we have to support? \$\endgroup\$ Commented Mar 23, 2021 at 11:28
  • \$\begingroup\$ @absoluteAquarian yes \$\endgroup\$
    – user100690
    Commented Mar 23, 2021 at 11:28
  • \$\begingroup\$ @Dingus oh oops, maybe a formatting issue \$\endgroup\$
    – user100690
    Commented Mar 23, 2021 at 11:29
  • 3
    \$\begingroup\$ Can we replace the operator symbols? Eg, use ^ rather than **? \$\endgroup\$
    – Jonah
    Commented Mar 23, 2021 at 12:52

17 Answers 17

5
\$\begingroup\$

J, 61 56 53 52 bytes

1 :0
('0'".@,~,@,.&' ')[u".@;@}:@,@,."1;:@'=: & &'
)

Try it online!

Note: -3 bytes off TIO for f=:

Operator changes used to match J's built-ins:

  • ** -> ^
  • / -> %
  • = -> ]

Additional notes:

  • This is adverb that modifies the program to interpret, which is given as a list of 3-element directives. The resulting verb then takes the input as an argument, and returns the result.
  • Assumes the program string to execute will be read from right-to-left -- again, this is in keeping with J's normal convention.
\$\endgroup\$
4
\$\begingroup\$

PowerShell 7, 133 ... 101 100 bytes

-5 bytes thanks to Wasif! Then +1 byte to fix an issue with -- always being read as the decrement operator, regardless of context.
-2 bytes thanks to mazzy!

Takes input as an array of directives in the form @('command','operation',number); the output is a TIO-Compatible PowerShell program.

'switch("$args"|% T*y){'
$args|%{$1,$2,$3=$_
"$1{`$a="
$2[1]?"""`$a*""*$3+1|iex}":"`$a$2 $3}"}
'}$a'

Link is to a 116-byte TIO-Friendly (PowerShell 6 and below) version of the code. Try it online!

Explanation

'switch("$args"|% T*y){'        # The first part of the interpreter switches 
                                # on the input program's characters. Switching
                                # on an array in powershell processes the 
                                # switch statement for each element.

$args|%{$1,$2,$3=$_             # For each array in the input array, set $1,
                                # $2, $3 to the first, second, and third 
                                # element, respectively.

"$1{`$a="                       # output the case for the letter of the command
                                # start the case with setting the accumulator
                                # equal to whatever else is in this case

$2[1]                           # If the second character of the command exists
                                # In other words, the command is '**'

    ?"""`$a*""*$3+1|iex}"       # Make the body of the case a special way to
                                # calculate powers in PowerShell which is
                                # shorter than [Math]::Pow - effectively
                                # builds a string representation of the 
                                # calculation, then evaluates that string.

    :"`$a$2$3}"}                # If the command isn't '**', we just make the
                                # command '{accumulator}{command}{number}'
                                # for example: '$a/3', making the whole case
                                # '{$a=$a/3}'

'}$a'                          # Close the switch block, output the accumulator

Output

Outputs a very ugly interpreter; for the example input provided in the challenge, the interpreter looks like this:

switch("$args"|% T*y){
a{$a=
"$a*"*3+1|iex}
b{$a=
$a*2}
c{$a=
$a+15}
d{$a=
$a=0}
e{$a=
$a/8}
}$a

And, of course, Try The Output Interpreter!

\$\endgroup\$
4
  • 1
    \$\begingroup\$ Dang! You are very fast, there is no need to define $a=0, it is automatically initialized, which can save you 5 bytes, Try It Online :-) \$\endgroup\$
    – Wasif
    Commented Mar 23, 2021 at 16:40
  • 1
    \$\begingroup\$ "$args" Try it online! \$\endgroup\$
    – mazzy
    Commented Mar 23, 2021 at 18:39
  • 1
    \$\begingroup\$ "}``$a" -> '}$a' :) \$\endgroup\$
    – mazzy
    Commented Mar 23, 2021 at 22:09
  • \$\begingroup\$ @mazzy thanks again! \$\endgroup\$ Commented Mar 24, 2021 at 11:58
4
\$\begingroup\$

Python 3.8, 120 106 93 bytes

Saved 27 bytes thanks to @kaya3

lambda f:lambda i,a=0:[a:={n:eval({'=':v}.get(o,str(a)+o+v))for n,o,v in f}[k]for k in i][-1]

Try it online!

Hey, my first golf! This golf utilizes Python 3.8's walrus operator. You can insert the interpreter's commands as a list tuples into a function that returns a function which accepts commands as a string/list of strings (and, optionally, an initial value for the accumulator).

If this lambda is assigned to f and run as:

f([('a','**','3'),('b','*','2'),('c','+','15'),('d','=','0'),('e','/','8')])

will give you:

<function __main__.<lambda>.<locals>.<lambda>(i, a=0)>

which if assigned to f2 can be run as:

f2('cabdcbbe')  

which outputs:

7.5

Ungolfed version:

def interpreter_generator(commands):
    def interpreter(instructions, accum=0):
        for i in instructions:
            accum = {n: eval({'=': v}.get(o, str(accum) + o + v)) for n, o, v in commands}[i]
            
        return accum

    return interpreter

commands = [('a','**','3'), ('b','*','2'), ('c','+','15'), ('d','=','0'), ('e','/','8')]
instructions = 'cabdcbbe'
my_interpreter = interpreter_generator(commands)
print(my_interpreter(instructions))
\$\endgroup\$
2
  • \$\begingroup\$ This saves 14 bytes; more might be possible with something similar. lambda f:lambda i,a=0:[a:={n:eval('lambda x:'+{'=':''}.get(o,'x'+o)+v)for n,o,v in f}[k](a)for k in i][-1] \$\endgroup\$
    – kaya3
    Commented Mar 24, 2021 at 18:31
  • 1
    \$\begingroup\$ Doing the expression in the eval instead of creating a lambda gets it down to 93 bytes: lambda f:lambda i,a=0:[a:={n:eval({'=':v}.get(o,str(a)+o+v))for n,o,v in f}[k]for k in i][-1] \$\endgroup\$
    – kaya3
    Commented Mar 24, 2021 at 18:48
3
\$\begingroup\$

JavaScript (ES6), 73 bytes

Expects an array of [letter, operation, value] triplets. Returns a function that expects an array of letters.

a=>`a=>a.reduce((A,c)=>eval({${a.map(([c,o,v])=>c+`:'A${o+v}'`)}}[c]),0)`

Try it online!

\$\endgroup\$
3
\$\begingroup\$

Python 3, 202 \$\cdots\$ 136 134 bytes

lambda s:'%{\nfloat p;\n%}\n%%\n'+''.join(a+' p=p'+b+('(','w(p,')[b>'=']+c+');\n'for a,b,c in s)+'%%\nmain(){yylex();printf("%f",p);}'

Try it online!

Inputs the target language as a list of 3-tuples of strings. Uses o as the power operator.
Generates the Flex code that will generate a C program that will parse the input language.

If this lambda is assigned to f and run as:

print(f([["a","o","3"],["b","*","2"],["c","+","15"],["d","=","0"],["e","/","8"]]))  

then that will output:

%{
float p;
%}
%%
a p=pow(p,3);
b p=p*(2);
c p=p+(15);
d p=p=(0);
e p=p/(8);
%%
main(){yylex();printf("%f",p);}

which if stored in file write_an_interpreter_generator.l and then run through flex and then compiled:

flex write_an_interpreter_generator.l
gcc lex.yy.c -o write_an_interpreter_generator -lfl -lm  

will produce lexer write_an_interpreter_generator, which if run as:

echo 'cabdcbbe' | ./write_an_interpreter_generator  

outputs:

7.500000
\$\endgroup\$
0
2
\$\begingroup\$

Jelly, 7 bytes

ZyW€}FV

Try it online!

The example language would be represented like this: [['a', '*3'], ['b', '×2'], ['c', '+15'], ['d', 'ṛ0'], ['e', '÷8']] Operators: = assignment, + = addition, _ = subtraction, × = multiplication, ÷ = division, * = power. The program is given as the second argument.

\$\endgroup\$
3
  • \$\begingroup\$ It feels as though there should be some way to shorten W€} but I can't find it :/ \$\endgroup\$ Commented Mar 23, 2021 at 14:49
  • \$\begingroup\$ 4 bytes by requiring the program to be a list of characters rather than a single string \$\endgroup\$ Commented Mar 23, 2021 at 14:51
  • \$\begingroup\$ @ChartZBelatedly That's right, but I felt like that was stretching the input format too much. \$\endgroup\$
    – xigoi
    Commented Mar 23, 2021 at 15:08
2
\$\begingroup\$

Haskell, 121 bytes

f d="($0).foldr(\\(Just g)->(.g))id.map(`lookup`["++(init$d>>=(\(c,o,p)->["('",c,"',(",o,"(",p,"))),"]>>=id))++"]);x!y=y"

Try it online!

Uses the default operators except for = which is replaced by !.

The function f accepts a list of directives such as [("a","**","3"),("b","*","2"),("c","+","15"),("d","!","0"),("e","/","8")] and returns a Haskell function such as

($0).foldr(\(Just g)->(.g))id.map(`lookup`[('a',(**(3))),('b',(*(2))),('c',(+(15))),('d',(!(0))),('e',(/(8)))]);x!y=y

(Try it online!). This function is the interpreter; in the above example, when called with input "cabdcbbe", it returns 7.5.

How?

The idea is to use the list of directives to embed a lookup table in the interpreter. In the above example, the lookup table is [('a',(**3)),('b',(*2)),('c',(+15)),('d',(!0)),('e',(/8))] (some brackets removed for clarity). This table maps each character to the corresponding function. Using the lookup table, the interpreter is able to convert the list of characters (e.g. "cabdcbbe") to a list of functions (e.g. [(+15),(**3),(*2),(!0),(+15),(*2),(*2),(/8)]), which is then foldred with the composition operator (.) to yield the full (interpreted) program. Finally, the program (which, like everything in Haskell, is a function) is applied to the value 0, returning the desired result.

Haskell, 113 bytes

Assuming the list of instructions can be read right-to-left.

f d="g i=foldr(.)id[f|c<-i,(a,f)<-["++(init$d>>=(\(c,o,p)->["('",c,"',(",o,"(",p,"))),"]>>=id))++"],a==c]0;x!y=y"

Same idea as above, but without the need to reverse the string.

\$\endgroup\$
2
\$\begingroup\$

Ruby, 55 bytes

->x{"a=0;$<.chars{|c|a=eval(?a+#{x.to_h}[c])*1.0};p a"}

Try it online!

Outputs a full Ruby program which reads characters from the input. As it turns out, returning a function seems to be longer, by my attempt.

Takes input as an array like:

[
    ["a", "**3"],
    ["b", "*2"],
    ["c", "+15"],
    ["d", "=0"],
    ["e", "/8"],
]

The function simply converts this to a hash and indexes each character in the input by this. Unfortunately I can't use $. as the accumulator since it cannot store floats, apparently. The a=0;...;p a bit annoys me, but I couldn't quite squeeze it out.

Alternative Attempts

->x{"a=0;$<.chars{|c|a=eval ?a+#{x.to_h}[c]+'.0'};p a"}
->x{"p$<.chars.inject(0){|a,c|eval(?a+#{x.to_h}[c])*1.0}"}
->x{"p$<.chars.inject(0){|a,c|eval ?a+#{x.to_h}[c]+'.0'}"}

Similar variants for returning functions, but I couldn't find a neat way to abuse the function nature.

\$\endgroup\$
2
\$\begingroup\$

Haskell, 89 bytes

f c="a!c"++concat["|c=='"++a++"'=a"++o++x|(a,o,x)<-c,o/="="]++"|1>0=0\ne=print.foldl(!)0"

Try it online!

  • generates this Haskell program:
a!c|c=='a'=a**3|c=='b'=a*2|c=='c'=a+15|c=='e'=a/8|1>0=0
e=print.foldl(!)0

Try it online!

  • uses 1>0 guard to handle operator =
\$\endgroup\$
2
\$\begingroup\$

Charcoal, 35 bytes

”y≔⁰θF⁺Sψ≡ι”FA⭆⪪”y≔,θI,θ”,⁺§ιλκ”yIθ

Try it online! Generates this Charcoal program:

≔⁰θF⁺Sψ≡ιa≔XθI3θb≔×θI2θc≔⁺θI15θd≔⎇⁰θI0θe≔∕θI8θIθ

Try it online! Explanation:

”y≔⁰θF⁺Sψ≡ι”

Print code that initialises the accumulator and switches over the input's characters, plus a null terminator, in a loop.

FA

Loop over each directive.

⭆⪪”y≔,θI,θ”,⁺§ιλκ

Interleave the directive with the characters needed to turn it into a case clause.

”yIθ

Print code to output the final value of the accumulator.

The commands can be any ASCII string of the user's choice, not just a single letter.

The required operators are as follows:

  • : Exponentiation
  • ×: Multiplication
  • : Division
  • : Addition
  • : Subtraction
  • ⎇⁰: Assignment

Two other operators are also supported, these are:

  • ÷: Integer division
  • : Modulo
\$\endgroup\$
2
\$\begingroup\$

Python 3.8 (pre-release), 68 bytes

lambda c:(f:=lambda p,a=0:f(p[1:],eval(f'(a{c[p[0]]})'))if p else a)

Try it online!

replaces the '=' operator with ':=' in order to utilize python 3.8+ walrus expressions

\$\endgroup\$
1
\$\begingroup\$

Excel outputs VBA, 167 bytes

="Function f(s)
for i =1 to len(s)
j=mid(s,i,1)
select case j 
"&TEXTJOIN("
",,IF(A:A="","","CASE """&A:A&"""
a"&IF(B:B="=","","=a")&B:B&C:C))&"
End Select
Next
f=a
End Function"

Input is in columns A, B, C. Oddly enough output must be pasted somewhere else before it can be copied into VBA. Otherwise it adds quotes in weird places. Excel is a quirky one.

\$\endgroup\$
1
\$\begingroup\$

Python 2, 153 bytes

print"0  v\n<vi<"
for c,p in input():n=int(p[1:],16);p=[n/10*"a+"+`n%10`+p[0],":"*n+"*"*n][p[0]=="^"];print"^>:'"+c+"'=?!v~"+p+"\n^v       <"
print" >~n"

Try it online!

Example output:

><>, 146 bytes

0  v
<vi<
^>:'a'=?!v~:::***
^v       <
^>:'b'=?!v~2*
^v       <
^>:'c'=?!v~5a++
^v       <
^>:'d'=?!v~~0
^v       <
^>:'e'=?!v~8,
^v       <
 >~n

Try it online!

Currently this only handles positive integers; Negative integers could probably be handled with some more bytes but float arguments would be a huge pain. Division is floating point by default, however. Commands must be one character, but that character can be totally arbitrary.

The ><> program works by keeping only the accumulator on the stack in between commands.

/ is denoted by ,, its ><> equivalent. ^ doesn't exist in ><>, so it's done by multiplying the accumulator by itself n times: e.g. ::::**** for n=4. = is implemented by dropping the accumulator with ~ and pushing the new value n.

Example ><> program, commented:

    0  v                  | push 0 onto the stack; proceed down
    <vi<                  | read a command from input
    ^>:'a'=?!v~:::***     | The following pairs of lines: 
    ^v       <            | :'c'=     | non-destructively compare to command character
    ^>:'b'=?!v~2*         |      ?!v  | proceed downwards if not equal
    ^v       <            |           | otherwise
    ^>:'c'=?!v~5a++       |      ~    | destroy the command
    ^v       <            |       n   | push the operand
    ^>:'d'=?!v~~0         |        op | do  acc <op> n
    ^v       <            |           | implicitly wrap around, hit ^, proceed back up
    ^>:'e'=?!v~8,         |
    ^v       <            | if no match is found we must be done
     >~n                  | destroy the terminating character and output 
                          |   the last line will repeat and crash due to empty stack
\$\endgroup\$
1
\$\begingroup\$

Wolfram Language (Mathematica), 55 bytes

lD/*##@0&@@ToExpression[#/.(#->"#"<>##2<>"&"&@@@l)]&

Try it online!

Input ^ for exponentiation and - for subtraction.


42 bytes

lD/*##@0&@@<|#->Curry[#2]@#3&@@@l|>/@#&

Try it online!

This one's probably a bit too cheaty with the operator symbols - input the appropriate Mathematica function's name instead of the corresponding symbol.

\$\endgroup\$
1
\$\begingroup\$

Emotion, 74 bytes

🤒😊😅🥑🥕🍌🍏🧖🤽😊😄🥦☕🌽😍😄🤼🚶🖕😌🤝🤧😍😁🧝🧟🧡👽😷😘😴👎🤕💌🙍😔😀🏃👉😰😍🤣🤴😼🙋🙎🌶🏌💣💌🙍😔😄🏃👉😰😔😃🏃👉😑🤺😕😚🤐😎⛷🤔😎⛷🤔😦🏃😺

Try it online!

Try the sample interpreter!

The input is a string of space-separated directives. Each directive is a comma-separated list of the instruction name, the operation, and the value.

The operation names have been changed for efficiency:

+ -> add

- -> subtract

* -> multiply

/ -> divide

** -> math.pow

Explanation

🤒 Store the first stack value in the a register.
😊😅🥑🥕🍌🍏🧖🤽 Push literal load 0
😊😄🥦☕🌽 Push literal swp
😍😄🤼🚶🖕 Push literal iterate
😌 Push the value contained in the a register.
🤝 Push a list of the first stack values split by spaces.
🤧 Enter an iteration block over the first stack value.
😍😁🧝🧟🧡👽 Push literal ldr o
😷 Push the value contained in the iteration element register.
😘😴 Push literal ,
👎 Push a list of strings obtained by splitting the second stack value with the first stack value.
🤕 Store the first stack value in the b register.
💌🙍 Push literal load 
😔 Push the value contained in the b register.
😀 Push literal 0
🏃👉 Push the value in the list of the second stack value at the index of the first stack value.
😰 Push the sum of the second and first stack values.
😍🤣🤴😼🙋🙎🌶🏌💣 Push literal if equal
💌🙍 Push literal load 
😔 Push the value contained in the b register.
😄 Push literal 2
🏃👉 Push the value in the list of the second stack value at the index of the first stack value.
😰 Push the sum of the second and first stack values.
😔 Push the value contained in the b register.
😃 Push literal 1
🏃👉 Push the value in the list of the second stack value at the index of the first stack value.
😑 Push a copy of the first stack value.
🤺😕 Push literal set
😚 Enter a conditional block if the top two stack values are equal.
🤐 Remove the first stack values from the stack.
😎 End a control flow structure.
⛷🤔 Push literal end
😎 End a control flow structure.
⛷🤔 Push literal end
😦 Collapse all stack values into a list, then push that list.
🏃😺 Compile a list of Emotion instructions on the top of the stack and print the compiler output.
\$\endgroup\$
0
\$\begingroup\$

CSASM v2.3.0.1, 985 bytes

Looks like we've come full circle. CSASM is a language written with a C# backer and is parsed by a C# interpreter and this code generates C# code that's an interpreter for another language...


Generates a C# file named a in the same directory the CSASM executable is executed from.

Expects a space-separated string for the commands and no spaces within the commands themselves.

Example (input: a+1 b*3 c**2 d/4 e=0):

Output:

using System;class A{static void Main(){var a=0.0;var s=Console.ReadLine();for(int i=0;i<s.Length;i++){if(s[i]=='a')a=a+1;else if(s[i]=='b')a=a*3;else if(s[i]=='c')a=Math.Pow(a,2);else if(s[i]=='d')a=a/4;else if(s[i]=='e')a=a=0;}Console.Write(a);}}

Formatted:

using System;
class Program{
    static void Main(){
        var a = 0.0;
        var s = Console.ReadLine();
        for(int i = 0; i < s.Length; i++){
            if(s[i] == 'a')
                a = a + 1;
            else if(s[i] == 'b')
                a = a * 3;
            else if(s[i] == 'c')
                a = Math.Pow(a, 2);
            else if(s[i] == 'd')
                a = a / 4;
            else if(s[i] == 'e')
                a = a = 0;
        }
        Console.Write(a);
    }
}

Code

func main:
.local a : i32
.local b : str
.local c : f64
lda 0
sta $1
in ""
push " "
div
pop $a
push 1
pop $io.f0
push 2
pop $io.m0
push 1
pop $io.s0
push "a"
pop $io.p0
io.w0 "using System;class A{static void Main(){var a=0.0;var s=Console.ReadLine();for(int i=0;i<s.Length;i++){"
.lbl a
push $a
dup
ldelem $1
dup
pop b
push "**"
index
push 1
add
pop a
push b
len
pop $5
push 0
pop $2
.lbl b
push b
conv ~arr:char
ldelem $2
pop $3
clf.o
push $2
push 2
comp.gte
push $f.o
brfalse m
clf.o
push $3
push '+'
comp
push $f.o
brtrue g
push $3
push '-'
comp
push $f.o
brtrue g
.lbl m
clf.n
push $3
conv str
pop $3
clf.n
push b
push $2
push $5
substr
conv f64
pop c
push $f.n
brtrue h
push $2
brtrue f
push $1
brfalse e
io.w0 "else "
.lbl e
push "if(s[i]=='"
push $3
add
push "')a="
add
pop $4
io.w0 $4
br i
.lbl f
push a
brfalse g
io.w0 "Math.Pow(a,"
inc $2
br i
.lbl g
push "a"
push $3
add
pop $3
io.w0 $3
br i
.lbl h
io.w0 c
push $5
push 1
sub
pop $2
push a
brfalse k
io.w0 ")"
.lbl k
io.w0 ";"
.lbl i
inc $2
push $2
push $5
sub
brtrue b
inc $1
push $a
len
push $1
sub
brtrue a
io.w0 "}Console.Write(a);}}"
push null
pop $io.p0
ret
end

Commented and ungolfed:

func main:
    ; these locals are needed since CSASM only has 6 value registers
    .local hasExponent : i32
    .local curCommand : str
    .local commandValue : f64

    ; Initialize the outer loop counter to zero
    lda 0
    sta $1

    ; Get the input, split it along space chars and put it in $a
    in ""
    push " "
    div
    pop $a

    ; Initialize a file handle as writing, FileMode.Create and Stream
    push 1
    pop $io.f0
    push 2
    pop $io.m0
    push 1
    pop $io.s0

    ; Start the handle by setting the file path
    push "a"
    pop $io.p0

    ; The first chunk of the generated program
    io.w0 "using System;class A{static void Main(){var a=0.0;var s=Console.ReadLine();for(int i=0;i<s.Length;i++){"

    .lbl outerLoop
        ; Get the value in $a at index $1
        push $a
        dup
        ldelem $1

        ; Then set the variable "hasExponent" to if said value contains "**"
        dup
        pop curCommand
        push "**"
        index
        push 1
        add
        pop hasExponent
        
        ; Store the length into "$5"
        push curCommand
        len
        pop $5

        ; Initialize the inner loop counter to zero
        push 0
        pop $2

        .lbl innerLoop
            ; Get the char in "curCommand" at index $2 and put it in $3
            push curCommand
            conv ~arr:char
            ldelem $2
            pop $3

            ; Clear the Comparison flag
            clf.o

            ; if $2 >= 2, ignore the check below
            push $2
            push 2
            comp.gte
            push $f.o
            brfalse checkCommandValue
            clf.o
            ; if $3 == '+' or $3 == '-', don't try to parse the rest of the string
            ;   as an <f64> yet
            push $3
            push '+'
            comp
            push $f.o
            brtrue printCommandStart
            push $3
            push '-'
            comp
            push $f.o
            brtrue printCompoundStart

        .lbl checkCommandValue
            ; Clear the Conversion flag
            clf.n

            ; Convert the current char into a string and put it back into $3
            push $3
            conv str
            pop $3

            clf.n
            ; Get the rest of the substring
            push curCommand
            push $2
            push $5
            substr
            conv f64
            pop commandValue
            push $f.n
            brtrue printStatementEnd

            ; If $2 is > 0, the stuff after the "a=" part needs to be checked
            push $2
            brtrue checkPowStart

            ; If this is the first command being parsed, don't print the "else" part
            push $1
            brfalse printStatementStart
            io.w0 "else "
            
        .lbl printStatementStart
            ; Begin the if block
            push "if(s[i]=='"
            push $3
            add
            push "')a="
            add
            pop $4
            io.w0 $4
            br getNextChar

        .lbl checkPowStart
            ; If a is non-zero, this command used "**".  Thus, it needs to be converted
            ;   to Math.Pow()
            push hasExponent
            brfalse printCompoundStart
            io.w0 "Math.Pow(a,"
            inc $2
            br getNextChar

        .lbl printCompoundStart
            ; The other commands follow the pattern of "a = a <operator> <value>;"
            push "a"
            push $3
            add
            pop $3
            io.w0 $3
            br getNextChar

        .lbl printStatementEnd
            ; Print the value at the end of the command, then close the statement
            io.w0 commandValue
            push $5
            push 1
            sub
            pop $2
            push hasExponent
            brfalse notPowStatementClose
            io.w0 ")"
        .lbl notPowStatementClose
            io.w0 ";"

        .lbl getNextChar
            ; Increment $2 and see if there's still more chars to parse
            inc $2
            push $2
            push $5
            sub
            brtrue innerLoop

        ; Increment $1 and see if there's more commands to parse
        inc $1
        push $a
        len
        push $1
        sub
        brtrue outerLoop

    ; The last chunk of the generated program
    io.w0 "}Console.Write(a);}}"

    ; Close the file handle by setting the path to null
    push null
    pop $io.p0

    ret
end
\$\endgroup\$
1
  • 1
    \$\begingroup\$ Reading the comments in the OP has indicated that ** could be replaced with ^. I will do this and try to golf the code further later \$\endgroup\$ Commented Mar 23, 2021 at 14:43
0
\$\begingroup\$

PowerShell, 102 100 bytes

Inspired by @Zaelin Goodman

'switch("$args"|% t*y){'
$args-replace'^.','$0{$a=$a$''}#'-replace'.a.(\*\d+)','"$a*"$1+1|iex'
'}$a'

Try it online!

With arguments a**3 b*2 c+15 d=0 e/8 the script generates:

switch("$args"|% t*y){
a{$a="$a*"*3+1|iex}#**3
b{$a=$a*2}#*2
c{$a=$a+15}#+15
d{$a=$a=0}#=0
e{$a=$a/8}#/8
}$a

Try it online!

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.