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 }