Skip to content

Commit

Permalink
feat: add support for another way to initialize dictionaries
Browse files Browse the repository at this point in the history
  • Loading branch information
denispionicul committed Jan 5, 2025
2 parents d313159 + a4cdd13 commit 858cadc
Show file tree
Hide file tree
Showing 14 changed files with 1,039 additions and 916 deletions.
4 changes: 2 additions & 2 deletions RobloxCS.Luau/AST/AssignmentFunctionName.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ public class AssignmentFunctionName : Name
{
public Name Left { get; }
public char Operator { get; set; }
public IdentifierName Right { get; }
public SimpleName Right { get; }

public AssignmentFunctionName(Name left, IdentifierName right, char @operator = '.')
public AssignmentFunctionName(Name left, SimpleName right, char @operator = '.')
{
Left = left;
Right = right;
Expand Down
10 changes: 2 additions & 8 deletions RobloxCS.Luau/AST/GenericName.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,8 @@ public class GenericName(string text, List<string> typeArguments) : SimpleName
public string Text { get; } = text;
public List<string> TypeArguments { get; } = typeArguments;

public override void Render(LuauWriter luau)
{
luau.Write(Text);
luau.Write('<');
luau.Write(string.Join(", ", TypeArguments));
luau.Write('>');
}
public override void Render(LuauWriter luau) => luau.Write(ToString());

public override string ToString() => Text;
public override string ToString() => Text + '<' + string.Join(", ", TypeArguments) + '>';
}
}
35 changes: 14 additions & 21 deletions RobloxCS.Luau/AST/TableInitializer.cs
Original file line number Diff line number Diff line change
@@ -1,15 +1,10 @@
namespace RobloxCS.Luau
{
public class TableInitializer : Expression
public class TableInitializer(List<Expression>? values = null, List<Expression>? keys = null)
: Expression
{
public List<Expression> Values { get; }
public List<Expression> Keys { get; }

public TableInitializer(List<Expression>? values = null, List<Expression>? keys = null)
{
Values = values ?? [];
Keys = keys ?? [];
}
public List<Expression> Values { get; } = values ?? [];
public List<Expression> Keys { get; } = keys ?? [];

public override void Render(LuauWriter luau)
{
Expand All @@ -21,6 +16,7 @@ public override void Render(LuauWriter luau)
luau.WriteLine();
luau.PushIndent();
}

foreach (var value in Values)
{
var index = Values.IndexOf(value);
Expand All @@ -40,24 +36,21 @@ public override void Render(LuauWriter luau)
}

value.Render(luau);
if (value != Values.Last())
{
luau.Write(',');
if (hasAnyKeys && value is not AnonymousFunction)
{
luau.WriteLine();
}
else
{
luau.Write(' ');
}
}
if (value == Values.Last()) continue;

luau.Write(',');
if (hasAnyKeys && value is not AnonymousFunction)
luau.WriteLine();
else
luau.Write(' ');
}

if (hasAnyKeys)
{
luau.PopIndent();
luau.WriteLine();
}

luau.Write('}');
}
}
Expand Down
4 changes: 2 additions & 2 deletions RobloxCS.Luau/AST/TypeAlias.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@
{
public sealed class TypeAlias : Statement
{
public IdentifierName Name { get; }
public SimpleName Name { get; }
public TypeRef Type { get; }

public TypeAlias(IdentifierName name, TypeRef type)
public TypeAlias(SimpleName name, TypeRef type)
{
Name = name;
Type = type;
Expand Down
119 changes: 86 additions & 33 deletions RobloxCS.Luau/AstUtility.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,20 +12,25 @@ public static class AstUtility
/// <summary>file path -> dictionary(identifier name, amount of times identifier is used)</summary>
private static readonly Dictionary<string, Dictionary<string, uint>> _identifierDeclarations = [];

/// <summary>Adds one to the expression</summary>
public static Expression AddOne(Expression expression)
{
return expression is Literal literal && int.TryParse(literal.ValueText, out var value)
? new Literal((value + 1).ToString())
: new BinaryOperator(expression, "+", new Literal("1"));
}

/// <summary>Subtracts one from the expression</summary>
public static Expression SubtractOne(Expression expression)
{
return expression is Literal literal && int.TryParse(literal.ValueText, out var value)
? new Literal((value - 1).ToString())
: new BinaryOperator(expression, "-", new Literal("1"));
}

/// <summary>
/// Creates type info table for runtime type objects
/// </summary>
public static TableInitializer CreateTypeInfo(Type type)
{
List<Expression> keys = [
Expand Down Expand Up @@ -153,6 +158,9 @@ public static TableInitializer CreateTypeInfo(Type type)
return new TableInitializer(values, keys);
}

/// <summary>
/// Creates constructor info table for runtime type objects (via .GetType() and typeof())
/// </summary>
public static TableInitializer CreateConstructorInfo(ConstructorInfo type)
{
List<Expression> keys = [
Expand All @@ -165,12 +173,17 @@ public static TableInitializer CreateConstructorInfo(ConstructorInfo type)
return new TableInitializer(values, keys);
}

/// <code>CS.defineGlobal(name, value)</code>
public static Call DefineGlobal(Name name, Expression value) =>
CSCall("defineGlobal", new Literal($"\"{name}\""), value);

/// <code>CS.getGlobal(name)</code>
public static Call GetGlobal(Name name) =>
CSCall("getGlobal", new Literal($"\"{name}\""));

/// <summary>
/// Creates a call to a CS library method
/// </summary>
public static Call CSCall(string methodName, params Expression[] arguments) =>
new Call(
new MemberAccess(
Expand All @@ -180,6 +193,9 @@ public static Call CSCall(string methodName, params Expression[] arguments) =>
CreateArgumentList(arguments.ToList())
);

/// <summary>
/// Creates a call to a bit32 library method
/// </summary>
public static Call Bit32Call(string methodName, params Expression[] arguments) =>
new Call(
new MemberAccess(
Expand All @@ -192,6 +208,11 @@ public static Call Bit32Call(string methodName, params Expression[] arguments) =
public static ArgumentList CreateArgumentList(List<Expression> arguments) =>
new ArgumentList(arguments.ConvertAll(expression => new Argument(expression)));

/// <summary>
/// Returns the full name of a C# node's parent.
/// This method is meant for getting the absolute location of classes, enums, etc.
/// For example a class under the namespace "Some.Namespace" would return a <see cref="MemberAccess"/> that transpiles to "Some.Namespace".
/// </summary>
public static Expression? GetFullParentName(SyntaxNode node)
{
switch (node.Parent)
Expand All @@ -201,24 +222,32 @@ public static ArgumentList CreateArgumentList(List<Expression> arguments) =>
return null;
}

var parentName = CreateIdentifierName(node.Parent);
var parentName = CreateSimpleName(node.Parent);
var parentLocation = GetFullParentName(node.Parent);
return parentLocation == null ?
(
node.Parent.SyntaxTree == node.SyntaxTree ?
parentName
: CSCall("getGlobal", new Literal($"\"{parentName.Text}\""))
: CSCall("getGlobal", new Literal($"\"{(parentName is GenericName genericName ? genericName.Text : parentName.ToString())}\""))
)
: new MemberAccess(parentLocation, parentName);
}

/// <code>
/// if name == nil then
/// name = initializer
/// end
/// </code>
public static If Initializer(Name name, Expression initializer) =>
new If(
new(
new BinaryOperator(name, "==", Nil()),
new ExpressionStatement(new Assignment(name, initializer)),
null
);

/// <summary>
/// Takes a <see cref="MemberAccess"/> and converts it into a <see cref="QualifiedName"/>, given that <see cref="MemberAccess.Expression"/> inherits from <see cref="Name"/>
/// </summary>
public static QualifiedName QualifiedNameFromMemberAccess(MemberAccess memberAccess)
{
var left = memberAccess.Expression is MemberAccess leftMemberAccess ?
Expand All @@ -228,13 +257,30 @@ public static QualifiedName QualifiedNameFromMemberAccess(MemberAccess memberAcc
return new QualifiedName(left, memberAccess.Name);
}

/// <summary>
/// Creates a discard variable if <see cref="valueParent"/> is an <see cref="ExpressionStatementSyntax"/>
/// </summary>
public static Node DiscardVariableIfExpressionStatement(SyntaxNode node, Node value, SyntaxNode? valueParent) =>
valueParent is ExpressionStatementSyntax ?
DiscardVariable(node, (Expression)value)
: value;

/// <code>local _ = discardedValue</code>
public static Variable DiscardVariable(SyntaxNode node, Expression value) =>
new Variable(CreateIdentifierName(node, "_"), true, value);
new(CreateSimpleName<IdentifierName>(node, "_"), true, value);

/// <summary>
/// Takes a SimpleName (which GenericName extends from) and converts it into a standard IdentifierName
/// </summary>
public static IdentifierName GetNonGenericName(SimpleName simpleName)
{
if (simpleName is IdentifierName identifierName)
return identifierName;

return new IdentifierName(simpleName is GenericName genericName
? genericName.Text
: simpleName.ToString());
}

public static Name CreateName(string text)
{
Expand All @@ -247,19 +293,40 @@ public static Name CreateName(string text)
.Skip(1)
.Aggregate(expression, (current, piece) => new QualifiedName(current, new IdentifierName(piece)));
}

public static TNameNode CreateSimpleName<TNameNode>(SyntaxNode node, bool registerIdentifier = false, bool bypassReserved = false)
where TNameNode : SimpleName
{
return (TNameNode)CreateSimpleName(node, registerIdentifier, bypassReserved);
}

public static IdentifierName CreateIdentifierName(SyntaxNode node, bool registerIdentifier = false, bool bypassReserved = false) =>
CreateIdentifierName(node, Utility.GetNamesFromNode(node).First(), registerIdentifier, bypassReserved);
public static TNameNode CreateSimpleName<TNameNode>(SyntaxNode node, string name, bool registerIdentifier = false, bool bypassReserved = false)
where TNameNode : SimpleName
{
return (TNameNode)CreateSimpleName(node, name, registerIdentifier, bypassReserved);
}

public static SimpleName CreateSimpleName(SyntaxNode node, bool registerIdentifier = false,
bool bypassReserved = false)
{
foreach (var name in Utility.GetNamesFromNode(node))
Console.WriteLine(name);

return CreateSimpleName(node, string.Join("", Utility.GetNamesFromNode(node)), registerIdentifier, bypassReserved);
}

public static IdentifierName CreateIdentifierName(SyntaxNode node, string name, bool registerIdentifier = false, bool bypassReserved = false)
public static SimpleName CreateSimpleName(SyntaxNode node, string name, bool registerIdentifier = false, bool bypassReserved = false)
{
if (RESERVED_IDENTIFIERS.Contains(name) && !bypassReserved)
Logger.UnsupportedError(node, $"Using '{name}' as an identifier", useIs: true, useYet: false);

return new IdentifierName(registerIdentifier ? FixIdentifierNameText(node, name, registerIdentifier) : name);

var text = registerIdentifier ? FixIdentifierNameText(node, name, registerIdentifier) : name;
return name.Contains('<') && name.Contains('>')
? new GenericName(text.Split('<').First(), Utility.ExtractTypeArguments(text))
: new IdentifierName(text);
}

// TODO: reference the correct identifier names
// TODO: per-scope duplicate handling
public static string FixIdentifierNameText(SyntaxNode node, string name, bool registerIdentifier = false)
{
_identifierDeclarations.TryAdd(node.SyntaxTree.FilePath, []);
Expand Down Expand Up @@ -297,35 +364,21 @@ public static string FixIdentifierNameText(SyntaxNode node, string name, bool re
else if (arrayMatch.Success)
return new ArrayType(CreateTypeRef(arrayMatch.Value.Trim())!);

return new(mappedType, rawPath: true);
return new TypeRef(mappedType, rawPath: true);
}

public static TypeRef? CreateTypeRef(TypeSyntax? type)
{
return CreateTypeRef(type?.ToString());
}
public static TypeRef? CreateTypeRef(TypeSyntax? type) => CreateTypeRef(type?.ToString());

public static Literal Vararg()
{
return new Literal("...");
}
public static Literal Vararg() => new("...");

public static Literal False()
{
return new Literal("false");
}
public static Literal False() => new("false");

public static Literal True()
{
return new Literal("true");
}
public static Literal True() => new("true");

public static Literal Nil()
{
return new Literal("nil");
}
public static Literal Nil() => new("nil");

public static TypeRef AnyType() =>
new TypeRef("any");
public static TypeRef AnyType() => new("any");


}
}
Loading

0 comments on commit 858cadc

Please sign in to comment.