Suggestions for Syntax Coloring Assignment

CSC 7000-002 Algorithms & Programming
Fall 1999, Mendel 115
Dr. David Matuszek

I think a lot of people are having trouble with this assignment, so here are some suggestions. These are only suggestions, not additional requirements, so if you are doing the program some other way, no problem.

I'm guessing that the two main trouble spots are (1) doing the string manipulation, and (2) program organization. I'll talk a bit about both, but it's the second of these that is the most important.

String manipulation. You can use the routines in <string.h> (recommended), or you can do everything yourself from scratch (only a little more work).

A string is just a contiguous sequence of characters. One way to get space for a string is to malloc it yourself:

char *p;
p = (char *) malloc (101);

A second way to get space for a string is to declare it as a character array:

char a[101];

Remember, if you declare an array, the name of the array is actually a pointer to the zeroth element of the array. So a and p are both of type char*, and you can use them in almost all the same ways. You can even use expressions like p[i] which (you should remember) really means *(p + i).  So you can index into an array, you can index off a char pointer, or you can just use a plain char pointer. One difference is that you can change what p points to (just assign something else to it), but a is a constant pointer; it always points to the same location in memory.

Notice that many of the routines return a char*, that is, a pointer to some character in the string. Even if your string is stored in an array, you can ignore the fact that it's an array, and use the pointer. Or, if the array is named a and the pointer into it is p, you can index into the array with a[p-a], and that's the same as p* (why?). But you're usually better off just using pointers.

Here's some sample code to do some (fairly meaningless) string manipulation. It's not really relevant to this assignment, it's just some string manipulation that you can maybe get ideas from. This code has comments in it that echo the code--normally a bad idea, but in this case I'm trying to explain the details of how the statements work.

  char *str, *front, *rear, ch; /* ch is a char, the rest are pointers */
  int i;

  /* allocate space for a string */
  str = (char *) malloc (101);
  
  /* copy a string into that space */
  strcpy (str, "hello there");

  /* print the string out */
  for (i = 0; i < 11; i++) /* Notice magic number -- bad style */
    printf ("%c", str[i]);
  printf ("\n");

  /* find end of string; notice body of loop is empty! */
  for (rear = str; *rear != '\0'; rear++) {} 
  /* we're at zero terminator, so back up one to last char */
  rear--;
 
  /* reverse the string, working from both ends toward the middle */
  front = str; 
  while (front < rear) {
    ch = *front; *front = *rear; *rear = ch; /* swap */
    front++;
    rear--;
  }
  /* print out the result */
  for (front = str; *front != '\0'; front++)
    printf ("%c", *front);
  printf ("\n");

Here's the output:

hello there
ereht olleh

Program organization. The first decision you have to make is, are you going to process the file a character at a time, a line at a time, or the whole file at once?

If you read in and process a character at a time, you have to do some very complicated logic to keep track of which keywords you might be in the middle of. This can get really, really complicated. If you've started doing it this way, you are probably better off starting over.

You could read in a line at a time, process that line, and print it out. Then you would be all done with that line. Or you could read in an entire array of lines, process them all, and then print them all out. I don't see any advantage to keeping an array, but I do see a couple of disadvantages: you have to keep track of where you are in the array, and (worse) you have to guess ahead of time how big an array you will need; if your guess is too small, the program is in trouble. So I would go with doing one line at a time.

The next decision is, where are you going to store your lines? There's no point in calling malloc() for each and every line. Get some storage and keep using the same storage over and over again. This way you won't have memory leaks. Get enough storage to handle the longest line (with a hefty safety margin); it's easier to guess how long a line might be than to guess how many lines long a file might be. In a real world program (that is, not just one for a class), you would be expected to put in checks to deal with the case where a line is longer than you allowed for.

Next decision: as you work on your line, are you going to work on it in place, or copy the changed line over to a second storage area? If you make a single change to a line, it probably won't make much difference which way you do it. However, you may need to make many changes to the same line, so you would have to copy it many times, and keep track of where you are copying from and where you are copying to. You can avoid that hassle by keeping just the one line and changing it in place.

One thing you have to be careful of when you are changing a line in place: don't do the same thing over and over again. For example, don't change & to &amp; and then change that to &amp;amp; and then to &amp;amp;amp; ... . Similarly, don't change for to <b>for</b>, then to <b><b>for</b></b>, ... . This error will typically cause your program to crash;you can avoid it by keeping careful track of where you are in the line.

The next set of decisions involves how you are going to break this program up into functions. What functions do you wish you had? <string.h> provides some, but usually not enough. Wouldn't it make your program a lot easier if you had a make_keywords_bold(char *line) function? Well, write one. How do you write a function like that? Well, it would be pretty easy if you had a make_one_keyword_bold(char *line, char *keyword) function, wouldn't it? Then you could just set up an array of keywords, and feed this function one keyword at a time.

How would you write a  make_one_keyword_bold(char *line, char *keyword) function? If you had a function that could look for one string (the keyword) inside another string (the line), that would be really helpful, wouldn't it? You do--it's called strstr(). But wait, that's not enough--you have to check the characters before and after the possible keyword, to make sure it isn't part of another word. strstr() will tell you where the possible keyword starts; strlen(keyword) will help you figure out where it ends. (But be sure you don't go off the beginning or end of the line.)

This may not be the exact set of functions you come up with, but it's the best way to go about it. The jargon term for it is top-down programming--you start at the top (the function you need to write), and think about what functions you wish you had to help you. Wish for things that are simpler than doing it all for you, but would really help you to do your job. Then take each of your wished-for functions and treat it the same way. Keep coming up with things to wish for until you get to things that are simple enough for you to write.