static List getInvokerTokens()

in subprojects/groovy-groovysh/src/main/groovy/org/apache/groovy/groovysh/completion/antlr4/ReflectionCompleter.groovy [256:384]


    static List<Token> getInvokerTokens(final List<Token> tokens) {
        int validIndex = tokens.size()
        if (validIndex == 0) {
            return []
        }
        // implementation goes backwards on token list, adding strings
        // to be evaluated later
        // need to collect using Strings, to support evaluation of string literals
        Stack<Integer> expectedOpeners = new Stack<Integer>()
        Token lastToken = null
        outerloop:
        for (Token loopToken in tokens.reverse()) {
            switch (loopToken.type) {
            // a combination of any of these can be evaluated without side effects
            // this just avoids any parentheses,
            // could maybe be extended further if harmless parentheses can be detected .
            // This allows already a lot of powerful simple completions, like [foo: Baz.bar]['foo'].
                case StringLiteral:
                    // must escape String for evaluation, need the original string e.g. for mapping key
                    break
                case LPAREN:
                    if (expectedOpeners.empty()) {
                        break outerloop
                    }
                    if (expectedOpeners.pop() != LPAREN) {
                        return []
                    }
                    break
                case LBRACK:
                    if (expectedOpeners.empty()) {
                        break outerloop
                    }
                    if (expectedOpeners.pop() != LBRACK) {
                        return []
                    }
                    break
                case RBRACK:
                    expectedOpeners.push(LBRACK)
                    break
                case RPAREN:
                    expectedOpeners.push(LPAREN)
                    break
            // tokens which indicate we have reached the beginning of a statement
            // operator tokens (must not be evaluated, as they can have side effects via evil overriding
                case SPACESHIP:
                case EQUAL:
                case NOTEQUAL:
                case ASSIGN:
                case GT:
                case LT:
                case GE:
                case LE:
                case ADD:
                case ADD_ASSIGN:
                case SUB:
                case SUB_ASSIGN:
                case MUL:
                case MUL_ASSIGN:
                case DIV:
                case DIV_ASSIGN:
                case BITOR:
                case OR_ASSIGN:
                case BITAND:
                case AND_ASSIGN:
                case XOR:
                case XOR_ASSIGN:
                case BITNOT:
                case OR:
                case AND:
                case NOT:
                case IN:
                case INSTANCEOF:
                    if (expectedOpeners.empty()) {
                        break outerloop
                    }
                    break
            // tokens which indicate we have reached the beginning of a statement
                case LBRACE:
                case SEMI:
//                case STRING_CTOR_START:
                case GStringBegin:
                    break outerloop
            // tokens we accept
                case Identifier:
                case CapitalizedIdentifier:
                    if (lastToken) {
                        if (lastToken.type == LPAREN) {
                            //Method invocation,must be avoided
                            return []
                        }
                        if (lastToken.type == Identifier || lastToken.type == CapitalizedIdentifier) {
                            // could be attempt to invoke closure like 'foo.each bar.baz'
                            return []
                        }
                    }
                    break
            // may begin expression when outside brackets (from back)
                case RANGE_INCLUSIVE:
                case RANGE_EXCLUSIVE_LEFT:
                case RANGE_EXCLUSIVE_RIGHT:
                case RANGE_EXCLUSIVE_FULL:
                case COLON:
                case COMMA:
                    if (expectedOpeners.empty()) {
                        break outerloop
                    }
            // harmless literals
                case BooleanLiteral:
//                case LITERAL_true:
//                case LITERAL_false:
                case BuiltInPrimitiveType:
//                case NUM_INT:
//                case NUM_FLOAT:
//                case NUM_LONG:
//                case NUM_DOUBLE:
//                case NUM_BIG_INT:
//                case NUM_BIG_DECIMAL:
                case METHOD_POINTER:
                case DOT:
                case SAFE_DOT:
                    break
                default:
                    return null
            } // end switch
            validIndex--
            lastToken = loopToken
        } // end for
        return tokens[(validIndex)..-1]
    }