private ExpressionOperator processLogic(final List<AbstractToken> tokens) {
            // Replace all term tokens with expression items.
            List<Object> out = gatherTerms(tokens);

            // Apply NOT operators.
            out = applyNotOperators(out);

            // Apply AND operators.
            out = applyAndOrOperators(out, TokenType.AND, Op.AND);

            // Apply OR operators.
            out = applyAndOrOperators(out, TokenType.OR, Op.OR);

            // Gather final expression items.
            final List<ExpressionItem> list = new ArrayList<>(out.size());
            for (final Object object : out) {
                if (object instanceof final ExpressionItem expressionItem) {
                    list.add(expressionItem);
                } else if (object instanceof final AbstractToken token) {
                    throw new TokenException(token, "Unexpected token");
                }
            }

            if (list.size() == 1 && list.getFirst() instanceof final ExpressionOperator expressionOperator) {
                return expressionOperator;
            }

            return ExpressionOperator
                    .builder()
                    .op(Op.AND)
                    .children(list)
                    .build();
        }
@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