private static CodeGenEnum HandleEnum(ParsedEnum pdEnum, IDictionary <string, TypeMemoryInfo> typesMemInfo)
        {
            if (!typesMemInfo.TryGetValue(pdEnum.UnderlyingType, out var memInfo))
            {
                throw new Exception($"Invalid base type `{pdEnum.UnderlyingType}` of enum `{pdEnum.Name}`");
            }

            var items = new CodeGenEnumItem[pdEnum.Items.Length];

            for (var i = 0; i < items.Length; i++)
            {
                items[i] = new CodeGenEnumItem(pdEnum.Items[i].Name, pdEnum.Items[i].Value);
            }

            var defaultValue = DefaultValueInfo.AssignTypeMember(pdEnum.Items[0].Name);

            typesMemInfo.Add(pdEnum.Name, new TypeMemoryInfo(memInfo.Size, memInfo.Size, defaultValue));

            return(new CodeGenEnum(pdEnum.Name, memInfo.Size, pdEnum.UnderlyingType, pdEnum.IsFlags, items));
        }
Example #2
0
        public ParsedSpec Run()
        {
            var vkXml = this.xmlCache.GetVkXml();

            var knownExtensions = new List <string>();

            foreach (var vkExtension in vkXml.Element("registry").Element("extensions").Elements("extension"))
            {
                string name = vkExtension.Attribute("name").Value;

                var nameParts = name.Split('_');

                string extensionSuffix = nameParts[1].ToLower();

                if (!knownExtensions.Contains(extensionSuffix))
                {
                    knownExtensions.Add(extensionSuffix);
                }
            }

            var typeXml = new Dictionary <string, ParsedType>();

            foreach (var vkType in vkXml.Element("registry").Element("types").Elements("type"))
            {
                string       name = vkType.Attribute("name")?.Value ?? vkType.Element("name").Value;
                var          categoryAttribute = vkType.Attribute("category");
                TypeCategory category          = categoryAttribute == null
                                                            ? TypeCategory.None
                                                            : (TypeCategory)Enum.Parse(typeof(TypeCategory), categoryAttribute.Value);
                string requires       = vkType.Attribute("requires")?.Value;
                string parent         = vkType.Attribute("parent")?.Value;
                string returnedOnly   = vkType.Attribute("returnedonly")?.Value;
                bool   isReturnedOnly = returnedOnly != null
                                        ? bool.Parse(returnedOnly)
                                        : false;

                bool   isTypePointer = false;
                string type          = vkType.Element("type")?.Value;
                if (type == "VK_MAKE_VERSION")
                {
                    type += vkType.Element("type").NextNode.ToString();
                }

                if (category == TypeCategory.funcpointer)
                {
                    type = ((XText)vkType.Nodes().First()).Value.Split(' ')[1];

                    if (type.EndsWith("*"))
                    {
                        type = type.TrimEnd('*');

                        isTypePointer = true;
                    }
                }


                string extension;

                string[] nameParts = GetNameParts(category == TypeCategory.funcpointer ? name.Substring(4) : name, out extension, knownExtensions);

                // VkDisplayModeKHR has two parents defined, but associated
                // handle should cover the requirements for the second
                // so just take the first
                if (parent != null)
                {
                    parent = parent.Split(',').First();
                }

                var newType = new ParsedType
                {
                    VkName         = name,
                    Category       = category,
                    Requires       = requires,
                    Parent         = parent,
                    IsReturnedOnly = isReturnedOnly,
                    NameParts      = nameParts,
                    Extension      = extension,
                    Type           = type,
                    IsTypePointer  = isTypePointer
                };

                foreach (var vkMember in vkType.Elements("member"))
                {
                    var    nameElement = vkMember.Element("name");
                    string memberName  = nameElement.Value;
                    string memberType  = vkMember.Element("type").Value;
                    string optional    = vkMember.Attribute("optional")?.Value;
                    bool   isOptional  = optional != null
                                        ? bool.Parse(optional)
                                        : false;

                    ParsedFixedLength fixedLength = new ParsedFixedLength();

                    var         typeNodes   = nameElement.NodesBeforeSelf();
                    PointerType pointerType = GetPointerType(typeNodes);

                    if (nameElement.NodesAfterSelf().Any())
                    {
                        string enumName = vkMember.Element("enum")?.Value;

                        if (enumName != null)
                        {
                            fixedLength.Value = enumName;
                            fixedLength.Type  = FixedLengthType.EnumReference;
                        }
                        else
                        {
                            fixedLength.Value = fixedLengthParser.Parse(nameElement.NextNode.ToString());
                            fixedLength.Type  = FixedLengthType.IntegerLiteral;
                        }
                    }
                    else
                    {
                        int fixedLengthIndex = memberName.IndexOf('[');

                        if (fixedLengthIndex >= 0)
                        {
                            string fixedLengthString = memberName.Substring(fixedLengthIndex);
                            memberName = memberName.Substring(0, fixedLengthIndex);

                            fixedLength.Value = fixedLengthParser.Parse(fixedLengthString);
                            fixedLength.Type  = FixedLengthType.IntegerLiteral;
                        }
                    }

                    string vkName = memberName;

                    int pointerCount = pointerType.GetPointerCount();

                    while (pointerCount > 0 && memberName.StartsWith("p"))
                    {
                        memberName = memberName.Substring(1);

                        pointerCount--;
                    }

                    ParsedLen[] dimensions = GetDimensions(name, vkMember, memberName);

                    // Capture member name without array suffix
                    string memberExtension;

                    string[] memberNameParts = GetNameParts(memberName, out memberExtension, knownExtensions, false);

                    string typeExtension;

                    string[] typeNameParts = GetNameParts(memberType, out typeExtension, knownExtensions, true);

                    string typeWithoutExtension = typeNameParts != null
                                                    ? "Vk" + string.Join("", typeNameParts.Select(CapitaliseFirst))
                                                    : null;

                    string values = vkMember.Attribute("values")?.Value;

                    if (vkName == "sType" && values == null)
                    {
                        //HACK VkDebugReportLayerFlagsEXT doesn't specify a
                        // fixed value for the sType field, so it must be
                        // scraped from the following comment.

                        if (vkMember.NextNode != null)
                        {
                            // Split on spaces and skip "Must" & "be"
                            values = ((XComment)vkMember.NextNode).Value.Trim().Split(' ')[2];
                        }
                    }

                    newType.Members.Add(new ParsedMember
                    {
                        VkName = vkName,
                        Type   = memberType,
                        TypeWithoutExtension = typeWithoutExtension,
                        TypeExtension        = typeExtension,
                        IsOptional           = isOptional,
                        FixedLength          = fixedLength,
                        PointerType          = pointerType,
                        NameParts            = memberNameParts,
                        Extension            = extension,
                        Dimensions           = dimensions,
                        Values = values
                    });
                }

                // Special parsing is required for funcpointer parameters
                if (category == TypeCategory.funcpointer)
                {
                    var functionTail = vkType.Element("name").NodesAfterSelf();

                    foreach (var typeElement in functionTail.Where(x => x.NodeType == XmlNodeType.Element).Cast <XElement>())
                    {
                        string pre  = ((XText)typeElement.PreviousNode).Value.Split(',').Last().Trim('(', ')', ';').TrimStart();
                        string post = ((XText)typeElement.NextNode).Value.Split(',').First().Trim('(', ')', ';').TrimEnd();

                        string      paramName   = new string(post.Reverse().TakeWhile(char.IsLetterOrDigit).Reverse().ToArray());
                        string      typeString  = pre + "@" + (post.Substring(0, post.Length - paramName.Length).Trim());
                        string      paramType   = typeElement.Value;
                        PointerType pointerType = MapTypeString(typeString);

                        string paramExtension;

                        string[] paramNameParts = GetNameParts(paramName, out paramExtension, knownExtensions, false);

                        newType.Members.Add(new ParsedMember
                        {
                            VkName      = paramName,
                            Type        = paramType,
                            PointerType = pointerType,
                            NameParts   = paramNameParts,
                            Extension   = paramExtension
                        });
                    }
                }

                typeXml.Add(name, newType);
            }

            var enumXml = new Dictionary <string, ParsedEnum>();

            foreach (var vkEnum in vkXml.Element("registry").Elements("enums"))
            {
                string name = vkEnum.Attribute("name").Value;
                string type = vkEnum.Attribute("type")?.Value;

                string extension;

                string[] nameParts = GetNameParts(name, out extension, knownExtensions);

                var newEnum = new ParsedEnum
                {
                    VkName    = name,
                    Type      = type,
                    NameParts = nameParts,
                    Extension = extension
                };

                foreach (var vkField in vkEnum.Elements("enum"))
                {
                    string fieldName = vkField.Attribute("name").Value;
                    bool   isBitmask = true;
                    string value     = vkField.Attribute("bitpos")?.Value;
                    string comment   = NormaliseComment(vkField.Attribute("comment")?.Value);

                    if (value == null)
                    {
                        isBitmask = false;
                        value     = vkField.Attribute("value").Value;

                        // Special case for mapping C "unsigned long long"
                        // (64-bit unsigned integer) to C# UInt64
                        if (value == "(~0ULL)")
                        {
                            value = "(~0UL)";
                        }

                        value = value.Trim('(', ')');
                    }

                    IEnumerable <string> fieldNameParts = GetEnumFieldNameParts(nameParts, fieldName, knownExtensions);

                    newEnum.Fields.Add(fieldName, new ParsedEnumField
                    {
                        VkName    = fieldName,
                        NameParts = fieldNameParts.ToArray(),
                        IsBitmask = isBitmask,
                        Value     = value,
                        Comment   = comment != null ? new List <string> {
                            comment
                        } : null
                    });
                }

                enumXml.Add(name, newEnum);
            }

            var commandXml = new Dictionary <string, ParsedCommand>();

            foreach (var vkCommand in vkXml.Element("registry").Element("commands").Elements("command"))
            {
                string name = vkCommand.Element("proto").Element("name").Value;
                string type = vkCommand.Element("proto").Element("type").Value;

                string extension;

                string[] nameParts = GetNameParts(name, out extension, knownExtensions);

                string[] verbExceptions = new[] { "cmd", "queue", "device" };

                string verb = verbExceptions.Contains(nameParts[0]) ? nameParts[1] : nameParts[0];

                string[] successCodes = vkCommand.Attribute("successcodes")?.Value?.Split(',');

                var newCommand = new ParsedCommand
                {
                    VkName       = name,
                    Type         = type,
                    NameParts    = nameParts,
                    Extension    = extension,
                    Verb         = verb,
                    SuccessCodes = successCodes
                };

                commandXml.Add(name, newCommand);

                foreach (var vkParam in vkCommand.Elements("param"))
                {
                    var nameElement = vkParam.Element("name");

                    string paramName = nameElement.Value;
                    string paramType = vkParam.Element("type").Value;
                    string optional  = vkParam.Attribute("optional")?.Value;
                    bool   isOptional;
                    bool.TryParse(optional, out isOptional);

                    var         typeNodes   = nameElement.NodesBeforeSelf();
                    PointerType pointerType = GetPointerType(typeNodes);

                    ParsedLen[] dimensions = GetDimensions(name, vkParam, paramName);

                    string paramExtension;

                    string[] paramNameParts = GetNameParts(paramName, out paramExtension, knownExtensions, false);

                    string typeExtension;

                    string[] typeNameParts = GetNameParts(paramType, out typeExtension, knownExtensions, true);

                    string typeWithoutExtension = typeNameParts != null
                                                    ? "Vk" + string.Join("", typeNameParts.Select(CapitaliseFirst))
                                                    : null;

                    newCommand.Params.Add(new ParsedParam
                    {
                        VkName = paramName,
                        Type   = paramType,
                        TypeWithoutExtension = typeWithoutExtension,
                        TypeExtension        = typeExtension,
                        PointerType          = pointerType,
                        NameParts            = paramNameParts,
                        Extension            = paramExtension,
                        IsOptional           = isOptional,
                        Dimensions           = dimensions
                    });
                }
            }

            var vkFeature = vkXml.Element("registry").Elements("feature").Single(x => x.Attribute("api").Value == "vulkan");

            var vkExtensions = vkXml.Element("registry").Element("extensions").Elements("extension").Where(x => x.Attribute("supported").Value == "vulkan");

            var filteredSpec = FilterRequiredElement(typeXml, enumXml, commandXml, vkFeature, vkExtensions, knownExtensions);

            foreach (var defineType in typeXml.Values.Where(x => x.Category == TypeCategory.define && x.VkName.StartsWith("VK_API_VERSION_")))
            {
                IEnumerable <string> fieldNameParts = GetEnumFieldNameParts(null, defineType.VkName, knownExtensions);

                filteredSpec.Constants.Add(defineType.VkName, new ParsedEnumField
                {
                    VkName    = defineType.VkName,
                    NameParts = fieldNameParts.ToArray(),
                    IsBitmask = false,
                    Value     = defineType.Type,
                    Comment   = null
                });
            }

            var vkDocsXmlCache = new DownloadedFileCache(this.tempFilePath, "https://raw.githubusercontent.com/FacticiusVir/SharpVk-Docs/master/Docs/vkDocs.xml");

            var vkDocsXml = XDocument.Load(vkDocsXmlCache.GetFileLocation().Result);

            foreach (var vkDocType in vkDocsXml.Element("docs").Element("types").Elements("type"))
            {
                string typeName = vkDocType.Attribute("name").Value;

                ParsedElement parsedElement = null;

                if (filteredSpec.Enumerations.ContainsKey(typeName))
                {
                    parsedElement = filteredSpec.Enumerations[typeName];
                }
                else if (filteredSpec.Types.ContainsKey(typeName))
                {
                    parsedElement = filteredSpec.Types[typeName];
                }

                if (parsedElement != null)
                {
                    var comment = new List <string> {
                        vkDocType.Attribute("summary").Value
                    };

                    var specification = vkDocType.Element("specification");

                    comment.AddRange(specification.Elements("para").Select(x => x.Value));

                    var description = vkDocType.Element("description");

                    parsedElement.Comment = new List <string>();

                    comment.AddRange(description.Elements("para").Select(x => x.Value));
                    comment.RemoveAll(x => x.StartsWith(".Valid Usage"));

                    int totalLength = 0;

                    foreach (var para in comment)
                    {
                        totalLength += para.Length;

                        if (totalLength > 2000)
                        {
                            break;
                        }
                        else
                        {
                            parsedElement.Comment.Add(NormaliseComment(para));
                        }
                    }

                    IEnumerable <ParsedElement> members = null;

                    var parsedType = parsedElement as ParsedType;
                    var parsedEnum = parsedElement as ParsedEnum;

                    if (parsedType != null)
                    {
                        members = parsedType.Members;
                    }
                    else if (parsedEnum != null)
                    {
                        members = parsedEnum.Fields.Values;
                    }

                    if (members != null)
                    {
                        foreach (var vkDocMember in vkDocType.Element("members").Elements("member"))
                        {
                            string memberName    = vkDocMember.Attribute("name").Value;
                            string memberSummary = NormaliseComment(vkDocMember.Value);

                            var member = members.FirstOrDefault(x => x.VkName == memberName);

                            if (member != null)
                            {
                                member.Comment = new List <string> {
                                    memberSummary
                                };
                            }
                        }
                    }
                }
            }

            foreach (var vkDocCommand in vkDocsXml.Element("docs").Element("commands").Elements("command"))
            {
                string commandName = vkDocCommand.Attribute("name").Value;

                if (filteredSpec.Commands.ContainsKey(commandName))
                {
                    var parsedCommand = filteredSpec.Commands[commandName];

                    parsedCommand.Comment = new List <string> {
                        vkDocCommand.Attribute("summary").Value
                    };
                }
            }

            return(filteredSpec);
        }