| ------------------------------------------------------------------------------ |
| -- -- |
| -- GNAT COMPILER COMPONENTS -- |
| -- -- |
| -- R E P I N F O - I N P U T -- |
| -- -- |
| -- B o d y -- |
| -- -- |
| -- Copyright (C) 2018-2022, 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. See the GNU General Public License -- |
| -- for more details. You should have received a copy of the GNU General -- |
| -- Public License distributed with GNAT; see file COPYING3. If not, go to -- |
| -- http://www.gnu.org/licenses for a complete copy of the license. -- |
| -- -- |
| -- GNAT was originally developed by the GNAT team at New York University. -- |
| -- Extensive contributions were provided by Ada Core Technologies Inc. -- |
| -- -- |
| ------------------------------------------------------------------------------ |
| |
| with Alloc; |
| with Csets; use Csets; |
| with Hostparm; use Hostparm; |
| with Namet; use Namet; |
| with Output; use Output; |
| with Snames; use Snames; |
| with Table; |
| with Ttypes; |
| |
| package body Repinfo.Input is |
| |
| SSU : Pos renames Ttypes.System_Storage_Unit; |
| -- Value for Storage_Unit |
| |
| type JSON_Entity_Kind is (JE_Record_Type, JE_Array_Type, JE_Other); |
| -- Kind of an entity |
| |
| type JSON_Entity_Node (Kind : JSON_Entity_Kind := JE_Other) is record |
| Esize : Node_Ref_Or_Val; |
| RM_Size : Node_Ref_Or_Val; |
| case Kind is |
| when JE_Record_Type => Variant : Nat; |
| when JE_Array_Type => Component_Size : Node_Ref_Or_Val; |
| when JE_Other => Dummy : Boolean; |
| end case; |
| end record; |
| pragma Unchecked_Union (JSON_Entity_Node); |
| -- Record to represent an entity |
| |
| package JSON_Entity_Table is new Table.Table ( |
| Table_Component_Type => JSON_Entity_Node, |
| Table_Index_Type => Nat, |
| Table_Low_Bound => 1, |
| Table_Initial => Alloc.Rep_JSON_Table_Initial, |
| Table_Increment => Alloc.Rep_JSON_Table_Increment, |
| Table_Name => "JSON_Entity_Table"); |
| -- Table of entities |
| |
| type JSON_Component_Node is record |
| Bit_Offset : Node_Ref_Or_Val; |
| Esize : Node_Ref_Or_Val; |
| end record; |
| -- Record to represent a component |
| |
| package JSON_Component_Table is new Table.Table ( |
| Table_Component_Type => JSON_Component_Node, |
| Table_Index_Type => Nat, |
| Table_Low_Bound => 1, |
| Table_Initial => Alloc.Rep_JSON_Table_Initial, |
| Table_Increment => Alloc.Rep_JSON_Table_Increment, |
| Table_Name => "JSON_Component_Table"); |
| -- Table of components |
| |
| type JSON_Variant_Node is record |
| Present : Node_Ref_Or_Val; |
| Variant : Nat; |
| Next : Nat; |
| end record; |
| -- Record to represent a variant |
| |
| package JSON_Variant_Table is new Table.Table ( |
| Table_Component_Type => JSON_Variant_Node, |
| Table_Index_Type => Nat, |
| Table_Low_Bound => 1, |
| Table_Initial => Alloc.Rep_JSON_Table_Initial, |
| Table_Increment => Alloc.Rep_JSON_Table_Increment, |
| Table_Name => "JSON_Variant_Table"); |
| -- Table of variants |
| |
| ------------------------------------- |
| -- Get_JSON_Component_Bit_Offset -- |
| ------------------------------------- |
| |
| function Get_JSON_Component_Bit_Offset |
| (Name : String; |
| Record_Name : String) return Node_Ref_Or_Val |
| is |
| Namid : constant Valid_Name_Id := Name_Find (Record_Name & '.' & Name); |
| Index : constant Int := Get_Name_Table_Int (Namid); |
| |
| begin |
| -- Return No_Uint if no information is available for the component |
| |
| if Index = 0 then |
| return No_Uint; |
| end if; |
| |
| return JSON_Component_Table.Table (Index).Bit_Offset; |
| end Get_JSON_Component_Bit_Offset; |
| |
| ------------------------------- |
| -- Get_JSON_Component_Size -- |
| ------------------------------- |
| |
| function Get_JSON_Component_Size (Name : String) return Node_Ref_Or_Val is |
| Namid : constant Valid_Name_Id := Name_Find (Name); |
| Index : constant Int := Get_Name_Table_Int (Namid); |
| |
| begin |
| -- Return No_Uint if no information is available for the component |
| |
| if Index = 0 then |
| return No_Uint; |
| end if; |
| |
| return JSON_Entity_Table.Table (Index).Component_Size; |
| end Get_JSON_Component_Size; |
| |
| ---------------------- |
| -- Get_JSON_Esize -- |
| ---------------------- |
| |
| function Get_JSON_Esize (Name : String) return Node_Ref_Or_Val is |
| Namid : constant Valid_Name_Id := Name_Find (Name); |
| Index : constant Int := Get_Name_Table_Int (Namid); |
| |
| begin |
| -- Return No_Uint if no information is available for the entity |
| |
| if Index = 0 then |
| return No_Uint; |
| end if; |
| |
| return JSON_Entity_Table.Table (Index).Esize; |
| end Get_JSON_Esize; |
| |
| ---------------------- |
| -- Get_JSON_Esize -- |
| ---------------------- |
| |
| function Get_JSON_Esize |
| (Name : String; |
| Record_Name : String) return Node_Ref_Or_Val |
| is |
| Namid : constant Valid_Name_Id := Name_Find (Record_Name & '.' & Name); |
| Index : constant Int := Get_Name_Table_Int (Namid); |
| |
| begin |
| -- Return No_Uint if no information is available for the entity |
| |
| if Index = 0 then |
| return No_Uint; |
| end if; |
| |
| return JSON_Component_Table.Table (Index).Esize; |
| end Get_JSON_Esize; |
| |
| ------------------------ |
| -- Get_JSON_RM_Size -- |
| ------------------------ |
| |
| function Get_JSON_RM_Size (Name : String) return Node_Ref_Or_Val is |
| Namid : constant Valid_Name_Id := Name_Find (Name); |
| Index : constant Int := Get_Name_Table_Int (Namid); |
| |
| begin |
| -- Return No_Uint if no information is available for the entity |
| |
| if Index = 0 then |
| return No_Uint; |
| end if; |
| |
| return JSON_Entity_Table.Table (Index).RM_Size; |
| end Get_JSON_RM_Size; |
| |
| ----------------------- |
| -- Read_JSON_Stream -- |
| ----------------------- |
| |
| procedure Read_JSON_Stream (Text : Text_Buffer; File_Name : String) is |
| |
| type Text_Position is record |
| Index : Text_Ptr := 0; |
| Line : Natural := 0; |
| Column : Natural := 0; |
| end record; |
| -- Record to represent position in the text |
| |
| type Token_Kind is |
| (J_NULL, |
| J_TRUE, |
| J_FALSE, |
| J_NUMBER, |
| J_INTEGER, |
| J_STRING, |
| J_ARRAY, |
| J_OBJECT, |
| J_ARRAY_END, |
| J_OBJECT_END, |
| J_COMMA, |
| J_COLON, |
| J_EOF); |
| -- JSON token kind. Note that in ECMA 404 there is no notion of integer. |
| -- Only numbers are supported. In our implementation we return J_INTEGER |
| -- if there is no decimal part in the number. The semantic is that this |
| -- is a J_NUMBER token that might be represented as an integer. Special |
| -- token J_EOF means that end of stream has been reached. |
| |
| function Decode_Integer (Lo, Hi : Text_Ptr) return Uint; |
| -- Decode and return the integer in Text (Lo .. Hi) |
| |
| function Decode_Name (Lo, Hi : Text_Ptr) return Valid_Name_Id; |
| -- Decode and return the name in Text (Lo .. Hi) |
| |
| function Decode_Symbol (Lo, Hi : Text_Ptr) return TCode; |
| -- Decode and return the expression symbol in Text (Lo .. Hi) |
| |
| procedure Error (Msg : String); |
| pragma No_Return (Error); |
| -- Print an error message and raise an exception |
| |
| procedure Read_Entity; |
| -- Read an entity |
| |
| function Read_Name return Valid_Name_Id; |
| -- Read a name |
| |
| function Read_Name_With_Prefix return Valid_Name_Id; |
| -- Read a name and prepend a prefix |
| |
| function Read_Number return Uint; |
| -- Read a number |
| |
| function Read_Numerical_Expr return Node_Ref_Or_Val; |
| -- Read a numerical expression |
| |
| procedure Read_Record; |
| -- Read a record |
| |
| function Read_String return Valid_Name_Id; |
| -- Read a string |
| |
| procedure Read_Token |
| (Kind : out Token_Kind; |
| Token_Start : out Text_Position; |
| Token_End : out Text_Position); |
| -- Read a token and return it (this is a standard JSON lexer) |
| |
| procedure Read_Token_And_Error |
| (TK : Token_Kind; |
| Token_Start : out Text_Position; |
| Token_End : out Text_Position); |
| pragma Inline (Read_Token_And_Error); |
| -- Read a specified token and error out on failure |
| |
| function Read_Variant_Part return Nat; |
| -- Read a variant part |
| |
| procedure Skip_Value; |
| -- Skip a value |
| |
| Pos : Text_Position := (Text'First, 1, 1); |
| -- The current position in the text buffer |
| |
| Name_Buffer : Bounded_String (4 * Max_Name_Length); |
| -- The buffer used to build full qualifed names |
| |
| Prefix_Len : Natural := 0; |
| -- The length of the prefix present in Name_Buffer |
| |
| ---------------------- |
| -- Decode_Integer -- |
| ---------------------- |
| |
| function Decode_Integer (Lo, Hi : Text_Ptr) return Uint is |
| Len : constant Nat := Int (Hi) - Int (Lo) + 1; |
| |
| begin |
| -- Decode up to 9 characters manually, otherwise call into Uint |
| |
| if Len < 10 then |
| declare |
| Val : Int := 0; |
| |
| begin |
| for J in Lo .. Hi loop |
| Val := Val * 10 |
| + Character'Pos (Text (J)) - Character'Pos ('0'); |
| end loop; |
| return UI_From_Int (Val); |
| end; |
| |
| else |
| declare |
| Val : Uint := Uint_0; |
| |
| begin |
| for J in Lo .. Hi loop |
| Val := Val * 10 |
| + Character'Pos (Text (J)) - Character'Pos ('0'); |
| end loop; |
| return Val; |
| end; |
| end if; |
| end Decode_Integer; |
| |
| ------------------- |
| -- Decode_Name -- |
| ------------------- |
| |
| function Decode_Name (Lo, Hi : Text_Ptr) return Valid_Name_Id is |
| begin |
| -- Names are stored in lower case so fold them if need be |
| |
| if Is_Upper_Case_Letter (Text (Lo)) then |
| declare |
| S : String (Integer (Lo) .. Integer (Hi)); |
| |
| begin |
| for J in Lo .. Hi loop |
| S (Integer (J)) := Fold_Lower (Text (J)); |
| end loop; |
| |
| return Name_Find (S); |
| end; |
| |
| else |
| declare |
| S : String (Integer (Lo) .. Integer (Hi)); |
| for S'Address use Text (Lo)'Address; |
| |
| begin |
| return Name_Find (S); |
| end; |
| end if; |
| end Decode_Name; |
| |
| --------------------- |
| -- Decode_Symbol -- |
| --------------------- |
| |
| function Decode_Symbol (Lo, Hi : Text_Ptr) return TCode is |
| |
| function Cmp12 (A, B : Character) return Boolean; |
| pragma Inline (Cmp12); |
| -- Compare Text (Lo + 1 .. Lo + 2) with A & B. |
| |
| ------------- |
| -- Cmp12 -- |
| ------------- |
| |
| function Cmp12 (A, B : Character) return Boolean is |
| begin |
| return Text (Lo + 1) = A and then Text (Lo + 2) = B; |
| end Cmp12; |
| |
| Len : constant Nat := Int (Hi) - Int (Lo) + 1; |
| |
| -- Start of processing for Decode_Symbol |
| |
| begin |
| case Len is |
| when 1 => |
| case Text (Lo) is |
| when '+' => |
| return Plus_Expr; |
| when '-' => |
| return Minus_Expr; -- or Negate_Expr |
| when '*' => |
| return Mult_Expr; |
| when '<' => |
| return Lt_Expr; |
| when '>' => |
| return Gt_Expr; |
| when '&' => |
| return Bit_And_Expr; |
| when '#' => |
| return Discrim_Val; |
| when others => |
| null; |
| end case; |
| when 2 => |
| if Text (Lo) = '/' then |
| case Text (Lo + 1) is |
| when 't' => |
| return Trunc_Div_Expr; |
| when 'c' => |
| return Ceil_Div_Expr; |
| when 'f' => |
| return Floor_Div_Expr; |
| when 'e' => |
| return Exact_Div_Expr; |
| when others => |
| null; |
| end case; |
| elsif Text (Lo + 1) = '=' then |
| case Text (Lo) is |
| when '<' => |
| return Le_Expr; |
| when '>' => |
| return Ge_Expr; |
| when '=' => |
| return Eq_Expr; |
| when '!' => |
| return Ne_Expr; |
| when others => |
| null; |
| end case; |
| elsif Text (Lo) = 'o' and then Text (Lo + 1) = 'r' then |
| return Truth_Or_Expr; |
| end if; |
| when 3 => |
| case Text (Lo) is |
| when '?' => |
| if Cmp12 ('<', '>') then |
| return Cond_Expr; |
| end if; |
| when 'a' => |
| if Cmp12 ('b', 's') then |
| return Abs_Expr; |
| elsif Cmp12 ('n', 'd') then |
| return Truth_And_Expr; |
| end if; |
| when 'm' => |
| if Cmp12 ('a', 'x') then |
| return Max_Expr; |
| elsif Cmp12 ('i', 'n') then |
| return Min_Expr; |
| end if; |
| when 'n' => |
| if Cmp12 ('o', 't') then |
| return Truth_Not_Expr; |
| end if; |
| when 'x' => |
| if Cmp12 ('o', 'r') then |
| return Truth_Xor_Expr; |
| end if; |
| when 'v' => |
| if Cmp12 ('a', 'r') then |
| return Dynamic_Val; |
| end if; |
| when others => |
| null; |
| end case; |
| when 4 => |
| if Text (Lo) = 'm' |
| and then Text (Lo + 1) = 'o' |
| and then Text (Lo + 2) = 'd' |
| then |
| case Text (Lo + 3) is |
| when 't' => |
| return Trunc_Mod_Expr; |
| when 'c' => |
| return Ceil_Mod_Expr; |
| when 'f' => |
| return Floor_Mod_Expr; |
| when others => |
| null; |
| end case; |
| end if; |
| |
| pragma Annotate |
| (CodePeer, Intentional, |
| "condition predetermined", "Error called as defensive code"); |
| |
| when others => |
| null; |
| end case; |
| |
| Error ("unknown symbol"); |
| end Decode_Symbol; |
| |
| ----------- |
| -- Error -- |
| ----------- |
| |
| procedure Error (Msg : String) is |
| L : constant String := Pos.Line'Img; |
| C : constant String := Pos.Column'Img; |
| |
| begin |
| Set_Standard_Error; |
| Write_Eol; |
| Write_Str (File_Name); |
| Write_Char (':'); |
| Write_Str (L (L'First + 1 .. L'Last)); |
| Write_Char (':'); |
| Write_Str (C (C'First + 1 .. C'Last)); |
| Write_Char (':'); |
| Write_Line (Msg); |
| raise Invalid_JSON_Stream; |
| end Error; |
| |
| ------------------ |
| -- Read_Entity -- |
| ------------------ |
| |
| procedure Read_Entity is |
| Ent : JSON_Entity_Node; |
| Nam : Name_Id := No_Name; |
| Siz : Node_Ref_Or_Val; |
| Token_Start : Text_Position; |
| Token_End : Text_Position; |
| TK : Token_Kind; |
| |
| begin |
| Ent.Esize := No_Uint; |
| Ent.RM_Size := No_Uint; |
| Ent.Component_Size := No_Uint; |
| |
| -- Read the members as string : value pairs |
| |
| loop |
| case Read_String is |
| when Name_Name => |
| Nam := Read_Name; |
| when Name_Record => |
| if Nam = No_Name then |
| Error ("name expected"); |
| end if; |
| Ent.Variant := 0; |
| Prefix_Len := Natural (Length_Of_Name (Nam)); |
| Name_Buffer.Chars (1 .. Prefix_Len) := Get_Name_String (Nam); |
| Read_Record; |
| when Name_Variant => |
| Ent.Variant := Read_Variant_Part; |
| when Name_Size => |
| Siz := Read_Numerical_Expr; |
| Ent.Esize := Siz; |
| Ent.RM_Size := Siz; |
| when Name_Object_Size => |
| Ent.Esize := Read_Numerical_Expr; |
| when Name_Value_Size => |
| Ent.RM_Size := Read_Numerical_Expr; |
| when Name_Component_Size => |
| Ent.Component_Size := Read_Numerical_Expr; |
| when others => |
| Skip_Value; |
| end case; |
| |
| Read_Token (TK, Token_Start, Token_End); |
| if TK = J_OBJECT_END then |
| exit; |
| elsif TK /= J_COMMA then |
| Error ("comma expected"); |
| end if; |
| end loop; |
| |
| -- Store the entity into the table |
| |
| JSON_Entity_Table.Append (Ent); |
| |
| -- Associate the name with the entity |
| |
| if Nam = No_Name then |
| Error ("name expected"); |
| end if; |
| |
| Set_Name_Table_Int (Nam, JSON_Entity_Table.Last); |
| end Read_Entity; |
| |
| ----------------- |
| -- Read_Name -- |
| ----------------- |
| |
| function Read_Name return Valid_Name_Id is |
| Token_Start : Text_Position; |
| Token_End : Text_Position; |
| |
| begin |
| -- Read a single string |
| |
| Read_Token_And_Error (J_STRING, Token_Start, Token_End); |
| |
| return Decode_Name (Token_Start.Index + 1, Token_End.Index - 1); |
| end Read_Name; |
| |
| ----------------------------- |
| -- Read_Name_With_Prefix -- |
| ----------------------------- |
| |
| function Read_Name_With_Prefix return Valid_Name_Id is |
| Len : Natural; |
| Lo, Hi : Text_Ptr; |
| Token_Start : Text_Position; |
| Token_End : Text_Position; |
| |
| begin |
| -- Read a single string |
| |
| Read_Token_And_Error (J_STRING, Token_Start, Token_End); |
| Lo := Token_Start.Index + 1; |
| Hi := Token_End.Index - 1; |
| |
| -- Prepare for the concatenation with the prefix |
| |
| Len := Integer (Hi) - Integer (Lo) + 1; |
| if Prefix_Len + 1 + Len > Name_Buffer.Max_Length then |
| Error ("Name buffer too small"); |
| end if; |
| |
| Name_Buffer.Length := Prefix_Len + 1 + Len; |
| Name_Buffer.Chars (Prefix_Len + 1) := '.'; |
| |
| -- Names are stored in lower case so fold them if need be |
| |
| if Is_Upper_Case_Letter (Text (Lo)) then |
| for J in Lo .. Hi loop |
| Name_Buffer.Chars (Prefix_Len + 2 + Integer (J - Lo)) := |
| Fold_Lower (Text (J)); |
| end loop; |
| |
| else |
| declare |
| S : String (Integer (Lo) .. Integer (Hi)); |
| for S'Address use Text (Lo)'Address; |
| |
| begin |
| Name_Buffer.Chars (Prefix_Len + 2 .. Prefix_Len + 1 + Len) := S; |
| end; |
| end if; |
| |
| return Name_Find (Name_Buffer); |
| end Read_Name_With_Prefix; |
| |
| ------------------ |
| -- Read_Number -- |
| ------------------ |
| |
| function Read_Number return Uint is |
| Token_Start : Text_Position; |
| Token_End : Text_Position; |
| |
| begin |
| -- Only integers are to be expected here |
| |
| Read_Token_And_Error (J_INTEGER, Token_Start, Token_End); |
| |
| return Decode_Integer (Token_Start.Index, Token_End.Index); |
| end Read_Number; |
| |
| -------------------------- |
| -- Read_Numerical_Expr -- |
| -------------------------- |
| |
| function Read_Numerical_Expr return Node_Ref_Or_Val is |
| Code : TCode; |
| Nop : Integer; |
| Ops : array (1 .. 3) of Node_Ref_Or_Val; |
| TK : Token_Kind; |
| Token_Start : Text_Position; |
| Token_End : Text_Position; |
| |
| begin |
| -- Read either an integer or an expression |
| |
| Read_Token (TK, Token_Start, Token_End); |
| if TK = J_INTEGER then |
| return Decode_Integer (Token_Start.Index, Token_End.Index); |
| |
| elsif TK = J_OBJECT then |
| -- Read the code of the expression and decode it |
| |
| if Read_String /= Name_Code then |
| Error ("name expected"); |
| end if; |
| |
| Read_Token_And_Error (J_STRING, Token_Start, Token_End); |
| Code := Decode_Symbol (Token_Start.Index + 1, Token_End.Index - 1); |
| Read_Token_And_Error (J_COMMA, Token_Start, Token_End); |
| |
| -- Read the array of operands |
| |
| if Read_String /= Name_Operands then |
| Error ("operands expected"); |
| end if; |
| |
| Read_Token_And_Error (J_ARRAY, Token_Start, Token_End); |
| |
| Nop := 0; |
| Ops := (others => No_Uint); |
| loop |
| Nop := Nop + 1; |
| Ops (Nop) := Read_Numerical_Expr; |
| Read_Token (TK, Token_Start, Token_End); |
| if TK = J_ARRAY_END then |
| exit; |
| elsif TK /= J_COMMA then |
| Error ("comma expected"); |
| end if; |
| end loop; |
| |
| Read_Token_And_Error (J_OBJECT_END, Token_Start, Token_End); |
| |
| -- Resolve the ambiguity for '-' now |
| |
| if Code = Minus_Expr and then Nop = 1 then |
| Code := Negate_Expr; |
| end if; |
| |
| return Create_Node (Code, Ops (1), Ops (2), Ops (3)); |
| |
| else |
| Error ("numerical expression expected"); |
| end if; |
| end Read_Numerical_Expr; |
| |
| ------------------- |
| -- Read_Record -- |
| ------------------- |
| |
| procedure Read_Record is |
| Comp : JSON_Component_Node; |
| First_Bit : Node_Ref_Or_Val := No_Uint; |
| Is_First : Boolean := True; |
| Nam : Name_Id := No_Name; |
| Position : Node_Ref_Or_Val := No_Uint; |
| TK : Token_Kind; |
| Token_Start : Text_Position; |
| Token_End : Text_Position; |
| |
| begin |
| -- Read a possibly empty array of components |
| |
| Read_Token_And_Error (J_ARRAY, Token_Start, Token_End); |
| |
| loop |
| Read_Token (TK, Token_Start, Token_End); |
| if Is_First and then TK = J_ARRAY_END then |
| exit; |
| elsif TK /= J_OBJECT then |
| Error ("object expected"); |
| end if; |
| |
| -- Read the members as string : value pairs |
| |
| loop |
| case Read_String is |
| when Name_Name => |
| Nam := Read_Name_With_Prefix; |
| when Name_Discriminant => |
| Skip_Value; |
| when Name_Position => |
| Position := Read_Numerical_Expr; |
| when Name_First_Bit => |
| First_Bit := Read_Number; |
| when Name_Size => |
| Comp.Esize := Read_Numerical_Expr; |
| when others => |
| Error ("invalid component"); |
| end case; |
| |
| Read_Token (TK, Token_Start, Token_End); |
| if TK = J_OBJECT_END then |
| exit; |
| elsif TK /= J_COMMA then |
| Error ("comma expected"); |
| end if; |
| end loop; |
| |
| -- Compute Component_Bit_Offset from Position and First_Bit, |
| -- either symbolically or literally depending on Position. |
| |
| if No (Position) or else No (First_Bit) then |
| Error ("bit offset expected"); |
| end if; |
| |
| if Position < Uint_0 then |
| declare |
| Bit_Position : constant Node_Ref_Or_Val := |
| Create_Node (Mult_Expr, Position, UI_From_Int (SSU)); |
| begin |
| if First_Bit = Uint_0 then |
| Comp.Bit_Offset := Bit_Position; |
| else |
| Comp.Bit_Offset := |
| Create_Node (Plus_Expr, Bit_Position, First_Bit); |
| end if; |
| end; |
| else |
| Comp.Bit_Offset := Position * SSU + First_Bit; |
| end if; |
| |
| -- Store the component into the table |
| |
| JSON_Component_Table.Append (Comp); |
| |
| -- Associate the name with the component |
| |
| if Nam = No_Name then |
| Error ("name expected"); |
| end if; |
| |
| Set_Name_Table_Int (Nam, JSON_Component_Table.Last); |
| |
| Read_Token (TK, Token_Start, Token_End); |
| if TK = J_ARRAY_END then |
| exit; |
| elsif TK /= J_COMMA then |
| Error ("comma expected"); |
| end if; |
| |
| Is_First := False; |
| end loop; |
| end Read_Record; |
| |
| ------------------ |
| -- Read_String -- |
| ------------------ |
| |
| function Read_String return Valid_Name_Id is |
| Token_Start : Text_Position; |
| Token_End : Text_Position; |
| Nam : Valid_Name_Id; |
| |
| begin |
| -- Read the string and the following colon |
| |
| Read_Token_And_Error (J_STRING, Token_Start, Token_End); |
| Nam := Decode_Name (Token_Start.Index + 1, Token_End.Index - 1); |
| Read_Token_And_Error (J_COLON, Token_Start, Token_End); |
| |
| return Nam; |
| end Read_String; |
| |
| ------------------ |
| -- Read_Token -- |
| ------------------ |
| |
| procedure Read_Token |
| (Kind : out Token_Kind; |
| Token_Start : out Text_Position; |
| Token_End : out Text_Position) |
| is |
| procedure Next_Char; |
| -- Update Pos to point to next char |
| |
| function Is_Whitespace return Boolean; |
| pragma Inline (Is_Whitespace); |
| -- Return True of current character is a whitespace |
| |
| function Is_Structural_Token return Boolean; |
| pragma Inline (Is_Structural_Token); |
| -- Return True if current character is one of the structural tokens |
| |
| function Is_Token_Sep return Boolean; |
| pragma Inline (Is_Token_Sep); |
| -- Return True if current character is a token separator |
| |
| procedure Delimit_Keyword (Kw : String); |
| -- Helper function to parse tokens such as null, false and true |
| |
| --------------- |
| -- Next_Char -- |
| --------------- |
| |
| procedure Next_Char is |
| begin |
| if Pos.Index > Text'Last then |
| Pos.Column := Pos.Column + 1; |
| elsif Text (Pos.Index) = ASCII.LF then |
| Pos.Column := 1; |
| Pos.Line := Pos.Line + 1; |
| else |
| Pos.Column := Pos.Column + 1; |
| end if; |
| Pos.Index := Pos.Index + 1; |
| end Next_Char; |
| |
| ------------------- |
| -- Is_Whitespace -- |
| ------------------- |
| |
| function Is_Whitespace return Boolean is |
| begin |
| return |
| Pos.Index <= Text'Last |
| and then |
| (Text (Pos.Index) = ASCII.LF |
| or else |
| Text (Pos.Index) = ASCII.CR |
| or else |
| Text (Pos.Index) = ASCII.HT |
| or else |
| Text (Pos.Index) = ' '); |
| end Is_Whitespace; |
| |
| ------------------------- |
| -- Is_Structural_Token -- |
| ------------------------- |
| |
| function Is_Structural_Token return Boolean is |
| begin |
| return |
| Pos.Index <= Text'Last |
| and then |
| (Text (Pos.Index) = '[' |
| or else |
| Text (Pos.Index) = ']' |
| or else |
| Text (Pos.Index) = '{' |
| or else |
| Text (Pos.Index) = '}' |
| or else |
| Text (Pos.Index) = ',' |
| or else |
| Text (Pos.Index) = ':'); |
| end Is_Structural_Token; |
| |
| ------------------ |
| -- Is_Token_Sep -- |
| ------------------ |
| |
| function Is_Token_Sep return Boolean is |
| begin |
| return |
| Pos.Index > Text'Last |
| or else |
| Is_Whitespace |
| or else |
| Is_Structural_Token; |
| end Is_Token_Sep; |
| |
| --------------------- |
| -- Delimit_Keyword -- |
| --------------------- |
| |
| procedure Delimit_Keyword (Kw : String) is |
| pragma Unreferenced (Kw); |
| begin |
| while not Is_Token_Sep loop |
| Token_End := Pos; |
| Next_Char; |
| end loop; |
| end Delimit_Keyword; |
| |
| CC : Character; |
| Can_Be_Integer : Boolean := True; |
| |
| -- Start of processing for Read_Token |
| |
| begin |
| -- Skip leading whitespaces |
| |
| while Is_Whitespace loop |
| Next_Char; |
| end loop; |
| |
| -- Initialize token delimiters |
| |
| Token_Start := Pos; |
| Token_End := Pos; |
| |
| -- End of stream reached |
| |
| if Pos.Index > Text'Last then |
| Kind := J_EOF; |
| return; |
| end if; |
| |
| CC := Text (Pos.Index); |
| |
| if CC = '[' then |
| Next_Char; |
| Kind := J_ARRAY; |
| return; |
| elsif CC = ']' then |
| Next_Char; |
| Kind := J_ARRAY_END; |
| return; |
| elsif CC = '{' then |
| Next_Char; |
| Kind := J_OBJECT; |
| return; |
| elsif CC = '}' then |
| Next_Char; |
| Kind := J_OBJECT_END; |
| return; |
| elsif CC = ',' then |
| Next_Char; |
| Kind := J_COMMA; |
| return; |
| elsif CC = ':' then |
| Next_Char; |
| Kind := J_COLON; |
| return; |
| elsif CC = 'n' then |
| Delimit_Keyword ("null"); |
| Kind := J_NULL; |
| return; |
| elsif CC = 'f' then |
| Delimit_Keyword ("false"); |
| Kind := J_FALSE; |
| return; |
| elsif CC = 't' then |
| Delimit_Keyword ("true"); |
| Kind := J_TRUE; |
| return; |
| elsif CC = '"' then |
| -- We expect a string |
| -- Just scan till the end the of the string but do not attempt |
| -- to decode it. This means that even if we get a string token |
| -- it might not be a valid string from the ECMA 404 point of |
| -- view. |
| |
| Next_Char; |
| while Pos.Index <= Text'Last and then Text (Pos.Index) /= '"' loop |
| if Text (Pos.Index) in ASCII.NUL .. ASCII.US then |
| Error ("control character not allowed in string"); |
| end if; |
| |
| if Text (Pos.Index) = '\' then |
| Next_Char; |
| if Pos.Index > Text'Last then |
| Error ("non terminated string token"); |
| end if; |
| |
| case Text (Pos.Index) is |
| when 'u' => |
| for Idx in 1 .. 4 loop |
| Next_Char; |
| if Pos.Index > Text'Last |
| or else (Text (Pos.Index) not in 'a' .. 'f' |
| and then |
| Text (Pos.Index) not in 'A' .. 'F' |
| and then |
| Text (Pos.Index) not in '0' .. '9') |
| then |
| Error ("invalid unicode escape sequence"); |
| end if; |
| end loop; |
| when '\' | '/' | '"' | 'b' | 'f' | 'n' | 'r' | 't' => |
| null; |
| when others => |
| Error ("invalid escape sequence"); |
| end case; |
| end if; |
| Next_Char; |
| end loop; |
| |
| -- No quote found report and error |
| |
| if Pos.Index > Text'Last then |
| Error ("non terminated string token"); |
| end if; |
| |
| Token_End := Pos; |
| |
| -- Go to next char and ensure that this is separator. Indeed |
| -- construction such as "string1""string2" are not allowed |
| |
| Next_Char; |
| if not Is_Token_Sep then |
| Error ("invalid syntax"); |
| end if; |
| Kind := J_STRING; |
| return; |
| elsif CC = '-' or else CC in '0' .. '9' then |
| -- We expect a number |
| if CC = '-' then |
| Next_Char; |
| end if; |
| |
| if Pos.Index > Text'Last then |
| Error ("invalid number"); |
| end if; |
| |
| -- Parse integer part of a number. Superfluous leading zeros are |
| -- not allowed. |
| |
| if Text (Pos.Index) = '0' then |
| Token_End := Pos; |
| Next_Char; |
| elsif Text (Pos.Index) in '1' .. '9' then |
| Token_End := Pos; |
| Next_Char; |
| while Pos.Index <= Text'Last |
| and then Text (Pos.Index) in '0' .. '9' |
| loop |
| Token_End := Pos; |
| Next_Char; |
| end loop; |
| else |
| Error ("invalid number"); |
| end if; |
| |
| if Is_Token_Sep then |
| -- Valid integer number |
| |
| Kind := J_INTEGER; |
| return; |
| elsif Text (Pos.Index) /= '.' |
| and then Text (Pos.Index) /= 'e' |
| and then Text (Pos.Index) /= 'E' |
| then |
| Error ("invalid number"); |
| end if; |
| |
| -- Check for a fractional part |
| |
| if Text (Pos.Index) = '.' then |
| Can_Be_Integer := False; |
| Token_End := Pos; |
| Next_Char; |
| if Pos.Index > Text'Last |
| or else Text (Pos.Index) not in '0' .. '9' |
| then |
| Error ("invalid number"); |
| end if; |
| |
| while Pos.Index <= Text'Last |
| and then Text (Pos.Index) in '0' .. '9' |
| loop |
| Token_End := Pos; |
| Next_Char; |
| end loop; |
| |
| end if; |
| |
| -- Check for exponent part |
| |
| if Pos.Index <= Text'Last |
| and then (Text (Pos.Index) = 'e' or else Text (Pos.Index) = 'E') |
| then |
| Token_End := Pos; |
| Next_Char; |
| if Pos.Index > Text'Last then |
| Error ("invalid number"); |
| end if; |
| |
| if Text (Pos.Index) = '-' then |
| -- Also a few corner cases can lead to an integer, assume |
| -- that the number is not an integer. |
| |
| Can_Be_Integer := False; |
| end if; |
| |
| if Text (Pos.Index) = '-' or else Text (Pos.Index) = '+' then |
| Next_Char; |
| end if; |
| |
| if Pos.Index > Text'Last |
| or else Text (Pos.Index) not in '0' .. '9' |
| then |
| Error ("invalid number"); |
| end if; |
| |
| while Pos.Index <= Text'Last |
| and then Text (Pos.Index) in '0' .. '9' |
| loop |
| Token_End := Pos; |
| Next_Char; |
| end loop; |
| end if; |
| |
| if Is_Token_Sep then |
| -- Valid decimal number |
| |
| if Can_Be_Integer then |
| Kind := J_INTEGER; |
| else |
| Kind := J_NUMBER; |
| end if; |
| return; |
| else |
| Error ("invalid number"); |
| end if; |
| elsif CC = EOF then |
| Kind := J_EOF; |
| else |
| Error ("Unexpected character"); |
| end if; |
| end Read_Token; |
| |
| ---------------------------- |
| -- Read_Token_And_Error -- |
| ---------------------------- |
| |
| procedure Read_Token_And_Error |
| (TK : Token_Kind; |
| Token_Start : out Text_Position; |
| Token_End : out Text_Position) |
| is |
| Kind : Token_Kind; |
| |
| begin |
| -- Read a token and errout out if not of the expected kind |
| |
| Read_Token (Kind, Token_Start, Token_End); |
| if Kind /= TK then |
| Error ("specific token expected"); |
| end if; |
| end Read_Token_And_Error; |
| |
| ------------------------- |
| -- Read_Variant_Part -- |
| ------------------------- |
| |
| function Read_Variant_Part return Nat is |
| Next : Nat := 0; |
| TK : Token_Kind; |
| Token_Start : Text_Position; |
| Token_End : Text_Position; |
| Var : JSON_Variant_Node; |
| |
| begin |
| -- Read a nonempty array of components |
| |
| Read_Token_And_Error (J_ARRAY, Token_Start, Token_End); |
| |
| loop |
| Read_Token_And_Error (J_OBJECT, Token_Start, Token_End); |
| |
| Var.Variant := 0; |
| |
| -- Read the members as string : value pairs |
| |
| loop |
| case Read_String is |
| when Name_Present => |
| Var.Present := Read_Numerical_Expr; |
| when Name_Record => |
| Read_Record; |
| when Name_Variant => |
| Var.Variant := Read_Variant_Part; |
| when others => |
| Error ("invalid variant"); |
| end case; |
| |
| Read_Token (TK, Token_Start, Token_End); |
| if TK = J_OBJECT_END then |
| exit; |
| elsif TK /= J_COMMA then |
| Error ("comma expected"); |
| end if; |
| end loop; |
| |
| -- Chain the variant and store it into the table |
| |
| Var.Next := Next; |
| JSON_Variant_Table.Append (Var); |
| Next := JSON_Variant_Table.Last; |
| |
| Read_Token (TK, Token_Start, Token_End); |
| if TK = J_ARRAY_END then |
| exit; |
| elsif TK /= J_COMMA then |
| Error ("comma expected"); |
| end if; |
| end loop; |
| |
| return Next; |
| end Read_Variant_Part; |
| |
| ------------------ |
| -- Skip_Value -- |
| ------------------ |
| |
| procedure Skip_Value is |
| Array_Depth : Natural := 0; |
| Object_Depth : Natural := 0; |
| TK : Token_Kind; |
| Token_Start : Text_Position; |
| Token_End : Text_Position; |
| |
| begin |
| -- Read a value without recursing |
| |
| loop |
| Read_Token (TK, Token_Start, Token_End); |
| |
| case TK is |
| when J_STRING | J_INTEGER | J_NUMBER => |
| null; |
| when J_ARRAY => |
| Array_Depth := Array_Depth + 1; |
| when J_ARRAY_END => |
| Array_Depth := Array_Depth - 1; |
| when J_OBJECT => |
| Object_Depth := Object_Depth + 1; |
| when J_OBJECT_END => |
| Object_Depth := Object_Depth - 1; |
| when J_COLON | J_COMMA => |
| if Array_Depth = 0 and then Object_Depth = 0 then |
| Error ("value expected"); |
| end if; |
| when others => |
| Error ("value expected"); |
| end case; |
| |
| exit when Array_Depth = 0 and then Object_Depth = 0; |
| end loop; |
| end Skip_Value; |
| |
| Token_Start : Text_Position; |
| Token_End : Text_Position; |
| TK : Token_Kind; |
| Is_First : Boolean := True; |
| |
| -- Start of processing for Read_JSON_Stream |
| |
| begin |
| -- Read a possibly empty array of entities |
| |
| Read_Token_And_Error (J_ARRAY, Token_Start, Token_End); |
| |
| loop |
| Read_Token (TK, Token_Start, Token_End); |
| if Is_First and then TK = J_ARRAY_END then |
| exit; |
| elsif TK /= J_OBJECT then |
| Error ("object expected"); |
| end if; |
| |
| Read_Entity; |
| |
| Read_Token (TK, Token_Start, Token_End); |
| if TK = J_ARRAY_END then |
| exit; |
| elsif TK /= J_COMMA then |
| Error ("comma expected"); |
| end if; |
| |
| Is_First := False; |
| end loop; |
| end Read_JSON_Stream; |
| |
| end Repinfo.Input; |