Ada Integer'Width error - width

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.

Related

Implementation of Integer'Value("X") in Ada

I'm going to create a subprogram with two parameters; one string and one integer. The subrprogram is going to compare these two and see if they are the same.
For instance:
Type a string containing exactly 5 characters, and an Integer: 12345 123
-- User types in bold
They are not the same!
with Ada.Text_IO; use Ada.Text_IO;
with Ada.Integer_Text_IO; use Ada.Integer_Text_IO;
procedure Test2 is
function String_Integer_Check(
S : in String;
I : in Integer) return Boolean is
begin
if Integer'Value(S) = I then
return True;
else
return False;
end if;
end String_Integer_Check;
S : String(1..5);
I : Integer;
begin
Put("Type in a string containing exactly 5 characters, and an integer: ");
Get(S);
Get(I);
Put("They are ");
if String_Integer_Check(S, I) = False then
Put("not ");
end if;
Put("the same.");
end Test2;
My program works, assuming that the user types in a string of 5 characters. If the user doesn't my program won't work. How do I fix this?
If I type 123 1234 (String is 3 characters and the Integer is 4 numbers), I will get this error:
They are
raised CONTRAINT_ERROR : bad input for 'Value: "123 1"
Ensure the two inputs are on different lines. The I/O problems you are seeing result from mixing string I/O and integer I/O on the same input line. This is a problem when the string portion of the input contains more or less than 5 characters.
with Ada.Text_IO; use Ada.Text_IO;
with Ada.Integer_Text_IO; use Ada.Integer_Text_IO;
procedure value_compare is
str_num : String (1 .. 80);
length : Natural;
num : Integer;
begin
Put ("Enter a 5 digit number: ");
Get_Line (Item => str_num, Last => length);
if length = 5 then
Put ("Enter a number: ");
Get (num);
if num = Integer'Value (str_num(1..Length)) then
Put_Line ("The two values are equal.");
else
Put_Line ("The two values are not equal.");
end if;
else
Put_Line
("The input value " & str_num (1 .. length) &
" does not contain 5 exactly characters.");
end if;
end value_compare;

Storing string references

Problem
There are multiple ways to store string reference, so how would you do it in the example code? Currently the problem is with storing access to string because it is causing non-local pointer cannot point to local object. Is storing 'First and 'Last to reference a string a preferable way?
String reference storage
This record stores reference to a string. The First and Last is supposed to point to a string. The Name should be able to the same I think, but that will cause non-local pointer cannot point to local object when a local string is assigned to that. So the current work around solution is to use First and Last.
type Segment is record
First : Positive;
Last : Positive;
Length : Natural := 0;
Name : access String;
end record;
Assigning sub string reference
The commented line is causing non-local pointer cannot point to local object. This is because Item is local. Source is not local and that is the string I want sub string references from.
procedure Find (Source : aliased String; Separator : Character; Last : out Natural; Item_Array : out Segment_Array) is
P : Positive := Source'First;
begin
for I in Item_Array'Range loop
declare
Item : aliased String := Separated_String_Next (Source, Separator, P);
begin
exit when Item'Length = 0;
Item_Array (I).Length := Item'Length;
Item_Array (I).First := Item'First;
Item_Array (I).Last := Item'Last;
--Item_Array (I).Name := Item'Access;
Last := I;
end;
end loop;
end;
Example
with Ada.Text_IO;
with Ada.Integer_Text_IO;
procedure Main is
use Ada.Text_IO;
use Ada.Integer_Text_IO;
function Separated_String_Next (Source : String; Separator : Character; P : in out Positive) return String is
A : Positive := P;
B : Positive;
begin
while A <= Source'Last and then Source(A) = Separator loop
A := A + 1;
end loop;
P := A;
while P <= Source'Last and then Source(P) /= Separator loop
P := P + 1;
end loop;
B := P - 1;
while P <= Source'Last and then Source(P) = Separator loop
P := P + 1;
end loop;
return Source (A .. B);
end;
type Segment is record
First : Positive;
Last : Positive;
Length : Natural := 0;
Name : access String;
end record;
type Segment_Array is array (Integer range <>) of Segment;
procedure Find (Source : String; Separator : Character; Last : out Natural; Item_Array : out Segment_Array) is
P : Positive := Source'First;
begin
for I in Item_Array'Range loop
declare
Item : aliased String := Separated_String_Next (Source, Separator, P);
begin
exit when Item'Length = 0;
Item_Array (I).Length := Item'Length;
Item_Array (I).First := Item'First;
Item_Array (I).Last := Item'Last;
--Item_Array (I).Name := Item'Access;
Last := I;
end;
end loop;
end;
Source : String := ",,Item1,,,Item2,,Item3,,,,,,";
Item_Array : Segment_Array (1 .. 100);
Last : Natural;
begin
Find (Source, ',', Last, Item_Array);
Put_Line (Source);
Put_Line ("Index First Last Name");
for I in Item_Array (Item_Array'First .. Last)'Range loop
Put (I, 5);
Put (Item_Array (I).First, 6);
Put (Item_Array (I).Last, 5);
Put (" ");
Put (Source (Item_Array (I).First .. Item_Array (I).Last));
New_Line;
end loop;
end;
Output
,,Item1,,,Item2,,Item3,,,,,,
Index First Last Name
1 3 7 Item1
2 11 15 Item2
3 18 22 Item3
The error message tells you exactly what is wrong : Item is a string declared locally, i.e. on the stack, and you are assigning its address to an access type (pointer). I hope I don't need to explain why that won't work.
The immediate answer - which isn't wrong but isn't best practice either, is to allocate space for a new string - in a storage pool or on the heap - which is done with new.
Item : access String := new String'(Separated_String_Next (Source, Separator, P));
...
Item_Array (I).Name := Item;
Note that some other record members, at least, Length all appear to be completely redundant since it is merely a copy of its eponymous attributes, so should probably be eliminated (unless there's a part of the picture I can't see).
There are better answers. Sometimes you need to use access types, and handle their object lifetimes and all the ways they can go wrong. But more often their appearance is a hint that something in the design can be improved : for example:
the Unbounded_String may manage your strings more simply
You could use the length as a discriminant on the Segment record, and store the actual string (not an Access) in the record itself
Ada.Containers are a standard library of containers to abstract over handling the storage yourself (much as the STL is used in C++).
If you DO decide you need access types, it's better to use a named access type type Str_Access is access String; - then you can create a storage pool specific to Str_Acc types, and release the entire pool in one operation, to simplify object lifetime management and eliminate memory leaks.
Note the above essentially "deep copies" the slices of the Source string. If there is a specific need to "shallow copy" it - i.e. refer to the specific substrings in place - AND you can guarantee its object lifetime, this answer is not what you want. If so, please clarify the intent of the question.
For a "shallow copy" the approach in the question essentially fails because Item is already a deep copy ... on the stack.
The closest approach I can see is to make the source string aliassed ... you MUST do as you want each Segment to refer to it ... and pass its access to the Find procedure.
Then each Segment becomes a tuple of First, Last, (redundant Length) and access to the entire string (rather than a substring).
procedure Find (Source : access String; Separator : Character;
Last : out Natural; Item_Array : out Segment_Array) is
P : Positive := Source'First;
begin
for I in Item_Array'Range loop
declare
Item : String := Separated_String_Next (Source.all, Separator, P);
begin
exit when Item'Length = 0;
...
Item_Array (I).Name := Source;
Last := I;
end;
end loop;
end;
Source : aliased String := ",,Item1,,,Item2,,Item3,,,,,,";
...
Find (Source'access, ',', Last, Item_Array);
for I in Item_Array (Item_Array'First .. Last)'Range loop
...
Put (Item_Array (I).Name(Item_Array (I).First .. Item_Array (I).Last));
New_Line;
end loop;
A helper to extract a string from a Segment would probably be useful:
function get(S : Segment) return String is
begin
return S.Name(S.First .. S.Last);
end get;
...
Put (get(Item_Array (I));
The only rationale I can see for such a design is where the set of strings to be parsed or dissected will barely fit in memory so duplication must be avoided. Perhaps also embedded programming or some such discipline where dynamic (heap) allocation is discouraged or even illegal.
I see no solution involving address arithmetic within a string, since an array is not merely its contents - if you point within it, you lose the attributes. You can make the same criticism of the equivalent C design : you can identify the start of a substring with a pointer, but you can't just stick a null terminator at the end of the substring without breaking the original string.
Given the bigger picture ... what you need, rather than the low level details of how you want to achieve it, there are probably better solutions.

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;

Strange behaviour when simply adding strings in Lazarus - FreePascal

The program has several "encryption" algorithms. This one should blockwise reverse the input. "He|ll|o " becomes "o |ll|He" (block length of 2).
I add two strings, in this case appending the result string to the current "block" string and making that the result. When I add the result first and then the block it works fine and gives me back the original string. But when i try to reverse the order it just gives me the the last "block".
Several other functions that are used for "rotation" are above.
//amount of blocks
function amBl(i1:integer;i2:integer):integer;
begin
if (i1 mod i2) <> 0 then result := (i1 div i2) else result := (i1 div i2) - 1;
end;
//calculation of block length
function calcBl(keyStr:string):integer;
var i:integer;
begin
result := 0;
for i := 1 to Length(keyStr) do
begin
result := (result + ord(keyStr[i])) mod 5;
result := result + 2;
end;
end;
//desperate try to add strings
function append(s1,s2:string):string;
begin
insert(s2,s1,Length(s1)+1);
result := s1;
end;
function rotation(inStr,keyStr:string):string;
var //array of chars -> string
block,temp:string;
//position in block variable
posB:integer;
//block length and block count variable
bl, bc:integer;
//null character as placeholder
n : ansiChar;
begin
//calculating block length 2..6
bl := calcBl(keyStr);
setLength(block,bl);
result := '';
temp := '';
{n := #00;}
for bc := 0 to amBl(Length(inStr),bl) do
begin
//filling block with chars starting from back of virtual block (in inStr)
for posB := 1 to bl do
begin
block[posB] := inStr[bc * bl + posB];
{if inStr[bc * bl + posB] = ' ' then block[posB] := n;}
end;
//adding the block in front of the existing result string
temp := result;
result := block + temp;
//result := append(block,temp);
//result := concat(block,temp);
end;
end;
(full code http://pastebin.com/6Uarerhk)
After all the loops "result" has the right value, but in the last step (between "result := block + temp" and the "end;" of the function) "block" replaces the content of "result" with itself completely, it doesn't add result at the end anymore.
And as you can see I even used a temp variable to try to work around that.. doesnt change anything though.
I am 99.99% certain that your problem is due to a subtle bug in your code. However, your deliberate efforts to hide the relevant code mean that we're really shooting in the dark. You haven't even been clear about where you're seeing the shortened Result: GUI Control/Debugger/Writeln
The irony is that you have all the information at your fingertips to provide a small concise demonstration of your problem - including sample input and expected output.
So without the relevant information, I can only guess; I do think I have a good hunch though.
Try the following code and see if you have a similar experience with S3:
S1 := 'a'#0;
S2 := 'bc';
S3 := S1 + S2;
The reason for my hunch is that #0 is a valid character in a string: but whenever that string needs to be processed as PChar, #0 will be interpreted as a string terminator. This could very well cause the "strange behaviour" you're seeing.
So it's quite probable that you have at least one of the following 2 bugs in your code:
You are always processing 1 too many characters; with the extra character being #0.
When your input string has an odd number of characters: your algorithm (which relies on pairs of characters) adds an extra character with value #0.
Edit
With the additional source code, my hunch is confirmed:
Suppose you have a 5 character string, and key that produces block length 2.
Your inner loop (for posB := 1 to bl do) will read beyond the length of inStr on the last iteration of the outer loop.
So if the next character in memory happens to be #0, you will be doing exactly as described above.
Additional problem. You have the following code:
//calculating block length 2..6
bl := calcBl(keyStr);
Your assumption in the comment is wrong. From the implementation of calcBl, if keyStr is empty, your result will be 0.

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