diff --git a/ScriptCore/Math/Attributes/MatrixAttribute.cs b/ScriptCore/Math/Attributes/MatrixAttribute.cs new file mode 100644 index 0000000..ac82cea --- /dev/null +++ b/ScriptCore/Math/Attributes/MatrixAttribute.cs @@ -0,0 +1,111 @@ +using System; + +namespace GlitchyEngine.Math.Attributes; + +/// +/// Specifies that fields for the components of a matrix should be generated as well as basic methods like constructors, type casts and equality checks. +/// +[AttributeUsage(AttributeTargets.Struct)] +public sealed class MatrixAttribute : Attribute +{ + /// + /// The Type of the matrices components. + /// + public Type Type { get; } + /// + /// The number of rows the matrix has. + /// + public int Rows { get; } + /// + /// The number of columns the matrix has. + /// + public int Columns { get; } + /// + /// The base name of the matrix. (Name of matrix without the row and column count) + /// + public string BaseName { get; } + + /// + /// Creates a new instance of the class. + /// + /// + /// + /// + /// + public MatrixAttribute(Type type, int rows, int columns, string baseName) + { + Type = type; + Rows = rows; + Columns = columns; + BaseName = baseName; + } +} + +/// +/// Specifies that the matrix type should have comparison operators (>, <, >= and <=) generated for it. +/// +[AttributeUsage(AttributeTargets.Struct)] +public sealed class ComparableMatrixAttribute : Attribute +{ +} + +/// +/// Specifies that the matrix type should have component wise math operators (+, -) generated for it. +/// +[AttributeUsage(AttributeTargets.Struct)] +public class MatrixMathAttribute : Attribute { } + +/// +/// Specifies that the matrix type should have component wise logic operators (&, ^ and |) generated for it. +/// +[AttributeUsage(AttributeTargets.Struct)] +public class MatrixLogicAttribute : Attribute { } + +/// +/// Generates a multiplication overload for matrix type with the specified vector type. +/// +[AttributeUsage(AttributeTargets.Struct, AllowMultiple = true)] +public class MatrixVectorMultiplicationAttribute : Attribute +{ + /// + /// The type of the vector that will be multiplied with the matrix. + /// + public Type VectorType { get; set; } + + /// + /// Creates a new instance of the class. + /// + /// + public MatrixVectorMultiplicationAttribute(Type vectorType) + { + VectorType = vectorType; + } +} + +/// +/// Generates a cast cast from the matrix type to the specified target type. +/// +[AttributeUsage(AttributeTargets.Struct, AllowMultiple = true)] +public class MatrixCastAttribute : Attribute +{ + /// + /// The type of the target matrix type. + /// + public Type TargetType { get; set; } + + /// + /// If the cast will be explicit; if it will an implicit cast. + /// + public bool IsExplicit { get; set; } + + /// + /// Creates a new instance of the class. + /// + /// + /// + public MatrixCastAttribute(Type targetType, bool isExplicit) + { + TargetType = targetType; + IsExplicit = isExplicit; + } +} diff --git a/ScriptCore/Math/Math.cs b/ScriptCore/Math/Math.cs index f56a6ba..6a45d3a 100644 --- a/ScriptCore/Math/Math.cs +++ b/ScriptCore/Math/Math.cs @@ -9,7 +9,7 @@ namespace GlitchyEngine.Math; /// /// Provides constants and methods for trigonometric and vector calculations. /// -public static class Math +public static partial class Math { /// An optimal representation of π. public const float Pi = 3.141592654f; @@ -35,6 +35,7 @@ public static class Math /// Converts radians to degrees public const float DegToRad = Pi / 180.0f; +#region any / all /// Returns true if at least one of the components is true. public static bool any(bool value) => value; @@ -48,18 +49,58 @@ public static class Math /// Returns true if at least one of the components is true. public static bool any(bool4 value) => value.X || value.Y || value.Z || value.W; + /// Returns true if at least one of the components is true. + public static bool any(bool2x2 value) => value.M11 || value.M12 || value.M21 || value.M22; + + /// Returns true if at least one of the components is true. + public static bool any(bool3x3 value) => value.M11 || value.M12 || value.M13 || + value.M21 || value.M22 || value.M23 || + value.M31 || value.M32 || value.M33; + + /// Returns true if at least one of the components is true. + public static bool any(bool4x4 value) => value.M11 || value.M12 || value.M13 || value.M14 || + value.M21 || value.M22 || value.M23 || value.M24 || + value.M31 || value.M32 || value.M33 || value.M34 || + value.M41 || value.M42 || value.M43 || value.M44; + /// Returns true if all of the components are true. public static bool all(bool value) => value; /// Returns true if all of the components are true. - public static bool all(bool2 value) => value.X && value.Y; + public static bool all(bool2 value) => value is { X: true, Y: true }; /// Returns true if all of the components are true. - public static bool all(bool3 value) => value.X && value.Y && value.Z; + public static bool all(bool3 value) => value is { X: true, Y: true, Z: true }; /// Returns true if all of the components are true. - public static bool all(bool4 value) => value.X && value.Y && value.Z && value.W; + public static bool all(bool4 value) => value is { X: true, Y: true, Z: true, W: true }; + /// Returns true if all of the components are true. + public static bool all(bool2x2 value) => value is + { + M11: true, M12: true, + M21: true, M22: true + }; + + /// Returns true if all of the components are true. + public static bool all(bool3x3 value) => value is + { + M11: true, M12: true, M13: true, + M21: true, M22: true, M23: true, + M31: true, M32: true, M33: true + }; + + /// Returns true if all of the components are true. + public static bool all(bool4x4 value) => value is + { + M11: true, M12: true, M13: true, M14: true, + M21: true, M22: true, M23: true, M24: true, + M31: true, M32: true, M33: true, M34: true, + M41: true, M42: true, M43: true, M44: true + }; + +#endregion + #region abs public static float abs(float value) diff --git a/ScriptCore/Math/Matrices/BoolMatrices.cs b/ScriptCore/Math/Matrices/BoolMatrices.cs new file mode 100644 index 0000000..333fe5c --- /dev/null +++ b/ScriptCore/Math/Matrices/BoolMatrices.cs @@ -0,0 +1,30 @@ +using GlitchyEngine.Math.Attributes; + +namespace GlitchyEngine.Math; + +/// +/// A matrix with 2 rows and 2 columns of boolean values. +/// +[Matrix(typeof(bool), 2, 2, "bool")] +[MatrixLogic] +public partial struct bool2x2 +{ +} + +/// +/// A matrix with 3 rows and 3 columns of boolean values. +/// +[Matrix(typeof(bool), 3, 3, "bool")] +[MatrixLogic] +public partial struct bool3x3 +{ +} + +/// +/// A matrix with 4 rows and 4 columns of boolean values. +/// +[Matrix(typeof(bool), 4, 4, "bool")] +[MatrixLogic] +public partial struct bool4x4 +{ +} diff --git a/ScriptCore/Math/Matrices/DoubleMatrices.cs b/ScriptCore/Math/Matrices/DoubleMatrices.cs new file mode 100644 index 0000000..ee4c678 --- /dev/null +++ b/ScriptCore/Math/Matrices/DoubleMatrices.cs @@ -0,0 +1,90 @@ +using GlitchyEngine.Math.Attributes; + +namespace GlitchyEngine.Math; + +/// +/// A matrix with 2 rows and 2 columns of double-precision floating-point values. +/// +[Matrix(typeof(double), 2, 2, "double")] +[ComparableMatrix] +[MatrixMath] +[MatrixVectorMultiplication(typeof(double2))] +[MatrixCast(typeof(float), true)] +public partial struct double2x2 +{ + /// + /// A 2x2 matrix whose elements are all equal to zero. + /// + public static readonly double2x2 Zero = new(0.0); + + /// + /// A 2x2 matrix whose elements are all equal to one. + /// + public static readonly double2x2 One = new(1.0); + + /// + /// The identity 2x2 matrix. + /// + public static readonly double2x2 Identity = new( + 1.0, 0.0, + 0.0, 1.0); +} + +/// +/// A matrix with 3 rows and 3 columns of double-precision floating-point values. +/// +[Matrix(typeof(double), 3, 3, "double")] +[ComparableMatrix] +[MatrixMath] +[MatrixVectorMultiplication(typeof(double3))] +[MatrixCast(typeof(float), true)] +public partial struct double3x3 +{ + /// + /// A 3x3 matrix whose elements are all equal to zero. + /// + public static readonly double3x3 Zero = new(0.0); + + /// + /// A 3x3 matrix whose elements are all equal to one. + /// + public static readonly double3x3 One = new(1.0); + + /// + /// The identity 3x3 matrix. + /// + public static readonly double3x3 Identity = new( + 1.0, 0.0, 0.0, + 0.0, 1.0, 0.0, + 0.0, 0.0, 1.0); +} + +/// +/// A matrix with 4 rows and 4 columns of double-precision floating-point values. +/// +[Matrix(typeof(double), 4, 4, "double")] +[ComparableMatrix] +[MatrixMath] +[MatrixVectorMultiplication(typeof(double4))] +[MatrixCast(typeof(float), true)] +public partial struct double4x4 +{ + /// + /// A 4x4 matrix whose elements are all equal to zero. + /// + public static readonly double4x4 Zero = new(0.0); + + /// + /// A 4x4 matrix whose elements are all equal to one. + /// + public static readonly double4x4 One = new(1.0); + + /// + /// The identity 4x4 matrix. + /// + public static readonly double4x4 Identity = new( + 1.0, 0.0, 0.0, 0.0, + 0.0, 1.0, 0.0, 0.0, + 0.0, 0.0, 1.0, 0.0, + 0.0, 0.0, 0.0, 1.0); +} diff --git a/ScriptCore/Math/Matrices/FloatMatrices.cs b/ScriptCore/Math/Matrices/FloatMatrices.cs new file mode 100644 index 0000000..5a9ddd9 --- /dev/null +++ b/ScriptCore/Math/Matrices/FloatMatrices.cs @@ -0,0 +1,125 @@ +using GlitchyEngine.Math.Attributes; + +namespace GlitchyEngine.Math; + +/// +/// A matrix with 2 rows and 2 columns of single-precision floating-point values. +/// +/// +/// The matrix is stored in a column-major order. +/// The positions of the elements is the following: +/// +/// +/// +/// +/// +/// +/// +///
M11 M12
M21 M22
+/// The memory order is M11, M21, M12, M22. +///
+[Matrix(typeof(float), 2, 2, "float")] +[ComparableMatrix] +[MatrixMath] +[MatrixVectorMultiplication(typeof(float2))] +[MatrixCast(typeof(double), true)] +[MatrixCast(typeof(Half), true)] +[MatrixCast(typeof(int), true)] +[MatrixCast(typeof(uint), true)] +public partial struct float2x2 +{ + /// + /// A 2x2 matrix whose elements are all equal to zero. + /// + public static readonly float2x2 Zero = new(0.0f); + + /// + /// A 2x2 matrix whose elements are all equal to one. + /// + public static readonly float2x2 One = new(1.0f); + + /// + /// The identity 2x2 matrix. + /// + public static readonly float2x2 Identity = new( + 1.0f, 0.0f, + 0.0f, 1.0f); + + /// + /// Creates a new 2x2 matrix that represents a rotation by a specified angle. + /// + /// The angle of rotation. + /// The matrix representing the rotation. + public static float2x2 Rotation(float angle) + { + float cos = Math.cos(angle); + float sin = Math.sin(angle); + + return new float2x2(cos, -sin, sin, cos); + } +} + +/// +/// A matrix with 3 rows and 3 columns of single-precision floating-point values. +/// +[Matrix(typeof(float), 3, 3, "float")] +[ComparableMatrix] +[MatrixMath] +[MatrixVectorMultiplication(typeof(float3))] +[MatrixCast(typeof(double), true)] +[MatrixCast(typeof(Half), true)] +[MatrixCast(typeof(int), true)] +[MatrixCast(typeof(uint), true)] +public partial struct float3x3 +{ + /// + /// A 3x3 matrix whose elements are all equal to zero. + /// + public static readonly float3x3 Zero = new(0.0f); + + /// + /// A 3x3 matrix whose elements are all equal to one. + /// + public static readonly float3x3 One = new(1.0f); + + /// + /// The identity 3x3 matrix. + /// + public static readonly float3x3 Identity = new( + 1.0f, 0.0f, 0.0f, + 0.0f, 1.0f, 0.0f, + 0.0f, 0.0f, 1.0f); +} + +/// +/// A matrix with 4 rows and 4 columns of single-precision floating-point values. +/// +[Matrix(typeof(float), 4, 4, "float")] +[ComparableMatrix] +[MatrixMath] +[MatrixVectorMultiplication(typeof(float4))] +[MatrixCast(typeof(double), true)] +[MatrixCast(typeof(Half), true)] +[MatrixCast(typeof(int), true)] +[MatrixCast(typeof(uint), true)] +public partial struct float4x4 +{ + /// + /// A 4x4 matrix whose elements are all equal to zero. + /// + public static readonly float4x4 Zero = new(0.0f); + + /// + /// A 4x4 matrix whose elements are all equal to one. + /// + public static readonly float4x4 One = new(1.0f); + + /// + /// The identity 4x4 matrix. + /// + public static readonly float4x4 Identity = new( + 1.0f, 0.0f, 0.0f, 0.0f, + 0.0f, 1.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 1.0f, 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f); +} diff --git a/ScriptCore/Math/Matrices/HalfMatrices.cs b/ScriptCore/Math/Matrices/HalfMatrices.cs new file mode 100644 index 0000000..c7276c4 --- /dev/null +++ b/ScriptCore/Math/Matrices/HalfMatrices.cs @@ -0,0 +1,87 @@ +using GlitchyEngine.Math.Attributes; + +namespace GlitchyEngine.Math; + +/// +/// A matrix with 2 rows and 2 columns of half-precision floating-point values. +/// +[Matrix(typeof(Half), 2, 2, "half")] +[ComparableMatrix] +[MatrixMath] +[MatrixCast(typeof(float), true)] +public partial struct half2x2 +{ + /// + /// A 2x2 matrix whose elements are all equal to zero. + /// + public static readonly half2x2 Zero = new((Half)0.0f); + + /// + /// A 2x2 matrix whose elements are all equal to one. + /// + public static readonly half2x2 One = new((Half)1.0f); + + /// + /// The identity 2x2 matrix. + /// + public static readonly half2x2 Identity = new( + (Half)1.0f, (Half)0.0f, + (Half)0.0f, (Half)1.0f); +} + +/// +/// A matrix with 3 rows and 3 columns of half-precision floating-point values. +/// +[Matrix(typeof(Half), 3, 3, "half")] +[ComparableMatrix] +[MatrixMath] +[MatrixCast(typeof(float), true)] +public partial struct half3x3 +{ + /// + /// A 3x3 matrix whose elements are all equal to zero. + /// + public static readonly half3x3 Zero = new((Half)0.0f); + + /// + /// A 3x3 matrix whose elements are all equal to one. + /// + public static readonly half3x3 One = new((Half)1.0f); + + /// + /// The identity 3x3 matrix. + /// + public static readonly half3x3 Identity = new( + (Half)1.0f, (Half)0.0f, (Half)0.0f, + (Half)0.0f, (Half)1.0f, (Half)0.0f, + (Half)0.0f, (Half)0.0f, (Half)1.0f); +} + +/// +/// A matrix with 4 rows and 4 columns of half-precision floating-point values. +/// +[Matrix(typeof(Half), 4, 4, "half")] +[ComparableMatrix] +[MatrixMath] +[MatrixCast(typeof(float), true)] +public partial struct half4x4 +{ + /// + /// A 4x4 matrix whose elements are all equal to zero. + /// + public static readonly half4x4 Zero = new((Half)0.0f); + + /// + /// A 4x4 matrix whose elements are all equal to one. + /// + public static readonly half4x4 One = new((Half)1.0f); + + /// + /// The identity 4x4 matrix. + /// + public static readonly half4x4 Identity = new( + (Half)1.0f, (Half)0.0f, (Half)0.0f, (Half)0.0f, + (Half)0.0f, (Half)1.0f, (Half)0.0f, (Half)0.0f, + (Half)0.0f, (Half)0.0f, (Half)1.0f, (Half)0.0f, + (Half)0.0f, (Half)0.0f, (Half)0.0f, (Half)1.0f); +} diff --git a/ScriptCore/Math/Matrices/IntMatrices.cs b/ScriptCore/Math/Matrices/IntMatrices.cs new file mode 100644 index 0000000..d3e6b42 --- /dev/null +++ b/ScriptCore/Math/Matrices/IntMatrices.cs @@ -0,0 +1,36 @@ +using GlitchyEngine.Math.Attributes; + +namespace GlitchyEngine.Math; + +/// +/// A matrix with 2 rows and 2 columns of 32-bit signed integer values. +/// +[Matrix(typeof(int), 2, 2, "int")] +[MatrixLogic] +[MatrixCast(typeof(float), true)] +[MatrixCast(typeof(uint), true)] +public partial struct int2x2 +{ +} + +/// +/// A matrix with 3 rows and 3 columns of 32-bit signed integer values. +/// +[Matrix(typeof(int), 3, 3, "int")] +[MatrixLogic] +[MatrixCast(typeof(float), true)] +[MatrixCast(typeof(uint), true)] +public partial struct int3x3 +{ +} + +/// +/// A matrix with 4 rows and 4 columns of 32-bit signed integer values. +/// +[Matrix(typeof(int), 4, 4, "int")] +[MatrixLogic] +[MatrixCast(typeof(float), true)] +[MatrixCast(typeof(uint), true)] +public partial struct int4x4 +{ +} diff --git a/ScriptCore/Math/Matrices/UIntMatrices.cs b/ScriptCore/Math/Matrices/UIntMatrices.cs new file mode 100644 index 0000000..f7f2300 --- /dev/null +++ b/ScriptCore/Math/Matrices/UIntMatrices.cs @@ -0,0 +1,36 @@ +using GlitchyEngine.Math.Attributes; + +namespace GlitchyEngine.Math; + +/// +/// A matrix with 2 rows and 2 columns of 32-bit unsigned integer values. +/// +[Matrix(typeof(uint), 2, 2, "uint")] +[MatrixLogic] +[MatrixCast(typeof(float), true)] +[MatrixCast(typeof(int), true)] +public partial struct uint2x2 +{ +} + +/// +/// A matrix with 3 rows and 3 columns of 32-bit unsigned integer values. +/// +[Matrix(typeof(uint), 3, 3, "uint")] +[MatrixLogic] +[MatrixCast(typeof(float), true)] +[MatrixCast(typeof(int), true)] +public partial struct uint3x3 +{ +} + +/// +/// A matrix with 4 rows and 4 columns of 32-bit unsigned integer values. +/// +[Matrix(typeof(uint), 4, 4, "uint")] +[MatrixLogic] +[MatrixCast(typeof(float), true)] +[MatrixCast(typeof(int), true)] +public partial struct uint4x4 +{ +} diff --git a/ScriptCore/Math/BoolVectors.cs b/ScriptCore/Math/Vectors/BoolVectors.cs similarity index 100% rename from ScriptCore/Math/BoolVectors.cs rename to ScriptCore/Math/Vectors/BoolVectors.cs diff --git a/ScriptCore/Math/DoubleVectors.cs b/ScriptCore/Math/Vectors/DoubleVectors.cs similarity index 100% rename from ScriptCore/Math/DoubleVectors.cs rename to ScriptCore/Math/Vectors/DoubleVectors.cs diff --git a/ScriptCore/Math/FloatVectors.cs b/ScriptCore/Math/Vectors/FloatVectors.cs similarity index 100% rename from ScriptCore/Math/FloatVectors.cs rename to ScriptCore/Math/Vectors/FloatVectors.cs diff --git a/ScriptCore/Math/HalfVectors.cs b/ScriptCore/Math/Vectors/HalfVectors.cs similarity index 100% rename from ScriptCore/Math/HalfVectors.cs rename to ScriptCore/Math/Vectors/HalfVectors.cs diff --git a/ScriptCore/Math/IntVectors.cs b/ScriptCore/Math/Vectors/IntVectors.cs similarity index 100% rename from ScriptCore/Math/IntVectors.cs rename to ScriptCore/Math/Vectors/IntVectors.cs diff --git a/ScriptCore/Math/UIntVectors.cs b/ScriptCore/Math/Vectors/UIntVectors.cs similarity index 100% rename from ScriptCore/Math/UIntVectors.cs rename to ScriptCore/Math/Vectors/UIntVectors.cs diff --git a/ScriptCoreGenerator/MatrixGenerator.cs b/ScriptCoreGenerator/MatrixGenerator.cs new file mode 100644 index 0000000..c1eb60d --- /dev/null +++ b/ScriptCoreGenerator/MatrixGenerator.cs @@ -0,0 +1,921 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Text; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp.Syntax; + +namespace ScriptCoreGenerator; + +[Generator] +public class MatrixGenerator : ISourceGenerator +{ + public void Initialize(GeneratorInitializationContext context) + { + context.RegisterForSyntaxNotifications(() => new MatrixSyntaxReceiver()); + } + + public void Execute(GeneratorExecutionContext context) + { + var receiver = (MatrixSyntaxReceiver?)context.SyntaxReceiver; + + if (receiver == null) return; + + StringBuilder builder = new(); + + foreach (var matrix in receiver.Matrices) + { + builder.Clear(); + + // Copy all usings from the original file + builder.AppendLine(matrix.Struct.GetParent().Usings.ToFullString()); + + builder.Append($$""" + #nullable enable + + using System; + // For "DebuggerBrowsable" and "DebuggerBrowsableState" + using System.Diagnostics; + + namespace {{matrix.Struct.GetNamespace()}}; + + public partial struct {{matrix.Name}} + { + + """); + + GenerateFields(matrix, builder); + GenerateConstructors(matrix, builder); + + GenerateCasts(matrix, builder); + + GenerateArrayAccess(matrix, builder); + + GenerateEqualityOperators(matrix, builder); + + GenerateEqualsMethod(matrix, builder); + + GenerateGetHashCode(matrix, builder); + + if (receiver.ComparableReceiver.Matrices.Contains(matrix.Name)) + { + GenerateComparisonOperators(matrix, builder); + } + + if (receiver.MathReceiver.Matrices.Contains(matrix.Name)) + { + GenerateMathOperators(matrix, builder); + } + + if (receiver.LogicReceiver.Matrices.Contains(matrix.Name)) + { + GenerateLogicOperators(matrix, builder); + } + + GenerateCastOperators(receiver.Matrices, matrix, matrix.ElementTypeName, builder, context); + + foreach (var cast in receiver.CastReceiver.Casts.Where(c => c.SourceTypeName == matrix.Name)) + { + GenerateCastOperators(receiver.Matrices, matrix, cast.TargetElementTypeName, builder, context); + + } + + foreach (var vectorMultiplication in receiver.VectorMultiplicationReceiver.Multiplications.Where(c => c.MatrixType == matrix.Name)) + { + VectorSyntaxReceiver.VectorDefinition vectorDefinition = receiver.VectorReceiver.Vectors.First(v => v.Name == vectorMultiplication.VectorType); + + if (vectorDefinition.ComponentCount != matrix.Rows && vectorDefinition.ComponentCount != matrix.Columns) + { + context.ReportDiagnostic(Diagnostic.Create( + new DiagnosticDescriptor( + "SG0002", + "Vector dimensions don't match matrix dimensions.", + $"Cannot generate vector-matrix multiplication for \"{vectorDefinition.Name}\" to \"{matrix.Name}\" because the dimensions don't match. The vectors component-count must match the matrices rows or columns or both.", + "Vector Generator", + DiagnosticSeverity.Error, + true), matrix.Struct.GetLocation())); + + continue; + } + + GenerateMatrixMulVector(matrix, vectorDefinition, builder); + GenerateVectorMulMatrix(matrix, vectorDefinition, builder); + } + + GenerateToString(matrix, builder); + + // There are too many swizzles for 4x4 matrices, Rider is literally dying :( + //GenerateSwizzle(matrix, builder); + + builder.AppendLine("}"); + + context.AddSource($"{matrix.Name}.g.cs", builder.ToString()); + } + } + + private void GenerateEqualsMethod(MatrixDefinition matrix, StringBuilder builder) + { + builder.Append($$""" + public override bool Equals(object? obj) + { + if (obj == null || obj is not {{matrix.Name}} other) + return false; + + return {{string.Join(" && ", Enumerable.Range(0, matrix.Rows).SelectMany(i => + Enumerable.Range(0, matrix.Columns).Select(j => + $"M{j + 1}{i + 1} == other.M{j + 1}{i + 1}")))}}; + } + + + """); + } + + private void GenerateGetHashCode(MatrixDefinition matrix, StringBuilder builder) + { + builder.Append(""" + public override int GetHashCode() + { + // Unchecked to allow overflow + unchecked + { + int hash = 17; + + """); + + for (int c = 0; c < matrix.Columns; c++) + for (int r = 0; r < matrix.Rows; r++) + { + builder.Append($"\t\t\thash = hash * 23 + M{r + 1}{c + 1}.GetHashCode();\n"); + } + + builder.Append(""" + return hash; + } + } + + + """); + } + + private void GenerateEqualityOperators(MatrixDefinition matrix, StringBuilder builder) + { + GenerateComparisonOperator("==", matrix, builder); + GenerateComparisonOperator("!=", matrix, builder); + } + + private void GenerateComparisonOperator(string op, MatrixDefinition matrix, StringBuilder builder) + { + builder.Append($$""" + public static bool{{matrix.Columns}}x{{matrix.Rows}} operator {{op}}({{matrix.Name}} left, {{matrix.Name}} right) + { + {{ComponentWiseCore(op, matrix)}} + } + + + """); + } + + private void GenerateFields(MatrixDefinition matrix, StringBuilder builder) + { + builder.Append($"\tpublic {matrix.ElementTypeName} "); + + for (int i = 0; i < matrix.Rows; i++) + { + for (int j = 0; j < matrix.Columns; j++) + { + if (i != 0 || j != 0) + builder.Append(", "); + + builder.Append($"M{j + 1}{i + 1}"); + } + } + + builder.Append(";\n\n"); + } + + private void GenerateConstructors(MatrixDefinition matrix, StringBuilder builder) + { + GenerateSingleConstructor(matrix, builder); + GenerateSingleElementConstructor(matrix, builder); + } + + private void GenerateSingleConstructor(MatrixDefinition matrix, StringBuilder builder) + { + builder.Append($$""" + /// + /// Creates a new {{matrix.Rows}}x{{matrix.Columns}} matrix with all elements set to the specified value. + /// + /// The value that all elements will be initialized with. + public {{matrix.Name}}({{matrix.ElementTypeName}} value) + { + + """); + + for (int i = 0; i < matrix.Rows; i++) + { + for (int j = 0; j < matrix.Columns; j++) + { + builder.Append($"\t\tM{j + 1}{i + 1} = value;\n"); + } + } + + builder.Append("\t}\n\n"); + } + + /// + /// Generates a constructor that takes an argument for each element in the matrix. + /// + private void GenerateSingleElementConstructor(MatrixDefinition matrix, StringBuilder builder) + { + builder.Append($$""" + /// + /// Creates a new {{matrix.Rows}}x{{matrix.Columns}} matrix with the specified elements. + /// + public {{matrix.Name}}({{string.Join(", ", + Enumerable.Range(0, matrix.Rows).SelectMany(i => + Enumerable.Range(0, matrix.Columns).Select(j => + $"{matrix.ElementTypeName} m{j + 1}{i + 1}"))) + }}) + { + + """); + + for (int i = 0; i < matrix.Rows; i++) + { + for (int j = 0; j < matrix.Columns; j++) + { + builder.Append($"\t\tM{j + 1}{i + 1} = m{j + 1}{i + 1};\n"); + } + } + + builder.Append("\t}\n\n"); + } + + private void GenerateCasts(MatrixDefinition matrix, StringBuilder builder) + { + builder.Append($$""" + public static implicit operator {{matrix.Name}}({{matrix.ElementTypeName}} value) + { + return new {{matrix.Name}}(value); + } + + + """); + + // TODO: Casts to other matrix sizes? + } + + private void GenerateArrayAccess(MatrixDefinition matrix, StringBuilder builder) + { + Generate1DArrayAccess(matrix, builder); + Generate2DArrayAccess(matrix, builder); + } + + private void Generate1DArrayAccess(MatrixDefinition matrix, StringBuilder builder) + { + StringBuilder getterSwitch = new(); + StringBuilder setterSwitch = new(); + + for (int i = 0; i < matrix.Rows; i++) + { + for (int j = 0; j < matrix.Columns; j++) + { + getterSwitch.Append($$""" + + case {{i * matrix.Columns + j}}: + return M{{j + 1}}{{i + 1}}; + """); + + setterSwitch.Append($$""" + + case {{i * matrix.Columns + j}}: + M{{j + 1}}{{i + 1}} = value; + break; + """); + } + } + + builder.Append($$""" + /// + /// Gets or sets the value at the specified index, indexed in a column-major order. + /// + /// The index of the element to get or set. + /// Thrown when the index is out of range. + public {{matrix.ElementTypeName}} this[int index] + { + get + { + switch (index) + {{{getterSwitch}} + default: + throw new IndexOutOfRangeException(); + } + } + + set + { + switch (index) + {{{setterSwitch}} + default: + throw new IndexOutOfRangeException(); + } + } + } + + + """); + } + + private void Generate2DArrayAccess(MatrixDefinition matrix, StringBuilder builder) + { + builder.Append($$""" + /// + /// Gets or sets the value at the specified row and column. + /// + /// The row of the element to get or set. + /// The column of the element to get or set. + /// Thrown when the index is out of range. + public {{matrix.ElementTypeName}} this[int row, int column] + { + get => this[row * {{matrix.Columns}} + column]; + + set => this[row * {{matrix.Columns}} + column] = value; + } + + + """); + } + + private void GenerateComparisonOperators(MatrixDefinition matrix, StringBuilder builder) + { + GenerateComparisonOperator(">", matrix, builder); + GenerateComparisonOperator("<", matrix, builder); + GenerateComparisonOperator(">=", matrix, builder); + GenerateComparisonOperator("<=", matrix, builder); + } + + private void GenerateMathOperators(MatrixDefinition matrix, StringBuilder builder) + { + GenerateUnaryOperatorOverload("+", matrix, builder); + GenerateUnaryOperatorOverload("-", matrix, builder); + + GenerateComponentWiseOperator("+", matrix, builder); + GenerateComponentWiseOperator("-", matrix, builder); + + GenerateMatrixMultiplications(matrix, builder); + } + + private void GenerateUnaryOperatorOverload(string op, MatrixDefinition matrix, StringBuilder builder) + { + string args = string.Join(",\n", Enumerable.Range(0, matrix.Rows).SelectMany(i => + Enumerable.Range(0, matrix.Columns).Select(j => + $"\t\t\t{op}value.M{j + 1}{i + 1}"))); + + builder.Append($$""" + public static {{matrix.Name}} operator {{op}}({{matrix.Name}} value) + { + return new( + {{args}}); + } + + + """); + } + + private void GenerateComponentWiseOperator(string op, MatrixDefinition matrix, StringBuilder builder) + { + string arguments = string.Join(",\n", Enumerable.Range(0, matrix.Rows).SelectMany(i => + Enumerable.Range(0, matrix.Columns).Select(j => + $"\t\t\tleft.M{j + 1}{i + 1} {op} right.M{j + 1}{i + 1}"))); + + builder.Append($$""" + public static {{matrix.Name}} operator {{op}}({{matrix.Name}} left, {{matrix.Name}} right) + { + {{ComponentWiseCore(op, matrix)}} + } + + + """); + } + + private string ComponentWiseCore(string op, MatrixDefinition matrix) + { + string arguments = string.Join(",\n", Enumerable.Range(0, matrix.Rows).SelectMany(i => + Enumerable.Range(0, matrix.Columns).Select(j => + $"\t\t\tleft.M{j + 1}{i + 1} {op} right.M{j + 1}{i + 1}"))); + + StringBuilder builder = new(); + + builder.Append($$""" + return new( + {{arguments}}); + """); + + return builder.ToString(); + } + + private void GenerateLogicOperators(MatrixDefinition matrix, StringBuilder builder) + { + GenerateComponentWiseOperator("&", matrix, builder); + GenerateComponentWiseOperator("^", matrix, builder); + GenerateComponentWiseOperator("|", matrix, builder); + } + + private void GenerateCastOperators(List matrices, MatrixDefinition sourceMatrix, string targetElementTypeName, StringBuilder builder, GeneratorExecutionContext context) + { + IEnumerable targetMatrices = + matrices.Where(target => target.ElementTypeName == targetElementTypeName && + target.Columns <= sourceMatrix.Columns && target.Rows <= sourceMatrix.Rows && + (target.Columns < sourceMatrix.Columns || target.Rows < sourceMatrix.Rows)); + + foreach (var targetMatrixDefinition in targetMatrices) + { + MatrixCastSyntaxReceiver.Cast cast = new MatrixCastSyntaxReceiver.Cast(sourceMatrix.Name, targetMatrixDefinition.Name, true); + + GenerateCastOperator(cast, sourceMatrix, targetMatrixDefinition, builder, context); + } + } + + private void GenerateCastOperator(MatrixCastSyntaxReceiver.Cast cast, MatrixDefinition sourceMatrix, MatrixDefinition targetMatrix, StringBuilder builder, GeneratorExecutionContext context) + { + if (sourceMatrix.Columns < targetMatrix.Columns || sourceMatrix.Rows < targetMatrix.Rows) + { + context.ReportDiagnostic(Diagnostic.Create( + new DiagnosticDescriptor( + "SG0001", + "Matrix-Dimensions don't match for cast.", + $"Cannot cast from \"{sourceMatrix.Name}\" to \"{targetMatrix.Name}\" because the source matrix is smaller than the target matrix.", + "Matrix Generator", + DiagnosticSeverity.Error, + true), sourceMatrix.Struct.GetLocation())); + + return; + } + + builder.Append($$""" + public static {{(cast.IsExplicit ? "explicit" : "implicit")}} operator {{targetMatrix.Name}}({{sourceMatrix.Name}} value) + { + {{targetMatrix.Name}} result = new(); + {{string.Join("\n", Enumerable.Range(0, targetMatrix.Rows).SelectMany(i => + Enumerable.Range(0, targetMatrix.Columns).Select(j => + $"\t\tresult.M{j + 1}{i + 1} = ({targetMatrix.ElementTypeName})value.M{j + 1}{i + 1};")))}} + + return result; + } + + + """); + } + + private void GenerateMatrixMultiplications(MatrixDefinition matrix, StringBuilder builder) + { + builder.Append($$""" + public static {{matrix.Name}} operator *({{matrix.ElementTypeName}} left, {{matrix.Name}} right) + { + return new( + {{string.Join(",\n", Enumerable.Range(0, matrix.Rows).SelectMany(i => + Enumerable.Range(0, matrix.Columns).Select(j => + $"\t\t\tleft * right.M{j + 1}{i + 1}")))}} + ); + } + + public static {{matrix.Name}} operator *({{matrix.Name}} left, {{matrix.ElementTypeName}} right) + { + return new( + {{string.Join(",\n", Enumerable.Range(0, matrix.Rows).SelectMany(i => + Enumerable.Range(0, matrix.Columns).Select(j => + $"\t\t\tleft.M{j + 1}{i + 1} * right")))}} + ); + } + + public static {{matrix.Name}} ComponentWiseMultiply({{matrix.Name}} left, {{matrix.Name}} right) + { + {{ComponentWiseCore("*", matrix)}} + } + + public static {{matrix.Name}} ComponentWiseDivide({{matrix.Name}} left, {{matrix.Name}} right) + { + {{ComponentWiseCore("/", matrix)}} + } + + public static {{matrix.Name}} ComponentWiseModulo({{matrix.Name}} left, {{matrix.Name}} right) + { + {{ComponentWiseCore("%", matrix)}} + } + + + """); + + GenerateMatrixMulMatrix(matrix, builder); + } + + /// + /// Overloads the multiplication operator for the matrix multiplication from linear algebra. + /// + private void GenerateMatrixMulMatrix(MatrixDefinition matrix, StringBuilder builder) + { + builder.Append($$""" + public static {{matrix.Name}} operator *({{matrix.Name}} left, {{matrix.Name}} right) + { + {{matrix.Name}} result = new(); + + """); + + for (int r = 0; r < matrix.Rows; r++) + { + for (int c = 0; c < matrix.Columns; c++) + { + builder.Append($"\t\tresult.M{r + 1}{c + 1} = "); + + for (int k = 0; k < matrix.Columns; k++) + { + if (k != 0) + builder.Append(" + "); + + builder.Append($"left.M{r + 1}{k + 1} * right.M{k + 1}{c + 1}"); + } + + builder.Append(";\n"); + } + } + + builder.Append(""" + + return result; + } + + + """); + } + + private void GenerateMatrixMulVector(MatrixDefinition matrix, VectorSyntaxReceiver.VectorDefinition vector, StringBuilder builder) + { + if (vector.ComponentCount != matrix.Columns) + return; + + builder.Append($$""" + public static {{vector.Name}} operator *({{matrix.Name}} left, {{vector.Name}} right) + { + {{vector.Name}} result = new(); + + """); + + for (int i = 0; i < vector.ComponentCount; i++) + { + builder.Append($"\t\tresult.{VectorGenerator.ComponentNames[i]} = "); + + for (int j = 0; j < matrix.Columns; j++) + { + if (j != 0) + builder.Append(" + "); + + builder.Append($"left.M{i + 1}{j + 1} * right.{VectorGenerator.ComponentNames[j]}"); + } + + builder.Append(";\n"); + } + + builder.Append(""" + + return result; + } + + + """); + } + + private void GenerateVectorMulMatrix(MatrixDefinition matrix, VectorSyntaxReceiver.VectorDefinition vector, StringBuilder builder) + { + if (vector.ComponentCount != matrix.Rows) + return; + + builder.Append($$""" + public static {{vector.Name}} operator *({{vector.Name}} left, {{matrix.Name}} right) + { + {{vector.Name}} result = new(); + + """); + + for (int i = 0; i < vector.ComponentCount; i++) + { + builder.Append($"\t\tresult.{VectorGenerator.ComponentNames[i]} = "); + + for (int j = 0; j < matrix.Rows; j++) + { + if (j != 0) + builder.Append(" + "); + + builder.Append($"left.{VectorGenerator.ComponentNames[j]} * right.M{j + 1}{i + 1}"); + } + + builder.Append(";\n"); + } + + builder.Append(""" + + return result; + } + + + """); + } + + private void GenerateToString(MatrixDefinition matrix, StringBuilder builder) + { + builder.Append(""" + public override string ToString() => $" + """); + + for (int i = 0; i < matrix.Rows; i++) + { + if (i != 0) + builder.Append(", "); + + builder.Append("{{"); + + for (int c = 0; c < matrix.Columns; c++) + { + if (c != 0) + builder.Append(", "); + + builder.Append($"M{c + 1}{i + 1}: {{M{c + 1}{i + 1}}}"); + } + + builder.Append("}}"); + } + + builder.Append("\";\n\n"); + } + + private string BuildSwizzleName(IEnumerable components) + { + StringBuilder builder = new(); + + foreach (var component in components) + { + //Reconstruct row and column from index + builder.Append($"M{component % 4 + 1}{component / 4 + 1}"); + } + + return builder.ToString(); + } + + /** + * Returns true if the swizzle operator is invalid (same component assigned twice). + */ + static bool IsSwizzleSetterValid(IEnumerable components) + { + return components.Distinct().Count() == components.Count(); + } + + private void GenerateSwizzle(MatrixDefinition matrix, StringBuilder builder) + { + int[] cmp = new int[4]; + + for (int componentCount = 2; componentCount <= 4; componentCount++) + { + int cmp2max = componentCount > 2 ? matrix.Rows * matrix.Columns : 1; + int cmp3max = componentCount > 3 ? matrix.Rows * matrix.Columns : 1; + + for (cmp[0] = 0; cmp[0] < matrix.Rows * matrix.Columns; cmp[0]++) + for (cmp[1] = 0; cmp[1] < matrix.Rows * matrix.Columns; cmp[1]++) + for (cmp[2] = 0; cmp[2] < cmp2max; cmp[2]++) + for (cmp[3] = 0; cmp[3] < cmp3max; cmp[3]++) + { + StringBuilder swizzleName = new(); + StringBuilder swizzleGetterConstructor = new(); + StringBuilder swizzleSetterConstructor = new(); + + bool setterValid = IsSwizzleSetterValid(cmp.Take(componentCount)); + + for (int c = 0; c < componentCount; c++) + { + int elementIndex = cmp[c]; + + //Reconstruct row and column from index + string element = $"M{elementIndex % 4 + 1}{elementIndex / 4 + 1}"; + + swizzleName.Append(element); + + if(c != 0) + { + swizzleGetterConstructor.Append(", "); + } + swizzleGetterConstructor.Append(element); + + if (setterValid) + { + swizzleSetterConstructor.Append($"\n\t\t\t{element} = value.{VectorGenerator.ComponentNames[c]};"); + } + } + + builder.Append($$""" + [DebuggerBrowsable(DebuggerBrowsableState.Never)] + public {{matrix.BaseName}}{{componentCount}} {{swizzleName}} + { + get => new({{swizzleGetterConstructor}}); + + """); + + if (setterValid) + { + builder.Append($$""" + set + {{{swizzleSetterConstructor}} + } + + """); + } + + builder.Append("\t}\n\n"); + } + } + } +} + +public class MatrixDefinition +{ + public string Name { get; } + public StructDeclarationSyntax Struct { get; } + + public int Rows { get; } + public int Columns { get; } + + public string BaseName { get; } + + public string ElementTypeName { get; } + + public MatrixDefinition(string name, StructDeclarationSyntax @struct, int rows, int columns, string baseName, string elementTypeName) + { + Name = name; + Struct = @struct; + Rows = rows; + Columns = columns; + BaseName = baseName; + ElementTypeName = elementTypeName; + } +} + +public class MatrixSyntaxReceiver : ISyntaxReceiver +{ + public ComparableMatrixSyntaxReceiver ComparableReceiver = new(); + public MatrixMathSyntaxReceiver MathReceiver = new(); + public MatrixLogicSyntaxReceiver LogicReceiver = new(); + public MatrixVectorMultiplicationSyntaxReceiver VectorMultiplicationReceiver = new(); + public MatrixCastSyntaxReceiver CastReceiver = new(); + + public VectorSyntaxReceiver VectorReceiver = new(); + + public List Matrices { get; } = new(); + + public void OnVisitSyntaxNode(SyntaxNode syntaxNode) + { + VectorReceiver.OnVisitSyntaxNode(syntaxNode); + + ComparableReceiver.OnVisitSyntaxNode(syntaxNode); + MathReceiver.OnVisitSyntaxNode(syntaxNode); + LogicReceiver.OnVisitSyntaxNode(syntaxNode); + VectorMultiplicationReceiver.OnVisitSyntaxNode(syntaxNode); + CastReceiver.OnVisitSyntaxNode(syntaxNode); + + if (syntaxNode is not AttributeSyntax { Name: IdentifierNameSyntax { Identifier.Text: "Matrix" } } attr) + return; + + var @struct = attr.GetParent(); + var name = @struct.Identifier.Text; + + if (attr?.ArgumentList?.Arguments == null || attr.ArgumentList.Arguments.Count != 4) + return; + + string? elementTypeName = (attr.ArgumentList.Arguments[0].Expression as TypeOfExpressionSyntax)?.Type.ToFullString(); + + if (elementTypeName == null) + return; + + int? columnCount = (attr.ArgumentList.Arguments[1].Expression as LiteralExpressionSyntax)?.Token.Value as int?; + + if (columnCount == null) + return; + + int? rowCount = (attr.ArgumentList.Arguments[2].Expression as LiteralExpressionSyntax)?.Token.Value as int?; + + if (rowCount == null) + return; + + string? baseName = (attr.ArgumentList.Arguments[3].Expression as LiteralExpressionSyntax)?.Token.ValueText; + + if (baseName == null) + return; + + Matrices.Add(new MatrixDefinition(name, @struct, rowCount.Value, columnCount.Value, baseName, elementTypeName)); + } +} + +public class ComparableMatrixSyntaxReceiver : ISyntaxReceiver +{ + public List Matrices { get; } = new(); + + public void OnVisitSyntaxNode(SyntaxNode syntaxNode) + { + if (syntaxNode is not AttributeSyntax { Name: IdentifierNameSyntax { Identifier.Text: "ComparableMatrix" } } attr) + return; + + var @struct = attr.GetParent(); + var name = @struct.Identifier.Text; + + Matrices.Add(name); + } +} + +public class MatrixMathSyntaxReceiver : ISyntaxReceiver +{ + public List Matrices { get; } = new(); + + public void OnVisitSyntaxNode(SyntaxNode syntaxNode) + { + if (syntaxNode is not AttributeSyntax { Name: IdentifierNameSyntax { Identifier.Text: "MatrixMath" } } attr) + return; + + var @struct = attr.GetParent(); + var name = @struct.Identifier.Text; + + Matrices.Add(name); + } +} + +public class MatrixLogicSyntaxReceiver : ISyntaxReceiver +{ + public List Matrices { get; } = new(); + + public void OnVisitSyntaxNode(SyntaxNode syntaxNode) + { + if (syntaxNode is not AttributeSyntax { Name: IdentifierNameSyntax { Identifier.Text: "MatrixLogic" } } attr) + return; + + var @struct = attr.GetParent(); + var name = @struct.Identifier.Text; + + Matrices.Add(name); + } +} + +public class MatrixVectorMultiplicationSyntaxReceiver : ISyntaxReceiver +{ + public List Multiplications { get; } = new(); + + public void OnVisitSyntaxNode(SyntaxNode syntaxNode) + { + if (syntaxNode is not AttributeSyntax { Name: IdentifierNameSyntax { Identifier.Text: "MatrixVectorMultiplication" } } attr) + return; + + var @struct = attr.GetParent(); + var matrixType = @struct.Identifier.Text; + + string? vectorType = (attr.ArgumentList?.Arguments[0].Expression as TypeOfExpressionSyntax)?.Type.ToFullString(); + + if (vectorType == null) + return; + + Multiplications.Add(new Multiplication(matrixType, vectorType)); + } + + public record Multiplication(string MatrixType, string VectorType) + { + public string MatrixType { get; } = MatrixType; + public string VectorType { get; } = VectorType; + } +} + +public class MatrixCastSyntaxReceiver : ISyntaxReceiver +{ + public List Casts { get; } = new(); + + public void OnVisitSyntaxNode(SyntaxNode syntaxNode) + { + if (syntaxNode is not AttributeSyntax { Name: IdentifierNameSyntax { Identifier.Text: "MatrixCast" } } attr) + return; + + var @struct = attr.GetParent(); + var sourceTypeName = @struct.Identifier.Text; + + var targetElementTypeName = (attr.ArgumentList?.Arguments[0].Expression as TypeOfExpressionSyntax)?.Type.ToFullString(); + + if (targetElementTypeName == null) + return; + + var isExplicit = (attr.ArgumentList?.Arguments[1].Expression as LiteralExpressionSyntax)?.Token.Value as bool?; + + if (isExplicit == null) + return; + + Casts.Add(new Cast(sourceTypeName, targetElementTypeName, isExplicit.Value)); + } + + public record Cast (string SourceTypeName, string TargetElementTypeName, bool IsExplicit) + { + public string SourceTypeName { get; } = SourceTypeName; + public string TargetElementTypeName { get; } = TargetElementTypeName; + public bool IsExplicit { get; } = IsExplicit; + } +} diff --git a/ScriptCoreGenerator/ScriptCoreGenerator.csproj b/ScriptCoreGenerator/ScriptCoreGenerator.csproj index 713630e..a609f1d 100644 --- a/ScriptCoreGenerator/ScriptCoreGenerator.csproj +++ b/ScriptCoreGenerator/ScriptCoreGenerator.csproj @@ -5,6 +5,7 @@ latest true true + enable diff --git a/ScriptCoreGenerator/VectorGenerator.cs b/ScriptCoreGenerator/VectorGenerator.cs index 434b9ea..5d008e3 100644 --- a/ScriptCoreGenerator/VectorGenerator.cs +++ b/ScriptCoreGenerator/VectorGenerator.cs @@ -24,7 +24,7 @@ public class VectorGenerator : ISourceGenerator { _context = context; - var receiver = (VectorSyntaxReceiver)context.SyntaxReceiver; + var receiver = (VectorSyntaxReceiver?)context.SyntaxReceiver; if (receiver == null) return; @@ -71,7 +71,7 @@ public class VectorGenerator : ISourceGenerator namespace {{vector.Struct.GetNamespace()}}; - public partial struct {{vector.VectorName}} + public partial struct {{vector.Name}} { """); @@ -87,24 +87,24 @@ public class VectorGenerator : ISourceGenerator GenerateEquals(vector, builder); GenerateGetHashCode(vector, builder); - if (receiver.ComparableReceiver.ComparableVectors.Contains(vector.VectorName)) + if (receiver.ComparableReceiver.ComparableVectors.Contains(vector.Name)) { GenerateComparisonOperators(vector, builder); } - if (receiver.MathReceiver.MathVectors.Contains(vector.VectorName)) + if (receiver.MathReceiver.MathVectors.Contains(vector.Name)) { GenerateMathOperators(vector, builder); } - if (receiver.LogicReceiver.LogicVectors.Contains(vector.VectorName)) + if (receiver.LogicReceiver.LogicVectors.Contains(vector.Name)) { GenerateLogicOperators(vector, builder); } - foreach (var cast in receiver.CastReceiver.VectorCasts.Where(c => c.FromType == vector.VectorName)) + foreach (var cast in receiver.CastReceiver.VectorCasts.Where(c => c.FromType == vector.Name)) { - VectorDefinition toVectorDefinition = receiver.Vectors.First(v => v.VectorName == cast.ToType); + VectorDefinition toVectorDefinition = receiver.Vectors.First(v => v.Name == cast.ToType); GenerateCastOperator(cast, vector, toVectorDefinition, builder); } @@ -115,7 +115,7 @@ public class VectorGenerator : ISourceGenerator builder.Append('}'); - context.AddSource($"{vector.VectorName}.g.cs", builder.ToString()); + context.AddSource($"{vector.Name}.g.cs", builder.ToString()); } } @@ -124,7 +124,7 @@ public class VectorGenerator : ISourceGenerator builder.Append($$""" public override bool Equals(object other) { - if (other is {{vector.VectorName}} otherVector) + if (other is {{vector.Name}} otherVector) return Math.all(this == otherVector); return false; @@ -183,7 +183,7 @@ public class VectorGenerator : ISourceGenerator new DiagnosticDescriptor( "SG0001", "Vector-Dimensions don't match for cast.", - $"Cannot cast from \"{fromVector.VectorName}\" to \"{toVector.VectorName}\" because the dimensions don't match.", + $"Cannot cast from \"{fromVector.Name}\" to \"{toVector.Name}\" because the dimensions don't match.", "Vector Generator", DiagnosticSeverity.Error, true), fromVector.Struct.GetLocation())); @@ -240,9 +240,9 @@ public class VectorGenerator : ISourceGenerator } builder.Append($$""" - public static implicit operator {{vector.VectorName}}({{vector.ElementTypeName}} value) + public static implicit operator {{vector.Name}}({{vector.ElementTypeName}} value) { - return new {{vector.VectorName}}({{castBuilder}}); + return new {{vector.Name}}({{castBuilder}}); } @@ -307,7 +307,7 @@ public class VectorGenerator : ISourceGenerator } builder.Append($$""" - public {{vector.VectorName}} ({{parameters}}) + public {{vector.Name}} ({{parameters}}) {{{body}} } @@ -318,7 +318,7 @@ public class VectorGenerator : ISourceGenerator private void Generatefloat3Constructors(VectorDefinition vector, StringBuilder builder) { builder.Append($$""" - public {{vector.VectorName}}({{vector.BaseName}}2 xy, {{vector.ElementTypeName}} z) + public {{vector.Name}}({{vector.BaseName}}2 xy, {{vector.ElementTypeName}} z) { X = xy.X; Y = xy.Y; @@ -328,7 +328,7 @@ public class VectorGenerator : ISourceGenerator """); builder.Append($$""" - public {{vector.VectorName}}({{vector.ElementTypeName}} x, {{vector.BaseName}}2 yz) + public {{vector.Name}}({{vector.ElementTypeName}} x, {{vector.BaseName}}2 yz) { X = x; Y = yz.X; @@ -341,7 +341,7 @@ public class VectorGenerator : ISourceGenerator private void Generatefloat4Constructors(VectorDefinition vector, StringBuilder builder) { builder.Append($$""" - public {{vector.VectorName}}({{vector.BaseName}}2 xy, {{vector.BaseName}}2 zw) + public {{vector.Name}}({{vector.BaseName}}2 xy, {{vector.BaseName}}2 zw) { //XY = xy; //ZW = zw; @@ -355,7 +355,7 @@ public class VectorGenerator : ISourceGenerator """); builder.Append($$""" - public {{vector.VectorName}}({{vector.BaseName}}2 xy, {{vector.ElementTypeName}} z, {{vector.ElementTypeName}} w) + public {{vector.Name}}({{vector.BaseName}}2 xy, {{vector.ElementTypeName}} z, {{vector.ElementTypeName}} w) { X = xy.X; Y = xy.Y; @@ -367,7 +367,7 @@ public class VectorGenerator : ISourceGenerator """); builder.Append($$""" - public {{vector.VectorName}}({{vector.ElementTypeName}} x, {{vector.BaseName}}2 yz, {{vector.ElementTypeName}} w) + public {{vector.Name}}({{vector.ElementTypeName}} x, {{vector.BaseName}}2 yz, {{vector.ElementTypeName}} w) { X = x; Y = yz.X; @@ -379,7 +379,7 @@ public class VectorGenerator : ISourceGenerator """); builder.Append($$""" - public {{vector.VectorName}}({{vector.ElementTypeName}} x, {{vector.ElementTypeName}} y, {{vector.BaseName}}2 zw) + public {{vector.Name}}({{vector.ElementTypeName}} x, {{vector.ElementTypeName}} y, {{vector.BaseName}}2 zw) { X = x; Y = y; @@ -391,7 +391,7 @@ public class VectorGenerator : ISourceGenerator """); builder.Append($$""" - public {{vector.VectorName}}({{vector.BaseName}}3 xyz, {{vector.ElementTypeName}} w) + public {{vector.Name}}({{vector.BaseName}}3 xyz, {{vector.ElementTypeName}} w) { X = xyz.X; Y = xyz.Y; @@ -403,7 +403,7 @@ public class VectorGenerator : ISourceGenerator """); builder.Append($$""" - public {{vector.VectorName}}({{vector.ElementTypeName}} x, {{vector.BaseName}}3 yzw) + public {{vector.Name}}({{vector.ElementTypeName}} x, {{vector.BaseName}}3 yzw) { X = x; Y = yzw.X; @@ -482,7 +482,7 @@ public class VectorGenerator : ISourceGenerator } builder.Append($$""" - public static bool{{vector.ComponentCount}} operator {{op}}({{vector.VectorName}} left, {{vector.VectorName}} right) + public static bool{{vector.ComponentCount}} operator {{op}}({{vector.Name}} left, {{vector.Name}} right) { return new bool{{vector.ComponentCount}}({{arguments}}); } @@ -524,9 +524,9 @@ public class VectorGenerator : ISourceGenerator } builder.Append($$""" - public static {{vector.VectorName}} operator {{op}}({{vector.VectorName}} value) + public static {{vector.Name}} operator {{op}}({{vector.Name}} value) { - return new {{vector.VectorName}}({{negativeArguments}}); + return new {{vector.Name}}({{negativeArguments}}); } @@ -548,9 +548,9 @@ public class VectorGenerator : ISourceGenerator } builder.Append($$""" - public static {{vector.VectorName}} operator {{op}}({{vector.VectorName}} left, {{vector.VectorName}} right) + public static {{vector.Name}} operator {{op}}({{vector.Name}} left, {{vector.Name}} right) { - return new {{vector.VectorName}}({{arguments}}); + return new {{vector.Name}}({{arguments}}); } @@ -567,9 +567,9 @@ public class VectorGenerator : ISourceGenerator } builder.Append($$""" - public static {{vector.VectorName}} operator {{op}}({{vector.VectorName}} left, {{vector.ElementTypeName}} right) + public static {{vector.Name}} operator {{op}}({{vector.Name}} left, {{vector.ElementTypeName}} right) { - return new {{vector.VectorName}}({{arguments}}); + return new {{vector.Name}}({{arguments}}); } @@ -586,9 +586,9 @@ public class VectorGenerator : ISourceGenerator } builder.Append($$""" - public static {{vector.VectorName}} operator {{op}}({{vector.ElementTypeName}} left, {{vector.VectorName}} right) + public static {{vector.Name}} operator {{op}}({{vector.ElementTypeName}} left, {{vector.Name}} right) { - return new {{vector.VectorName}}({{arguments}}); + return new {{vector.Name}}({{arguments}}); } @@ -720,21 +720,27 @@ public class VectorSyntaxReceiver : ISyntaxReceiver var @struct = attr.GetParent(); var name = @struct.Identifier.Text; - var elementTypeName = (attr.ArgumentList.Arguments[0].Expression as TypeOfExpressionSyntax).Type.ToFullString(); + var elementTypeName = (attr.ArgumentList?.Arguments[0].Expression as TypeOfExpressionSyntax)?.Type.ToFullString(); - var componentCount = (attr.ArgumentList.Arguments[1].Expression as LiteralExpressionSyntax).Token.Value as int?; + if (elementTypeName == null) + return; + + var componentCount = (attr.ArgumentList?.Arguments[1].Expression as LiteralExpressionSyntax)?.Token.Value as int?; if (componentCount == null) return; - var baseName = (attr.ArgumentList.Arguments[2].Expression as LiteralExpressionSyntax).Token.ValueText; + var baseName = (attr.ArgumentList?.Arguments[2].Expression as LiteralExpressionSyntax)?.Token.ValueText; + if (baseName == null) + return; + Vectors.Add(new VectorDefinition(name, @struct, componentCount.Value, baseName, elementTypeName)); } public class VectorDefinition { - public string VectorName { get; } + public string Name { get; } public StructDeclarationSyntax Struct { get; } public int ComponentCount { get; } @@ -743,9 +749,9 @@ public class VectorSyntaxReceiver : ISyntaxReceiver public string ElementTypeName { get; } - public VectorDefinition(string vectorName, StructDeclarationSyntax @struct, int componentCount, string baseName, string elementTypeName) + public VectorDefinition(string name, StructDeclarationSyntax @struct, int componentCount, string baseName, string elementTypeName) { - VectorName = vectorName; + Name = name; Struct = @struct; ComponentCount = componentCount; BaseName = baseName; @@ -814,9 +820,12 @@ public class VectorCastSyntaxReceiver : ISyntaxReceiver var @struct = attr.GetParent(); var fromType = @struct.Identifier.Text; - var toType = (attr.ArgumentList.Arguments[0].Expression as TypeOfExpressionSyntax).Type.ToFullString(); + var toType = (attr.ArgumentList?.Arguments[0].Expression as TypeOfExpressionSyntax)?.Type.ToFullString(); - var isExplicit = (attr.ArgumentList.Arguments[1].Expression as LiteralExpressionSyntax).Token.Value as bool?; + if (toType == null) + return; + + var isExplicit = (attr.ArgumentList?.Arguments[1].Expression as LiteralExpressionSyntax)?.Token.Value as bool?; if (isExplicit == null) return;