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

512 lines
11 KiB
Beef
Raw Normal View History

2021-01-11 09:41:43 -08:00
using System;
using System.Diagnostics;
using System.Reflection;
2021-01-15 14:28:21 -08:00
using System.Collections;
2021-01-11 09:41:43 -08:00
namespace Tests
{
class Comptime
{
[AttributeUsage(.All)]
struct IFaceAAttribute : Attribute, IComptimeTypeApply
{
String mMemberName;
int32 mInitVal;
public int32 InitVal
{
set mut
{
mInitVal = value;
}
}
public this(String memberName)
{
mMemberName = memberName;
mInitVal = 0;
}
[Comptime]
public void ApplyToType(Type type)
{
Compiler.EmitTypeBody(type, scope $"""
2021-01-11 09:41:43 -08:00
public int32 m{mMemberName} = {mInitVal};
public int32 GetVal{mMemberName}() => mC;
""");
}
}
[AttributeUsage(.Method)]
struct LogAttribute : Attribute, IComptimeMethodApply
{
public static String gLog = new .() ~ delete _;
2021-01-11 09:41:43 -08:00
[Comptime]
public void ApplyToMethod(ComptimeMethodInfo method)
{
String emit = scope $"LogAttribute.gLog.AppendF($\"Called {method}";
for (var fieldIdx < method.ParamCount)
emit.AppendF($" {{ {method.GetParamName(fieldIdx)} }}");
2021-01-16 12:35:51 -08:00
emit.Append("\\n\");");
Compiler.EmitMethodEntry(method, emit);
2021-01-16 12:35:51 -08:00
if (var genericType = method.ReturnType as SpecializedGenericType)
{
if ((genericType.UnspecializedType == typeof(Result<>)) || (genericType.UnspecializedType == typeof(Result<,>)))
{
Compiler.EmitMethodExit(method, """
if (@return case .Err)
LogAttribute.gLog.AppendF($"Error: {@return}");
""");
}
}
2021-01-11 09:41:43 -08:00
}
}
[IFaceA("C", InitVal=345)]
class ClassA
{
public int mA = 123;
[OnCompile(.TypeInit), Comptime]
public static void Generate()
{
Compiler.EmitTypeBody(typeof(Self), """
2021-01-11 09:41:43 -08:00
public int32 mB = 234;
public int32 GetValB() => mB;
""");
}
}
2021-02-25 10:14:22 -08:00
[IFaceA("C", InitVal=345)]
struct StructA
{
public int mA = 123;
public static StructA sSA;
public const StructA cSA = .();
2021-02-25 10:14:22 -08:00
[OnCompile(.TypeInit), Comptime]
public static void Generate()
{
Compiler.EmitTypeBody(typeof(Self), """
public int32 mB = 234;
public int32 GetValB() => mB;
""");
}
}
2021-01-16 12:35:51 -08:00
enum MethodAErr
{
2021-01-16 12:35:51 -08:00
ErrorA,
ErrorB
}
2021-01-16 12:35:51 -08:00
[Log]
static Result<int, MethodAErr> MethodA(int a, int b)
{
return .Err(.ErrorB);
}
2021-01-16 06:26:55 -08:00
static Type GetBiggerType(Type t)
2021-01-15 14:28:21 -08:00
{
2021-01-16 06:26:55 -08:00
switch (t)
2021-01-15 14:28:21 -08:00
{
2021-01-16 06:26:55 -08:00
case typeof(int8): return typeof(int16);
case typeof(int16): return typeof(int32);
case typeof(int32): return typeof(int64);
case typeof(float): return typeof(double);
2021-01-15 14:28:21 -08:00
}
2021-01-16 06:26:55 -08:00
return null;
2021-01-15 14:28:21 -08:00
}
2021-01-16 06:26:55 -08:00
static TTo GetBigger<TFrom, TTo>(TFrom val) where TTo : comptype(GetBiggerType(typeof(TFrom))), operator explicit TFrom
2021-01-15 14:28:21 -08:00
{
2021-01-16 06:26:55 -08:00
return (.)val;
2021-01-15 14:28:21 -08:00
}
public struct TypePrinter<T>
{
const int cFieldCount = GetFieldCount();
const (int32, StringView)[cFieldCount] cMembers = Make();
static int GetFieldCount()
{
int fieldCount = 0;
for (let field in typeof(T).GetFields())
if (!field.IsStatic)
fieldCount++;
return fieldCount;
}
static decltype(cMembers) Make()
{
if (cFieldCount == 0)
return default(decltype(cMembers));
#unwarn
decltype(cMembers) fields = ?;
int i = 0;
for (let field in typeof(T).GetFields())
{
if (!field.IsStatic)
fields[i++] = (field.MemberOffset, field.Name);
}
return fields;
}
public override void ToString(String strBuffer)
{
for (var t in cMembers)
{
if (@t != 0)
strBuffer.Append("\n");
strBuffer.AppendF($"{t.0} {t.1}");
}
}
}
struct TestType
{
public float mX;
public float mY;
public float mZ;
}
class SerializationContext
{
public String mStr = new String() ~ delete _;
public void Serialize<T>(String name, T val) where T : struct
{
mStr.AppendF($"{name} {val}\n");
}
}
interface ISerializable
{
void Serialize(SerializationContext ctx);
}
[AttributeUsage(.Enum | .Struct | .Class, .NotInherited | .ReflectAttribute | .DisallowAllowMultiple)]
struct SerializableAttribute : Attribute, IComptimeTypeApply
{
[Comptime]
public void ApplyToType(Type type)
{
const String SERIALIZE_NAME = "void ISerializable.Serialize(SerializationContext ctx)\n";
String serializeBuffer = new .();
Compiler.Assert(!type.IsUnion);
for (let field in type.GetFields())
{
if (!field.IsInstanceField || field.DeclaringType != type)
continue;
serializeBuffer.AppendF($"\n\tctx.Serialize(\"{field.Name}\", {field.Name});");
}
Compiler.EmitTypeBody(type, scope $"{SERIALIZE_NAME}{{{serializeBuffer}\n}}\n");
Compiler.EmitAddInterface(type, typeof(ISerializable));
}
}
[Serializable]
struct Foo : this(float x, float y)
{
}
public class ComponentHandler<T>
where T : struct
{
uint8* data;
protected override void GCMarkMembers()
{
T* ptr = (T*)data;
GC.Mark!((*ptr));
}
}
[CheckEnum]
enum EnumA
{
case A(int64 aa);
case B(float bb);
}
[AttributeUsage(.All)]
public struct CheckEnumAttribute : Attribute, IComptimeTypeApply
{
public void ApplyToType(Type type)
{
int fieldIdx = 0;
for (var field in type.GetFields())
{
switch (fieldIdx)
{
case 0:
Test.Assert(field.Name == "$payload");
Test.Assert(field.MemberOffset == 0);
Test.Assert(field.FieldType == typeof(int64));
case 1:
Test.Assert(field.Name == "$discriminator");
Test.Assert(field.MemberOffset == 8);
Test.Assert(field.FieldType == typeof(int8));
}
fieldIdx++;
}
Test.Assert(fieldIdx == 4);
}
}
const String cTest0 = Compiler.ReadText("Test0.txt");
const String cTest1 = new String('A', 12);
2021-12-20 09:52:29 -05:00
const uint8[?] cTest0Binary = Compiler.ReadBinary("Test0.txt");
class ClassB<T> where T : const int
{
public typealias TA = comptype(GetVal(10, T));
public const int cTimesTen = 10 * T;
[Comptime]
static Type GetVal(int a, int b)
{
return typeof(float);
}
}
struct Yes;
struct No;
struct IsDictionary<T>
{
public typealias Result = comptype(_isDict(typeof(T)));
[Comptime]
private static Type _isDict(Type type)
{
if (let refType = type as SpecializedGenericType && refType.UnspecializedType == typeof(Dictionary<,>))
return typeof(Yes);
return typeof(No);
}
}
struct GetArg<T, C>
where C : const int
{
public typealias Result = comptype(_getArg(typeof(T), C));
[Comptime]
private static Type _getArg(Type type, int argIdx)
{
if (let refType = type as SpecializedGenericType)
return refType.GetGenericArg(argIdx);
return typeof(void);
}
}
public class DictWrapper<T> where T : var
{
private T mValue = new .() ~ delete _;
}
extension DictWrapper<T>
where T : var
where IsDictionary<T>.Result : Yes
{
typealias TKey = GetArg<T, const 0>.Result;
typealias TValue = GetArg<T, const 1>.Result;
typealias KeyValuePair = (TKey key, TValue value);
typealias KeyRefValuePair = (TKey key, TValue* valueRef);
public ValueEnumerator GetValueEnumerator()
{
return ValueEnumerator(this);
}
public struct ValueEnumerator : IRefEnumerator<TValue*>, IEnumerator<TValue>, IResettable
{
private SelfOuter mParent;
private int_cosize mIndex;
private TValue mCurrent;
const int_cosize cDictEntry = 1;
const int_cosize cKeyValuePair = 2;
public this(SelfOuter parent)
{
mParent = parent;
mIndex = 0;
mCurrent = default;
}
public bool MoveNext() mut
{
// Use unsigned comparison since we set index to dictionary.count+1 when the enumeration ends.
// dictionary.count+1 could be negative if dictionary.count is Int32.MaxValue
while ((uint)mIndex < (uint)mParent.[Friend]mValue.[Friend]mCount)
{
if (mParent.[Friend]mValue.[Friend]mEntries[mIndex].mHashCode >= 0)
{
mCurrent = mParent.[Friend]mValue.[Friend]mEntries[mIndex].mValue;
mIndex++;
return true;
}
mIndex++;
}
mIndex = mParent.[Friend]mValue.[Friend]mCount + 1;
mCurrent = default;
return false;
}
public TValue Current
{
#unwarn
get { return mCurrent; }
}
public ref TValue CurrentRef
{
get mut { return ref mCurrent; }
}
public ref TKey Key
{
get
{
return ref mParent.[Friend]mValue.[Friend]mEntries[mIndex].mKey;
}
}
public void Reset() mut
{
mIndex = 0;
mCurrent = default;
}
public Result<TValue> GetNext() mut
{
if (!MoveNext())
return .Err;
return Current;
}
public Result<TValue*> GetNextRef() mut
{
if (!MoveNext())
return .Err;
return &CurrentRef;
}
}
}
public struct GetTupleField<TTuple, C> where C : const int
{
public typealias Type = comptype(GetTupleFieldType(typeof(TTuple), C));
[Comptime]
private static Type GetTupleFieldType(Type type, int index)
{
if (type.IsGenericParam)
{
Compiler.Assert(type.IsGenericParam);
String tName = type.GetFullName(.. scope .());
Compiler.Assert(tName == "TTuple");
return typeof(var);
}
Compiler.Assert(type.IsTuple);
return type.GetField(index).Get().FieldType;
}
}
[Comptime]
static int GetMixinVal()
{
int a = 23;
Compiler.Mixin("a += 100;");
Compiler.MixinRoot(scope String("b += 200;"));
return a;
}
2021-01-11 09:41:43 -08:00
[Test]
public static void TestBasics()
{
ClassA ca = scope .();
Test.Assert(ca.mA == 123);
Test.Assert(ca.mB == 234);
Test.Assert(ca.GetValB() == 234);
Test.Assert(ca.mC == 345);
Test.Assert(ca.GetValC() == 345);
2021-02-25 10:14:22 -08:00
StructA sa = .();
Test.Assert(sa.mA == 123);
Test.Assert(sa.mB == 234);
Test.Assert(sa.GetValB() == 234);
Test.Assert(sa.mC == 345);
Test.Assert(sa.GetValC() == 345);
Compiler.Mixin("int val = 99;");
Test.Assert(val == 99);
2021-01-16 12:35:51 -08:00
MethodA(34, 45).IgnoreError();
Debug.Assert(LogAttribute.gLog == "Called Tests.Comptime.MethodA(int a, int b) 34 45\nError: Err(ErrorB)");
2021-01-15 14:28:21 -08:00
2021-01-16 06:26:55 -08:00
var v0 = GetBigger((int8)123);
Test.Assert(v0.GetType() == typeof(int16));
String str = scope .();
TypePrinter<TestType> tp = .();
tp.ToString(str);
Debug.Assert(str == """
0 mX
4 mY
8 mZ
""");
Foo bar = .(10, 2);
ISerializable iSer = bar;
SerializationContext serCtx = scope .();
iSer.Serialize(serCtx);
Test.Assert(serCtx.mStr == "x 10\ny 2\n");
Test.Assert(cTest0 == "Test\n0");
Test.Assert(cTest1 == "AAAAAAAAAAAA");
Test.Assert((Object)cTest1 == (Object)"AAAAAAAAAAAA");
2021-12-20 09:52:29 -05:00
Test.Assert((cTest0Binary[0] == (.)'T') && ((cTest0Binary.Count == 6) || (cTest0Binary.Count == 7)));
ClassB<const 3>.TA f = default;
Test.Assert(typeof(decltype(f)) == typeof(float));
Test.Assert(ClassB<const 3>.cTimesTen == 30);
DictWrapper<Dictionary<int, float>> dictWrap = scope .();
dictWrap.[Friend]mValue.Add(1, 2.3f);
dictWrap.[Friend]mValue.Add(2, 3.4f);
int idx = 0;
for (var value in dictWrap.GetValueEnumerator())
{
if (idx == 0)
Test.Assert(value == 2.3f);
else
Test.Assert(value == 3.4f);
++idx;
}
Test.Assert(idx == 2);
var tuple = ((int16)1, 2.3f);
GetTupleField<decltype(tuple), const 0>.Type tupType0;
GetTupleField<decltype(tuple), const 1>.Type tupType1;
Test.Assert(typeof(decltype(tupType0)) == typeof(int16));
Test.Assert(typeof(decltype(tupType1)) == typeof(float));
int b = 34;
Test.Assert(GetMixinVal() == 123);
Test.Assert(b == 234);
2021-01-11 09:41:43 -08:00
}
}
}