1 module RuleTokenTest;
2 
3 import RuleTranslatorLexer;
4 import RuleTranslatorParser: RuleTranslatorParser;
5 import RuleTranslatorBaseListener;
6 import antlr.v4.runtime.ANTLRInputStream;
7 import antlr.v4.runtime.CommonToken;
8 import antlr.v4.runtime.CommonTokenFactory;
9 import antlr.v4.runtime.TokenFactory;
10 import antlr.v4.runtime.CommonTokenStream;
11 import antlr.v4.runtime.TokenStream;
12 import antlr.v4.runtime.tree.TerminalNode;
13 import antlr.v4.runtime.CharStream;
14 import antlr.v4.runtime.LexerNoViableAltException;
15 import antlr.v4.runtime.Token;
16 import antlr.v4.runtime.TokenSource;
17 import antlr.v4.runtime.TokenStreamRewriter : TokenStreamRewriter;
18 import antlr.v4.runtime.atn.ParserATNSimulator;
19 import antlr.v4.runtime.tree.ParseTreeWalker;
20 import dshould : be, equal, not, should;
21 import dshould.thrown;
22 import std.conv : to;
23 import std.stdio : File, writefln;
24 import std.file;
25 import unit_threaded;
26 import std.typecons;
27 import std.variant;
28 
29 class ResultTokenFactory : CommonTokenFactory {
30 
31     this(CharStream input) {
32     }
33 
34     override
35     ResultToken create(int type, Variant text) {
36         return new ResultToken(type, text);
37     }
38 
39     override
40     ResultToken create(TokenFactorySourcePair source, int type,
41                        Variant text, int channel, int start, int stop,
42                        int line, int charPositionInLine ) {
43         auto t = new  ResultToken(source, type, channel,
44                                   start, stop);
45         t.setLine(line);
46         t.setCharPositionInLine(charPositionInLine);
47         return t;
48     }
49 }
50 
51 /**
52  * A Token source with Result as additional attribute
53  */
54 
55 alias TokenFactorySourcePair = Tuple!(TokenSource, "a", CharStream, "b");
56 
57 struct Result { ushort indent; string text;}
58 
59 ushort indent;
60 
61 class ResultToken : CommonToken {
62 
63     public Result[] res;
64 
65     this (int type, Variant text) {
66         super(type, text);
67     }
68 
69     this( TokenFactorySourcePair source, int type,
70           int channel, int start, int stop) {
71         super(source, type, channel, start, stop);
72         Result r;
73         r.text = super.getText.to!string;
74         res ~= r;
75     }
76 
77     override string toString() {
78         import std.format : format;
79         return format("%s", res);
80     }
81 
82     override Variant getText() {
83         debug(TokenStreamRewriter) {
84             import std.stdio;
85             writefln("\ngetText: res = %s, type = %s", res, type);
86         }
87         Variant r = res;
88         return r;
89     }
90 }
91 
92 public class ResultListener : RuleTranslatorBaseListener {
93 
94     private TokenStreamRewriter rewriter;
95 
96     private bool nextOfNewline = false;
97 
98     ushort indentText = 0;
99 
100     this(TokenStream tokens)
101     {
102         rewriter = new TokenStreamRewriter(tokens);
103     }
104     /**
105      * <p>The default implementation does nothing.</p>
106      */
107     override public void enterFile_input(RuleTranslatorParser.File_inputContext ctx) {
108     }
109 
110     /**
111      * {@inheritDoc}
112      *
113      * <p>The default implementation does nothing.</p>
114      */
115     override public void exitStmt(RuleTranslatorParser.StmtContext ctx) {
116         debug(TokenStreamRewriter) {
117             import std.stdio : writefln;
118             writefln("exitStmt ctx.start = %s, ctx.stop = %s", ctx.start, ctx.stop);
119         }
120     }
121 
122     /**
123      * {@inheritDoc}
124      *
125      * <p>The default implementation does nothing.</p>
126      */
127     override public void visitTerminal(TerminalNode node) {
128         debug (TokenStreamRewriter) {
129             writefln("\nvisitTerminal -> \"%s\", node.symbol -> %s, indentText = %s",
130                      node.getText,
131                      node.getSymbol,
132                      indentText
133                      );
134         }
135         switch (node.getSymbol.getType) {
136 
137         case RuleTranslatorParser.INDENT:
138             indentText++;
139             break;
140         case RuleTranslatorParser.DEDENT:
141             indentText--;
142             break;
143         case RuleTranslatorParser.NEWLINE:
144             nextOfNewline = true;
145             import std.array : join;
146             Result r;
147             r.indent = indentText;
148             break;
149         case RuleTranslatorParser.EOF:
150             debug(TokenStreamRewriter) {
151                 import std.stdio : writeln;
152                 writeln("\n+++ EOF +++");
153             }
154             break;
155         default:
156             debug(TokenStreamRewriter)
157                 writefln("\ndefault: node.getText.type = %s", node.getText.type);
158             if (nextOfNewline) {
159                 auto r = node.getText.get!(Result[]);
160                 r[0].indent = indentText;
161             }
162             nextOfNewline = false;
163             break;
164         }
165     }
166 
167 }
168 
169 @Tags("CustomerToken", "tt")
170 @("using result struct")
171 unittest {
172 
173     auto expected =
174         `rule Delay as DELAY de
175 base de . Phrases
176 if a :
177     "Information" "zu"
178     "Zug" ZugNr "nach" "Berlin"
179 `;
180     auto antlrInput = new ANTLRInputStream(File("unittest/complex/rule.rul", "r"));
181     auto lexer = new RuleTranslatorLexer(antlrInput);
182     auto factory = new ResultTokenFactory(antlrInput);
183     lexer.tokenFactory(factory);
184     auto ln = lexer.getGrammarFileName;
185     auto cts = new CommonTokenStream(lexer);
186     cts.fill;
187     auto parser = new RuleTranslatorParser(cts);
188     parser.tokenFactory(factory);
189     // Specify entry point
190     auto rootContext = parser.file_input;
191     parser.numberOfSyntaxErrors.should.be.equal(0);
192     auto extractor = new ResultListener(cts);
193     auto walker = new ParseTreeWalker;
194     walker.walk(extractor, rootContext);
195     auto r = extractor.rewriter.getText.get!(Result[]);
196 
197     import std.array : appender;
198     auto buf = appender!(string);
199     auto nextOfNewline = false;
200 
201     foreach (i, s ; r) {
202         auto t = cts.get(to!int(i));
203         if (t.getType == RuleTranslatorParser.NEWLINE) {
204             if (i)
205                 buf.put("\n");
206             nextOfNewline = true;
207             continue;
208         }
209         else if (t.getType == RuleTranslatorParser.INDENT ||
210                  t.getType == RuleTranslatorParser.DEDENT) {
211             continue;
212         }
213         else {
214             if (nextOfNewline) {
215                 while (s.indent--)
216                     buf.put("    ");
217                 buf.put(s.text);
218             }
219             else
220                 buf.put(" " ~ s.text);
221             nextOfNewline = false;
222         }
223     }
224     buf.data.should.equal(expected);
225 }