Write a chicken interpreter!

8

2

You have to write an interpreter for a cool language called Chicken!

You should read a Chicken program from a file, standard input, program or function arguments, or whatever is most convenient for your language, as well as input to the program.

You should print or return the result of interpreting the program according to the Chicken language specification.

More description about the language.


Chicken Program Overview

Chicken operates on a single stack, which composes its entire memory model. As instructions are executed, the program will push and pop values from the stack, but there are also instructions that allow the program to modify other parts of the stack at will.

There are three segments in the stack:

  1. The registers, at indices 0 and 1. Index 0 is a reference to the stack itself, and index 1 is a reference to the user input. Mostly used for instruction 6 (see below).
  2. The loaded code: for each line of code there is cell in this segment that contains the number of "chicken"s in the line. This is padded with a 0 (opcode for terminate program) at the end.
  3. The actual program stack, where values are pushed/popped as the program runs. Note that the segments are not isolated, which means that it is possible to create self-modifying code or execute code from this segment of the stack space.

The Chicken ISA

Chicken's instruction set is based around the number of times the word "chicken" appears on each line of the program. An empty line terminates the program and prints the topmost value in the stack.

The Chicken instruction set, by number of "chicken"s per line:

  1. Push the literal string "chicken" to the stack
  2. Add top two stack values as natural numbers and push the result.
  3. Subtract top two values as natural numbers and push the result.
  4. Multiply top two values as natural numbers and push the result.
  5. Compare two top values for equality, push 1 if they are equal and 0 otherwise.
  6. Look at the next instruction to determine which source to load from: 0 loads from stack, 1 loads from user input. Top of stack points to address/index to load from the given source; load that value and push it onto stack. Since this is a double wide instruction, the instruction pointer skips the instruction used to determine the source.
  7. Top of stack points to address/index to store to. The value below that will be popped and stored in the stack at the given index.
  8. Top of stack is a relative offset to jump to. If the value below that is truthy, then the program jumps by the offset.
  9. Interprets the top of the stack as ascii and pushes the corresponding character.
  10. (10 + N) Pushes the literal number n-10 onto the stack.

Example

Assume the program is:

chicken chicken chicken chicken chicken chicken chicken chicken chicken chicken chicken
chicken chicken chicken chicken chicken chicken
(an empty line)

(A cat program. Note that the empty line is required because of the preceding line having 6 "chicken".)

Input provided to Chicken program

Chicken

Output

Chicken

The Chicken.js reference implementation.


Error detection

The interpreter should leave an error and terminate when any word not "chicken" is present in the source.


Good luck!

user54200

Posted 2016-06-22T14:22:34.897

Reputation:

3You need to copy the language specifications into the question. Questions shouldn't rely on external links. – mbomb007 – 2016-06-22T14:43:52.650

1Original JS interpreter – mbomb007 – 2016-06-22T14:52:35.113

Well, Why not do that yourself too? – None – 2016-06-22T14:55:54.480

1It's your question. You determine the specs. – mbomb007 – 2016-06-22T14:58:56.430

5The file input restricts this challenge to specific languages. For instance, it makes it impossible to produce a chicken answer, which I'm sure you'll agree is disappointing. – Aaron – 2016-06-22T15:11:04.783

Languages that don't use file input, can use user inputs. since the chicken program ends when there are no "chicken" in a line, You can use that. – None – 2016-06-22T15:14:47.547

@Aaron Anyway, good point. – None – 2016-06-22T15:16:12.197

How to handle invalid code? Can we just assume that all input programs are valid chicken programs? – Bassdrop Cumberwubwubwub – 2016-06-22T15:20:00.287

Good point. The program should have an error if it has any word except "chicken". anything else is a valid chicken program. – None – 2016-06-22T15:23:59.253

You should update your post with the points noted in the comments. As of now, this challenge is quite vague and will most likely either be downvoted, or closed as unclear. – Bassdrop Cumberwubwubwub – 2016-06-22T15:25:39.270

Also your 8 chicken and 9 chicken are the same instruction. Maybe it's wise to copy the instructions from the esolang page – Bassdrop Cumberwubwubwub – 2016-06-22T15:26:40.507

Oh lol that is wierd – None – 2016-06-22T15:27:56.833

Languages that don't use file input, can use user inputs. since the chicken program ends when there are no "chicken" in a line, You can use that. That's a very important distinction. You should [edit] that into the original post. – James – 2016-06-22T16:35:10.623

I already did that. – None – 2016-06-23T08:16:37.547

it will be awesome if somebody write this in chicken – user902383 – 2016-06-23T13:46:22.507

@user902383 Yeah. – None – 2016-06-23T16:25:43.647

sorry if this is off-topic but can anyone explain me what 6 7 8 instructions do ? i don't understand them from esolang wiki and this article - also is 9 instruction something like getchar() ? – None – 2016-06-24T16:05:29.410

I've edited the challenge to open it up a little, and clarify some points. The error to be given was not specified, so I think just termination should be fine. – cat – 2016-06-24T18:24:37.267

Can you clarify what six chickens does? "Double wide instruction. Next instruction indicates source to load from. 0 loads from stack, 1 loads from user input. Top of stack points to address/index to load onto stack." What does "double wide" mean? What does "load" mean? What does "points to address/index" mean? Edit: #7 & #8 are unclear also. Not everyone is a compsci major. At least link to an explanation, if it's too long to include. – msh210 – 2016-06-24T20:18:51.823

I have included an esolang site. but I have not found a site that does explain it better. – None – 2016-06-25T01:23:34.170

Yeah, that should help. – None – 2016-06-25T01:28:24.507

I tried to clarify the spec more based on that implementation. – Value Ink – 2016-06-25T02:25:49.553

Good.Now that makes more sense. – None – 2016-06-25T02:52:24.900

Answers

1

Ruby, 335 bytes

Takes the input file name as a command line argument and takes user input (for instruction #6) from STDIN.

Because of Ruby "truthy" (everything except false and nil) being different from the Javascript "truthy" (Ruby truthy plus 0, empty strings, etc.), there might be some edge cases where programs that work fine on a JS interpreter fail on this one because of instruction #8, such as if "" is on the stack. I've fixed the biggest case, though, which is falsy 0.

Works with the test program and the Hello World program on the Chicken website.

+(/^(#{c='chicken'}|\s)*$/m=~f=$<.read+"

")
s=[0,STDIN.read]+f.lines.map{|l|l.split.size};s[0]=s;i=1
s<<(s[i]<10?[->{c},->{x,y=s.pop 2;x+y},->{x,y=s.pop 2;x-y},->{s.pop*s.pop},->{s.pop==s.pop},->{s[s[i+=1]][s.pop]},->{s[s.pop]=s.pop;s.pop},->{l,k,j=s.pop 3;i+=j if k&&k!=0;l},->{s.pop.chr}][s[i]-1][]:s[i]-10)while s[i+=1]>0
$><<s.pop

Explanation

The interpreter immediately starts off by running a regex match /^(chicken|\s)*$/m against the entire file ($<.read), which makes sure the file contains nothing but chicken and whitespace. In Ruby, this operator returns the index for the match, or nil if it wasn't found.

Two byte-saving tricks are used here: instead of directly matching chicken, the string substitution operator #{} is used instead to also assign that string to a variable for later (saves 1 byte), and when storing the contents of the file to a variable for processing, it appends two newlines to allow the lines function later to naturally append an extra 0 to the end of the instruction set. (Two are needed because of ignored trailing newline, which is necessary for the Chicken program.)

The error used is NoMethodError: undefined method '+@' for nil:NilClass, which is done by wrapping the regex match in parens and putting a + in front. If the file matches the pattern, you get +0, which evaluates to 0 and proceeds normally.

Next, the stack is assembled. The initial list must be created before the self-reference to the stack can be assigned, so a placeholder is used and then replaced. The instruction pointer is set to 1 instead of 2 because post-increment operators don't exist in Ruby.

Finally, it uses the lambda trick from @BassdropCumberwubwubwub to determine what to push on the stack next. If an operation doesn't push anything onto the stack, the interpreter simply pops an extra value so the stack stays the same. (This saves bytes over adding a push operation into every single lambda.)

Ungolfed code:

f = $<.read + "\n\n"
+(/^(chicken|\s)*$/m =~ f)
s = [0, STDIN.read] + f.lines.map{|l|l.split.size}
s[0] = s
i = 1

while s[i += 1] > 0
    if s[i] < 10
        s.push [
            ->{'chicken'},
            ->{
                x,y = s.pop 2
                x+y
                },
            ->{
                x,y = s.pop 2
                x-y
                },
            ->{s.pop*s.pop},
            ->{s.pop==s.pop},
            ->{s[s[i+=1]][s.pop]},
            ->{s[s.pop]=s.pop;s.pop},
            ->{
                l,k,j=s.pop 3
                i+=j if k&&k!=0
                l
                },
            ->{s.pop.chr}
        ][s[i] - 1][]
    else
        s.push(s[i] - 10)
    end
end

print s.pop

Value Ink

Posted 2016-06-22T14:22:34.897

Reputation: 10 608

Actually, I don't think I can make this short. (+1) – None – 2016-06-26T02:53:09.933

4

Javascript ES6, 398 bytes

By far the longest golf I have ever done, I'm sure this can be improved but my brain doesn't recognize anything other than chicken at this point.

(a,b)=>{for(c='chicken',s=[j=0,b,...A=a.split`
`.map(m=>m.split(c).length-1)],i=A.length+2;j<A.length;([_=>s[++i]=c,_=>s[--i]=s[i]+s[i+1],_=>s[--i]=s[i]-s[i+1],_=>s[--i]=s[i]*s[i+1],_=>s[--i]=s[i]==s[i+1],_=>s[i]=s[2+j++]?b[s[i]]:s[s[i]],_=>s[s[i--]]=s[i--],_=>j+=s[--i]?s[--i+2]:0,_=>s[i]=String.fromCharCode(s[i])][s[j+2]-1]||(_=>s[++i]=s[j+1]-10))(j++));return /[^chicken \n]\w/g.test(a)?0:s[i]}

I will edit the explanation in when my brain starts functioning again. Here's a slightly ungolfed version for now.
Outputs a falsey value (0) for everything which isn't chicken

(a,b)=>{
    for(c='chicken',s=[j=0,b,...A=a.split`
    `.map(m=>m.split(c).length-1)],i=A.length+2; // loop init
    j<A.length; // loop condition
    ( // everything else
        [
            _=>s[++i]=c,
            _=>s[--i]=s[i]+s[i+1],
            _=>s[--i]=s[i]-s[i+1],
            _=>s[--i]=s[i]*s[i+1],
            _=>s[--i]=s[i]==s[i+1],
            _=>s[i]=s[2+j++]?b[s[i]]:s[s[i]],
            _=>s[s[i--]]=s[i--],
            _=>j+=s[--i]?s[--i+2]:0,
            _=>s[i]=String.fromCharCode(s[i])
        ][s[j+2]-1]
        ||(_=>s[++i]=s[j+1]-10)
    )(j++)
);
return /[^chicken \n]\w/g.test(a)?0:s[i]}

Try it here

f=
  (a,b)=>{for(c='chicken',s=[j=0,b,...A=a.split`
`.map(m=>m.split(c).length-1)],i=A.length+2;j<A.length;([_=>s[++i]=c,_=>s[--i]=s[i]+s[i+1],_=>s[--i]=s[i]-s[i+1],_=>s[--i]=s[i]*s[i+1],_=>s[--i]=s[i]==s[i+1],_=>s[i]=s[2+j++]?b[s[i]]:s[s[i]],_=>s[s[i--]]=s[i--],_=>j+=s[--i]?s[--i+2]:0,_=>s[i]=String.fromCharCode(s[i])][s[j+2]-1]||(_=>s[++i]=s[j+1]-10))(j++));return /[^chicken \n]\w/g.test(a)?0:s[i]}

i.innerHTML = f(`chicken chicken chicken chicken chicken chicken chicken chicken chicken chicken chicken
chicken chicken chicken chicken chicken chicken
`, 'Hello world!')
<pre id=i>

Bassdrop Cumberwubwubwub

Posted 2016-06-22T14:22:34.897

Reputation: 5 707

Well,let me wait for some more answers, and find out who is the winner. – None – 2016-06-23T15:43:16.567

This fails the "Error detection". You can do so by adding if(!/^(chicken\s?)+$/.test(a))throw'There are any words except "chicken".'; right at the beggining of your interpreter. – Ismael Miguel – 2016-06-23T15:58:08.133

@Matthew, what are your thoughts on that? There are certain languages which don't have an error type, those can usually output a falsey value instead. It's kinda vague in the OP so I assumed this was fine. – Bassdrop Cumberwubwubwub – 2016-06-23T16:15:05.683

You can output the error, telling them something was wrong. – None – 2016-06-23T16:17:48.620

@Matthew I'm sorry, this is still too vague. What is gently? 0 is gently in my eyes.. – Bassdrop Cumberwubwubwub – 2016-06-23T16:21:47.217

I mean: Do something more than just terminating the program. – None – 2016-06-23T16:24:35.080

1@BassdropCumberwubwubwub What the O.P. meant is to, for example, throw an exception or output something to stderr or exit the program with a non-zero code. Something that shows that something is not right. In Javascript, you can throw an exception, return an Error object, show an alert, write to the console using console.erro() or anything similar. – Ismael Miguel – 2016-06-23T17:57:22.597

The spec would be much more clear if you can just exit on non-chicken words. – cat – 2016-06-24T18:21:31.800

Including a bogus word with all the letters in "chicken", such as kiche or something, doesn't cause the system to output 0. – Value Ink – 2016-06-25T05:55:01.317

@KevinLau-notKenny So true. – None – 2016-06-26T02:57:03.983