File : reverse_line_tokens.adb
------------------------------------------------------------------------------
-- Demonstration code to read a line, split it into tokens, and reverse them.
------------------------------------------------------------------------------
-- This version is the Ada way of programming but likely fail to process huge
-- amounts of data due to stack overflow.
------------------------------------------------------------------------------
with Ada.Text_IO; use Ada.Text_IO;
with Ada.Strings.Maps, Ada.Strings.Fixed; use Ada.Strings;
procedure reverse_line_tokens is
type Token is record
first, last : Positive; -- Slice marker of an external string
end record;
-- The list of all tokens is an simple array of all tokens.
-- It's not possible to hold an array of Strings, because Strings are
-- unconstrained.
type Tokens is array (Positive range <>) of Token;
-- The empty array required for recursion termination.
-- Even the array does not contain any elements, Ada requires a dummy
-- definition structure.
no_tokens : constant Tokens (1 .. 0) := (others => (1, 1));
-- Read the whole line from standard input into a single string.
-- Note, that the String size is returned, too! You do not need
-- to guess how large the array might be.
function get_whole_line return String;
-- Split a string into a array of tokens. Note, that the array size is
-- returned, too! You do not need to guess how large the array might be.
function split (line : String) return Tokens;
-- Implementation of get_whole_line.
-- A chunk of Characters is read at once from the current line.
-- Get_Line uses two out parameters, but all constrained parameters are
-- always "in" parameter for there own constraints. So the Get_Line
-- procedure learns about the size of the chunk. Because the constraints
-- als always "in", they can't be changed. Get_Line returns the really
-- filled amount as a second out parameter, and fills the chunk partially.
-- If the line continues, the function is called recursivly to get the
-- rest of the line and than concatenate both results.
function get_whole_line return String is
buff : String (1 .. 100);
last : Natural;
begin
Get_Line (buff, last);
if last < buff'Last then
return buff (buff'First .. last);
else
return buff & get_whole_line;
end if;
end get_whole_line;
-- Implementation of split.
-- Scans the string for tokens using Ada.Strings.Fixed.Find_Token.
-- The out parameter "last" is used a the iteration variable.
-- If a token was found, collect the rest by a recursive call and
-- combine those results together.
function split (line : String) return Tokens is
first : Positive;
last : Natural;
begin
Fixed.Find_Token (
Source => line,
Set => Maps.To_Set (" " & ASCII.HT & ASCII.LF),
Test => Outside,
First => first,
Last => last
);
if last = 0 then
return no_tokens;
else
return Token'(first, last) & split (line (last + 1 .. line'Last));
end if;
end split;
-- Start real work here.
begin
loop
declare
-- Read in the whole line into a variable which should be immutable.
-- The required size (constraint) comes from the defining value.
line : constant String := get_whole_line;
-- Split the whole text into an array of tokens.
-- The required size (constraint) comes from the defining value.
toks : constant Tokens := split (line);
begin
-- Stop endless loop at predefined input.
exit when line = "quit";
for i in reverse toks'Range loop
-- Output a token slice.
Put (line (toks (i).first .. toks (i).last));
if i /= toks'First then -- prevent a trailing one.
Put (' ');
end if;
end loop;
New_Line;
end;
end loop;
-- Graceful exit.
Put_Line ("Sie haben ein einfaches Programm sehr glücklich gemacht.");
exception
-- Ungraceful exit, EOF detected.
when End_Error =>
Put_Line ("Sie haben ein einfaches Programm sehr unglücklich gemacht.");
end reverse_line_tokens;