Code/utils/AttributesParser.cs

Parser for shader or particle attribute declarations in source files. It reads a file, uses regex to find attribute and DynamicCombo declarations, and populates dictionaries for floats, vectors, textures, colors and dynamic combos using an injected IAttributeTypeSet for type conversions and defaults.

File AccessReflection
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;

namespace Sandbox;

public class AttributesParser<TFloat, TGradient>
{
	private readonly IAttributeTypeSet<TFloat, TGradient> _typeSet;
	public AttributesParser(IAttributeTypeSet<TFloat, TGradient> typeSet)
	{
		if (!((typeof(TFloat) == typeof(ParticleFloat) && typeof(TGradient) == typeof(ParticleGradient)) ||
		      (typeof(TFloat) == typeof(float) && typeof(TGradient) == typeof(Color))))
		{
			throw new InvalidOperationException("Only Particle or Native types allowed.");
		}
		_typeSet = typeSet;
	}
	public Dictionary<String, TGradient> Colors { get; set; }
	
	public Dictionary<String, TFloat> Floats { get; set; }
	
	public Dictionary<String, Vector2> Floats2 { get; set; }

	public Dictionary<String, Vector3> Floats3 { get; set; }
	
	public Dictionary<String, Vector4> Floats4 { get; set; }
	
	public Dictionary<String, Texture> Textures { get; set; }
	
	public Dictionary<String, int> DynamicCombos { get; set; }


	private const string AttributesPattern = @"(float(?:[2,3,4])?|Texture2D) +.+ +< *Attribute\(.?""(.+)"".?\)\;(?:[^>]*?Default(?:[1,2,3,4])?\(([^)]*)\))?[^>]*>\;";
	private const string DynamicComboPattern = "DynamicCombo\\(([^,]+),.+\\)\\;";

	public void ParseAttributes( string filePath )
	{
		string fileContent = FileSystem.Mounted.ReadAllText( filePath );
		var attributeMatches = Regex.Matches(fileContent, AttributesPattern);
		var dynamicComboMatches = Regex.Matches(fileContent, DynamicComboPattern);

		foreach ( Match match in dynamicComboMatches )
		{
			var value = match.Groups[1].Value.Trim();
			if(!DynamicCombos.ContainsKey(value)) DynamicCombos.Add(value, 0);
		}

		foreach (Match match in attributeMatches)
		{
			var type = match.Groups[1].Value;
			var value = match.Groups[2].Value;
			var defaultValue = match.Groups[3].Value;
			SetAttribute(type, value, defaultValue);
		}
	}

	private void SetAttribute( string attributeType, string attributeName, string defaultValue )
	{

		switch ( attributeType )
		{
			case "float":
				if ( !Floats.ContainsKey( attributeName ) ) SetFloat( attributeName, defaultValue );
				break;
			case "float2":
				if ( !Floats2.ContainsKey( attributeName ) ) SetFloat2( attributeName, defaultValue );
				break;
			case "float3":
				if ( !Floats3.ContainsKey( attributeName ) ) SetFloat3( attributeName, defaultValue );
				break;
			case "float4":
				if ( !Floats4.ContainsKey( attributeName ) && !Colors.ContainsKey( attributeName ) ) SetFloat4( attributeName );
				break;
			case "Texture2D":
				if ( !Textures.ContainsKey( attributeName ) ) Textures.Add( attributeName, Texture.White );
				break;
			default: break;
		}
	}

	private void SetFloat( string attributeName, string defaultValue )
	{
		TFloat value = !string.IsNullOrEmpty( defaultValue )
			? _typeSet.ParseFloat( defaultValue )
			: _typeSet.GetDefaultFloat();
		Floats.Add( attributeName, value );
	}

	private void SetFloat2( string attributeName, string defaultValue )
	{
		Vector2 value = !string.IsNullOrEmpty( defaultValue )
			? ParseVector2( defaultValue )
			: new Vector2();
		Floats2.Add( attributeName, value );
	}

	private void SetFloat3( string attributeName, string defaultValue )
	{
		Vector3 value = !string.IsNullOrEmpty( defaultValue )
			? ParseVector3( defaultValue )
			: new Vector3();
		Floats3.Add( attributeName, value );
	}

	private static Vector2 ParseVector2( string raw )
	{
		var p = raw.Split( ',' );
		return new Vector2(
			float.Parse( p[0].Trim(), System.Globalization.CultureInfo.InvariantCulture ),
			float.Parse( p[1].Trim(), System.Globalization.CultureInfo.InvariantCulture )
		);
	}

	private static Vector3 ParseVector3( string raw )
	{
		var p = raw.Split( ',' );
		return new Vector3(
			float.Parse( p[0].Trim(), System.Globalization.CultureInfo.InvariantCulture ),
			float.Parse( p[1].Trim(), System.Globalization.CultureInfo.InvariantCulture ),
			float.Parse( p[2].Trim(), System.Globalization.CultureInfo.InvariantCulture )
		);
	}

	private void SetFloat4( string attributeName)
	{
		if ( attributeName.ToLower().Contains( "color" ) )
		{
			Colors.Add( attributeName, _typeSet.GetDefaultColor() );
		}
		else
		{
			Floats4.Add( attributeName, new Vector4(  ) );	
		}
		
	}
}