I am learning Ada (by trying https://adventofcode.com/2018/ problems). To start with, I am trying to develop a number of "utility" packages that will help with text processing etc.
I have successfully written a function that will read from stdin and return an array of Unbounded_Strings for each input line.
I am trying to modify that function to do the same, but instead convert each Unbounded_String to an Integer before insertion into the array.
Here is my package:
get_stdin.ads:
with Ada.Strings.Unbounded;
package get_stdin is
type IntegerArray is array (Natural range <>) of Integer;
function get_ints return IntegerArray;
end get_stdin;
get_stdin.adb:
with Ada.Text_IO;
with Ada.Text_IO.Unbounded_IO;
with Ada.Strings.Unbounded;
with Ada.Strings;
package body get_stdin is
function get_ints return IntegerArray is
Counter : Natural := 0;
Str : Ada.Strings.Unbounded.Unbounded_String;
Arr : IntegerArray(0..10000);
begin
while not Ada.Text_IO.End_Of_File loop
Str := Ada.Text_IO.Unbounded_IO.Get_Line;
Arr(Counter) := Integer'Value(Ada.Strings.Unbounded.To_String(Str));
Counter := Counter + 1;
end loop;
return Arr(0..Counter-1);
end get_ints;
end get_stdin;
I am calling using this package inside this procedure:
procedure d1 is
StdinArr : get_stdin.IntegerArray := get_stdin.get_ints;
begin
null; -- Array processing to follow
end;
This successfully compiles, and I then pipe in my input text file:
me#mypc /cygdrive/c/Users/me/aoc2018
$ cat d1.txt
-6
-1
-18
-10
...etc
me#mypc /cygdrive/c/Users/me/aoc2018
$ cat d1.txt | ./d1.exe
raised CONSTRAINT_ERROR : bad input for 'Value: "-6"
"-6" is the first value in the text file. My string-to-integer conversion code was essentially copied from this question.
I am not sure why a bad input error is raised.
It raises the same error if I replace -6 with a positive integer in the file
This is running under Cygwin on Windows 10.
Compiled/linked with gnatmake version 7.3.0
Note: I'm just getting started with Ada so there's probably lots of issues with my code in general.
What am I doing wrong and how can I fix this function/package to return my IntegerArray type correctly filled with Integers?
This was a line endings issue. I was running under cygwin on Windows 10. My text file has Windows-style line endings.
Using dos2unix:
$ cat d1.txt | dos2unix.exe | ./d1.exe
was sufficient to make it work correctly.
If anyone can explain precisely why, that would be interesting. I'm guessing that Get_Line only strips off the \n character, not the \r.
Related
I was expecting this program to raise an error when I feed it 3 as a valid Scale value, but no such luck:
with Ada.Text_IO; use Ada.Text_IO;
procedure predicate is
type Scale is new Integer
with Dynamic_Predicate => Scale in 1 | 2 | 4 | 8;
GivesWarning : Scale := 3; -- gives warning
begin
Put_Line ("Hello World");
loop
Put_Line ("Gimme a value");
declare
AnyValue : Integer := Integer'Value (Get_Line);
S : Scale := Scale (AnyValue); -- no check done!
begin
Put_Line ("okay, that works" & S'Image);
end;
end loop;
end predicate;
I found this related question, but there the requirement is to use an enum., and the solution is to define an array from enum -> value.
I want something that gives me at least a warning at compile time, and allows me to check at runtime as well, and that raises an error if I try to put an invalid value in. Then, if I can use SPARK to prove that no invalid values can occur, I could turn off said checks. I was under the impression that this was how Static_ / Dynamic_ predicates work, so the example above took me by surprise.
You need to enable assertions. Either compile with -gnata or set an appropriate Assertion_Policy
pragma Assertion_Policy(Dynamic_Predicate => Check);
I'm in the process of making my own package for an Ada main program. I read a string followed by an integer and another string and the problem is I need to cut the first string at sign of first space. I don't know how to do it and I've searched stack overflow only to find solutions in other languages.
My code right now in the package body is:
Get_Line(Item.String, Item.X1)
where X1 is an integer and String is the string. This works if you define the length in the type to match the exact length of your input but of course you want to be able to insert whatever you want and thus it doesn't work.
Can somebody point me in the right direction?
Thanks
Why do you need to make a package for an Ada main program? Most compilers need them to be parameterless library-level procedures.
Anyway, this might give you some tips.
with Ada.Text_IO;
with Ada.Integer_Text_IO;
procedure Agrell is
begin
declare
Line : constant String := Ada.Text_IO.Get_Line;
This is how to deal with reading a string of unknown length. You have to work out how to preserve it for future use (maybe use an Unbounded_String?)
The_Integer : Integer;
begin
Looking_For_Space :
for J in Line'Range loop
if Line (J) = ' ' then
Everything from Line’First to J - 1 is the string you wanted.
declare
Dummy : Positive;
begin
Ada.Integer_Text_IO.Get (From => Line (J .. Line'Last),
Item => The_Integer,
Last => Dummy);
end;
OK, now we have The Integer...
...
exit Looking_For_Space;
... and we’re done with the first line.
end if;
end loop Looking_For_Space;
end;
end Agrell;
I found this question and the first answer contains some example code demonstrating how to start an executable with Ada code. The output of the executable is written to the standard output.
What options do I have to read the output of the executable for further parsing/processing in Ada (for example line by line)?
If you use GNAT, then you might want to take a look at Get_Command_Output in the GNAT.Expect package. Here's an example:
with Ada.Text_IO, GNAT.Expect;
procedure Main is
Command : constant String := "gnat";
Argument_1 : aliased String := "--version";
Input : constant String := "";
Status : aliased Integer := 0;
-- Execute the command and retrieve the output.
Output : String :=
GNAT.Expect.Get_Command_Output
(Command => Command,
Arguments => (1 => Argument_1'Unchecked_Access),
Input => Input,
Status => Status'Access,
Err_To_Out => False);
-- NOTE: Cheating with Unchecked_Access, OK for demo. You may want
-- to properly new and Free these strings (see Argument_List
-- type in package GNAT.OS_Lib).
begin
Ada.Text_IO.Put_Line (Output);
end Main;
The program returns after execution:
$ ./main
GNAT Community 2019 (20190517-83)
Copyright (C) 1996-2019, Free Software Foundation, Inc.
This is free software; see the source for copying conditions.
There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
As can be seen, the result is returned as a single string. You will have do the line splitting yourself.
Update
An update in response to some comments below.
You might also consider using the system function if you're targeting the Windows platform (see also this post on SO). Quoting from the function reference:
The system function passes command to the command interpreter, which executes the string as an operating-system command.
This is similar to what the program cmd.exe does. You can obtain the output of the command by redirecting its output to a file (i.e. using >) and then subsequently read it back. Here's an example:
with Ada.Text_IO;
with Ada.Text_IO.Unbounded_IO;
with Ada.Strings.Unbounded;
with Interfaces.C;
with Interfaces.C.Strings;
procedure Main is
use Ada.Strings.Unbounded;
Content : Unbounded_String := Null_Unbounded_String;
begin
-- Execute.
declare
use Interfaces.C;
use Interfaces.C.Strings;
function system (command : chars_ptr) return int
with Import, Convention => C, External_Name => "system";
command : chars_ptr := New_String("gnat --version > gnat_version.out");
result : int := system (command);
begin
-- Check value of result (omitted in this example).
Free(Command);
end;
-- Read.
declare
use Ada.Text_IO;
use Ada.Text_IO.Unbounded_IO;
Fd : File_Type;
begin
Open (Fd, In_File, "./gnat_version.out");
while not End_Of_File (Fd) loop
Content := Content
& Unbounded_String'(Get_Line (Fd))
& ASCII.CR & ASCII.LF; -- Restore the line break removed by Get_Line.
end loop;
Close (fd);
end;
-- Show.
Ada.Text_IO.Unbounded_IO.Put (Content);
end Main;
I am unable to get the following script to return my input value; I've looked up ARM as well as John Barnes book but to no avail. In theory it should work.
Anyone know why? I'm a newby so the Barnes book and the ARM are probably too advanced for me.
with Ada.Text_IO;
use Ada.Text_IO;
procedure ron is
A : Character;
begin
Put_Line ("Hi Ron, how are you?");
A := Character'Value (Get_Line);
Put_Line ("So you feel" &
Character'Image (A));
end ron;
--TERMINAL OUTPUT
--ronhans#amante ~/Desktop $ gnatmake -gnat2012 ron.adb
--gcc-4.8 -c -gnat2012 ron.adb
--gnatbind -x ron.ali
--gnatlink ron.ali
--ronhans#amante ~/Desktop $ ./ron
--Hi Ron, how are you?
--well.
--raised CONSTRAINT_ERROR : bad input for 'Value: "well."
If you look in the LRM, you will see that Ada.Text_IO.Get_Line returns a String:
with Ada.Text_IO;
procedure Ron is
begin
Ada.Text_IO.Put_Line ("Hi Ron, how are you?");
declare
Reply : constant String := Ada.Text_IO.Get_Line;
begin
Ada.Text_IO.Put_Line ("So you feel " & Reply & "?");
end;
end Ron;
The problem with you program is that you try to put an array of characters into a single character. Instead of of using A : Character, try to define an array type something like
type Character_Array_T (1 .. 10) of Character;
...
A : Character_Array_T;
or use
with Ada.Strings.Unbounded;
...
A : Ada.Strings.Unbounded.Unbounded_String;
I suggest the use of an unbounded string, so that the input is not bounded to some specific string length, if your intention is to read out an input several times. Ada type string requires you to specify the string length and this length is exactly the number of characters this string should contain.
See Wiki, unbounded strings and Unbounded string handling for the reference.
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