/// <summary> /// Places a new constant in the specified enum, if it doesn't already exist. /// The existing constant is replaced iff the new has a numeric value and the old /// has a reference value (eg 0x5 is preferred over AttribMask.Foo) /// </summary> /// <param name="s"></param> /// <param name="t"></param> /// <returns></returns> internal static Bind.Structures.Enum Merge(Bind.Structures.Enum s, Bind.Structures.Constant t) { if (!s.ConstantCollection.ContainsKey(t.Name)) { s.ConstantCollection.Add(t.Name, t); } else { // Tried to add a constant that already exists. If one constant // is like: 'Foo = 0x5' and the other like: 'Foo = Bar.Foo', then // keep the first one. if (!Char.IsDigit(((Constant)s.ConstantCollection[t.Name]).Value[0])) { s.ConstantCollection.Remove(t.Name); s.ConstantCollection.Add(t.Name, t); } } return(s); }
/// <summary> /// Places a new constant in the specified enum, if it doesn't already exist. /// The existing constant is replaced iff the new has a numeric value and the old /// has a reference value (eg 0x5 is preferred over AttribMask.Foo) /// </summary> /// <param name="s"></param> /// <param name="t"></param> /// <returns></returns> internal static Enum Merge(Enum s, Constant t) { if (!s.ConstantCollection.ContainsKey(t.Name)) { s.ConstantCollection.Add(t.Name, t); } else { // Tried to add a constant that already exists. If one constant // is like: 'Foo = 0x5' and the other like: 'Foo = Bar.Foo', then // keep the first one. if (!String.IsNullOrEmpty(s.ConstantCollection[t.Name].Reference)) { s.ConstantCollection[t.Name] = t; } } return s; }
/// <summary> /// Places a new constant in the specified enum, if it doesn't already exist. /// The existing constant is replaced iff the new has a numeric value and the old /// has a reference value (eg 0x5 is preferred over AttribMask.Foo) /// </summary> /// <param name="s"></param> /// <param name="t"></param> /// <returns></returns> internal static Enum Merge(Enum s, Constant t) { if (!s.ConstantCollection.ContainsKey(t.Name)) { s.ConstantCollection.Add(t.Name, t); } else { // Tried to add a constant that already exists. If one constant // is like: 'Foo = 0x5' and the other like: 'Foo = Bar.Foo', then // keep the first one. if (!Char.IsDigit(((Constant)s.ConstantCollection[t.Name]).Value[0])) { s.ConstantCollection.Remove(t.Name); s.ConstantCollection.Add(t.Name, t); } } return s; }
// There are cases when a value is an aliased constant, with no enum specified. // (e.g. FOG_COORD_ARRAY_TYPE = GL_FOG_COORDINATE_ARRAY_TYPE) // In this case try searching all enums for the correct constant to alias (stupid opengl specs). // This turns every bare alias into a normal alias that is processed afterwards. static void ResolveBareAlias(Constant c, EnumCollection enums) { // Constants are considered bare aliases when they don't have a reference and // their values are non-numeric. if (String.IsNullOrEmpty(c.Reference) && !Char.IsDigit(c.Value[0])) { // Skip generic GLenum, as this doesn't help resolve references. foreach (Enum e in enums.Values.Where(e => e.Name != "GLenum")) { if (e.ConstantCollection.ContainsKey(c.Value)) { c.Reference = e.Name; break; } } } }
static void ReplaceConstant(XPathNavigator enum_override, Constant c) { if (enum_override != null) { XPathNavigator constant_override = enum_override.SelectSingleNode(String.Format("token[@name='{0}']", c.OriginalName)) ?? enum_override.SelectSingleNode(String.Format("token[@name={0}]", c.Name)); if (constant_override != null) { foreach (XPathNavigator node in constant_override.SelectChildren(XPathNodeType.Element)) { switch (node.Name) { case "name": c.Name = (string)node.TypedValue; break; case "value": c.Value = (string)node.TypedValue; break; } } } } }
private EnumCollection ReadEnums(XPathNavigator nav) { EnumCollection enums = new EnumCollection(); Enum all = new Enum() { Name = Settings.CompleteEnumName }; if (nav != null) { foreach (XPathNavigator node in nav.SelectChildren("enum", String.Empty)) { Enum e = new Enum() { Name = node.GetAttribute("name", String.Empty), Type = node.GetAttribute("type", String.Empty) }; if (String.IsNullOrEmpty(e.Name)) throw new InvalidOperationException(String.Format("Empty name for enum element {0}", node.ToString())); foreach (XPathNavigator param in node.SelectChildren(XPathNodeType.Element)) { Constant c = null; switch (param.Name) { case "token": c = new Constant { Name = param.GetAttribute("name", String.Empty), Value = param.GetAttribute("value", String.Empty) }; break; case "use": c = new Constant { Name = param.GetAttribute("token", String.Empty), Reference = param.GetAttribute("enum", String.Empty), Value = param.GetAttribute("token", String.Empty), }; break; default: throw new NotSupportedException(); } Utilities.Merge(all, c); try { if (!e.ConstantCollection.ContainsKey(c.Name)) { e.ConstantCollection.Add(c.Name, c); } else if (e.ConstantCollection[c.Name].Value != c.Value) { var existing = e.ConstantCollection[c.Name]; if (existing.Reference != null && c.Reference == null) { e.ConstantCollection[c.Name] = c; } else if (existing.Reference == null && c.Reference != null) { // Keep existing } else { Console.WriteLine("[Warning] Conflicting token {0}.{1} with value {2} != {3}", e.Name, c.Name, e.ConstantCollection[c.Name].Value, c.Value); } } } catch (ArgumentException ex) { Console.WriteLine("[Warning] Failed to add constant {0} to enum {1}: {2}", c.Name, e.Name, ex.Message); } } Utilities.Merge(enums, e); } } Utilities.Merge(enums, all); return enums; }
public virtual EnumCollection ReadEnums(StreamReader specFile) { Trace.WriteLine("Reading opengl enumerant specs."); Trace.Indent(); EnumCollection enums = new EnumCollection(); // complete_enum contains all opengl enumerants. Enum complete_enum = new Enum(); complete_enum.Name = Settings.CompleteEnumName; do { string line = NextValidLine(specFile); if (String.IsNullOrEmpty(line)) break; line = line.Replace('\t', ' '); // We just encountered the start of a new enumerant: while (!String.IsNullOrEmpty(line) && line.Contains("enum")) { string[] words = line.Split(Utilities.Separators, StringSplitOptions.RemoveEmptyEntries); if (words.Length == 0) continue; // Declare a new enumerant Enum e = new Enum(); e.Name = Char.IsDigit(words[0][0]) ? Settings.ConstantPrefix + words[0] : words[0]; // And fill in the values for this enumerant do { line = NextValidLine(specFile); if (String.IsNullOrEmpty(line) || line.StartsWith("#")) continue; if (line.Contains("enum:") || specFile.EndOfStream) break; line = line.Replace('\t', ' '); words = line.Split(Utilities.Separators, StringSplitOptions.RemoveEmptyEntries); if (words.Length == 0) continue; // If we reach this point, we have found a new value for the current enumerant Constant c = new Constant(); if (line.Contains("=")) { // Trim the name's prefix, but only if not in Tao compat mode. if (Settings.Compatibility == Settings.Legacy.Tao) { } else { if (words[0].StartsWith(Settings.ConstantPrefix)) words[0] = words[0].Substring(Settings.ConstantPrefix.Length); if (Char.IsDigit(words[0][0])) words[0] = Settings.ConstantPrefix + words[0]; } c.Name = words[0]; c.Value = words[2]; } else if (words[0] == "use") { // Trim the prefix. if (words[2].StartsWith(Settings.ConstantPrefix)) words[2] = words[2].Substring(Settings.ConstantPrefix.Length); // If the remaining string starts with a digit, we were wrong above. // Re-add the "GL_" if (Char.IsDigit(words[2][0])) words[2] = Settings.ConstantPrefix + words[2]; c.Name = words[2]; c.Reference = words[1]; c.Value = words[2]; } else { // Typical cause is hand-editing the specs and forgetting to add an '=' sign. throw new InvalidOperationException(String.Format( "[Error] Invalid constant definition: \"{0}\"", line)); } //if (!String.IsNullOrEmpty(c.Name) && !e.Members.Contains.Contains(c)) //SpecTranslator.Merge(e.Members, c); if (!e.ConstantCollection.ContainsKey(c.Name)) e.ConstantCollection.Add(c.Name, c); else Trace.WriteLine(String.Format( "Spec error: Constant {0} defined twice in enum {1}, discarding last definition.", c.Name, e.Name)); // Insert the current constant in the list of all constants. //SpecTranslator.Merge(complete_enum.Members, c); complete_enum = Utilities.Merge(complete_enum, c); } while (!specFile.EndOfStream); // At this point, the complete value list for the current enumerant has been read, so add this // enumerant to the list. //e.StartDirectives.Add(new CodeRegionDirective(CodeRegionMode.Start, "public enum " + e.Name)); //e.EndDirectives.Add(new CodeRegionDirective(CodeRegionMode.End, "public enum " + e.Name)); // (disabled) Hack - discard Boolean enum, it fsucks up the fragile translation code ahead. //if (!e.Name.Contains("Bool")) //Utilities.Merge(enums, e); //e.Translate(); if (!enums.ContainsKey(e.Name)) enums.Add(e.Name, e); else { // The enum already exists, merge constants. foreach (Constant t in e.ConstantCollection.Values) Utilities.Merge(enums[e.Name], t); } } } while (!specFile.EndOfStream); enums.Add(complete_enum.Name, complete_enum); Trace.Unindent(); return enums; }
string GetDeclarationString(Constant c) { if (String.IsNullOrEmpty(c.Name)) { throw new InvalidOperationException("Invalid Constant: Name is empty"); } return String.Format("{0} = {1}((int){2}{3})", c.Name, c.Unchecked ? "unchecked" : String.Empty, !String.IsNullOrEmpty(c.Reference) ? c.Reference + Settings.NamespaceSeparator : String.Empty, c.Value); }
public virtual EnumCollection ReadEnums(StreamReader specFile) { Trace.WriteLine("Reading opengl enumerant specs."); Trace.Indent(); EnumCollection enums = new EnumCollection(); // complete_enum contains all opengl enumerants. Bind.Structures.Enum complete_enum = new Bind.Structures.Enum(); complete_enum.Name = Settings.CompleteEnumName; do { string line = NextValidLine(specFile); if (String.IsNullOrEmpty(line)) break; line = line.Replace('\t', ' '); // We just encountered the start of a new enumerant: while (!String.IsNullOrEmpty(line) && line.Contains("enum")) { string[] words = line.Split(Utilities.Separators, StringSplitOptions.RemoveEmptyEntries); if (words.Length == 0) continue; // Declare a new enumerant Bind.Structures.Enum e = new Bind.Structures.Enum(); e.Name = Char.IsDigit(words[0][0]) ? Settings.ConstantPrefix + words[0] : words[0]; // And fill in the values for this enumerant do { line = NextValidLine(specFile); if (String.IsNullOrEmpty(line) || line.StartsWith("#")) continue; if (line.Contains("enum:") || specFile.EndOfStream) break; line = line.Replace('\t', ' '); words = line.Split(Utilities.Separators, StringSplitOptions.RemoveEmptyEntries); if (words.Length == 0) continue; // If we reach this point, we have found a new value for the current enumerant Constant c = new Constant(); if (line.Contains("=")) { // Trim the name's prefix, but only if not in Tao compat mode. if (Settings.Compatibility == Settings.Legacy.Tao) { } else { if (words[0].StartsWith(Settings.ConstantPrefix)) words[0] = words[0].Substring(Settings.ConstantPrefix.Length); if (Char.IsDigit(words[0][0])) words[0] = Settings.ConstantPrefix + words[0]; } c.Name = words[0]; uint number; if (UInt32.TryParse(words[2].Replace("0x", String.Empty), System.Globalization.NumberStyles.AllowHexSpecifier, null, out number)) { // The value is a number, check if it should be unchecked. if (number > 0x7FFFFFFF) { c.Unchecked = true; } } else { // The value is not a number. Strip the prefix. if (words[2].StartsWith(Settings.ConstantPrefix)) words[2] = words[2].Substring(Settings.ConstantPrefix.Length); // If the name now starts with a digit (doesn't matter whether we // stripped "GL_" above), add a "GL_" prefix. // (e.g. GL_4_BYTES). if (Char.IsDigit(words[2][0])) words[2] = Settings.ConstantPrefix + words[2]; } c.Value = words[2]; } else if (words[0] == "use") { // Trim the prefix. if (words[2].StartsWith(Settings.ConstantPrefix)) words[2] = words[2].Substring(Settings.ConstantPrefix.Length); // If the remaining string starts with a digit, we were wrong above. // Re-add the "GL_" if (Char.IsDigit(words[2][0])) words[2] = Settings.ConstantPrefix + words[2]; c.Name = words[2]; c.Reference = words[1]; c.Value = words[2]; } //if (!String.IsNullOrEmpty(c.Name) && !e.Members.Contains.Contains(c)) //SpecTranslator.Merge(e.Members, c); if (!e.ConstantCollection.ContainsKey(c.Name)) e.ConstantCollection.Add(c.Name, c); else Trace.WriteLine(String.Format( "Spec error: Constant {0} defined twice in enum {1}, discarding last definition.", c.Name, e.Name)); // Insert the current constant in the list of all constants. //SpecTranslator.Merge(complete_enum.Members, c); complete_enum = Utilities.Merge(complete_enum, c); } while (!specFile.EndOfStream); // At this point, the complete value list for the current enumerant has been read, so add this // enumerant to the list. //e.StartDirectives.Add(new CodeRegionDirective(CodeRegionMode.Start, "public enum " + e.Name)); //e.EndDirectives.Add(new CodeRegionDirective(CodeRegionMode.End, "public enum " + e.Name)); // (disabled) Hack - discard Boolean enum, it fsucks up the fragile translation code ahead. //if (!e.Name.Contains("Bool")) //Utilities.Merge(enums, e); //e.Translate(); if (!enums.ContainsKey(e.Name)) { enums.Add(e.Name, e); } else { // The enum already exists, merge constants. Trace.WriteLine(String.Format("Conflict: Enum {0} already exists, merging constants.", e.Name)); foreach (Constant t in e.ConstantCollection.Values) { Utilities.Merge(enums[e.Name], t); } } //enums.Add(e); } //SpecTranslator.Merge(enums, complete_enum); } while (!specFile.EndOfStream); enums.Add(complete_enum.Name, complete_enum); Trace.Unindent(); return enums; }
public override EnumCollection ReadEnums(StreamReader specFile) { // First, read all enum definitions from spec and override file. // Afterwards, read all token/enum overrides from overrides file. // Every single enum is merged into EnumCollection enums = new EnumCollection(); Enum all = new Enum() { Name = Settings.CompleteEnumName }; XPathDocument specs = new XPathDocument(specFile); XPathDocument overrides = new XPathDocument(new StreamReader(Path.Combine(Settings.InputPath, functionOverridesFile))); foreach (XPathNavigator nav in new XPathNavigator[] { specs.CreateNavigator().SelectSingleNode("/signatures"), overrides.CreateNavigator().SelectSingleNode("/overrides/add") }) { if (nav != null) { foreach (XPathNavigator node in nav.SelectChildren("enum", String.Empty)) { Enum e = new Enum() { Name = node.GetAttribute("name", String.Empty), Type = node.GetAttribute("type", String.Empty) }; if (String.IsNullOrEmpty(e.Name)) throw new InvalidOperationException(String.Format("Empty name for enum element {0}", node.ToString())); foreach (XPathNavigator param in node.SelectChildren(XPathNodeType.Element)) { Constant c = new Constant(param.GetAttribute("name", String.Empty), param.GetAttribute("value", String.Empty)); Utilities.Merge(all, c); try { e.ConstantCollection.Add(c.Name, c); } catch (ArgumentException ex) { Console.WriteLine("[Warning] Failed to add constant {0} to enum {1}: {2}", c.Name, e.Name, ex.Message); } } Utilities.Merge(enums, e); } } } Utilities.Merge(enums, all); enums.Translate(overrides); return enums; }
EnumCollection ReadEnums(XPathNavigator nav) { EnumCollection enums = new EnumCollection(); Enum all = new Enum() { Name = Settings.CompleteEnumName }; if (nav != null) { var reuse_list = new List<KeyValuePair<Enum, string>>(); // First pass: collect all available tokens and enums foreach (XPathNavigator node in nav.SelectChildren("enum", String.Empty)) { Enum e = new Enum() { Name = node.GetAttribute("name", String.Empty).Trim(), Type = node.GetAttribute("type", String.Empty).Trim() }; if (String.IsNullOrEmpty(e.Name)) throw new InvalidOperationException(String.Format("Empty name for enum element {0}", node.ToString())); // It seems that all flag collections contain "Mask" in their names. // This looks like a heuristic, but it holds 100% in practice // (checked all enums to make sure). e.IsFlagCollection = e.Name.ToLower().Contains("mask"); foreach (XPathNavigator param in node.SelectChildren(XPathNodeType.Element)) { Constant c = null; switch (param.Name) { case "token": c = new Constant { Name = param.GetAttribute("name", String.Empty).Trim(), Value = param.GetAttribute("value", String.Empty).Trim() }; break; case "use": c = new Constant { Name = param.GetAttribute("token", String.Empty).Trim(), Reference = param.GetAttribute("enum", String.Empty).Trim(), Value = param.GetAttribute("token", String.Empty).Trim(), }; break; case "reuse": var reuse_enum = param.GetAttribute("enum", String.Empty).Trim(); reuse_list.Add(new KeyValuePair<Enum, string>(e, reuse_enum)); break; default: throw new NotSupportedException(); } if (c != null) { Utilities.Merge(all, c); try { if (!e.ConstantCollection.ContainsKey(c.Name)) { e.ConstantCollection.Add(c.Name, c); } else if (e.ConstantCollection[c.Name].Value != c.Value) { var existing = e.ConstantCollection[c.Name]; if (existing.Reference != null && c.Reference == null) { e.ConstantCollection[c.Name] = c; } else if (existing.Reference == null && c.Reference != null) { // Keep existing } else { Console.WriteLine("[Warning] Conflicting token {0}.{1} with value {2} != {3}", e.Name, c.Name, e.ConstantCollection[c.Name].Value, c.Value); } } } catch (ArgumentException ex) { Console.WriteLine("[Warning] Failed to add constant {0} to enum {1}: {2}", c.Name, e.Name, ex.Message); } } } Utilities.Merge(enums, e); } // Second pass: resolve "reuse" directives restart: foreach (var pair in reuse_list) { var e = pair.Key; var reuse = pair.Value; var count = e.ConstantCollection.Count; if (enums.ContainsKey(reuse)) { var reuse_enum = enums[reuse]; foreach (var token in reuse_enum.ConstantCollection.Values) { Utilities.Merge(e, token); } } else { Console.WriteLine("[Warning] Reuse token not found: {0}", reuse); } if (count != e.ConstantCollection.Count) { // Restart resolution of reuse directives whenever // we modify an enum. This is the simplest (brute) way // to resolve chains of reuse directives: // e.g. enum A reuses B which reuses C goto restart; } } } Utilities.Merge(enums, all); return enums; }
/// <summary> /// Replces the Value of the given constant with the value referenced by the [c.Reference, c.Value] pair. /// </summary> /// <param name="c">The Constant to translate</param> /// <param name="enums">The list of enums to check.</param> /// <param name="auxEnums">The list of auxilliary enums to check.</param> /// <returns>True if the reference was found; false otherwise.</returns> public static bool TranslateConstantWithReference(Constant c, EnumCollection enums, EnumCollection auxEnums) { if (!String.IsNullOrEmpty(c.Reference)) { Constant referenced_constant; if (enums.ContainsKey(c.Reference) && enums[c.Reference].ConstantCollection.ContainsKey(c.Value)) { TranslateConstantWithReference(enums[c.Reference].ConstantCollection[c.Value] as Constant, enums, auxEnums); referenced_constant = (enums[c.Reference].ConstantCollection[c.Value] as Constant); } else if (auxEnums.ContainsKey(c.Reference) && auxEnums[c.Reference].ConstantCollection.ContainsKey(c.Value)) { TranslateConstantWithReference(auxEnums[c.Reference].ConstantCollection[c.Value] as Constant, enums, auxEnums); referenced_constant = (auxEnums[c.Reference].ConstantCollection[c.Value] as Constant); } else { Console.WriteLine("[Warning] Reference {0} not found for token {1}.", c.Reference, c); return false; } //else throw new InvalidOperationException(String.Format("Unknown Enum \"{0}\" referenced by Constant \"{1}\"", // c.Reference, c.ToString())); c.Value = referenced_constant.Value; c.Reference = null; c.Unchecked = referenced_constant.Unchecked; } return true; }
/// <summary> /// Replces the Value of the given constant with the value referenced by the [c.Reference, c.Value] pair. /// </summary> /// <param name="c">The Constant to translate</param> /// <param name="enums">The list of enums to check.</param> /// <param name="auxEnums">The list of auxilliary enums to check.</param> /// <returns>True if the reference was found; false otherwise.</returns> public static bool TranslateConstantWithReference(Constant c, EnumCollection enums, EnumCollection auxEnums) { if (c == null) throw new ArgumentNullException("c"); if (enums == null) throw new ArgumentNullException("enums"); if (++CurrentReferenceDepth >= MaxReferenceDepth) throw new InvalidOperationException("Enum specification contains cycle"); if (!String.IsNullOrEmpty(c.Reference)) { Constant referenced_constant; if (enums.ContainsKey(c.Reference) && enums[c.Reference].ConstantCollection.ContainsKey(c.Value)) { // Transitively translate the referenced token // Todo: this may cause loops if two tokens reference each other. // Add a max reference depth and bail out? TranslateConstantWithReference(enums[c.Reference].ConstantCollection[c.Value], enums, auxEnums); referenced_constant = (enums[c.Reference].ConstantCollection[c.Value]); } else if (auxEnums != null && auxEnums.ContainsKey(c.Reference) && auxEnums[c.Reference].ConstantCollection.ContainsKey(c.Value)) { // Legacy from previous generator incarnation. // Todo: merge everything into enums and get rid of auxEnums. TranslateConstantWithReference(auxEnums[c.Reference].ConstantCollection[c.Value], enums, auxEnums); referenced_constant = (auxEnums[c.Reference].ConstantCollection[c.Value]); } else if (enums.ContainsKey(Settings.CompleteEnumName) && enums[Settings.CompleteEnumName].ConstantCollection.ContainsKey(c.Value)) { // Try the All enum var reference = enums[Settings.CompleteEnumName].ConstantCollection[c.Value]; if (reference.Reference == null) referenced_constant = (enums[Settings.CompleteEnumName].ConstantCollection[c.Value]); else { --CurrentReferenceDepth; return false; } } else { --CurrentReferenceDepth; return false; } //else throw new InvalidOperationException(String.Format("Unknown Enum \"{0}\" referenced by Constant \"{1}\"", // c.Reference, c.ToString())); c.Value = referenced_constant.Value; c.Reference = null; } --CurrentReferenceDepth; return true; }