0

I've been struggling with this little issue for a while. I am trying to create my own implementation of an internal JSON structure. The challenge is that with Ada I have to use an access type to make it recursive and access types have the risk of leaking if I don't have it tightly controlled. In order to make it controlled, I kept all the real activity private I provided Get (Source:...) and Set (Target:...; Value:...) functions/procedures for the Node type that will attempt to verify and handle any existing Vector (json-array) or Map (json-object) elements. In order to further ensure that I was using stable features of Ada 2012 and catching contents as they go out of scope, I tried to use a Protected_Controlled type and "managing" Ada libraries, but found that the container libraries couldn't handle protected types, so I used simply Controlled. The Finalize (...) procedure is for any Vector or Map types and recursively frees the Node_Value.Reference.

My question is if I am applying Ada 2012 correctly, or else how do I create a memory managed recursion of a type that could be either a vector/map or a string/number?

private

    ...

   type Node_Access is access Node;
   type Node_Value is new Ada.Finalization.Controlled with record
      Reference : Node_Access;
   end record;
   overriding procedure Initialize (Item : in out Node_Value);
   overriding procedure Adjust (Item : in out Node_Value);
   overriding procedure Finalize (Item : in out Node_Value);

    ...

   package Of_Array is new Ada.Containers.Indefinite_Vectors (Natural, Node_Value);
   package Of_Object is new Ada.Containers.Indefinite_Ordered_Maps (Wide_String, Node_Value);

   type Node is record
      ...
      Vector    : aliased Of_Array.Vector;
      Object    : aliased Of_Object.Map;
   end record
     with Size => 96;

   procedure Free is new Ada.Unchecked_Deallocation (Node, Node_Access);
Micah W
  • 378
  • 2
  • 8
  • Have you looked at [`GNATCOLL.JSON`](https://docs.adacore.com/gnatcoll-docs/json.html)? – trashgod Jul 22 '17 at 20:47
  • Your `Node` can be [mutable if the discriminant has a default](https://en.wikibooks.org/wiki/Ada_Programming/Types/record#Mutable_and_immutable_variant_records) – Simon Wright Jul 22 '17 at 21:19
  • @trashgod Thankyou, but just as with any _serious_ beginner I am hoping to become good enough that I don't have to rely on GPL libraries. I like the GCC modified GPL, LGPL, and various others. I prefer release my own code this way. I have analyzed scenarios, and to me GPL licensing is just too problematic, and a little like software patents, it tends to slow real progress from happening. Anyway, this is _my_ attempt at making JSON as close to native Ada in style and functionality as possible. Thanks for the help you all! (esp. you Simon Wright) – Micah W Jul 22 '17 at 21:47
  • I meant to suggest reading `gnatcoll-json.ads`, which illustrates @SimonWright's [suggestion](https://stackoverflow.com/questions/45258325/in-ada-how-do-i-recursively-map-and-memory-manage-a-type-within-itself#comment77482134_45258325). – trashgod Jul 23 '17 at 00:39
  • 2
    I fully support your preferences! but, AdaCore have released a lot of their packages on Github, and the [gnatcoll one](https://github.com/AdaCore/gnatcoll) in particular has the [GCC Runtime Library exception](https://www.gnu.org/licenses/gcc-exception-3.1.en.html). – Simon Wright Jul 23 '17 at 20:05
  • Thank you again. I didn't know that the "runtime" exception applied to library collections. I have been ever concerned about that same issue with the AWS, which does have the GMGPL text as well as the GPL. Now I am likely to confirm with AdaCore that was their intent.Mutability, it seems, is also not allowed when something is created with `new` and accessed, so children would not be able to become different types. For now, I went with all types, but I am seriously thinking about _another_ wrapper Controlled record or another access type to fix it... – Micah W Jul 25 '17 at 01:22

2 Answers2

1

The way to do it (in my opinion) is to use OOP and have an abstract element as the root node of a family of types representing the different kinds of data which can be stored.

An array of elements can then be implemented as a vector of the class rooted at the abstract element type. An "object" can be implemented as a hash-table with a string key and the class rooted at the abstract element type as the values.

Jacob Sparre Andersen
  • 6,733
  • 17
  • 22
  • Before editing/trimming down to what I have presented now, That is exactly the approach I took. I may return to it as well, but because objects declared in the heap only are immutable, I left that model. I may go back to it because I clearly overlooked the fact that the the standard library Maps and Vectors objects need to be destroyed/freed before new assignment, so mutability is not important anymore... Thank you for your additional help. – Micah W Jul 28 '17 at 06:14
  • @MicahW It sounds like you've misunderstood some things. Being immutable and being stored in a storage pool (the Ada equivalent of the heap) are not in any way connected concepts. Also, the user does not have to call any "destroy/free" operations on maps and vectors from the standard library. – Jacob Sparre Andersen Jul 28 '17 at 07:47
  • You read inference where there is none intended. Of coarse the programmer does not need to call Free for the standard library containers; I was referring to the internal _logical_ necessity of freeing the component at a given location (i.e. SomeVector(3)). I would be happy to be wrong, but what I have read is that if a variable is declared with **`new`**, then the actual in the storage pool is immutable. This is the only relation (hence my wording "heap only"). – Micah W Jul 28 '17 at 14:37
  • @MicahW An example: with Ada.Text_IO; procedure New_Example is type Something is (Value_1, Value_2); type Reference is access all Something; Item : constant Reference := new Something'(Value_1); begin Ada.Text_IO.Put_Line (Something'Image (Item.all)); Item.all := Value_2; Ada.Text_IO.Put_Line (Something'Image (Item.all)); end New_Example; – Jacob Sparre Andersen Jul 29 '17 at 07:09
  • @MicahW Although I'm quite sure the GNAT implementations of the unbounded containers are using `new`, there are actually also definite, bounded containers, which neither use `new`, nor `Unchecked_Deallocation`. – Jacob Sparre Andersen Jul 29 '17 at 07:18
1

Self-referential types without access types are a valid use for type extension in combination with an indefinite container. A simple example is S-expressions, or Sexes. A Sex is either an atom or a list of zero or more Sexes. The right way to be able to do this would be

with Ada.Containers.Indefinite_Vectors;
package Sexes is
   type Sex is private;
   -- Operations on Sex
private -- Sexes
   package Sex_List is new Ada.Containers.Indefinite_Vectors
      (Index_Type => Positive, Element_Type => Sex); -- Illegal

   type Sex (Is_Atom : Boolean := False) is record
      case Is_Atom is
      when False =>
         Value : Atom;
      when True =>
         List : Sex_List.Vector;
      end case;
   end record;
end Sexes;

but Ada doesn't allow this. We can use type extension to get around this:

private -- Sexes
   type Root is tagged null record;

   package Sex_List is new Ada.Containers.Indefinite_Vectors
      (Index_Type => Positive, Element_Type => Root'Class);

   type Sex (Is_Atom : Boolean := False) is new Root with record
      case Is_Atom is
      when False =>
         Value : Atom;
      when True =>
         List : Sex_List.Vector;
      end case;
   end record;
end Sexes;

which is legal. The only catch is that you have to convert anything taken from List to Sex (or Node in your case).

HTH; sorry about the late response.

  • Good Answer. According to the original plan, I have a discriminated record, but to avoid any trouble with it being immutable, I made it a component of the Node record (record in a record). I May just re-write my current experiment to include the Class vector that you suggest. With the record-in-a-record currently used, the appropriate discriminants don't need to be visible to the library user. After trying and evaluating this technique, I'll report back... – Micah W Aug 23 '17 at 23:31
  • Just wanted to check in. In all fairness, your solution seems best, though after re-writing the library three times, I have yet to make it work as expected - When converting types, it seems that direct access to the components is ineffective. Jacob Sparre Anderson has contributed very well to my understanding, too. My next attempt will be with another one-element Vector or List as an internal wrapper to the Value which will be made visible by the conversion, hope this works... – Micah W Aug 31 '17 at 09:29