1 module gamma.grammar.hyper.PrintingHyperVisitor; 2 3 import gamma.grammar.hyper.Group; 4 import gamma.grammar.hyper.HyperVisitor; 5 import gamma.grammar.hyper.Option; 6 import gamma.grammar.hyper.Repetition; 7 import gamma.grammar.hyper.RepetitionAlternative; 8 import gamma.grammar.Alternative; 9 import gamma.grammar.Grammar; 10 import gamma.grammar.Node; 11 import gamma.grammar.Rule; 12 import gamma.grammar.SymbolNode; 13 import std.range; 14 15 version (unittest) import gamma.grammar.GrammarBuilder; 16 17 public auto printingHyperVisitor(Writer)(Writer writer) 18 out (visitor; visitor !is null) 19 { 20 return new PrintingHyperVisitor!Writer(writer); 21 } 22 23 public class PrintingHyperVisitor(Writer) : HyperVisitor 24 { 25 private Writer writer; 26 27 private string indentation; 28 29 public this(Writer writer) 30 { 31 this.writer = writer; 32 } 33 34 public void visit(Grammar grammar) 35 { 36 foreach (i, rule; grammar.rules.enumerate) 37 { 38 if (i > 0) 39 this.writer.put("\n"); 40 rule.accept(this); 41 } 42 } 43 44 public void visit(Alternative alternative) 45 { 46 foreach (i, node; alternative.rhs.enumerate) 47 { 48 if (i > 0) 49 { 50 this.writer.put("\n"); 51 this.writer.put(this.indentation); 52 } 53 node.accept(this); 54 } 55 } 56 57 public void visit(SymbolNode symbolNode) 58 { 59 // auto hyperSymbolNode = cast(HyperSymbolNode) symbolNode; 60 61 this.writer.put(symbolNode.symbol.toString); 62 } 63 64 public void visit(Rule rule) 65 { 66 Alternative alternative = rule.alternatives.front; 67 68 alternative.lhs.accept(this); 69 this.writer.put(":"); 70 this.indentation = null; 71 printHyperExpr(rule.alternatives); 72 if (!rule.alternatives.back.rhs.empty) 73 this.writer.put(".\n"); 74 else 75 this.writer.put(" .\n"); 76 } 77 78 public void visit(Group group) 79 { 80 this.writer.put("("); 81 printHyperExpr(group.rule.alternatives); 82 this.writer.put("\n"); 83 this.writer.put(this.indentation); 84 this.writer.put(")"); 85 } 86 87 public void visit(Option option) 88 { 89 this.writer.put("["); 90 printHyperExpr(option.rule.alternatives); 91 this.writer.put("\n"); 92 this.writer.put(this.indentation); 93 this.writer.put("]"); 94 } 95 96 public void visit(Repetition repetition) 97 { 98 this.writer.put("{"); 99 printHyperExpr(repetition.rule.alternatives); 100 this.writer.put("\n"); 101 this.writer.put(this.indentation); 102 this.writer.put("}"); 103 } 104 105 public void visit(RepetitionAlternative alternative) 106 { 107 visit(cast(Alternative) alternative); 108 } 109 110 private void printHyperExpr(Alternative[] alternatives) 111 { 112 const indentation = this.indentation; 113 114 scope (exit) 115 this.indentation = indentation; 116 117 this.indentation ~= " "; 118 foreach (i, alternative; alternatives.enumerate) 119 { 120 if (i == 0) 121 { 122 if (!alternative.rhs.empty) 123 { 124 this.writer.put("\n"); 125 this.writer.put(this.indentation); 126 } 127 } 128 else 129 { 130 this.writer.put("\n"); 131 this.writer.put(indentation); 132 if (!alternative.rhs.empty) 133 this.writer.put(" | "); 134 else 135 this.writer.put(" |"); 136 } 137 alternative.accept(this); 138 } 139 } 140 } 141 142 @("write hyper grammar") 143 unittest 144 { 145 import std.array : appender; 146 import std..string : outdent, stripLeft; 147 148 with (TestGrammarBuilder()) 149 { 150 rule("A: A |"); 151 rule("B: | B"); 152 153 auto writer = appender!string; 154 auto visitor = printingHyperVisitor(writer); 155 156 visitor.visit(grammar); 157 158 const expected = ` 159 A: 160 A 161 | . 162 163 B: 164 | B. 165 `; 166 167 assert(writer[] == expected.outdent.stripLeft); 168 } 169 }