1 /** 2 Dynamic grammars: 3 - no CT parsing (this one will stay) 4 - no memoization (could be added) 5 - slightly less handy parameterized rules (could be better) 6 - no semantic actions (drat, this one is the worse) 7 - no calling from other grammars? 8 9 Advantages: 10 - fully runtime configurable: change rules, add rules, delete rules. 11 */ 12 module pegged.dynamic.grammar; 13 14 import std.algorithm : startsWith; 15 import std.array; 16 import std.stdio; 17 18 import pegged.peg; 19 import pegged.parser; 20 import pegged.dynamic.peg; 21 22 struct ParameterizedRule 23 { 24 size_t numArgs; 25 Dynamic delegate(Dynamic[]) code; 26 27 Dynamic opCall(D...)(D rules) 28 { 29 Dynamic[] args; 30 foreach(i,rule; rules) 31 { 32 static if (is(typeof(rule) == Dynamic)) 33 args ~= rule; 34 else 35 args ~= rule(); // Dynamic delegate() 36 } 37 38 if(rules.length == numArgs) 39 { 40 switch(numArgs) 41 { 42 case 1: 43 return code([args[0]]); 44 case 2: 45 return code([args[0], args[1]]); 46 case 3: 47 return code([args[0], args[1], args[2]]); 48 case 4: 49 return code([args[0], args[1], args[2], args[3]]); 50 case 5: 51 return code([args[0], args[1], args[2], args[3], args[4]]); 52 case 6: 53 return code([args[0], args[1], args[2], args[3], args[4], args[5]]); 54 default: 55 throw new Exception("Unimplemented parameterized rule with more than 6 arguments."); 56 } 57 } 58 else 59 throw new Exception( "ParameterizedRule called with " 60 ~ to!string(rules.length) 61 ~ " args. Waited for " 62 ~ to!string(numArgs) ~ "."); 63 return code(args); 64 } 65 } 66 67 ParameterizedRule parameterizedRule(size_t n, Dynamic delegate(Dynamic[] d) code) 68 { 69 ParameterizedRule pr; 70 pr.numArgs = n; 71 pr.code = code; 72 return pr; 73 } 74 75 struct DynamicGrammar 76 { 77 string grammarName; 78 string startingRule; 79 Dynamic[string] rules; 80 ParameterizedRule[string] paramRules; 81 82 ParseTree decimateTree(ParseTree p) 83 { 84 if(p.children.length == 0) return p; 85 86 ParseTree[] filterChildren(ParseTree pt) 87 { 88 ParseTree[] result; 89 foreach(child; pt.children) 90 { 91 if ( (child.name.startsWith(grammarName) && child.matches.length != 0) 92 || !child.successful && child.children.length == 0) 93 { 94 child.children = filterChildren(child); 95 result ~= child; 96 } 97 else if (child.name.startsWith("keep!(")) // 'keep' node are never discarded. 98 // They have only one child, the node to keep 99 { 100 result ~= child.children[0]; 101 } 102 else // discard this node, but see if its children contain nodes to keep 103 { 104 result ~= filterChildren(child); 105 } 106 } 107 return result; 108 } 109 p.children = filterChildren(p); 110 return p; 111 } 112 113 ParseTree opCall(ParseTree p) 114 { 115 return decimateTree(rules[startingRule](p)); 116 /+ 117 result.children = [result]; 118 result.name = grammarName; 119 return result; 120 +/ 121 } 122 123 ParseTree opCall(string input) 124 { 125 return decimateTree(rules[startingRule](ParseTree(``, false, [], input, 0, 0))); 126 /+ 127 result.children = [result]; 128 result.name = grammarName; 129 return result; 130 +/ 131 } 132 133 void opIndexAssign(D)(D code, string s) 134 { 135 rules[s]= named(code, grammarName ~ "." ~ s); 136 } 137 138 Dynamic opIndex(string s) 139 { 140 return rules[s]; 141 } 142 } 143 144 // Helper to insert 'Spacing' before and after Primaries 145 ParseTree spaceArrow(ParseTree input) 146 { 147 ParseTree wrapInSpaces(ParseTree p) 148 { 149 ParseTree spacer = 150 ParseTree("Pegged.Prefix", true, null, null, 0,0, [ 151 ParseTree("Pegged.Suffix", true, null, null, 0, 0, [ 152 ParseTree("Pegged.Primary", true, null, null, 0, 0, [ 153 ParseTree("Pegged.RhsName", true, null, null, 0,0, [ 154 ParseTree("Pegged.Identifier", true, ["Spacing"]) 155 ]) 156 ]) 157 ]) 158 ]); 159 ParseTree result = ParseTree("Pegged.WrapAround", true, p.matches, p.input, p.begin, p.end, p.children); 160 result.children = spacer ~ result.children ~ spacer; 161 return result; 162 } 163 return modify!( p => p.name == "Pegged.Primary", 164 wrapInSpaces)(input); 165 } 166 167 Dynamic makeRule(string def, Dynamic[string] context) 168 { 169 ParseTree p = Pegged.decimateTree(Pegged.Definition(def)); 170 return makeRule(p, context); 171 } 172 173 Dynamic makeRule(ParseTree def, Dynamic[string] context) 174 { 175 Dynamic code; 176 177 Dynamic getDyn(string name) 178 { 179 if (name in context) 180 return context[name]; 181 else 182 throw new Exception("Unknown name: " ~ name); 183 } 184 185 Dynamic ruleFromTree(ParseTree p) 186 { 187 //writeln("rfT: ", p.name, " ", p.matches); 188 //Dynamic result; 189 switch(p.name) 190 { 191 case "Pegged.Expression": 192 //if (p.children.length > 1) // OR expression 193 //{ 194 Dynamic[] children; 195 foreach(seq; p.children) 196 children ~= ruleFromTree(seq); 197 return distribute!(or)(children); 198 //} 199 //else // One child -> just a sequence, no need for a or!( , ) 200 //{ 201 // return ruleFromTree(p.children[0]); 202 //} 203 //break; 204 case "Pegged.Sequence": 205 //if (p.children.length > 1) // real sequence 206 //{ 207 Dynamic[] children; 208 foreach(seq; p.children) 209 children ~= ruleFromTree(seq); 210 return distribute!(pegged.dynamic.peg.and)(children); 211 /+} 212 else // One child -> just a Suffix, no need for a and!( , ) 213 { 214 return ruleFromTree(p.children[0]); 215 } 216 +/ 217 case "Pegged.Prefix": 218 ParseTree temp; 219 foreach_reverse(i,child; p.children[0..$-1]) // transforming a list into a linear tree 220 { 221 temp = p.children[$-1]; 222 p.children[$-1] = child; 223 p.children[$-1].children = [temp]; 224 } 225 return ruleFromTree(p.children[$-1]); 226 case "Pegged.Suffix": 227 foreach(child; p.children[1..$]) 228 { 229 ParseTree temp = p.children[0]; 230 p.children[0] = child; 231 p.children[0].children = [temp]; 232 } 233 return ruleFromTree(p.children[0]); 234 case "Pegged.Primary": 235 return ruleFromTree(p.children[0]); 236 case "Pegged.RhsName": 237 return ruleFromTree(p.children[0]); 238 case "Pegged.Identifier": 239 //writeln("Identifier: ", p.matches[0], " ");//, getDyn(p.matches[0])()(ParseTree()).name); 240 return getDyn(p.matches[0]); 241 case "Pegged.Literal": 242 //writeln("Literal: ", p.matches); 243 if(p.matches.length == 3) // standard case 244 return literal(p.matches[1]); 245 else // only two children -> empty literal 246 return eps(); 247 case "Pegged.CharClass": 248 if (p.children.length > 1) 249 { 250 Dynamic[] children; 251 foreach(seq; p.children) 252 children ~= ruleFromTree(seq); 253 return distribute!(pegged.dynamic.peg.or)(children); 254 } 255 else // One child -> just a sequence, no need for a or!( , ) 256 { 257 return ruleFromTree(p.children[0]); 258 } 259 //break; 260 case "Pegged.CharRange": 261 /// Make the generation at the Char level: directly what is needed, be it `` or "" or whatever 262 if (p.children.length > 1) // a-b range 263 { 264 return charRange(p.matches[0].front, p.matches[2].front); 265 } 266 else // lone char 267 { 268 //result = "pegged.peg.literal!("; 269 string ch = p.matches[0]; 270 switch (ch) 271 { 272 case "\\[": 273 case "\\]": 274 case "\\-": 275 return literal(ch[0..1]); 276 case "\\\'": 277 return literal("\'"); 278 case "\\`": 279 return literal("`"); 280 case "\\": 281 case "\\\\": 282 return literal("\\"); 283 case "\"": 284 case "\\\"": 285 return literal("\""); 286 case "\n": 287 case "\r": 288 case "\t": 289 return literal(ch); 290 default: 291 return literal(ch); 292 } 293 } 294 case "Pegged.Char": 295 string ch = p.matches[0]; 296 switch (ch) 297 { 298 case "\\[": 299 case "\\]": 300 case "\\-": 301 302 case "\\\'": 303 case "\\\"": 304 case "\\`": 305 case "\\\\": 306 return literal(ch[1..$]); 307 case "\n": 308 case "\r": 309 case "\t": 310 return literal(ch); 311 default: 312 return literal(ch); 313 } 314 //break; 315 case "Pegged.POS": 316 return posLookahead(ruleFromTree(p.children[0])); 317 case "Pegged.NEG": 318 return negLookahead(ruleFromTree(p.children[0])); 319 case "Pegged.FUSE": 320 return fuse(ruleFromTree(p.children[0])); 321 case "Pegged.DISCARD": 322 return discard(ruleFromTree(p.children[0])); 323 case "Pegged.KEEP": 324 return keep(ruleFromTree(p.children[0])); 325 case "Pegged.DROP": 326 return drop(ruleFromTree(p.children[0])); 327 case "Pegged.PROPAGATE": 328 return propagate(ruleFromTree(p.children[0])); 329 case "Pegged.OPTION": 330 return option(ruleFromTree(p.children[0])); 331 case "Pegged.ZEROORMORE": 332 return zeroOrMore(ruleFromTree(p.children[0])); 333 case "Pegged.ONEORMORE": 334 return oneOrMore(ruleFromTree(p.children[0])); 335 case "Pegged.Action": 336 Dynamic result = ruleFromTree(p.children[0]); 337 foreach(act; p.matches[1..$]) 338 result = action(result, getDyn(act)); 339 return result; 340 case "Pegged.ANY": 341 return any(); 342 case "Pegged.WrapAround": 343 return wrapAround( ruleFromTree(p.children[0]) 344 , ruleFromTree(p.children[1]) 345 , ruleFromTree(p.children[2])); 346 default: 347 throw new Exception("Bad tree: " ~ p.toString()); 348 } 349 } 350 351 switch(def.children[1].children[0].name) 352 { 353 case "Pegged.LEFTARROW": 354 code = ruleFromTree(def.children[2]); 355 break; 356 case "Pegged.FUSEARROW": 357 code = fuse(ruleFromTree(def.children[2])); 358 break; 359 case "Pegged.DISCARDARROW": 360 code = discard(ruleFromTree(def.children[2])); 361 break; 362 case "Pegged.KEEPARROW": 363 code = keep(ruleFromTree(def.children[2])); 364 break; 365 case "Pegged.DROPARROW": 366 code = drop(ruleFromTree(def.children[2])); 367 break; 368 case "Pegged.PROPAGATEARROW": 369 code = propagate(ruleFromTree(def.children[2])); 370 break; 371 case "Pegged.SPACEARROW": 372 ParseTree modified = spaceArrow(def.children[2]); 373 code = ruleFromTree(modified); 374 break; 375 case "Pegged.ACTIONARROW": 376 Dynamic actionResult = ruleFromTree(def.children[2]); 377 foreach(act; def.children[1].matches[1..$]) 378 actionResult = action(actionResult, getDyn(act)); 379 code = actionResult; 380 break; 381 default: 382 throw new Exception("Unknow arrow: " ~ def.children[1].children[0].name); 383 //break; 384 } 385 386 return named(code, def.matches[0]); 387 } 388 389 DynamicGrammar grammar(string definition, Dynamic[string] context = null) 390 { 391 //writeln("Entering dyn gram"); 392 ParseTree defAsParseTree = Pegged(definition); 393 //writeln(defAsParseTree); 394 if (!defAsParseTree.successful) 395 { 396 // To work around a strange bug with ParseTree printing at compile time 397 throw new Exception("Bad grammar input: " ~ defAsParseTree.toString("")); 398 } 399 400 DynamicGrammar gram; 401 foreach(name, rule; context) 402 { 403 gram.rules[name] = rule; 404 } 405 406 ParseTree p = defAsParseTree.children[0]; 407 408 string grammarName = p.children[0].matches[0]; 409 string shortGrammarName = p.children[0].matches[0]; 410 gram.grammarName = shortGrammarName; 411 412 // Predefined spacing 413 gram.rules["Spacing"] = discard(zeroOrMore(or(literal(" "), literal("\t"), literal("\n"), literal("\r")))); 414 415 ParseTree[] definitions = p.children[1 .. $]; 416 417 foreach(i,def; definitions) 418 { 419 gram[def.matches[0]] = fail(); 420 } 421 422 foreach(i,def; definitions) 423 { 424 string shortName = def.matches[0]; 425 426 if (i == 0) 427 gram.startingRule = shortName; 428 // prepending the global grammar name, to get a qualified-name rule 'Gram.Rule' 429 def.matches[0] = shortGrammarName ~ "." ~ def.matches[0]; 430 gram.rules[shortName] = makeRule(def, gram.rules); 431 } 432 433 return gram; 434 } 435 436 Dynamic distribute(alias fun)(Dynamic[] args) 437 { 438 //mixin(makeSwitch(40)); 439 switch(args.length) 440 { 441 case 1: 442 return fun(args[0]); 443 case 2: 444 return fun(args[0], args[1]); 445 case 3: 446 return fun(args[0], args[1], args[2]); 447 case 4: 448 return fun(args[0], args[1], args[2], args[3]); 449 case 5: 450 return fun(args[0], args[1], args[2], args[3], args[4]); 451 case 6: 452 return fun(args[0], args[1], args[2], args[3], args[4], args[5]); 453 default: 454 return fun(fun(args[0], args[1], args[2], args[3], args[4], args[5]), distribute!fun(args[6..$])); 455 } 456 }