emacs-elpa-diffs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[elpa] externals/wisi 957b480837 4/4: Add missing files


From: Stephen Leake
Subject: [elpa] externals/wisi 957b480837 4/4: Add missing files
Date: Sun, 10 Jul 2022 12:58:56 -0400 (EDT)

branch: externals/wisi
commit 957b4808379b73382fd258a8962e54dbe596a5c1
Author: Stephen Leake <stephen_leake@stephe-leake.org>
Commit: Stephen Leake <stephen_leake@stephe-leake.org>

    Add missing files
    
    * wisitoken-generate-tree_sitter.adb: New file.
    * wisitoken-generate-tree_sitter.ads: New file.
---
 wisitoken-generate-tree_sitter.adb | 1322 ++++++++++++++++++++++++++++++++++++
 wisitoken-generate-tree_sitter.ads |   39 ++
 2 files changed, 1361 insertions(+)

diff --git a/wisitoken-generate-tree_sitter.adb 
b/wisitoken-generate-tree_sitter.adb
new file mode 100644
index 0000000000..e7ff08e786
--- /dev/null
+++ b/wisitoken-generate-tree_sitter.adb
@@ -0,0 +1,1322 @@
+--  Abstract :
+--
+--  Translate a wisitoken grammar file to a tree-sitter grammar file.
+--
+--  References:
+--
+--  [1] tree-sitter grammar: 
https://tree-sitter.github.io/tree-sitter/creating-parsers#the-grammar-dsl
+--
+--  Copyright (C) 2020 - 2022 Stephen Leake All Rights Reserved.
+--
+--  This library is free software;  you can redistribute it and/or modify it
+--  under terms of the  GNU General Public License  as published by the Free
+--  Software  Foundation;  either version 3,  or (at your  option) any later
+--  version. This library is distributed in the hope that it will be useful,
+--  but WITHOUT ANY WARRANTY;  without even the implied warranty of MERCHAN-
+--  TABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+pragma License (GPL);
+
+with Ada.Text_IO; use Ada.Text_IO;
+with SAL.Gen_Unbounded_Definite_Vectors;
+with WisiToken.BNF.Output_Ada_Common;
+with WisiToken.Syntax_Trees.LR_Utils;
+with WisiToken_Grammar_Editing;
+with Wisitoken_Grammar_Actions; use Wisitoken_Grammar_Actions;
+package body WisiToken.Generate.Tree_Sitter is
+   use WisiToken.Syntax_Trees;
+
+   procedure Eliminate_Empty_Productions
+     (Data : in out WisiToken_Grammar_Runtime.User_Data_Type;
+      Tree : in out WisiToken.Syntax_Trees.Tree)
+   is
+      Ignore_Lines    : Boolean := False;
+
+      type Empty_Nonterm is record
+         Name       : Ada.Strings.Unbounded.Unbounded_String;
+         Empty_Node : WisiToken.Syntax_Trees.Node_Access := 
WisiToken.Syntax_Trees.Invalid_Node_Access;
+      end record;
+
+      package Empty_Nonterm_Lists is new SAL.Gen_Unbounded_Definite_Vectors
+        (Positive_Index_Type, Empty_Nonterm, Default_Element => (others => 
<>));
+
+      Empty_Nonterms  : Empty_Nonterm_Lists.Vector;
+      Nodes_To_Delete : Valid_Node_Access_Lists.List;
+      Nodes_To_Check  : Valid_Node_Access_Lists.List;
+      --  If we edit a node to now contain an optional item, it might become
+      --  possibly empty.
+
+      function Get_Text (Node : in Valid_Node_Access) return String
+      is begin
+         return WisiToken_Grammar_Runtime.Get_Text (Data, Tree, Node);
+      end Get_Text;
+
+      function Can_Be_Empty (Node : in Valid_Node_Access) return Node_Access
+      --  Return a descendant node of Node that can be empty, 
Invalid_Node_Access if none.
+      with Pre => To_Token_Enum (Tree.ID (Node)) in
+                  rhs_list_ID | rhs_item_list_ID | rhs_alternative_list_ID | 
rhs_element_ID
+      is
+         use Syntax_Trees.LR_Utils;
+      begin
+         case To_Token_Enum (Tree.ID (Node)) is
+         when rhs_list_ID =>
+            declare
+               RHS_List : constant Constant_List := Creators.Create_List 
(Tree, Node, +rhs_list_ID, +rhs_ID);
+            begin
+               for RHS of RHS_List loop
+                  if Tree.RHS_Index (RHS) = 0 then
+                     return RHS;
+                  end if;
+                  declare
+                     Empty_Node : constant Node_Access := Can_Be_Empty 
(Tree.Child (RHS, 1));
+                  begin
+                     if Empty_Node /= Invalid_Node_Access then
+                        return Empty_Node;
+                     end if;
+                  end;
+               end loop;
+               return Invalid_Node_Access;
+            end;
+
+         when rhs_item_list_ID =>
+            declare
+               Item_List : constant Constant_List := Creators.Create_List
+                 (Tree, Node, +rhs_item_list_ID, +rhs_element_ID);
+            begin
+               for Element of Item_List loop
+                  declare
+                     Empty_Node : constant Node_Access := Can_Be_Empty 
(Element);
+                  begin
+                     if Empty_Node = Invalid_Node_Access then
+                        --  This item can't be empty, so the list can't be 
empty.
+                        return Invalid_Node_Access;
+                     end if;
+                  end;
+               end loop;
+               --  All items can be empty
+               return Node;
+            end;
+
+         when rhs_element_ID =>
+            declare
+               Item : constant Valid_Node_Access := Tree.Find_Descendant 
(Node, +rhs_item_ID);
+            begin
+               case Tree.RHS_Index (Item) is
+               when 0 | 1 =>
+                  return Invalid_Node_Access;
+
+               when 2 =>
+                  --  If the only elements in an rhs_item_list are attributes, 
the list
+                  --  is empty for LR generation purposes.
+                  return Item;
+
+               when 3 =>
+                  return Item;
+
+               when 4 =>
+                  case Tree.RHS_Index (Tree.Child (Item, 1)) is
+                  when 0 | 3 | 5 =>
+                     return Item;
+                  when 1 | 2 =>
+                     return Can_Be_Empty (Tree.Child (Tree.Child (Item, 1), 
2));
+                  when 4 =>
+                     return Invalid_Node_Access;
+                  when others =>
+                     raise SAL.Programmer_Error;
+                  end case;
+
+               when 5 =>
+                  return Can_Be_Empty (Tree.Child (Tree.Child (Item, 1), 2));
+
+               when others =>
+                  raise SAL.Programmer_Error;
+               end case;
+            end;
+
+         when rhs_alternative_list_ID =>
+            declare
+               RHS_Alt_List : constant Constant_List := Creators.Create_List
+                 (Tree, Node, +rhs_alternative_list_ID, +rhs_item_list_ID);
+            begin
+               for Item_List of RHS_Alt_List loop
+                  declare
+                     Empty_Node : constant Node_Access := Can_Be_Empty 
(Item_List);
+                  begin
+                     if Empty_Node /= Invalid_Node_Access then
+                        return Empty_Node;
+                     end if;
+                  end;
+               end loop;
+               return Invalid_Node_Access;
+            end;
+
+         when others =>
+            raise SAL.Programmer_Error;
+         end case;
+      end Can_Be_Empty;
+
+      procedure Find_Empty_Nodes (Node : in Valid_Node_Access)
+      is begin
+         if Ignore_Lines then
+            case To_Token_Enum (Tree.ID (Node)) is
+            when declaration_ID =>
+               --  Contained in a compilation_unit that is already marked for 
deletion
+               case To_Token_Enum (Tree.ID (Tree.Child (Node, 2))) is
+               when ELSIF_ID =>
+                  --  | PERCENT ELSIF IDENTIFIER EQUAL IDENTIFIER
+                  --  | PERCENT ELSIF IDENTIFIER IN IDENTIFIER_BAR_LIST
+                  declare
+                     use WisiToken.BNF;
+                  begin
+                     if "lexer" = Get_Text (Tree.Child (Node, 3)) then
+                        Ignore_Lines := not 
WisiToken_Grammar_Runtime.Get_Lexer_Set
+                          (Data, Tree, Tree.Child (Node, 5)) 
(Tree_Sitter_Lexer);
+
+                     elsif "parser" = Get_Text (Tree.Child (Node, 3)) then
+                        Ignore_Lines := not 
WisiToken_Grammar_Runtime.Get_Generate_Algorithm_Set
+                          (Data, Tree, Tree.Child (Node, 5)) 
(WisiToken.BNF.Tree_Sitter);
+
+                     else
+                        raise SAL.Programmer_Error;
+                     end if;
+
+                     if Trace_Generate_EBNF > Outline then
+                        Ada.Text_IO.Put_Line
+                          ("ignore lines " & Ignore_Lines'Image & " line" &
+                             Tree.Line_Region (Tree.Child (Node, 1), 
Trailing_Non_Grammar => True).First'Image);
+                     end if;
+                  end;
+
+               when END_ID =>
+                  --  | PERCENT END IF
+                  Ignore_Lines := False;
+                  if Trace_Generate_EBNF > Outline then
+                     Ada.Text_IO.Put_Line
+                       ("ignore lines false line" &
+                          Tree.Line_Region (Tree.Child (Node, 1), 
Trailing_Non_Grammar => True).First'Image);
+                  end if;
+
+               when others =>
+                  null;
+               end case;
+
+            when compilation_unit_ID =>
+               Nodes_To_Delete.Append (Node);
+               Find_Empty_Nodes (Tree.Child (Node, 1));
+
+            when compilation_unit_list_ID =>
+               declare
+                  Children : constant Node_Access_Array := Tree.Children 
(Node);
+               begin
+                  case To_Token_Enum (Tree.ID (Children (1))) is
+                  when compilation_unit_list_ID =>
+                     Find_Empty_Nodes (Children (1));
+                     Find_Empty_Nodes (Children (2));
+
+                  when compilation_unit_ID =>
+                     Find_Empty_Nodes (Children (1));
+
+                  when others =>
+                     raise SAL.Programmer_Error;
+                  end case;
+               end;
+
+            when others =>
+               --  FIXME tree-sitter: handle rhs_list %if, %end if
+               null;
+            end case;
+            return;
+         end if;
+
+         case To_Token_Enum (Tree.ID (Node)) is
+         --  SOI, EOI first, then Enum_Token_ID alphabetical order
+
+         when Wisi_SOI_ID | Wisi_EOI_ID =>
+            null;
+
+         when compilation_unit_ID =>
+            Find_Empty_Nodes (Tree.Child (Node, 1));
+
+         when compilation_unit_list_ID =>
+            declare
+               Children : constant Node_Access_Array := Tree.Children (Node);
+            begin
+               case To_Token_Enum (Tree.ID (Children (1))) is
+               when compilation_unit_list_ID =>
+                  Find_Empty_Nodes (Children (1));
+                  Find_Empty_Nodes (Children (2));
+               when compilation_unit_ID =>
+                  Find_Empty_Nodes (Children (1));
+               when others =>
+                  raise SAL.Programmer_Error;
+               end case;
+            end;
+
+         when declaration_ID =>
+            case To_Token_Enum (Tree.ID (Tree.Child (Node, 2))) is
+            when IF_ID | ELSIF_ID =>
+               --  | PERCENT (IF | ELSIF) IDENTIFIER (EQUAL IDENTIFIER | IN 
IDENTIFIER_BAR_list)
+               Nodes_To_Delete.Append (Node);
+               declare
+                  use WisiToken.BNF;
+               begin
+                  if "lexer" = Get_Text (Tree.Child (Node, 3)) then
+                     Ignore_Lines := not 
WisiToken_Grammar_Runtime.Get_Lexer_Set
+                       (Data, Tree, Tree.Child (Node, 5)) (Tree_Sitter_Lexer);
+
+                  elsif "parser" = Get_Text (Tree.Child (Node, 3)) then
+                     Ignore_Lines := not 
WisiToken_Grammar_Runtime.Get_Generate_Algorithm_Set
+                       (Data, Tree, Tree.Child (Node, 5)) 
(WisiToken.BNF.Tree_Sitter);
+
+                  else
+                     raise SAL.Programmer_Error;
+                  end if;
+
+                  if Ignore_Lines and Trace_Generate_EBNF > Outline then
+                     Ada.Text_IO.Put_Line
+                       ("ignore lines true line" &
+                          Tree.Line_Region (Tree.Child (Node, 1), 
Trailing_Non_Grammar => True).First'Image);
+                  end if;
+
+               end;
+
+            when END_ID =>
+               --  %end if
+               Nodes_To_Delete.Append (Node);
+
+            when others =>
+               null;
+            end case;
+
+         when nonterminal_ID =>
+            --  FIXME tree-sitter: handle %if in rhs_list; need test case
+
+            --  tree-sitter allows the start nonterm of the grammar to be 
empty.
+            --  For WisiToken, that's always wisitoken_accept_ID, which is not 
in
+            --  the grammar file. So we ignore that case.
+
+            declare
+               Empty_Node : constant Node_Access := Can_Be_Empty (Tree.Child 
(Node, 3));
+            begin
+               if Empty_Node /= Invalid_Node_Access then
+                  Empty_Nonterms.Append
+                    ((+WisiToken_Grammar_Runtime.Get_Text (Data, Tree, 
Tree.Child (Node, 1)),
+                      Empty_Node));
+               end if;
+            end;
+
+         when wisitoken_accept_ID =>
+            Find_Empty_Nodes (Tree.Child (Node, 2));
+
+         when others =>
+            raise SAL.Not_Implemented with Image (Tree.ID (Node), 
Wisitoken_Grammar_Actions.Descriptor);
+         end case;
+      end Find_Empty_Nodes;
+
+      procedure Delete_Node (Node : in Valid_Node_Access)
+      with Pre => To_Token_Enum (Tree.ID (Node)) in compilation_unit_ID | 
declaration_ID | rhs_list_ID | rhs_ID
+      is
+         use Syntax_Trees.LR_Utils;
+      begin
+         if Trace_Generate_EBNF > Detail then
+            Ada.Text_IO.Put_Line
+              ("delete " & Tree.Image (Node, Node_Numbers => True));
+         end if;
+
+         case To_Token_Enum (Tree.ID (Node)) is
+         when compilation_unit_ID | declaration_ID =>
+            declare
+               Element : constant Valid_Node_Access :=
+                 (if Tree.ID (Node) = +compilation_unit_ID then Node else 
Tree.Parent (Node));
+
+               Container : List   := Creators.Create_From_Element
+                 (Tree, Element, +compilation_unit_list_ID, 
+compilation_unit_ID, Separator_ID => Invalid_Token_ID);
+               Cur       : Cursor := Container.To_Cursor (Element);
+            begin
+               Container.Delete (Cur);
+            end;
+
+         when rhs_list_ID =>
+            --  %if in an rhs_list is not a canonical list element, so we can't
+            --  use LR_Utils.Delete.
+            raise SAL.Not_Implemented;
+
+         when rhs_ID =>
+            declare
+               Container : List   := Creators.Create_From_Element
+                 (Tree, Node, +rhs_list_ID, +rhs_ID, Separator_ID => 
Invalid_Token_ID);
+               Cur       : Cursor := Container.To_Cursor (Node);
+            begin
+               Container.Delete (Cur);
+            end;
+
+         when others =>
+            raise SAL.Programmer_Error;
+         end case;
+      end Delete_Node;
+
+      procedure Make_Non_Empty (Empty_Node : in Valid_Node_Access)
+      with Pre => To_Token_Enum (Tree.ID (Empty_Node)) in
+                  rhs_ID | rhs_item_list_ID | rhs_item_ID
+      is
+         use WisiToken.Syntax_Trees.LR_Utils;
+         use all type SAL.Base_Peek_Type;
+
+         procedure Make_Non_Empty_RHS_Item (Item : in Valid_Node_Access)
+         with Pre => Tree.ID (Item) = +rhs_item_ID
+         is
+            Item_Var : Valid_Node_Access := Item;
+         begin
+            case Tree.RHS_Index (Item) is
+            when 0 | 1 | 2 =>
+               raise SAL.Programmer_Error;
+
+            when 3 => -- rhs_optional_item
+               declare
+                  Optional_Item :  Valid_Node_Access := Tree.Child (Item, 1);
+               begin
+                  case Tree.RHS_Index (Optional_Item) is
+                  when 0         =>
+                     Tree.Set_Children
+                       (Node     => Optional_Item,
+                        New_ID   => (+rhs_group_item_ID, 0),
+                        Children =>
+                          (1     => Tree.Add_Terminal (+LEFT_PAREN_ID),
+                           2     => Tree.Child (Optional_Item, 2),
+                           3     => Tree.Add_Terminal (+RIGHT_PAREN_ID)));
+
+                     Tree.Set_Children
+                       (Node     => Item_Var,
+                        New_ID   => (+rhs_item_ID, 5),
+                        Children =>
+                          (1     => Optional_Item));
+
+                  when 1         =>
+                     Tree.Set_Children
+                       (Node     => Optional_Item,
+                        New_ID   => (+rhs_group_item_ID, 0),
+                        Children => Tree.Children (Optional_Item) (1 .. 3));
+
+                     Tree.Set_Children
+                       (Node     => Item_Var,
+                        New_ID   => (+rhs_item_ID, 5),
+                        Children =>
+                          (1     => Optional_Item));
+
+                  when 2         =>
+                     Tree.Set_Children
+                       (Node     => Item_Var,
+                        New_ID   => (+rhs_item_ID, 0),
+                        Children => (1 => Tree.Child (Optional_Item, 1)));
+
+                  when 3         =>
+                     Tree.Set_Children
+                       (Node     => Item_Var,
+                        New_ID   => (+rhs_item_ID, 1),
+                        Children => (1 => Tree.Child (Optional_Item, 1)));
+
+                  when others =>
+                     raise SAL.Programmer_Error;
+                  end case;
+               end;
+
+            when 4 =>
+               declare
+                  Multiple_Item : Valid_Node_Access := Tree.Child (Item, 1);
+               begin
+                  case Tree.RHS_Index (Multiple_Item) is
+                  when 0 | 3 | 5 =>
+                     Tree.Set_Children
+                       (Multiple_Item,
+                        (+rhs_multiple_item_ID,
+                         (case Tree.RHS_Index (Multiple_Item) is
+                          when 0 => 1,
+                          when 3 => 2,
+                          when 5 => 4,
+                          when others => raise SAL.Programmer_Error)),
+                        (case Tree.RHS_Index (Multiple_Item) is
+                         when 0 | 3 =>
+                           (1 => Tree.Child (Multiple_Item, 1),
+                            2 => Tree.Child (Multiple_Item, 2),
+                            3 => Tree.Child (Multiple_Item, 3),
+                            4 => (case Tree.RHS_Index (Multiple_Item) is
+                                  when 0 => Tree.Add_Terminal (+MINUS_ID),
+                                  when 3 => Tree.Add_Terminal (+PLUS_ID),
+                                  when others => raise SAL.Programmer_Error)),
+                         when 5 => (1 => Tree.Child (Multiple_Item, 1)),
+                         when others => raise SAL.Programmer_Error));
+
+                  when others =>
+                     raise SAL.Programmer_Error with "make_non_empty_rhs_item 
" & Tree.Image
+                       (Multiple_Item, RHS_Index => True, Node_Numbers => 
True);
+                  end case;
+               end;
+
+            when others =>
+               raise SAL.Programmer_Error;
+            end case;
+         end Make_Non_Empty_RHS_Item;
+
+      begin
+         case To_Token_Enum (Tree.ID (Empty_Node)) is
+         when rhs_item_ID =>
+            Make_Non_Empty_RHS_Item (Empty_Node);
+
+         when rhs_item_list_ID =>
+            --  Entire item_list can be empty
+            declare
+               Item_List : constant Constant_List := Creators.Create_List
+                 (Tree, Empty_Node, +rhs_item_list_ID, +rhs_element_ID);
+            begin
+               --  If there is more than one item in the rhs_item_list, we can
+               --  arbitrarily make the first non-empty. See ada_lite_ebnf.wy
+               --  handled_sequence_of_statements.
+               Make_Non_Empty_RHS_Item (Tree.Find_Descendant (Element 
(Item_List.First), +rhs_item_ID));
+            end;
+
+         when rhs_ID =>
+            Delete_Node (Empty_Node);
+
+         when others =>
+            raise SAL.Programmer_Error;
+         end case;
+      end Make_Non_Empty;
+
+      procedure Make_Optional (Name : in String)
+      is
+         procedure Find_Nodes (Node : in Valid_Node_Access)
+         is
+            use all type SAL.Base_Peek_Type;
+            Node_Var : Node_Access := Node;
+         begin
+            case To_Token_Enum (Tree.ID (Node)) is
+            --  common code first, then Enum_Token_ID alphabetical order
+
+            when wisitoken_accept_ID =>
+               Find_Nodes (Tree.Child (Node, 2));
+
+            when compilation_unit_ID =>
+               Find_Nodes (Tree.Child (Node, 1));
+
+            when compilation_unit_list_ID | rhs_alternative_list_ID | 
rhs_item_list_ID | rhs_list_ID =>
+               declare
+                  Children : constant Node_Access_Array := Tree.Children 
(Node);
+               begin
+                  case Tree.RHS_Index (Node) is
+                  when 0 =>
+                     Find_Nodes (Children (1));
+
+                  when 1 =>
+                     Find_Nodes (Children (1));
+                     Find_Nodes
+                       (Children
+                          ((if To_Token_Enum (Tree.ID (Node)) in rhs_list_ID | 
rhs_alternative_list_ID then 3 else 2)));
+
+                  when others =>
+                     --  rhs_list can have other rhs_index, but those nodes 
should have been
+                     --  deleted by now.
+                     raise SAL.Programmer_Error with "Make_Optional.Find_Nodes 
list: rhs_index" &
+                       Tree.RHS_Index (Node)'Image & " node " & Tree.Image
+                         (Node, Node_Numbers => True);
+                  end case;
+               end;
+
+            when declaration_ID =>
+               return;
+
+            when nonterminal_ID =>
+               if Name = Get_Text (Tree.Child (Node, 1)) then
+                  return;
+               end if;
+
+               Find_Nodes (Tree.Child (Node, 3));
+
+            when rhs_element_ID =>
+               Find_Nodes (Tree.Child (Node, (if Tree.RHS_Index (Node) = 0 
then 1 else 3)));
+
+            when rhs_group_item_ID =>
+               Find_Nodes (Tree.Child (Node, 2));
+
+            when rhs_item_ID =>
+               case Tree.RHS_Index (Node) is
+               when 0 | 1 =>
+                  if Name = Get_Text (Tree.Child (Node, 1)) then
+                     Nodes_To_Check.Append (Node);
+                     declare
+                        Child : constant Valid_Node_Access := 
WisiToken_Grammar_Editing.Add_RHS_Optional_Item
+                          (Tree,
+                           RHS_Index => (if Tree.RHS_Index (Node) = 0 then 2 
else 3),
+                           Content   => Tree.Child (Node, 1));
+                     begin
+                        Tree.Set_Children (Node_Var, (+rhs_item_ID, 3), (1 => 
Child));
+                     end;
+                  end if;
+
+               when 2 =>
+                  null;
+
+               when 3 | 4 | 5 =>
+                  Find_Nodes (Tree.Child (Node, 1));
+
+               when others =>
+                  raise SAL.Programmer_Error;
+               end case;
+
+            when rhs_multiple_item_ID =>
+               case Tree.RHS_Index (Node) is
+               when 0 | 1 | 2 | 3 =>
+                  Find_Nodes (Tree.Child (Node, 2));
+
+               when 4 =>
+                  Nodes_To_Check.Append (Node);
+
+                  Tree.Set_Children
+                    (Node_Var, (+rhs_multiple_item_ID, 5), (Tree.Child (Node, 
1), Tree.Add_Terminal (+STAR_ID)));
+
+               when 5 =>
+                  --  already optional
+                  null;
+               when others =>
+                  raise SAL.Programmer_Error;
+               end case;
+
+            when rhs_optional_item_ID =>
+               case Tree.RHS_Index (Node) is
+               when 0 | 1 =>
+                  Find_Nodes (Tree.Child (Node, 2));
+
+               when 2 | 3 =>
+                  --  already optional
+                  null;
+               when others =>
+                  raise SAL.Programmer_Error;
+               end case;
+
+            when rhs_ID =>
+               if Tree.Child_Count (Node) = 0 then
+                  return;
+               else
+                  Find_Nodes (Tree.Child (Node, 1));
+               end if;
+
+            when others =>
+               raise SAL.Programmer_Error with "Make_Optional.Find_Nodes name 
" & Name & " in node " & Tree.Image
+                 (Node, Node_Numbers => True);
+            end case;
+         end Find_Nodes;
+
+      begin
+         Find_Nodes (Tree.Root);
+      end Make_Optional;
+
+   begin
+      if Trace_Generate_EBNF > Outline then
+         Ada.Text_IO.New_Line;
+         Ada.Text_IO.Put_Line ("tree_sitter eliminate empty productions 
start");
+         if Trace_Generate_EBNF > Detail then
+            Tree.Print_Tree (Tree.Root);
+         end if;
+      end if;
+
+      Find_Empty_Nodes (Tree.Root);
+      --  Also finds %if etc, adds them to Nodes_To_Delete.
+
+      if Trace_Generate_EBNF > Outline then
+         Ada.Text_IO.Put_Line ("nodes to delete:" & 
Nodes_To_Delete.Length'Image);
+      end if;
+
+      for Node of Nodes_To_Delete loop
+         Delete_Node (Node);
+      end loop;
+
+      Data.Error_Reported.Clear;
+
+      Tree.Validate_Tree
+        (Data, Data.Error_Reported,
+         Root             => Tree.Root,
+         Validate_Node    => WisiToken_Grammar_Editing.Validate_Node'Access,
+         Node_Index_Order => True);
+
+      if Trace_Generate_EBNF > Outline then
+         Ada.Text_IO.Put_Line ("empty nonterms:");
+         for Nonterm of Empty_Nonterms loop
+            Ada.Text_IO.Put (-Nonterm.Name & ", ");
+         end loop;
+         Ada.Text_IO.New_Line (2);
+      end if;
+
+      for Nonterm of Empty_Nonterms loop
+         Make_Non_Empty (Nonterm.Empty_Node);
+      end loop;
+
+      if Trace_Generate_EBNF > Outline then
+         Ada.Text_IO.Put_Line ("after Make_Non_Empty");
+         Ada.Text_IO.New_Line;
+      end if;
+
+      Tree.Validate_Tree
+        (Data, Data.Error_Reported,
+         Root             => Tree.Root,
+         Validate_Node    => WisiToken_Grammar_Editing.Validate_Node'Access,
+         Node_Index_Order => False);
+
+      for Nonterm of Empty_Nonterms loop
+         Make_Optional (-Nonterm.Name);
+      end loop;
+
+      if Trace_Generate_EBNF > Outline then
+         Ada.Text_IO.Put_Line ("after Make_Optional");
+         Ada.Text_IO.New_Line;
+      end if;
+
+      Tree.Validate_Tree
+        (Data, Data.Error_Reported,
+         Root             => Tree.Root,
+         Validate_Node    => WisiToken_Grammar_Editing.Validate_Node'Access,
+         Node_Index_Order => False);
+
+      declare
+         use Valid_Node_Access_Lists;
+         Cur  : Cursor := Nodes_To_Check.First;
+         Temp : Cursor;
+      begin
+         loop
+            exit when not Has_Element (Cur);
+            declare
+               RHS_List_Node : constant Valid_Node_Access := 
Tree.Find_Ancestor (Element (Cur), +rhs_list_ID);
+               Nonterm_Node  : constant Valid_Node_Access := Tree.Parent 
(RHS_List_Node);
+
+               Empty_Node : constant Node_Access := Can_Be_Empty 
(RHS_List_Node);
+            begin
+               if Empty_Node /= Invalid_Node_Access then
+                  declare
+                     Nonterm_Name : constant String := Get_Text (Tree.Child 
(Nonterm_Node, 1));
+                  begin
+                     if Trace_Generate_EBNF > Outline then
+                        Ada.Text_IO.Put_Line ("newly empty nonterm " & 
Nonterm_Name);
+                     end if;
+
+                     Make_Non_Empty (Empty_Node);
+                     Make_Optional (Nonterm_Name);
+                  end;
+               end if;
+            end;
+            Temp := Cur;
+            Cur  := Next (Cur);
+            Nodes_To_Check.Delete (Temp);
+         end loop;
+      end;
+
+      if Trace_Generate_EBNF > Outline then
+         Ada.Text_IO.Put_Line ("after Nodes_To_Check");
+         Ada.Text_IO.New_Line;
+      end if;
+
+      Tree.Validate_Tree
+        (Data, Data.Error_Reported,
+         Root             => Tree.Root,
+         Validate_Node    => WisiToken_Grammar_Editing.Validate_Node'Access,
+         Node_Index_Order => False);
+
+      if Trace_Generate_EBNF > Detail then
+         Ada.Text_IO.New_Line;
+         Ada.Text_IO.Put_Line ("tree_sitter eliminate empty productions end");
+         Tree.Print_Tree (Tree.Root);
+      end if;
+   end Eliminate_Empty_Productions;
+
+   procedure Print_Tree_Sitter
+     (Data             : in     WisiToken_Grammar_Runtime.User_Data_Type;
+      Tree             : in out Syntax_Trees.Tree;
+      Lexer            : in     WisiToken.Lexer.Handle;
+      Output_File_Name : in     String;
+      Language_Name    : in     String)
+   is
+      use all type Ada.Containers.Count_Type;
+
+      File : File_Type;
+
+      Extras    : WisiToken.BNF.String_Lists.List;
+      Conflicts : WisiToken.BNF.String_Lists.List;
+
+      Start_Node : Node_Access := Invalid_Node_Access;
+
+      --  Local specs
+
+      procedure Put_RHS_Item_List (Node : in Valid_Node_Access; First : in 
Boolean)
+      with Pre => Tree.ID (Node) = +rhs_item_list_ID;
+
+      --  Local bodies
+
+      function Get_Text (Tree_Index : in Valid_Node_Access) return String
+      is
+         function Strip_Delimiters (Tree_Index : in Valid_Node_Access) return 
String
+         is
+            Region : Buffer_Region renames Tree.Byte_Region (Tree_Index, 
Trailing_Non_Grammar => False);
+         begin
+            if -Tree.ID (Tree_Index) in RAW_CODE_ID | REGEXP_ID | ACTION_ID 
then
+               --  Strip delimiters. We don't strip leading/trailing spaces to 
preserve indent.
+               return Lexer.Buffer_Text ((Region.First + 2, Region.Last - 2));
+
+               --  We don't strip string delimiters; tree-setter can use the 
same ones.
+            else
+               return Lexer.Buffer_Text (Region);
+            end if;
+         end Strip_Delimiters;
+
+      begin
+         case Tree.Label (Tree_Index) is
+         when Source_Terminal =>
+            return Strip_Delimiters (Tree_Index);
+
+         when Virtual_Terminal =>
+            --  Terminal keyword inserted during tree edit. We could check for
+            --  Identifier, but that will be caught later.
+            return Image (Tree.ID (Tree_Index), 
Wisitoken_Grammar_Actions.Descriptor);
+
+         when Virtual_Identifier =>
+            raise SAL.Programmer_Error;
+
+         when Nonterm =>
+            declare
+               use all type Ada.Strings.Unbounded.Unbounded_String;
+               Result       : Ada.Strings.Unbounded.Unbounded_String;
+               Tree_Indices : constant Valid_Node_Access_Array := 
Tree.Get_Terminals (Tree_Index);
+               Need_Space   : Boolean                                      := 
False;
+            begin
+               for Tree_Index of Tree_Indices loop
+                  Result := Result & (if Need_Space then " " else "") &
+                    Get_Text (Tree_Index);
+                  Need_Space := True;
+               end loop;
+               return -Result;
+            end;
+         end case;
+      end Get_Text;
+
+      procedure Not_Translated (Label : in String; Node : in Valid_Node_Access)
+      is begin
+         New_Line (File);
+         Put (File, "// " & Label & ": not translated: " & Node_Access'Image 
(Node) & ":" &
+                Tree.Image (Node, Children => True));
+
+         Put_Line
+           (Current_Error,
+            Tree.Error_Message
+              (Node,
+               "not translated: " &
+                 Tree.Image
+                   (Node,
+                    RHS_Index    => True,
+                    Children     => True,
+                    Node_Numbers => True)));
+      end Not_Translated;
+
+      procedure Put_RHS_Alternative_List (Node : in Valid_Node_Access; First : 
in Boolean)
+      with Pre => Tree.ID (Node) = +rhs_alternative_list_ID
+      is begin
+         case Tree.RHS_Index (Node) is
+         when 0 =>
+            --  If only alternative, don't need "choice()".
+            Put_RHS_Item_List (Tree.Child (Node, 1), First => True);
+
+         when 1 =>
+            if First then
+               Put (File, "choice(");
+            end if;
+
+            Put_RHS_Alternative_List (Tree.Child (Node, 1), First => False);
+            Put (File, ", ");
+            Put_RHS_Item_List (Tree.Child (Node, 3), First => True);
+
+            if First then
+               Put (File, ")");
+            end if;
+
+         when others =>
+            Not_Translated ("Put_RHS_Alternative_List", Node);
+         end case;
+      end Put_RHS_Alternative_List;
+
+      procedure Put_RHS_Optional_Item (Node : in Valid_Node_Access)
+      with Pre => Tree.ID (Node) = +rhs_optional_item_ID
+      is begin
+         Put (File, "optional(");
+
+         case Tree.RHS_Index (Node) is
+         when 0 | 1 =>
+            Put_RHS_Alternative_List (Tree.Child (Node, 2), First => True);
+         when 2 =>
+            Put (File, "$." & Get_Text (Tree.Child (Node, 1)));
+         when 3 =>
+            --  STRING_LITERAL_2
+            Put (File, Get_Text (Tree.Child (Node, 1)));
+         when others =>
+            Not_Translated ("Put_RHS_Optional_Item", Node);
+         end case;
+
+         Put (File, ")");
+      end Put_RHS_Optional_Item;
+
+      procedure Put_RHS_Multiple_Item (Node : in Valid_Node_Access)
+      with Pre => Tree.ID (Node) = +rhs_multiple_item_ID
+      is begin
+         case Tree.RHS_Index (Node) is
+         when 0 | 3 =>
+            Put (File, "repeat(");
+            Put_RHS_Alternative_List (Tree.Child (Node, 2), First => True);
+            Put (File, ")");
+
+         when 1 | 2 =>
+            Put (File, "repeat1(");
+            Put_RHS_Alternative_List (Tree.Child (Node, 2), First => True);
+            Put (File, ")");
+
+         when 4 =>
+            Put (File, "repeat1(");
+            Put (File, "$." & Get_Text (Tree.Child (Node, 1)));
+            Put (File, ")");
+
+         when 5 =>
+            Put (File, "repeat(");
+            Put (File, "$." & Get_Text (Tree.Child (Node, 1)));
+            Put (File, ")");
+
+         when others =>
+            Not_Translated ("Put_RHS_Multiple_Item", Node);
+         end case;
+      end Put_RHS_Multiple_Item;
+
+      procedure Put_RHS_Group_Item (Node : in Valid_Node_Access)
+      with Pre => Tree.ID (Node) = +rhs_group_item_ID
+      is begin
+         Put_RHS_Alternative_List (Tree.Child (Node, 2), First => True);
+      end Put_RHS_Group_Item;
+
+      procedure Put_RHS_Item (Node : in Valid_Node_Access)
+      with Pre => Tree.ID (Node) = +rhs_item_ID
+      is begin
+         case Tree.RHS_Index (Node) is
+         when 0 =>
+            declare
+               Ident : constant String     := Get_Text (Node);
+               Decl  : constant Node_Access := 
WisiToken_Grammar_Editing.Find_Declaration (Data, Tree, Ident);
+            begin
+               if Decl = Invalid_Node_Access then
+                  Generate.Put_Error (Tree.Error_Message (Node, "decl for '" & 
Ident & "' not found"));
+
+               elsif Tree.ID (Decl) = +nonterminal_ID then
+                  Put (File, "$." & Get_Text (Tree.Child (Decl, 1)));
+
+               else
+                  case Tree.RHS_Index (Decl) is
+                  when 0 =>
+                     case To_Token_Enum (Tree.ID (Tree.Child (Tree.Child 
(Decl, 2), 1))) is
+                     when KEYWORD_ID =>
+                        Put (File, Get_Text (Tree.Child (Decl, 4)));
+
+                     when NON_GRAMMAR_ID =>
+                        Not_Translated ("put_rhs_item", Node);
+
+                     when Wisitoken_Grammar_Actions.TOKEN_ID =>
+                        declare
+                           use WisiToken.Syntax_Trees.LR_Utils;
+                           List : constant Constant_List := 
Creators.Create_List
+                             (Tree, Tree.Child (Decl, 4), 
+declaration_item_list_ID, +declaration_item_ID);
+                           Item : constant Valid_Node_Access := Tree.Child 
(Element (List.First), 1);
+                        begin
+                           case To_Token_Enum (Tree.ID (Item)) is
+                           when REGEXP_ID =>
+                              Put (File, "$." & Ident);
+
+                           when STRING_LITERAL_1_ID | STRING_LITERAL_2_ID =>
+                              --  FIXME tree-sitter: STRING_LITERAL_1_ID in 
regexp is case insensitive; not
+                              --  clear how to do that in tree-sitter.
+                              Put (File, Get_Text (Item));
+
+                           when others =>
+                              Not_Translated ("put_rhs_item ident token", 
Node);
+                           end case;
+                        end;
+
+                     when others =>
+                        Not_Translated ("put_rhs_item ident", Node);
+                     end case;
+
+                  when others =>
+                     Not_Translated ("put_rhs_item 0", Node);
+                  end case;
+               end if;
+            end;
+
+         when 1 =>
+            --  STRING_LITERAL_2
+            Put (File, Get_Text (Node));
+
+         when 2 =>
+            --  ignore attribute
+            null;
+
+         when 3 =>
+            Put_RHS_Optional_Item (Tree.Child (Node, 1));
+
+         when 4 =>
+            Put_RHS_Multiple_Item (Tree.Child (Node, 1));
+
+         when 5 =>
+            Put_RHS_Group_Item (Tree.Child (Node, 1));
+
+         when others =>
+            Not_Translated ("Put_RHS_Item", Node);
+         end case;
+      end Put_RHS_Item;
+
+      procedure Put_RHS_Element (Node : in Valid_Node_Access)
+      with Pre => Tree.ID (Node) = +rhs_element_ID
+      is begin
+         case Tree.RHS_Index (Node) is
+         when 0 =>
+            Put_RHS_Item (Tree.Child (Node, 1));
+
+         when 1 =>
+            --  Ignore the label
+            Put_RHS_Item (Tree.Child (Node, 3));
+
+         when others =>
+            Not_Translated ("Put_RHS_Element", Node);
+         end case;
+      end Put_RHS_Element;
+
+      procedure Put_RHS_Item_List (Node : in Valid_Node_Access; First : in 
Boolean)
+      is
+         Children : constant Node_Access_Array := Tree.Children (Node);
+      begin
+         if Children'Length = 1 then
+            Put_RHS_Element (Children (1));
+         else
+            if First then
+               Put (File, "seq(");
+            end if;
+            Put_RHS_Item_List (Children (1), First => False);
+            Put (File, ", ");
+            Put_RHS_Element (Children (2));
+
+            if First then
+               Put (File, ")");
+            end if;
+         end if;
+      end Put_RHS_Item_List;
+
+      procedure Put_RHS (Node : in Valid_Node_Access)
+      with Pre => Tree.ID (Node) = +rhs_ID
+      is begin
+         case Tree.RHS_Index (Node) is
+         when 0 =>
+            Generate.Put_Error
+              (Tree.Error_Message
+                 ((declare
+                      RHS_List : constant Valid_Node_Access := Tree.Parent 
(Node);
+                   begin
+                      (case Tree.RHS_Index (RHS_List) is
+                       when 0 => RHS_List,
+                       when others => Tree.Child (RHS_List, 2))),
+                  "empty RHS forbidden by tree-sitter"));
+
+         when 1 .. 3 =>
+            Put_RHS_Item_List (Tree.Child (Node, 1), First => True);
+            --  tree-sitter does not have actions in the grammar
+
+         when others =>
+            Not_Translated ("put_rhs", Node);
+         end case;
+      end Put_RHS;
+
+      procedure Put_RHS_List (Node : in Valid_Node_Access; First : in Boolean)
+      with Pre => Tree.ID (Node) = +rhs_list_ID
+      is
+         Children : constant Node_Access_Array := Tree.Children (Node);
+      begin
+         case Tree.RHS_Index (Node) is
+         when 0 =>
+            Put_RHS (Children (1));
+
+         when 1 =>
+            if First then
+               Put (File, "choice(");
+            end if;
+
+            Put_RHS_List (Children (1), First => False);
+            Put (File, ",");
+            Put_RHS (Children (3));
+
+            if First then
+               Put (File, ")");
+            end if;
+
+         when 2 .. 4 =>
+            --  Should have been eliminated by Eliminate_Empty_Productions
+            raise SAL.Programmer_Error with "Print_Tree_Sitter rhs_list %if " &
+              Tree.Image (Node, Node_Numbers => True);
+
+         when others =>
+               raise SAL.Programmer_Error;
+         end case;
+      end Put_RHS_List;
+
+      procedure Process_Node (Node : in Valid_Node_Access)
+      is begin
+         if Node = Start_Node then
+            return;
+         end if;
+
+         case To_Token_Enum (Tree.ID (Node)) is
+         --  SOI, EOI first, then Enum_Token_ID alphabetical order
+
+         when Wisi_SOI_ID | Wisi_EOI_ID =>
+            null;
+
+         when compilation_unit_ID =>
+            Process_Node (Tree.Child (Node, 1));
+
+         when compilation_unit_list_ID =>
+            declare
+               Children : constant Node_Access_Array := Tree.Children (Node);
+            begin
+               case To_Token_Enum (Tree.ID (Children (1))) is
+               when compilation_unit_list_ID =>
+                  Process_Node (Children (1));
+                  Process_Node (Children (2));
+               when compilation_unit_ID =>
+                  Process_Node (Children (1));
+               when others =>
+                  raise SAL.Programmer_Error;
+               end case;
+            end;
+
+         when declaration_ID =>
+            raise SAL.Not_Implemented with "FIXME: match current 
wisitoken_grammar.wy";
+            --  case To_Token_Enum (Tree.ID (Tree.Child (Node, 2))) is
+            --  when Wisitoken_Grammar_Actions.TOKEN_ID | NON_GRAMMAR_ID =>
+            --     --  We need tokens with 'regexp' values because they are 
not defined
+            --     --  elsewhere, 'punctuation' tokens for consistent names, 
and
+            --     --  'line-comment' to allow comments. tree-sitter default 
'extras'
+            --     --  handles whitespace and newline, but if we define 
'comment', we
+            --     --  also need 'new-line' and 'whitespace'.
+            --     declare
+            --        use Ada.Strings;
+            --        use Ada.Strings.Fixed;
+            --        use WisiToken.Syntax_Trees.LR_Utils;
+            --        Name  : constant String        := Get_Text (Tree.Child 
(Node, 3));
+            --        Class : constant Token_Enum_ID := To_Token_Enum (Tree.ID 
(Tree.Child (Tree.Child (Node, 2), 1)));
+            --        Kind  : constant String        :=
+            --          (if Class in NON_GRAMMAR_ID | 
Wisitoken_Grammar_Actions.TOKEN_ID
+            --           then Get_Text (Tree.Child (Tree.Child (Node, 2), 3))
+            --           else "keyword");
+            --        List  : constant Constant_List    := Creators.Create_List
+            --          (Tree, Tree.Child (Node, 4), 
+declaration_item_list_ID, +declaration_item_ID);
+            --        Value : constant Valid_Node_Access := Tree.Child 
(Element (List.First), 1);
+            --        --  We are ignoring any repair image
+            --     begin
+            --        if Class = NON_GRAMMAR_ID then
+            --           if Kind = "line-comment" then
+            --              --  WORKAROUND: tree-sitter 0.16.6 treats rule 
"token(seq('--',
+            --              --  /.*/))" correctly for an Ada comment, but not 
extra "/--.*/". See
+            --              --  github tree-sitter issue 651 - closed without 
resolving this
+            --              --  question, but it does provide a workaround.
+            --              Put_Line (File, Name & ": $ => token(seq(" & 
Get_Text (Value) & ", /.*/)),");
+            --              Extras.Append ("$." & Name);
+            --           else
+            --              Extras.Append ("/" & Trim (Get_Text (Value), Both) 
& "/");
+            --           end if;
+
+            --        elsif Kind = "punctuation" then
+            --           Put_Line (File, Name & ": $ => " & Get_Text (Value) & 
",");
+
+            --        elsif To_Token_Enum (Tree.ID (Value)) = REGEXP_ID then
+            --           Put_Line (File, Name & ": $ => /" & Trim (Get_Text 
(Value), Both) & "/,");
+
+            --        end if;
+            --     end;
+
+            --  when 1 =>
+            --     --  new-line with no regexp; tree-sitter defaults to DOS, 
Unix newline.
+            --     null;
+
+            --  when 2 =>
+            --     --  FIXME tree-sitter: CODE copyright_license
+            --     null;
+
+            --  when 3 =>
+            --     declare
+            --        Kind : constant String := Get_Text (Tree.Child (Node, 
2));
+            --     begin
+            --        --  FIXME tree-sitter: lexer_regexp
+            --        if Kind = "conflict" then
+            --           --  .wy LR format:
+            --           --  %conflict action LHS [| action LHS]* 'on token' on
+            --           --            I      I+1
+            --           --
+            --           --  .wy Tree_Sitter format:
+            --           --  %conflict LHS (LHS)*
+            --           --
+            --           --  .js format:
+            --           --  [$.LHS, $.LHS, ...]
+
+            --           declare
+            --              use Ada.Strings.Unbounded;
+
+            --            Tree_Indices : constant Valid_Node_Access_Array := 
Tree.Get_Terminals (Tree.Child (Node, 3));
+            --              Result       : Unbounded_String                := 
+"[";
+            --           begin
+            --              if Tree_Indices'Length < 3 or else Tree.ID 
(Tree_Indices (3)) /= +BAR_ID then
+            --                 --  Tree_Sitter format
+            --                 for LHS of Tree_Indices loop
+            --                    Result := @ & "$." & Get_Text (LHS) & ", ";
+            --                 end loop;
+
+            --              else
+            --                 --  LR format
+            --                 declare
+            --                    use all type SAL.Base_Peek_Type;
+            --                    I : SAL.Peek_Type := Tree_Indices'First;
+            --                 begin
+            --                    loop
+            --                       Result := @ & "$." & Get_Text 
(Tree_Indices (I + 1)) & ", ";
+
+            --                       I := I + 2;
+            --                       exit when Tree.ID (Tree_Indices (I)) /= 
+BAR_ID;
+            --                       I := I + 1;
+            --                    end loop;
+            --                 end;
+            --              end if;
+            --              Conflicts.Append (-Result & ']');
+            --           end;
+            --        end if;
+            --     end;
+
+            --  when 4 =>
+            --     --  %case_insensitive
+            --     null;
+
+            --  when 5 .. 9 =>
+            --     --  Should have been eliminated by 
Eliminate_Empty_Productions
+            --     raise SAL.Programmer_Error with "Print_Tree_Sitter 
declaration %if " &
+            --       Tree.Image (Node, Node_Numbers => True);
+
+            --  when others =>
+            --     raise SAL.Programmer_Error;
+            --  end case;
+
+         when nonterminal_ID =>
+            declare
+               Children : constant Node_Access_Array := Tree.Children (Node);
+            begin
+               Put (File, Get_Text (Children (1)) & ": $ => ");
+
+               Put_RHS_List (Children (3), First => True);
+
+               Put_Line (File, ",");
+            end;
+
+         when wisitoken_accept_ID =>
+            --  Child 1 is SOI, 2 compilation_unit_list
+            Process_Node (Tree.Child (Node, 2));
+
+         when others =>
+            raise SAL.Not_Implemented with Image (Tree.ID (Node), 
Wisitoken_Grammar_Actions.Descriptor);
+         end case;
+      end Process_Node;
+   begin
+      if Trace_Generate_EBNF > Outline then
+         Ada.Text_IO.Put_Line ("translate to tree_sitter");
+      end if;
+
+      Create (File, Out_File, Output_File_Name);
+      Put_Line (File, "// generated from " & Tree.Lexer.File_Name & " -*- 
buffer-read-only:t -*-");
+
+      Put_Line (File, "module.exports = grammar({");
+      Put_Line (File, "  name: '" & Language_Name & "',");
+
+      Put_Line (File, "  rules: {");
+
+      --  Start symbol must be the first rule; that's how tree-sitter knows
+      --  it's the start symbol. accept rule with wisi-eoi is implicit in
+      --  tree-sitter (as in .wy).
+      if -Data.Language_Params.Start_Token = "" then
+         Generate.Put_Error (Generate.Error_Message (Tree.Lexer.File_Name, 1, 
"%start not specified"));
+      else
+         declare
+            Temp : constant Node_Access := 
WisiToken_Grammar_Editing.Find_Declaration
+              (Data, Tree, -Data.Language_Params.Start_Token);
+         begin
+            Process_Node (Temp);
+            Start_Node := Temp;
+         end;
+      end if;
+
+      Process_Node (Tree.Root);
+      Put (File, "  }");
+
+      if Conflicts.Length > 0 then
+         Put_Line (File, ",");
+         Put_Line (File, "  conflicts: $ => [");
+         for Item of Conflicts loop
+            Put_Line (File, "    " & Item & ",");
+         end loop;
+         Put (File, "  ]");
+      end if;
+
+      if Extras.Length > 0 then
+         Put_Line (File, ",");
+         Put_Line (File, "  extras: $ => [");
+         for Item of Extras loop
+            Put_Line (File, "    " & Item & ",");
+         end loop;
+         Put_Line (File, "  ],");
+      end if;
+      Put (File, "  }");
+
+      Put_Line (File, ");");
+      Close (File);
+   end Print_Tree_Sitter;
+
+   procedure Create_Test_Main (Output_File_Name_Root : in String)
+   is
+      use WisiToken.BNF;
+
+      Ada_Name_Root : constant String := Output_Ada_Common.File_Name_To_Ada 
(Output_File_Name_Root);
+      Unit_Name     : constant String := Ada_Name_Root & "_Tree_Sitter_Run";
+
+      File_Name : constant String := To_Lower (Unit_Name) & ".adb";
+
+      File : File_Type;
+
+   begin
+      Create (File, Out_File, File_Name);
+      Set_Output (File);
+
+      Put_File_Header (Ada_Comment);
+      --  no Copyright_License; just a test file
+      New_Line;
+
+      Put_Line ("with Interfaces.C.Extensions;");
+      Put_Line ("with Gen_Tree_Sitter_Parser_Run;");
+      Put_Line ("procedure " & Unit_Name);
+      Put_Line ("is");
+      Put_Line ("   function Tree_Sitter_" & Ada_Name_Root & " return 
Interfaces.C.Extensions.void_ptr");
+      Put_Line ("   with Import     => True,");
+      Put_Line ("     External_Name => ""tree_sitter_" & Ada_Name_Root & 
""",");
+      Put_Line ("     Convention    => C;");
+      Put_Line ("   procedure Parse_Run is new Gen_Tree_Sitter_Parser_Run");
+      Put_Line ("     (Tree_Sitter_Language => Tree_Sitter_" & Ada_Name_Root & 
");");
+      Put_Line ("begin");
+      Put_Line ("   Parse_Run;");
+      Put_Line ("end " & Unit_Name & ";");
+      Close (File);
+      Set_Output (Standard_Output);
+   end Create_Test_Main;
+
+end WisiToken.Generate.Tree_Sitter;
+--  Local Variables:
+--  ada-case-strict: nil
+--  End:
diff --git a/wisitoken-generate-tree_sitter.ads 
b/wisitoken-generate-tree_sitter.ads
new file mode 100644
index 0000000000..31075396a8
--- /dev/null
+++ b/wisitoken-generate-tree_sitter.ads
@@ -0,0 +1,39 @@
+--  Abstract :
+--
+--  WisiToken utilities for using the tree-sitter parser.
+--
+--  Copyright (C) 2020, 2021, 2022 Free Software Foundation All Rights 
Reserved.
+--
+--  This library is free software;  you can redistribute it and/or modify it
+--  under terms of the  GNU General Public License  as published by the Free
+--  Software  Foundation;  either version 3,  or (at your  option) any later
+--  version. This library is distributed in the hope that it will be useful,
+--  but WITHOUT ANY WARRANTY;  without even the implied warranty of MERCHAN-
+--  TABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+pragma License (Modified_GPL);
+
+with WisiToken.Lexer;
+with WisiToken.Syntax_Trees;
+with WisiToken_Grammar_Runtime;
+package WisiToken.Generate.Tree_Sitter is
+
+   procedure Eliminate_Empty_Productions
+     (Data : in out WisiToken_Grammar_Runtime.User_Data_Type;
+      Tree : in out WisiToken.Syntax_Trees.Tree);
+   --  Edit Tree to eliminate productions that can be empty, which are
+   --  forbidden by the tree-sitter generator.
+   --
+   --  Also processes %if, so subsequent passes don't have to.
+
+   procedure Print_Tree_Sitter
+     (Data             : in     WisiToken_Grammar_Runtime.User_Data_Type;
+      Tree             : in out WisiToken.Syntax_Trees.Tree;
+      Lexer            : in     WisiToken.Lexer.Handle;
+      Output_File_Name : in     String;
+      Language_Name    : in     String);
+   --  Tree is 'in out' because we use WisiToken.Syntax_Tree.LR_Utils lists.
+
+   procedure Create_Test_Main (Output_File_Name_Root : in String);
+
+end WisiToken.Generate.Tree_Sitter;



reply via email to

[Prev in Thread] Current Thread [Next in Thread]