token: function()

in nifi-frontend/src/main/frontend/apps/nifi/src/app/ui/common/property-table/editors/nf-editor/modes/nfel.ts [144:678]


                token: function (stream: StringStream, states: any) {
                    // consume any whitespace
                    if (stream.eatSpace()) {
                        return null;
                    }

                    // if we've hit the end of the line
                    if (stream.eol()) {
                        return null;
                    }

                    // get the current character
                    const current: string | null = stream.peek();

                    // if we've hit some comments... will consume the remainder of the line
                    if (current === '#') {
                        // consume the pound
                        stream.next();

                        const afterPound: string | null = stream.peek();
                        if (afterPound !== '{') {
                            stream.skipToEnd();
                            return 'comment';
                        } else {
                            // unconsume the pound
                            stream.backUp(1);
                        }
                    }

                    // get the current state
                    const state: any = states.get();

                    // the current input is invalid
                    if (state.context === NfEl.INVALID) {
                        stream.skipToEnd();
                        return null;
                    }

                    // within an expression
                    if (state.context === NfEl.EXPRESSION) {
                        const attributeOrSubjectlessFunctionExpression =
                            /^[^'"#${}()[\],:;\/*\\\s\t\r\n0-9][^'"#${}()[\],:;\/*\\\s\t\r\n]*/;

                        // attempt to extract a function name
                        const attributeOrSubjectlessFunctionName: string[] = stream.match(
                            attributeOrSubjectlessFunctionExpression,
                            false
                        );

                        // if the result returned a match
                        if (
                            attributeOrSubjectlessFunctionName !== null &&
                            attributeOrSubjectlessFunctionName.length === 1
                        ) {
                            // consume the entire token to better support suggest below
                            stream.match(attributeOrSubjectlessFunctionExpression);

                            // if the result returned a match and is followed by a (
                            if (
                                self.subjectlessFunctionRegex.test(attributeOrSubjectlessFunctionName[0]) &&
                                stream.peek() === '('
                            ) {
                                // --------------------
                                // subjectless function
                                // --------------------

                                // context change to function
                                state.context = NfEl.ARGUMENTS;

                                // style for function
                                return 'builtin';
                            } else {
                                // ---------------------
                                // attribute or function
                                // ---------------------

                                // context change to function or subject... not sure yet
                                state.context = NfEl.SUBJECT_OR_FUNCTION;

                                // this could be an attribute or a partial function name... style as attribute until we know
                                return 'variable-2';
                            }
                        } else if (current === "'" || current === '"') {
                            // --------------
                            // string literal
                            // --------------

                            // handle the string literal
                            const expressionStringResult: string | null = self.handleStringLiteral(stream, state);

                            // considered a quoted variable
                            if (expressionStringResult !== null) {
                                // context change to function
                                state.context = NfEl.SUBJECT;
                            }

                            return expressionStringResult;
                        } else if (current === '$') {
                            // -----------------
                            // nested expression
                            // -----------------

                            const expressionDollarResult: string | null = self.handleStart(
                                '$',
                                NfEl.EXPRESSION,
                                stream,
                                states
                            );

                            // if we've found an embedded expression we need to...
                            if (expressionDollarResult !== null) {
                                // transition back to subject when this expression completes
                                state.context = NfEl.SUBJECT;
                            }

                            return expressionDollarResult;
                        } else if (current === '#' && self.parametersSupported) {
                            // --------------------------
                            // nested parameter reference
                            // --------------------------

                            // handle the nested parameter reference
                            const parameterReferenceResult = self.handleStart('#', NfEl.PARAMETER, stream, states);

                            // if we've found an embedded parameter reference we need to...
                            if (parameterReferenceResult !== null) {
                                // transition back to subject when this parameter reference completes
                                state.context = NfEl.SUBJECT;
                            }

                            return parameterReferenceResult;
                        } else if (current === '}') {
                            // -----------------
                            // end of expression
                            // -----------------

                            // consume the close
                            stream.next();

                            // signifies the end of an expression
                            if (typeof states.pop() === 'undefined') {
                                return null;
                            } else {
                                // style as expression
                                return 'bracket';
                            }
                        } else {
                            // ----------
                            // unexpected
                            // ----------

                            // consume to move along
                            stream.skipToEnd();
                            state.context = NfEl.INVALID;

                            // unexpected...
                            return null;
                        }
                    }

                    // within a subject
                    if (state.context === NfEl.SUBJECT || state.context === NfEl.SUBJECT_OR_FUNCTION) {
                        // if the next character indicates the start of a function call
                        if (current === ':') {
                            // -------------------------
                            // trigger for function name
                            // -------------------------

                            // consume the colon and update the context
                            stream.next();
                            state.context = NfEl.FUNCTION;

                            // consume any addition whitespace
                            stream.eatSpace();

                            // don't style
                            return null;
                        } else if (current === '}') {
                            // -----------------
                            // end of expression
                            // -----------------

                            // consume the close
                            stream.next();

                            // signifies the end of an expression
                            if (typeof states.pop() === 'undefined') {
                                return null;
                            } else {
                                // style as expression
                                return 'bracket';
                            }
                        } else {
                            // ----------
                            // unexpected
                            // ----------

                            // consume to move along
                            stream.skipToEnd();
                            state.context = NfEl.INVALID;

                            // unexpected...
                            return null;
                        }
                    }

                    // within a function
                    if (state.context === NfEl.FUNCTION) {
                        // attempt to extract a function name
                        const functionName = stream.match(/^[a-zA-Z]+/, false);

                        // if the result returned a match
                        if (functionName !== null && functionName.length === 1) {
                            // consume the entire token to ensure the whole function
                            // name is matched. this is an issue with functions like
                            // substring and substringAfter since 'substringA' would
                            // match the former and when we really want to autocomplete
                            // against the latter.
                            stream.match(/^[a-zA-Z]+/);

                            // see if this matches a known function and is followed by (
                            if (self.functionRegex.test(functionName[0]) && stream.peek() === '(') {
                                // --------
                                // function
                                // --------

                                // change context to arguments
                                state.context = NfEl.ARGUMENTS;

                                // style for function
                                return 'builtin';
                            } else {
                                // ------------------------------
                                // maybe function... not sure yet
                                // ------------------------------

                                // not sure yet...
                                return null;
                            }
                        } else {
                            // ----------
                            // unexpected
                            // ----------

                            // consume and move along
                            stream.skipToEnd();
                            state.context = NfEl.INVALID;

                            // unexpected...
                            return null;
                        }
                    }

                    // within arguments
                    if (state.context === NfEl.ARGUMENTS) {
                        if (current === '(') {
                            // --------------
                            // argument start
                            // --------------

                            // consume the open paranthesis
                            stream.next();

                            // change context to handle an argument
                            state.context = NfEl.ARGUMENT;

                            // start of arguments
                            return null;
                        } else if (current === ')') {
                            // --------------
                            // argument close
                            // --------------

                            // consume the close paranthesis
                            stream.next();

                            // change context to subject for potential chaining
                            state.context = NfEl.SUBJECT;

                            // end of arguments
                            return null;
                        } else if (current === ',') {
                            // ------------------
                            // argument separator
                            // ------------------

                            // consume the comma
                            stream.next();

                            // change context back to argument
                            state.context = NfEl.ARGUMENT;

                            // argument separator
                            return null;
                        } else {
                            // ----------
                            // unexpected
                            // ----------

                            // consume and move along
                            stream.skipToEnd();
                            state.context = NfEl.INVALID;

                            // unexpected...
                            return null;
                        }
                    }

                    // within a specific argument
                    if (state.context === NfEl.ARGUMENT) {
                        if (current === "'" || current === '"') {
                            // --------------
                            // string literal
                            // --------------

                            // handle the string literal
                            const argumentStringResult: string | null = self.handleStringLiteral(stream, state);

                            // successfully processed a string literal...
                            if (argumentStringResult !== null) {
                                // change context back to arguments
                                state.context = NfEl.ARGUMENTS;
                            }

                            return argumentStringResult;
                        } else if (
                            stream.match(
                                /^[-\+]?((([0-9]+\.[0-9]*)([eE][+-]?([0-9])+)?)|((\.[0-9]+)([eE][+-]?([0-9])+)?)|(([0-9]+)([eE][+-]?([0-9])+)))/
                            )
                        ) {
                            // -------------
                            // Decimal value
                            // -------------
                            // This matches the following ANTLR spec for deciamls
                            //
                            // DECIMAL :     OP? ('0'..'9')+ '.' ('0'..'9')* EXP?    ^([0-9]+\.[0-9]*)([eE][+-]?([0-9])+)?
                            //             | OP? '.' ('0'..'9')+ EXP?
                            //             | OP? ('0'..'9')+ EXP;
                            //
                            // fragment OP: ('+'|'-');
                            // fragment EXP : ('e'|'E') ('+'|'-')? ('0'..'9')+ ;

                            // change context back to arguments
                            state.context = NfEl.ARGUMENTS;

                            // style for decimal (use same as number)
                            return 'number';
                        } else if (stream.match(/^[-\+]?[0-9]+/)) {
                            // -------------
                            // integer value
                            // -------------

                            // change context back to arguments
                            state.context = NfEl.ARGUMENTS;

                            // style for integers
                            return 'number';
                        } else if (stream.match(/^((true)|(false))/)) {
                            // -------------
                            // boolean value
                            // -------------

                            // change context back to arguments
                            state.context = NfEl.ARGUMENTS;

                            // style for boolean (use same as number)
                            return 'number';
                        } else if (current === ')') {
                            // ----------------------------------
                            // argument close (zero arg function)
                            // ----------------------------------

                            // consume the close parenthesis
                            stream.next();

                            // change context to subject for potential chaining
                            state.context = NfEl.SUBJECT;

                            // end of arguments
                            return null;
                        } else if (current === '$') {
                            // -----------------
                            // nested expression
                            // -----------------

                            // handle the nested expression
                            const argumentDollarResult: string | null = self.handleStart(
                                '$',
                                NfEl.EXPRESSION,
                                stream,
                                states
                            );

                            // if we've found an embedded expression we need to...
                            if (argumentDollarResult !== null) {
                                // transition back to arguments when then expression completes
                                state.context = NfEl.ARGUMENTS;
                            }

                            return argumentDollarResult;
                        } else if (current === '#' && self.parametersSupported) {
                            // --------------------------
                            // nested parameter reference
                            // --------------------------

                            // handle the nested parameter reference
                            const parameterReferenceResult: string | null = self.handleStart(
                                '#',
                                NfEl.PARAMETER,
                                stream,
                                states
                            );

                            // if we've found an embedded parameter reference we need to...
                            if (parameterReferenceResult !== null) {
                                // transition back to arguments when this parameter reference completes
                                state.context = NfEl.ARGUMENTS;
                            }

                            return parameterReferenceResult;
                        } else {
                            // ----------
                            // unexpected
                            // ----------

                            // consume and move along
                            stream.skipToEnd();
                            state.context = NfEl.INVALID;

                            // unexpected...
                            return null;
                        }
                    }

                    // within a parameter reference
                    if (
                        state.context === NfEl.PARAMETER ||
                        state.context === NfEl.SINGLE_QUOTE_PARAMETER ||
                        state.context === NfEl.DOUBLE_QUOTE_PARAMETER
                    ) {
                        // attempt to extract a parameter name
                        const parameterName: string[] = stream.match(self.parameterKeyRegex, false);

                        // if the result returned a match
                        if (parameterName !== null && parameterName.length === 1) {
                            // consume the entire token to ensure the whole function
                            // name is matched. this is an issue with functions like
                            // substring and substringAfter since 'substringA' would
                            // match the former and when we really want to autocomplete
                            // against the latter.
                            stream.match(self.parameterKeyRegex);

                            // see if this matches a known function and is followed by (
                            if (self.parameterRegex.test(parameterName[0])) {
                                // ------------------
                                // resolved parameter
                                // ------------------

                                // style for function
                                return 'builtin';
                            } else {
                                // --------------------
                                // unresolved parameter
                                // --------------------

                                // style for function
                                return 'string';
                            }
                        }

                        if (state.context === NfEl.SINGLE_QUOTE_PARAMETER) {
                            return self.handleParameterEnd(stream, state, states, () => current === "'");
                        }

                        if (state.context === NfEl.DOUBLE_QUOTE_PARAMETER) {
                            return self.handleParameterEnd(stream, state, states, () => current === '"');
                        }

                        if (current === '}') {
                            // -----------------
                            // end of expression
                            // -----------------

                            // consume the close
                            stream.next();

                            // signifies the end of an parameter reference
                            if (typeof states.pop() === 'undefined') {
                                return null;
                            } else {
                                // style as expression
                                return 'bracket';
                            }
                        } else {
                            // ----------
                            // unexpected
                            // ----------

                            // consume and move along
                            stream.skipToEnd();
                            state.context = NfEl.INVALID;

                            // unexpected...
                            return null;
                        }
                    }

                    // signifies the potential start of an expression
                    if (current === '$') {
                        return self.handleStart('$', NfEl.EXPRESSION, stream, states);
                    }

                    // signifies the potential start of a parameter reference
                    if (current === '#' && self.parametersSupported) {
                        return self.handleStart('#', NfEl.PARAMETER, stream, states);
                    }

                    // signifies the end of an expression
                    if (current === '}') {
                        stream.next();
                        if (typeof states.pop() === 'undefined') {
                            return null;
                        } else {
                            return 'bracket';
                        }
                    }

                    // ----------------------------------------------------------
                    // extra characters that are around expression[s] end up here
                    // ----------------------------------------------------------

                    // consume the character to keep things moving along
                    stream.next();
                    return null;
                }