--  Abstract :
--
--  Syntax tree type and operations, providing parse streams as
--  described in [1], but generalized for parallel parsers. Data
--  structures and operations are optimized for incremental parsing.
--
--  References :
--
--  [1] Elad Lahav 2004, Efficient Semantic Analysis for Text Editors
--
--  Design :
--
--  There is one syntax tree; parallel parsers all add nodes to the
--  same tree, maintaining different roots via Stream_IDs.
--
--  During parsing, the Shared_Stream contains all of the input source
--  text, either as terminal tokens from the lexer in batch parse, or
--  a mix of terminal and nonterminal tokens from Parse.Edit_Tree in
--  incremental parse.
--
--  Node_Index is used only for debugging. Node_Index on nonterms is
--  negative. Node_Index on terminal nodes created by the lexer in the
--  shared stream is positive; Node_Index on virtual nodes inserted by
--  error recover is negative.
--
--  During a batch parse, Node_Index on terminals is sequential, as a
--  consequence of lexing the source code first; Node_Index on
--  nonterms is unique within the tree, and abs Node_Index of a
--  nonterm is greater than abs Node_Index of any of its children.
--  Error recover inserts and deletes terminals, with non-sequential
--  Node_Index.
--
--  In incremental parse, Node_Index on terminals is not sequential,
--  and Node_Index is not unique within the tree.
--
--  Error recover uses Sequential_Index to determine success, and to
--  control where terminals are inserted and deleted. To be
--  incremental, Sequential_Index is only set in a small portion of
--  the shared stream at error recover start, extended as needed
--  during error recover, and cleared when error recover completes.
--
--  During and after parsing, the sequence of terminals in the parse
--  stream or syntax tree is given by Next_Terminal/Prev_Terminal.
--
--  Each parallel parser uses one stream as the parse stack and
--  auxiliary input stream. The auxiliary input stream contains tokens
--  that are pushed back in error recovery, or broken down from
--  Shared_Stream in incremental parse.
--
--  Nodes that are deleted from the parse stream during error recover
--  are referenced from the preceding terminal node or SOI, so they
--  may be restored on the next incremental parse if appropriate.
--  Similarly, parse errors are referenced from the error node. In
--  order to avoid editing shared nodes, any nodes that are edited to
--  add deleted or error references are copied to the parse stream
--  first.
--
--  Each node contains a Parent link, to make it easy to traverse the
--  tree in any direction after parsing is done. We do not set the
--  Parent links while parsing, to avoid having to copy nodes. During
--  batch parsing, parent links are not set; error recover must use
--  explicit Parent stack versions of tree routines. All Parent links
--  are set when parse completes; condition Tree.Editable ensures that
--  there is a single fully parsed tree with all parent links set. At
--  the start of incremental parse (during and after Edit_Tree), the
--  shared stream has complete parent links. While editing the syntax
--  tree after parse, any functions that modify children or parent
--  relationships update the corresponding links, setting them to
--  Invalid_Node_Access as appropriate.
--
--  We don't store the parse State in syntax tree nodes, to avoid
--  having to copy nodes during parsing. State is stored in the parse
--  stream elements. This means Parse.LR.Undo_Reduce has to call
--  Action_For to compute the state for the child nodes.
--
--  Type Tree is limited because a bit-copy is not a good start on copy
--  for assign; use Copy_Tree.
--
--  We can't traverse Tree.Streams to deallocate tree Nodes, either
--  when streams are terminated or during Finalize; in general Nodes
--  are referenced multiple times in multiple streams. So we keep
--  track of nodes to deallocate in Tree.Nodes. Nodes are deallocated
--  in Clear_Parse_Streams and when the entire tree is Finalized.
--
--  Copyright (C) 2018 - 2021 Free Software Foundation, Inc.
--
--  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.Finalization;
with Ada.Iterator_Interfaces;
with Ada.Unchecked_Deallocation;
with SAL.Gen_Definite_Doubly_Linked_Lists.Gen_Image_Aux;
with SAL.Gen_Definite_Doubly_Linked_Lists_Ref_Count;
with SAL.Gen_Indefinite_Doubly_Linked_Lists;
with SAL.Gen_Trimmed_Image;
with SAL.Gen_Unbounded_Definite_Stacks;
with SAL.Gen_Unbounded_Definite_Vectors;
with SAL.Gen_Unbounded_Sparse_Ordered_Sets;
with SAL.Generic_Decimal_Image;
with WisiToken.Lexer;
package WisiToken.Syntax_Trees is
   use all type Ada.Containers.Count_Type;
   use all type SAL.Base_Peek_Type;

   type Node (<>) is private;
   type Node_Access is access all Node;
   subtype Valid_Node_Access is not null Node_Access;

   Invalid_Node_Access : constant Node_Access := null;
   Dummy_Node : constant Valid_Node_Access;
   --  Use when you must initialize a Valid_Node_Access before overwritting it.

   type Node_Access_Array is array (Positive_Index_Type range <>) of Node_Access;
   type Valid_Node_Access_Array is array (Positive_Index_Type range <>) of Valid_Node_Access;

   function To_Node_Access (Item : in Valid_Node_Access_Array) return Node_Access_Array;
   function To_Valid_Node_Access (Item : in Node_Access_Array) return Valid_Node_Access_Array;

   package Valid_Node_Access_Lists is new SAL.Gen_Definite_Doubly_Linked_Lists (Valid_Node_Access);
   use all type Valid_Node_Access_Lists.Cursor;

   package Node_Stacks is new SAL.Gen_Unbounded_Definite_Stacks (Valid_Node_Access);

   type Stream_ID is private;
   Invalid_Stream_ID : constant Stream_ID;

   type Stream_ID_Array is array (Positive_Index_Type range <>) of Stream_ID;

   type Stream_Index is private;
   Invalid_Stream_Index : constant Stream_Index;

   type Node_Index is new Integer range Integer'First + 1 .. Integer'Last;
   subtype Positive_Node_Index is Node_Index range 1 .. Node_Index'Last;
   Invalid_Node_Index : constant Node_Index := Node_Index'Last;

   type Base_Sequential_Index is new Integer;
   Invalid_Sequential_Index : constant Base_Sequential_Index := Base_Sequential_Index'Last;
   subtype Sequential_Index is Base_Sequential_Index range Base_Sequential_Index'First .. Invalid_Sequential_Index - 1;
   --  Identifies a sequence of tokens in Shared_Stream during error
   --  recovery. Index 1 is the error token (set in
   --  wisitoken-parse-lr-mckenzie_recover-base.adb Initialize).
   --
   --  We need arbitrarily large negative index for Push_Back and
   --  Undo_Reduce error recover operations, and arbitrarily large
   --  positive index for handling unterminated strings.

   type Base_Tree is new Ada.Finalization.Limited_Controlled with record
      --  Visible components of Tree.

      Lexer : WisiToken.Lexer.Handle;
      --  Declared here because it provides access to the source text; any
      --  code that needs access to Tree mostly likely also needs access to
      --  the source text.
   end record;

   type Tree is new Base_Tree with private;
   --  Use Copy_Tree to get a copy.

   type Tree_Variable_Reference (Element : not null access Tree) is null record with
     Implicit_Dereference => Element;

   type Tree_Constant_Reference (Element : not null access constant Tree) is null record with
     Implicit_Dereference => Element;

   overriding procedure Finalize (Tree : in out Syntax_Trees.Tree);
   --  Free any allocated storage.

   procedure Free_Augmented (Tree : in Syntax_Trees.Tree);
   --  Free Augmented in all nodes.

   procedure Clear
     (Tree        : in out Syntax_Trees.Tree;
      Free_Memory : in     Boolean := False)
   with Post => Tree.Cleared;
   --  Delete all nodes in all streams, reset for new lex and parse.
   --  Free_Memory applies to internal bookkeeping; leaving it False may
   --  slightly speed parsing a similar sized file as the previous one.

   function Is_Empty (Tree : in Syntax_Trees.Tree) return Boolean;

   type Error_Data is abstract tagged null record;
   --  A node that has error data is call an "error node".
   --
   --  Since nodes can be shared between parse streams, an error node
   --  must be copied to the parse stream when any change is made to the
   --  error data; different parsers can make different changes.
   --  Update_Error handles this. There are no variable references to
   --  errors.
   --
   --  Errors can contain references to nodes, which must be updated
   --  properly by Copy_Tree; Adjust_Copy must do this. We don't
   --  use "limited" for this to avoid the finalization overhead.

   procedure Adjust_Copy (Data : in out Error_Data)
   is abstract;
   --  Some or all of Data has been copied by Copy_Tree or Copy_Node;
   --  update any Node_Access values using Copied_Node.

   function Dispatch_Equal (Left : in Error_Data; Right : in Error_Data'Class) return Boolean
   is abstract;
   --  True if Left matches Right for purposes of Update_Error and
   --  Delete_Errors_In_Input, below.
   --
   --  Not named "=" because that's always ambiguous with the predefined "=".

   function To_Message
     (Data       : in Error_Data;
      Tree       : in Syntax_Trees.Tree'Class;
      Error_Node : in Valid_Node_Access)
     return Error_Data'Class
   is abstract;
   --  Convert Data to a simple message; it is being moved to another
   --  node (see wisitoken-parse-lr.adb Undo_Reduce).

   function Image
     (Data       : in Error_Data;
      Tree       : in Syntax_Trees.Tree'Class;
      Error_Node : in Valid_Node_Access)
     return String
   is abstract;
   --  Should not include file name, line number; a higher level will add
   --  that if desired.

   function Class_Image (Data : in Error_Data) return String
   is abstract;
   --  Return image of Data'Class; ie "parser" or "lexer". For Tree.Image (Node).

   --  See comment in Put_Tree body about Half_Node_Index
   subtype Half_Node_Index is Node_Index range Node_Index'First / 2 .. Node_Index'Last / 2;
   package Node_Index_Array_Node_Access is new SAL.Gen_Unbounded_Definite_Vectors
     (Half_Node_Index, Node_Access, Default_Element => Invalid_Node_Access);

   procedure Set_Node_Access
     (Data           : in out Error_Data;
      Node_Index_Map : in     Node_Index_Array_Node_Access.Vector)
   is abstract;
   --  Called from Get_Tree to convert Data.Input_Node_Index to *_Node.
   --  Required because some errors reference later nodes.

   procedure Validate_Error
     (Data                : in     Error_Data;
      Tree                : in     Syntax_Trees.Tree'Class;
      Error_Node          : in     Valid_Node_Access;
      Node_Error_Reported : in out Boolean)
   is abstract;
   --  Called by Validate_Tree for each error; validate that any node
   --  links are in Tree.Nodes, and anything else that needs validating.
   --  Node_Error_Reported is described in the
   --  comment for Validate_Node below.

   type Null_Error_Data is new Error_Data with null record;
   --  For Error_Data parameters when there is no error.

   overriding procedure Adjust_Copy (Data : in out Null_Error_Data) is null;
   overriding function Dispatch_Equal
     (Left  : in Null_Error_Data;
      Right : in Error_Data'Class)
     return Boolean
   is (Right in Null_Error_Data);
   overriding function To_Message
     (Data       : in Null_Error_Data;
      Tree       : in Syntax_Trees.Tree'Class;
      Error_Node : in Valid_Node_Access)
     return Error_Data'Class
   is (raise SAL.Programmer_Error);

   overriding function Image
     (Data       : in Null_Error_Data;
      Tree       : in Syntax_Trees.Tree'Class;
      Error_Node : in Valid_Node_Access)
     return String
   is ("null");

   overriding function Class_Image (Data : in Null_Error_Data) return String is ("null");

   overriding procedure Set_Node_Access
     (Data           : in out Null_Error_Data;
      Node_Index_Map : in     Node_Index_Array_Node_Access.Vector)
   is null;

   overriding
   procedure Validate_Error
     (Data                : in     Null_Error_Data;
      Tree                : in     Syntax_Trees.Tree'Class;
      Error_Node          : in     Valid_Node_Access;
      Node_Error_Reported : in out Boolean)
   is null;

   pragma Warnings (Off, """others"" choice is redundant");
   No_Error : constant Null_Error_Data := (others => <>);
   pragma Warnings (On);

   No_Error_Classwide : constant Error_Data'Class := Error_Data'Class (No_Error);

   package Error_Data_Lists is new SAL.Gen_Indefinite_Doubly_Linked_Lists (Error_Data'Class);

   Null_Error_List : Error_Data_Lists.List renames Error_Data_Lists.Empty_List;

   type Error_Data_List_Const_Ref (List : not null access constant Error_Data_Lists.List) is private
   with Implicit_Dereference => List;
   --  There is no "_var_ref"; error nodes must be copied on any change.
   --  See note at Error_Data declaration.

   type Node_Label is
     (Source_Terminal,    -- text is user input, accessed via Lexer
      Virtual_Terminal,   -- no text; inserted during error recovery
      Virtual_Identifier, -- text in user data, created during tree rewrite
      Nonterm);           -- nonterminal node.
   subtype Terminal_Label is Node_Label range Source_Terminal .. Virtual_Identifier;
   subtype Virtual_Terminal_Label is Node_Label range Virtual_Terminal .. Virtual_Identifier;

   function Label (Node : in Valid_Node_Access) return Node_Label;

   function Shared_Stream (Tree : in Syntax_Trees.Tree) return Stream_ID;

   type Stream_Node_Ref is record
      Stream  : Stream_ID;
      Element : Stream_Index;
      Node    : Node_Access  := Invalid_Node_Access;
      --  If both valid, Element contains Node in Stream. In some cases,
      --  Element is valid but Node is Invalid_Node_Access (for example, if
      --  the ref is the First_Terminal in an empty nonterm). In post-parse
      --  actions, the parse stream is deleted, so Stream is
      --  Invalid_Stream_Index, Element is Invalid_Stream_Index, but Node is
      --  valid.
   end record;

   Invalid_Stream_Node_Ref : constant Stream_Node_Ref;

   function Correct_Stream_Node
     (Tree : in Syntax_Trees.Tree;
      Ref  : in Stream_Node_Ref)
     return Boolean;
   --  True if Ref = Invalid_Stream_Node_Ref or Ref.Node =
   --  Invalid_Node_Access or Stream contains Element, which contains
   --  Node.
   --
   --  We allow Ref.Node = Invalid_Node_Access so a Stream_Node_Ref can
   --  be First_Terminal of an empty nonterm, while still allowing
   --  Next_Terminal (Ref).
   --
   --  Note that this is False in post-parse actions; there are no
   --  streams, so Element is Invalid_Stream_Index.

   function Valid_Stream_Node
     (Tree : in Syntax_Trees.Tree;
      Ref  : in Stream_Node_Ref)
     return Boolean;
   --  True if Ref refers to a node (possibly an empty nonterm).

   function To_Stream_Node_Ref
     (Tree    : in Syntax_Trees.Tree;
      Stream  : in Stream_ID;
      Node : in Valid_Node_Access)
     return Stream_Node_Ref
   with Pre => Tree_Parents_Set (Tree),
     Post => Tree.Valid_Stream_Node (To_Stream_Node_Ref'Result);

   subtype Terminal_Ref is Stream_Node_Ref
   with Dynamic_Predicate =>
     Terminal_Ref.Node = Invalid_Node_Access or else
     Label (Terminal_Ref.Node) in Terminal_Label;

   function Valid_Terminal
     (Tree : in Syntax_Trees.Tree;
      Ref  : in Terminal_Ref)
     return Boolean
   is (Valid_Stream_Node (Tree, Ref) and Ref.Node /= Invalid_Node_Access);
   --  True if Ref refers to a Terminal node.

   function Single_Terminal (Ref : in Stream_Node_Ref) return Boolean;
   --  True if Ref contains a single terminal node.

   function Valid_Single_Terminal
     (Tree : in Syntax_Trees.Tree;
      Ref  : in Stream_Node_Ref)
     return Boolean
   is (Valid_Stream_Node (Tree, Ref) and then Single_Terminal (Ref));

   subtype Single_Terminal_Ref is Stream_Node_Ref
   with Dynamic_Predicate =>
     Single_Terminal_Ref.Node = Invalid_Node_Access or else
     Single_Terminal (Single_Terminal_Ref);

   function Rooted (Ref : in Stream_Node_Ref) return Boolean;
   --  True if Ref.Element.Node = Ref.Node.

   subtype Rooted_Ref is Stream_Node_Ref
   with Dynamic_Predicate =>
     (Rooted_Ref = Invalid_Stream_Node_Ref --  allows initialization
        or else Rooted (Rooted_Ref));

   function To_Rooted_Ref
     (Tree    : in Syntax_Trees.Tree;
      Stream  : in Stream_ID;
      Element : in Stream_Index)
     return Rooted_Ref
   with Pre => Element /= Invalid_Stream_Index;

   type Stream_Node_Parents is record
      Ref     : Stream_Node_Ref;
      Parents : Node_Stacks.Stack;
      --  Parents stores the path from Ref.Element.Node to Ref.Node. Parents
      --  is empty if Ref is rooted (ie Ref.Element.Node = Ref.Node). If not
      --  rooted, Parents.Peek (1) is Ref.Node parent, and Parents.Peek
      --  (Parents.Depth) is Ref.Element.Node.
   end record;

   Invalid_Stream_Node_Parents : constant Stream_Node_Parents;

   type Stream_Node_Parents_Array is array (Positive_Index_Type range <>) of Stream_Node_Parents;

   function Parents_Valid (Ref : in Stream_Node_Parents) return Boolean;
   --  True if Parents gives the path from Element.Node to Node, or Element or Node is invalid.

   function Valid_Stream_Node_Parents
     (Tree : in Syntax_Trees.Tree;
      Ref  : in Stream_Node_Parents)
     return Boolean;

   function To_Stream_Node_Parents (Tree : in Syntax_Trees.Tree; Ref : in Stream_Node_Ref) return Stream_Node_Parents
   with Pre => Ref = Invalid_Stream_Node_Ref or else Tree_Parents_Set (Tree) or else
               (Rooted (Ref) or Ref.Node = Tree.First_Terminal (Get_Node (Ref.Element))),
     Post => Parents_Valid (To_Stream_Node_Parents'Result);

   type Recover_Token (Virtual : Boolean := True) is record
      --  Virtual is True if there is no node in the syntax tree that is
      --  this token; it was created by error recover.

      --  Declared here because it needs Node_Access

      --  Maintaining a syntax tree during error recovery is too slow, so we
      --  store enough information in the recover stack to perform
      --  In_Parse_Actions, Language_Fixes, Push_Back and Undo_Reduce
      --  operations. and to apply the solution to the main parser state. We
      --  make thousands of copies of the parse stack during recover, so
      --  minimizing size and compute time for this is critical.

      case Virtual is
      when True =>
         ID : Token_ID := Invalid_Token_ID;

         First_Terminal : Node_Access := Invalid_Node_Access;
         Last_Terminal  : Node_Access := Invalid_Node_Access;
         --  For ID in Nonterminals, first and last terminal of this token in
         --  the Tree shared or parse stream, cached from children;
         --  Invalid_Node_Access if the children are virtual. For terminals,
         --  Invalid_Node_Access. Used to detect empty nonterm and compute
         --  Name.

         Name : Buffer_Region := Null_Buffer_Region;
         --  Set and used by In_Parse_Actions.

         Contains_Virtual_Terminal : Boolean := False;
         --  True if any contained terminal is Virtual.

      when False =>
         Element_Node : Node_Access := Invalid_Node_Access;
         Node         : Node_Access := Invalid_Node_Access;
         --  This token in the Tree shared or parse stream.
         --
         --  This implements a variant of Stream_Node_Ref for recover.
         --
         --  For terminals, Element_Node = Node.
         --
         --  For nonterminals, Node = some descendant of Element_Node (similar
         --  to a Stream_Node_Ref).
         --
         --  This token can be virtual, if from Shared_Stream of an edited
         --  tree.
         --
         --  In a non-default Recover_Token, Element_Node cannot be
         --  Invalid_Node_Access. Node can be Invalid_Node_Access when it is
         --  nominally a terminal and Element_Node is an empty nonterm.
      end case;
   end record;

   subtype Virtual_Recover_Token is Recover_Token (Virtual => True);
   subtype Real_Recover_Token is Recover_Token (Virtual => False);

   Invalid_Recover_Token : constant Recover_Token := (Virtual => True, ID => Invalid_Token_ID, others => <>);

   function Node_ID (Tree : in Syntax_Trees.Tree; Item : in Recover_Token) return Token_ID
   with Pre => Item.Virtual = False;
   function Element_ID (Tree : in Syntax_Trees.Tree; Item : in Recover_Token) return Token_ID;

   function Element_Is_Terminal (Tree : in Syntax_Trees.Tree; Item : in Recover_Token) return Boolean;
   --  Virtual ID or Element_Node.

   function Byte_Region (Tree : in Syntax_Trees.Tree; Item : in Recover_Token) return Buffer_Region;

   function Name (Tree : in Syntax_Trees.Tree; Item : in Recover_Token) return Buffer_Region;
   --  If Node.Name = Null_Buffer_Region and Is_Terminal (Node.ID),
   --  return Node.Byte_Region; else return Node.Name.

   procedure Set_Name
     (Tree : in     Syntax_Trees.Tree;
      Item : in out Recover_Token;
      Name : in     Buffer_Region);

   function Contains_Virtual_Terminal (Tree : in Syntax_Trees.Tree; Item : in Recover_Token) return Boolean;
   function Contains_Virtual_Terminal
     (Tree : in Syntax_Trees.Tree;
      Node : in Valid_Node_Access)
     return Boolean;

   function Is_Empty_Nonterm
     (Tree : in Syntax_Trees.Tree;
      Item : in Recover_Token)
     return Boolean;
   --  True if node contains no terminals.

   function First_Terminal (Tree : in Syntax_Trees.Tree; Item : in Recover_Token) return Node_Access;
   function Last_Terminal (Tree : in Syntax_Trees.Tree; Item : in Recover_Token) return Node_Access;

   function To_Real_Recover_Token (Item : in Stream_Node_Ref) return Real_Recover_Token
   with Pre => Item.Element /= Invalid_Stream_Index;

   function Make_Rooted (Item : in Recover_Token) return Recover_Token;

   function Image
     (Tree : in Syntax_Trees.Tree;
      Item : in Recover_Token)
     return String;

   type Recover_Token_Array is array (Positive_Index_Type range <>) of Recover_Token;

   ----------
   --  User_Data_Type

   type Base_Augmented is tagged null record;

   function Image_Augmented (Aug : in Base_Augmented) return String
   is ("");

   type Augmented_Class_Access is access all Base_Augmented'Class;
   type Augmented_Class_Access_Constant is access constant Base_Augmented'Class;

   procedure Shift
     (Augmented        : in out Base_Augmented;
      Shift_Bytes      : in     Base_Buffer_Pos;
      Shift_Chars      : in     Base_Buffer_Pos;
      Shift_Line       : in     Base_Line_Number_Type;
      Last_Stable_Byte : in     Buffer_Pos)
   is null;
   --  Add Shift_* to Augmented positions.

   procedure Free is new Ada.Unchecked_Deallocation (Base_Augmented'Class, Augmented_Class_Access);

   type User_Data_Type is tagged limited null record;
   --  Many test languages don't need this, so we default the procedures
   --  to null.

   type User_Data_Access is access all User_Data_Type'Class;
   type User_Data_Access_Constant is access constant User_Data_Type'Class;

   procedure Initialize_Actions
     (User_Data : in out User_Data_Type;
      Tree      : in     Syntax_Trees.Tree'Class)
   is null;
   --  Called by Execute_Actions, before processing the tree, after
   --  Insert_Token/Delete_Token.

   procedure Lexer_To_Augmented
     (User_Data     : in out User_Data_Type;
      Tree          : in out Syntax_Trees.Tree'Class;
      Token         : in     Lexer.Token;
      Grammar_Token : in     Valid_Node_Access)
   is null;
   --  Token is a grammar or non-grammar token that was just returned by
   --  User_Data.Lexer. If grammar, it is Grammar_Token; if non-grammar,
   --  it has already been added to Grammar_Token (which is SOI if before
   --  first grammar token in input). Called before parsing, once for
   --  each non-grammar token in the input stream.

   function Copy_Augmented
     (User_Data : in User_Data_Type;
      Augmented : in not null Augmented_Class_Access)
     return Augmented_Class_Access;
   --  Default implementation raises SAL.Programmer_Error.

   function Insert_After
     (User_Data           : in out User_Data_Type;
      Tree                : in     Syntax_Trees.Tree'Class;
      Insert_Token        : in     Valid_Node_Access;
      Insert_Before_Token : in     Valid_Node_Access;
      Comment_Present     : in     Boolean;
      Blank_Line_Present  : in     Boolean)
     return WisiToken.Insert_Location
   with Post'Class => (if not (Blank_Line_Present or Comment_Present) then Insert_After'Result /= Between);
   --  Return an insert location for Insert_Token. This can affect which
   --  line it appears on, which affects indentation. Should be called from
   --  user-overridden Insert_Token.
   --
   --  If Comment_Present, there is a comment between Tree.Prev_Terminal
   --  (Insert_Before_Token) and Insert_Before_Token.
   --
   --  If Blank_Line_Present, there is at least one blank line
   --  immediately after Tree.Prev_Terminal (Insert_Before_Token) (before
   --  any comment).
   --
   --  The default implementation always returns Before_Next.

   procedure Insert_Token
     (User_Data      : in out User_Data_Type;
      Tree           : in out Syntax_Trees.Tree'Class;
      Inserted_Token : in     Syntax_Trees.Valid_Node_Access)
   is null
   with Pre'Class => Tree_Parents_Set (Tree) and Tree.Is_Virtual_Terminal (Inserted_Token);
   --  Inserted_Token was inserted in error recovery. Move
   --  Inserted_Token.Non_Grammar as needed to control which line the
   --  token is on.
   --
   --  Called from Execute_Actions for each inserted token, before
   --  Initialize_Actions.

   procedure Delete_Tokens
     (User_Data  : in out User_Data_Type;
      Tree       : in     Syntax_Trees.Tree'Class;
      Prev_Token : in     Valid_Node_Access)
   is null
   with Pre'Class =>
     Tree_Parents_Set (Tree) and
     Tree.Has_Following_Deleted (Prev_Token);
   --  Prev_Token.Following_Deleted contains tokens that were deleted in
   --  error recovery; Prev_Token is the non-deleted terminal token
   --  before the deleted tokens in the parse stream.
   --
   --  Any Non_Grammar that were on the deleted tokens have been moved to
   --  Prev_Token.Non_Grammar during error recover; user code
   --  may now move them somewhere else if desired.
   --
   --  Called from Execute_Actions for each node with deleted tokens,
   --  before Initialize_Actions.

   procedure Reduce
     (User_Data : in out User_Data_Type;
      Tree      : in     Syntax_Trees.Tree'Class;
      Nonterm   : in     Valid_Node_Access)
   is null;
   --  Called by Parser.Execute_Actions, just before processing Nonterm;
   --  Nonterm was created by a 'reduce' parse action.

   ----------
   --  In_, Post_ Parse_Actions
   --
   --  Declared here because Breakdown needs Optimized_List, and the
   --  actions need Tree and Valid_Node_Access.

   type Post_Parse_Action is access procedure
     (User_Data : in out User_Data_Type'Class;
      Tree      : in out Syntax_Trees.Tree;
      Nonterm   : in     Valid_Node_Access);
   --  Routines of this type are called by
   --  WisiToken.LR.Parser.Execute_Actions when it processes a Nonterm
   --  node in the syntax tree.

   Null_Action : constant Post_Parse_Action := null;

   package In_Parse_Actions is
      type Status_Label is
        (Ok,
         Missing_Name_Error, -- block start has name, required block end name missing
         Extra_Name_Error,   -- block start has no name, end has one
         Match_Names_Error); -- both names present, but don't match

      subtype Error is Status_Label range Status_Label'Succ (Ok) .. Status_Label'Last;

      type Status (Label : Status_Label := Ok) is record
         case Label is
         when Ok =>
            null;

         when Error =>
            Begin_Name : SAL.Base_Peek_Type;
            End_Name   : SAL.Base_Peek_Type;
            --  Begin, End are 0 if the corresponding token is absent (not just
            --  empty) in the production.
         end case;
      end record;

      subtype Error_Status is Status
      with Dynamic_Predicate => Error_Status.Label /= Ok;

      type In_Parse_Action is access function
        (Tree           : in     Syntax_Trees.Tree;
         Nonterm        : in out Recover_Token;
         Tokens         : in     Recover_Token_Array;
         Recover_Active : in     Boolean)
        return Status;
      --  Called during parsing and error recovery to implement higher level
      --  checks, such as block name matching in Ada.
   end In_Parse_Actions;

   type RHS_Info is record
      In_Parse_Action   : In_Parse_Actions.In_Parse_Action := null;
      Post_Parse_Action : Syntax_Trees.Post_Parse_Action   := null;
   end record;

   package RHS_Info_Arrays is new SAL.Gen_Unbounded_Definite_Vectors
     (Natural, RHS_Info, Default_Element => (others => <>));

   type Production_Info is record
      Optimized_List : Boolean := False;
      RHSs           : RHS_Info_Arrays.Vector;
   end record;

   package Production_Info_Trees is new SAL.Gen_Unbounded_Definite_Vectors
     (Token_ID, Production_Info, Default_Element => (others => <>));
   --  Indexed by Production_ID.

   ----------
   --  Parsing operations (including error recovery and incremental
   --  parse), Tree and Node attributes.

   function New_Stream
     (Tree       : in out Syntax_Trees.Tree;
      Old_Stream : in     Stream_ID)
     return Stream_ID
   with
     Pre => Old_Stream = Invalid_Stream_ID or else
            (not Tree_Parents_Set (Tree) and Tree.Stream_Count > 1 and Tree.Is_Valid (Old_Stream)),
     Post => Tree.Is_Valid (New_Stream'Result);
   --  Create a new parse stream, initially copied from Old_Stream.

   function Stream_Count (Tree : in Syntax_Trees.Tree) return SAL.Base_Peek_Type;
   --  Number of parse streams plus the shared stream.

   function First_Parse_Stream (Tree : in Syntax_Trees.Tree) return Stream_ID
   with Pre => Tree.Stream_Count >= 2;

   function Last_Parse_Stream (Tree : in Syntax_Trees.Tree) return Stream_ID
   with Pre => Tree.Stream_Count >= 2;

   procedure Next_Parse_Stream (Tree : in Syntax_Trees.Tree; Stream : in out Stream_ID)
   with Pre => Stream /= Invalid_Stream_ID;

   function Stream_Length (Tree : in Syntax_Trees.Tree; Stream : in Stream_ID) return SAL.Base_Peek_Type
   with Pre => Tree.Is_Valid (Stream);
   --  Stack + input

   function Stream_Input_Length (Tree : in Syntax_Trees.Tree; Stream : in Stream_ID) return SAL.Base_Peek_Type
   with Pre => Tree.Is_Valid (Stream);

   function Stack_Depth (Tree : in Syntax_Trees.Tree; Stream : in Stream_ID) return SAL.Base_Peek_Type
   with Pre => Tree.Is_Valid (Stream);

   procedure Delete_Stream (Tree : in out Syntax_Trees.Tree; Stream : in out Stream_ID)
   with Pre => Tree.Is_Valid (Stream);
   --  Delete the stream

   function Is_Valid (Tree : in Syntax_Trees.Tree; Stream : in Stream_ID) return Boolean;
   --  Stream is available for parsing operations.

   function Contains
     (Tree   : in Syntax_Trees.Tree;
      Stream : in Stream_ID;
      Token  : in Stream_Index)
     return Boolean;
   --  True if Stream and Token are valid, and Token is an element of Stream.

   function Trimmed_Image (Item : in Stream_Index) return String;
   --  Trimmed_Image of item.node_index.

   function Trimmed_Image (Node : in Node_Access) return String;
   --  Trimmed_Image of item.node_index.

   function Get_Node
     (Tree    : in Syntax_Trees.Tree;
      Stream  : in Stream_ID;
      Element : in Stream_Index)
     return Valid_Node_Access
   with Pre => Tree.Contains (Stream, Element) or Tree.Contains (Tree.Shared_Stream, Element);
   --  Parser.Current_Token may be from either stream.

   function Get_Node
     (Element : in Stream_Index)
     return Valid_Node_Access
   with Pre => Element /= Invalid_Stream_Index;
   --  When we don't have the stream.

   procedure Start_Lex
     (Tree           : in out Syntax_Trees.Tree)
   with Pre => Tree.Cleared,
     Post => Tree.Lexable;
   --  Create empty Tree.Shared_Stream, add SOI node containing
   --  Tree.Lexer.Begin_Pos values to it.

   procedure Start_Parse
     (Tree   : in out Syntax_Trees.Tree;
      Stream : in     Stream_ID;
      State  : in     State_Index)
   with Pre => (not Tree.Traversing and Stream /= Tree.Shared_Stream and Tree.Is_Valid (Stream)) and then
               Tree.Stream_Length (Stream) = 0;
   --  State must be the parser start state; it is stored in Tree.SOI.
   --  Sets Tree.Parents_Set False.

   procedure Start_Edit (Tree : in out Syntax_Trees.Tree)
   with Pre => Tree.Editable,
     Post => Tree.Parseable;
   --  Construct Tree.Shared_Stream from Tree.SOI, Tree.Root, Tree.EOI.
   --
   --  On return, Tree is ready for Parse.Edit_Tree.
   --
   --  Parents_Set remains True. Parse.Edit_Tree calls Breakdown on
   --  Shared_Stream Elements, which removes some parent links. However,
   --  the remaining stream elements all have complete parent links; the
   --  links removed point to nodes that are not accessible from the
   --  shared stream.

   function Reduce
     (Tree             : in out Syntax_Trees.Tree;
      Stream           : in     Stream_ID;
      Production       : in     WisiToken.Production_ID;
      Child_Count      : in     Ada.Containers.Count_Type;
      State            : in     State_Index;
      Recover_Conflict : in     Boolean)
     return Rooted_Ref
   with
     Pre => not Tree.Traversing and not Tree_Parents_Set (Tree) and Tree.Is_Valid (Stream) and
            Stream /= Tree.Shared_Stream,
     Post => Reduce'Result.Stream = Stream and Tree.Valid_Stream_Node (Reduce'Result);
   --  Reduce Child_Count tokens on Stream top of stack to a new Nonterm
   --  node on Stream top of stack. Result points to the new Nonterm
   --  node.
   --
   --  Set Result byte_region, char_region, line, column,
   --  first_terminal to min/max of children.
   --
   --  See comment in 'type Node' below for Recover_Conflict.

   procedure Shift
     (Tree   : in out Syntax_Trees.Tree;
      Stream : in     Stream_ID;
      State  : in     State_Index);
   --  Shift Stream current input token onto Stream stack. Then set State
   --  in the Stream element.
   --
   --  Does _not_ clear shifted node Error_List; this shift may be part
   --  of error recover.

   function Pop
     (Tree      : in out Syntax_Trees.Tree;
      Stream    : in     Stream_ID)
     return Valid_Node_Access
   with Pre => not Tree.Traversing and Tree.Is_Valid (Stream);
   --  Delete Stream stack top, returning its node.

   procedure Push
     (Tree   : in out Syntax_Trees.Tree;
      Stream : in     Stream_ID;
      Node   : in     Valid_Node_Access;
      State  : in     State_Index)
   with Pre => not Tree.Traversing and Tree.Is_Valid (Stream);
   --  State, Node become Stream stack top.

   procedure Push_Back
     (Tree   : in out Syntax_Trees.Tree;
      Stream : in     Stream_ID);
   --  Move Stream.Stack_Top to Stream input.

   procedure Move_Shared_To_Input
     (Tree   : in out Syntax_Trees.Tree;
      Stream : in     Stream_ID);
   --  Insert Stream.Shared_Link into Parse_Stream input, increment
   --  Stream.Shared_Link.
   --
   --  This is often needed before Left_Breakdown while parsing.

   procedure Move_Shared_To_Input
     (Tree   : in out Syntax_Trees.Tree;
      First  : in     Stream_Node_Ref;
      Last   : in     Stream_Node_Ref;
      Stream : in     Stream_ID)
   with Pre => Valid_Stream_Node (Tree, First) and Valid_Stream_Node (Tree, Last) and
               First.Stream = Tree.Shared_Stream and Last.Stream = Tree.Shared_Stream and
               Stream /= Tree.Shared_Stream;
   --  Insert Shared_Stream elements First .. Last into Stream input.
   --  Update Parse_Stream.Shared_Link to next Shared_Stream element
   --  after Last.

   procedure Breakdown
     (Tree           : in out Syntax_Trees.Tree;
      Ref            : in out Stream_Node_Parents;
      Productions    : in     Production_Info_Trees.Vector;
      User_Data      : in     Syntax_Trees.User_Data_Access_Constant;
      First_Terminal : in     Boolean)
   with Pre => Tree.Valid_Stream_Node_Parents (Ref) and
               Tree.Label (Ref.Ref.Element) = Nonterm and
               (if First_Terminal
                then not Tree.Is_Empty_Nonterm (Ref.Ref.Node)
                else Get_Node (Ref.Ref.Element) /= Invalid_Node_Access) and
               Tree.Stack_Top (Ref.Ref.Stream) /= Ref.Ref.Element,
     Post => Parents_Valid (Ref) and
             (if First_Terminal
              then Tree.Valid_Terminal (Ref.Ref)
              else Valid_Stream_Node (Tree, Ref.Ref))
             and Ref.Ref.Node =
             (if First_Terminal
              then Tree.First_Terminal (Get_Node (Ref.Ref.Element))
              else Get_Node (Ref.Ref.Element));
   --  Bring descendants of Ref.Element to the parse stream. If
   --  First_Terminal, stop when First_Terminal of one of the parse
   --  stream elements = Ref.Node; otherwise stop when one of the element
   --  nodes = Ref.Node. Ref.Element is updated to the terminating
   --  element.
   --
   --  If Ref.Parents nonterms contain any errors, the errors are moved to
   --  the first terminal of that nonterm, copying ancestor nodes, and
   --  Ref.Parents is updated to match. If Ref.Node is one of those first
   --  terminals, it will be copied.
   --
   --  If one of the nodes brought to the parse stream is an optimized
   --  list, the list is split at the immediate ancestor of Ref.Node.
   --
   --  The stack top is unchanged.
   --
   --  Parent links are set to Invalid_Node_Access as appropriate, but
   --  child links are not, since Breakdown is called from main parse,
   --  where nodes are shared.

   procedure Breakdown
     (Tree           : in out Syntax_Trees.Tree;
      Ref            : in out Stream_Node_Ref;
      Productions    : in     Production_Info_Trees.Vector;
      User_Data      : in     User_Data_Access_Constant;
      First_Terminal : in     Boolean)
   with Pre => Valid_Stream_Node (Tree, Ref) and Tree.Label (Ref.Element) = Nonterm and
               (if First_Terminal
                then not Tree.Is_Empty_Nonterm (Ref.Node)
                else Get_Node (Ref.Element) /= Invalid_Node_Access) and
               Tree.Stack_Top (Ref.Stream) /= Ref.Element,
     Post => Valid_Stream_Node (Tree, Ref);
   --  Same as Breakdown (Stream_Node_Parents), but supports not
   --  Tree.Parents_Set, using an internal Stream_Node_Parent.

   procedure Left_Breakdown
     (Tree      : in out Syntax_Trees.Tree;
      Ref       : in out Stream_Node_Ref;
      User_Data : in     User_Data_Access_Constant)
   with Pre =>
     Valid_Stream_Node (Tree, Ref) and then
     (Tree.Label (Ref.Element) = Nonterm and
        Tree.First_Terminal (Ref).Node /= Invalid_Node_Access and
        Tree.Stack_Top (Ref.Stream) /= Ref.Element),
     Post => Valid_Single_Terminal (Tree, Ref);
   --  Similar to Breakdown; bring first terminal of Ref.Element to
   --  the stream Ref.Stream. Ref.Node is ignored on input.

   function State (Tree : in Syntax_Trees.Tree; Stream : in Stream_ID) return State_Index
   with Pre => Tree.Is_Valid (Stream);
   --  Return State from Stream.Stack_Top.

   function State
     (Tree    : in Syntax_Trees.Tree;
      Stream  : in Stream_ID;
      Element : in Stream_Index)
     return Unknown_State_Index
   with Pre => Tree.Contains (Stream, Element);
   --  If Element is in input, state may be Unknown_State.

   function Stream_First
     (Tree     : in Syntax_Trees.Tree;
      Stream   : in Stream_ID;
      Skip_SOI : in Boolean)
     return Stream_Index
   with Pre => Tree.Is_Valid (Stream);

   function Stream_First
     (Tree     : in Syntax_Trees.Tree;
      Stream   : in Stream_ID;
      Skip_SOI : in Boolean)
     return Rooted_Ref
   with Pre => Tree.Is_Valid (Stream);

   function Stream_Last
     (Tree     : in Syntax_Trees.Tree;
      Stream   : in Stream_ID;
      Skip_EOI : in Boolean)
     return Stream_Index
   with Pre => Tree.Is_Valid (Stream);

   function Stream_Last
     (Tree     : in Syntax_Trees.Tree;
      Stream   : in Stream_ID;
      Skip_EOI : in Boolean)
     return Rooted_Ref
   with Pre => Tree.Is_Valid (Stream);

   function Stack_Top
     (Tree   : in Syntax_Trees.Tree;
      Stream : in Stream_ID)
     return Stream_Index
   with Pre => Tree.Is_Valid (Stream);

   function Has_Input
     (Tree   : in Syntax_Trees.Tree;
      Stream : in Stream_ID)
     return Boolean
   with Pre => Stream /= Tree.Shared_Stream and Tree.Is_Valid (Stream);
   --  Return True if there is a stream element after Stack_Top.

   function First_Input
     (Tree   : in Syntax_Trees.Tree;
      Stream : in Stream_ID)
     return Rooted_Ref
   with Pre => Tree.Has_Input (Stream),
     Post => Correct_Stream_Node (Tree, First_Input'Result);
   --  Return first stream element after Stack_Top.

   function Current_Token
     (Tree   : in Syntax_Trees.Tree;
      Stream : in Stream_ID)
     return Rooted_Ref
   with Post => Correct_Stream_Node (Tree, Current_Token'Result);
   --  If Stream has input, then first input of Stream; otherwise
   --  Stream.Shared_Link.

   function Shared_Token
     (Tree   : in Syntax_Trees.Tree;
      Stream : in Stream_ID)
     return Rooted_Ref
   with Post => Correct_Stream_Node (Tree, Shared_Token'Result);
   --  Stream.Shared_Link.

   procedure Delete_Current_Token
     (Tree   : in out Syntax_Trees.Tree;
      Stream : in     Stream_ID);
   --  User must call Add_Deleted with Deleted_Ref => Tree.Current_Input
   --  before calling Delete_Current_Token.

   function Stream_Next
     (Tree    : in Syntax_Trees.Tree;
      Stream  : in Stream_ID;
      Element : in Stream_Index)
     return Stream_Index
   with Pre => Element = Invalid_Stream_Index or else
               (Tree.Contains (Stream, Element) or Tree.Contains (Tree.Shared_Stream, Element));
   --  If Element is Invalid_Stream_Index, result is Stream_First (= SOI).
   --  Does not follow Stream.Shared_Link

   function Stream_Next
     (Tree : in Syntax_Trees.Tree;
      Ref  : in Stream_Node_Ref)
     return Rooted_Ref
   with Pre => Valid_Stream_Node (Tree, Ref),
     Post => Correct_Stream_Node (Tree, Stream_Next'Result);
   --  Return stream element after Ref.Element.

   procedure Stream_Next
     (Tree   : in     Syntax_Trees.Tree;
      Ref    : in out Stream_Node_Ref;
      Rooted : in     Boolean)
   with Pre => Valid_Stream_Node (Tree, Ref),
     Post => Correct_Stream_Node (Tree, Ref) and then
             (Ref = Invalid_Stream_Node_Ref or else
                (if Rooted
                 then Tree.Get_Node (Ref.Stream, Ref.Element) = Ref.Node
                 else Tree.First_Terminal (Tree.Get_Node (Ref.Stream, Ref.Element)) = Ref.Node));
   --  Update Ref to root or first terminal of next stream element after
   --  Ref.Element. Follows Shared_Link.

   procedure Stream_Next
     (Tree   : in     Syntax_Trees.Tree;
      Ref    : in out Stream_Node_Parents;
      Rooted : in     Boolean)
   with Pre => Correct_Stream_Node (Tree, Ref.Ref) and Parents_Valid (Ref),
     Post => Correct_Stream_Node (Tree, Ref.Ref) and Parents_Valid (Ref) and
             (Ref.Ref = Invalid_Stream_Node_Ref or else
                (if Rooted
                 then Tree.Get_Node (Ref.Ref.Stream, Ref.Ref.Element) = Ref.Ref.Node
                 else Tree.First_Terminal (Tree.Get_Node (Ref.Ref.Stream, Ref.Ref.Element)) = Ref.Ref.Node));
   --  Update Ref to root or first terminal of next stream element after
   --  Ref.Element.

   function Stream_Prev
     (Tree    : in Syntax_Trees.Tree;
      Stream  : in Stream_ID;
      Element : in Stream_Index)
     return Stream_Index
   with Pre => Tree.Contains (Stream, Element);

   function Stream_Prev
     (Tree : in Syntax_Trees.Tree;
      Ref  : in Stream_Node_Ref)
     return Rooted_Ref
   with Pre => Valid_Stream_Node (Tree, Ref),
     Post => Correct_Stream_Node (Tree, Stream_Prev'Result);
   --  Return stream element before Ref.Element.

   procedure Stream_Prev
     (Tree   : in     Syntax_Trees.Tree;
      Ref    : in out Stream_Node_Ref;
      Rooted : in     Boolean := True)
   with Pre => Valid_Stream_Node (Tree, Ref),
     Post => Correct_Stream_Node (Tree, Ref) and
             (Ref = Invalid_Stream_Node_Ref or else
                (if Rooted
                 then Tree.Get_Node (Ref.Stream, Ref.Element) = Ref.Node
                 else Tree.Last_Terminal (Tree.Get_Node (Ref.Stream, Ref.Element)) = Ref.Node));
   --  Update Ref to root or last terminal of stream element before Ref.Element.

   procedure Stream_Prev
     (Tree   : in     Syntax_Trees.Tree;
      Ref    : in out Stream_Node_Parents;
      Rooted : in     Boolean)
   with Pre => Valid_Stream_Node_Parents (Tree, Ref),
     Post => Correct_Stream_Node (Tree, Ref.Ref) and
             (Ref.Ref = Invalid_Stream_Node_Ref or else
                (if Rooted
                 then Tree.Get_Node (Ref.Ref.Stream, Ref.Ref.Element) = Ref.Ref.Node
                 else Tree.Last_Terminal (Tree.Get_Node (Ref.Ref.Stream, Ref.Ref.Element)) = Ref.Ref.Node));

   procedure Stream_Insert
     (Tree   : in out Syntax_Trees.Tree;
      Stream : in     Stream_ID;
      Node   : in     Valid_Node_Access;
      Before : in     Stream_Index)
   with Pre => Tree.Contains (Stream, Before);
   --  Insert a new stream element on Stream containing Node, before
   --  Before. Clears Node.Parent.

   function Stream_Insert
     (Tree   : in out Syntax_Trees.Tree;
      Stream : in     Stream_ID;
      Node   : in     Valid_Node_Access;
      Before : in     Stream_Index)
     return Stream_Node_Ref
   with Pre => Tree.Contains (Stream, Before);
   --  Insert a new stream element on Stream containing Node, before Before.
   --  Result references new element.

   function Peek
     (Tree   : in Syntax_Trees.Tree;
      Stream : in Stream_ID;
      Count  : in SAL.Peek_Type := 1)
     return Stream_Index
   with Pre => Tree.Is_Valid (Stream);
   --  Return Count element on stack in Stream; Count = 1
   --  returns stack top.

   function Add_Terminal
     (Tree     : in out Syntax_Trees.Tree;
      Stream   : in     Stream_ID;
      Terminal : in     Lexer.Token;
      Errors   : in     Error_Data_Lists.List)
     return Single_Terminal_Ref
   with Pre => not Tree.Traversing and Stream = Tree.Shared_Stream,
     Post => Tree.Label (Add_Terminal'Result.Node) = Source_Terminal;
   --  Append a new Source_Terminal element to Stream. Result points to the added
   --  node.

   type Token_Array_Var_Ref (Element : not null access WisiToken.Lexer.Token_Arrays.Vector) is private
   with Implicit_Dereference => Element;

   type Token_Array_Const_Ref (Element : not null access constant WisiToken.Lexer.Token_Arrays.Vector) is private
   with Implicit_Dereference => Element;

   function Has_Non_Grammar
     (Tree     : in Syntax_Trees.Tree;
      Terminal : in Valid_Node_Access)
     return Boolean
   with Pre => Tree.Label (Terminal) in Terminal_Label;

   function Non_Grammar_Var
     (Tree     : in Syntax_Trees.Tree;
      Terminal : in Valid_Node_Access)
     return Token_Array_Var_Ref
   with Pre => Tree.Label (Terminal) in Terminal_Label;

   function Non_Grammar_Const
     (Tree     : in Syntax_Trees.Tree;
      Terminal : in Valid_Node_Access)
     return Token_Array_Const_Ref
   with Pre => Tree.Label (Terminal) in Terminal_Label;

   procedure Insert_Source_Terminal
     (Tree     : in out Syntax_Trees.Tree;
      Stream   : in     Stream_ID;
      Terminal : in     Lexer.Token;
      Before   : in     Stream_Index;
      Errors   : in     Error_Data_Lists.List)
   with Pre => not Tree.Traversing and (Before = Invalid_Stream_Index or else Tree.Contains (Stream, Before));
   --  Insert a new Source_Terminal element on Stream, before Before.

   function Insert_Source_Terminal
     (Tree     : in out Syntax_Trees.Tree;
      Stream   : in     Stream_ID;
      Terminal : in     Lexer.Token;
      Before   : in     Stream_Index;
      Errors   : in     Error_Data_Lists.List)
     return Single_Terminal_Ref
   with Pre => not Tree.Traversing and (Before = Invalid_Stream_Index or else Tree.Contains (Stream, Before)),
     Post => Tree.Label (Insert_Source_Terminal'Result.Node) = Source_Terminal;
   --  Insert a new Source_Terminal element on Stream, before Before.
   --  Result points to the added element.

   function Insert_Virtual_Terminal
     (Tree     : in out Syntax_Trees.Tree;
      Stream   : in     Stream_ID;
      Terminal : in     Token_ID)
     return Single_Terminal_Ref
   with
     Pre  => not Tree.Traversing and Tree.Is_Valid (Stream) and Stream /= Tree.Shared_Stream,
     Post => Tree.Label (Insert_Virtual_Terminal'Result.Node) = Virtual_Terminal;
   --  Insert a new Virtual_Terminal element into Stream, after
   --  Stack_Top. Result refers to the added node.

   procedure Shift
     (Tree             : in     Syntax_Trees.Tree;
      Node             : in     Valid_Node_Access;
      Shift_Bytes      : in     Base_Buffer_Pos;
      Shift_Chars      : in     Base_Buffer_Pos;
      Shift_Lines      : in     Base_Line_Number_Type;
      Last_Stable_Byte : in     Base_Buffer_Pos;
      Non_Grammar_Next : in out Lexer.Token_Arrays.Extended_Index)
   with Pre => Tree.Label (Node) in Terminal_Label;
   --  Add Shift_* to token, non_grammar, and augmented corresponding
   --  regions. If a non_grammar is adjacent to or after
   --  Last_Stable_Byte, set Non_Grammar_Next to it, without shifting,
   --  and skip the rest of non_grammar.

   procedure Set_Node_Index
     (Tree       : in Syntax_Trees.Tree;
      Node       : in Valid_Node_Access;
      Node_Index : in Syntax_Trees.Node_Index);

   procedure Stream_Delete
     (Tree    : in out Syntax_Trees.Tree;
      Stream  : in     Stream_ID;
      Element : in out Stream_Index)
   with
     Pre  => Tree.Contains (Stream, Element),
     Post => Element = Invalid_Stream_Index;
   --  Delete Element from Stream. If Element = Stream.Stack_Top,
   --  Stack_Top is set to Invalid_Stream_Index.

   function Label (Tree : in Syntax_Trees.Tree; Node : in Valid_Node_Access) return Node_Label;
   function Label (Tree : in Syntax_Trees.Tree; Element : in Stream_Index) return Node_Label
   with Pre => Element /= Invalid_Stream_Index;

   function Child_Count (Tree : in Syntax_Trees.Tree; Node : in Valid_Node_Access) return SAL.Base_Peek_Type;

   function Children (Tree : in Syntax_Trees.Tree; Node : in Valid_Node_Access) return Node_Access_Array
   with Pre => Tree.Is_Nonterm (Node);
   --  Any children that were deleted by tree editing are returned as
   --  Invalid_Node_Access.

   function Child
     (Tree        : in Syntax_Trees.Tree;
      Node        : in Valid_Node_Access;
      Child_Index : in Positive_Index_Type)
     return Node_Access
   with Pre => Tree.Is_Nonterm (Node);
   --  Returns Invalid_Node_Access if Child_Index is outside range of
   --  Node.Children.

   function Has_Children (Tree : in Syntax_Trees.Tree; Node : in Valid_Node_Access) return Boolean
   with Pre => Tree.Is_Nonterm (Node);

   function Has_Child
     (Tree  : in Syntax_Trees.Tree;
      Node  : in Valid_Node_Access;
      Child : in Valid_Node_Access)
     return Boolean
   with Pre => Tree.Is_Nonterm (Node);

   function Has_Parent (Tree : in Syntax_Trees.Tree; Child : in Valid_Node_Access) return Boolean;

   function Is_Nonterm (Tree : in Syntax_Trees.Tree; Node : in Valid_Node_Access) return Boolean;
   function Is_Source_Terminal (Tree : in Syntax_Trees.Tree; Node : in Valid_Node_Access) return Boolean;
   function Is_Virtual_Terminal (Tree : in Syntax_Trees.Tree; Node : in Valid_Node_Access) return Boolean;
   function Is_Terminal (Tree : in Syntax_Trees.Tree; Node : in Valid_Node_Access) return Boolean;

   function Is_Empty_Nonterm (Tree : in Syntax_Trees.Tree; Node : in Valid_Node_Access) return Boolean;
   --  True if Node contains no terminals.

   function Is_Empty_Or_Virtual_Nonterm
     (Tree : in Syntax_Trees.Tree;
      Node : in Valid_Node_Access)
     return Boolean;
   --  True if Node contains no terminals, or all terminals are virtual,
   --  and thus have an empty Buffer_Region for Byte_ and Char_Region.

   function Is_Virtual_Identifier (Tree : in Syntax_Trees.Tree; Node : in Valid_Node_Access) return Boolean;
   function Recover_Conflict (Tree : in Syntax_Trees.Tree; Node : in Valid_Node_Access) return Boolean
   with Pre => Tree.Label (Node) = Nonterm;

   function Traversing (Tree : in Syntax_Trees.Tree) return Boolean;

   procedure Set_Insert_Location
     (Tree            : in Syntax_Trees.Tree;
      Node            : in Valid_Node_Access;
      Insert_Location : in WisiToken.Insert_Location)
   with Pre => Tree.Is_Virtual_Terminal (Node);

   procedure Set_Name
     (Tree   : in Syntax_Trees.Tree;
      Node   : in Valid_Node_Access;
      Region : in Buffer_Region)
   with Pre => Tree.Is_Nonterm (Node);

   function ID (Tree : in Syntax_Trees.Tree; Node : in Valid_Node_Access) return WisiToken.Token_ID;
   function ID (Tree : in Syntax_Trees.Tree; Element : in Stream_Index) return Token_ID;

   function Production_ID
     (Tree : in Syntax_Trees.Tree;
      Node : in Valid_Node_Access)
     return WisiToken.Production_ID
   with Pre => Tree.Is_Nonterm (Node);

   function Byte_Region (Tree : in Syntax_Trees.Tree; Index : in Stream_Index) return WisiToken.Buffer_Region
   with Pre => Index /= Invalid_Stream_Index;

   function Byte_Region
     (Tree                 : in Syntax_Trees.Tree;
      Node                 : in Valid_Node_Access;
      Trailing_Non_Grammar : in Boolean)
     return WisiToken.Buffer_Region;
   --  If Trailing_Non_Grammar, any non_grammar attached to last terminal
   --  in Node is included in region.
   --
   --  If Tree.Parents_Set:
   --
   --  Byte_Region of Virtual_Terminal is an empty region with .First
   --  determined by Insert_Location, using previous or next
   --  source_terminal or non_grammar.
   --
   --  Byte_Region of an empty nonterm with Trailing_Non_Grammar False is
   --  an empty region; .First gives nominal location in source text,
   --  using previous or next source_terminal or non_grammar.
   --
   --  If not Tree.Parents_Set, does as much of the above as possible,
   --  returning Null_Buffer_Region if would need Parents_Set.

   function Byte_Region
     (Tree                 : in Syntax_Trees.Tree;
      Ref                  : in Stream_Node_Ref;
      Trailing_Non_Grammar : in Boolean := False)
     return WisiToken.Buffer_Region
   with Pre => Tree_Parents_Set (Tree) and Valid_Stream_Node (Tree, Ref);
   --  Return Byte_Region of Ref.Node, using stream to find prev, next
   --  non_grammar if needed.

   function Byte_Region
     (Tree                 : in Syntax_Trees.Tree;
      Ref                  : in Stream_Node_Parents;
      Parse_Stream         : in Stream_ID;
      Trailing_Non_Grammar : in Boolean := False)
     return WisiToken.Buffer_Region
   with Pre => Tree.Valid_Stream_Node_Parents (Ref);
   --  Same as Byte_Region (Stream_Node_Ref), for use when parents are
   --  not set. See Prev_Terminal (tree, stream_node_parents) for meaning
   --  of Parse_Stream.

   function Name (Tree : in Syntax_Trees.Tree; Node : in Valid_Node_Access) return Buffer_Region;
   --  If Node.Label in Terminal_Label, return Node.Byte_Region; else if
   --  Node.Name is not Null_Buffer_Region, return Node.Name; else return
   --  Node.Byte_Region.

   function Name (Tree : in Syntax_Trees.Tree; Ref : in Stream_Node_Ref) return Buffer_Region
   with Pre => Valid_Stream_Node (Tree, Ref);
   --  Call Name with Ref.Element.Node.

   function Char_Region
     (Tree                 : in Syntax_Trees.Tree;
      Node                 : in Valid_Node_Access;
      Trailing_Non_Grammar : in Boolean)
     return WisiToken.Buffer_Region;
   --  Similar to Byte_Region.

   function Line_At_Byte_Pos
     (Tree     : in Syntax_Trees.Tree;
      Byte_Pos : in Buffer_Pos)
     return Base_Line_Number_Type
   with Pre => Tree.Editable;
   --  Return line that contains Byte_Pos; Invalid_Line_Number if outside
   --  range of text spanned by Tree. If Byte_Pos is on a New_Line,
   --  result is the line that the character ends.

   function Line_Region (Tree : in Syntax_Trees.Tree) return WisiToken.Line_Region
   with Pre => Tree.SOI /= Invalid_Node_Access and Tree.EOI /= Invalid_Node_Access;
   --  Region spanned by entire tree; from SOI and EOI.

   function Line_Region
     (Tree                 : in Syntax_Trees.Tree;
      Node                 : in Valid_Node_Access;
      Trailing_Non_Grammar : in Boolean)
     return WisiToken.Line_Region
   with Pre => Tree.Editable;
   --  Lines of tokens in Node. First is the line started by the first
   --  New_Line or SOI (start of input) before the first terminal in
   --  Node. If Trailing_Non_Grammar and the last terminal of Node has
   --  Non_Grammar, Last is the line ended by the last New_Line of
   --  Last_Terminal (Node).Non_Grammar; if not Trailing_Non_Grammar or
   --  Node does not have Non_Grammar, Last is the line ended by the
   --  first New_Line or EOI after the last terminal of Node.
   --
   --  Trailing_Non_Grammar => False is used to get the line_region of a
   --  multi-line token.

   function Line_Region
     (Tree                 : in Syntax_Trees.Tree;
      Ref                  : in Stream_Node_Ref;
      Trailing_Non_Grammar : in Boolean)
     return WisiToken.Line_Region
   with Pre => Tree.Valid_Stream_Node (Ref) and
               (Tree_Parents_Set (Tree) or else
                  Rooted (Ref) or else
                  Ref.Node = Tree.First_Terminal (Get_Node (Ref.Element)));
   --  Same as Line_Region (Ref.Node), using Ref.Stream to find
   --  prev/next non_grammar.
   --
   --  If not Tree.Parents_Set, constructs a Stream_Node_Parents
   --  internally.

   function Byte_Region_Of_Line_Region
     (Tree : in Syntax_Trees.Tree;
      Ref  : in Stream_Node_Ref)
     return WisiToken.Buffer_Region
   with Pre => Tree.Valid_Stream_Node (Ref) and Tree_Parents_Set (Tree);

   function Line_Region
     (Tree                 : in Syntax_Trees.Tree;
      Ref                  : in Stream_Node_Parents;
      Parse_Stream         : in Stream_ID;
      Trailing_Non_Grammar : in Boolean := True)
     return WisiToken.Line_Region
   with Pre => Tree.Valid_Stream_Node_Parents (Ref);
   --  Same as Line_Region (Stream_Node_Ref), for use when parents are
   --  not set. See Prev_Terminal (tree, stream_node_parents) for meaning
   --  of Parse_Stream.

   function Line_Region
     (Tree   : in Syntax_Trees.Tree;
      Stream : in Stream_ID;
      Ref    : in Real_Recover_Token)
     return WisiToken.Line_Region
   with Pre => Ref.Element_Node = Ref.Node or Ref.Node = Tree.First_Terminal (Ref.Element_Node);
   --  Constructs a Stream_Node_Parents from Stream, Ref. Assumes
   --  Trailing_Non_Grammar => True. For use in error recovery.

   function Column (Tree : in Syntax_Trees.Tree; Node : in Valid_Node_Access) return Ada.Text_IO.Count
   with Pre => Tree.Editable and Tree.Subtree_Root (Node) = Tree.Root;
   --  Column of first char of Node; offset from first character on line,
   --  origin 0 (WisiToken and Emacs standard). If Node is empty or
   --  Virtual, result is 0.

   function Column
     (Tree   : in Syntax_Trees.Tree;
      Node   : in Valid_Node_Access;
      Stream : in Stream_ID)
     return Ada.Text_IO.Count;
   --  Same as Column, but Node must be in Stream or Shared_Stream.

   function RHS_Index
     (Tree : in Syntax_Trees.Tree;
      Node : in Valid_Node_Access)
     return Natural
   with Pre => Tree.Is_Nonterm (Node);

   function Get_Recover_Token
     (Tree : in Syntax_Trees.Tree;
      Ref  : in Stream_Node_Ref)
     return Recover_Token;

   function Get_Recover_Token
     (Tree : in Syntax_Trees.Tree;
      Node : in Valid_Node_Access)
     return Recover_Token;
   --  Treat Node as a stream element.

   function Children_Recover_Tokens
     (Tree : in Syntax_Trees.Tree;
      Node : in Valid_Node_Access)
     return Recover_Token_Array
   with Pre => Tree.Label (Node) = Nonterm;

   function Children_Recover_Tokens
     (Tree    : in Syntax_Trees.Tree;
      Stream  : in Stream_ID;
      Element : in Stream_Index)
     return Recover_Token_Array
   with Pre => Tree.Contains (Stream, Element) and Tree.Label (Element) = Nonterm;

   procedure Set_Augmented
     (Tree  : in Syntax_Trees.Tree;
      Node  : in Valid_Node_Access;
      Value : in Augmented_Class_Access)
   with Pre => Tree.Augmented (Node) = null;
   --  Value will be deallocated when Tree is finalized.

   function Augmented
     (Tree : in Syntax_Trees.Tree;
      Node : in Valid_Node_Access)
     return Augmented_Class_Access;
   --  Returns result of Set_Augmented.

   function Augmented_Const
     (Tree : in Syntax_Trees.Tree;
      Node : in Valid_Node_Access)
     return Augmented_Class_Access_Constant;

   procedure Clear_Augmented (Tree : in Syntax_Trees.Tree);
   --  Free all Augmented in Tree.

   function Find_Ancestor
     (Tree       : in Syntax_Trees.Tree;
      Node       : in Valid_Node_Access;
      ID         : in Token_ID;
      Max_Parent : in Boolean := False)
     return Node_Access
   with Pre => Tree_Parents_Set (Tree);
   function Find_Ancestor
     (Tree       : in Syntax_Trees.Tree;
      Node       : in Valid_Node_Access;
      IDs        : in Token_ID_Array;
      Max_Parent : in Boolean := False)
     return Node_Access
   with Pre => Tree_Parents_Set (Tree);
   --  Return the ancestor of Node that contains one of IDs (starting
   --  search with Node.Parent), or Invalid_Node_Access if none match.
   --
   --  If Max_Parent, return max parent found if none match; this will be
   --  Invalid_Node_Access if Node has no parent.

   function Find_Sibling
     (Tree : in Syntax_Trees.Tree;
      Node : in Valid_Node_Access;
      ID   : in Token_ID)
     return Node_Access
   with Pre => Tree_Parents_Set (Tree) and Tree.Has_Parent (Node);
   --  Return the sibling of Node that contains ID, or Invalid_Node_Access if
   --  none match.

   function Find_Child
     (Tree : in Syntax_Trees.Tree;
      Node : in Valid_Node_Access;
      ID   : in Token_ID)
     return Node_Access
   with Pre => Tree.Is_Nonterm (Node);
   --  Return the child of Node whose ID is ID, or Invalid_Node_Access if
   --  none match.

   function Find_Descendant
     (Tree : in Syntax_Trees.Tree;
      Node : in Valid_Node_Access;
      ID   : in Token_ID)
     return Node_Access;
   --  Return the descendant of Node (may be Node; depth first search)
   --  whose ID is ID, or Invalid_Node_Access if none match.

   function Find_Descendant
     (Tree      : in     Syntax_Trees.Tree;
      Node      : in     Valid_Node_Access;
      Predicate : access function (Tree : in Syntax_Trees.Tree; Node : in Valid_Node_Access) return Boolean)
     return Node_Access;
   --  Return the first descendant of Node (may be Node; depth first
   --  search) for which Predicate returns True, or Invalid_Node_Access
   --  if none do.

   function Is_Descendant_Of
     (Tree       : in Syntax_Trees.Tree;
      Root       : in Valid_Node_Access;
      Descendant : in Valid_Node_Access)
     return Boolean
   with Pre => Tree_Parents_Set (Tree);

   function Subtree_Root (Tree : in Syntax_Trees.Tree; Node : in Valid_Node_Access) return Valid_Node_Access
   with Pre => Tree_Parents_Set (Tree);
   --  Return top ancestor of Node.

   procedure Process_Tree
     (Tree         : in out Syntax_Trees.Tree;
      Process_Node : access procedure
        (Tree : in out Syntax_Trees.Tree;
         Node : in     Valid_Node_Access);
      Root         : in     Node_Access := Invalid_Node_Access)
   with Pre => Root /= Invalid_Node_Access or else Tree.Root /= Invalid_Node_Access;
   --  Traverse subtree of Tree rooted at Root (default single remaining
   --  stream element) in depth-first order, calling Process_Node on each
   --  node.

   function Identifier (Tree : in Syntax_Trees.Tree; Node : in Valid_Node_Access) return Base_Identifier_Index
   with Pre => Tree.Is_Virtual_Identifier (Node);

   function Prev_Non_Grammar
     (Tree : in Syntax_Trees.Tree;
      Node : in Valid_Node_Access)
     return Node_Access
   with Pre => Tree_Parents_Set (Tree);
   --  Return first node before Node that has a non-empty Non_Grammar.
   --  If Node = Tree.Root or Tree.SOI, return Tree.SOI.
   --
   --  Returns Invalid_Node_Access only in broken trees; we tolerate this
   --  here so we can use this in Error_Message.

   procedure Prev_Non_Grammar
     (Tree : in     Syntax_Trees.Tree;
      Ref  : in out Stream_Node_Ref);

   procedure Prev_Non_Grammar
     (Tree         : in     Syntax_Trees.Tree;
      Ref          : in out Stream_Node_Parents;
      Parse_Stream : in     Stream_ID)
   with Pre => Tree.Valid_Stream_Node_Parents (Ref),
     Post => Tree.Correct_Stream_Node (Ref.Ref) and Parents_Valid (Ref);

   function First_Non_Grammar
     (Tree : in Syntax_Trees.Tree;
      Node : in Valid_Node_Access)
     return Node_Access;
   --  Return first node in subtree under Node that has a non-empty
   --  Non_Grammar.
   --  Invalid_Node_Access if none.

   function Last_Non_Grammar
     (Tree : in Syntax_Trees.Tree;
      Node : in Valid_Node_Access)
     return Node_Access;
   --  Return last node in subtree under Node that has a non-empty
   --  Non_Grammar.
   --  Invalid_Node_Access if none.

   function Next_Non_Grammar
     (Tree : in Syntax_Trees.Tree;
      Node : in Valid_Node_Access)
     return Valid_Node_Access
   with Pre => Tree_Parents_Set (Tree);
   --  Return first node after Last_Terminal (Node) that has a non-empty
   --  Non_Grammar. If Node = Tree.Root or Tree.EOI, return Tree.EOI.

   procedure Next_Non_Grammar
     (Tree : in     Syntax_Trees.Tree;
      Ref  : in out Stream_Node_Parents)
   with Pre => Tree.Valid_Stream_Node_Parents (Ref),
     Post => Tree.Correct_Stream_Node (Ref.Ref) and Parents_Valid (Ref);

   type New_Line_Ref is record
      Node : Node_Access;
      --  Node is Invalid_Node_Access, or Node.Label is in Source_Terminal |
      --  Virtual_Terminal.

      Non_Grammar_Index : SAL.Base_Peek_Type := 0;
      --  If Node is Invalid_Node_Access, then there is no new_line. If
      --  Non_Grammar_Index is 0, Node is a Source_Terminal containing at
      --  least one new_line. Otherwise Non_Gramamr_Index is the index in
      --  Node.Non_Grammar containing a new_line.

      First : Boolean := True;
      --  If First, the new_line is the first in the token or
      --  non_grammar; it was found by Next_New_Line. If not First, the
      --  new_line is the last; it was found by Prev_New_Line;

      Pos : Base_Buffer_Pos := Invalid_Buffer_Pos;
      --  The byte position of the new_line.

      Line : Base_Line_Number_Type := Invalid_Line_Number;
      --  The line number after the new_line.
   end record;

   Invalid_New_Line_Ref : constant New_Line_Ref := (others => <>);

   function Prev_New_Line
     (Tree       : in Syntax_Trees.Tree;
      Node       : in Valid_Node_Access;
      Start_Line : in Base_Line_Number_Type := Invalid_Line_Number)
     return New_Line_Ref
   with Pre => Tree_Parents_Set (Tree);
   --  If Node is SOI, returns reference to SOI.Non_Grammar (1).
   --  Otherwise, return a reference to the first New_Line preceding
   --  First_Terminal (Node).Byte_Region.First.
   --
   --  If Start_Line is not Invalid_Line_Number, it must be the line
   --  number at the beginning of Node. If Start_Line is
   --  Invalid_Line_Number, first searches towards SOI for a non_grammar
   --  giving a line number, computes Start_Line, and continues as
   --  above.

   function Line_At_Node
     (Tree   : in Syntax_Trees.Tree;
      Stream : in Stream_ID;
      Ref    : in Real_Recover_Token)
     return Base_Line_Number_Type;
   --  Return line at first byte of Ref.Node; Invalid_Line_Number if too hard to find.

   function Line_At_Node
     (Tree : in Syntax_Trees.Tree;
      Node : in Valid_Node_Access)
     return Line_Number_Type
   with Pre => Tree_Parents_Set (Tree);
   --  Return line at first byte of Node.

   function Line_At_Node
     (Tree         : in Syntax_Trees.Tree;
      Ref          : in Stream_Node_Ref;
      Parse_Stream : in Stream_ID)
     return Line_Number_Type
   with Pre => Valid_Stream_Node (Tree, Ref);
   --  Return line at first byte of Ref.Node.

   function Line_At_Node
     (Tree         : in Syntax_Trees.Tree;
      Ref          : in Stream_Node_Parents;
      Parse_Stream : in Stream_ID)
     return Line_Number_Type
   with Pre => Valid_Stream_Node_Parents (Tree, Ref);
   --  Return line at first byte of Ref.Node.

   function First_Source_Terminal
     (Tree                 : in Syntax_Trees.Tree;
      Node                 : in Valid_Node_Access;
      Trailing_Non_Grammar : in Boolean;
      Following            : in Boolean)
     return Node_Access
   with Pre => (if Following then Tree_Parents_Set (Tree) else True),
    Post =>
       (if Following
        then First_Source_Terminal'Result /= Invalid_Node_Access and then
         (Tree.Label (First_Source_Terminal'Result) = Source_Terminal or
            Tree.Non_Grammar_Const (First_Source_Terminal'Result).Length > 0));
   --  Return a terminal node that can give byte or char pos.
   --
   --  If Trailing_Non_Grammar, return first terminal in Node that is a
   --  Source_Terminal, or a virtual terminal with non-empty non_grammar.
   --  If not Trailing_Non_Grammar, only return a Source_Terminal.
   --
   --  If Following, return a matching terminal following Node if none
   --  found in Node.

   function Next_Source_Terminal
     (Tree                 : in Syntax_Trees.Tree;
      Node                 : in Valid_Node_Access;
      Trailing_Non_Grammar : in Boolean)
     return Node_Access
   with Pre => Tree_Parents_Set (Tree);
   --  Return the next terminal node after Node that can give byte or
   --  char pos; Invalid_Node_Access if there is no such node.
   --
   --  If Trailing_Non_Grammar, return next terminal after Ref.Ref.Node
   --  that is a Source_Terminal, or a virtual terminal with non-empty
   --  non_grammar. If not Trailing_Non_Grammar, only return a
   --  Source_Terminal.

   procedure Next_Source_Terminal
     (Tree                 : in     Syntax_Trees.Tree;
      Ref                  : in out Stream_Node_Ref;
      Trailing_Non_Grammar : in     Boolean)
   with Pre => Valid_Stream_Node (Tree, Ref) and Tree_Parents_Set (Tree),
     Post => Tree.Correct_Stream_Node (Ref);
   --  Update Ref to the next terminal node that can give byte or char
   --  pos.
   --
   --  If Trailing_Non_Grammar, return next terminal after Ref.Node
   --  that is a Source_Terminal, or a virtual terminal with non-empty
   --  non_grammar. If not Trailing_Non_Grammar, only return a
   --  Source_Terminal.

   function Prev_Source_Terminal
     (Tree                 : in Syntax_Trees.Tree;
      Node                 : in Valid_Node_Access;
      Trailing_Non_Grammar : in Boolean)
     return Node_Access
   with Pre => Tree_Parents_Set (Tree);

   function Prev_Source_Terminal
     (Tree                 : in Syntax_Trees.Tree;
      Ref                  : in Stream_Node_Ref;
      Trailing_Non_Grammar : in Boolean)
     return Stream_Node_Ref
   with Pre => Valid_Stream_Node (Tree, Ref) and Tree_Parents_Set (Tree),
     Post => Tree.Correct_Stream_Node (Prev_Source_Terminal'Result);
   --  Return the previous terminal node that can give byte or char pos.
   --
   --  If Trailing_Non_Grammar, return prev terminal before Ref.Ref.Node
   --  that is a Source_Terminal, or a virtual terminal with non-empty
   --  non_grammar. If not Trailing_Non_Grammar, only return a
   --  Source_Terminal.

   procedure Prev_Source_Terminal
     (Tree                 : in     Syntax_Trees.Tree;
      Ref                  : in out Stream_Node_Parents;
      Parse_Stream         : in     Stream_ID;
      Trailing_Non_Grammar : in     Boolean)
   with Pre => Valid_Stream_Node_Parents (Tree, Ref),
     Post   => Tree.Correct_Stream_Node (Ref.Ref);

   function Count_Terminals (Tree : in Syntax_Trees.Tree; Node : in Valid_Node_Access) return Natural;

   function Get_Terminals (Tree : in Syntax_Trees.Tree; Node : in Valid_Node_Access) return Valid_Node_Access_Array
   with Post => (for all Node of Get_Terminals'Result => Tree.Label (Node) in Terminal_Label);
   --  Return sequence of terminals in Node.

   function Get_Terminal_IDs (Tree : in Syntax_Trees.Tree; Node : in Valid_Node_Access) return Token_ID_Array;
   --  Same as Get_Terminals, but return the IDs.

   function First_Terminal (Tree : in Syntax_Trees.Tree; Node : in Valid_Node_Access) return Node_Access;
   --  First of Get_Terminals. Invalid_Node_Access if Node is an empty nonterminal.

   function First_Terminal
     (Tree    : in     Syntax_Trees.Tree;
      Node    : in     Valid_Node_Access;
      Parents : in out Node_Stacks.Stack)
     return Node_Access;
   --  Same as First_Terminal (Tree, Node), also updates Parents to
   --  store path from Node to the first terminal.

   procedure First_Terminal
     (Tree : in     Syntax_Trees.Tree;
      Ref  : in out Stream_Node_Ref)
   with Pre => Valid_Stream_Node (Tree, Ref),
     Post => Valid_Stream_Node (Tree, Ref);
   --  Update Ref to first terminal of Ref.Element.Node or a following
   --  stream element. Continues search in Shared_Stream at
   --  Stream.Shared_Link; will always find EOI.

   function First_Terminal
     (Tree : in Syntax_Trees.Tree;
      Ref  : in Stream_Node_Ref)
     return Terminal_Ref
   with Pre => Valid_Stream_Node (Tree, Ref),
     Post => Valid_Stream_Node (Tree, First_Terminal'Result);
   --  Return first terminal in Ref.Element.Node or a following stream element.
   --  Continues search in Shared_Stream; will always find EOI, so never
   --  Invalid_Stream_Element.
   --
   --  Use First_Terminal_In_Node to not look in following stream
   --  elements.

   function First_Terminal_In_Node
     (Tree : in Syntax_Trees.Tree;
      Ref  : in Stream_Node_Ref)
     return Terminal_Ref
   with Pre => Valid_Stream_Node (Tree, Ref);
   --  Return first terminal in Ref.Node; Invalid_Node_Access if none.

   procedure First_Terminal
     (Tree      : in     Syntax_Trees.Tree;
      Ref       : in out Stream_Node_Parents;
      Following : in     Boolean)
   with Pre => Tree.Valid_Stream_Node_Parents (Ref),
     Post => Parents_Valid (Ref);
   --  Update Ref to first terminal in Ref.Ref.Node or, if Following, a
   --  following stream element - continues search in Shared_Stream.

   function First_Terminal
     (Tree : in Syntax_Trees.Tree;
      Ref  : in Rooted_Ref)
     return Stream_Node_Parents
   with Pre => Valid_Stream_Node (Tree, Ref),
     Post => Valid_Stream_Node (Tree, First_Terminal'Result.Ref) and
             Label (First_Terminal'Result.Ref.Node) in Terminal_Label and
             Parents_Valid (First_Terminal'Result);
   --  Return first terminal in Ref.Node or a following element.

   function Last_Terminal (Tree : in Syntax_Trees.Tree; Node : in Valid_Node_Access) return Node_Access;
   --  Last terminal in subtree under Node. Invalid_Node_Access if none.

   function Last_Terminal
     (Tree    : in     Syntax_Trees.Tree;
      Node    : in     Valid_Node_Access;
      Parents : in out Node_Stacks.Stack)
     return Node_Access;
   --  Same as Last_Terminal, also initializes Parents.
   --
   --  We don't have "Pre => Parents.Is_Empty" or "Post => Parents_Valid
   --  (Parents, Last_Terminal'Result)", because we call this function
   --  recursively to build Parents.
   --
   --  Visible for use with error recovery Configuration input stream.

   procedure Last_Terminal
     (Tree         : in     Syntax_Trees.Tree;
      Ref          : in out Stream_Node_Parents;
      Parse_Stream : in     Stream_ID;
      Preceding    : in     Boolean)
   with Pre => Valid_Stream_Node_Parents (Tree, Ref),
     Post => Parents_Valid (Ref);
   --  Update Ref to last terminal of Ref.Ref.Element.Node or, if
   --  Preceding, preceding element.

   procedure Prev_Terminal (Tree : in Syntax_Trees.Tree; Node : in out Node_Access)
   with Pre => Tree_Parents_Set (Tree),
     Post => Node = Invalid_Node_Access or else
             Tree.Label (Node) in Terminal_Label;

   function Prev_Terminal (Tree : in Syntax_Trees.Tree; Node : in Valid_Node_Access) return Node_Access
   with Pre => Tree_Parents_Set (Tree);
   --  WORKAROUND: See comment in body on why this postcondition is commented out.
   --  Post => Prev_Terminal'Result = Invalid_Node_Access or else
   --          Tree.Label (Prev_Terminal'Result) in Terminal_Label;
   --  Return the terminal that is immediately before Node in subtree
   --  containing Node; Invalid_Node_Access if Node is the first terminal
   --  in that subtree.

   function Prev_Terminal
     (Tree : in Syntax_Trees.Tree;
      Ref  : in Terminal_Ref)
     return Terminal_Ref
   with Pre => Tree_Parents_Set (Tree) and Tree.Valid_Terminal (Ref),
     Post => Tree.Correct_Stream_Node (Prev_Terminal'Result);

   procedure Prev_Terminal
     (Tree : in     Syntax_Trees.Tree;
      Ref  : in out Terminal_Ref)
   with Pre => Tree_Parents_Set (Tree) and Tree.Valid_Terminal (Ref),
     Post => Tree.Correct_Stream_Node (Ref);

   procedure Prev_Terminal
     (Tree         : in     Syntax_Trees.Tree;
      Ref          : in out Stream_Node_Parents;
      Parse_Stream : in     Stream_ID;
      Preceding    : in     Boolean)
   with Pre => Correct_Stream_Node (Tree, Ref.Ref) and Parents_Valid (Ref),
     Post => Tree.Correct_Stream_Node (Ref.Ref) and Parents_Valid (Ref);
   --  If Parse_Stream is not Invalid_Stream_ID and Ref.Stream is
   --  Shared_Stream, switches from Shared_Stream to Parse_Stream at
   --  Parse_Stream.Shared_Link. If not Preceeding and there is no
   --  previous terminal in Ref.Ref.Element, set ref.Ref.Node to
   --  invalid_Node_Access.

   procedure Next_Terminal (Tree : in Syntax_Trees.Tree; Node : in out Node_Access)
   with Pre => Tree_Parents_Set (Tree),
     Post => Node = Invalid_Node_Access or else
             Tree.Label (Node) in Terminal_Label;

   function Next_Terminal (Tree : in Syntax_Trees.Tree; Node : in Valid_Node_Access) return Node_Access
   with Pre => Tree_Parents_Set (Tree);
   --  WORKAROUND: See comment in Prev_Terminal body on why this postcondition is commented out.
   --  Post => Next_Terminal'Result = Invalid_Node_Access or else
   --          Tree.Label (Next_Terminal'Result) in Terminal_Label;
   --  Return the terminal that is immediately after Node in subtree
   --  containing Node; Invalid_Node_Access if Node is the last terminal
   --  in that subtree.

   procedure Next_Terminal
     (Tree : in     Syntax_Trees.Tree;
      Ref  : in out Terminal_Ref)
   with Pre => Tree_Parents_Set (Tree) and Valid_Terminal (Tree, Ref),
     Post => Correct_Stream_Node (Tree, Ref);
   --  Update Ref to the next terminal that is after Ref.Node in Stream.
   --  Continues search in Shared_Stream; will always find EOI. Result is
   --  Invalid_Stream_Node_Ref if Ref.Node is EOI.

   procedure Next_Terminal
     (Tree      : in     Syntax_Trees.Tree;
      Ref       : in out Stream_Node_Parents;
      Following : in     Boolean)
   with Pre => Correct_Stream_Node (Tree, Ref.Ref) and Parents_Valid (Ref),
     Post => Correct_Stream_Node (Tree, Ref.Ref) and Parents_Valid (Ref);
   --  Same as Next_Terminal (Tree, Ref), using Ref.Parents for parent
   --  links. Ref.Parents is initialized by First_Terminal. If not
   --  Following, do not step out of Ref.Ref.Element; set Ref.Ref.Node to
   --  Invalid_Node_Access if there is no next terminal in
   --  Ref.Ref.Element.

   function Next_Terminal
     (Tree : in Syntax_Trees.Tree;
      Ref  : in Terminal_Ref)
     return Terminal_Ref
   with Pre => Tree_Parents_Set (Tree) and Valid_Terminal (Tree, Ref),
     Post => Correct_Stream_Node (Tree, Next_Terminal'Result);
   --  Same as procedure Next_Terminal, but return result.

   procedure Next_Terminal
     (Tree    : in     Syntax_Trees.Tree;
      Node    : in out Node_Access;
      Parents : in out Node_Stacks.Stack);
   --  Same as Next_Terminal, using Parents instead of node parent links.
   --  Parent is initialized by First_Terminal.
   --
   --  Visible for use with error recovery Configuration input stream.

   function Get_Sequential_Index (Tree : in Syntax_Trees.Tree; Node : in Node_Access) return Base_Sequential_Index
   with Pre => Node = Invalid_Node_Access or else Tree.Label (Node) in Terminal_Label;
   --  For convenience, returns Invalid_Sequential_Index if Node =
   --  Invalid_Node_Access.
   --
   --  Not named "Sequential_Index" becasue that conflicts with the
   --  subtype.

   procedure Set_Sequential_Index
     (Tree  : in Syntax_Trees.Tree;
      Node  : in Valid_Node_Access;
      Index : in Base_Sequential_Index)
   with Pre => Tree.Label (Node) in Terminal_Label;

   function First_Sequential_Terminal
     (Tree : in Syntax_Trees.Tree;
      Node : in Node_Access)
     return Node_Access;
   --  Return first terminal with valid Sequential_Index in Node;
   --  Invalid_Node_Access if none.

   function First_Sequential_Terminal
     (Tree    : in     Syntax_Trees.Tree;
      Node    : in     Node_Access;
      Parents : in out Node_Stacks.Stack)
     return Node_Access;
   --  Same as Tree.First_Sequential_Terminal (Node), also initialize
   --  Parents.

   procedure First_Sequential_Terminal
     (Tree      : in     Syntax_Trees.Tree;
      Node      : in out Node_Access;
      Parents   : in out Node_Stacks.Stack);
   --  Update Node to first terminal with valid Sequential_Index in Node;
   --  Invalid_Node_Access if none. Also initialize Parents.

   procedure First_Sequential_Terminal
     (Tree      : in     Syntax_Trees.Tree;
      Ref       : in out Syntax_Trees.Stream_Node_Parents;
      Following : in     Boolean)
   with Pre => Tree.Valid_Stream_Node_Parents (Ref),
     Post => Tree.Correct_Stream_Node (Ref.Ref) and Parents_Valid (Ref);
   --  Return first terminal with valid Sequential_Index in Ref.Node or,
   --  if Following, a following stream element; continues search in
   --  Tree.Shared_Stream. Invalid_Stream_Node_Parents if none found.

   procedure First_Sequential_Terminal
     (Tree : in     Syntax_Trees.Tree;
      Ref  : in out Stream_Node_Ref)
   with Pre => Valid_Stream_Node (Tree, Ref) and Tree_Parents_Set (Tree),
     Post => Correct_Stream_Node (Tree, Ref);
   --  Return first terminal with valid Sequential_Index in Ref.Node or a
   --  following stream element; continues search in Tree.Shared_Stream.
   --  Invalid_Node_Access if none found.

   function First_Sequential_Terminal
     (Tree : in Syntax_Trees.Tree;
      Ref  : in Syntax_Trees.Rooted_Ref)
     return Terminal_Ref
   with Pre => Valid_Stream_Node (Tree, Ref),
     Post => Correct_Stream_Node (Tree, First_Sequential_Terminal'Result);
   --   Same as First_Sequential_Terminal, does not require Parents_Set.

   function Last_Sequential_Terminal
     (Tree    : in     Syntax_Trees.Tree;
      Node    : in     Node_Access;
      Parents : in out Node_Stacks.Stack)
     return Node_Access;
   --  Return last terminal in Node that has a valid Sequential_Index,
   --  also initialize Parents.

   function Last_Sequential_Terminal
     (Tree    : in     Syntax_Trees.Tree;
      Node    : in     Node_Access)
     return Node_Access;
   --  Return last terminal in Node that has a valid Sequential_Index.
   --  Uses an internal parents stack.

   procedure Last_Sequential_Terminal
     (Tree         : in     Syntax_Trees.Tree;
      Ref          : in out Syntax_Trees.Stream_Node_Parents;
      Parse_Stream : in     Stream_ID;
      Preceding    : in     Boolean)
   with Pre => Tree.Valid_Stream_Node_Parents (Ref),
     Post => Correct_Stream_Node (Tree, Ref.Ref) and Parents_Valid (Ref);
   --  Update Ref to last terminal with valid Sequential_Index in
   --  Ref.Node or, if Preceding, a preceding stream element; if
   --  Ref.Stream is Tree.Shared_Stream, switches to Parse_Stream at
   --  Parse_Stream.Shared_Link. Invalid_Node_Access if none found.

   procedure Next_Sequential_Terminal
     (Tree    : in     Syntax_Trees.Tree;
      Node    : in out Node_Access;
      Parents : in out Node_Stacks.Stack)
   with Pre => Tree.Label (Node) in Terminal_Label;
   --  Update Node to the first terminal with valid Sequential_Index
   --  following Node. .

   procedure Next_Sequential_Terminal
     (Tree : in     Syntax_Trees.Tree;
      Ref  : in out Syntax_Trees.Stream_Node_Ref)
   with Pre => Valid_Stream_Node (Tree, Ref) and Tree_Parents_Set (Tree),
     Post => Correct_Stream_Node (Tree, Ref);

   procedure Next_Sequential_Terminal
     (Tree      : in     Syntax_Trees.Tree;
      Ref       : in out Syntax_Trees.Stream_Node_Parents;
      Following : in     Boolean)
   with Pre => Tree.Valid_Stream_Node_Parents (Ref),
     Post => Correct_Stream_Node (Tree, Ref.Ref) and Parents_Valid (Ref);
   --  Update Node to the first terminal with valid Sequential_Index
   --  succeeding Node. Can step past EOI. If not Following, do not step
   --  out of Ref.Ref.Element.

   procedure Prev_Sequential_Terminal
     (Tree      : in     Syntax_Trees.Tree;
      Node      : in out Node_Access;
      Parents   : in out Node_Stacks.Stack)
   with Pre => Tree.Label (Node) in Terminal_Label;
   --  Update Node to the last terminal with valid Sequential_Index
   --  preceding Node. Can step past SOI.

   procedure Prev_Sequential_Terminal
     (Tree         : in     Syntax_Trees.Tree;
      Ref          : in out Stream_Node_Parents;
      Parse_Stream : in     Stream_ID;
      Preceding    : in     Boolean)
   with Pre => Tree.Valid_Stream_Node_Parents (Ref),
     Post => Correct_Stream_Node (Tree, Ref.Ref) and Parents_Valid (Ref);

   function Get_IDs
     (Tree : in Syntax_Trees.Tree;
      Node : in Valid_Node_Access;
      ID   : in Token_ID)
     return Valid_Node_Access_Array;
   --  Return all descendants of Node matching ID.

   ----------
   --  Post-parsing operations; editing the tree. The tree has one or
   --  zero streams, so these subprograms have no stream argument.
   --
   --  Some of these are also used for Packrat parsing, and don't have a
   --  precondition.

   function Cleared (Tree : in Syntax_Trees.Tree) return Boolean;
   --  True if there are no streams and no nodes.

   function Lexable (Tree : in Syntax_Trees.Tree) return Boolean;
   --  True if there is a shared stream that contains only SOI.

   function Parseable (Tree : in Syntax_Trees.Tree) return Boolean;
   --  True if there are no parse streams and
   --  Shared_Stream holds a lexed or edited stream.

   function Fully_Parsed (Tree : in Syntax_Trees.Tree) return Boolean;
   --  True if Tree.Root is valid; a parse succeeded.
   --
   --  LR parse leaves a parse stream containing SOI and root; Packrat
   --  parse does not use a parse stream, it just creates nonterminal
   --  nodes.

   function Editable (Tree : in Syntax_Trees.Tree) return Boolean;
   --  True if Clear_Parse_Streams and Set_Parents have been called; the
   --  remaining tree may be arbitrarily edited.

   function Copy_Subtree
     (Tree      : in out Syntax_Trees.Tree;
      Root      : in     Node_Access;
      User_Data : in     User_Data_Access_Constant)
     return Node_Access
   with Pre => Editable (Tree);
   --  Deep copy (into Tree) subtree of Tree rooted at Root. Return root
   --  of new subtree; it has no parent.
   --
   --  If Root is Invalid_Node_Access, returns Invalid_Node_Access.

   procedure Copy_Tree
     (Source      : in     Tree;
      Destination :    out Tree;
      User_Data   : in     User_Data_Access_Constant)
   with Pre => Editable (Source);
   --  The subtree at Tree.Root is copied. Destination parents are set.
   --  All references are deep copied; Source may be finalized after this
   --  operation.
   --
   --  All Node_Index values in Destination are reset to be sequential;
   --  useful for Put_Tree.

   function Copied_Node (Node : in Valid_Node_Access) return Node_Access;
   --  Only valid during Copy_Tree; Tree must be the original tree.
   --  Returns the copy of Node in the copied tree.

   procedure Put_Tree
     (Tree      : in Syntax_Trees.Tree;
      File_Name : in String);
   --  Output to File_Name a text representation of Tree that can be read
   --  by Get_Tree.
   --
   --  File_Name must not exist.
   --
   --  The representation uses Node_Index to identify each node; it must
   --  be unique. Non-unique Node_Index is detected, and raises
   --  Programmer_Error. If Tree has non-unique Node_Index because of
   --  editing or incremental parse, use Copy_Tree first to normalize
   --  Node_Index.

   procedure Get_Tree
     (Tree      : in out Syntax_Trees.Tree;
      File_Name : in     String);
   --  Read the output of Put_Tree from File_Name, populate Tree.

   procedure Clear_Parse_Streams (Tree : in out Syntax_Trees.Tree);
   --  Delete the parse streams, but not the nodes they reference.

   procedure Finish_Parse (Tree : in out Syntax_Trees.Tree)
   with Pre => Tree.Root /= Invalid_Node_Access,
     Post => Tree.Editable;
   --  Delete the parse streams and shared stream. Delete all nodes
   --  not reachable from the root. Call Set_Parents if not
   --  Tree.Parents_Set.

   function Tree_Parents_Set (Tree : in Syntax_Trees.Tree) return Boolean;
   --  WORKAROUND: gnat 13 is confused about Tree.Parents_Set in aspects,
   --  so we use an unambiguous name.

   function Parents_Set (Tree : in Syntax_Trees.Tree) return Boolean;

   procedure Set_Parents
     (Tree   : in out Syntax_Trees.Tree;
      Stream : in     Stream_ID := Invalid_Stream_ID);
   --  If Stream is not Invalid_Stream_ID, set parents in all elements of
   --  Stream. Otherwise, if Tree.Root is set, sets parents in tree
   --  rooted at Tree.Root.
   --
   --  No precondition for packrat.

   function Valid_Root (Tree : in Syntax_Trees.Tree) return Boolean;
   --  True if Tree has a single root.

   function Root (Tree : in Syntax_Trees.Tree) return Node_Access
   with Pre => Tree.Valid_Root;
   --  The node set by Set_Root; Invalid_Node_Access if that has not been called.

   procedure Set_Root (Tree : in out Syntax_Trees.Tree; New_Root : in Valid_Node_Access)
   with Pre =>
     Tree.ID (New_Root) = Tree.Lexer.Descriptor.Accept_ID and then
     Tree.Label (New_Root) = Nonterm and then
     Tree.Child_Count (New_Root) > 0;
   --  Set Tree.Root to Root. If New_Root.Children does not start with
   --  Tree.SOI, prepend it. If New_Root.Children does not end with
   --  Tree.EOI, append it.
   --
   --  Precondition matches Packrat parser conditions at end of parse.

   function SOI (Tree : in Syntax_Trees.Tree) return Node_Access;
   --  Return node representing start of input in the shared stream. It
   --  has non_grammar giving the first line number, and all non_grammar
   --  before the first grammar node.
   --
   --  Note that SOI may be copied in a parse stream, when it has
   --  Following_Deleted.

   function EOI (Tree : in Syntax_Trees.Tree) return Node_Access;
   --  Return node representing end of input in the shared stream. It has
   --  non_grammar giving the last line number. Invalid_Node_Access if it
   --  has not yet been seen by the lexer.
   --
   --  Note that EOI may be copied in a parse stream, when it has an error.

   function Parent
     (Tree  : in Syntax_Trees.Tree;
      Node  : in Valid_Node_Access;
      Count : in Positive := 1)
     return Node_Access
   with Pre => Tree_Parents_Set (Tree);
   --  Return Count parent of Node.

   function Find_Byte_Pos
     (Tree                 : in Syntax_Trees.Tree;
      Byte_Pos             : in Buffer_Pos;
      Trailing_Non_Grammar : in Boolean)
     return Node_Access;
   --  Return the terminal that contains (including non_grammar if
   --  Trailing_Non_Grammar) or is first after Byte_Pos.
   --  Invalid_Node_Access if Byte_Pos is after text spanned by Tree.

   function Find_Byte_Pos
     (Tree                 : in Syntax_Trees.Tree;
      Byte_Pos             : in Buffer_Pos;
      Trailing_Non_Grammar : in Boolean;
      Start_At             : in Terminal_Ref;
      Stream               : in Stream_ID := Invalid_Stream_ID)
     return Terminal_Ref
   with Pre =>
     Tree_Parents_Set (Tree) and
     (Start_At /= Invalid_Stream_Node_Ref or Stream /= Invalid_Stream_ID);
   --  Return the terminal that contains (including non_grammar if
   --  Trailing_Non_Grammar) or is first after Byte_Pos.
   --  Invalid_Stream_Node_Ref if Byte_Pos is after text spanned by
   --  Tree.Stream.
   --
   --  If Start_At is not Invalid_Stream_Node_Ref, start search there,
   --  move forward. If Start_At is Invalid_Stream_Node_Ref, start search
   --  at SOI in Stream.

   function Find_Char_Pos
     (Tree                 : in Syntax_Trees.Tree;
      Char_Pos             : in Buffer_Pos;
      Trailing_Non_Grammar : in Boolean;
      After                : in Boolean := False)
     return Node_Access;
   --  If After, return the first terminal after or containing
   --  Char_Point. Otherwise return the terminal containing Char_Point.
   --  If Include_Non_Grammar, non_grammar is included in token
   --  char_region. Invalid_Node_Access if none.
   --
   --  Adding a "Before" option here would significantly complicate the
   --  logic, and would only make a difference when Char_Pos is in the
   --  whitespace immediately before a token; calling code can move
   --  Char_Pos out of such whitespace if it matters.

   function Find_New_Line
     (Tree : in Syntax_Trees.Tree;
      Line : in Line_Number_Type)
     return Node_Access
   with Pre => Tree.Editable,
     Post => Find_New_Line'Result = Invalid_Node_Access or else
             (Tree.Is_Terminal (Find_New_Line'Result));
   --  Return the terminal node containing a non_grammar that ends Line -
   --  1, or the multi-line terminal node that contains the new_line that
   --  ends Line - 1. Result is Invalid_Node_Access if Line is outside
   --  range spanned by Tree.

   function Find_New_Line
     (Tree                : in     Syntax_Trees.Tree;
      Line                : in     Line_Number_Type;
      Line_Begin_Char_Pos :    out Buffer_Pos)
     return Node_Access
   with Pre => Tree.Editable,
     Post => Find_New_Line'Result = Invalid_Node_Access or else
             (Tree.Is_Terminal (Find_New_Line'Result));
   --  Same as Find_New_Line, also updates Line_Begin_Char_Pos to first
   --  char pos on Line.

   procedure Next_New_Line
     (Tree               : in     Syntax_Trees.Tree;
      Start_Ref          : in     Terminal_Ref;
      After_Non_Grammar  : in     Positive_Index_Type;
      Result_Ref         :    out Terminal_Ref;
      Result_Non_Grammar :    out Positive_Index_Type)
   with Pre => Tree.Non_Grammar_Const (Start_Ref.Node).Last_Index >= After_Non_Grammar,
     Post => Tree.Non_Grammar_Const (Result_Ref.Node).Last_Index >= Result_Non_Grammar;
   --  Return next New_Line or EOI.

   function Line_Begin_Char_Pos
     (Tree : in Syntax_Trees.Tree;
      Line : in Line_Number_Type)
     return Buffer_Pos
   with Pre => Tree.Editable;
   --  First character on Line in text spanned by tree under Tree.Root;
   --  it may be in no token, or in a grammar or non-grammar token.
   --  Result is Invalid_Buffer_Pos if Line is not in the text spanned by
   --  Tree, or if Line is inside a multi-line token.

   function Line_Begin_Char_Pos
     (Tree : in Syntax_Trees.Tree;
      Line : in Line_Number_Type;
      Stream : in Stream_ID)
     return Buffer_Pos;
   --  Same as other Line_Begin_Char_Pos, but searches in Stream instead of
   --  Tree.Root. If not found there, continues searching input in
   --  Shared_Stream.

   function Line_Begin_Token
     (Tree : in Syntax_Trees.Tree;
      Line : in Line_Number_Type)
     return Node_Access
   with Pre => Tree_Parents_Set (Tree);
   --  Return the node under Tree.Root of the first terminal token on
   --  line Line; Invalid_Node_Access if there are no grammar tokens on
   --  the line (ie only comment or whitespace), or the line is outside
   --  the text spanned by Tree.

   function Line_Begin_Token
     (Tree                      : in Syntax_Trees.Tree;
      Line                      : in Line_Number_Type;
      Stream                    : in Stream_ID;
      Following_Source_Terminal : in Boolean)
     return Node_Access;
   --  Same as other Line_Begin_Token, but searches in Stream instead of
   --  Tree.Root. If not found there, continues searching input in
   --  Shared_Stream.
   --
   --  If Following_Source_Terminal, returns next Source_Terminal in
   --  stream if there are no grammar tokens on Line.

   function Add_Nonterm
     (Tree          : in out Syntax_Trees.Tree;
      Production    : in     WisiToken.Production_ID;
      Children      : in     Valid_Node_Access_Array;
      Clear_Parents : in     Boolean)
     return Valid_Node_Access
   with Pre => not Tree.Traversing and Children'First = 1;
   --  Add a new Nonterm node (not on any stream), containing
   --  Children, with no parent. Result points to the added node..
   --
   --  If Parents_Set, Children.Parent are set to the new node. If a
   --  child has a previous parent, then if Clear_Parents, the
   --  corresponding entry in the parent's Children is set to null; if
   --  not Clear_Parents and assertions are enabled, Assertion_Error is
   --  raised.

   function Add_Terminal
     (Tree     : in out Syntax_Trees.Tree;
      Terminal : in     Lexer.Token;
      Errors   : in     Error_Data_Lists.List)
     return Valid_Node_Access
   with Pre => not Tree.Traversing and Tree.Editable;
   --  Add a new Terminal node with no parent, on no stream. Result
   --  points to the added node.

   function Add_Terminal
     (Tree       : in out Syntax_Trees.Tree;
      Terminal   : in     Token_ID)
     return Valid_Node_Access
   with Pre => not Tree.Traversing and Tree.Editable;
   --  Add a new Virtual_Terminal node with no parent, on no stream.
   --  Result points to the added node.

   procedure Add_Deleted
     (Tree          : in out Syntax_Trees.Tree;
      Deleted_Node  : in     Valid_Node_Access;
      Prev_Terminal : in out Stream_Node_Parents;
      User_Data     : in     User_Data_Access_Constant)
   with Pre =>
     Tree.Label (Deleted_Node) in Terminal_Label and
     Tree.Valid_Stream_Node_Parents (Prev_Terminal) and
     Prev_Terminal.Ref.Stream /= Tree.Shared_Stream and
     Tree.Label (Prev_Terminal.Ref.Node) = Source_Terminal;
   --  Copy Prev_Terminal.Ref.Node and any ancestors, add Deleted_Node to
   --  Prev_Terminal.Ref.Node.Following_Deleted. Update Prev_Terminal to
   --  point to copied node. Move any non_grammar from Deleted_Node to
   --  Prev_Terminal.Ref.Node.
   --
   --  Note that this does _not_ delete Deleted_Node from the input; use
   --  Delete_Current_Token for that.

   function Has_Following_Deleted
     (Tree : in Syntax_Trees.Tree;
      Node : in Valid_Node_Access)
     return Boolean
   with Pre => Tree.Label (Node) = Source_Terminal;

   type Valid_Node_Access_List_Var_Ref (List : not null access Valid_Node_Access_Lists.List) is private
   with Implicit_Dereference => List;

   function Following_Deleted
     (Tree : in out Syntax_Trees.Tree;
      Node : in     Valid_Node_Access)
     return Valid_Node_Access_List_Var_Ref
   with Pre => Tree.Label (Node) = Source_Terminal;

   procedure Delete_Subtree
     (Tree : in out Syntax_Trees.Tree;
      Root : in out Node_Access);
   --  Free all nodes under Root
   --
   --  No precondition; called from Finalize.

   function Add_Identifier
     (Tree        : in out Syntax_Trees.Tree;
      ID          : in     Token_ID;
      Identifier  : in     Identifier_Index)
     return Valid_Node_Access
   with Pre => not Tree.Traversing and Tree.Editable;
   --  Add a new Virtual_Identifier node with no parent, on no stream.
   --  Result points to the added node.

   function Child_Index
     (Tree   : in Syntax_Trees.Tree;
      Parent : in Valid_Node_Access;
      Child  : in Valid_Node_Access)
     return SAL.Peek_Type
   with Pre => Tree.Has_Child (Parent, Child);

   procedure Replace_Child
     (Tree                 : in out Syntax_Trees.Tree;
      Parent               : in     Valid_Node_Access;
      Child_Index          : in     SAL.Peek_Type;
      Old_Child            : in     Node_Access;
      New_Child            : in     Node_Access;
      Old_Child_New_Parent : in     Node_Access := Invalid_Node_Access)
   with
     Pre => not Tree.Traversing and Tree.Editable and
            (Tree.Is_Nonterm (Parent) and then
               (Tree.Child (Parent, Child_Index) = Old_Child and
                  (Old_Child = Invalid_Node_Access or else
                     Tree.Parent (Old_Child) = Parent)));
   --  In Parent.Children, replace child at Child_Index with New_Child.
   --  Unless Old_Child is Invalid_Node_Access, set Old_Child.Parent to
   --  Old_Child_New_Parent (may be Invalid_Node_Access). Unless New_Child
   --  is Invalid_Node_Access, set New_Child.Parent to Parent.

   procedure Set_Children
     (Tree     : in out Syntax_Trees.Tree;
      Node     : in out Valid_Node_Access;
      New_ID   : in     WisiToken.Production_ID;
      Children : in     Node_Access_Array)
   with
     Pre => not Tree.Traversing and Tree.Editable and
            Tree.Is_Nonterm (Node) and (for all C of Children => C /= Invalid_Node_Access);
   --  If parents of current Node.Children are not Invalid_Node_Access,
   --  set corresponding entry in those parents to Invalid_Node_Access,
   --  then set the child parent to Invalid_Node_Access.
   --
   --  Then set ID of Node to New_ID, and Node.Children to Children; set
   --  parents of Children to Node.
   --
   --  We use a precondition on Children, rather than
   --  Valid_Node_Access_Array, so constructs like:
   --
   --     Tree.Set_Children (node, new_id, tree.children ())
   --
   --  are legal.
   --
   --  Node is 'in out' because it must be reallocated if Children'length
   --  /= Node.Children'length. If it is reallocated,
   --  Node.Parent.Children is updated; the caller must update any other
   --  copies of Node.

   procedure Clear_Parent
     (Tree           : in out Syntax_Trees.Tree;
      Node           : in     Valid_Node_Access;
      Clear_Children : in     Boolean)
   with Pre => not Tree.Traversing;
   --  If Clear_Children and Node.Parent /= Invalid_Node_Access, set
   --  child in Node.Parent to Invalid_Node_Access, and if Node.Parent =
   --  Tree.Root, set Tree.Root to Node. Finally, set Node.Parent to
   --  Invalid_Node_Access.
   --
   --  Clear_Children should be False unless Tree is Editable or Node is
   --  in Shared_Stream.

   function First_Recover_Conflict (Tree : in Syntax_Trees.Tree) return Stream_Node_Ref
   with
     Pre  => Tree_Parents_Set (Tree),
     Post => First_Recover_Conflict'Result = Invalid_Stream_Node_Ref or else
             Tree.Recover_Conflict (First_Recover_Conflict'Result.Node);
   --  First recover conflict node in Tree; Invalid_Stream_Node_Ref if none.

   procedure First_Recover_Conflict (Tree : in Syntax_Trees.Tree; Ref : in out Stream_Node_Ref)
   with Pre => Tree_Parents_Set (Tree);
   --  First recover conflict node at or after Ref; Invalid_Stream_Ref if none.

   ----------
   --  Accessing parse errors

   function Contains_Error
     (Tree       : in Syntax_Trees.Tree;
      Error_Node : in Valid_Node_Access;
      Data       : in Error_Data'Class)
     return Boolean;
   --  True if Error_Node's error list contains an element matching Data.

   procedure Add_Errors
     (Tree   : in out Syntax_Trees.Tree;
      Stream : in     Stream_ID;
      Node   : in     Valid_Node_Access;
      Errors : in     Error_Data_Lists.List)
   with Pre => Stream = Tree.Shared_Stream and Tree.Label (Node) = Source_Terminal;
   --  Add Errors to Node; Node is not copied first.
   --
   --  This should only be used when lexing new text, Errors are lexer
   --  errors which occurred while lexing a non_grammar token, and Node is
   --  the previously lexed grammar token.

   procedure Add_Error_To_Input
     (Tree      : in out Syntax_Trees.Tree;
      Stream    : in     Stream_ID;
      Data      : in     Error_Data'Class;
      User_Data : in     User_Data_Access_Constant);
   --  Add Data to the error list of the First_Terminal of the current
   --  input token. If the current input is in Shared_Stream, copy to
   --  Stream first.

   procedure Add_Error_To_Stack_Top
     (Tree      : in out Syntax_Trees.Tree;
      Stream    : in     Stream_ID;
      Data      : in     Error_Data'Class;
      User_Data : in     User_Data_Access_Constant);
   --  Copy Stream.Stack_Top.Node, add Data to its error list.

   procedure Add_Errors
     (Tree      : in out Syntax_Trees.Tree;
      Error_Ref : in out Stream_Node_Parents;
      Errors    : in     Error_Data_Lists.List;
      User_Data : in     User_Data_Access_Constant)
   with Pre => Tree.Valid_Stream_Node_Parents (Error_Ref) and
     (for all Err of Errors => not Tree.Contains_Error (Error_Ref.Ref.Node, Err));
   --  Copy Error_Ref.Node and parents, add Errors to its error list.
   --  Update Error_Ref to point to copied node.

   type Error_Predicate is access function (Cur : in Error_Data_Lists.Cursor) return Boolean;

   procedure Delete_Errors_In_Input
     (Tree      : in out Syntax_Trees.Tree;
      Stream    : in     Stream_ID;
      Predicate : in     Error_Predicate;
      User_Data : in     User_Data_Access_Constant);
   --  Delete errors in Current_Token where Predicate returns True.
   --
   --  If Current_Token is a nonterm, deletes errors from the entire
   --  subtree.

   function Input_Has_Matching_Error
     (Tree   : in Syntax_Trees.Tree;
      Stream : in Stream_ID;
      Data   : in Error_Data'Class)
     return Boolean;
   --  Return True if Data matches (according to Dispatching_Equal) an
   --  error on the current input node.

   function Error_List (Tree : in Syntax_Trees.Tree; Node : in Valid_Node_Access) return Error_Data_List_Const_Ref;
   --  To change the error data, use Update_Error; see note at
   --  declaration of Error_Data.
   --
   --  Returns an empty list if Has_Error (Node) is false, so users can
   --  just use 'for Err of Tree.Error_List (Node) loop'

   type Error_Ref is private;
   --  Used when tree is fully parsed.

   Invalid_Error_Ref : constant Error_Ref;

   procedure Delete_Error
     (Tree  : in out Syntax_Trees.Tree;
      Error : in out Error_Ref)
   with Pre => Tree_Parents_Set (Tree) and Has_Error (Error);
   --  Delete Error from its containing node. Error is updated to next
   --  error (Invalid_Error_Ref if none).

   type Stream_Error_Ref is private;
   --  Used while parsing

   Invalid_Stream_Error_Ref : constant Stream_Error_Ref;

   type Error_Node_Features is record
      Label     : Node_Label       := Node_Label'First;
      --  Also determines which predicate applies.

      Seq_Index : Base_Sequential_Index := Invalid_Sequential_Index;
      --  of First_Terminal

      Terminal_Predicate  : Error_Predicate := null;
      --  Either Parse_Error or Error_Message.

      Deleted : Boolean := False;
      --  Node is deleted by an error recover op; Label must be in Terminal_Label.

      Prev_Term_Seq_Index : Base_Sequential_Index := Invalid_Sequential_Index;
      --  When Deleted; sequential index of terminal containing error node
      --  in Following_Deleted.
   end record;

   function Current_Error_Ref
     (Tree                : in Syntax_Trees.Tree;
      Stream              : in Stream_ID;
      Terminal_Predicate  : in Error_Predicate;
      Nonterm_Predicate   : in Error_Predicate;
      Error_Node_Features : in Syntax_Trees.Error_Node_Features := (others => <>))
     return Stream_Error_Ref
   with Post => Has_Error (Current_Error_Ref'Result);
   --  Return error matching Predicate from Stream input or stack.
   --
   --  If First_Term_Seq_Index is Invalid_Term_Seq_Index, looks only in
   --  First_Terminal (Current_Token (Stream)) with Terminal_Predicate and
   --  Peek (Stream) with Nonterm_Predicate.
   --
   --  Otherwise, Error_Node_Features gives information needed to find
   --  the error node in Stream input and stack; error recover may have
   --  pushed elements before the error node in the input, or after the
   --  error node on the stack, and the error node may have been deleted.

   procedure Delete_Error
     (Tree  : in out Syntax_Trees.Tree;
      Error : in out Stream_Error_Ref)
   with Pre => Tree_Parents_Set (Tree) and Has_Error (Error);
   --  Delete Error from its containing node. Error is updated to next
   --  error (Invalid_Stream_Error_Ref if none).

   procedure Update_Error
     (Tree      : in out Syntax_Trees.Tree;
      Stream    : in     Stream_ID;
      Error_Ref : in     Stream_Error_Ref;
      Data      : in     Error_Data'Class;
      User_Data : in     User_Data_Access_Constant)
   with
     Pre => not Tree_Parents_Set (Tree) and Tree.Contains_Error (Error_Node (Error_Ref), Data),
     Post => Tree.Contains_Error (Error_Node (Error_Ref), Data);
   --  Update error list element matching Data.
   --
   --  First copy Error_Ref.Node and all ancestors; Update Error_Ref to
   --  point to new stream element with copied nodes.

   type Stream_Error_Cursor is private;
   --  We need an extra layer of indirection for Stream_Error_Ref
   --  iterators because Stream_ID used in Stream_Node_Parents is
   --  private.

   function Error (Item : in Stream_Error_Cursor) return Stream_Error_Ref;

   function Error (Item : in Error_Ref) return Error_Data'Class;
   function Error (Item : in Stream_Error_Ref) return Error_Data'Class;

   function Error_Node (Tree : in Syntax_Trees.Tree; Error : in Error_Ref) return Valid_Node_Access
   with Pre => Error /= Invalid_Error_Ref;

   function Error_Node (Error : in Stream_Error_Ref) return Valid_Node_Access
   with Pre => Error /= Invalid_Stream_Error_Ref;

   function Error_Node (Tree : in Syntax_Trees.Tree; Error : in Stream_Error_Ref) return Valid_Node_Access
   with Pre => Error /= Invalid_Stream_Error_Ref;

   function Error_Stream_Node_Ref (Tree : in Syntax_Trees.Tree; Error : in Stream_Error_Ref) return Stream_Node_Ref
   with Pre => Error_Deleted (Error) = Valid_Node_Access_Lists.No_Element;

   function Error_Deleted (Error : in Stream_Error_Ref) return Valid_Node_Access_Lists.Cursor;

   function First_Error (Tree : in Syntax_Trees.Tree) return Error_Ref
   with Pre => Tree_Parents_Set (Tree);
   --  Return first error node in Tree.

   function First_Error (Tree : in Syntax_Trees.Tree; Stream : in Stream_ID) return Stream_Error_Ref;
   --  Return first error node in Stream.

   procedure Next_Error (Tree : in Syntax_Trees.Tree; Error : in out Error_Ref)
   with Pre => Tree_Parents_Set (Tree) and Error /= Invalid_Error_Ref;
   --  Update Error to next error node.

   procedure Next_Error (Tree : in Syntax_Trees.Tree; Error : in out Stream_Error_Ref)
   with Pre => Error /= Invalid_Stream_Error_Ref;
   --  Update Error to next error node.

   function Error_Count (Tree : in Syntax_Trees.Tree) return Ada.Containers.Count_Type
   with Pre => Tree_Parents_Set (Tree);
   function Error_Count (Tree : in Syntax_Trees.Tree; Stream : in Stream_ID) return Ada.Containers.Count_Type;

   function Has_Errors (Tree : in Syntax_Trees.Tree) return Boolean;
   --  If there are errors, faster than Error_Count; also independent of
   --  Tree.Parents_Set.

   function Has_Error (Error : in Error_Ref) return Boolean;
   function Has_Error (Error : in Stream_Error_Ref) return Boolean;
   function Has_Error (Position : in Stream_Error_Cursor) return Boolean;
   function Has_Error (Node : in Valid_Node_Access) return Boolean;
   function Has_Error (Tree : in Syntax_Trees.Tree; Node : in Valid_Node_Access) return Boolean;

   function Has_Error_Class
     (Tree        : in Syntax_Trees.Tree;
      Node        : in Valid_Node_Access;
      Error_Class : in Error_Data'Class)
     return Error_Ref;
   --  Return a reference to the first error in Node that is in Error_Class;
   --  Invalid_Error_Ref if none.

   package Error_Iterator_Interfaces is new Ada.Iterator_Interfaces
     (Cursor      => Error_Ref,
      Has_Element => Has_Error);

   function Error_Iterate
     (Tree : aliased in Syntax_Trees.Tree)
     return Error_Iterator_Interfaces.Forward_Iterator'Class;
   --  Iterates over errors.

   package Stream_Error_Iterator_Interfaces is new Ada.Iterator_Interfaces
     (Cursor      => Stream_Error_Cursor,
      Has_Element => Has_Error);

   function Stream_Error_Iterate
     (Tree   : aliased in Syntax_Trees.Tree;
      Stream :         in Stream_ID)
     return Stream_Error_Iterator_Interfaces.Forward_Iterator'Class;
   --  Iterates over errors in Stream.

   ----------
   --  Debug and error message utils.
   --
   --  Typically no preconditions so they help with debugging errors
   --  detected by other preconditions.

   function Trimmed_Image (Tree : in Syntax_Trees.Tree; Item : in Stream_ID) return String;
   function Next_Stream_ID_Trimmed_Image (Tree : in Syntax_Trees.Tree) return String;
   --  Trimmed integer.

   function Image
     (Tree         : in Syntax_Trees.Tree;
      Children     : in Boolean     := False;
      Non_Grammar  : in Boolean     := False;
      Augmented    : in Boolean     := False;
      Line_Numbers : in Boolean     := False;
      Root         : in Node_Access := Invalid_Node_Access)
     return String;
   --  Image of all streams, or root node if no streams.
   --  If Children, subtree of each stream element is included.

   function Image
     (Tree          : in Syntax_Trees.Tree;
      Stream        : in Stream_ID;
      Stack         : in Boolean := True;
      Input         : in Boolean := True;
      Shared        : in Boolean := False;
      Children      : in Boolean := False;
      Node_Numbers  : in Boolean := True;
      Non_Grammar   : in Boolean := False;
      Augmented     : in Boolean := False;
      Line_Numbers  : in Boolean := False;
      State_Numbers : in Boolean := True)
     return String;
   --  Image of each node. If Stack, includes stack; if Input, includes
   --  input; if Shared, includes continuation in Shared_Stream. If
   --  Children, each entire subtree is included, with newlines, as in
   --  Print_Tree.

   function Image
     (Tree                  : in Syntax_Trees.Tree;
      Element               : in Stream_Index;
      State                 : in Boolean := False;
      Children              : in Boolean := False;
      RHS_Index             : in Boolean := False;
      Node_Numbers          : in Boolean := False;
      Terminal_Node_Numbers : in Boolean := False;
      Line_Numbers          : in Boolean := False;
      Non_Grammar           : in Boolean := False;
      Augmented             : in Boolean := False;
      Expecting             : in Boolean := False)
     return String;
   --  Element can be from any stream, or Invalid_Stream_Index

   function Image
     (Tree                  : in Syntax_Trees.Tree;
      Node                  : in Node_Access;
      Children              : in Boolean := False;
      RHS_Index             : in Boolean := False;
      Node_Numbers          : in Boolean := False;
      Terminal_Node_Numbers : in Boolean := False;
      Line_Numbers          : in Boolean := False;
      Non_Grammar           : in Boolean := False;
      Augmented             : in Boolean := False;
      Expecting             : in Boolean := False;
      Safe_Only             : in Boolean := False)
     return String;
   --  If Safe_Only, assume Node is not in tree or some children were
   --  deleted, so can't use Prev_/Next_ anything.

   function Image
     (Tree                  : in Syntax_Trees.Tree;
      Nodes                 : in Node_Access_Array;
      RHS_Index             : in Boolean := False;
      Node_Numbers          : in Boolean := False;
      Terminal_Node_Numbers : in Boolean := False;
      Line_Numbers          : in Boolean := False;
      Non_Grammar           : in Boolean := False;
      Augmented             : in Boolean := False)
     return String;

   function Image
     (Tree                  : in Syntax_Trees.Tree;
      Ref                   : in Stream_Node_Ref;
      First_Terminal        : in Boolean := False;
      Node_Numbers          : in Boolean := False;
      Terminal_Node_Numbers : in Boolean := False;
      Line_Numbers          : in Boolean := False;
      Non_Grammar           : in Boolean := False;
      Augmented             : in Boolean := False;
      Expecting             : in Boolean := False)
     return String;
   --  If First_Terminal, show First_Terminal of Ref.Node if Ref is rooted.

   function Image
     (Tree : in Syntax_Trees.Tree;
      List : in Valid_Node_Access_Lists.List)
     return String;

   function Decimal_Image is new SAL.Generic_Decimal_Image (Node_Index);
   function Trimmed_Image is new SAL.Gen_Trimmed_Image (Node_Index);
   function Trimmed_Image is new SAL.Gen_Trimmed_Image (Base_Sequential_Index);

   function Get_Node_Index (Node : in Node_Access) return Node_Index;
   function Get_Node_Index (Tree : in Syntax_Trees.Tree; Node : in Node_Access) return Node_Index;
   function Get_Node_Index (Element : in Stream_Index) return Node_Index;
   function Get_Node_Index
     (Tree    : in Syntax_Trees.Tree;
      Stream  : in Stream_ID;
      Element : in Stream_Index)
     return Node_Index
   with Pre => Element = Invalid_Stream_Index or else Tree.Contains (Stream, Element);
   --  Version without Tree requires Syntax_Trees.Get_Node_Index. Returns
   --  Invalid_Node_Index for Invalid_Node_Access.

   procedure Enable_Ref_Count_Check (Tree : in out Syntax_Trees.Tree; Stream : in Stream_ID; Enable : in Boolean)
   with Pre => Stream /= Invalid_Stream_ID;
   --  Default is enabled.
   --
   --  Disabling is useful when there are bugs you want to ignore.

   function Node_Access_Compare (Left, Right : in Node_Access) return SAL.Compare_Result
   with Pre => Left /= Invalid_Node_Access and Right /= Invalid_Node_Access;
   --  Only valid on a batch parsed tree, where Node_Index is ordered and
   --  unique.
   --
   --  Left, Right can't be Valid_Node access because
   --  SAL.Gen_Unbounded_Sparse_Ordered_Sets requires a valid default
   --  initialization.

   package Node_Sets is new SAL.Gen_Unbounded_Sparse_Ordered_Sets (Node_Access, Node_Access_Compare);

   function Error_Message
     (Tree    : in Syntax_Trees.Tree;
      Node    : in Valid_Node_Access;
      Message : in String)
     return String
   with Pre => Tree_Parents_Set (Tree);
   --  File_Name from Tree.Lexer, line, column from Node

   function Error_Message
     (Tree    : in Syntax_Trees.Tree;
      Ref     : in Stream_Node_Ref;
      Message : in String)
     return String;
   --  File_Name from Tree.Lexer, line, column from Node

   type Validate_Node is access procedure
     (Tree                : in     Syntax_Trees.Tree;
      Node                : in     Valid_Node_Access;
      Data                : in     User_Data_Access;
      Node_Error_Reported : in out Boolean);
   --  Called by Validate_Tree for each node visited; perform checks
   --  other than parent/child, output error messages to
   --  Tree.Lexer.Trace.
   --
   --  Set Node_Error_Reported True if any errors are reported.
   --
   --  Unless Node_Error_Reported already true on entry, output Image
   --  (Tree, Node, Node_Numbers => True) once before any error messages.

   procedure Mark_In_Tree
     (Tree                : in     Syntax_Trees.Tree;
      Node                : in     Valid_Node_Access;
      Data                : in     User_Data_Access;
      Node_Error_Reported : in out Boolean);
   --  Mark Node as being "in tree".
   --
   --  If this is provided to Validate_Tree as the Validate_Node
   --  argument, Validate_Tree will check that all nodes in Tree.Nodes
   --  are marked as "in tree".

   function In_Tree (Tree : in Syntax_Trees.Tree; Node : in Valid_Node_Access) return Boolean;
   --  True if Node is in Tree.Nodes. Useful for Validate_Node, Validate_Error.

   procedure Validate_Tree
     (Tree              : in out Syntax_Trees.Tree;
      User_Data         : in     User_Data_Access;
      Error_Reported    : in out Node_Sets.Set;
      Node_Index_Order  : in     Boolean;
      Byte_Region_Order : in     Boolean                    := True;
      Line_Number_Order : in     Boolean                    := True;
      Root              : in     Node_Access                := Invalid_Node_Access;
      Validate_Node     : in     Syntax_Trees.Validate_Node := null);
   --  Verify that no children are Invalid_Node_Access. Verify
   --  child/parent links. If Node_Index_Order, verify that
   --  Node.Node_Index > Node.Children.Node_Index (which is true in a
   --  batch parse tree). Call Validate_Node for each visited node.
   --  Violations output a message to Text_IO.Current_Error.
   --  Error_Reported is used to avoid outputing an error for a node more
   --  than once.

   procedure Sequential_Index_Cleared (Tree : in Syntax_Trees.Tree);
   --  Raises SAL.Programmer_Error if any node in Tree or Tree.Streams
   --  has Sequential_Index /= Invalid_Sequential_Index.

   procedure Print_Tree
     (Tree         : in Syntax_Trees.Tree;
      Root         : in Node_Access := Invalid_Node_Access;
      Line_Numbers : in Boolean     := False;
      Non_Grammar  : in Boolean     := False;
      Augmented    : in Boolean     := False;
      Safe_Only    : in Boolean     := False);
   --  Print tree rooted at Root (default Tree.Root) to
   --  Tree.Lexer.Trace, for debugging.
   --
   --  This is the same as Trace.Put_Line (Tree.Image (..., Children =>
   --  True)), but avoids storing the entire trace image on the stack;
   --  required for large trees.

   procedure Print_Streams
     (Tree        : in     Syntax_Trees.Tree;
      Children    : in     Boolean := False;
      Non_Grammar : in     Boolean := False);

   function Tree_Size_Image (Tree : in Syntax_Trees.Tree) return String;
   --  For debugging; node counts.

   procedure Print_Ref_Counts (Tree : in Syntax_Trees.Tree);

   procedure Find_Node (Tree : in Syntax_Trees.Tree; Node : in Valid_Node_Access);
   --  Print to tree.lexer.trace the index of Node in Tree.Nodes; useful
   --  in debugger for a stable way to access Node.

private
   type Error_List_Access is access all Error_Data_Lists.List;
   procedure Free is new Ada.Unchecked_Deallocation (Error_Data_Lists.List, Error_List_Access);

   type Node
     (Label       : Node_Label;
      Child_Count : SAL.Base_Peek_Type)
   --  Descriminants have no default because allocated nodes are
   --  constrained anyway (ARM 4.8 6/3).
   is record
      Copied_Node : Node_Access;
      --  Only set during Copy_Tree

      ID : WisiToken.Token_ID := Invalid_Token_ID;

      Node_Index : Syntax_Trees.Node_Index := Invalid_Node_Index;
      --  If Terminal_Label, positive, and corresponds to text order after
      --  initial lex. If Nonterm, negative, arbitrary. After a batch parse,
      --  node indices are unique within the tree, but after incremental
      --  editing, they are reused because nodes created for unsuccessful
      --  parse streams are deleted.

      Parent : Node_Access := Invalid_Node_Access;

      Augmented : Augmented_Class_Access := null;

      Error_List : Error_List_Access;
      --  We store an access to an error list object in each node, rather
      --  than a list object, to reduce the size of a node; almost all nodes
      --  have no errors.

      case Label is
      when Terminal_Label =>
         Non_Grammar : aliased Lexer.Token_Arrays.Vector;
         --  Immediately following Node. In initial lex, this can only be in a
         --  Source_Terminal node. User Insert_Terminal can move it to a
         --  Virtual_Terminal node, editing the tree can copy it to a
         --  Virtual_Identifier node.
         --
         --  Not a pointer, because many nodes have non_grammar, and to
         --  simplify using Tree.Non_Grammar_Var.

         Sequential_Index : Syntax_Trees.Base_Sequential_Index := Invalid_Sequential_Index;

         case Label is
         when Source_Terminal =>
            Byte_Region    : Buffer_Region         := Null_Buffer_Region;
            Char_Region    : Buffer_Region         := Null_Buffer_Region;
            New_Line_Count : Base_Line_Number_Type := 0;
            --  Data from lexer. We store the absolute buffer region here to avoid
            --  storing all whitespace in the tree. Edit_Tree shifts these for
            --  incremental parse. We don't store Line_Region here, because it
            --  changes when Insert_Terminal moves Non_Grammar; Non_Grammars all
            --  store Line_Region. We store New_Line_Count to allow computing line
            --  numbers from previous or following Non_Grammar across multi-line
            --  tokens.

            Following_Deleted : aliased Valid_Node_Access_Lists.List;
            --  Nodes that follow this terminal that were deleted by error
            --  recovery.
            --  FIXME: change to ptr like error_list, for space saving?

         when Virtual_Terminal_Label =>
            Insert_Location : WisiToken.Insert_Location := Before_Next;
            --  Overridden Insert_Token can change the default.
            --  If the node has non_grammar tokens, Insert_Location must be
            --  Between.

            case Label is
            when Virtual_Terminal =>
               null;
            when Virtual_Identifier =>
               Identifier : Identifier_Index; -- index into user data
            when Source_Terminal | Nonterm =>
               null;
            end case;

         when Nonterm =>
            null;
         end case;

      when Nonterm =>
         Virtual : Boolean := False;
         --  True if any child node is Virtual_Terminal or Nonterm with Virtual
         --  set. Used by In_Parse_Actions and error recover, via
         --  Contains_Virtual_Terminal.

         Recover_Conflict : Boolean := False;
         --  True if this node is created during error recover Resume when more
         --  than one parser is active, due to a grammar conflict. Edit_Tree
         --  must breakdown this node to redo the conflict resolution; the
         --  correct resolution may require a different branch.
         --  test_incremental.adb Undo_Conflict_01.

         RHS_Index : Natural;
         --  With ID, index into Productions.

         Name_Offset : Base_Buffer_Pos := 0;
         Name_Length : Base_Buffer_Pos := 0;
         --  Name_* are set and checked by In_Parse_Actions. We use an offset
         --  from First_Terminal (Node).Byte_Region.First, rather than a
         --  Buffer_Region, to avoid needing to shift it during Edit_Tree for
         --  incremental parse. IMPROVEME: generalize for other actions;
         --  post_parse_augmented, in_parse_augmented.

         Children : Node_Access_Array (1 .. Child_Count);
         --  We use an explicit array, rather than a pointer to the first
         --  child, to preserve child indices while editing the tree.
      end case;
   end record;

   type Stream_Label is range -2 .. Integer'Last;
   --  First parser has label 0, for compatibility with tests, and for
   --  general sanity. There is no practical upper limit; parsing a large
   --  file spawns and terminates thousands of parsers.

   Invalid_Stream_Label : constant Stream_Label := -2;
   Shared_Stream_Label  : constant Stream_Label := -1;

   function Trimmed_Image is new SAL.Gen_Trimmed_Image (Stream_Label);

   type Stream_Element is record
      --  We use separate stream pointers, rather than reusing the nonterm
      --  child pointers as in [1], to allow each parallel parser to have
      --  its own stream. This also preserves Child_Index when children are
      --  deleted during editing.
      Node : Node_Access  := Invalid_Node_Access;

      State : Unknown_State_Index := Unknown_State;
      --  Parse state that is on the parse stack with this token.
      --  Unknown_State in Shared_Stream or a parse stream input.
   end record;

   --  We often hold copies of Stream_Element_Lists cursors while editing
   --  a tree, so we use a ref_count list to detect dangling references.
   --  This is not measureably slower according to the ada-mode timing
   --  benchmarks.
   package Stream_Element_Lists is new SAL.Gen_Definite_Doubly_Linked_Lists_Ref_Count (Stream_Element);
   use all type Stream_Element_Lists.Cursor;

   type Stream_Index is record
      Cur : Stream_Element_Lists.Cursor;
   end record;

   Invalid_Stream_Index : constant Stream_Index := (Cur => Stream_Element_Lists.No_Element);

   type Parse_Stream is record
      Label : Stream_Label := Invalid_Stream_Label;

      Stack_Top : Stream_Element_Lists.Cursor := Stream_Element_Lists.No_Element;
      --  The top of the parse stack. The stack is Stack_Top and previous
      --  elements, the input stream is the following elements, or
      --  Shared_Stream if Stack_Top.Next is Invalid_Stream_Index. In
      --  batch parsing with no error correction, this is always Last. In
      --  Shared_Stream, always Invalid_Stream_Index.

      Shared_Link : Stream_Element_Lists.Cursor := Stream_Element_Lists.No_Element;
      --  A complete parse stream consists of elements in
      --  Parse_Stream.Elements, followed by elements in
      --  Shared_Stream.Elements starting at Shared_Link, terminating in an
      --  EOI element. EOI is never shifted to the parse stream, but it can
      --  be copied to the parse stream to add an error. Then Shared_Link is
      --  No_Element.

      Elements : Stream_Element_Lists.List;
   end record;

   package Parse_Stream_Lists is new SAL.Gen_Definite_Doubly_Linked_lists (Parse_Stream);
   use all type Parse_Stream_Lists.Cursor;

   type Stream_ID is record
      Cur : Parse_Stream_Lists.Cursor;
   end record;

   Invalid_Stream_ID : constant Stream_ID := (Cur => Parse_Stream_Lists.No_Element);

   Invalid_Stream_Node_Ref : constant Stream_Node_Ref :=
     (Invalid_Stream_ID, Invalid_Stream_Index, Invalid_Node_Access);

   Invalid_Stream_Node_Parents : constant Stream_Node_Parents := (Invalid_Stream_Node_Ref, Parents => <>);

   package Node_Access_Arrays is new SAL.Gen_Unbounded_Definite_Vectors (Positive_Node_Index, Node_Access, null);

   type Tree is new Base_Tree with record
      Next_Stream_Label : Stream_Label := Shared_Stream_Label + 1;

      Next_Terminal_Node_Index : Node_Index := 1;

      Root : Node_Access := Invalid_Node_Access;
      SOI  : Node_Access := Invalid_Node_Access;
      EOI  : Node_Access := Invalid_Node_Access;

      Streams : Parse_Stream_Lists.List;

      Shared_Stream : Stream_ID;

      Nodes : Node_Access_Arrays.Vector;
      --  Stores ref to all nodes, for Finalize.

      Traversing : Boolean := False;
      --  True while traversing tree in Process_Tree.
      --  Declared in Base_Tree so it is cleared by Finalize.

      Parents_Set : Boolean := False;
      --  We don't set Node.Parent until after parse is done; see Design
      --  note above.
   end record;

   procedure Free is new Ada.Unchecked_Deallocation (Node, Node_Access);

   function Tree_Parents_Set (Tree : in Syntax_Trees.Tree) return Boolean
   is (Tree.Parents_Set);

   Dummy_Node : constant Node_Access := new Node'(Label => Virtual_Identifier, Child_Count => 0, others => <>);

   type Token_Array_Var_Ref (Element : not null access WisiToken.Lexer.Token_Arrays.Vector) is record
      Dummy : Integer := raise Program_Error with "uninitialized reference";
   end record;

   type Token_Array_Const_Ref (Element : not null access constant WisiToken.Lexer.Token_Arrays.Vector) is record
      Dummy : Integer := raise Program_Error with "uninitialized reference";
   end record;

   type Valid_Node_Access_List_Var_Ref (List : not null access Valid_Node_Access_Lists.List) is record
      Dummy : Integer := raise Program_Error with "uninitialized reference";
   end record;

   function Node_Image
     (Node : in Node_Access;
      Tree : in Syntax_Trees.Tree'Class)
     return String
   is (Tree.Image (Node, Node_Numbers => True));

   function Node_List_Image is new Valid_Node_Access_Lists.Gen_Image_Aux (Tree'Class, Node_Image);

   function Image
     (Tree : in Syntax_Trees.Tree;
      List : in Valid_Node_Access_Lists.List)
     return String
   is (Node_List_Image (List, Tree));

   ----------
   --  Errors

   type Error_Data_List_Const_Ref (List : not null access constant Error_Data_Lists.List) is record
      Dummy : Integer := raise Program_Error;
   end record;

   Empty_Error_List : aliased constant Error_Data_Lists.List := Error_Data_Lists.Empty_List;
   --  WORKAROUND: with GNAT Community 2021, adding 'aliased' in
   --  sal-gen_indefinite_doubly_linked_lists.ads doesn't work.

   type Error_Ref is record
      --  Used when tree is fully parsed.
      Node    : Node_Access;
      Deleted : Valid_Node_Access_Lists.Cursor;
      --  If Node = Invalid_Node_Access, no error. If Deleted = No_Element,
      --  the error node is Node. If Deleted /= No_Element, any error on
      --  Node has already been visited, the error node is Element
      --  (Deleted), which is a Source_Terminal, and Element
      --  (Deleted).Parent = Node.

      Error : Error_Data_Lists.Cursor;
      --  Element in error node Error_Data_List.
   end record;

   Invalid_Error_Ref : constant Error_Ref :=
     (Invalid_Node_Access, Valid_Node_Access_Lists.No_Element, Error_Data_Lists.No_Element);

   type Error_Iterator (Tree : not null access constant Syntax_Trees.Tree)
     is new Error_Iterator_Interfaces.Forward_Iterator with
     null record;

   overriding function First (Object : Error_Iterator) return Error_Ref;

   overriding function Next
     (Object   : Error_Iterator;
      Position : Error_Ref)
     return Error_Ref;

   type Stream_Error_Ref is record
      --  Used while parsing
      Ref     : Stream_Node_Parents;
      --  Same meaning as Node in Error_Ref.

      Deleted : Valid_Node_Access_Lists.Cursor;
      --  Same as Deleted in Error_Ref.

      Error   : Error_Data_Lists.Cursor;
      --  Same as Error in Error_Ref.
   end record;

   Invalid_Stream_Error_Ref : constant Stream_Error_Ref :=
     (Invalid_Stream_Node_Parents, Valid_Node_Access_Lists.No_Element, Error_Data_Lists.No_Element);

   type Stream_Error_Cursor is record
      SER : Stream_Error_Ref;
   end record;

   type Stream_Error_Iterator (Tree : not null access constant Syntax_Trees.Tree)
   is new Stream_Error_Iterator_Interfaces.Forward_Iterator with record
      Stream : Parse_Stream_Lists.Cursor;
   end record;

   overriding function First (Object : Stream_Error_Iterator) return Stream_Error_Cursor;

   overriding function Next
     (Object   : Stream_Error_Iterator;
      Position : Stream_Error_Cursor)
     return Stream_Error_Cursor;

end WisiToken.Syntax_Trees;
