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 }