<commit_before>﻿#nullable enable
using Mono.Cecil;
using Mono.Cecil.Cil;
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;

namespace VCSFramework.V2
{
    internal interface IStackPusher
    {
        void PerformStackPushOps(IStackTracker stackTracker, ImmutableArray<Label> parameters);
    }

    internal interface ICallMacro
    {
        MethodDefinition Method { get; }
    }

    public abstract record Macro : AssemblyEntry
    {
        public MacroLabel Label { get; }
        public ImmutableArray<Label> Params { get; }
        public ImmutableArray<MacroEffectAttribute> Effects { get; }
        /// <summary>What instructions this macro replaces.</summary>
        public ImmutableArray<Instruction> Instructions { get; init; } = ImmutableArray<Instruction>.Empty;
        public ImmutableArray<ArrayLetOp> StackLets { get; init; } = ImmutableArray<ArrayLetOp>.Empty;

        public Macro(MacroLabel label, params Label[] parameters)
        {
            Label = label;
            Params = parameters.ToImmutableArray();
            Effects = GetType().GetCustomAttributes(false).OfType<MacroEffectAttribute>().ToImmutableArray();
        }

        public Macro(Instruction instruction, MacroLabel label, params Label[] parameters)
        {
            Label = label;
            Params = parameters.ToImmutableArray();
            Effects = GetType().GetCustomAttributes(false).OfType<MacroEffectAttribute>().ToImmutableArray();
            Instructions = new[] { instruction }.ToImmutableArray();
        }

        public Macro(IEnumerable<Instruction> instructions, MacroLabel label, params Label[] parameters)
        {
            Label = label;
            Params = parameters.ToImmutableArray();
            Effects = GetType().GetCustomAttributes(false).OfType<MacroEffectAttribute>().ToImmutableArray();
            Instructions = instructions.ToImmutableArray();
        }

        public Macro WithStackLets(IStackTracker stackTracker, TypeLabel nothingType, SizeLabel nothingSize)
        {
            var stackPops = Effects.OfType<PopStackAttribute>().SingleOrDefault()?.Count;
            var stackPushes = Effects.OfType<PushStackAttribute>().SingleOrDefault()?.Count;

            if (stackPops == null && stackPushes == null)
            {
                // Doesn't touch the stack.
                return this;
            }

            if (stackPops != null)
            {
                stackTracker.Pop((int)stackPops);
            }
            if (stackPushes != null)
            {
                // @TODO - We can enforce this by including the interface with auto-gen
                // (if annotated to push stack), and thus requiring user to add a partial
                // to implement it.
                if (this is IStackPusher stackPusher)
                    stackPusher.PerformStackPushOps(stackTracker, Params);
                else
                    throw new InvalidOperationException($"{GetType().Name} is annotated with {nameof(PushStackAttribute)} but doesn't implement {nameof(IStackPusher)}");
            }

            return this with
            {
                StackLets = stackTracker.GenerateStackSetters()
            };
        }

        public override string ToString()
        {
            var paramString = string.Join(", ", Params);
            return $".{Label} {paramString}";
        }
    }

    public sealed partial record PushConstant : IStackPusher
    {
        public void PerformStackPushOps(IStackTracker stackTracker, ImmutableArray<Label> parameters)
            => stackTracker.Push(Type, Size);
    }

    public partial record Duplicate : IStackPusher
    {
        public void PerformStackPushOps(IStackTracker stackTracker, ImmutableArray<Label> parameters)
            => stackTracker.Push(StackType, StackSize);
    }

    public partial record PushGlobal : IStackPusher
    {
        public void PerformStackPushOps(IStackTracker stackTracker, ImmutableArray<Label> parameters)
            => stackTracker.Push(Type, Size);
    }

    public partial record PushAddressOfGlobal : IStackPusher
    {
        public void PerformStackPushOps(IStackTracker stackTracker, ImmutableArray<Label> parameters)
            => stackTracker.Push(PointerType, PointerSize);
    }

    public partial record PushAddressOfLocal : IStackPusher
    {
        public void PerformStackPushOps(IStackTracker stackTracker, ImmutableArray<Label> parameters)
            => stackTracker.Push(PointerType, PointerSize);
    }

    public partial record PushAddressOfField : IStackPusher
    {
        public void PerformStackPushOps(IStackTracker stackTracker, ImmutableArray<Label> parameters)
            => stackTracker.Push(PointerType, PointerStackSize);
    }

    public partial record PushDereferenceFromStack : IStackPusher
    {
        public void PerformStackPushOps(IStackTracker stackTracker, ImmutableArray<Label> parameters)
            => stackTracker.Push(Type, Size);
    }

    public partial record PushFieldFromStack : IStackPusher
    {
        public void PerformStackPushOps(IStackTracker stackTracker, ImmutableArray<Label> parameters)
            => stackTracker.Push(FieldType, FieldSize);
    }

    public partial record AddFromGlobalAndConstant : IStackPusher
    {
        public void PerformStackPushOps(IStackTracker stackTracker, ImmutableArray<Label> parameters)
            => stackTracker.Push(
                new GetAddResultType(GlobalType, ConstantType),
                new GetSizeFromBuiltInType(new(0)));
    }

    public sealed record StoreTo : Macro
    {
        public StoreTo(Instruction instruction, GlobalLabel global)
            : base(instruction, new MacroLabel("storeTo"), global) { }

        public GlobalLabel Global => (GlobalLabel)Params[0];

        public void Deconstruct(out ImmutableArray<Instruction> instructions, out GlobalLabel global)
        {
            instructions = Instructions;
            global = Global;
        }
    }

    public partial record AddFromStack : IStackPusher
    {
        public void PerformStackPushOps(IStackTracker stackTracker, ImmutableArray<Label> parameters)
        {
            stackTracker.Push(
                new GetAddResultType(FirstOperandStackType, SecondOperandStackType),
                new GetSizeFromBuiltInType(new StackTypeArrayLabel(0)));
        }
    }

    public partial record PushLocal : IStackPusher
    {
        public void PerformStackPushOps(IStackTracker stackTracker, ImmutableArray<Label> parameters)
            => stackTracker.Push(Type, Size);
    }

    public partial record SubFromStack : IStackPusher
    {
        public void PerformStackPushOps(IStackTracker stackTracker, ImmutableArray<Label> parameters)
        {
            stackTracker.Push(
                new GetAddResultType(FirstOperandStackType, SecondOperandStackType),
                new GetSizeFromBuiltInType(new StackTypeArrayLabel(0)));
        }
    }

    public partial record OrFromStack : IStackPusher
    {
        public void PerformStackPushOps(IStackTracker stackTracker, ImmutableArray<Label> parameters)
            => stackTracker.Push(new GetBitOpResultType(FirstOperandStackType, SecondOperandStackType), new GetSizeFromBuiltInType(new(0)));
    }
<commit_msg>Revert "Use auto-gen'd partial properties since they magically work now."<commit_after>﻿#nullable enable
using Mono.Cecil;
using Mono.Cecil.Cil;
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;

namespace VCSFramework.V2
{
    internal interface IStackPusher
    {
        void PerformStackPushOps(IStackTracker stackTracker, ImmutableArray<Label> parameters);
    }

    internal interface ICallMacro
    {
        MethodDefinition Method { get; }
    }

    public abstract record Macro : AssemblyEntry
    {
        public MacroLabel Label { get; }
        public ImmutableArray<Label> Params { get; }
        public ImmutableArray<MacroEffectAttribute> Effects { get; }
        /// <summary>What instructions this macro replaces.</summary>
        public ImmutableArray<Instruction> Instructions { get; init; } = ImmutableArray<Instruction>.Empty;
        public ImmutableArray<ArrayLetOp> StackLets { get; init; } = ImmutableArray<ArrayLetOp>.Empty;

        public Macro(MacroLabel label, params Label[] parameters)
        {
            Label = label;
            Params = parameters.ToImmutableArray();
            Effects = GetType().GetCustomAttributes(false).OfType<MacroEffectAttribute>().ToImmutableArray();
        }

        public Macro(Instruction instruction, MacroLabel label, params Label[] parameters)
        {
            Label = label;
            Params = parameters.ToImmutableArray();
            Effects = GetType().GetCustomAttributes(false).OfType<MacroEffectAttribute>().ToImmutableArray();
            Instructions = new[] { instruction }.ToImmutableArray();
        }

        public Macro(IEnumerable<Instruction> instructions, MacroLabel label, params Label[] parameters)
        {
            Label = label;
            Params = parameters.ToImmutableArray();
            Effects = GetType().GetCustomAttributes(false).OfType<MacroEffectAttribute>().ToImmutableArray();
            Instructions = instructions.ToImmutableArray();
        }

        public Macro WithStackLets(IStackTracker stackTracker, TypeLabel nothingType, SizeLabel nothingSize)
        {
            var stackPops = Effects.OfType<PopStackAttribute>().SingleOrDefault()?.Count;
            var stackPushes = Effects.OfType<PushStackAttribute>().SingleOrDefault()?.Count;

            if (stackPops == null && stackPushes == null)
            {
                // Doesn't touch the stack.
                return this;
            }

            if (stackPops != null)
            {
                stackTracker.Pop((int)stackPops);
            }
            if (stackPushes != null)
            {
                // @TODO - We can enforce this by including the interface with auto-gen
                // (if annotated to push stack), and thus requiring user to add a partial
                // to implement it.
                if (this is IStackPusher stackPusher)
                    stackPusher.PerformStackPushOps(stackTracker, Params);
                else
                    throw new InvalidOperationException($"{GetType().Name} is annotated with {nameof(PushStackAttribute)} but doesn't implement {nameof(IStackPusher)}");
            }

            return this with
            {
                StackLets = stackTracker.GenerateStackSetters()
            };
        }

        public override string ToString()
        {
            var paramString = string.Join(", ", Params);
            return $".{Label} {paramString}";
        }
    }

    public sealed partial record PushConstant : IStackPusher
    {
        public void PerformStackPushOps(IStackTracker stackTracker, ImmutableArray<Label> parameters)
            => stackTracker.Push((TypeLabel)parameters[1], (SizeLabel)parameters[2]);
    }

    public partial record Duplicate : IStackPusher
    {
        public void PerformStackPushOps(IStackTracker stackTracker, ImmutableArray<Label> parameters)
            => stackTracker.Push((StackTypeArrayLabel)parameters[0], (StackSizeArrayLabel)parameters[1]);
    }

    public partial record PushGlobal : IStackPusher
    {
        public void PerformStackPushOps(IStackTracker stackTracker, ImmutableArray<Label> parameters)
            => stackTracker.Push((TypeLabel)parameters[1], (BaseSizeLabel)parameters[2]);
    }

    public partial record PushAddressOfGlobal : IStackPusher
    {
        public void PerformStackPushOps(IStackTracker stackTracker, ImmutableArray<Label> parameters)
            => stackTracker.Push((PointerTypeLabel)parameters[1], new PointerSizeLabel(true));
    }

    public partial record PushAddressOfLocal : IStackPusher
    {
        public void PerformStackPushOps(IStackTracker stackTracker, ImmutableArray<Label> parameters)
            => stackTracker.Push(PointerType, PointerSize);
    }

    public partial record PushAddressOfField : IStackPusher
    {
        public void PerformStackPushOps(IStackTracker stackTracker, ImmutableArray<Label> parameters)
            => stackTracker.Push(PointerType, PointerStackSize);
    }

    public partial record PushDereferenceFromStack : IStackPusher
    {
        public void PerformStackPushOps(IStackTracker stackTracker, ImmutableArray<Label> parameters)
            => stackTracker.Push(Type, Size);
    }

    public partial record PushFieldFromStack : IStackPusher
    {
        public void PerformStackPushOps(IStackTracker stackTracker, ImmutableArray<Label> parameters)
            => stackTracker.Push(FieldType, FieldSize);
    }

    public partial record AddFromGlobalAndConstant : IStackPusher
    {
        public void PerformStackPushOps(IStackTracker stackTracker, ImmutableArray<Label> parameters)
            => stackTracker.Push(
                new GetAddResultType((TypeLabel)parameters[1], (TypeLabel)parameters[4]),
                new GetSizeFromBuiltInType(new(0)));
    }

    public sealed record StoreTo : Macro
    {
        public StoreTo(Instruction instruction, GlobalLabel global)
            : base(instruction, new MacroLabel("storeTo"), global) { }

        public GlobalLabel Global => (GlobalLabel)Params[0];

        public void Deconstruct(out ImmutableArray<Instruction> instructions, out GlobalLabel global)
        {
            instructions = Instructions;
            global = Global;
        }
    }

    public partial record AddFromStack : IStackPusher
    {
        public void PerformStackPushOps(IStackTracker stackTracker, ImmutableArray<Label> parameters)
        {
            stackTracker.Push(
                new GetAddResultType((StackTypeArrayLabel)parameters[0], (StackTypeArrayLabel)parameters[2]),
                new GetSizeFromBuiltInType(new StackTypeArrayLabel(0)));
        }
    }

    public partial record PushLocal : IStackPusher
    {
        public void PerformStackPushOps(IStackTracker stackTracker, ImmutableArray<Label> parameters)
            => stackTracker.Push((TypeLabel)parameters[1], (BaseSizeLabel)parameters[2]);
    }

    public partial record SubFromStack : IStackPusher
    {
        public void PerformStackPushOps(IStackTracker stackTracker, ImmutableArray<Label> parameters)
        {
            stackTracker.Push(
                new GetAddResultType((StackTypeArrayLabel)parameters[0], (StackTypeArrayLabel)parameters[2]),
                new GetSizeFromBuiltInType(new StackTypeArrayLabel(0)));
        }
    }

    public partial record OrFromStack : IStackPusher
    {
        public void PerformStackPushOps(IStackTracker stackTracker, ImmutableArray<Label> parameters)
            => stackTracker.Push(new GetBitOpResultType(new StackTypeArrayLabel(0), new StackTypeArrayLabel(1)), new GetSizeFromBuiltInType(new(0)));
    }

{'fraction_non_alphanumeric': 0.08139225342739252, 'fraction_numerical': 0.001929988020764009, 'mean_word_length': 2.7278590920367156, 'pattern_counts': {'":': 0, '<': 61, '<?xml version=': 0, '>': 85, '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': '1531854', 'n_tokens_mistral': 3847, 'n_tokens_neox': 3767, 'n_words': 854}