Execute HQ9+/E

From Rosetta Code
Execute HQ9+/E is an implementation of HQ9+. Other implementations of HQ9+.
Execute HQ9+/E is part of RCHQ9+. You may find other members of RCHQ9+ at Category:RCHQ9+.

The original author of this example (written in E) would like to note the following deficiencies in the HQ9+ specification:

  • The treatment of unrecognized characters other than whitespace is unspecified. However, the only implementation provided, in OCaml, rejects them, so this implementation also does. Additionally, rejecting unknown characters ensures that the compiler will not incorrectly compile HQ9+ programs using future extensions.
  • While the given "qqqq" example implies that whitespace (specifically, trailing newlines) is permissible and that operation codes are case-insensitive, neither of these are explicitly stated.
  • The initial value of the accumulator is unspecified. This implementation has chosen 11472, which is of course the value such that executing the HQ9+ program "HQ9+" will result in the accumulator having a value equal to the length of the program's output.

def makeSeqExpr := <elang:evm.makeSeqExpr>
def makeLiteralExpr := <elang:evm.makeLiteralExpr>
def eParser := <elang:syntax.makeEParser>

# the extra $ protects this as an RC example by breaking up the close tag
def `@_<syntaxhighlight lang="e">@beerSource</la${""}ng>@_` :=
  <http://rosettacode.org/mw/index.php?title=99_Bottles_of_Beer&action=raw>.getText()
def beerProgram := eParser(beerSource)

def opcodes := [
  'H' => def h := e`stdout.println("Hello, World!")`,
  'Q' => def q := e`stdout.println(mySource)`,
  '9' =>          e`{ def println := stdout.println; $beerProgram }`,
  '+' =>          e`::"the accumulator" += 1`,
  'h' => h,
  'q' => q,
]

/** HQ9+ compiler.
    
    The provided program is compiled into a function taking the output stream as an argument. */
def ::"RCHQ9+"(program :String) {
  return e`
    def ::"RCHQ9+ program"(stdout) {
      def mySource := ${makeLiteralExpr(null, program, null)}
      var ::"the accumulator" := 11472
      ${
        var exprs := []
        for ch ? (E.toString(ch) =~ rx`\S`) in program {
          exprs with= opcodes.fetch(ch, fn {
            throw(`Unrecognized HQ9+ opcode: $ch`)
          })
        }
        makeSeqExpr(null, exprs, null)
      }
    }
  `.eval(safeScope)
  # Maintenance note: Do not change this from safeScope without considering
  # the consequences as the beerProgram is incorporated from a wiki source
  # over unsecured HTTP.
}