-- Abstract : -- -- Parser for Wisi grammar files, producing Ada source -- files for a parser. -- -- Copyright (C) 2012 - 2015, 2017 - 2023 Free Software Foundation, Inc. -- -- The WisiToken package 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 MERCHANTABILITY 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.Calendar; with Ada.Command_Line; with Ada.Directories; with Ada.Exceptions; with Ada.Real_Time; with Ada.Strings.Fixed; with Ada.Strings.Maps; with Ada.Strings.Unbounded; with Ada.Text_IO; with GNAT.Traceback.Symbolic; with WisiToken.BNF.Generate_Utils; with WisiToken.BNF.Output_Ada; with WisiToken.BNF.Output_Ada_Common; with WisiToken.BNF.Output_Ada_Emacs; with WisiToken.BNF.Output_Elisp_Common; with WisiToken.Generate.LR.LALR_Generate; with WisiToken.Generate.LR.LR1_Generate; with WisiToken.Generate.Packrat; with WisiToken.Parse.LR.Parser_No_Recover; -- for reading BNF file with WisiToken.Productions; with WisiToken.Syntax_Trees; with WisiToken.Text_IO_Trace; with WisiToken.Generate.Tree_Sitter; with WisiToken_Grammar_Editing; with WisiToken_Grammar_Runtime; with Wisitoken_Grammar_Main; procedure WisiToken.BNF.Generate is use all type SAL.Base_Peek_Type; procedure Put_Usage is use Ada.Text_IO; First : Boolean := True; begin Put_Line (Standard_Error, "version 4.2.1"); -- matches release version in Docs/wisitoken.html Put_Line (Standard_Error, "wisitoken-bnf-generate [options] {wisi grammar file}"); Put_Line (Standard_Error, "Generate source code implementing a parser for the grammar."); New_Line (Standard_Error); Put_Line (Standard_Error, "The following grammar file directives control parser generation:"); Put_Line (Standard_Error, "%generate [] [] [text_rep]"); Put_Line (Standard_Error, " specify one of each generate parameter. May be repeated."); Put (Standard_Error, " algorithm: "); for I of Generate_Algorithm_Image loop if First then First := False; else Put (Standard_Error, " | "); end if; Put (Standard_Error, I.all); end loop; New_Line (Standard_Error); Put (Standard_Error, " output language: "); First := True; for I of Output_Language_Image loop if First then First := False; else Put (Standard_Error, " | "); end if; Put (Standard_Error, I.all); end loop; New_Line (Standard_Error); Put_Line (Standard_Error, " interface: interface Process | Module"); Put_Line (Standard_Error, " only valid with Ada_Emacs:"); Put_Line (Standard_Error, " Process is for an external subprocess communicating with Emacs."); Put_Line (Standard_Error, " Module is for a dynamically loaded Emacs module."); Put (Standard_Error, " lexer: "); First := True; for I of Output_Language_Image loop if First then First := False; else Put (Standard_Error, " | "); end if; Put (Standard_Error, I.all); end loop; New_Line (Standard_Error); Put_Line (Standard_Error, " text_rep: output LR parse table in a text file, not as source code; for large tables"); New_Line (Standard_Error); Put_Line (Standard_Error, "options:"); Put_Line (Standard_Error, " --help: show this help"); -- verbosity meaning is actually determined by output choice; -- they should be consistent with this description. Put_Line (Standard_Error, " --verbosity sets verbosity levels (default 0):"); Put_Line (Standard_Error, " 0 - only error messages to standard error"); Put_Line (Standard_Error, " 1 - add diagnostics to standard out"); Put_Line (Standard_Error, " 2 - more diagnostics to standard out, ignore unused tokens, unknown conflicts"); Put_Line (Standard_Error, " --generate ...: override grammar file %generate directive"); Put_Line (Standard_Error, " --output_bnf : output translated EBNF source to _bnf.wy"); Put_Line (Standard_Error, " --ignore_conflicts; ignore excess/unknown conflicts"); Put_Line (Standard_Error, " --test_main; generate standalone main program for running the generated parser, modify file names"); Put_Line (Standard_Error, "verbosity keys:"); Enable_Trace_Help; end Put_Usage; Language_Name : Ada.Strings.Unbounded.Unbounded_String; -- The language the grammar defines Grammar_File_Name_Base : Ada.Strings.Unbounded.Unbounded_String; Need_Gen_Alg_In_Actions_Name : Boolean := False; Output_BNF : Boolean := False; Ignore_Conflicts : Boolean := False; Test_Main : Boolean := False; Command_Generate_Set : Generate_Set_Access; -- override grammar file declarations Trace : aliased WisiToken.Text_IO_Trace.Trace; Input_Data : aliased WisiToken_Grammar_Runtime.User_Data_Type; Grammar_Parser : WisiToken.Parse.LR.Parser_No_Recover.Parser := Wisitoken_Grammar_Main.Create_Parser (Trace'Unchecked_Access, Input_Data'Unchecked_Access); Log_File : Ada.Text_IO.File_Type; -- not used procedure Use_Input_File (File_Name : in String) is use Ada.Text_IO; begin Grammar_File_Name_Base := +Ada.Directories.Base_Name (File_Name); Grammar_Parser.Tree.Lexer.Reset_With_File (File_Name); declare Language_Name_Dir : constant Integer := Ada.Strings.Fixed.Index (File_Name, Ada.Strings.Maps.To_Set ("/\"), Going => Ada.Strings.Backward); Language_Name_Ext : constant Integer := Ada.Strings.Fixed.Index (File_Name, ".wy"); begin Language_Name := +WisiToken.BNF.Output_Elisp_Common.Elisp_Name_To_Ada (File_Name ((if Language_Name_Dir = 0 then File_Name'First else Language_Name_Dir + 1) .. Language_Name_Ext - 1), Append_ID => False, Trim => 0); end; exception when Name_Error | Use_Error => raise Name_Error with "input file '" & File_Name & "' could not be opened."; end Use_Input_File; begin declare use Ada.Command_Line; Arg_Next : Integer := 1; begin loop exit when Argument (Arg_Next)(1) /= '-'; -- --help, --verbosity first, then alphabetical if Argument (Arg_Next) = "--help" then Put_Usage; return; elsif Argument (Arg_Next) = "--verbosity" then Arg_Next := Arg_Next + 1; WisiToken.Enable_Trace (Argument (Arg_Next)); Arg_Next := Arg_Next + 1; elsif Argument (Arg_Next) = "--ignore_conflicts" then Ignore_Conflicts := True; Arg_Next := Arg_Next + 1; elsif Argument (Arg_Next) = "--generate" then Arg_Next := Arg_Next + 1; declare Tuple : Generate_Tuple; Done : Boolean := False; begin begin Tuple.Gen_Alg := Generate_Algorithm'Value (Argument (Arg_Next)); Arg_Next := Arg_Next + 1; exception when Constraint_Error => raise User_Error with "invalid value for generator_algorithm: '" & Argument (Arg_Next) & ";"; end; if Tuple.Gen_Alg /= None then begin Tuple.Out_Lang := To_Output_Language (Argument (Arg_Next)); Arg_Next := Arg_Next + 1; end; loop exit when Done; declare Text : constant String := Argument (Arg_Next); begin if Text = "text_rep" then Tuple.Text_Rep := True; Arg_Next := Arg_Next + 1; elsif (for some I of Lexer_Image => To_Lower (Text) = I.all) then Tuple.Lexer := To_Lexer (Text); Arg_Next := Arg_Next + 1; elsif (for some I of Interface_Image => To_Lower (Text) = I.all) then Tuple.Interface_Kind := WisiToken.BNF.Valid_Interface'Value (Text); Arg_Next := Arg_Next + 1; else Done := True; end if; end; end loop; end if; Add (Command_Generate_Set, Tuple); end; elsif Argument (Arg_Next) = "--output_bnf" then Output_BNF := True; Arg_Next := Arg_Next + 1; elsif Argument (Arg_Next) = "--test_main" then Arg_Next := Arg_Next + 1; Test_Main := True; else raise User_Error with "invalid argument '" & Argument (Arg_Next) & "'"; end if; end loop; Use_Input_File (Argument (Arg_Next)); if Arg_Next /= Argument_Count then raise User_Error with "arg count" & Integer'Image (Argument_Count) & " different from expected count" & Integer'Image (Arg_Next); end if; end; if Trace_Generate > Outline then Trace.Put_Line ("parse grammar file"); end if; begin Grammar_Parser.Parse (Log_File); exception when WisiToken.Syntax_Error => if Grammar_Parser.Tree.Parents_Set then Grammar_Parser.Put_Errors; elsif Grammar_Parser.Tree.Stream_Count >= 2 then Grammar_Parser.Put_Errors (Grammar_Parser.Tree.Last_Parse_Stream); else Grammar_Parser.Put_Errors (Grammar_Parser.Tree.Shared_Stream); end if; raise; when E : WisiToken.Parse_Error => WisiToken.Generate.Put_Error (Ada.Exceptions.Exception_Message (E)); raise; end; declare use all type Ada.Strings.Unbounded.Unbounded_String; use Ada.Text_IO; use all type WisiToken_Grammar_Runtime.Meta_Syntax; Generate_Set : Generate_Set_Access; Multiple_Tuples : Boolean; Lexer_Done : Lexer_Set := (others => False); BNF_Tree : Syntax_Trees.Tree; -- Only filled when source is EBNF, alg is LR -- In general, all of the data in Generate_Utils.Generate_Data -- depends on the generate tuple parameters. However, if -- 'If_Lexer_Present' is false, then they don't depend on the lexer, -- and if 'If_Parser_Present' is false, then they don't depend on -- Gen_Alg, except for the parser table. But it's not worth trying to -- cache results in those cases; they only happen in test grammars, -- which are small. procedure Translate_To_BNF is begin if Trace_Generate > Outline or Trace_Generate_EBNF > Outline then Ada.Text_IO.Put_Line ("Translate EBNF tree to BNF"); end if; declare Time_Start : constant Ada.Calendar.Time := Ada.Calendar.Clock; begin Syntax_Trees.Copy_Tree (Source => Grammar_Parser.Tree, Destination => BNF_Tree, User_Data => Input_Data'Unchecked_Access); WisiToken_Grammar_Editing.Translate_EBNF_To_BNF (BNF_Tree, Input_Data); if Trace_Generate_EBNF > Detail then Ada.Text_IO.New_Line; Ada.Text_IO.Put_Line ("BNF tree:"); BNF_Tree.Print_Tree (Augmented => True); end if; if Output_BNF then WisiToken_Grammar_Editing.Print_Source (-Grammar_File_Name_Base & "_bnf.wy", BNF_Tree, Input_Data); end if; if Trace_Time then Ada.Text_IO.Put_Line ("translate to bnf time:" & Duration'Image (Ada.Calendar."-" (Ada.Calendar.Clock, Time_Start))); end if; if WisiToken.Generate.Error then raise WisiToken.Grammar_Error with "errors during translating EBNF to BNF: aborting"; end if; end; end Translate_To_BNF; procedure Parse_Check (Lexer : in Lexer_Type; Parser : in Generate_Algorithm; Phase : in WisiToken_Grammar_Runtime.Action_Phase) -- Ensure that the proper grammar file post-parse processing has been done. is use all type Ada.Containers.Count_Type; use all type WisiToken_Grammar_Runtime.Action_Phase; begin -- Specifying the parser and lexer can change the parsed grammar, due -- to %if {parser | lexer}. Input_Data.Reset (Lexer, Parser, Phase); case Phase is when Meta => if Trace_Generate > Outline then Trace.Put_Line ("post-parse main grammar tree META"); end if; Grammar_Parser.Execute_Actions; case Input_Data.Meta_Syntax is when Unknown => Input_Data.Meta_Syntax := BNF_Syntax; when BNF_Syntax | EBNF_Syntax => null; end case; when Other => if Input_Data.Meta_Syntax = EBNF_Syntax and BNF_Tree.Is_Empty then Translate_To_BNF; end if; if BNF_Tree.Is_Empty then if Trace_Generate > Outline then Trace.Put_Line ("post-parse main grammar tree OTHER"); end if; Grammar_Parser.Execute_Actions; else if Trace_Generate > Outline then Trace.Put_Line ("post-parse bnf grammar tree OTHER"); end if; WisiToken.Parse.Execute_Actions (BNF_Tree, Grammar_Parser.Productions, Input_Data'Unchecked_Access); end if; if Input_Data.Rule_Count = 0 or Input_Data.Tokens.Rules.Length = 0 then raise WisiToken.Grammar_Error with "no rules"; end if; end case; exception when E : WisiToken.Syntax_Error | WisiToken.Parse_Error => Ada.Text_IO.Put_Line (Ada.Text_IO.Standard_Error, Ada.Exceptions.Exception_Message (E)); Grammar_Parser.Put_Errors; raise; end Parse_Check; Cached_Recursions : WisiToken.Generate.Recursions := WisiToken.Generate.Empty_Recursions; begin -- Get the generate tuples Parse_Check (None, None, WisiToken_Grammar_Runtime.Meta); Need_Gen_Alg_In_Actions_Name := Input_Data.If_Lexer_Present; if Command_Generate_Set = null then if Input_Data.Generate_Set = null then raise User_Error with WisiToken.Generate.Error_Message (Grammar_Parser.Tree.Lexer.File_Name, 1, "generate algorithm, output_language, lexer, interface not specified"); end if; Generate_Set := Input_Data.Generate_Set; else Generate_Set := Command_Generate_Set; end if; Multiple_Tuples := -- Preserve output file names when overriding file %generate (if Input_Data.Generate_Set = null then False else Input_Data.Generate_Set'Length > 1) or (if Command_Generate_Set = null then False else Command_Generate_Set'Length > 1); for Tuple of Generate_Set.all loop declare -- We could use a cached Generate_Data if not -- (Input_Data.If_Lexer_Present or Input_Data.If_Parser_Present), but -- that would not save much time, and would complicate this logic. We -- do cache Recursions. Generate_Data : WisiToken.BNF.Generate_Utils.Generate_Data_Access; Packrat_Data : WisiToken.Generate.Packrat.Data_Access; begin if Trace_Generate > Outline then Trace.New_Line; Trace.Put_Line ("process tuple " & Image (Tuple)); end if; case Tuple.Gen_Alg is when None | External => if Input_Data.Meta_Syntax = EBNF_Syntax and BNF_Tree.Is_Empty then -- 'none' is used in unit tests to test bnf translation. Translate_To_BNF; end if; when Tree_Sitter => Parse_Check (Lexer => Tuple.Lexer, Parser => Tuple.Gen_Alg, Phase => WisiToken_Grammar_Runtime.Other); -- Generate_Data.Grammar is built from BNF_Tree; if we are generating -- actions, the check in Eliminate_Empty_Productions ensures that is -- the same as TS_Tree. Generate_Data := new WisiToken.BNF.Generate_Utils.Generate_Data' (WisiToken.BNF.Generate_Utils.Initialize (Input_Data'Unchecked_Access, Grammar_Parser.Tree.Lexer.File_Name, Ignore_Conflicts)); declare use WisiToken.Generate.Tree_Sitter; TS_Tree : Syntax_Trees.Tree; begin Syntax_Trees.Copy_Tree (Source => Grammar_Parser.Tree, Destination => TS_Tree, User_Data => Input_Data'Unchecked_Access); if Trace_Generate > Outline then Trace.Put_Line ("post-parse tree_sitter grammar tree OTHER"); end if; -- FIXME: something is screwing up augmented; if we don't clear it -- here, finalizing the other two trees crashes on a double free of -- some augmented. TS_Tree.Clear_Augmented; Eliminate_Empty_Productions (Input_Data, TS_Tree, No_Empty => Tuple.Out_Lang /= None); Print_Tree_Sitter (Input_Data, TS_Tree, TS_Tree.Lexer, Output_File_Name => -Grammar_File_Name_Base & ".js", Language_Name => -Language_Name); if WisiToken.Generate.Error then -- FIXME: support --warning=error raise WisiToken.Grammar_Error with "errors during translating grammar to tree-sitter: aborting"; end if; end; when LR_Packrat_Generate_Algorithm => Parse_Check (Lexer => Tuple.Lexer, Parser => Tuple.Gen_Alg, Phase => WisiToken_Grammar_Runtime.Other); Generate_Data := new WisiToken.BNF.Generate_Utils.Generate_Data' (WisiToken.BNF.Generate_Utils.Initialize (Input_Data'Unchecked_Access, Grammar_Parser.Tree.Lexer.File_Name, Ignore_Conflicts)); declare use all type WisiToken.Parse.LR.Parse_Table_Ptr; use Ada.Real_Time; Parse_Table_File_Name : constant String := -Grammar_File_Name_Base & "_" & To_Lower (Tuple.Gen_Alg'Image) & (if Input_Data.If_Lexer_Present then "_" & Lexer_Image (Input_Data.User_Lexer).all else "") & ".parse_table"; Time_Start : Time; Time_End : Time; procedure Parse_Table_Append_Stats is Parse_Table_File : File_Type; begin Open (Parse_Table_File, Append_File, Parse_Table_File_Name); Set_Output (Parse_Table_File); Generate_Data.Parser_State_Count := Generate_Data.LR_Parse_Table.State_Last - Generate_Data.LR_Parse_Table.State_First + 1; WisiToken.BNF.Generate_Utils.Put_Stats (Input_Data, Generate_Data.all); Set_Output (Standard_Output); Close (Parse_Table_File); end Parse_Table_Append_Stats; function To_McKenzie return WisiToken.Parse.LR.McKenzie_Param_Type is begin return Generate_Utils.To_McKenzie_Param (Generate_Data.all, Input_Data.McKenzie_Recover); exception when E : Not_Found => WisiToken.Generate.Put_Error (WisiToken.Generate.Error_Message (Grammar_Parser.Tree.Lexer.File_Name, 1, "In some %mckenzie_*; " & Ada.Exceptions.Exception_Message (E))); return WisiToken.Parse.LR.Default_McKenzie_Param; end To_McKenzie; begin if not Lexer_Done (Input_Data.User_Lexer) then Lexer_Done (Input_Data.User_Lexer) := True; case Input_Data.User_Lexer is when re2c_Lexer => WisiToken.BNF.Output_Ada_Common.Create_re2c_File (Input_Data, Tuple, Generate_Data.all, -Grammar_File_Name_Base); when others => null; end case; end if; case LR_Packrat_Generate_Algorithm'(Tuple.Gen_Alg) is when LALR => Time_Start := Clock; if Generate_Data.Grammar (Generate_Data.Descriptor.Accept_ID).LHS = Invalid_Token_ID then WisiToken.Generate.Put_Error (WisiToken.Generate.Error_Message (Grammar_Parser.Tree.Lexer.File_Name, 1, "%start token not specified or not found; no LALR parse table generated")); else if Trace_Generate > Outline then Trace.Put_Line ("generate LALR parse table"); if Trace_Generate > Detail or Trace_Generate_Conflicts > Detail then Trace.New_Line; Trace.Put_Line ("Tokens:"); WisiToken.Put_Tokens (Generate_Data.Descriptor.all); Trace.Put_Line ("Precedences:"); WisiToken.Put (Input_Data.Precedence_Lists, Input_Data.Precedence_Map); Trace.Put_Line ("grammar:"); WisiToken.Productions.Put (Generate_Data.Grammar, Generate_Data.Descriptor.all); Trace.New_Line; if Trace_Generate > Extra or Trace_Generate_Conflicts > Extra then Trace.Put_Line ("tree:"); Grammar_Parser.Tree.Print_Tree; Trace.New_Line; end if; end if; end if; Generate_Data.LR_Parse_Table := WisiToken.Generate.LR.LALR_Generate.Generate (Generate_Data.Grammar, Input_Data.Precedence_Lists, Generate_Data.Descriptor.all, Grammar_Parser.Tree.Lexer.File_Name, Input_Data.Language_Params.Error_Recover, Generate_Data.Conflicts, To_McKenzie, Input_Data.Max_Parallel, Parse_Table_File_Name, Include_Extra => Test_Main, Ignore_Conflicts => Ignore_Conflicts, Recursion_Strategy => Input_Data.Language_Params.Recursion_Strategy, Use_Cached_Recursions => not (Input_Data.If_Lexer_Present or Input_Data.If_Parser_Present), Recursions => Cached_Recursions); if WisiToken.Trace_Time then Time_End := Clock; Put_Line (Standard_Error, "LALR " & Lexer_Image (Tuple.Lexer).all & " generate time:" & Duration'Image (To_Duration (Time_End - Time_Start))); end if; Parse_Table_Append_Stats; end if; when LR1 => Time_Start := Clock; if Generate_Data.Grammar (Generate_Data.Descriptor.Accept_ID).LHS = Invalid_Token_ID then WisiToken.Generate.Put_Error (WisiToken.Generate.Error_Message (Grammar_Parser.Tree.Lexer.File_Name, 1, "%start token not specified or not found; no LALR parse table generated")); else if Trace_Generate > Outline then Trace.Put_Line ("generate LR1 parse table"); end if; Generate_Data.LR_Parse_Table := WisiToken.Generate.LR.LR1_Generate.Generate (Generate_Data.Grammar, Input_Data.Precedence_Lists, Generate_Data.Descriptor.all, Grammar_Parser.Tree.Lexer.File_Name, Input_Data.Language_Params.Error_Recover, Generate_Data.Conflicts, To_McKenzie, Input_Data.Max_Parallel, Parse_Table_File_Name, Include_Extra => Test_Main, Ignore_Conflicts => Ignore_Conflicts, Recursion_Strategy => Input_Data.Language_Params.Recursion_Strategy, Use_Cached_Recursions => not (Input_Data.If_Lexer_Present or Input_Data.If_Parser_Present), Recursions => Cached_Recursions); if Trace_Time then Time_End := Clock; Put_Line (Standard_Error, "LR1 " & Lexer_Image (Tuple.Lexer).all & " generate time:" & Duration'Image (To_Duration (Time_End - Time_Start))); end if; Parse_Table_Append_Stats; end if; when Packrat_Generate_Algorithm => -- The only significant computation done for Packrat is First, done -- in Initialize; not worth timing. Packrat_Data := new WisiToken.Generate.Packrat.Data' (WisiToken.Generate.Packrat.Initialize (Grammar_Parser.Tree.Lexer.File_Name, Generate_Data.Grammar, Generate_Data.Source_Line_Map, Generate_Data.Descriptor.First_Terminal)); declare -- There is no parse_table for packrat, but it's useful to have a -- definitive list of tokens and productions (after all %if are -- processed). Parse_Table_File : File_Type; begin Create (Parse_Table_File, Out_File, Parse_Table_File_Name); Set_Output (Parse_Table_File); Put_Line ("Tokens:"); WisiToken.Put_Tokens (Generate_Data.Descriptor.all); New_Line; Put_Line ("Productions:"); WisiToken.Productions.Put (Generate_Data.Grammar, Generate_Data.Descriptor.all); Set_Output (Standard_Output); Close (Parse_Table_File); end; Packrat_Data.Check_All (Generate_Data.Descriptor.all, Input_Data.Suppress); if WisiToken.Generate.Warning then WisiToken.Generate.Put_Warning ("warnings during packrat generation"); end if; end case; if WisiToken.Generate.Error then raise WisiToken.Grammar_Error with "errors: aborting"; end if; case Tuple.Gen_Alg is when LR_Generate_Algorithm => pragma Assert (Generate_Data.LR_Parse_Table /= null and then Generate_Data.LR_Parse_Table.Error_Recover_Enabled = Input_Data.Language_Params.Error_Recover); if Tuple.Text_Rep then WisiToken.Generate.LR.Put_Text_Rep (Generate_Data.LR_Parse_Table.all, Text_Rep_File_Name (-Grammar_File_Name_Base, Tuple, Input_Data.If_Lexer_Present)); end if; when others => null; end case; end; end case; case Tuple.Out_Lang is when None => null; when Ada_Lang => if Trace_Generate > Outline then Trace.Put_Line ("output Ada"); end if; WisiToken.BNF.Output_Ada (Input_Data, Grammar_Parser.Tree.Lexer.File_Name, -Grammar_File_Name_Base, Generate_Data.all, Packrat_Data, Tuple, Test_Main, Multiple_Tuples, Need_Gen_Alg_In_Actions_Name); when Ada_Emacs_Lang => if Trace_Generate > Outline then Trace.Put_Line ("output Ada for Emacs"); end if; WisiToken.BNF.Output_Ada_Emacs (Input_Data, Grammar_Parser.Tree.Lexer.File_Name, -Grammar_File_Name_Base, Generate_Data.all, Packrat_Data, Tuple, Test_Main, Multiple_Tuples, Need_Gen_Alg_In_Actions_Name, -Language_Name); end case; if WisiToken.Generate.Error then raise WisiToken.Grammar_Error with "errors: aborting"; end if; end; end loop; end; exception when WisiToken.Syntax_Error | WisiToken.Parse_Error => -- error message already output Ada.Command_Line.Set_Exit_Status (Ada.Command_Line.Failure); when E : User_Error => declare use Ada.Command_Line; use Ada.Exceptions; use Ada.Text_IO; begin Put_Line (Standard_Error, Exception_Message (E)); Put_Command_Line (Ada_Comment); Set_Exit_Status (Failure); Put_Usage; end; when E : WisiToken.Grammar_Error => -- error message not already output declare use Ada.Command_Line; use Ada.Exceptions; use Ada.Text_IO; begin Put_Line (Standard_Error, Exception_Message (E)); Set_Exit_Status (Failure); end; when E : others => -- IMPROVEME: for some exceptions, Error message already output via wisi.utils.Put_Error declare use Ada.Text_IO; use Ada.Exceptions; use Ada.Command_Line; begin Put_Line (Standard_Error, Exception_Name (E) & ": " & Exception_Message (E)); Put_Line (Standard_Error, GNAT.Traceback.Symbolic.Symbolic_Traceback (E)); Set_Exit_Status (Failure); end; end WisiToken.BNF.Generate;