Write an interpreter for my new programming language

5

1

I have a new programming language called Brussels-Sprout. Brussels-Sprout functions in an array of 256 boolean values, all initialized to zero, has one instruction pointer, and one "bit index".

After each (non-branching) instruction is executed, the instruction pointer is incremented by one. If the instruction pointer goes off the right side of the instruction array, the program terminates. Going off the left side is undefined behavior.

If the bit index is outside the bounds of the array, it should be reset to 0.

Brussels-Sprout uses the following keywords, which are always lowercase:

chew - Invert the value in the array at the bit index. (flip the bit at the bit index)

swallow - Increment the bit index.

vomit - Decrement the bit index.

complain - Output the bit at the bit index to stdout, or equivalent output log.

feedtodog - If the bit at the bit index is 0, skip the next instruction. Otherwise, increment instruction pointer by 1.

playwithfood - If the bit at the bit index is 1, jump backwards 2 instructions. Otherwise, increment instruction pointer by 1.

If an instruction not on this list is executed, undefined behavior occurs.

I'm sure that this sounds like a very certain other turing-tarpit programming language. But since this programming language operates in boolean values and not bytes, I think it is different.

Test cases:

chew complain -> 1
swallow chew swallow chew swallow vomit complain playwithfood -> 11
chew complain swallow chew complain swallow chew complain swallow complain -> 1110
chew feedtodog undefinedbehaviour complain -> 1

Your challenge is to write an interpreter for Brussels-Sprout. This is code golf, so shortest program wins!

Mason Watmough

Posted 2016-04-01T22:34:52.900

Reputation: 225

2How do we get the input? Can we take it as a list? – Rɪᴋᴇʀ – 2016-04-01T22:40:38.463

2And do all the bits start at 0 or 1? – Rɪᴋᴇʀ – 2016-04-01T22:42:16.537

10If those are the only options for interacting with Brussels sprouts, clearly you haven't had them roasted with olive oil, salt, and pepper. – Alex A. – 2016-04-01T22:45:38.567

9

Hey Mason! It is nice that you want to contribute to this site, but writing a good challenge is not easy and also requires some experience. 2 of your 3 challenges got closed so far and this one might be put on hold for being unclear as well. I would really recommend that you use the sandbox in the future, so you can get feedback and work potential flaws out before you post your challenge to the main site. However, I hope you stick around and have a great time here!

– Denker – 2016-04-01T22:46:32.807

4Also you should really add some testscases, since this is not a very trivial task to solve and it's hard to say if a potential solution works correctly. But please note that testcases are not a replacement for a clear specification of the I/O formats which is definitely missing here. – Denker – 2016-04-01T22:48:47.927

2@AlexA. Mmmm. Fried in coconut oil with garlic, a bit of ginger, and a dash of Tabasco. – James – 2016-04-02T00:13:21.783

You should specify what happens if unknown instructions are provided. My answer quits the program when they are found. CatsAreFluffy's answer may execute arbitrary existing instructions or just quit the program. – Victor Stafusa – 2016-04-02T01:08:27.443

Why is this closed? – CalculatorFeline – 2016-04-02T01:42:12.183

1@CatsAreFluffy Probably because it is underspecified, which is sad but true. It doesn't says for example, what would happen if it branches to outside the instruction array (something that we both exploited for halting) nor it specified what would happen if an invalid instruction is given (a hole that both your answer and my new answer exploits for golfing). Also, it lacks test cases, which are very important to give confindence against possibly buggy interpreters. – Victor Stafusa – 2016-04-02T01:59:50.823

A few test cases and some specification. – CalculatorFeline – 2016-04-02T02:27:19.053

1@CatsAreFluffy I think that chew swallow chew swallow vomit complain playwithfood is an infinite loop, because playwithfood would go back two instructions to vomit. And this would loop the sequence vomit complain playwithfood forever. The vomit instruction will keep decrementing the bit index forever, but it would be kept resetted to zero everytime that it tries to go to -1 (which is outside the bounds of the array). My interpreter loops forever on that keeping outputting 1s continuously forever. – Victor Stafusa – 2016-04-02T03:55:57.323

1Oops, please swallow once before running that program. But undefinedbehavior should not be in a testcase. – CalculatorFeline – 2016-04-02T13:10:05.270

1Is this reopenable now? – CalculatorFeline – 2016-04-02T15:01:54.447

Answers

3

Python 3 2, 200 187 149 146 136 bytes

-9bytes thanks to Cyoce

def f(n,a=[0]*256,x=0,z=0):
 while 1:y=a[z];exec'a[z]^=~1 z+=1 z-=1 print-y/2 if~y:x+=1 if~~y:x-=3'.split()["wlipdy".find(n[x][3])];x+=1

Input as list, exits by error. Bonus feature: you can specify start array, start IP, and start DP in that very exact order.

CalculatorFeline

Posted 2016-04-01T22:34:52.900

Reputation: 2 608

A few suggestions: first, x=0;z=0 can be x=z=0. Second, a=[0 for x in range(256)] can be a=[0]*256. Third, instead of the if statement chain, use a dictionary of strings then use exec on the result. – Cyoce – 2016-04-02T01:02:12.020

Did #1, I didn't know #2, considered #3. – CalculatorFeline – 2016-04-02T01:10:43.057

Goodbye bytes! (I miss my fluffy) – CalculatorFeline – 2016-04-02T01:11:43.307

Well 0 and -1, but yes, but I just got rid of those. But buts are confusing, but they're fun, so yeah. – CalculatorFeline – 2016-04-02T01:16:12.117

I think you can save a few bytes by switching to Python 2 if you're interested (you can remove the parens on print and exec) – Cyoce – 2016-04-02T01:19:30.580

-3 bytes. I'll take it. – CalculatorFeline – 2016-04-02T01:20:42.273

your solution seems to be complaining -2 instead of 1. – Cyoce – 2016-04-02T01:43:36.097

...-2? How? It should only spit out 0 and -1. – CalculatorFeline – 2016-04-02T01:44:35.493

fixed version, saved bytes (replace the dictionary method with this): 'a[z]^=~1|z+=1|z-=1|print -y/2|if~y:x+=1|if y:x-=3'.split('|')["wlipdy".find(n[x][3])] – Cyoce – 2016-04-02T01:47:23.227

Implementing. But what program spat out -2? – CalculatorFeline – 2016-04-02T01:49:44.447

Aaugh, it does it every time. But still -3 more bytes: drop the space after print and use ~~ in the last if to allow split(). – CalculatorFeline – 2016-04-02T01:57:29.620

Apparently + works instead. – CalculatorFeline – 2016-05-02T04:28:06.080

0

Java 8

1. Relaxed version (301 283 280 277 275 bytes)

Golfed as this:

class C{static int i,j,x,b[]=new int[256];public static void main(String[]z){Runnable[]r=new Runnable[26];r[20]=()->b[i]=-~x;r[9]=()->i++;r[6]=()->i--;r[13]=()->System.out.print(x);r[1]=()->j+=x;r[22]=()->j-=3*x;for(;;){x=b[i];r[z[j].charAt(3)-99].run();i*=~(i>>8)&1;j++;}}}

Somewhat ungolfed version (whitespaces and comments added):

class C {
    // Declared as static so they may be changed inside the lambdas without the compiler complaining about that.
    // i is the bit index and j is the instruction pointer.
    // x is just to avoid refering to b[i] in order to save 3 bytes.
    // b is our array. Uses ints instead of booleans to save bytes and use some clever math tricks.
    static int i, j, x, b[] = new int[256];

    public static void main(String[] z) {

        // An array of lambdas. The index is the ascii code of 4th letter of the command name subtracted from 99.
        // Thanks for CatsAreFluffy for telling about the 4th letter trick.
        Runnable[] r = new Runnable[26];
        r[20] = () -> b[i] = -~x; // chew
        r[9] = () -> i++; // swallow
        r[6] = () -> i--; // vomit
        r[13] = () -> System.out.print(x); // complain
        r[1] = () -> j += x; // feedtodog. It is 1 or 0 instead of 2 and 1 due to the j++ down there.
        r[22] = () -> j -= 3 * x; // playwithfood. It is -3 or 0 instead of -2 and +1 due to the j++ down there.

        // This is the interpreter itself. Runs forever (or until an exception is raised).
        for (;;) {
            x = b[i];

            // Fetch an instruction and runs it. May throw an exception or execute some arbitrary instruction if the instruction name is unknown or was mistyped.
            r[z[j].charAt(3) - 99].run();

            // Resets the bit index if out of range.
            i *= ~(i >> 8) & 1;

            // Next instruction. No special treatment for branching needed because they already considers this.
            j++;
        }
    }
}

We surely should have testcases. I am not sure if there is some bug hiding out there.

Anyway I run it with that (input is given as command line arguments):

chew complain swallow chew complain swallow chew complain swallow complain

And here is the output:

1110
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 10
    at C.main(C.java:1)

The ArrayIndexOutOfBoundsException is the standard way to finish the program, it is not a bug!

BTW, this answer abuses the fact that the 4th char of each command is different than each other (thanks CatsAreFluffy). This means that whew is interpreted as chew and that yellow is interpreted as swallow.

2. Strict version (354 337 334 331 329 bytes)

If strict conformance to command names are required (and just looking for the 4th letter in command's name is not enough), then I have this alternative similar answer with 329 bytes:

import java.util.*;class C{static int i,j,x,b[]=new int[256];public static void main(String[]z){List o=Arrays.asList("chew","swallow","vomit","complain","feedtodog","playwithfood");Runnable[]r={()->b[i]=-~x,()->i++,()->i--,()->System.out.print(x),()->j+=x,()->j-=3*x};for(;;){x=b[i];r[o.indexOf(z[j])].run();i*=~(i>>8)&1;j++;}}}

Somewhat ungolfed version (whitespaces and comments added):

// Needed for java.util.List and java.util.Arrays.
import java.util.*;

class C {
    // Declared as static so they may be changed inside the lambdas without the compiler complaining about that.
    // i is the bit index and j is the instruction pointer.
    // x is just to avoid refering to b[i] in order to save 3 bytes.
    // b is our array. Uses ints instead of booleans to save bytes and use some clever math tricks.
    static int i, j, x, b[] = new int[256];

    public static void main(String[] z) {

        // Declared as a list so we can use the indexOf method further down. Screw up the generics.
        List o = Arrays.asList("chew", "swallow", "vomit", "complain", "feedtodog", "playwithfood");

        // An array of lambdas.
        Runnable[] r = {
            () -> b[i] = -~x, // chew
            () -> i++, // swallow
            () -> i--, // vomit
            () -> System.out.print(x), // complain
            () -> j += x, // feedtodog. It is 1 and 0 instead of 2 and 1 due to the j++ down there.
            () -> j -= 3 * x // playwithfood. It is -3 and 0 instead of -2 and +1 due to the j++ down there.
        };

        // This is the interpreter itself. Runs forever (or until an exception is raised).
        for (;;) {
            x = b[i];

            // Fetch an instruction and runs it. Throws an exception on unknown or mistyped instructions.
            r[o.indexOf(z[j])].run();

            // Resets the bit index if out of range.
            i *= ~(i >> 8) & 1;

            // Next instruction. No special treatment for branching needed because the branching instructions already considers this.
            j++;
        }
    }
}

Victor Stafusa

Posted 2016-04-01T22:34:52.900

Reputation: 8 612

I don't Java, but can you shorten the list of strings by using a separator then splitting it into an array? i.e. javaSplitFunction("chew|swallow|vomit|complain|feedtodog|playwithfood|") – Cyoce – 2016-04-02T01:07:24.017

@Cyoce I could simply use an array instead and it would be easier and shorter, but then this screws me up further down when I have to use the List.indexOf method. To fix that screw-up, I would end with a longer answer. – Victor Stafusa – 2016-04-02T01:11:15.130

1Can you use a HashMap from Strings (or char) to Runnables? The fourth char of each command is distinct. – CalculatorFeline – 2016-04-02T01:24:22.203

@Cyoce No, because doing that would violate java's type system and produce a compile error. :( – Victor Stafusa – 2016-04-02T01:52:56.193

@CatsAreFluffy Edited. Thanks. :) – Victor Stafusa – 2016-04-02T01:53:08.257

Your strict answer is no longer "older'. – CalculatorFeline – 2016-04-02T02:13:28.320

@CatsAreFluffy Yes. Now I maintain the two versions. The relaxed is if the 4th char trick is allowed by the OP and the strict if it is not. :D – Victor Stafusa – 2016-04-02T02:15:30.860

1@VictorStafusa nevermind I'm stupid I forgot those were booleans. – Cyoce – 2016-04-02T02:44:16.933

The spec says that unknown words are undefined behavior, so the relaxed version is valid. Also, please add a bytecount tot he title (it breaks the graduation userscript leaderboard otherwise) – CalculatorFeline – 2017-06-22T15:35:51.470

0

SpecBAS (modern Sinclair BASIC interpreter) 202 Bytes

1 INPUT a$:DIM b(256)BASE 0,i$(),a$(SPLIT a$," "):DO 6:READ b,i$(b):LOOP:b=0,i=1:DO:c=b(b):EXECUTE i$(CODE a$(i,4)-99):b&=255,i+=1:LOOP:DATA 20,"b(b)=1-c",9,"b+=1",6,"b-=1",13,"?c;",1,"i+=c",22,"i-=3*c"

Or to make it a little clearer:

1 INPUT a$:
  DIM b(256) BASE 0,i$(),a$(SPLIT a$," "):
  DO 6:
    READ b,i$(b):
  LOOP:
  b=0,i=1:
  DO:
    c=b(b):
    EXECUTE i$(CODE a$(i,4)-99):
    b&=255,i+=1:
  LOOP:
  DATA 20,"b(b)=1-c",9,"b+=1",6,"b-=1",13,"?c;",1,"i+=c",22,"i-=3*c"

Uses some features that weren't available in the 80s, so there's that. Also shameless rips off some of the techniques in other entries. Sorry about that!

Input is from the user at runtime. Execution ends with an error "Subscript wrong".

ZXDunny

Posted 2016-04-01T22:34:52.900

Reputation: 39