Task

Implement a    HQ9+    interpreter or compiler.




% This program uses the "get_argv" function from PCLU's "useful.lib"
 
hq9plus = cluster is load, run
    rep = string
    own po: stream := stream$primary_output()
 
    bottles = proc (n: int) returns (string)
        if n=0 then return("No more bottles ")
        elseif n=1 then return("1 bottle ")
        else return(int$unparse(n) || " bottles ")
        end
    end bottles
 
    beer = proc ()
        for i: int in int$from_to_by(99,1,-1) do
            stream$putl(po, bottles(i) || "of beer on the wall,")
            stream$putl(po, bottles(i) || "of beer,")
            stream$puts(po, "Take ")
            if i=1 
                then stream$puts(po, "it")
                else stream$puts(po, "one")
            end
            stream$putl(po, " down and pass it around,")
            stream$putl(po, bottles(i-1) || "of beer on the wall!\n")
        end
    end beer
 
    quine = proc (c: rep) stream$puts(po, c) end quine
    hello = proc () stream$putl(po, "Hello, world!") end hello
 
    load = proc (fn: file_name) returns (cvt) signals (not_possible(string))
        prog: array[char] := array[char]$[]
        s: stream := stream$open(fn, "read") resignal not_possible
        while true do
            array[char]$addh(prog, stream$getc(s))
            except when end_of_file: break end
        end
        stream$close(s)
        return(rep$ac2s(prog))
    end load
 
    run = proc (prog: cvt) returns (int)
        acc: int := 0
        for c: char in rep$chars(prog) do
            if     c='h' | c='H' then hello()
            elseif c='q' | c='Q' then quine(prog)
            elseif c='9'         then beer()
            elseif c='+'         then acc := acc + 1
            end
        end
        return(acc)
    end run
end hq9plus
 
start_up = proc ()
    fn: file_name := file_name$parse(sequence[string]$bottom(get_argv()))
    hq9plus$run(hq9plus$load(fn))
end start_up