8
\$\begingroup\$

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!

\$\endgroup\$
  • 3
    \$\begingroup\$ You need to copy the language specifications into the question. Questions shouldn't rely on external links. \$\endgroup\$ – mbomb007 Jun 22 '16 at 14:43
  • 1
    \$\begingroup\$ Original JS interpreter \$\endgroup\$ – mbomb007 Jun 22 '16 at 14:52
  • \$\begingroup\$ Well, Why not do that yourself too? \$\endgroup\$ – user54200 Jun 22 '16 at 14:55
  • 1
    \$\begingroup\$ It's your question. You determine the specs. \$\endgroup\$ – mbomb007 Jun 22 '16 at 14:58
  • 5
    \$\begingroup\$ The 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. \$\endgroup\$ – Aaron Jun 22 '16 at 15:11
1
\$\begingroup\$

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
\$\endgroup\$
  • \$\begingroup\$ Actually, I don't think I can make this short. (+1) \$\endgroup\$ – user54200 Jun 26 '16 at 2:53
4
\$\begingroup\$

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>

\$\endgroup\$
  • \$\begingroup\$ Well,let me wait for some more answers, and find out who is the winner. \$\endgroup\$ – user54200 Jun 23 '16 at 15:43
  • \$\begingroup\$ 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. \$\endgroup\$ – Ismael Miguel Jun 23 '16 at 15:58
  • \$\begingroup\$ @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. \$\endgroup\$ – Bassdrop Cumberwubwubwub Jun 23 '16 at 16:15
  • \$\begingroup\$ You can output the error, telling them something was wrong. \$\endgroup\$ – user54200 Jun 23 '16 at 16:17
  • 1
    \$\begingroup\$ @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. \$\endgroup\$ – Ismael Miguel Jun 23 '16 at 17:57

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy