Command line arguments for Ada - ada

I'm writing an Ada program that is supposed to do case conversion for alphabetic characters. The program uses 1, 2 or 3 command line arguments. I pretty much have the thing written up, but I have no clue as to how to how to do arguments. the command line arguments are to:
A single character specifying whether uppercase conversion or lowercase conversion is to be
applied to the input. 'U' or 'u' signifies uppercase conversion; 'L' or 'l' specifies lowercase
conversion. This parameter is required for the program to run.
(optional) The name of the file to be used for input to the uppercase/lowercase conversion.
If this parameter is not specified, the program must read from standard input.
(optional and only used if the third command line parameter is also provided) The name of
the file to be used for output from the encryption or decryption process. If this parameter is
not specified, the program must write to standard output.
Any help?

You could use the standard package Ada.Command_Line to access command line arguments.
You have Argument_Count for the number of arguments.
You have Argument(Number : Positive) to get the argument string at position Number.

The Ada.Command_Line package is standard and perfectly adequate for the task you have.
More complex command line parsing becomes hard work using Ada.Command_Line. If you need named rather than positional association for your command line, see this Gem from Adacore on using Gnat.Command_Line for (less portable, if that matters, but) more Unix-like command line sequences of parameters and options.
There is also a Generic Command Line Parser which I have used successfully on a small project.

I would suggest something like that, as already said using Ada.Command_Line:
with
Ada.Text_IO,
Ada.Command_Line,
Ada.Strings.Bounded;
use
Ada.Text_IO,
Ada.Command_Line;
procedure Main is
package SB is new Ada.Strings.Bounded.Generic_Bounded_Length (Max => 100);
use SB;
Cur_Argument : SB.Bounded_String;
Input_File_Path : SB.Bounded_String;
Output_File_Path : SB.Bounded_String;
I : Integer := 1;
begin
-- For all the given arguments
while I < Argument_Count loop
Cur_Argument := SB.To_Bounded_String(Argument(I));
if Cur_Argument = "U" or Cur_Argument = "u"
then
-- stuff for uppercase
elsif Cur_Argument = "L" or Cur_Argument = "l"
then
-- stuff for lowercase
elsif Cur_Argument = "i"
then
-- following one is the path of the file
Input_File_Path := SB.To_Bounded_String(Argument(I+1));
i := i + 1;
elsif Cur_Argument = "o"
then
Output_File_Path := SB.To_Bounded_String(Argument(I+1));
i := i + 1;
else
Put_Line("Wrong arguments");
end if;
i := i + 1;
end loop;
end Main;

Related

Ada program to detect an end of line

I was assigned this task as my homework. I have a file which contains lines of text of varying lengths. The program is supposed to write the data onto the screen in precisely the same order in which it is written in the file, yet it fails to do so. To achieve the desired result I tried reading only one character per iteration so as to detect new line characters. What am I doing wrong?
WITH Ada.Text_IO;
WITH Ada.Characters.Latin_1;
USE Ada.Text_IO;
PROCEDURE ASCII_artwork IS
File : File_Type;
c : Character;
BEGIN
Open(File, In_File, "Winnie_The_Pooh.txt");
WHILE NOT End_Of_File(File) LOOP
Get(File, C);
IF (C = Ada.Characters.Latin_1.LF) THEN Put_Line(" "); ELSE
Put(C);
END IF;
END LOOP;
Close(File);
END ASCII_Artwork;
For each file, the Ada runtime maintains a fictitious "cursor". This is not the typical file position cursor (index), but one that indicates the position on a page, line, etc. (see also RM A.10 (7)). This is somewhat of an inheritance from the early versions of Ada.
Get stems from this same era and is expected to update the location of this cursor when some particular control characters are being read (e.g. an end-of-line mark). If Get reads such such a control character, it will only use it to update the cursor (internally) and then continue to read a next character (see also RM A.10.7 (3)). You'll therefore never detect an end-of-line mark when using Get.
This behavior, however, has some uncomfortable consequence: if a file ends with a sequence of control characters, then Get will keep reading those characters and hit the end of the file causing an End_Error exception.
You can, of course, catch this exception and handle it, but such a construct is dubious as having a sequence of control characters at the end of a file is actually not such an abnormal case (and hence dubious if worth an exception). As a programmer, however, you cannot change this behavior: it's defined by the language and the language will not be changed because it has been decided to keep Ada (highly) backwards compatible (which in itself is understandable given its field of application).
Hence, in your case, if you want stick to a character-by-character processing approach, I would suggest to move away from Get and instead use (for example) streams to perform I/O as in the example below.
main.adb
with Ada.Text_IO; use Ada.Text_IO;
with Ada.Text_IO.Text_Streams; use Ada.Text_IO.Text_Streams;
procedure ASCII_artwork IS
File : File_Type;
Input : Stream_Access;
Output : Stream_Access;
C : Character;
begin
Open (File, In_File, "Winnie_The_Pooh.txt");
Input := Stream (File);
Output := Stream (Standard_Output);
while not End_Of_File (File) loop
Character'Read (Input, C);
Character'Write (Output, C);
end loop;
Close(File);
end ASCII_Artwork;
Output is as expected (i.e. the content of this the file at ascii-art.de).
NOTE: Check the source code of the GNAT runtime to actually see how Get works internally (focus on the loop at the end).
As explained by DeeDee, text inputs are buffered linewise in Ada. The idea is to be able to read two integers on the same line. For consistency sake (the designers of Ada are picky on that...), Get(File, C) does the same. It is not practical in your case. Fortunately, Ada 95 has introduced Get_Immediate, to solve precisely that issue.
Otherwise, as suggested by Frédéric, you could use the function Get_Line to absorb Winnie_The_Pooh.txt line by line seamlessly. By the way, the Get_Line method will convert the different end-of-line conventions automatically.
Line terminators in Ada.Text_IO are a concept, not a character or sequence of characters in the file. (Although most commonly used file systems implement them as characters or sequences of characters in the file, there exist file systems that do not.) Line terminators must therefore be manipulated using the operations in the package. For reading, End_Of_Line checks to see if the cursor is at a line terminator, Skip_Line skips the next line terminator, and Get_Line may skip a line terminator. For writing, New_Line and Put_Line write line terminators.
For your problem, the canonical solution is to use the Get_Line function to read lines, and Put_Line to output the lines read.

Inconsistent argument passing mode

Consider the following code (which compile with GCC 4.7.4):
procedure Main is
procedure Sub_Proc(N : in out Integer) is
M : Integer;
begin
M := N;
end;
procedure Proc(N : out Integer) is
begin
Sub_Proc(N);
end;
N : Integer;
begin
Proc(N);
end Main;
The procedure Proc is supposed to ensure that the argument N which is an out argument will never be read inside his body. But it passes this argument to a procedure Sub_Proc which takes an in out argument, so potentially the previous argument will be read inside this procedure whereas the calling procedure ensured the opposite.
Is it a GCC bug or an Ada standard specificity ?
You’ll have received a warning on the Sub_Proc(N); call: "N" may be referenced before it has a value. So the compiler was trying to help!
In Ada 83, your program would have been illegal: 6.4.1(3) says "for the mode in out, the variable must not be a formal parameter of mode out”. Indeed, using -gnat83, and after a minor rearrangement of the code to allow it to compile, the equivalent to the above warning is the error (Ada 83) illegal reading of out parameter.
In Ada 95 and Ada 2012 it is OK to read the value of an out parameter after it has been assigned; in ARM95 6.4.1(15) we find that the value starts out uninitialized (as indicated by the warning message noted above), so using it is a Bad Idea.
So the answer is, GNAT’s behaviour conforms with the standard.

Get_line in Ada

I have a little problem in using get_line to be more concret I must take a line from a file and use it. I don't know how especially if the lines aren't constitute just from characters there's also float can I use get_line in this case?
Thank you. Let's start with this little example:
with Ada.Text_Io;
use Ada.Text_Io;
procedure Getline is
A:String;
T:string;
begin
Open(File => F, Mode => In_File, Name => Nom_Fichier);
A:=Get_Line(F,In_File, T);
Put(A);
end Getline;`
It looks like you're just guessing the parameters you should pass to Get_Line. I suggest you have a look at the relevant part in the ARM: The function Get_Line only takes a File_Type and returns a String; the procedure Get_Line takes a File_Type and, as output parameters, a String and a Natural.
Then, String is an indefinite subtype, meaning that you have to assign something to A at declaration, or provide boundaries for it. Here's a working version of your example code:
with Ada.Text_IO; use Ada.Text_IO;
procedure Getline is
F : File_Type;
File_Path : constant String := "testfile.stl";
begin
Open (File => F, Mode => In_File, Name => File_Path);
declare
A : String := Get_Line (F);
begin
Put (A);
end;
Close (File => F);
end Getline;
Before you try something more complex, you should get familiar with the basics of the language. The wikibook is a good place to start. If you want to get your actual question about reading floats from the line answered, you need to provide more details about how a potential line looks like.
Get_Line simply interprets the "line" (set of characters up to the next line terminator or end of file) as text, and gives it to the caller that way. Thus if the file contains:
10.52
Then your call to Get_Line will return the string "10.52".
It may be true that if you tried to read that using Float_Text_IO you would get the float value 10.52 back. However, there's no metadata associated with text in text files, so the computer has no way of knowing that text happens to be a representable float without parsing it and seeing if it can make a float out of it. It of course isn't going to bother to do that unless you ask it to via something like a call into Float_Text_IO

Ada Modeless Subprogram Parameters

If a parameter in Ada is left modeless, what happens?
what is the difference between
procedure my_func ( param_1 : in param_type )
and
procedure my_func ( param_1 : param_type )
I am new to ada and have been writing most of my procedures as the latter. The program compiles and runs as expected.
There is no difference - if no parameter mode is given, the compiler assumes "in".
See http://www.ada-auth.org/standards/12rm/html/RM-6-1.html line beginning 18/3.
-- Martin
As suggested by Martin, the default mode is 'in' if not provided.
I would like to add that if possible, you can sometimes experiment with things about which you are doubtful.
like look at the below simple code, I have not given any mode to the arguement 'no_1'.
And as seen I am assigning value of 'no_2' to it.
with Ada.Text_IO; use Ada.Text_IO;
procedure just_testing is
procedure get_value (no_1 : Integer);
procedure get_value (no_1 : Integer) is
no_2 : Integer := 2;
begin
no_1 := no_2;
end get_value;
begin
Put("auto mode");
end just_testing;
And when I compile this code, look what we get as error.
>gnatmake just_testing.adb
gcc -c just_testing.adb
just_testing.adb:10:09: assignment to "in" mode parameter not allowed
gnatmake: "just_testing.adb" compilation error
So, compiler makes it clear that the default mode is 'in', as we can not assign any value to argument with mode in.

The use of IN OUT in Ada

Given below is some code in ada
with TYPE_VECT_B; use TYPE_VECT_B;
Package TEST01 is
procedure TEST01
( In_State : IN VECT_B ;
Out_State : IN OUT VECT_B );
function TEST02
( In_State : IN VECT_B ) return Boolean ;
end TEST01;
The TYPE_VECT_B package specification and body is also defined below
Package TYPE_VECT_B is
type VECT_B is array (INTEGER range <>) OF BOOLEAN ;
rounded_data : float ;
count : integer ;
trace : integer ;
end TYPE_VECT_B;
Package BODY TYPE_VECT_B is
begin
null;
end TYPE_VECT_B;
What does the variable In_State and Out_State actually mean? I think In_State means input variable. I just get confused to what actually Out_State means?
An in parameter can be read but not written by the subprogram. in is the default. Prior to Ada 2012, functions were only allowed to have in parameters. The actual parameter is an expression.
An out parameter implies that the previous value is of no interest. The subprogram is expected to write to the parameter. After writing to the parameter, the subprogram can read back what it has written. On exit the actual parameter receives the value written to it (there are complications in this area!). The actual parameter must be a variable.
An in out parameter is like an out parameter except that the previous value is of interest and can be read by the subprogram before assignment. For example,
procedure Add (V : Integer; To : in out Integer; Limited_To : Integer)
is
begin
-- Check that the result wont be too large. This involves reading
-- the initial value of the 'in out' parameter To, which would be
-- wrong if To was a mere 'out' parameter (it would be
-- uninitialized).
if To + V > Limited_To then
To := Limited_To;
else
To := To + V;
end if;
end Add;
Basically, every parameter to a function or procedure has a direction to it. The options are in, out, in out (both), or access. If you don't see one of those, then it defaults to in.
in means data can go into the subroutine from the caller (via the parameter). You are allowed to read from in parameters inside the routine. out means data can come out of the routine that way, and thus you are allowed to assign values to the parameter inside the routine. In general, how the compiler accomplishes the data passing is up to the compiler, which is in accord with Ada's general philosophy of allowing you to specify what you want done, not how you want it done.
access is a special case, and is roughly like putting a "*" in your parameter definition in Cish languages.
The next question folks usually have is "if I pass something large as an in parameter, is it going to push all that data on the stack or something?" The answer is "no", unless your compiler writers are unconsionably stupid. Every Ada compiler I know of under the hood passes objects larger than fit in a machine register by reference. It is the compiler, not the details of your parameter passing mechanisim, that enforces not writing data back out of the routine. Again, you tell Ada what you want done, it figures out the most efficient way to do it.

Resources