1 /*
2  * [The "BSD license"]
3  *  Copyright (c) 2010 Terence Parr
4  *  All rights reserved.
5  *
6  *  Redistribution and use in source and binary forms, with or without
7  *  modification, are permitted provided that the following conditions
8  *  are met:
9  *  1. Redistributions of source code must retain the above copyright
10  *      notice, this list of conditions and the following disclaimer.
11  *  2. Redistributions in binary form must reproduce the above copyright
12  *      notice, this list of conditions and the following disclaimer in the
13  *      documentation and/or other materials provided with the distribution.
14  *  3. The name of the author may not be used to endorse or promote products
15  *      derived from this software without specific prior written permission.
16  *
17  *  THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18  *  IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19  *  OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20  *  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21  *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22  *  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23  *  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24  *  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25  *  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26  *  THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  */
28 package org.antlr.test;
29 
30 import org.antlr.Tool;
31 import org.antlr.codegen.CodeGenerator;
32 import org.antlr.grammar.v3.ANTLRParser;
33 import org.antlr.grammar.v3.ActionTranslator;
34 import org.antlr.runtime.CommonToken;
35 import org.stringtemplate.v4.ST;
36 import org.stringtemplate.v4.STGroup;
37 import org.antlr.tool.ErrorManager;
38 import org.antlr.tool.Grammar;
39 import org.antlr.tool.GrammarSemanticsMessage;
40 import org.antlr.tool.Message;
41 import org.junit.Test;
42 
43 /** Test templates in actions; %... shorthands */
44 public class TestTemplates extends BaseTest {
45 	private static final String LINE_SEP = System.getProperty("line.separator");
46 
47 	@Test
testTemplateConstructor()48     public void testTemplateConstructor() throws Exception {
49 		String action = "x = %foo(name={$ID.text});";
50 		String expecting = "x = templateLib.getInstanceOf(\"foo\"," +
51 			"new STAttrMap().put(\"name\", (ID1!=null?ID1.getText():null)));";
52 
53 		ErrorQueue equeue = new ErrorQueue();
54 		ErrorManager.setErrorListener(equeue);
55 		Grammar g = new Grammar(
56 			"grammar t;\n" +
57 			"options {\n" +
58 			"    output=template;\n" +
59 			"}\n" +
60 			"\n" +
61 			"a : ID {"+action+"}\n" +
62 			"  ;\n" +
63 			"\n" +
64 			"ID : 'a';\n");
65 		Tool antlr = newTool();
66 		CodeGenerator generator = new CodeGenerator(antlr, g, "Java");
67 		g.setCodeGenerator(generator);
68 		generator.genRecognizer(); // forces load of templates
69 		ActionTranslator translator =
70 			new ActionTranslator(generator,
71 										"a",
72 										new CommonToken(ANTLRParser.ACTION,action),1);
73 		String rawTranslation =
74 			translator.translate();
75 		STGroup templates =
76 			new STGroup();
77 		ST actionST = new ST(templates, rawTranslation);
78 		String found = actionST.render();
79 
80 		assertNoErrors(equeue);
81 
82 		assertEquals(expecting, found);
83 	}
84 
85 	@Test
testTemplateConstructorNoArgs()86     public void testTemplateConstructorNoArgs() throws Exception {
87 		String action = "x = %foo();";
88 		String expecting = "x = templateLib.getInstanceOf(\"foo\");";
89 
90 		ErrorQueue equeue = new ErrorQueue();
91 		ErrorManager.setErrorListener(equeue);
92 		Grammar g = new Grammar(
93 			"grammar t;\n" +
94 			"options {\n" +
95 			"    output=template;\n" +
96 			"}\n" +
97 			"\n" +
98 			"a : ID {"+action+"}\n" +
99 			"  ;\n" +
100 			"\n" +
101 			"ID : 'a';\n");
102 		Tool antlr = newTool();
103 		CodeGenerator generator = new CodeGenerator(antlr, g, "Java");
104 		g.setCodeGenerator(generator);
105 		generator.genRecognizer(); // forces load of templates
106 		ActionTranslator translator =
107 			new ActionTranslator(generator,
108 										"a",
109 										new CommonToken(ANTLRParser.ACTION,action),1);
110 		String rawTranslation =
111 			translator.translate();
112 		STGroup templates =
113 			new STGroup();
114 		ST actionST = new ST(templates, rawTranslation);
115 		String found = actionST.render();
116 
117 		assertNoErrors(equeue);
118 
119 		assertEquals(expecting, found);
120 	}
121 
122 	@Test
testIndirectTemplateConstructor()123     public void testIndirectTemplateConstructor() throws Exception {
124 		String action = "x = %({\"foo\"})(name={$ID.text});";
125 		String expecting = "x = templateLib.getInstanceOf(\"foo\"," +
126 			"new STAttrMap().put(\"name\", (ID1!=null?ID1.getText():null)));";
127 
128 		ErrorQueue equeue = new ErrorQueue();
129 		ErrorManager.setErrorListener(equeue);
130 		Grammar g = new Grammar(
131 			"grammar t;\n" +
132 			"options {\n" +
133 			"    output=template;\n" +
134 			"}\n" +
135 			"\n" +
136 			"a : ID {"+action+"}\n" +
137 			"  ;\n" +
138 			"\n" +
139 			"ID : 'a';\n");
140 		Tool antlr = newTool();
141 		CodeGenerator generator = new CodeGenerator(antlr, g, "Java");
142 		g.setCodeGenerator(generator);
143 		generator.genRecognizer(); // forces load of templates
144 		ActionTranslator translator =
145 			new ActionTranslator(generator,
146 										"a",
147 										new CommonToken(ANTLRParser.ACTION,action),1);
148 		String rawTranslation =
149 			translator.translate();
150 		STGroup templates =
151 			new STGroup();
152 		ST actionST = new ST(templates, rawTranslation);
153 		String found = actionST.render();
154 
155 		assertNoErrors(equeue);
156 
157 		assertEquals(expecting, found);
158 	}
159 
testStringConstructor()160 	@Test public void testStringConstructor() throws Exception {
161 		String action = "x = %{$ID.text};";
162 		String expecting = "x = new StringTemplate(templateLib,(ID1!=null?ID1.getText():null));";
163 
164 		ErrorQueue equeue = new ErrorQueue();
165 		ErrorManager.setErrorListener(equeue);
166 		Grammar g = new Grammar(
167 			"grammar t;\n" +
168 			"options {\n" +
169 			"    output=template;\n" +
170 			"}\n" +
171 			"\n" +
172 			"a : ID {"+action+"}\n" +
173 			"  ;\n" +
174 			"\n" +
175 			"ID : 'a';\n");
176 		Tool antlr = newTool();
177 		CodeGenerator generator = new CodeGenerator(antlr, g, "Java");
178 		g.setCodeGenerator(generator);
179 		generator.genRecognizer(); // forces load of templates
180 		ActionTranslator translator = new ActionTranslator(generator,
181 																	 "a",
182 																	 new CommonToken(ANTLRParser.ACTION,action),1);
183 		String rawTranslation =
184 			translator.translate();
185 		STGroup templates =
186 			new STGroup();
187 		ST actionST = new ST(templates, rawTranslation);
188 		String found = actionST.render();
189 
190 		assertNoErrors(equeue);
191 
192 		assertEquals(expecting, found);
193 	}
194 
testSetAttr()195 	@Test public void testSetAttr() throws Exception {
196 		String action = "%x.y = z;";
197 		String expecting = "(x).setAttribute(\"y\", z);";
198 
199 		ErrorQueue equeue = new ErrorQueue();
200 		ErrorManager.setErrorListener(equeue);
201 		Grammar g = new Grammar(
202 			"grammar t;\n" +
203 			"options {\n" +
204 			"    output=template;\n" +
205 			"}\n" +
206 			"\n" +
207 			"a : ID {"+action+"}\n" +
208 			"  ;\n" +
209 			"\n" +
210 			"ID : 'a';\n");
211 		Tool antlr = newTool();
212 		CodeGenerator generator = new CodeGenerator(antlr, g, "Java");
213 		g.setCodeGenerator(generator);
214 		generator.genRecognizer(); // forces load of templates
215 		ActionTranslator translator =
216 			new ActionTranslator(generator,
217 										"a",
218 										new CommonToken(ANTLRParser.ACTION,action),1);
219 		String rawTranslation =
220 			translator.translate();
221 		STGroup templates =
222 			new STGroup();
223 		ST actionST = new ST(templates, rawTranslation);
224 		String found = actionST.render();
225 
226 		assertNoErrors(equeue);
227 
228 		assertEquals(expecting, found);
229 	}
230 
testSetAttrOfExpr()231 	@Test public void testSetAttrOfExpr() throws Exception {
232 		String action = "%{foo($ID.text).getST()}.y = z;";
233 		String expecting = "(foo((ID1!=null?ID1.getText():null)).getST()).setAttribute(\"y\", z);";
234 
235 		ErrorQueue equeue = new ErrorQueue();
236 		ErrorManager.setErrorListener(equeue);
237 		Grammar g = new Grammar(
238 			"grammar t;\n" +
239 			"options {\n" +
240 			"    output=template;\n" +
241 			"}\n" +
242 			"\n" +
243 			"a : ID {"+action+"}\n" +
244 			"  ;\n" +
245 			"\n" +
246 			"ID : 'a';\n");
247 		Tool antlr = newTool();
248 		CodeGenerator generator = new CodeGenerator(antlr, g, "Java");
249 		g.setCodeGenerator(generator);
250 		generator.genRecognizer(); // forces load of templates
251 		ActionTranslator translator = new ActionTranslator(generator,
252 																	 "a",
253 																	 new CommonToken(ANTLRParser.ACTION,action),1);
254 		String rawTranslation =
255 			translator.translate();
256 		STGroup templates =
257 			new STGroup();
258 		ST actionST = new ST(templates, rawTranslation);
259 		String found = actionST.render();
260 
261 		assertNoErrors(equeue);
262 
263 		assertEquals(expecting, found);
264 	}
265 
testSetAttrOfExprInMembers()266 	@Test public void testSetAttrOfExprInMembers() throws Exception {
267 		ErrorQueue equeue = new ErrorQueue();
268 		ErrorManager.setErrorListener(equeue);
269 		Grammar g = new Grammar(
270 			"grammar t;\n" +
271 			"options {\n" +
272 			"    output=template;\n" +
273 			"}\n" +
274 			"@members {\n" +
275 			"%code.instr = o;" + // must not get null ptr!
276 			"}\n" +
277 			"a : ID\n" +
278 			"  ;\n" +
279 			"\n" +
280 			"ID : 'a';\n");
281 		Tool antlr = newTool();
282 		CodeGenerator generator = new CodeGenerator(antlr, g, "Java");
283 		g.setCodeGenerator(generator);
284 		generator.genRecognizer(); // forces load of templates
285 
286 		assertNoErrors(equeue);
287 	}
288 
testCannotHaveSpaceBeforeDot()289 	@Test public void testCannotHaveSpaceBeforeDot() throws Exception {
290 		String action = "%x .y = z;";
291 		String expecting = null;
292 
293 		ErrorQueue equeue = new ErrorQueue();
294 		ErrorManager.setErrorListener(equeue);
295 		Grammar g = new Grammar(
296 			"grammar t;\n" +
297 			"options {\n" +
298 			"    output=template;\n" +
299 			"}\n" +
300 			"\n" +
301 			"a : ID {"+action+"}\n" +
302 			"  ;\n" +
303 			"\n" +
304 			"ID : 'a';\n");
305 		Tool antlr = newTool();
306 		CodeGenerator generator = new CodeGenerator(antlr, g, "Java");
307 		g.setCodeGenerator(generator);
308 		generator.genRecognizer(); // forces load of templates
309 
310 		int expectedMsgID = ErrorManager.MSG_INVALID_TEMPLATE_ACTION;
311 		Object expectedArg = "%x";
312 		GrammarSemanticsMessage expectedMessage =
313 			new GrammarSemanticsMessage(expectedMsgID, g, null, expectedArg);
314 		checkError(equeue, expectedMessage);
315 	}
316 
testCannotHaveSpaceAfterDot()317 	@Test public void testCannotHaveSpaceAfterDot() throws Exception {
318 		String action = "%x. y = z;";
319 		String expecting = null;
320 
321 		ErrorQueue equeue = new ErrorQueue();
322 		ErrorManager.setErrorListener(equeue);
323 		Grammar g = new Grammar(
324 			"grammar t;\n" +
325 			"options {\n" +
326 			"    output=template;\n" +
327 			"}\n" +
328 			"\n" +
329 			"a : ID {"+action+"}\n" +
330 			"  ;\n" +
331 			"\n" +
332 			"ID : 'a';\n");
333 		Tool antlr = newTool();
334 		CodeGenerator generator = new CodeGenerator(antlr, g, "Java");
335 		g.setCodeGenerator(generator);
336 		generator.genRecognizer(); // forces load of templates
337 
338 		int expectedMsgID = ErrorManager.MSG_INVALID_TEMPLATE_ACTION;
339 		Object expectedArg = "%x.";
340 		GrammarSemanticsMessage expectedMessage =
341 			new GrammarSemanticsMessage(expectedMsgID, g, null, expectedArg);
342 		checkError(equeue, expectedMessage);
343 	}
344 
checkError(ErrorQueue equeue, GrammarSemanticsMessage expectedMessage)345 	protected void checkError(ErrorQueue equeue,
346 							  GrammarSemanticsMessage expectedMessage)
347 		throws Exception
348 	{
349 		/*
350 		System.out.println(equeue.infos);
351 		System.out.println(equeue.warnings);
352 		System.out.println(equeue.errors);
353 		*/
354 		Message foundMsg = null;
355 		for (int i = 0; i < equeue.errors.size(); i++) {
356 			Message m = (Message)equeue.errors.get(i);
357 			if (m.msgID==expectedMessage.msgID ) {
358 				foundMsg = m;
359 			}
360 		}
361 		assertTrue("no error; "+expectedMessage.msgID+" expected", equeue.errors.size()>0);
362 		assertTrue("too many errors; "+equeue.errors, equeue.errors.size()<=1);
363 		assertTrue("couldn't find expected error: "+expectedMessage.msgID, foundMsg!=null);
364 		assertTrue("error is not a GrammarSemanticsMessage",
365 				   foundMsg instanceof GrammarSemanticsMessage);
366 		assertEquals(expectedMessage.arg, foundMsg.arg);
367 		assertEquals(expectedMessage.arg2, foundMsg.arg2);
368 	}
369 
370 	// S U P P O R T
assertNoErrors(ErrorQueue equeue)371 	private void assertNoErrors(ErrorQueue equeue) {
372 		assertTrue("unexpected errors: "+equeue, equeue.errors.size()==0);
373 	}
374 }
375