asynchronousai's picture
Upload 33 files
1ae2e8e verified
using System.Reflection;
using System.Text.RegularExpressions;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
namespace RobloxCS
{
public static class Utility
{
public const string RuntimeAssemblyName = "Roblox";
public const string LuaRuntimeModuleName = "RuntimeLib";
public static string GetMappedType(string csharpType)
{
if (csharpType.EndsWith("[]"))
{
var arrayType = csharpType.Substring(0, csharpType.Length - 2);
return $"{{ {GetMappedType(arrayType)} }}";
}
if (csharpType.EndsWith('?'))
{
var nonNullableType = csharpType.Substring(0, csharpType.Length - 1);
return $"{GetMappedType(nonNullableType)}?";
}
switch (csharpType)
{
case "object":
return "any";
case "void":
case "null":
return "nil";
case "char":
case "Char":
case "String":
return "string";
case "double":
case "float":
return "number";
default:
if (Constants.INTEGER_TYPES.Contains(csharpType))
{
return "number";
}
return csharpType;
}
}
public static string GetBit32MethodName(string bitOp)
{
switch (bitOp)
{
case "&=":
case "&":
return "band";
case "|=":
case "|":
return "bor";
case "^=":
case "^":
return "bxor";
case ">>=":
case ">>":
return "rshift";
case ">>>=":
case ">>>":
return "arshift";
case "<<=":
case "<<":
return "lshift";
case "~":
return "bnot";
default:
return bitOp;
}
}
public static string GetMappedOperator(string op)
{
switch (op)
{
case "++":
return "+=";
case "--":
return "--";
case "!":
return "not ";
case "!=":
return "~=";
case "&&":
return "and";
case "||":
return "or";
default:
return op;
}
}
public static List<string> ExtractTypeArguments(string input)
{
var typeArguments = new List<string>();
var regex = new Regex(@"<(?<args>[^<>]+)>");
var match = regex.Match(input);
if (match.Success)
{
// Get the matched group containing the type arguments
var args = match.Groups["args"].Value;
// Split the arguments by comma and trim whitespace
var argsArray = args.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
foreach (var arg in argsArray)
{
typeArguments.Add(arg.Trim());
}
}
return typeArguments;
}
public static string FormatLocation(FileLinePositionSpan lineSpan)
{
return $"{(lineSpan.Path == "" ? "<anonymous>" : lineSpan.Path)}:{lineSpan.StartLinePosition.Line + 1}:{lineSpan.StartLinePosition.Character + 1}";
}
public static ISymbol? FindMember(INamespaceSymbol namespaceSymbol, string memberName)
{
var member = namespaceSymbol.GetMembers().FirstOrDefault<ISymbol?>(member => member?.Name == memberName, null);
if (member == null && namespaceSymbol.ContainingNamespace != null)
{
member = FindMember(namespaceSymbol.ContainingNamespace, memberName);
}
return member;
}
public static ISymbol? FindMemberDeep(INamedTypeSymbol namedTypeSymbol, string memberName)
{
var member = namedTypeSymbol.GetMembers().FirstOrDefault(member => member.Name == memberName);
if (namedTypeSymbol.BaseType != null && member == null)
{
return FindMemberDeep(namedTypeSymbol.BaseType, memberName);
}
return member;
}
public static List<string> GetNamesFromNode(SyntaxNode? node)
{
if (node is BaseExpressionSyntax baseExpression)
{
return [""];
}
var names = new List<string>();
if (node == null) return names;
var identifierProperty = node.GetType().GetProperty("Identifier");
var identifierValue = identifierProperty?.GetValue(node);
if (identifierProperty != null && identifierValue != null && identifierValue is SyntaxToken)
{
names.Add(((SyntaxToken)identifierValue).ValueText.Trim());
return names;
}
var childNodes = node.ChildNodes();
var qualifiedNameNodes = node.IsKind(SyntaxKind.QualifiedName) ? [(QualifiedNameSyntax)node] : childNodes.OfType<QualifiedNameSyntax>();
var identifierNameNodes = node.IsKind(SyntaxKind.IdentifierName) ? [(IdentifierNameSyntax)node] : childNodes.OfType<IdentifierNameSyntax>();
foreach (var qualifiedNameNode in qualifiedNameNodes)
{
foreach (var name in GetNamesFromNode(qualifiedNameNode.Left))
{
names.Add(name.Trim());
}
foreach (var name in GetNamesFromNode(qualifiedNameNode.Right))
{
names.Add(name.Trim());
}
}
foreach (var identifierNameNode in identifierNameNodes)
{
names.Add(identifierNameNode.Identifier.ValueText.Trim());
}
return names;
}
public static string FixPathSep(string path)
{
path = Path.TrimEndingDirectorySeparator(path);
return Regex.Replace(path.Replace("\\\\", "/").Replace('\\', '/').Replace("//", "/"), @"(?<!\.)\./", "");
}
public static string? GetRbxcsDirectory()
{
var directoryName = Path.GetDirectoryName(GetAssemblyDirectory()); // pretend like this isn't here lol
return directoryName == null ? null : FixPathSep(directoryName);
}
public static string GetAssemblyDirectory()
{
var location = FixPathSep(Assembly.GetExecutingAssembly().Location);
var directoryName = Path.GetDirectoryName(location)!;
return FixPathSep(directoryName);
}
public static string GetTargetFramework()
{
var assemblyDirectory = GetAssemblyDirectory();
if (assemblyDirectory == null)
{
Logger.Error("Failed to find RobloxCS assembly directory!");
}
return assemblyDirectory!.Split('/').Last();
}
public static List<T> FilterDuplicates<T>(IEnumerable<T> items, IEqualityComparer<T> comparer)
{
var seen = new Dictionary<T, bool>(comparer);
var result = new List<T>();
foreach (var item in items)
{
if (!seen.ContainsKey(item))
{
seen[item] = true;
result.Add(item);
}
}
return result;
}
public static void PrintChildNodes(SyntaxNode node)
{
Logger.Info($"{node.Kind()} node children: {node.ChildNodes().Count()}");
foreach (var child in node.ChildNodes())
{
Logger.Info(child.Kind().ToString() + ": " + child.GetText());
}
}
public static void PrintChildTokens(SyntaxNode node)
{
Logger.Info($"{node.Kind()} token children: {node.ChildTokens().Count()}");
foreach (var child in node.ChildTokens())
{
Logger.Info(child.Kind().ToString() + ": " + child.Text);
}
}
}
}