with Ada.Text_IO; use Ada.Text_IO;
with Ada.Integer_Text_IO; use Ada.Integer_Text_IO;
with Ada.Float_Text_IO; use Ada.Float_Text_IO;
with Ada.Numerics.Float_Random;
with Ada.Numerics.Discrete_Random;
procedure Probability is
subtype My_Characters is
Character range 'a' .. 'z';
package My_Random_Character_Package is
new Ada.Numerics.Discrete_Random(My_Characters);
use My_Random_Character_Package;
Gen_1 : My_Random_Character_Package.Generator;
procedure My_Character_Program(First_Character, Second_Character : in Character) is
begin
Put(First_Character);
Put(" ");
Put(Second_Character);
New_Line;
Put("All characters between ");
Put(First_Character);
Put(" and ");
Put(Second_Character);
Put(":");
for C in Character range First_Character .. Second_Character loop
Put(" ");
Put(A);
end loop;
end My_Character_Program;
procedure Part_2 is
First_Character, Second_Character : Character;
begin
Put_Line("PART 2:");
Put_Line("Two random characters from a to z will now be generated.");
Put("Random characters: ");
First_Character := Random(Gen_1);
Second_Character := Random(Gen_1);
if First_Character > Second_Character then
My_Character_Program(Second_Character, First_Character);
else
My_Character_Program(First_Character, Second_Character);
end if;
end Part_2;
begin
-- Reset(Gen_1);
Part_2;
New_Line(2);
end Probability;
The following program will pick two random letters between A and Z and then it will type out all the letters thats between them. So for instance if it randomly generates the letters
d and g
it will type out
d e f g
But I need your asstistance. Even if my program works I don't think its executed that well, especially the part 2. Is there not a way where I can put a part of my "Part 2 procedure" in my "My character program procedure"? As you can see I swaped the first and second character. Can I not do that in another way?
I appreciate any help that I could get.
Look at your 'toolbox' -- Ada provides exactly what you want, in the form of subtype and the Succ/Pred attributes, which you already used.
procedure My_Character_Program(First, Second : in Character) is
Subtype Middle is Character range
Character'Succ(First)..Character'Pred(Second);
begin
Put_Line( First & ' ' & Second );
Put("All characters between " & First & " and " & Second & ':');
for C in Middle loop
Put(' ' & C);
end loop;
Put_Line( "." );
end My_Character_Program;
As for Part_2, allow me to suggest that a much better method is to use inline declare-blocks and attributes/renaming.
--...
First_Character := Random(Gen_1);
Second_Character := Random(Gen_1);
Declare
First : Character renames Character'Min(First_Character,Second_Character);
Second : Character renames Character'Max(First_Character,Second_Character);
Begin
My_Character_Program(First, Second);
End;
I’d be inclined to try a bit of recursion (and reduce the number of calls to Put - sorry, style issue, not your question I know):
procedure My_Character_Program
(First_Character, Second_Character : in My_Characters)
is
begin
if First_Character > Second_Character then
My_Character_Program (Second_Character, First_Character);
else
begin
Put_Line (First_Character & " " & Second_Character);
Put("All characters between "
& First_Character
& " and "
& Second_Character
& ":");
for C in My_Characters range First_Character .. Second_Character
loop
Put(" " & C);
end loop;
New_Line;
end;
end if;
end My_Character_Program;
An alternate approach is:
with Ada.Text_IO; use Ada.Text_IO;
with Ada.Numerics.Discrete_Random;
procedure Main is
subtype letters is Character range 'a' .. 'z';
package rand_letter is new Ada.Numerics.Discrete_Random (letters);
use rand_letter;
seed : Generator;
procedure print_range (First : letters; Last : letters) is
begin
for C in Character'Min (First, Last) .. Character'Max (First, Last) loop
Put (C);
end loop;
New_Line;
end print_range;
begin
Reset (seed);
for I in 1 .. 7 loop
print_range (Random (seed), Random (seed));
end loop;
end Main;
Related
im a coding newbie, currently using freepascal to learn. i want to make a simple temperature converter. im struggling at how to validate the input. because im using "real" as var, and i cant validate the input to numbers and negative sign only. i want to write "false input" and repeat to temperature input, if the input contains alphabet/alphanumeric. but when i input the temperature in alphabet/alphanumeric it will just exiting from program. how to do this correctly? is there any solution?
input validation with "real" as the variable
here's my current code
program Fahrenheit_to_Celsius;
uses crt;
function strtofloat (floatstring : string) : extended;
var F: string;
floatvalue: extended;
C: real;
exit: boolean;
decision: shortstring;
begin
clrscr;
textattr :=$3;
gotoxy (52,1);
writeln ('Fahrenheit to Celsius Converter');
repeat
write ('Input Fahrenheit: ');
readln (F);
begin
try
F := strtofloat ('10 E 2');
except
on exception : econverterror do
showmessage (exception.message);
end;
try
F := strtofloat ('$FF');
except
on exception : econverterror do
showmessage (exception.message);
end;
C := ((F-32)*(5/9));
writeln ('Temperature in Celsius: ', C:0:5);
begin
writeln;
gotoxy ((wherex () + 56), (wherey() - 0));
writeln ('Convert again or exit?');
gotoxy ((wherex () + 31), (wherey() - 0));
writeln ('Input e then press enter to exit or input anything else to convert again');
write ('Decision:');
readln (decision);
if (decision = 'e') then
exit := true
else
exit := false;
gotoxy ((wherex () + 0), (wherey() - 3));
delline;
gotoxy ((wherex () + 0), (wherey() - 0));
delline;
end;
until exit;
end.
It's been a long time since I practiced pascal but I will give you an idea, I think you cannot validate it this way because if you declare the input as real once the program recive an non valid input it exits, so what I suggest, is to declare the variable as string instead, then you create a function to validate it, finally if the function returns true you convert the string to float.
The function should loop through every character in the recieved string and returns false if
one character is not a number or not a .
the string contains more then one . ( create a variable and increment it every time you catch a character equal to . )
the first character is .
the last character is .
Let's say I have a package strkern (I do) which exports function: strlen
package strkern is
function strlen(s: string) return natural;
end package strkern;
I write a new package stdstring which defines lots of exciting new operators, but also wants to reexport strlen
use work.strkern.all;
package stdstring is
-- rexport strlen
-- define exciting new operators
end package stdstring
So if one codes
use work.stdstring.all;
You get strlen(), and the exciting new operators.
How does one "re export" a function/type import from a sub package?
Short of having a dummy implementation with a new name that just calls the "to be imported" implementation
function strlen(s: string) return natural is
return strkern.strlen(s);
end function strlen;
By providing a Minimal, Complete and Verifiable example:
package strkern is
function strlen(s: string) return natural;
end package strkern;
package body strkern is
function strlen(s: string) return natural is
begin
return s'length;
end function;
end package body;
use work.strkern.all; -- NOT USED in this foreshortened example
package stdstring is
-- rexport strlen
-- define exciting new operators
alias strlen is work.strkern.strlen [string return natural];
end package stdstring;
use work.stdstring.all;
entity foo is
end entity;
architecture fum of foo is
begin
assert false
report "strlen of ""abcde"" is " & integer'image(strlen("abcde"))
severity NOTE;
end architecture;
we can demonstrate the use of an alias in a package to provide visibility of a declaration of a function found in another package.
ghdl -r foo
std_flub_up.vhdl:25:5:#0ms:(assertion note): strlen of "abcde" is 5
The questions code snippets don't demonstrate any 'exciting new operators' where operator is specific to predefined operators in VHDL. See IEEE Std 1076-2008 9.2 Operators.
The method of using an alias to make name visible is shown in
6.6.3 Nonobject aliases. Note that an alias for a subprogram requires a signature.
Some readers may be curious why the selected name suffix strlen is visible in the above MCVe alias. See 12.3 Visibility:
Visibility is either by selection or direct. A declaration is visible by selection at places that are defined as follows:
a) For a primary unit contained in a library: at the place of the suffix in a selected name whose prefix denotes the library.
...
f) For a declaration given in a package declaration, other than in a package declaration that defines an uninstantiated package: at the place of the suffix in a selected name whose prefix denotes the package.
...
Basically a selected name (work.strkern.strlen) suffix (strlen) declaration occurs in a separate name space defined by the prefix which will designate a primary unit (package strkern here in library work). The package declaration is made visible by rule a. The suffix is made visible by rule f. The library work is made visible by an implicit library declaration (see 13.2 Design libraries).
You cannot re-export anything from one package in another. The only option is to use both the strkern package and the stdstring package.
use work.strkern.all;
use work.stdstring.all;
I would recommend against creating a dummy implementation, because if a user were to include both packages, the duplicate function signatures will cause both to be invisible as the compiler will not know which one to use. The user will have to be explicit about which function to use:
work.strkern.strlen("Hello world");
work.stdstring.strlen("Hello world");
You can create aliases to the functions in the first package, but they cannot have the same name as the original:
alias str_len is strlen[string];
Maybe you want to investigate context clauses from VHDL2008. These allow you use several libraries and packages in a single context, and can be included elsewhere to include all the clauses in the context. The only issue here is that you are not allowed to use work, because work only means the "current working library". If you were to include a context within a another library, the work reference is now incorrect.
context my_string_packages is
library my_string_lib;
use my_string_lib.strkern.all;
use my_string_lib.stdstring.all;
end context my_string_packages;
library my_string_lib;
context my_string_lib.my_string_packages;
Package strdemo is aliasing strlen, strncpy
package strdemo is
alias strlen is work.strkern.strlen[string return natural];
alias strncpy is work.strkern.strncpy [string, string, integer, natural, natural, boolean];
function "+"(a:string; b:string) return string;
function "-"(a:string; b:string) return boolean;
function "*"(a:integer; b:character) return string;
function "*"(a:boolean; b:string) return string;
end package strdemo;
Here's the full listing of strkern
package strkern is
constant STRING_EOF : character := nul; -- \0 string termination from c
-- We can't assign strings but can easily assign string_segments to variables
-- Making a record instead of array because a record has no operators defined for it.
-- An array already has the & operator.
type string_segment is record
start, stop: natural;
end record string_segment;
-- We have 3 kinds of string: string, cstring, vstring. All export functions return vstring
-- There is no functionality difference between the 3, just implementation.
-- All functions accept string and return vstring. They can accept any string type but will
-- always return a vstring.
subtype cstring is string; -- null terminated c strlen <= 'length 'left = 1
subtype vstring is string; -- vhdl string strlen = 'length 'left >= 1
function strlen(s: string) return natural;
function safeseg(s: string; ss: string_segment := (0, 0); doreport: boolean := false) return vstring;
procedure strncpy(dest: out cstring; src: string; n: integer := -2; at: natural := 1; from: natural := 1; doreport: boolean := false);
end package strkern;
package body strkern is
-- Error messages
constant safeseg_left: string := "right operand: ";
constant safeseg_right: string := " overflows strlen: ";
-- Borrow integer whenelse() from stdlib
function whenelse(a: boolean; b, c: integer) return integer is begin
if a then return b; else return c;
end if;
end function whenelse;
function ii(i: integer) return vstring is begin
return integer'image(i);
end function ii;
-- These 4 are the only functions/procedures that use & = or segment/index strings
-- strlen is fundamental. It gives the correct answer on any type of string
function strlen(s: string) return natural is
variable i: natural := s'left;
begin
while i < s'right and s(i) /= STRING_EOF loop
i := i+1;
end loop;
if s'length = 0 then
return 0;
elsif s(i) = STRING_EOF then
return i-s'left;
else
return i+1-s'left;
end if;
end function strlen;
-- safely segment a string. if a=0 convert a cstring to vhdl string
-- otherwise use strlen and 'left to return correct segment string.
function safeseg(s: string; ss: string_segment := (0, 0); doreport: boolean := false) return vstring is
constant len: natural := strlen(s); -- strlen is an expensive function with long strings
-- This is the reason for stdstring. Debug reports get very verbose.
impure function dump return vstring is begin
return " safeseg(s, (" & ii(ss.start) & ", " & ii(ss.stop) & ")) s'left=" & ii(s'left) & " strlen(s)=" & ii(len);
end function dump;
begin
if doreport then -- debug. invokers can switch on doreport
report dump;
end if;
if ss.start = 0 then -- if ss.start=0 return the entire string as defined by strlen()
if len=0 then -- cannot use whenelse here
return "";
else
return s(s'left to s'left + len-1);
end if;
else
assert ss.stop <= len report safeseg_left & natural'image(ss.stop) & safeseg_right & dump;
return s(s'left + ss.start-1 to s'left + whenelse(ss.stop=0, s'length, ss.stop) -1);
end if;
end function safeseg;
-- The only way to assign strings
-- strncpy(dest, src) is effectively strcpy(dest, src) from C
-- It will non fail assert on overflow followed by an array out of bounds error
procedure strncpy(dest: out cstring; src: string; n: integer := -2; at: natural := 1; from: natural := 1; doreport: boolean := false) is
constant srclen: natural := strlen(src);
constant destspace: integer := dest'length + 1 - at;
variable copylen: integer := srclen + 1 - from;
impure function dump return vstring is begin
return " strncpy(str(" & ii(dest'length) & "), str(" & ii(srclen) & "), " & ii(n) & ", " & ii(at) & ", " & ii(from) & ")";
end function dump;
begin
if doreport then
report dump;
end if;
if n >= 0 and copylen > n then
copylen := n;
end if;
if n = -1 and copylen > destspace then
copylen := destspace;
end if;
assert copylen <= destspace report "overrun" & dump;
if copylen > 0 then
dest(at to at + copylen - 1) := src(from to from + copylen - 1);
end if;
if copylen < destspace then
dest(at + copylen) := STRING_EOF;
end if;
end procedure strncpy;
end package body strkern;
I have a procedure, that in theory, should be skipping whitespace using a look_ahead loop. Problem is, it's not working, if there's any whitespace in the input file, it is adding it to the array of records. I think my logic is correct, but could use another pair of eyes to let me know what I'm missing, and why it's not working.
PROCEDURE Read(Calc: OUT Calculation) IS
EOL: Boolean;
C: Character;
I: Integer := 1;
BEGIN
LOOP
LOOP
Look_Ahead(C, EOL);
EXIT WHEN EOL or C /= ' ';
Get(C);
END LOOP;
EXIT WHEN ADA.Text_IO.END_OF_FILE;
Look_Ahead(C, EOL);
IF Is_Digit(C) THEN
Calc.Element(I).Kind := Number;
Get(Calc.Element(I).Int_Value);
ELSE
Calc.Element(I).Kind := Symbol;
Get(Calc.Element(I).Char_Value);
END IF;
Calc.Len := Calc.Len+1;
IF Calc.Element(I).Char_Value = '=' THEN
EXIT;
END IF;
I := I+1;
END LOOP;
END Read;
EDIT: If any of the other procedures, the code for the record etc is needed for an answer, let me know and I will post it.
For Ada.Text_IO.Look_Ahead, ARM A.10.7(8) says
Sets End_Of_Line to True if at end of line, including if at end of page or at end of file; in each of these cases the value of Item is not specified. Otherwise, End_Of_Line is set to False and Item is set to the next character (without consuming it) from the file.
(my emphasis) and I think the "without consuming it" is key. Once Look_Ahead has found an interesting character, you need to call Get to retrieve that character.
I hacked this little demo together: I left end-of-file to exception handling, and I called Skip_Line once end-of-line’s been seen because just Get wasn’t right (sorry not to be more precise!).
with Ada.Text_IO;
with Ada.IO_Exceptions;
procedure Justiciar is
procedure Read is
Eol: Boolean;
C: Character;
begin
-- Instead of useful processing, echo the input to the output
-- replacing spaces with periods.
Outer:
loop
Inner:
loop
Ada.Text_IO.Look_Ahead (C, Eol);
exit Outer when Eol; -- C is undefined
exit Inner when C /= ' ';
Ada.Text_IO.Get (C); -- consume the space
Ada.Text_IO.Put ('.'); -- instead of the space for visibility
end loop Inner;
Ada.Text_IO.Get (C); -- consume the character which isnt a space
Ada.Text_IO.Put (C); -- print it (or other processing!)
end loop Outer;
Ada.Text_IO.Skip_Line; -- consume the newline
Ada.Text_IO.New_Line; -- clear for next call
end Read;
begin
loop
Ada.Text_IO.Put ("reading: ");
Read;
end loop;
exception
when Ada.IO_Exceptions.End_Error =>
null;
end Justiciar;
Usually it's better to read an entire line and parse it than to try to parse character by character. The latter is usually more complex, harder to understand, and more error prone. So I'd suggest something like
function De_Space (Source : String) return String is
Line : Unbounded_String := To_Unbounded_String (Source);
begin -- De_Space
Remove : for I in reverse 1 .. Length (Line) loop
if Element (Line, I) = ' ' then
Delete (Source => Line, From => I, Through => I);
end if;
end loop Remove;
return To_String (Line);
end De_Space;
Line : constant String := De_Space (Get_Line);
You can then loop over Line'range and parse it. Since I'm not clear if
Get(C);
Get(Calc.Element(I).Int_Value);
Get(Calc.Element(I).Char_Value);
represent 1, 2, or 3 different procedures, I can't really help with that part.
I'm trying to write an efficient program to count the number of new line characters from standard input. I wrote the following program:
with Ada.Text_IO;
procedure Main is
New_Lines : Integer := 0;
begin
while not Ada.Text_IO.End_Of_File loop
declare
Line : String := Ada.Text_IO.Get_Line;
begin
New_Lines := New_Lines + 1;
end;
end loop;
Ada.Text_IO.Put_Line (Integer'Image(New_Lines));
end Main;
How can this be made more efficient? I'm noticing that the compiler warns about not using Line. Maybe there's a way of specifying that I'm just interested in skipping to the new line character?
You could use Ada.Text_IO.Skip_Line instead, to avoid storing the lines on the stack, and getting rid of the warning about Line you mention.
A mofified version of your program:
with Ada.Text_IO;
procedure Main is
New_Lines : Integer := 0;
begin
while not Ada.Text_IO.End_Of_File loop
Ada.Text_IO.Skip_Line;
New_Lines := New_Lines + 1;
end loop;
Ada.Text_IO.Put_Line (Integer'Image(New_Lines));
end Main;
This is not guaranteed to count the last line, though, unless the file terminator is directly preceeded by a line terminator. (Although it seems at least GNAT will count it)
Be aware that on some platforms, the line terminator is not just a new line character, on windows, for example, it is CR+LF.
I'm currently writing a test bench for a VHDL design I made and I need to write a message to a text file. The message is of the format
[instance_name];[simulation_time]
(i.e. U0;700 ns) and the filename must be [instance_name].log. Getting the instance name and simulation time is no problem, but writing to a custom filename has been problematic. Under simulation, the instance name will be given in the format:
"U0\ComponentX\test\"
and I would like to replace the slashes with underscores. Is there an easy way to do this?
Our PoC Library has quite a big collection on string operations/functions. There is a str_replace function in PoC.strings that should solve your question. There is also the PoC.utils package with non string related functions, that could also be helpful in handling strings and file I/O.
A simple implementation:
function replace(str : STRING) return STRING
variable Result : STRING(str'range) := str;
begin
for i in str'range loop
if (Result(i) = '\') then
Result(i) := '_';
end if;
loop;
return Result;
end function;
Usage:
constant original : STRING := "U0\ComponentX\test\";
constant replaced : STRING := replace(original);
Simple replace character function that is a bit more versatile and does the same job would be (nothing wrong with #Paebbels's answer)
function fReplaceChar(
a : character;
x : character;
s : string) return string
is
variable ret : string(s'range) := s;
begin
for i in ret'range loop
if(ret(i) = a) then
ret(i) := x;
end if;
end loop;
return ret;
end function fReplaceChar;
If there are more than one character to replace, one can always stack the function:
function fReplaceChar(
a : character;
b : character;
x : character;
s : string) return string
is
begin
return fReplaceChar(b, x, fReplaceChar(a, x, s));
end function fReplaceChar;
or function call:
fReplaceChar(')','_',fReplaceChar(':','(','_',tb'instance_name));
So for example:
process
begin
report lf & tb'instance_name & lf &
fReplaceChar(')','_',fReplaceChar(':','(','_',tb'instance_name));
wait;
end process;
gives:
# ** Note:
# :tb(sim):
# _tb_sim__