<commit_before>﻿using System;
using System.CodeDom;
using System.Collections.Generic;
using System.IO;
using System.Text;

namespace IronAHK.Scripting
{
    partial class Parser
    {
        #region Wrappers

        CodeExpressionStatement[] ParseMultiExpression(string code)
        {
            var tokens = SplitTokens(code);
            var list = new List<CodeExpressionStatement>();
            var sub = new List<object>(tokens.Count);

            for (int i = 0; i <= tokens.Count; i++)
            {
                if (i == tokens.Count)
                    goto collect; // ffffff

                string check = tokens[i] as string;
                if (!(check != null && check.Length == 1 && check[0] == Multicast))
                {
                    sub.Add(tokens[i]);
                    continue;
                }

            collect:
                if (sub.Count > 0)
                {
                    list.Add(new CodeExpressionStatement(ParseExpression(sub)));
                    sub = new List<object>(tokens.Count);
                }
            }

            return list.ToArray();
        }

        CodeExpression ParseSingleExpression(string code)
        {
            return ParseExpression(SplitTokens(code));
        }

        #endregion

        CodeExpression ParseExpression(List<object> parts)
        {
            for (int i = 0; i < parts.Count; i++)
            {
                if (parts[i] is string)
                {
                    string part = (string)parts[i];
                    object result;
                    bool next = false;

                    #region Parentheses
                    if (part[0] == ParenOpen)
                    {
                        int levels = 1;
                        for (int x = i + 1; x < parts.Count; x++)
                        {
                            string current = parts[x] as string;
                            if (string.IsNullOrEmpty(current))
                                continue;
                            switch (current[0])
                            {
                                case ParenOpen:
                                    levels++;
                                    break;

                                case ParenClose:
                                    if (--levels == 0)
                                    {
                                        int count = x - i;

                                        var sub = new List<object>(count);

                                        for (int n = i + 1; n < x; n++)
                                            sub.Add(parts[n]);

                                        parts.RemoveRange(i, count + 1);

                                        if (sub.Count > 0)
                                            parts.Insert(i, ParseExpression(sub));

                                        next = true;
                                    }
                                    break;
                            }
                            if (next)
                                break;
                        }
                        if (levels != 0)
                            throw new ParseException(ExUnbalancedParens);
                        else if (next)
                            continue;
                    }
                    #endregion
                    #region Numerics
                    else if (IsPrimativeObject(part, out result))
                        parts[i] = new CodePrimitiveExpression(result);
                    #endregion
                    #region Variables
                    else if (IsIdentifier(part[0]))
                        parts[i] = VarId(part);
                    #endregion
                    #region Strings
                    else if (part.Length > 2 && part[0] == StringBound && part[part.Length - 1] == StringBound)
                        parts[i] = part.Substring(1, part.Length - 2);
                    #endregion
                    #region Assignments
                    else if (IsAssignOp(part))
                    {
                        int n = i - 1;
                        if (n < 0 || !(parts[n] is CodeComplexVariableReferenceExpression))
                            throw new ParseException("Can only assign to a variable");

                        // (x += y) => (x = x + y)
                        parts[i] = new CodeAssignExpression();
                        if (part[0] != AssignPre)
                        {
                            i++;
                            parts.Insert(i, parts[i - 2]);
                            i++;
                            parts.Insert(i, BinaryOperator(part.Substring(0, part.Length - 1)));
                        }
                    }
                    #endregion
                    #region Multiple statements
                    else if (part.Length == 1 && part[0] == Multicast)
                    {
                        throw new ParseException("Multiple expression statements not allowed here");
                    }
                    #endregion
                    #region Binary operators
                    else
                    {
                        parts[i] = BinaryOperator(part); // TODO: uniary operators
                    }
                    #endregion
                }
            }

            #region Binary operators

            var op = new CodeMethodReferenceExpression();
            op.TargetObject = new CodeThisReferenceExpression();
            op.MethodName = "Operate";
            bool scan = true;

            // HACK: operator precedence

            while (scan)
            {
                scan = false;
                for (int i = 0; i < parts.Count; i++)
                {
                    if (parts[i] is Script.Operator)
                    {
                        scan = true;
                        int x = i - 1, y = i + 1;
                        var invoke = new CodeMethodInvokeExpression();
                        invoke.Method = op;
                        invoke.Parameters.Add(new CodePrimitiveExpression((Script.Operator)parts[i]));
                        invoke.Parameters.Add((CodeExpression)parts[x]);
                        invoke.Parameters.Add((CodeExpression)parts[y]);
                        parts[x] = invoke;
                        parts.RemoveAt(y);
                        parts.RemoveAt(i);
                    }
                }
            }

            #endregion

            #region Assignments
            // HACK: assignments go from right to left
            for (int i = 0; i < parts.Count; i++)
            {
                if (parts[i] is CodeAssignExpression)
                {
                    var assign = (CodeAssignExpression)parts[i];
                    assign.Left = (CodeComplexVariableReferenceExpression)parts[i - 1];
                    assign.Right = (CodeExpression)parts[i + 1];
                    parts.RemoveAt(i - 1);
                    parts.RemoveAt(i + 1 - 1);
                }
            }
            #endregion

            if (parts.Count != 1)
                throw new ArgumentOutOfRangeException();

            return (CodeExpression)parts[0];
        }

        #region Operators

        Script.Operator BinaryOperator(string code)
        {
            char[] op = code.ToCharArray();

            switch (op[0])
            {
                case Add:
                    switch (op.Length)
                    {
                        case 1:
                            return Script.Operator.Add;

                        case 2:
                            return Script.Operator.Increment;
<commit_msg>Made assignments in expressions evaluate from right to left.<commit_after>﻿using System;
using System.CodeDom;
using System.Collections.Generic;
using System.IO;
using System.Text;

namespace IronAHK.Scripting
{
    partial class Parser
    {
        #region Wrappers

        CodeExpressionStatement[] ParseMultiExpression(string code)
        {
            var tokens = SplitTokens(code);
            var list = new List<CodeExpressionStatement>();
            var sub = new List<object>(tokens.Count);

            for (int i = 0; i <= tokens.Count; i++)
            {
                if (i == tokens.Count)
                    goto collect; // ffffff

                string check = tokens[i] as string;
                if (!(check != null && check.Length == 1 && check[0] == Multicast))
                {
                    sub.Add(tokens[i]);
                    continue;
                }

            collect:
                if (sub.Count > 0)
                {
                    list.Add(new CodeExpressionStatement(ParseExpression(sub)));
                    sub = new List<object>(tokens.Count);
                }
            }

            return list.ToArray();
        }

        CodeExpression ParseSingleExpression(string code)
        {
            return ParseExpression(SplitTokens(code));
        }

        #endregion

        CodeExpression ParseExpression(List<object> parts)
        {
            for (int i = 0; i < parts.Count; i++)
            {
                if (parts[i] is string)
                {
                    string part = (string)parts[i];
                    object result;
                    bool next = false;

                    #region Parentheses
                    if (part[0] == ParenOpen)
                    {
                        int levels = 1;
                        for (int x = i + 1; x < parts.Count; x++)
                        {
                            string current = parts[x] as string;
                            if (string.IsNullOrEmpty(current))
                                continue;
                            switch (current[0])
                            {
                                case ParenOpen:
                                    levels++;
                                    break;

                                case ParenClose:
                                    if (--levels == 0)
                                    {
                                        int count = x - i;

                                        var sub = new List<object>(count);

                                        for (int n = i + 1; n < x; n++)
                                            sub.Add(parts[n]);

                                        parts.RemoveRange(i, count + 1);

                                        if (sub.Count > 0)
                                            parts.Insert(i, ParseExpression(sub));

                                        next = true;
                                    }
                                    break;
                            }
                            if (next)
                                break;
                        }
                        if (levels != 0)
                            throw new ParseException(ExUnbalancedParens);
                        else if (next)
                            continue;
                    }
                    #endregion
                    #region Numerics
                    else if (IsPrimativeObject(part, out result))
                        parts[i] = new CodePrimitiveExpression(result);
                    #endregion
                    #region Variables
                    else if (IsIdentifier(part[0]))
                        parts[i] = VarId(part);
                    #endregion
                    #region Strings
                    else if (part.Length > 2 && part[0] == StringBound && part[part.Length - 1] == StringBound)
                        parts[i] = part.Substring(1, part.Length - 2);
                    #endregion
                    #region Assignments
                    else if (IsAssignOp(part))
                    {
                        int n = i - 1;
                        if (n < 0 || !(parts[n] is CodeComplexVariableReferenceExpression))
                            throw new ParseException("Can only assign to a variable");

                        // (x += y) => (x = x + y)
                        parts[i] = new CodeAssignExpression();
                        if (part[0] != AssignPre)
                        {
                            i++;
                            parts.Insert(i, parts[i - 2]);
                            i++;
                            parts.Insert(i, BinaryOperator(part.Substring(0, part.Length - 1)));
                        }
                    }
                    #endregion
                    #region Multiple statements
                    else if (part.Length == 1 && part[0] == Multicast)
                    {
                        throw new ParseException("Multiple expression statements not allowed here");
                    }
                    #endregion
                    #region Binary operators
                    else
                    {
                        parts[i] = BinaryOperator(part); // TODO: uniary operators
                    }
                    #endregion
                }
            }

            #region Binary operators

            var op = new CodeMethodReferenceExpression();
            op.TargetObject = new CodeThisReferenceExpression();
            op.MethodName = "Operate";
            bool scan = true;

            // HACK: operator precedence

            while (scan)
            {
                scan = false;
                for (int i = 0; i < parts.Count; i++)
                {
                    if (parts[i] is Script.Operator)
                    {
                        scan = true;
                        int x = i - 1, y = i + 1;
                        var invoke = new CodeMethodInvokeExpression();
                        invoke.Method = op;
                        invoke.Parameters.Add(new CodePrimitiveExpression((Script.Operator)parts[i]));
                        invoke.Parameters.Add((CodeExpression)parts[x]);
                        invoke.Parameters.Add((CodeExpression)parts[y]);
                        parts[x] = invoke;
                        parts.RemoveAt(y);
                        parts.RemoveAt(i);
                    }
                }
            }

            #endregion

            #region Assignments
            for (int i = parts.Count - 1; i > 0; i--)
            {
                if (parts[i] is CodeAssignExpression)
                {
                    int x = i - 1, y = i + 1;
                    var assign = (CodeAssignExpression)parts[i];
                    assign.Left = (CodeComplexVariableReferenceExpression)parts[x];
                    assign.Right = (CodeExpression)parts[y];
                    parts.RemoveAt(x);
                    parts.RemoveAt(i);
                }
            }
            #endregion

            if (parts.Count != 1)
                throw new ArgumentOutOfRangeException();

            return (CodeExpression)parts[0];
        }

        #region Operators

        Script.Operator BinaryOperator(string code)
        {
            char[] op = code.ToCharArray();

            switch (op[0])
            {
                case Add:
                    switch (op.Length)
                    {
                        case 1:
                            return Script.Operator.Add;

                        case 2:
                            return Script.Operator.Increment;

{'fraction_non_alphanumeric': 0.08285862247540135, 'fraction_numerical': 0.005308130502330399, 'mean_word_length': 0.8192416391898257, 'pattern_counts': {'":': 0, '<': 26, '<?xml version=': 0, '>': 22, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 44, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3097447', 'n_tokens_mistral': 3492, 'n_tokens_neox': 3164, 'n_words': 810}