Execute HQ9+

From Rosetta Code
Revision as of 02:50, 2 February 2011 by Thundergnat (talk | contribs) (→‎{{header|Perl 6}}: Made each interpreter instance reusable. Added a REPL.)
Task
Execute HQ9+
You are encouraged to solve this task according to the task description, using any language you may know.

Implement a HQ9+ interpreter or compiler for Rosetta Code.

Ada

see Execute HQ9+/Ada

AutoHotkey

<lang AutoHotkey>; http://www.autohotkey.com/forum/viewtopic.php?p=356268#356268

testCode := "hq9+HqQ+Qq"

MsgBox % RunHQ9Plus(testCode)

---------------------------------

RunHQ9Plus(input) {

 Loop, Parse, input
   If ( A_LoopField = "+" )
     acc++
   Else If ( A_LoopField = "H" )
     output .= "Hello, world!`n"
   Else If ( A_LoopField = "Q" )
     output .= input "`n"
   Else If ( A_LoopField = "9" )
     Loop, 99
     {
       ; following 4 lines could be only 1 long line
       output .= (99+1-A_Index) " bottles of beer on the wall`n"
       output .= (99+1-A_Index) " bottles of beer`n"
       output .= "Take one down, pass it around`n"
       output .= (99-A_Index) " bottles of beer on the wall`n`n"
     }
 Return output

}</lang>

C

<lang c>void runCode(char *code) {

   int c_len = strlen(code);
   int i, accumulator, bottles;
   for(i=0;i<c_len;i++)
   {
       switch(code[i])
       {
           case 'Q':
               printf("%s\n", code);
               break;
           case 'H':
               printf("Hello, world!\n");
               break;
           case '9':
               //Nice bottles song alg. from RC :)
               bottles = 99;
               do {
                   printf("%d bottles of beer on the wall\n", bottles);
                   printf("%d bottles of beer\n", bottles);
                   printf("Take one down, pass it around\n");
                   printf("%d bottles of beer on the wall\n\n", --bottles);
               } while( bottles > 0 );
               break;
           case '+':
               //Am I the only one finding this one weird? :o
               accumulator++;
               break;
       }
   }

};</lang>

C++

Basically the same as the C example, although this has been C++'ified with strings and streams. <lang cpp>void runCode(string code) {

   int c_len = code.length();
   int accumulator, bottles;
   for(int i=0;i<c_len;i++)
   {
       switch(code[i])
       {
           case 'Q':
               cout << code << endl;
               break;
           case 'H':
               cout << "Hello, world!" << endl;
               break;
           case '9':
               //Nice bottles song alg. from RC :)
               bottles = 99;
               do {
                   cout << bottles << " bottles of beer on the wall" << endl;
                   cout << bottles << " bottles of beer" << endl;
                   cout << "Take one down, pass it around" << endl;
                   cout << --bottles << " bottles of beer on the wall" << endl << endl;
               } while( bottles > 0 );
               break;
           case '+':
               //Am I the only one finding this one weird? :o
               accumulator++;
               break;
       }
   }

};</lang>

C#

<lang csharp> using System; using System.Collections.Generic; using System.Linq;

class Program {

   static void RunCode(string code)
   {
       int accumulator = 0;
       var opcodes = new Dictionary<char, Action>
       {
           {'H', () => Console.WriteLine("Hello, World!"))},
           {'Q', () => Console.WriteLine(code) },
           {'9', () => Console.WriteLine(Enumerable.Range(1,100).Reverse().Select(n => string.Format("{0} bottles of beer on the wall\n{0} bottles of beer\nTake one down, pass it around\n{1} bottles of beer on the wall\n", n, n-1)).Aggregate((a,b) => a + "\n" + b))},
           {'+', () => accumulator++ }
       }
       foreach(var c in code)
           opcodes[c]();
   }

} </lang>

Common Lisp

See RCHQ9+/Common Lisp.

E

See RCHQ9+/E.

Forth

<lang forth>variable accumulator

H cr ." Hello, world!" ;
Q cr 2dup type ;
9 99 verses ; \ http://rosettacode.org/wiki/99_Bottles_of_Beer#Forth
+ 1 accumulator +! ;
hq9+ ( "code" -- )
 parse-word 2dup bounds ?do
   i 1 [ get-current literal ] search-wordlist
   if execute else true abort" invalid HQ9+ instruction"
 then loop 2drop ;</lang>

Haskell

See RCHQ9+/Haskell.

Inform 7

<lang inform7>HQ9+ is a room.

After reading a command: interpret the player's command; reject the player's command.

To interpret (code - indexed text): let accumulator be 0; repeat with N running from 1 to the number of characters in code: let C be character number N in code in upper case; if C is "H": say "Hello, world!"; otherwise if C is "Q": say "[code][line break]"; otherwise if C is "9": repeat with iteration running from 1 to 99: let N be 100 - iteration; say "[N] bottle[s] of beer on the wall[line break]"; say "[N] bottle[s] of beer[line break]"; say "Take one down, pass it around[line break]"; say "[N - 1] bottle[s] of beer on the wall[paragraph break]"; otherwise if C is "+": increase accumulator by 1.</lang>

J

From 99 Bottles of Beer <lang J>bob =: ": , ' bottle' , (1 = ]) }. 's of beer'"_ bobw=: bob , ' on the wall'"_ beer=: bobw , ', ' , bob , '; take one down and pass it around, ' , bobw@<:</lang>

The rest of the interpreter: <lang J>H=: smoutput bind 'Hello, world!' Q=: smoutput @ [ hq9=: smoutput @: (beer"0) bind (1+i.-99) hqp=: (A=:1)1 :'0 0$A=:A+m[y'@]

hq9p=: H`H`Q`Q`hq9`hqp@.('HhQq9+' i. ])"_ 0~</lang>

Example use:

<lang J> hq9p 'hqQQq' Hello, world! hqQQq hqQQq hqQQq hqQQq</lang>

Java

See RCHQ9+/Java.

JavaScript

The function below executes a HQ9+ program and returns the program output as a string. <lang javascript>function hq9plus(code) {

 var out = ;
 var acc = 0;
 
 for (var i=0; i<code.length; i++) {
   switch (code.charAt(i)) {
     case 'H': out += "hello, world\n"; break;
     case 'Q': out += code + "\n"; break;
     case '9':
       for (var j=99; j>1; j--) {
         out += j + " bottles of beer on the wall, " + j + " bottles of beer.\n";
         out += "Take one down and pass it around, " + (j-1) + " bottles of beer.\n\n";
       }
       out += "1 bottle of beer on the wall, 1 bottle of beer.\n" +
           "Take one down and pass it around, no more bottles of beer on the wall.\n\n" +
           "No more bottles of beer on the wall, no more bottles of beer.\n" +
           "Go to the store and buy some more, 99 bottles of beer on the wall.\n";
       break;
     case '+': acc++; break;
   }
 }
 return out;

}</lang>

Liberty BASIC

<lang lb>'Try this hq9+ program - "hq9+HqQ+Qq" Prompt "Please input your hq9+ program."; code$ Print hq9plus$(code$) End

Function hq9plus$(code$)

   For i = 1 to Len(code$)
       Select Case
           Case Upper$(Mid$(code$, i, 1)) = "H"
               hq9plus$ = hq9plus$ + "Hello, world!"
           Case Upper$(Mid$(code$, i, 1)) = "Q"
               hq9plus$ = hq9plus$ + code$
           Case Mid$(code$, i, 1) = "9"
               For bottles = 99 To 1 Step -1
                    hq9plus$ = hq9plus$ + str$(bottles) + " bottle"
                    If (bottles > 1) Then hq9plus$ = hq9plus$ + "s"
                    hq9plus$ = hq9plus$ + " of beer on the wall, " + str$(bottles) + " bottle"
                    If (bottles > 1) Then hq9plus$ = hq9plus$ + "s"
                    hq9plus$ = hq9plus$ + " of beer,"  + chr$(13) + chr$(10) + "Take one down, pass it around, " + str$(bottles - 1) + " bottle"
                    If (bottles > 2) Or (bottles = 1) Then hq9plus$ = hq9plus$ + "s"
                    hq9plus$ = hq9plus$ + " of beer on the wall." + chr$(13) + chr$(10)
               Next bottles
               hq9plus$ = hq9plus$ + "No more bottles of beer on the wall, no more bottles of beer." _
                                   + chr$(13) + chr$(10) + "Go to the store and buy some more, 99 bottles of beer on the wall."
           Case Mid$(code$, i, 1) = "+"
               accumulator = (accumulator + 1)
       End Select
       If Mid$(code$, i, 1) <> "+" Then
           hq9plus$ = hq9plus$ + chr$(13) + chr$(10)
       End If
   Next i
   hq9plus$ = Left$(hq9plus$, (Len(hq9plus$) - 2))

End Function</lang>

Perl 6

The spec is kind of vague about how to do error handling... and whether white space is significant... and how the accumulator should be accessed... and pretty much everything else too.

<lang perl6>class HQ9Interpreter {

   has @!code;
   has $!accumulator;
   has $!pointer;

   method run ($code) {
       @!code = $code.comb;
       $!accumulator = 0;
       $!pointer = 0;
       while $!pointer < @!code {
           given @!code[$!pointer].lc {
               when 'h' { say 'Hello world!' }
               when 'q' { say @!code }
               when '9' { bob(99) }
               when '+' { $!accumulator++ }
               default  { note "Syntax error: Unknown command \"{@!code[$!pointer]}\"" }
           }

$!pointer++;

       }
   }
   sub bob ($beer is copy) {
       sub what  { "{$beer??$beer!!'No more'} bottle{$beer-1??'s'!!} of beer" };
       sub where { 'on the wall' };
       sub drink { $beer--; "Take one down, pass it around," }
       while $beer {
           .say for "&what() &where(),", "&what()!",
                    "&drink()", "&what() &where()!", 
       }
   }

}</lang>

Feed it a command string: <lang perl6>my $hq9 = HQ9Interpreter.new; $hq9.run("hHq+++Qq"); say; $hq9.run("Jhq.k+hQ");</lang>

Output:

Hello world!
Hello world!
hHq+++Qq
hHq+++Qq
hHq+++Qq

Syntax error: Unknown command "J"
Hello world!
Jhq.k+hQ
Syntax error: Unknown command "."
Syntax error: Unknown command "k"
Hello world!
Jhq.k+hQ

Or start a REPL (Read Execute Print Loop) and interact at the command line: <lang perl6>my $hq9 = HQ9Interpreter.new; while 1 {

   my $in = prompt('HQ9+>').chomp;
   last unless $in.chars;
   $hq9.run($in)

}</lang>

PicoLisp

<lang PicoLisp>(de hq9+ (Code)

  (let Accu 0
     (for C (chop Code)
        (case C
           ("H" (prinl "Hello, world"))
           ("Q" (prinl Code))
           ("9"
              (for (N 99 (gt0 N))
                 (prinl N " bottles of beer on the wall")
                 (prinl N " bottles of beer")
                 (prinl "Take one down, pass it around")
                 (prinl (dec 'N) " bottles of beer on the wall")
                 (prinl) ) )
           ("+" (inc 'Accu)) ) )
     Accu ) )</lang>

PureBasic

<lang PureBasic>Procedure hq9plus(code.s)

 Protected accumulator, i, bottles
 For i = 1 To Len(code)
   Select Mid(code, i, 1)
     Case "h", "H"
       PrintN("Hello, world!")
     Case "q", "Q"
       PrintN(code)
     Case "9"
       bottles = 99
       While bottles
         PrintN(Str(bottles) + " bottles of beer on the wall, " + Str(bottles) + " bottles of beer,")
         bottles - 1
         PrintN("Take one down, pass it around, " + Str(bottles) + " bottles of beer on the wall.")
       Wend
     Case "+"
       accumulator + 1
   EndSelect
 Next i

EndProcedure

If OpenConsole()

 Define testCode.s = "hq9+HqQ+Qq"
 hq9plus(testCode)
 
 Print(#CRLF$ + #CRLF$ + "Press ENTER to exit"): Input()
 CloseConsole()

EndIf</lang>

Python

See RCHQ9+/Python.

Ruby

See RCHQ9+/Ruby.

Scala

<lang Scala>def hq9plus(code: String) : String = {

   var out = ""
   var acc = 0
   def bottle(num: Int) : Unit = {
       if (num > 1) {
           out += num + " bottles of beer on the wall, " + num + " bottles of beer.\n"
           out += "Take one down and pass it around, " + (num - 1) + " bottle"
           if (num > 2) out += "s"
           out += " of beer.\n\n"
           bottle(num - 1)
       }
       else {
           out += "1 bottle of beer on the wall, 1 bottle of beer.\n" +
               "Take one down and pass it around, no more bottles of beer on the wall.\n\n" +
               "No more bottles of beer on the wall, no more bottles of beer.\n" +
               "Go to the store and buy some more, 99 bottles of beer on the wall.\n"
       }
   }
   def handle(char: Char) = char match {
       case 'H' => out += "Hello world!\n"
       case 'Q' => out += code + "\n"
       case '+' => acc += 1
       case '9' => bottle(99)
   }
   code.toList foreach handle
   out

}

println(hq9plus("HQ9+")) </lang>

Tcl

See RCHQ9+/Tcl.

Ursala

See RCHQ9+/Ursala.