1
0
Fork 0
mirror of https://github.com/beefytech/Beef.git synced 2025-06-24 02:28:01 +02:00
Beef/IDEHelper/Tests/src/Params.bf

356 lines
7.2 KiB
Beef
Raw Normal View History

2025-02-18 07:56:02 -08:00
#pragma warning disable 168
using System;
2025-02-18 07:56:02 -08:00
using System.Reflection;
namespace Tests;
class Params
{
2025-02-18 07:56:02 -08:00
[AttributeUsage(.Method)]
struct StringFormatAttribute : Attribute, IOnMethodInit
{
[Comptime]
public void OnMethodInit(MethodInfo methodInfo, Self* prev)
{
String code = scope .();
String format = null;
if (var constType = methodInfo.GetGenericArgType(0) as ConstExprType)
{
if (constType.ValueType == typeof(String))
{
2025-02-18 08:05:46 -08:00
format = String.GetById((.)constType.ValueData);
2025-02-18 07:56:02 -08:00
}
}
if (String.IsNullOrEmpty(format))
return;
int pos = 0;
int len = format.Length;
char8 ch = '\x00';
String s = null;
String fmt = "";
int autoArgIdx = 0;
bool hasTempStr = false;
void FormatError()
{
Runtime.FatalError("Format Error");
}
String bufOut = scope .();
void FlushOut()
{
if (bufOut.IsEmpty)
return;
code.Append("outStr.Append(");
bufOut.Quote(code);
code.Append(");\n");
bufOut.Clear();
}
while (true)
{
int p = pos;
int i = pos;
while (pos < len)
{
ch = format[pos];
pos++;
if (ch == '}')
{
if (pos < len && format[pos] == '}') // Treat as escape character for }}
pos++;
else
FormatError();
}
if (ch == '{')
{
if (pos < len && format[pos] == '{') // Treat as escape character for {{
pos++;
else
{
pos--;
break;
}
}
bufOut.Append(ch);
}
if (pos == len) break;
pos++;
int index = 0;
if (pos == len || (ch = format[pos]) < '0' || ch > '9')
{
if ((pos < len) &&
((ch == '}') || (ch == ':') || (ch == ',')))
index = autoArgIdx++;
else
FormatError();
}
else
{
repeat
{
index = index * 10 + ch - '0';
pos++;
if (pos == len) FormatError();
ch = format[pos];
}
while (ch >= '0' && ch <= '9' && index < 1000000);
}
var paramType = methodInfo.GetParamType(index + 3);
var paramName = methodInfo.GetParamName(index + 3);
while (pos < len && (ch = format[pos]) == ' ') pos++;
bool leftJustify = false;
int width = 0;
if (ch == ',')
{
pos++;
while (pos < len && format[pos] == ' ') pos++;
if (pos == len) FormatError();
ch = format[pos];
if (ch == '-')
{
leftJustify = true;
pos++;
if (pos == len) FormatError();
ch = format[pos];
}
if (ch < '0' || ch > '9') FormatError();
repeat
{
width = width * 10 + ch - '0';
pos++;
if (pos == len) FormatError();
ch = format[pos];
}
while (ch >= '0' && ch <= '9' && width < 1000000);
}
while (pos < len && (ch = format[pos]) == ' ') pos++;
//Object arg = args[index];
if (ch == ':')
{
if (fmt == "")
fmt = scope:: String(64);
else
fmt.Clear();
bool isFormatEx = false;
pos++;
p = pos;
i = pos;
while (true)
{
if (pos == len) FormatError();
ch = format[pos];
pos++;
if (ch == '{')
{
isFormatEx = true;
if (pos < len && format[pos] == '{') // Treat as escape character for {{
pos++;
else
FormatError();
}
else if (ch == '}')
{
// We only treat '}}' as an escape character if the format had an opening '{'. Otherwise we just close on the first '}'
if ((isFormatEx) && (pos < len && format[pos] == '}')) // Treat as escape character for }}
pos++;
else
{
pos--;
break;
}
}
if (fmt == null)
{
fmt = scope:: String(0x100);
}
fmt.Append(ch);
}
}
if (ch != '}') Runtime.FatalError("Format error");
pos++;
FlushOut();
bool checkNull = paramType.IsObject || paramType.IsInterface;
bool hasWidth = width != 0;
bool hasFmt = fmt != "";
if (checkNull)
{
code.AppendF($"if ({paramName} == null)\n");
code.Append(" outStr.Append(\"null\");\nelse\n{\n");
}
if ((!leftJustify) && (width > 0))
{
// We need a temporary string
if (!hasTempStr)
{
code.Insert(0, "var s = scope String(128);\n");
hasTempStr = false;
}
else
{
code.Append("s.Clear();\n");
}
if (paramType.ImplementsInterface(typeof(IFormattable)))
{
if (!hasFmt)
{
code.AppendF("if (provider != null)\n ");
}
code.AppendF($"((IFormattable){paramName}).ToString(s, ");
fmt.Quote(code);
code.AppendF($", provider);\n");
if (!hasFmt)
{
code.AppendF("else\n");
code.AppendF($" {paramName}.ToString(s);\n");
}
}
else
{
code.AppendF($"{paramName}.ToString(s);\n");
}
code.AppendF($"outStr.Append(' ', {width} - s.[Friend]mLength);\n");
code.Append("outStr.Append(s);\n");
}
else
{
if (hasWidth)
{
code.AppendF($"int start_{pos} = outStr.[Friend]mLength;\n");
}
if (paramType.ImplementsInterface(typeof(IFormattable)))
{
if (!hasFmt)
{
code.AppendF("if (provider != null)\n ");
}
code.AppendF($"((IFormattable){paramName}).ToString(outStr, ");
fmt.Quote(code);
code.AppendF($", provider);\n");
if (!hasFmt)
{
code.AppendF("else\n");
code.AppendF($" {paramName}.ToString(outStr);\n");
}
}
else
{
code.AppendF($"{paramName}.ToString(outStr);\n");
}
if (hasWidth)
{
code.AppendF($"outStr.Append(' ', {width} - (outStr.[Friend]mLength - start_{pos}));\n");
}
}
if (checkNull)
{
code.Append("}\n");
}
}
FlushOut();
Compiler.EmitMethodEntry(methodInfo, code);
}
}
[StringFormat]
static void StrFormat<TStr, TArgs>(String outStr, IFormatProvider provider, TStr formatStr, params TArgs args) where TStr : const String where TArgs : Tuple
{
}
class ClassA<T> where T : Tuple
{
public static int Test(delegate int(char8 a, params T) dlg, params T par)
{
return dlg('A', params par);
}
}
class ClassB : ClassA<(int a, float b)>
{
}
2025-02-18 07:56:02 -08:00
static void Test1<T>(params T args) where T : Tuple
{
Test3<T>(args);
}
static void Test2<T>(params T args) where T : Delegate
{
var vals = args;
Test3(args);
}
static void Test3<T>(T args) where T : Tuple
{
}
static void Test4<T>(params (int, float) args)
{
var a = args;
int arg0 = a.0;
float arg1 = a.1;
}
struct StructA
{
public int mA;
public override void ToString(String strBuffer)
{
strBuffer.AppendF($"StructA({mA})");
}
}
[Test]
public static void TestBasics()
{
int val = ClassB.Test(scope (a, __a, b) =>
{
return (.)a + (.)__a + (.)b;
}, 10, 2.3f);
Test.Assert(val == 65+10+2);
2025-02-18 07:56:02 -08:00
Test1(1, 2.3f);
delegate void(int a, float b) dlg = default;
Test2<delegate void(int a, float b)>(1, 2.3f);
String tStr = null;
StructA sa = .() { mA = 123 };
String str = StrFormat(.. scope .(), null, $"This is a test string {123,-6} with some numbers in it {234,6} {0x1234:X} {sa} {tStr}.");
Test.Assert(str == "This is a test string 123 with some numbers in it 234 1234 StructA(123) null.");
}
}