1
votes

Im working on an assignment that's similar to the flag package problem already solved on this site ( Package bodies and main programs. Simple assignment (Ada) )

This time however I need to draw a book cover with 4 folds on it as shown below:

Book with folds at the breaking points: 3, 10, 15, 17.
Height = 2, Width = 20, 
Title (string(1..100),
Author (string(1..50).

<Title>
<Author>
|--|------|----|-|---| 
|  |      |    | |   |
|  |      |    | |   |   
|--|------|----|-|---|

I get a host of problems emanating from line 25 in exercise_bluebook_main.adb, but the central one that bugs me the most is

exercise_bluebook_main.adb:25:11: expected type "Standard.Integer" exercise_bluebook_main.adb:25:11: found type "Breaking_Array" defined at exercise_bluebook.ads:7

Why does it expect a standard integer when Im out for the array as defined? Furthermore I think in the declare part of the main procedure line 15 Im in for trouble as the :=get_line is already taken by "Title". How would you solve that, anyone?

Maybe there are more faults in the code than that, Im thankful for any and all input as long as its constructive!

my .ads file

with ada.text_IO; use ada.text_IO;
with ada.integer_text_IO; use ada.integer_text_IO;

package Exercise_BlueBook is

   subtype Break_Points is integer range 1..20;
   type Breaking_Array is array (1..4) of Break_Points;
   type Book_Type is private;

   procedure Get (Item: out Book_Type;
          Title: in String;
          Author: in String;
          Width: in Integer;
          Height: in Integer;
          Break_Points: in Breaking_Array);

   procedure Put (Item: in Book_Type);


private

   type Book_Type is
      record

     Title : String(1..100);
     Title_L : Integer;
     Author : String(1..50);
     Author_L : Integer;
     Width : Integer;
     Height : Integer;
     Break_Points : Integer; 

      end record;

end Exercise_BlueBook;

my package body adb file

with ada.text_IO; use ada.text_IO;
with ada.integer_text_IO; use ada.integer_text_IO;

with Exercise_Bluebook; use Exercise_Bluebook;

package body Exercise_BlueBook is

  procedure Get (Item: out Book_Type;
         Title: in String;
         Author: in String;
         Width: in Integer;
         Height: in Integer;
         Break_Points: in Breaking_Array) is

  begin

     Item.Title_L := Integer'Min (Item.Title'Length, Title'Length);
     Item.Title (1..Item.Title_L) := Title(Title'First .. Item.Title_L);
     Item.Author_L := Integer'Min (Item.Author'Length, Author'Length);
     Item.Author (1..Item.Author_L) := Author (Author'First .. Item.Author_L);
     Item.Width := Width;
     Item.Height := Height;
     Item.Break_Points := Break_Points;

  end Get;

  procedure Put (Item: in Book_Type) is


  begin

     Put_Line(Item.Title(1..Item.Title_L));
     Put_Line(Item.Author(1..Item.Author_L));


     for H in 1..Item.Height loop
    Put("!");

    for I in 1..Item.Width loop
       Put("-");
       if I = Breaking_Array(1) then
          Put("!");
       elsif I = Breaking_Array(2) then
          Put("!");
       elsif I = Breaking_Array(3) then
          Put("!");
       elsif I = Breaking_Array(4) then
          Put("!");
       end if;
    end loop;
    Put_Line("!");
     end loop;

     end Put;

end Exercise_BlueBook;

my main procedure

with ada.text_IO; use ada.text_IO;
with ada.integer_text_IO; use ada.integer_text_IO;

with Exercise_Bluebook; use Exercise_Bluebook;

procedure Exercise_BlueBook_Main is

   B : Book_Type;

begin

   Put("Enter the name of the book: ");
   declare
      Title : constant String := Get_Line; -- First Get_Line already taken
      Author: constant String := Get_Line; -- How do I solve this for "Author"?
      Width: Integer;
      Height: Integer;
      Break_Points : Exercise_BlueBook.Breaking_Array;
   begin
      Put("Enter the book's width: ");
      Get(Width);
      Put("Enter the book's height: ");
      Get(Height);
      Put("Enter the breaking points for the fold: ");
      Get(Break_Points); -- Won't read this as an array?

      Get(B, 
      Title => Title,
      Author => Author,
      Width => Width,
      Height => Height,
      Break_Points => Break_Points);
   end;

   New_Line;
   Put(B);


end Exercise_BlueBook_Main;
2

2 Answers

4
votes

As it stands, the main program prompts for the title, then calls Get_Line for the Title, then another Get_Line for the Author. The following data is read using Gets; the way that works is that characters are read, skipping white space until something that could be a part of a number is found, then more characters are read until something that couldn’t be part of a number is found.

The reason we’ve been suggesting the form

   declare
      Title : constant String := Get_Line;

is that a String is a fixed-length array of characters; how can you know ahead of time how long to make it? and the way this works is that the actual length required is determined at run time by the length of the initialization value returned by Get_Line.

You could fix the lack of a prompt for the author by changing the prompt: "Enter the book’s name and the author’s name, hit RET after each:".

Or you could nest them:

   Put("Enter the name of the book: ");
   declare
      Title : constant String := Get_Line;
   begin
      Put("Enter the author's name: ");
      declare
         Author: constant String := Get_Line;
         Width: Integer;
         Height: Integer;
         Break_Points : Exercise_BlueBook.Breaking_Array;
      begin
         Put("Enter the book's width: ");
         Get(Width);
         Put("Enter the book's height: ");
         Get(Height);
         Put("Enter the breaking points for the fold: ");

And now we come to read the Break_Points. You need to read each element with its own Get:

         for J in Break_Points'Range loop
            Get(Break_Points(J));
         end loop;
         Get(B,
             Title => Title,
             Author => Author,
             Width => Width,
             Height => Height,
             Break_Points => Break_Points);
      end;
   end;
2
votes

The Get subprogram from the Ada.Integer_Text_IO package will read only one integer. You could extend it by adding a loop:

main.adb

with Ada.Text_IO; use Ada.Text_IO;

procedure Main is

   generic
      type Num is range <>;
      type Arr is array (Integer range <>) of Num;
   procedure Get (From : String; Item : out Arr);

   ---------
   -- Get --
   ---------

   procedure Get (From : String; Item : out Arr) is

      package Num_IO is
        new Ada.Text_IO.Integer_IO (Num);

      Last : Integer := From'First - 1;

   begin
      for Idx in Item'Range loop
         Num_IO.Get (From (Last + 1 .. From'Last), Item (Idx), Last);
      end loop;
   end Get;


   subtype Break_Points is Integer range 1 .. 20;
   type Breaking_Array is array (Integer range <>) of Break_Points;

   procedure Get_Breaking_Array is
     new Get (Break_Points, Breaking_Array);

   BA : Breaking_Array (1 .. 4);

begin

   Get_Breaking_Array (Get_Line, BA);

   New_Line;
   Put_Line("Result:");

   for BP of BA loop
      Put (BP'Image);
      New_Line;
   end loop;

exception
   when Data_Error  =>
      Put_Line ("Error: Invalid data.");

   when End_Error  =>
      Put_Line ("Error: Not enough elements.");

end Main;

output (too many elements, only first 4 are read).

$ ./main
1 2 3 4 5 6

Result:
 1
 2
 3
 4

output (too few elements).

$ ./main
1 2 3
Error: Not enough elements.

output (non-numeric data).

$ ./main
1 2 x 4
Error: Invalid data.

output (value of element outside range).

$ ./main
1 2 3 100
Error: Invalid data.