private void processSelect(final KeywordGroup keywordGroup,
                                   final Map<String, Sort> sortMap,
                                   final Map<String, Integer> groupMap,
                                   final Map<String, IncludeExcludeFilter> filterMap,
                                   final TableSettings.Builder tableSettingsBuilder,
                                   final Query.Builder queryBuilder) {
            final List<AbstractToken> children = keywordGroup.getChildren();
            AbstractToken fieldToken = null;
            Expression fieldExpression = null;
            String columnName = null;
            boolean afterAs = false;
            boolean doneAs = false;
            final Map<String, AtomicInteger> columnCount = new HashMap<>();
            final List<Column> columns = new ArrayList<>();

            for (final AbstractToken token : children) {

                if (TokenType.FUNCTION_GROUP.equals(token.getTokenType())) {
                    // If a function has been used for a column then parse it.
                    if (fieldExpression != null || fieldToken != null) {
                        throw new TokenException(token, "Unexpected expression");
                    }

                    final FunctionGroup functionGroup = (FunctionGroup) token;
                    columnName = functionGroup.getText();
                    final ExpressionParser expressionParser = new ExpressionParser(new ParamFactory(expressionMap));
                    try {
                        fieldExpression = expressionParser
                                .parse(expressionContext, fieldIndex, Collections.singletonList(token));
                    } catch (final ParseException e) {
                        throw new TokenException(keywordGroup, e.getMessage());
                    }

                } else if (TokenType.isString(token) || TokenType.MULTIPLICATION.equals(token.getTokenType())) {
                    // If we have a string then it is either a field name or a column name if provided after AS.
                    // Note that we also allow `*` (TokenType.MULTIPLICATION) to add `all` fields.
                    if (afterAs) {
                        if (doneAs) {
                            throw new TokenException(token, "Syntax exception, unexpected column name");
                        } else {
                            columnName = token.getUnescapedText();
                            doneAs = true;
                        }
                    } else if (fieldToken == null) {
                        if (fieldExpression != null) {
                            throw new TokenException(token, "Unexpected field");
                        }

                        fieldToken = token;
                        columnName = token.getUnescapedText();
                    } else {
                        throw new TokenException(token, "Syntax exception, expected AS");
                    }

                } else if (TokenType.AS.equals(token.getTokenType())) {
                    // We found AS so prepare for a column name.
                    if (fieldToken == null && fieldExpression == null) {
                        throw new TokenException(token, "Syntax exception, expected field name or expression");
                    }
                    if (afterAs) {
                        throw new TokenException(token, "Unexpected AS");
                    }

                    afterAs = true;

                } else if (TokenType.COMMA.equals(token.getTokenType())) {
                    addColumn(sortMap,
                            groupMap,
                            filterMap,
                            queryBuilder,
                            columnCount,
                            columns,
                            token,
                            fieldToken,
                            columnName,
                            fieldExpression);

                    fieldToken = null;
                    fieldExpression = null;
                    columnName = null;
                    afterAs = false;
                    doneAs = false;
                }
            }

            // Add final field if we have one.
            if (fieldToken != null || fieldExpression != null) {
                addColumn(sortMap,
                        groupMap,
                        filterMap,
                        queryBuilder,
                        columnCount,
                        columns,
                        fieldToken,
                        fieldToken,
                        columnName,
                        fieldExpression);
            }

            // Modify columns if we have a time window.
            final List<Column> modifiedColumns = optionalCompiledWindow.map(compiledWindow ->
                    compiledWindow.addPeriodColumns(columns, expressionMap)).orElse(columns);
            tableSettingsBuilder.addColumns(modifiedColumns);
        }
@param isLenient Ignores any additional child tokens found. This is to support getting just
                 the data source from a partially written query, which is likely to not be
                 a valid query. For example, you may type
                 <pre>{@code from View sel}</pre> and at this point do code completion on 'sel'
                 so if this method is called it will treat 'sel as an extra child.
stroom-query/stroom-query-common/src/main/java/stroom/query/language/SearchRequestFactory.java