--|********************* C U T H E R E ************************** ------------------------------------------------------------------ --| Assignment 1. --| Eugene Giuliano Jr --| Oct 10, 1996 --| --| FILE: --| Calculator.Ada --| PROCEDURE NAME: --| Calculator --| PURPOSE: --| Implement a simple calculator. --| DEVELOPER: --| Eugene Giuliano Jr --| DATE WRITTEN: --| ~96-10-08 --| MODIFIED: --| ------------------------------------------------------------------ --|********************* C U T H E R E ************************** --|********************* C U T H E R E ************************** WITH Text_IO; WITH Integer_Text_IO; PROCEDURE Get_First_Digit (Digit : OUT INTEGER) IS Temp : INTEGER; BEGIN -- Get_First_Digit LOOP BEGIN Text_IO.Put (Item => "Enter the first digit "); Integer_Text_IO.Get (Item => Temp); Digit := Temp; EXIT; EXCEPTION WHEN Constraint_Error => Text_IO.Put ("Value entered is out of range. Please try again."); Text_IO.New_Line; WHEN Text_IO.Data_Error => Text_IO.Put ("Value entered is not an integer. Please try again."); Text_IO.New_Line; Text_IO.Skip_Line; END; END LOOP; END Get_First_Digit; --|********************* C U T H E R E ************************** WITH Text_IO; WITH Integer_Text_IO; PROCEDURE Get_Second_Digit (Digit : OUT INTEGER) IS Temp : INTEGER; BEGIN -- Get_Second_Digit LOOP BEGIN Text_IO.Put (Item => "Enter the second digit "); Integer_Text_IO.Get (Item => Temp); Digit := Temp; EXIT; EXCEPTION WHEN Constraint_Error => Text_IO.Put ("Value entered is out of range. Please try again."); Text_IO.New_Line; WHEN Text_IO.Data_Error => Text_IO.Put ("Value entered is not an integer. Please try again."); Text_IO.New_Line; Text_IO.Skip_Line; END; END LOOP; END Get_Second_Digit; --|********************* C U T H E R E ************************** WITH Text_IO; PROCEDURE Get_Operator (Operator : OUT CHARACTER) IS Temp : CHARACTER; Response_Ok : BOOLEAN := False; BEGIN -- Get_Operator WHILE NOT Response_Ok LOOP Text_IO.Put (Item => "Enter the operator [+,-,*,/]: "); Text_IO.Get (Item => Temp); CASE Temp IS WHEN '+'|'-'|'*'|'/' => Operator := Temp; Response_Ok := True; WHEN OTHERS => Text_IO.Put ("Unrecognized operator. Please try again."); Text_IO.New_Line; END CASE; END LOOP; END Get_Operator; --|********************* C U T H E R E ************************** WITH Text_IO; WITH Integer_Text_IO; PROCEDURE Check_Divisor (Divisor : IN INTEGER; Not_Zero : OUT BOOLEAN) IS BEGIN -- Check_Divisor IF Divisor /= 0 Then Not_Zero := True; ELSE Not_Zero := False; Text_IO.Put ("Division by Zero not allowed."); Text_IO.New_Line; END IF; END Check_Divisor; --|********************* C U T H E R E ************************** WITH Text_IO; WITH Integer_Text_IO; PROCEDURE Perform_Operation (First_Digit : IN INTEGER; Second_Digit: IN INTEGER; Operation : IN CHARACTER; Result : OUT INTEGER) IS BEGIN -- Perform_Operation CASE Operation IS WHEN '+' => Result := First_Digit + Second_Digit; WHEN '-' => Result := First_Digit - Second_Digit; WHEN '*' => Result := First_Digit * Second_Digit; WHEN '/' => Result := First_Digit / Second_Digit; WHEN OTHERS => Result := 0; Text_IO.Put ("Unrecognized operator. Please try again."); Text_IO.New_Line; END CASE; END Perform_Operation; --|********************* C U T H E R E ************************** WITH Text_IO; PROCEDURE Prompt_Again (Response : OUT BOOLEAN) IS Temp : CHARACTER; Response_Ok : BOOLEAN := False; BEGIN -- Prompt_Again WHILE NOT Response_Ok LOOP Text_IO.Put (Item => "Perform Another Calculation [Y/N]? "); Text_IO.Get (Item => Temp); CASE Temp IS WHEN 'y'|'Y' => Response := True; Response_Ok := True; WHEN 'n'|'N' => Response := False; Response_Ok := True; WHEN OTHERS => Text_IO.Put ("Enter only a 'Y' or 'N'. Please try again."); Text_IO.New_Line; END CASE; END LOOP; END Prompt_Again; WITH Text_IO; WITH Integer_Text_IO; WITH Get_First_Digit; WITH Get_Second_Digit; WITH Get_Operator; WITH Check_Divisor; WITH Perform_Operation; WITH Prompt_Again; PROCEDURE Calculator IS First_Digit : INTEGER := 0; Second_Digit : INTEGER := 0; Result : INTEGER := 0; Operation : CHARACTER := '+'; Repeat : BOOLEAN := True; Not_Zero : BOOLEAN := True; BEGIN -- Calculator WHILE Repeat LOOP Get_First_Digit (First_Digit); Get_Operator (Operation); Get_Second_Digit (Second_Digit); IF Operation = '/' THEN Check_Divisor (Second_Digit, Not_Zero); ELSE Not_Zero := True; END IF; IF Not_Zero THEN Perform_Operation (First_Digit, Second_Digit, Operation, Result); Text_IO.Put (Item => "Result = "); Integer_Text_IO.Put (Item => Result); Text_IO.New_Line; END IF; Prompt_Again (Repeat); END LOOP; END Calculator; -- COMMENTS by G. Levine 10/17/96 -- This is a very credible first Ada program. I assume that you -- would have done a lot of things differently if you knew more Ada. -- Functions and procedures were introduced for reasons of reuse. -- Read the work of Grace Hopper as she talks of the development of a -- correct cosine routine in octal, that was then passed to everyone else -- working on a computer application. -- Functions, procedures, and other modules then began to be used for -- modularity. Although creating modular programs supports reuse in that -- it assists in maintenance, it is not specific to reuse. -- Ada allows us to divorce these two uses for modules, with its -- internal and external modules. If you only want to divide your program -- into smaller units to make it easier to develop and test, use internal -- modules that hide implementation details from the outside world -- (the implementation should generally not be available to others). -- These modules can then be very specific to their program's requirements. -- If you want to create modules that may be used by other parts of -- the program, or by different programs, then create external units. -- In that case, the external units should be generalized as much as -- possible to assist reuse. -- In particular, I wonder if your procedure Check_Divisor should not -- be nested within Perform_Operation. Alternatively, you might -- handle this procedure's function with an exception in Perform_Operation. -- In any case, Get_First_Digit and Get_Second_Digit should be combined -- as a single module, with the string "first" or "second" entered as -- a parameter. Better yet, the entire output line might be a -- parameter, for a slightly more reusable module. -- Of course, the use of the predeclared INTEGER type is neither portable -- nor appropriate for the application. Did you mean to limit yourself -- to a digit, as the name indicates, rather than a general number? -- You can make the operation that inputs a number much more reusable -- with the following two files; The specification: -- get_number.ads --~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -- generic -- type INTEGER_TYPE is range <>; -- procedure GET_NUMBER (Number: out INTEGER_TYPE; -- Message: in STRING); --~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -- and then in the body of the procedure, which is called -- get_number.adb, place ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ --with TEXT_IO; --procedure GET_NUMBER (Number: out INTEGER_TYPE; -- Message: in STRING) is -- Temp : INTEGER_TYPE; -- package INTEGER_TEXT_IO is new TEXT_IO.INTEGER_IO (INTEGER_TYPE); --BEGIN -- your code inserted here -- LOOP -- BEGIN -- Text_IO.Put (Item => Message ); -- Integer_Text_IO.Get (Item => Temp); -- Number := Temp; -- EXIT; -- EXCEPTION -- WHEN Constraint_Error => -- Text_IO.Put ("Value entered is out of range. Please try again."); -- Text_IO.New_Line; -- WHEN Text_IO.Data_Error => -- Text_IO.Put ("Value entered is not an integer. Please try again."); -- Text_IO.New_Line; -- Text_IO.Skip_Line; -- END; -- END LOOP; --END GET_NUMBER ; --~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -- call this with an instantiation such as --~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ --with TEXT_IO, GET_NUMBER; --procedure Calculator is -- type NUMBER_TYPE is range -100 ..100; -- First, Second: NUMBER_TYPE; -- procedure Get_Value is new Get_Number (INTEGER_TYPE => NUMBER_TYPE); -- begin -- Get_Value (First, "Enter the first number"); -- Get_Value (Second, "Enter the second number"); -- end Calculator; --~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -- Unfortunately, when you execute this, you find some of the bugs of -- GNAT Ada. Although the run time program flags 101 as "not an integer", -- it does not catch 202. I have informed the GNAT people, and they -- say that they will fix this bug next time. In the meantime, they -- recommend that you run with the -gnato option (see Gnat under -- environments), which is quite expensive, but OK for development. -- Run this program on the VAX DEC compiler and the out-of-range values -- are correctly caught. -- You might consider using Text_IO.Put_Line instead of Put and New_line. -- I know that Feldman's text does not, but I have no problem with it. -- Of course, the use of literals in Get_Operator does not support -- reusability. If this module is nested inside of Calculator, I -- would say that there is no reason to make it reusable. -- Consider making Check_Divisor a function rather than a procedure, -- allowing the main procedure to check -- "if Operation /= '/' or else Divisor_OK (Second_Digit) then" -- Perform Operation, -- etc. -- -- Interestingly enough, Perform_Operation could be generic, with the -- specific operation passed as a parameter. It would be useful to -- try as an exercise, but is not critical in such a simple example. -- Your style matches Feldman's text rather than our style guides. -- We'll make a decision on this for our library when the entire -- class is ready to discuss style guides.