Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

More generation tests #32

Merged
merged 13 commits into from
Jan 9, 2025
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
<div align="center">
<a href="https://discord.gg/nFcsW3C33u"><img src="https://discordapp.com/api/guilds/1136305719226937425/embed.png" alt="Discord server" /></a>
<a href="https://github.com/roblox-csharp/roblox-cs/actions"><img src="https://github.com/roblox-csharp/roblox-cs/actions/workflows/ci.yml/badge.svg?branch=master" alt="CI Status" /></a>
<a href="https://github.com/roblox-csharp/roblox-cs/actions"><img src="https://github.com/roblox-csharp/roblox-cs/actions/workflows/cd.yml/badge.svg?branch=master" alt="CD Status" /></a>
<a href="https://github.com/roblox-csharp/roblox-cs/actions"><img src="https://github.com/roblox-csharp/roblox-cs/actions/workflows/cd.yml/badge.svg?branch=master" alt="CD Status" /></a>
<a href="https://coveralls.io/github/roblox-csharp/roblox-cs?branch=master"><img src="https://coveralls.io/repos/github/roblox-csharp/roblox-cs/badge.svg?branch=master" alt="Coverage Status" /></a>
</div>

Expand Down
39 changes: 31 additions & 8 deletions RobloxCS.Luau/AST/TableInitializer.cs
Original file line number Diff line number Diff line change
@@ -1,12 +1,35 @@
namespace RobloxCS.Luau;

public class TableInitializer(
List<Expression>? values = null,
List<Expression>? keys = null,
bool treatIdentifiersAsKeyNames = false) : Expression
public class TableInitializer : Expression
{
public List<Expression> Values { get; } = values ?? [];
public List<Expression> Keys { get; } = keys ?? [];
public List<Expression> Values { get; }
public List<Expression> Keys { get; }
public List<KeyValuePair<Expression, Expression>> KeyValuePairs { get; }
public bool TreatIdentifiersAsKeyNames { get; }

public TableInitializer(List<Expression>? values = null,
List<Expression>? keys = null,
bool treatIdentifiersAsKeyNames = false)
{
TreatIdentifiersAsKeyNames = treatIdentifiersAsKeyNames;
Values = values ?? [];
Keys = keys ?? [];

KeyValuePairs = [];
for (var i = 0; i < Math.Max(Values.Count, Keys.Count); i++)
{
var key = Keys.ElementAtOrDefault(i);
var value = Values.ElementAtOrDefault(i);
if (key == null || value == null) continue;

KeyValuePairs.Add(KeyValuePair.Create(key, value));
}

AddChildren(Values);
AddChildren(Keys);
}



public override void Render(LuauWriter luau)
{
Expand All @@ -25,11 +48,11 @@ public override void Render(LuauWriter luau)
var key = Keys.ElementAtOrDefault(index);
if (key != null)
{
if (!treatIdentifiersAsKeyNames || key is not IdentifierName)
if (!TreatIdentifiersAsKeyNames || key is not IdentifierName)
luau.Write('[');

key.Render(luau);
if (!treatIdentifiersAsKeyNames || key is not IdentifierName)
if (!TreatIdentifiersAsKeyNames || key is not IdentifierName)
luau.Write(']');

luau.Write(" = ");
Expand Down
7 changes: 6 additions & 1 deletion RobloxCS.Luau/AstUtility.cs
Original file line number Diff line number Diff line change
Expand Up @@ -255,7 +255,7 @@ public static Call Bit32Call(string methodName, params Expression[] arguments) =
public static ArgumentList CreateArgumentList(List<Expression> arguments) =>
new(arguments.ConvertAll(expression => new Argument(expression)));

public static SimpleName TypeNameFromSymbol(ITypeSymbol symbol)
public static SimpleName TypeNameFromSymbol(ISymbol symbol)
{
if (symbol is not INamedTypeSymbol { TypeParameters.Length: > 0 } namedTypeSymbol)
return new IdentifierName(symbol.Name);
Expand Down Expand Up @@ -368,6 +368,11 @@ public static IdentifierName GetNonGenericName(SimpleName simpleName)
: simpleName.ToString());
}

public static Name CreateName(SyntaxNode node, bool registerIdentifier = false, bool bypassReserved = false)
{
return CreateName(node, string.Join("", StandardUtility.GetNamesFromNode(node)), registerIdentifier, bypassReserved);
}

public static Name CreateName(SyntaxNode node, string text, bool registerIdentifier = false, bool bypassReserved = false)
{
Name name = CreateSimpleName(node, text, registerIdentifier, bypassReserved);
Expand Down
91 changes: 46 additions & 45 deletions RobloxCS.Luau/Macros.cs
Original file line number Diff line number Diff line change
Expand Up @@ -105,18 +105,18 @@ public class Macro(SemanticModel semanticModel)
case "List":
case "IEnumerable":
{
var elementTypeName = (IdentifierName)visit(genericName.TypeArgumentList.Arguments.First())!;
var expanded = new IdentifierName($"{{ {StandardUtility.GetMappedType(elementTypeName.Text)} }}");
var elementTypeName = visit(genericName.TypeArgumentList.Arguments.First())!;
var expanded = new IdentifierName($"{{ {StandardUtility.GetMappedType(elementTypeName.ToString()!)} }}");
expanded.MarkExpanded(MacroKind.IEnumerableType);

return expanded;
}

case "Dictionary":
{
var keyTypeName = (IdentifierName)visit(genericName.TypeArgumentList.Arguments.First())!;
var valueTypeName = (IdentifierName)visit(genericName.TypeArgumentList.Arguments.Last())!;
var expanded = new IdentifierName($"{{ [{StandardUtility.GetMappedType(keyTypeName.Text)}]: {StandardUtility.GetMappedType(valueTypeName.Text)} }}");
var keyTypeName = visit(genericName.TypeArgumentList.Arguments.First())!;
var valueTypeName = visit(genericName.TypeArgumentList.Arguments.Last())!;
var expanded = new IdentifierName($"{{ [{StandardUtility.GetMappedType(keyTypeName.ToString()!)}]: {StandardUtility.GetMappedType(valueTypeName.ToString()!)} }}");
expanded.MarkExpanded(MacroKind.DictionaryType);

return expanded;
Expand Down Expand Up @@ -193,58 +193,59 @@ public class Macro(SemanticModel semanticModel)
/// <returns>The expanded expression of the macro, or null if no macro was applied</returns>
public Expression? ObjectCreation(Func<SyntaxNode, Node?> visit, BaseObjectCreationExpressionSyntax baseObjectCreation) {
// generic objects
var type = (baseObjectCreation is ObjectCreationExpressionSyntax objectCreation
? _semanticModel.GetSymbolInfo(objectCreation.Type)
: _semanticModel.GetSymbolInfo(baseObjectCreation)).Symbol!.ContainingSymbol as INamedTypeSymbol;
var symbol = baseObjectCreation is ObjectCreationExpressionSyntax objectCreation
? _semanticModel.GetSymbolInfo(objectCreation.Type).Symbol
: _semanticModel.GetSymbolInfo(baseObjectCreation).Symbol?.ContainingSymbol;

if (type is { TypeParameters.Length: > 0 }) {
switch (type.Name) {
case "List":
{
var expressions = baseObjectCreation.Initializer?.Expressions.Select(expression => (Expression)visit(expression)!).ToList();
var table = new TableInitializer(expressions ?? []);
table.MarkExpanded(MacroKind.ListConstruction);
if (symbol is not INamedTypeSymbol { TypeParameters.Length: > 0 } namedTypeSymbol)
return null;

switch (namedTypeSymbol.Name) {
case "List":
{
var expressions = baseObjectCreation.Initializer?.Expressions.Select(expression => (Expression)visit(expression)!).ToList();
var table = new TableInitializer(expressions ?? []);
table.MarkExpanded(MacroKind.ListConstruction);

return table;
}
return table;
}

case "Dictionary": {
var values = new List<Expression>();
var keys = new List<Expression>();
case "Dictionary": {
var values = new List<Expression>();
var keys = new List<Expression>();

if (baseObjectCreation.Initializer != null) {
foreach (var expression in baseObjectCreation.Initializer.Expressions)
if (baseObjectCreation.Initializer != null) {
foreach (var expression in baseObjectCreation.Initializer.Expressions)
{
switch (expression)
{
case AssignmentExpressionSyntax assignmentExpression:
{
switch (expression)
{
case AssignmentExpressionSyntax assignmentExpression:
{
var key = (Expression)visit(assignmentExpression.Left)!;
var value = (Expression)visit(assignmentExpression.Right)!;
var key = (Expression)visit(assignmentExpression.Left)!;
var value = (Expression)visit(assignmentExpression.Right)!;

values.Add(value);
keys.Add(key);
break;
}
values.Add(value);
keys.Add(key);
break;
}

case InitializerExpressionSyntax initializerExpression:
{
var key = (Expression)visit(initializerExpression.Expressions[0])!;
var value = (Expression)visit(initializerExpression.Expressions[1])!;
case InitializerExpressionSyntax initializerExpression:
{
var key = (Expression)visit(initializerExpression.Expressions[0])!;
var value = (Expression)visit(initializerExpression.Expressions[1])!;

values.Add(value);
keys.Add(key);
break;
}
}
values.Add(value);
keys.Add(key);
break;
}
}
}
}

var table = new TableInitializer(values, keys);
table.MarkExpanded(MacroKind.DictionaryConstruction);
var table = new TableInitializer(values, keys);
table.MarkExpanded(MacroKind.DictionaryConstruction);

return table;
}
return table;
}
}

Expand Down
61 changes: 27 additions & 34 deletions RobloxCS.Shared/StandardUtility.cs
Original file line number Diff line number Diff line change
Expand Up @@ -84,27 +84,6 @@ public static string GetDefaultValueForType(string typeName)
return member;
}

public static void PrettyPrint(object? obj)
{
if (obj == null)
{
Console.WriteLine("null");
return;
}

var type = obj.GetType();
var properties = type.GetProperties(BindingFlags.Public | BindingFlags.Instance);
var result = $"{type.Name}:\n";

foreach (var property in properties)
{
var value = property.GetValue(obj, null);
result += $" {property.Name}: {value}\n";
}

Console.WriteLine(result);
}

public static List<T> FilterDuplicates<T>(IEnumerable<T> items, IEqualityComparer<T> comparer) where T : notnull
{
var seen = new Dictionary<T, bool>(comparer);
Expand Down Expand Up @@ -133,6 +112,7 @@ public static string GetMappedType(string csharpType)
var typeArgs = ExtractTypeArguments(csharpType).ConvertAll(GetMappedType);
var returnType = typeArgs.Last();
typeArgs = typeArgs.SkipLast(1).ToList();

return $"({string.Join(", ", typeArgs)}) -> {returnType}";
}

Expand All @@ -141,7 +121,7 @@ public static string GetMappedType(string csharpType)
"object" => "any",
"void" or "null" => "nil",
"char" or "Char" or "String" => "string",
"bool" => "boolean",
"Boolean" or "bool" => "boolean",
_ => INTEGER_TYPES.Contains(csharpType) || DECIMAL_TYPES.Contains(csharpType)
? "number"
: csharpType
Expand Down Expand Up @@ -198,6 +178,16 @@ public static List<string> ExtractTypeArguments(string input)
public static bool IsGlobal(SyntaxNode node) =>
node.Parent.IsKind(SyntaxKind.GlobalStatement) || node.Parent.IsKind(SyntaxKind.CompilationUnit);

public static NameSyntax GetNameNode(List<string> pieces)
{
if (pieces.Count <= 1)
return SyntaxFactory.IdentifierName(pieces.FirstOrDefault() ?? "");

var left = GetNameNode(pieces.SkipLast(1).ToList());
var right = SyntaxFactory.IdentifierName(pieces.Last());
return SyntaxFactory.QualifiedName(left, right);
}

public static List<string> GetNamesFromNode(SyntaxNode? node)
{
if (node is BaseExpressionSyntax)
Expand Down Expand Up @@ -231,20 +221,23 @@ List<string> addGenerics(List<string> currentNames)
}

var childNodes = node.ChildNodes().ToList();
var qualifiedNameNodes = node is QualifiedNameSyntax qualifiedName
var qualifiedNameNodes = (node is QualifiedNameSyntax qualifiedName
? [qualifiedName]
: childNodes.OfType<QualifiedNameSyntax>();
var simpleNameNodes = node is SimpleNameSyntax simpleName
: childNodes.OfType<QualifiedNameSyntax>()).ToList();
var simpleNameNodes = (node is SimpleNameSyntax simpleName
? [simpleName]
: childNodes.OfType<SimpleNameSyntax>();

foreach (var qualifiedNameNode in qualifiedNameNodes)
{
names.AddRange(GetNamesFromNode(qualifiedNameNode.Left).Select(name => name.Trim()));
names.AddRange(GetNamesFromNode(qualifiedNameNode.Right).Select(name => name.Trim()));
}

names.AddRange(simpleNameNodes.Select(simpleNameNode => simpleNameNode.ToString().Trim()));
: childNodes.OfType<SimpleNameSyntax>()).ToList();

if (simpleNameNodes.Count <= 1)
foreach (var qualifiedNameNode in qualifiedNameNodes)
{
names.AddRange(GetNamesFromNode(qualifiedNameNode.Left).Select(name => name.Trim()));
names.AddRange(GetNamesFromNode(qualifiedNameNode.Right).Select(name => name.Trim()));
}

if (qualifiedNameNodes.Count <= 1)
names.AddRange(simpleNameNodes.Select(simpleNameNode => simpleNameNode.ToString().Trim()));

return addGenerics(names);
}
}
Loading
Loading