Function reading from standard input without any "in" parameters - ada

Perhaps this is simple, and I am just missing some basic information, but I can't seem to find the answer anywhere.
I'm writing a Get_Word function for class, here is the relevant section of the spec file my prof wrote:
function Get_Word return Ustring;
-- return a space-separated word from standard input
procedure Fill_Word_List(Wl : in out Ustring_Vector);
-- read a text file from standard in and add all
-- space-separated words to the word list wl
I've written the Get_Word function, and am trying to test it out with this code:
with Ada.Text_IO; use Ada.Text_Io;
with Ada.Integer_Text_IO; use Ada.Integer_Text_IO;
procedure ngramtest is
Name : String(1..80);
File : File_Type;
Size : Natural;
function Get_Word return String is
-- I'm using a strings instead of Unbounded_Strings for testing purposes.
Word : String(1..80) := (others => ' ');
Char : Character;
File : File_Type;
Eol : Boolean;
I : Integer := 1;
begin
--this code below, when uncommented reveals whether or not the file is open.
--if Is_Open(File) then
-- Word := (1..80 => 'y');
--else
-- Word := (1..80 => 'n');
--end if;
loop
Look_Ahead(File, Char, Eol);
if Eol then
exit;
elsif Char = ' ' then
exit;
else
Get (File, Char);
Word(I) := Char;
I := I + 1;
end if;
end loop;
return Word(1..Word'Last);
end Get_Word;
begin
Put ("Enter filename: ");
Get_Line (Name, Size);
Open (File, Mode => In_File, Name => Name(1..Size));
Put (Get_Word);
Close(File);
end ngramtest;
It compiles, but at runtime I get an exception telling me that the file isn't open, and the commented out section returns "nnnnnn..." meaning that the file is not open within the function.
My question is how am I to read from standard input if i'm not allowed to use in parameters in my function? Without them the function won't be able to access files.
Essentially, how can I "Get_Word"?
Sorry if this is simple, but I'm completely lost.

You need to set your "File" variable to standard input:
File : File_Type := Ada.Text_IO.Standard_Input;

Related

Function and procedure subprogram

I need to only use one function and one procedure and unfortunately I didn't manage to solve this problem.
The problem goes as following:
Create a subprogram that has three parameters. One string and two sign.
The subroutine must pick the first and last character in the string. The main program will then print these two characters.
Create a subprogram that retrieves a character, from the keyboard, which represents a truth value (see driving example).
The subprogram must send back the corresponding truth value to the main program.
In the terminal it should look like:
Type a string containing 6 characters: Overfl
First character is: O
Last character is : l
Type F or T (for False or True): T
You typed True
This is how my code looks like:
with Ada.Text_IO; use Ada.Text_IO;
procedure Test is
procedure String_Check(
S : in String;
Char_1, Char_2 : out Character
) is
begin
Char_1 := S(S'First);
Char_2 := S(S'Last);
end String_Check;
function Character_Check return Boolean is
Check : Integer;
begin
Put("Type F or T (For False or True): ");
Get(Check)
if Check = 'T' then
return True;
else
return False;
end if;
end Character_Check;
Char_1, Char_2 : Character;
Check : Character;
S : String(1 .. 6);
begin
Put("Type a string containing 6 characters: ");
Get(S);
String_Check(S, Char_1, Char_2);
Put("First character is: ");
Put(Char_1);
New_Line;
Put("Last character is: ");
Put(Char_2);
Skip_Line;
New_Line(2);
Put("You typed ");
if Character_Check then
Put("True");
else
Put("False");
end if;
end Test;
My code runs but it doesn't fulfill the given requirements. I guess I can't use a function in my second subprogram because the instruction says "create a subprogram that receives a character from the keyboard". How am I supposed to do Get in a function? But if I make my second program to a procedure I have to make my first program to a function. How am I supposed to do return for two values at the same time?

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.

How to save Doubly_Linked_Lists text to a file for Ada language? Also how to have not fixed size string input?

I am writing a school project to store string to the doubly linked list, but I don't know how to save the final list to a text file. I also what the user to choose the text file name. Can someone show me how to do that? Also for Ada can I have a not fixed-length string as input? Right now I set the string length to 5 but I really wanted to have a flexible length for the string. Below is my code thank you.
with Ada.Containers.Doubly_Linked_Lists;
with Ada.Text_Io; use Ada.Text_Io;
with Ada.Strings.Unbounded; use Ada.Strings.Unbounded;
with Ada.Integer_Text_IO; use Ada.Integer_Text_IO;
with Ada.IO_Exceptions;
procedure main is
type Command is (A, C, P, E, M);
package Command_IO is new Ada.Text_IO.Enumeration_IO (Command);
package String_List is new Ada.Containers.Doubly_Linked_Lists(Unbounded_String);
use String_List;
Userinput : String(1 .. 5) := (others => ' '); --This string length is 5
InsertLocation : String(1 .. 5) := (others => ' '); --This string length is 5
text : List; -- List is from Doubly_Linked_Lists
F : File_Type;
begin
loop
declare
Cmd : Command;
procedure Print(Position : Cursor) is -- this subprogram print all string from list
begin
Put_Line(To_String(Element(Position)));
--Put_Line("K");
end Print;
begin
Put_Line("*****************************");
Put_Line("*Enter A to add a text *");
Put_Line("*Enter C to insert a text *");
Put_Line("*Enter P the print text list*");
Put_Line("*Enter E the exit program *");
Put_Line("*Enter M to save the list *");
Put_Line("*****************************");
Put_Line(" ");
Put(">> ");
Command_IO.Get (Cmd);
Ada.Text_IO.Skip_Line;
Put_Line ("read " & Cmd'Image); -- ' to sort out the not-fully-Ada-aware syntax highlighting
Put_Line (" " );
case Cmd is
when a =>
Put_Line("Enter a text " );
Put(">> " );
Get(Userinput);
text.Append(To_Unbounded_String(Userinput)); -- if letter is a add it to the doubly link list
Put_line(" " );
when c =>
Put_Line("Enter a text location you want to insert " );
Put(">> " );
Get(Userinput);
Put_Line("Enter a text " );
Put(">> " );
Get(InsertLocation);
text.Insert(Before => text.Find(To_Unbounded_String(Userinput)), New_Item => To_Unbounded_String( InsertLocation ));
when p =>
text.Iterate(Print'access);
Put_line(" " );
when m =>
Put_Line("Save to file");
Create (F, Out_File, "file.txt");
Put_Line (F, "This string will be written to the file file.txt");
Close (F);
when e =>
Put_Line("Program Exit");
exit;
end case;
exception
when Ada.IO_Exceptions.Data_Error =>
Put_Line ("unrecognised command");
Put_Line (" ");
end;
end loop;
end main;
You look like you have all the parts. You can use Text_IO for writing to the file and you can use Unbounded_String to hold strings of any size. For reading in strings of any size, you can use the procedure Get_Line in Text_IO which returns a string of any length or if you really want to use Unbounded_Strings, you can use Ada.Text_IO.Unbounded_IO.Get_Line. I could also optionally suggest looking at Indefinite_Doubly_Linked_List instead of using Unbounded_String, depending on your use case.
For writing to file, just loop over the List and write each element to a file using Text_IO style functions.
Here is some scaffolding showing some of this.
with Ada.Text_IO; use Ada.Text_IO;
with Ada.Containers.Indefinite_Doubly_Linked_Lists;
use Ada.Containers;
procedure Hello is
package Lists is new Indefinite_Doubly_Linked_Lists(String);
Text : Lists.List;
begin
Put_Line("Hello, world!");
Put_Line("Enter a text:" );
declare
User_Input : String := Get_Line;
begin
Text.Append(New_Item => User_Input);
end;
Put_Line("Enter Text to Insert at:");
declare
Location : String := Get_Line;
begin
Put_Line("Enter New Text:");
declare
User_Input : String := Get_Line;
begin
Text.Insert(Before => Text.Find(Location), New_Item => User_Input);
end;
end;
-- Loop through list and write each element
for Line of Text loop
Put_Line(Line);
-- Write to file here using Text_IO file operations
end loop;
end Hello;
Side note, if you spend more time searching your data than you do creating your list, you can also look at Map packages. Ada has both hashed and ordered maps (with indefinite options as well).
A couple of observations:
(1) Although a String is a fixed-length object, the length can be determined at initialisation:
declare
Str : constant String := Get_Line;
begin
...
You may have to be a bit creative about using the value in Str outside its scope, the declare block. For example, if it’s the name of a file to be opened, the File_Type object to be opened could be in an outer scope.
(2) If you want to read an integer, use Ada.Integer_Text_IO.Get (followed by Skip_Line, because the Get terminates when the number has been read, leaving the rest of the line, including the terminator, in the input buffer)
Looks like you are on the right path.
A couple of points :
you can ask the user to enter a filename (e.g. in a funciton returning String, used to initialise a variable as in Simon's answer. This function could suggest a default which will be used if the user just presses Return. Then you pass that string to the Create call.
Probably the best way to output complex things like lists is to learn Stream I/O. This may be more complex to set up than you need right now, but ultimately it would let you simply "Write" the whole list to file in a single operation. Each data type has 'Read and 'Write attributes, which are procedures you can overload with your own procedures to format the file however you wish. The default 'Write attribute for a list will iterate over the whole List calling each element's own 'Write procedure. (It may also insert information about the List you don't want, that's why it's overloadable...)

Ada - How do you read an array from a single line of input?

My question is pretty simple, I have input that looks like this...
0 0 0 1 1 1 -1 -1 -1 1
And I need to store these values into an array but I can't figure it out. This is what I have so far...
with Ada.Text_IO; use Ada.Text_IO;
procedure Main is
type arr is array(1..10) of Integer;
Data : arr;
begin
for I in 1..arr'Length loop
Data(I) := Integer'Value(Get_Line);
end loop;
end Main;
I know this wrong and it's pretty obvious why this isn't working. I'm trying to store multiple values into a single integer, I need a way to iterate over the input or load all the values at once. How would you do this in Ada?
You can use Get_Line to get the whole line as a string and then Ada.Integer_Text_IO to parse the string:
with Ada.Text_IO; use Ada.Text_IO;
with Ada.Integer_Text_IO; use Ada.Integer_Text_IO;
procedure Hello is
Line : String := Get_Line;
Value : Integer;
Last : Positive := 1;
begin
while Last < Line'Last loop
Get(Line(Last..Line'Last),Value,Last);
Put_Line(Value'Image); -- Save the value to an array here instead
Last := Last + 1; -- Needed to move to the next part of the string
end loop;
end Hello;
After that, you can load the values into an array in the loop or however you like.
Example output:
$gnatmake -o hello *.adb
gcc -c hello.adb
gnatbind -x hello.ali
gnatlink hello.ali -o hello
$hello
0
0
0
1
1
1
-1
-1
-1
1
EDIT: Adding a recursive option that is more general. This will read a line from STDIN and recursively concatenate the values into an array. It uses the secondary stack to do so in GNAT.
with Ada.Text_IO; use Ada.Text_IO;
with Ada.Integer_Text_IO; use Ada.Integer_Text_Io;
procedure Hello is
-- Need an array return type
type Integer_Array is array (Positive range <>) of Integer;
-- Recursive function
function Get_Ints return Integer_Array is
Value : Integer;
begin
-- Read from STDIN using Integer_Text_IO;
Get(Value);
-- Concatinate recursively
return Integer_Array'(1 => Value) & Get_Ints;
exception
-- I found different exceptions with different versions
-- of GNAT, so using "others" to cover all versions
when others =>
-- Using Ada2012 syntax here. If not using Ada2012
-- then just declare the Empty variable somewhere
-- and then return it here
return Empty : Integer_Array(1..0);
end Get_Ints;
Result : Integer_Array := Get_Ints;
begin
Put_Line("Hello, world!");
Put_Line(Integer'Image(Result'Length));
for E of Result loop
Put(Integer'Image(E) & " ");
end loop;
end Hello;
If you know that you have 10 elements to read, it can be done a little more simply like this:
with Ada.Text_IO; use Ada.Text_IO;
with Ada.Integer_Text_IO; use Ada.Integer_Text_IO;
procedure Hello is
A: array (1..10) of Integer;
begin
for V of A loop
Get(V);
end loop;
for I in A'Range loop
Put(I, 5);
Put(": ");
Put(A(I), 5);
New_Line;
end loop;
end Hello;
If you don't actually know how many elements to read in advance, please update the question.
Even though this already was answered, I'd like to add a couple improvements to Jere's answer.
It is more Ada-like to terminate the recursion using End_Of_File rather than an exception. Plus, it makes the program clearer.
Also, using tail-call recursion instead of normal recursion allows the compiler to perform some optimization.
function Get_Ints(input : in File_Type) return Integer_Array is
function Get_Ints_Rec(accumulator : in Integer_Array) return Integer_Array is
value : Integer;
begin
if End_Of_File(input) then
return accumulator;
else
begin
Get(input, value);
exception
when Data_Error => -- problem when reading
if not End_Of_Line(input) then
Skip_Line(input);
end if;
return Get_Ints_Rec(acc);
end;
return Get_Ints_Rec(accumulator & (1 => value));
end if;
end Get_Ints_Rec;
acc : constant Integer_Array(1 .. 0) := (others => 0);
begin
return Get_Ints_Rec(acc);
end Get_Ints;

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