Parsing json5/js object literals in Ada

295 Views Asked by At

New to Ada. Trying to work with some objects like the following {name:'Hermann',age:33} in an a project and I'd rather not write my own json parser for this. Is there either:

  • a way for me to configure Gnatcolls.JSON to parse and write these objects
    or
  • a different library I can use with json5 or javascript object literal support?
1

There are 1 best solutions below

0
On

I wrote a JSON parser from the spec pretty quickly for work while doing other things, and it took a day/day-and-a-half; it's not particularly hard and I'll see about posting it to github or something.

However, JSON5 is different enough that re-implementing it would be on the order of the same difficulty as writing some sort of adaptor. Editing the parser to accept the new constructs might be more difficult than one might anticipate, as the IdentifierName allowed as a key means that you can't simply chain together the sequence (1) "get-open-brace", (2) "consume-whitespace", (3) "get-a-string", (4) "consume-whitespace", (5) "get-a-colon, (6) "consume-whitespace", (7) "get-JSON-object", (8) "consume-whitespace", (9) "get-a-character; if comma, go to #1, otherwise it should be an end-brace".

Perhaps one thing that makes things easier is to equate the stream- and string-operations so that you only have one production-method for your objects; there are three main ways to do this:

  1. Make a generic such that it takes a string and gives the profile for the stream-operation.

  2. Make a pair of overloaded functions that provide the same interface.

  3. Make a stream that is a string; the following does this:

    Package Example is
    -- String_Stream allows uses a string to buffer the underlying stream,
    -- it may be initialized with content from a string or a given length for
    -- the string underlying the stream.
    --
    -- This is intended for the construction and consumption of string-data
    -- using stream-operations
    Type String_Stream(<>) is new Ada.Streams.Root_Stream_Type with Private;
    
    Subtype Root_Stream_Class is Ada.Streams.Root_Stream_Type'Class;
    
    -- Create a String_Stream.
    Function "+"( Length : Natural ) return String_Stream;
    Function "+"( Text   : String  ) return String_Stream;
    Function "+"( Length : Natural ) return not null access Root_Stream_Class;
    Function "+"( Text   : String  ) return not null access Root_Stream_Class;
    
    -- Retrieve the remaining string-data; the (POSITION..DATA'LENGTH) slice.
    Function "-"( Stream : String_Stream ) return String;
    
    -- Retrieve the string-data; the (1..DATA'LENGTH) slice.
    Function Data(Stream : String_Stream ) return String;
    
    Private
    Pragma Assert( Ada.Streams.Stream_Element'Size = String'Component_Size );
    
    Overriding
    procedure Read
      (Stream : in out String_Stream;
       Item   :    out Ada.Streams.Stream_Element_Array;
       Last   :    out Ada.Streams.Stream_Element_Offset);
    
    Overriding
    procedure Write
      (Stream : in out String_Stream;
       Item   :        Ada.Streams.Stream_Element_Array);
    
    Type String_Stream(Length : Ada.Streams.Stream_Element_Count) is
      new Ada.Streams.Root_Stream_Type with record
        Data     : Ada.Streams.Stream_Element_Array(1..Length);
        Position : Ada.Streams.Stream_Element_Count;
      End record;
    End Example;
    

With implementation of:

Package Body Example is
    Use Ada.Streams;

    -------------------
    --  INITALIZERS  --
    -------------------

    Function From_String( Text   : String  ) return String_Stream
      with Inline, Pure_Function;
    Function Buffer     ( Length : Natural ) return String_Stream
      with Inline, Pure_Function;


    --------------
    --  R E A D --
    --------------

    Procedure Read
      (Stream : in out String_Stream;
       Item   :    out Ada.Streams.Stream_Element_Array;
       Last   :    out Ada.Streams.Stream_Element_Offset) is
        Use Ada.IO_Exceptions, Ada.Streams;
    Begin
        -- When there is a read of zero, do nothing.
        -- When there is a read beyond the buffer's bounds, raise an exception.
        --  Note: I've used two cases here-
        --      1) when the read is greater than the buffer,
        --      2) when the read would go beyond the buffer.
        -- Finally, read the given amount of data and update the position.
        if Item'Length = 0 then
            null;
        elsif Item'Length > Stream.Data'Length then
            Raise End_Error with "Request is larger than the buffer's size.";
        elsif Stream_Element_Offset'Pred(Stream.Position)+Item'Length > Stream.Data'Length then
            Raise End_Error with "Buffer will over-read.";
        else
            Declare
                Subtype Selection is Stream_Element_Offset range
                  Stream.Position..Stream.Position+Stream_Element_Offset'Pred(Item'Length);
            Begin
                Item(Item'Range):= Stream.Data(Selection);
                Stream.Position:= Stream_Element_Offset'Succ(Selection'Last);
                Last:= Selection'Last;--Stream.Position;
            End;
        end if;
    End Read;


    -----------------
    --  W R I T E  --
    -----------------

    Procedure Write
      (Stream : in out String_Stream;
       Item   :        Ada.Streams.Stream_Element_Array) is
    Begin
        Declare
            Subtype Selection is Stream_Element_Offset range
              Stream.Position..Stream.Position+Stream_Element_Offset'Pred(Item'Length);
        Begin
            Stream.Data(Selection):= Item(Item'Range);
            Stream.Position:= Stream_Element_Offset'Succ(Selection'Last);
        End;
    End Write;


    ----------------------------------
    --  INITALIZER IMPLEMENTATIONS  --
    ----------------------------------

    -- Create a buffer of the given length, zero-filled.
    Function Buffer( Length : Natural ) return String_Stream is
        Len : Constant Ada.Streams.Stream_Element_Offset :=
          Ada.Streams.Stream_Element_Offset(Length);
    Begin
        Return Result : Constant String_Stream:=
          (Root_Stream_Type with
           Position =>  1,
           Data     => (1..Len => 0),
           Length   =>  Len
          );
    End Buffer;

    -- Create a buffer from the given string.
    Function From_String( Text : String ) return String_Stream is
        Use Ada.Streams;

        Subtype Element_Range is Stream_Element_Offset range
          Stream_Element_Offset(Text'First)..Stream_Element_Offset(Text'Last);
        Subtype Constrained_Array  is Stream_Element_Array(Element_Range);
        Subtype Constrained_String is String(Text'Range);

        Function Convert is new Ada.Unchecked_Conversion(
           Source => Constrained_String,
           Target => Constrained_Array
          );
    Begin
        Return Result : Constant String_Stream:=
          (Root_Stream_Type with
           Position => Element_Range'First,
           Data     => Convert( Text ),
           Length   => Text'Length
          );
    End From_String;


    -- Classwide returning renames, for consistancy/overload.
    Function To_Stream( Text   : String  ) return Root_Stream_Class is
        ( From_String(Text) ) with Inline, Pure_Function;
    Function To_Stream( Length : Natural ) return Root_Stream_Class is
        ( Buffer(Length)    ) with Inline, Pure_Function;


    ----------------------------
    --  CONVERSION OPERATORS  --
    ----------------------------

    -- Allocating / access-returning initalizing operations.
    Function "+"( Length : Natural ) return not null access Root_Stream_Class is
      ( New Root_Stream_Class'(To_Stream(Length)) );
    Function "+"( Text   : String  ) return not null access Root_Stream_Class is
      ( New Root_Stream_Class'(To_Stream(Text))   );

    -- Conversion from text or integer to a stream; renaming of the initalizers.
    Function "+"( Text   : String  ) return String_Stream renames From_String;
    Function "+"( Length : Natural ) return String_Stream renames Buffer;

    -- Convert a given Stream_Element_Array to a String.
    Function "-"( Data   : Ada.Streams.Stream_Element_Array ) Return String is
        Subtype Element_Range is Natural range
          Natural(Data'First)..Natural(Data'Last);
        Subtype Constrained_Array  is Stream_Element_Array(Data'Range);
        Subtype Constrained_String is String(Element_Range);

        Function Convert is new Ada.Unchecked_Conversion(
           Source => Constrained_Array,
           Target => Constrained_String
          );

    Begin
        Return Convert( Data );
    End "-";


    ----------------------
    --  DATA RETRIEVAL  --
    ----------------------

    Function "-"( Stream : String_Stream ) return String is
    Begin
        Return -Stream.Data(Stream.Position..Stream.Length);
    End "-";


    Function Data(Stream : String_Stream ) return String is
    Begin
        Return -Stream.Data;
    End Data;


End Example;