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.core.parseexception; 8 import std.algorithm; 9 import std.conv; 10 11 /** 12 Flags for toString of ParseException. 13 */ 14 enum ExceptionStringFlags 15 { 16 none = 0, 17 noBacktrace = 1, 18 noLocation = 2, 19 } 20 21 /** 22 Base class for exceptions of parser and lexer. 23 */ 24 class ParseException : Exception 25 { 26 /** 27 Construct exception. 28 */ 29 this(string msg, string file = __FILE__, size_t line = __LINE__) 30 { 31 super(msg, file, line); 32 } 33 34 /** 35 Generate string for this exception. 36 */ 37 alias toString = Exception.toString; 38 39 /// ditto 40 void toString(string inputString, scope void delegate(in char[]) sink, 41 ExceptionStringFlags flags = ExceptionStringFlags.none) const 42 { 43 if (flags & ExceptionStringFlags.noBacktrace) 44 sink(text("Parse Error: ", msg, "\n")); 45 else 46 toString(sink); 47 } 48 49 /** 50 Get message for this exception. 51 */ 52 string simpleMsg() const 53 { 54 return msg; 55 } 56 57 /** 58 Get exception with maximum end location for exceptions representing 59 multiple exceptions. 60 */ 61 const(ParseException) maxEndException() const 62 { 63 return this; 64 } 65 66 bool allowBacktrack() const 67 { 68 return msg.startsWith("unexpected") || msg.startsWith("EOF"); 69 } 70 71 bool laterEnd(const ParseException other) const 72 { 73 return false; 74 } 75 } 76 77 /** 78 Exception type used by parser and lexer. 79 */ 80 class SingleParseException(Location) : ParseException 81 { 82 /** 83 Start location for error. 84 */ 85 Location markStart; 86 87 /** 88 End location for error. 89 */ 90 Location markEnd; 91 92 /** 93 Construct exception. 94 */ 95 this(string msg, Location markStart, Location markEnd, 96 string file = __FILE__, size_t line = __LINE__) 97 { 98 this.markStart = markStart; 99 this.markEnd = markEnd; 100 assert(markStart <= markEnd); 101 super(msg, file, line); 102 } 103 104 override void toString(scope void delegate(in char[]) sink) const 105 { 106 assert(markStart <= markEnd); 107 sink("Parse Error("); 108 static if (__traits(hasMember, Location, "toPrettyString")) 109 sink(markStart.toPrettyString); 110 else 111 sink(text(markStart)); 112 sink("): "); 113 sink(msg); 114 sink("\n"); 115 super.toString(sink); 116 } 117 118 override void toString(string inputString, scope void delegate(in char[]) sink, 119 ExceptionStringFlags flags = ExceptionStringFlags.none) const 120 { 121 assert(markStart <= markEnd); 122 123 if ((flags & ExceptionStringFlags.noLocation) == 0) 124 { 125 sink("Parse Error("); 126 static if (__traits(hasMember, Location, "toPrettyString")) 127 sink(markStart.toPrettyString); 128 else 129 sink(text(markStart)); 130 sink("): "); 131 } 132 133 sink(msg); 134 sink("\n"); 135 136 if ((flags & ExceptionStringFlags.noBacktrace) == 0) 137 super.toString(sink); 138 } 139 140 override const(ParseException) maxEndException() const 141 { 142 return this; 143 } 144 145 override bool laterEnd(const ParseException other) const 146 { 147 auto singleOther = cast(const(SingleParseException)) other; 148 if (singleOther is null) 149 return false; 150 return markEnd > singleOther.markEnd; 151 } 152 } 153 154 /** 155 Exception used by backtracking in the parser. 156 */ 157 class BacktrackParseException : ParseException 158 { 159 ParseException[] nextErrors; 160 string[] prods; 161 162 /** 163 Construct exception. 164 */ 165 this(string msg, string[] prods, ParseException[] nextErrors, 166 string file = __FILE__, size_t line = __LINE__) 167 { 168 this.nextErrors = nextErrors; 169 this.prods = prods; 170 super(msg, file, line); 171 } 172 173 override void toString(scope void delegate(in char[]) sink) const 174 { 175 foreach (i, e; nextErrors) 176 { 177 sink(prods[i]); 178 sink("\n"); 179 if (e !is null) 180 e.toString(sink); 181 else 182 sink("null???"); 183 sink("\n"); 184 } 185 super.toString(sink); 186 } 187 188 override void toString(string inputString, scope void delegate(in char[]) sink, 189 ExceptionStringFlags flags = ExceptionStringFlags.none) const 190 { 191 foreach (i, e; nextErrors) 192 { 193 sink(prods[i]); 194 sink("\n"); 195 if (e !is null) 196 e.toString(inputString, sink, flags); 197 else 198 sink("null???"); 199 sink("\n"); 200 } 201 super.toString(sink); 202 } 203 204 override const(ParseException) maxEndException() const 205 { 206 import std.typecons; 207 208 Rebindable!(const(ParseException)) max; 209 foreach (i, e; nextErrors) 210 { 211 auto e2 = e.maxEndException(); 212 if (max is null || e2.laterEnd(max)) 213 { 214 max = e2; 215 } 216 } 217 return max; 218 } 219 220 override string simpleMsg() const 221 { 222 return maxEndException().msg; 223 } 224 225 override bool allowBacktrack() const 226 { 227 foreach (e; nextErrors) 228 { 229 if (!e.allowBacktrack()) 230 return false; 231 } 232 return true; 233 } 234 } 235 236 /** 237 Exception used to combine multiple other exceptions in GLR parser. 238 */ 239 class MultiParseException : ParseException 240 { 241 ParseException[] nextErrors; 242 string[] prods; 243 244 /** 245 Construct exception. 246 */ 247 this(string msg, ParseException[] nextErrors, string file = __FILE__, size_t line = __LINE__) 248 { 249 assert(nextErrors.length); 250 this.nextErrors = nextErrors; 251 super(msg, file, line); 252 } 253 254 override void toString(scope void delegate(in char[]) sink) const 255 { 256 sink("MultiParseException\n"); 257 foreach (i, e; nextErrors) 258 { 259 if (e !is null) 260 e.toString(sink); 261 else 262 sink("null???"); 263 sink("\n"); 264 } 265 //super.toString(sink); 266 } 267 268 override void toString(string inputString, scope void delegate(in char[]) sink, 269 ExceptionStringFlags flags = ExceptionStringFlags.none) const 270 { 271 sink("MultiParseException\n"); 272 foreach (i, e; nextErrors) 273 { 274 if (e !is null) 275 e.toString(inputString, sink, flags); 276 else 277 sink("null???"); 278 sink("\n"); 279 } 280 //super.toString(sink); 281 } 282 283 override const(ParseException) maxEndException() const 284 { 285 import std.typecons; 286 287 Rebindable!(const(ParseException)) max; 288 foreach (i, e; nextErrors) 289 { 290 auto e2 = e.maxEndException(); 291 if (max is null || e2.laterEnd(max)) 292 { 293 max = e2; 294 } 295 } 296 return max; 297 } 298 299 override string simpleMsg() const 300 { 301 string r; 302 r ~= "MultiParseException: "; 303 foreach (i, e; nextErrors) 304 { 305 if (i > 0) 306 r ~= " | "; 307 if (e !is null) 308 r ~= e.simpleMsg; 309 else 310 r ~= "null???"; 311 } 312 return r; 313 } 314 }