private ExpressionTerm createInTerm(
                final List<AbstractToken> tokens) {
            ExpressionTerm expressionTerm;

            final AbstractToken fieldToken = tokens.get(0);
            final AbstractToken conditionToken = tokens.get(1);
            final AbstractToken valueToken = tokens.get(2);

            if (TokenType.DICTIONARY.equals(valueToken.getTokenType())) {
                if (tokens.size() < 4) {
                    throw new TokenException(valueToken, "Expected dictionary name");
                }
                if (tokens.size() > 4) {
                    throw new TokenException(tokens.get(4), "Unexpected token");
                }
                final AbstractToken dictionaryNameToken = tokens.get(3);
                final Set<TokenType> stringTypes = Set.of(
                        TokenType.SINGLE_QUOTED_STRING,
                        TokenType.DOUBLE_QUOTED_STRING,
                        TokenType.STRING);
                if (!stringTypes.contains(dictionaryNameToken.getTokenType())) {
                    throw new TokenException(dictionaryNameToken, "Expected dictionary name not " +
                                                                  dictionaryNameToken.getTokenType() +
                                                                  " token");
                }
                final String field = fieldToken.getUnescapedText();
                final String dictionaryName = dictionaryNameToken.getUnescapedText().trim();
                final DocRef dictionaryRef;
                try {
                    dictionaryRef = docResolver.resolveDocRef("Dictionary", dictionaryName);
                } catch (final RuntimeException e) {
                    throw new TokenException(dictionaryNameToken, e.getMessage());
                }

                expressionTerm = ExpressionTerm
                        .builder()
                        .field(field)
                        .condition(Condition.IN_DICTIONARY)
                        .docRef(dictionaryRef)
                        .build();

            } else if (valueToken instanceof final TokenGroup tokenGroup) {
                if (tokens.size() > 3) {
                    throw new TokenException(tokens.get(3), "Unexpected token");
                }

                final Set<TokenType> allowedTypes = Set.of(
                        TokenType.SINGLE_QUOTED_STRING,
                        TokenType.DOUBLE_QUOTED_STRING,
                        TokenType.STRING,
                        TokenType.NUMBER,
                        TokenType.DATE_TIME);
                final List<AbstractToken> children = tokenGroup.getChildren();
                final StringBuilder sb = new StringBuilder();
                AbstractToken lastToken = null;
                for (final AbstractToken token : children) {
                    if (TokenType.COMMA.equals(token.getTokenType())) {
                        if (lastToken == null) {
                            throw new TokenException(token, "Unexpected leading comma");
                        } else if (TokenType.COMMA.equals(lastToken.getTokenType())) {
                            throw new TokenException(token, "Unexpected comma");
                        }
                    } else if (allowedTypes.contains(token.getTokenType())) {
                        if (lastToken != null) {
                            if (!TokenType.COMMA.equals(lastToken.getTokenType())) {
                                throw new TokenException(token, "Expected comma delimited");
                            }
                            sb.append(", ");
                        }
                        sb.append(token.getUnescapedText());
                    } else {
                        throw new TokenException(token, "Unexpected token");
                    }
                    lastToken = token;
                }
                if (lastToken != null && TokenType.COMMA.equals(lastToken.getTokenType())) {
                    throw new TokenException(lastToken, "Unexpected trailing comma");
                }

                final String field = fieldToken.getUnescapedText();
                expressionTerm = ExpressionTerm
                        .builder()
                        .field(field)
                        .condition(Condition.IN)
                        .value(sb.toString())
                        .build();

            } else {
                throw new TokenException(valueToken, "Expected parentheses after IN clause");
            }

            return expressionTerm;
        }
@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