One of the most difficult, and most useful skills in the software industry is the capability to fix problems... especially in code that you didn't write. Today, I'm going to give you a large and complicated piece of C software that has several bugs. Your job is to find and fix those bugs
First, we need to talk a little bit about what a "bug" is. For the most part (and for today's lab), a bug consists of a situation where a program calculates an incorrect answer, or performs some unexpected action which results in an incorrect answer or no answer at all. That's going to be pretty easy to identify for this lab because the "program" in question performs math calculations - basically a glorified calculator. A bug occurs when it gets the wrong answer. In the more general case, it's often hard to define exactly what a bug is and is not. For instance, is a compiler warning a bug, even though the program gets the right answer? Is it a bug if the user enters something incorrect which the program didn't expect, and gets the wrong answer?
I'm providing code in calc.c and the associated Makefile. When this code is debugged and built, it will take an input specification as an argument, parse that specification, evaluate the specification, and print the result of that evaluation.
The specification read as input is typically contained in double quotes so that it will be treated as a single argument to calc, even though it has embedded blanks. The actual specification seen by the program as argv will be everything between the double quotes.
The format of the specification is as follows
When the calc program executes correctly, it evaluates all expressions, and prints the value of the last statement in the statement list.
The Makefile has a "test" target that invokes the calculator using seven different specfications, ranging from fairly simple to pretty complex.
Your job today is to get the code working so that it not only completes all seven tests succsesfully, but also give the right answer in all seven cases.
The calc.c file has four major bugs that I'm aware of. None of the bugs are caused by design errors. In other words, if you fix all the typo's in the code, it will work correctly. Each of the four errors consists of just one or two keystrokes. The trick is to find these errors. Keep track of the bugs you find and fix - you will have to describe them in the lab report.
I don't expect you to understand all the code in calc.c at this point. There is a lot of material we haven't covered yet. There is even some compiler theory built into this code that you won't learn in CS-220... probably won't learn in detail until you take a programming languages course. However, here is a quick summary of what is going on so you won't be totally lost.
The calc.c main function does two basic functions. First, it invokes the getExpList function which parses the user's specification, and puts it into a "parse tree". A parse tree consists of a root with a specific action, along with a left sub-tree and a right sub-tree. For instance, the expression "4*2" creates a parse tree with the action of "multiply", a left subtree that has the constant value "4", and a right sub-tree that has the constant value "2".
The getExprList function tries to turn the user specification into a list of expressions, where the left sub-tree is a single statement, and the right sub-tree is an expression list. It does this by invoking the getStatement function. If there are more statements after that statement, it makes a tree. If that's the last statement, it just returns the tree associated with the statement.
The getStatement function in turn looks for lower level constructs, and so on. The entire outline of the tree building functions is summarized by the "grammar" in the long block comment at the beginning of the file.
Once the tree has been built, you can invoke the printTree function...even from gdb... to print out what the tree looks like. For instance, if you are at line 134 in gdb, you could run the gdb command print printTree(t,"debug") to print out the entire tree.
The second major operation in the main function is to invoke the evalTree function once the tree has been built to evaluate the tree. The tree is evaluated by first evaluating the left sub-tree, then evaluating the right sub-tree, then performing the desired action, and returning the result of the evaluation. For instance, in the example above where the root is "multiple, the left sub-tree is "4" and the right sub-tree is "2", then the evalTree function gets "4" as the value of the left sub-tree, "2" as the value of the right sub-tree, and mutliplies them together to get and return the value, "8".
Download and edit the following file: lab4_report.txt. Then submit your editted file on Blackboard in the Lab 4 submission area.