diff --git a/src/GDShrapt.Reader.Tests/AssertHelper.cs b/src/GDShrapt.Reader.Tests/AssertHelper.cs index 3608043..476ae6c 100644 --- a/src/GDShrapt.Reader.Tests/AssertHelper.cs +++ b/src/GDShrapt.Reader.Tests/AssertHelper.cs @@ -45,7 +45,10 @@ internal static void NoInvalidTokens(GDNode node) messageBuilder.AppendLine(); for (int i = 0; i < invalidTokens.Length; i++) - messageBuilder.AppendLine((i+1) + ". " + invalidTokens[i]); + { + var token = invalidTokens[i]; + messageBuilder.AppendLine($"{token.StartLine}.{token.StartColumn}: " + token); + } 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 3d0b7e9..17384f1 100644 --- a/src/GDShrapt.Reader.Tests/ParsingTests.cs +++ b/src/GDShrapt.Reader.Tests/ParsingTests.cs @@ -1895,17 +1895,17 @@ func _init( } match b: - 0: + 0: - pass - var t: - for i in range(10) : + pass + var t: + for i in range(10) : - var c = b + a + i + t + var c = b + a + i + t - new_method() + new_method() - print(c) + print(c) print(""done"") @@ -2562,7 +2562,7 @@ public void StatementAtributesTest2() } [TestMethod] - public void ParseGetNodeNewSyntax() + public void ParseGetUniqueNodeTest() { var reader = new GDScriptReader(); @@ -2788,5 +2788,68 @@ class_name MyClass extends Node AssertHelper.CompareCodeStrings(code, @class.ToString()); AssertHelper.NoInvalidTokens(@class); } + + [TestMethod] + public void ParseGetNodeWithOnreadyTest() + { + var reader = new GDScriptReader(); + + var code = @"@onready var sprite2d = $Sprite2D +@onready var animation_player = $ShieldBar/AnimationPlayer"; + + var @class = reader.ParseFileContent(code); + + AssertHelper.CompareCodeStrings(code, @class.ToString()); + AssertHelper.NoInvalidTokens(@class); + } + + [TestMethod] + public void ParseDifferentIntendationsTest() + { + var reader = new GDScriptReader(); + + var code = @"var prop4: + set=_setter, + get = _getter + +func _setter(x): + var f = func(): + pass + + var f2 = func(): + var x2 = 10 + var f3 = func(): + pass + pass + var f5 = func(): + pass + pass + + +func _getter(): + return 0 + +func _init(x): + pass + +# Called when the node enters the scene tree for the first time. +func _ready(): + pass # Replace with function body. + + +# Called every frame. 'delta' is the elapsed time since the previous frame. +func _process(delta): + pass +"; + + var @class = reader.ParseFileContent(code); + + Assert.AreEqual(1, @class.Variables.Count()); + Assert.AreEqual(4, @class.AllNodes.Where(x => x is GDMethodExpression).Count()); + Assert.AreEqual(5, @class.Methods.Count()); + + AssertHelper.CompareCodeStrings(code, @class.ToString()); + AssertHelper.NoInvalidTokens(@class); + } } } \ No newline at end of file diff --git a/src/GDShrapt.Reader/Basics/GDSyntaxToken.cs b/src/GDShrapt.Reader/Basics/GDSyntaxToken.cs index 3c95d7a..dfdd472 100644 --- a/src/GDShrapt.Reader/Basics/GDSyntaxToken.cs +++ b/src/GDShrapt.Reader/Basics/GDSyntaxToken.cs @@ -490,8 +490,6 @@ public List ExtractAllMethodScopeVisibleDeclarationsFromParents(in while (true) { - node = node.Parent; - if (node == null || node is GDClassDeclaration || node is GDInnerClassDeclaration) break; @@ -500,6 +498,8 @@ public List ExtractAllMethodScopeVisibleDeclarationsFromParents(in foreach (var item in node.GetMethodScopeDeclarations(beforeLine)) results.Add(item); + + node = node.Parent; } return results; diff --git a/src/GDShrapt.Reader/Declarations/GDVariableDeclaration.cs b/src/GDShrapt.Reader/Declarations/GDVariableDeclaration.cs index 1f4558e..e39730a 100644 --- a/src/GDShrapt.Reader/Declarations/GDVariableDeclaration.cs +++ b/src/GDShrapt.Reader/Declarations/GDVariableDeclaration.cs @@ -161,9 +161,9 @@ internal override void HandleChar(char c, GDReadingState state) case State.Comma: state.PushAndPass(new GDSetGetAccessorsResolver(this, this, true, true, Intendation + 1), c); return; - //case State.SecondAccessorDeclarationNode: - // state.PushAndPass(new GDSetGetAccessorsResolver(this, this, Comma != null, false, Intendation + 1), c); - // break; + case State.SecondAccessorDeclarationNode: + state.PushAndPass(new GDSetGetAccessorsResolver(this, this, Comma != null, false, Intendation + 1), c); + break; default: this.HandleAsInvalidToken(c, state, x => x.IsNewLine()); break; @@ -184,11 +184,11 @@ internal override void HandleNewLineChar(GDReadingState state) return; } - /*if (_form.State == State.SecondAccessorDeclarationNode) + if (_form.State == State.SecondAccessorDeclarationNode) { state.PushAndPassNewLine(new GDSetGetAccessorsResolver(this, this, Comma != null, false, Intendation + 1)); return; - }*/ + } state.PopAndPassNewLine(); } diff --git a/src/GDShrapt.Reader/GDReadSettings.cs b/src/GDShrapt.Reader/GDReadSettings.cs index d48e8e9..e020669 100644 --- a/src/GDShrapt.Reader/GDReadSettings.cs +++ b/src/GDShrapt.Reader/GDReadSettings.cs @@ -3,10 +3,10 @@ public class GDReadSettings { /// - /// Read 4 spaces when intendation is calculated into tabs. - /// Default value is TRUE + /// Amount of spaces that equals a single tabulation + /// Default value is 4 /// - public bool ReadFourSpacesAsIntendation { get; set; } = true; + public int SingleTabSpacesCost { get; set; } = 4; public int ReadBufferSize { get; set; } = 1024; diff --git a/src/GDShrapt.Reader/GDReadingState.cs b/src/GDShrapt.Reader/GDReadingState.cs index ec47e34..4a1e181 100644 --- a/src/GDShrapt.Reader/GDReadingState.cs +++ b/src/GDShrapt.Reader/GDReadingState.cs @@ -18,6 +18,11 @@ internal class GDReadingState readonly Stack _readersStack = new Stack(64); GDReader CurrentReader => _readersStack.PeekOrDefault(); + /// + /// The first intendation in the code is used for the next ones as a pattern like in the Godot's editor. + /// + public int? IntendationInSpacesCount { get; set; } = null; + public GDReadingState(GDReadSettings settings) { Settings = settings; diff --git a/src/GDShrapt.Reader/GDShrapt.Reader.csproj b/src/GDShrapt.Reader/GDShrapt.Reader.csproj index a0006d9..a338ea8 100644 --- a/src/GDShrapt.Reader/GDShrapt.Reader.csproj +++ b/src/GDShrapt.Reader/GDShrapt.Reader.csproj @@ -3,7 +3,7 @@ netstandard2.0 true - 4.2.0-alpha + 4.3.0-alpha elamaunt GDShrapt GDShrapt.Reader is .Net library and object-oriented one-pass parser of GDScript. It can build a lexical tree of GDScript code or generate a new code from scratch. @@ -16,8 +16,8 @@ Usage: Just create a GDScriptReader instance and call methods from it.https://github.com/elamaunt/GDShrapt git GDShrapt GDScript reader parser codegeneration Godot lexical analyzer - 4.2.0 - 4.2.0 + 4.3.0 + 4.3.0 true diff --git a/src/GDShrapt.Reader/Resolvers/GDClassMembersResolver.cs b/src/GDShrapt.Reader/Resolvers/GDClassMembersResolver.cs index 909d70d..6c35d72 100644 --- a/src/GDShrapt.Reader/Resolvers/GDClassMembersResolver.cs +++ b/src/GDShrapt.Reader/Resolvers/GDClassMembersResolver.cs @@ -129,7 +129,7 @@ private void Complete(GDReadingState state, string sequence) if (sequence[0] == '@') { - Owner.HandleReceivedToken(state.Push(new GDCustomAttribute(LineIntendationThreshold))); + Owner.HandleReceivedToken(state.Push(new GDCustomAttribute(CurrentResolvedIntendationInSpaces))); for (int i = 0; i < sequence.Length; i++) state.PassChar(sequence[i]); @@ -229,14 +229,14 @@ void HandleStaticIfMet(ITokenReceiver spaceReceiver, Action push, bool break; case "signal": { - var m = new GDSignalDeclaration(LineIntendationThreshold); + var m = new GDSignalDeclaration(CurrentResolvedIntendationInSpaces); HandleStaticIfMet(m, () => Owner.HandleReceivedToken(state.Push(m))); m.Add(new GDSignalKeyword()); } break; case "enum": { - var m = new GDEnumDeclaration(LineIntendationThreshold); + var m = new GDEnumDeclaration(CurrentResolvedIntendationInSpaces); HandleStaticIfMet(m, () => Owner.HandleReceivedToken(state.Push(m))); m.Add(new GDEnumKeyword()); } @@ -262,28 +262,28 @@ void HandleStaticIfMet(ITokenReceiver spaceReceiver, Action push, bool break; case "func": { - var m = new GDMethodDeclaration(LineIntendationThreshold); + var m = new GDMethodDeclaration(CurrentResolvedIntendationInSpaces); HandleStaticIfMet(m, () => Owner.HandleReceivedToken(state.Push(m)), false); m.Add(new GDFuncKeyword()); } break; case "const": { - var m = new GDVariableDeclaration(LineIntendationThreshold); + var m = new GDVariableDeclaration(CurrentResolvedIntendationInSpaces); HandleStaticIfMet(m, () => Owner.HandleReceivedToken(state.Push(m))); m.Add(new GDConstKeyword()); } break; case "var": { - var m = new GDVariableDeclaration(LineIntendationThreshold); + var m = new GDVariableDeclaration(CurrentResolvedIntendationInSpaces); HandleStaticIfMet(m, () => Owner.HandleReceivedToken(state.Push(m)), false); m.Add(new GDVarKeyword()); } break; case "class": { - var m = new GDInnerClassDeclaration(LineIntendationThreshold); + var m = new GDInnerClassDeclaration(CurrentResolvedIntendationInSpaces); HandleStaticIfMet(m, () => Owner.HandleReceivedToken(state.Push(m))); m.Add(new GDClassKeyword()); } diff --git a/src/GDShrapt.Reader/Resolvers/GDElifResolver.cs b/src/GDShrapt.Reader/Resolvers/GDElifResolver.cs index ab8d0de..4c5252d 100644 --- a/src/GDShrapt.Reader/Resolvers/GDElifResolver.cs +++ b/src/GDShrapt.Reader/Resolvers/GDElifResolver.cs @@ -19,7 +19,7 @@ protected override void OnFail(GDReadingState state) protected override void OnMatch(GDReadingState state) { - var branch = new GDElifBranch(LineIntendationThreshold); + var branch = new GDElifBranch(CurrentResolvedIntendationInSpaces); branch.Add(new GDElifKeyword()); diff --git a/src/GDShrapt.Reader/Resolvers/GDElseResolver.cs b/src/GDShrapt.Reader/Resolvers/GDElseResolver.cs index b7e6607..d2abcda 100644 --- a/src/GDShrapt.Reader/Resolvers/GDElseResolver.cs +++ b/src/GDShrapt.Reader/Resolvers/GDElseResolver.cs @@ -19,7 +19,7 @@ protected override void OnFail(GDReadingState state) protected override void OnMatch(GDReadingState state) { - var branch = new GDElseBranch(LineIntendationThreshold); + var branch = new GDElseBranch(CurrentResolvedIntendationInSpaces); branch.Add(new GDElseKeyword()); diff --git a/src/GDShrapt.Reader/Resolvers/GDIntendedResolver.cs b/src/GDShrapt.Reader/Resolvers/GDIntendedResolver.cs index e0c5ced..fd7abee 100644 --- a/src/GDShrapt.Reader/Resolvers/GDIntendedResolver.cs +++ b/src/GDShrapt.Reader/Resolvers/GDIntendedResolver.cs @@ -1,11 +1,11 @@ -using System.Net.Sockets; -using System.Text; +using System.Text; namespace GDShrapt.Reader { internal abstract class GDIntendedResolver : GDResolver { - public int LineIntendationThreshold { get; } + public int CurrentResolvedIntendationInSpaces { get; private set; } + public int LineIntendationInSpacesThreshold { get; } readonly StringBuilder _sequenceBuilder = new StringBuilder(); @@ -15,7 +15,6 @@ internal abstract class GDIntendedResolver : GDResolver public int CalculatedIntendation => _lineIntendation; bool _lineIntendationEnded; - int _spaceCounter; bool _inComment; bool _intendationTokensSent; bool _lineSplitted; @@ -24,11 +23,11 @@ internal abstract class GDIntendedResolver : GDResolver public bool AllowZeroIntendationOnFirstLine { get; set; } - public GDIntendedResolver(IIntendedTokenReceiver owner, int lineIntendation) + public GDIntendedResolver(IIntendedTokenReceiver owner, int lineIntendationInSpaces) : base(owner) { Owner = owner; - LineIntendationThreshold = lineIntendation; + LineIntendationInSpacesThreshold = lineIntendationInSpaces; } internal sealed override void HandleChar(char c, GDReadingState state) @@ -49,6 +48,12 @@ bool HandleIntendation(char c, GDReadingState state) if (_lineIntendationEnded) return false; + //int singleIntendationSpacesCount = 4; + //bool singleIntendationResolved = state.IntendationInSpacesCount.HasValue; + + //if (singleIntendationResolved) + // singleIntendationSpacesCount = state.IntendationInSpacesCount.Value; + if (AllowZeroIntendationOnFirstLine && _firstLine && !c.IsSpace() && !c.IsNewLine() && c != '#' && c != '\\') { _lineIntendationEnded = true; @@ -66,7 +71,6 @@ bool HandleIntendation(char c, GDReadingState state) { _firstLine = false; _inComment = false; - _spaceCounter = 0; _lineIntendation = 0; } @@ -89,28 +93,16 @@ bool HandleIntendation(char c, GDReadingState state) if (c == '\t') { - if (_spaceCounter > 0) - { - // TODO: warning spaces before tabs - } - - _spaceCounter = 0; - _lineIntendation++; + _lineIntendation += state.Settings.SingleTabSpacesCost; _sequenceBuilder.Append(c); return true; } - if (c == ' ' && state.Settings.ReadFourSpacesAsIntendation) + if (c == ' ') { - _spaceCounter++; + _lineIntendation++; _sequenceBuilder.Append(c); - if (_spaceCounter == 4) - { - _spaceCounter = 0; - _lineIntendation++; - } - return true; } else @@ -118,7 +110,7 @@ bool HandleIntendation(char c, GDReadingState state) _lineIntendationEnded = true; // The 'end of the block' condition - if (LineIntendationThreshold > _lineIntendation) + if (LineIntendationInSpacesThreshold > _lineIntendation) { OnIntendationThresholdMet(state); state.Pop(); @@ -131,10 +123,7 @@ bool HandleIntendation(char c, GDReadingState state) return true; } - if (LineIntendationThreshold < _lineIntendation) - { - // TODO: warning invalid extra intendation - } + CurrentResolvedIntendationInSpaces = _lineIntendation; } } @@ -297,7 +286,7 @@ protected void SendIntendationTokensToOwner() Owner.HandleReceivedToken(new GDIntendation() { Sequence = space.Sequence, - LineIntendationThreshold = LineIntendationThreshold + LineIntendationThreshold = CurrentResolvedIntendationInSpaces }); } else @@ -308,7 +297,7 @@ protected void SendIntendationTokensToOwner() Owner.HandleReceivedToken(new GDIntendation() { Sequence = string.Empty, - LineIntendationThreshold = LineIntendationThreshold + LineIntendationThreshold = CurrentResolvedIntendationInSpaces }); } } @@ -329,7 +318,6 @@ protected void ResetIntendation() _sequenceBuilder.Clear(); _lineIntendation = 0; _lineIntendationEnded = false; - _spaceCounter = 0; _intendationTokensSent = false; _firstLine = true; } diff --git a/src/GDShrapt.Reader/Resolvers/GDMatchCasesResolver.cs b/src/GDShrapt.Reader/Resolvers/GDMatchCasesResolver.cs index cdb3eca..836620e 100644 --- a/src/GDShrapt.Reader/Resolvers/GDMatchCasesResolver.cs +++ b/src/GDShrapt.Reader/Resolvers/GDMatchCasesResolver.cs @@ -29,7 +29,7 @@ internal override void HandleCharAfterIntendation(char c, GDReadingState state) _lastSpace = null; } - Owner.HandleReceivedToken(state.Push(new GDMatchCaseDeclaration(LineIntendationThreshold))); + Owner.HandleReceivedToken(state.Push(new GDMatchCaseDeclaration(CurrentResolvedIntendationInSpaces))); state.PassChar(c); } diff --git a/src/GDShrapt.Reader/Resolvers/GDSetGetAccessorsResolver.cs b/src/GDShrapt.Reader/Resolvers/GDSetGetAccessorsResolver.cs index b9fbb62..a277555 100644 --- a/src/GDShrapt.Reader/Resolvers/GDSetGetAccessorsResolver.cs +++ b/src/GDShrapt.Reader/Resolvers/GDSetGetAccessorsResolver.cs @@ -115,7 +115,7 @@ internal override void HandleCharAfterIntendation(char c, GDReadingState state) { SendIntendationTokensToOwner(); state.Pop(); - var accessor = new GDGetAccessorBodyDeclaration(LineIntendationThreshold); + var accessor = new GDGetAccessorBodyDeclaration(CurrentResolvedIntendationInSpaces); Owner.HandleReceivedToken(accessor); state.Push(accessor); PassStoredSequence(state); @@ -127,7 +127,7 @@ internal override void HandleCharAfterIntendation(char c, GDReadingState state) { SendIntendationTokensToOwner(); state.Pop(); - var accessor = new GDSetAccessorBodyDeclaration(LineIntendationThreshold); + var accessor = new GDSetAccessorBodyDeclaration(CurrentResolvedIntendationInSpaces); Owner.HandleReceivedToken(accessor); state.Push(accessor); PassStoredSequence(state); @@ -143,13 +143,13 @@ internal override void HandleCharAfterIntendation(char c, GDReadingState state) GDReader reader; if (_state == State.GotGetKeyword) { - var accessor = new GDGetAccessorMethodDeclaration(LineIntendationThreshold); + var accessor = new GDGetAccessorMethodDeclaration(CurrentResolvedIntendationInSpaces); Owner.HandleReceivedToken(accessor); reader = accessor; } else { - var accessor = new GDSetAccessorMethodDeclaration(LineIntendationThreshold); + var accessor = new GDSetAccessorMethodDeclaration(CurrentResolvedIntendationInSpaces); Owner.HandleReceivedToken(accessor); reader = accessor; } diff --git a/src/GDShrapt.Reader/Resolvers/GDStatementsResolver.cs b/src/GDShrapt.Reader/Resolvers/GDStatementsResolver.cs index 3427068..af7cd5f 100644 --- a/src/GDShrapt.Reader/Resolvers/GDStatementsResolver.cs +++ b/src/GDShrapt.Reader/Resolvers/GDStatementsResolver.cs @@ -37,7 +37,7 @@ internal override void HandleCharAfterIntendation(char c, GDReadingState state) } // Resolving multiple expressions on the same string - var statement = new GDExpressionStatement(LineIntendationThreshold); + var statement = new GDExpressionStatement(CurrentResolvedIntendationInSpaces); Owner.HandleReceivedToken(statement); state.PushAndPass(statement, c); } @@ -163,35 +163,35 @@ private GDStatement CompleteAsStatement(GDReadingState state, string sequence) } case "if": { - var s = new GDIfStatement(LineIntendationThreshold); + var s = new GDIfStatement(CurrentResolvedIntendationInSpaces); s.IfBranch.Add(new GDIfKeyword()); statement = s; break; } case "for": { - var s = new GDForStatement(LineIntendationThreshold); + var s = new GDForStatement(CurrentResolvedIntendationInSpaces); s.Add(new GDForKeyword()); statement = s; break; } case "while": { - var s = new GDWhileStatement(LineIntendationThreshold); + var s = new GDWhileStatement(CurrentResolvedIntendationInSpaces); s.Add(new GDWhileKeyword()); statement = s; break; } case "match": { - var s = new GDMatchStatement(LineIntendationThreshold); + var s = new GDMatchStatement(CurrentResolvedIntendationInSpaces); s.Add(new GDMatchKeyword()); statement = s; break; } case "var": { - var s = new GDVariableDeclarationStatement(LineIntendationThreshold); + var s = new GDVariableDeclarationStatement(CurrentResolvedIntendationInSpaces); s.Add(new GDVarKeyword()); statement = s; break; @@ -211,7 +211,7 @@ private GDStatement CompleteAsStatement(GDReadingState state, string sequence) private GDExpressionStatement CompleteAsExpressionStatement(GDReadingState state) { - var statement = new GDExpressionStatement(LineIntendationThreshold); + var statement = new GDExpressionStatement(CurrentResolvedIntendationInSpaces); SendIntendationTokensToOwner(); Owner.HandleReceivedToken(statement); diff --git a/src/GDShrapt.Reader/SimpleTokens/GDIntendation.cs b/src/GDShrapt.Reader/SimpleTokens/GDIntendation.cs index d4aaef0..c8c1462 100644 --- a/src/GDShrapt.Reader/SimpleTokens/GDIntendation.cs +++ b/src/GDShrapt.Reader/SimpleTokens/GDIntendation.cs @@ -1,11 +1,11 @@ -using System.Text; +using System; +using System.Text; namespace GDShrapt.Reader { public sealed class GDIntendation : GDSimpleSyntaxToken { - int _lineIntendation; - int _spaceCounter; + int _lineIntendationInSpaces; internal StringBuilder _sequenceBuilder = new StringBuilder(); @@ -16,32 +16,20 @@ internal override void HandleChar(char c, GDReadingState state) { if (c == '\t') { - if (_spaceCounter > 0) - throw new GDInvalidStateException(); - - _spaceCounter = 0; - _sequenceBuilder.Append(c); - _lineIntendation++; + _lineIntendationInSpaces += state.Settings.SingleTabSpacesCost; return; } - if (c == ' ' && state.Settings.ReadFourSpacesAsIntendation) + if (c == ' ') { - _spaceCounter++; _sequenceBuilder.Append(c); - - if (_spaceCounter == 4) - { - _spaceCounter = 0; - _lineIntendation++; - } - + _lineIntendationInSpaces++; return; } Sequence = _sequenceBuilder.ToString(); - LineIntendationThreshold = _lineIntendation; + LineIntendationThreshold = _lineIntendationInSpaces; state.PopAndPass(c); } @@ -53,13 +41,22 @@ public override GDSyntaxToken Clone() Sequence = Sequence }; } - /// /// Counts all in parents and updates with the count. /// Also updates the . /// - public void Update() + /// Whitespace pattern. Used as a single intendation. If null, a single \t character is used. + /// If used not a whitespace pattern + public void Update(string pattern = null) { + if (pattern == null) + pattern = "\t"; + + if (string.IsNullOrEmpty(pattern)) + throw new ArgumentException("Intendation pattern must not be empty."); + if (!string.IsNullOrWhiteSpace(pattern)) + throw new ArgumentException("Intendation pattern must be a whitespace."); + var p = Parent; int intendation = 0; @@ -72,7 +69,20 @@ public void Update() } LineIntendationThreshold = intendation; - Sequence = new string('\t', intendation); + + if (intendation == 0) + Sequence = string.Empty; + else if (intendation == 1) + Sequence = pattern; + else + { + var builder = new StringBuilder(pattern.Length * intendation); + + for (int i = 0; i < intendation; i++) + builder.Append(pattern); + + Sequence = builder.ToString(); + } } public override string ToString()