using System; using System.Collections.Generic; // This file is part of the ANX.Framework created by the // "ANX.Framework developer group" and released under the Ms-PL license. // For details see: http://anxframework.codeplex.com/license namespace ANX.Framework.Content.Pipeline.Helpers.HLSLParser { /// /// http://msdn.microsoft.com/en-us/library/windows/desktop/bb509706%28v=vs.85%29.aspx /// public class Variable : IShaderElement { #region Constants private static char[] SemanticEndChars = new char[] { ' ', ':', '<', ';', '=' }; private static readonly List AllTypeModifiers = new List( new string[] { "const", "row_major", "column_major", "extern", "nointerpolation", "precise", "shared", "groupshared", "static", "uniform", "volatile", // struct additions "linear", "centroid", "noperspective", "sample" }); private static readonly List AllTypeNames = new List( new string[] { "int", "bool", "uint", "dword", "half", "float", "double", // "min16float", // "min10float", // "min16int", // "min12int", // "min16uint", // vector "vector", // matrix "matrix", "texture", "Texture2D", }); #endregion #region Public public string[] TypeModifiers { get; private set; } public string Name { get; private set; } public string InitialValue { get; private set; } public int ArraySize { get; private set; } public string Type { get; private set; } public string Annotations { get; private set; } public string Semantic { get; private set; } public string Packoffset { get; private set; } public string Register { get; private set; } public int[] Dimensions { get; private set; } #endregion #region Constructor protected Variable(ParseTextWalker walker) { ParseType(walker); ReadNameAndArraySize(walker); ParseExtraParameters(walker); ParseAnnotations(walker); ReadInitialValue(walker); if (walker.Text.StartsWith(";")) { walker.Seek(1); } } #endregion #region ParseType private void ParseType(ParseTextWalker walker) { string currentText = walker.Text; int nextSpaceIndex = FindInitialTypeEndSpace(currentText); string variableType = currentText.Substring(0, nextSpaceIndex); variableType = variableType.Replace(" ", ""); walker.Seek(nextSpaceIndex + 1); variableType = ResolveMatrixAndArraySyntax(variableType); ParseDimensions(variableType); } #endregion #region FindInitialTypeEndSpace private int FindInitialTypeEndSpace(string text) { int searchScope = 0; if (text.StartsWith("vector") || text.StartsWith("matrix")) { searchScope = text.IndexOf('>'); } else if (text.StartsWith("Texture") || text.StartsWith("texture")) { string textureGeneric = text; textureGeneric = textureGeneric.Substring(textureGeneric.IndexOf(' ')); textureGeneric = textureGeneric.Trim(); if (textureGeneric.StartsWith("<")) { searchScope = text.IndexOf('>'); } } return text.IndexOf(' ', searchScope); } #endregion #region ResolveMatrixAndArraySyntax private string ResolveMatrixAndArraySyntax(string text) { if (text.Contains("<") && text.StartsWith("matrix") || text.StartsWith("vector")) { text = text.Substring(text.IndexOf('<') + 1); text = text.Trim().TrimEnd('>'); string[] parts = text.Split(','); text = parts[0].Trim(); for (int index = 1; index < parts.Length; index++) { text += (index > 1 ? "x" : "") + parts[index].Trim(); } } return text; } #endregion #region ParseDimensions private void ParseDimensions(string typeText) { List dimensions = new List(); Type = typeText; int numeralRemoverIndex = typeText.Length - 1; while (numeralRemoverIndex >= 1) { if (Char.IsDigit(Type[numeralRemoverIndex])) { dimensions.Insert(0, int.Parse(Type[numeralRemoverIndex].ToString())); Type = Type.Substring(0, numeralRemoverIndex); } else if (Type[numeralRemoverIndex] == 'x' && Char.IsDigit(Type[numeralRemoverIndex - 1])) { Type = Type.Substring(0, numeralRemoverIndex); } else { break; } numeralRemoverIndex--; } Dimensions = dimensions.ToArray(); } #endregion #region ReadNameAndArraySize private void ReadNameAndArraySize(ParseTextWalker walker) { string currentText = walker.Text; int nameIndex = 0; while (nameIndex < currentText.Length) { char currentChar = currentText[nameIndex]; if (currentChar == ' ' || currentChar == ':' || currentChar == '<' || currentChar == ';' || currentChar == '=') { break; } Name += currentChar; nameIndex++; } if (Name.Contains("[")) { Name = Name.TrimEnd(']'); int openBraceIndex = Name.IndexOf('['); ArraySize = int.Parse(Name.Substring(openBraceIndex + 1)); Name = Name.Substring(0, openBraceIndex); } walker.Seek(nameIndex); } #endregion #region ParseExtraParameters private void ParseExtraParameters(ParseTextWalker walker) { string currentText = walker.Text; char currentChar = currentText[0]; if (currentChar == '<' || currentChar == ';' || currentChar == '=') { return; } int afterColonIndex = 1; string extraText = currentText.Substring(afterColonIndex); if (extraText.Trim().StartsWith("packoffset")) { int endIndex = extraText.IndexOf(')') + 1; Packoffset = extraText.Substring(0, endIndex).Trim(); walker.Seek(endIndex + afterColonIndex); } else if (extraText.Trim().StartsWith("register")) { int endIndex = extraText.IndexOf(')') + 1; Register = extraText.Substring(0, endIndex).Trim(); walker.Seek(endIndex + afterColonIndex); } else { int beforeLength = extraText.Length; extraText = extraText.TrimStart(' '); int lowestEndIndex = -1; foreach (char semanticEndChar in SemanticEndChars) { int indexOfEndChar = extraText.IndexOf(semanticEndChar); if (indexOfEndChar != -1 && (lowestEndIndex == -1 || indexOfEndChar < lowestEndIndex)) { lowestEndIndex = indexOfEndChar; } } Semantic = extraText.Substring(0, lowestEndIndex).Trim(); walker.Seek(lowestEndIndex + afterColonIndex + (beforeLength - extraText.Length)); } } #endregion #region ParseAnnotations private void ParseAnnotations(ParseTextWalker walker) { string currentText = walker.Text; if (currentText[0] != '<') return; int endIndex = currentText.IndexOf('>'); Annotations = currentText.Substring(1, endIndex - 1); walker.Seek(endIndex + 1); } #endregion #region ReadInitialValue private void ReadInitialValue(ParseTextWalker walker) { string text = walker.Text; if (text[0] == '<' || text[0] == ';' || text[0] == ':') { return; } int valueEndIndex = text.IndexOf(';', 1); InitialValue = text.Substring(1, valueEndIndex - 1); InitialValue = InitialValue.Trim(); } #endregion #region GetTypeModifiers public static string[] GetTypeModifiers(ParseTextWalker walker) { if (walker == null) return new string[0]; if (String.IsNullOrEmpty(walker.Text)) return new string[0]; string currentText = walker.Text; int firstSpaceIndex = currentText.IndexOf(' '); if (firstSpaceIndex == -1) { return AllTypeModifiers.Contains(currentText) ? new string[] { currentText } : new string[0]; } var result = new List(); while (firstSpaceIndex != -1) { string currentElement = currentText.Substring(0, firstSpaceIndex); if (AllTypeModifiers.Contains(currentElement)) { result.Add(currentElement); } else { break; } walker.Seek(firstSpaceIndex + 1); currentText = walker.Text; firstSpaceIndex = currentText.IndexOf(' '); } return result.ToArray(); } #endregion #region IsVariableFollowing public static bool IsVariableFollowing(ParseTextWalker walker) { string currentText = walker.Text; foreach (string typeName in AllTypeNames) { if (currentText.StartsWith(typeName)) return true; } return false; } #endregion #region TryParse public static Variable TryParse(ParseTextWalker walker) { string[] typeModifiersFound = GetTypeModifiers(walker); if (IsVariableFollowing(walker)) { return new Variable(walker) { TypeModifiers = typeModifiersFound, }; } else return null; } #endregion #region ToString public override string ToString() { string result = ""; result = AddTypeModifiersToString(result); result += Type; result = AddDimensionsToString(result); result += " " + Name; if (ArraySize > 0) { result += "[" + ArraySize + "]"; } if (String.IsNullOrEmpty(Semantic) == false) { result += " : " + Semantic; } if (String.IsNullOrEmpty(Packoffset) == false) { result += " : " + Packoffset; } if (String.IsNullOrEmpty(Register) == false) { result += " : " + Register; } if (String.IsNullOrEmpty(Annotations) == false) { result += " <" + Annotations + ">"; } if (String.IsNullOrEmpty(InitialValue) == false) { result += " = " + InitialValue; } result += ";"; return result; } #endregion #region AddTypeModifiersToString private string AddTypeModifiersToString(string text) { foreach (string modifier in TypeModifiers) { text += modifier + " "; } return text; } #endregion #region AddDimensionsToString private string AddDimensionsToString(string text) { for (int index = 0; index < Dimensions.Length; index++) { text += (index > 0 ? "x" : "") + Dimensions[index]; } return text; } #endregion } }