switch root namespace name to NITLOAD
This commit is contained in:
@@ -0,0 +1,429 @@
|
||||
// -----------------------------------------------------------------------------
|
||||
// file="GlFunctionFamilyAnalyzer"
|
||||
// Project: StgSharp
|
||||
// Copyright (c) Nitload.
|
||||
// SPDX-License-Identifier: MIT
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using StgSharp.GenerateGL.Naming;
|
||||
using StgSharp.GenerateGL.Registry;
|
||||
|
||||
namespace StgSharp.GenerateGL.Analysis
|
||||
{
|
||||
/// <summary>
|
||||
/// Builds inspectable function-family candidates without changing generation.
|
||||
/// </summary>
|
||||
internal static class GlFunctionFamilyAnalyzer
|
||||
{
|
||||
|
||||
public static GlFunctionFamilyAnalysis Analyze(
|
||||
GlRegistryModel registry
|
||||
)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(registry);
|
||||
return Analyze(registry, registry.Commands);
|
||||
}
|
||||
|
||||
public static GlFunctionFamilyAnalysis Analyze(
|
||||
GlRegistryProjection projection
|
||||
)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(projection);
|
||||
return Analyze(projection.Source, projection.Commands);
|
||||
}
|
||||
|
||||
private static GlFunctionFamilyAnalysis Analyze(
|
||||
GlRegistryModel registry,
|
||||
IReadOnlyList<GlCommandDefinition> commands
|
||||
)
|
||||
{
|
||||
Dictionary<string, IReadOnlyList<GlCommandDefinition>> commandsByName = commands
|
||||
.GroupBy(value => value.Name, StringComparer.Ordinal)
|
||||
.ToDictionary(
|
||||
value => value.Key,
|
||||
value => (IReadOnlyList<GlCommandDefinition>)Array.AsReadOnly(
|
||||
value.ToArray()),
|
||||
StringComparer.Ordinal);
|
||||
|
||||
Dictionary<string, GlFunctionNameShape> shapes =
|
||||
GlFunctionNameFactorizer.Factor(registry, commands);
|
||||
Dictionary<string, FamilyBuilder> builders = new Dictionary<string, FamilyBuilder>(
|
||||
StringComparer.Ordinal);
|
||||
List<GlFunctionRelation> relations = new List<GlFunctionRelation>();
|
||||
|
||||
foreach (GlCommandDefinition command in commands)
|
||||
{
|
||||
if (shapes.TryGetValue(command.Name, out GlFunctionNameShape? shape))
|
||||
{
|
||||
ReadBuilder(builders, shape.FamilyName).Add(command, shape);
|
||||
}
|
||||
}
|
||||
|
||||
AddRegistryRelations(commands, commandsByName, shapes, builders, relations);
|
||||
AddSingleValuePointerRelations(commandsByName, shapes, builders, relations);
|
||||
|
||||
GlFunctionFamily[] families = builders.Values
|
||||
.Where(value => value.Members.Count > 1 || value.Relations.Count != 0)
|
||||
.Select(value => value.Build())
|
||||
.OrderBy(value => value.Name, StringComparer.Ordinal)
|
||||
.ToArray();
|
||||
HashSet<string> classifiedNames = families
|
||||
.SelectMany(value => value.Members)
|
||||
.Select(value => value.Command.Name)
|
||||
.ToHashSet(StringComparer.Ordinal);
|
||||
GlCommandDefinition[] unclassified = commands
|
||||
.Where(value => !classifiedNames.Contains(value.Name))
|
||||
.OrderBy(value => value.Name, StringComparer.Ordinal)
|
||||
.ToArray();
|
||||
|
||||
return new GlFunctionFamilyAnalysis(
|
||||
Freeze(families),
|
||||
Freeze(unclassified),
|
||||
Freeze(relations));
|
||||
}
|
||||
|
||||
private static void AddRegistryRelations(
|
||||
IReadOnlyList<GlCommandDefinition> commands,
|
||||
Dictionary<string, IReadOnlyList<GlCommandDefinition>> commandsByName,
|
||||
Dictionary<string, GlFunctionNameShape> shapes,
|
||||
IDictionary<string, FamilyBuilder> builders,
|
||||
List<GlFunctionRelation> relations
|
||||
)
|
||||
{
|
||||
foreach (GlCommandDefinition command in commands)
|
||||
{
|
||||
foreach (GlNamedReference reference in command.VectorEquivalents)
|
||||
{
|
||||
bool exists = commandsByName.TryGetValue(
|
||||
reference.Name,
|
||||
out IReadOnlyList<GlCommandDefinition>? targets);
|
||||
GlFunctionRelation relation = new GlFunctionRelation(
|
||||
command.Name,
|
||||
reference.Name,
|
||||
GlFunctionRelationKind.RegistryVectorEquivalent,
|
||||
exists
|
||||
? GlFunctionRelationState.Verified
|
||||
: GlFunctionRelationState.MissingTarget,
|
||||
exists
|
||||
? "declared by gl.xml <vecequiv>"
|
||||
: "gl.xml <vecequiv> target is not registered");
|
||||
relations.Add(relation);
|
||||
|
||||
string familyName = ReadRelationFamilyName(
|
||||
command.Name,
|
||||
reference.Name,
|
||||
shapes);
|
||||
FamilyBuilder builder = ReadBuilder(builders, familyName);
|
||||
builder.Add(
|
||||
command,
|
||||
shapes.TryGetValue(command.Name, out GlFunctionNameShape? sourceShape)
|
||||
? sourceShape
|
||||
: null);
|
||||
builder.Relations.Add(relation);
|
||||
if (!exists)
|
||||
{
|
||||
builder.Diagnostics.Add(
|
||||
$"Missing vector-equivalent target '{reference.Name}'.");
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach (GlCommandDefinition target in targets!)
|
||||
{
|
||||
builder.Add(
|
||||
target,
|
||||
shapes.TryGetValue(target.Name, out GlFunctionNameShape? targetShape)
|
||||
? targetShape
|
||||
: null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void AddSingleValuePointerRelations(
|
||||
Dictionary<string, IReadOnlyList<GlCommandDefinition>> commandsByName,
|
||||
Dictionary<string, GlFunctionNameShape> shapes,
|
||||
IDictionary<string, FamilyBuilder> builders,
|
||||
List<GlFunctionRelation> relations
|
||||
)
|
||||
{
|
||||
HashSet<string> existingPairs = relations
|
||||
.Select(value => $"{value.SourceName}\0{value.TargetName}")
|
||||
.ToHashSet(StringComparer.Ordinal);
|
||||
foreach ((string commandName, GlFunctionNameShape pointerShape) in shapes)
|
||||
{
|
||||
if (!pointerShape.IsVectorForm ||
|
||||
!commandsByName.TryGetValue(
|
||||
commandName,
|
||||
out IReadOnlyList<GlCommandDefinition>? pointerCommands))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
string scalarName = commandName[..^1];
|
||||
if (!shapes.TryGetValue(scalarName, out GlFunctionNameShape? scalarShape) ||
|
||||
!commandsByName.TryGetValue(
|
||||
scalarName,
|
||||
out IReadOnlyList<GlCommandDefinition>? scalarCommands))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach (GlCommandDefinition pointer in pointerCommands)
|
||||
{
|
||||
if (pointer.Parameters.Count == 0 ||
|
||||
!string.Equals(pointer.Parameters[^1].Length, "1", StringComparison.Ordinal))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach (GlCommandDefinition scalar in scalarCommands)
|
||||
{
|
||||
if (existingPairs.Contains($"{scalar.Name}\0{pointer.Name}"))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
bool valid = IsSingleValuePointerPair(scalar, pointer, out string detail);
|
||||
GlFunctionRelation relation = new GlFunctionRelation(
|
||||
scalar.Name,
|
||||
pointer.Name,
|
||||
GlFunctionRelationKind.InferredSingleValuePointer,
|
||||
valid
|
||||
? GlFunctionRelationState.Verified
|
||||
: GlFunctionRelationState.Rejected,
|
||||
detail);
|
||||
relations.Add(relation);
|
||||
|
||||
FamilyBuilder builder = ReadBuilder(builders, pointerShape.FamilyName);
|
||||
builder.Add(scalar, scalarShape);
|
||||
builder.Add(pointer, pointerShape);
|
||||
builder.Relations.Add(relation);
|
||||
if (!valid)
|
||||
{
|
||||
builder.Diagnostics.Add(
|
||||
$"Rejected scalar/pointer pair {scalar.Name} -> {pointer.Name}: " +
|
||||
detail);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static bool IsSingleValuePointerPair(
|
||||
GlCommandDefinition scalar,
|
||||
GlCommandDefinition pointer,
|
||||
out string detail
|
||||
)
|
||||
{
|
||||
if (!string.Equals(
|
||||
scalar.Return.TypeName,
|
||||
pointer.Return.TypeName,
|
||||
StringComparison.Ordinal) ||
|
||||
scalar.Parameters.Count != pointer.Parameters.Count)
|
||||
{
|
||||
detail = "return type or parameter count differs";
|
||||
return false;
|
||||
}
|
||||
|
||||
int lastIndex = scalar.Parameters.Count - 1;
|
||||
for (int index = 0; index < lastIndex; index++)
|
||||
{
|
||||
if (!HaveEquivalentParameterTypes(
|
||||
scalar.Parameters[index],
|
||||
pointer.Parameters[index]))
|
||||
{
|
||||
detail = $"parameter {index + 1} differs";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
GlParameterDefinition scalarValue = scalar.Parameters[lastIndex];
|
||||
GlParameterDefinition pointerValue = pointer.Parameters[lastIndex];
|
||||
if (!string.Equals(
|
||||
scalarValue.TypeName,
|
||||
pointerValue.TypeName,
|
||||
StringComparison.Ordinal) ||
|
||||
scalarValue.DeclarationText.Contains('*', StringComparison.Ordinal) ||
|
||||
!pointerValue.DeclarationText.Contains('*', StringComparison.Ordinal) ||
|
||||
!pointerValue.DeclarationText.TrimStart().StartsWith(
|
||||
"const ",
|
||||
StringComparison.Ordinal) ||
|
||||
!string.Equals(pointerValue.Length, "1", StringComparison.Ordinal))
|
||||
{
|
||||
detail = "last parameter is not a matching T / const T* len=1 pair";
|
||||
return false;
|
||||
}
|
||||
|
||||
detail = "verified T / const T* len=1 pair";
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool HaveEquivalentParameterTypes(
|
||||
GlParameterDefinition left,
|
||||
GlParameterDefinition right
|
||||
)
|
||||
{
|
||||
return string.Equals(left.TypeName, right.TypeName, StringComparison.Ordinal) &&
|
||||
left.DeclarationText.Contains('*', StringComparison.Ordinal) ==
|
||||
right.DeclarationText.Contains('*', StringComparison.Ordinal);
|
||||
}
|
||||
|
||||
private static string ReadRelationFamilyName(
|
||||
string sourceName,
|
||||
string targetName,
|
||||
Dictionary<string, GlFunctionNameShape> shapes
|
||||
)
|
||||
{
|
||||
if (shapes.TryGetValue(sourceName, out GlFunctionNameShape? sourceShape))
|
||||
{
|
||||
return sourceShape.FamilyName;
|
||||
}
|
||||
|
||||
if (shapes.TryGetValue(targetName, out GlFunctionNameShape? targetShape))
|
||||
{
|
||||
return targetShape.FamilyName;
|
||||
}
|
||||
|
||||
string source = RemoveGlPrefix(sourceName);
|
||||
string target = RemoveGlPrefix(targetName);
|
||||
if (target.EndsWith('v') &&
|
||||
string.Equals(source, target[..^1], StringComparison.Ordinal))
|
||||
{
|
||||
return CSharpIdentifierPolicy.NormalizeMemberName(source);
|
||||
}
|
||||
|
||||
int length = 0;
|
||||
int maximum = Math.Min(source.Length, target.Length);
|
||||
while (length < maximum && source[length] == target[length])
|
||||
{
|
||||
length++;
|
||||
}
|
||||
|
||||
string familyName = length == 0 ? source : source[..length].TrimEnd('_');
|
||||
return CSharpIdentifierPolicy.NormalizeMemberName(familyName);
|
||||
}
|
||||
|
||||
private static string RemoveGlPrefix(
|
||||
string name
|
||||
)
|
||||
{
|
||||
return name.StartsWith("gl", StringComparison.Ordinal) && name.Length > 2
|
||||
? name[2..]
|
||||
: name;
|
||||
}
|
||||
|
||||
private static FamilyBuilder ReadBuilder(
|
||||
IDictionary<string, FamilyBuilder> builders,
|
||||
string familyName
|
||||
)
|
||||
{
|
||||
if (!builders.TryGetValue(familyName, out FamilyBuilder? builder))
|
||||
{
|
||||
builder = new FamilyBuilder(familyName);
|
||||
builders.Add(familyName, builder);
|
||||
}
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
private static ReadOnlyCollection<T> Freeze<T>(
|
||||
IEnumerable<T> values
|
||||
)
|
||||
{
|
||||
return Array.AsReadOnly(values.ToArray());
|
||||
}
|
||||
|
||||
private sealed class FamilyBuilder
|
||||
{
|
||||
|
||||
private readonly Dictionary<string, GlFunctionFamilyMember> _members = new Dictionary<string, GlFunctionFamilyMember>(
|
||||
StringComparer.Ordinal);
|
||||
|
||||
public FamilyBuilder(
|
||||
string name
|
||||
)
|
||||
{
|
||||
Name = name;
|
||||
}
|
||||
|
||||
public string Name { get; }
|
||||
|
||||
public Dictionary<string, GlFunctionFamilyMember>.ValueCollection Members =>
|
||||
_members.Values;
|
||||
|
||||
public List<GlFunctionRelation> Relations { get; } = new List<GlFunctionRelation>();
|
||||
|
||||
public List<string> Diagnostics { get; } = new List<string>();
|
||||
|
||||
public void Add(
|
||||
GlCommandDefinition command,
|
||||
GlFunctionNameShape? shape
|
||||
)
|
||||
{
|
||||
_members.TryAdd(
|
||||
command.Name,
|
||||
new GlFunctionFamilyMember(command, shape));
|
||||
}
|
||||
|
||||
public GlFunctionFamily Build()
|
||||
{
|
||||
GlFunctionFamilyMember[] members = _members.Values
|
||||
.OrderBy(value => value.Command.Name, StringComparer.Ordinal)
|
||||
.ToArray();
|
||||
string[] diagnostics = Diagnostics
|
||||
.Distinct(StringComparer.Ordinal)
|
||||
.OrderBy(value => value, StringComparer.Ordinal)
|
||||
.ToArray();
|
||||
return new GlFunctionFamily(
|
||||
Name,
|
||||
Freeze(members),
|
||||
Freeze(Relations.Distinct().ToArray()),
|
||||
Freeze(diagnostics),
|
||||
ReadShapeNamePolicy(members));
|
||||
}
|
||||
|
||||
private static GlFunctionShapeNamePolicy ReadShapeNamePolicy(
|
||||
IReadOnlyCollection<GlFunctionFamilyMember> members
|
||||
)
|
||||
{
|
||||
GlFunctionFamilyMember[] scalarMembers = members
|
||||
.Where(
|
||||
value => value.NameShape is
|
||||
{ IsVectorForm: false, Shape: not null })
|
||||
.ToArray();
|
||||
if (scalarMembers.Select(value => value.NameShape!.Shape)
|
||||
.Distinct(StringComparer.Ordinal)
|
||||
.Count() < 2)
|
||||
{
|
||||
return GlFunctionShapeNamePolicy.NotApplicable;
|
||||
}
|
||||
|
||||
bool collision = scalarMembers
|
||||
.GroupBy(ReadNativeSignature, StringComparer.Ordinal)
|
||||
.Any(group => group.Select(value => value.NameShape!.Shape)
|
||||
.Distinct(StringComparer.Ordinal)
|
||||
.Count() > 1);
|
||||
return collision
|
||||
? GlFunctionShapeNamePolicy.MustRetain
|
||||
: GlFunctionShapeNamePolicy.CanRemove;
|
||||
}
|
||||
|
||||
private static string ReadNativeSignature(
|
||||
GlFunctionFamilyMember member
|
||||
)
|
||||
{
|
||||
GlCommandDefinition command = member.Command;
|
||||
return $"{command.Return.TypeName}:" + string.Join(
|
||||
",",
|
||||
command.Parameters.Select(
|
||||
value => $"{value.TypeName}:{value.DeclarationText.Contains('*', StringComparison.Ordinal)}"));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
// -----------------------------------------------------------------------------
|
||||
// file="GlFunctionFamilyModel"
|
||||
// Project: StgSharp
|
||||
// Copyright (c) Nitload.
|
||||
// SPDX-License-Identifier: MIT
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
using System.Collections.Generic;
|
||||
using StgSharp.GenerateGL.Registry;
|
||||
|
||||
namespace StgSharp.GenerateGL.Analysis
|
||||
{
|
||||
internal sealed record GlFunctionNameShape(
|
||||
string FamilyName,
|
||||
string? Shape,
|
||||
int? Columns,
|
||||
int? Rows,
|
||||
string TypeSuffix,
|
||||
bool IsVectorForm
|
||||
);
|
||||
|
||||
internal sealed record GlFunctionFamilyMember(
|
||||
GlCommandDefinition Command,
|
||||
GlFunctionNameShape? NameShape
|
||||
);
|
||||
|
||||
internal sealed record GlFunctionRelation(
|
||||
string SourceName,
|
||||
string TargetName,
|
||||
GlFunctionRelationKind Kind,
|
||||
GlFunctionRelationState State,
|
||||
string Detail
|
||||
);
|
||||
|
||||
internal sealed record GlFunctionFamily(
|
||||
string Name,
|
||||
IReadOnlyList<GlFunctionFamilyMember> Members,
|
||||
IReadOnlyList<GlFunctionRelation> Relations,
|
||||
IReadOnlyList<string> Diagnostics,
|
||||
GlFunctionShapeNamePolicy ShapeNamePolicy
|
||||
);
|
||||
|
||||
internal sealed record GlFunctionFamilyAnalysis(
|
||||
IReadOnlyList<GlFunctionFamily> Families,
|
||||
IReadOnlyList<GlCommandDefinition> UnclassifiedCommands,
|
||||
IReadOnlyList<GlFunctionRelation> Relations
|
||||
);
|
||||
|
||||
internal enum GlFunctionRelationKind
|
||||
{
|
||||
RegistryVectorEquivalent,
|
||||
InferredSingleValuePointer,
|
||||
}
|
||||
|
||||
internal enum GlFunctionRelationState
|
||||
{
|
||||
Verified,
|
||||
MissingTarget,
|
||||
Rejected,
|
||||
}
|
||||
|
||||
internal enum GlFunctionShapeNamePolicy
|
||||
{
|
||||
NotApplicable,
|
||||
CanRemove,
|
||||
MustRetain,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,472 @@
|
||||
// -----------------------------------------------------------------------------
|
||||
// file="GlFunctionNameFactorizer"
|
||||
// Project: StgSharp
|
||||
// Copyright (c) Nitload.
|
||||
// SPDX-License-Identifier: MIT
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using StgSharp.GenerateGL.Naming;
|
||||
using StgSharp.GenerateGL.Registry;
|
||||
|
||||
namespace StgSharp.GenerateGL.Analysis
|
||||
{
|
||||
/// <summary>
|
||||
/// Factors command-name alternatives by their common prefix and interprets only the
|
||||
/// remaining suffix as overload axes.
|
||||
/// </summary>
|
||||
internal static class GlFunctionNameFactorizer
|
||||
{
|
||||
|
||||
private static readonly string[] _typeSuffixes =
|
||||
{
|
||||
"ui64",
|
||||
"i64",
|
||||
"ui",
|
||||
"ub",
|
||||
"us",
|
||||
"b",
|
||||
"s",
|
||||
"i",
|
||||
"f",
|
||||
"d",
|
||||
"h",
|
||||
"x",
|
||||
};
|
||||
|
||||
public static Dictionary<string, GlFunctionNameShape> Factor(
|
||||
GlRegistryModel registry,
|
||||
IReadOnlyList<GlCommandDefinition> commands
|
||||
)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(registry);
|
||||
ArgumentNullException.ThrowIfNull(commands);
|
||||
|
||||
string[] vendorSuffixes = ReadVendorSuffixes(registry);
|
||||
NameEntry[] entries = commands
|
||||
.Select(
|
||||
command => new NameEntry(
|
||||
command,
|
||||
command.Name,
|
||||
ReadCoreName(command.Name, vendorSuffixes)))
|
||||
.ToArray();
|
||||
TrieNode root = new TrieNode(string.Empty);
|
||||
for (int index = 0; index < entries.Length; index++)
|
||||
{
|
||||
Add(root, entries[index].CoreName, index);
|
||||
}
|
||||
|
||||
List<FactorCandidate>[] candidatesByEntry = Enumerable.Range(0, entries.Length)
|
||||
.Select(_ => new List<FactorCandidate>())
|
||||
.ToArray();
|
||||
CollectCandidates(root, entries, candidatesByEntry);
|
||||
|
||||
Dictionary<string, GlFunctionNameShape> result = new Dictionary<string, GlFunctionNameShape>(
|
||||
StringComparer.Ordinal);
|
||||
for (int index = 0; index < entries.Length; index++)
|
||||
{
|
||||
FactorCandidate? candidate = candidatesByEntry[index]
|
||||
.OrderByDescending(value => value.DistinctVariantCount)
|
||||
.ThenByDescending(value => value.MemberCount)
|
||||
.ThenBy(value => value.FamilyName.Length)
|
||||
.FirstOrDefault();
|
||||
if (candidate is null ||
|
||||
!candidate.Shapes.TryGetValue(index, out GlFunctionNameShape? shape))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
result.TryAdd(entries[index].CommandName, shape);
|
||||
}
|
||||
|
||||
AddFixedVectorPairs(result, commands);
|
||||
return result;
|
||||
}
|
||||
|
||||
private static void AddFixedVectorPairs(
|
||||
Dictionary<string, GlFunctionNameShape> shapes,
|
||||
IReadOnlyList<GlCommandDefinition> commands
|
||||
)
|
||||
{
|
||||
Dictionary<string, GlCommandDefinition> commandsByName = commands.ToDictionary(
|
||||
value => value.Name,
|
||||
StringComparer.Ordinal);
|
||||
foreach (GlCommandDefinition vector in commands)
|
||||
{
|
||||
if (!vector.Name.EndsWith('v') ||
|
||||
shapes.ContainsKey(vector.Name) ||
|
||||
vector.Parameters.Count == 0 ||
|
||||
!int.TryParse(vector.Parameters[^1].Length, out int componentCount) ||
|
||||
componentCount < 2)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
string scalarName = vector.Name[..^1];
|
||||
if (shapes.ContainsKey(scalarName) ||
|
||||
!commandsByName.TryGetValue(
|
||||
scalarName,
|
||||
out GlCommandDefinition? scalar) ||
|
||||
!IsFixedVectorPair(scalar, vector, componentCount))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
string familyName = CSharpIdentifierPolicy.NormalizeMemberName(
|
||||
scalarName.StartsWith("gl", StringComparison.Ordinal) &&
|
||||
scalarName.Length > 2
|
||||
? scalarName[2..]
|
||||
: scalarName);
|
||||
shapes.Add(
|
||||
scalarName,
|
||||
new GlFunctionNameShape(
|
||||
familyName,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
string.Empty,
|
||||
false));
|
||||
shapes.Add(
|
||||
vector.Name,
|
||||
new GlFunctionNameShape(
|
||||
familyName,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
string.Empty,
|
||||
true));
|
||||
}
|
||||
}
|
||||
|
||||
private static bool IsFixedVectorPair(
|
||||
GlCommandDefinition scalar,
|
||||
GlCommandDefinition vector,
|
||||
int componentCount
|
||||
)
|
||||
{
|
||||
if (!string.Equals(
|
||||
scalar.Return.TypeName,
|
||||
vector.Return.TypeName,
|
||||
StringComparison.Ordinal) ||
|
||||
scalar.Parameters.Count != vector.Parameters.Count - 1 + componentCount)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
int prefixCount = vector.Parameters.Count - 1;
|
||||
for (int index = 0; index < prefixCount; index++)
|
||||
{
|
||||
if (!HaveEquivalentScalarTypes(
|
||||
scalar.Parameters[index].TypeName,
|
||||
vector.Parameters[index].TypeName) ||
|
||||
scalar.Parameters[index].DeclarationText.Contains('*', StringComparison.Ordinal) !=
|
||||
vector.Parameters[index].DeclarationText.Contains('*', StringComparison.Ordinal))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
GlParameterDefinition vectorValue = vector.Parameters[^1];
|
||||
if (!vectorValue.DeclarationText.Contains('*', StringComparison.Ordinal))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int index = prefixCount; index < scalar.Parameters.Count; index++)
|
||||
{
|
||||
if (!HaveEquivalentScalarTypes(
|
||||
scalar.Parameters[index].TypeName,
|
||||
vectorValue.TypeName) ||
|
||||
scalar.Parameters[index].DeclarationText.Contains('*', StringComparison.Ordinal))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool HaveEquivalentScalarTypes(
|
||||
string? left,
|
||||
string? right
|
||||
)
|
||||
{
|
||||
if (string.Equals(left, right, StringComparison.Ordinal))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return left is "GLint" or "GLsizei" &&
|
||||
right is "GLint" or "GLsizei";
|
||||
}
|
||||
|
||||
private static List<int> CollectCandidates(
|
||||
TrieNode node,
|
||||
IReadOnlyList<NameEntry> entries,
|
||||
IReadOnlyList<List<FactorCandidate>> candidatesByEntry
|
||||
)
|
||||
{
|
||||
List<int> descendants = new List<int>(node.Terminals);
|
||||
foreach (TrieNode child in node.Children.Values)
|
||||
{
|
||||
descendants.AddRange(CollectCandidates(child, entries, candidatesByEntry));
|
||||
}
|
||||
|
||||
if (node.Prefix.Length == 0 || descendants.Count < 2)
|
||||
{
|
||||
return descendants;
|
||||
}
|
||||
|
||||
Dictionary<int, GlFunctionNameShape> shapes = new Dictionary<int, GlFunctionNameShape>();
|
||||
foreach (int index in descendants)
|
||||
{
|
||||
string coreName = entries[index].CoreName;
|
||||
if (coreName.Length <= node.Prefix.Length ||
|
||||
!TryParseVariantSuffix(
|
||||
coreName[node.Prefix.Length..],
|
||||
node.Prefix,
|
||||
entries[index].Command,
|
||||
out GlFunctionNameShape? shape))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
shapes.Add(index, shape!);
|
||||
}
|
||||
|
||||
int distinctVariantCount = shapes.Keys
|
||||
.Select(index => entries[index].CoreName)
|
||||
.Distinct(StringComparer.Ordinal)
|
||||
.Count();
|
||||
if (distinctVariantCount < 2)
|
||||
{
|
||||
return descendants;
|
||||
}
|
||||
|
||||
FactorCandidate candidate = new FactorCandidate(
|
||||
node.Prefix,
|
||||
shapes,
|
||||
distinctVariantCount);
|
||||
foreach (int index in shapes.Keys)
|
||||
{
|
||||
candidatesByEntry[index].Add(candidate);
|
||||
}
|
||||
|
||||
return descendants;
|
||||
}
|
||||
|
||||
private static bool TryParseVariantSuffix(
|
||||
string suffix,
|
||||
string familyName,
|
||||
GlCommandDefinition command,
|
||||
out GlFunctionNameShape? shape
|
||||
)
|
||||
{
|
||||
bool isVectorForm = suffix.EndsWith('v');
|
||||
if (isVectorForm)
|
||||
{
|
||||
suffix = suffix[..^1];
|
||||
}
|
||||
|
||||
string? typeSuffix = null;
|
||||
foreach (string candidate in _typeSuffixes)
|
||||
{
|
||||
if (suffix.EndsWith(candidate, StringComparison.Ordinal))
|
||||
{
|
||||
typeSuffix = candidate;
|
||||
suffix = suffix[..^candidate.Length];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (typeSuffix is null ||
|
||||
!IsTypeSuffixConsistent(command, typeSuffix) ||
|
||||
!TryParseShape(
|
||||
suffix,
|
||||
out string? shapeText,
|
||||
out int? columns,
|
||||
out int? rows))
|
||||
{
|
||||
shape = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
shape = new GlFunctionNameShape(
|
||||
CSharpIdentifierPolicy.NormalizeMemberName(familyName),
|
||||
shapeText,
|
||||
columns,
|
||||
rows,
|
||||
typeSuffix,
|
||||
isVectorForm);
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool IsTypeSuffixConsistent(
|
||||
GlCommandDefinition command,
|
||||
string typeSuffix
|
||||
)
|
||||
{
|
||||
string? glType = typeSuffix switch
|
||||
{
|
||||
"b" => "GLbyte",
|
||||
"ub" => "GLubyte",
|
||||
"s" => "GLshort",
|
||||
"us" => "GLushort",
|
||||
"i" => "GLint",
|
||||
"ui" => "GLuint",
|
||||
"i64" => "GLint64",
|
||||
"ui64" => "GLuint64",
|
||||
"f" => "GLfloat",
|
||||
"d" => "GLdouble",
|
||||
"h" => "GLhalf",
|
||||
"x" => "GLfixed",
|
||||
_ => null,
|
||||
};
|
||||
if (glType is null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return string.Equals(command.Return.TypeName, glType, StringComparison.Ordinal) ||
|
||||
command.Parameters.Any(
|
||||
value => string.Equals(
|
||||
value.TypeName,
|
||||
glType,
|
||||
StringComparison.Ordinal));
|
||||
}
|
||||
|
||||
private static bool TryParseShape(
|
||||
string value,
|
||||
out string? shape,
|
||||
out int? columns,
|
||||
out int? rows
|
||||
)
|
||||
{
|
||||
if (value.Length == 0)
|
||||
{
|
||||
shape = null;
|
||||
columns = null;
|
||||
rows = null;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (value.Length == 1 && value[0] is >= '1' and <= '4')
|
||||
{
|
||||
shape = value;
|
||||
columns = value[0] - '0';
|
||||
rows = 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (value.Length == 3 &&
|
||||
value[0] is >= '2' and <= '4' &&
|
||||
value[1] == 'x' &&
|
||||
value[2] is >= '2' and <= '4')
|
||||
{
|
||||
shape = value;
|
||||
columns = value[0] - '0';
|
||||
rows = value[2] - '0';
|
||||
return true;
|
||||
}
|
||||
|
||||
shape = null;
|
||||
columns = null;
|
||||
rows = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
private static void Add(
|
||||
TrieNode root,
|
||||
string value,
|
||||
int entryIndex
|
||||
)
|
||||
{
|
||||
TrieNode current = root;
|
||||
foreach (char character in value)
|
||||
{
|
||||
if (!current.Children.TryGetValue(character, out TrieNode? child))
|
||||
{
|
||||
child = new TrieNode(current.Prefix + character);
|
||||
current.Children.Add(character, child);
|
||||
}
|
||||
|
||||
current = child;
|
||||
}
|
||||
|
||||
current.Terminals.Add(entryIndex);
|
||||
}
|
||||
|
||||
private static string ReadCoreName(
|
||||
string commandName,
|
||||
IReadOnlyList<string> vendorSuffixes
|
||||
)
|
||||
{
|
||||
string name = commandName.StartsWith("gl", StringComparison.Ordinal) &&
|
||||
commandName.Length > 2
|
||||
? commandName[2..]
|
||||
: commandName;
|
||||
foreach (string vendorSuffix in vendorSuffixes)
|
||||
{
|
||||
if (name.Length > vendorSuffix.Length && name.EndsWith(
|
||||
vendorSuffix,
|
||||
StringComparison.Ordinal))
|
||||
{
|
||||
return name[..^vendorSuffix.Length];
|
||||
}
|
||||
}
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
private static string[] ReadVendorSuffixes(
|
||||
GlRegistryModel registry
|
||||
)
|
||||
{
|
||||
return registry.Extensions
|
||||
.Select(value => value.Name.Split('_', StringSplitOptions.RemoveEmptyEntries))
|
||||
.Where(value => value.Length > 2 && value[0] == "GL")
|
||||
.Select(value => value[1])
|
||||
.Where(value => value.All(character => char.IsUpper(character) || char.IsDigit(character)))
|
||||
.Distinct(StringComparer.Ordinal)
|
||||
.OrderByDescending(value => value.Length)
|
||||
.ThenBy(value => value, StringComparer.Ordinal)
|
||||
.ToArray();
|
||||
}
|
||||
|
||||
private sealed record NameEntry(
|
||||
GlCommandDefinition Command,
|
||||
string CommandName,
|
||||
string CoreName
|
||||
);
|
||||
|
||||
private sealed record FactorCandidate(
|
||||
string FamilyName,
|
||||
IReadOnlyDictionary<int, GlFunctionNameShape> Shapes,
|
||||
int DistinctVariantCount
|
||||
)
|
||||
{
|
||||
public int MemberCount => Shapes.Count;
|
||||
}
|
||||
|
||||
private sealed class TrieNode
|
||||
{
|
||||
|
||||
public TrieNode(
|
||||
string prefix
|
||||
)
|
||||
{
|
||||
Prefix = prefix;
|
||||
}
|
||||
|
||||
public string Prefix { get; }
|
||||
|
||||
public Dictionary<char, TrieNode> Children { get; } = new Dictionary<char, TrieNode>();
|
||||
|
||||
public List<int> Terminals { get; } = new List<int>();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user