2
0
Эх сурвалжийг харах

Add: TableMethodDeclarationStatementNode

AnnulusGames 1 жил өмнө
parent
commit
3acd777258

+ 51 - 5
src/Lua/CodeAnalysis/Compilation/LuaCompiler.cs

@@ -545,7 +545,7 @@ public sealed class LuaCompiler : ISyntaxNodeVisitor<ScopeCompilationContext, bo
     // function declaration
     public bool VisitFunctionDeclarationExpressionNode(FunctionDeclarationExpressionNode node, ScopeCompilationContext context)
     {
-        var funcIndex = CompileFunctionProto(ReadOnlyMemory<char>.Empty, context, node.ParameterNodes, node.Nodes, node.HasVariableArguments);
+        var funcIndex = CompileFunctionProto(ReadOnlyMemory<char>.Empty, context, node.ParameterNodes, node.Nodes, node.HasVariableArguments, false);
 
         // push closure instruction
         context.PushInstruction(Instruction.Closure(context.StackPosition, funcIndex), node.Position, true);
@@ -562,7 +562,7 @@ public sealed class LuaCompiler : ISyntaxNodeVisitor<ScopeCompilationContext, bo
         });
 
         // compile function
-        var funcIndex = CompileFunctionProto(node.Name, context, node.ParameterNodes, node.Nodes, node.HasVariableArguments);
+        var funcIndex = CompileFunctionProto(node.Name, context, node.ParameterNodes, node.Nodes, node.HasVariableArguments, false);
 
         // push closure instruction
         context.PushInstruction(Instruction.Closure(context.StackPosition, funcIndex), node.Position, true);
@@ -572,7 +572,7 @@ public sealed class LuaCompiler : ISyntaxNodeVisitor<ScopeCompilationContext, bo
 
     public bool VisitFunctionDeclarationStatementNode(FunctionDeclarationStatementNode node, ScopeCompilationContext context)
     {
-        var funcIndex = CompileFunctionProto(node.Name, context, node.ParameterNodes, node.Nodes, node.HasVariableArguments);
+        var funcIndex = CompileFunctionProto(node.Name, context, node.ParameterNodes, node.Nodes, node.HasVariableArguments, false);
 
         // add closure
         var index = context.Function.GetConstantIndex(node.Name.ToString());
@@ -586,20 +586,61 @@ public sealed class LuaCompiler : ISyntaxNodeVisitor<ScopeCompilationContext, bo
         return true;
     }
 
-    int CompileFunctionProto(ReadOnlyMemory<char> functionName, ScopeCompilationContext context, IdentifierNode[] parameters, SyntaxNode[] statements, bool hasVarArg)
+    public bool VisitTableMethodDeclarationStatementNode(TableMethodDeclarationStatementNode node, ScopeCompilationContext context)
+    {
+        var funcIdentifier = node.MemberPath[^1];
+        var funcIndex = CompileFunctionProto(funcIdentifier.Name, context, node.ParameterNodes, node.Nodes, node.HasVariableArguments, node.HasSelfParameter);
+
+        // add closure
+        var index = context.Function.GetConstantIndex(funcIdentifier.Name.ToString());
+
+        var r = context.StackPosition;
+
+        // assign global variable
+        var first = node.MemberPath[0];
+        var tableIndex = LoadIdentifier(first.Name, context, first.Position, true);
+        
+        for (int i = 1; i < node.MemberPath.Length - 1; i++)
+        {
+            var member = node.MemberPath[i];
+            var constant = context.Function.GetConstantIndex(member.Name.ToString());
+            context.PushInstruction(Instruction.GetTable(context.StackPosition, tableIndex, (ushort)(constant + 256)), member.Position, true);
+            tableIndex = context.StackTopPosition;
+        }
+
+        // push closure instruction
+        var closureIndex = context.StackPosition;
+        context.PushInstruction(Instruction.Closure(closureIndex, funcIndex), node.Position, true);
+
+        // set table
+        context.PushInstruction(Instruction.SetTable(tableIndex, (ushort)(index + 256), closureIndex), funcIdentifier.Position);
+
+        context.StackPosition = r;
+        return true;
+    }
+
+    int CompileFunctionProto(ReadOnlyMemory<char> functionName, ScopeCompilationContext context, IdentifierNode[] parameters, SyntaxNode[] statements, bool hasVarArg, bool hasSelfParameter)
     {
         using var funcContext = context.CreateChildFunction();
         funcContext.ChunkName = functionName.ToString();
         funcContext.ParameterCount = parameters.Length;
         funcContext.HasVariableArguments = hasVarArg;
 
+        if (hasSelfParameter)
+        {
+            funcContext.Scope.AddLocalVariable("self".AsMemory(), new()
+            {
+                RegisterIndex = 0,
+            });
+        }
+
         // add arguments
         for (int i = 0; i < parameters.Length; i++)
         {
             var parameter = parameters[i];
             funcContext.Scope.AddLocalVariable(parameter.Name, new()
             {
-                RegisterIndex = (byte)i
+                RegisterIndex = (byte)(i + (hasSelfParameter ? 1 : 0)),
             });
         }
 
@@ -935,6 +976,11 @@ public sealed class LuaCompiler : ISyntaxNodeVisitor<ScopeCompilationContext, bo
             {
                 return variable.RegisterIndex;
             }
+            else if (p == variable.RegisterIndex)
+            {
+                context.StackPosition++;
+                return p;
+            }
             else
             {
                 context.PushInstruction(Instruction.Move(p, variable.RegisterIndex), sourcePosition, true);

+ 41 - 1
src/Lua/CodeAnalysis/Syntax/DisplayStringSyntaxVisitor.cs

@@ -1,5 +1,4 @@
 using System.Text;
-using Lua.CodeAnalysis.Syntax;
 using Lua.CodeAnalysis.Syntax.Nodes;
 
 namespace Lua.CodeAnalysis.Syntax;
@@ -193,6 +192,47 @@ public sealed class DisplayStringSyntaxVisitor : ISyntaxNodeVisitor<DisplayStrin
         return true;
     }
 
+    public bool VisitTableMethodDeclarationStatementNode(TableMethodDeclarationStatementNode node, Context context)
+    {
+        context.Append("function ");
+        
+        for (int i = 0; i < node.MemberPath.Length; i++)
+        {
+            context.Append(node.MemberPath[i].Name.ToString());
+
+            if (i == node.MemberPath.Length - 2 && node.HasSelfParameter)
+            {
+                context.Append(":");
+            }
+            else if (i != node.MemberPath.Length - 1)
+            {
+                context.Append(".");
+            }
+        }
+
+        context.Append("(");
+        AddStatementList(node.ParameterNodes, context);
+        if (node.HasVariableArguments)
+        {
+            if (node.ParameterNodes.Length > 0) context.Append(", ");
+            context.Append("...");
+        }
+        context.AppendLine(")");
+
+        using (context.BeginIndentScope())
+        {
+            foreach (var childNode in node.Nodes)
+            {
+                childNode.Accept(this, context);
+                context.AppendLine();
+            }
+        }
+
+        context.AppendLine("end");
+
+        return true;
+    }
+
     public bool VisitGenericForStatementNode(GenericForStatementNode node, Context context)
     {
         context.Append($"for ");

+ 1 - 0
src/Lua/CodeAnalysis/Syntax/ISyntaxNodeVisitor.cs

@@ -29,6 +29,7 @@ public interface ISyntaxNodeVisitor<TContext, TResult>
     TResult VisitNumericForStatementNode(NumericForStatementNode node, TContext context);
     TResult VisitGenericForStatementNode(GenericForStatementNode node, TContext context);
     TResult VisitTableConstructorExpressionNode(TableConstructorExpressionNode node, TContext context);
+    TResult VisitTableMethodDeclarationStatementNode(TableMethodDeclarationStatementNode node, TContext context);
     TResult VisitTableIndexerAccessExpressionNode(TableIndexerAccessExpressionNode node, TContext context);
     TResult VisitTableMemberAccessExpressionNode(TableMemberAccessExpressionNode node, TContext context);
     TResult VisitCallTableMethodExpressionNode(CallTableMethodExpressionNode node, TContext context);

+ 9 - 0
src/Lua/CodeAnalysis/Syntax/Nodes/TableMethodDeclarationStatementNode.cs

@@ -0,0 +1,9 @@
+namespace Lua.CodeAnalysis.Syntax.Nodes;
+
+public record TableMethodDeclarationStatementNode(IdentifierNode[] MemberPath, IdentifierNode[] ParameterNodes, StatementNode[] Nodes, bool HasVariableArguments, bool HasSelfParameter, SourcePosition Position) : StatementNode(Position)
+{
+    public override TResult Accept<TContext, TResult>(ISyntaxNodeVisitor<TContext, TResult> visitor, TContext context)
+    {
+        return visitor.VisitTableMethodDeclarationStatementNode(this, context);
+    }
+}

+ 108 - 78
src/Lua/CodeAnalysis/Syntax/Parser.cs

@@ -106,7 +106,11 @@ public ref struct Parser
                     // local function
                     if (enumerator.Current.Type is SyntaxTokenType.Function)
                     {
-                        return ParseLocalFunctionDeclarationStatement(ref enumerator);
+                        // skip 'function' keyword
+                        CheckCurrentAndSkip(ref enumerator, SyntaxTokenType.Function, out var functionToken);
+                        enumerator.SkipEoL();
+
+                        return ParseLocalFunctionDeclarationStatement(ref enumerator, functionToken);
                     }
 
                     CheckCurrent(ref enumerator, SyntaxTokenType.Identifier);
@@ -124,7 +128,20 @@ public ref struct Parser
                 }
                 break;
             case SyntaxTokenType.Function:
-                return ParseFunctionDeclarationStatement(ref enumerator);
+                {
+                    // skip 'function' keyword
+                    CheckCurrentAndSkip(ref enumerator, SyntaxTokenType.Function, out var functionToken);
+                    enumerator.SkipEoL();
+
+                    if (enumerator.GetNext(true).Type is SyntaxTokenType.Dot or SyntaxTokenType.Colon)
+                    {
+                        return ParseTableMethodDeclarationStatement(ref enumerator, functionToken);
+                    }
+                    else
+                    {
+                        return ParseFunctionDeclarationStatement(ref enumerator, functionToken);
+                    }
+                }
         }
 
         LuaParseException.UnexpectedToken(ChunkName, enumerator.Current.Position, enumerator.Current);
@@ -148,19 +165,10 @@ public ref struct Parser
         CheckCurrent(ref enumerator, SyntaxTokenType.Do);
         var doToken = enumerator.Current;
 
-        using var statements = new PooledList<StatementNode>(64);
-
         // parse statements
-        while (enumerator.MoveNext())
-        {
-            if (enumerator.Current.Type is SyntaxTokenType.End) break;
-            if (enumerator.Current.Type is SyntaxTokenType.EndOfLine or SyntaxTokenType.SemiColon) continue;
+        var statements = ParseStatementList(ref enumerator, SyntaxTokenType.End);
 
-            var node = ParseStatement(ref enumerator);
-            statements.Add(node);
-        }
-
-        return new DoStatementNode(statements.AsSpan().ToArray(), doToken.Position);
+        return new DoStatementNode(statements, doToken.Position);
     }
 
     GotoStatementNode ParseGotoStatement(ref SyntaxTokenEnumerator enumerator)
@@ -344,19 +352,10 @@ public ref struct Parser
         // skip 'do' keyword
         CheckCurrentAndSkip(ref enumerator, SyntaxTokenType.Do, out _);
 
-        using var statements = new PooledList<StatementNode>(64);
-
         // parse statements
-        while (enumerator.MoveNext())
-        {
-            if (enumerator.Current.Type is SyntaxTokenType.End) break;
-            if (enumerator.Current.Type is SyntaxTokenType.EndOfLine or SyntaxTokenType.SemiColon) continue;
+        var statements = ParseStatementList(ref enumerator, SyntaxTokenType.End);
 
-            var node = ParseStatement(ref enumerator);
-            statements.Add(node);
-        }
-
-        return new WhileStatementNode(condition, statements.AsSpan().ToArray(), whileToken.Position);
+        return new WhileStatementNode(condition, statements, whileToken.Position);
     }
 
     RepeatStatementNode ParseRepeatStatement(ref SyntaxTokenEnumerator enumerator)
@@ -364,17 +363,8 @@ public ref struct Parser
         // skip 'repeat' keyword
         CheckCurrentAndSkip(ref enumerator, SyntaxTokenType.Repeat, out var repeatToken);
 
-        using var statements = new PooledList<StatementNode>(64);
-
         // parse statements
-        while (enumerator.MoveNext())
-        {
-            if (enumerator.Current.Type is SyntaxTokenType.Until) break;
-            if (enumerator.Current.Type is SyntaxTokenType.EndOfLine or SyntaxTokenType.SemiColon) continue;
-
-            var node = ParseStatement(ref enumerator);
-            statements.Add(node);
-        }
+        var statements = ParseStatementList(ref enumerator, SyntaxTokenType.Until);
 
         // skip 'until keyword'
         CheckCurrentAndSkip(ref enumerator, SyntaxTokenType.Until, out _);
@@ -383,7 +373,7 @@ public ref struct Parser
         // parse condition
         var condition = ParseExpression(ref enumerator, GetPrecedence(enumerator.Current.Type));
 
-        return new RepeatStatementNode(condition, statements.AsSpan().ToArray(), repeatToken.Position);
+        return new RepeatStatementNode(condition, statements, repeatToken.Position);
     }
 
     NumericForStatementNode ParseNumericForStatement(ref SyntaxTokenEnumerator enumerator, SyntaxToken forToken)
@@ -428,19 +418,10 @@ public ref struct Parser
         // skip 'do' keyword
         CheckCurrent(ref enumerator, SyntaxTokenType.Do);
 
-        using var statements = new PooledList<StatementNode>(64);
-
         // parse statements
-        while (enumerator.MoveNext())
-        {
-            if (enumerator.Current.Type is SyntaxTokenType.End) break;
-            if (enumerator.Current.Type is SyntaxTokenType.EndOfLine or SyntaxTokenType.SemiColon) continue;
-
-            var node = ParseStatement(ref enumerator);
-            statements.Add(node);
-        }
+        var statements = ParseStatementList(ref enumerator, SyntaxTokenType.End);
 
-        return new NumericForStatementNode(varName, initialValueNode, limitNode, stepNode, statements.AsSpan().ToArray(), forToken.Position);
+        return new NumericForStatementNode(varName, initialValueNode, limitNode, stepNode, statements, forToken.Position);
     }
 
     GenericForStatementNode ParseGenericForStatement(ref SyntaxTokenEnumerator enumerator, SyntaxToken forToken)
@@ -459,39 +440,26 @@ public ref struct Parser
         // skip 'do' keyword
         CheckCurrent(ref enumerator, SyntaxTokenType.Do);
 
-        using var statements = new PooledList<StatementNode>(64);
-
         // parse statements
-        while (enumerator.MoveNext())
-        {
-            if (enumerator.Current.Type is SyntaxTokenType.End) break;
-            if (enumerator.Current.Type is SyntaxTokenType.EndOfLine or SyntaxTokenType.SemiColon) continue;
+        var statements = ParseStatementList(ref enumerator, SyntaxTokenType.End);
 
-            var node = ParseStatement(ref enumerator);
-            statements.Add(node);
-        }
-
-        return new GenericForStatementNode(identifiers, expression, statements.AsSpan().ToArray(), forToken.Position);
+        return new GenericForStatementNode(identifiers, expression, statements, forToken.Position);
     }
 
-    FunctionDeclarationStatementNode ParseFunctionDeclarationStatement(ref SyntaxTokenEnumerator enumerator)
+    FunctionDeclarationStatementNode ParseFunctionDeclarationStatement(ref SyntaxTokenEnumerator enumerator, SyntaxToken functionToken)
     {
-        var (Name, Identifiers, Statements, HasVariableArgments, FunctionToken) = ParseFunctionDeclarationCore(ref enumerator, false);
-        return new FunctionDeclarationStatementNode(Name, Identifiers, Statements, HasVariableArgments, FunctionToken.Position);
+        var (Name, Identifiers, Statements, HasVariableArgments) = ParseFunctionDeclarationCore(ref enumerator, false);
+        return new FunctionDeclarationStatementNode(Name, Identifiers, Statements, HasVariableArgments, functionToken.Position);
     }
 
-    LocalFunctionDeclarationStatementNode ParseLocalFunctionDeclarationStatement(ref SyntaxTokenEnumerator enumerator)
+    LocalFunctionDeclarationStatementNode ParseLocalFunctionDeclarationStatement(ref SyntaxTokenEnumerator enumerator, SyntaxToken functionToken)
     {
-        var (Name, Identifiers, Statements, HasVariableArgments, FunctionToken) = ParseFunctionDeclarationCore(ref enumerator, false);
-        return new LocalFunctionDeclarationStatementNode(Name, Identifiers, Statements, HasVariableArgments, FunctionToken.Position);
+        var (Name, Identifiers, Statements, HasVariableArgments) = ParseFunctionDeclarationCore(ref enumerator, false);
+        return new LocalFunctionDeclarationStatementNode(Name, Identifiers, Statements, HasVariableArgments, functionToken.Position);
     }
 
-    (ReadOnlyMemory<char> Name, IdentifierNode[] Identifiers, StatementNode[] Statements, bool HasVariableArgments, SyntaxToken FunctionToken) ParseFunctionDeclarationCore(ref SyntaxTokenEnumerator enumerator, bool isAnonymous)
+    (ReadOnlyMemory<char> Name, IdentifierNode[] Identifiers, StatementNode[] Statements, bool HasVariableArgments) ParseFunctionDeclarationCore(ref SyntaxTokenEnumerator enumerator, bool isAnonymous)
     {
-        // skip 'function' keyword
-        CheckCurrentAndSkip(ref enumerator, SyntaxTokenType.Function, out var functionToken);
-        enumerator.SkipEoL();
-
         ReadOnlyMemory<char> name;
 
         if (isAnonymous)
@@ -524,19 +492,60 @@ public ref struct Parser
         // skip ')'
         CheckCurrent(ref enumerator, SyntaxTokenType.RParen);
 
-        using var statements = new PooledList<StatementNode>(64);
-
         // parse statements
-        while (enumerator.MoveNext())
+        var statements = ParseStatementList(ref enumerator, SyntaxTokenType.End);
+
+        return (name, identifiers, statements, hasVarArg);
+    }
+
+    TableMethodDeclarationStatementNode ParseTableMethodDeclarationStatement(ref SyntaxTokenEnumerator enumerator, SyntaxToken functionToken)
+    {
+        using var names = new PooledList<IdentifierNode>(32);
+        var hasSelfParameter = false;
+
+        while (true)
         {
-            if (enumerator.Current.Type is SyntaxTokenType.End) break;
-            if (enumerator.Current.Type is SyntaxTokenType.EndOfLine or SyntaxTokenType.SemiColon) continue;
+            CheckCurrent(ref enumerator, SyntaxTokenType.Identifier);
+            names.Add(new IdentifierNode(enumerator.Current.Text, enumerator.Current.Position));
+            MoveNextWithValidation(ref enumerator);
+            enumerator.SkipEoL();
 
-            var node = ParseStatement(ref enumerator);
-            statements.Add(node);
+            if (enumerator.Current.Type is SyntaxTokenType.Dot or SyntaxTokenType.Colon)
+            {
+                if (hasSelfParameter)
+                {
+                    LuaParseException.UnexpectedToken(ChunkName, enumerator.Current.Position, enumerator.Current);
+                }
+                hasSelfParameter = enumerator.Current.Type is SyntaxTokenType.Colon;
+
+                MoveNextWithValidation(ref enumerator);
+                enumerator.SkipEoL();
+            }
+            else if (enumerator.Current.Type is SyntaxTokenType.LParen)
+            {
+                // skip '('
+                MoveNextWithValidation(ref enumerator);
+                enumerator.SkipEoL();
+                break;
+            }
         }
 
-        return (name, identifiers, statements.AsSpan().ToArray(), hasVarArg, functionToken);
+        // parse parameters
+        var identifiers = enumerator.Current.Type is SyntaxTokenType.Identifier
+            ? ParseIdentifierList(ref enumerator)
+            : [];
+
+        // check variable arguments
+        var hasVarArg = enumerator.Current.Type is SyntaxTokenType.VarArg;
+        if (hasVarArg) enumerator.MoveNext();
+
+        // skip ')'
+        CheckCurrent(ref enumerator, SyntaxTokenType.RParen);
+
+        // parse statements
+        var statements = ParseStatementList(ref enumerator, SyntaxTokenType.End);
+
+        return new TableMethodDeclarationStatementNode(names.AsSpan().ToArray(), identifiers, statements, hasVarArg, hasSelfParameter, functionToken.Position);
     }
 
     bool TryParseExpression(ref SyntaxTokenEnumerator enumerator, OperatorPrecedence precedence, [NotNullWhen(true)] out ExpressionNode? result)
@@ -836,8 +845,12 @@ public ref struct Parser
 
     FunctionDeclarationExpressionNode ParseFunctionDeclarationExpression(ref SyntaxTokenEnumerator enumerator)
     {
-        var (_, Identifiers, Statements, HasVariableArgments, FunctionToken) = ParseFunctionDeclarationCore(ref enumerator, true);
-        return new FunctionDeclarationExpressionNode(Identifiers, Statements, HasVariableArgments, FunctionToken.Position);
+        // skip 'function' keyword
+        CheckCurrentAndSkip(ref enumerator, SyntaxTokenType.Function, out var functionToken);
+        enumerator.SkipEoL();
+        
+        var (_, Identifiers, Statements, HasVariableArgments) = ParseFunctionDeclarationCore(ref enumerator, true);
+        return new FunctionDeclarationExpressionNode(Identifiers, Statements, HasVariableArgments, functionToken.Position);
     }
 
     ExpressionNode[] ParseCallFunctionArguments(ref SyntaxTokenEnumerator enumerator)
@@ -920,6 +933,23 @@ public ref struct Parser
         return buffer.AsSpan().ToArray();
     }
 
+    StatementNode[] ParseStatementList(ref SyntaxTokenEnumerator enumerator, SyntaxTokenType endToken)
+    {
+        using var statements = new PooledList<StatementNode>(64);
+
+        // parse statements
+        while (enumerator.MoveNext())
+        {
+            if (enumerator.Current.Type == endToken) break;
+            if (enumerator.Current.Type is SyntaxTokenType.EndOfLine or SyntaxTokenType.SemiColon) continue;
+
+            var node = ParseStatement(ref enumerator);
+            statements.Add(node);
+        }
+
+        return statements.AsSpan().ToArray();
+    }
+
     void CheckCurrentAndSkip(ref SyntaxTokenEnumerator enumerator, SyntaxTokenType expectedToken, out SyntaxToken token)
     {
         CheckCurrent(ref enumerator, expectedToken);