An "if-else" statement supports
selection from only two alternatives; we can of course nest such
statements or use constructs such as "if-elsif-else", but it is usually more succinct to use a case
statement. Case statements allow selection from many alternatives where each alternative is
linked to a predicate,
referred to as a selector, which when evaluated to true causes an associated
program statement (or statements) to be executed.
In Ada the general format of a case statement is as follows:
case SELECTOR is when`=>' ... end case;
Selections may be made according to:
Selectors must be of a discrete type (such as an integer or a character). Example case statement:
case N is when 0 => PUT_LINE("N equals 0"); when 1 | 5 => PUT_LINE("N equals 1 or 5"); when 2..4 => PUT_LINE("N equals 2, 3 or 4"); when others => PUT_LINE("N less than 0 or greater than 5"); end case;
where N is of type integer. The above states that:
(Reference: Skansholm 1997, pp129-130).
Design and develop a simple calculator Ada program. The calculator should be able to resolve simple arithmetic expressions of the form:
Where < OPERAND > is an integer of some kind and < OPERATOR > is one of the operators `+', `-', `*' or `/' (integer division). Thus given the expression:
63*35
The program should calculate the value of the expression and display the result.
Note: remember to include a divide by zero test.
A top down analysis of the problem is given below:
We have established that the default integer range is -2147483647..2147483647. Thus the highest value that we can compute is 14654*14654 (14655*14655 > 2147483647), and the lowest value is -14654*14654 (-14654*14654 < -2147483647). Will therefore define the operands as subtypes of the type integer with a range of -14654..14654. We will implement this analysis using the following three procedures:
NAME | DESCRIPTION | TYPE | RANGE |
---|---|---|---|
OPERAND_1 | Global input variable | OPERAND_T | -14654..14654 |
OPERAND_2 | Global input variable | OPERAND_T | -14654..14654 |
OPERATOR | Global input variable | CHARACTER | Default |
NAME | DESCRIPTION | TYPE | RANGE |
---|---|---|---|
OPERAND_1 | Formal paramete | OPERAND_T | -14654..14654 |
OPERAND_2 | Formal paramete | OPERAND_T | -14654..14654 |
SELECTOR | Formal paramete | CHARACTER | Default |
NAME | DESCRIPTION | TYPE | RANGE |
---|---|---|---|
OP_1 | Formal parameter | OPERAND_T | -14654..14654 |
OP_2 | Formal parameter | OPERAND_T | -14654..14654 |
A complete Nassi-Shneiderman design for the above is given below. Note how the case statement is included in the design.
The flow of control associated with this design is indicated by the data flow diagram given below. There is not an official construct to indicate a case statement in a DFD, however, the construct given below adequately allows us to identify the paths through the proposed software.
As with all top down designs we commence the implementation at the highest level:
-- CALCULATOR -- 7 August 1997 -- Frans Coenen -- Dept Computer Science, University of Liverpool with TEXT_IO; use TEXT_IO; procedure CALCULATOR is subtype OPERAND_T is INTEGER range -14654..14654; package INTEGER_INOUT is new INTEGER_IO(INTEGER); use INTEGER_INOUT; OPERAND_1, OPERAND_2 : OPERAND_T; OPERATOR : CHARACTER; ----------------------------------------------------------------- -- MAKE_CALCULATION procedure to direct calculation procedure MAKE_CALCULATION(OPERAND_1: OPERAND_T; OPERATOR: CHARACTER; OPERAND_2: OPERAND_T) is begin PUT_LINE("In MAKE_CALCULATION"); PUT(" OPERAND_1 = "); PUT(OPERAND_1); NEW_LINE; PUT(" OPERATOR = "); PUT(OPERATOR); NEW_LINE; PUT(" OPERAND_2 = "); PUT(OPERAND_2); NEW_LINE; end MAKE_CALCULATION; ----------------------------------------------------------------- -- TOP LEVEL begin -- Input expression PUT_LINE("Write a simple arithmetic expression: "); GET(OPERAND_1); GET(OPERATOR); GET(OPERAND_2); -- Process expression MAKE_CALCULATION(OPERAND_1,OPERATOR,OPERAND_2); end CALCULATOR;
We should now test this part of the implementation. No calculation is carried out at this stage, however we are in a position to carry out some BVA and limit testing. A set of appropriate test cases is given in the Table below.
TEST CASE | EXPECTED RESULT | ||
---|---|---|---|
OPERAND_1 | OPERATOR | OPERAND_2 | OUTPUT |
-14655 | * | * | CONSTRAINT_ERROR |
14655 | * | * | CONSTRAINT_ERROR |
-14653 | + | -14655 | CONSTRAINT_ERROR |
14653 | - | 14655 | CONSTRAINT_ERROR |
-14653 | * | -14653 | OK |
14653 | \ | 14653 | OK |
The second part of the implementation is as follows (we will implement levels 2 and 3 together).
-- CALCULATOR -- 7 August 1997 -- Frans Coenen -- Dept Computer Science, University of Liverpool with TEXT_IO; use TEXT_IO; procedure CALCULATOR is subtype OPERAND_T us INTEGER range -14654..14654; package INTEGER_INOUT is new INTEGER_IO(INTEGER); use INTEGER_INOUT; OPERAND_1, OPERAND_2 : INTEGER; OPERATOR : CHARACTER; ----------------------------------------------------------------------- -- MAKE_CALCULATION procedure to direct calculation procedure MAKE_CALCULATION(OP_1: OPERAND_T; SELECTOR: CHARACTER; OP_2: OPERAND_T) is ------------------------------------------------------------------- -- DIVIDE BY ZERO CHECK procedure DIVISION(OP_1, OP_2: OPERAND_T) is begin if (OP_2 /= 0) then PUT(OP_1 / OP_2); else PUT("Divide by zero not permitted"); end if; end DIVISION; ------------------------------------------------------------------- begin case SELECTOR is when '+' => PUT(OP_1 + OP_2); when '-' => PUT(OP_1 - OP_2); when '*' => PUT(OP_1 * OP_2); when '/' => DIVISION(OP_1,OP_2); when others => PUT("Unrecognised operator: "); PUT(SELECTOR); end case; NEW_LINE; end MAKE_CALCULATION; ----------------------------------------------------------------------- -- TOP LEVEL begin -- Input expression PUT_LINE("Write a simple arithmetic expression: "); GET(OPERAND_1); GET(OPERATOR); GET(OPERAND_2); -- Process expression MAKE_CALCULATION(OPERAND_1,OPERATOR,OPERAND_2); end CALCULATOR;
BVA and Limit Testing: Already undertaken after the top level implementation.
Arithmetic testing: We should also include test to ensure the correct operation of the arithmetic expressions (it is not good practice to assume that such simple expressions will operate correctly). Thus the following test cases would be appropriate.
TEST CASE | EXPECTED RESULT | ||
---|---|---|---|
OPERAND_1 | OPERATOR | OPERAND_2 | OUTPUT |
3 | + | 3 | 6 |
3 | + | 0 | 3 |
3 | + | -3 | 0 |
0 | + | 3 | 3 |
0 | + | 0 | 0 |
0 | + | -3 | -3 |
-3 | + | 3 | 0 |
-3 | + | 0 | -3 |
-3 | + | -3 | -6 |
TEST CASE | EXPECTED RESULT | ||
---|---|---|---|
OPERAND_1 | OPERATOR | OPERAND_2 | OUTPUT |
3 | - | 3 | 0 |
3 | - | 0 | 3 |
3 | - | -3 | 6 |
0 | - | 3 | -3 |
0 | - | 0 | 0 |
0 | - | -3 | 3 |
-3 | - | 3 | -6 |
-3 | - | 0 | -3 |
-3 | - | -3 | 0 |
TEST CASE | EXPECTED RESULT | ||
---|---|---|---|
OPERAND_1 | OPERATOR | OPERAND_2 | OUTPUT |
3 | * | 3 | 9 |
3 | * | 0 | 0 |
3 | * | -3 | -9 |
0 | * | 3 | 0 |
0 | * | 0 | 0 |
0 | * | -3 | 0 |
-3 | * | 3 | -9 |
-3 | * | 0 | 0 |
-3 | * | -3 | 9 |
TEST CASE | EXPECTED RESULT | ||
---|---|---|---|
OPERAND_1 | OPERATOR | OPERAND_2 | OUTPUT |
3 | / | 3 | 1 |
3 | / | 0 | Divide by zero error |
3 | / | -3 | -1 |
0 | / | 3 | 0 |
0 | / | 0 | Divide by zero error |
0 | / | -3 | 0 |
-3 | / | 3 | -1 |
-3 | / | 0 | Divide by zero error |
-3 | / | -3 | 1 |
Path Testing: The above code should be tested so that each path through the code (resulting from the case statement and the if-else statement) is tested. A suggested set of test cases is given in the table below. All these test cases, except the last, will already have been run as part of black box testing strategies and therefore do not need to be run again.
TEST CASE | EXPECTED RESULT | ||
---|---|---|---|
OPERAND_1 | OPERATOR | OPERAND_2 | OUTPUT |
3 | + | 3 | 6 |
3 | - | 3 | 0 |
3 | * | 3 | 9 |
3 | / | 0 | Error message |
3 | / | 3 | 1 |
3 | ^ | 3 | Error message |
This should also be done.
Example Problem Calculator Report.
Example Problem Temperature Conversion Plus Report.
Created and maintained by Frans Coenen. Last updated 11 October 1999