Letter conversion program for Ada - ada

I made a letter conversion program in Ada that will take any lowercase letter and change it to uppercase and vice versa. The entire thing works until you get to my argument_count = 3 portion. Its supposed to output text to another file, and its just not working at all. My exception error when others=> error keeps popping up.
It's supposed to go something like this when you input it into terminal or command-line:
./case_converter u plain.text output
With Ada.Text_IO, Ada.Command_Line, Ada.Characters.Handling;
Use Ada.Text_IO, Ada.Command_Line, Ada.Characters.Handling;
Procedure case_conversion is
Argument_Error : Exception; --the excpetion raise for bad command-line arguments.
Input, Output : File_Type;-- input and output files, if specified.
File : File_Type;
Eol : Boolean;
Char : Character;
Function To_Upper (upper : Character) Return Character is
Begin
Case upper is
When 'a' =>
Return 'A';
When 'b' =>
Return 'B';
When 'c' =>
Return 'C';
When 'd' =>
Return 'D';
When 'e' =>
Return 'E';
When 'f' =>
Return 'F';
When 'g' =>
Return 'G';
When 'h' =>
Return 'H';
When 'i' =>
Return 'I';
When 'j' =>
Return 'J';
When 'k' =>
Return 'K';
When 'l' =>
Return 'l';
When 'm' =>
Return 'M';
When 'n' =>
Return 'N';
When 'o' =>
Return 'O';
When 'p' =>
Return 'P';
When 'q' =>
Return 'Q';
When 'r' =>
Return 'R';
When 's' =>
Return 'S';
When 't' =>
Return 'T';
When 'u' =>
Return 'U';
When 'v' =>
Return 'V';
When 'w' =>
Return 'W';
When 'x' =>
Return 'X';
When 'y' =>
Return 'Y';
When 'z' =>
Return 'Z';
When Others =>
Return char; -- This will let me know if there is a bug in the code, and to just return char.
end case;
end To_Upper;
Function To_Lower (lower : Character) Return character is
Begin
Case lower is
When 'A' =>
Return 'a';
When 'B' =>
Return 'b';
When 'C' =>
Return 'c';
When 'D' =>
Return 'd';
When 'E' =>
Return 'e';
When 'F' =>
Return 'f';
When 'G' =>
Return 'g';
When 'H' =>
Return 'h';
When 'I' =>
Return 'i';
When 'J' =>
Return 'j';
When 'K' =>
Return 'k';
When 'L' =>
Return 'l';
When 'M' =>
Return 'm';
When 'N' =>
Return 'n';
When 'O' =>
Return 'o';
When 'P' =>
Return 'p';
When 'Q' =>
Return 'q';
When 'R' =>
Return 'r';
When 'S' =>
Return 's';
When 'T' =>
Return 't';
When 'U' =>
Return 'u';
When 'V' =>
Return 'v';
When 'W' =>
Return 'w';
When 'X' =>
Return 'x';
When 'Y' =>
Return 'y';
When 'Z' =>
Return 'z';
When Others =>
Return char;
end case;
End To_Lower;
begin
If argument_count < 1 then
Raise Argument_Error;
end if;
IF Argument_Count > 1 THEN
null;
END IF;
IF Argument_Count > 2 THEN
null;
END IF;
If argument_Count = 1 then
loop
look_Ahead (Char, Eol);
if Argument (1) = "U" or Argument (1) = "u" then
Get (char);
Char := (To_Upper(char));
Put (char);
elsif
Argument (1) = "L" or Argument (1) = "l" then
Get (char);
Char := (To_Lower(char));
Put (char);
end if;
end loop;
end if;
-----------------------------------------------------------
If Argument_Count = 2 then
Open (file, In_File, Argument (2));
end if;
While Not End_of_File (file) loop
Look_Ahead (File, Char, EoL);
If Argument (1) = "U" or Argument (1) = "u" then
Get (file, char);
Char := (To_Upper(char));
Put (char);
elsif
Argument (1) = "L" or Argument (1) = "l" then
Get (file, char);
Char := (To_Lower(char));
Put (char);
end if;
If EoL then
New_Line;
end if;
end loop;
------------------------------------------------------------
If Argument_Count = 3 then
Open (file, In_File, Argument (2));
Create (Output, Out_file, Argument(3));
While Not End_of_File (file) loop
-- Look_Ahead (file, Char, EoL);
If EoL then
New_Line(output);
end if;
If Argument (1) = "U" or Argument (1) = "u" then
Get (file, char);
Char := (To_Upper(char));
Put (output, char);
elsif Argument (1) = "L" or Argument (1) = "l" then
---------
If Eol then
new_line (output);
end if;
Get (file, char);
Char := (To_Lower(char));
Put (output, char);
end if;
end loop;
end if;
IF Is_Open(input) THEN
Close(Input);
END IF;
IF Is_Open(Output) THEN
Close(Output);
END IF;
EXCEPTION
WHEN Name_Error =>
Put_Line("The file " & Argument(2) & " cannot be used for input");
WHEN OTHERS =>
Put_Line("Usage: case_converter U|L [infile [outfile]]");
end case_conversion;

Why not the Ada.Characters.Handling.To_Upper (and To_Lower) Function?
If you really need to check that the character is alphabetic use Is_Letter.

It's often a bad idea to catch exceptions with a when others, unless you're sure what exceptions might be raised (and even very experienced developers can get this wrong).
In your case, you should comment out the 'when others' lines; when you run your program and the exception occurs, it will be reported (usually with the line number where it happened, though not - on my machine - in this case). It helps to get a stack trace: this is harder on Mac OS X than on other machines, I wrote about it here.
I ran
$ ./case_conversion u case_conversion.adb t
Execution terminated by unhandled exception
Exception name: ADA.IO_EXCEPTIONS.STATUS_ERROR
Message: file not open
Call stack traceback locations:
0x1000103bc 0x10000c9eb 0x100001e9a 0x100001a19
and decoded the stack trace with
$ atos -o case_conversion 0x1000103bc 0x10000c9eb 0x100001e9a 0x100001a19
system__file_io__check_read_status (in case_conversion) (s-fileio.adb:207)
ada__text_io__end_of_file (in case_conversion) (a-textio.adb:304)
_ada_case_conversion (in case_conversion) (case_conversion.adb:177)
main (in case_conversion) (b~case_conversion.adb:265)
and looked at case_conversion.adb:177:
173 if Argument_Count = 2 then
174 Open (File, In_File, Argument (2));
175 end if;
176
177 while not End_Of_File (File) loop
So, what happens if Argument_Count is 3?
If you're not running on Mac OS X, instead of atos try addr2line.

Shark has it right BTW. Its silly not to use the built-in routine for this.
However, if you were to want to write it yourself for some weird reason, the fact that the letters are contiguous and in the same sequence for upper and lower makes the way you are going about it even sillier. Notice that every entry in those big honking case statements of yours are the same distrance from each other. To make each case entry, you could just copy the previous one and increase the value of both sides by one. This is the exact kind of repeated mechanical activity we invented computers to help us with, isn't it? :-)
The traditional way to covert between the two is to just add the difference between the two sets of characters in the collating sequence (most folks pick the letter 'A'). For example:
Upper_To_Lower_Difference : constant := character'pos('a') - character'pos('A');
function To_Upper (Lower : character) return character is
begin
if Lower in 'a'..'z' then
return character'val(character'pos(Lower) + Upper_To_Lower_Difference);
end if;
return Lower;
end To_Upper;

Related

Ada: How to get Access to Vector element?

I have a collection of things, which I deliberately want to allocate on the heap and access them 'by reference':
with Ada.Text_IO; use Ada.Text_IO;
with Ada.Containers.Indefinite_Hashed_Maps;
with Ada.Containers; use Ada.Containers;
procedure Main is
type Thing_Key is new Integer;
type Thing is record
Key : Thing_Key;
Data : Integer;
end record;
type Thing_Access is access all Thing;
function Image (T : Thing) return String is
(T.Key'Image & '(' & T.Data'Image & ')');
function "=" (A, B : Thing) return Boolean is
(A.Key = B.Key);
function Thing_Hash (K : Thing_Key) return Hash_Type is
(Hash_Type (K));
package Thing_Map is new
Ada.Containers.Indefinite_Hashed_Maps
(Key_Type => Thing_Key,
Element_Type => Thing,
Hash => Thing_Hash,
Equivalent_Keys => "=");
use Thing_Map;
Map : Thing_Map.Map;
C : Cursor;
P : Thing_Access;
begin
P := new Thing '(Key => 1, Data => 2); -- on the heap
Map.Insert (P.Key, P.all);
Put_Line (Image (P.all)); -- '1( 2)', as expected
P.Data := 99;
Put_Line (Image (P.all)); -- '1( 99)', as expected
C := Map.Find (1); -- Get cursor to thing
-- Set P to point at the thing at the cursor?
-- Following lines don't compile
P := Map (C)'Access; -- access-to-variable designates constant
P := Map (C).Reference; -- undefined selector "Reference" for overloaded prefix
P := Map (C).Get_Element_Access; -- undefined selector "Get_Element_Access" for overloaded prefix
P := Map.Reference (C); -- no visible interpretation of "Reference" matches expected type "Thing_Access"
end Main;
What is the syntax to get a pointer from a cursor?
I assume that you only want to store elements on the heap in order to access them by reference for manipulation. However, you don't need to do that when using Ada containers. All containers have some way of accessing the elements by reference readily available (via some Constant_Reference or Reference function that can typically be omitted because of the Variable_Indexing aspect defined on the container type; see, for example, section 6.3 in the Ada 2012 rationale, and/or the answer of #Timur Samkharadze).
If you want to store the key as part of the element, then I think it might be more appropriate to use a hashed set (see RM A.18.7, RM A.18.8 and on learn.adacore.com). An element in a hashed set can be accessed by reference via the function Reference_Preserving_Key (see also RM 96.10 (3)).
Below are two examples: the first example shows how to update an element in a Hashed_Map and the second example shows how to update an element in a Hashed_Set, both using a key:
main.adb (Hashed_Map)
with Ada.Text_IO; use Ada.Text_IO;
with Ada.Containers; use Ada.Containers;
with Ada.Containers.Hashed_Maps;
procedure Main is
type Thing_Key is new Integer;
type Thing is record
Key : Thing_Key;
Data : Integer;
end record;
function Image (T : Thing) return String is
("Key = " & T.Key'Image & ", Value = " & T.Data'Image);
function Hash (K : Thing_Key) return Hash_Type is (Hash_Type (K));
package Things is new Ada.Containers.Hashed_Maps
(Key_Type => Thing_Key,
Element_Type => Thing,
Hash => Hash,
Equivalent_Keys => "=");
Map : Things.Map;
begin
-- Inserting 4 elements. Note that the key is now stored twice: once in
-- the map's key index (its hash, to be more precise), and once in the item
-- itself (unhashed). You must now ensure that the key value in the
-- element does not accidentally get out-of-sync with the hashed key in the
-- map's key index (e.g. when you update the stored element). Of course,
-- you could also you just omit the key in the element itself if possible
-- given your use-case.
Map.Insert (Key => 1, New_Item => (Key => 1, Data => 10));
Map.Insert (Key => 2, New_Item => (Key => 2, Data => 20));
Map.Insert (Key => 3, New_Item => (Key => 3, Data => 30));
Map.Insert (Key => 4, New_Item => (Key => 4, Data => 40));
for T of Map loop
Put_Line (Image (T));
end loop;
New_Line;
-- Update element with key 3.
--
-- Note that the following expressions are all equivalent:
--
-- Map.Reference (3).Element.all.Data := 300; -- Original expression
-- Map.Reference (3).Element.Data := 300; -- Omit "all" due to implicit dereferencing of access types in Ada.
-- Map.Reference (3).Data := 300; -- Omit "Element" due to the "Implicit_Dereferencing" aspect on the "Hashed_Maps.Reference_Type".
-- Map (3).Data := 300; -- Omit "Reference" due to the "Variable_Indexing" aspect on the "Hashed_Maps.Map" type.
--
Map (3).Data := 300;
-- Example if you really need a pointer to element with key 3.
declare
type Thing_Access is not null access all Thing;
type Thing_Constant_Access is not null access constant Thing;
-- Element is mutable via P , i.e. P.Data := 301 (OK)
-- Element is not mutable via CP, i.e. CP.Data := 302 (Error)
P : Thing_Access := Map.Reference (3).Element;
CP : Thing_Constant_Access := Map.Constant_Reference (3).Element;
begin
null;
end;
for T of Map loop
Put_Line (Image (T));
end loop;
New_Line;
end Main;
main.adb (Hashed_Set)
with Ada.Text_IO; use Ada.Text_IO;
with Ada.Containers; use Ada.Containers;
with Ada.Containers.Hashed_Sets;
procedure Main is
type Thing_Key is new Integer;
type Thing is record
Key : Thing_Key;
Data : Integer;
end record;
function Image (T : Thing) return String is
("Key = " & T.Key'Image & ", Value = " & T.Data'Image);
function Key (T : Thing) return Thing_Key is (T.Key);
function Hash (T : Thing) return Hash_Type is (Hash_Type (T.Key));
function Hash (K : Thing_Key) return Hash_Type is (Hash_Type (K));
package Things is new Ada.Containers.Hashed_Sets
(Element_Type => Thing,
Hash => Hash,
Equivalent_Elements => "=");
package Things_Keys is new Things.Generic_Keys
(Key_Type => Thing_Key,
Key => Key,
Hash => Hash,
Equivalent_Keys => "=");
Set : Things.Set;
begin
-- Inserting 4 elements. Note that the key is stored only in the element.
Set.Insert ((Key => 1, Data => 10));
Set.Insert ((Key => 2, Data => 20));
Set.Insert ((Key => 3, Data => 30));
Set.Insert ((Key => 4, Data => 40));
for T of Set loop
Put_Line (Image (T));
end loop;
New_Line;
-- Update the element. See also RM 96.10 (3). Opposed to most other
-- containers, you cannot omit "Reference_Preserving_Key" as the "Set" type
-- does not have a "Variable_Indexing" aspect specifying "Reference_Preserving_Key".
-- Hence, you need write it out explicitly.
Things_Keys.Reference_Preserving_Key (Set, 3).Data := 300;
-- Example if you really need a pointer to element with key 3.
declare
type Thing_Access is not null access all Thing;
type Thing_Constant_Access is not null access constant Thing;
-- Element is mutable via P , i.e. P.Data := 301 (OK)
-- Element is not mutable via CP, i.e. CP.Data := 302 (Error)
P : Thing_Access := Things_Keys.Reference_Preserving_Key (Set, 3).Element;
CP : Thing_Constant_Access := Things_Keys.Constant_Reference (Set, 3).Element;
begin
null;
end;
for T of Set loop
Put_Line (Image (T));
end loop;
New_Line;
end Main;
output (same for both)
Key = 1, Value = 10
Key = 2, Value = 20
Key = 3, Value = 30
Key = 4, Value = 40
Key = 1, Value = 10
Key = 2, Value = 20
Key = 3, Value = 300
Key = 4, Value = 40
You might want to use P := Map.Reference(C).Element;
Function Reference returns a value of Reference_Type that has aspect Implicit_Dereference whose value is Element and whose type is not null access Element_Type.

CONSTRAINT_ERROR with "index check failed"

I'm making an arithmetical expression interpreter in Ada.
Example input: "ADD a b;ADD b c;PRN c;SUB c a;PRN c;"
I have a long code, so I dont want to copy all of that, instead I try to explain the short piece of that, where I have got the error.
's' is a State, represented by a record, with the field 'Size' and an array, called Expressions. An expression is represented by a record, with the fields: Op (enum type), LHS and RHS(Characters).
The function notSpaceLinSearch finds the index of the first element in the input string that is not a space.
So my question is, why the error could be raised, and why just in the 5th time of index referring?
Thanks for your answers in advance.
while loopIndex <= numOfExpressions loop
s.Size := s.Size + 1;
notSpaceLinSearch(charArray, ' ', contains, notSpaceIndex);
foundChar := charArray(notSpaceIndex);
case foundChar is
when 'A' => s.Expressions(s.Size).Op := ADD;
when 'S' => s.Expressions(s.Size).Op := SUB;
when 'M' => s.Expressions(s.Size).Op := MUL;
when 'P' => s.Expressions(s.Size).Op := PRN;
when 'I' => s.Expressions(s.Size).Op := INI; -- raised CONSTRAINT_ERROR .... index check failed
when others => null;
end case;
....
....
...
end loop;
Thanks for the answers, the problem was the wrong inicialization of the Expressions array. (N-1 instead of N).

How can I find the `'First` and `'Last` expressions for a string field in a record using ASIS

I'm using ASIS to analyse a big Ada project. One of the things I need to do is to find the 'First and 'Last expressions for a string field in a record variable.
My problem occurs when I have a Discrete_Range, which is not A_Discrete_Simple_Expression_Range (for which one can use the functions Lower_Bound and Upper_Bound directly), but instead A_Discrete_Range_Attribute_Reference.
The source example which I'm analysing basically looks like this:
with Ada.Text_IO;
procedure Minimal_Example is
type R is
record
F : String (1 .. 5);
end record;
V : R;
subtype S is String (V.F'Range); -- It would have been nice if they didn't do like this.
function F return S is ("12345");
begin
Ada.Text_IO.Put_Line (F);
end Minimal_Example;
Here is a minimised version of the program I use to perform the analysis:
-- Standard library packages:
with Ada.Wide_Text_IO;
-- ASIS packages:
with Asis;
with Asis.Ada_Environments;
with Asis.Compilation_Units;
with Asis.Declarations;
with Asis.Definitions;
with Asis.Elements;
with Asis.Expressions;
with Asis.Implementation;
with Asis.Iterator;
with Asis.Statements;
with Asis.Text;
procedure Minimal_Analyzer is
procedure Pre_Operation (Element : in Asis.Element;
Control : in out Asis.Traverse_Control;
State : in out Boolean) is
pragma Unreferenced (Control, State);
use all type Asis.Element_Kinds;
use all type Asis.Statement_Kinds;
begin
if Asis.Elements.Element_Kind (Element) = A_Statement and then
Asis.Elements.Statement_Kind (Element) = A_Procedure_Call_Statement
then
for Parameter_Association of Asis.Statements.Call_Statement_Parameters (Statement => Element,
Normalized => True) loop
declare
Actual_Parameter : Asis.Element;
Type_Of_Expression : Asis.Element;
Type_Definition : Asis.Definition;
Constraint : Asis.Constraint;
begin
Actual_Parameter := Asis.Expressions.Actual_Parameter (Parameter_Association);
Type_Of_Expression := Asis.Expressions.Corresponding_Expression_Type (Actual_Parameter);
Type_Definition := Asis.Declarations.Type_Declaration_View (Declaration => Type_Of_Expression);
Constraint := Asis.Definitions.Subtype_Constraint (Type_Definition);
for Index_Range of Asis.Definitions.Discrete_Ranges (Constraint) loop
declare
Range_Attribute : Asis.Definition;
Range_Prefix : Asis.Element;
begin
Range_Attribute := Asis.Definitions.Range_Attribute (Index_Range);
Range_Prefix := Asis.Expressions.Prefix (Range_Attribute);
Ada.Wide_Text_IO.Put_Line (Asis.Elements.Debug_Image (Range_Prefix));
Ada.Wide_Text_IO.Put_Line (Asis.Text.Element_Image (Range_Prefix));
end;
end loop;
end;
end loop;
end if;
end Pre_Operation;
procedure Post_Operation (Element : in Asis.Element;
Control : in out Asis.Traverse_Control;
State : in out Boolean) is null;
procedure Traverse_Declaration is
new Asis.Iterator.Traverse_Element (State_Information => Boolean,
Pre_Operation => Pre_Operation,
Post_Operation => Post_Operation);
Context : Asis.Context;
begin
Asis.Implementation.Initialize ("");
Asis.Ada_Environments.Associate (The_Context => Context,
Name => "CLPG",
Parameters => "-CA -FM");
Asis.Ada_Environments.Open (The_Context => Context);
Analyze :
declare
Complation_Unit_Body : Asis.Compilation_Unit;
Complation_Unit_Body_Declaration : Asis.Declaration;
Process_Control : Asis.Traverse_Control := Asis.Continue;
State : Boolean := False;
begin
Complation_Unit_Body := Asis.Compilation_Units.Compilation_Unit_Body (Name => "Minimal_Example",
The_Context => Context);
Complation_Unit_Body_Declaration := Asis.Elements.Unit_Declaration (Compilation_Unit => Complation_Unit_Body);
Traverse_Declaration (Element => Complation_Unit_Body_Declaration,
Control => Process_Control,
State => State);
end Analyze;
Asis.Ada_Environments.Close (The_Context => Context);
Asis.Ada_Environments.Dissociate (The_Context => Context);
Asis.Implementation.Finalize (Parameters => "");
end Minimal_Analyzer;
Project file:
with "asis";
project Build is
for Main use ("minimal_analyzer.adb",
"minimal_example.adb");
for Source_Dirs use (".");
for Object_Dir use "obj";
for Exec_Dir use "bin";
package Builder is
for Default_Switches ("Ada") use ("-m", -- Do not recompile if only comments have changed
"-s", -- Recompile if switches change
"-j0"); -- Build concurrently
end Builder;
package Compiler is
for Default_Switches ("Ada") use ("-gnatoU",
"-gnat2012",
"-funwind-tables",
"-fstack-check",
"-gnata");
end Compiler;
end Build;
Build command:
gprbuild -j0 -p -P build.gpr
You need to have ASIS installed to build the tool. If you run minimal_analyzer from the directory where minimal_example.adb is located, you get the output:
Element Debug_Image:
A_SELECTED_COMPONENT
located in Minimal_Example (body, Unit_Id = 2, Context_Id = 1)
text position : minimal_example.adb:8:40
Nodes:
Node : 2332 - N_SELECTED_COMPONENT
R_Node : 2332 - N_SELECTED_COMPONENT
Node_Field_1 : 0 - N_EMPTY
Node_Field_2 : 0 - N_EMPTY
Rel_Sloc : 157
obtained from the tree /tmp/minimal_example.adt (Tree_Id = 1)
V.F
... but how can I get to the definition of V.F, so I can extract the Discrete_Simple_Expression_Range 1 .. 5?
I found a solution:
The trick is to know when to use ASIS.Expressions.Corresponding_Name_Declaration...
-- Standard library packages:
with Ada.Wide_Text_IO;
-- ASIS packages:
with ASIS;
with ASIS.Ada_Environments;
with ASIS.Compilation_Units;
with ASIS.Declarations;
with ASIS.Definitions;
with ASIS.Elements;
with ASIS.Expressions;
with ASIS.Implementation;
with ASIS.Iterator;
with ASIS.Statements;
with ASIS.Text;
procedure Minimal_Analyzer is
procedure Pre_Operation (Element : in ASIS.Element;
Control : in out ASIS.Traverse_Control;
State : in out Boolean) is
pragma Unreferenced (Control, State);
use all type ASIS.Element_Kinds;
use all type ASIS.Statement_Kinds;
begin
if ASIS.Elements.Element_Kind (Element) = A_Statement and then
ASIS.Elements.Statement_Kind (Element) = A_Procedure_Call_Statement
then
for Parameter_Association of ASIS.Statements.Call_Statement_Parameters (Statement => Element,
Normalized => True) loop
declare
Actual_Parameter : ASIS.Element;
Type_Of_Expression : ASIS.Element;
Type_Definition : ASIS.Definition;
Constraint : ASIS.Constraint;
begin
Actual_Parameter := ASIS.Expressions.Actual_Parameter (Parameter_Association);
Type_Of_Expression := ASIS.Expressions.Corresponding_Expression_Type (Actual_Parameter);
Type_Definition := ASIS.Declarations.Type_Declaration_View (Declaration => Type_Of_Expression);
Constraint := ASIS.Definitions.Subtype_Constraint (Type_Definition);
for Index_Range of ASIS.Definitions.Discrete_Ranges (Constraint) loop
declare
Range_Attribute : ASIS.Definition;
Range_Prefix : ASIS.Element;
Field_Name : ASIS.Defining_Name;
Field_Declaration : ASIS.Element;
Field_Definition : ASIS.Definition;
Field_Type_Definition : ASIS.Definition;
Constraint : ASIS.Constraint;
begin
Range_Attribute := ASIS.Definitions.Range_Attribute (Index_Range);
Range_Prefix := ASIS.Expressions.Prefix (Range_Attribute);
Field_Name := ASIS.Expressions.Selector (Range_Prefix);
Field_Declaration := ASIS.Expressions.Corresponding_Name_Declaration (Field_Name);
Field_Definition := ASIS.Declarations.Object_Declaration_View (Field_Declaration);
Field_Type_Definition := ASIS.Definitions.Component_Definition_View (Component_Definition => Field_Definition);
Constraint := ASIS.Definitions.Subtype_Constraint (Field_Type_Definition);
for Index_Range of ASIS.Definitions.Discrete_Ranges (Constraint) loop
declare
First, Last : ASIS.Expression;
begin
First := ASIS.Definitions.Lower_Bound (Index_Range);
Last := ASIS.Definitions.Upper_Bound (Index_Range);
Ada.Wide_Text_IO.Put_Line (ASIS.Elements.Debug_Image (First));
Ada.Wide_Text_IO.Put_Line (ASIS.Text.Element_Image (First));
Ada.Wide_Text_IO.Put_Line (ASIS.Elements.Debug_Image (Last));
Ada.Wide_Text_IO.Put_Line (ASIS.Text.Element_Image (Last));
end;
end loop;
end;
end loop;
end;
end loop;
end if;
end Pre_Operation;
procedure Post_Operation (Element : in ASIS.Element;
Control : in out ASIS.Traverse_Control;
State : in out Boolean) is null;
procedure Traverse_Declaration is
new ASIS.Iterator.Traverse_Element (State_Information => Boolean,
Pre_Operation => Pre_Operation,
Post_Operation => Post_Operation);
Context : ASIS.Context;
begin
ASIS.Implementation.Initialize ("");
ASIS.Ada_Environments.Associate (The_Context => Context,
Name => "CLPG",
Parameters => "-CA -FM");
ASIS.Ada_Environments.Open (The_Context => Context);
Analyze :
declare
Complation_Unit_Body : ASIS.Compilation_Unit;
Complation_Unit_Body_Declaration : ASIS.Declaration;
Process_Control : ASIS.Traverse_Control := ASIS.Continue;
State : Boolean := False;
begin
Complation_Unit_Body := ASIS.Compilation_Units.Compilation_Unit_Body (Name => "Minimal_Example",
The_Context => Context);
Complation_Unit_Body_Declaration := ASIS.Elements.Unit_Declaration (Compilation_Unit => Complation_Unit_Body);
Traverse_Declaration (Element => Complation_Unit_Body_Declaration,
Control => Process_Control,
State => State);
end Analyze;
ASIS.Ada_Environments.Close (The_Context => Context);
ASIS.Ada_Environments.Dissociate (The_Context => Context);
ASIS.Implementation.Finalize (Parameters => "");
end Minimal_Analyzer;

How to use refs to constant Strings in arrays and records?

I am planning to convert some programs written in C/C++ to Ada.
These make heavy use of constant char literals often as ragged arrays like:
const char * stringsA = { "Up", "Down", "Shutdown" };
or string references in records like:
typedef struct
{
int something;
const char * regexp;
const char * errormsg
} ERRORDESCR;
ERRORDESCR edscrs [ ] =
{
{ 1, "regexpression1", "Invalid char in person name" },
{ 2, "regexp2", "bad bad" }
};
The presets are calculated by the C/C++ compiler and I want the Ada compiler to be
able to do that too.
I used Google and searched for ragged arrays but could only find two ways of
presetting the strings. One in Rationale for Ada 95 by John Barnes and another
at http://computer-programming-forum.com/44-ada/d4767ad6125feac7.htm.
These are shown as stringsA and stringsB below.
StringsA is defined in two stages, which is a bit tedious if there are hundreds
of strings to set up. StringsB uses one step only, but is compiler dependent.
Question 1: are there other ways?
Question 2: would the second stringsB work with GNAT Ada?
I have not started converting. The packages below are just for experimenting
and teaching myself...
package ragged is
type String_ptr is access constant String;
procedure mydummy;
end ragged;
package body ragged is
s1: aliased constant String := "Up";
s2: aliased constant String := "Down";
s3: aliased constant String := "Shutdown";
stringsA: array (1 .. 3) of String_ptr :=
(s1'Access, s2'Access, s3'Access); -- works
stringsB: array (1 .. 3) of String_ptr :=
(new String'("Up"), new String'("Down"),
new String'("Shutdown")); -- may work, compiler-dependent
-- this would be convenient and clear...
--stringsC: array (1 .. 3) of String_ptr :=
-- ("Up", "Down", "Shutdown"); -- BUT Error, expected String_ptr values
--stringsD: array (1 .. 3) of String_ptr :=
--("Up"'Access, "Down"'Access, "Shutdown"'Access); --Error - bad Access use
--stringsE: array (1 .. 3) of String_ptr :=
--(String_ptr("Up"), String_ptr("Down"),
-- String_ptr("Shutdown")); -- Error, invalid conversion
procedure mydummy is
begin
null;
end;
end ragged;
A little judicious operator overloading can do this in a less cluttered manner:
(Within the package body)
function New_String(S : String) return String_Ptr is
begin
return new String'(S);
end New_String;
function "+" (S : String) return String_Ptr renames New_String;
Now you can do:
stringsC: array (1 .. 3) of String_ptr := (+"Up", +"Down", +"Shutdown");
Not enough space in comment for this
Test program
package raggedtest is
type String_ptr is access constant String;
procedure mytest;
end raggedtest;
with ada.text_IO; use Ada.Text_IO;
package body raggedtest is
s1: aliased constant String := "Up";
s2: aliased constant String := "Down";
s3: aliased constant String := "Shutdown";
stringsA: array (1 .. 3) of String_ptr :=
(s1'Access, s2'Access, s3'Access);
stringsB: array (1 .. 3) of String_ptr :=
(new String'("UpB"), new String'("DownB"),
new String'("ShutdownB"));
function New_String(S : String) return String_Ptr is
begin
return new String'(S);
end New_String;
function "+" (S : String) return String_Ptr renames New_String;
stringsC: array (1 .. 3) of String_ptr := (+"UpC", +"DownC", +"ShutdownC");
procedure mytest is
begin
put ( "s1A: " ); put( stringsA(1).all ); New_line;
put ( "s2A " ); put( stringsA(2).all ); New_line;
put ( "s3A: " ); put( stringsA(3).all ); New_line;
put ( "s1B: " ); put( stringsB(1).all ); New_line;
put ( "s2B " ); put( stringsB(2).all ); New_line;
put ( "s3B: " ); put( stringsB(3).all ); New_line;
put ( "s1C: " ); put( stringsC(1).all ); New_line;
put ( "s2C " ); put( stringsC(2).all ); New_line;
put ( "s3C: " ); put( stringsC(3).all ); New_line;
end;
end raggedtest;
with raggedtest; use raggedtest;
procedure main is
begin
mytest;
end main;

Ada - getting string from text file and store in array

Hi im just wondering how to put data in an array if i loop txt and store it in A_Composite Name.
procedure Main is
type An_Array is array (Natural range <>) of A_Composite;
type A_Composite is
record
Name : Unbounded_String;
end record;
File : Ada.Text_IO.File_Type;
Line_Count : Integer := 0;
begin
Ada.Text_IO.Open (File => File,
Mode => Ada.Text_IO.In_File,
Name => "highscore.txt");
while not Ada.Text_IO.End_Of_File (File) loop
declare
Line :String := Ada.Text_IO.Get_Line (File);
begin
--I want to store Line String to array. but i don't know how to do it
end;
end loop;
Ada.Text_IO.Close (File);
end Main;
Ok, you have an unconstrained array here. This has implications; you see an unconstrained array gains its definite length when the object (general sense, not OOP) is declared or initialized.
As an example, let's look at strings (which are unconstrained arrays of characters) for an example to see how this works:
-- Create a string of 10 asterisks; the initialization takes those bounds.
A : constant string(1..10):= (others => '*');
-- Create a string of 10 asterisks; but determined by the initialization value.
B : constant string := (1..10 => '*');
-- Another way of declaring a string of 10 asterisks.
C : constant string := ('*','*','*','*','*','*','*','*','*','*');
Now, you can get these bounds from a function call; this means that we can use function-calls to return these values recursively.
Function Get_Text return An_Array is
Package Unbounded renames Ada.Strings.Unbounded;
-- You'll actually want the Get_Line that takes a file.
Function Get_Line return Unbounded.Unbounded_String
renames Unbounded.Text_IO.Get_Line;
begin
return (1 => (Name => Get_Line)) & Get_Text;
exception
when End_Error => return ( 1..0 => (Name => Unbounded.Null_Unbounded_String) );
end Get_Text;
So, that's how you'd do it using an unconstrained array.

Resources