Skip to content

Commit

Permalink
Merge branch 'topic/vadim/integer-formatting' into 'master'
Browse files Browse the repository at this point in the history
Implement formatting specifiers for integer formatters.

Closes #213

See merge request eng/ide/VSS!284
  • Loading branch information
godunko committed Sep 8, 2023
2 parents 564e99b + a16ea63 commit 76e8614
Show file tree
Hide file tree
Showing 7 changed files with 569 additions and 53 deletions.
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ check_text:
.objs/tests/test_string_split
.objs/tests/test_string_split_lines
.objs/tests/test_string
.objs/tests/test_string_template
.objs/tests/test_string_vector
for f in testsuite/text/w3c-i18n-tests-casing/*.txt; do \
echo " $$f"; .objs/tests/test_string_casing_w3c_i18n $$f || return 1; \
Expand Down
1 change: 1 addition & 0 deletions gnat/tests/vss_text_tests.gpr
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ project VSS_Text_Tests is
"test_string_slice",
"test_string_split",
"test_string_split_lines",
"test_string_template",
"test_string_vector",
"test_word_iterators");

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ package VSS.Implementation.Character_Codes is
Space : constant := 16#00_0020#; -- ' '

Quotation_Mark : constant := 16#00_0022#; -- '"'

Number_Sign : constant := 16#00_0023#; -- '#'
Dollar_Sign : constant := 16#00_0024#; -- '$'

Apostrophe : constant := 16#00_0027#; -- '''
Expand Down
292 changes: 285 additions & 7 deletions source/text/implementation/vss-strings-formatters-generic_integers.adb
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,37 @@
-- SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
--

with Interfaces;

with VSS.Characters.Latin;
with VSS.Implementation.Character_Codes;
with VSS.Implementation.String_Handlers;
with VSS.Unicode;

package body VSS.Strings.Formatters.Generic_Integers is

type Sign_Options is
(Compact,
-- Don't preserve space for sign, but start negative values with
-- hyphen-minus.
Space_Or_Minus,
-- Preserve space for sign, fill it by whitespace for positive values.
Plus_Or_Minus);
-- Preserve space for sign, fill it by plus sign for positive values.

type Formatter_Options is record
Sign : Sign_Options := Compact;
Width : VSS.Strings.Grapheme_Cluster_Count := 0;
Leading_Zeros : Boolean := False;
Base : Natural := 10;
Group : VSS.Strings.Grapheme_Cluster_Count := 0;
Separator : VSS.Characters.Virtual_Character := '_';
end record;

procedure Parse
(Format : VSS.Strings.Virtual_String;
Options : in out Formatter_Options);

------------
-- Format --
------------
Expand All @@ -15,18 +44,134 @@ package body VSS.Strings.Formatters.Generic_Integers is
Format : VSS.Strings.Formatters.Format_Information)
return VSS.Strings.Virtual_String
is
Buffer : constant Wide_Wide_String :=
Integer_Type'Wide_Wide_Image (Self.Value);
use VSS.Implementation.Character_Codes;
use type Interfaces.Unsigned_128;
use type VSS.Unicode.Code_Point_Unit;

Buffer : Wide_Wide_String (1 .. Integer_Type'Size);
First : Positive := Buffer'Last + 1;
Options : Formatter_Options;
Negative : Boolean;
Value : Interfaces.Unsigned_128;
Result : VSS.Strings.Virtual_String;
Digit : VSS.Unicode.Code_Point_Unit;
Length : VSS.Strings.Grapheme_Cluster_Count;

procedure Append_Sign;

-----------------
-- Append_Sign --
-----------------

procedure Append_Sign is
begin
if Negative then
Result.Append (VSS.Characters.Latin.Hyphen_Minus);

else
case Options.Sign is
when Compact =>
null;

when Space_Or_Minus =>
Result.Append (VSS.Characters.Latin.Space);

when Plus_Or_Minus =>
Result.Append (VSS.Characters.Latin.Plus_Sign);
end case;
end if;
end Append_Sign;

begin
if Buffer (Buffer'First) = ' ' then
return
VSS.Strings.To_Virtual_String
(Buffer (Buffer'First + 1 .. Buffer'Last));
Parse (Format.Format, Options);

-- Process sign

if Self.Value < 0 then
declare
pragma Suppress (Overflow_Check);

begin
Negative := True;
Value := Interfaces.Unsigned_128 (-Self.Value);
end;

else
Negative := False;
Value := Interfaces.Unsigned_128 (Self.Value);
end if;

-- Convert positive integer value into the text representation.

if Value = 0 then
First := @ - 1;
Buffer (First) := Wide_Wide_Character'Val (Digit_Zero);
end if;

while Value /= 0 loop
Digit :=
VSS.Unicode.Code_Point_Unit
(Value mod Interfaces.Unsigned_128 (Options.Base));

if Digit in 0 .. 9 then
First := @ - 1;
Buffer (First) := Wide_Wide_Character'Val (Digit + Digit_Zero);

elsif Digit in 10 .. 25 then
First := @ - 1;
Buffer (First) :=
Wide_Wide_Character'Val (Digit - 10 + Latin_Capital_Letter_A);

else
raise Program_Error;
end if;

Value := Value / Interfaces.Unsigned_128 (Options.Base);
end loop;

-- Fill leading zeros/spaces and sign.

Length := VSS.Strings.Grapheme_Cluster_Count (Buffer'Last - First + 1);

if Options.Width = 0 then
Append_Sign;

elsif Options.Leading_Zeros then
Append_Sign;

for J in reverse Length + 1 .. Options.Width loop
Result.Append (VSS.Characters.Latin.Digit_Zero);

if Options.Group /= 0
and then (J - 1) mod Options.Group = 0
then
Result.Append (Options.Separator);
end if;
end loop;

else
return VSS.Strings.To_Virtual_String (Buffer);
for J in reverse Length + 1 .. Options.Width loop
Result.Append (VSS.Characters.Latin.Space);
end loop;

Append_Sign;
end if;

-- Append text representation.

for J in First .. Buffer'Last loop
Result.Append (VSS.Characters.Virtual_Character (Buffer (J)));

if Options.Group /= 0
and then J /= Buffer'Last
and then VSS.Strings.Grapheme_Cluster_Count (Buffer'Last - J)
mod Options.Group = 0
then
Result.Append (Options.Separator);
end if;
end loop;

return Result;
end Format;

-----------
Expand Down Expand Up @@ -59,4 +204,137 @@ package body VSS.Strings.Formatters.Generic_Integers is
return Self.Name;
end Name;

-----------
-- Parse --
-----------

procedure Parse
(Format : VSS.Strings.Virtual_String;
Options : in out Formatter_Options)
is
use VSS.Implementation.Character_Codes;
use type VSS.Unicode.Code_Point_Unit;

type States is
(Initial, Zero_Width_Base_Group, Width, Base, Group, Error);

Handler :
constant VSS.Implementation.Strings.String_Handler_Access :=
VSS.Implementation.Strings.Handler (Format.Data);
Position : VSS.Implementation.Strings.Cursor;
Code : VSS.Unicode.Code_Point'Base;
State : States := Initial;

begin
Handler.Before_First_Character (Format.Data, Position);

while Handler.Forward_Element (Format.Data, Position, Code) loop
case State is
when Initial =>
case Code is
when Plus_Sign =>
State := Zero_Width_Base_Group;
Options.Sign := Plus_Or_Minus;

when Hyphen_Minus =>
State := Zero_Width_Base_Group;
Options.Sign := Space_Or_Minus;

when Digit_Zero =>
State := Width;
Options.Leading_Zeros := True;
Options.Width := 0;

when Digit_One .. Digit_Nine =>
State := Width;
Options.Leading_Zeros := False;
Options.Width :=
VSS.Strings.Grapheme_Cluster_Count (Code - Digit_Zero);

when Number_Sign =>
State := Base;
Options.Base := 0;

when Low_Line =>
State := Group;

when others =>
State := Error;
end case;

when Zero_Width_Base_Group =>
case Code is
when Digit_Zero =>
State := Width;
Options.Leading_Zeros := True;
Options.Width := 0;

when Digit_One .. Digit_Nine =>
State := Width;
Options.Leading_Zeros := False;
Options.Width :=
VSS.Strings.Grapheme_Cluster_Count (Code - Digit_Zero);

when Number_Sign =>
State := Base;
Options.Base := 0;

when Low_Line =>
State := Group;

when others =>
State := Error;
end case;

when Width =>
case Code is
when Digit_Zero .. Digit_Nine =>
Options.Width :=
@ * 10
+ VSS.Strings.Grapheme_Cluster_Count
(Code - Digit_Zero);

when Number_Sign =>
State := Base;
Options.Base := 0;

when Low_Line =>
State := Group;

when others =>
State := Error;
end case;

when Base =>
case Code is
when Digit_Zero .. Digit_Nine =>
Options.Base := @ * 10 + Natural (Code - Digit_Zero);

when Low_Line =>
State := Group;

when others =>
State := Error;
end case;

when Group =>
case Code is
when Digit_Zero .. Digit_Nine =>
Options.Group :=
@ * 10
+ VSS.Strings.Grapheme_Cluster_Count
(Code - Digit_Zero);

when others =>
State := Error;
Options.Separator :=
VSS.Characters.Virtual_Character'Val (Code);
end case;

when Error =>
exit;
end case;
end loop;
end Parse;

end VSS.Strings.Formatters.Generic_Integers;
Loading

0 comments on commit 76e8614

Please sign in to comment.