RHODES UNIVERSITY


Computer Science 301 - 2012 - Programming Language Translation

Well, here you are. Here is the free information you have all been waiting for, with some extra bits of advice:


Preliminary to the examination

The code below may look vaguely familiar. It is a Cocol description of a "four function" integer calculator with the addition of a memory of 26 cells, named by the 26 lower case letters of the standard alphabet. The calculator also allows one to read values into these cells, and to compute values for expressions which might either be printed or stored in variables. Typical input for this calculator might read

             a = 12;
             b = 15;
             read(c);
             write(a + b * c);

(The C# version is almost identical, of course.)

    import library.*;

    COMPILER Calc  $CN
    /* Simple four function calculator with 26 memory cells - Coco/R for Java
       P.D. Terry, Rhodes University, 2012 */

      static int[] mem = new int[26];

    CHARACTERS
      lf         = CHR(10) .
      digit      = "0123456789" .
      letter     = "abcdefghijklmnopqrstuvwxyz" .

    TOKENS
      number     = digit { digit } .
      variable   = letter .

    PRAGMAS
      CodeOn     = "$C+" .
      CodeOff    = "$C-" .

    COMMENTS FROM "//" TO lf
    COMMENTS FROM "/*" TO "*/"

    IGNORE CHR(0) .. CHR(31)

    PRODUCTIONS
      Calc                               (. int index = 0; int value = 0; .)
      = { (   Variable<out index>
              WEAK "="
              Expression<out value>      (. mem[index] = value; .)
            | "write" "("
              Expression<out value>      (. IO.writeLine(value); .)
              { ","
                Expression<out value>    (. IO.writeLine(value); .)
              } ")"
            | "read" "("
                Variable<out index>      (. mem[index] = IO.readInt(); .)
              ")"
           ) SYNC ";"
        } EOF .

      Expression<out int expVal>         (. int expVal1 = 0; .)
      = Term<out expVal>
        {   "+" Term<out expVal1>        (. expVal += expVal1; .)
          | "-" Term<out expVal1>        (. expVal -= expVal1; .)
        } .

      Term<out int termVal>              (. int termVal1 = 0; .)
      = Factor<out termVal>
        {   "*" Factor<out termVal1>     (. termVal *= termVal1; .)
          | "/" Factor<out termVal1>     (. if (termVal1 == 0) SemError("division by zero");
                                            else termVal /= termVal1; .)
          | "%" Factor<out termVal1>     (. if (termVal1 == 0) SemError("division by zero");
                                            else termVal %= termVal1; .)
        } .

      Factor<out int factVal>            (. factVal = 0;
                                            int index, factVal2; .)
      =   number                         (. try {
                                              factVal = Integer.parseInt(token.val);
                                            } catch (NumberFormatException e) {
                                              factVal = 0; SemError("number out of range");
                                            } .)
        | Variable<out index>            (. factVal = mem[index]; .)
        | "(" Expression<out factVal>
          ")" .

      Variable<out int index>
      = variable                         (. index = token.val.charAt(0) - 'a'; .) .

    END Calc.

The exercise for the first part of today is to make some quite far-reaching extensions to this grammar and calculator to allow a user to define zero or more simple integer functions ahead of the statements that will process the expressions in terms of the variables and constants they incorporate.

In particular cases one might be tempted to do this for a known set of functions by hard-coding them into the system - for example by extending Factor as follows to incorporate a very simple (restrictive) SQR function:

      Factor<out int factVal>            (. factVal = 0;
                                            int index, factVal2; .)
      =   number                         (. try {
                                              factVal = Integer.parseInt(token.val);
                                            } catch (NumberFormatException e) {
                                              factVal = 0; SemError("number out of range");
                                            } .)
        | Variable<out index>            (. factVal = mem[index]; .)
        | "SQR" "("  Variable<out index> (. factVal = mem[index] * mem[index]; .)
          ")"
        | "(" Expression<out factVal>
          ")" .

However, this does not easily generalize, and a different approach might be as follows: Change the actions in the above grammar so that, rather than obey each statement as it is analysed, the system will generate PVM code for a simple program which can then be executed on the PVM - that is, effectively write something like a subset of the extant Parva compiler, (but very much simpler).

With this in mind, the exercise for today is to use Coco/R to build a system for running a "program" like the following:

      // User defined functions precede the statements that might require them

      SQR(x) returns x * x;

      Sum(x, y, z) returns x + y + z;

      // Typical statements that use these functions

      read("Supply three numbers ", x, y, z);
      a = Sum(x, y, z);
      s = SQR(x);
      write("Sum is ", a, " x^2 is ", s);

That should keep you busy for a few hours!

In adopting this approach you can make use of the files provided in the examination kit free1j.zip (Java version) or free1c.zip (C# version) which you will find on the course website. In particular, you will find

In addition you will find, in the root folder and the CalcPVM folder:

It is suggested that you proceed as follows:

The way in which the PVM handles function calls is described in chapter 14 of the text. Since you have not worked with the new opcodes FHDR, CALL and RET before, it will not be giving too much away to illustrate the sort of code that should be generated for a simple function definition, and for a corresponding call of this function.

Allowing ourselves the luxury of using names and labels for illustration, rather than the numerical offsets that are required in the code proper:

         Fun(x, y, z) returns (x + y) / z;

             Sum   LDL     x      Push value of argument x
                   LDL     y      Push value of argument y
                   ADD            Now have value of x + y at TOS
                   LDL     z      Push value of argument z
                   DIV            Now have value of (x + y) / z at TOS
                   STL     0      Store result on bottom of stack frame
                   RET            Return to caller

         a = 2 * Fun(x, y, z);

                   LDC     2      Push 2 on stack in anticipation of later multiplication
                   FHDR           Create standard stack frame
                   LDL     x      Push value of first argument onto frame header
                   LDL     y      Push value of second argument onto frame header
                   LDL     z      Push value of third argument onto frame header
                   CALL    Fun    Call function - returned value left at TOS
                   MUL            Will now have the value of 2 * Fun(x, y, z) at TOS
                   STL     a      Store on variable a

         b = Fun(100, 50, z) - 4;

                   FHDR           Create standard stack frame
                   LDC     100    Push value of first argument onto frame header
                   LDC     50     Push value of second argument onto frame header
                   LDL     z      Push value of third argument onto frame header
                   CALL    Fun    Call function - returned value left at TOS
                   LDC     4      Push 4 on stack ready to subtract
                   SUB            Will now have the value of Fun(100, 50, z) - 4 at TOS
                   STL     b      Store on variable b

Have fun, and good luck!