Ada linker not letting me call my procedure - ada

Here is my code:
procedure String_To_Int(str: String) is
str_length : Integer := str'Size / 8;
ASCII_Values_Array: array (Integer range 1 .. str_length) of Integer;
begin
Text_Io.Put_Line(str & " has a length of " & natural'image(str_length));
for x in 1 .. str_length loop
ASCII_Values_Array(x) := Character'Pos(str(x));
Text_Io.Put_Line(natural'image(ASCII_Values_Array(x)));
end loop;
end String_To_Int;
and I am trying to call it with:
String_To_Int(str => "abcdefghijklmnopqrstuvwxyz");
but the compiler is telling me:
Saw '(', expected: , :
And I have no clue what is wrong about how I am calling my procedure. I have looked at many other examples of procedure calls and this looks exactly the same. Any help is appreciated!

There's something you aren't telling us.
./test_sti;
abcdefghijklmnopqrstuvwxyz has a length of 26
97
98
99
...
121
122
Now you don't say what you're actually doing, but here's what I did.
I called String_To_Int from another procedure, in a file test_sti.adb.
with String_To_Int;
procedure test_sti is
begin
String_To_Int(str => "abcdefghijklmnopqrstuvwxyz");
end;
Note that String_To_Int is a separate procedure, in its own file, so the with clause tells the compiler to look for it. I could have declared it locally, i.e. between "is" and "begin" and saved both the files below, but separation is probably better design.
Now anything "with"ed will have both a specification and a body - in this case, specification string_to_int.ads :
procedure String_To_Int(str: String);
and body string_to_int.adb :
with Ada.Text_IO;
use Ada;
procedure String_To_Int(str: String) is
str_length : Integer := str'Size / 8;
ASCII_Values_Array: array (Integer range 1 .. str_length) of Integer;
begin
Text_Io.Put_Line(str & " has a length of " & natural'image(str_length));
for x in 1 .. str_length loop
ASCII_Values_Array(x) := Character'Pos(str(x));
Text_Io.Put_Line(natural'image(ASCII_Values_Array(x)));
end loop;
end String_To_Int;
and to build the lot, simply
gnatmake test_sti.adb
and the compiler works out its own dependencies, no Makefile necessary.
It's actually a bit odd (but perfectly legal) to have a separate "compilation unit" like this for just one procedure. More normally it would either be declared locally, or it would be a part of a package - either a collection of utilities like Ada.Text_IO, or something like a class if you are familiar with Java or C++.
Incidentally, String_To_Int is a very odd procedure : instead of declaring its variables as
str_length : Integer := str'Size / 8;
ASCII_Values_Array: array (Integer range 1 .. str_length) of Integer;
it's cleaner to use the attributes more consistently:
str_length : constant natural := str'Length;
ASCII_Values_Array: array (str'range) of Integer;
and express the loop condition as
for x in str'range loop

Related

SigSegv when passing array element to a recursive function in a loop in Pascal

So in this program we ceate an array Tab1 with Random values in its 10 elements then we get the factorial of each Tab1 element and put it in Tab2, using two methods the iterative and the recursive one. When using the iterative function Tab2 is filled with factorials with no problems but when I use the recursive function the program quits immediately. If you can help me understanding the problem in depth I would be so much appreciated...I read about segmentation fault on Wikipedia which said that it's because the program is trying to get to a memery location that it doesn't have the permission to enter but the problem is when I choose a special element from Tab1 for example Tab1[5] and pass it to factorielleRecursive in Calc2 it works just as fine, any thoughts?
Program recursive;
Type
T = array [1..10] of LongInt;
Var
Tab1, Tab2 : T;
num : integer;
Function FactorielleIterative(N : integer) : integer;
Var
F, i : integer;
Begin
F := 1;
for i:=1 to N Do
F := F*i;
FactorielleIterative := F;
End;
Function FactorielleRecursive(N : LongInt) : LongInt;
Begin
if (N=1) Then
FactorielleRecursive := 1
Else
FactorielleRecursive := N * FactorielleRecursive(N-1);
End;
Procedure Fill(var Tab : T);
Var
i : Integer;
Begin
Randomize;
For i:=1 to 10 Do
Begin
Tab[i] := Random(10);
End;
For i:=1 to 10 Do
Write('[', Tab[i], '] ');
End;
Procedure Calc1(Tab1 : T; var Tab2 : T);
Var
i : integer;
Begin
For i:=1 to 10 Do
Begin
Tab2[i] := FactorielleIterative(Tab1[i]);
End;
For i:=1 to 10 Do
Write('[', Tab2[i], '] ');
End;
Procedure Calc2(Tab : T; var Tab2 : T);
Var
i : integer;
Begin
For i:=1 to 10 Do
Begin
Tab2[i] := FactorielleRecursive(Tab[i]);
End;
For i:=1 to 10 Do
Write('[', Tab2[i], '] ');
End;
Begin
Write('Tab1 : ');
Writeln;
Fill(Tab1);
Writeln;
Writeln;
Write('Tab2 : Iterative method');
Writeln;
Calc1(Tab1, Tab2);
Writeln;
Writeln;
Write('Tab2 : Recursive method');
Writeln;
Calc2(Tab1, Tab2);
Readln;
End.
You are not taking in consideration what happens if the Tab[] array contains a zero value. This situation is possible since you call Random(10) which will return a value in the range 0 .. 9.
In Function FactorielleIterative() an argument of zero is treated as a '1' value (because the for loop is not executed).
In Function FactorielleRecursive() an argument of zero is treated as a '0' value, with the consequence of the recursive call FactorielleRecursive(N-1); leading to range overflow.
The solution is simple so I leave it to you to fix, in order not to spoil your homework.
My thoughts:
I could not reproduce this issue in my setup:
bash$ fpc so.pas && ./so
Free Pascal Compiler version 3.2.0+dfsg-12 [2021/01/25] for x86_64
Copyright (c) 1993-2020 by Florian Klaempfl and others
Target OS: Linux for x86-64
Compiling so.pas
so.pas(6,1) Note: Local variable "num" not used
Linking so
76 lines compiled, 0.1 sec
1 note(s) issued
NOTE: unused variable should be removed.
Can you provide more details about your compiler and its options at compile time and your OS?
Tab1[5] is just a random value: it changes run by run. How did you use it for verification? Do you know what was its current value? I called FactorielleRecursive(10); from the main block and I could not experience any issue with that. Is this issue intermittent in your runtime environment?
Your iterative function get and returns only Integer. I think you should change the return type to LongInt as you did in recursive way. Here a runtime difference between the two version (with some overflow: see the negative value(s)):
Tab2 : Iterative method
[5040]
[720]
[2]
[1]
[720]
[5040]
[24]
[1]
[-25216] <<<<<<<<<<<<<<<<< Integer overflow
[24]
Tab2 : Recursive method
[5040]
[720]
[2]
[1]
[720]
[5040]
[24]
[1]
[40320]
[24]
Minor notes:
You can extract repetitive code parts into a procedure/function. In this case the for loops repeated 3 times to write out contents of Tab array.
You can merge Write and WriteLn into a single statement. For example WriteLn('Tab2 : Iterative method'); - it is a more compact form.

How do I enforce that a type hold only be a fixed set of non-contiguous values?

I was expecting this program to raise an error when I feed it 3 as a valid Scale value, but no such luck:
with Ada.Text_IO; use Ada.Text_IO;
procedure predicate is
type Scale is new Integer
with Dynamic_Predicate => Scale in 1 | 2 | 4 | 8;
GivesWarning : Scale := 3; -- gives warning
begin
Put_Line ("Hello World");
loop
Put_Line ("Gimme a value");
declare
AnyValue : Integer := Integer'Value (Get_Line);
S : Scale := Scale (AnyValue); -- no check done!
begin
Put_Line ("okay, that works" & S'Image);
end;
end loop;
end predicate;
I found this related question, but there the requirement is to use an enum., and the solution is to define an array from enum -> value.
I want something that gives me at least a warning at compile time, and allows me to check at runtime as well, and that raises an error if I try to put an invalid value in. Then, if I can use SPARK to prove that no invalid values can occur, I could turn off said checks. I was under the impression that this was how Static_ / Dynamic_ predicates work, so the example above took me by surprise.
You need to enable assertions. Either compile with -gnata or set an appropriate Assertion_Policy
pragma Assertion_Policy(Dynamic_Predicate => Check);

Using multidimensional array in Ada

In this code, I need help writing a multidimensional array with a range between 2020-01-01 to 2119-12-31.
My code works but as you see there are no arrays in it. How can I write this code with only arrays?
with Ada.Text_IO; use Ada.Text_IO;
with Ada.Integer_Text_IO; use Ada.Integer_Text_IO;
Procedure Date is
type date_type is record
Str : string (1..8);
Length : Natural := 0; end record;
A: date_type;
begin
loop
Put ("Enter a date between 2020-01-01 to 2119-12-31 : ");
Get_Line (A.Str, A.Length);
exit when A.Length = 8;
Put_Line ("Wrong input. Try again.");
end loop;
Put_Line (A.Str (1 .. 4) & "-" & A.Str (5 .. 6) & "-" & A.Str (7 .. 8));
end Date;
Perhaps, rather than a multi-dimensional array you should consider using a record such as
type Year_Number is range 1900..3000;
type Month_Number is range 1..12;
type Day_Number is range 1..31;
type Date_Rec is record
Year : Year_Number;
Month : Month_Number;
Day : Day_Number;
end record;
subtype Year_String is string (1..4);
subtype Month_String is string (1..2);
subtype Day_String is string (1..2);
function To_Date (Yr : Year_String; Mnth : Month_String; Dy : Day_String)
return Date_Rec is
Result : Date_Rec;
begin
Result.Year := Year_Number'Value (Yr);
Result.Month := Month_Number'Value (Mnth);
Result.Day := Day_Number'Value (Dy);
return Result;
end To_Date;
You can now pass around instances of Date_
Rec doing whatever you want with the date.
If you go this far then you might want to consider using the Time type described in Ada Language Reference Manual sections 9.6 and 9.6.1.
You haven't asked a reasonable question here because "I want to use arrays" is not a good reason to use an array.
"This problem can be best solved with an array ... but how do I deal with ... ?" would be a reasonable question, but you haven't stated a problem,let alone one that needs an array.
This is important because "using an array" is thinking in the solution domain, like "using a chisel". It's not the way to think about programming in Ada, (or IMO in any language).
Try thinking in the problem domain first : instead of "I want to use a chisel", I think "I want to recess this hinge so the door fits precisely" and a chisel is the neatest way of doing the job.
Then "How can I best validate a date?" would be one reasonable question, or "how can I store events that happen on each day for 100 years?"
The answer to the first question is probably in the Ada.Calendar package. Possibly the Value function in Ada.Calendar.Formatting, with an exception handler to catch incomprehensible strings and make the user try again.
with Ada.Text_IO; use Ada.Text_IO;
with Ada.Calendar;
with Ada.Calendar.Formatting;
procedure date is
Date : Ada.Calendar.Time;
Done : Boolean;
begin
loop
Put ("Enter a date between 2020-01-01 to 2119-12-31 : ");
Done := TRUE;
declare
A : String := Get_Line & " 12:00:00";
begin
Date := Ada.Calendar.Formatting.Value(A); -- validate it's a date
Done := Ada.Calendar.Year(Date) >= 2020
and Ada.Calendar.Year(Date) < 2120; -- validate correct range
exception
when Constraint_Error => Done := False; -- Formatting.Value failed
end;
exit when Done;
Put("Try Again : ");
end loop;
end date;
The answer to the second is probably a 1-dimensional array indexed by Day_Count from Ada.Calendar.Arithmetic but let's use the wrong tool : a 3D array indexed by your range of years, Month_Number and Day_Number from the Ada.Calendar base package.
with Ada.Text_IO; use Ada.Text_IO;
with Ada.Calendar; use Ada.Calendar;
with Ada.Calendar.Formatting;
with Ada.Strings.Unbounded; use Ada.Strings.Unbounded;
procedure date is
Date : Ada.Calendar.Time;
Done : Boolean := TRUE;
Event_Array : array(2020 .. 2119,
Ada.Calendar.Month_Number,
Ada.Calendar.Day_Number) of Unbounded_String;
begin
Event_Array := (others => (others => (others => Null_Unbounded_String)));
Event_Array(2020,11,3) := To_Unbounded_String("nothing much");
loop
Put ("Enter a date between 2020-01-01 to 2119-12-31 : ");
Done := TRUE;
declare
A : String := Get_Line & " 12:00:00";
begin
Date := Ada.Calendar.Formatting.Value(A);
Done := Ada.Calendar.Year(Date) >= 2020
and Ada.Calendar.Year(Date) < 2120;
exception
when Constraint_Error => Done := False;
end;
exit when Done;
Put("Try Again : ");
end loop;
Put_Line("Today " & Ada.Calendar.Formatting.Image(Date) & " : " &
To_String(Event_Array(Year(Date), Month(Date), Day(Date))) & " happened");
end date;
Test with the string 2020-11-03

How do I convert an unbounded string to an integer?

I am learning Ada (by trying https://adventofcode.com/2018/ problems). To start with, I am trying to develop a number of "utility" packages that will help with text processing etc.
I have successfully written a function that will read from stdin and return an array of Unbounded_Strings for each input line.
I am trying to modify that function to do the same, but instead convert each Unbounded_String to an Integer before insertion into the array.
Here is my package:
get_stdin.ads:
with Ada.Strings.Unbounded;
package get_stdin is
type IntegerArray is array (Natural range <>) of Integer;
function get_ints return IntegerArray;
end get_stdin;
get_stdin.adb:
with Ada.Text_IO;
with Ada.Text_IO.Unbounded_IO;
with Ada.Strings.Unbounded;
with Ada.Strings;
package body get_stdin is
function get_ints return IntegerArray is
Counter : Natural := 0;
Str : Ada.Strings.Unbounded.Unbounded_String;
Arr : IntegerArray(0..10000);
begin
while not Ada.Text_IO.End_Of_File loop
Str := Ada.Text_IO.Unbounded_IO.Get_Line;
Arr(Counter) := Integer'Value(Ada.Strings.Unbounded.To_String(Str));
Counter := Counter + 1;
end loop;
return Arr(0..Counter-1);
end get_ints;
end get_stdin;
I am calling using this package inside this procedure:
procedure d1 is
StdinArr : get_stdin.IntegerArray := get_stdin.get_ints;
begin
null; -- Array processing to follow
end;
This successfully compiles, and I then pipe in my input text file:
me#mypc /cygdrive/c/Users/me/aoc2018
$ cat d1.txt
-6
-1
-18
-10
...etc
me#mypc /cygdrive/c/Users/me/aoc2018
$ cat d1.txt | ./d1.exe
raised CONSTRAINT_ERROR : bad input for 'Value: "-6"
"-6" is the first value in the text file. My string-to-integer conversion code was essentially copied from this question.
I am not sure why a bad input error is raised.
It raises the same error if I replace -6 with a positive integer in the file
This is running under Cygwin on Windows 10.
Compiled/linked with gnatmake version 7.3.0
Note: I'm just getting started with Ada so there's probably lots of issues with my code in general.
What am I doing wrong and how can I fix this function/package to return my IntegerArray type correctly filled with Integers?
This was a line endings issue. I was running under cygwin on Windows 10. My text file has Windows-style line endings.
Using dos2unix:
$ cat d1.txt | dos2unix.exe | ./d1.exe
was sufficient to make it work correctly.
If anyone can explain precisely why, that would be interesting. I'm guessing that Get_Line only strips off the \n character, not the \r.

Ada, raised CONSTRAINT_ERROR : bad input for 'Value: "well."

I am unable to get the following script to return my input value; I've looked up ARM as well as John Barnes book but to no avail. In theory it should work.
Anyone know why? I'm a newby so the Barnes book and the ARM are probably too advanced for me.
with Ada.Text_IO;
use Ada.Text_IO;
procedure ron is
A : Character;
begin
Put_Line ("Hi Ron, how are you?");
A := Character'Value (Get_Line);
Put_Line ("So you feel" &
Character'Image (A));
end ron;
--TERMINAL OUTPUT
--ronhans#amante ~/Desktop $ gnatmake -gnat2012 ron.adb
--gcc-4.8 -c -gnat2012 ron.adb
--gnatbind -x ron.ali
--gnatlink ron.ali
--ronhans#amante ~/Desktop $ ./ron
--Hi Ron, how are you?
--well.
--raised CONSTRAINT_ERROR : bad input for 'Value: "well."
If you look in the LRM, you will see that Ada.Text_IO.Get_Line returns a String:
with Ada.Text_IO;
procedure Ron is
begin
Ada.Text_IO.Put_Line ("Hi Ron, how are you?");
declare
Reply : constant String := Ada.Text_IO.Get_Line;
begin
Ada.Text_IO.Put_Line ("So you feel " & Reply & "?");
end;
end Ron;
The problem with you program is that you try to put an array of characters into a single character. Instead of of using A : Character, try to define an array type something like
type Character_Array_T (1 .. 10) of Character;
...
A : Character_Array_T;
or use
with Ada.Strings.Unbounded;
...
A : Ada.Strings.Unbounded.Unbounded_String;
I suggest the use of an unbounded string, so that the input is not bounded to some specific string length, if your intention is to read out an input several times. Ada type string requires you to specify the string length and this length is exactly the number of characters this string should contain.
See Wiki, unbounded strings and Unbounded string handling for the reference.

Resources