diff --git a/src/GDShrapt.Reader.Tests/AssertHelper.cs b/src/GDShrapt.Reader.Tests/AssertHelper.cs index a00a5bc..3608043 100644 --- a/src/GDShrapt.Reader.Tests/AssertHelper.cs +++ b/src/GDShrapt.Reader.Tests/AssertHelper.cs @@ -35,7 +35,7 @@ internal static void CompareCodeStrings(string s1, string s2) } #endif - Assert.AreEqual(s1, s2); + Assert.AreEqual(s1, s2, "The code strings are not same"); } internal static void NoInvalidTokens(GDNode node) @@ -47,7 +47,7 @@ internal static void NoInvalidTokens(GDNode node) for (int i = 0; i < invalidTokens.Length; i++) messageBuilder.AppendLine((i+1) + ". " + invalidTokens[i]); - Assert.AreEqual(0, invalidTokens.Length, messageBuilder.ToString()); + Assert.AreEqual(0, invalidTokens.Length, messageBuilder.ToString(), "There are invalid tokens in the code"); } } } diff --git a/src/GDShrapt.Reader.Tests/ParsingTests.cs b/src/GDShrapt.Reader.Tests/ParsingTests.cs index a2200a5..e679b73 100644 --- a/src/GDShrapt.Reader.Tests/ParsingTests.cs +++ b/src/GDShrapt.Reader.Tests/ParsingTests.cs @@ -2215,5 +2215,93 @@ public void ParseInlinedLambda() AssertHelper.CompareCodeStrings(code, @statementsList.ToString()); AssertHelper.NoInvalidTokens(@statementsList); } + + [TestMethod] + public void RpcAttributeTest() + { + var reader = new GDScriptReader(); + + var code = @"@rpc +func fn(): pass + +@rpc(""any_peer"", ""unreliable_ordered"") +func fn_update_pos(): pass + +@rpc(""authority"", ""call_remote"", ""unreliable"", 0) # Equivalent to @rpc +func fn_default(): pass"; + + var @class = reader.ParseFileContent(code); + + var attributes = @class.AllNodes.OfType().ToArray(); + + Assert.AreEqual(3, attributes.Length); + + Assert.AreEqual("@rpc", attributes[0].ToString()); + Assert.AreEqual("@rpc(\"any_peer\", \"unreliable_ordered\")", attributes[1].ToString()); + Assert.AreEqual("@rpc(\"authority\", \"call_remote\", \"unreliable\", 0)", attributes[2].ToString()); + + AssertHelper.CompareCodeStrings(code, @class.ToString()); + AssertHelper.NoInvalidTokens(@class); + } + + [TestMethod] + public void NewClassAttributesTest() + { + var reader = new GDScriptReader(); + + var code = @"@static_unload +@tool +extends Node"; + + var @class = reader.ParseFileContent(code); + var attributes = @class.AllNodes.OfType().ToArray(); + Assert.AreEqual(3, attributes.Length); + + Assert.AreEqual("@static_unload", attributes[0].ToString()); + Assert.AreEqual("@tool", attributes[1].ToString()); + Assert.AreEqual("extends Node", attributes[2].ToString()); + + AssertHelper.CompareCodeStrings(code, @class.ToString()); + AssertHelper.NoInvalidTokens(@class); + } + + [TestMethod] + public void StatementAtributesTest() + { + var reader = new GDScriptReader(); + + var code = @"func test(): + print(""hello"") + return + @warning_ignore(""unreachable_code"") + print(""unreachable"")"; + + var @class = reader.ParseFileContent(code); + + var attributes = @class.AllNodes.OfType().ToArray(); + Assert.AreEqual(1, attributes.Length); + + AssertHelper.CompareCodeStrings(code, @class.ToString()); + AssertHelper.NoInvalidTokens(@class); + } + + [TestMethod] + public void StatementAtributesTest2() + { + var reader = new GDScriptReader(); + + var code = @"func test(): + print(""hello"") + return + @warning_ignore(""unreachable_code"") print(""unreachable"")"; + + var @class = reader.ParseFileContent(code); + + var attributes = @class.AllNodes.OfType().ToArray(); + Assert.AreEqual(1, attributes.Length); + + AssertHelper.CompareCodeStrings(code, @class.ToString()); + AssertHelper.NoInvalidTokens(@class); + } } } \ No newline at end of file diff --git a/src/GDShrapt.Reader/Atributes/GDAttribute.cs b/src/GDShrapt.Reader/Atributes/GDAttribute.cs index 7c78e1d..89b0cd1 100644 --- a/src/GDShrapt.Reader/Atributes/GDAttribute.cs +++ b/src/GDShrapt.Reader/Atributes/GDAttribute.cs @@ -48,6 +48,8 @@ public enum State } readonly GDTokensForm _form; + readonly bool _parseWithoutBrackets; + public override GDTokensForm Form => _form; public GDTokensForm TypedForm => _form; @@ -56,6 +58,12 @@ public GDAttribute() _form = new GDTokensForm(this); } + internal GDAttribute(bool parseWithoutBrackets) + : this() + { + _parseWithoutBrackets = parseWithoutBrackets; + } + public override GDNode CreateEmptyInstance() { return new GDAttribute(); @@ -167,7 +175,7 @@ void ITokenSkipReceiver.HandleReceivedTokenSkip() { if (_form.IsOrLowerState(State.OpenBracket)) { - _form.State = State.Completed; + _form.State = _parseWithoutBrackets ? State.Parameters : State.Completed; return; } diff --git a/src/GDShrapt.Reader/Basics/GDNode.cs b/src/GDShrapt.Reader/Basics/GDNode.cs index fa702d6..addd2b9 100644 --- a/src/GDShrapt.Reader/Basics/GDNode.cs +++ b/src/GDShrapt.Reader/Basics/GDNode.cs @@ -389,6 +389,11 @@ void ITokenReceiver.HandleReceivedToken(GDSpace token) Form.AddBeforeActiveToken(token); } + void ITokenReceiver.HandleReceivedToken(GDAttribute token) + { + Form.AddBeforeActiveToken(token); + } + void ITokenReceiver.HandleReceivedToken(GDSpace token) { Form.AddBeforeActiveToken(token); diff --git a/src/GDShrapt.Reader/Basics/Receiving/ITokenReceiver.cs b/src/GDShrapt.Reader/Basics/Receiving/ITokenReceiver.cs index 4fca636..8974eb7 100644 --- a/src/GDShrapt.Reader/Basics/Receiving/ITokenReceiver.cs +++ b/src/GDShrapt.Reader/Basics/Receiving/ITokenReceiver.cs @@ -11,6 +11,7 @@ public interface ITokenReceiver { void HandleReceivedToken(GDComment token); void HandleReceivedToken(GDSpace token); + void HandleReceivedToken(GDAttribute token); void HandleReceivedToken(GDInvalidToken token); void HandleReceivedToken(GDMultiLineSplitToken token); } diff --git a/src/GDShrapt.Reader/Declarations/GDClassMemberAttributeDeclaration.cs b/src/GDShrapt.Reader/Declarations/GDClassMemberAttributeDeclaration.cs index 9bd7be8..581eb56 100644 --- a/src/GDShrapt.Reader/Declarations/GDClassMemberAttributeDeclaration.cs +++ b/src/GDShrapt.Reader/Declarations/GDClassMemberAttributeDeclaration.cs @@ -16,13 +16,16 @@ public enum State } readonly GDTokensForm _form; + readonly bool _parseWithoutBrackets; + public override GDTokensForm Form => _form; public GDTokensForm TypedForm => _form; - internal GDClassMemberAttributeDeclaration(int intendation) + internal GDClassMemberAttributeDeclaration(int intendation, bool parseWithoutBrackets) : base(intendation) { _form = new GDTokensForm(this); + _parseWithoutBrackets = parseWithoutBrackets; } public GDClassMemberAttributeDeclaration() @@ -57,7 +60,7 @@ internal override void HandleChar(char c, GDReadingState state) return; } - Attribute = state.PushAndPass(new GDAttribute(), c); + Attribute = state.PushAndPass(new GDAttribute(_parseWithoutBrackets), c); _form.State = State.Completed; break; default: diff --git a/src/GDShrapt.Reader/GDReceiver.cs b/src/GDShrapt.Reader/GDReceiver.cs index 08b4450..bb125d2 100644 --- a/src/GDShrapt.Reader/GDReceiver.cs +++ b/src/GDShrapt.Reader/GDReceiver.cs @@ -20,6 +20,11 @@ public void HandleReceivedToken(GDComment token) Tokens.Add(token); } + public void HandleReceivedToken(GDAttribute token) + { + Tokens.Add(token); + } + public void HandleReceivedToken(GDNewLine token) { Tokens.Add(token); diff --git a/src/GDShrapt.Reader/GDShrapt.Reader.csproj b/src/GDShrapt.Reader/GDShrapt.Reader.csproj index dd5e1f3..06dc34f 100644 --- a/src/GDShrapt.Reader/GDShrapt.Reader.csproj +++ b/src/GDShrapt.Reader/GDShrapt.Reader.csproj @@ -16,8 +16,8 @@ Usage: Just create GDScriptReader instance and call methods from it.https://github.com/elamaunt/GDShrapt git GDShrapt GDScript reader parser codegeneration Godot lexical analyzer - 3.2.1 - 3.2.1 + 4.0.0 + 4.0.0 true diff --git a/src/GDShrapt.Reader/Resolvers/GDClassMembersResolver.cs b/src/GDShrapt.Reader/Resolvers/GDClassMembersResolver.cs index 6f4fb56..51ef36c 100644 --- a/src/GDShrapt.Reader/Resolvers/GDClassMembersResolver.cs +++ b/src/GDShrapt.Reader/Resolvers/GDClassMembersResolver.cs @@ -129,11 +129,10 @@ private void Complete(GDReadingState state, string sequence) if (sequence[0] == '@') { - Owner.HandleReceivedToken(state.Push(new GDClassMemberAttributeDeclaration(LineIntendationThreshold))); + Owner.HandleReceivedToken(state.Push(new GDClassMemberAttributeDeclaration(LineIntendationThreshold, false))); - if (sequence != null) - for (int i = 0; i < sequence.Length; i++) - state.PassChar(sequence[i]); + for (int i = 0; i < sequence.Length; i++) + state.PassChar(sequence[i]); return; } @@ -190,6 +189,19 @@ void HandleStaticIfMet(ITokenReceiver spaceReceiver, Action push, bool switch (sequence) { + case "tool": + case "extends": + case "class_name": + { + // Invalid order of the atributes found. + // Handling them as a ClassMemberAttribute instances. + // TODO: rework to GDClassCustomAttribute. + Owner.HandleReceivedToken(state.Push(new GDClassMemberAttributeDeclaration(LineIntendationThreshold, true))); + + for (int i = 0; i < sequence.Length; i++) + state.PassChar(sequence[i]); + } + break; case "signal": { var m = new GDSignalDeclaration(LineIntendationThreshold); diff --git a/src/GDShrapt.Reader/Resolvers/GDContentResolver.cs b/src/GDShrapt.Reader/Resolvers/GDContentResolver.cs index 60d5202..b362ab4 100644 --- a/src/GDShrapt.Reader/Resolvers/GDContentResolver.cs +++ b/src/GDShrapt.Reader/Resolvers/GDContentResolver.cs @@ -53,7 +53,9 @@ private void HandleSequence(string seq, GDReadingState state) case "signal": case "const": case "class": + case "@static_unload": case "static": + case "@onready": case "onready": Owner.HandleReceivedToken(state.Push(new GDInnerClassDeclaration(CalculatedIntendation))); break; @@ -71,13 +73,17 @@ private void HandleSequence(string seq, GDReadingState state) case "extends": case "class_name": case "tool": + case "@tool": case "var": case "func": case "const": case "signal": case "export": + case "@export": case "class": + case "@static_unload": case "static": + case "@onready": case "onready": Owner.HandleReceivedToken(state.Push(new GDClassDeclaration())); break; diff --git a/src/GDShrapt.Reader/Resolvers/GDExpressionResolver.cs b/src/GDShrapt.Reader/Resolvers/GDExpressionResolver.cs index f5befaf..cf90518 100644 --- a/src/GDShrapt.Reader/Resolvers/GDExpressionResolver.cs +++ b/src/GDShrapt.Reader/Resolvers/GDExpressionResolver.cs @@ -518,6 +518,11 @@ public void HandleReceivedToken(GDComment token) Owner.HandleReceivedToken(token); } + public void HandleReceivedToken(GDAttribute token) + { + Owner.HandleReceivedToken(token); + } + public void HandleReceivedToken(GDSpace token) { Owner.HandleReceivedToken(token); diff --git a/src/GDShrapt.Reader/Resolvers/GDStatementsResolver.cs b/src/GDShrapt.Reader/Resolvers/GDStatementsResolver.cs index 9899729..f5a0dbf 100644 --- a/src/GDShrapt.Reader/Resolvers/GDStatementsResolver.cs +++ b/src/GDShrapt.Reader/Resolvers/GDStatementsResolver.cs @@ -1,4 +1,5 @@ -using System.Text; +using System; +using System.Text; namespace GDShrapt.Reader { @@ -50,6 +51,14 @@ internal override void HandleCharAfterIntendation(char c, GDReadingState state) return; } + if (c == '@') + { + SendIntendationTokensToOwner(); + Owner.HandleReceivedToken(state.Push(new GDAttribute())); + state.PassChar(c); + return; + } + if (char.IsLetter(c)) { _sequenceBuilder.Append(c); diff --git a/src/GDShrapt.Reader/Resolvers/GDTypeResolver.cs b/src/GDShrapt.Reader/Resolvers/GDTypeResolver.cs index 12ff453..d7ae8e9 100644 --- a/src/GDShrapt.Reader/Resolvers/GDTypeResolver.cs +++ b/src/GDShrapt.Reader/Resolvers/GDTypeResolver.cs @@ -189,6 +189,11 @@ void ITokenReceiver.HandleReceivedToken(GDStringNode token) _string = token; } + void ITokenReceiver.HandleReceivedToken(GDAttribute token) + { + throw new GDInvalidStateException(); + } + void ITokenSkipReceiver.HandleReceivedTokenSkip() { throw new GDInvalidStateException();