/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.codehaus.groovy.syntax; import org.codehaus.groovy.GroovyBugError; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.Map; import java.util.Set; /** * Typing information for the CST system. The types here are those * used by CSTNode, Token, and Reduction. * * @author <a href="mailto:bob@werken.com">bob mcwhirter</a> * @author <a href="mailto:cpoirier@dreaming.org">Chris Poirier</a> */ public class Types { //--------------------------------------------------------------------------- // TYPES: NOTE THAT ORDERING AND VALUES ARE IMPORTANT TO LOCAL ROUTINES! // // SPECIAL TOKENS public static final int EOF = -1; // end of file public static final int UNKNOWN = 0; // the unknown token // // RELEVANT WHITESPACE public static final int NEWLINE = 5; // \n // // OPERATORS AND OTHER MARKERS public static final int LEFT_CURLY_BRACE = 10; // { public static final int RIGHT_CURLY_BRACE = 20; // } public static final int LEFT_SQUARE_BRACKET = 30; // [ public static final int RIGHT_SQUARE_BRACKET = 40; // ] public static final int LEFT_PARENTHESIS = 50; // ( public static final int RIGHT_PARENTHESIS = 60; // ) public static final int DOT = 70; // . public static final int DOT_DOT = 75; // .. public static final int DOT_DOT_DOT = 77; // ... public static final int NAVIGATE = 80; // -> public static final int FIND_REGEX = 90; // =~ public static final int MATCH_REGEX = 94; // ==~ public static final int REGEX_PATTERN = 97; // ~ public static final int EQUAL = 100; // = public static final int EQUALS = EQUAL; public static final int ASSIGN = EQUAL; public static final int COMPARE_NOT_EQUAL = 120; // != public static final int COMPARE_IDENTICAL = 121; // === public static final int COMPARE_NOT_IDENTICAL = 122; // !== public static final int COMPARE_EQUAL = 123; // == public static final int COMPARE_LESS_THAN = 124; // < public static final int COMPARE_LESS_THAN_EQUAL = 125; // <= public static final int COMPARE_GREATER_THAN = 126; // > public static final int COMPARE_GREATER_THAN_EQUAL = 127; // >= public static final int COMPARE_TO = 128; // <=> public static final int COMPARE_NOT_IN = 129; // !in public static final int COMPARE_NOT_INSTANCEOF = 130; // !instanceof public static final int NOT = 160; // ! public static final int LOGICAL_OR = 162; // || public static final int LOGICAL_AND = 164; // && public static final int LOGICAL_OR_EQUAL = 166; // ||= public static final int LOGICAL_AND_EQUAL = 168; // &&= public static final int PLUS = 200; // + public static final int MINUS = 201; // - public static final int MULTIPLY = 202; // * public static final int DIVIDE = 203; // / public static final int INTDIV = 204; // \ public static final int MOD = 205; // % public static final int STAR_STAR = 206; // ** public static final int POWER = STAR_STAR; // ** public static final int PLUS_EQUAL = 210; // += public static final int MINUS_EQUAL = 211; // -= public static final int MULTIPLY_EQUAL = 212; // *= public static final int DIVIDE_EQUAL = 213; // /= public static final int INTDIV_EQUAL = 214; // \= public static final int MOD_EQUAL = 215; // %= public static final int POWER_EQUAL = 216; // **= public static final int ELVIS_EQUAL = 217; // ?= public static final int PLUS_PLUS = 250; // ++ public static final int PREFIX_PLUS_PLUS = 251; // ++ public static final int POSTFIX_PLUS_PLUS = 252; // ++ public static final int PREFIX_PLUS = 253; // + public static final int MINUS_MINUS = 260; // -- public static final int PREFIX_MINUS_MINUS = 261; // -- public static final int POSTFIX_MINUS_MINUS = 262; // -- public static final int PREFIX_MINUS = 263; // - (negation) public static final int LEFT_SHIFT = 280; // << public static final int RIGHT_SHIFT = 281; // >> public static final int RIGHT_SHIFT_UNSIGNED = 282; // >>> public static final int LEFT_SHIFT_EQUAL = 285; // <<= public static final int RIGHT_SHIFT_EQUAL = 286; // >>= public static final int RIGHT_SHIFT_UNSIGNED_EQUAL = 287; // >>>= public static final int STAR = MULTIPLY; public static final int COMMA = 300; // - public static final int COLON = 310; // : public static final int SEMICOLON = 320; // ; public static final int QUESTION = 330; // ? // TODO refactor PIPE to be BITWISE_OR public static final int PIPE = 340; // | public static final int DOUBLE_PIPE = LOGICAL_OR; // || public static final int BITWISE_OR = PIPE; // | public static final int BITWISE_AND = 341; // & public static final int BITWISE_XOR = 342; // ^ public static final int BITWISE_OR_EQUAL = 350; // |= public static final int BITWISE_AND_EQUAL = 351; // &= public static final int BITWISE_XOR_EQUAL = 352; // ^= public static final int BITWISE_NEGATION = REGEX_PATTERN; // ~ // // LITERALS public static final int STRING = 400; // any bare string data public static final int IDENTIFIER = 440; // anything text and not a keyword public static final int INTEGER_NUMBER = 450; // integer public static final int DECIMAL_NUMBER = 451; // decimal // // KEYWORDS: (PRIMARILY) CLASS/METHOD DECLARATION MODIFIERS public static final int KEYWORD_PRIVATE = 500; // declaration visibility public static final int KEYWORD_PROTECTED = 501; // declaration visibility public static final int KEYWORD_PUBLIC = 502; // declaration visibility public static final int KEYWORD_ABSTRACT = 510; // method body missing public static final int KEYWORD_FINAL = 511; // declaration cannot be overridden public static final int KEYWORD_NATIVE = 512; // a native code entry point public static final int KEYWORD_TRANSIENT = 513; // property should not be persisted public static final int KEYWORD_VOLATILE = 514; // compiler should never cache property public static final int KEYWORD_SYNCHRONIZED = 520; // modifier and block type public static final int KEYWORD_STATIC = 521; // modifier and block type // // KEYWORDS: TYPE SYSTEM public static final int KEYWORD_DEF = 530; // identifies a function declaration public static final int KEYWORD_DEFMACRO = 539; // XXX br identifies a macro declaration public static final int KEYWORD_CLASS = 531; // identifies a class declaration public static final int KEYWORD_INTERFACE = 532; // identifies an interface declaration public static final int KEYWORD_MIXIN = 533; // identifies a mixin declaration public static final int KEYWORD_IMPLEMENTS = 540; // specifies the interfaces implemented by a class public static final int KEYWORD_EXTENDS = 541; // specifies the base class/interface for a new one public static final int KEYWORD_THIS = 542; // method variable points to the current instance public static final int KEYWORD_SUPER = 543; // method variable points to the base instance public static final int KEYWORD_INSTANCEOF = 544; // type comparator public static final int KEYWORD_PROPERTY = 545; // deprecated; identifies a property public static final int KEYWORD_NEW = 546; // used to create a new instance of a class public static final int KEYWORD_PACKAGE = 550; // declares the package scope public static final int KEYWORD_IMPORT = 551; // declares an external class public static final int KEYWORD_AS = 552; // used in import statements to create an alias // // KEYWORDS: CONTROL STRUCTURES public static final int KEYWORD_RETURN = 560; // returns from a closure or method public static final int KEYWORD_IF = 561; // if public static final int KEYWORD_ELSE = 562; // else public static final int KEYWORD_DO = 570; // do loop public static final int KEYWORD_WHILE = 571; // while loop public static final int KEYWORD_FOR = 572; // for loop public static final int KEYWORD_IN = 573; // for (each) loop separator public static final int KEYWORD_BREAK = 574; // exits a loop or block public static final int KEYWORD_CONTINUE = 575; // restarts a loop on the next iteration public static final int KEYWORD_SWITCH = 576; // switch block public static final int KEYWORD_CASE = 577; // item in a switch block public static final int KEYWORD_DEFAULT = 578; // catch-all item in a switch block public static final int KEYWORD_TRY = 580; // block to monitor for exceptions public static final int KEYWORD_CATCH = 581; // catch block for a particular exception public static final int KEYWORD_FINALLY = 582; // block to always execute on exit of the try public static final int KEYWORD_THROW = 583; // statement to throw an exception public static final int KEYWORD_THROWS = 584; // method modifier to declare thrown transactions public static final int KEYWORD_ASSERT = 585; // alternate throw for code invariants // // KEYWORDS: PRIMITIVE TYPES public static final int KEYWORD_VOID = 600; // void public static final int KEYWORD_BOOLEAN = 601; // boolean public static final int KEYWORD_BYTE = 602; // 1 byte integer public static final int KEYWORD_SHORT = 603; // 2 byte integer public static final int KEYWORD_INT = 604; // 4 byte integer public static final int KEYWORD_LONG = 605; // 8 byte integer public static final int KEYWORD_FLOAT = 606; // 32 bit floating point number public static final int KEYWORD_DOUBLE = 607; // 64 bit floating point number public static final int KEYWORD_CHAR = 608; // unicode character code // // KEYWORDS: SPECIAL VALUES public static final int KEYWORD_TRUE = 610; // boolean truth public static final int KEYWORD_FALSE = 611; // boolean false public static final int KEYWORD_NULL = 612; // missing instance // // KEYWORDS: RESERVED public static final int KEYWORD_CONST = 700; // reserved in java and groovy public static final int KEYWORD_GOTO = 701; // reserved in java and groovy // // SPECIAL (CALCULATED) MEANINGS public static final int SYNTH_COMPILATION_UNIT = 800; // reserved: a synthetic root for a CST public static final int SYNTH_CLASS = 801; // applied to class names public static final int SYNTH_INTERFACE = 802; // applied to interface names public static final int SYNTH_MIXIN = 803; // applied to mixin names public static final int SYNTH_METHOD = 804; // applied to method names public static final int SYNTH_PROPERTY = 805; // applied to property names public static final int SYNTH_PARAMETER_DECLARATION = 806; // applied to method/closure parameter names public static final int SYNTH_LIST = 810; // applied to "[" that marks a list public static final int SYNTH_MAP = 811; // applied to "[" that marks a map public static final int SYNTH_GSTRING = 812; // a complete GString public static final int SYNTH_METHOD_CALL = 814; // applied to the optional "(" that marks a call to a method public static final int SYNTH_CAST = 815; // applied to "(" that marks a type cast public static final int SYNTH_BLOCK = 816; // applied to "{" that marks a block public static final int SYNTH_CLOSURE = 817; // applied to "{" that marks a closure public static final int SYNTH_LABEL = 818; // applied to a statement label public static final int SYNTH_TERNARY = 819; // applied to "?" that marks a ternary expression public static final int SYNTH_TUPLE = 820; // applied to "{" that marks an array initializer public static final int SYNTH_VARIABLE_DECLARATION = 830; // applied to an identifier that specifies // the type of a variable declaration // // GSTRING TOKENS public static final int GSTRING_START = 901; // any marker tha begins a GString public static final int GSTRING_END = 902; // any matching marker that ends a GString public static final int GSTRING_EXPRESSION_START = 903; // the ${ marker that starts a GString expression public static final int GSTRING_EXPRESSION_END = 904; // the } marker that ends a GString expression // // TYPE CLASSES public static final int ANY = 1000; // anything public static final int NOT_EOF = 1001; // anything but EOF public static final int GENERAL_END_OF_STATEMENT = 1002; // ";", "\n", EOF public static final int ANY_END_OF_STATEMENT = 1003; // ";", "\n", EOF, "}" public static final int ASSIGNMENT_OPERATOR = 1100; // =, +=, etc. public static final int COMPARISON_OPERATOR = 1101; // ==, ===, >, <, etc. public static final int MATH_OPERATOR = 1102; // +, -, / *, %, plus the LOGICAL_OPERATORS public static final int LOGICAL_OPERATOR = 1103; // ||, &&, ! public static final int RANGE_OPERATOR = 1104; // .., ... public static final int REGEX_COMPARISON_OPERATOR = 1105; // =~, etc. public static final int DEREFERENCE_OPERATOR = 1106; // ., -> public static final int BITWISE_OPERATOR = 1107; // |, &, <<, >>, >>>, ^, ~ public static final int PREFIX_OPERATOR = 1200; // ++, !, etc. public static final int POSTFIX_OPERATOR = 1210; // ++, etc. public static final int INFIX_OPERATOR = 1220; // +, -, =, etc. public static final int PREFIX_OR_INFIX_OPERATOR = 1230; // +, - public static final int PURE_PREFIX_OPERATOR = 1235; // prefix +, prefix - public static final int KEYWORD = 1300; // any keyword public static final int SYMBOL = 1301; // any symbol public static final int LITERAL = 1310; // strings, numbers, identifiers public static final int NUMBER = 1320; // integers and decimals public static final int SIGN = 1325; // "+", "-" public static final int NAMED_VALUE = 1330; // true, false, null public static final int TRUTH_VALUE = 1331; // true, false public static final int PRIMITIVE_TYPE = 1340; // void, byte, short, int, etc. public static final int CREATABLE_PRIMITIVE_TYPE = 1341; // any PRIMITIVE_TYPE except void public static final int LOOP = 1350; // do, while, etc. public static final int RESERVED_KEYWORD = 1360; // const, goto, etc. public static final int KEYWORD_IDENTIFIER = 1361; // keywords that can appear as identifiers public static final int SYNTHETIC = 1370; // any of the SYNTH types public static final int TYPE_DECLARATION = 1400; // class, interface, mixin public static final int DECLARATION_MODIFIER = 1410; // public, private, abstract, etc. public static final int TYPE_NAME = 1420; // identifiers, primitive types public static final int CREATABLE_TYPE_NAME = 1430; // identifiers, primitive types except void public static final int MATCHED_CONTAINER = 1500; // (, ), [, ], {, } public static final int LEFT_OF_MATCHED_CONTAINER = 1501; // (, [, { public static final int RIGHT_OF_MATCHED_CONTAINER = 1502; // ), ], } public static final int EXPRESSION = 1900; // all of the below 1900 series public static final int OPERATOR_EXPRESSION = 1901; // "."-"<<" public static final int SYNTH_EXPRESSION = 1902; // cast, ternary, and closure expression public static final int KEYWORD_EXPRESSION = 1903; // new, this, super, instanceof, true, false, null public static final int LITERAL_EXPRESSION = 1904; // LITERAL public static final int ARRAY_EXPRESSION = 1905; // "[" public static final int SIMPLE_EXPRESSION = 1910; // LITERAL, this, true, false, null public static final int COMPLEX_EXPRESSION = 1911; // SIMPLE_EXPRESSION, and various molecules // // TYPE GROUPS (OPERATIONS SUPPORT) public static final int PARAMETER_TERMINATORS = 2000; // ")", "," public static final int ARRAY_ITEM_TERMINATORS = 2001; // "]", "," public static final int TYPE_LIST_TERMINATORS = 2002; // "implements", "throws", "{", "," public static final int OPTIONAL_DATATYPE_FOLLOWERS = 2003; // identifier, "[", "." public static final int SWITCH_BLOCK_TERMINATORS = 2004; // "case", "default", "}" public static final int SWITCH_ENTRIES = 2005; // "case", "default" public static final int METHOD_CALL_STARTERS = 2006; // LITERAL, "(", "{" public static final int UNSAFE_OVER_NEWLINES = 2007; // things the expression parser should cross lines for in it doesn't have to public static final int PRECLUDES_CAST_OPERATOR = 2008; // anything that prevents (X) from being a cast //--------------------------------------------------------------------------- // TYPE HIERARCHIES /** * Given two types, returns true if the second describes the first. */ public static boolean ofType( int specific, int general ) { if( general == specific ) { return true; } switch( general ) { case ANY: return true; case NOT_EOF: return specific >= UNKNOWN && specific <= SYNTH_VARIABLE_DECLARATION; case GENERAL_END_OF_STATEMENT: switch( specific ) { case EOF: case NEWLINE: case SEMICOLON: return true; } break; case ANY_END_OF_STATEMENT: switch( specific ) { case EOF: case NEWLINE: case SEMICOLON: case RIGHT_CURLY_BRACE: return true; } break; case ASSIGNMENT_OPERATOR: return specific == EQUAL || (specific >= PLUS_EQUAL && specific <= ELVIS_EQUAL) || (specific >= LOGICAL_OR_EQUAL && specific <= LOGICAL_AND_EQUAL) || (specific >= LEFT_SHIFT_EQUAL && specific <= RIGHT_SHIFT_UNSIGNED_EQUAL) || (specific >= BITWISE_OR_EQUAL && specific <= BITWISE_XOR_EQUAL); case COMPARISON_OPERATOR: return specific >= COMPARE_NOT_EQUAL && specific <= COMPARE_TO; case MATH_OPERATOR: return (specific >= PLUS && specific <= RIGHT_SHIFT_UNSIGNED) || (specific >= NOT && specific <= LOGICAL_AND) || (specific >= BITWISE_OR && specific <= BITWISE_XOR); case LOGICAL_OPERATOR: return specific >= NOT && specific <= LOGICAL_AND; case BITWISE_OPERATOR: return (specific >= BITWISE_OR && specific <= BITWISE_XOR) || specific == BITWISE_NEGATION; case RANGE_OPERATOR: return specific == DOT_DOT || specific == DOT_DOT_DOT; case REGEX_COMPARISON_OPERATOR: return specific == FIND_REGEX || specific == MATCH_REGEX; case DEREFERENCE_OPERATOR: return specific == DOT || specific == NAVIGATE; case PREFIX_OPERATOR: switch( specific ) { case MINUS: case PLUS_PLUS: case MINUS_MINUS: return true; } /* FALL THROUGH */ case PURE_PREFIX_OPERATOR: switch( specific ) { case REGEX_PATTERN: case NOT: case PREFIX_PLUS: case PREFIX_PLUS_PLUS: case PREFIX_MINUS: case PREFIX_MINUS_MINUS: case SYNTH_CAST: return true; } break; case POSTFIX_OPERATOR: switch( specific ) { case PLUS_PLUS: case POSTFIX_PLUS_PLUS: case MINUS_MINUS: case POSTFIX_MINUS_MINUS: return true; } break; case INFIX_OPERATOR: switch( specific ) { case DOT: case NAVIGATE: case LOGICAL_OR: case LOGICAL_AND: case BITWISE_OR: case BITWISE_AND: case BITWISE_XOR: case LEFT_SHIFT: case RIGHT_SHIFT: case RIGHT_SHIFT_UNSIGNED: case FIND_REGEX: case MATCH_REGEX: case DOT_DOT: case DOT_DOT_DOT: case KEYWORD_INSTANCEOF: return true; } return (specific >= COMPARE_NOT_EQUAL && specific <= COMPARE_TO) || (specific >= PLUS && specific <= MOD_EQUAL) || specific == EQUAL || (specific >= PLUS_EQUAL && specific <= ELVIS_EQUAL) || (specific >= LOGICAL_OR_EQUAL && specific <= LOGICAL_AND_EQUAL) || (specific >= LEFT_SHIFT_EQUAL && specific <= RIGHT_SHIFT_UNSIGNED_EQUAL) || (specific >= BITWISE_OR_EQUAL && specific <= BITWISE_XOR_EQUAL); case PREFIX_OR_INFIX_OPERATOR: switch( specific ) { case POWER: case PLUS: case MINUS: case PREFIX_PLUS: case PREFIX_MINUS: return true; } break; case KEYWORD: return specific >= KEYWORD_PRIVATE && specific <= KEYWORD_GOTO; case SYMBOL: return specific >= NEWLINE && specific <= PIPE; case LITERAL: return specific >= STRING && specific <= DECIMAL_NUMBER; case NUMBER: return specific == INTEGER_NUMBER || specific == DECIMAL_NUMBER; case SIGN: switch( specific ) { case PLUS: case MINUS: return true; } break; case NAMED_VALUE: return specific >= KEYWORD_TRUE && specific <= KEYWORD_NULL; case TRUTH_VALUE: return specific == KEYWORD_TRUE || specific == KEYWORD_FALSE; case TYPE_NAME: if( specific == IDENTIFIER ) { return true; } /* FALL THROUGH */ case PRIMITIVE_TYPE: return specific >= KEYWORD_VOID && specific <= KEYWORD_CHAR; case CREATABLE_TYPE_NAME: if( specific == IDENTIFIER ) { return true; } /* FALL THROUGH */ case CREATABLE_PRIMITIVE_TYPE: return specific >= KEYWORD_BOOLEAN && specific <= KEYWORD_CHAR; case LOOP: switch( specific ) { case KEYWORD_DO: case KEYWORD_WHILE: case KEYWORD_FOR: return true; } break; case RESERVED_KEYWORD: return specific >= KEYWORD_CONST && specific <= KEYWORD_GOTO; case KEYWORD_IDENTIFIER: switch( specific ) { case KEYWORD_CLASS: case KEYWORD_INTERFACE: case KEYWORD_MIXIN: case KEYWORD_DEF: case KEYWORD_DEFMACRO: case KEYWORD_IN: case KEYWORD_PROPERTY: return true; } break; case SYNTHETIC: return specific >= SYNTH_COMPILATION_UNIT && specific <= SYNTH_VARIABLE_DECLARATION; case TYPE_DECLARATION: return specific >= KEYWORD_CLASS && specific <= KEYWORD_MIXIN; case DECLARATION_MODIFIER: return specific >= KEYWORD_PRIVATE && specific <= KEYWORD_STATIC; case MATCHED_CONTAINER: switch( specific ) { case LEFT_CURLY_BRACE: case RIGHT_CURLY_BRACE: case LEFT_SQUARE_BRACKET: case RIGHT_SQUARE_BRACKET: case LEFT_PARENTHESIS: case RIGHT_PARENTHESIS: return true; } break; case LEFT_OF_MATCHED_CONTAINER: switch( specific ) { case LEFT_CURLY_BRACE: case LEFT_SQUARE_BRACKET: case LEFT_PARENTHESIS: return true; } break; case RIGHT_OF_MATCHED_CONTAINER: switch( specific ) { case RIGHT_CURLY_BRACE: case RIGHT_SQUARE_BRACKET: case RIGHT_PARENTHESIS: return true; } break; case PARAMETER_TERMINATORS: return specific == RIGHT_PARENTHESIS || specific == COMMA; case ARRAY_ITEM_TERMINATORS: return specific == RIGHT_SQUARE_BRACKET || specific == COMMA; case TYPE_LIST_TERMINATORS: switch( specific ) { case KEYWORD_IMPLEMENTS: case KEYWORD_THROWS: case LEFT_CURLY_BRACE: case COMMA: return true; } break; case OPTIONAL_DATATYPE_FOLLOWERS: switch( specific ) { case IDENTIFIER: case LEFT_SQUARE_BRACKET: case DOT: return true; } break; case SWITCH_BLOCK_TERMINATORS: if( specific == RIGHT_CURLY_BRACE ) { return true; } /* FALL THROUGH */ case SWITCH_ENTRIES: return specific == KEYWORD_CASE || specific == KEYWORD_DEFAULT; case METHOD_CALL_STARTERS: if( specific >= STRING && specific <= DECIMAL_NUMBER ) { return true; } switch( specific ) { case LEFT_PARENTHESIS: case GSTRING_START: case SYNTH_GSTRING: case KEYWORD_NEW: return true; } break; case UNSAFE_OVER_NEWLINES: if( ofType(specific, SYMBOL) ) { switch( specific ) { case LEFT_CURLY_BRACE: case LEFT_PARENTHESIS: case LEFT_SQUARE_BRACKET: case PLUS: case PLUS_PLUS: case MINUS: case MINUS_MINUS: case REGEX_PATTERN: case NOT: return true; } return false; } switch( specific ) { case KEYWORD_INSTANCEOF: case GSTRING_EXPRESSION_START: case GSTRING_EXPRESSION_END: case GSTRING_END: return false; } return true; case PRECLUDES_CAST_OPERATOR: switch( specific ) { case PLUS: case MINUS: case PREFIX_MINUS: case PREFIX_MINUS_MINUS: case PREFIX_PLUS: case PREFIX_PLUS_PLUS: case LEFT_PARENTHESIS: return false; } return !ofType( specific, COMPLEX_EXPRESSION ); case OPERATOR_EXPRESSION: return specific >= DOT && specific <= RIGHT_SHIFT_UNSIGNED; case SYNTH_EXPRESSION: switch( specific ) { case SYNTH_CAST: case SYNTH_CLOSURE: case SYNTH_TERNARY: return true; } break; case KEYWORD_EXPRESSION: switch( specific ) { case KEYWORD_NEW: case KEYWORD_THIS: case KEYWORD_SUPER: case KEYWORD_INSTANCEOF: case KEYWORD_TRUE: case KEYWORD_FALSE: case KEYWORD_NULL: return true; } break; case LITERAL_EXPRESSION: return specific >= STRING && specific <= DECIMAL_NUMBER; case ARRAY_EXPRESSION: return specific == LEFT_SQUARE_BRACKET; case EXPRESSION: if( specific >= DOT && specific <= RIGHT_SHIFT_UNSIGNED ) { return true; } if( specific >= STRING && specific <= DECIMAL_NUMBER ) { return true; } switch( specific ) { case SYNTH_CAST: case SYNTH_CLOSURE: case SYNTH_TERNARY: case SYNTH_GSTRING: case KEYWORD_NEW: case KEYWORD_THIS: case KEYWORD_SUPER: case KEYWORD_INSTANCEOF: case KEYWORD_TRUE: case KEYWORD_FALSE: case KEYWORD_NULL: case LEFT_SQUARE_BRACKET: return true; } break; case COMPLEX_EXPRESSION: switch( specific ) { case KEYWORD_NEW: case SYNTH_METHOD_CALL: case SYNTH_GSTRING: case SYNTH_LIST: case SYNTH_MAP: case SYNTH_CLOSURE: case SYNTH_TERNARY: case SYNTH_VARIABLE_DECLARATION: return true; } /* FALL THROUGH */ case SIMPLE_EXPRESSION: if( specific >= STRING && specific <= DECIMAL_NUMBER ) { return true; } switch( specific ) { case KEYWORD_SUPER: case KEYWORD_THIS: case KEYWORD_TRUE: case KEYWORD_FALSE: case KEYWORD_NULL: return true; } break; } return false; } //--------------------------------------------------------------------------- // TYPE COERSIONS /** * Given two types, returns true if the first can be viewed as the second. * NOTE that <code>canMean()</code> is orthogonal to <code>ofType()</code>. */ public static boolean canMean( int actual, int preferred ) { if( actual == preferred ) { return true; } switch( preferred ) { case SYNTH_PARAMETER_DECLARATION: case IDENTIFIER: switch( actual ) { case IDENTIFIER: case KEYWORD_DEF: case KEYWORD_DEFMACRO: case KEYWORD_CLASS: case KEYWORD_INTERFACE: case KEYWORD_MIXIN: return true; } break; case SYNTH_CLASS: case SYNTH_INTERFACE: case SYNTH_MIXIN: case SYNTH_METHOD: case SYNTH_PROPERTY: return actual == IDENTIFIER; case SYNTH_LIST: case SYNTH_MAP: return actual == LEFT_SQUARE_BRACKET; case SYNTH_CAST: return actual == LEFT_PARENTHESIS; case SYNTH_BLOCK: case SYNTH_CLOSURE: return actual == LEFT_CURLY_BRACE; case SYNTH_LABEL: return actual == COLON; case SYNTH_VARIABLE_DECLARATION: return actual == IDENTIFIER; } return false; } /** * Converts a node from a generic type to a specific prefix type. * Throws a <code>GroovyBugError</code> if the type can't be converted * and requested. */ public static void makePrefix(CSTNode node, boolean throwIfInvalid ) { switch( node.getMeaning() ) { case PLUS: node.setMeaning( PREFIX_PLUS ); break; case MINUS: node.setMeaning( PREFIX_MINUS ); break; case PLUS_PLUS: node.setMeaning( PREFIX_PLUS_PLUS ); break; case MINUS_MINUS: node.setMeaning( PREFIX_MINUS_MINUS ); break; default: if( throwIfInvalid ) { throw new GroovyBugError( "cannot convert to prefix for type [" + node.getMeaning() + "]" ); } } } /** * Converts a node from a generic type to a specific postfix type. * Throws a <code>GroovyBugError</code> if the type can't be converted. */ public static void makePostfix(CSTNode node, boolean throwIfInvalid ) { switch( node.getMeaning() ) { case PLUS_PLUS: node.setMeaning( POSTFIX_PLUS_PLUS ); break; case MINUS_MINUS: node.setMeaning( POSTFIX_MINUS_MINUS ); break; default: if( throwIfInvalid ) { throw new GroovyBugError( "cannot convert to postfix for type [" + node.getMeaning() + "]" ); } } } //--------------------------------------------------------------------------- // OPERATOR PRECEDENCE /** * Returns the precedence of the specified operator. Non-operator's will * receive -1 or a GroovyBugError, depending on your preference. */ public static int getPrecedence( int type, boolean throwIfInvalid ) { switch( type ) { case LEFT_PARENTHESIS: return 0; case EQUAL: case PLUS_EQUAL: case MINUS_EQUAL: case MULTIPLY_EQUAL: case DIVIDE_EQUAL: case INTDIV_EQUAL: case MOD_EQUAL: case POWER_EQUAL: case ELVIS_EQUAL: case LOGICAL_OR_EQUAL: case LOGICAL_AND_EQUAL: case LEFT_SHIFT_EQUAL: case RIGHT_SHIFT_EQUAL: case RIGHT_SHIFT_UNSIGNED_EQUAL: case BITWISE_OR_EQUAL: case BITWISE_AND_EQUAL: case BITWISE_XOR_EQUAL: return 5; case QUESTION: return 10; case LOGICAL_OR: return 15; case LOGICAL_AND: return 20; case BITWISE_OR: case BITWISE_AND: case BITWISE_XOR: return 22; case COMPARE_IDENTICAL: case COMPARE_NOT_IDENTICAL: return 24; case COMPARE_NOT_EQUAL: case COMPARE_EQUAL: case COMPARE_LESS_THAN: case COMPARE_LESS_THAN_EQUAL: case COMPARE_GREATER_THAN: case COMPARE_GREATER_THAN_EQUAL: case COMPARE_TO: case FIND_REGEX: case MATCH_REGEX: case KEYWORD_INSTANCEOF: case COMPARE_NOT_INSTANCEOF: return 25; case DOT_DOT: case DOT_DOT_DOT: return 30; case LEFT_SHIFT: case RIGHT_SHIFT: case RIGHT_SHIFT_UNSIGNED: return 35; case PLUS: case MINUS: return 40; case MULTIPLY: case DIVIDE: case INTDIV: case MOD: return 45; case NOT: case REGEX_PATTERN: return 50; case SYNTH_CAST: return 55; case PLUS_PLUS: case MINUS_MINUS: case PREFIX_PLUS_PLUS: case PREFIX_MINUS_MINUS: case POSTFIX_PLUS_PLUS: case POSTFIX_MINUS_MINUS: return 65; case PREFIX_PLUS: case PREFIX_MINUS: return 70; case POWER: return 72; case SYNTH_METHOD: case LEFT_SQUARE_BRACKET: return 75; case DOT: case NAVIGATE: return 80; case KEYWORD_NEW: return 85; } if( throwIfInvalid ) { throw new GroovyBugError( "precedence requested for non-operator" ); } return -1; } //--------------------------------------------------------------------------- // TEXTS private static final Map<Integer,String> TEXTS = new HashMap<Integer,String>(); // symbol/keyword type -> text private static final Map<String,Integer> LOOKUP = new HashMap<String,Integer>(); // text -> symbol/keyword type private static final Set<String> KEYWORDS = new HashSet<String>(); // valid keywords public static Collection<String> getKeywords() { return Collections.unmodifiableSet(KEYWORDS); } public static boolean isKeyword(final String text) { return KEYWORDS.contains(text); } /** * Returns the type for the specified symbol/keyword text. Returns UNKNOWN * if the text isn't found. You can filter finds on a type. */ public static int lookup( String text, int filter ) { int type = UNKNOWN; if( LOOKUP.containsKey(text) ) { type = LOOKUP.get(text); if( filter != UNKNOWN && !ofType(type, filter) ) { type = UNKNOWN; } } return type; } /** * Returns the type for the specified keyword text. Returns UNKNOWN * if the text isn't found. */ public static int lookupKeyword( String text ) { return lookup( text, KEYWORD ); } /** * Returns the type for the specified symbol text. Returns UNKNOWN * if the text isn't found. */ public static int lookupSymbol( String text ) { return lookup( text, SYMBOL ); } /** * Returns the text for the specified type. Returns "" if the * text isn't found. */ public static String getText( int type ) { String text = ""; if( TEXTS.containsKey(type) ) { text = TEXTS.get( type ); } return text; } /** * Adds a element to the TEXTS and LOOKUP. */ private static void addTranslation( String text, int type ) { TEXTS.put( type, text ); LOOKUP.put( text, type ); } /** * Adds a element to the KEYWORDS, TEXTS and LOOKUP. */ private static void addKeyword( String text, int type ) { KEYWORDS.add(text); addTranslation(text, type); } static { // // SYMBOLS addTranslation( "\n" , NEWLINE ); addTranslation( "{" , LEFT_CURLY_BRACE ); addTranslation( "}" , RIGHT_CURLY_BRACE ); addTranslation( "[" , LEFT_SQUARE_BRACKET ); addTranslation( "]" , RIGHT_SQUARE_BRACKET ); addTranslation( "(" , LEFT_PARENTHESIS ); addTranslation( ")" , RIGHT_PARENTHESIS ); addTranslation( "." , DOT ); addTranslation( ".." , DOT_DOT ); addTranslation( "..." , DOT_DOT_DOT ); addTranslation( "->" , NAVIGATE ); addTranslation( "=~" , FIND_REGEX ); addTranslation( "==~" , MATCH_REGEX ); addTranslation( "~" , REGEX_PATTERN ); addTranslation( "=" , EQUAL ); addTranslation( "!=" , COMPARE_NOT_EQUAL ); addTranslation( "===" , COMPARE_IDENTICAL ); addTranslation( "!==" , COMPARE_NOT_IDENTICAL ); addTranslation( "==" , COMPARE_EQUAL ); addTranslation( "<" , COMPARE_LESS_THAN ); addTranslation( "<=" , COMPARE_LESS_THAN_EQUAL ); addTranslation( ">" , COMPARE_GREATER_THAN ); addTranslation( ">=" , COMPARE_GREATER_THAN_EQUAL ); addTranslation( "<=>" , COMPARE_TO ); addTranslation( "!in" , COMPARE_NOT_IN ); addTranslation( "!instanceof" , COMPARE_NOT_INSTANCEOF ); addTranslation( "!" , NOT ); addTranslation( "||" , LOGICAL_OR ); addTranslation( "&&" , LOGICAL_AND ); addTranslation( "||=" , LOGICAL_OR_EQUAL ); addTranslation( "&&=" , LOGICAL_AND_EQUAL ); addTranslation( "+" , PLUS ); addTranslation( "-" , MINUS ); addTranslation( "*" , MULTIPLY ); addTranslation( "/" , DIVIDE ); addTranslation( "\\" , INTDIV ); addTranslation( "%" , MOD ); addTranslation( "**" , POWER ); addTranslation( "+=" , PLUS_EQUAL ); addTranslation( "-=" , MINUS_EQUAL ); addTranslation( "*=" , MULTIPLY_EQUAL ); addTranslation( "/=" , DIVIDE_EQUAL ); addTranslation( "\\=" , INTDIV_EQUAL ); addTranslation( "%=" , MOD_EQUAL ); addTranslation( "**=" , POWER_EQUAL ); addTranslation( "?=" , ELVIS_EQUAL ); addTranslation( "++" , PLUS_PLUS ); addTranslation( "--" , MINUS_MINUS ); addTranslation( "<<" , LEFT_SHIFT ); addTranslation( ">>" , RIGHT_SHIFT ); addTranslation( ">>>" , RIGHT_SHIFT_UNSIGNED ); addTranslation( "<<=" , LEFT_SHIFT_EQUAL ); addTranslation( ">>=" , RIGHT_SHIFT_EQUAL ); addTranslation( ">>>=" , RIGHT_SHIFT_UNSIGNED_EQUAL ); addTranslation( "&" , BITWISE_AND ); addTranslation( "^" , BITWISE_XOR ); addTranslation( "|=" , BITWISE_OR_EQUAL ); addTranslation( "&=" , BITWISE_AND_EQUAL ); addTranslation( "^=" , BITWISE_XOR_EQUAL ); addTranslation( "," , COMMA ); addTranslation( ":" , COLON ); addTranslation( ";" , SEMICOLON ); addTranslation( "?" , QUESTION ); addTranslation( "|" , PIPE ); addTranslation( "${}" , GSTRING_EXPRESSION_START ); // // Keywords addKeyword( "abstract" , KEYWORD_ABSTRACT ); addKeyword( "as" , KEYWORD_AS ); addKeyword( "assert" , KEYWORD_ASSERT ); addKeyword( "break" , KEYWORD_BREAK ); addKeyword( "case" , KEYWORD_CASE ); addKeyword( "catch" , KEYWORD_CATCH ); addKeyword( "class" , KEYWORD_CLASS ); addKeyword( "const" , KEYWORD_CONST ); addKeyword( "continue" , KEYWORD_CONTINUE ); addKeyword( "def" , KEYWORD_DEF ); addKeyword( "defmacro" , KEYWORD_DEF ); // xxx br defmacro addKeyword( "default" , KEYWORD_DEFAULT ); addKeyword( "do" , KEYWORD_DO ); addKeyword( "else" , KEYWORD_ELSE ); addKeyword( "extends" , KEYWORD_EXTENDS ); addKeyword( "final" , KEYWORD_FINAL ); addKeyword( "finally" , KEYWORD_FINALLY ); addKeyword( "for" , KEYWORD_FOR ); addKeyword( "goto" , KEYWORD_GOTO ); addKeyword( "if" , KEYWORD_IF ); addKeyword( "in" , KEYWORD_IN ); addKeyword( "implements" , KEYWORD_IMPLEMENTS ); addKeyword( "import" , KEYWORD_IMPORT ); addKeyword( "instanceof" , KEYWORD_INSTANCEOF ); addKeyword( "interface" , KEYWORD_INTERFACE ); addKeyword( "mixin" , KEYWORD_MIXIN ); addKeyword( "native" , KEYWORD_NATIVE ); addKeyword( "new" , KEYWORD_NEW ); addKeyword( "package" , KEYWORD_PACKAGE ); addKeyword( "private" , KEYWORD_PRIVATE ); addKeyword( "property" , KEYWORD_PROPERTY ); addKeyword( "protected" , KEYWORD_PROTECTED ); addKeyword( "public" , KEYWORD_PUBLIC ); addKeyword( "return" , KEYWORD_RETURN ); addKeyword( "static" , KEYWORD_STATIC ); addKeyword( "super" , KEYWORD_SUPER ); addKeyword( "switch" , KEYWORD_SWITCH ); addKeyword( "synchronized", KEYWORD_SYNCHRONIZED ); addKeyword( "this" , KEYWORD_THIS ); addKeyword( "throw" , KEYWORD_THROW ); addKeyword( "throws" , KEYWORD_THROWS ); addKeyword( "transient" , KEYWORD_TRANSIENT ); addKeyword( "try" , KEYWORD_TRY ); addKeyword( "volatile" , KEYWORD_VOLATILE ); addKeyword( "while" , KEYWORD_WHILE ); addKeyword( "true" , KEYWORD_TRUE ); addKeyword( "false" , KEYWORD_FALSE ); addKeyword( "null" , KEYWORD_NULL ); addKeyword( "void" , KEYWORD_VOID ); addKeyword( "boolean" , KEYWORD_BOOLEAN ); addKeyword( "byte" , KEYWORD_BYTE ); addKeyword( "int" , KEYWORD_INT ); addKeyword( "short" , KEYWORD_SHORT ); addKeyword( "long" , KEYWORD_LONG ); addKeyword( "float" , KEYWORD_FLOAT ); addKeyword( "double" , KEYWORD_DOUBLE ); addKeyword( "char" , KEYWORD_CHAR ); } //--------------------------------------------------------------------------- // DESCRIPTIONS private static final Map<Integer,String> DESCRIPTIONS = new HashMap<Integer,String>(); /** * Gets the description for the specified type. */ public static String getDescription( int type ) { if (DESCRIPTIONS.containsKey(type)) { return DESCRIPTIONS.get(type); } return "<>"; } /** * Adds a description to the set. */ private static void addDescription(int type, String description) { if (description.startsWith("<") && description.endsWith(">")) { DESCRIPTIONS.put(type, description); } else { DESCRIPTIONS.put(type, '"' + description + '"'); } } static { Iterator<String> iterator = LOOKUP.keySet().iterator(); while( iterator.hasNext() ) { String text = iterator.next(); int key = LOOKUP.get(text); addDescription( key, text ); } addDescription( NEWLINE , "<newline>" ); addDescription( PREFIX_PLUS_PLUS , "<prefix ++>" ); addDescription( POSTFIX_PLUS_PLUS , "<postfix ++>" ); addDescription( PREFIX_MINUS_MINUS , "<prefix -->" ); addDescription( POSTFIX_MINUS_MINUS , "<postfix -->" ); addDescription( PREFIX_PLUS , "<positive>" ); addDescription( PREFIX_MINUS , "<negative>" ); addDescription( STRING , "<string literal>" ); addDescription( IDENTIFIER , "<identifier>" ); addDescription( INTEGER_NUMBER , "<integer>" ); addDescription( DECIMAL_NUMBER , "<decimal>" ); addDescription( SYNTH_COMPILATION_UNIT , "<compilation unit>" ); addDescription( SYNTH_CLASS , "<class>" ); addDescription( SYNTH_INTERFACE , "<interface>" ); addDescription( SYNTH_MIXIN , "<mixin>" ); addDescription( SYNTH_METHOD , "<method>" ); addDescription( SYNTH_METHOD_CALL , "<method call>" ); addDescription( SYNTH_PROPERTY , "<property>" ); addDescription( SYNTH_PARAMETER_DECLARATION , "<parameter>" ); addDescription( SYNTH_LIST , "<list>" ); addDescription( SYNTH_MAP , "<map>" ); addDescription( SYNTH_TUPLE , "<tuple>" ); addDescription( SYNTH_GSTRING , "<gstring>" ); addDescription( SYNTH_CAST , "<cast>" ); addDescription( SYNTH_BLOCK , "<block>" ); addDescription( SYNTH_CLOSURE , "<closure>" ); addDescription( SYNTH_TERNARY , "<ternary>" ); addDescription( SYNTH_LABEL , "<label>" ); addDescription( SYNTH_VARIABLE_DECLARATION , "<variable declaration>" ); addDescription( GSTRING_START , "<start of gstring tokens>" ); addDescription( GSTRING_END , "<end of gstring tokens>" ); addDescription( GSTRING_EXPRESSION_START , "<start of gstring expression>"); addDescription( GSTRING_EXPRESSION_END , "<end of gstring expression>" ); addDescription( ASSIGNMENT_OPERATOR , "<assignment operator>" ); addDescription( COMPARISON_OPERATOR , "<comparison operator>" ); addDescription( MATH_OPERATOR , "<math operator>" ); addDescription( LOGICAL_OPERATOR , "<logical operator>" ); addDescription( BITWISE_OPERATOR , "<bitwise operator>" ); addDescription( RANGE_OPERATOR , "<range operator>" ); addDescription( REGEX_COMPARISON_OPERATOR , "<regex comparison operator>" ); addDescription( DEREFERENCE_OPERATOR , "<dereference operator>" ); addDescription( PREFIX_OPERATOR , "<prefix operator>" ); addDescription( POSTFIX_OPERATOR , "<postfix operator>" ); addDescription( INFIX_OPERATOR , "<infix operator>" ); addDescription( KEYWORD , "<keyword>" ); addDescription( LITERAL , "<literal>" ); addDescription( NUMBER , "<number>" ); addDescription( NAMED_VALUE , "<named value>" ); addDescription( TRUTH_VALUE , "<truth value>" ); addDescription( PRIMITIVE_TYPE , "<primitive type>" ); addDescription( CREATABLE_PRIMITIVE_TYPE , "<creatable primitive type>" ); addDescription( LOOP , "<loop>" ); addDescription( RESERVED_KEYWORD , "<reserved keyword>" ); addDescription( SYNTHETIC , "<synthetic>" ); addDescription( TYPE_DECLARATION , "<type declaration>" ); addDescription( DECLARATION_MODIFIER , "<declaration modifier>" ); addDescription( TYPE_NAME , "<type name>" ); addDescription( CREATABLE_TYPE_NAME , "<creatable type name>" ); addDescription( MATCHED_CONTAINER , "<matched container>" ); addDescription( LEFT_OF_MATCHED_CONTAINER , "<left of matched container>" ); addDescription( RIGHT_OF_MATCHED_CONTAINER , "<right of matched container>" ); addDescription( SWITCH_ENTRIES , "<valid in a switch body>" ); } private static final int[] ASSIGNMENT_TYPES; static { ASSIGNMENT_TYPES = new int[] { EQUAL, BITWISE_AND_EQUAL, BITWISE_OR_EQUAL, BITWISE_XOR_EQUAL, PLUS_EQUAL, MINUS_EQUAL, MULTIPLY_EQUAL, DIVIDE_EQUAL, INTDIV_EQUAL, MOD_EQUAL, POWER_EQUAL, LEFT_SHIFT_EQUAL, RIGHT_SHIFT_EQUAL, RIGHT_SHIFT_UNSIGNED_EQUAL, ELVIS_EQUAL }; Arrays.sort(ASSIGNMENT_TYPES); } public static boolean isAssignment(int type) { return Arrays.binarySearch(ASSIGNMENT_TYPES, type) >= 0; } }