summaryrefslogtreecommitdiff
path: root/tool/template.adb
blob: a28fff81a87136cbef1b6956b19bc1d847041ca2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156


--  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;