exceptioninputada

How to check the input? Ada language


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.


Solution

  • 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