public static Expression substituteBoolExpression(ConfigVariableExpander cve, Expression expression) {
        try {
            if (expression instanceof BinaryBooleanExpression) {
                BinaryBooleanExpression binaryBoolExp = (BinaryBooleanExpression) expression;
                Expression substitutedLeftExp = substituteBoolExpression(cve, binaryBoolExp.getLeft());
                Expression substitutedRightExp = substituteBoolExpression(cve, binaryBoolExp.getRight());
                if (substitutedLeftExp != binaryBoolExp.getLeft() || substitutedRightExp != binaryBoolExp.getRight()) {
                    Constructor<? extends BinaryBooleanExpression> constructor = binaryBoolExp.getClass().getConstructor(SourceWithMetadata.class, Expression.class, Expression.class);
                    return constructor.newInstance(binaryBoolExp.getSourceWithMetadata(), substitutedLeftExp, substitutedRightExp);
                }
            } else if (expression instanceof UnaryBooleanExpression) {
                UnaryBooleanExpression unaryBoolExp = (UnaryBooleanExpression) expression;
                Expression substitutedExp = substituteBoolExpression(cve, unaryBoolExp.getExpression());
                if (substitutedExp != unaryBoolExp.getExpression()) {
                    Constructor<? extends UnaryBooleanExpression> constructor = unaryBoolExp.getClass().getConstructor(SourceWithMetadata.class, Expression.class);
                    return constructor.newInstance(unaryBoolExp.getSourceWithMetadata(), substitutedExp);
                }
            } else if (expression instanceof ValueExpression && !(expression instanceof RegexValueExpression) && (((ValueExpression) expression).get() != null)) {
                Object expanded = CompiledPipeline.expandConfigVariableKeepingSecrets(cve, ((ValueExpression) expression).get());
                return new ValueExpression(expression.getSourceWithMetadata(), expanded);
            }

            return expression;
        } catch (NoSuchMethodException | InstantiationException | IllegalAccessException | InvocationTargetException | InvalidIRException e) {
            throw new IllegalStateException("Unable to instantiate substituted condition expression", e);
        }
    }
@Test
    public void basicUnaryBooleanSubstitution() throws InvalidIRException {
        SourceWithMetadata swm = new SourceWithMetadata("proto", "path", 1, 8, "if !(\"${BIG}\" == 100) { add_tag => \"not\" }");
        ValueExpression valueExp = new ValueExpression(swm, "${BIG}");
        UnaryBooleanExpression expression = new Not(swm, valueExp);

        ConfigVariableExpander cve = getConfigVariableExpander();
        UnaryBooleanExpression substituted = (UnaryBooleanExpression) ExpressionSubstitution.substituteBoolExpression(cve, expression);
        assertEquals(((ValueExpression) substituted.getExpression()).get(), "100");
    }