1 /* 2 * Copyright (c) 2008, 2014, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 grammar JSL; 27 28 options { 29 backtrack=true; 30 } 31 32 tokens { 33 STAR = '*' ; 34 SLASH = '/' ; 35 PLUS = '+' ; 36 DASH = '-' ; 37 LT = '<' ; 38 GT = '>' ; 39 LTEQ = '<=' ; 40 GTEQ = '>=' ; 41 EQEQ = '==' ; 42 NEQ = '!=' ; 43 AND = '&&' ; 44 XOR = '^^' ; 45 OR = '||' ; 46 INC = '++' ; 47 DEC = '--' ; 48 49 STAREQ = '*=' ; 50 SLASHEQ = '/=' ; 51 PLUSEQ = '+=' ; 52 DASHEQ = '-=' ; 53 54 LEFT_PAREN = '(' ; 55 RIGHT_PAREN = ')' ; 56 LEFT_BRACKET = '[' ; 57 RIGHT_BRACKET = ']' ; 58 LEFT_BRACE = '{' ; 59 RIGHT_BRACE = '}' ; 60 61 LEFT_FRENCH = '<<' ; 62 RIGHT_FRENCH = '>>' ; 63 64 DOT = '.' ; 65 COMMA = ',' ; 66 EQUAL = '=' ; 67 BANG = '!' ; 68 TILDE = '~' ; 69 QUESTION = '?' ; 70 COLON = ':' ; 71 SEMICOLON = ';' ; 72 73 IF = 'if' ; 74 ELSE = 'else' ; 75 WHILE = 'while' ; 76 DO = 'do' ; 77 FOR = 'for' ; 78 79 UNROLL = 'unroll' ; 80 81 CONTINUE = 'continue' ; 82 BREAK = 'break' ; 83 DISCARD = 'discard' ; 84 RETURN = 'return' ; 85 86 VOID = 'void' ; 87 } 88 89 @header { 90 package com.sun.scenario.effect.compiler; 91 92 import com.sun.scenario.effect.compiler.model.*; 93 import com.sun.scenario.effect.compiler.tree.*; 94 } 95 96 @lexer::header { 97 package com.sun.scenario.effect.compiler; 98 } 99 100 @lexer::members { 101 // allow tests to turn on quiet mode, to reduce spewage 102 public static boolean quiet; 103 104 public void emitErrorMessage(String error) { 105 if (quiet) return; 106 super.emitErrorMessage(error); 107 } 108 } 109 110 @members { 111 private SymbolTable symbols = new SymbolTable(); 112 private TreeMaker tm = new TreeMaker(symbols); 113 114 public SymbolTable getSymbolTable() { 115 return symbols; 116 } 117 118 // fail on first error for now 119 // TODO: collect errors and recover... 120 protected void mismatch(IntStream input, int tokenType, BitSet follow) throws RecognitionException { 121 MismatchedTokenException ex = new MismatchedTokenException(tokenType, input); 122 System.err.println("Token mismatch at " + ex.line + ":" + ex.charPositionInLine); 123 throw ex; 124 } 125 126 public void recoverFromMismatchedSet(IntStream input, int ttype, BitSet follow) 127 throws RecognitionException { 128 throw new MissingTokenException(ttype, input, null); 129 } 130 } 131 132 @rulecatch { 133 catch (RecognitionException ex) { 134 throw ex; 135 } 136 } 137 138 field_selection returns [String fields] 139 : r=RGBA_FIELDS { $fields = $r.text; } 140 | x=XYZW_FIELDS { $fields = $x.text; } 141 ; 142 143 primary_expression returns [Expr expr] 144 : IDENTIFIER { $expr = tm.variable($IDENTIFIER.text); } 145 | INTCONSTANT { $expr = tm.literal(Type.INT, Integer.valueOf($INTCONSTANT.text)); } 146 | FLOATCONSTANT { $expr = tm.literal(Type.FLOAT, Float.valueOf($FLOATCONSTANT.text)); } 147 | BOOLCONSTANT { $expr = tm.literal(Type.BOOL, Boolean.valueOf($BOOLCONSTANT.text)); } 148 | LEFT_PAREN e=expression RIGHT_PAREN { $expr = tm.parenExpr($e.expr); } 149 ; 150 151 primary_or_call returns [Expr expr] 152 : e=primary_expression { $expr = $e.expr; } 153 | f=function_call { $expr = $f.expr; } 154 ; 155 156 // 157 // TODO: not sure how to do this properly without mutual left-recursion; 158 // for now we hack it to allow: 159 // arr[3].rgb 160 // arr[3] 161 // val.rgb 162 // val++ 163 // val-- 164 // val 165 // but not things like: 166 // arr[3].r++ 167 // 168 postfix_expression returns [Expr expr] 169 : e=primary_or_call LEFT_BRACKET ae=expression RIGHT_BRACKET fs=field_selection 170 { $expr = tm.fieldSelect(tm.arrayAccess($e.expr, $ae.expr), $fs.fields); } 171 | e=primary_or_call LEFT_BRACKET ae=expression RIGHT_BRACKET 172 { $expr = tm.arrayAccess($e.expr, $ae.expr); } 173 | e=primary_or_call fs=field_selection 174 { $expr = tm.fieldSelect($e.expr, $fs.fields); } 175 | e=primary_or_call INC 176 { $expr = tm.unary(UnaryOpType.INC, $e.expr); } 177 | e=primary_or_call DEC 178 { $expr = tm.unary(UnaryOpType.DEC, $e.expr); } 179 | e=primary_or_call 180 { $expr = $e.expr; } 181 ; 182 183 // From the GLSL spec... 184 // Grammar Note: Constructors look like functions, but lexical 185 // analysis recognized most of them as keywords. They are now 186 // recognized through "type_specifier". 187 188 function_call returns [Expr expr] 189 : id=IDENTIFIER LEFT_PAREN p=function_call_parameter_list? RIGHT_PAREN 190 { 191 $expr = tm.call($id.text, p!=null ? $p.exprList : null); 192 } 193 | ts=type_specifier LEFT_PAREN p=function_call_parameter_list? RIGHT_PAREN 194 { 195 Type type = Type.fromToken($ts.text); 196 $expr = tm.vectorCtor(type, p!=null ? $p.exprList : null); 197 } 198 ; 199 200 function_call_parameter_list returns [List<Expr> exprList = new ArrayList<Expr>()] 201 : a=assignment_expression { $exprList.add($a.expr); } 202 (COMMA a=assignment_expression {$exprList.add($a.expr); } 203 )* 204 ; 205 206 unary_expression returns [Expr expr] 207 : p=postfix_expression { $expr = $p.expr; } 208 | INC u=unary_expression { $expr = tm.unary(UnaryOpType.INC, $u.expr); } 209 | DEC u=unary_expression { $expr = tm.unary(UnaryOpType.DEC, $u.expr); } 210 | PLUS u=unary_expression { $expr = tm.unary(UnaryOpType.PLUS, $u.expr); } 211 | DASH u=unary_expression { $expr = tm.unary(UnaryOpType.MINUS, $u.expr); } 212 | BANG u=unary_expression { $expr = tm.unary(UnaryOpType.NOT, $u.expr); } 213 ; 214 215 // From the GLSL spec... 216 // Grammar Note: No traditional style type casts. 217 218 // From the GLSL spec... 219 // Grammar Note: No '*' or '&' unary ops. Pointers are not supported. 220 221 multiplicative_expression returns [Expr expr] 222 : a=unary_expression { $expr = $a.expr; } 223 (STAR b=multiplicative_expression { $expr = tm.binary(BinaryOpType.MUL, $expr, $b.expr); } 224 |SLASH b=multiplicative_expression { $expr = tm.binary(BinaryOpType.DIV, $expr, $b.expr); } 225 )* 226 ; 227 228 additive_expression returns [Expr expr] 229 : a=multiplicative_expression { $expr = $a.expr; } 230 (PLUS b=multiplicative_expression { $expr = tm.binary(BinaryOpType.ADD, $expr, $b.expr); } 231 |DASH b=multiplicative_expression { $expr = tm.binary(BinaryOpType.SUB, $expr, $b.expr); } 232 )* 233 ; 234 235 relational_expression returns [Expr expr] 236 : a=additive_expression { $expr = $a.expr; } 237 (LTEQ b=additive_expression { $expr = tm.binary(BinaryOpType.LTEQ, $expr, $b.expr); } 238 |GTEQ b=additive_expression { $expr = tm.binary(BinaryOpType.GTEQ, $expr, $b.expr); } 239 |LT b=additive_expression { $expr = tm.binary(BinaryOpType.LT, $expr, $b.expr); } 240 |GT b=additive_expression { $expr = tm.binary(BinaryOpType.GT, $expr, $b.expr); } 241 )* 242 ; 243 244 equality_expression returns [Expr expr] 245 : a=relational_expression { $expr = $a.expr; } 246 (EQEQ b=relational_expression { $expr = tm.binary(BinaryOpType.EQEQ, $expr, $b.expr); } 247 | NEQ b=relational_expression { $expr = tm.binary(BinaryOpType.NEQ, $expr, $b.expr); } 248 )* 249 ; 250 251 logical_and_expression returns [Expr expr] 252 : a=equality_expression { $expr = $a.expr; } 253 (AND b=equality_expression { $expr = tm.binary(BinaryOpType.AND, $expr, $b.expr); } 254 )* 255 ; 256 257 logical_xor_expression returns [Expr expr] 258 : a=logical_and_expression { $expr = $a.expr; } 259 (XOR b=logical_and_expression { $expr = tm.binary(BinaryOpType.XOR, $expr, $b.expr); } 260 )* 261 ; 262 263 logical_or_expression returns [Expr expr] 264 : a=logical_xor_expression { $expr = $a.expr; } 265 (OR b=logical_xor_expression { $expr = tm.binary(BinaryOpType.OR, $expr, $b.expr); } 266 )* 267 ; 268 269 ternary_part 270 : QUESTION expression COLON assignment_expression 271 ; 272 273 // TODO: handle ternary 274 conditional_expression returns [Expr expr] 275 : a=logical_or_expression ternary_part? { $expr = $a.expr; } 276 ; 277 278 assignment_expression returns [Expr expr] 279 : a=unary_expression op=assignment_operator b=assignment_expression 280 { $expr = tm.binary(BinaryOpType.forSymbol($op.text), $a.expr, $b.expr); } 281 | c=conditional_expression 282 { $expr = $c.expr; } 283 ; 284 285 assignment_operator 286 : EQUAL 287 | STAREQ 288 | SLASHEQ 289 | PLUSEQ 290 | DASHEQ 291 ; 292 293 // TODO: handle expression lists? 294 //expression returns [List<Expr> exprList = new ArrayList<Expr>()] 295 // : e=assignment_expression { $exprList.add($e.expr); } 296 // (COMMA e=assignment_expression { $exprList.add($e.expr); })* 297 // ; 298 299 expression returns [Expr expr] 300 : e=assignment_expression { $expr = $e.expr; } 301 ; 302 303 function_prototype returns [Function func] 304 : t=type_specifier id=IDENTIFIER LEFT_PAREN p=parameter_declaration_list? RIGHT_PAREN 305 { 306 Type type = Type.fromToken($t.text); 307 $func = symbols.declareFunction($id.text, type, (p != null) ? $p.paramList : null); 308 } 309 ; 310 311 parameter_declaration returns [Param param] 312 : t=type_specifier id=IDENTIFIER 313 { 314 Type type = Type.fromToken($t.text); 315 $param = new Param($id.text, type); 316 } 317 ; 318 319 parameter_declaration_list returns [List<Param> paramList = new ArrayList<Param>()] 320 : p=parameter_declaration { $paramList.add($p.param); } 321 (COMMA p=parameter_declaration { $paramList.add($p.param); } )* 322 ; 323 324 declaration_identifier_and_init returns [String name, Expr arrayInit, Expr init] 325 : id=IDENTIFIER { $name = $id.text; } 326 (LEFT_BRACKET ae=constant_expression { $arrayInit = $ae.expr; } RIGHT_BRACKET)? 327 (EQUAL e=initializer { $init = $e.expr; })? 328 ; 329 330 single_declaration returns [VarDecl decl] 331 : t=fully_specified_type d=declaration_identifier_and_init 332 { 333 int arraySize = -1; 334 Expr ainit = $d.arrayInit; 335 if (ainit != null) { 336 if (ainit instanceof LiteralExpr) { 337 Object val = ((LiteralExpr)ainit).getValue(); 338 if (!(val instanceof Integer)) { 339 throw new RuntimeException("Array size must be an integer"); 340 } 341 arraySize = ((Integer)val).intValue(); 342 } else if (ainit instanceof VariableExpr) { 343 Variable var = ((VariableExpr)ainit).getVariable(); 344 Object val = var.getConstValue(); 345 if (!(val instanceof Integer) || var.getQualifier() != Qualifier.CONST) { 346 throw new RuntimeException("Array size must be a constant integer"); 347 } 348 arraySize = ((Integer)val).intValue(); 349 } 350 } 351 352 Object constValue = null; 353 if ($t.qual == Qualifier.CONST) { 354 Expr cinit = $d.init; 355 if (cinit == null) { 356 throw new RuntimeException("Constant value must be initialized"); 357 } 358 // TODO: for now, allow some basic expressions on the rhs 359 // of the constant declaration... 360 //if (!(cinit instanceof LiteralExpr)) { 361 // throw new RuntimeException("Constant initializer must be a literal (for now)"); 362 //} 363 Type ctype = cinit.getResultType(); 364 if (ctype != $t.type) { 365 throw new RuntimeException("Constant type must match that of initializer"); 366 } 367 if (cinit instanceof LiteralExpr) { 368 constValue = ((LiteralExpr)cinit).getValue(); 369 } else { 370 // TODO: This is gross, but to support complex constant 371 // initializers (such as "const FOO = BAR / 42.0;") we 372 // will just save the full text of the rhs and hope that 373 // the backend does the right thing with it. The real 374 // solution obviously would be to evaluate the expression 375 // now and reduce it to a single value. 376 constValue = $d.init.toString(); 377 } 378 } 379 380 Variable var = 381 symbols.declareVariable($d.name, 382 $t.type, $t.qual, $t.precision, 383 arraySize, constValue); 384 $decl = tm.varDecl(var, $d.init); 385 } 386 ; 387 388 declaration returns [List<VarDecl> declList = new ArrayList<VarDecl>()] 389 : s=single_declaration { $declList.add($s.decl); } 390 (COMMA d=declaration_identifier_and_init 391 { 392 Variable base = $s.decl.getVariable(); 393 Variable var = 394 symbols.declareVariable($d.name, 395 base.getType(), 396 base.getQualifier(), 397 base.getPrecision()); 398 $declList.add(tm.varDecl(var, $d.init)); 399 } 400 )* SEMICOLON 401 ; 402 403 // From GLSL spec... 404 // Grammar Note: No 'enum', or 'typedef'. 405 406 fully_specified_type returns [Qualifier qual, Precision precision, Type type] 407 : tq=type_qualifier tp=type_precision ts=type_specifier 408 { 409 $qual = Qualifier.fromToken($tq.text); 410 $precision = Precision.fromToken($tp.text); 411 $type = Type.fromToken($ts.text); 412 } 413 | tq=type_qualifier ts=type_specifier 414 { 415 $qual = Qualifier.fromToken($tq.text); 416 $type = Type.fromToken($ts.text); 417 } 418 | tp=type_precision ts=type_specifier 419 { 420 $precision = Precision.fromToken($tp.text); 421 $type = Type.fromToken($ts.text); 422 } 423 | ts=type_specifier 424 { 425 $type = Type.fromToken($ts.text); 426 } 427 ; 428 429 type_qualifier 430 : 'const' 431 | 'param' 432 ; 433 434 type_precision 435 : 'lowp' 436 | 'mediump' 437 | 'highp' 438 ; 439 440 type_specifier 441 : type_specifier_nonarray array_brackets? 442 ; 443 444 array_brackets 445 : LEFT_BRACKET constant_expression RIGHT_BRACKET 446 ; 447 448 type_specifier_nonarray 449 : TYPE 450 | VOID 451 ; 452 453 initializer returns [Expr expr] 454 : e=assignment_expression { $expr = $e.expr; } 455 ; 456 457 declaration_statement returns [Stmt stmt] 458 : d=declaration { $stmt = tm.declStmt($d.declList); } 459 ; 460 461 statement returns [Stmt stmt] 462 : c=compound_statement { $stmt = $c.stmt; } 463 | s=simple_statement { $stmt = $s.stmt; } 464 ; 465 466 // From GLSL spec... 467 // Grammar Note: No labeled statements; 'goto' is not supported. 468 469 simple_statement returns [Stmt stmt] 470 : d=declaration_statement { $stmt = $d.stmt; } 471 | e=expression_statement { $stmt = $e.stmt; } 472 | s=selection_statement { $stmt = $s.stmt; } 473 | i=iteration_statement { $stmt = $i.stmt; } 474 | j=jump_statement { $stmt = $j.stmt; } 475 ; 476 477 compound_statement returns [Stmt stmt] 478 @init { 479 List<Stmt> stmtList = new ArrayList<Stmt>(); 480 } 481 : LEFT_BRACE (s=statement { stmtList.add($s.stmt); })* RIGHT_BRACE 482 { $stmt = tm.compoundStmt(stmtList); } 483 ; 484 485 statement_no_new_scope returns [Stmt stmt] 486 : c=compound_statement_no_new_scope { $stmt = $c.stmt; } 487 | s=simple_statement { $stmt = $s.stmt; } 488 ; 489 490 compound_statement_no_new_scope returns [Stmt stmt] 491 @init { 492 List<Stmt> stmtList = new ArrayList<Stmt>(); 493 } 494 : LEFT_BRACE (s=statement { stmtList.add($s.stmt); })* RIGHT_BRACE 495 { $stmt = tm.compoundStmt(stmtList); } 496 ; 497 498 expression_statement returns [Stmt stmt] 499 : SEMICOLON { $stmt = tm.exprStmt(null); } 500 | e=expression SEMICOLON { $stmt = tm.exprStmt($e.expr); } 501 ; 502 503 constant_expression returns [Expr expr] 504 : c=conditional_expression { $expr = $c.expr; } 505 ; 506 507 selection_statement returns [Stmt stmt] 508 : IF LEFT_PAREN e=expression RIGHT_PAREN a=statement (ELSE b=statement)? 509 { $stmt = tm.selectStmt($e.expr, $a.stmt, (b != null) ? $b.stmt : null); } 510 ; 511 512 // TODO: implement second half? 513 condition returns [Expr expr] 514 : e=expression {$expr = $e.expr; } 515 // | fully_specified_type IDENTIFIER EQUAL initializer 516 ; 517 518 iteration_statement returns [Stmt stmt] 519 : WHILE LEFT_PAREN c=condition RIGHT_PAREN snns=statement_no_new_scope 520 { $stmt = tm.whileStmt($c.expr, $snns.stmt); } 521 | DO s=statement WHILE LEFT_PAREN e=expression RIGHT_PAREN SEMICOLON 522 { $stmt = tm.doWhileStmt($s.stmt, $e.expr); } 523 | u=unroll_modifier FOR LEFT_PAREN init=for_init_statement rem=for_rest_statement RIGHT_PAREN snns=statement_no_new_scope 524 { $stmt = tm.forStmt($init.stmt, $rem.cond, $rem.expr, $snns.stmt, $u.max, $u.check); } 525 | FOR LEFT_PAREN init=for_init_statement rem=for_rest_statement RIGHT_PAREN snns=statement_no_new_scope 526 { $stmt = tm.forStmt($init.stmt, $rem.cond, $rem.expr, $snns.stmt, -1, -1); } 527 ; 528 529 unroll_modifier returns [int max, int check] 530 : UNROLL LEFT_PAREN m=INTCONSTANT COMMA c=INTCONSTANT RIGHT_PAREN 531 { $max = Integer.valueOf($m.text); $check = Integer.valueOf($c.text); } 532 ; 533 534 for_init_statement returns [Stmt stmt] 535 : e=expression_statement { $stmt = $e.stmt; } 536 | d=declaration_statement { $stmt = $d.stmt; } 537 ; 538 539 for_rest_statement returns [Expr cond, Expr expr] 540 : c=condition SEMICOLON e=expression? { $cond = $c.expr; if (e != null) $expr = $e.expr; } 541 | SEMICOLON e=expression? { if (e != null) $expr = $e.expr; } 542 ; 543 544 jump_statement returns [Stmt stmt] 545 : CONTINUE SEMICOLON { $stmt = tm.continueStmt(); } 546 | BREAK SEMICOLON { $stmt = tm.breakStmt(); } 547 | DISCARD SEMICOLON { $stmt = tm.discardStmt(); } 548 | RETURN SEMICOLON { $stmt = tm.returnStmt(null); } 549 | RETURN e=expression SEMICOLON { $stmt = tm.returnStmt($e.expr); } 550 ; 551 552 // From GLSL spec... 553 // Grammar Note: No 'goto'. Gotos are not supported. 554 555 translation_unit returns [ProgramUnit prog] 556 @init { 557 List<ExtDecl> declList = new ArrayList<ExtDecl>(); 558 } 559 : (e=external_declaration { declList.addAll($e.res); } )+ 560 { $prog = tm.programUnit(declList); } 561 ; 562 563 external_declaration returns [List<ExtDecl> res = new ArrayList<ExtDecl>()] 564 : f=function_definition { $res.add($f.def); } 565 | d=declaration { $res.addAll($d.declList); } 566 | g=glue_block { $res.add($g.block); } 567 ; 568 569 // From GLSL spec... 570 // Grammar Note: No 'switch'. Switch statements not supported. 571 572 function_definition returns [FuncDef def] 573 @init { 574 symbols.enterFrame(); 575 } 576 : p=function_prototype s=compound_statement_no_new_scope { $def = tm.funcDef($p.func, $s.stmt); } 577 ; 578 finally { 579 symbols.exitFrame(); 580 } 581 582 glue_block returns [GlueBlock block] 583 : g=GLUE_BLOCK { $block = tm.glueBlock($g.text.substring(2, $g.text.length()-2)); } 584 ; 585 586 TYPE 587 : 'float2' 588 | 'float3' 589 | 'float4' 590 | 'float' 591 | 'int2' 592 | 'int3' 593 | 'int4' 594 | 'int' 595 | 'bool2' 596 | 'bool3' 597 | 'bool4' 598 | 'bool' 599 | 'sampler' 600 | 'lsampler' 601 | 'fsampler' 602 ; 603 604 BOOLCONSTANT 605 : 'true' 606 | 'false' 607 ; 608 609 RGBA_FIELDS 610 : DOT RFIELD RFIELD RFIELD RFIELD 611 | DOT RFIELD RFIELD RFIELD 612 | DOT RFIELD RFIELD 613 | DOT RFIELD 614 ; 615 616 fragment 617 RFIELD : 'r' | 'g' | 'b' | 'a' ; 618 619 XYZW_FIELDS 620 : DOT XFIELD XFIELD XFIELD XFIELD 621 | DOT XFIELD XFIELD XFIELD 622 | DOT XFIELD XFIELD 623 | DOT XFIELD 624 ; 625 626 fragment 627 XFIELD : 'x' | 'y' | 'z' | 'w' ; 628 629 IDENTIFIER 630 : LETTER (LETTER|DIGIT)* 631 ; 632 633 fragment 634 LETTER 635 : '$' 636 | 'A'..'Z' 637 | 'a'..'z' 638 | '_' 639 ; 640 641 INTCONSTANT : ('0' | '1'..'9' DIGIT*) ; 642 643 FLOATCONSTANT 644 : DIGIT+ '.' DIGIT* 645 | '.' DIGIT+ 646 ; 647 648 fragment 649 DIGIT : '0'..'9' ; 650 651 WS : (' '|'\r'|'\t'|'\u000C'|'\n') {$channel=HIDDEN;} 652 ; 653 654 COMMENT 655 : '/*' ( options {greedy=false;} : . )* '*/' {$channel=HIDDEN;} 656 ; 657 658 LINE_COMMENT 659 : '//' ~('\n'|'\r')* '\r'? '\n' {$channel=HIDDEN;} 660 ; 661 662 GLUE_BLOCK 663 : LEFT_FRENCH .* RIGHT_FRENCH 664 ;