1 module gamma.grammar.PrintingVisitor; 2 3 import gamma.grammar.Alternative; 4 import gamma.grammar.Grammar; 5 import gamma.grammar.Rule; 6 import gamma.grammar.SymbolNode; 7 import gamma.grammar.Visitor; 8 import std.range; 9 10 version (unittest) import gamma.grammar.GrammarBuilder; 11 12 public auto printingVisitor(Writer)(Writer writer) 13 out (visitor; visitor !is null) 14 { 15 return new PrintingVisitor!Writer(writer); 16 } 17 18 public class PrintingVisitor(Writer) : Visitor 19 { 20 private Writer writer; 21 22 public this(Writer writer) 23 { 24 this.writer = writer; 25 } 26 27 public void visit(Grammar grammar) 28 { 29 foreach (i, rule; grammar.rules.enumerate) 30 { 31 if (i > 0) 32 this.writer.put("\n"); 33 rule.accept(this); 34 } 35 } 36 37 public void visit(Alternative alternative) 38 { 39 foreach (i, node; alternative.rhs.enumerate) 40 { 41 if (i > 0) 42 this.writer.put(" "); 43 node.accept(this); 44 } 45 } 46 47 public void visit(SymbolNode symbolNode) 48 { 49 this.writer.put(symbolNode.symbol.toString); 50 } 51 52 public void visit(Rule rule) 53 { 54 rule.alternatives.front.lhs.accept(this); 55 this.writer.put(" ="); 56 foreach (i, alternative; rule.alternatives.enumerate) 57 { 58 if (i == 0) 59 { 60 if (!alternative.rhs.empty) 61 this.writer.put("\n "); 62 } 63 else 64 { 65 if (!alternative.rhs.empty) 66 this.writer.put("\n | "); 67 else 68 this.writer.put("\n |"); 69 } 70 alternative.accept(this); 71 } 72 if (!rule.alternatives.back.rhs.empty) 73 this.writer.put(".\n"); 74 else 75 this.writer.put(" .\n"); 76 } 77 } 78 79 @("write grammar") 80 unittest 81 { 82 import std.array : appender; 83 import std..string : outdent, stripLeft; 84 85 with (TestGrammarBuilder()) 86 { 87 rule("A: A |"); 88 rule("B: | B"); 89 90 auto writer = appender!string; 91 auto visitor = printingVisitor(writer); 92 93 visitor.visit(grammar); 94 95 const expected = ` 96 A = 97 A 98 | . 99 100 B = 101 | B. 102 `; 103 104 assert(writer[] == expected.outdent.stripLeft); 105 } 106 }