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

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.

Related

Solving sudoku in Ada

This is mostly solved now thank you
procedure Sudoku is
-- Set Array Types
type arr is array(1..9,1..9) of integer;
type solutions is array(integer range <>)of integer;
-- Declare Variable Types
infp : File_Type;
fileName : string(1..50);
fromFile : string(1..9);
last: natural;
num : arr;
j : integer;
--
--Function to check if board is full
--
function isTrue(board : arr) return boolean is
--Variable Declaration
numCheck : integer;
begin
for x in 1..9 loop
for y in 1..9 loop
if board(x,y) /= 0 then
numCheck := board(x,y);
for k in 1..9 loop
if numCheck = board(x,k) and k /= y then
put_line("Unsolvable Puzzle");
return false;
end if;
if numCheck = board(k,y) and k /= x then
put(x);
put(y);
put_line("Unsolvable Puzzle");
return false;
end if;
end loop;
end if;
end loop;
end loop;
return true;
end isTrue;
A procedure looks like
procedure Proc (params) is
{declarations}
begin
{statements}
end Proc;
which is a pattern you use without problem in the nested procedures; but it applies just the same to the outer sudoku procedures. Note that {declarations} can include nested subprograms.
At line 43 you end the first sudoku, which began at line 5, without any
begin
{statements}
and at line 44 you start a second sudoku, which you end at line 124, again without any
begin
{statements}
So that makes two outer-level procedures in the same file, which is something that GNAT does not support out of the box.
In any case, I think you probably only want one sudoku procedure, so you need to merge the two declarative regions and write a body that calls the nested procedures as appropriate (something I can’t help you with).

My code in Pascal does not want to work correctly. What is the problem in?

I am writing program: sum of real numbers using recursive function.
What is wrong with it? It shows me just last entered numebr.
type
Indexy = 1..100;
TPoleReal = array [Indexy] of Real;
var
j: word;
r, realRes: real;
tpr: TPoleReal;
function SoucetCisel(n: TPoleReal; j: word): real;
begin
if j>0 then begin
SoucetCisel:=SoucetCisel + n[j];
j:=j-1;
end
end;
begin i:=0; j:=0;
while not seekeof do begin
read(r); Inc(j);
tpr[j]:=r;
writeln(j, ' ', tpr[j]);
end;
realRes:= SoucetCisel(tpr, j);
writeln(realRes);
end.
For debugging purposes I suggest you simplify the main part of your code to
begin
i:=0;
j:=0;
tpr[j] := 1;
Inc(j);
tpr[2] := 2;
realRes:= SoucetCisel(tpr, j);
writeln(realRes);
end.
That should make it make it much easier to appreciate what the problem is.
The first problem with your SoucetCisel function is that it isn't actually recursive.
A recursive function is one which calls itself with altered arguments, as in the
archetypical Factorial function
function Factorial(N : Integer)
begin
if N = 1 then
Factorial := 1
else
Factorial := N * Factorial(N - 1);
end;
The recursive call in this is the line
Factorial := Factorial(N - 1);
Your SoucetCisel doesn't do that, it simply adds the initial value of the function result
to the value of n[j], so it is not recursive at all.
The other problem is that, as written, it has no defined return value. In all the
Pascal implementations I've come across, the return value is undefined on entry to the
function and stays undefined until some value is explicitly assigned to it. The
function result is usually some space on the stack which the compiler-generated
code of the function reserves but which initially (on entry to the function) holds some random value, resulting from previous usage of the stack.
So, what the result of your SoucetCisel function is evaluated from is effectively
SoucetCisel := ARandomNumber + n[j]
which of course is just another random number. Obviously, you fix this aspect
of your function by ensuring that an explicit assignment to the function result is made
immediately on entry to the function. As a general rule, all execution paths through a function should lead through a statement which explicitly assigns a
value to the function result.
Then, you need to rewrite the remainder of it so that it actually is recursive
in the way your task requires.
While you're doing those two things, I would suggest that you use a more
helpful parameter name than the anonymous 'n'. 'n' is usually used to refer to an uninteresting integer.
update I'm not sure from your comment whether it was supposed to be serious. In case it was, consider these two functions
function SumOfReals(Reals : TPoleReal; j : word): real;
var
i : Integer;
begin
SumOfReals := 0;
for i := 1 to j do
SumOfReals := SumOfReals + Reals[i];
end;
function SumOfRealsRecursive(Reals : TPoleReal; j : word): real;
var
i : Integer;
begin
SumOfRealsRecursive := Reals[j];
if j > 1 then
SumOfRealsRecursive := SumOfRealsRecursive + SumOfRealsRecursive(Reals, j -1);
end;
These functions both do the same thing, namely evaluate the sum of the contents
of the Reals array up to and including the index j. The first one does so iteratively,
simply traversing the Reals array, which the second does it recursively. However,
it should be obvious that the recursive version is absolutely pointless in this case because
the iterative version does the same thing but far more efficiently, because
it does not involve copying the entire Reals array for each recursive call, which the recursive version does.
As I told you in a comment before. Try this code for your pascal program:
type
Indexy = 1..100;
TPoleReal = array [Indexy] of Real;
var
j: word;
r, realRes: real;
tpr: TPoleReal;
function SoucetCisel(n: TPoleReal; j: word): real;
begin
if j>0 then begin
SoucetCisel:=SoucetCisel(n, j-1) + n[j];
end
end;
begin i:=0; j:=0;
while not seekeof do begin
read(r); Inc(j);
tpr[j]:=r;
writeln(j, ' ', tpr[j]);
end;
realRes:= SoucetCisel(tpr, j);
writeln(realRes);
end.

Interfacing Ada to C - getting Wide Strings from wchar_t *

I'm interfacing to a USB device (on Debian Stretch) using hidraw, and I need to process some information supplied by the USB device in the form of wchar_t* which I need to convert into (Ada) Wide_String. This is giving some trouble and I'm not seeing a clean way forward using the facilities in Interfaces.C and Interfaces.C.Strings.
All files are edited down without destroying their consistency. They will build, but without one of these, they won't actually run.
The problem is that device information like Serial Number and Product Name are presented by the Linux device driver as an access stddef_h.wchar_t from which I want to return a Wide_String or even a normal String) and I'm not seeing any good way to get there.
Interfaces.C.Strings has function Value (Item : in chars_ptr) return String; but no equivalent exists for Wide_String that I can see. So I think I need an equivalent Value function for wide characters.
The approach below uses To_Ada (from Interfaces.C) to return a Wide_String given a wchar_array. It fails, of course, because an access wchar_t is not convertible to a wchar_array.
-- helper function to deal with wchar_t * to wide_string
function Value (P : access stddef_h.wchar_t) return Wide_String is
temp : Wide_String(1 .. 256);
count : natural := 0;
-- ugliness to convert pointer types
type sd_wchar_ptr is access all stddef_h.wchar_t;
type wchar_array_ptr is access wchar_array;
Function To_Wchar_Array_Ptr is new Ada.Unchecked_Conversion(sd_wchar_ptr, wchar_array_ptr);
-- this does NOT create the required wchar_array pointer
WCP : wchar_array_ptr := To_Wchar_Array_Ptr(sd_wchar_ptr(P));
begin
Put_Line("Wide string");
To_Ada(WCP.all, temp, count);
Put_Line("Wide string length " & natural'image(count));
return temp(1..count);
end Value;
and the inevitable result
./test_hid
Wide string
Execution terminated by unhandled exception raised STORAGE_ERROR :
stack overflow or erroneous memory access
A similar character by character approach would be possible ... if (and I can't believe I'm saying this!) you could increment access types...
Feels like there's something missing from Interfaces.C here... what am I missing? any ideas to get round this relatively trivial seeming stumbling block?
EDIT : I'm leaning towards some brazen theft from the Interfaces.C.Strings sources with appropriate changes, but I'd welcome alternative suggestions.
The rest of this below is the full story so far (including all code necessary to reproduce)
Step 1 : generate low level Ada bindings automatically using gcc.
gcc -c -fdump-ada-spec-slim /usr/include/hidapi/hidapi.h
producing the low level binding package hidapi_hidapi_h
pragma Ada_2005;
pragma Style_Checks (Off);
with Interfaces.C; use Interfaces.C;
with Interfaces.C.Strings;
with stddef_h;
with System;
package hidapi_hidapi_h is
-- see source file /usr/include/hidapi/hidapi.h
type hid_device_info is record
path : Interfaces.C.Strings.chars_ptr; -- /usr/include/hidapi/hidapi.h:51
vendor_id : aliased unsigned_short; -- /usr/include/hidapi/hidapi.h:53
product_id : aliased unsigned_short; -- /usr/include/hidapi/hidapi.h:55
serial_number : access stddef_h.wchar_t; -- /usr/include/hidapi/hidapi.h:57
release_number : aliased unsigned_short; -- /usr/include/hidapi/hidapi.h:60
manufacturer_string : access stddef_h.wchar_t; -- /usr/include/hidapi/hidapi.h:62
product_string : access stddef_h.wchar_t; -- /usr/include/hidapi/hidapi.h:64
usage_page : aliased unsigned_short; -- /usr/include/hidapi/hidapi.h:67
usage : aliased unsigned_short; -- /usr/include/hidapi/hidapi.h:70
interface_number : aliased int; -- /usr/include/hidapi/hidapi.h:75
next : access hid_device_info; -- /usr/include/hidapi/hidapi.h:78
end record;
pragma Convention (C_Pass_By_Copy, hid_device_info); -- /usr/include/hidapi/hidapi.h:49
function hid_enumerate (arg1 : unsigned_short; arg2 : unsigned_short) return access hid_device_info; -- /usr/include/hidapi/hidapi.h:132
pragma Import (C, hid_enumerate, "hid_enumerate");
end hidapi_hidapi_h;
This is a low level binding, exposing C types (and the binding generator has decided that the wchar_t in Interfaces.C isn't good enough, it wants one from stddef.h too, so...
pragma Ada_2005;
pragma Style_Checks (Off);
with Interfaces.C; use Interfaces.C;
package stddef_h is
-- unsupported macro: NULL ((void *)0)
subtype size_t is unsigned_long; -- /usr/lib/gcc/x86_64-linux-gnu/6/include/stddef.h:216
subtype wchar_t is int; -- /usr/lib/gcc/x86_64-linux-gnu/6/include/stddef.h:328
end stddef_h;
Because it is a low level binding; we want to hide it (and implement RAII etc) behind a simpler and more usable high level binding, so ... (below)
with Ada.Finalization; use Ada.Finalization;
private with hidapi_hidapi_h;
private with System;
package hidapi is
type id is new natural range 0 .. 2**16 - 1;
type hid_device is new Limited_Controlled with private;
-- find first matching devices by enumeration : the RA part of RAII.
function enumerate (vendor_id, product_id : id) return hid_device;
-- accessors for device characteristics on enumerated device
function Serial_No (D : hid_device) return Wide_String;
function Product_String (D : hid_device) return Wide_String;
private
type hid_device is new Limited_Controlled with record
member : access hidapi_hidapi_h.hid_device_info;
addr : System.Address;
end record;
end hidapi;
and its implementation, containing the problem function value to return a Wide_String.
with hidapi_hidapi_h;
with Interfaces.C; use Interfaces.C;
with Ada.Text_IO; use Ada.Text_IO;
with Ada.Unchecked_Conversion;
with stddef_h;
package body hidapi is
function enumerate (vendor_id, product_id : id) return hid_device is
use hidapi_hidapi_h;
first : access hid_device_info;
begin
first := hid_enumerate(unsigned_short(vendor_id), unsigned_short(product_id));
if first /= null then
return H : hid_device do
H.member := first;
H.addr := System.Null_Address;
end return;
else raise Program_Error;
end if;
end enumerate;
-- helper function to deal with wchar_t * to wide_string
function Value (P : access stddef_h.wchar_t) return Wide_String is
temp : Wide_String(1 .. 256);
count : natural := 0;
type sd_wchar_ptr is access all stddef_h.wchar_t;
type wchar_array_ptr is access wchar_array;
Function To_Wchar_Array_Ptr is new Ada.Unchecked_Conversion(sd_wchar_ptr, wchar_array_ptr);
WCP : wchar_array_ptr := To_Wchar_Array_Ptr(sd_wchar_ptr(P));
begin
Put_Line("Wide string");
To_Ada(WCP.all, temp, count);
Put_Line("Wide string length " & natural'image(count));
return temp(1..count);
end Value;
function Serial_No (D : hid_device) return Wide_String is
use hidapi_hidapi_h;
begin
return Value(D.member.serial_number);
end Serial_No;
function Product_String (D : hid_device) return Wide_String is
use hidapi_hidapi_h;
begin
return Value(D.member.product_string);
end Product_String;
end hidapi;
And of course a test case to exercise it...
with Hidapi;
with Ada.Wide_Text_IO;
procedure Test_Hid is
usbrelay_vendor_id : constant Hidapi.id := 16#16c0#;
usbrelay_product_id : constant Hidapi.id := 16#05df#;
Device : Hidapi.hid_device := Hidapi.Enumerate(usbrelay_vendor_id, usbrelay_product_id);
begin
Ada.Wide_Text_IO.Put_Line("Serial : " & Device.Serial_No);
Ada.Wide_Text_IO.Put_Line("Product : " & Device.Product_String);
end Test_Hid;
One answer, slavishly copying the approach in the package body for Tnterfaces.C.Strings with necessary changes.
The naughty stuff is in functions "+" and Peek which use Unchecked Conversions on pointers,
to permit address arithmetic. Not pointer increment, but pointer+offset. One change is that the offset has to be scaled for 4 byte characters. I haven't set that scaling in a portable manner, but note that "+" will overload for each different return type so that offsets will be scaled appropriately for different named access types.
to allow the stddef_h.wchar_t to be viewed as a Wide_Wide_Character in the absence of any type conversion function. Whether the representation is correct is another matter (here, it is) but this technique could also be used to fake the input type of a suitable conversion function like To_Ada in Interfaces.C.
The remainder is straightforward character by character handling. One other change (so far) is to return Wide_Wide_Character rather than Wide_Character (because as the stddef_h package above reveals, the stored characters are 32 bit, same size as Interfaces.C.int. I'm happy to change my interface, but Wide_String could be easily handled by Ada.Strings packages.
type sd_wchar_ptr is access all stddef_h.wchar_t;
type w_w_char_ptr is access all char32_t;
-- Two Unchecked_Conversions to allow pointer arithmetic
-- And a third to allow the resulting storage to be interpreted as Wide_Wide_Char
function To_Sd_wchar_ptr is new Ada.Unchecked_Conversion (System.Address, sd_wchar_ptr);
function To_Address is new Ada.Unchecked_Conversion (sd_wchar_ptr, System.Address);
function To_Wchar_Ptr is new Ada.Unchecked_Conversion (sd_wchar_ptr, w_w_char_ptr);
-- pointer + offset arithmetic, with offset scaled for size of stddef_h.wchar_t;
-- TODO: attempted better way of computing word size ran into type errors
function "+" (Left : sd_wchar_ptr; Right : size_t) return sd_wchar_ptr is
begin
return To_Sd_wchar_ptr (To_Address (Left) + Storage_Offset (Right) * 4);
end "+";
function Peek (From : sd_wchar_ptr) return char32_t is
begin
return To_Wchar_Ptr(From).all;
end Peek;
function Strlen (Item : sd_wchar_ptr) return size_t is
Item_Index : size_t := 0;
begin
if Item = Null then
raise Program_Error;
end if;
loop
if Peek (Item + Item_Index) = char32_nul then
return Item_Index;
end if;
Item_Index := Item_Index + 1;
end loop;
end Strlen;
function Value (Item : sd_wchar_ptr) return char32_array is
Result : char32_array (0 .. Strlen (Item));
begin
if Item = Null then
raise Program_Error;
end if;
Put_Line("String length " & size_t'image(Strlen(Item)));
-- Note that the following loop will also copy the terminating Nul
for J in Result'Range loop
Result (J) := Peek (Item + J);
end loop;
return Result;
end Value;
-- helper function to deal with wchar_t * to wide_wide_string
function Value (Item : access stddef_h.wchar_t) return Wide_Wide_String is
begin
return To_Ada (Value (sd_wchar_ptr(Item)));
end Value;

Calling "Ada.float_text_IO.get" from a package to read user input from the console

I want to use Ada.float_text_IO.Get , in a packagefile created : (.adb)&(.ads) from a mainfile to read user input from the console using the procedure:
procedure Get(Item : out Num; Width : in Field := 0);
There is no example anywhere . Help :)
Here is a complete working example which includes a package specification (floatget.ads) and body (floatget.adb) and a separate program (mainfile.adb) to call the Read_Floats_From_Console function in the FloatGet package.
The Ada 95 RM section A.10.9 Input-Output for Real Types indicates Ada.Float_Text_IO.Get skips leading whitespace and parses a float literal value of various formats from a file, optionally if you use the form without the File : in File_Type; argument then Get will read from the console.
Contents of floatget.ads package specification:
-- floatget.ads
--
-- FloatGet package specification defining procedure to read floats
-- from console
package FloatGet is
procedure Read_Floats_From_Console;
end FloatGet;
Contents of floatget.adb package body:
-- floatget.adb
--
-- FloatGet package body using Ada.Float_Text_IO.Get to read floats
-- from console
with Ada.Text_IO; use Ada.Text_IO;
with Ada.Float_Text_IO;
package body FloatGet is
procedure Read_Floats_From_Console is
Val : Float;
Sum : Float := 0.0;
N : Integer := 0;
begin
Put_Line("Enter float values, one per line. Press CTRL-D when done.");
Read_Loop:
loop
exit Read_Loop when End_Of_File;
begin
Ada.Float_Text_IO.Get (Val);
exception
when Data_Error =>
Put_Line ("ERROR: Invalid float format");
exit Read_Loop;
end;
Put ("Read value = ");
Ada.Float_Text_IO.Put (Val);
Put (", ");
Ada.Float_Text_IO.Put (Val, Fore => 3, Exp => 0);
Put (", ");
Ada.Float_Text_IO.Put (Val, Aft => 2, Exp => 0);
New_Line;
Sum := Sum + Val;
N := N + 1;
end loop Read_Loop;
Put_Line ("Number of values = " & Integer'Image(N));
Put ("Sum of values = ");
Ada.Float_Text_IO.Put (Sum, Exp => 0);
New_Line;
end Read_Floats_From_Console;
end FloatGet;
Contents of mainfile.adb:
-- mainfile.adb
--
-- Main program calls FloatGet.Read_Floats_From_Console
--
-- Compilation: gnatmake floatget mainfile
--
-- Usage: ./mainfile
--
with FloatGet;
procedure MainFile is
begin
FloatGet.Read_Floats_From_Console;
end MainFile;
How to compile it... (tested with gnat 4.8.4, 4.9.3, 5.2.0 on Ubuntu 14.04)
$ gnatmake floatget mainfile
Sample run illustrating several valid float values...
$ ./mainfile
Enter float values, one per line. Press CTRL-D when done.
1
Read value = 1.00000E+00, 1.00000, 1.00
-1
Read value = -1.00000E+00, -1.00000, -1.00
42
Read value = 4.20000E+01, 42.00000, 42.00
-42
Read value = -4.20000E+01, -42.00000, -42.00
.3
Read value = 3.00000E-01, 0.30000, 0.30
-.3
Read value = -3.00000E-01, -0.30000, -0.30
2.0e3
Read value = 2.00000E+03, 2000.00000, 2000.00
-2.0e3
Read value = -2.00000E+03, -2000.00000, -2000.00
-16#1C.#e-1
Read value = -1.75000E+00, -1.75000, -1.75
2#.11#e6
Read value = 4.80000E+01, 48.00000, 48.00
... Press CTRL-D here ...
Number of values = 10
Sum of values = 46.25000
This example doesn't rely upon anything gnat-specific so should work with any Ada 95 compiler although I have no way to test with other compilers besides gnat presently.
EDIT Simplified to only read from console instead per OP's request.
There are two alternatives: wrap the procedure or rename it.
with Ada.Text_IO;
with Ada.Float_Text_IO;
procedure Main is
package My_Float_Text_IO is
procedure Get_1 (Item : out Float; Width : Ada.Text_IO.Field := 0);
procedure Get_2 (Item : out Float; Width : Ada.Text_IO.Field := 0) renames Ada.Float_Text_IO.Get;
end;
package body My_Float_Text_IO is
procedure Get_1 (Item : out Float; Width : Ada.Text_IO.Field := 0) is
begin
Ada.Float_Text_IO.Get (Item, Width);
end;
end;
F : Float;
begin
My_Float_Text_IO.Get_1 (F);
My_Float_Text_IO.Get_2 (F);
end;
Put specification code into .ads files and body cody (implementation code) into .adb files.
You can also just put one procedure or function in .ads and .adb file without package. You can also have a specification package without body package.

Ada linker not letting me call my procedure

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

Resources