<commit_before>/// <reference path="../../estree.ts" />

namespace Styx.Expressions.Negator {
    const equalityComparisonOperators = ["==", "===", "!=", "!=="];
    
    export function negateTruthiness(expression: ESTree.Expression): ESTree.Expression {
        if (expression.type === ESTree.NodeType.Literal) {
            let literal = <ESTree.Literal>expression;
            
            if (typeof literal.value === "boolean") {
                return createBooleanLiteral(!literal.value);
            }
        } else if (expression.type === ESTree.NodeType.UnaryExpression) {
            let unaryExpression = <ESTree.UnaryExpression>expression;
            
            if (unaryExpression.operator === "!") {
                return unaryExpression.argument;
            }
        } else if (expression.type === ESTree.NodeType.BinaryExpression) {
            let binaryExpression = <ESTree.BinaryExpression>expression;
            
            if (equalityComparisonOperators.indexOf(binaryExpression.operator) > -1) {
                return invertEqualityComparisonOperator(binaryExpression);
            }
        } else if (expression.type === ESTree.NodeType.LogicalExpression) {
            return invertLogicalExpression(<ESTree.LogicalExpression>expression);
        }
        
        return wrapInUnaryNegationExpression(expression);
    }
    
    function createBooleanLiteral(value: boolean): ESTree.Literal {
        return {
            type: ESTree.NodeType.Literal,
            value: value
        };
    }
    
    function invertEqualityComparisonOperator(binaryExpression: ESTree.BinaryExpression): ESTree.BinaryExpression {
        let isNegated = binaryExpression.operator.charAt(0) === "!";
        let firstCharOfInvertedOperator = isNegated ? "=" : "!";
        let restOfInvertedOperator = binaryExpression.operator.substr(1);
        
        return {
            type: ESTree.NodeType.BinaryExpression,
            operator: firstCharOfInvertedOperator + restOfInvertedOperator,
            left: binaryExpression.left,
            right: binaryExpression.right            
        };
    }
    
    function invertLogicalExpression(logicalExpression: ESTree.LogicalExpression): ESTree.LogicalExpression {
        // The only two logical operators are && and ||
        let invertedOperator = logicalExpression.operator === "&&" ? "||" : "&&";
        
        return {
            type: ESTree.NodeType.LogicalExpression,
            operator: invertedOperator,
            left: negateTruthiness(logicalExpression.left),
            right: negateTruthiness(logicalExpression.right)
        };
    }
    
    function wrapInUnaryNegationExpression(expression: ESTree.Expression): ESTree.UnaryExpression {
        return {
            type: ESTree.NodeType.UnaryExpression,
            operator: "!",
            prefix: true,
            argument: expression
        };
    }
}<commit_msg>Explains the usage of De Morgan's laws<commit_after>/// <reference path="../../estree.ts" />

namespace Styx.Expressions.Negator {
    const equalityComparisonOperators = ["==", "===", "!=", "!=="];
    
    export function negateTruthiness(expression: ESTree.Expression): ESTree.Expression {
        if (expression.type === ESTree.NodeType.Literal) {
            let literal = <ESTree.Literal>expression;
            
            if (typeof literal.value === "boolean") {
                return createBooleanLiteral(!literal.value);
            }
        } else if (expression.type === ESTree.NodeType.UnaryExpression) {
            let unaryExpression = <ESTree.UnaryExpression>expression;
            
            if (unaryExpression.operator === "!") {
                return unaryExpression.argument;
            }
        } else if (expression.type === ESTree.NodeType.BinaryExpression) {
            let binaryExpression = <ESTree.BinaryExpression>expression;
            
            if (equalityComparisonOperators.indexOf(binaryExpression.operator) > -1) {
                return invertEqualityComparisonOperator(binaryExpression);
            }
        } else if (expression.type === ESTree.NodeType.LogicalExpression) {
            return invertLogicalExpression(<ESTree.LogicalExpression>expression);
        }
        
        return wrapInUnaryNegationExpression(expression);
    }
    
    function createBooleanLiteral(value: boolean): ESTree.Literal {
        return {
            type: ESTree.NodeType.Literal,
            value: value
        };
    }
    
    function invertEqualityComparisonOperator(binaryExpression: ESTree.BinaryExpression): ESTree.BinaryExpression {
        let isNegated = binaryExpression.operator.charAt(0) === "!";
        let firstCharOfInvertedOperator = isNegated ? "=" : "!";
        let restOfInvertedOperator = binaryExpression.operator.substr(1);
        
        return {
            type: ESTree.NodeType.BinaryExpression,
            operator: firstCharOfInvertedOperator + restOfInvertedOperator,
            left: binaryExpression.left,
            right: binaryExpression.right
        };
    }
    
    function invertLogicalExpression(logicalExpression: ESTree.LogicalExpression): ESTree.LogicalExpression {
        // The only two logical operators are && and ||
        let invertedOperator = logicalExpression.operator === "&&" ? "||" : "&&";
        
        return {
            type: ESTree.NodeType.LogicalExpression,
            operator: invertedOperator,
            
            // Perform simplification according to De Morgan's laws
            left: negateTruthiness(logicalExpression.left),
            right: negateTruthiness(logicalExpression.right)
        };
    }
    
    function wrapInUnaryNegationExpression(expression: ESTree.Expression): ESTree.UnaryExpression {
        return {
            type: ESTree.NodeType.UnaryExpression,
            operator: "!",
            prefix: true,
            argument: expression
        };
    }
}
{'fraction_non_alphanumeric': 0.10215053763440861, 'fraction_numerical': 0.0010080645161290322, 'mean_word_length': 2.2780837004405288, 'pattern_counts': {'":': 0, '<': 13, '<?xml version=': 0, '>': 15, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3847933', 'n_tokens_mistral': 1526, 'n_tokens_neox': 1463, 'n_words': 257}