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.production;
8 public import dparsergen.core.grammarinfo;
9 import std.array;
10 
11 struct TagID
12 {
13     ubyte id;
14 
15     int opCmp(TagID other) const pure nothrow
16     {
17         if (id < other.id)
18             return -1;
19         if (id > other.id)
20             return 1;
21         return 0;
22     }
23 }
24 
25 enum StdAnnotations = [
26     "empty",
27     "array",
28     "backtrack",
29     "deactivated",
30     "directUnwrap",
31     "flatten",
32     "ignoreInConflict",
33     "ignoreToken",
34     "inContextOnly",
35     "lookahead",
36     "lowPrio",
37     "regArray",
38     "start",
39     "string",
40     "regexLookahead",
41     "store",
42     "compareTrue",
43     "compareFalse",
44     "eager",
45     "eagerEnd",
46     "recursiveLexer",
47     "inheritAnyTag",
48     "minimalMatch"
49 ];
50 
51 mixin(() {
52     import std.string;
53 
54     string r = "enum AnnotationFlags {";
55     r ~= "NONE = 0, ";
56     foreach (i, a; StdAnnotations)
57         r ~= format("%s = %d, ", a, 1 << i);
58     r ~= format("ALL = %d", (1 >> StdAnnotations.length) - 1);
59     r ~= "}";
60     return r;
61 }());
62 
63 struct Annotations
64 {
65     AnnotationFlags stdAnnotations;
66     immutable(string)[] otherAnnotations;
67     this(immutable(string)[] annotations)
68     {
69         add(annotations);
70     }
71 
72     void add(string annotation)
73     {
74         import std.algorithm;
75 
76         mixin(() {
77             import std.string;
78 
79             string r = "";
80             foreach (i, a; StdAnnotations)
81                 r ~= format("%sif(annotation == \"%s\") stdAnnotations |= AnnotationFlags.%s;",
82                     (i) ? "else " : "", a, a);
83             r ~= "else if (!std.algorithm.canFind(otherAnnotations, annotation)) otherAnnotations ~= annotation;";
84             return r;
85         }());
86     }
87 
88     void add(immutable(string)[] annotations)
89     {
90         foreach (annotation; annotations)
91         {
92             add(annotation);
93         }
94     }
95 
96     void add(const Annotations annotations)
97     {
98         stdAnnotations |= annotations.stdAnnotations;
99         foreach (annotation; annotations.otherAnnotations)
100         {
101             add(annotation);
102         }
103     }
104 
105     bool canFind(string annotation) const pure nothrow
106     {
107         mixin(() {
108             import std.string;
109 
110             string r = "";
111             foreach (i, a; StdAnnotations)
112                 r ~= format("if (annotation == \"%s\") return (stdAnnotations & AnnotationFlags.%s) != 0;",
113                     a, a);
114             return r;
115         }());
116         import std.algorithm;
117 
118         return std.algorithm.canFind(otherAnnotations, annotation);
119     }
120 
121     bool contains(string annotation)() const pure nothrow
122     {
123         mixin(() {
124             import std.string;
125 
126             foreach (i, a; StdAnnotations)
127                 if (a == annotation)
128                     return format("return (stdAnnotations & AnnotationFlags.%s) != 0;", a);
129             string r;
130             r ~= "import std.algorithm;";
131             r ~= "return std.algorithm.canFind(otherAnnotations, annotation);";
132             return r;
133         }());
134     }
135 
136     string toString() const
137     {
138         import std.conv;
139         import std.string;
140 
141         string r = "[";
142         foreach (i, a; StdAnnotations)
143             if (stdAnnotations & (1 << i))
144                 r ~= text("\"", a, "\", ");
145         foreach (a; otherAnnotations)
146             r ~= text("\"", a, "\", ");
147         if (r.endsWith(", "))
148             r = r[0 .. $ - 2];
149         r ~= "]";
150         return r;
151     }
152 
153     string toStringCode(bool spaceAfter = false) const
154     {
155         Appender!string app;
156         toStringCode(app, spaceAfter);
157         return app.data;
158     }
159 
160     void toStringCode(ref Appender!string app, bool spaceAfter = false) const
161     {
162         foreach (i, a; StdAnnotations)
163             if (stdAnnotations & (1 << i))
164             {
165                 if (!spaceAfter)
166                     app.put(" ");
167                 app.put("@");
168                 app.put(a);
169                 if (spaceAfter)
170                     app.put(" ");
171             }
172         foreach (a; otherAnnotations)
173         {
174             if (!spaceAfter)
175                 app.put(" ");
176             app.put("@");
177             app.put(a);
178             if (spaceAfter)
179                 app.put(" ");
180         }
181     }
182 }
183 
184 struct TagUsage
185 {
186     TagID tag;
187     bool inherit;
188     bool needed;
189     bool reject;
190 
191     int opCmp(TagUsage other) const pure nothrow
192     {
193         if (tag.id < other.tag.id)
194             return -1;
195         if (tag.id > other.tag.id)
196             return 1;
197         return 0;
198     }
199 }
200 
201 struct Token
202 {
203     string name;
204     Annotations annotations;
205 }
206 
207 struct Nonterminal
208 {
209     string name;
210     NonterminalFlags flags;
211     Annotations annotations;
212     immutable(SymbolID)[] buildNonterminals;
213     immutable(TagID)[] possibleTags;
214 }
215 
216 struct Tag
217 {
218     string name;
219 }
220 
221 struct SymbolInstance
222 {
223     Symbol symbol;
224     alias symbol this;
225     string subToken;
226     string symbolInstanceName;
227     bool unwrapProduction;
228     bool dropNode;
229     Annotations annotations;
230     immutable(Symbol)[] negLookaheads;
231     immutable(TagUsage)[] tags;
232 }
233 
234 struct Production
235 {
236     NonterminalID nonterminalID = NonterminalID(SymbolID.max);
237     immutable(SymbolInstance)[] symbols;
238     ProductionID productionID = ProductionID.max;
239     Annotations annotations;
240     Symbol[] negLookaheads;
241     bool negLookaheadsAnytoken;
242     bool isVirtual;
243     RewriteRule[] rewriteRules;
244     immutable(TagID)[] tags;
245 
246     immutable(Production*) idup() const
247     {
248         immutable(RewriteRule)[] newRewriteRules;
249         foreach (rule; rewriteRules)
250             newRewriteRules ~= immutable(RewriteRule)(rule.applyProduction.idup,
251                     rule.parameters.idup, rule.startOf, rule.newPos);
252         return new immutable(Production)(nonterminalID, symbols, productionID, annotations,
253                 negLookaheads.idup, negLookaheadsAnytoken, isVirtual, newRewriteRules, tags);
254     }
255 
256     Production* dup() const
257     {
258         RewriteRule[] newRewriteRules;
259         foreach (rule; rewriteRules)
260             newRewriteRules ~= RewriteRule(rule.applyProduction,
261                     rule.parameters.dup, rule.startOf, rule.newPos);
262         return new Production(nonterminalID, symbols, productionID, annotations,
263                 negLookaheads.dup, negLookaheadsAnytoken, isVirtual, newRewriteRules, tags);
264     }
265 }
266 
267 struct RewriteRule
268 {
269     const Production* applyProduction;
270     size_t[] parameters;
271     size_t startOf = size_t.max; // copy start Location from here
272     size_t newPos;
273 }