------------------------------------------------------------------------------ -- -- -- TGen -- -- -- -- Copyright (C) 2023, AdaCore -- -- -- -- TGen is free software; you can redistribute it and/or modify it under -- -- under terms of the GNU General Public License as published by the -- -- Free Software Foundation; either version 3, or (at your option) any -- -- later version. This software is distributed in the hope that it will be -- -- useful but WITHOUT 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 -- -- . -- ------------------------------------------------------------------------------ with Ada.Strings; use Ada.Strings; with Ada.Strings.Unbounded; use Ada.Strings.Unbounded; with Ada.Strings.Maps; use Ada.Strings.Maps; package body TGen.JSON.Unparse is procedure Remove_Trailing_Comma_And_Spaces (Text : in out Unbounded_String); function Unparse_Array (Sizes : JSON_Array; Val : JSON_Array) return Unbounded_String; function Unparse_Unconstrained_Array (Val : JSON_Value) return JSON_Value; function Unparse_Constrained_Array (Val : JSON_Value) return JSON_Value; function Unparse_Record (Val : JSON_Value) return Unbounded_String; function Unparse_Non_Discriminated_Record (Val : JSON_Value) return JSON_Value; function Unparse_Discriminated_Record (Val : JSON_Value) return JSON_Value; function Unparse_Quotient (Val : JSON_Value) return JSON_Value; -------------------------------------- -- Remove_Trailing_Comma_And_Spaces -- -------------------------------------- procedure Remove_Trailing_Comma_And_Spaces (Text : in out Unbounded_String) is begin Trim (Text, Right); Trim (Text, Null_Set, To_Set (',')); end Remove_Trailing_Comma_And_Spaces; ------------------- -- Unparse_Array -- ------------------- function Unparse_Array (Sizes : JSON_Array; Val : JSON_Array) return Unbounded_String is type Nat_Array is array (Natural range <>) of Natural; function Pp_Arr (Current_Index : in out Positive; Sizes : Nat_Array) return Unbounded_String; -- Unflatten the generated array ------------ -- Pp_Arr -- ------------ function Pp_Arr (Current_Index : in out Positive; Sizes : Nat_Array) return Unbounded_String is Unparsed_Value : Unbounded_String; Current_Arr_Size : constant Natural := Sizes (Sizes'First); begin Append (Unparsed_Value, "("); -- Special cases for array of size 0 and array of size 1 if Current_Arr_Size = 0 then -- Print an empty aggregate Append (Unparsed_Value, "others => <>"); elsif Current_Arr_Size = 1 then -- Print an aggregate with the others keyword as we can't -- have an array declared e.g. (1) but (others => 1), or -- ( => 1) is allowed. As we don't have the index -- here, pick the former. Append (Unparsed_Value, "others => "); end if; for I in 1 .. Current_Arr_Size loop -- We have reached the last index type: unparse the component -- value if Sizes'Length = 1 then -- An array component can't be of an unconstrained type. Append (Unparsed_Value, UTF8_String' (Unparse (Get (Val, Current_Index)).Get ("value"))); Current_Index := @ + 1; else -- Otherwise, generate the nested array recursively Append (Unparsed_Value, Pp_Arr (Current_Index, Sizes (Sizes'First + 1 .. Sizes'Last))); end if; Append (Unparsed_Value, ", "); end loop; Trim (Unparsed_Value, Right); Trim (Unparsed_Value, Null_Set, To_Set (',')); Append (Unparsed_Value, ")"); return Unparsed_Value; end Pp_Arr; Sizes_Arr : Nat_Array (1 .. Length (Sizes)); Dummy_Index : Positive := 1; begin -- Start by getting all of the sizes for I in Sizes_Arr'Range loop Sizes_Arr (I) := Natural'Value (Get (Array_Element (Sizes, I))); end loop; return Pp_Arr (Dummy_Index, Sizes_Arr); end Unparse_Array; --------------------------------- -- Unparse_Unconstrained_Array -- --------------------------------- function Unparse_Unconstrained_Array (Val : JSON_Value) return JSON_Value is Result : constant JSON_Value := Create_Object; Constraints : Unbounded_String; Dimensions : constant JSON_Array := Val.Get ("dimensions"); begin for Dimension of Dimensions loop Append (Constraints, "("); Append (Constraints, UTF8_String'(Dimension.Get ("First"))); Append (Constraints, " .. "); Append (Constraints, UTF8_String'(Dimension.Get ("Last"))); Append (Constraints, ") "); end loop; Trim (Constraints, Left); Set_Field (Result, "constraints", Constraints); Set_Field (Result, "value", Unparse_Array (Val.Get ("sizes"), Val.Get ("array"))); return Result; end Unparse_Unconstrained_Array; ------------------------------- -- Unparse_Constrained_Array -- ------------------------------- function Unparse_Constrained_Array (Val : JSON_Value) return JSON_Value is Result : constant JSON_Value := Create_Object; begin Set_Field (Result, "value", Unparse_Array (Val.Get ("sizes"), Val.Get ("array"))); return Result; end Unparse_Constrained_Array; -------------------- -- Unparse_Record -- -------------------- function Unparse_Record (Val : JSON_Value) return Unbounded_String is Unparsed_Value : Unbounded_String; procedure Process_Component (Name : UTF8_String; Value : JSON_Value); procedure Process_Component (Name : UTF8_String; Value : JSON_Value) is begin Append (Unparsed_Value, Name); Append (Unparsed_Value, " => "); -- A record component can't be of an unconstrained type Append (Unparsed_Value, UTF8_String'(Get (Unparse (Value), "value"))); Append (Unparsed_Value, ", "); end Process_Component; begin Append (Unparsed_Value, "("); Map_JSON_Object (Val, Process_Component'Access); -- If this is a null record, explicitly generate an empty aggregate if Length (Unparsed_Value) = 1 then Append (Unparsed_Value, "others => <>"); else -- Otherwise, we have to trim the resulting value Remove_Trailing_Comma_And_Spaces (Unparsed_Value); end if; Append (Unparsed_Value, ")"); return Unparsed_Value; end Unparse_Record; -------------------------------------- -- Unparse_Non_Discriminated_Record -- -------------------------------------- function Unparse_Non_Discriminated_Record (Val : JSON_Value) return JSON_Value is Result : constant JSON_Value := Create_Object; begin Set_Field (Result, "value", Unparse_Record (Get (Val, "components"))); return Result; end Unparse_Non_Discriminated_Record; ---------------------------------- -- Unparse_Discriminated_Record -- ---------------------------------- function Unparse_Discriminated_Record (Val : JSON_Value) return JSON_Value is Result : constant JSON_Value := Create_Object; Constraints : Unbounded_String; Components : constant Unbounded_String := Unparse_Record (Get (Val, "components")); procedure Process_Discr (Name : UTF8_String; Value : JSON_Value); ------------------- -- Process_Discr -- ------------------- procedure Process_Discr (Name : UTF8_String; Value : JSON_Value) is begin Append (Constraints, Name); Append (Constraints, " => "); -- A record discriminant can't be of an unconstrained type Append (Constraints, UTF8_String'(Unparse (Value).Get ("value"))); Append (Constraints, ", "); end Process_Discr; begin -- Deal with the discriminants Append (Constraints, "("); Map_JSON_Object (Get (Val, "discriminants"), Process_Discr'Access); Remove_Trailing_Comma_And_Spaces (Constraints); Append (Constraints, ")"); Set_Field (Result, "constraints", Constraints); -- Also add the discriminant values to the aggregate expression, as it -- is required to put the discriminant value in a discriminated record -- aggregate expression. Set_Field (Result, "value", Slice (Constraints, 1, Length (Constraints) - 1) & ", " & Slice (Components, 2, Length (Components))); return Result; end Unparse_Discriminated_Record; ---------------------- -- Unparse_Quotient -- ---------------------- function Unparse_Quotient (Val : JSON_Value) return JSON_Value is Result : constant JSON_Value := Create_Object; Quotient_String : constant Unbounded_String := Get (Val, "value"); Div_Index : constant Natural := Index (Quotient_String, To_Set ('/')); begin -- A quotient string is e.g. "2 / 4" Set_Field (Result, "value", Slice (Quotient_String, 1, Div_Index - 2) & ".0 " & Slice (Quotient_String, Div_Index, Length (Quotient_String)) & ".0"); return Result; end Unparse_Quotient; ------------- -- Unparse -- ------------- function Unparse (Val : JSON_Value) return JSON_Value is Result : constant JSON_Value := Create_Object; Val_Str : constant String := Val.Write; pragma Unreferenced (Val_Str); begin -- Dispatch on the right unparsing function case Kind (Val) is when JSON_Object_Type => if Has_Field (Val, "discriminants") then -- Discriminated record case return Unparse_Discriminated_Record (Val); elsif Has_Field (Val, "components") then -- Non discriminated record case return Unparse_Non_Discriminated_Record (Val); elsif Has_Field (Val, "dimensions") then -- Unconstrained array case return Unparse_Unconstrained_Array (Val); elsif Has_Field (Val, "array") then -- Constrained array case return Unparse_Constrained_Array (Val); elsif Has_Field (Val, "quotient") then -- Unparse floating point / fixed point value stored in a -- quotient string. return Unparse_Quotient (Val); else -- Defensive code raise Program_Error with "Unknown value representation"; end if; when others => Set_Field (Result, "value", Val); return Result; end case; end Unparse; end TGen.JSON.Unparse;