-- Programmed by Jedidiah Barber -- Released into the public domain -- This is a tool that reads in a file with an exact template -- and a file with sets of substitution rules with each rule -- of the form X=Y and with each set of rules separated by -- one or more blank lines. -- It then applies each set of rules to a copy of the template -- and puts the result to standard output. -- The specific use this tool was written for was to deal with -- very repetitive codegen such as binding static char get/set. -- But of course, it can be useful for other things too. with Ada.Characters.Latin_1, Ada.Command_Line, Ada.Containers.Indefinite_Ordered_Maps, Ada.Direct_IO, Ada.Directories, Ada.Strings.Maps, Ada.Strings.Unbounded, Ada.Text_IO; procedure Template is package Latin renames Ada.Characters.Latin_1; package ACom renames Ada.Command_Line; package ADir renames Ada.Directories; package SMap renames Ada.Strings.Maps; package SU renames Ada.Strings.Unbounded; package TIO renames Ada.Text_IO; package Char_IO is new Ada.Direct_IO (Character); My_Template : Char_IO.File_Type; My_Rule_List : TIO.File_Type; Element_In : Character; Template_Input, Copy_Input : SU.Unbounded_String; Sub_Line : SU.Unbounded_String; Cut_At : Natural; package String_Maps is new Ada.Containers.Indefinite_Ordered_Maps (Key_Type => String, Element_Type => String); Rule_Map : String_Maps.Map; procedure Process (Text : in out SU.Unbounded_String; Rules : in String_Maps.Map) is Token_At : Natural; begin for C in Rules.Iterate loop Token_At := 1; while Token_At <= SU.Length (Text) loop Token_At := SU.Index (Text, String_Maps.Key (C), Token_At); exit when Token_At = 0; SU.Replace_Slice (Text, Token_At, Token_At + String_Maps.Key (C)'Length - 1, String_Maps.Element (C)); Token_At := Token_At + String_Maps.Element (C)'Length; end loop; end loop; end Process; begin if ACom.Argument_Count /= 2 then TIO.Put_Line (TIO.Standard_Error, "ERROR: Need an input template file and a substitution list file"); ACom.Set_Exit_Status (ACom.Failure); return; end if; if not ADir.Exists (ACom.Argument (1)) then TIO.Put_Line (TIO.Standard_Error, "ERROR: Input template file does not exist"); ACom.Set_Exit_Status (ACom.Failure); return; end if; if not ADir.Exists (ACom.Argument (2)) then TIO.Put_Line (TIO.Standard_Error, "ERROR: Substitution list file does not exist"); ACom.Set_Exit_Status (ACom.Failure); return; end if; Char_IO.Open (My_Template, Char_IO.In_File, ACom.Argument (1)); while not Char_IO.End_Of_File (My_Template) loop Char_IO.Read (My_Template, Element_In); SU.Append (Template_Input, Element_In); end loop; Char_IO.Close (My_Template); TIO.Open (My_Rule_List, TIO.In_File, ACom.Argument (2)); while not TIO.End_Of_File (My_Rule_List) loop SU.Set_Unbounded_String (Sub_Line, TIO.Get_Line (My_Rule_List)); Cut_At := SU.Index (Sub_Line, SMap.To_Set ('=')); if Cut_At = 0 and SU.Length (Sub_Line) > 0 then TIO.Put_Line (TIO.Standard_Error, "WARNING: Malformed rule """ & SU.To_String (Sub_Line) & """ with no production"); end if; if Cut_At = 0 and not Rule_Map.Is_Empty then Copy_Input := Template_Input; Process (Copy_Input, Rule_Map); TIO.Put (SU.To_String (Copy_Input)); Rule_Map.Clear; elsif Cut_At = 1 then TIO.Put_Line (TIO.Standard_Error, "WARNING: Malformed rule """ & SU.To_String (Sub_Line) & """ with null pattern"); elsif Cut_At > 1 then Rule_Map.Include (SU.Slice (Sub_Line, 1, Cut_At - 1), SU.Slice (Sub_Line, Cut_At + 1, SU.Length (Sub_Line))); end if; end loop; if not Rule_Map.Is_Empty then Process (Template_Input, Rule_Map); TIO.Put (SU.To_String (Template_Input)); end if; TIO.Close (My_Rule_List); end Template;