1 /** 2 * Ultra-simple XML grammar 3 */ 4 module pegged.examples.xml; 5 6 import pegged.grammar; 7 8 import std.array; 9 10 string[] nameStack; 11 12 /// Semantic action to push a tag name on a stack 13 O opening(O)(O o) 14 { 15 if (o.successful) 16 nameStack ~= o.matches; 17 return o; 18 } 19 20 /** 21 * Semantic action to pop the name stack and compare a tag name 22 * The parsing fails if the tags do not nest correctly (See docs, the Semantic Actions wiki page) 23 */ 24 O closing(O)(O o) 25 { 26 if (o.matches.empty || nameStack.back != o.matches[0]) 27 o.successful = false; 28 else 29 nameStack.popBack(); 30 return o; 31 } 32 33 /** 34 * Semantic action to flush the name stack 35 */ 36 O flush(O)(O o) 37 { 38 nameStack = null; 39 return o; 40 } 41 42 mixin(grammar(` 43 XML: 44 Node <- OpeningTag{opening} (Node / Text)* ClosingTag{closing} 45 OpeningTag <- :"<" identifier :">" 46 ClosingTag <- :"</" identifier :">" 47 Text <~ (!OpeningTag !ClosingTag .)+ 48 `)); 49 50 unittest 51 { 52 auto p1 = XML("<a></a>"); 53 assert(p1.successful); 54 assert(p1.matches == ["a", "a"]); 55 assert(p1.children[0].children.length == 2); 56 57 assert(p1.children[0].name == "XML.Node"); 58 assert(p1.children[0].children[0].name == "XML.OpeningTag"); 59 assert(p1.children[0].children[1].name == "XML.ClosingTag"); 60 61 assert(!XML("<b></>").successful); // incomplete closing tag 62 assert(!XML("<c></d>").successful); // unmatched tag 63 assert( XML("<e><f></f></e>").successful); // OK 64 assert(!XML("<g><h></g></h>").successful); // badly enclosed tags 65 66 auto p2 = XML("<text>Hello World! This is an <emph>important</emph> announcement!</text>"); 67 assert(p2.successful); 68 69 assert(p2.matches == ["text", "Hello World! This is an ", "emph", "important", "emph", " announcement!", "text"]); 70 assert(p2.children[0].children[0].matches == ["text"]); 71 assert(p2.children[0].children[1].matches == ["Hello World! This is an "]); 72 assert(p2.children[0].children[$-1].matches == ["text"]); 73 }