switch root namespace name to NITLOAD

This commit is contained in:
Youmu Liu
2026-09-02 18:15:03 +08:00
parent 4218baad48
commit 7c7101ee48
502 changed files with 710 additions and 721 deletions
@@ -0,0 +1,258 @@
// -----------------------------------------------------------------------------
// file="DesktopGlCoreRegistryFilter"
// Project: StgSharp
// Copyright (c) Nitload.
// SPDX-License-Identifier: MIT
// -----------------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using System.Linq;
namespace StgSharp.GenerateGL.Registry
{
/// <summary>
/// Selects the active desktop OpenGL core API without extension requirements.
/// </summary>
internal static class DesktopGlCoreRegistryFilter
{
public const string ApiName = "gl";
public const string CoreProfile = "core";
public const string LatestVersion = "4.6";
public static GlRegistryProjection Apply(
GlRegistryModel registry,
string maximumVersion = LatestVersion
)
{
ArgumentNullException.ThrowIfNull(registry);
Version maximum = ReadVersion(maximumVersion);
GlFeatureDefinition[] features = registry.Features
.Where(
value => string.Equals(value.Api, ApiName, StringComparison.Ordinal) &&
ReadVersion(value.Number) <= maximum)
.ToArray();
Dictionary<string, GlCommandDefinition> commandDefinitions = ReadCommandDefinitions(
registry);
Dictionary<string, GlEnumDefinition> enumDefinitions = ReadEnumDefinitions(registry);
SymbolSelection commands = new SymbolSelection();
SymbolSelection enums = new SymbolSelection();
foreach (GlFeatureDefinition feature in features)
{
foreach (GlRequirementBlock block in feature.Requirements.Where(AppliesToTarget))
{
foreach (GlRegistryReference reference in block.References)
{
SymbolSelection? selection = reference.Kind switch
{
GlRegistryReferenceKind.Command => commands,
GlRegistryReferenceKind.Enum => enums,
GlRegistryReferenceKind.Type => null,
_ => throw new InvalidDataException(
$"Unknown registry reference kind '{reference.Kind}'."),
};
if (selection is null)
{
continue;
}
if (block.Operation == GlRequirementOperation.Require)
{
selection.Require(reference.Name, feature.Number);
}
else
{
selection.Remove(reference.Name);
}
}
}
}
GlCommandDefinition[] selectedCommands = commands.ReadActiveNames()
.Select(
name => ReadDefinition(
commandDefinitions,
name,
"command"))
.ToArray();
GlEnumDefinition[] selectedEnums = enums.ReadActiveNames()
.Select(
name => ReadDefinition(
enumDefinitions,
name,
"enum"))
.ToArray();
return new GlRegistryProjection(
registry,
ApiName,
CoreProfile,
maximumVersion,
Freeze(features),
Freeze(selectedCommands),
Freeze(selectedEnums),
commands.FreezeIntroducedVersions(),
enums.FreezeIntroducedVersions());
}
private static bool AppliesToTarget(
GlRequirementBlock block
)
{
bool matchesApi = block.Api is null || string.Equals(
block.Api,
ApiName,
StringComparison.Ordinal);
bool matchesProfile = block.Profile is null || string.Equals(
block.Profile,
CoreProfile,
StringComparison.Ordinal);
return matchesApi && matchesProfile;
}
private static Dictionary<string, GlCommandDefinition> ReadCommandDefinitions(
GlRegistryModel registry
)
{
Dictionary<string, GlCommandDefinition> result = new Dictionary<string, GlCommandDefinition>(
StringComparer.Ordinal);
foreach (GlCommandDefinition command in registry.Commands)
{
if (!result.TryAdd(command.Name, command))
{
throw new InvalidDataException(
$"Command '{command.Name}' has more than one definition.");
}
}
return result;
}
private static Dictionary<string, GlEnumDefinition> ReadEnumDefinitions(
GlRegistryModel registry
)
{
Dictionary<string, GlEnumDefinition> result = new Dictionary<string, GlEnumDefinition>(
StringComparer.Ordinal);
foreach (GlEnumDefinition value in registry.EnumBlocks.SelectMany(
block => block.Enums).Where(IsDesktopDefinition))
{
if (!result.TryGetValue(value.Name, out GlEnumDefinition? previous))
{
result.Add(value.Name, value);
continue;
}
if (previous.Value is not null && value.Value is not null && !string.Equals(
previous.Value,
value.Value,
StringComparison.Ordinal))
{
throw new InvalidDataException(
$"Enum '{value.Name}' has conflicting desktop values " +
$"'{previous.Value}' and '{value.Value}'.");
}
if (previous.Value is null && value.Value is not null)
{
result[value.Name] = value;
}
}
return result;
}
private static bool IsDesktopDefinition(
GlEnumDefinition value
)
{
return value.Api is null || string.Equals(
value.Api,
ApiName,
StringComparison.Ordinal);
}
private static T ReadDefinition<T>(
IReadOnlyDictionary<string, T> definitions,
string name,
string kind
)
{
if (!definitions.TryGetValue(name, out T? definition))
{
throw new InvalidDataException(
$"Desktop OpenGL core references undefined {kind} '{name}'.");
}
return definition;
}
private static Version ReadVersion(
string value
)
{
if (!Version.TryParse(value, out Version? version))
{
throw new InvalidDataException(
$"OpenGL feature version '{value}' is not a valid version.");
}
return version;
}
private static ReadOnlyCollection<T> Freeze<T>(
IEnumerable<T> values
)
{
return Array.AsReadOnly(values.ToArray());
}
private sealed class SymbolSelection
{
private readonly HashSet<string> _active = new HashSet<string>(StringComparer.Ordinal);
private readonly Dictionary<string, string> _introducedVersions = new Dictionary<string, string>(
StringComparer.Ordinal);
private readonly List<string> _order = new List<string>();
public void Require(
string name,
string version
)
{
_active.Add(name);
if (_introducedVersions.TryAdd(name, version))
{
_order.Add(name);
}
}
public void Remove(
string name
)
{
_active.Remove(name);
}
public IEnumerable<string> ReadActiveNames()
{
return _order.Where(_active.Contains);
}
public ReadOnlyDictionary<string, string> FreezeIntroducedVersions()
{
return new ReadOnlyDictionary<string, string>(
new Dictionary<string, string>(
_introducedVersions,
StringComparer.Ordinal));
}
}
}
}
@@ -0,0 +1,59 @@
// -----------------------------------------------------------------------------
// file="GlApiDefinition"
// Project: StgSharp
// Copyright (c) Nitload.
// SPDX-License-Identifier: MIT
// -----------------------------------------------------------------------------
using System.Collections.Generic;
namespace StgSharp.GenerateGL.Registry
{
internal sealed record GlFeatureDefinition(
string Name,
string Api,
string Number,
string? Protect,
string? Comment,
IReadOnlyList<GlRequirementBlock> Requirements,
GlRegistryElement Element
);
internal sealed record GlExtensionDefinition(
string Name,
string? SupportedApis,
string? Protect,
string? Comment,
IReadOnlyList<GlRequirementBlock> Requirements,
GlRegistryElement Element
);
internal sealed record GlRequirementBlock(
GlRequirementOperation Operation,
string? Api,
string? Profile,
string? Comment,
IReadOnlyList<GlRegistryReference> References,
GlRegistryElement Element
);
internal sealed record GlRegistryReference(
GlRegistryReferenceKind Kind,
string Name,
string? Comment,
GlRegistryElement Element
);
internal enum GlRequirementOperation
{
Require,
Remove,
}
internal enum GlRegistryReferenceKind
{
Type,
Enum,
Command,
}
}
@@ -0,0 +1,56 @@
// -----------------------------------------------------------------------------
// file="GlCommandDefinition"
// Project: StgSharp
// Copyright (c) Nitload.
// SPDX-License-Identifier: MIT
// -----------------------------------------------------------------------------
using System.Collections.Generic;
namespace StgSharp.GenerateGL.Registry
{
internal sealed record GlCommandBlock(
string? Namespace,
IReadOnlyList<GlCommandDefinition> Commands,
GlRegistryElement Element
);
internal sealed record GlCommandDefinition(
string Name,
GlReturnDefinition Return,
IReadOnlyList<GlParameterDefinition> Parameters,
IReadOnlyList<GlNamedReference> Aliases,
IReadOnlyList<GlNamedReference> VectorEquivalents,
IReadOnlyList<GlCommandProtocol> Protocols,
string? Comment,
GlRegistryElement Element
);
internal sealed record GlReturnDefinition(
string DeclarationText,
string? TypeName,
string? Group,
string? Kind,
string? ObjectClass,
GlRegistryElement Element
);
internal sealed record GlParameterDefinition(
string Name,
string DeclarationText,
string? TypeName,
string? Group,
string? Kind,
string? ObjectClass,
string? Length,
GlRegistryElement Element
);
internal sealed record GlCommandProtocol(
string Type,
string Opcode,
string? Name,
string? Comment,
GlRegistryElement Element
);
}
@@ -0,0 +1,50 @@
// -----------------------------------------------------------------------------
// file="GlEnumDefinition"
// Project: StgSharp
// Copyright (c) Nitload.
// SPDX-License-Identifier: MIT
// -----------------------------------------------------------------------------
using System.Collections.Generic;
namespace StgSharp.GenerateGL.Registry
{
internal sealed record GlEnumDefinition(
string Name,
string? Value,
string? Api,
string? TypeSuffix,
string? Group,
string? Alias,
string? Comment,
GlRegistryElement Element
);
internal sealed record GlEnumBlock(
string? Namespace,
string? Group,
string? Type,
string? Start,
string? End,
string? Vendor,
string? Comment,
IReadOnlyList<GlEnumDefinition> Enums,
IReadOnlyList<GlUnusedEnumRange> UnusedRanges,
GlRegistryElement Element
);
internal sealed record GlEnumGroup(
string Name,
string? Comment,
IReadOnlyList<GlNamedReference> Members,
GlRegistryElement Element
);
internal sealed record GlUnusedEnumRange(
string Start,
string? End,
string? Vendor,
string? Comment,
GlRegistryElement Element
);
}
@@ -0,0 +1,30 @@
// -----------------------------------------------------------------------------
// file="GlRegistryElement"
// Project: StgSharp
// Copyright (c) Nitload.
// SPDX-License-Identifier: MIT
// -----------------------------------------------------------------------------
using System.Collections.Generic;
namespace StgSharp.GenerateGL.Registry
{
/// <summary>
/// Preserves the XML representation shared by every registry model item.
/// </summary>
internal sealed record GlRegistryElement(
GlSourceLocation Location,
string RawXml,
IReadOnlyDictionary<string, string> Attributes
);
internal readonly record struct GlSourceLocation(
int LineNumber,
int LinePosition
);
internal sealed record GlNamedReference(
string Name,
GlRegistryElement Element
);
}
@@ -0,0 +1,126 @@
// -----------------------------------------------------------------------------
// file="GlRegistryModel"
// Project: StgSharp
// Copyright (c) Nitload.
// SPDX-License-Identifier: MIT
// -----------------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
namespace StgSharp.GenerateGL.Registry
{
/// <summary>
/// Contains the structural OpenGL registry model without managed type mapping.
/// </summary>
internal sealed class GlRegistryModel
{
public GlRegistryModel(
string sourcePath,
string? comment,
IEnumerable<GlTypeDefinition> types,
IEnumerable<GlKindDefinition> kinds,
IEnumerable<GlEnumGroup> enumGroups,
IEnumerable<GlEnumBlock> enumBlocks,
IEnumerable<GlCommandBlock> commandBlocks,
IEnumerable<GlFeatureDefinition> features,
IEnumerable<GlExtensionDefinition> extensions,
GlRegistryElement element
)
{
ArgumentException.ThrowIfNullOrWhiteSpace(sourcePath);
ArgumentNullException.ThrowIfNull(types);
ArgumentNullException.ThrowIfNull(kinds);
ArgumentNullException.ThrowIfNull(enumGroups);
ArgumentNullException.ThrowIfNull(enumBlocks);
ArgumentNullException.ThrowIfNull(commandBlocks);
ArgumentNullException.ThrowIfNull(features);
ArgumentNullException.ThrowIfNull(extensions);
ArgumentNullException.ThrowIfNull(element);
SourcePath = sourcePath;
Comment = comment;
Types = Freeze(types);
Kinds = Freeze(kinds);
EnumGroups = Freeze(enumGroups);
EnumBlocks = Freeze(enumBlocks);
CommandBlocks = Freeze(commandBlocks);
Commands = Freeze(CommandBlocks.SelectMany(value => value.Commands));
Features = Freeze(features);
Extensions = Freeze(extensions);
Element = element;
TypesByName = CreateIndex(Types, value => value.Name);
KindsByName = CreateIndex(Kinds, value => value.Name);
EnumGroupsByName = CreateIndex(EnumGroups, value => value.Name);
EnumsByName = CreateIndex(
EnumBlocks.SelectMany(value => value.Enums),
value => value.Name);
CommandsByName = CreateIndex(Commands, value => value.Name);
FeaturesByName = CreateIndex(Features, value => value.Name);
ExtensionsByName = CreateIndex(Extensions, value => value.Name);
}
public string SourcePath { get; }
public string? Comment { get; }
public IReadOnlyList<GlTypeDefinition> Types { get; }
public IReadOnlyList<GlKindDefinition> Kinds { get; }
public IReadOnlyList<GlEnumGroup> EnumGroups { get; }
public IReadOnlyList<GlEnumBlock> EnumBlocks { get; }
public IReadOnlyList<GlCommandBlock> CommandBlocks { get; }
public IReadOnlyList<GlCommandDefinition> Commands { get; }
public IReadOnlyList<GlFeatureDefinition> Features { get; }
public IReadOnlyList<GlExtensionDefinition> Extensions { get; }
public GlRegistryElement Element { get; }
public IReadOnlyDictionary<string, IReadOnlyList<GlTypeDefinition>> TypesByName { get; }
public IReadOnlyDictionary<string, IReadOnlyList<GlKindDefinition>> KindsByName { get; }
public IReadOnlyDictionary<string, IReadOnlyList<GlEnumGroup>> EnumGroupsByName { get; }
public IReadOnlyDictionary<string, IReadOnlyList<GlEnumDefinition>> EnumsByName { get; }
public IReadOnlyDictionary<string, IReadOnlyList<GlCommandDefinition>> CommandsByName { get; }
public IReadOnlyDictionary<string, IReadOnlyList<GlFeatureDefinition>> FeaturesByName { get; }
public IReadOnlyDictionary<string, IReadOnlyList<GlExtensionDefinition>> ExtensionsByName { get; }
private static ReadOnlyDictionary<string, IReadOnlyList<T>> CreateIndex<T>(
IEnumerable<T> values,
Func<T, string> readName
)
{
Dictionary<string, IReadOnlyList<T>> index = values.GroupBy(
readName,
StringComparer.Ordinal)
.ToDictionary(
value => value.Key,
value => (IReadOnlyList<T>)Freeze(value),
StringComparer.Ordinal);
return new ReadOnlyDictionary<string, IReadOnlyList<T>>(index);
}
private static ReadOnlyCollection<T> Freeze<T>(
IEnumerable<T> values
)
{
return Array.AsReadOnly(values.ToArray());
}
}
}
@@ -0,0 +1,26 @@
// -----------------------------------------------------------------------------
// file="GlRegistryProjection"
// Project: StgSharp
// Copyright (c) Nitload.
// SPDX-License-Identifier: MIT
// -----------------------------------------------------------------------------
using System.Collections.Generic;
namespace StgSharp.GenerateGL.Registry
{
/// <summary>
/// Represents one API/profile target selected from the complete registry.
/// </summary>
internal sealed record GlRegistryProjection(
GlRegistryModel Source,
string Api,
string Profile,
string MaximumVersion,
IReadOnlyList<GlFeatureDefinition> Features,
IReadOnlyList<GlCommandDefinition> Commands,
IReadOnlyList<GlEnumDefinition> Enums,
IReadOnlyDictionary<string, string> CommandIntroducedVersions,
IReadOnlyDictionary<string, string> EnumIntroducedVersions
);
}
@@ -0,0 +1,511 @@
// -----------------------------------------------------------------------------
// file="GlRegistryReader"
// Project: StgSharp
// Copyright (c) Nitload.
// SPDX-License-Identifier: MIT
// -----------------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using System.Linq;
using System.Xml;
using System.Xml.Linq;
using StgSharp.GenerateGL.Tree;
namespace StgSharp.GenerateGL.Registry
{
/// <summary>
/// Projects the generic XML tree into the structural OpenGL registry model.
/// </summary>
internal static class GlRegistryReader
{
private static readonly IReadOnlyDictionary<string, string> _emptyAttributes =
new ReadOnlyDictionary<string, string>(
new Dictionary<string, string>(StringComparer.Ordinal));
public static GlRegistryModel Read(
XmlTreeDocument document
)
{
ArgumentNullException.ThrowIfNull(document);
XElement root = document.Tree.Root ?? throw new InvalidDataException(
$"XML document '{document.SourcePath}' does not contain a root element.");
if (root.Name != "registry")
{
throw CreateError(
document,
root,
$"Expected <registry>, but found <{root.Name}>.");
}
ValidateChildren(
document,
root,
"comment",
"types",
"kinds",
"groups",
"enums",
"commands",
"feature",
"extensions");
return new GlRegistryModel(
document.SourcePath,
ReadOptionalElementText(root, "comment"),
ReadSection(document, root, "types", "type").Select(
value => ReadType(document, value)),
ReadSection(document, root, "kinds", "kind").Select(
value => ReadKind(document, value)),
ReadSection(document, root, "groups", "group").Select(
value => ReadEnumGroup(document, value)),
root.Elements("enums").Select(value => ReadEnumBlock(document, value)),
root.Elements("commands").Select(
value => ReadCommandBlock(document, value)),
root.Elements("feature").Select(value => ReadFeature(document, value)),
ReadSection(document, root, "extensions", "extension").Select(
value => ReadExtension(document, value)),
ReadElement(root));
}
private static GlTypeDefinition ReadType(
XmlTreeDocument document,
XElement element
)
{
string name = ReadOptionalAttribute(element, "name") ??
ReadOptionalElementText(element, "name") ??
throw CreateError(
document,
element,
"A registry <type> requires a name attribute or child element.");
return new GlTypeDefinition(
name,
ReadDeclarationText(element),
ReadOptionalAttribute(element, "api"),
ReadOptionalAttribute(element, "requires"),
ReadOptionalAttribute(element, "type"),
ReadOptionalAttribute(element, "comment"),
ReadElement(element));
}
private static GlKindDefinition ReadKind(
XmlTreeDocument document,
XElement element
)
{
return new GlKindDefinition(
ReadRequiredAttribute(document, element, "name"),
ReadRequiredAttribute(document, element, "desc"),
ReadElement(element));
}
private static GlEnumGroup ReadEnumGroup(
XmlTreeDocument document,
XElement element
)
{
ValidateChildren(document, element, "enum");
return new GlEnumGroup(
ReadRequiredAttribute(document, element, "name"),
ReadOptionalAttribute(element, "comment"),
Freeze(
element.Elements("enum").Select(
value => new GlNamedReference(
ReadRequiredAttribute(document, value, "name"),
ReadElement(value)))),
ReadElement(element));
}
private static GlEnumBlock ReadEnumBlock(
XmlTreeDocument document,
XElement element
)
{
ValidateChildren(document, element, "enum", "unused");
return new GlEnumBlock(
ReadOptionalAttribute(element, "namespace"),
ReadOptionalAttribute(element, "group"),
ReadOptionalAttribute(element, "type"),
ReadOptionalAttribute(element, "start"),
ReadOptionalAttribute(element, "end"),
ReadOptionalAttribute(element, "vendor"),
ReadOptionalAttribute(element, "comment"),
Freeze(
element.Elements("enum").Select(
value => ReadEnum(document, value))),
Freeze(
element.Elements("unused").Select(
value => ReadUnusedEnumRange(document, value))),
ReadElement(element));
}
private static GlEnumDefinition ReadEnum(
XmlTreeDocument document,
XElement element
)
{
return new GlEnumDefinition(
ReadRequiredAttribute(document, element, "name"),
ReadOptionalAttribute(element, "value"),
ReadOptionalAttribute(element, "api"),
ReadOptionalAttribute(element, "type"),
ReadOptionalAttribute(element, "group"),
ReadOptionalAttribute(element, "alias"),
ReadOptionalAttribute(element, "comment"),
ReadElement(element));
}
private static GlUnusedEnumRange ReadUnusedEnumRange(
XmlTreeDocument document,
XElement element
)
{
return new GlUnusedEnumRange(
ReadRequiredAttribute(document, element, "start"),
ReadOptionalAttribute(element, "end"),
ReadOptionalAttribute(element, "vendor"),
ReadOptionalAttribute(element, "comment"),
ReadElement(element));
}
private static GlCommandBlock ReadCommandBlock(
XmlTreeDocument document,
XElement element
)
{
ValidateChildren(document, element, "command");
return new GlCommandBlock(
ReadOptionalAttribute(element, "namespace"),
Freeze(
element.Elements("command").Select(
value => ReadCommand(document, value))),
ReadElement(element));
}
private static GlCommandDefinition ReadCommand(
XmlTreeDocument document,
XElement element
)
{
ValidateChildren(
document,
element,
"proto",
"param",
"alias",
"vecequiv",
"glx");
XElement prototype = element.Element("proto") ?? throw CreateError(
document,
element,
"A registry <command> requires a <proto> child element.");
string name = ReadRequiredElementText(document, prototype, "name");
return new GlCommandDefinition(
name,
ReadReturn(document, prototype),
Freeze(
element.Elements("param").Select(
value => ReadParameter(document, value))),
Freeze(
element.Elements("alias").Select(
value => ReadNamedReference(document, value))),
Freeze(
element.Elements("vecequiv").Select(
value => ReadNamedReference(document, value))),
Freeze(
element.Elements("glx").Select(
value => ReadCommandProtocol(document, value))),
ReadOptionalAttribute(element, "comment"),
ReadElement(element));
}
private static GlReturnDefinition ReadReturn(
XmlTreeDocument document,
XElement element
)
{
ValidateChildren(document, element, "ptype", "name");
_ = ReadRequiredElementText(document, element, "name");
return new GlReturnDefinition(
ReadDeclarationText(element),
ReadOptionalElementText(element, "ptype"),
ReadOptionalAttribute(element, "group"),
ReadOptionalAttribute(element, "kind"),
ReadOptionalAttribute(element, "class"),
ReadElement(element));
}
private static GlParameterDefinition ReadParameter(
XmlTreeDocument document,
XElement element
)
{
ValidateChildren(document, element, "ptype", "name");
return new GlParameterDefinition(
ReadRequiredElementText(document, element, "name"),
ReadDeclarationText(element),
ReadOptionalElementText(element, "ptype"),
ReadOptionalAttribute(element, "group"),
ReadOptionalAttribute(element, "kind"),
ReadOptionalAttribute(element, "class"),
ReadOptionalAttribute(element, "len"),
ReadElement(element));
}
private static GlNamedReference ReadNamedReference(
XmlTreeDocument document,
XElement element
)
{
return new GlNamedReference(
ReadRequiredAttribute(document, element, "name"),
ReadElement(element));
}
private static GlCommandProtocol ReadCommandProtocol(
XmlTreeDocument document,
XElement element
)
{
return new GlCommandProtocol(
ReadRequiredAttribute(document, element, "type"),
ReadRequiredAttribute(document, element, "opcode"),
ReadOptionalAttribute(element, "name"),
ReadOptionalAttribute(element, "comment"),
ReadElement(element));
}
private static GlFeatureDefinition ReadFeature(
XmlTreeDocument document,
XElement element
)
{
ValidateChildren(document, element, "require", "remove");
return new GlFeatureDefinition(
ReadRequiredAttribute(document, element, "name"),
ReadRequiredAttribute(document, element, "api"),
ReadRequiredAttribute(document, element, "number"),
ReadOptionalAttribute(element, "protect"),
ReadOptionalAttribute(element, "comment"),
Freeze(
element.Elements().Select(
value => ReadRequirement(document, value))),
ReadElement(element));
}
private static GlExtensionDefinition ReadExtension(
XmlTreeDocument document,
XElement element
)
{
ValidateChildren(document, element, "require", "remove");
return new GlExtensionDefinition(
ReadRequiredAttribute(document, element, "name"),
ReadOptionalAttribute(element, "supported"),
ReadOptionalAttribute(element, "protect"),
ReadOptionalAttribute(element, "comment"),
Freeze(
element.Elements().Select(
value => ReadRequirement(document, value))),
ReadElement(element));
}
private static GlRequirementBlock ReadRequirement(
XmlTreeDocument document,
XElement element
)
{
ValidateChildren(document, element, "type", "enum", "command");
GlRequirementOperation operation = element.Name.LocalName switch
{
"require" => GlRequirementOperation.Require,
"remove" => GlRequirementOperation.Remove,
_ => throw CreateError(
document,
element,
$"Expected <require> or <remove>, but found <{element.Name}>."),
};
return new GlRequirementBlock(
operation,
ReadOptionalAttribute(element, "api"),
ReadOptionalAttribute(element, "profile"),
ReadOptionalAttribute(element, "comment"),
Freeze(
element.Elements().Select(
value => ReadRegistryReference(document, value))),
ReadElement(element));
}
private static GlRegistryReference ReadRegistryReference(
XmlTreeDocument document,
XElement element
)
{
GlRegistryReferenceKind kind = element.Name.LocalName switch
{
"type" => GlRegistryReferenceKind.Type,
"enum" => GlRegistryReferenceKind.Enum,
"command" => GlRegistryReferenceKind.Command,
_ => throw CreateError(
document,
element,
$"Unsupported registry reference <{element.Name}>."),
};
return new GlRegistryReference(
kind,
ReadRequiredAttribute(document, element, "name"),
ReadOptionalAttribute(element, "comment"),
ReadElement(element));
}
private static IEnumerable<XElement> ReadSection(
XmlTreeDocument document,
XElement root,
XName sectionName,
XName itemName
)
{
foreach (XElement section in root.Elements(sectionName))
{
ValidateChildren(document, section, itemName.LocalName);
foreach (XElement item in section.Elements(itemName))
{
yield return item;
}
}
}
private static string ReadRequiredAttribute(
XmlTreeDocument document,
XElement element,
XName name
)
{
string? value = ReadOptionalAttribute(element, name);
if (value is null)
{
throw CreateError(
document,
element,
$"<{element.Name}> requires the '{name}' attribute.");
}
return value;
}
private static string? ReadOptionalAttribute(
XElement element,
XName name
)
{
string? value = (string?)element.Attribute(name);
return string.IsNullOrWhiteSpace(value) ? null : value;
}
private static string ReadRequiredElementText(
XmlTreeDocument document,
XElement element,
XName name
)
{
string? value = ReadOptionalElementText(element, name);
if (value is null)
{
throw CreateError(
document,
element,
$"<{element.Name}> requires a non-empty <{name}> child element.");
}
return value;
}
private static string? ReadOptionalElementText(
XElement element,
XName name
)
{
string? value = element.Element(name)?.Value.Trim();
return string.IsNullOrWhiteSpace(value) ? null : value;
}
private static string ReadDeclarationText(
XElement element
)
{
return element.Value.Trim();
}
private static GlRegistryElement ReadElement(
XElement element
)
{
IXmlLineInfo lineInfo = element;
GlSourceLocation location = lineInfo.HasLineInfo()
? new GlSourceLocation(lineInfo.LineNumber, lineInfo.LinePosition)
: default;
IReadOnlyDictionary<string, string> attributes = element.HasAttributes
? new ReadOnlyDictionary<string, string>(
element.Attributes().ToDictionary(
value => value.Name.ToString(),
value => value.Value,
StringComparer.Ordinal))
: _emptyAttributes;
return new GlRegistryElement(
location,
element.ToString(SaveOptions.DisableFormatting),
attributes);
}
private static ReadOnlyCollection<T> Freeze<T>(
IEnumerable<T> values
)
{
return Array.AsReadOnly(values.ToArray());
}
private static void ValidateChildren(
XmlTreeDocument document,
XElement element,
params string[] allowedNames
)
{
HashSet<string> allowed = new HashSet<string>(
allowedNames,
StringComparer.Ordinal);
XElement? unsupported = element.Elements().FirstOrDefault(
value => !allowed.Contains(value.Name.LocalName));
if (unsupported is not null)
{
throw CreateError(
document,
unsupported,
$"Unsupported <{unsupported.Name}> child in <{element.Name}>.");
}
}
private static InvalidDataException CreateError(
XmlTreeDocument document,
XElement element,
string message
)
{
IXmlLineInfo lineInfo = element;
string location = lineInfo.HasLineInfo()
? $"({lineInfo.LineNumber},{lineInfo.LinePosition})"
: string.Empty;
return new InvalidDataException(
$"{document.SourcePath}{location}: {message}");
}
}
}
@@ -0,0 +1,25 @@
// -----------------------------------------------------------------------------
// file="GlTypeDefinition"
// Project: StgSharp
// Copyright (c) Nitload.
// SPDX-License-Identifier: MIT
// -----------------------------------------------------------------------------
namespace StgSharp.GenerateGL.Registry
{
internal sealed record GlTypeDefinition(
string Name,
string DeclarationText,
string? Api,
string? RequiredType,
string? Category,
string? Comment,
GlRegistryElement Element
);
internal sealed record GlKindDefinition(
string Name,
string Description,
GlRegistryElement Element
);
}