Make a +-#$%! interpreter

6

1

Because we haven't had enough of these, let's do another weird language!

  • + increments the accumulator
  • - decrements the accumulator
  • # outputs the character with the codepoint of the accumulator modulo 127, and then resets the accumulator
  • $ is like # but it doesn't reset the accumulator
  • % reads a single character of input and then set the accumulator to that. If it is EOF, exit the program
  • ! sets the accumulator to 0

At the end of the program, if the accumulator is 0, exit, otherwise, start again from the beginning, preserving the accumulator state.

Note that this language has no implementation so it is defined by this post.

Challenge

Given a valid +-#$%! program (containing only those characters) and the program input (containing only printable ASCII characters), return its output.

Examples

program, input -> output // comments
%$, hello -> hello // CAT program
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++#+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++#++++++++++++++++++++++++++++++++#++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++#++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++#+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++#++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++#+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++#, -> Hi there
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++$+++++++++++++++++++++++++++++++++$++++++++++++++++++++++++++++++++++++++++++++++++++++++$++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++$+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++$++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++$+++++++++++++$++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++$!, -> Hi there

HyperNeutrino

Posted 2017-05-24T04:49:02.133

Reputation: 26 575

5I promise you this is the last similar challenge you'll see from me for a long time. – HyperNeutrino – 2017-05-24T04:51:29.577

4so many "languages" even w/o turing complete... – tsh – 2017-05-24T05:30:23.237

1Shouldn't the + be a ;? – caird coinheringaahing – 2017-05-24T06:20:23.927

1Also it's so great that all three ;# challenges are in the HNQ at the same time – caird coinheringaahing – 2017-05-24T06:21:51.347

Can we take the input for the program as list of characters? – ovs – 2017-05-24T07:31:57.827

3Next time, at least add some kind of control flow statements. – Sanchises – 2017-05-24T07:38:42.930

1Please rename this to 'Make a +-#$%! interpreter' to keep consistent. :P – totallyhuman – 2017-05-24T10:56:15.353

3I'm kinda getting tired of these... – programmer5000 – 2017-05-24T13:34:52.787

6If your going to make a language, make it Turing Complete. – mbomb007 – 2017-05-24T13:36:38.593

@tsh Just meant to be simple :) – HyperNeutrino – 2017-05-24T13:51:33.687

@RandomUser In the original one it was but now it's not :p – HyperNeutrino – 2017-05-24T13:51:51.217

@ovs Sure, you may. – HyperNeutrino – 2017-05-24T13:52:09.833

@Sanchises I've decided to not make a next time because people (cc programmer5000) are getting tired of these. – HyperNeutrino – 2017-05-24T13:52:30.550

@totallyhuman Okay sure :P – HyperNeutrino – 2017-05-24T13:52:40.967

@programmer5000 Okay sorry. I promise this is the last of these from me. No guarantees about other people :P – HyperNeutrino – 2017-05-24T13:53:02.237

@mbomb007 Again, just meant to be simple, but I am currently making a language that will hopefully be TC :P – HyperNeutrino – 2017-05-24T13:53:19.300

@HyperNeutrino oh come on! People can't be getting tired of this already, I'm nearly ready to release my new language, +p! :) – Socratic Phoenix – 2017-05-25T14:29:27.870

Answers

10

><>, 112 bytes

You may be thinking "Is ><> really the best language to write an interpreter in?", to which my answer is "no, but let's try anyway!"

i:}b(?v
0(?;v0<o}\.24;!?:}\    /}~51. /}1-32./}1+32./}~i:
6*bb< o:}\?="#":  \?(b:\?="!":\?="-":\?="+":\?="%":$%+

Try it online, or at the fish pond!

Takes input from STDIN: first the +-#$%! code, then optionally a newline and any extra input.

This could do with some explanation.

  • The first loop is i:}b(?v. This reads from STDIN to the stack until it hits a character less than b = 11, which means either a newline (charcode 10) or EOF (charcode -1).

  • Once that's done, the fish swims down…

      v
    v0<
    <
    

    …into the main loop, adding a 0 to the stack to use as the accumulator.

  • First, the fish sees bb*6+% — this computes 127 = 11*11+6 and takes the modulus of the accumulator. Then $ brings forward the first character from the stack.

  • Now, we get to the bulk of the code — reading the character. There are seven branches in this bit of the code. (I think I've tetrissed the branches as much as I can, but there might be more blank space that can be removed here.)

    0(?;                                       /}~i:
                                               \?="%":
    

    Note that the fish starts from the bottom right, moving left, and wraps when it hits the side of the code. If the character is a literal "%", we shift it to the back of the stack for safekeeping, delete the accumulator, replace it with the first character in STDIN, check if that's EOF (that is, if its charcode is negative), and end the program if so. We then hit the start of the main loop again.

                                 /}1-32./}1+32.
                                 \?="-":\?="+":
    

    If the character is "+", we put it on the back of the stack again, add 1 to the accumulator, and jump to the start of the main loop. The "-" case is similar.

                          /}~51.
                          \?="!":
    

    If the character is "!", delete the accumulator and jump to just before the start of the main loop, where we first created the accumulator by adding a 0 to the stack.

             .24;!?:}\
                     \?(b:
    

    If the character is EOF or newline, check if the accumulator is 0, and terminate the program if so; otherwise, jump back to the start of the main loop. We kept all the used characters on the stack, so now we're back at the start of the program anyway.

          o}\
            \?="#":
    

    If the character is "#", output the accumulator as a character. We're then conveniently back just before the main loop, where we created the accumulator with a 0.

     
         o:}
    

    If we've reached this branch, the character can only be "$", since the stack only contained the characters +-#$%! and EOF or linefeed. Therefore we print a copy of the accumulator, and we're back at the start of the main loop.

Not a tree

Posted 2017-05-24T04:49:02.133

Reputation: 3 106

7why is 'fish playground' not called 'fish pond' instead? – None – 2017-05-24T12:16:01.460

1@Orangesandlemons: I don't know, but I'm going to call it that anyway! – Not a tree – 2017-05-24T12:23:31.663

2Well this somehow beats the Python answer. Nice. – HyperNeutrino – 2017-05-24T12:23:43.333

2

Python 2, 186 bytes

p,i=input()
a=0;f=1;r=''
while a^f:
 f=0
 for c in p:
	if c in'#$':r+=chr(a%127);a=a*(c>'#')
	if'%'==c:
	 if i:a=ord(i[0]);i=i[1:]
	 else:a=0;break
	a+=(c=='+')-(c>'+')-a*(c<'"')
print r

Try it online!

ovs

Posted 2017-05-24T04:49:02.133

Reputation: 21 408

Well, it seems that you don't need f.

– Erik the Outgolfer – 2017-05-24T13:57:51.613

@EriktheOutgolfer this will break when the accumulator gets negative – ovs – 2017-05-24T14:15:20.843

why not a*=c<'#'? – Cyoce – 2017-05-24T18:40:40.993

2

Pyth, 89 bytes

KwJ1WJ=J0FHKIqH\+=+J1)IqH\-=-J1)IqH\#pC%J127=J0)IqH\$pC%J127)IqH\!=J0)IqH\%IqzkB=JChz=ztz

Takes the input in the first line and the output in the second. It's just a simple for loop over the input with some ifs. I'm sure it could be much shorter, but i don't care enough right now.

Try it!

Python code it compiles to:

assign('z',input())
assign("K",input())
assign("J",1)
while J:
 assign('J',0)
 for H in num_to_range(K):
  if equal(H,"+"):
   assign('J',plus(J,1))
  if equal(H,"-"):
   assign('J',minus(J,1))
  if equal(H,"#"):
   Pprint(Pchr(mod(J,127)))
   assign('J',0)
  if equal(H,"$"):
   Pprint(Pchr(mod(J,127)))
  if equal(H,"!"):
   assign('J',0)
  if equal(H,"%"):
   if equal(z,k):
    break
   assign('J',Pchr(head(z)))
   assign('z',tail(z))

KarlKastor

Posted 2017-05-24T04:49:02.133

Reputation: 2 352

1

Java 8, 225 194 187 bytes

s->d(s,0);void d(char[]s,int n){for(int c:s){n+=c>44?-1:c>42?1:0;System.out.print(c>34&c<37?(char)(n%127):c==37?new java.util.Scanner(System.in).nextLine():"");n=c<36?0:n;}if(n>0)d(s,n);}

-15 bytes by converting Java 7 to Java 8.
-14 bytes thanks to @Zacharý by replacing String input with char-array input.
-2 bytes by some small fixes
-7 bytes thanks to @ceilingcat

Explanation:

Try it here: first test case with user-input.
Try it here: other two test-cases.

s->                            // Method (1) with char-array parameter and no return-type
  d(s,0);                      //  Call another method with the char-array and 0

void d(char[]s,int n){         // Method (2) with character-array and integer parameters
  for(int c:s){                //  Loop over the characters of the input
    n+=c>44?                   //   If the character is '-':
        -1                     //    Decrease `n` by 1
       :c>42?                  //   Else if the character is '+':
        1                      //    Increase `n` by 1
       :                       //   Else:
        0;                     //    Leave `n` the same
  System.out.print(c>34&c<37?  //   If the character is '#' or '$':
    (char)(n%127)              //    Print the `n modulo 127` as character
   :c==37?                     //   Else-if the character is '%':
    new java.util.Scanner(System.in).nextLine()
                               //    Read & output the user-input
   :                           //   Else:
    "");                       //    Output nothing
  n=c<36?                      //   Then, if the character is '#' or '!':
     0                         //    Reset `n` to 0
    :                          //   Else:
     n;}                       //    Leave `n` the same
  if(n>0)                      //  If `n` is now larger than 0:
    d(s,n);}                   //   Do a recursive-call while leaving `n` as is

Kevin Cruijssen

Posted 2017-05-24T04:49:02.133

Reputation: 67 575

Is it possible to accept the argument s of c as a char array? – Zacharý – 2017-08-10T22:11:12.667

@Zacharý Thanks. I've also converted it to Java 8, answer golfed two more small things. – Kevin Cruijssen – 2017-08-11T06:51:55.687

1

Bash/gcc, 184 172

This first reads the program on stdin, then after an EOF, reads program input. Looks valid to me.

gcc -x c -<<<"a;main(){do{$(sed -e's/#/$!/g' -e's/./&;/g' -e's/[+-]/a&&/g' -es/!/a=0/g -e's,%,a=getchar();if(a==-1)exit(0),g' -e's/\$/putchar(a%127)/g')}while(a);}"
./a.out

If you didn't already notice, I'm just constructing, compiling and running a C program on the fly. Note: your compiler might complain loudly about the horrible code generated, but I believe we were allowed extra output on stderr.

Before I began, I expected this to be shorter than it turned out; I'm really unhappy with the % handling. Suggestions welcome :)

tomsmeding

Posted 2017-05-24T04:49:02.133

Reputation: 2 034

1

Python 3, 121 bytes

x,i=0,int
for c in input():
 if c in'$#':print(end=chr(x%128))
 x+=i(c=='+')-i(c=='-')-i(c in'!#')*x
 if'%'==c:x=input()

Matthew Torrence

Posted 2017-05-24T04:49:02.133

Reputation: 11

Welcome to PPCG, amazing first golf! – Zacharý – 2017-08-10T22:07:24.760

If you reach the end and the accumulator isn't 0, you need to restart from the beginning – ovs – 2017-09-09T10:13:28.430

0

C, 170 bytes

main(int c,char **v){for(int A=0,i=0;c=v[1][i++];A+1)A=c==0?i=0,A?A:-1:c=='+'?A+1:c=='-'?A-1:c=='!'?0:c=='$'?putchar(A%127),A:c=='#'?putchar(A%127),0:c=='%'?getchar():A;}

Ungolfed

main(int c,char **v){
    for(int A=0,i=0;c=v[1][i++];A+1)
        A=
            c==0 ? i=0, A?A:-1:    // EOF == -1
            c=='+' ? A+1:
            c=='-' ? A-1:
            c=='!' ? 0:
            c=='$' ? putchar(A%127),A:
            c=='#' ? putchar(A%127),0:
            c=='%' ? getchar():
            A;
}

6 bytes can be saved by replacing +-!$#% with decimal equivalents, but that would obfuscate the code. 9 bytes can be saved when using a K&R compatible compiler by moving the declaration int A=0,i=0 to the start of main, A,i,main(... However I only posted code that I could test.

Corrected in response to comments by Dave. Also switched to using the value of EOF (-1) instead of "EOF".

user230118

Posted 2017-05-24T04:49:02.133

Reputation: 239

1

You can test such C code online, here

– Conor O'Brien – 2017-05-25T22:20:35.007

char **v => char**v. – Zacharý – 2017-08-10T22:07:53.983

143 bytes – ceilingcat – 2020-01-22T23:30:33.227

0

JavaScript (ES6), 197 bytes

(s,i="")=>(i=[...i].map(c=>c.charCodeAt()),a=0,o="",u=_=>{[...s].map(c=>eval(["a++","a--",p="o+=a?String.fromCharCode(a%127):''",r="a=0",p+","+r,"a=i.shift()"]["+-$!#%".indexOf(c)])),a&&u()},u(),o)

Cleaned up

(s, i="") => (
    i = [...i].map(c=>c.charCodeAt()),
    a = 0,
    o = "",
    u = _ => {
        [...s].map(c => eval(
            ["a++", "a--", p="o+=a?String.fromCharCode(a%127):''", r="a=0", p+","+r, "a=i.shift()"][ "+-$!#%".indexOf(c) ]
        )),
        a && u()
    },
    u(),
    o
)

Test Snippet

f=
(s,i="")=>(i=[...i].map(c=>c.charCodeAt()),a=0,o="",u=_=>{[...s].map(c=>eval(["a++","a--",p="o+=a?String.fromCharCode(a%127):''",r="a=0",p+","+r,"a=i.shift()"]["+-$!#%".indexOf(c)])),a&&u()},u(),o)
<style>*{font-family:Consolas;}</style>
Code:&nbsp;
<textarea id="C" rows="2" cols="50"></textarea><br>
Input: <input id="I"><br>
<button onclick="O.innerHTML=f(C.value, I.value)">Run</button><br>
<pre id="O"></pre>

Justin Mariner

Posted 2017-05-24T04:49:02.133

Reputation: 4 746

0

Ruby, 143 149 146 bytes

First line of STDIN is the code, the rest is the input.

Whoops, getc doesn't actually exist directly.

a=0
e=i=gets
i.chars{|c|eval Hash["+a+=1 -a-=1 #P;a=0 $P %a=$<.getc||exit !a=0".gsub(?P,"$><<(a%127).chr").scan /(\S)(\S+)/][c]||"";e=a}while e!=0

Try it online!

Value Ink

Posted 2017-05-24T04:49:02.133

Reputation: 10 608

0

C, 145 139 bytes

c,a;f(){for(;~c;c=getchar())c==43?a++:c==45?a--:c==35?putchar(a%127),a=0:c==36?putchar(a%127):c==37?!~(a=getchar())?exit(0):0:c==33?a=0:0;}

That was easy. I definitely recommend compiling with all warnings off (-w). Will golf.

MD XF

Posted 2017-05-24T04:49:02.133

Reputation: 11 605

0

shortC, 95 bytes

c,a;AO;~c;c=G)c==43?a++:c==45?a--:c==35?Pa%127),a=0:c==36?Pa%127:c==37?!~(a=G)?T0:0:c==33?a=0:0;

MD XF

Posted 2017-05-24T04:49:02.133

Reputation: 11 605

0

OCaml, 202 bytes

let c c=print_char(char_of_int(c mod 127))
let rec f a p=match Array.fold_left(fun a->function '+'->a+1|'-'->a-1|'#'->c
a;0|'$'->c a;a|'%'->Scanf.scanf"%c"int_of_char|'!'->0|_->a)a p with
0->()|a->f a p

Takes the program as an array of char because OCaml does not have String.fold*

Try it online!

juloo65

Posted 2017-05-24T04:49:02.133

Reputation: 81