1 #!/usr/bin/ruby 2 # encoding: utf-8 3 4 require 'antlr3/test/functional' 5 6 class TestAutoAST < ANTLR3::Test::Functional 7 8 def parse( grammar, rule, input, expect_errors = false ) 9 @grammar = inline_grammar( grammar ) 10 compile_and_load @grammar 11 grammar_module = self.class.const_get( @grammar.name ) 12 13 grammar_module::Lexer.send( :include, ANTLR3::Test::CollectErrors ) 14 grammar_module::Lexer.send( :include, ANTLR3::Test::CaptureOutput ) 15 grammar_module::Parser.send( :include, ANTLR3::Test::CollectErrors ) 16 grammar_module::Parser.send( :include, ANTLR3::Test::CaptureOutput ) 17 18 lexer = grammar_module::Lexer.new( input ) 19 parser = grammar_module::Parser.new( lexer ) 20 21 r = parser.send( rule ) 22 parser.reported_errors.should be_empty unless expect_errors 23 result = '' 24 25 unless r.nil? 26 result += r.result if r.respond_to?( :result ) 27 result += r.tree.inspect if r.tree 28 end 29 return( expect_errors ? [ result, parser.reported_errors ] : result ) 30 end 31 32 def tree_parse( grammar, tree_grammar, rule, tree_rule, input ) 33 @grammar = inline_grammar( grammar ) 34 @tree_grammar = inline_grammar( tree_grammar ) 35 compile_and_load @grammar 36 compile_and_load @tree_grammar 37 38 grammar_module = self.class.const_get( @grammar.name ) 39 tree_grammar_module = self.class.const_get( @tree_grammar.name ) 40 41 grammar_module::Lexer.send( :include, ANTLR3::Test::CollectErrors ) 42 grammar_module::Lexer.send( :include, ANTLR3::Test::CaptureOutput ) 43 grammar_module::Parser.send( :include, ANTLR3::Test::CollectErrors ) 44 grammar_module::Parser.send( :include, ANTLR3::Test::CaptureOutput ) 45 tree_grammar_module::TreeParser.send( :include, ANTLR3::Test::CollectErrors ) 46 tree_grammar_module::TreeParser.send( :include, ANTLR3::Test::CaptureOutput ) 47 48 lexer = grammar_module::Lexer.new( input ) 49 parser = grammar.module::Parser.new( lexer ) 50 r = parser.send( rule ) 51 nodes = ANTLR3::CommonTreeNodeStream( r.tree ) 52 nodes.token_stream = parser.input 53 walker = tree_grammar_module::TreeParser.new( nodes ) 54 r = walker.send( tree_rule ) 55 56 return( r ? r.tree.inspect : '' ) 57 end 58 59 60 example 'flat token list' do 61 result = parse( <<-'END', :a, 'abc 34' ) 62 grammar TokenList; 63 options {language=Ruby;output=AST;} 64 a : ID INT ; 65 ID : 'a'..'z'+ ; 66 INT : '0'..'9'+; 67 WS : (' '|'\n') {$channel=HIDDEN;}; 68 END 69 result.should == 'abc 34' 70 end 71 72 example 'token list in a single-alternative subrule' do 73 result = parse( <<-'END', :a, 'abc 34' ) 74 grammar TokenListInSingleAltBlock; 75 options {language=Ruby;output=AST;} 76 a : (ID INT) ; 77 ID : 'a'..'z'+ ; 78 INT : '0'..'9'+; 79 WS : (' '|'\n') {$channel=HIDDEN;} ; 80 END 81 result.should == 'abc 34' 82 end 83 84 example "simple root at the outer level via the `^' operator" do 85 result = parse( <<-'END', :a, 'abc 34' ) 86 grammar SimpleRootAtOuterLevel; 87 options {language=Ruby;output=AST;} 88 a : ID^ INT ; 89 ID : 'a'..'z'+ ; 90 INT : '0'..'9'+; 91 WS : (' '|'\n') {$channel=HIDDEN;} ; 92 END 93 result.should == '(abc 34)' 94 end 95 96 example "outer-level root changing token order from the `^' operator" do 97 result = parse( <<-'END', :a, '34 abc' ) 98 grammar SimpleRootAtOuterLevelReverse; 99 options {language=Ruby;output=AST;} 100 a : INT ID^ ; 101 ID : 'a'..'z'+ ; 102 INT : '0'..'9'+; 103 WS : (' '|'\n') {$channel=HIDDEN;} ; 104 END 105 result.should == '(abc 34)' 106 end 107 108 example "leaving out tokens using the `!' operator" do 109 result = parse( <<-'END', :a, 'abc 34 dag 4532' ) 110 grammar Bang; 111 options {language=Ruby;output=AST;} 112 a : ID INT! ID! INT ; 113 ID : 'a'..'z'+ ; 114 INT : '0'..'9'+; 115 WS : (' '|'\n') {$channel=HIDDEN;} ; 116 END 117 118 result.should == 'abc 4532' 119 end 120 121 example "tokens in `(...)?' optional subrule" do 122 result = parse( <<-'END', :a, 'a 1 b' ) 123 grammar OptionalThenRoot; 124 options {language=Ruby;output=AST;} 125 a : ( ID INT )? ID^ ; 126 ID : 'a'..'z'+ ; 127 INT : '0'..'9'+; 128 WS : (' '|'\n') {$channel=HIDDEN;} ; 129 END 130 result.should == '(b a 1)' 131 end 132 133 example "labeled literal-string root token" do 134 result = parse( <<-'END', :a, 'void foo;' ) 135 grammar LabeledStringRoot; 136 options {language=Ruby;output=AST;} 137 a : v='void'^ ID ';' ; 138 ID : 'a'..'z'+ ; 139 INT : '0'..'9'+; 140 WS : (' '|'\n') {$channel=HIDDEN;} ; 141 END 142 result.should == '(void foo ;)' 143 end 144 145 example 'rule with token wildcard' do 146 result = parse( <<-'END', :a, 'void foo;' ) 147 grammar Wildcard; 148 options {language=Ruby;output=AST;} 149 a : v='void'^ . ';' ; 150 ID : 'a'..'z'+ ; 151 INT : '0'..'9'+; 152 WS : (' '|'\n') {$channel=HIDDEN;} ; 153 END 154 result.should == '(void foo ;)' 155 end 156 157 example "token wildcard as root via the `^' operator" do 158 result = parse( <<-'END', :a, 'void foo;' ) 159 grammar WildcardRoot; 160 options {language=Ruby;output=AST;} 161 a : v='void' .^ ';' ; 162 ID : 'a'..'z'+ ; 163 INT : '0'..'9'+; 164 WS : (' '|'\n') {$channel=HIDDEN;} ; 165 END 166 result.should == '(foo void ;)' 167 end 168 169 example "labeled token wildcard as root via the `^' operator" do 170 result = parse( <<-'END', :a, 'void foo;' ) 171 grammar WildcardRootWithLabel; 172 options {language=Ruby;output=AST;} 173 a : v='void' x=.^ ';' ; 174 ID : 'a'..'z'+ ; 175 INT : '0'..'9'+; 176 WS : (' '|'\n') {$channel=HIDDEN;} ; 177 END 178 result.should == '(foo void ;)' 179 end 180 181 182 example "token wildcard as root (with list label)" do 183 result = parse( <<-'END', :a, 'void foo;' ) 184 grammar WildcardRootWithListLabel; 185 options {language=Ruby;output=AST;} 186 a : v='void' x=.^ ';' ; 187 ID : 'a'..'z'+ ; 188 INT : '0'..'9'+; 189 WS : (' '|'\n') {$channel=HIDDEN;} ; 190 191 END 192 result.should == '(foo void ;)' 193 end 194 195 example "trashed token wildcard" do 196 result = parse( <<-'END', :a, 'void foo;' ) 197 grammar WildcardBangWithListLabel; 198 options {language=Ruby;output=AST;} 199 a : v='void' x=.! ';' ; 200 ID : 'a'..'z'+ ; 201 INT : '0'..'9'+; 202 WS : (' '|'\n') {$channel=HIDDEN;} ; 203 204 END 205 result.should == 'void ;' 206 end 207 208 example "multiple occurences of the `^' operator in a list of tokens" do 209 result = parse( <<-'END', :a, 'a 34 c' ) 210 grammar RootRoot; 211 options {language=Ruby;output=AST;} 212 a : ID^ INT^ ID ; 213 ID : 'a'..'z'+ ; 214 INT : '0'..'9'+; 215 WS : (' '|'\n') {$channel=HIDDEN;} ; 216 217 END 218 result.should == '(34 a c)' 219 end 220 221 example "another case of multiple occurences of the `^' operator" do 222 result = parse( <<-'END', :a, 'a 34 c' ) 223 grammar RootRoot2; 224 options {language=Ruby;output=AST;} 225 a : ID INT^ ID^ ; 226 ID : 'a'..'z'+ ; 227 INT : '0'..'9'+; 228 WS : (' '|'\n') {$channel=HIDDEN;} ; 229 230 END 231 result.should == '(c (34 a))' 232 end 233 234 example "root-hoist using `^' from within a (...)+ block" do 235 result = parse( <<-'END', :a, 'a 34 * b 9 * c' ) 236 grammar RootThenRootInLoop; 237 options {language=Ruby;output=AST;} 238 a : ID^ (INT '*'^ ID)+ ; 239 ID : 'a'..'z'+ ; 240 INT : '0'..'9'+; 241 WS : (' '|'\n') {$channel=HIDDEN;} ; 242 243 END 244 result.should == '(* (* (a 34) b 9) c)' 245 end 246 247 example "nested subrules without any AST ops resulting in a flat list" do 248 result = parse( <<-'END', :a, 'void a b;' ) 249 grammar NestedSubrule; 250 options {language=Ruby;output=AST;} 251 a : 'void' (({ 252 #do nothing 253 } ID|INT) ID | 'null' ) ';' ; 254 ID : 'a'..'z'+ ; 255 INT : '0'..'9'+; 256 WS : (' '|'\n') {$channel=HIDDEN;} ; 257 258 END 259 result.should == 'void a b ;' 260 end 261 262 example "invoking another rule without any AST ops, resulting in a flat list" do 263 result = parse( <<-'END', :a, 'int a' ) 264 grammar InvokeRule; 265 options {language=Ruby;output=AST;} 266 a : type ID ; 267 type : { 268 # do nothing 269 }'int' | 'float' ; 270 ID : 'a'..'z'+ ; 271 INT : '0'..'9'+; 272 WS : (' '|'\n') {$channel=HIDDEN;} ; 273 274 END 275 result.should == 'int a' 276 end 277 278 example "hoisting the results of another rule as root using the `^' operator" do 279 result = parse( <<-'END', :a, 'int a' ) 280 grammar InvokeRuleAsRoot; 281 options {language=Ruby;output=AST;} 282 a : type^ ID ; 283 type : { 284 # do nothing 285 }'int' | 'float' ; 286 ID : 'a'..'z'+ ; 287 INT : '0'..'9'+; 288 WS : (' '|'\n') {$channel=HIDDEN;} ; 289 290 END 291 result.should == '(int a)' 292 end 293 294 example "hoisting another rule's true as root using the `^' operator (with a label)" do 295 result = parse( <<-'END', :a, 'int a' ) 296 grammar InvokeRuleAsRootWithLabel; 297 options {language=Ruby;output=AST;} 298 a : x=type^ ID ; 299 type : { 300 # do nothing 301 }'int' | 'float' ; 302 ID : 'a'..'z'+ ; 303 INT : '0'..'9'+; 304 WS : (' '|'\n') {$channel=HIDDEN;} ; 305 306 END 307 result.should == '(int a)' 308 end 309 310 example "hoisting another rule's result tree as root using the `^' operator (with a list += label)" do 311 result = parse( <<-'END', :a, 'int a' ) 312 grammar InvokeRuleAsRootWithListLabel; 313 options {language=Ruby;output=AST;} 314 a : x+=type^ ID ; 315 type : { 316 # do nothing 317 }'int' | 'float' ; 318 ID : 'a'..'z'+ ; 319 INT : '0'..'9'+; 320 WS : (' '|'\n') {$channel=HIDDEN;} ; 321 322 END 323 result.should == '(int a)' 324 end 325 326 example "root-hoist via `^' within a (...)* loop resulting in a deeply-nested tree" do 327 result = parse( <<-'END', :a, 'a+b+c+d' ) 328 grammar RuleRootInLoop; 329 options {language=Ruby;output=AST;} 330 a : ID ('+'^ ID)* ; 331 ID : 'a'..'z'+ ; 332 INT : '0'..'9'+; 333 WS : (' '|'\n') {$channel=HIDDEN;} ; 334 335 END 336 result.should == '(+ (+ (+ a b) c) d)' 337 end 338 339 example "hoisting another rule's result tree as root from within a (...)* loop resulting in a deeply nested tree" do 340 result = parse( <<-'END', :a, 'a+b+c-d' ) 341 grammar RuleInvocationRuleRootInLoop; 342 options {language=Ruby;output=AST;} 343 a : ID (op^ ID)* ; 344 op : { 345 # do nothing 346 }'+' | '-' ; 347 ID : 'a'..'z'+ ; 348 INT : '0'..'9'+; 349 WS : (' '|'\n') {$channel=HIDDEN;} ; 350 351 END 352 result.should == '(- (+ (+ a b) c) d)' 353 end 354 355 example "using tail recursion to build deeply-nested expression trees" do 356 result = parse( <<-'END', :s, '3 exp 4 exp 5' ) 357 grammar TailRecursion; 358 options {language=Ruby;output=AST;} 359 s : a ; 360 a : atom ('exp'^ a)? ; 361 atom : INT ; 362 ID : 'a'..'z'+ ; 363 INT : '0'..'9'+; 364 WS : (' '|'\n') {$channel=HIDDEN;} ; 365 366 END 367 result.should == '(exp 3 (exp 4 5))' 368 end 369 370 example "simple token node from a token type set" do 371 result = parse( <<-'END', :a, 'abc' ) 372 grammar TokenSet; 373 options {language=Ruby; output=AST;} 374 a : ID|INT ; 375 ID : 'a'..'z'+ ; 376 INT : '0'..'9'+; 377 WS : (' '|'\n') {$channel=HIDDEN;} ; 378 END 379 result.should == 'abc' 380 end 381 382 example "hoisting a token-type set token as root with `^'" do 383 result = parse( <<-'END', :a, '+abc' ) 384 grammar SetRoot; 385 options {language=Ruby;output=AST;} 386 a : ('+' | '-')^ ID ; 387 ID : 'a'..'z'+ ; 388 INT : '0'..'9'+; 389 WS : (' '|'\n') {$channel=HIDDEN;} ; 390 391 END 392 result.should == '(+ abc)' 393 end 394 395 example "hoisting a token-type set token as root with `^' (with a label)" do 396 result = parse( <<-'END', :a, '+abc' ) 397 grammar SetRootWithLabel; 398 options {language=Ruby;output=AST;} 399 a : (x=('+' | '-'))^ ID ; 400 ID : 'a'..'z'+ ; 401 INT : '0'..'9'+; 402 WS : (' '|'\n') {$channel=HIDDEN;} ; 403 404 END 405 result.should == '+ abc' 406 end 407 408 example "hoisting a token-type set token as root from within a (...)* loop" do 409 result = parse( <<-'END', :a, 'a+b-c' ) 410 grammar SetAsRuleRootInLoop; 411 options {language=Ruby;output=AST;} 412 a : ID (('+'|'-')^ ID)* ; 413 ID : 'a'..'z'+ ; 414 INT : '0'..'9'+; 415 WS : (' '|'\n') {$channel=HIDDEN;} ; 416 417 END 418 result.should == '(- (+ a b) c)' 419 end 420 421 example "an `~' inverted token-type set element" do 422 result = parse( <<-'END', :a, '34+2' ) 423 grammar NotSet; 424 options {language=Ruby;output=AST;} 425 a : ~ID '+' INT ; 426 ID : 'a'..'z'+ ; 427 INT : '0'..'9'+; 428 WS : (' '|'\n') {$channel=HIDDEN;} ; 429 430 END 431 result.should == '34 + 2' 432 end 433 434 example "a `~' inverted token-type set in a rule (with a label)" do 435 result = parse( <<-'END', :a, '34+2' ) 436 grammar NotSetWithLabel; 437 options {language=Ruby;output=AST;} 438 a : x=~ID '+' INT ; 439 ID : 'a'..'z'+ ; 440 INT : '0'..'9'+; 441 WS : (' '|'\n') {$channel=HIDDEN;} ; 442 443 END 444 result.should == '34 + 2' 445 end 446 447 example "a `~' inverted token-type set element in a rule (with a list += label)" do 448 result = parse( <<-'END', :a, '34+2' ) 449 grammar NotSetWithListLabel; 450 options {language=Ruby;output=AST;} 451 a : x=~ID '+' INT ; 452 ID : 'a'..'z'+ ; 453 INT : '0'..'9'+; 454 WS : (' '|'\n') {$channel=HIDDEN;} ; 455 456 END 457 result.should == '34 + 2' 458 end 459 460 example "a `~' inverted token-type set element hoisted to root via `^'" do 461 result = parse( <<-'END', :a, '34 55' ) 462 grammar NotSetRoot; 463 options {language=Ruby;output=AST;} 464 a : ~'+'^ INT ; 465 ID : 'a'..'z'+ ; 466 INT : '0'..'9'+; 467 WS : (' '|'\n') {$channel=HIDDEN;} ; 468 469 END 470 result.should == '(34 55)' 471 end 472 473 example "hoisting a `~' inverted token-type set to root using `^' (with label)" do 474 result = parse( <<-'END', :a, '34 55' ) 475 grammar NotSetRootWithLabel; 476 options {language=Ruby;output=AST;} 477 a : x=~'+'^ INT ; 478 ID : 'a'..'z'+ ; 479 INT : '0'..'9'+; 480 WS : (' '|'\n') {$channel=HIDDEN;} ; 481 END 482 result.should == '(34 55)' 483 end 484 485 example "hoisting a `~' inverted token-type set to root using `^' (with list += label)" do 486 result = parse( <<-'END', :a, '34 55' ) 487 grammar NotSetRootWithListLabel; 488 options {language=Ruby;output=AST;} 489 a : x+=~'+'^ INT ; 490 ID : 'a'..'z'+ ; 491 INT : '0'..'9'+; 492 WS : (' '|'\n') {$channel=HIDDEN;} ; 493 END 494 result.should == '(34 55)' 495 end 496 497 example "hoisting a `~' inverted token-type set to root from within a (...)* loop" do 498 result = parse( <<-'END', :a, '3+4+5' ) 499 grammar NotSetRuleRootInLoop; 500 options {language=Ruby;output=AST;} 501 a : INT (~INT^ INT)* ; 502 blort : '+' ; 503 ID : 'a'..'z'+ ; 504 INT : '0'..'9'+; 505 WS : (' '|'\n') {$channel=HIDDEN;} ; 506 507 END 508 result.should == '(+ (+ 3 4) 5)' 509 end 510 511 example "multiple tokens with the same label in a rule" do 512 result = parse( <<-'END', :a, 'a b' ) 513 grammar TokenLabelReuse; 514 options {language=Ruby;output=AST;} 515 a returns [result] : id=ID id=ID { 516 $result = "2nd id=\%s," \% $id.text 517 } ; 518 ID : 'a'..'z'+ ; 519 INT : '0'..'9'+; 520 WS : (' '|'\n') {$channel=HIDDEN;} ; 521 522 END 523 result.should == '2nd id=b,a b' 524 end 525 526 example "multiple tokens with the same label in a rule (with a `^' root hoist)" do 527 result = parse( <<-'END', :a, 'a b' ) 528 grammar TokenLabelReuse2; 529 options {language=Ruby;output=AST;} 530 a returns [result]: id=ID id=ID^ {$result = "2nd id=#{$id.text},"} ; 531 ID : 'a'..'z'+ ; 532 INT : '0'..'9'+; 533 WS : (' '|'\n') {$channel=HIDDEN;} ; 534 535 END 536 result.should == '2nd id=b,(b a)' 537 end 538 539 example "extra token in a simple declaration" do 540 result, errors = parse( <<-'END', :decl, 'int 34 x=1;', true ) 541 grammar ExtraTokenInSimpleDecl; 542 options {language=Ruby;output=AST;} 543 decl : type^ ID '='! INT ';'! ; 544 type : 'int' | 'float' ; 545 ID : 'a'..'z'+ ; 546 INT : '0'..'9'+; 547 WS : (' '|'\n') {$channel=HIDDEN;} ; 548 549 END 550 errors.should == [ "line 1:4 extraneous input \"34\" expecting ID" ] 551 result.should == '(int x 1)' 552 end 553 554 example "missing ID in a simple declaration" do 555 result, errors = parse( <<-'END', :decl, 'int =1;', true ) 556 grammar MissingIDInSimpleDecl; 557 options {language=Ruby;output=AST;} 558 tokens {EXPR;} 559 decl : type^ ID '='! INT ';'! ; 560 type : 'int' | 'float' ; 561 ID : 'a'..'z'+ ; 562 INT : '0'..'9'+; 563 WS : (' '|'\n') {$channel=HIDDEN;} ; 564 END 565 errors.should == [ "line 1:4 missing ID at \"=\"" ] 566 result.should == '(int <missing ID> 1)' 567 end 568 569 example "missing token of a token-type set in a simple declaration" do 570 result, errors = parse( <<-'END', :decl, 'x=1;', true ) 571 grammar MissingSetInSimpleDecl; 572 options {language=Ruby;output=AST;} 573 tokens {EXPR;} 574 decl : type^ ID '='! INT ';'! ; 575 type : 'int' | 'float' ; 576 ID : 'a'..'z'+ ; 577 INT : '0'..'9'+; 578 WS : (' '|'\n') {$channel=HIDDEN;} ; 579 580 END 581 errors.should == [ "line 1:0 mismatched input \"x\" expecting set nil" ] 582 result.should == '(<error: x> x 1)' 583 end 584 585 example "missing INT token simulated with a `<missing INT>' error node" do 586 result, errors = parse( <<-'END', :a, 'abc', true ) 587 grammar MissingTokenGivesErrorNode; 588 options {language=Ruby;output=AST;} 589 a : ID INT ; // follow is EOF 590 ID : 'a'..'z'+ ; 591 INT : '0'..'9'+; 592 WS : (' '|'\n') {$channel=HIDDEN;} ; 593 594 END 595 errors.should == [ "line 0:-1 missing INT at \"<EOF>\"" ] 596 result.should == 'abc <missing INT>' 597 end 598 599 example "missing token from invoked rule results in error node with a resync attribute" do 600 result, errors = parse( <<-'END', :a, 'abc', true ) 601 grammar MissingTokenGivesErrorNodeInInvokedRule; 602 options {language=Ruby;output=AST;} 603 a : b ; 604 b : ID INT ; // follow should see EOF 605 ID : 'a'..'z'+ ; 606 INT : '0'..'9'+; 607 WS : (' '|'\n') {$channel=HIDDEN;} ; 608 609 END 610 errors.should == [ "line 0:-1 mismatched input \"<EOF>\" expecting INT" ] 611 result.should == '<mismatched token: <EOF>, resync = abc>' 612 end 613 614 example "extraneous ID token displays error and is ignored in AST output" do 615 result, errors = parse( <<-'END', :a, 'abc ick 34', true ) 616 grammar ExtraTokenGivesErrorNode; 617 options {language=Ruby;output=AST;} 618 a : b c ; 619 b : ID ; 620 c : INT ; 621 ID : 'a'..'z'+ ; 622 INT : '0'..'9'+; 623 WS : (' '|'\n') {$channel=HIDDEN;} ; 624 625 END 626 errors.should == [ "line 1:4 extraneous input \"ick\" expecting INT" ] 627 result.should == 'abc 34' 628 end 629 630 example "missing ID token simulated with a `<missing ID>' error node" do 631 result, errors = parse( <<-'END', :a, '34', true ) 632 grammar MissingFirstTokenGivesErrorNode; 633 options {language=Ruby;output=AST;} 634 a : ID INT ; 635 ID : 'a'..'z'+ ; 636 INT : '0'..'9'+; 637 WS : (' '|'\n') {$channel=HIDDEN;} ; 638 639 END 640 errors.should == [ "line 1:0 missing ID at \"34\"" ] 641 result.should == '<missing ID> 34' 642 end 643 644 example "another case where a missing ID token is simulated with a `<missing ID>' error node" do 645 result, errors = parse( <<-'END', :a, '34', true ) 646 grammar MissingFirstTokenGivesErrorNode2; 647 options {language=Ruby;output=AST;} 648 a : b c ; 649 b : ID ; 650 c : INT ; 651 ID : 'a'..'z'+ ; 652 INT : '0'..'9'+; 653 WS : (' '|'\n') {$channel=HIDDEN;} ; 654 655 END 656 errors.should == [ "line 1:0 missing ID at \"34\"" ] 657 result.should == '<missing ID> 34' 658 end 659 660 example "no viable alternative for rule is represented as a single `<unexpected: ...>' error node" do 661 result, errors = parse( <<-'END', :a, '*', true ) 662 grammar NoViableAltGivesErrorNode; 663 options {language=Ruby;output=AST;} 664 a : b | c ; 665 b : ID ; 666 c : INT ; 667 ID : 'a'..'z'+ ; 668 S : '*' ; 669 INT : '0'..'9'+; 670 WS : (' '|'\n') {$channel=HIDDEN;} ; 671 END 672 errors.should == [ "line 1:0 no viable alternative at input \"*\"" ] 673 result.should == "<unexpected: 0 S[\"*\"] @ line 1 col 0 (0..0), resync = *>" 674 end 675 676 example "token with a `+=' list label hoisted to root with `^'" do 677 result = parse( <<-'END', :a, 'a' ) 678 grammar TokenListLabelRuleRoot; 679 options {language=Ruby;output=AST;} 680 a : id+=ID^ ; 681 ID : 'a'..'z'+ ; 682 INT : '0'..'9'+; 683 WS : (' '|'\n') {$channel=HIDDEN;} ; 684 685 END 686 result.should == 'a' 687 end 688 689 example "token with a list `+=' label trashed with `!'" do 690 result = parse( <<-'END', :a, 'a' ) 691 grammar TokenListLabelBang; 692 options {language=Ruby;output=AST;} 693 a : id+=ID! ; 694 ID : 'a'..'z'+ ; 695 INT : '0'..'9'+; 696 WS : (' '|'\n') {$channel=HIDDEN;} ; 697 698 END 699 result.should == '' 700 end 701 702 example "using list `+=' labels to collect trees of invoked rules" do 703 result = parse( <<-'END', :a, 'a b' ) 704 grammar RuleListLabel; 705 options {language=Ruby;output=AST;} 706 a returns [result]: x+=b x+=b { 707 t = $x[1] 708 $result = "2nd x=#{t.inspect}," 709 }; 710 b : ID; 711 ID : 'a'..'z'+ ; 712 INT : '0'..'9'+; 713 WS : (' '|'\n') {$channel=HIDDEN;} ; 714 715 END 716 result.should == '2nd x=b,a b' 717 end 718 719 example "using a list `+=' label to collect the trees of invoked rules within a (...)+ block" do 720 result = parse( <<-'END', :a, 'a b' ) 721 grammar RuleListLabelRuleRoot; 722 options {language=Ruby;output=AST;} 723 a returns [result] : ( x+=b^ )+ { 724 $result = "x=\%s," \% $x[1].inspect 725 } ; 726 b : ID; 727 ID : 'a'..'z'+ ; 728 INT : '0'..'9'+; 729 WS : (' '|'\n') {$channel=HIDDEN;} ; 730 731 END 732 result.should == 'x=(b a),(b a)' 733 end 734 735 example "trashing the tree of an invoked rule with `!' while collecting the tree with a list `+=' label" do 736 result = parse( <<-'END', :a, 'a b' ) 737 grammar RuleListLabelBang; 738 options {language=Ruby;output=AST;} 739 a returns [result] : x+=b! x+=b { 740 $result = "1st x=#{$x[0].inspect}," 741 } ; 742 b : ID; 743 ID : 'a'..'z'+ ; 744 INT : '0'..'9'+; 745 WS : (' '|'\n') {$channel=HIDDEN;} ; 746 747 END 748 result.should == '1st x=a,b' 749 end 750 751 example "a whole bunch of different elements" do 752 result = parse( <<-'END', :a, 'a b b c c d' ) 753 grammar ComplicatedMelange; 754 options {language=Ruby;output=AST;} 755 a : A b=B b=B c+=C c+=C D {s = $D.text} ; 756 A : 'a' ; 757 B : 'b' ; 758 C : 'c' ; 759 D : 'd' ; 760 WS : (' '|'\n') {$channel=HIDDEN;} ; 761 END 762 result.should == 'a b b c c d' 763 end 764 765 example "rule return values in addition to AST output" do 766 result = parse( <<-'END', :a, 'abc 34' ) 767 grammar ReturnValueWithAST; 768 options {language=Ruby;output=AST;} 769 a returns [result] : ID b { $result = $b.i.to_s + "\n" } ; 770 b returns [i] : INT {$i=$INT.text.to_i}; 771 ID : 'a'..'z'+ ; 772 INT : '0'..'9'+; 773 WS : (' '|'\n') {$channel=HIDDEN;} ; 774 775 END 776 result.should == "34\nabc 34" 777 end 778 779 example "a (...)+ loop containing a token-type set" do 780 result = parse( <<-'END', :r, 'abc 34 d' ) 781 grammar SetLoop; 782 options { language=Ruby;output=AST; } 783 r : (INT|ID)+ ; 784 ID : 'a'..'z' + ; 785 INT : '0'..'9' +; 786 WS: (' ' | '\n' | '\t')+ {$channel = HIDDEN;}; 787 788 END 789 result.should == 'abc 34 d' 790 end 791 792 end 793