<commit_before>﻿// Copyright (c) <NAME>. All rights reserved.
// Licensed under the BSD-Clause 2 license. See license.txt file in the project root for full license information.
using System;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
using System.Text;
using Scriban.Helpers;

namespace Scriban.Runtime
{
    [ScriptSyntax("binary expression", "<expression> operator <expression>")]
    public class ScriptBinaryExpression : ScriptExpression
    {
        public ScriptExpression Left { get; set; }

        public ScriptBinaryOperator Operator { get; set; }

        public ScriptExpression Right { get; set; }

        public override void Evaluate(TemplateContext context)
        {
            var leftValueOriginal = context.Evaluate(Left);
            var leftValue = leftValueOriginal;
            var rightValueOriginal = context.Evaluate(Right);
            object rightValue = rightValueOriginal;

            if (Operator == ScriptBinaryOperator.EmptyCoalescing)
            {
                context.Result = leftValue ?? rightValue;
            }
            else if (Operator == ScriptBinaryOperator.And || Operator == ScriptBinaryOperator.Or)
            {
                var leftBoolValue = ScriptValueConverter.ToBool(leftValue);
                var rightBoolValue = ScriptValueConverter.ToBool(rightValue);
                if (Operator == ScriptBinaryOperator.And)
                {
                    context.Result = leftBoolValue && rightBoolValue;
                }
                else
                {
                    context.Result = leftBoolValue || rightBoolValue;
                }
            }
            else if (leftValue is IList || rightValue is IList)
            {
                // Special path for IList to allow custom binary expression
                context.Result = ScriptArray.CustomOperator.EvaluateBinaryExpression(this, leftValue,
                    rightValue);
            }
            else
            {
                switch (Operator)
                {
                    case ScriptBinaryOperator.CompareEqual:
                    case ScriptBinaryOperator.CompareNotEqual:
                    case ScriptBinaryOperator.CompareGreater:
                    case ScriptBinaryOperator.CompareLess:
                    case ScriptBinaryOperator.CompareGreaterOrEqual:
                    case ScriptBinaryOperator.CompareLessOrEqual:
                    case ScriptBinaryOperator.Add:
                    case ScriptBinaryOperator.Substract:
                    case ScriptBinaryOperator.Multiply:
                    case ScriptBinaryOperator.Divide:
                    case ScriptBinaryOperator.DivideRound:
                    case ScriptBinaryOperator.Modulus:
                    case ScriptBinaryOperator.RangeInclude:
                    case ScriptBinaryOperator.RangeExclude:
                        var leftType = leftValue?.GetType();
                        var rightType = rightValue?.GetType();

                        if (leftValue is string || rightValue is string)
                        {
                            context.Result = CalculateToString(Operator, leftValue, rightValue);
                            {
                                // TODO: Log an error if CalculateToString return null?
                                //context.LogError(Span, $"Operation [{Operator}] on strings not supported");
                            }
                        }
                        else
                        {
                            context.Result = Calculate(Operator, leftValue, leftType, rightValue, rightType);
                        }
                        break;
                    default:
                        throw new InvalidOperationException($"Operator [{Operator}] is not implemented");
                }
            }
        }

        public override string ToString()
        {
            return $"{Left} {Operator.ToText()} {Right}";
        }

        private object CalculateToString(ScriptBinaryOperator op, object left, object right)
        {
            switch (op)
            {
                case ScriptBinaryOperator.Add:
                    return ScriptValueConverter.ToString(Span, left) + ScriptValueConverter.ToString(Span, right);
                case ScriptBinaryOperator.Multiply:
                    if (right is int)
                    {
                        var temp = left;
                        left = right;
                        right = temp;
                    }

                    if (left is int)
                    {
                        var rightText = ScriptValueConverter.ToString(Span, right);
                        var builder = new StringBuilder();
                        for (int i = 0; i < (int) left; i++)
                        {
                            builder.Append(rightText);<commit_msg>Evaluate only << and >> for list values otherwise use standard binary expressions<commit_after>﻿// Copyright (c) <NAME>. All rights reserved.
// Licensed under the BSD-Clause 2 license. See license.txt file in the project root for full license information.
using System;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
using System.Text;
using Scriban.Helpers;

namespace Scriban.Runtime
{
    [ScriptSyntax("binary expression", "<expression> operator <expression>")]
    public class ScriptBinaryExpression : ScriptExpression
    {
        public ScriptExpression Left { get; set; }

        public ScriptBinaryOperator Operator { get; set; }

        public ScriptExpression Right { get; set; }

        public override void Evaluate(TemplateContext context)
        {
            var leftValueOriginal = context.Evaluate(Left);
            var leftValue = leftValueOriginal;
            var rightValueOriginal = context.Evaluate(Right);
            object rightValue = rightValueOriginal;

            if (Operator == ScriptBinaryOperator.EmptyCoalescing)
            {
                context.Result = leftValue ?? rightValue;
                return;
            }
            else if (Operator == ScriptBinaryOperator.And || Operator == ScriptBinaryOperator.Or)
            {
                var leftBoolValue = ScriptValueConverter.ToBool(leftValue);
                var rightBoolValue = ScriptValueConverter.ToBool(rightValue);
                if (Operator == ScriptBinaryOperator.And)
                {
                    context.Result = leftBoolValue && rightBoolValue;
                }
                else
                {
                    context.Result = leftBoolValue || rightBoolValue;
                }

                return;
            }
            else
            {
                switch (Operator)
                {
                    case ScriptBinaryOperator.ShiftLeft:
                    case ScriptBinaryOperator.ShiftRight:
                        if (leftValue is IList || rightValue is IList)
                        {
                            // Special path for IList to allow custom binary expression
                            context.Result = ScriptArray.CustomOperator.EvaluateBinaryExpression(this, leftValue,
                                rightValue);
                            return;
                        }
                        break;
                    case ScriptBinaryOperator.CompareEqual:
                    case ScriptBinaryOperator.CompareNotEqual:
                    case ScriptBinaryOperator.CompareGreater:
                    case ScriptBinaryOperator.CompareLess:
                    case ScriptBinaryOperator.CompareGreaterOrEqual:
                    case ScriptBinaryOperator.CompareLessOrEqual:
                    case ScriptBinaryOperator.Add:
                    case ScriptBinaryOperator.Substract:
                    case ScriptBinaryOperator.Multiply:
                    case ScriptBinaryOperator.Divide:
                    case ScriptBinaryOperator.DivideRound:
                    case ScriptBinaryOperator.Modulus:
                    case ScriptBinaryOperator.RangeInclude:
                    case ScriptBinaryOperator.RangeExclude:
                        var leftType = leftValue?.GetType();
                        var rightType = rightValue?.GetType();

                        if (leftValue is string || rightValue is string)
                        {
                            context.Result = CalculateToString(Operator, leftValue, rightValue);
                            {
                                // TODO: Log an error if CalculateToString return null?
                                //context.LogError(Span, $"Operation [{Operator}] on strings not supported");
                            }
                        }
                        else
                        {
                            context.Result = Calculate(Operator, leftValue, leftType, rightValue, rightType);
                        }
                        return;
                }
            }

            throw new ScriptRuntimeException(Span, $"Operator [{Operator.ToText()}] is not implemented for the left [{Left}] / right [{Right}]");
        }

        public override string ToString()
        {
            return $"{Left} {Operator.ToText()} {Right}";
        }

        private object CalculateToString(ScriptBinaryOperator op, object left, object right)
        {
            switch (op)
            {
                case ScriptBinaryOperator.Add:
                    return ScriptValueConverter.ToString(Span, left) + ScriptValueConverter.ToString(Span, right);
                case ScriptBinaryOperator.Multiply:
                    if (right is int)
                    {
                        var temp = left;
                        left = right;
                        right = temp;
                    }

                    if (left is int)
                    {
                        var rightText = ScriptValueConverter.ToString(Span, right);
                        var builder = new StringBuilder();
                        for (int i = 0; i < (int) left; i++)
                        {
                            builder.Append(rightText);
{'fraction_non_alphanumeric': 0.06607369758576874, 'fraction_numerical': 0.0003909686247678624, 'mean_word_length': 1.340883093113704, 'pattern_counts': {'":': 0, '<': 13, '<?xml version=': 0, '>': 11, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6951441', 'n_tokens_mistral': 2179, 'n_tokens_neox': 2009, 'n_words': 554}