------------------------------------------------------------------------------
--                                                                          --
--                         GNAT COMPILER COMPONENTS                         --
--                                                                          --
--                G N A T . F O R M A T T E D _ S T R I N G                 --
--                                                                          --
--                                 S p e c                                  --
--                                                                          --
--           Copyright (C) 2014-2025, Free Software Foundation, Inc.        --
--                                                                          --
-- GNAT is free software;  you can  redistribute it  and/or modify it under --
-- terms of the  GNU General Public License as published  by the Free Soft- --
-- ware  Foundation;  either version 3,  or (at your option) any later ver- --
-- sion.  GNAT is distributed in the hope that it will be useful, but WITH- --
-- OUT ANY WARRANTY;  without even the  implied warranty of MERCHANTABILITY --
-- or FITNESS FOR A PARTICULAR PURPOSE.                                     --
--                                                                          --
-- As a special exception under Section 7 of GPL version 3, you are granted --
-- additional permissions described in the GCC Runtime Library Exception,   --
-- version 3.1, as published by the Free Software Foundation.               --
--                                                                          --
-- You should have received a copy of the GNU General Public License and    --
-- a copy of the GCC Runtime Library Exception along with this program;     --
-- see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see    --
-- <http://www.gnu.org/licenses/>.                                          --
--                                                                          --
-- GNAT was originally developed  by the GNAT team at  New York University. --
-- Extensive contributions were provided by Ada Core Technologies Inc.      --
--                                                                          --
------------------------------------------------------------------------------

--  This package add support for formatted string as supported by C printf()

--  A simple usage is:
--
--     Put_Line (-(+"%s" & "a string"));
--
--  or with a constant for the format:
--
--     declare
--       Format : constant Formatted_String := +"%s";
--     begin
--       Put_Line (-(Format & "a string"));
--     end;
--
--  Finally a more complex example:
--
--     declare
--        F : Formatted_String := +"['%c' ; %10d]";
--        C : Character := 'v';
--        I : Integer := 98;
--     begin
--        F := F & C & I;
--        Put_Line (-F);
--     end;

--  Which will display:

--     ['v' ;         98]

--  Each format specifier is: %[flags][width][.precision][length]specifier

--  Specifiers:
--    d or i    Signed decimal integer
--    u         Unsigned decimal integer
--    o         Unsigned octal
--    x         Unsigned hexadecimal integer
--    X         Unsigned hexadecimal integer (uppercase)
--    f         Decimal floating point, lowercase
--    F         Decimal floating point, uppercase
--    e         Scientific notation (mantissa/exponent), lowercase
--    E         Scientific notation (mantissa/exponent), uppercase
--    g         Interpret the precision as the number of significant figures,
--              choose the most adequate of decimal or scientific notation, and
--              trim trailing zeroes and point. Letters are in lowercase.
--    G         Same as g, except that letters are in uppercase
--    c         Character
--    s         String of characters
--    p         Pointer address
--    %         A % followed by another % character will write a single %

--  Flags:

--    -         Left-justify within the given field width;
--              Right justification is the default.

--    +         Forces to preceed the result with a plus or minus sign (+ or -)
--              even for positive numbers. By default, only negative numbers
--              are preceded with a - sign.

--    (space)   If no sign is going to be written, a blank space is inserted
--              before the value.

--    #         Used with o, x or X specifiers the value is preceeded with
--              0, 0x or 0X respectively for values different than zero.
--              Used with a, A, e, E, f, F, g or G it forces the written
--              output to contain a decimal point even if no more digits
--              follow. By default, if no digits follow, no decimal point is
--              written.

--    ~         As above, but using Ada style based <base>#<number>#

--    0         Left-pads the number with zeroes (0) instead of spaces when
--              padding is specified.

--  Width:
--    number    Minimum number of characters to be printed. If the value to
--              be printed is shorter than this number, the result is padded
--              with blank spaces. The value is not truncated even if the
--              result is larger.

--    *         The width is not specified in the format string, but as an
--              additional integer value argument preceding the argument that
--              has to be formatted.
--  Precision:
--    number    For integer specifiers (d, i, o, u, x, X): precision specifies
--              the minimum number of digits to be written. If the value to be
--              written is shorter than this number, the result is padded with
--              leading zeros. The value is not truncated even if the result
--              is longer. A precision of 0 means that no character is written
--              for the value 0.

--              For e, E, f and F specifiers: this is the number of digits to
--              be printed after the decimal point (by default, this is 6).
--              For g and G specifiers: This is the maximum number of
--              significant digits to be printed.

--              For s: this is the maximum number of characters to be printed.
--              By default all characters are printed until the ending null
--              character is encountered.

--              If the period is specified without an explicit value for
--              precision, 0 is assumed.

--    .*        The precision is not specified in the format string, but as an
--              additional integer value argument preceding the argument that
--              has to be formatted.

with Ada.Text_IO;
with System;

private with Ada.Finalization;
private with Ada.Strings.Unbounded;

package GNAT.Formatted_String is
   use Ada;

   type Formatted_String (<>) is private;
   --  A format string as defined for printf routine. This string is the
   --  actual format for all the parameters added with the "&" routines below.
   --  Note that a Formatted_String object can't be reused as it serves as
   --  recipient for the final result. That is, each use of "&" will build
   --  incrementally the final result string which can be retrieved with
   --  the "-" routine below.

   Format_Error : exception;
   --  Raised for every mismatch between the parameter and the expected format
   --  and for malformed format.

   function "+" (Format : String) return Formatted_String;
   --  Create the format string

   function "-" (Format : Formatted_String) return String;
   --  Get the result of the formatted string corresponding to the current
   --  rendering (up to the last parameter formated).

   function "&"
     (Format : Formatted_String;
      Var    : Character) return Formatted_String;
   --  A character, expect a %c

   function "&"
     (Format : Formatted_String;
      Var    : String) return Formatted_String;
   --  A string, expect a %s

   function "&"
     (Format : Formatted_String;
      Var    : Boolean) return Formatted_String;
   --  A boolean image, expect a %s

   function "&"
     (Format : Formatted_String;
      Var    : Integer) return Formatted_String;
   --  An integer, expect a %d, %o, %x, %X

   function "&"
     (Format : Formatted_String;
      Var    : Long_Integer) return Formatted_String;
   --  As above

   function "&"
     (Format : Formatted_String;
      Var    : System.Address) return Formatted_String;
   --  An address, expect a %p

   function "&"
     (Format : Formatted_String;
      Var    : Float) return Formatted_String;
   --  A float, expect %f, %e, %F, %E, %g, %G

   function "&"
     (Format : Formatted_String;
      Var    : Long_Float) return Formatted_String;
   --  As above

   function "&"
     (Format : Formatted_String;
      Var    : Duration) return Formatted_String;
   --  As above

   --  Some generics

   generic
      type Int is range <>;

      with procedure Put
        (To   : out String;
         Item : Int;
         Base : Text_IO.Number_Base);
   function Int_Format
     (Format : Formatted_String;
      Var    : Int) return Formatted_String;
   --  As for Integer above

   generic
      type Int is mod <>;

      with procedure Put
        (To   : out String;
         Item : Int;
         Base : Text_IO.Number_Base);
   function Mod_Format
     (Format : Formatted_String;
      Var    : Int) return Formatted_String;
   --  As for Integer above

   generic
      type Flt is digits <>;

      with procedure Put
        (To   : out String;
         Item : Flt;
         Aft  : Text_IO.Field;
         Exp  : Text_IO.Field);
   function Flt_Format
     (Format : Formatted_String;
      Var    : Flt) return Formatted_String;
   --  As for Float above

   generic
      type Flt is delta <>;

      with procedure Put
        (To   : out String;
         Item : Flt;
         Aft  : Text_IO.Field;
         Exp  : Text_IO.Field);
   function Fixed_Format
     (Format : Formatted_String;
      Var    : Flt) return Formatted_String;
   --  As for Float above

   generic
      type Flt is delta <> digits <>;

      with procedure Put
        (To   : out String;
         Item : Flt;
         Aft  : Text_IO.Field;
         Exp  : Text_IO.Field);
   function Decimal_Format
     (Format : Formatted_String;
      Var    : Flt) return Formatted_String;
   --  As for Float above

   generic
      type Enum is (<>);
   function Enum_Format
     (Format : Formatted_String;
      Var    : Enum) return Formatted_String;
   --  As for String above, output the string representation of the enumeration

private
   use Ada.Strings.Unbounded;

   type I_Vars is array (Positive range 1 .. 2) of Integer;
   --  Used to keep 2 numbers for the possible * for the width and precision

   type Data (Size : Natural) is record
      Ref_Count    : Natural := 1;
      Index        : Positive := 1;      -- format index for next value
      Result       : Unbounded_String;   -- current value
      Current      : Natural;            -- the current format number
      Stored_Value : Natural := 0;       -- number of stored values in Stack
      Stack        : I_Vars;
      Format       : String (1 .. Size); -- the format string
   end record;

   type Data_Access is access Data;

   --  The formatted string record is controlled and do not need an initialize
   --  as it requires an explit initial value. This is given with "+" and
   --  properly initialize the record at this point.

   type Formatted_String is new Finalization.Controlled with record
      D : Data_Access;
   end record;

   overriding procedure Adjust   (F : in out Formatted_String);
   overriding procedure Finalize (F : in out Formatted_String);

end GNAT.Formatted_String;
