1 
2 //          Copyright Tim Schendekehl 2023.
3 // Distributed under the Boost Software License, Version 1.0.
4 //    (See accompanying file LICENSE_1_0.txt or copy at
5 //          https://www.boost.org/LICENSE_1_0.txt)
6 
7 module dparsergen.generator.ebnf;
8 import dparsergen.core.dynamictree;
9 import dparsergen.core.location;
10 import dparsergen.core.nodetype;
11 import dparsergen.core.utils;
12 import std.algorithm;
13 import std.conv;
14 import std.exception;
15 import std.string;
16 
17 alias Tree = DynamicParseTree!(LocationAll, LocationRangeStartEnd);
18 alias TreeToken = DynamicParseTreeToken!(LocationAll, LocationRangeStartEnd);
19 alias TreeNonterminal = DynamicParseTreeNonterminal!(LocationAll, LocationRangeStartEnd);
20 alias TreeArray = DynamicParseTreeArray!(LocationAll, LocationRangeStartEnd);
21 
22 string concatTree(Tree)(Tree tree)
23 {
24     string r;
25     if (tree is null)
26         return "";
27     if (tree.nodeType == NodeType.token)
28         r = " " ~ tree.content;
29     else
30         foreach (c; tree.childs)
31             r ~= concatTree(c);
32     return r;
33 }
34 
35 string ebnfTreeToString(const Tree tree, const Tree parent = null)
36 {
37     string r;
38     if (tree.name == "Concatenation")
39     {
40         r = "{";
41         bool needsSpace;
42         foreach (c; tree.childs[0 .. $ - 1])
43         {
44             if (c.nodeType == NodeType.array)
45             {
46                 foreach (c2; c.childs)
47                 {
48                     if (needsSpace)
49                         r ~= " ";
50                     r ~= ebnfTreeToString(c2);
51                     needsSpace = true;
52                 }
53             }
54             else
55             {
56                 if (needsSpace)
57                     r ~= " ";
58                 r ~= ebnfTreeToString(c);
59                 needsSpace = true;
60             }
61         }
62         if (tree.childs[$ - 1].memberOrDefault!"childs".length)
63         {
64             r ~= " " ~ ebnfTreeToString(tree.childs[$ - 1], tree);
65             if (r.endsWith(" "))
66                 r = r[0 .. $ - 1];
67         }
68         r ~= "}";
69     }
70     else if (tree.name == "Alternation")
71     {
72         r = "{";
73         void addAlternation(const Tree tree)
74         {
75             if (tree.name == "Alternation")
76             {
77                 addAlternation(tree.childs[0]);
78                 r ~= " | ";
79                 r ~= ebnfTreeToString(tree.childs[2]);
80             }
81             else
82             {
83                 r ~= ebnfTreeToString(tree);
84             }
85         }
86 
87         addAlternation(tree);
88         r ~= "}";
89     }
90     else if (tree.name == "ParenExpression")
91     {
92         r = ebnfTreeToString(tree.childs[1]);
93     }
94     else if (tree.name == "MacroInstance")
95     {
96         const(Tree)[] childs = tree.childs[2].memberOrDefault!"childs";
97         r = text(tree.childs[0].content, "(", childs.filter!(c => !c.isToken)
98                 .map!(c => ebnfTreeToString(c))
99                 .join(", "), ")");
100     }
101     else if (tree.name == "Tuple")
102     {
103         const(Tree)[] childs = tree.childs[1].memberOrDefault!"childs";
104         r = text("t(", childs.filter!(c => !c.isToken)
105                 .map!(c => ebnfTreeToString(c))
106                 .join(", "), ")");
107     }
108     else if (tree.name == "SubToken")
109     {
110         r = text("{", tree.childs
111                 .filter!(c => !c.isToken)
112                 .map!(c => ebnfTreeToString(c))
113                 .join(" >> "), "}");
114     }
115     else if (tree.name == "TokenMinus")
116     {
117         r = text("{", tree.childs
118                 .filter!(c => !c.isToken)
119                 .map!(c => ebnfTreeToString(c))
120                 .join(" - "), "}");
121     }
122     else if (tree.name == "Annotation")
123     {
124         r = text("@", tree.childs[1].content);
125         if (tree.childs[2]!is null)
126         {
127             r ~= "(" ~ concatTree(tree.childs[2].childs[1]).strip() ~ ")";
128         }
129         r ~= " ";
130     }
131     else if (tree.name == "NegativeLookahead")
132     {
133         if (tree.childs[1].isToken)
134             r = text("!", tree.childs[1].content);
135         else
136             r = text("!", ebnfTreeToString(tree.childs[1]));
137         r ~= " ";
138     }
139     else
140     {
141         foreach (c; tree.childs)
142         {
143             if (c is null)
144             {
145             }
146             else if (c.nodeType == NodeType.token)
147             {
148                 r ~= c.content;
149                 if (c.content == ",")
150                     r ~= " ";
151             }
152             else
153             {
154                 r ~= ebnfTreeToString(c);
155             }
156         }
157     }
158     return r;
159 }
160 
161 enum DeclarationType
162 {
163     auto_,
164     token,
165     fragment
166 }
167 
168 struct Declaration
169 {
170     DeclarationType type;
171     string name;
172     Tree exprTree;
173     string[] parameters;
174     immutable(string)[] annotations;
175     size_t variadicParameterIndex = size_t.max;
176     string documentation;
177 }
178 
179 struct EBNF
180 {
181     Declaration[] symbols;
182     string[2][] matchingTokens;
183     string[] imports;
184     size_t startTokenID;
185     size_t startNonterminalID;
186     size_t startProductionID;
187     string globalDocumentation;
188 }
189 
190 Tree replaceNames(Tree[string] table, Tree tree)
191 {
192     if (tree is null)
193         return tree;
194     if (tree.nodeType == NodeType.token)
195         return tree;
196 
197     if (tree.name == "Name" && tree.childs[0].content in table)
198     {
199         return table[tree.childs[0].content];
200     }
201     else if (tree.name.among("MacroInstance", "Tuple"))
202     {
203         Tree[] childs = tree.childs.dup;
204         foreach (ref c; childs)
205         {
206             c = replaceNames(table, c);
207         }
208         Tree[] parameters = childs[$ - 2].memberOrDefault!"childs";
209         Tree[] newParameters;
210         foreach (p; parameters)
211         {
212             Tree realParameter = p;
213             Tree annotatedExpression;
214             if (p.nodeType == NodeType.nonterminal && p.name == "AnnotatedExpression")
215             {
216                 annotatedExpression = p;
217                 realParameter = annotatedExpression.childs[$ - 1];
218             }
219 
220             if (realParameter.nodeType == NodeType.nonterminal
221                     && realParameter.name == "UnpackVariadicList"
222                     && realParameter.childs[0].content in table)
223             {
224                 Tree replacement = table[realParameter.childs[0].content];
225                 while (replacement.nodeType == NodeType.nonterminal
226                         && replacement.name == "AnnotatedExpression")
227                     replacement = replacement.childs[$ - 1];
228                 enforce(replacement.name == "Tuple",
229                         text("Parameter is not tuple, but ", replacement));
230                 foreach (c; replacement.childs[$ - 2].memberOrDefault!"childs")
231                 {
232                     if (c.isToken)
233                         continue;
234                     while (c.nodeType == NodeType.nonterminal && c.name == "AnnotatedExpression")
235                         c = c.childs[$ - 1];
236                     if (c is null)
237                         continue;
238                     if (annotatedExpression)
239                     {
240                         newParameters ~= new TreeNonterminal(annotatedExpression.nonterminalID,
241                                 annotatedExpression.productionID,
242                                 annotatedExpression.childs.dup, annotatedExpression.grammarInfo);
243                         newParameters[$ - 1].childs[$ - 1] = c;
244                     }
245                     else
246                         newParameters ~= c;
247                 }
248             }
249             else
250                 newParameters ~= p;
251         }
252         childs[$ - 2] = new TreeArray(newParameters, null);
253         auto r = new TreeNonterminal(tree.nonterminalID, tree.productionID,
254                 childs, tree.grammarInfo);
255         return r;
256     }
257     else
258     {
259         Tree[] childs = tree.childs.dup;
260         foreach (ref c; childs)
261         {
262             c = replaceNames(table, c);
263         }
264         if (tree.nodeType == NodeType.nonterminal)
265             return new TreeNonterminal(tree.nonterminalID, tree.productionID,
266                     childs, tree.grammarInfo);
267         else if (tree.nodeType == NodeType.array)
268             return new TreeArray(childs, tree.grammarInfo);
269         else
270             assert(false);
271     }
272 }