From bcb2c7dde150eb9092480b6dd35380a1678d0d06 Mon Sep 17 00:00:00 2001 From: elamaunt Date: Wed, 23 Jun 2021 01:32:11 +0500 Subject: [PATCH] Fixed comments. Implemented node paths. Fixed exports reading. Added new tests. All tests have been passed. --- src/GDShrapt.Reader.Tests/AssertHelper.cs | 2 +- .../GDShrapt.Reader.Tests.csproj | 1 + src/GDShrapt.Reader.Tests/ParsingTests.cs | 338 +++++++++++++++--- src/GDShrapt.Reader.Tests/SyntaxTests.cs | 78 +++- src/GDShrapt.Reader/Basics/GDNode.cs | 23 +- .../Basics/Receiving/IPathReceiver.cs | 8 - .../Declarations/GDEnumValueDeclaration.cs | 3 - .../Declarations/GDExportDeclaration.cs | 23 +- .../Declarations/GDMethodDeclaration.cs | 129 ++++++- .../Declarations/GDParameterDeclaration.cs | 78 +++- .../Declarations/GDVariableDeclaration.cs | 12 +- .../GDArrayInitializerExpression.cs | 7 + .../Expressions/GDCallExression.cs | 7 + .../GDDictionaryInitializerExpression.cs | 10 +- .../Expressions/GDGetNodeExpression.cs | 43 +-- .../Expressions/GDMemberOperatorExpression.cs | 3 +- .../Expressions/GDNumberExpression.cs | 6 +- src/GDShrapt.Reader/GDReadingState.cs | 4 +- src/GDShrapt.Reader/GDScriptReader.cs | 2 +- src/GDShrapt.Reader/GDTokensForm.cs | 53 +++ .../Lists/GDCommaSeparatedList.cs | 2 +- .../Lists/GDExportParametersList.cs | 28 +- src/GDShrapt.Reader/Lists/GDPathList.cs | 54 +++ .../Resolvers/GDClassMembersResolver.cs | 27 +- ...kenResolver.cs => GDDataTokensResolver.cs} | 13 +- .../Resolvers/GDExportResolver.cs | 5 +- .../Resolvers/GDExpressionResolver.cs | 35 +- .../Resolvers/GDIntendedResolver.cs | 17 +- .../Resolvers/GDKeywordResolver.cs | 4 +- .../Resolvers/GDResolvingHelper.cs | 32 +- .../Resolvers/GDSequenceResolver.cs | 12 +- .../Resolvers/GDStartTrimmingResolver.cs | 29 ++ .../Resolvers/GDStatementsResolver.cs | 12 +- src/GDShrapt.Reader/SimpleTokens/GDNumber.cs | 12 +- src/GDShrapt.Reader/SimpleTokens/GDPath.cs | 10 - .../SimpleTokens/GDRightSlash.cs | 7 + src/GDShrapt.Reader/SimpleTokens/GDString.cs | 99 +++-- .../Statements/GDExpressionStatement.cs | 66 +++- 38 files changed, 1021 insertions(+), 273 deletions(-) delete mode 100644 src/GDShrapt.Reader/Basics/Receiving/IPathReceiver.cs create mode 100644 src/GDShrapt.Reader/Lists/GDPathList.cs rename src/GDShrapt.Reader/Resolvers/{GDDataTokenResolver.cs => GDDataTokensResolver.cs} (75%) create mode 100644 src/GDShrapt.Reader/Resolvers/GDStartTrimmingResolver.cs delete mode 100644 src/GDShrapt.Reader/SimpleTokens/GDPath.cs create mode 100644 src/GDShrapt.Reader/SimpleTokens/GDRightSlash.cs diff --git a/src/GDShrapt.Reader.Tests/AssertHelper.cs b/src/GDShrapt.Reader.Tests/AssertHelper.cs index 9becd63..c0ff569 100644 --- a/src/GDShrapt.Reader.Tests/AssertHelper.cs +++ b/src/GDShrapt.Reader.Tests/AssertHelper.cs @@ -4,7 +4,7 @@ namespace GDShrapt.Reader.Tests { public static class AssertHelper { - internal static void CompareStrings(string s1, string s2) + internal static void CompareCodeStrings(string s1, string s2) { Assert.AreEqual( s1.Replace("\r", "").Replace(" ", "\t"), diff --git a/src/GDShrapt.Reader.Tests/GDShrapt.Reader.Tests.csproj b/src/GDShrapt.Reader.Tests/GDShrapt.Reader.Tests.csproj index 7006e57..6901a36 100644 --- a/src/GDShrapt.Reader.Tests/GDShrapt.Reader.Tests.csproj +++ b/src/GDShrapt.Reader.Tests/GDShrapt.Reader.Tests.csproj @@ -7,6 +7,7 @@ + diff --git a/src/GDShrapt.Reader.Tests/ParsingTests.cs b/src/GDShrapt.Reader.Tests/ParsingTests.cs index 5a95f95..92ac887 100644 --- a/src/GDShrapt.Reader.Tests/ParsingTests.cs +++ b/src/GDShrapt.Reader.Tests/ParsingTests.cs @@ -48,7 +48,7 @@ func save(path, resource, flags): Assert.AreEqual(1, @class.Methods.ElementAt(1).Statements.Count); Assert.AreEqual(1, @class.Methods.ElementAt(2).Statements.Count); - AssertHelper.CompareStrings(code, @class.ToString()); + AssertHelper.CompareCodeStrings(code, @class.ToString()); } [TestMethod] @@ -96,7 +96,7 @@ public void ParseLogicalExpressionTest() Assert.AreEqual("c", ((GDIdentifierExpression)@rightDualOperator.LeftExpression).Identifier.Sequence); Assert.AreEqual("d", ((GDIdentifierExpression)@rightDualOperator.RightExpression).Identifier.Sequence); - AssertHelper.CompareStrings(code, expression.ToString()); + AssertHelper.CompareCodeStrings(code, expression.ToString()); } [TestMethod] @@ -117,7 +117,7 @@ public void ExpressionsPriorityTest() Assert.AreEqual(GDDualOperatorType.Assignment, @dualOperator.OperatorType); Assert.AreEqual("a > b > c", @dualOperator.LeftExpression.ToString()); - AssertHelper.CompareStrings(code, expression.ToString()); + AssertHelper.CompareCodeStrings(code, expression.ToString()); } [TestMethod] @@ -144,7 +144,7 @@ public void IfStatementTest() Assert.IsInstanceOfType(ifStatement.IfBranch.Statements[0], typeof(GDExpressionStatement)); Assert.IsInstanceOfType(((GDExpressionStatement)ifStatement.IfBranch.Statements[0]).Expression, typeof(GDReturnExpression)); - AssertHelper.CompareStrings(code, statement.ToString()); + AssertHelper.CompareCodeStrings(code, statement.ToString()); } [TestMethod] @@ -176,7 +176,7 @@ public void IfElseStatementTest() Assert.IsInstanceOfType(((GDExpressionStatement)ifStatement.IfBranch.Statements[0]).Expression, typeof(GDReturnExpression)); Assert.IsInstanceOfType(((GDExpressionStatement)ifStatement.ElseBranch.Statements[0]).Expression, typeof(GDDualOperatorExression)); - AssertHelper.CompareStrings(code, statement.ToString()); + AssertHelper.CompareCodeStrings(code, statement.ToString()); } [TestMethod] @@ -198,7 +198,7 @@ public void IfStatementTest2() Assert.IsNotNull(ifStatement.IfBranch.Expression); Assert.IsInstanceOfType(ifStatement.IfBranch.Expression, typeof(GDReturnExpression)); - AssertHelper.CompareStrings(code, statement.ToString()); + AssertHelper.CompareCodeStrings(code, statement.ToString()); } [TestMethod] @@ -230,7 +230,7 @@ public void IfElseStatementTest2() Assert.IsInstanceOfType(ifStatement.ElseBranch.Statements[1], typeof(GDExpressionStatement)); Assert.IsInstanceOfType(((GDExpressionStatement)ifStatement.ElseBranch.Statements[1]).Expression, typeof(GDReturnExpression)); - AssertHelper.CompareStrings(code, statement.ToString()); + AssertHelper.CompareCodeStrings(code, statement.ToString()); } [TestMethod] @@ -274,7 +274,7 @@ public void ElifStatementTest() Assert.IsInstanceOfType(ifStatement.ElseBranch.Statements[1], typeof(GDExpressionStatement)); Assert.IsInstanceOfType(((GDExpressionStatement)ifStatement.ElseBranch.Statements[1]).Expression, typeof(GDReturnExpression)); - AssertHelper.CompareStrings(code, statement.ToString()); + AssertHelper.CompareCodeStrings(code, statement.ToString()); } [TestMethod] @@ -302,7 +302,7 @@ public void FunctionTypeTest() Assert.AreEqual("my_int_function", method.Identifier?.Sequence); Assert.AreEqual(true, method.IsStatic); - AssertHelper.CompareStrings(code, declaration.ToString()); + AssertHelper.CompareCodeStrings(code, declaration.ToString()); } [TestMethod] @@ -327,7 +327,7 @@ public void ForStatementTest() Assert.AreEqual(1, forStatement.Statements.Count); Assert.IsInstanceOfType(forStatement.Statements[0], typeof(GDExpressionStatement)); - AssertHelper.CompareStrings(code, statement.ToString()); + AssertHelper.CompareCodeStrings(code, statement.ToString()); } [TestMethod] @@ -352,7 +352,7 @@ public void ForStatementTest2() Assert.AreEqual(1, forStatement.Statements.Count); Assert.IsInstanceOfType(forStatement.Statements[0], typeof(GDExpressionStatement)); - AssertHelper.CompareStrings(code, statement.ToString()); + AssertHelper.CompareCodeStrings(code, statement.ToString()); } [TestMethod] @@ -376,7 +376,7 @@ public void WhileStatementTest() Assert.AreEqual(1, whileStatement.Statements.Count); Assert.IsInstanceOfType(whileStatement.Statements[0], typeof(GDExpressionStatement)); - AssertHelper.CompareStrings(code, statement.ToString()); + AssertHelper.CompareCodeStrings(code, statement.ToString()); } [TestMethod] @@ -400,7 +400,7 @@ public void WhileStatementTest2() Assert.AreEqual(1, whileStatement.Statements.Count); Assert.IsInstanceOfType(whileStatement.Statements[0], typeof(GDExpressionStatement)); - AssertHelper.CompareStrings(code, statement.ToString()); + AssertHelper.CompareCodeStrings(code, statement.ToString()); } [TestMethod] @@ -444,7 +444,7 @@ public void MatchStatementTest() Assert.AreEqual("print(\"Two are better than one!\")", matchStatement.Cases[1].Statements[0].ToString()); Assert.AreEqual("print(\"Oh snap! It's a string!\")", matchStatement.Cases[2].Statements[0].ToString()); - AssertHelper.CompareStrings(code, statement.ToString()); + AssertHelper.CompareCodeStrings(code, statement.ToString()); } [TestMethod] @@ -485,7 +485,7 @@ public void MatchStatementTest2() Assert.AreEqual(1, matchStatement.Cases[1].Statements.Count); Assert.AreEqual(1, matchStatement.Cases[2].Statements.Count); - AssertHelper.CompareStrings(code, statement.ToString()); + AssertHelper.CompareCodeStrings(code, statement.ToString()); } [TestMethod] @@ -514,7 +514,7 @@ public void ArrayTest() Assert.AreEqual("1", arrayInitializer.Values[2].ToString()); Assert.AreEqual("\"Hello World\"", arrayInitializer.Values[3].ToString()); - AssertHelper.CompareStrings(code, statement.ToString()); + AssertHelper.CompareCodeStrings(code, statement.ToString()); } [TestMethod] @@ -548,7 +548,7 @@ public void DictionaryTest() Assert.AreEqual("\"test\"", dictionaryInitializer.KeyValues[2].Value.ToString()); Assert.AreEqual("\"World\"", dictionaryInitializer.KeyValues[3].Value.ToString()); - AssertHelper.CompareStrings(code, statement.ToString()); + AssertHelper.CompareCodeStrings(code, statement.ToString()); } [TestMethod] @@ -570,7 +570,7 @@ public void StringTest() Assert.AreEqual(GDStringBoundingChar.DoubleQuotas, stringExpression.String.BoundingChar); Assert.AreEqual("test", stringExpression.String.Value); - AssertHelper.CompareStrings(code, statement.ToString()); + AssertHelper.CompareCodeStrings(code, statement.ToString()); } [TestMethod] @@ -592,7 +592,7 @@ public void StringTest2() Assert.AreEqual(GDStringBoundingChar.SingleQuotas, stringExpression.String.BoundingChar); Assert.AreEqual("te\"\"st", stringExpression.String.Value); - AssertHelper.CompareStrings(code, statement.ToString()); + AssertHelper.CompareCodeStrings(code, statement.ToString()); } [TestMethod] @@ -614,7 +614,7 @@ public void MultilineStringTest() Assert.AreEqual(GDStringBoundingChar.DoubleQuotas, stringExpression.String.BoundingChar); Assert.AreEqual("te\"\"st", stringExpression.String.Value); - AssertHelper.CompareStrings(code, statement.ToString()); + AssertHelper.CompareCodeStrings(code, statement.ToString()); } [TestMethod] @@ -636,11 +636,11 @@ public void MultilineStringTest2() Assert.AreEqual(GDStringBoundingChar.SingleQuotas, stringExpression.String.BoundingChar); Assert.AreEqual("te'\"st", stringExpression.String.Value); - AssertHelper.CompareStrings(code, statement.ToString()); + AssertHelper.CompareCodeStrings(code, statement.ToString()); } [TestMethod] - public void VariableExportTest() + public void ExportTest() { var reader = new GDScriptReader(); @@ -659,7 +659,148 @@ public void VariableExportTest() Assert.AreEqual("a", variableDeclaration.Identifier.Sequence); Assert.AreEqual("123", variableDeclaration.Initializer.ToString()); - AssertHelper.CompareStrings(code, classDeclaration.ToString()); + AssertHelper.CompareCodeStrings(code, classDeclaration.ToString()); + } + + [TestMethod] + public void ExportDeclarationsTest() + { + var reader = new GDScriptReader(); + + var code = @" +# If the exported value assigns a constant or constant expression, +# the type will be inferred and used in the editor. + +export var number = 5 + +# Export can take a basic data type as an argument, which will be +# used in the editor. + +export(int) var number + +# Export can also take a resource type to use as a hint. + +export(Texture) var character_face +export(PackedScene) var scene_file +# There are many resource types that can be used this way, try e.g. +# the following to list them: +export(Resource) var resource + +# Integers and strings hint enumerated values. + +# Editor will enumerate as 0, 1 and 2. +export(int, ""Warrior"", ""Magician"", ""Thief"") var character_class +# Editor will enumerate with string names. +export(String, ""Rebecca"", ""Mary"", ""Leah"") var character_name + +# Named enum values + +# Editor will enumerate as THING_1, THING_2, ANOTHER_THING. +enum NamedEnum { THING_1, THING_2, ANOTHER_THING = -1 } + export(NamedEnum) var x + +# Strings as paths + +# String is a path to a file. +export(String, FILE) var f +# String is a path to a directory. +export(String, DIR) var f +# String is a path to a file, custom filter provided as hint. +export(String, FILE, ""*.txt"") var f + +# Using paths in the global filesystem is also possible, +# but only in scripts in ""tool"" mode. + +# String is a path to a PNG file in the global filesystem. +export(String, FILE, GLOBAL, ""*.png"") var tool_image +# String is a path to a directory in the global filesystem. +export(String, DIR, GLOBAL) var tool_dir + +# The MULTILINE setting tells the editor to show a large input +# field for editing over multiple lines. +export(String, MULTILINE) var text + +# Limiting editor input ranges + +# Allow integer values from 0 to 20. +export(int, 20) var i +# Allow integer values from -10 to 20. +export(int, -10, 20) var j +# Allow floats from -10 to 20 and snap the value to multiples of 0.2. +export(float, -10, 20, 0.2) var k +# Allow values 'y = exp(x)' where 'y' varies between 100 and 1000 +# while snapping to steps of 20. The editor will present a +# slider for easily editing the value. +export(float, EXP, 100, 1000, 20) var l + +# Floats with easing hint + +# Display a visual representation of the 'ease()' function +# when editing. +export(float, EASE) var transition_speed + +# Colors + +# Color given as red-green-blue value (alpha will always be 1). +export(Color, RGB) var col +# Color given as red-green-blue-alpha value. +export(Color, RGBA) var col + +# Nodes + +# Another node in the scene can be exported as a NodePath. +export(NodePath) var node_path +# Do take note that the node itself isn't being exported - +# there is one more step to call the true node: +var node = get_node(node_path) + +# Resources + +export(Resource) var resource +# In the Inspector, you can then drag and drop a resource file +# from the FileSystem dock into the variable slot. + +# Opening the inspector dropdown may result in an +# extremely long list of possible classes to create, however. +# Therefore, if you specify an extension of Resource such as: +export(AnimationNode) var resource +# The drop-down menu will be limited to AnimationNode and all +# its inherited classes. +"; + var classDeclaration = reader.ParseFileContent(code); + + Assert.IsNotNull(classDeclaration); + + var exports = classDeclaration.AllNodes.OfType().ToArray(); + + Assert.AreEqual(24, exports.Length); + + Assert.AreEqual(0, exports[0].Parameters.Count); + Assert.AreEqual(1, exports[1].Parameters.Count); + Assert.AreEqual(1, exports[2].Parameters.Count); + Assert.AreEqual(1, exports[3].Parameters.Count); + Assert.AreEqual(1, exports[4].Parameters.Count); + Assert.AreEqual(4, exports[5].Parameters.Count); + Assert.AreEqual(4, exports[6].Parameters.Count); + Assert.AreEqual(1, exports[7].Parameters.Count); + Assert.AreEqual(2, exports[8].Parameters.Count); + Assert.AreEqual(2, exports[9].Parameters.Count); + Assert.AreEqual(3, exports[10].Parameters.Count); + Assert.AreEqual(4, exports[11].Parameters.Count); + Assert.AreEqual(3, exports[12].Parameters.Count); + Assert.AreEqual(2, exports[13].Parameters.Count); + Assert.AreEqual(2, exports[14].Parameters.Count); + Assert.AreEqual(3, exports[15].Parameters.Count); + Assert.AreEqual(4, exports[16].Parameters.Count); + Assert.AreEqual(5, exports[17].Parameters.Count); + Assert.AreEqual(2, exports[18].Parameters.Count); + Assert.AreEqual(2, exports[19].Parameters.Count); + Assert.AreEqual(2, exports[20].Parameters.Count); + Assert.AreEqual(1, exports[21].Parameters.Count); + Assert.AreEqual(1, exports[22].Parameters.Count); + Assert.AreEqual(1, exports[23].Parameters.Count); + + AssertHelper.CompareCodeStrings(code, classDeclaration.ToString()); } [TestMethod] @@ -688,7 +829,7 @@ public void EnumTest() Assert.IsNull(enumDeclaration.Values[1].Value); Assert.AreEqual("3", enumDeclaration.Values[2].Value?.ToString()); - AssertHelper.CompareStrings(code, classDeclaration.ToString()); + AssertHelper.CompareCodeStrings(code, classDeclaration.ToString()); } [TestMethod] @@ -717,7 +858,7 @@ public void EnumTest2() Assert.IsNull(enumDeclaration.Values[1].Value); Assert.IsNotNull(enumDeclaration.Values[2].Value); - AssertHelper.CompareStrings(code, classDeclaration.ToString()); + AssertHelper.CompareCodeStrings(code, classDeclaration.ToString()); } [TestMethod] @@ -737,7 +878,7 @@ public void ClassNameTest() Assert.IsNotNull(classDeclaration.ClassName.Icon); Assert.AreEqual("res://interface/icons/item.png", classDeclaration.ClassName.Icon.Value); - AssertHelper.CompareStrings(code, classDeclaration.ToString()); + AssertHelper.CompareCodeStrings(code, classDeclaration.ToString()); } [TestMethod] @@ -754,7 +895,7 @@ public void ExtendsTest() Assert.AreEqual(1, classDeclaration.Atributes.Count); Assert.AreEqual("Test", classDeclaration.Extends.Type.Sequence); - AssertHelper.CompareStrings(code, classDeclaration.ToString()); + AssertHelper.CompareCodeStrings(code, classDeclaration.ToString()); } [TestMethod] @@ -771,7 +912,7 @@ public void ExtendsTest2() Assert.AreEqual(1, classDeclaration.Atributes.Count); Assert.AreEqual("res://path/to/character.gd", classDeclaration.Extends.Path.Value); - AssertHelper.CompareStrings(code, classDeclaration.ToString()); + AssertHelper.CompareCodeStrings(code, classDeclaration.ToString()); } [TestMethod] @@ -792,7 +933,7 @@ public void NumberTest(string code) Assert.AreEqual(GDNumberType.LongDecimal, numberExpression.Number.ResolveNumberType()); Assert.AreEqual(1234, numberExpression.Number.ValueInt64); - AssertHelper.CompareStrings(code, statement.ToString()); + AssertHelper.CompareCodeStrings(code, statement.ToString()); } [TestMethod] @@ -813,7 +954,7 @@ public void NumberTest2(string code) Assert.AreEqual(GDNumberType.LongHexadecimal, numberExpression.Number.ResolveNumberType()); Assert.AreEqual(36689, numberExpression.Number.ValueInt64); - AssertHelper.CompareStrings(code, statement.ToString()); + AssertHelper.CompareCodeStrings(code, statement.ToString()); } [TestMethod] @@ -834,7 +975,7 @@ public void NumberTest3(string code) Assert.AreEqual(GDNumberType.LongBinary, numberExpression.Number.ResolveNumberType()); Assert.AreEqual(42, numberExpression.Number.ValueInt64); - AssertHelper.CompareStrings(code, statement.ToString()); + AssertHelper.CompareCodeStrings(code, statement.ToString()); } [TestMethod] @@ -857,7 +998,7 @@ public void NumberTest4(string code) Assert.AreEqual(GDNumberType.Double, numberExpression.Number.ResolveNumberType()); Assert.AreEqual(value, numberExpression.Number.ValueDouble); - AssertHelper.CompareStrings(code, statement.ToString()); + AssertHelper.CompareCodeStrings(code, statement.ToString()); } [TestMethod] @@ -910,7 +1051,7 @@ public void DualOperatorsTest(params string[] operators) Assert.AreEqual("b", dualOperatorExpression.RightExpression.ToString()); Assert.AreEqual(op, dualOperatorExpression.OperatorType.Print()); - AssertHelper.CompareStrings(code, expression.ToString()); + AssertHelper.CompareCodeStrings(code, expression.ToString()); } } @@ -936,7 +1077,7 @@ public void SingleOperatorsTest(params string[] operators) Assert.AreEqual("a", singleOperatorExpression.TargetExpression.ToString()); Assert.AreEqual(op, singleOperatorExpression.OperatorType.Print()); - AssertHelper.CompareStrings(code, expression.ToString()); + AssertHelper.CompareCodeStrings(code, expression.ToString()); } } @@ -972,7 +1113,7 @@ public void PropertyTest() Assert.AreEqual("set_speed", variableDeclaration.SetMethodIdentifier .Sequence); Assert.AreEqual("get_speed", variableDeclaration.GetMethodIdentifier.Sequence); - AssertHelper.CompareStrings(code, classDeclaration.ToString()); + AssertHelper.CompareCodeStrings(code, classDeclaration.ToString()); } [TestMethod] @@ -1016,7 +1157,7 @@ public void PropertyTest2() Assert.AreEqual("set_height", variableDeclaration.SetMethodIdentifier.Sequence); - AssertHelper.CompareStrings(code, classDeclaration.ToString()); + AssertHelper.CompareCodeStrings(code, classDeclaration.ToString()); } [TestMethod] @@ -1044,7 +1185,7 @@ public void SignalTest() Assert.AreEqual("value", signalDeclaration.Parameters[0]?.Identifier?.Sequence); Assert.AreEqual("other_value", signalDeclaration.Parameters[1]?.Identifier?.Sequence); - AssertHelper.CompareStrings(code, classDeclaration.ToString()); + AssertHelper.CompareCodeStrings(code, classDeclaration.ToString()); } [TestMethod] @@ -1069,7 +1210,7 @@ public void IfExpressionTest() Assert.AreEqual("y < 10", ifExpression.Condition.ToString()); Assert.AreEqual("-1", ifExpression.FalseExpression.ToString()); - AssertHelper.CompareStrings(code, expression.ToString()); + AssertHelper.CompareCodeStrings(code, expression.ToString()); } [TestMethod] @@ -1101,7 +1242,7 @@ public void IfExpressionTest2() Assert.AreEqual("-y != 10", ifExpression.Condition.ToString()); Assert.AreEqual("n", ifExpression.FalseExpression.ToString()); - AssertHelper.CompareStrings(code, declaration.ToString()); + AssertHelper.CompareCodeStrings(code, declaration.ToString()); } [TestMethod] @@ -1122,7 +1263,7 @@ public void NegativeNumberTest() Assert.AreEqual(GDNumberType.LongDecimal, numberExpression.Number.ResolveNumberType()); Assert.AreEqual(-10, numberExpression.Number.ValueInt64); - AssertHelper.CompareStrings(code, expression.ToString()); + AssertHelper.CompareCodeStrings(code, expression.ToString()); } [TestMethod] @@ -1163,7 +1304,7 @@ public void BracketsTest() Assert.AreEqual("(10-20)", singleOperator.TargetExpression.ToString()); - AssertHelper.CompareStrings(code, expression.ToString()); + AssertHelper.CompareCodeStrings(code, expression.ToString()); } [TestMethod] @@ -1188,7 +1329,7 @@ public void MatchDefaultOperatorTest() Assert.AreEqual(1, matchStatement.Cases[1].Conditions.Count); Assert.IsInstanceOfType(matchStatement.Cases[1].Conditions[0], typeof(GDMatchDefaultOperatorExpression)); - AssertHelper.CompareStrings(code, statement.ToString()); + AssertHelper.CompareCodeStrings(code, statement.ToString()); } [TestMethod] @@ -1245,7 +1386,118 @@ public void MethodsChainTest() var identifierExpression = callExpression.CallerExpression.CastOrAssert(); Assert.AreEqual("A", identifierExpression.Identifier?.Sequence); - AssertHelper.CompareStrings(code, expression.ToString()); + AssertHelper.CompareCodeStrings(code, expression.ToString()); + } + + [TestMethod] + public void BaseMethodCallTest() + { + var reader = new GDScriptReader(); + + var code = @"func _init(res : string = ""Hello world"").(res) -> void: + ._init(""1234""); + pass"; + + var @class = reader.ParseFileContent(code); + + Assert.IsNotNull(@class); + Assert.AreEqual(1, @class.Methods.Count()); + + var method = @class.Methods.First(); + Assert.IsNotNull(method); + + Assert.IsTrue(method.ReturnType.IsVoid); + Assert.AreEqual("_init", method.Identifier.Sequence); + Assert.AreEqual(1, method.Parameters.Count); + Assert.AreEqual(1, method.BaseCallParameters.Count); + + var parameter = method.Parameters[0]; + Assert.IsNotNull(parameter); + + Assert.AreEqual("res", parameter.Identifier.Sequence); + Assert.AreEqual("string", parameter.Type.Sequence); + Assert.AreEqual("\"Hello world\"", parameter.DefaultValue.ToString()); + + var baseCallParameter = method.BaseCallParameters[0]; + + Assert.AreEqual("res", baseCallParameter.ToString()); + + Assert.AreEqual(2, method.Statements.Count); + + var callStatement = method.Statements[0].CastOrAssert(); + + Assert.IsInstanceOfType(callStatement.Tokens.Last(), typeof(GDSemiColon)); + + var callExpression = callStatement.Expression.CastOrAssert(); + + Assert.AreEqual("._init", callExpression.CallerExpression.ToString()); + Assert.AreEqual("\"1234\"", callExpression.Parameters.ToString()); + + var passStatement = method.Statements[1].CastOrAssert().Expression.CastOrAssert(); + + Assert.IsNotNull(passStatement); + Assert.AreEqual(1, passStatement.Tokens.Count()); + + AssertHelper.CompareCodeStrings(code, @class.ToString()); + } + + [TestMethod] + public void StringEscapingTest() + { + var reader = new GDScriptReader(); + + var code = "\"Hello \\\" World\""; + + var expression = reader.ParseExpression(code); + + Assert.IsNotNull(expression); + Assert.IsInstanceOfType(expression, typeof(GDStringExpression)); + Assert.AreEqual("Hello \\\" World", ((GDStringExpression)expression).String.Value); + + AssertHelper.CompareCodeStrings(code, expression.ToString()); + } + + [TestMethod] + public void GetNodeTest() + { + var reader = new GDScriptReader(); + + var code = @"$Animation/Root/ _345/ end .CallMethod()"; + + var expression = reader.ParseExpression(code); + + Assert.IsNotNull(expression); + + var call = expression.CastOrAssert(); + + var memberOperator = call.CallerExpression.CastOrAssert(); + Assert.AreEqual("CallMethod", memberOperator.Identifier.Sequence); + + var getNodeExpression = memberOperator.CallerExpression.CastOrAssert(); + Assert.AreEqual("Animation/Root/ _345/ end ", getNodeExpression.Path.ToString()); + + AssertHelper.CompareCodeStrings(code, expression.ToString()); + } + + [TestMethod] + public void NodePathTest() + { + var reader = new GDScriptReader(); + + var code = @"@""/root/MyAutoload"".get_name(0)"; + + var expression = reader.ParseExpression(code); + + var call = expression.CastOrAssert(); + Assert.AreEqual("0", call.Parameters.ToString()); + + var memberOperator = call.CallerExpression.CastOrAssert(); + Assert.AreEqual("get_name", memberOperator.Identifier.Sequence); + + var nodePathExpression = memberOperator.CallerExpression.CastOrAssert(); + Assert.AreEqual("/root/MyAutoload", nodePathExpression.Path.Value); + + AssertHelper.CompareCodeStrings(code, expression.ToString()); } } } diff --git a/src/GDShrapt.Reader.Tests/SyntaxTests.cs b/src/GDShrapt.Reader.Tests/SyntaxTests.cs index d7a904e..63d0c2d 100644 --- a/src/GDShrapt.Reader.Tests/SyntaxTests.cs +++ b/src/GDShrapt.Reader.Tests/SyntaxTests.cs @@ -1,4 +1,6 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; +using FluentAssertions; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using System.Linq; namespace GDShrapt.Reader.Tests { @@ -41,7 +43,36 @@ func get_recognized_extensions(res): # func comment var @class = reader.ParseFileContent(code); Assert.IsNotNull(@class); - AssertHelper.CompareStrings(code, @class.ToString()); + + var comments = @class.AllTokens + .OfType() + .Select(x => x.ToString()) + .ToArray(); + + Assert.AreEqual(17, comments.Length); + + comments.Should().BeEquivalentTo(new[] + { + "# before tool comment", + "# tool comment", + "# before class name comment", + "# class name comment", + "# before extends comment", + "# extends comment", + "# before const comment", + "# const comment", + "# before func comment 1", + "# before func comment 2", + "# func comment", + "# before if statement comment", + "# if expression comment", + "# before return statement comment", + "# if true statement comment", + "# end file comment 1", + "# end file comment 2" + }); + + AssertHelper.CompareCodeStrings(code, @class.ToString()); } [TestMethod] @@ -63,7 +94,48 @@ enum { a, # a comment var @class = reader.ParseFileContent(code); Assert.IsNotNull(@class); - AssertHelper.CompareStrings(code, @class.ToString()); + + var comments = @class.AllTokens + .OfType() + .Select(x => x.ToString()) + .ToArray(); + + Assert.AreEqual(8, comments.Length); + + comments.Should().BeEquivalentTo(new[] + { + "# before enum comment", + "# a comment", + "# before b comment", + "# b comment", + "# before c comment", + "# c comment}", + "# after c comment", + "# enum ending comment" + }); + + AssertHelper.CompareCodeStrings(code, @class.ToString()); + } + + [TestMethod] + public void DictionaryCodeStyleTest() + { + var reader = new GDScriptReader(); + + var code = @" +{ +a: 0, +""1"": x # : x [1,2,3] + d = l + f + d = lkj :[1,2,3] +# : xa: 0, +}"; + + var expression = reader.ParseExpression(code); + Assert.IsNotNull(expression); + Assert.IsInstanceOfType(expression, typeof(GDDictionaryInitializerExpression)); + Assert.AreEqual(3, ((GDDictionaryInitializerExpression)expression).KeyValues.Count); + Assert.AreEqual(2, expression.AllTokens.OfType().Count()); + AssertHelper.CompareCodeStrings(code, "\n"+expression.ToString()); } } } diff --git a/src/GDShrapt.Reader/Basics/GDNode.cs b/src/GDShrapt.Reader/Basics/GDNode.cs index d3048bf..70230d5 100644 --- a/src/GDShrapt.Reader/Basics/GDNode.cs +++ b/src/GDShrapt.Reader/Basics/GDNode.cs @@ -7,7 +7,7 @@ namespace GDShrapt.Reader /// /// Basic GDScript node, may contains multiple tokens /// - public abstract partial class GDNode : GDSyntaxToken, IStyleTokensReceiver + public abstract class GDNode : GDSyntaxToken, IStyleTokensReceiver { internal abstract GDTokensForm Form { get; } @@ -17,12 +17,12 @@ public IEnumerable AllTokens { get { - foreach (var token in Tokens) + foreach (var token in Form) { if (token is GDNode node) { foreach (var nodeToken in node.AllTokens) - yield return token; + yield return nodeToken; } else yield return token; @@ -30,6 +30,23 @@ public IEnumerable AllTokens } } + public IEnumerable AllNodes + { + get + { + foreach (var token in Form) + { + if (token is GDNode node) + { + yield return node; + + foreach (var nodeToken in node.AllNodes) + yield return nodeToken; + } + } + } + } + /// /// Removes child node or does nothing if node is already removed. /// diff --git a/src/GDShrapt.Reader/Basics/Receiving/IPathReceiver.cs b/src/GDShrapt.Reader/Basics/Receiving/IPathReceiver.cs deleted file mode 100644 index 9c5b6b0..0000000 --- a/src/GDShrapt.Reader/Basics/Receiving/IPathReceiver.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace GDShrapt.Reader -{ - internal interface IPathReceiver - { - void HandleReceivedToken(GDPath token); - void HandleReceivedIdentifierSkip(); - } -} diff --git a/src/GDShrapt.Reader/Declarations/GDEnumValueDeclaration.cs b/src/GDShrapt.Reader/Declarations/GDEnumValueDeclaration.cs index 27c89b2..742a2fa 100644 --- a/src/GDShrapt.Reader/Declarations/GDEnumValueDeclaration.cs +++ b/src/GDShrapt.Reader/Declarations/GDEnumValueDeclaration.cs @@ -13,19 +13,16 @@ public GDIdentifier Identifier get => _form.Token0; set => _form.Token0 = value; } - internal GDColon Colon { get => (GDColon)_form.Token1; set => _form.Token1 = value; } - internal GDAssign Assign { get => (GDAssign)_form.Token1; set => _form.Token1 = value; } - public GDExpression Value { get => _form.Token2; diff --git a/src/GDShrapt.Reader/Declarations/GDExportDeclaration.cs b/src/GDShrapt.Reader/Declarations/GDExportDeclaration.cs index ce394bd..57e49a8 100644 --- a/src/GDShrapt.Reader/Declarations/GDExportDeclaration.cs +++ b/src/GDShrapt.Reader/Declarations/GDExportDeclaration.cs @@ -43,27 +43,23 @@ public GDExportDeclaration() internal override void HandleChar(char c, GDReadingState state) { - if (IsSpace(c)) - { - _form.AddBeforeActiveToken(state.Push(new GDSpace())); - state.PassChar(c); - return; - } - switch (_form.State) { case State.Export: - this.ResolveKeyword(c, state); + if (!this.ResolveStyleToken(c, state)) + this.ResolveKeyword(c, state); break; case State.OpenBracket: - this.ResolveOpenBracket(c, state); + if (!this.ResolveStyleToken(c, state)) + this.ResolveOpenBracket(c, state); break; case State.Parameters: _form.State = State.CloseBracket; state.PushAndPass(Parameters, c); break; case State.CloseBracket: - this.ResolveCloseBracket(c, state); + if (!this.ResolveStyleToken(c, state)) + this.ResolveCloseBracket(c, state); break; default: state.PopAndPass(c); @@ -73,6 +69,13 @@ internal override void HandleChar(char c, GDReadingState state) internal override void HandleNewLineChar(GDReadingState state) { + if (_form.State == State.Parameters) + { + _form.State = State.CloseBracket; + state.PushAndPassNewLine(Parameters); + return; + } + state.PopAndPassNewLine(); } diff --git a/src/GDShrapt.Reader/Declarations/GDMethodDeclaration.cs b/src/GDShrapt.Reader/Declarations/GDMethodDeclaration.cs index ef760ed..43147eb 100644 --- a/src/GDShrapt.Reader/Declarations/GDMethodDeclaration.cs +++ b/src/GDShrapt.Reader/Declarations/GDMethodDeclaration.cs @@ -8,7 +8,8 @@ public sealed class GDMethodDeclaration : GDClassMember, ITokenReceiver, IKeywordReceiver, ITypeReceiver, - ITokenReceiver + ITokenReceiver, + ITokenReceiver { internal GDStaticKeyword StaticKeyword { @@ -36,23 +37,43 @@ internal GDCloseBracket CloseBracket get => _form.Token5; set => _form.Token5 = value; } - internal GDReturnTypeKeyword ReturnTypeKeyword + + internal GDPoint BaseCallPoint { get => _form.Token6; set => _form.Token6 = value; } - public GDType ReturnType + + internal GDOpenBracket BaseCallOpenBracket { get => _form.Token7; set => _form.Token7 = value; } + public GDExpressionsList BaseCallParameters { get => _form.Token8 ?? (_form.Token8 = new GDExpressionsList()); } + + internal GDCloseBracket BaseCallCloseBracket + { + get => _form.Token9; + set => _form.Token9 = value; + } + + internal GDReturnTypeKeyword ReturnTypeKeyword + { + get => _form.Token10; + set => _form.Token10 = value; + } + public GDType ReturnType + { + get => _form.Token11; + set => _form.Token11 = value; + } internal GDColon Colon { - get => _form.Token8; - set => _form.Token8 = value; + get => _form.Token12; + set => _form.Token12 = value; } - public GDStatementsList Statements { get => _form.Token9 ?? (_form.Token9 = new GDStatementsList(Intendation + 1)); } + public GDStatementsList Statements { get => _form.Token13 ?? (_form.Token13 = new GDStatementsList(Intendation + 1)); } public bool IsStatic => StaticKeyword != null; @@ -64,6 +85,12 @@ enum State OpenBracket, Parameters, CloseBracket, + + BaseCallPoint, + BaseCallOpenBracket, + BaseCallParameters, + BaseCallCloseBracket, + ReturnTypeKeyword, Type, Colon, @@ -71,23 +98,23 @@ enum State Completed, } - readonly GDTokensForm _form; + readonly GDTokensForm _form; internal override GDTokensForm Form => _form; internal GDMethodDeclaration(int intendation) : base(intendation) { - _form = new GDTokensForm(this); + _form = new GDTokensForm(this); } public GDMethodDeclaration() { - _form = new GDTokensForm(this); + _form = new GDTokensForm(this); } internal override void HandleChar(char c, GDReadingState state) { - if (IsSpace(c) && _form.State != State.Parameters) + if (IsSpace(c) && _form.State != State.Parameters && _form.State != State.BaseCallParameters) { _form.AddBeforeActiveToken(state.Push(new GDSpace())); state.PassChar(c); @@ -115,6 +142,21 @@ internal override void HandleChar(char c, GDReadingState state) case State.CloseBracket: this.ResolveCloseBracket(c, state); break; + + case State.BaseCallPoint: + this.ResolvePoint(c, state); + break; + case State.BaseCallOpenBracket: + this.ResolveOpenBracket(c, state); + break; + case State.BaseCallParameters: + _form.State = State.BaseCallCloseBracket; + state.PushAndPass(BaseCallParameters, c); + break; + case State.BaseCallCloseBracket: + this.ResolveCloseBracket(c, state); + break; + case State.ReturnTypeKeyword: this.ResolveKeyword(c, state); break; @@ -135,11 +177,24 @@ internal override void HandleChar(char c, GDReadingState state) internal override void HandleNewLineChar(GDReadingState state) { + if (_form.State == State.Parameters) + { + _form.State = State.CloseBracket; + state.PushAndPassNewLine(Parameters); + return; + } + + if (_form.State == State.BaseCallParameters) + { + _form.State = State.BaseCallCloseBracket; + state.PushAndPassNewLine(BaseCallParameters); + return; + } + if (_form.StateIndex <= (int)State.Statements) { _form.State = State.Completed; - state.Push(Statements); - state.PassNewLine(); + state.PushAndPassNewLine(Statements); return; } @@ -223,6 +278,13 @@ void ITokenReceiver.HandleReceivedToken(GDOpenBracket token) return; } + if (_form.State == State.BaseCallOpenBracket) + { + _form.State = State.BaseCallParameters; + BaseCallOpenBracket = token; + return; + } + throw new GDInvalidReadingStateException(); } @@ -234,6 +296,12 @@ void ITokenReceiver.HandleReceivedTokenSkip() return; } + if (_form.State == State.BaseCallOpenBracket) + { + _form.State = State.BaseCallParameters; + return; + } + throw new GDInvalidReadingStateException(); } @@ -241,17 +309,30 @@ void ITokenReceiver.HandleReceivedToken(GDCloseBracket token) { if (_form.State == State.CloseBracket) { - _form.State = State.ReturnTypeKeyword; + _form.State = State.BaseCallPoint; CloseBracket = token; return; } + if (_form.State == State.BaseCallCloseBracket) + { + _form.State = State.ReturnTypeKeyword; + BaseCallCloseBracket = token; + return; + } + throw new GDInvalidReadingStateException(); } void ITokenReceiver.HandleReceivedTokenSkip() { if (_form.State == State.CloseBracket) + { + _form.State = State.BaseCallPoint; + return; + } + + if (_form.State == State.BaseCallCloseBracket) { _form.State = State.ReturnTypeKeyword; return; @@ -271,6 +352,28 @@ void IKeywordReceiver.HandleReceivedToken(GDReturnTypeKeywo throw new GDInvalidReadingStateException(); } + void ITokenReceiver.HandleReceivedToken(GDPoint token) + { + if (_form.State == State.BaseCallPoint) + { + _form.State = State.BaseCallOpenBracket; + BaseCallPoint = token; + return; + } + + throw new GDInvalidReadingStateException(); + } + + void ITokenReceiver.HandleReceivedTokenSkip() + { + if (_form.State == State.BaseCallPoint) + { + _form.State = State.ReturnTypeKeyword; + return; + } + + throw new GDInvalidReadingStateException(); + } void IKeywordReceiver.HandleReceivedKeywordSkip() { diff --git a/src/GDShrapt.Reader/Declarations/GDParameterDeclaration.cs b/src/GDShrapt.Reader/Declarations/GDParameterDeclaration.cs index 33cfb0d..282da89 100644 --- a/src/GDShrapt.Reader/Declarations/GDParameterDeclaration.cs +++ b/src/GDShrapt.Reader/Declarations/GDParameterDeclaration.cs @@ -3,7 +3,9 @@ public sealed class GDParameterDeclaration : GDNode, IIdentifierReceiver, ITokenReceiver, - ITypeReceiver + ITypeReceiver, + ITokenReceiver, + IExpressionsReceiver { public GDIdentifier Identifier { @@ -21,19 +23,33 @@ public GDType Type set => _form.Token2 = value; } + internal GDAssign Assign + { + get => _form.Token3; + set => _form.Token3 = value; + } + + public GDExpression DefaultValue + { + get => _form.Token4; + set => _form.Token4 = value; + } + enum State { Identifier, Colon, Type, + Assign, + DefaultValue, Completed } - readonly GDTokensForm _form; + readonly GDTokensForm _form; internal override GDTokensForm Form => _form; public GDParameterDeclaration() { - _form = new GDTokensForm(this); + _form = new GDTokensForm(this); } internal override void HandleChar(char c, GDReadingState state) @@ -52,6 +68,12 @@ internal override void HandleChar(char c, GDReadingState state) case State.Type: this.ResolveType(c, state); break; + case State.Assign: + this.ResolveAssign(c, state); + break; + case State.DefaultValue: + this.ResolveExpression(c, state); + break; default: state.PopAndPass(c); break; @@ -102,7 +124,7 @@ void ITokenReceiver.HandleReceivedTokenSkip() { if (_form.State == State.Colon) { - _form.State = State.Completed; + _form.State = State.Assign; return; } @@ -113,7 +135,7 @@ void ITypeReceiver.HandleReceivedToken(GDType token) { if (_form.State == State.Type) { - _form.State = State.Completed; + _form.State = State.Assign; Type = token; return; } @@ -131,5 +153,51 @@ void ITypeReceiver.HandleReceivedTypeSkip() throw new GDInvalidReadingStateException(); } + + void ITokenReceiver.HandleReceivedToken(GDAssign token) + { + if (_form.State == State.Assign) + { + _form.State = State.DefaultValue; + Assign = token; + return; + } + + throw new GDInvalidReadingStateException(); + } + + void ITokenReceiver.HandleReceivedTokenSkip() + { + if (_form.State == State.Assign) + { + _form.State = State.Completed; + return; + } + + throw new GDInvalidReadingStateException(); + } + + void IExpressionsReceiver.HandleReceivedToken(GDExpression token) + { + if (_form.State == State.DefaultValue) + { + _form.State = State.Completed; + DefaultValue = token; + return; + } + + throw new GDInvalidReadingStateException(); + } + + void IExpressionsReceiver.HandleReceivedExpressionSkip() + { + if (_form.State == State.DefaultValue) + { + _form.State = State.Completed; + return; + } + + throw new GDInvalidReadingStateException(); + } } } \ No newline at end of file diff --git a/src/GDShrapt.Reader/Declarations/GDVariableDeclaration.cs b/src/GDShrapt.Reader/Declarations/GDVariableDeclaration.cs index 68599f7..887bc7c 100644 --- a/src/GDShrapt.Reader/Declarations/GDVariableDeclaration.cs +++ b/src/GDShrapt.Reader/Declarations/GDVariableDeclaration.cs @@ -153,16 +153,13 @@ internal override void HandleChar(char c, GDReadingState state) this.ResolveType(c, state); break; case State.Assign: - state.Push(new GDSingleCharTokenResolver(this)); - state.PassChar(c); + state.PushAndPass(new GDSingleCharTokenResolver(this), c); break; case State.Initializer: - state.Push(new GDExpressionResolver(this)); - state.PassChar(c); + state.PushAndPass(new GDExpressionResolver(this), c); break; case State.SetGet: - state.Push(new GDKeywordResolver(this)); - state.PassChar(c); + state.PushAndPass(new GDKeywordResolver(this), c); break; case State.SetMethod: this.ResolveIdentifier(c, state); @@ -181,8 +178,7 @@ internal override void HandleChar(char c, GDReadingState state) internal override void HandleNewLineChar(GDReadingState state) { - state.Pop(); - state.PassNewLine(); + state.PopAndPassNewLine(); } void IKeywordReceiver.HandleReceivedToken(GDConstKeyword token) diff --git a/src/GDShrapt.Reader/Expressions/GDArrayInitializerExpression.cs b/src/GDShrapt.Reader/Expressions/GDArrayInitializerExpression.cs index 92a3edb..eb70ebb 100644 --- a/src/GDShrapt.Reader/Expressions/GDArrayInitializerExpression.cs +++ b/src/GDShrapt.Reader/Expressions/GDArrayInitializerExpression.cs @@ -57,6 +57,13 @@ internal override void HandleChar(char c, GDReadingState state) internal override void HandleNewLineChar(GDReadingState state) { + if (_form.State == State.Values) + { + _form.State = State.SquareCloseBracket; + state.PushAndPassNewLine(Values); + return; + } + state.PopAndPassNewLine(); } diff --git a/src/GDShrapt.Reader/Expressions/GDCallExression.cs b/src/GDShrapt.Reader/Expressions/GDCallExression.cs index 4319a3c..b0744eb 100644 --- a/src/GDShrapt.Reader/Expressions/GDCallExression.cs +++ b/src/GDShrapt.Reader/Expressions/GDCallExression.cs @@ -68,6 +68,13 @@ internal override void HandleChar(char c, GDReadingState state) internal override void HandleNewLineChar(GDReadingState state) { + if (_form.State == State.Parameters) + { + _form.State = State.CloseBracket; + state.PushAndPassNewLine(Parameters); + return; + } + state.PopAndPassNewLine(); } diff --git a/src/GDShrapt.Reader/Expressions/GDDictionaryInitializerExpression.cs b/src/GDShrapt.Reader/Expressions/GDDictionaryInitializerExpression.cs index 670e40c..16d996d 100644 --- a/src/GDShrapt.Reader/Expressions/GDDictionaryInitializerExpression.cs +++ b/src/GDShrapt.Reader/Expressions/GDDictionaryInitializerExpression.cs @@ -57,8 +57,14 @@ internal override void HandleChar(char c, GDReadingState state) internal override void HandleNewLineChar(GDReadingState state) { - state.Pop(); - state.PassNewLine(); + if (_form.State == State.KeyValues) + { + _form.State = State.FigureCloseBracket; + state.PushAndPassNewLine(KeyValues); + return; + } + + state.PopAndPassNewLine(); } void ITokenReceiver.HandleReceivedToken(GDFigureOpenBracket token) diff --git a/src/GDShrapt.Reader/Expressions/GDGetNodeExpression.cs b/src/GDShrapt.Reader/Expressions/GDGetNodeExpression.cs index f8e8d0a..7793a69 100644 --- a/src/GDShrapt.Reader/Expressions/GDGetNodeExpression.cs +++ b/src/GDShrapt.Reader/Expressions/GDGetNodeExpression.cs @@ -1,10 +1,7 @@ -using System; - -namespace GDShrapt.Reader +namespace GDShrapt.Reader { public sealed class GDGetNodeExpression : GDExpression, - ITokenReceiver, - IPathReceiver + ITokenReceiver { public override int Priority => GDHelper.GetOperationPriority(GDOperationType.GetNode); @@ -14,11 +11,7 @@ internal GDDollar Dollar set => _form.Token0 = value; } - public GDPath Path - { - get => _form.Token1; - set => _form.Token1 = value; - } + public GDPathList Path { get => _form.Token1 ?? (_form.Token1 = new GDPathList()); } enum State { @@ -27,11 +20,11 @@ enum State Completed } - readonly GDTokensForm _form; + readonly GDTokensForm _form; internal override GDTokensForm Form => _form; public GDGetNodeExpression() { - _form = new GDTokensForm(this); + _form = new GDTokensForm(this); } internal override void HandleChar(char c, GDReadingState state) @@ -44,7 +37,8 @@ internal override void HandleChar(char c, GDReadingState state) this.ResolveDollar(c, state); break; case State.Path: - this.ResolvePath(c, state); + _form.State = State.Completed; + state.PushAndPass(Path, c); break; default: state.PopAndPass(c); @@ -79,28 +73,5 @@ void ITokenReceiver.HandleReceivedTokenSkip() throw new GDInvalidReadingStateException(); } - - void IPathReceiver.HandleReceivedToken(GDPath token) - { - if (_form.State == State.Path) - { - _form.State = State.Completed; - Path = token; - return; - } - - throw new GDInvalidReadingStateException(); - } - - void IPathReceiver.HandleReceivedIdentifierSkip() - { - if (_form.State == State.Path) - { - _form.State = State.Completed; - return; - } - - throw new GDInvalidReadingStateException(); - } } } diff --git a/src/GDShrapt.Reader/Expressions/GDMemberOperatorExpression.cs b/src/GDShrapt.Reader/Expressions/GDMemberOperatorExpression.cs index b491592..476b69d 100644 --- a/src/GDShrapt.Reader/Expressions/GDMemberOperatorExpression.cs +++ b/src/GDShrapt.Reader/Expressions/GDMemberOperatorExpression.cs @@ -62,8 +62,7 @@ internal override void HandleChar(char c, GDReadingState state) internal override void HandleNewLineChar(GDReadingState state) { - state.Pop(); - state.PassNewLine(); + state.PopAndPassNewLine(); } protected override GDExpression PriorityRebuildingPass() diff --git a/src/GDShrapt.Reader/Expressions/GDNumberExpression.cs b/src/GDShrapt.Reader/Expressions/GDNumberExpression.cs index b6e92b5..2a16deb 100644 --- a/src/GDShrapt.Reader/Expressions/GDNumberExpression.cs +++ b/src/GDShrapt.Reader/Expressions/GDNumberExpression.cs @@ -40,14 +40,12 @@ internal override void HandleChar(char c, GDReadingState state) return; } - state.Pop(); - state.PassChar(c); + state.PopAndPass(c); } internal override void HandleNewLineChar(GDReadingState state) { - state.Pop(); - state.PassNewLine(); + state.PopAndPassNewLine(); } } } diff --git a/src/GDShrapt.Reader/GDReadingState.cs b/src/GDShrapt.Reader/GDReadingState.cs index 9b88003..d583387 100644 --- a/src/GDShrapt.Reader/GDReadingState.cs +++ b/src/GDShrapt.Reader/GDReadingState.cs @@ -49,7 +49,7 @@ public void CompleteReading() /// public void PassNewLine() { - CurrentReader.HandleNewLineChar(this); + CurrentReader?.HandleNewLineChar(this); } /// @@ -63,7 +63,7 @@ public void PassString(string s) public void PassSharpChar() { - CurrentReader.HandleSharpChar(this); + CurrentReader?.HandleSharpChar(this); } /// diff --git a/src/GDShrapt.Reader/GDScriptReader.cs b/src/GDShrapt.Reader/GDScriptReader.cs index a0f47c3..53f65ad 100644 --- a/src/GDShrapt.Reader/GDScriptReader.cs +++ b/src/GDShrapt.Reader/GDScriptReader.cs @@ -67,7 +67,7 @@ public GDExpression ParseExpression(string content) var state = new GDReadingState(Settings); var receiver = new GDReceiver(); - state.Push(new GDExpressionResolver(receiver)); + state.Push(new GDStartTrimmingResolver(receiver, () => new GDExpressionResolver(receiver))); var buffer = new char[Settings.ReadBufferSize]; int count = 0; diff --git a/src/GDShrapt.Reader/GDTokensForm.cs b/src/GDShrapt.Reader/GDTokensForm.cs index 6ae0071..5fc047e 100644 --- a/src/GDShrapt.Reader/GDTokensForm.cs +++ b/src/GDShrapt.Reader/GDTokensForm.cs @@ -561,6 +561,59 @@ public GDTokensForm(GDNode owner) public T12 Token12 { get => Get(12); set => Set(value, 12); } } + internal class GDTokensForm : GDTokensForm + where STATE : struct, System.Enum + where T0 : GDSyntaxToken + where T1 : GDSyntaxToken + where T2 : GDSyntaxToken + where T3 : GDSyntaxToken + where T4 : GDSyntaxToken + where T5 : GDSyntaxToken + where T6 : GDSyntaxToken + where T7 : GDSyntaxToken + where T8 : GDSyntaxToken + where T9 : GDSyntaxToken + where T10 : GDSyntaxToken + where T11 : GDSyntaxToken + where T12 : GDSyntaxToken + where T13 : GDSyntaxToken + { + public GDTokensForm(GDNode owner) + : base(owner, 14) + { + + } + + public void AddBeforeToken0(GDSyntaxToken token) => AddMiddle(token, 0); + public T0 Token0 { get => Get(0); set => Set(value, 0); } + public void AddBeforeToken1(GDSyntaxToken token) => AddMiddle(token, 1); + public T1 Token1 { get => Get(1); set => Set(value, 1); } + public void AddBeforeToken2(GDSyntaxToken token) => AddMiddle(token, 2); + public T2 Token2 { get => Get(2); set => Set(value, 2); } + public void AddBeforeToken3(GDSyntaxToken token) => AddMiddle(token, 3); + public T3 Token3 { get => Get(3); set => Set(value, 3); } + public void AddBeforeToken4(GDSyntaxToken token) => AddMiddle(token, 4); + public T4 Token4 { get => Get(4); set => Set(value, 4); } + public void AddBeforeToken5(GDSyntaxToken token) => AddMiddle(token, 5); + public T5 Token5 { get => Get(5); set => Set(value, 5); } + public void AddBeforeToken6(GDSyntaxToken token) => AddMiddle(token, 6); + public T6 Token6 { get => Get(6); set => Set(value, 6); } + public void AddBeforeToken7(GDSyntaxToken token) => AddMiddle(token, 7); + public T7 Token7 { get => Get(7); set => Set(value, 7); } + public void AddBeforeToken8(GDSyntaxToken token) => AddMiddle(token, 8); + public T8 Token8 { get => Get(8); set => Set(value, 8); } + public void AddBeforeToken9(GDSyntaxToken token) => AddMiddle(token, 9); + public T9 Token9 { get => Get(9); set => Set(value, 9); } + public void AddBeforeToken10(GDSyntaxToken token) => AddMiddle(token, 10); + public T10 Token10 { get => Get(10); set => Set(value, 10); } + public void AddBeforeToken11(GDSyntaxToken token) => AddMiddle(token, 11); + public T11 Token11 { get => Get(11); set => Set(value, 11); } + public void AddBeforeToken12(GDSyntaxToken token) => AddMiddle(token, 12); + public T12 Token12 { get => Get(12); set => Set(value, 12); } + public void AddBeforeToken13(GDSyntaxToken token) => AddMiddle(token, 13); + public T13 Token13 { get => Get(13); set => Set(value, 13); } + } + internal abstract class GDTokensForm : GDTokensForm where STATE : struct, System.Enum { diff --git a/src/GDShrapt.Reader/Lists/GDCommaSeparatedList.cs b/src/GDShrapt.Reader/Lists/GDCommaSeparatedList.cs index 3f7c46c..7a230ef 100644 --- a/src/GDShrapt.Reader/Lists/GDCommaSeparatedList.cs +++ b/src/GDShrapt.Reader/Lists/GDCommaSeparatedList.cs @@ -2,7 +2,7 @@ { public abstract class GDCommaSeparatedList : GDSeparatedList, ITokenReceiver - where NODE : GDNode + where NODE : GDSyntaxToken { bool _checkedNextNode; internal abstract GDReader ResolveNode(); diff --git a/src/GDShrapt.Reader/Lists/GDExportParametersList.cs b/src/GDShrapt.Reader/Lists/GDExportParametersList.cs index 4f7ee65..399fe07 100644 --- a/src/GDShrapt.Reader/Lists/GDExportParametersList.cs +++ b/src/GDShrapt.Reader/Lists/GDExportParametersList.cs @@ -1,31 +1,17 @@ namespace GDShrapt.Reader { - public sealed class GDExportParametersList : GDSeparatedList, + public sealed class GDExportParametersList : GDCommaSeparatedList, IDataTokenReceiver, ITokenReceiver { - internal override void HandleChar(char c, GDReadingState state) + internal override bool IsStopChar(char c) { - if (this.ResolveStyleToken(c, state)) - return; - - if (this.ResolveInvalidToken(c, state, x => (x.IsDataStartCharToken() && !IsExpressionStopChar(x)) || x == ')' || x == ',' || x.IsSpace())) - return; - - if (IsExpressionStopChar(c)) - { - if (c == ')') - { - state.PopAndPass(c); - return; - } + return !c.IsDataStartCharToken(); + } - this.ResolveComma(c, state); - } - else - { - this.ResolveDataToken(c, state); - } + internal override GDReader ResolveNode() + { + return new GDDataTokensResolver(this); } internal override void HandleNewLineChar(GDReadingState state) diff --git a/src/GDShrapt.Reader/Lists/GDPathList.cs b/src/GDShrapt.Reader/Lists/GDPathList.cs new file mode 100644 index 0000000..3623340 --- /dev/null +++ b/src/GDShrapt.Reader/Lists/GDPathList.cs @@ -0,0 +1,54 @@ +namespace GDShrapt.Reader +{ + public sealed class GDPathList : GDSeparatedList, + IIdentifierReceiver, + ITokenReceiver + { + bool _switch; + bool _ended; + + internal override void HandleChar(char c, GDReadingState state) + { + if (this.ResolveStyleToken(c, state)) + return; + + if (_ended) + { + state.PopAndPass(c); + return; + } + + if (!_switch) + this.ResolveIdentifier(c, state); + else + this.ResolveRightSlash(c, state); + } + + internal override void HandleNewLineChar(GDReadingState state) + { + _ended = true; + state.PopAndPassNewLine(); + } + + void IIdentifierReceiver.HandleReceivedToken(GDIdentifier token) + { + _switch = !_switch; + ListForm.Add(token); + } + + void ITokenReceiver.HandleReceivedToken(GDRightSlash token) + { + _switch = !_switch; + ListForm.Add(token); + } + void IIdentifierReceiver.HandleReceivedIdentifierSkip() + { + _ended = true; + } + + void ITokenReceiver.HandleReceivedTokenSkip() + { + _ended = true; + } + } +} diff --git a/src/GDShrapt.Reader/Resolvers/GDClassMembersResolver.cs b/src/GDShrapt.Reader/Resolvers/GDClassMembersResolver.cs index 22b4cdb..18ff817 100644 --- a/src/GDShrapt.Reader/Resolvers/GDClassMembersResolver.cs +++ b/src/GDShrapt.Reader/Resolvers/GDClassMembersResolver.cs @@ -31,16 +31,33 @@ internal override void HandleCharAfterIntendation(char c, GDReadingState state) return; } - if (!IsSpace(c)) + if (char.IsLetter(c)) { _sequenceBuilder.Append(c); } else { - var sequence = _sequenceBuilder.ToString(); - ResetSequence(); - Complete(state, sequence); - state.PassChar(c); + if (_sequenceBuilder.Length > 0) + { + var sequence = _sequenceBuilder.ToString(); + ResetSequence(); + Complete(state, sequence); + state.PassChar(c); + } + else + { + if (IsSpace(c)) + { + Owner.HandleReceivedToken(state.Push(new GDSpace())); + } + else + { + SendIntendationTokensToOwner(); + Owner.HandleReceivedToken(state.Push(new GDInvalidToken(x => char.IsLetter(x) || x.IsSpace() || x.IsNewLine()))); + } + + state.PassChar(c); + } } } diff --git a/src/GDShrapt.Reader/Resolvers/GDDataTokenResolver.cs b/src/GDShrapt.Reader/Resolvers/GDDataTokensResolver.cs similarity index 75% rename from src/GDShrapt.Reader/Resolvers/GDDataTokenResolver.cs rename to src/GDShrapt.Reader/Resolvers/GDDataTokensResolver.cs index 2a10728..c8107a2 100644 --- a/src/GDShrapt.Reader/Resolvers/GDDataTokenResolver.cs +++ b/src/GDShrapt.Reader/Resolvers/GDDataTokensResolver.cs @@ -1,10 +1,10 @@ namespace GDShrapt.Reader { - internal class GDDataTokenResolver : GDResolver + internal class GDDataTokensResolver : GDResolver { new IDataTokenReceiver Owner { get; } - public GDDataTokenResolver(IDataTokenReceiver owner) + public GDDataTokensResolver(IDataTokenReceiver owner) : base(owner) { Owner = owner; @@ -19,7 +19,12 @@ internal override void HandleChar(char c, GDReadingState state) return; } - state.Pop(); + if (IsNumberStartChar(c) || c == '-') + { + Owner.HandleReceivedToken(state.Push(new GDNumber())); + state.PassChar(c); + return; + } if (IsStringStartChar(c)) { @@ -36,7 +41,7 @@ internal override void HandleChar(char c, GDReadingState state) } Owner.HandleReceivedTokenSkip(); - state.PassChar(c); + state.PopAndPass(c); } internal override void HandleNewLineChar(GDReadingState state) diff --git a/src/GDShrapt.Reader/Resolvers/GDExportResolver.cs b/src/GDShrapt.Reader/Resolvers/GDExportResolver.cs index 4982649..92f42e4 100644 --- a/src/GDShrapt.Reader/Resolvers/GDExportResolver.cs +++ b/src/GDShrapt.Reader/Resolvers/GDExportResolver.cs @@ -11,15 +11,16 @@ public GDExportResolver(IExportReceiver owner) Owner = owner; } - protected override void OnFail() + protected override void OnFail(GDReadingState state) { Owner.HandleReceivedExportSkip(); } - protected override void OnMatch() + protected override void OnMatch(GDReadingState state) { var declaration = new GDExportDeclaration(); declaration.SendKeyword(new GDExportKeyword()); Owner.HandleReceivedExport(declaration); + state.Push(declaration); } } } diff --git a/src/GDShrapt.Reader/Resolvers/GDExpressionResolver.cs b/src/GDShrapt.Reader/Resolvers/GDExpressionResolver.cs index e759f3b..b8a0889 100644 --- a/src/GDShrapt.Reader/Resolvers/GDExpressionResolver.cs +++ b/src/GDShrapt.Reader/Resolvers/GDExpressionResolver.cs @@ -48,6 +48,13 @@ internal override void HandleChar(char c, GDReadingState state) { if (!CheckKeywords(state)) CompleteExpression(state); + + if (_lastSpace != null) + { + Owner.HandleReceivedToken(_lastSpace); + _lastSpace = null; + } + state.PassChar(c); return; } @@ -112,7 +119,7 @@ internal override void HandleChar(char c, GDReadingState state) if (c == '.') { - PushAndSave(state, new GDMemberOperatorExpression()); + PushAndSwap(state, new GDMemberOperatorExpression()); state.PassChar(c); return; } @@ -302,6 +309,8 @@ private void CompleteExpression(GDReadingState state) _expression = null; + state.Pop(); + if (last != null) { // Handle negative number from Negate operator and GDNumberExpression. It must be zero tokens between negate and the number @@ -312,9 +321,17 @@ private void CompleteExpression(GDReadingState state) { numberExpression.Number.Negate(); last = operatorExpression.TargetExpression; - } - Owner.HandleReceivedToken(last.RebuildRootOfPriorityIfNeeded()); + Owner.HandleReceivedToken(last.RebuildRootOfPriorityIfNeeded()); + + // Send all tokens after Single operator to current reader + foreach (var token in operatorExpression.Form.GetAllTokensAfter(1)) + state.PassString(token.ToString()); + } + else + { + Owner.HandleReceivedToken(last.RebuildRootOfPriorityIfNeeded()); + } } else Owner.HandleReceivedExpressionSkip(); @@ -324,23 +341,15 @@ private void CompleteExpression(GDReadingState state) Owner.HandleReceivedToken(_lastSpace); _lastSpace = null; } - - state.Pop(); } private void PushAndSwap(GDReadingState state, T node) where T: GDExpression, IExpressionsReceiver { if (_expression != null) - { node.SendExpression(_expression); - - /* if (_lastSpace != null) - { - _expression.SendSpace(_lastSpace); - _lastSpace = null; - }*/ - } + else + node.HandleReceivedExpressionSkip(); if (_lastSpace != null) { diff --git a/src/GDShrapt.Reader/Resolvers/GDIntendedResolver.cs b/src/GDShrapt.Reader/Resolvers/GDIntendedResolver.cs index d6c9d78..93ad9d8 100644 --- a/src/GDShrapt.Reader/Resolvers/GDIntendedResolver.cs +++ b/src/GDShrapt.Reader/Resolvers/GDIntendedResolver.cs @@ -12,6 +12,7 @@ internal abstract class GDIntendedResolver : GDResolver bool _lineIntendationEnded; int _spaceCounter; bool _inComment; + bool _intendationTokensSent; new IIntendationReceiver Owner { get; } @@ -84,7 +85,8 @@ bool HandleIntendation(char c, GDReadingState state) { _lineIntendationEnded = true; - if (LineIntendationThreshold != _lineIntendation) + // The 'end of the block' condition + if (LineIntendationThreshold > _lineIntendation) { state.Pop(); @@ -95,10 +97,15 @@ bool HandleIntendation(char c, GDReadingState state) state.PassChar(c); return true; } - } + if (LineIntendationThreshold < _lineIntendation) + { + // TODO: warning invalid extra intendation + } + } } + // It's OK return false; } @@ -123,6 +130,11 @@ internal override void HandleSharpChar(GDReadingState state) protected void SendIntendationTokensToOwner() { + if (_intendationTokensSent) + return; + + _intendationTokensSent = true; + GDComment comment = null; GDSpace space = null; @@ -208,6 +220,7 @@ protected void ResetIntendation() _lineIntendation = 0; _lineIntendationEnded = false; _spaceCounter = 0; + _intendationTokensSent = false; } } } \ No newline at end of file diff --git a/src/GDShrapt.Reader/Resolvers/GDKeywordResolver.cs b/src/GDShrapt.Reader/Resolvers/GDKeywordResolver.cs index 05447f3..7a17cf6 100644 --- a/src/GDShrapt.Reader/Resolvers/GDKeywordResolver.cs +++ b/src/GDShrapt.Reader/Resolvers/GDKeywordResolver.cs @@ -15,12 +15,12 @@ public GDKeywordResolver(IKeywordReceiver owner) Owner = owner; } - protected override void OnMatch() + protected override void OnMatch(GDReadingState state) { Owner.HandleReceivedToken(new KEYWORD()); } - protected override void OnFail() + protected override void OnFail(GDReadingState state) { Owner.HandleReceivedKeywordSkip(); } diff --git a/src/GDShrapt.Reader/Resolvers/GDResolvingHelper.cs b/src/GDShrapt.Reader/Resolvers/GDResolvingHelper.cs index 7f13e23..f8270bf 100644 --- a/src/GDShrapt.Reader/Resolvers/GDResolvingHelper.cs +++ b/src/GDShrapt.Reader/Resolvers/GDResolvingHelper.cs @@ -56,20 +56,6 @@ public static bool ResolveInvalidToken(this ITokenReceiver receiver, char c, GDR return true; } - public static bool ResolvePath(this IPathReceiver receiver, char c, GDReadingState state) - { - if (!IsSpace(c)) - { - receiver.HandleReceivedToken(state.Push(new GDPath())); - state.PassChar(c); - return true; - } - - receiver.HandleReceivedIdentifierSkip(); - state.PassChar(c); - return false; - } - public static void ResolveKeyword(this IKeywordReceiver receiver, char c, GDReadingState state) where T : GDSyntaxToken, IGDKeywordToken, new() { @@ -102,6 +88,20 @@ public static bool ResolveAt(this ITokenReceiver receiver, char c, GDReadi return result; } + public static bool ResolveRightSlash(this ITokenReceiver receiver, char c, GDReadingState state) + { + var result = c == '/'; + if (result) + receiver.HandleReceivedToken(new GDRightSlash()); + else + { + receiver.HandleReceivedTokenSkip(); + state.PassChar(c); + } + return result; + } + + public static bool ResolveColon(this ITokenReceiver receiver, char c, GDReadingState state) { var result = c == ':'; @@ -309,7 +309,7 @@ public static void ResolveExpression(this IExpressionsReceiver receiver, char c, public static bool ResolveDataToken(this IDataTokenReceiver receiver, char c, GDReadingState state) { - if (IsNumberStartChar(c)) + if (IsNumberStartChar(c) || c == '-') { receiver.HandleReceivedToken(state.Push(new GDNumber())); state.PassChar(c); @@ -415,7 +415,7 @@ public static bool ResolveString(this IStringReceiver receiver, char c, GDReadin return false; } - public static bool IsDataStartCharToken(this char c) => IsIdentifierStartChar(c) || IsNumberStartChar(c) || IsStringStartChar(c); + public static bool IsDataStartCharToken(this char c) => IsIdentifierStartChar(c) || IsNumberStartChar(c) || IsStringStartChar(c) || IsNumberStartChar(c) || c == '-'; public static bool IsCommentStartChar(this char c) => c == '#'; public static bool IsSpace(this char c) => c == ' ' || c == '\t'; public static bool IsIdentifierStartChar(this char c) => c == '_' || char.IsLetter(c); diff --git a/src/GDShrapt.Reader/Resolvers/GDSequenceResolver.cs b/src/GDShrapt.Reader/Resolvers/GDSequenceResolver.cs index dcbc7c0..be387ec 100644 --- a/src/GDShrapt.Reader/Resolvers/GDSequenceResolver.cs +++ b/src/GDShrapt.Reader/Resolvers/GDSequenceResolver.cs @@ -20,13 +20,13 @@ internal override void HandleChar(char c, GDReadingState state) if (Index == s.Length) { state.Pop(); - OnMatch(); + OnMatch(state); } return; } state.Pop(); - OnFail(); + OnFail(state); for (int i = 0; i < Index - 1; i++) state.PassChar(s[i]); @@ -34,8 +34,8 @@ internal override void HandleChar(char c, GDReadingState state) state.PassChar(c); } - protected abstract void OnFail(); - protected abstract void OnMatch(); + protected abstract void OnFail(GDReadingState state); + protected abstract void OnMatch(GDReadingState state); internal override void HandleNewLineChar(GDReadingState state) { @@ -45,7 +45,7 @@ internal override void HandleNewLineChar(GDReadingState state) internal override void HandleSharpChar(GDReadingState state) { state.Pop(); - OnFail(); + OnFail(state); var s = Sequence; @@ -56,7 +56,7 @@ internal override void HandleSharpChar(GDReadingState state) internal override void ForceComplete(GDReadingState state) { state.Pop(); - OnFail(); + OnFail(state); var s = Sequence; diff --git a/src/GDShrapt.Reader/Resolvers/GDStartTrimmingResolver.cs b/src/GDShrapt.Reader/Resolvers/GDStartTrimmingResolver.cs new file mode 100644 index 0000000..acbd613 --- /dev/null +++ b/src/GDShrapt.Reader/Resolvers/GDStartTrimmingResolver.cs @@ -0,0 +1,29 @@ +using System; + +namespace GDShrapt.Reader +{ + internal sealed class GDStartTrimmingResolver : GDResolver + { + private readonly Func _factory; + + public GDStartTrimmingResolver(IStyleTokensReceiver owner, Func factory) + : base(owner) + { + _factory = factory; + } + + internal override void HandleChar(char c, GDReadingState state) + { + if (Owner.ResolveStyleToken(c, state)) + return; + + state.Pop(); + state.PushAndPass(_factory(), c); + } + + internal override void HandleNewLineChar(GDReadingState state) + { + Owner.HandleReceivedToken(new GDNewLine()); + } + } +} diff --git a/src/GDShrapt.Reader/Resolvers/GDStatementsResolver.cs b/src/GDShrapt.Reader/Resolvers/GDStatementsResolver.cs index 0503801..317cfe0 100644 --- a/src/GDShrapt.Reader/Resolvers/GDStatementsResolver.cs +++ b/src/GDShrapt.Reader/Resolvers/GDStatementsResolver.cs @@ -46,7 +46,7 @@ internal override void HandleCharAfterIntendation(char c, GDReadingState state) } else { - Owner.HandleReceivedToken(state.Push(new GDInvalidToken(x => char.IsLetter(x)))); + CompleteAsExpressionStatement(state); state.PassChar(c); } } @@ -150,18 +150,24 @@ private GDStatement CompleteAsStatement(GDReadingState state, string sequence) return statement; } - private GDExpressionStatement CompleteAsExpressionStatement(GDReadingState state, string sequence) + private GDExpressionStatement CompleteAsExpressionStatement(GDReadingState state) { var statement = new GDExpressionStatement(); SendIntendationTokensToOwner(); Owner.HandleReceivedToken(statement); state.Push(statement); - state.PassString(sequence); return statement; } + private GDExpressionStatement CompleteAsExpressionStatement(GDReadingState state, string sequence) + { + var statement = CompleteAsExpressionStatement(state); + state.PassString(sequence); + return statement; + } + internal override void ForceComplete(GDReadingState state) { if (_sequenceBuilder.Length > 0) diff --git a/src/GDShrapt.Reader/SimpleTokens/GDNumber.cs b/src/GDShrapt.Reader/SimpleTokens/GDNumber.cs index 8eff58d..46b4199 100644 --- a/src/GDShrapt.Reader/SimpleTokens/GDNumber.cs +++ b/src/GDShrapt.Reader/SimpleTokens/GDNumber.cs @@ -103,6 +103,12 @@ internal override void HandleChar(char c, GDReadingState state) } else { + if (_stringBuilder.Length == 0 && c == '-') + { + _stringBuilder.Append(c); + return; + } + if (_digitsCounter == 1 && (c == 'b' || c == 'x')) { @@ -157,16 +163,14 @@ internal override void HandleChar(char c, GDReadingState state) } CompleteString(); - state.Pop(); - state.PassChar(c); + state.PopAndPass(c); } } internal override void HandleNewLineChar(GDReadingState state) { CompleteString(); - state.Pop(); - state.PassNewLine(); + state.PopAndPassNewLine(); } internal override void ForceComplete(GDReadingState state) diff --git a/src/GDShrapt.Reader/SimpleTokens/GDPath.cs b/src/GDShrapt.Reader/SimpleTokens/GDPath.cs deleted file mode 100644 index 63e94f8..0000000 --- a/src/GDShrapt.Reader/SimpleTokens/GDPath.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace GDShrapt.Reader -{ - public sealed class GDPath : GDCharSequence - { - internal override bool CanAppendChar(char c, GDReadingState state) - { - return !IsSpace(c); - } - } -} diff --git a/src/GDShrapt.Reader/SimpleTokens/GDRightSlash.cs b/src/GDShrapt.Reader/SimpleTokens/GDRightSlash.cs new file mode 100644 index 0000000..13b648f --- /dev/null +++ b/src/GDShrapt.Reader/SimpleTokens/GDRightSlash.cs @@ -0,0 +1,7 @@ +namespace GDShrapt.Reader +{ + public sealed class GDRightSlash : GDSingleCharToken + { + public override char Char => '/'; + } +} diff --git a/src/GDShrapt.Reader/SimpleTokens/GDString.cs b/src/GDShrapt.Reader/SimpleTokens/GDString.cs index f53bdf1..21a5a69 100644 --- a/src/GDShrapt.Reader/SimpleTokens/GDString.cs +++ b/src/GDShrapt.Reader/SimpleTokens/GDString.cs @@ -8,7 +8,7 @@ public sealed class GDString : GDLiteralToken readonly StringBuilder _stringBuilder = new StringBuilder(); bool _stringStarted; int _boundingCharsCounter; - + bool _escapeNextChar; public bool Multiline { get; set; } public GDStringBoundingChar BoundingChar { get; set; } public string Value { get; set; } @@ -36,29 +36,51 @@ internal override void HandleChar(char c, GDReadingState state) else { var bc = GetBoundingChar(); - if (c != bc) + + if (c == '\\') { - _stringStarted = true; + if (!_escapeNextChar) + { + _escapeNextChar = true; + _stringStarted = true; - for (int i = 0; i < _boundingCharsCounter -1; i++) - _stringBuilder.Append(bc); + for (int i = 0; i < _boundingCharsCounter - 1; i++) + _stringBuilder.Append(bc); - _boundingCharsCounter = 0; - _stringBuilder.Append(c); + _boundingCharsCounter = 0; + _stringBuilder.Append(c); + } + else + { + _escapeNextChar = false; + _stringBuilder.Append(c); + } } else { - _boundingCharsCounter++; - - if (_boundingCharsCounter == 3) + if (c != bc) { - Multiline = true; - _boundingCharsCounter = 0; _stringStarted = true; + + for (int i = 0; i < _boundingCharsCounter - 1; i++) + _stringBuilder.Append(bc); + + _boundingCharsCounter = 0; + _stringBuilder.Append(c); } + else + { + _boundingCharsCounter++; - } + if (_boundingCharsCounter == 3) + { + Multiline = true; + _boundingCharsCounter = 0; + _stringStarted = true; + } + } + } } return; @@ -66,31 +88,54 @@ internal override void HandleChar(char c, GDReadingState state) var boundingChar = GetBoundingChar(); - if (c == boundingChar) + if (c == '\\') { - if (!Multiline) + if (!_escapeNextChar) { - Value = _stringBuilder.ToString(); - state.Pop(); + _escapeNextChar = true; + + for (int i = 0; i < _boundingCharsCounter; i++) + _stringBuilder.Append(boundingChar); + + _boundingCharsCounter = 0; + + _stringBuilder.Append(c); } else { - _boundingCharsCounter++; - - if (_boundingCharsCounter == 3) + _escapeNextChar = false; + _stringBuilder.Append(c); + } + } + else + { + if (c == boundingChar && !_escapeNextChar) + { + if (!Multiline) { Value = _stringBuilder.ToString(); state.Pop(); } + else + { + _boundingCharsCounter++; + + if (_boundingCharsCounter == 3) + { + Value = _stringBuilder.ToString(); + state.Pop(); + } + } } - } - else - { - for (int i = 0; i < _boundingCharsCounter; i++) - _stringBuilder.Append(boundingChar); + else + { + for (int i = 0; i < _boundingCharsCounter; i++) + _stringBuilder.Append(boundingChar); - _boundingCharsCounter = 0; - _stringBuilder.Append(c); + _boundingCharsCounter = 0; + _escapeNextChar = false; + _stringBuilder.Append(c); + } } } diff --git a/src/GDShrapt.Reader/Statements/GDExpressionStatement.cs b/src/GDShrapt.Reader/Statements/GDExpressionStatement.cs index 6e05a54..3aa1325 100644 --- a/src/GDShrapt.Reader/Statements/GDExpressionStatement.cs +++ b/src/GDShrapt.Reader/Statements/GDExpressionStatement.cs @@ -1,6 +1,8 @@ namespace GDShrapt.Reader { - public sealed class GDExpressionStatement : GDStatement, IExpressionsReceiver + public sealed class GDExpressionStatement : GDStatement, + IExpressionsReceiver, + ITokenReceiver { public GDExpression Expression { @@ -8,47 +10,62 @@ public GDExpression Expression set => _form.Token0 = value; } + internal GDSemiColon SemiColon + { + get => _form.Token1; + set => _form.Token1 = value; + } + enum State { Expression, + SemiColon, Completed } - readonly GDTokensForm _form; + readonly GDTokensForm _form; internal override GDTokensForm Form => _form; internal GDExpressionStatement(int lineIntendation) : base(lineIntendation) { - _form = new GDTokensForm(this); + _form = new GDTokensForm(this); } public GDExpressionStatement() { - _form = new GDTokensForm(this); + _form = new GDTokensForm(this); } internal override void HandleChar(char c, GDReadingState state) { - if (_form.State == State.Expression) - state.Push(new GDExpressionResolver(this)); - else - state.Pop(); - - state.PassChar(c); + switch (_form.State) + { + case State.Expression: + if (!this.ResolveStyleToken(c, state)) + this.ResolveExpression(c, state); + break; + case State.SemiColon: + if (!this.ResolveStyleToken(c, state)) + this.ResolveSemiColon(c, state); + break; + default: + if (!this.ResolveStyleToken(c, state)) + this.ResolveInvalidToken(c, state, x => !x.IsSpace()); + break; + } } internal override void HandleNewLineChar(GDReadingState state) { - state.Pop(); - state.PassNewLine(); + state.PopAndPassNewLine(); } void IExpressionsReceiver.HandleReceivedToken(GDExpression token) { if (_form.State == State.Expression) { - _form.State = State.Completed; + _form.State = State.SemiColon; Expression = token; return; } @@ -66,5 +83,28 @@ void IExpressionsReceiver.HandleReceivedExpressionSkip() throw new GDInvalidReadingStateException(); } + + void ITokenReceiver.HandleReceivedToken(GDSemiColon token) + { + if (_form.State == State.SemiColon) + { + _form.State = State.Completed; + SemiColon = token; + return; + } + + throw new GDInvalidReadingStateException(); + } + + void ITokenReceiver.HandleReceivedTokenSkip() + { + if (_form.State == State.SemiColon) + { + _form.State = State.Completed; + return; + } + + throw new GDInvalidReadingStateException(); + } } } \ No newline at end of file