Initialize string in Ada with null characters - ada

I am new to Ada,
I need to make initialize a string with null characters, how can I do it?
So far I can initialize a string with spaces as follows:
user_str : String(1..50) := (others => ' ');

user_str : String(1..50) := (others => Character'Val(0));
or
user_str : String(1..50) := (others => Ada.Characters.Latin_1.NUL);
Unlike some other languages, Ada does not have a special syntax for embedding speclal characters in character or string literals (like C's '\0', for example).
(Of course the latter requires an appropriate with clause.)

try with:
user_str : String(1..50) := (others => ascii.nul);

Related

Using File_Type as a record component?

I'm trying to write a lexer in Ada and have run into an issue.
procedure main is
...
type lexer is tagged record
input : ada.text_io.file_type;
index : integer;
end record;
...
my_lexer : lexer;
input_file_name : bounded_string;
input_file : ada.text_io.file_type;
next_token : token;
begin
input_file_name := get_input_file;
ada.text_io.open(
file => input_file,
mode => ada.text_io.in_file,
name => to_string(input_file_name)
);
my_lexer := (input => input_file, index => 0);
next_token := my_lexer.get_next_token;
ada.text_io.put_line(to_string(next_token.text));
end main;
On the line my_lexer := (input => input_file); I get the following error:
main.adb:22:17: nonlimited tagged type cannot have limited components
I understand this to be an issue since ada.text_io.in_file is a limited type, but if I were to just remove this from the record, and instead pass it as an argument to get_next_token,
...
type lexer is tagged record
index : integer;
end record;
function get_next_token(this: lexer'class; input_file: ada.text_io.file_type) return token;
...
Then this would make the state of the lexer invalid if you were to switch the input_file between calls of get_next_token thus breaking it.
Is there a way in Ada to create something like this like you would in C?
struct lexer {
FILE *input;
int index;
};
One approach, allowing the lexer type to be tagged limited is to create the limited object in-place, so that its components (specifically the limited file_type component) aren't created elsewhere (legal) and then assigned (copied, illegal). In this case, the file component of lexer is passed to the Open call, which updates it in place.
procedure lex2 is
type lexer is tagged limited record
input : ada.text_io.file_type;
index : integer;
end record;
input_file_name : unbounded_string;
my_lexer : lexer;
-- next_token : token;
begin
input_file_name := get_input_file;
my_lexer.index := 0;
ada.text_io.open( file => my_lexer.input,
mode => ada.text_io.in_file,
name => to_string(input_file_name));
-- next_token := my_lexer.get_next_token;
-- ada.text_io.put_line(to_string(next_token.text));
end lex2;
Usually when you run into problems like this there are two solutions
Define a new type that is an access to the limited type and use it in your record, or
Make your type limited. This is maybe (in my opinion) the cleaner solution since you do not have to mess with allocation, release, and stuff. (Personally, I try to avoid access types as much as possible). Of course, making it limited will prevent you to assign it, but maybe this is not a major problem in the case of a lexer.
What I'd do is hide all these details and create an abstraction:
package Lexing is
type Info is tagged limited private;
function Is_Open (State : in Info) return Boolean;
-- Description of subprogram here
procedure Open (State : in out Info; Name : in String) with
Pre => not State_Is_Open,
Post => State.Is_Open;
-- Description of subprogram here
procedure Close (State : in out Info) with
Pre => State.Is_Open,
Post => not State.Is_Open;
-- Description of subprogram here
type Token_Info is tagged private;
function Next (State : in out Info) return Token_Info with
Pre => State.Is_Open;
-- Description of subprogram here
function Text (Token : in Token_Info) return String;
-- Description of subprogram here
...
private -- Lexing
...
end Lexing;
Obviously the fun bits are left as an exercise for the reader.
I'm not sure if this is the best solution, but this is what I've come up with:
procedure main is
type file_access is access all ada.text_io.file_type;
...
type lexer is tagged record
input : file_access;
index : integer;
end record;
...
my_lexer : lexer;
input_file_name : bounded_string;
input_file : aliased ada.text_io.file_type;
next_token : token;
begin
input_file_name := get_input_file;
ada.text_io.open(
file => input_file,
mode => ada.text_io.in_file,
name => to_string(input_file_name)
);
my_lexer := (input => input_file'access, index => 0);
next_token := my_lexer.get_next_token;
ada.text_io.put_line(to_string(next_token.text));
end main;
Seems a bit convoluted, but I believe it's a way to ensure an access of input_file lives for as long as the procedure.

Ada pattern for bit fields

In C, the use of bits in some form of unsigned char or int to represent non-exclusive conditions is very common and, by use of the & | and ~ operators, is extremely efficient. From my limited Ada experience, the equivalent in Ada would be as illustrated in the following code.
with Ada.Text_IO; use Ada.Text_IO;
procedure Main is
type Colours is (Red, Green, Blue, Orange, Yellow, Purple);
type BitFieldType is array (Colours) of Boolean;
pragma Pack (BitFieldType);
RedBitField : constant BitFieldType := (Red => True, others => False);
GreenBitField : constant BitFieldType := (Green => True, others => False);
BlueBitField : constant BitFieldType := (Blue => True, others => False);
OrangeBitField : constant BitFieldType := (Orange => True, others => False);
YellowBitField : constant BitFieldType := (Yellow => True, others => False);
PurpleBitField : constant BitFieldType := (Purple => True, others => False);
NoColourBitField : constant BitFieldType := (others => False);
AllColoursBitField : constant BitFieldType := (others => True);
MyBitField : BitFieldType;
MyOtherBitField : BitFieldType;
Counter : Integer := 0;
begin
MyBitField := not RedBitField;
MyOtherBitField := RedBitField;
if (MyOtherBitField or MyBitField) = AllColoursBitField then
Counter := Counter + 1;
end if;
if (MyBitField and MyOtherBitField) = NoColourBitField then
Counter := Counter + 1;
end if;
Put_Line ("Counter is " & Integer'image (Counter));
end Main;
This appears somewhat clunky. Is there a better and more Lovelacey way to use bit maps like this?
What are you actually trying to achieve with your bitfields? You seem to want to write C using Ada. If that is true then consider using a modular type in Ada where you would use an unsigned type in C.
Section 4.5.1 of the Ada 2012 Reference Manual states:
For modular types, the predefined logical operators are defined on a
bit-by-bit basis, using the binary representation of the value of the
operands to yield a binary representation for the result, where zero
represents False and one represents True. If this result is outside
the base range of the type, a final subtraction by the modulus is
performed to bring the result into the base range of the type.
The logical operators on arrays are performed on a
component-by-component basis on matching components (as for equality —
see 4.5.2), using the predefined logical operator for the component
type. The bounds of the resulting array are those of the left operand.
For example, an unsigned type for your example could be defined as
type Color_Matrix is mod 2**6;
Red : constant Color_Matrix := 2#100000#;
Green : constant Color_Matrix := 2#010000#;
Blue : constant Color_Matrix := 2#001000#;
Orange : constant Color_Matrix := 2#000100#;
Yellow : constant Color_Matrix := 2#000010#;
Purple : constant Color_Matrix := 2#000001#;
No_Color : constant Color_Matrix := 0;
All_Colors : constant Color_Matrix := 2#111111#;
You can now perform all your familiar operations on instances of Color_Matrix.
Edit:
Additional information comparing Ada represenation clauses and C/C++ bitfields can be found at https://sworthodoxy.blogspot.com/2014/03/ada-vs-c-bit-fields.html
It does depend what you are trying to do.
Often you'll see convoluted use of the & | ~ << >> operators (or sometimes even && ||) and easy-to-get-wrong mask values in C to set, clear or test a single bit (e.g. turn RED on or off in a BitFieldType) instead of accessing the bit directly:
MyBitField(Red) := TRUE;
If MyBitField(Orange) then ...
Funnily enough, for microcontrollers with bit set, clear and test instructions, it's quite a difficult job for the compiler to translate the C code into the obvious simple instruction.
I really should not be spending my Saturday doing pupils homework! ;-)
Try to move as much as possible to the declaration part. You may do something like this:
-- Warning: Not tested
with Ada.Text_IO;
procedure Bit_Fields is
type Typ_Counter is range 0 .. 1_000_000; -- Fix this!
package Counter_Io is new Ada.Text_Io.Integer_Io (Typ_Counter);
procedure Increment (Counter : in out Typ_Counter; On_Condition : Boolean) is
begin
if On_Condition then
Counter := Counter + 1; -- May overflow!
end if;
end Increment;
type Typ_Colour is mod 2**8 with Size => 8; -- Look into this!
Colour_Max : constant Typ_Colour := Typ_Colour'Last;
Colour_None : constant Typ_Colour := Typ_Colour'First;
type Knd_Colour is (Red, Green, Blue, Orange, Yellow, Purple);
type Arr_Colour is array (Knd_Colour) of Typ_Colour;
None : constant Arr_Colour := (others => Colour_None);
Max : constant Arr_Colour := (others => Colour_Max);
generic
with function Operation (Left, Right : Typ_Colour) return Typ_Colour;
function Generic_Operation (Left, Right : Arr_Colour) return Arr_Colour;
function Generic_Operation (Left, Right : Arr_Colour) return Arr_Colour
is
Result : Arr_Colour;
begin
for Gun in Result'Range loop
Result (Gun) := Operation (Left => Left (Gun),
Right => Right (Gun));
end loop;
return Result;
end Generic_Operation;
function "or" is new Generic_Operation (Operation => "or");
function "and" is new Generic_Operation (Operation => "and");
My_Colours : Arr_Colour;
My_Other_Colours : Arr_Colour;
Counter : Typ_Counter := 0;
begin
My_Colours := (Red => not Colour_Max, others => Colour_None);
My_Other_Colours := (Red => Colour_Max, others => Colour_None);
Increment (Counter, On => (My_Other_Colours or My_Colours) = Max);
Increment (Counter, On => (My_Colours and My_Other_Colours) = None);
declare
use Ada.Text_Io, Counter_IO;
begin
Put ("Counter is ");
Put (Counter, Width => 0);
New_Line;
end;
end Bit_Fields;

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).

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.

Hexa decimal conversions in Ada

I need to convert Integer numbers to a 3 characters long String Hexa decimal, such that for 10 I need to get 00A. I'm not sure what is the most efficient way of doing this. This is what I have at the moment, however the following method adds a 16# to the output, increasing the length of the string.
function Integer2Hexa(Hex_Int : Integer) return String is
Hexa : String(1..3);
begin
Ada.Integer_Text_IO.Put(Hexa,Hex_Int,16);
return Hexa;
end Integer2Hexa;
Thanks in advance.
This is an implementation using the Ada Standard Library. It is simple, but it might be inefficient.
with Ada.Integer_Text_IO;
with Ada.Text_Io;
with Ada.Strings.Fixed;
procedure Dec2Hex is
function Integer2Hexa (Hex_Int : Integer; Width : Positive := 3)
return String is
Hex_Prefix_Length : constant := 3;
Hexa : String (1 .. Hex_Prefix_Length + Width + 1);
Result : String (1 .. Width);
Start : Natural;
begin
Ada.Integer_Text_IO.Put (Hexa,Hex_Int, 16);
Start := Ada.Strings.Fixed.Index (Source => Hexa, Pattern => "#");
Ada.Strings.Fixed.Move
(Source => Hexa (Start + 1 .. Hexa'Last - 1),
Target => Result,
Justify => Ada.Strings.Right,
Pad => '0');
return Result;
end Integer2Hexa;
begin
Ada.Text_Io.Put_Line (Integer2Hexa (10));
Ada.Text_Io.Put_Line (Integer2Hexa (16#FFF#));
Ada.Text_Io.Put_Line (Integer2Hexa (6));
Ada.Text_Io.Put_Line (Integer2Hexa (32, Width => 4));
end Dec2Hex;
I am not sure about the most efficient way to do this either, but whenever I have the problem of converting an integer to a string representation using some other base other than 10 I would consider using Dmitry A. Kazakovs Simple Components and in particular its Strings_Edit package:
http://www.dmitry-kazakov.de/ada/strings_edit.htm

Resources