<commit_before>﻿#nullable enable
using Mono.Cecil;
using Mono.Cecil.Cil;
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using VCSFramework.V2;

namespace VCSCompiler.V2
{
    internal static class AssemblyTemplate
    {
        private static readonly ILabel StartLabel = new BranchTargetLabel("START");

        public static IEnumerable<IAssemblyEntry> Foo(
            Function entryPoint,
            ImmutableArray<Function> nonInlineFunctions,
            ImmutableArray<AssignLabel> labelAssignments)
        {
            yield return new Comment($"Generated on {DateTime.Now:R}");
            yield return new Blank();
            yield return new CpuOp("6502");
            yield return new ProgramCounterAssignOp(0xF000);
            yield return new Blank();
            yield return new IncludeOp("vcs.h");
            yield return new IncludeOp("vil.h");
            yield return new Blank();
            
            foreach (var assignment in labelAssignments)
                yield return assignment;
            yield return new Blank();
            yield return StartLabel;
            foreach (var entry in entryPoint.Body)
                yield return entry;
            
            foreach (var func in nonInlineFunctions)
            {
                yield return new Blank();
                foreach (var entry in func.Body)
                    yield return entry;
            }
            yield return new Blank();
            yield return new ProgramCounterAssignOp(0xFFFC);
            yield return new WordOp(StartLabel);
            yield return new WordOp(StartLabel);
        }

        public static string FooToString(IEnumerable<IAssemblyEntry> program, SourceAnnotation annotations)
        {
            var currentMethodStack = new Stack<MethodDefinition>();
            var builder = new StringBuilder();
            foreach (var entry in program)
            {
                foreach (var str in GetStringFromEntry(entry, currentMethodStack.Count == 0 ? null : currentMethodStack.Peek(), annotations))
                    builder.AppendLine(str);
                switch (entry)
                {
                    case MethodLabel m:
                        currentMethodStack.Push(m.Method);
                        break;
                    case InlineFunction f:
                        currentMethodStack.Push(f.Definition);
                        break;
                    case EndFunction:
                        currentMethodStack.Pop();
                        break;
                }
            }
            return builder.ToString();
        }

        private static IEnumerable<string> GetStringFromEntry(IAssemblyEntry entry, MethodDefinition? method, SourceAnnotation annotations) => entry switch
        {
            IMacroCall mc => GetStringFromMacro(mc, method, annotations),
            // @TODO - Check if comments contain "/*" or "*/" already and fallback to "//" ?
            MultilineComment mc => mc.Text.Prepend("/*").Append("*/"),
            InlineAssembly ia => ia.Assembly,
            InlineFunction or EndFunction => Enumerable.Empty<string>(),
            _ => Enumerable.Repeat(entry switch
            {<commit_msg>Support indentation of assembly output.<commit_after>﻿#nullable enable
using Mono.Cecil;
using Mono.Cecil.Cil;
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using VCSFramework.V2;

namespace VCSCompiler.V2
{
    internal static class AssemblyTemplate
    {
        private sealed record IndentInfo(int DeltaIndent = 0, int? AbsoluteIndent = null);
        private static readonly ILabel StartLabel = new BranchTargetLabel("START");

        public static IEnumerable<IAssemblyEntry> Foo(
            Function entryPoint,
            ImmutableArray<Function> nonInlineFunctions,
            ImmutableArray<AssignLabel> labelAssignments)
        {
            yield return new Comment($"Generated on {DateTime.Now:R}");
            yield return new Blank();
            yield return new CpuOp("6502");
            yield return new ProgramCounterAssignOp(0xF000);
            yield return new Blank();
            yield return new IncludeOp("vcs.h");
            yield return new IncludeOp("vil.h");
            yield return new Blank();
            
            foreach (var assignment in labelAssignments)
                yield return assignment;
            yield return new Blank();
            yield return StartLabel;
            foreach (var entry in entryPoint.Body)
                yield return entry;
            
            foreach (var func in nonInlineFunctions)
            {
                yield return new Blank();
                foreach (var entry in func.Body)
                    yield return entry;
            }
            yield return new Blank();
            yield return new ProgramCounterAssignOp(0xFFFC);
            yield return new WordOp(StartLabel);
            yield return new WordOp(StartLabel);
        }

        public static string FooToString(IEnumerable<IAssemblyEntry> program, SourceAnnotation annotations)
        {
            const string IndentString = "\t";
            var currentMethodStack = new Stack<MethodDefinition>();
            var builder = new StringBuilder();
            var indentLevel = 0;
            foreach (var entry in program)
            {
                var indentInfo = GetIndentInfo(entry, indentLevel);
                foreach (var str in GetStringFromEntry(entry, currentMethodStack.Count == 0 ? null : currentMethodStack.Peek(), annotations))
                    builder.AppendLine($"{string.Join(string.Empty, Enumerable.Repeat(IndentString, indentInfo.AbsoluteIndent ?? indentLevel))}{str}");
                indentLevel += indentInfo.DeltaIndent;

                switch (entry)
                {
                    case MethodLabel m:
                        currentMethodStack.Push(m.Method);
                        break;
                    case InlineFunction f:
                        currentMethodStack.Push(f.Definition);
                        break;
                    case EndFunction:
                        currentMethodStack.Pop();
                        break;
                }
            }
            return builder.ToString();

            static IndentInfo GetIndentInfo(IAssemblyEntry entry, int indentLevel) => entry switch
            {
                InstructionLabel => new(AbsoluteIndent: 0),
                InlineFunction or MethodLabel => new(DeltaIndent: 1),
                EndFunction => new(DeltaIndent: -1),
                _ => new()
            };
        }

        private static IEnumerable<string> GetStringFromEntry(IAssemblyEntry entry, MethodDefinition? method, SourceAnnotation annotations) => entry switch
        {
            IMacroCall mc => GetStringFromMacro(mc, method, annotations),
            // @TODO - Check if comments contain "/*" or "*/" already and fallback to "//" ?
            MultilineComment mc => mc.Text.Prepend("/*").Append("*/"),
            InlineAssembly ia => ia.Assembly,
            InlineFunction or EndFunction => Enumerable.Empty<string>(),
            _ => Enumerable.Repeat(entry switch
            {
{'fraction_non_alphanumeric': 0.07955771305285868, 'fraction_numerical': 0.003910463861920173, 'mean_word_length': 1.8126659082290482, 'pattern_counts': {'":': 0, '<': 17, '<?xml version=': 0, '>': 34, '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': '5880194', 'n_tokens_mistral': 1771, 'n_tokens_neox': 1678, 'n_words': 493}