Ada.Text_IO.End_Of_File buffering side effect - io

I am trying to build an REPL in Ada after not having touched the language for many years.
If I check for END_OF_FILE in the code, the way that Text_IO buffers input and output changes. I remember this being the case but cannot find a description of why or how to deal with this. (I suspect this is a difference between terminal IO and file based IO)
Can anyone point me to a description of why Text_IO behaves this way?
with Ada.Text_IO; use Ada.Text_IO;
with Ada.Exceptions; use Ada.Exceptions;
with Ada.Strings.Unbounded; use Ada.Strings.Unbounded;
procedure fred is
Buffer: Unbounded_String := Null_Unbounded_String;
L : integer := 0;
begin
-- while (not End_Of_File)
loop
L := L+1;
put("--> ");
Buffer := To_Unbounded_String(Get_Line);
Put_Line(Integer'image(L) & " " & to_String(Buffer));
end loop;
exception
when END_ERROR =>
put_line("");
put_line("Bye ...");
end fred;
Running this code as-is gives
--> a
1 a
--> s
2 s
--> d
3 d
-->
Bye ...
If I uncomment the while statement I get
a
--> 1 a
s
--> 2 s
d
--> 3 d

Since End_Of_File requires user input, just place it right where you read the user input:
loop
L := L + 1;
Put ("--> ");
exit when End_Of_File;
Buffer := To_Unbounded_String (Get_Line);
Put_Line (Integer'Image(L) & " " & To_String (Buffer));
end loop;

Related

Storing and using strings of varying length (ADA)

Im working on a problem where I need to make a set of boxes according to an input number where each and every box has a unique name. I've managed to create the boxes but I've only managed to insert one name on all of them as my names are overwritten in the name collecting procedure.
here is the code https://pastebin.com/FBMvvrn4
with Ada.Text_IO; use Ada.Text_IO;
with Ada.Float_Text_IO; use Ada.Float_Text_IO;
with Ada.Integer_Text_IO; use Ada.Integer_Text_IO;
procedure exercise is
N : Integer;
Names : String(1..10);
L : Integer;
procedure Objectcatcha (N: out Integer) is
begin
Put("Enter amount of objects: ");
Get(N);
end Objectcatcha;
procedure Namescatcha (Names: out string; L: out integer) is
begin
for I in 1..N loop
Get_Line(Names, L);
end loop;
end Namescatcha;
procedure SpaceBox(Names: in String; L: in Integer; N : in integer) is
begin
for I in 1..N loop
Put("+-----------+ ");
end loop;
New_Line;
for I in 1..N loop
Put("! ");
Put(Names(1..L));
for J in (L+1)..10 loop
Put(" ");
end loop;
Put("!");
if I = N then
Put("");
else
Put("<>---");
end if;
end loop;
New_Line;
for I in 1..N loop
Put("+-----------+ ");
end loop;
end SpaceBox;
begin
Objectcatcha(N);
Put("Enter the name of the objects: ");
Namescatcha(Names, L);
SpaceBox(Names,L, N);
end exercise;
I've been sitting around a lot with this and Id be very glad if someone could help me find a way to name each box individually.
Thanks in advance!
Where you can, (and you can here), just declare a variable of the exact size to hold the name you are using. This can be done by declaring it as an indefinite array, and initialising it with the correct name.
So your main program could be:
Objectcatcha(N);
For I in 1 to N loop
Put("Enter the name of the next object: ");
Declare
Name : String := Namescatcha;
Begin
SpaceBox(Name, Name'Length, N);
End;
End loop;
Namescatcha is now a function returning just the right size of string:
function Namescatcha return String is
begin
return Getline;
end Namescatcha;
and you should probably rewrite Spacebox without L (you can always use Name'Length to see the length of Name)
Brian Drummond already covered how to get back a variable length name and some means to work with them. In order to address your one name overwriting all the names problem, you have to consider that you are using one name variable to hold them all so it makes sense that one is overwriting the others. To store multiple names together, consider using an Indefinite_Vector to hold them. In your Objectcatcha procedure you get the capacity so use that to set the size of the vector
with Ada.Text_IO; use Ada.Text_IO;
with Ada.Containers.Indefinite_Vectors; use Ada.Containers;
-- Other stuff
package Name_Vectors is new Indefinite_Vectors
(Index_Type => Positive,
Element_Type => String);
Names : Name_Vectors.Vector;
package Count_Type_IO is new Integer_IO(Count_Type);
procedure Objectcatcha is
Number : Count_Type;
Last : Positive; -- Placeholder for Get call
begin
Put("Enter amount of objects: ");
Count_Type_IO.Get
(From => Get_Line,
Item => Number,
Last => Last);
Names.Reserve_Capacity(Number);
end Objectcatcha;
procedure Namescatcha is
begin
for Index in 1..Names.Capacity loop
Names.Append(Get_Line);
end loop;
end Namescatcha;
You will need to adjust your SpaceBox procedure to use the vector instead of the name or to only do one name at a time (your choice).
A few notes:
1. I changed your Get call to Get_Line for getting the number of names, you can change it back if you want.
2. When I stored the names in the vector, the last character stored might be the "new line" character, so you may have to strip it out. That's easy to do. Just use all the characters of the name except the last one. for example:
EDIT: Simon Wright indicated that this shouldn't be needed. My implementation does, so I'll leave this here if you have a setup similar to what I tested on and the new lines are copied.
declare
Name : String := Names(Index);
begin
Put(Name(1..Name'Length-1));
end;
Since your program seems to be a non-performance-critical application, I'd use variable-size strings to avoid storing the N different string lengths.
In plain Ada variable-size strings are called Unbounded_String.
Here, your exercise using an open-source package (hac_pack: spec, body) which facilitates things around variable-size strings.
with HAC_Pack; use HAC_Pack;
procedure Names_in_Boxes is
Max : constant := 100;
type Names_Type is array (1 .. Max) of VString;
procedure Objectcatcha (N: out Integer) is
begin
Put("Enter amount of objects: ");
Get(N);
Skip_Line;
end Objectcatcha;
procedure Namescatcha (Names: out Names_Type; N : in Integer) is
begin
for I in 1..N loop
Put(+"Object " & I & ": ");
Get_Line(Names (I));
end loop;
end Namescatcha;
procedure SpaceBox(Names: in Names_Type; N : in Integer) is
begin
Put_Line (N * (+"+-----------+ "));
for I in 1..N loop
Put("! " & Names(I) & (10 - Length(Names(I))) * ' ' & "!");
if I = N then
Put("");
else
Put("<>---");
end if;
end loop;
New_Line;
Put_Line (N * (+"+-----------+ "));
end SpaceBox;
-- "Global" variables, unknown to
-- Objectcatcha, Namescatcha, SpaceBox:
N : Integer;
Names : Names_Type;
begin
Objectcatcha(N);
Put_Line("Enter the name of the objects: ");
Namescatcha(Names, N);
SpaceBox(Names, N);
end Names_in_Boxes;
Thanks Zerte, Jere and Brian for your examples, it's much appreciated. Unfortunately I can't use third party packages so that rules out Zertes solution and as for Jere Im sorry but Im just a simple codemonkey with very shallow ADA knowledge and your example is just too complicated for me. Even if I got the exact code and it worked I still wouldnt learn it because it differs too much from what my school is teaching out. Like the procedures not having in/out parameters lets say for one. Maybe I misunderstand and its not that bad but at first glance it seems too complex for my level of ADA.
Brians I thought would work but what it does is, because it loops Spacebox N times, it creates N^2 amount of boxes, and on separate lines, when I only need N boxes on one line. Is there any way we could perhaps patch up the code to fix this because it seemed promising?
Thanks again for your time all of you!
with Ada.Text_IO; use Ada.Text_IO;
with Ada.Float_Text_IO; use Ada.Float_Text_IO;
with Ada.Integer_Text_IO; use Ada.Integer_Text_IO;
with Ada.Strings.Unbounded; use Ada.Strings.Unbounded;
with Ada.Strings.Unbounded.Text_IO; use Ada.Strings.Unbounded.Text_IO;
procedure exercise is
Y : Ada.Text_IO.Count;
N : Integer;
Names : Unbounded_string;
type Names_Type is array (Positive range <>) of Unbounded_String;
procedure Objectcatcha (N : out Integer) is
begin
Put("Enter amount of objects: ");
Get(N);
Skip_Line;
end Objectcatcha;
procedure Namescatcha (Names: out Names_Type; N : in Integer) is
begin
for I in Names'Range loop
Get_Line(Names(I));
end loop;
end Namescatcha;
procedure SpaceBox (Names: in Names_Type; N : in Integer) is
begin
for I in 1..N loop
Put("+-----------+ ");
end loop;
New_Line;
for I in Names'Range loop
Put("! ");
Put(Names(I));
Y := Ada.Text_IO.Count(I);
Set_Col(13+18*(Y-1));
Put("!");
if I = N then
Put("");
else
Put("<>---");
end if;
end loop;
New_Line;
for I in 1..N loop
Put("+-----------+ ");
end loop;
end SpaceBox;
begin
Objectcatcha(N);
declare
A : Names_Type (1..N);
begin
Put("Enter the name of the objects: ");
Namescatcha(A, N);
SpaceBox(A, N);
end;
end exercise;
This code works exactly the way I wanted it to, so I think it's finally solved: yay! :)

Ada Integer'Width error

I have this Ada package code which theoretically is well written:
with Ada.Text_IO;
with Ada.Characters.Handling;
package body pkg_procedure is
procedure Read_Integer(Num : out Integer) is
Intro : constant Character := ASCII.LF;
Back : constant Character := ASCII.Del;
Char : Character;
Fin : Boolean := False;
Number : Natural := 0;
String_Number : String (1 .. Integer'Width – 1);
begin
Ada.Text_IO.New_line;
Ada.Text_IO.Put ("Write down a number and press Enter: ");
while not Fin loop
Ada.Text_IO.Get_Immediate (Char);
if Ada.Characters.Handling.Is_Digit (Char) then
Number := Number + 1;
String_Number(Number) := Char;
Ada.Text_IO.Put (Char);
elsif Char = Intro then
Fin := True;
elsif Number > 0 and Char = Back then
Ada.Text_IO.Put (ASCII.BS & ' ' & ASCII.BS);
Number := Number + 1;
end if;
end loop;
Number := Integer'Value (String_Number (1 .. Number));
Ada.Text_IO.New_line;
Num := Number;
exception
when Constraint_Error =>
Ada.Text_IO.New_line;
Ada.Text_IO.Put_Line ("Sorry: " & String_Number & " is too long to store it");
Num := 0;
end Read_Integer;
end pkg_procedure;
When I compile the program I obtain an error on this instruction which says: binary operator expected.
I can't fix it. I am totally new to this programming language.
The problem turns out to be that the - in
String_Number : String (1 .. Integer'Width – 1);
isn’t a plain - but a wide character with encoding e28093 - EN DASH.
I found this because, having seen that various exploratory changes didn’t show the error, I reverted to your original and tried compiling with -gnatw8 (input is UTF-8) as well as -gnatl for mixing messages with the program text, which resulted in
13. String_Number : String (1 .. Integer'Width – 1);
12
>>> binary operator expected
>>> illegal wide character
I suspect you provided us the wrong part of your code as this
with Ada.Text_Io; use Ada.Text_Io;
procedure TestInt is
number : String (1 .. Integer'Width - 1);
begin
Put_Line("Width=" & Integer'Image(Integer'Width - 1));
end TestInt;
works like a charm, if we ignore the warning on number which is not used, and return as expected :
Width= 10
Please be more precise and provide a full compilable sample.
I might also be interesting to tell us which compiler you use and on which operating system.

Ada- attempting to remove blank spaces from a string?

This question is in regards to a program I'm working on for an intro to programming class in Ada. The only packages I can use are the basic text, integer, and float packages. I'm writing a procedure right now to remove the spaces from the string and I keep getting a constraint error on the same line. This is the line:
if inputString(count) /= Character'Val(32) then
I have count set to start at 1 and then increment through a loop until the end of the string, which would in turn check each character of the string to see if it was different than the character value of a blank space (32). I was then going to include this line:
noSpace(count..count) := inputString(count..count)
where noSpace is to be the string with no spaces. My line of thinking may be completely off and I may need to approach this from a completely different angle.
Update: I've now managed to get past the error message and successfully remove the spaces from my string. But now when I print the new string there is a random variety of characters and shaded spaces after it. I believe the problem is caused because the new string is shorter than the previous one and the last characters are different.
Probably the bug is hid somewhere else.
Try this:
with Ada.Text_IO;
procedure String_Remove_Space is
function Count_Space( S : String ) return Natural is
Sum_Of_Space : Natural := 0;
begin
for I in S'range loop
if S(I) = ' ' then
Sum_Of_Space := Sum_Of_Space + 1;
end if;
end loop;
return Sum_Of_Space;
end Count_Space;
Input_String : String := "Apple is on the tree";
Input_No : String := "AppleIsOnTheTree";
No_Space : String(1..Input_String'Length - Count_Space(Input_String));
Index : Positive := 1;
begin
for I in Input_String'range loop
if Input_String(I) /= ' ' then
No_Space(Index) := Input_String(I);
Index := Index + 1;
end if;
end loop;
Ada.Text_IO.Put_Line(Input_String);
Ada.Text_IO.Put_Line(No_Space);
end String_Remove_Space;
If there are spaces in your input string, the valid part of the output string must be shorter. I imagine you wrote something like
noSpace : String := inputString;
to start things off with noSpace as long as it would need to be if there were no spaces in inputString? then, as you squeeze the spaces out, you seem to be writing garbage (undefined characters) to the end of noSpace. This doesn’t matter so long as you only deal with the valid part.
I tried this:
with Ada.Text_IO; use Ada.Text_IO;
procedure Lareaper is
function Strip_Space (S : String) return String is
Result : String := S;
Current : Positive := Result'First;
Last : Natural := Result'Last;
begin
loop
exit when Current > Last; -- processed the whole string
if Result (Current) = ' ' then
-- slide the rest of the string back one
Result (Current .. Last - 1) := Result (Current + 1 .. Last);
-- which reduces the length by 1 too
Last := Last - 1;
else
-- non-space character, skip
Current := Current + 1;
end if;
end loop;
-- return only the part of the result that doesn't contain spaces
return Result (Result'First .. Last);
end Strip_Space;
begin
Put_Line ('|' & Strip_Space ("") & '|');
Put_Line ('|' & Strip_Space (" ") & '|');
Put_Line ('|' & Strip_Space ("a") & '|');
Put_Line ('|' & Strip_Space ("ab") & '|');
Put_Line ('|' & Strip_Space (" a") & '|');
Put_Line ('|' & Strip_Space ("a ") & '|');
Put_Line ('|' & Strip_Space ("a b") & '|');
Put_Line ('|' & Strip_Space (" a b ") & '|');
end Lareaper;
which outputs
$ ./lareaper
||
||
|a|
|ab|
|a|
|a|
|ab|
|ab|
Count should start at InputString'First, not at 1.
In particular, when InputString is created as a slice/substring of another string, it is very likely that its first character is not at index 1.
Just to show you another possible solution, although one I would never use in real life:
function Strip_Space (S : String) return String is
begin
if S'Length = 0 then
return "";
elsif S (S'First) = ' ' then
return Strip_Space (S (S'First + 1 .. S'Last));
else
return S (S'First) & Strip_Space (S (S'First + 1 .. S'Last));
end if;
end Strip_Space;
And then there's the way I would really do it. I didn't read Simon's answer carefully enough to realize that he was sliding substrings around. The most efficient answer, I think, is similar to Mark's, but you don't actually need to count the spaces in the string first.
function Strip_Space (Input_String : String) return String is
No_Space : String(1..Input_String'Length);
Index : Positive := 1;
begin
for I in Input_String'range loop
if Input_String(I) /= ' ' then
No_Space(Index) := Input_String(I);
Index := Index + 1;
end if;
end loop;
return No_Space(1 .. Index - 1);
end Strip_Space;

Pascal program doesnt show the full output

My school assignment was to create a menu-like program with pascal that shows the code of other programs. It reads the text from a file line by line and prints it to the console. It works just fine to the point when it doesnt show the full output. This is the output that it shows:
http://postimg.org/image/3r4ua8poz/
When it runs it shows everything line by line, but at the end you can only see the part of the code that fits in the console window and you cant scroll up. Can anyone tell me how to fix this? It will be much appresiated.
Here is the actuall code:
program pascalMenu //ime na programata
{$mode objfpc};//OOP pascal ili neshto takova(zaduljitelno e)
uses sysutils,
crt, graph;
Var choice : array[1..5] of string;//masiv, v sluchaq sadurja chas ot imenata na failovete
programFile : Text;//shte sudurja imeto na faila
userFile,line,number,filename : string;
i,j,value : integer;
c : char;
begin
writeln('This program will open all of our previous tasks.');
//pulnene na masiva
for i := 1 to 5 do
begin
str(i,number);
choice[i] := 'program' + number;
end;
j:=1;
repeat
writeln( 'Use the arrow keys to navigate, ENTER to open and ESC to close.');
//pokazvane na elementite v masiva sled vsqka vrutka na cikula
for i:= 1 to 5 do
begin
if i = j then
begin
//tva e za cveta
textcolor(red);
writeln(choice[i]);
textcolor(white);
continue;
end;
writeln(choice[i]);
end;
//beggining the menu loop
c := readkey;//chetene na kopche rofl
clrscr;// - chisti konzolata(optional)
if ord(c) = 0 then
c := readkey;
value := ord(c);
//options
case value of
72 :
begin
j:= j - 1;
if j < 1 then j :=5;
end;
80:
begin
j:= j + 1;
if j > 5 then j:= 1;
end;
13:
begin
str(j,number);
filename := 'program' + number + '.txt';
assignfile(programFile,filename);//prilaga imeto na faila na promenlivata
//tva nz za kakvo e, obache sled 5-q opit reshi, che bez nego nemoje
try
reset(programFile);//otvarq faila za chetene
repeat
readln(programFile, line);//chete 1 red ot faila v line
writeln(line);
until (Eof(programFile));//eof - end of file
closefile(programFile);//zaduljitelno!! bez nego ne mogat dase otvarqt 2/poveche faila
//zaradi greshka v read/write dostupa
except on E:EInOutError do begin
writeln('The program could not read the file. Check the file''''s''name and directory.');end;
end;
writeln('Press any key to return to the main menu.');
writeln('P.S. - This will erase the text');
readln;
clrscr;
end;
end;
until value =27;
end.
The scrolling is prohibited by the use of unit Crt. Unuse it.
Or have a look at the "lister" demo that comes with FPC, which tries a bit of the same (cursor movement for selection and displaying)

Ada: Getting user input to a String(1..10) and filling the rest with whitespace

I have defined
subtype String10 is String(1..10);
and I am attempting to get keyboard input to it without having to manually enter whitespace before hitting enter. I tried get_line() but from some reason it wouldn't actually wait for input before outputting the get put() command, and I also think it will just leave whatever was in the string before there and not fill it with white space.
I know about and have used Bounded_String and Unbounded_String, but I am wondering if there is a way to make this work.
I've tried making a function for it:
--getString10--
procedure getString10(s : string10) is
c : character;
k : integer;
begin
for i in integer range 1..10 loop
get(c);
if Ada.Text_IO.End_Of_Line = false then
s(i) := c;
else
k := i;
exit;
end if;
end loop;
for i in integer range k..10 loop
s(i) := ' ';
end loop;
end getString10;
but, here, I know the s(i) doesn't work, and I don't think the
"if Ada.Text_IO.End_Of_Line = false then"
does what I'm hoping it will do either. It's kinda just a placeholder while I look for the actual way to do it.
I been searching for a couple hours now, but Ada documentation isn't as available or clear as other languages. I've found a lot about getting strings, but not what I'm looking for.
Just pre-initialize the string with spaces before calling Get_Line.
Here's a little program I just threw together:
with Ada.Text_IO; use Ada.Text_IO;
procedure Foo is
S: String(1 .. 10) := (others => ' ');
Last: Integer;
begin
Put("Enter S: ");
Get_Line(S, Last);
Put_Line("S = """ & S & """");
Put_Line("Last = " & Integer'Image(Last));
end Foo;
and the output I get when I run it:
Enter S: hello
S = "hello "
Last = 5
Another possibility, rather than pre-initializing the string, is to set the remainder to spaces after the Get_Line call:
with Ada.Text_IO; use Ada.Text_IO;
procedure Foo is
S: String(1 .. 10);
Last: Integer;
begin
Put("Enter S: ");
Get_Line(S, Last);
S(Last+1 .. S'Last) := (others => ' ');
Put_Line("S = """ & S & """");
Put_Line("Last = " & Integer'Image(Last));
end Foo;
For very large arrays, the latter approach might be more efficient because it doesn't assign the initial portion of the string twice, but in practice the difference is unlikely to be significant.
As an alternative, use either function Get_Line, which returns a fixed-length String that "has a lower bound of 1 and an upper bound of the number of characters read." The example Line_By_Line uses the variation that reads from a file. If need be, you can then use procedure Move to copy the Source string to the Target string; the procedure automatically pads with space by default.
Addendum: For example, this Line_Test pads with * and silently truncates long lines on the right.
with Ada.Integer_Text_IO;
with Ada.Strings.Fixed;
with Ada.Text_IO;
procedure Line_Test is
Line_Count : Natural := 0;
Buffer: String(1 .. 10);
begin
while not Ada.Text_IO.End_Of_File loop
declare
Line : String := Ada.Text_IO.Get_Line;
begin
Line_Count := Line_Count + 1;
Ada.Integer_Text_IO.Put(Line_Count, 0);
Ada.Text_IO.Put_Line(": " & Line);
Ada.Strings.Fixed.Move(
Source => Line,
Target => Buffer,
Drop => Ada.Strings.Right,
Justify => Ada.Strings.Left,
Pad => '*');
Ada.Integer_Text_IO.Put(Line_Count, 0);
Ada.Text_IO.Put_Line(": " & Buffer);
end;
end loop;
end Line_Test;

Resources