1 module pegged.dynamic.peg; 2 3 import std.array: join; 4 import std.conv: to; 5 6 import std.stdio; 7 8 import pegged.peg; 9 10 alias ParseTree delegate(ParseTree) Dynamic; 11 12 string getName(D)(D rule) 13 { 14 return callDynamic(rule, ParseTree()).name; 15 } 16 17 ParseTree callDynamic(D)(D d, string s) 18 { 19 static if (is(typeof(d) : ParseTree delegate(ParseTree)) || is(typeof(d) : ParseTree function(ParseTree))) 20 return d(ParseTree("",false,[], s)); 21 else static if (is(typeof(d) : ParseTree delegate(ParseTree) delegate()) || is(typeof(d) : ParseTree function(ParseTree) delegate())) 22 return d()(ParseTree("",false,[], s)); 23 else static if (is(typeof(d) : ParseTree delegate(string)) || is(typeof(d) : ParseTree function(string))) 24 return d(s); 25 else static if (is(typeof(d) : ParseTree delegate(string) delegate()) || is(typeof(d) : ParseTree function(string) delegate())) 26 return d()(s); 27 else 28 static assert(false, "Bad callDynamic, with type " ~ D.stringof); 29 } 30 31 ParseTree callDynamic(D)(D d, ParseTree p) 32 { 33 static if (is(typeof(d) : ParseTree delegate(ParseTree)) || is(typeof(d) : ParseTree function(ParseTree))) 34 return d(p); 35 else static if (is(typeof(d) : ParseTree delegate(ParseTree) delegate()) || is(typeof(d) : ParseTree function(ParseTree) delegate())) 36 return d()(p); 37 else static if (is(typeof(d) : ParseTree delegate(string)) || is(typeof(d) : ParseTree function(string))) 38 return d(p.input[p.end..$]); 39 else static if (is(typeof(d) : ParseTree delegate(string) delegate()) || is(typeof(d) : ParseTree function(string) delegate())) 40 return d()(p.input[p.end..$]); 41 else 42 static assert(false, "Bad callDynamic, with type " ~ D.stringof); 43 } 44 45 Dynamic fail() 46 { 47 return (ParseTree p) 48 { 49 return ParseTree("fail", false, ["fail"], p.input, p.end, p.end); 50 }; 51 } 52 53 Dynamic eoi() 54 { 55 return (ParseTree p) 56 { 57 if (p.end == p.input.length) 58 return ParseTree("eoi", true, [], p.input, p.end, p.end); 59 else 60 return ParseTree("eoi", false, ["end of input"], p.input, p.end, p.end); 61 }; 62 } 63 64 Dynamic eps() 65 { 66 return (ParseTree p) 67 { 68 return ParseTree("eps", true, [""], p.input, p.end, p.end); 69 }; 70 } 71 72 Dynamic any() 73 { 74 return(ParseTree p) 75 { 76 if (p.end < p.input.length) 77 return ParseTree("any", true, [p.input[p.end..p.end+1]], p.input, p.end, p.end+1); 78 else 79 return ParseTree("any", false, ["any char"], p.input, p.end, p.end); 80 }; 81 } 82 83 Dynamic literal(string s) 84 { 85 return (ParseTree p) 86 { 87 if (p.end+s.length <= p.input.length && p.input[p.end..p.end+s.length] == s) 88 return ParseTree("literal!(\"" ~ s ~ "\")", true, [s], p.input, p.end, p.end+s.length); 89 else 90 return ParseTree("literal!(\"" ~ s ~ "\")", false, [`"` ~ s ~ `"`], p.input, p.end, p.end); 91 92 }; 93 } 94 95 Dynamic charRange(dchar begin, dchar end) 96 { 97 return (ParseTree p) 98 { 99 string longName = "a char between '"~to!string(begin)~"' and '"~to!string(end)~"'"; 100 if (p.end < p.input.length && p.input[p.end] >= begin && p.input[p.end] <= end) 101 return ParseTree("charRange!(" ~ to!string(begin) ~ ", " ~ to!string(end) ~ ")", true, [p.input[p.end..p.end+1]], p.input, p.end, p.end+1); 102 else 103 return ParseTree("charRange!(" ~ to!string(begin) ~ ", " ~ to!string(end) ~ ")", false, [longName], p.input, p.end, p.end); 104 }; 105 106 } 107 108 Dynamic wrapAround(B, M, A)(B before, M middle, A after) 109 { 110 return(ParseTree p) 111 { 112 ParseTree temp = callDynamic(before,p); 113 if (!temp.successful) 114 return temp; 115 116 ParseTree result = callDynamic(middle,temp); 117 if (!result.successful) 118 return result; 119 result.begin = temp.begin; 120 121 temp = callDynamic(after,result); 122 if (!temp.successful) 123 return temp; 124 125 result.end = temp.end; 126 return result; 127 }; 128 } 129 130 Dynamic zeroOrMore(D)(D d) 131 { 132 return (ParseTree p) 133 { 134 string name = "zeroOrMore!(" ~ getName(d) ~ ")"; 135 ParseTree result = ParseTree(name, true, [], p.input, p.end, p.end); 136 ParseTree temp = callDynamic(d,result); 137 while(temp.successful 138 && (temp.begin < temp.end // To avoid infinite loops on epsilon-matching rules 139 || temp.name.startsWith("discard!("))) 140 { 141 result.matches ~= temp.matches; 142 result.children ~= temp; 143 result.end = temp.end; 144 temp = callDynamic(d, result); 145 } 146 result.successful = true; 147 return result; 148 }; 149 } 150 151 Dynamic oneOrMore(D)(D d) 152 { 153 return(ParseTree p) 154 { 155 string name = "oneOrMore!(" ~ getName(d) ~ ")"; 156 ParseTree result = ParseTree(name, false, [], p.input, p.end, p.end); 157 ParseTree temp = callDynamic(d, result); 158 159 if (!temp.successful) 160 { 161 result.matches = temp.matches; 162 result.children = [temp]; 163 result.end = temp.end; 164 } 165 else 166 { 167 while( temp.successful 168 && (temp.begin < temp.end // To avoid infinite loops on epsilon-matching rules 169 || temp.name.startsWith("discard!("))) 170 { 171 result.matches ~= temp.matches; 172 result.children ~= temp; 173 result.end = temp.end; 174 temp = callDynamic(d, result); 175 } 176 result.successful = true; 177 } 178 return result; 179 }; 180 } 181 182 Dynamic option(D)(D d) 183 { 184 return (ParseTree p) 185 { 186 string name = "option!(" ~ getName(d) ~ ")"; 187 ParseTree result = callDynamic(d, p); 188 if (result.successful) 189 return ParseTree(name, true, result.matches, result.input, result.begin, result.end, [result]); 190 else 191 return ParseTree(name, true, [], p.input, p.end, p.end, null); 192 }; 193 } 194 195 Dynamic and(T...)(T rules) if (T.length) 196 { 197 return (ParseTree p) 198 { 199 bool keepNode(ParseTree node) 200 { 201 return node.name.startsWith("keep!(") 202 || ( !node.name.startsWith("discard!(") 203 //&& !node.name.startsWith("drop!(") 204 && node.matches !is null 205 //&& node.begin != node.end 206 ); 207 } 208 209 210 string name = "and!(" ~ ")"; 211 212 ParseTree result = ParseTree(name, false, [], p.input, p.end, p.end, []); 213 214 foreach(i,r; rules) 215 { 216 ParseTree temp = callDynamic(r, result); 217 218 result.end = temp.end; 219 if (temp.successful) 220 { 221 if (keepNode(temp)) 222 { 223 result.matches ~= temp.matches; 224 if (temp.name.startsWith("drop!(")) 225 {} 226 else if (temp.name.startsWith("propagate!(")) 227 result.children ~= temp.children; 228 else 229 result.children ~= temp; 230 } 231 } 232 else 233 { 234 result.children ~= temp;// add the failed node, to indicate which failed 235 if (temp.matches.length > 0) 236 result.matches ~= temp.matches[$-1]; 237 return result; // and end the parsing attempt right there 238 } 239 } 240 result.successful = true; 241 return result; 242 }; 243 } 244 245 Dynamic or(T...)(T rules) 246 { 247 return (ParseTree p) 248 { 249 // error-management 250 ParseTree longestFail = ParseTree("or", false, [], p.input, p.end, 0); 251 string[] errorStrings; 252 size_t errorStringChars; 253 string orErrorString; 254 255 ParseTree[rules.length] results; 256 string[rules.length] names; 257 size_t[rules.length] failedLength; 258 size_t maxFailedLength; 259 260 // Real 'or' loop 261 foreach(i,r; rules) 262 { 263 ParseTree temp = callDynamic(r, p); 264 if (temp.successful) 265 { 266 temp.children = [temp]; 267 temp.name = "or"; 268 return temp; 269 } 270 else 271 { 272 enum errName = " (" ~")"; 273 failedLength[i] = temp.end; 274 if (temp.end >= longestFail.end) 275 { 276 maxFailedLength = temp.end; 277 longestFail = temp; 278 names[i] = errName; 279 results[i] = temp; 280 281 if (temp.end == longestFail.end) 282 errorStringChars += temp.matches[$-1].length + errName.length + 4; 283 else 284 errorStringChars = temp.matches[$-1].length + errName.length + 4; 285 } 286 // Else, this error parsed less input than another one: we discard it. 287 } 288 } 289 290 291 // All subrules failed, we will take the longest match as the result 292 // If more than one node failed at the same (farthest) position, we concatenate their error messages 293 294 295 char[] errString;// = new char[](errorStringChars); 296 errString.length = errorStringChars; 297 uint start = 0; 298 foreach(i; 0..rules.length) 299 { 300 if (failedLength[i] == maxFailedLength) 301 { 302 auto temp = results[i]; 303 auto len = temp.matches[$-1].length; 304 auto nlen = names[i].length; 305 errString[start .. start+len] = temp.matches[$-1][]; 306 errString[start+len .. start+len+names[i].length] = names[i][]; 307 errString[start+len+nlen .. start+len+nlen+4] = " or "; 308 start += len + names[i].length + 4; 309 } 310 } 311 orErrorString = cast(string)(errString[0..$-4]); 312 313 longestFail.matches = longestFail.matches[0..$-1] // discarding longestFail error message 314 ~ [orErrorString]; // and replacing it by the new, concatenated one. 315 longestFail.name = "or"; 316 longestFail.begin = p.end; 317 return longestFail; 318 }; 319 } 320 321 Dynamic posLookahead(D)(D d) 322 { 323 return (ParseTree p) 324 { 325 string name = "posLookahead!(" ~ getName(d) ~ ")"; 326 ParseTree temp = callDynamic(d,p); 327 if (temp.successful) 328 return ParseTree(name, temp.successful, [], p.input, p.end, p.end); 329 else 330 return ParseTree(name, temp.successful, [temp.matches[$-1]], p.input, p.end, p.end); 331 }; 332 } 333 334 Dynamic negLookahead(D)(D d) 335 { 336 return (ParseTree p) 337 { 338 string name = "negLookahead!(" ~ getName(d) ~ ")"; 339 ParseTree temp = callDynamic(d,p); 340 if (temp.successful) 341 return ParseTree(name, false, ["anything but \"" ~ p.input[temp.begin..temp.end] ~ "\""], p.input, p.end, p.end); 342 else 343 return ParseTree(name, true, [], p.input, p.end, p.end); 344 }; 345 } 346 347 Dynamic named(D)(D d, string name) 348 { 349 return (ParseTree p) 350 { 351 ParseTree result = callDynamic(d,p); 352 result.name = name; 353 return result; 354 }; 355 } 356 357 Dynamic action(D, A)(D d, A act) 358 { 359 return (ParseTree p) 360 { 361 return callDynamic(act, callDynamic(d,p)); 362 }; 363 } 364 365 Dynamic fuse(D)(D d) 366 { 367 return(ParseTree p) 368 { 369 p = callDynamic(d,p); 370 if (p.successful) 371 { 372 if (p.matches.length != 0) 373 p.matches = [std.array.join(p.matches)]; 374 375 p.children = null; // also discard children 376 } 377 return p; 378 }; 379 } 380 381 Dynamic discardChildren(D)(D d) 382 { 383 return (ParseTree p) 384 { 385 p = callDynamic(d,p); 386 p.children = null; 387 return p; 388 }; 389 } 390 391 Dynamic discardMatches(D)(D d) 392 { 393 return (ParseTree p) 394 { 395 p = callDynamic(d,p); 396 if (p.successful) 397 p.matches = null; 398 return p; 399 }; 400 } 401 402 Dynamic discard(D)(D d) 403 { 404 return (ParseTree p) 405 { 406 ParseTree result = callDynamic(d,p); 407 result.name = "discard!(" ~ getName(d) ~ ")"; 408 //result.begin = result.end; 409 result.children = null; 410 if (result.successful) 411 result.matches = null;//to keep error messages, if any 412 413 return result; 414 }; 415 } 416 417 Dynamic drop(D)(D d) 418 { 419 return (ParseTree p) 420 { 421 ParseTree result = callDynamic(d,p); 422 result.children = null; 423 if (result.successful) 424 result.name = "drop!(" ~ getName(d) ~ ")"; 425 return result; 426 }; 427 } 428 429 Dynamic propagate(D)(D d) 430 { 431 return (ParseTree p) 432 { 433 ParseTree result = callDynamic(d,p); 434 if (result.successful) 435 result.name = "propagate!(" ~ getName(d) ~ ")"; 436 return result; 437 }; 438 } 439 440 Dynamic keep(D)(D d) 441 { 442 return (ParseTree p) 443 { 444 ParseTree result = callDynamic(d,p); 445 if (result.successful) 446 { 447 result.children = [result]; 448 result.name = "keep!(" ~ getName(d) ~ ")"; 449 } 450 return result; 451 }; 452 }