Computer Science 3 - 2001

Programming Language Translation


Practical for Week 17, beginning 13 August 2001 - Solutions

Complete solutions (sources) can be found on the course WWW pages in the file PRAC17A.ZIP.


Task 2 - Extensions to the Simple Calculator

All that was needed here was to extend the last production:

   COMPILER Calc  $XCN
   /* Extended simple four function calculator
      P.D. Terry, Rhodes University, 2001 */

   CHARACTERS
     digit      = "0123456789" .
     hexdigit   = digit + "ABCDEF" .

   IGNORE CHR(1) .. CHR(31)

   TOKENS
     decNumber  = digit { digit } .
     hexNumber  = "$" hexdigit { hexdigit } .

   PRODUCTIONS
     Calc       = { Expression "=" } EOF .
     Expression = Term { "+" Term  |  "-" Term } .
     Term       = Factor { "*" Factor |  "/" Factor } .
     Factor     =   decNumber | hexNumber
                  | "sqrt" "(" Expression ")"
                  | "(" Expression ")" .
   END Calc.


Task 3 - Stack assembler

Be careful to split the opcodes into the three categories - those that take a numeric address, those that take no address, and PRS as a special case. Note how the optional sign is introduced into the number token. Here is a solution that uses the comment facility in Cocol. Note that we have to be very careful here, as line marks are actually significant in assembler:

   COMPILER STASM1 $XCN
   /* Grammar for simple stack assembler
      P.D. Terry, Rhodes University, 2001 */

   CHARACTERS
     cr       = CHR(13) .
     lf       = CHR(10) .
     letter   = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" .
     digit    = "0123456789" .
     noquote1 = ANY - "'" - lf - CHR(0) .
     noquote2 = ANY - '"' - lf - CHR(0) .

   IGNORE CHR(9) .. CHR(11)
   IGNORE CHR(13)

   COMMENTS FROM ";" TO cr

   TOKENS
     number =  ["+" | "-" ] digit { digit } .
     string =  "'" noquote1 { noquote1 } "'" | '"' noquote2 { noquote2 } '"' .
     EOL    =  lf .

   PRODUCTIONS
     STASM1     =  { Statement EOL } EOF .

     Statement  = [ [ number ] /* ignored */
                    ( OneWord | TwoWord | LoadString ) ] .

     OneWord    =     "NEG" | "ADD" | "SUB" | "MUL" | "DVD" | "ODD" | "EQL"
                    | "NEQ" | "LSS" | "GEQ" | "GTR" | "LEQ" | "STK"
                    | "STO" | "HLT" | "INN" | "PRN" | "INC" | "PRC"
                    | "NLN" | "VAL" | "NOP" .

     TwoWord    = ( "LIT" | "ADR" | "DSP" | "BRN" | "BZE" ) number .

     LoadString =  "PRS" string .

   END STASM1.
and here is a solution that defines comments as a special kind of TOKEN
   COMPILER STASM2 $XCN
   /* Grammar for simple stack assembler
      P.D. Terry, Rhodes University, 2001 */

   CHARACTERS
     cr        = CHR(13) .
     letter    = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" .
     digit     = "0123456789" .
     noquote1  = ANY - "'" - cr - CHR(0) .
     noquote2  = ANY - '"' - cr - CHR(0) .
     printable = CHR(31) .. CHR(255) .

   IGNORE  CHR(9) .. CHR(12)

   TOKENS
     number  =  ["+" | "-" ] digit { digit } .
     string  =  "'" noquote1 { noquote1 } "'" | '"' noquote2 { noquote2 } '"' .
     comment = ";" { printable } .
     EOL     =  cr .

   PRODUCTIONS
     STASM2     =  { Statement EOL } EOF .

     Statement  = [ [ number ] /* ignored */
                    ( OneWord | TwoWord | LoadString ) ] [ comment ] .

     OneWord    =     "NEG" | "ADD" | "SUB" | "MUL" | "DVD" | "ODD" | "EQL"
                    | "NEQ" | "LSS" | "GEQ" | "GTR" | "LEQ" | "STK"
                    | "STO" | "HLT" | "INN" | "PRN" | "INC" | "PRC"
                    | "NLN" | "VAL" | "NOP" .

     TwoWord    = ( "LIT" | "ADR" | "DSP" | "BRN" | "BZE" ) number .

     LoadString =  "PRS" string .

   END STASM2.


Task 4 - Spoornet are looking for programmers

The wheels came off in many solutions. It is quite hard to get right, and one cannot find an LL(1) grammar. Here is a simple first stab, but with no safety regulations.

   COMPILER Train1 $XCN
   /* Grammar for simple railway trains
      P.D. Terry, Rhodes University, 2001 */

   IGNORE CASE
   IGNORE CHR(1) .. CHR(31)
   COMMENTS FROM "(*" TO "*)" NESTED

   PRODUCTIONS
     Train1       = { OneTrain } EOF .
     OneTrain     = LocoPart GoodsPart HumanPart "." .
     LocoPart     = "loco" { "loco" } .
     GoodsPart    = Truck { Truck } .
     HumanPart    = "guard" | { "coach" } "brake" .
     Truck        = "coal" | "open" | "cattle" | "fuel" | "petrol" .
   END Train1.

Here is an attempt at safety. But this one insists on at least two safe trucks in any train, and is not LL(1)

   COMPILER Train2 $XCN
   /* Grammar for safer railway trains
      P.D. Terry, Rhodes University, 2001 */

   IGNORE CASE
   IGNORE CHR(1) .. CHR(31)
   COMMENTS FROM "(*" TO "*)" NESTED

   PRODUCTIONS
     Train2       = { OneTrain } EOF .
     OneTrain     = LocoPart GoodsPart HumanPart "." .
     LocoPart     = "loco" { "loco" } .
     GoodsPart    = SafeTruck { AnyTruck } .
     HumanPart    = "guard" | SafeTruck { "coach" } "brake" .
     SafeTruck    = "coal" | "open" | "cattle" .
     AnyTruck     = SafeTruck | "fuel" | "petrol" .
   END Train2.
Here is a better one, but still non-LL(1)
   COMPILER Train3 $XCN
   /* Grammar for safer railway trains
      P.D. Terry, Rhodes University, 2001 */

   IGNORE CASE
   IGNORE CHR(1) .. CHR(31)
   COMMENTS FROM "(*" TO "*)" NESTED

   PRODUCTIONS
     Train3       = { OneTrain } EOF .
     OneTrain     = LocoPart Load "." .
     LocoPart     = "loco" { "loco" } .
     Load         = SafeTruck Tail.
     Tail         =   { AnyTruck } "guard"
                    | Passengers
                    | { AnyTruck } SafeTruck Passengers .
     Passengers   = { "coach" } "brake" .
     SafeTruck    = "coal" | "open" | "cattle" .
     AnyTruck     = SafeTruck | "fuel" | "petrol" .
   END Train3.
and here is yet another, but still non-LL(1)
   COMPILER Train4 $XCN
   /* Grammar for safer railway trains
      P.D. Terry, Rhodes University, 2001 */

   IGNORE CASE
   IGNORE CHR(1) .. CHR(31)
   COMMENTS FROM "(*" TO "*)" NESTED

   PRODUCTIONS
     Train4       = { OneTrain } EOF .
     OneTrain     = LocoPart Load "." .
     LocoPart     = "loco" { "loco" } .
     Load         = SafeTruck Tail.
     Tail         = Passengers | { AnyTruck } SafeTail .
     SafeTail     = SafeTruck ( Passengers | "guard" ) .
     Passengers   = { "coach" } "brake" .
     SafeTruck    = "coal" | "open" | "cattle" .
     AnyTruck     = SafeTruck | "fuel" | "petrol" .
   END Train4.


Task 5 - Highland Gatherings

The solutions received were very mixed. Many didn't capture the idea that there should be N competitions each with M bands. A typical over simplified attempt looks like this

     Gath1         = { Competition } .
     Competition   = SlowQuick |  MSR |  Medley .
     SlowQuick     = "SlowMarch" "March"  .
     MSR           = "March" "Strathspey" "Reel" [ "March" ] .
     Medley        = OneTune { OneTune } .
     OneTune       =   "March" | "SlowMarch" | "Jig" | "HornPipe" | "Reel"
                     | "Strathspey" { "Strathspey" } "Reel" .

If we want to introduce the idea that there are multiple competitions with multiple bands, then surely it makes sense to try

   COMPILER Gath1 $XCN
   /* Describes the Pipe Band events at a Highland Gathering
      P.D. Terry, Rhodes University, 2001 */

   IGNORE CHR(1) .. CHR(31)

   PRODUCTIONS

     Gath2       = { Competition } .
     Competition = Band { Band } .
     Band        = SlowQuick | MSR | Medley .
     SlowQuick   = "SlowMarch" "March" .
     MSR         = "March" "Strathspey" "Reel" [ "March" ] .
     Medley      = OneTune { OneTune } .
     OneTune     =   "March" | "SlowMarch" | "Jig" | "HornPipe" | "Reel"
                   | "Strathspey" { "Strathspey" } "Reel" .

   END Gath2.
This is badly non LL(1) (It represents a continual wail of noise). We need to get some sort of break between bands at least:
   COMPILER Gath3 $XCN
   /* Describes the Pipe Band events at a Highland Gathering
      P.D. Terry, Rhodes University, 2001 */

   IGNORE CHR(1) .. CHR(31)

   PRODUCTIONS

     Gath3       = { "AnnounceCompetition" Competition } .
     Competition = Band { Band } .
     Band        = "AnnounceBand" ( SlowQuick | MSR | Medley ).
     SlowQuick   = "SlowMarch" "March" "break" .
     MSR         = "March" "Strathspey" "Reel" [ "March" ] "break" .
     Medley      = OneTune { OneTune } "break" .
     OneTune     =   "March" | "SlowMarch" | "Jig" | "HornPipe" | "Reel"
                   | "Strathspey" { "Strathspey" } "Reel" .

   END Gath3.
Even this is non-LL(1). We can get an LL(1) grammar if we make the right kind of announcements:
   COMPILER Gath4 $XCN
   /* Describes the Pipe Band events at a Highland Gathering
      P.D. Terry, Rhodes University, 2001 */

   IGNORE CHR(1) .. CHR(31)

   PRODUCTIONS

     Gath4       = { "AnnounceCompetition" Competition } .
     Competition = Band { Band } .
     Band        =   "AnnounceSlow"  SlowQuick
                   | "AnnounceMSR" MSR
                   | "AnnounceMedley" Medley .
     SlowQuick   = "SlowMarch" "March" "break" .
     MSR         = "March" "Strathspey" "Reel" [ "March" ] "break" .
     Medley      = OneTune { OneTune } "break" .
     OneTune     =   "March" | "SlowMarch" | "Jig" | "HornPipe" | "Reel"
                   | "Strathspey" { "Strathspey" } "Reel" .

   END Gath4.
Here is yet another solution:
   COMPILER Gath5 $XCN
   /* Describes the Pipe Band events at a Highland Gathering
      P.D. Terry, Rhodes University, 2001 */

   IGNORE CHR(1) .. CHR(31)

   PRODUCTIONS

     Gath5         = { Competition } .
     Competition   =   "AnnounceSlow"   SlowQuickComp
                     | "AnnounceMSR"    MSRComp
                     | "AnnounceMedley" MedleyComp .
     SlowQuickComp = SlowQuick { SlowQuick } .
     SlowQuick     = "SlowMarch" "March" "break" .
     MSRComp       = MSR { MSR } .
     MSR           = "March" "Strathspey" "Reel" [ "March" ] "break" .
     MedleyComp    = Medley { Medley } .
     Medley        = OneTune { OneTune } "break" .
     OneTune       =   "March" | "SlowMarch" | "Jig" | "HornPipe" | "Reel"
                     | "Strathspey" { "Strathspey" } "Reel" .

   END Gath5.
In all these notice that we have not fallen into the trap of defining:
     OneTune     =   "March" | "SlowMarch" | "Jig" | "HornPipe" | "Reel"
                   | "Strathspey" { "Strathspey" } "Reel" { "Reel" } .
which is non-LL(1) again, or of trying to write
     OneTune     =   "March" | "SlowMarch" | "Jig" | "HornPipe" |
                   | "Strathspey" { "Strathspey" } "Reel" { "Reel" } .
which is LL(1), but precludes reels being played without a strathspey immediately before them.


Task 6 - Extensions to Clang

These were very simple really. The extensions for expressions can be done in different ways, depending on the precedence ordering. The solution below is inspired by the way they are defined in Pascal, Modula and Oberon.

    Condition      = Expression .
    Expression     = SimpExpression [ RelOp SimpExpression ] .
    SimpExpression = ( "+" Term | "-" Term | Term ) { AddOp Term } .
    Term           = Factor { MulOp Factor } .
    Factor         = Designator | number | "(" Expression ")" .
    AddOp          = "+" | "-" | "OR" .
    MulOp          = "*" | "/" | "AND" .

and here is a grammar based on that used in C and C++:

     Expression    = OrExp { "OR" OrExp } .
     OrExp         = AndExp { "AND" AndExp } .
     AndExp        = AddExp [ RelOp AddExp ] .
     AddExp        = MulExp  { AddOp MulExp } .
     MulExp        = Factor { MulOp Factor } .
     Factor        = Primary | ( "+" | "-" ) Factor .
     Primary       = Designator | number | "TRUE" | "FALSE" | "(" Expression ")" .

CASE or SWITCH statements can be done in various ways. Here is one that captures the spirit of the example suggested in the question sheet

    CaseStatement     = "CASE" Expression "OF"
                          { OneCase }
                          [ "DEFAULT" ":" StatementSequence ]
                        "END" .
    OneCase           = Label { "," Label } ":" StatementSequence .
    StatementSequence = Statement { ";" Statement } .
    Label             = number .
It might be argued that the "default" arm be mandatory (it can always have only an empty statement if you really want to mop up all "other" cases in the same empty way):
    CaseStatement     = "CASE" Expression "OF"
                          { OneCase }
                          "DEFAULT" ":" StatementSequence
                        "END" .

Notice that we do not use identifiers or strings as "labels". Identifiers are no use because they can also start statements, and to attempt to use them would cause LL(1) errors. Strings are not used because the expressions in Clang cannot yield anything but simple integer values to match against possible labels.


Home  © P.D. Terry