I've just started learning Ada and I cannot figure out how to keep the program running when the user input is beyond the declared range of a variable. I'd like to print info about bad range of input and then ask user for input again.
This is my simple code:
with Ada.Text_IO;
use Ada.Text_IO;
procedure Main is
type Score is range 0..100;
a : Score;
begin
Put_Line ("Enter value range 0-100: ");
a := Score'Value(Get_Line);
if a'Valid then
Put_Line ("You entered" & Score'Image (a));
else
Put_Line ("Bad range of input");
end if;
end Main;
Shouldn't I use the "range" in order to check the input, but rather some if's with >, < restrictions?
My other approach was to try this with exceptions, but it also doesn't work as I want it to:
with Ada.Text_IO;
with Ada.IO_Exceptions;
use Ada.Text_IO;
procedure Main is
type Score is range 0..100;
a : Score;
begin
loop
begin
Put_Line ("Enter value range 0-100: ");
a := Score'Value(Get_Line);
Put_Line ("You entered" & Score'Image (a));
exit;
exception
when Ada.IO_Exceptions.Data_Error =>
Put_Line("Bad range of input");
end;
end loop;
end Main;
I believe the problem is in my lack of understanding this language, but I hope there is some kind of easy solution for this, thanks for any help.
Now you know a magical incantation that works, but I doubt you understand why it works, or why your other incantations didn't work. I will go into painful pedagogical detail about that, in hopes that some of it might be useful or of interest.
In Ada, when you declare a type, the type is anonymous, and the name (Score
) you give is the name of the first-named subtype. The first-named subtype may have constraints that don't apply to the anonymous base type. For some types, including integer types, it's possible to refer to the anonymous base type with 'Base
.
Since you declared Score
using range
, it is a signed integer type and its base type is (roughly) symmetrical around zero. So your declaration is equivalent to something like
type Score'Base is range -128 .. 127;
subtype Score is Score'Base range 0 .. 100;
(this is not Ada and will not compile).
Score'Value
returns a value of Score'Base
(ARM 3.5 (53)), so if you input "101"
or "-3"
, Score'Value
will succeed and return the appropriate value. When you assign that value to your variable of subtype Score
, a check is performed that the value is in the range of Score
; when that fails, Constraint_Error
is raised. If you input an invalid image, such as "200"
or "xyz"
, Score'Value
fails and raises Constraint_Error
. So you have two kinds of incorrect input resulting in two different failures, both of which happen to raise the same exception.
Your first version failed because you never got to the if
statement. Your second version failed because Ada.Text_IO.Get_Line
never raises Data_Error
.
When dealing with numeric input, I advise that a complete line be read into a String and you then parse out the value(s) from that String, as you have done. However, 'Value
will reject some input that you might want to consider valid. For example, you might want to accept "23 skidoo"
and get the value 23 from it. For that, you might want to instantiate Ada.Text_IO.Integer_IO
for your numeric (sub)type and use the Get
function that takes a String
parameter:
package Score_IO is new Ada.Text_IO.Integer_IO (Num => Score);
...
Score_IO.Get (From => "23 skidoo", Item => A, Last => Last);
will set A
to 23 and Last
to the index of '3'
in From
(2).
HTH