switch root namespace name to NITLOAD
This commit is contained in:
@@ -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
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user