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.tool.*; 32 import org.junit.Test; 33 34 import java.io.File; 35 36 public class TestCompositeGrammars extends BaseTest { 37 protected boolean debug = false; 38 39 @Test public void testWildcardStillWorks() throws Exception { 40 ErrorQueue equeue = new ErrorQueue(); 41 ErrorManager.setErrorListener(equeue); 42 String grammar = 43 "parser grammar S;\n" + 44 "a : B . C ;\n"; // not qualified ID 45 Grammar g = new Grammar(grammar); 46 assertEquals("unexpected errors: "+equeue, 0, equeue.errors.size()); 47 } 48 49 @Test public void testDelegatorInvokesDelegateRule() throws Exception { 50 String slave = 51 "parser grammar S;\n" + 52 "a : B {System.out.println(\"S.a\");} ;\n"; 53 mkdir(tmpdir); 54 writeFile(tmpdir, "S.g", slave); 55 String master = 56 "grammar M;\n" + 57 "import S;\n" + 58 "s : a ;\n" + 59 "B : 'b' ;" + // defines B from inherited token space 60 "WS : (' '|'\\n') {skip();} ;\n" ; 61 String found = execParser("M.g", master, "MParser", "MLexer", 62 "s", "b", debug); 63 assertEquals("S.a\n", found); 64 } 65 66 @Test public void testDelegatorInvokesDelegateRuleWithArgs() throws Exception { 67 // must generate something like: 68 // public int a(int x) throws RecognitionException { return gS.a(x); } 69 // in M. 70 String slave = 71 "parser grammar S;\n" + 72 "a[int x] returns [int y] : B {System.out.print(\"S.a\"); $y=1000;} ;\n"; 73 mkdir(tmpdir); 74 writeFile(tmpdir, "S.g", slave); 75 String master = 76 "grammar M;\n" + 77 "import S;\n" + 78 "s : label=a[3] {System.out.println($label.y);} ;\n" + 79 "B : 'b' ;" + // defines B from inherited token space 80 "WS : (' '|'\\n') {skip();} ;\n" ; 81 String found = execParser("M.g", master, "MParser", "MLexer", 82 "s", "b", debug); 83 assertEquals("S.a1000\n", found); 84 } 85 86 @Test public void testDelegatorInvokesDelegateRuleWithReturnStruct() throws Exception { 87 // must generate something like: 88 // public int a(int x) throws RecognitionException { return gS.a(x); } 89 // in M. 90 String slave = 91 "parser grammar S;\n" + 92 "a : B {System.out.print(\"S.a\");} ;\n"; 93 mkdir(tmpdir); 94 writeFile(tmpdir, "S.g", slave); 95 String master = 96 "grammar M;\n" + 97 "import S;\n" + 98 "s : a {System.out.println($a.text);} ;\n" + 99 "B : 'b' ;" + // defines B from inherited token space 100 "WS : (' '|'\\n') {skip();} ;\n" ; 101 String found = execParser("M.g", master, "MParser", "MLexer", 102 "s", "b", debug); 103 assertEquals("S.ab\n", found); 104 } 105 106 @Test public void testDelegatorAccessesDelegateMembers() throws Exception { 107 String slave = 108 "parser grammar S;\n" + 109 "@members {\n" + 110 " public void foo() {System.out.println(\"foo\");}\n" + 111 "}\n" + 112 "a : B ;\n"; 113 mkdir(tmpdir); 114 writeFile(tmpdir, "S.g", slave); 115 String master = 116 "grammar M;\n" + // uses no rules from the import 117 "import S;\n" + 118 "s : 'b' {gS.foo();} ;\n" + // gS is import pointer 119 "WS : (' '|'\\n') {skip();} ;\n" ; 120 String found = execParser("M.g", master, "MParser", "MLexer", 121 "s", "b", debug); 122 assertEquals("foo\n", found); 123 } 124 125 @Test public void testDelegatorInvokesFirstVersionOfDelegateRule() throws Exception { 126 String slave = 127 "parser grammar S;\n" + 128 "a : b {System.out.println(\"S.a\");} ;\n" + 129 "b : B ;\n" ; 130 mkdir(tmpdir); 131 writeFile(tmpdir, "S.g", slave); 132 String slave2 = 133 "parser grammar T;\n" + 134 "a : B {System.out.println(\"T.a\");} ;\n"; // hidden by S.a 135 writeFile(tmpdir, "T.g", slave2); 136 String master = 137 "grammar M;\n" + 138 "import S,T;\n" + 139 "s : a ;\n" + 140 "B : 'b' ;\n" + 141 "WS : (' '|'\\n') {skip();} ;\n" ; 142 String found = execParser("M.g", master, "MParser", "MLexer", 143 "s", "b", debug); 144 assertEquals("S.a\n", found); 145 } 146 147 @Test public void testDelegatesSeeSameTokenType() throws Exception { 148 String slave = 149 "parser grammar S;\n" + // A, B, C token type order 150 "tokens { A; B; C; }\n" + 151 "x : A {System.out.println(\"S.x\");} ;\n"; 152 mkdir(tmpdir); 153 writeFile(tmpdir, "S.g", slave); 154 String slave2 = 155 "parser grammar T;\n" + 156 "tokens { C; B; A; }\n" + // reverse order 157 "y : A {System.out.println(\"T.y\");} ;\n"; 158 mkdir(tmpdir); 159 writeFile(tmpdir, "T.g", slave2); 160 // The lexer will create rules to match letters a, b, c. 161 // The associated token types A, B, C must have the same value 162 // and all import'd parsers. Since ANTLR regenerates all imports 163 // for use with the delegator M, it can generate the same token type 164 // mapping in each parser: 165 // public static final int C=6; 166 // public static final int EOF=-1; 167 // public static final int B=5; 168 // public static final int WS=7; 169 // public static final int A=4; 170 171 String master = 172 "grammar M;\n" + 173 "import S,T;\n" + 174 "s : x y ;\n" + // matches AA, which should be "aa" 175 "B : 'b' ;\n" + // another order: B, A, C 176 "A : 'a' ;\n" + 177 "C : 'c' ;\n" + 178 "WS : (' '|'\\n') {skip();} ;\n" ; 179 String found = execParser("M.g", master, "MParser", "MLexer", 180 "s", "aa", debug); 181 assertEquals("S.x\n" + 182 "T.y\n", found); 183 } 184 185 @Test public void testDelegatesSeeSameTokenType2() throws Exception { 186 ErrorQueue equeue = new ErrorQueue(); 187 ErrorManager.setErrorListener(equeue); 188 String slave = 189 "parser grammar S;\n" + // A, B, C token type order 190 "tokens { A; B; C; }\n" + 191 "x : A {System.out.println(\"S.x\");} ;\n"; 192 mkdir(tmpdir); 193 writeFile(tmpdir, "S.g", slave); 194 String slave2 = 195 "parser grammar T;\n" + 196 "tokens { C; B; A; }\n" + // reverse order 197 "y : A {System.out.println(\"T.y\");} ;\n"; 198 mkdir(tmpdir); 199 writeFile(tmpdir, "T.g", slave2); 200 201 String master = 202 "grammar M;\n" + 203 "import S,T;\n" + 204 "s : x y ;\n" + // matches AA, which should be "aa" 205 "B : 'b' ;\n" + // another order: B, A, C 206 "A : 'a' ;\n" + 207 "C : 'c' ;\n" + 208 "WS : (' '|'\\n') {skip();} ;\n" ; 209 writeFile(tmpdir, "M.g", master); 210 Tool antlr = newTool(new String[] {"-lib", tmpdir}); 211 CompositeGrammar composite = new CompositeGrammar(); 212 Grammar g = new Grammar(antlr,tmpdir+"/M.g",composite); 213 composite.setDelegationRoot(g); 214 g.parseAndBuildAST(); 215 g.composite.assignTokenTypes(); 216 217 String expectedTokenIDToTypeMap = "[A=4, B=5, C=6, WS=7]"; 218 String expectedStringLiteralToTypeMap = "{}"; 219 String expectedTypeToTokenList = "[A, B, C, WS]"; 220 221 assertEquals(expectedTokenIDToTypeMap, 222 realElements(g.composite.tokenIDToTypeMap).toString()); 223 assertEquals(expectedStringLiteralToTypeMap, g.composite.stringLiteralToTypeMap.toString()); 224 assertEquals(expectedTypeToTokenList, 225 realElements(g.composite.typeToTokenList).toString()); 226 227 assertEquals("unexpected errors: "+equeue, 0, equeue.errors.size()); 228 } 229 230 @Test public void testCombinedImportsCombined() throws Exception { 231 // for now, we don't allow combined to import combined 232 ErrorQueue equeue = new ErrorQueue(); 233 ErrorManager.setErrorListener(equeue); 234 String slave = 235 "grammar S;\n" + // A, B, C token type order 236 "tokens { A; B; C; }\n" + 237 "x : 'x' INT {System.out.println(\"S.x\");} ;\n" + 238 "INT : '0'..'9'+ ;\n" + 239 "WS : (' '|'\\n') {skip();} ;\n"; 240 mkdir(tmpdir); 241 writeFile(tmpdir, "S.g", slave); 242 243 String master = 244 "grammar M;\n" + 245 "import S;\n" + 246 "s : x INT ;\n"; 247 writeFile(tmpdir, "M.g", master); 248 Tool antlr = newTool(new String[] {"-lib", tmpdir}); 249 CompositeGrammar composite = new CompositeGrammar(); 250 Grammar g = new Grammar(antlr,tmpdir+"/M.g",composite); 251 composite.setDelegationRoot(g); 252 g.parseAndBuildAST(); 253 g.composite.assignTokenTypes(); 254 255 assertEquals("unexpected errors: "+equeue, 1, equeue.errors.size()); 256 String expectedError = "error(161): "+tmpdir.toString().replaceFirst("\\-[0-9]+","")+"/M.g:2:8: combined grammar M cannot import combined grammar S"; 257 assertEquals("unexpected errors: "+equeue, expectedError, equeue.errors.get(0).toString().replaceFirst("\\-[0-9]+","")); 258 } 259 260 @Test public void testSameStringTwoNames() throws Exception { 261 ErrorQueue equeue = new ErrorQueue(); 262 ErrorManager.setErrorListener(equeue); 263 String slave = 264 "parser grammar S;\n" + 265 "tokens { A='a'; }\n" + 266 "x : A {System.out.println(\"S.x\");} ;\n"; 267 mkdir(tmpdir); 268 writeFile(tmpdir, "S.g", slave); 269 String slave2 = 270 "parser grammar T;\n" + 271 "tokens { X='a'; }\n" + 272 "y : X {System.out.println(\"T.y\");} ;\n"; 273 mkdir(tmpdir); 274 writeFile(tmpdir, "T.g", slave2); 275 276 String master = 277 "grammar M;\n" + 278 "import S,T;\n" + 279 "s : x y ;\n" + 280 "WS : (' '|'\\n') {skip();} ;\n" ; 281 writeFile(tmpdir, "M.g", master); 282 Tool antlr = newTool(new String[] {"-lib", tmpdir}); 283 CompositeGrammar composite = new CompositeGrammar(); 284 Grammar g = new Grammar(antlr,tmpdir+"/M.g",composite); 285 composite.setDelegationRoot(g); 286 g.parseAndBuildAST(); 287 g.composite.assignTokenTypes(); 288 289 String expectedTokenIDToTypeMap = "[A=4, WS=5, X=6]"; 290 String expectedStringLiteralToTypeMap = "{'a'=4}"; 291 String expectedTypeToTokenList = "[A, WS, X]"; 292 293 assertEquals(expectedTokenIDToTypeMap, 294 realElements(g.composite.tokenIDToTypeMap).toString()); 295 assertEquals(expectedStringLiteralToTypeMap, g.composite.stringLiteralToTypeMap.toString()); 296 assertEquals(expectedTypeToTokenList, 297 realElements(g.composite.typeToTokenList).toString()); 298 299 Object expectedArg = "X='a'"; 300 Object expectedArg2 = "A"; 301 int expectedMsgID = ErrorManager.MSG_TOKEN_ALIAS_CONFLICT; 302 GrammarSemanticsMessage expectedMessage = 303 new GrammarSemanticsMessage(expectedMsgID, g, null, expectedArg, expectedArg2); 304 checkGrammarSemanticsError(equeue, expectedMessage); 305 306 assertEquals("unexpected errors: "+equeue, 1, equeue.errors.size()); 307 308 String expectedError = 309 "error(158): T.g:2:10: cannot alias X='a'; string already assigned to A"; 310 assertEquals(expectedError, equeue.errors.get(0).toString()); 311 } 312 313 @Test public void testSameNameTwoStrings() throws Exception { 314 ErrorQueue equeue = new ErrorQueue(); 315 ErrorManager.setErrorListener(equeue); 316 String slave = 317 "parser grammar S;\n" + 318 "tokens { A='a'; }\n" + 319 "x : A {System.out.println(\"S.x\");} ;\n"; 320 mkdir(tmpdir); 321 writeFile(tmpdir, "S.g", slave); 322 String slave2 = 323 "parser grammar T;\n" + 324 "tokens { A='x'; }\n" + 325 "y : A {System.out.println(\"T.y\");} ;\n"; 326 327 writeFile(tmpdir, "T.g", slave2); 328 329 String master = 330 "grammar M;\n" + 331 "import S,T;\n" + 332 "s : x y ;\n" + 333 "WS : (' '|'\\n') {skip();} ;\n" ; 334 writeFile(tmpdir, "M.g", master); 335 Tool antlr = newTool(new String[] {"-lib", tmpdir}); 336 CompositeGrammar composite = new CompositeGrammar(); 337 Grammar g = new Grammar(antlr,tmpdir+"/M.g",composite); 338 composite.setDelegationRoot(g); 339 g.parseAndBuildAST(); 340 g.composite.assignTokenTypes(); 341 342 String expectedTokenIDToTypeMap = "[A=4, T__6=6, WS=5]"; 343 String expectedStringLiteralToTypeMap = "{'a'=4, 'x'=6}"; 344 String expectedTypeToTokenList = "[A, WS, T__6]"; 345 346 assertEquals(expectedTokenIDToTypeMap, 347 realElements(g.composite.tokenIDToTypeMap).toString()); 348 assertEquals(expectedStringLiteralToTypeMap, sortMapToString(g.composite.stringLiteralToTypeMap)); 349 assertEquals(expectedTypeToTokenList, 350 realElements(g.composite.typeToTokenList).toString()); 351 352 Object expectedArg = "A='x'"; 353 Object expectedArg2 = "'a'"; 354 int expectedMsgID = ErrorManager.MSG_TOKEN_ALIAS_REASSIGNMENT; 355 GrammarSemanticsMessage expectedMessage = 356 new GrammarSemanticsMessage(expectedMsgID, g, null, expectedArg, expectedArg2); 357 checkGrammarSemanticsError(equeue, expectedMessage); 358 359 assertEquals("unexpected errors: "+equeue, 1, equeue.errors.size()); 360 361 String expectedError = 362 "error(159): T.g:2:10: cannot alias A='x'; token name already assigned to 'a'"; 363 assertEquals(expectedError, equeue.errors.get(0).toString()); 364 } 365 366 @Test public void testImportedTokenVocabIgnoredWithWarning() throws Exception { 367 ErrorQueue equeue = new ErrorQueue(); 368 ErrorManager.setErrorListener(equeue); 369 String slave = 370 "parser grammar S;\n" + 371 "options {tokenVocab=whatever;}\n" + 372 "tokens { A='a'; }\n" + 373 "x : A {System.out.println(\"S.x\");} ;\n"; 374 mkdir(tmpdir); 375 writeFile(tmpdir, "S.g", slave); 376 377 String master = 378 "grammar M;\n" + 379 "import S;\n" + 380 "s : x ;\n" + 381 "WS : (' '|'\\n') {skip();} ;\n" ; 382 writeFile(tmpdir, "M.g", master); 383 Tool antlr = newTool(new String[] {"-lib", tmpdir}); 384 CompositeGrammar composite = new CompositeGrammar(); 385 Grammar g = new Grammar(antlr,tmpdir+"/M.g",composite); 386 composite.setDelegationRoot(g); 387 g.parseAndBuildAST(); 388 g.composite.assignTokenTypes(); 389 390 Object expectedArg = "S"; 391 int expectedMsgID = ErrorManager.MSG_TOKEN_VOCAB_IN_DELEGATE; 392 GrammarSemanticsMessage expectedMessage = 393 new GrammarSemanticsMessage(expectedMsgID, g, null, expectedArg); 394 checkGrammarSemanticsWarning(equeue, expectedMessage); 395 396 assertEquals("unexpected errors: "+equeue, 0, equeue.errors.size()); 397 assertEquals("unexpected errors: "+equeue, 1, equeue.warnings.size()); 398 399 String expectedError = 400 "warning(160): S.g:2:10: tokenVocab option ignored in imported grammar S"; 401 assertEquals(expectedError, equeue.warnings.get(0).toString()); 402 } 403 404 @Test public void testImportedTokenVocabWorksInRoot() throws Exception { 405 ErrorQueue equeue = new ErrorQueue(); 406 ErrorManager.setErrorListener(equeue); 407 String slave = 408 "parser grammar S;\n" + 409 "tokens { A='a'; }\n" + 410 "x : A {System.out.println(\"S.x\");} ;\n"; 411 mkdir(tmpdir); 412 writeFile(tmpdir, "S.g", slave); 413 414 String tokens = 415 "A=99\n"; 416 writeFile(tmpdir, "Test.tokens", tokens); 417 418 String master = 419 "grammar M;\n" + 420 "options {tokenVocab=Test;}\n" + 421 "import S;\n" + 422 "s : x ;\n" + 423 "WS : (' '|'\\n') {skip();} ;\n" ; 424 writeFile(tmpdir, "M.g", master); 425 Tool antlr = newTool(new String[] {"-lib", tmpdir}); 426 CompositeGrammar composite = new CompositeGrammar(); 427 Grammar g = new Grammar(antlr,tmpdir+"/M.g",composite); 428 composite.setDelegationRoot(g); 429 g.parseAndBuildAST(); 430 g.composite.assignTokenTypes(); 431 432 String expectedTokenIDToTypeMap = "[A=99, WS=101]"; 433 String expectedStringLiteralToTypeMap = "{'a'=100}"; 434 String expectedTypeToTokenList = "[A, 'a', WS]"; 435 436 assertEquals(expectedTokenIDToTypeMap, 437 realElements(g.composite.tokenIDToTypeMap).toString()); 438 assertEquals(expectedStringLiteralToTypeMap, g.composite.stringLiteralToTypeMap.toString()); 439 assertEquals(expectedTypeToTokenList, 440 realElements(g.composite.typeToTokenList).toString()); 441 442 assertEquals("unexpected errors: "+equeue, 0, equeue.errors.size()); 443 } 444 445 @Test public void testSyntaxErrorsInImportsNotThrownOut() throws Exception { 446 ErrorQueue equeue = new ErrorQueue(); 447 ErrorManager.setErrorListener(equeue); 448 String slave = 449 "parser grammar S;\n" + 450 "options {toke\n"; 451 mkdir(tmpdir); 452 writeFile(tmpdir, "S.g", slave); 453 454 String master = 455 "grammar M;\n" + 456 "import S;\n" + 457 "s : x ;\n" + 458 "WS : (' '|'\\n') {skip();} ;\n" ; 459 writeFile(tmpdir, "M.g", master); 460 Tool antlr = newTool(new String[] {"-lib", tmpdir}); 461 CompositeGrammar composite = new CompositeGrammar(); 462 Grammar g = new Grammar(antlr,tmpdir+"/M.g",composite); 463 composite.setDelegationRoot(g); 464 g.parseAndBuildAST(); 465 g.composite.assignTokenTypes(); 466 467 // whole bunch of errors from bad S.g file 468 assertEquals("unexpected errors: "+equeue, 5, equeue.errors.size()); 469 } 470 471 @Test public void testSyntaxErrorsInImportsNotThrownOut2() throws Exception { 472 ErrorQueue equeue = new ErrorQueue(); 473 ErrorManager.setErrorListener(equeue); 474 String slave = 475 "parser grammar S;\n" + 476 ": A {System.out.println(\"S.x\");} ;\n"; 477 mkdir(tmpdir); 478 writeFile(tmpdir, "S.g", slave); 479 480 String master = 481 "grammar M;\n" + 482 "import S;\n" + 483 "s : x ;\n" + 484 "WS : (' '|'\\n') {skip();} ;\n" ; 485 writeFile(tmpdir, "M.g", master); 486 Tool antlr = newTool(new String[] {"-lib", tmpdir}); 487 CompositeGrammar composite = new CompositeGrammar(); 488 Grammar g = new Grammar(antlr,tmpdir+"/M.g",composite); 489 composite.setDelegationRoot(g); 490 g.parseAndBuildAST(); 491 g.composite.assignTokenTypes(); 492 493 // whole bunch of errors from bad S.g file 494 assertEquals("unexpected errors: "+equeue, 3, equeue.errors.size()); 495 } 496 497 @Test public void testDelegatorRuleOverridesDelegate() throws Exception { 498 String slave = 499 "parser grammar S;\n" + 500 "a : b {System.out.println(\"S.a\");} ;\n" + 501 "b : B ;\n" ; 502 mkdir(tmpdir); 503 writeFile(tmpdir, "S.g", slave); 504 String master = 505 "grammar M;\n" + 506 "import S;\n" + 507 "b : 'b'|'c' ;\n" + 508 "WS : (' '|'\\n') {skip();} ;\n" ; 509 String found = execParser("M.g", master, "MParser", "MLexer", 510 "a", "c", debug); 511 assertEquals("S.a\n", found); 512 } 513 514 @Test public void testDelegatorRuleOverridesLookaheadInDelegate() throws Exception { 515 String slave = 516 "parser grammar JavaDecl;\n" + 517 "type : 'int' ;\n" + 518 "decl : type ID ';'\n" + 519 " | type ID init ';' {System.out.println(\"JavaDecl: \"+$decl.text);}\n" + 520 " ;\n" + 521 "init : '=' INT ;\n" ; 522 mkdir(tmpdir); 523 writeFile(tmpdir, "JavaDecl.g", slave); 524 String master = 525 "grammar Java;\n" + 526 "import JavaDecl;\n" + 527 "prog : decl ;\n" + 528 "type : 'int' | 'float' ;\n" + 529 "\n" + 530 "ID : 'a'..'z'+ ;\n" + 531 "INT : '0'..'9'+ ;\n" + 532 "WS : (' '|'\\n') {skip();} ;\n" ; 533 // for float to work in decl, type must be overridden 534 String found = execParser("Java.g", master, "JavaParser", "JavaLexer", 535 "prog", "float x = 3;", debug); 536 assertEquals("JavaDecl: floatx=3;\n", found); 537 } 538 539 @Test public void testDelegatorRuleOverridesDelegates() throws Exception { 540 String slave = 541 "parser grammar S;\n" + 542 "a : b {System.out.println(\"S.a\");} ;\n" + 543 "b : B ;\n" ; 544 mkdir(tmpdir); 545 writeFile(tmpdir, "S.g", slave); 546 547 String slave2 = 548 "parser grammar T;\n" + 549 "tokens { A='x'; }\n" + 550 "b : B {System.out.println(\"T.b\");} ;\n"; 551 writeFile(tmpdir, "T.g", slave2); 552 553 String master = 554 "grammar M;\n" + 555 "import S, T;\n" + 556 "b : 'b'|'c' {System.out.println(\"M.b\");}|B|A ;\n" + 557 "WS : (' '|'\\n') {skip();} ;\n" ; 558 String found = execParser("M.g", master, "MParser", "MLexer", 559 "a", "c", debug); 560 assertEquals("M.b\n" + 561 "S.a\n", found); 562 } 563 564 // LEXER INHERITANCE 565 566 @Test public void testLexerDelegatorInvokesDelegateRule() throws Exception { 567 String slave = 568 "lexer grammar S;\n" + 569 "A : 'a' {System.out.println(\"S.A\");} ;\n" + 570 "C : 'c' ;\n"; 571 mkdir(tmpdir); 572 writeFile(tmpdir, "S.g", slave); 573 String master = 574 "lexer grammar M;\n" + 575 "import S;\n" + 576 "B : 'b' ;\n" + 577 "WS : (' '|'\\n') {skip();} ;\n" ; 578 String found = execLexer("M.g", master, "M", "abc", debug); 579 assertEquals("S.A\nabc\n", found); 580 } 581 582 @Test public void testLexerDelegatorRuleOverridesDelegate() throws Exception { 583 String slave = 584 "lexer grammar S;\n" + 585 "A : 'a' {System.out.println(\"S.A\");} ;\n" + 586 "B : 'b' {System.out.println(\"S.B\");} ;\n"; 587 mkdir(tmpdir); 588 writeFile(tmpdir, "S.g", slave); 589 String master = 590 "lexer grammar M;\n" + 591 "import S;\n" + 592 "A : 'a' B {System.out.println(\"M.A\");} ;\n" + 593 "WS : (' '|'\\n') {skip();} ;\n" ; 594 String found = execLexer("M.g", master, "M", "ab", debug); 595 assertEquals("S.B\n" + 596 "M.A\n" + 597 "ab\n", found); 598 } 599 600 @Test public void testLexerDelegatorRuleOverridesDelegateLeavingNoRules() throws Exception { 601 // M.Tokens has nothing to predict tokens from S. Should 602 // not include S.Tokens alt in this case? 603 String slave = 604 "lexer grammar S;\n" + 605 "A : 'a' {System.out.println(\"S.A\");} ;\n"; 606 mkdir(tmpdir); 607 writeFile(tmpdir, "S.g", slave); 608 String master = 609 "lexer grammar M;\n" + 610 "import S;\n" + 611 "A : 'a' {System.out.println(\"M.A\");} ;\n" + 612 "WS : (' '|'\\n') {skip();} ;\n" ; 613 writeFile(tmpdir, "/M.g", master); 614 615 ErrorQueue equeue = new ErrorQueue(); 616 ErrorManager.setErrorListener(equeue); 617 Tool antlr = newTool(new String[] {"-lib", tmpdir}); 618 CompositeGrammar composite = new CompositeGrammar(); 619 Grammar g = new Grammar(antlr,tmpdir+"/M.g",composite); 620 composite.setDelegationRoot(g); 621 g.parseAndBuildAST(); 622 composite.assignTokenTypes(); 623 composite.defineGrammarSymbols(); 624 composite.createNFAs(); 625 g.createLookaheadDFAs(false); 626 627 // predict only alts from M not S 628 String expectingDFA = 629 ".s0-'a'->.s1\n" + 630 ".s0-{'\\n', ' '}->:s3=>2\n" + 631 ".s1-<EOT>->:s2=>1\n"; 632 org.antlr.analysis.DFA dfa = g.getLookaheadDFA(1); 633 FASerializer serializer = new FASerializer(g); 634 String result = serializer.serialize(dfa.startState); 635 assertEquals(expectingDFA, result); 636 637 // must not be a "unreachable alt: Tokens" error 638 assertEquals("unexpected errors: "+equeue, 0, equeue.errors.size()); 639 } 640 641 @Test public void testInvalidImportMechanism() throws Exception { 642 // M.Tokens has nothing to predict tokens from S. Should 643 // not include S.Tokens alt in this case? 644 String slave = 645 "lexer grammar S;\n" + 646 "A : 'a' {System.out.println(\"S.A\");} ;\n"; 647 mkdir(tmpdir); 648 writeFile(tmpdir, "S.g", slave); 649 String master = 650 "tree grammar M;\n" + 651 "import S;\n" + 652 "a : A ;"; 653 writeFile(tmpdir, "/M.g", master); 654 655 ErrorQueue equeue = new ErrorQueue(); 656 ErrorManager.setErrorListener(equeue); 657 Tool antlr = newTool(new String[] {"-lib", tmpdir}); 658 CompositeGrammar composite = new CompositeGrammar(); 659 Grammar g = new Grammar(antlr,tmpdir+"/M.g",composite); 660 composite.setDelegationRoot(g); 661 g.parseAndBuildAST(); 662 663 assertEquals("unexpected errors: "+equeue, 1, equeue.errors.size()); 664 assertEquals("unexpected errors: "+equeue, 0, equeue.warnings.size()); 665 666 String expectedError = 667 "error(161): "+tmpdir.toString().replaceFirst("\\-[0-9]+","")+"/M.g:2:8: tree grammar M cannot import lexer grammar S"; 668 assertEquals(expectedError, equeue.errors.get(0).toString().replaceFirst("\\-[0-9]+","")); 669 } 670 671 @Test public void testSyntacticPredicateRulesAreNotInherited() throws Exception { 672 // if this compiles, it means that synpred1_S is defined in S.java 673 // but not MParser.java. MParser has its own synpred1_M which must 674 // be separate to compile. 675 String slave = 676 "parser grammar S;\n" + 677 "a : 'a' {System.out.println(\"S.a1\");}\n" + 678 " | 'a' {System.out.println(\"S.a2\");}\n" + 679 " ;\n" + 680 "b : 'x' | 'y' {;} ;\n"; // preds generated but not need in DFA here 681 mkdir(tmpdir); 682 writeFile(tmpdir, "S.g", slave); 683 String master = 684 "grammar M;\n" + 685 "options {backtrack=true;}\n" + 686 "import S;\n" + 687 "start : a b ;\n" + 688 "nonsense : 'q' | 'q' {;} ;" + // forces def of preds here in M 689 "WS : (' '|'\\n') {skip();} ;\n" ; 690 String found = execParser("M.g", master, "MParser", "MLexer", 691 "start", "ax", debug); 692 assertEquals("S.a1\n", found); 693 } 694 695 @Test public void testKeywordVSIDGivesNoWarning() throws Exception { 696 ErrorQueue equeue = new ErrorQueue(); 697 ErrorManager.setErrorListener(equeue); 698 String slave = 699 "lexer grammar S;\n" + 700 "A : 'abc' {System.out.println(\"S.A\");} ;\n" + 701 "ID : 'a'..'z'+ ;\n"; 702 mkdir(tmpdir); 703 writeFile(tmpdir, "S.g", slave); 704 String master = 705 "grammar M;\n" + 706 "import S;\n" + 707 "a : A {System.out.println(\"M.a\");} ;\n" + 708 "WS : (' '|'\\n') {skip();} ;\n" ; 709 String found = execParser("M.g", master, "MParser", "MLexer", 710 "a", "abc", debug); 711 712 assertEquals("unexpected errors: "+equeue, 0, equeue.errors.size()); 713 assertEquals("unexpected warnings: "+equeue, 0, equeue.warnings.size()); 714 715 assertEquals("S.A\nM.a\n", found); 716 } 717 718 @Test public void testWarningForUndefinedToken() throws Exception { 719 ErrorQueue equeue = new ErrorQueue(); 720 ErrorManager.setErrorListener(equeue); 721 String slave = 722 "lexer grammar S;\n" + 723 "A : 'abc' {System.out.println(\"S.A\");} ;\n"; 724 mkdir(tmpdir); 725 writeFile(tmpdir, "S.g", slave); 726 String master = 727 "grammar M;\n" + 728 "import S;\n" + 729 "a : ABC A {System.out.println(\"M.a\");} ;\n" + 730 "WS : (' '|'\\n') {skip();} ;\n" ; 731 // A is defined in S but M should still see it and not give warning. 732 // only problem is ABC. 733 734 rawGenerateAndBuildRecognizer("M.g", master, "MParser", "MLexer", debug); 735 736 assertEquals("unexpected errors: "+equeue, 0, equeue.errors.size()); 737 assertEquals("unexpected warnings: "+equeue, 1, equeue.warnings.size()); 738 739 String expectedError = 740 "warning(105): "+tmpdir.toString().replaceFirst("\\-[0-9]+","")+File.separator+"M.g:3:5: no lexer rule corresponding to token: ABC"; 741 assertEquals(expectedError, equeue.warnings.get(0).toString().replaceFirst("\\-[0-9]+","")); 742 } 743 744 /** Make sure that M can import S that imports T. */ 745 @Test public void test3LevelImport() throws Exception { 746 ErrorQueue equeue = new ErrorQueue(); 747 ErrorManager.setErrorListener(equeue); 748 String slave = 749 "parser grammar T;\n" + 750 "a : T ;\n" ; 751 mkdir(tmpdir); 752 writeFile(tmpdir, "T.g", slave); 753 String slave2 = 754 "parser grammar S;\n" + // A, B, C token type order 755 "import T;\n" + 756 "a : S ;\n" ; 757 mkdir(tmpdir); 758 writeFile(tmpdir, "S.g", slave2); 759 760 String master = 761 "grammar M;\n" + 762 "import S;\n" + 763 "a : M ;\n" ; 764 writeFile(tmpdir, "M.g", master); 765 Tool antlr = newTool(new String[] {"-lib", tmpdir}); 766 CompositeGrammar composite = new CompositeGrammar(); 767 Grammar g = new Grammar(antlr,tmpdir+"/M.g",composite); 768 composite.setDelegationRoot(g); 769 g.parseAndBuildAST(); 770 g.composite.assignTokenTypes(); 771 g.composite.defineGrammarSymbols(); 772 773 String expectedTokenIDToTypeMap = "[M=4, S=5, T=6]"; 774 String expectedStringLiteralToTypeMap = "{}"; 775 String expectedTypeToTokenList = "[M, S, T]"; 776 777 assertEquals(expectedTokenIDToTypeMap, 778 realElements(g.composite.tokenIDToTypeMap).toString()); 779 assertEquals(expectedStringLiteralToTypeMap, g.composite.stringLiteralToTypeMap.toString()); 780 assertEquals(expectedTypeToTokenList, 781 realElements(g.composite.typeToTokenList).toString()); 782 783 assertEquals("unexpected errors: "+equeue, 0, equeue.errors.size()); 784 785 boolean ok = 786 rawGenerateAndBuildRecognizer("M.g", master, "MParser", null, false); 787 boolean expecting = true; // should be ok 788 assertEquals(expecting, ok); 789 } 790 791 @Test public void testBigTreeOfImports() throws Exception { 792 ErrorQueue equeue = new ErrorQueue(); 793 ErrorManager.setErrorListener(equeue); 794 String slave = 795 "parser grammar T;\n" + 796 "x : T ;\n" ; 797 mkdir(tmpdir); 798 writeFile(tmpdir, "T.g", slave); 799 slave = 800 "parser grammar S;\n" + 801 "import T;\n" + 802 "y : S ;\n" ; 803 mkdir(tmpdir); 804 writeFile(tmpdir, "S.g", slave); 805 806 slave = 807 "parser grammar C;\n" + 808 "i : C ;\n" ; 809 mkdir(tmpdir); 810 writeFile(tmpdir, "C.g", slave); 811 slave = 812 "parser grammar B;\n" + 813 "j : B ;\n" ; 814 mkdir(tmpdir); 815 writeFile(tmpdir, "B.g", slave); 816 slave = 817 "parser grammar A;\n" + 818 "import B,C;\n" + 819 "k : A ;\n" ; 820 mkdir(tmpdir); 821 writeFile(tmpdir, "A.g", slave); 822 823 String master = 824 "grammar M;\n" + 825 "import S,A;\n" + 826 "a : M ;\n" ; 827 writeFile(tmpdir, "M.g", master); 828 Tool antlr = newTool(new String[] {"-lib", tmpdir}); 829 CompositeGrammar composite = new CompositeGrammar(); 830 Grammar g = new Grammar(antlr,tmpdir+"/M.g",composite); 831 composite.setDelegationRoot(g); 832 g.parseAndBuildAST(); 833 g.composite.assignTokenTypes(); 834 g.composite.defineGrammarSymbols(); 835 836 String expectedTokenIDToTypeMap = "[A=4, B=5, C=6, M=7, S=8, T=9]"; 837 String expectedStringLiteralToTypeMap = "{}"; 838 String expectedTypeToTokenList = "[A, B, C, M, S, T]"; 839 840 assertEquals(expectedTokenIDToTypeMap, 841 realElements(g.composite.tokenIDToTypeMap).toString()); 842 assertEquals(expectedStringLiteralToTypeMap, g.composite.stringLiteralToTypeMap.toString()); 843 assertEquals(expectedTypeToTokenList, 844 realElements(g.composite.typeToTokenList).toString()); 845 846 assertEquals("unexpected errors: "+equeue, 0, equeue.errors.size()); 847 848 boolean ok = 849 rawGenerateAndBuildRecognizer("M.g", master, "MParser", null, false); 850 boolean expecting = true; // should be ok 851 assertEquals(expecting, ok); 852 } 853 854 @Test public void testRulesVisibleThroughMultilevelImport() throws Exception { 855 ErrorQueue equeue = new ErrorQueue(); 856 ErrorManager.setErrorListener(equeue); 857 String slave = 858 "parser grammar T;\n" + 859 "x : T ;\n" ; 860 mkdir(tmpdir); 861 writeFile(tmpdir, "T.g", slave); 862 String slave2 = 863 "parser grammar S;\n" + // A, B, C token type order 864 "import T;\n" + 865 "a : S ;\n" ; 866 mkdir(tmpdir); 867 writeFile(tmpdir, "S.g", slave2); 868 869 String master = 870 "grammar M;\n" + 871 "import S;\n" + 872 "a : M x ;\n" ; // x MUST BE VISIBLE TO M 873 writeFile(tmpdir, "M.g", master); 874 Tool antlr = newTool(new String[] {"-lib", tmpdir}); 875 CompositeGrammar composite = new CompositeGrammar(); 876 Grammar g = new Grammar(antlr,tmpdir+"/M.g",composite); 877 composite.setDelegationRoot(g); 878 g.parseAndBuildAST(); 879 g.composite.assignTokenTypes(); 880 g.composite.defineGrammarSymbols(); 881 882 String expectedTokenIDToTypeMap = "[M=4, S=5, T=6]"; 883 String expectedStringLiteralToTypeMap = "{}"; 884 String expectedTypeToTokenList = "[M, S, T]"; 885 886 assertEquals(expectedTokenIDToTypeMap, 887 realElements(g.composite.tokenIDToTypeMap).toString()); 888 assertEquals(expectedStringLiteralToTypeMap, g.composite.stringLiteralToTypeMap.toString()); 889 assertEquals(expectedTypeToTokenList, 890 realElements(g.composite.typeToTokenList).toString()); 891 892 assertEquals("unexpected errors: "+equeue, 0, equeue.errors.size()); 893 } 894 895 @Test public void testNestedComposite() throws Exception { 896 // Wasn't compiling. http://www.antlr.org/jira/browse/ANTLR-438 897 ErrorQueue equeue = new ErrorQueue(); 898 ErrorManager.setErrorListener(equeue); 899 String gstr = 900 "lexer grammar L;\n" + 901 "T1: '1';\n" + 902 "T2: '2';\n" + 903 "T3: '3';\n" + 904 "T4: '4';\n" ; 905 mkdir(tmpdir); 906 writeFile(tmpdir, "L.g", gstr); 907 gstr = 908 "parser grammar G1;\n" + 909 "s: a | b;\n" + 910 "a: T1;\n" + 911 "b: T2;\n" ; 912 mkdir(tmpdir); 913 writeFile(tmpdir, "G1.g", gstr); 914 915 gstr = 916 "parser grammar G2;\n" + 917 "import G1;\n" + 918 "a: T3;\n" ; 919 mkdir(tmpdir); 920 writeFile(tmpdir, "G2.g", gstr); 921 String G3str = 922 "grammar G3;\n" + 923 "import G2;\n" + 924 "b: T4;\n" ; 925 mkdir(tmpdir); 926 writeFile(tmpdir, "G3.g", G3str); 927 928 Tool antlr = newTool(new String[] {"-lib", tmpdir}); 929 CompositeGrammar composite = new CompositeGrammar(); 930 Grammar g = new Grammar(antlr,tmpdir+"/G3.g",composite); 931 composite.setDelegationRoot(g); 932 g.parseAndBuildAST(); 933 g.composite.assignTokenTypes(); 934 g.composite.defineGrammarSymbols(); 935 936 String expectedTokenIDToTypeMap = "[T1=4, T2=5, T3=6, T4=7]"; 937 String expectedStringLiteralToTypeMap = "{}"; 938 String expectedTypeToTokenList = "[T1, T2, T3, T4]"; 939 940 assertEquals(expectedTokenIDToTypeMap, 941 realElements(g.composite.tokenIDToTypeMap).toString()); 942 assertEquals(expectedStringLiteralToTypeMap, g.composite.stringLiteralToTypeMap.toString()); 943 assertEquals(expectedTypeToTokenList, 944 realElements(g.composite.typeToTokenList).toString()); 945 946 assertEquals("unexpected errors: "+equeue, 0, equeue.errors.size()); 947 948 boolean ok = 949 rawGenerateAndBuildRecognizer("G3.g", G3str, "G3Parser", null, false); 950 boolean expecting = true; // should be ok 951 assertEquals(expecting, ok); 952 } 953 954 @Test public void testHeadersPropogatedCorrectlyToImportedGrammars() throws Exception { 955 String slave = 956 "parser grammar S;\n" + 957 "a : B {System.out.print(\"S.a\");} ;\n"; 958 mkdir(tmpdir); 959 writeFile(tmpdir, "S.g", slave); 960 String master = 961 "grammar M;\n" + 962 "import S;\n" + 963 "@header{package mypackage;}\n" + 964 "@lexer::header{package mypackage;}\n" + 965 "s : a ;\n" + 966 "B : 'b' ;" + // defines B from inherited token space 967 "WS : (' '|'\\n') {skip();} ;\n" ; 968 boolean ok = antlr("M.g", "M.g", master, debug); 969 boolean expecting = true; // should be ok 970 assertEquals(expecting, ok); 971 } 972 973 }