Computer Science 3 - 2002 - Test on Prac 11

1. Some people never remember, no matter how often you remind them - What is your name (surname)? [1]

Pat (Terry)

2. Here is an attempt to solve the problem you were set last week of checking a list of computer names and IP numbers for duplicates:

   1   COMPILER ChkIP $XCN
   2
   3   #include "misc.h"
   4   #include "list.h"
   5
   6   struct String {
   7     char * str;   // a string!
   8     friend int operator == (String x, String y)
   9       { return strcmp(x.str, y.str) == 0; }
  10     friend int operator != (String x, String y)
  11       { return strcmp(x.str, y.str) != 0; }
  12   };
  13
  14   typedef List<String> StringList;
  15   StringList IPList;
  16
  17   IGNORE CHR(1) .. CHR(31)
  18
  19   CHARACTERS
  20     digit   = "0123456789" .
  21     letter  = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" .
  22     eol     = CHR(10)
  23
  24   COMMENTS FROM "#" TO eol
  25
  26   TOKENS
  27     ipnumber = digit { "." | digit } .
  28     compname = letter { letter | digit | "." | "-" } .
  29
  30   PRODUCTIONS
  31     ChkIP = { Entry }
  32     EOF                           (. if (Successful()) printf("\n all correct"); .)
  33     .
  34
  35     Entry = IPNumber Name { Name } .
  36
  37     Name
  38     =                             (. String S;       StringList SL; .)
  39     name                          (. LexString(S.str, 1000);
  40                                      if (SL.isMember(S)) SemError(1002);
  41                                      else SL.add(S); .) .
  42
  43     IPNumber
  44     =                             (. char str[100];   String n; .)
  45     ipnumber                      (. LexString(str, 99);
  46                                      n.str = str;
  47                                      if (IPList.isMember(n)) SemError(1001);
  48                                      else IPList.add(n); .) .
  49
  50   END ChkIP.

Leave aside for the moment the fact that no attempt has been made to check that the component parts of an IP number like 123.456.789.0 must all lie in the range 0 .. 255, and accept that we could simply treat an IPNumber as a string.

Identify as many other problems as you can with the above solution and comment briefly on what they are. I do NOT want you to develop a completely new solution or write out the one you used in the prac. I want you simply to point out places where the syntax might be wrong, and where the user has made some fundamentally bad decisions in applying the actions.

There were quite a number of errors that could be found:

(a) From a Coco point of view there was only syntactic error one - a missing period in line 22.

(b) The definition of the ipnumber token - apart from the problem of keeping the components in the range 0 ... 255 - is inadequate, as it would allow, for example 33 or 3...5 or 3.5.6.7.8.9 as IP numbers.

(c) The attempt to declare a string field in line 7 represents a classic mistake that many people make - declaring a pointer to a string array does not of itself allocate storage for that string array.

(d) Similarly, the attempt in line 46 to copy one string to another represents another classic mistake that people make in C and C++ - copying pointers to character arrays does not copy the contents of those arrays.

(e) The value of 1000 used in the call to LexString in line 39 is stupid (and dangerous!).

(f) The definition of the SL list of strings was in completely the wrong place. As supplied it would have created a new "local" list each time a new computer name was encountered.

There are several places where people identified "errors" that are not errors:

(a) The IGNORE CHR(1) .. CHR(31) in line 17 specifies a set of characters that Coco generated scanners will ignore in scanning for a new token. Although the lf character (CHR(10) is in this set, that does not mean that we cannot specify that comments extend FROM "#" TO eol. A comment is not a token. The scanner, on encountering the # that starts a comment will simply ignore all characters from that point to the end of line, and then search for a token again.

(b) One does not have to create a special #include file to contain the structure declaration and typedef - although in the pracs it was recommended that you do this. In most cases it will be necessary, as those declarations will have to be visible in other sections of the system, and for which purpose a #include pragma will have to be added to the compiler frame file as well as to the ATG file. But if - as here - you can get away with a few variables and declarations "global" to the parser only, the simple structure above will suffice.

(c) String is not a C++ (or even Java) keyword. It is, of course, a common name for a frequently used class in both languages!

(d) Successful() is a Boolean function provided by the Coco parser interface (just as LexString and LexName are provided) for ease of use in some applications. So it was not "undeclared' as some people thought.

(e) Only one typedef was needed, as both lists were simply lists of "strings"

A better solution, still using "global" declarations of the two lists needed would have been as follows:

    COMPILER ChkIP $XCN

    #include "misc.h"
    #include "list.h"

    struct String {
      char str[100];   // a string!
      friend int operator == (String x, String y)
        { return strcmp(x.str, y.str) == 0; }
      friend int operator != (String x, String y)
        { return strcmp(x.str, y.str) != 0; }
    };

    typedef List<String> StringList;

    StringList IPList;
    StringList NameList;

    IGNORE CHR(1) .. CHR(31)

    CHARACTERS
      digit   = "0123456789" .
      letter  = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" .
      eol     = CHR(10) .

    COMMENTS FROM "#" TO eol

    TOKENS
      ipnumber = digit [ digit [ digit ]] "." digit [ digit [ digit ]] "."
                 digit [ digit [ digit ]] "." digit [ digit [ digit ]] .
      compname = letter { letter | digit | "." | "-" } .

    PRODUCTIONS
      ChkIP = { Entry }
      EOF                           (. if (Successful()) printf("\n all correct"); .) .

      Entry = IPNumber Name { Name } .

      Name
      =                             (. String s; .)
      compname                      (. LexString(s.str, 99);
                                       if (NameList.isMember(s)) SemError(1002);
                                       else NameList.add(s); .) .

      IPNumber
      =                             (. String n; .)
      ipnumber                      (. LexString(n.str, 99);
                                       if (IPList.isMember(n)) SemError(1001);
                                       else IPList.add(n); .) .

    END ChkIP.

3. The grammar below attempts to describe your present hard-working lifestyle.

As you know, we are having trouble providing you with enough tutors. As you may not know, we are going to have problems paying them, because they have recently told me that they want to be paid a fee for each occasion they are consulted. So what I need now is a list of tutors with a count of such times. What could I add to the grammar to produce me such a list?

    COMPILER Prac $XCN

    #include "misc.h"
    #include "list.h"

    struct Tutor {
      char name[100];   // a string!
      friend int operator == (Tutor x, Tutor y)
        { return strcmp(x.name, y.name) == 0; }
      friend int operator != (Tutor x, Tutor y)
        { return strcmp(x.name, y.name) != 0; }
    };

    IGNORE CHR(1) .. CHR(31)

    CHARACTERS
      letter  = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" .
    TOKENS
      name = letter { letter } .
    PRODUCTIONS
      Prac     = Handouts { Task } "Submit" .
      Handouts = "PracSheet" { "HintSheet" } .
      Task     = "Attempt" { ConsultTutor  "Attempt" }  .
      ConsultTutor = name .
    END Prac.

This was intended to be easy, and it was encouraging to see that several people produced excellent solutions. But it was also distressing that some people seemed to get nowhere at all, and clearly failed to see the connection between what they should have achieved in the prac and this question - which also required a simple list of values to be set up. Here is a simple solution, using a globally declared list (which was quite adequate in this case). The additions to the skeleton supplied above are indicated by ** in the left margin:

    COMPILER Prac $XCN

    #include "misc.h"
    #include "list.h"

    struct Tutor {
      char name[100];                            // a string!
**    int count;                                 // a number of consultations!
      friend int operator == (Tutor x, Tutor y)
        { return strcmp(x.name, y.name) == 0; }
      friend int operator != (Tutor x, Tutor y)
        { return strcmp(x.name, y.name) != 0; }
    };

**  typedef List<Tutor> TutorList;
**  TutorList Tutors;

    IGNORE CHR(1) .. CHR(31)

    CHARACTERS
      letter  = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" .
    TOKENS
      name = letter { letter } .
    PRODUCTIONS
      Prac = Handouts { Task }
**    "Submit"               (. for (int i = 0; i < Tutors.length(); i++)
**                                printf("%-12s %d\n", Tutors[i].name, Tutors[i].count);
**                           .) .
      Handouts = "PracSheet" { "HintSheet" } .
      Task = "Attempt" { ConsultTutor  "Attempt" }  .
      ConsultTutor
**    =                     (. Tutor hero; .)
**    name                  (. LexString(hero.name, sizeof(hero.name)-1);
**                             if (Tutors.isMember(hero))
**                               Tutors[Tutors.position(hero)].count++;
**                             else {
**                               hero.count = 1;
**                               Tutors.add(hero);
**                             }
**                          .) .
    END Prac.

Note that (a) it was surely obvious that you had to create a list? (many solutions did not do this, and just used a single tutor); (b) you were implicitly asked to write out the list (many people failed to attempt this); (c) the first time a tutor is encountered his/her count has to become 1 (not 0); (d) it is not an error if a tutor does not appear in the list - we simply have to add to the list; (e) you must be careful about how to store the name obtained using LexString in the Tutor record.

Some people attempted to declare the tutor list in the production for Prac. Fair enough. In this case it would be necessary to (a) pass the list down the productions to ConsultTutor; (b) move the declaration of struct Tutor etc to a separate #include file; (c) set up and modify a prac.frm frame file. The modified grammar is shown below, and the rest of the files can be found in this week's solution file PRAC11A.ZIP if you want to take this further.

    COMPILER Prac $XCN

    #include "prac.h"

    IGNORE CHR(1) .. CHR(31)

    CHARACTERS
      letter  = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" .
    TOKENS
      name = letter { letter } .
    PRODUCTIONS
      Prac
      =                      (. TutorList Tutors; .)
      Handouts
      { Task<Tutors> }
      "Submit"               (. for (int i = 0; i < Tutors.length(); i++)
                                  printf("%-12s %d\n", Tutors[i].name, Tutors[i].count);
                             .) .

      Handouts = "PracSheet" { "HintSheet" } .

      Task<TutorList &FriendlyPeople>
      = "Attempt" { ConsultTutor<FriendlyPeople> "Attempt" } .

      ConsultTutor<TutorList &Slaves>
      =                     (. Tutor hero; .)
      name                  (. LexString(hero.name, sizeof(hero.name)-1);
                               int myHero = Slaves.position(hero);
                               if (myHero >= 0)
                                 Slaves[myHero].count++;
                               else {
                                 hero.count = 1;
                                 Slaves.add(hero);
                               }
                            .) .

    END Prac.


Home  © P.D. Terry