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 }