using System.Reflection; using System.Diagnostics; using System.Collections; using System.Security.Cryptography; namespace System { static class Compiler { public abstract class Generator { public enum Flags { None = 0, AllowRegenerate = 1 } public String mCmdInfo = new String() ~ delete _; public Dictionary mParams = new .() ~ delete _; public abstract String Name { get; } public StringView ProjectName => mParams["ProjectName"]; public StringView ProjectDir => mParams["ProjectDir"]; public StringView FolderDir => mParams["FolderDir"]; public StringView Namespace => mParams["Namespace"]; public StringView DefaultNamespace => mParams["DefaultNamespace"]; public StringView WorkspaceName => mParams["WorkspaceName"]; public StringView WorkspaceDir => mParams["WorkspaceDir"]; public StringView DateTime => mParams["DateTime"]; public void Fail(StringView error) { mCmdInfo.AppendF("error\t"); error.QuoteString(mCmdInfo); mCmdInfo.Append("\n"); } public void AddEdit(StringView dataName, StringView label, StringView defaultValue) { mCmdInfo.AppendF($"addEdit\t"); dataName.QuoteString(mCmdInfo); mCmdInfo.Append("\t"); label.QuoteString(mCmdInfo); mCmdInfo.Append("\t"); defaultValue.QuoteString(mCmdInfo); mCmdInfo.Append("\n"); } public void AddCombo(StringView dataName, StringView label, StringView defaultValue, Span values) { mCmdInfo.AppendF($"addCombo\t"); dataName.QuoteString(mCmdInfo); mCmdInfo.Append("\t"); label.QuoteString(mCmdInfo); mCmdInfo.Append("\t"); defaultValue.QuoteString(mCmdInfo); for (var value in values) { mCmdInfo.Append("\t"); value.QuoteString(mCmdInfo); } mCmdInfo.Append("\n"); } public void AddCheckbox(StringView dataName, StringView label, bool defaultValue) { mCmdInfo.AppendF($"addCheckbox\t"); dataName.QuoteString(mCmdInfo); mCmdInfo.Append("\t"); label.QuoteString(mCmdInfo); mCmdInfo.AppendF($"\t{defaultValue}\n"); } public bool GetString(StringView key, String outVal) { if (mParams.TryGetAlt(key, var matchKey, var value)) { outVal.Append(value); return true; } return false; } public virtual void InitUI() { } public virtual void Generate(String outFileName, String outText, ref Flags generateFlags) { } static String GetName() where T : Generator { T val = scope T(); String str = val.Name; return str; } void HandleArgs(String args) { for (var line in args.Split('\n', .RemoveEmptyEntries)) { int tabPos = line.IndexOf('\t'); var key = line.Substring(0, tabPos); var value = line.Substring(tabPos + 1); if (mParams.TryAdd(key, var keyPtr, var valuePtr)) { *keyPtr = key; *valuePtr = value; } } } static String InitUI(String args) where T : Generator { T val = scope T(); val.HandleArgs(args); val.InitUI(); return val.mCmdInfo; } static String Generate(String args) where T : Generator { T val = scope T(); val.HandleArgs(args); String fileName = scope .(); String outText = scope .(); Flags flags = .None; val.Generate(fileName, outText, ref flags); val.mCmdInfo.Append("fileName\t"); fileName.QuoteString(val.mCmdInfo); val.mCmdInfo.Append("\n"); val.mCmdInfo.Append("data\n"); if (flags.HasFlag(.AllowRegenerate)) { bool writeArg = false; for (var line in args.Split('\n', .RemoveEmptyEntries)) { int tabPos = line.IndexOf('\t'); var key = line.Substring(0, tabPos); var value = line.Substring(tabPos + 1); if (key == "Generator") writeArg = true; if (writeArg) { val.mCmdInfo.AppendF($"// {key}={value}\n"); } } var hash = MD5.Hash(.((.)outText.Ptr, outText.Length)); val.mCmdInfo.AppendF($"// GenHash={hash}\n\n"); } val.mCmdInfo.Append(outText); return val.mCmdInfo; } } public class NewClassGenerator : Generator { public override String Name => "New Class"; public override void InitUI() { AddEdit("name", "Class Name", ""); } public override void Generate(String outFileName, String outText, ref Flags generateFlags) { var name = mParams["name"]; if (name.EndsWith(".bf", .OrdinalIgnoreCase)) name.RemoveFromEnd(3); outFileName.Append(name); outText.AppendF( $""" namespace {Namespace} {{ class {name} {{ }} }} """); } } public struct MethodBuilder { void* mNative; public void Emit(String str) { Comptime_MethodBuilder_EmitStr(mNative, str); } public void Emit(Type type) { } } public static class Options { [LinkName("#AllocStackCount")] public static extern int32 AllocStackCount; } [LinkName("#CallerLineNum")] public static extern int CallerLineNum; [LinkName("#CallerFilePath")] public static extern String CallerFilePath; [LinkName("#CallerFileName")] public static extern String CallerFileName; [LinkName("#CallerFileDir")] public static extern String CallerFileDir; [LinkName("#CallerMemberName")] public static extern String CallerMemberName; [LinkName("#CallerProject")] public static extern String CallerProject; [LinkName("#CallerExpression")] public static extern String[0x00FFFFFF] CallerExpression; [LinkName("#ProjectName")] public static extern String ProjectName; [LinkName("#ModuleName")] public static extern String ModuleName; [LinkName("#TimeLocal")] public static extern String TimeLocal; [LinkName("#IsComptime")] public static extern bool IsComptime; [LinkName("#IsBuilding")] public static extern bool IsBuilding; [LinkName("#IsReified")] public static extern bool IsReified; [LinkName("#CompileRev")] public static extern int32 CompileRev; [Comptime] public static void Assert(bool cond) { if (!cond) Runtime.FatalError("Assert failed"); } static extern void* Comptime_MethodBuilder_EmitStr(void* native, StringView str); static extern void* Comptime_CreateMethod(int32 typeId, StringView methodName, Type returnType, MethodFlags methodFlags); static extern void Comptime_EmitTypeBody(int32 typeId, StringView text); static extern void Comptime_EmitMethodEntry(int64 methodHandle, StringView text); static extern void Comptime_EmitMethodExit(int64 methodHandle, StringView text); static extern void Comptime_EmitMixin(StringView text); [Comptime(OnlyFromComptime=true)] public static MethodBuilder CreateMethod(Type owner, StringView methodName, Type returnType, MethodFlags methodFlags) { MethodBuilder builder = .(); builder.[Friend]mNative = Comptime_CreateMethod((.)owner.TypeId, methodName, returnType, methodFlags); return builder; } [Comptime(OnlyFromComptime=true)] public static void EmitTypeBody(Type owner, StringView text) { Comptime_EmitTypeBody((.)owner.TypeId, text); } [Comptime(OnlyFromComptime=true)] public static void EmitMethodEntry(ComptimeMethodInfo methodHandle, StringView text) { Comptime_EmitMethodEntry(methodHandle.mNativeMethodInstance, text); } [Comptime(OnlyFromComptime=true)] public static void EmitMethodExit(ComptimeMethodInfo methodHandle, StringView text) { Comptime_EmitMethodExit(methodHandle.mNativeMethodInstance, text); } [Comptime] public static void Mixin(StringView text) { if (Compiler.IsComptime) Comptime_EmitMixin(text); } } }