wisitoken_4.2.1_dc778486/wisitoken-syntax_trees-lr_utils.ads

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
--  Abstract :
--
--  Utilities for navigating syntax trees produced by an LR parser.
--
--  Design :
--
--  It would be safer if Cursor contained a pointer to Iterator; then
--  Copy and Splice could just take Cursor arguments. But that
--  requires mode 'aliased in' for First, Last, which is not
--  conformant with Ada.Iterator_Interfaces.
--
--  Copyright (C) 2019, 2020, 2022 Stephen Leake All Rights Reserved.
--
--  This library is free software;  you can redistribute it and/or modify it
--  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 library is distributed in the hope that it will be useful,
--  but WITHOUT ANY WARRANTY;  without even the implied warranty of MERCHAN-
--  TABILITY 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.

pragma License (Modified_GPL);

with Ada.Iterator_Interfaces;
with SAL.Gen_Unconstrained_Array_Image_Aux;
package WisiToken.Syntax_Trees.LR_Utils is

   procedure Raise_Programmer_Error
     (Label : in String;
      Tree  : in WisiToken.Syntax_Trees.Tree;
      Node  : in WisiToken.Syntax_Trees.Node_Access);
   pragma No_Return (Raise_Programmer_Error);

   ----------
   --  List functions
   --
   --  A list has one of the following grammar forms:
   --
   --  list : list element | element ;
   --  list : element | list element ;
   --
   --  list : list separator element | element ;
   --  list : element | list separator element ;
   --
   --  In the syntax tree, this looks like:
   --
   --  list: Root
   --  | list
   --  | | list
   --  | | | element: First
   --  | | separator?
   --  | | element: 2
   --  | separator?
   --  | element: 3
   --  separator?
   --  element: Last

   type Constant_List (<>) is tagged private with
     Constant_Indexing => List_Constant_Ref,
     Default_Iterator  => Iterate_Constant,
     Iterator_Element  => Valid_Node_Access;

   function Tree (Container : in Constant_List) return Tree_Constant_Reference
   with Pre => not Container.Is_Invalid;

   function Is_Invalid (Container : in Constant_List) return Boolean;

   function Is_Empty (Container : in Constant_List) return Boolean;
   --  Returns True if Container is invalid, or if Container is empty

   function Root (Container : in Constant_List) return Node_Access
   with Pre => not Container.Is_Invalid;

   function List_ID (Container : in Constant_List) return Token_ID
   with Pre => not Container.Is_Invalid;

   function Element_ID (Container : in Constant_List) return Token_ID
   with Pre => not Container.Is_Invalid;

   function Count (Container : in Constant_List) return Ada.Containers.Count_Type
   with Pre => not Container.Is_Invalid;

   function Contains (Container : in Constant_List; Node : in Valid_Node_Access) return Boolean
   with Pre => not Container.Is_Invalid;

   type Cursor is private;

   No_Element : constant Cursor;

   function To_Cursor (Container : in Constant_List; Node : in Valid_Node_Access) return Cursor
   with Pre => (not Container.Is_Invalid) and then
               (Container.Contains (Node) and Container.Tree.ID (Node) = Container.Element_ID);

   function Contains (Container : in Constant_List; Item : in Cursor) return Boolean
   with Pre => not Container.Is_Invalid;

   function Has_Element (Cursor : in LR_Utils.Cursor) return Boolean;

   function Element (Cursor : in LR_Utils.Cursor) return Node_Access;
   --  Invalid_Node_Access if not Has_Element (Cursor).

   package Iterator_Interfaces is new Ada.Iterator_Interfaces (Cursor, Has_Element);

   type Iterator (Container : not null access constant Constant_List'Class) is
     new Iterator_Interfaces.Reversible_Iterator
     with null record;

   function First (Container : in Constant_List) return Cursor;
   function Last  (Container : in Constant_List) return Cursor;

   overriding function First (Iter : in Iterator) return Cursor is (Iter.Container.First);
   overriding function Last  (Iter : in Iterator) return Cursor is (Iter.Container.Last);
   overriding function Next     (Iter : in Iterator; Position : Cursor) return Cursor;
   overriding function Previous (Iter : in Iterator; Position : Cursor) return Cursor;

   function List_Constant_Ref
     (Container : aliased in Constant_List'Class;
      Position  :         in Cursor)
     return Valid_Node_Access
   with Pre => Has_Element (Position);

   type Constant_Iterator (Container : not null access constant Constant_List) is new
     Iterator_Interfaces.Reversible_Iterator
     with null record;

   overriding function First (Iter : in Constant_Iterator) return Cursor is (Iter.Container.First);
   overriding function Last  (Iter : in Constant_Iterator) return Cursor is (Iter.Container.Last);
   overriding function Next     (Iter : in Constant_Iterator; Position : Cursor) return Cursor;
   overriding function Previous (Iter : in Constant_Iterator; Position : Cursor) return Cursor;

   function Iterate_Constant (Container : aliased in Constant_List'Class) return Constant_Iterator
   is (Iterator_Interfaces.Reversible_Iterator with Container'Access);

   type Find_Equal is access function
     (Target : in String;
      List   : in Constant_List'Class;
      Node   : in Valid_Node_Access)
   return Boolean;
   --  Function called by Find to compare Target to Node. Target, List
   --  are the Find arguments; Node is an element of List. Return True if
   --  Node matches Target.

   function Find
     (Container : in Constant_List;
      Target    : in Valid_Node_Access)
     return Cursor
   with Pre => not Container.Is_Invalid and Container.Tree.ID (Target) = Container.Element_ID;

   function Find
     (Container : in Constant_List;
      Target    : in String;
      Equal     : in Find_Equal)
     return Cursor
   with Pre => not Container.Is_Invalid;

   type List (<>) is new Constant_List with private with
     Default_Iterator  => Iterate,
     Iterator_Element  => Valid_Node_Access;

   function Separator_ID (Container : in List) return Token_ID
   with Pre => not Container.Is_Invalid;

   function Iterate (Container : aliased in List'Class) return Iterator
   is (Iterator_Interfaces.Reversible_Iterator with Container'Access);

   package Creators is
      --  Nested package so these are not primitive, and don't have to be
      --  overridden for List.

      function Create_List
        (Tree         : aliased in out WisiToken.Syntax_Trees.Tree;
         Root         :         in     Valid_Node_Access;
         List_ID      :         in     WisiToken.Token_ID;
         Element_ID   :         in     WisiToken.Token_ID;
         Separator_ID :         in     WisiToken.Token_ID)
        return List
      with Pre => Tree.Editable and (Tree.Is_Nonterm (Root) and then Tree.Has_Children (Root)) and
                  Tree.ID (Root) = List_ID;
      --  If there is no separator, set Separator_ID = WisiToken.Invalid_Token_ID
      --  The list cannot be empty; use Empty_List for an empty list.

      function Create_List
        (Tree       : aliased in out WisiToken.Syntax_Trees.Tree;
         Root       :         in     Valid_Node_Access;
         List_ID    :         in     WisiToken.Token_ID;
         Element_ID :         in     WisiToken.Token_ID)
        return Constant_List
      with Pre => (Tree.Is_Nonterm (Root) and then Tree.Has_Children (Root)) and Tree.ID (Root) = List_ID;
      --  The separator is only need when adding new elements.

      function Create_List
        (Container :         in     Constant_List;
         Tree      : aliased in out WisiToken.Syntax_Trees.Tree;
         Root      :         in     Valid_Node_Access)
        return Constant_List
      with Pre => (Container.Tree.Is_Nonterm (Root) and then
                   Container.Tree.Has_Children (Root)) and
                  Container.Tree.ID (Root) = Container.List_ID;
      --  Same as Create_List, get all other params from Container.
      --  Need Tree for non-constant view.

      function Create_List (Container : in out List; Root : in Valid_Node_Access) return List
      with Pre => (Container.Tree.Is_Nonterm (Root) and then Container.Tree.Has_Children (Root)) and
                  Container.Tree.ID (Root) = Container.List_ID;
      --  Same as Create_List, get all other params from Container.

      function Create_From_Element
        (Tree         : aliased in out WisiToken.Syntax_Trees.Tree;
         Element      :         in     Valid_Node_Access;
         List_ID      :         in     WisiToken.Token_ID;
         Element_ID   :         in     WisiToken.Token_ID;
         Separator_ID :         in     WisiToken.Token_ID)
        return List
      with Pre => Tree.Editable and
                  Tree.ID (Tree.Parent (Element)) = List_ID and
                  Tree.ID (Element) = Element_ID and
                  Tree.ID (Tree.Parent (Element)) = List_ID;
      --  Same as Create_List, but it first finds the root as an ancestor of
      --  Element.

      function Create_From_Element (Container : in out List; Element : in Valid_Node_Access) return List
      with Pre => Container.Tree.ID (Container.Tree.Parent (Element)) = Container.List_ID and
                  Container.Tree.ID (Element) = Container.Element_ID and
                  Container.Tree.ID (Container.Tree.Parent (Element)) = Container.List_ID;
      --  Same as Create_From_Element, get all other params from Container.

      function Create_From_Element
        (Tree       : aliased in out WisiToken.Syntax_Trees.Tree;
         Element    :         in     Valid_Node_Access;
         List_ID    :         in     WisiToken.Token_ID;
         Element_ID :         in     WisiToken.Token_ID)
        return Constant_List
      with Pre => Tree.ID (Tree.Parent (Element)) = List_ID and
                  Tree.ID (Element) = Element_ID and
                  Tree.ID (Tree.Parent (Element)) = List_ID;
      --  Same as Create_List, but it first finds the root as an ancestor of
      --  Element.

      function Invalid_List (Tree : aliased in out WisiToken.Syntax_Trees.Tree) return List;
      function Invalid_List (Tree : aliased in out WisiToken.Syntax_Trees.Tree) return Constant_List;
      --  First, Last return empty cursor, count returns 0, all other
      --  operations fail a precondition check.
      --
      --  Useful when the result should never be used, but must be present,
      --  as in a conditional expression.

      function Empty_List
        (Tree              : aliased in out WisiToken.Syntax_Trees.Tree;
         List_ID           :         in     WisiToken.Token_ID;
         Multi_Element_RHS :         in     Natural;
         Element_ID        :         in     WisiToken.Token_ID;
         Separator_ID      :         in     WisiToken.Token_ID)
        return List
      with Pre => Tree.Editable;
      --  Result Root returns Invalid_Node_Access; First, Last return empty
      --  cursor, count returns 0; Append works correctly.

      function Empty_List (Container : in out List) return List;
      --  Same as Empty_List, get all other params from Container.

   end Creators;

   function Compatible (A, B : in Constant_List'Class) return Boolean;
   --  True if A and B are not invalid, and all components are the same
   --  except Root.

   procedure Append
     (Container   : in out List;
      New_Element : in     Valid_Node_Access)
   with Pre => not Container.Is_Invalid and then Container.Tree.ID (New_Element) = Container.Element_ID;
   --  Append New_Item to Container, including Container.Separator_ID if
   --  it is not Invalid_Token_Index.
   --
   --  If Container was Empty, or if Container.Root has no parent in
   --  Tree, the modified list has no parent. Otherwise, the parent of
   --  Container.Root is updated to hold the new Container.Root.

   procedure Prepend
     (Container   : in out List;
      New_Element : in     Valid_Node_Access)
   with Pre => not Container.Is_Invalid and then Container.Tree.ID (New_Element) = Container.Element_ID;
   --  Prepend New_Item to Container, including Container.Separator_ID if
   --  it is not Invalid_Token_Index.
   --
   --  Container.Root parent is updated.

   procedure Insert
     (Container   : in out List;
      New_Element : in     Valid_Node_Access;
      After       : in     Cursor)
   with Pre => not Container.Is_Invalid and then
               (Container.Tree.ID (New_Element) = Container.Element_ID and
                (After = No_Element or else Container.Contains (After)));
   --  Insert New_Item into Container after Ater, including
   --  Container.Separator_ID if it is not Invalid_Token_Index.
   --
   --  If After is No_Element, calls Prepend.
   --
   --  If Container was Empty, or if Container.Root has no parent, the
   --  modified list has no parent. Otherwise, if After is
   --  Container.Last, the parent of Container.Root is updated to hold
   --  the new Container.Root.

   procedure Copy
     (Source_List  : in     Constant_List'Class;
      Source_First : in     Cursor := No_Element;
      Source_Last  : in     Cursor := No_Element;
      Dest_List    : in out List'Class;
      User_Data    : in     User_Data_Access_Constant)
   with Pre => Compatible (Source_List, Dest_List);
   --  Deep copy slice of Source_List, appending to Dest_List.
   --
   --  If First = No_Element, copy from List.First.
   --  If Last = No_Element, copy thru List.Last.

   procedure Delete
     (Container : in out List;
      Item      : in out Cursor)
   with Pre => Container.Contains (Item);
   --  Delete Item from Container. Parent of Container.Root is updated
   --  appropriately. Cursor is set to No_Element.

   type Skip_Label is (Nested, Skip);

   type Skip_Item (Label : Skip_Label := Skip) is
   --  The default values must be valid to allow Skip_Info to be default
   --  initialized.
   record
      Element : Node_Access := null;
      case Label is
      when Nested =>
         --  Element is an element in the list currently being copied
         --  containing a nested list with an element to skip (given by Element
         --  in the next Skip_Item). The nested list is defined by:
         List_Root         : Valid_Node_Access;
         List_ID           : Token_ID;
         Element_ID        : Token_ID;
         Separator_ID      : Token_ID;
         Multi_Element_RHS : Natural;

      when Skip =>
         --  Element is the element in the current list to skip.
         null;
      end case;
   end record;
   subtype Nested_Skip_Item is Skip_Item (Nested);

   function Image (Item : in Skip_Item; Descriptor : in WisiToken.Descriptor) return String
   is ("(" & Item.Label'Image & ", " & Trimmed_Image (Get_Node_Index (Item.Element)) &
         (case Item.Label is
          when Nested => "," & Trimmed_Image (Get_Node_Index (Item.List_Root)) & ", " &
               Image (Item.List_ID, Descriptor),
          when Skip => "") &
         ")");

   type Skip_Array is array (Positive_Index_Type range <>) of Skip_Item;

   type Skip_Info (Skip_Last : SAL.Base_Peek_Type) is
   record
      --  Skip_Last may be Positive_Index_Type'First - 1 to indicate an
      --  empty or invalid skip list.
      Start_List_Root         : Node_Access := Invalid_Node_Access;
      Start_List_ID           : Token_ID   := Invalid_Token_ID;
      Start_Element_ID        : Token_ID   := Invalid_Token_ID;
      Start_Separator_ID      : Token_ID   := Invalid_Token_ID;
      Start_Multi_Element_RHS : Natural    := 0;
      Skips                   : Skip_Array (Positive_Index_Type'First .. Skip_Last);
   end record;

   function Image is new SAL.Gen_Unconstrained_Array_Image_Aux
     (Positive_Index_Type, Skip_Item, Skip_Array, WisiToken.Descriptor, Image);

   function Image (Item : in Skip_Info; Descriptor : in WisiToken.Descriptor) return String
   is ("(" &
         (if Item.Start_List_ID = Invalid_Token_ID
          then ""
          else Trimmed_Image (Get_Node_Index (Item.Start_List_Root)) & ", " &
             Image (Item.Start_List_ID, Descriptor) & ", " &
             Image (Item.Skips, Descriptor))
         & ")");

   function Valid_Skip_List (Tree : aliased in out Syntax_Trees.Tree; Skip_List : in Skip_Array) return Boolean;
   --  The last element must be Skip, preceding elements must all be
   --  Nested. The Element in each array element must have ID = preceding
   --  Element_ID. The net result of all skips must not be empty, unless
   --  there is only one item (Skip); Start_List_Root may contain only
   --  that.

   function Copy_Skip_Nested
     (Skip_List :         in     Skip_Info;
      Tree      : aliased in out Syntax_Trees.Tree;
      User_Data :         in     User_Data_Access_Constant)
     return Node_Access
   with Pre => Tree.Editable and
               (Skip_List.Start_List_ID /= Invalid_Token_ID and then
                (Valid_Skip_List (Tree, Skip_List.Skips) and
                 Skip_List.Start_List_ID /= Skip_List.Start_Element_ID));
   --  Copy list rooted at Skip_List.Start_List, skipping one element as
   --  indicated by Skip_List.Skip. Return root of copied list.
   --
   --  Result is Invalid_Node_Access (indicating an empty list) if
   --  Skip_List has only one item (Skip), and Skip_List.Start_List_Root
   --  has only that item.
   --
   --  Raises SAL.Programmer_Error if skip item described by Skip_List is
   --  not found.

   function List_Root
     (Tree    : in Syntax_Trees.Tree;
      Node    : in Valid_Node_Access;
      List_ID : in Token_ID)
     return Valid_Node_Access
   with Pre => Tree.ID (Node) = List_ID;

private
   type Cursor is record
      Node : Node_Access;
   end record;

   No_Element : constant Cursor := (Node => Invalid_Node_Access);

   type Constant_List (Tree : not null access WisiToken.Syntax_Trees.Tree) is tagged
     --  We'd prefer to have Tree be 'constant' here, but then it would
     --  also be constant in List, where we _don't_ want that. An
     --  alternative design would be to not derive List from Constant_List;
     --  then we would would have to duplicate all operations. Another
     --  alternative design would be to derive Constant_List from List, and
     --  tighten the constraint on Tree in Constant_List. But that doesn't
     --  work either. Since most applications using LR_Utils are editing
     --  the tree anyway, we live with requiring variable access.
     --
     --  The current design also means we can't specify Variable_Indexing
     --  on List.
   record
      Root       : WisiToken.Syntax_Trees.Node_Access;
      List_ID    : WisiToken.Token_ID;
      Element_ID : WisiToken.Token_ID;
   end record;

   type List is new Constant_List with
   record
      One_Element_RHS   : Natural;
      Multi_Element_RHS : Natural;
      Separator_ID      : WisiToken.Token_ID;
   end record;

   function Tree (Container : in Constant_List) return Tree_Constant_Reference
   is (Element => Container.Tree);

   function Is_Invalid (Container : in Constant_List) return Boolean
   is (Container.List_ID = Invalid_Token_ID);

   function Is_Empty (Container : in Constant_List) return Boolean
   is (Container.Root = Invalid_Node_Access);

   function Root (Container : in Constant_List) return Node_Access
   is (Container.Root);

   function List_ID (Container : in Constant_List) return Token_ID
   is (Container.List_ID);

   function Element_ID (Container : in Constant_List) return Token_ID
   is (Container.Element_ID);

   function Has_Element (Cursor : in LR_Utils.Cursor) return Boolean
   is (Cursor.Node /= Invalid_Node_Access);

   function Element (Cursor : in LR_Utils.Cursor) return Node_Access
   is (Cursor.Node);

   function Separator_ID (Container : in List) return Token_ID
   is (Container.Separator_ID);

   function Compatible (A, B : in Constant_List'Class) return Boolean
   is
     (A.Tree = B.Tree and
        A.List_ID /= Invalid_Token_ID and
        B.List_ID /= Invalid_Token_ID and
        A.List_ID = B.List_ID and
        A.Element_ID = B.Element_ID);

end WisiToken.Syntax_Trees.LR_Utils;