diff --git a/.github/scripts/build-and-pack-nuget.sh b/.github/scripts/build-and-pack-nuget.sh index 2bfc0501..a19f08b8 100644 --- a/.github/scripts/build-and-pack-nuget.sh +++ b/.github/scripts/build-and-pack-nuget.sh @@ -31,8 +31,8 @@ can_build_project() { # Get the dependencies - dotnet list outputs them with paths like ..\ProjectName\ProjectName.csproj # We need to extract just the project name from the filename local deps=$(dotnet list "$project" reference 2>/dev/null | grep '\.csproj$' | while read -r line; do - # Remove everything up to the last backslash, then remove .csproj extension - echo "$line" | sed 's/.*\\//' | sed 's/\.csproj$//' + # Remove everything up to the last slash (forward or back), then remove .csproj extension + echo "$line" | sed 's/.*[\\\/]//' | sed 's/\.csproj$//' done | tr '\n' ' ') for dep in $deps; do @@ -72,7 +72,7 @@ while [ -n "$REMAINING_PROJECTS" ] && [ $ITERATION -lt $MAX_ITERATIONS ]; do echo "$STILL_REMAINING" | tr ';' '\n' | while read -r p; do if [ -n "$p" ]; then echo " - $(basename $(dirname "$p"))" - echo " Dependencies: $(dotnet list "$p" reference 2>/dev/null | grep -E "^\s+.*\.csproj" | sed 's/.*\///' | sed 's/\.csproj.*//' | tr '\n' ' ')" + echo " Dependencies: $(dotnet list "$p" reference 2>/dev/null | grep -E "^\s+.*\.csproj" | sed 's/.*[\\\/]//' | sed 's/\.csproj.*//' | tr '\n' ' ')" fi done echo "Already built: $BUILT_PROJECTS" @@ -159,10 +159,10 @@ while [ -n "$REMAINING_PROJECTS" ] && [ $ITERATION -lt $MAX_ITERATIONS ]; do dotnet restore "$project" -p:Configuration=Release --verbosity minimal --force --no-cache # Build the project - dotnet build "$project" --configuration Release --no-restore --verbosity minimal + dotnet build "$project" -p:PACK=true -p:WarningLevel=0 -p:RunAnalyzers=false -p:SuppressTfmSupportBuildWarnings=true --configuration Release --no-restore --verbosity minimal # Pack directly to artifacts with project name folder structure for proper NuGet feed - dotnet pack "$project" --configuration Release --no-build --output "./artifacts/${PROJECT_NAME}" -p:PACK=true --verbosity minimal + dotnet pack "$project" -p:PACK=true --configuration Release --no-build --output "./artifacts/${PROJECT_NAME}" --verbosity minimal fi BUILT_PROJECTS="${BUILT_PROJECTS}${PROJECT_NAME};" diff --git a/Directory.Build.props b/Directory.Build.props index ae833a36..cfc5905e 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -6,7 +6,7 @@ - + diff --git a/Directory.Packages.props b/Directory.Packages.props index b4499b70..eaf391c7 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -4,12 +4,14 @@ + + @@ -30,21 +32,21 @@ - - - - - - + + + + + + - - - - + + + + - - - + + + \ No newline at end of file diff --git a/NuGet.config b/NuGet.config index 8a2bf809..d4b82f99 100644 --- a/NuGet.config +++ b/NuGet.config @@ -16,9 +16,9 @@ - + diff --git a/docs/changelogs/War3Net.Build.Core.changelog.md b/docs/changelogs/War3Net.Build.Core.changelog.md index 806477fd..95d5542a 100644 --- a/docs/changelogs/War3Net.Build.Core.changelog.md +++ b/docs/changelogs/War3Net.Build.Core.changelog.md @@ -1,5 +1,13 @@ # War3Net.Build.Core Changelog +## v6.0.0 +### Breaking changes +- The EscapedStringProvider class has been moved to War3Net.CodeAnalysis.Jass. +### Changes +- Added W3MathF class. +### Bugfixes +- Fixed exception when deserializing MapInfo.EditorVersion as JSON string. + ## v1.5.3 ### Changes - Add UseNewFormat property to MapCustomTextTriggers. diff --git a/docs/changelogs/War3Net.Build.changelog.md b/docs/changelogs/War3Net.Build.changelog.md index 00a17935..017ed5dd 100644 --- a/docs/changelogs/War3Net.Build.changelog.md +++ b/docs/changelogs/War3Net.Build.changelog.md @@ -1,5 +1,11 @@ # War3Net.Build Changelog +## v6.0.0 +### Breaking changes +- MapScriptBuilder methods have been renamed and now write to an IndentedTextWriter instead of returning a syntax class. +- TriggerRenderer and TriggerRendererContext now expect an IndentedTextWriter instead of JassRenderer/TextWriter. +- MapScriptBuilder's C# api methods have been removed, you can now use the GenerateGlobals methods and manually transpile to C#. + ## v1.5.0 ### Changes - Support parsing and serializing .wtg files. diff --git a/docs/changelogs/War3Net.CodeAnalysis.Jass.changelog.md b/docs/changelogs/War3Net.CodeAnalysis.Jass.changelog.md new file mode 100644 index 00000000..cc5b44cd --- /dev/null +++ b/docs/changelogs/War3Net.CodeAnalysis.Jass.changelog.md @@ -0,0 +1,5 @@ +# War3Net.CodeAnalysis.Jass Changelog + +## v6.0.0 +### Breaking changes +- Breaking changes have been documented in [the migration guide](../guides/jass-migration-guide-v5-to-v6.md) diff --git a/docs/guides/jass-migration-guide-v5-to-v6.md b/docs/guides/jass-migration-guide-v5-to-v6.md new file mode 100644 index 00000000..aa854aa5 --- /dev/null +++ b/docs/guides/jass-migration-guide-v5-to-v6.md @@ -0,0 +1,231 @@ +# War3Net.CodeAnalysis.Jass v5 -> v6 migration guide + +## Overview + +War3Net.CodeAnalysis.Jass v6.0.0 introduced major breaking changes to make the library better suited for certain use cases. + +New classes have been added for tokens and trivia, making it possible to model a JASS script with 100% accuracy. + +This guide contains mapping tables for migrating your code from v5 to v6. + +## Syntax changes + +
New types + +New syntax node classes to hold tokens: + +| Class name | Token(s) | +|---------------------------------------|-----------------------| +| `JassElementAccessClauseSyntax` | `[` and `]` | +| `JassElseIfClauseDeclaratorSyntax` | `elseif` and `then` | +| `JassEmptyParameterListSyntax` | `takes` and `nothing` | +| `JassGlobalConstantDeclarationSyntax` | `constant` | +| `JassIfClauseDeclaratorSyntax` | `if` and `then` | +| `JassReturnClauseSyntax` | `returns` | + +Other new syntax node classes: + +| Class name | Purpose | +|-----------------------------------------------|--------------------------------------------------------------------------------------------| +| `JassIfClauseSyntax` | Holds `JassIfClauseDeclaratorSyntax` and statements | +| `JassParameterListOrEmptyParameterListSyntax` | Abstract base class for empty (`takes nothing`) and not-empty (`takes `) lists | +| `JassSyntaxNode` | Abstract base class for all new and existing syntax node classes | + +Other new types: + +| Type name | Purpose | +|-------------------------|--------------------------------------------------------------------------------| +| `JassSyntaxKind` | Mainly used to tell the difference between literals and operators | +| `JassSyntaxNodeOrToken` | Can be used to model "custom script actions" (one full line of the map script) | +| `JassSyntaxToken` | Holds a token's text and trivia | +| `JassSyntaxTrivia` | Contains trivia as text (whitespace, newlines, single line comments) | +| `JassSyntaxTriviaList` | A collection of leading or trailing trivia for a token | + +
+ +
Renamed types + +The following interfaces have been changed to abstract classes: + +| Old type name | New type name | +|------------------------------|---------------------------------------| +| `IExpressionSyntax` | `JassExpressionSyntax` | +| `IGlobalDeclarationSyntax` | `JassGlobalDeclarationSyntax` | +| `IStatementSyntax` | `JassStatementSyntax` | +| `ITopLevelDeclarationSyntax` | `JassTopLevelDeclarationSyntax` | +| `IVariableDeclaratorSyntax` | `JassVariableOrArrayDeclaratorSyntax` | + +The following classes have been renamed: + +| Old class name | New class name | +|--------------------------------------|---------------------------------------| +| `JassArrayReferenceExpressionSyntax` | `JassElementAccessExpressionSyntax` | +| `JassGlobalDeclarationListSyntax` | `JassGlobalsDeclarationSyntax` | +| `JassGlobalDeclarationSyntax` | `JassGlobalVariableDeclarationSyntax` | + +
+ +
Replaced types + +Specific literal expression classes have been removed, you can now use the generic JassLiteralExpressionSyntax class. + +The new JassLiteralExpressionSyntax contains a token (with string text) property instead of the actual type of the expression. + +| Old type | New JassSyntaxKind | +|------------------------------------------|-----------------------------------------------| +| `JassBooleanLiteralExpressionSyntax` | `JassSyntaxKind.BooleanLiteralExpression` | +| `JassCharacterLiteralExpressionSyntax` | `JassSyntaxKind.CharacterLiteralExpression` | +| `JassDecimalLiteralExpressionSyntax` | `JassSyntaxKind.DecimalLiteralExpression` | +| `JassFourCCLiteralExpressionSyntax` | `JassSyntaxKind.FourCCLiteralExpression` | +| `JassHexadecimalLiteralExpressionSyntax` | `JassSyntaxKind.HexadecimalLiteralExpression` | +| `JassNullLiteralExpressionSyntax` | `JassSyntaxKind.NullLiteralExpression` | +| `JassOctalLiteralExpressionSyntax` | `JassSyntaxKind.OctalLiteralExpression` | +| `JassRealLiteralExpressionSyntax` | `JassSyntaxKind.RealLiteralExpression` | +| `JassStringLiteralExpressionSyntax` | `JassSyntaxKind.StringLiteralExpression` | + +Script line interfaces have been removed, the new `JassSyntaxNodeOrToken` serves the same purpose: + +| Removed interface | +|--------------------------| +| `IDeclarationLineSyntax` | +| `IGlobalLineSyntax` | +| `IStatementLineSyntax` | + +Custom script action classes have been removed, these have been replaced by new syntax node classes or the `JassSyntaxToken` class: + +| Removed class | Replacement class or token kind | +|-------------------------------------|-------------------------------------| +| `JassDebugCustomScriptAction` | NO REPLACEMENT YET | +| `JassElseCustomScriptAction` | `JassSyntaxKind.ElseKeyword` | +| `JassElseIfCustomScriptAction` | `JassElseIfClauseDeclaratorSyntax` | +| `JassEndFunctionCustomScriptAction` | `JassSyntaxKind.EndFunctionKeyword` | +| `JassEndGlobalsCustomScriptAction` | `JassSyntaxKind.EndGlobalsKeyword` | +| `JassEndIfCustomScriptAction` | `JassSyntaxKind.EndIfKeyword` | +| `JassEndLoopCustomScriptAction` | `JassSyntaxKind.EndLoopKeyword` | +| `JassFunctionCustomScriptAction` | `JassFunctionDeclaratorSyntax` | +| `JassGlobalsCustomScriptAction` | `JassSyntaxKind.GlobalsKeyword` | +| `JassIfCustomScriptAction` | `JassIfClauseDeclaratorSyntax` | +| `JassLoopCustomScriptAction` | `JassSyntaxKind.LoopKeyword` | + +The `BinaryOperatorType` and `UnaryOperatorType` enums have been removed, the type of the operator can now be determined by the syntax kind of the operator token: + +| Old member name | New token kind | +|-------------------------------------|-----------------------------------------| +| `BinaryOperatorType.Add` | `JassSyntaxKind.PlusToken` | +| `BinaryOperatorType.Subtract` | `JassSyntaxKind.MinusToken` | +| `BinaryOperatorType.Multiplication` | `JassSyntaxKind.AsteriskToken` | +| `BinaryOperatorType.Division` | `JassSyntaxKind.SlashToken` | +| `BinaryOperatorType.GreaterThan` | `JassSyntaxKind.GreaterThanToken` | +| `BinaryOperatorType.LessThan` | `JassSyntaxKind.LessThanToken` | +| `BinaryOperatorType.Equals` | `JassSyntaxKind.EqualsEqualsToken` | +| `BinaryOperatorType.NotEquals` | `JassSyntaxKind.ExclamationEqualsToken` | +| `BinaryOperatorType.GreaterOrEqual` | `JassSyntaxKind.GreaterThanEqualsToken` | +| `BinaryOperatorType.LessOrEqual` | `JassSyntaxKind.LessThanEqualsToken` | +| `BinaryOperatorType.And` | `JassSyntaxKind.AndKeyword` | +| `BinaryOperatorType.Or` | `JassSyntaxKind.OrKeyword` | +| `UnaryOperatorType.Plus` | `JassSyntaxKind.PlusToken` | +| `UnaryOperatorType.Minus` | `JassSyntaxKind.MinusToken` | +| `UnaryOperatorType.Not` | `JassSyntaxKind.NotKeyword` | + +Comments and empty lines are now handled by trivia: + +| Removed class | +| --------------------| +| `JassCommentSyntax` | +| `JassEmptySyntax` | + +
+ +
Removed types + +The following types have been removed: + +| Removed type | Alternative | +|-------------------------------|---------------------------------------------------------------------| +| `IInvocationSyntax` | NO ALTERNATIVE | +| `IVariableDeclaratorSyntax` | Use `GetVariableType()` and `GetIdentifierName()` extension methods | +| `JassDebugCustomScriptAction` | NO ALTERNATIVE | + +The following types are no longer relevant: + +| Removed type | Alternative | +|-----------------------------------------|----------------------------------------------------| +| `JassStatementListSyntax` | Use `ImmutableArray` directly | +| `JassVariableReferenceExpressionSyntax` | Use `JassIdentifierNameSyntax` directly | + +The following classes were unused and are no longer relevant: + +| Removed class | +|----------------------------------| +| `IMemberDeclarationSyntax` | +| `IScopedDeclarationSyntax` | +| `IScopedGlobalDeclarationSyntax` | + +
+ +
Renamed members + +The following methods have been renamed in syntax node classes: + +| Old method name | New method name | +|-----------------|------------------| +| `Equals` | `IsEquivalentTo` | + +The following properties have been renamed. + +If the containing type has been changed or renamed, the new type name is also listed in the new column. + +| Old property name | New property name | +|----------------------------------------------|---------------------------------------------------------| +| `JassArgumentListSyntax.Arguments` | `ArgumentList` | +| `JassArrayReferenceExpressionSyntax.Indexer` | `JassElementAccessExpressionSyntax.ElementAccessClause` | +| `JassCallStatementSyntax.Arguments` | `ArgumentList` | +| `JassGlobalDeclarationListSyntax.Globals` | `JassGlobalsDeclarationSyntax.GlobalDeclarations` | +| `JassInvocationExpressionSyntax.Arguments` | `ArgumentList` | +| `JassParameterListSyntax.Empty` | `JassEmptyParameterListSyntax.Value` | +| `JassParameterListSyntax.Parameters` | `ParameterList` | +| `JassTypeSyntax.Boolean` | `JassPredefinedTypeSyntax.Boolean` | +| `JassTypeSyntax.Code` | `JassPredefinedTypeSyntax.Code` | +| `JassTypeSyntax.Handle` | `JassPredefinedTypeSyntax.Handle` | +| `JassTypeSyntax.Integer` | `JassPredefinedTypeSyntax.Integer` | +| `JassTypeSyntax.Nothing` | `JassPredefinedTypeSyntax.Nothing` | +| `JassTypeSyntax.Real` | `JassPredefinedTypeSyntax.Real` | +| `JassTypeSyntax.String` | `JassPredefinedTypeSyntax.String` | + +
+ +
Replaced members + +The following properties have been replaced: + +| Old property name | New property type and name | Old type | +|---------------------------------------|-----------------------------------------------------|---------------------------| +| `JassBinaryExpressionSyntax.Operator` | `JassSyntaxToken OperatorToken` | `BinaryOperatorType` | +| `JassElseClauseSyntax.Body` | `ImmutableArray Statements` | `JassStatementListSyntax` | +| `JassIdentifierNameSyntax.Name` | `JassSyntaxToken Token` | `string` | +| `JassSetStatementSyntax.Indexer` | `JassElementAccessClauseSyntax ElementAccessClause` | `IExpressionSyntax` | +| `JassUnaryExpressionSyntax.Operator` | `JassSyntaxToken OperatorToken` | `UnaryOperatorType` | + +Other replacements: + +| Old property name | Alternative | +|----------------------------------------------------------|------------------------------------------------------| +| `JassBooleanLiteralExpressionSyntax.False` | `JassSyntaxFactory.Literal(false)` | +| `JassBooleanLiteralExpressionSyntax.True` | `JassSyntaxFactory.Literal(true)` | +| `JassNativeFunctionDeclarationSyntax.FunctionDeclarator` | All declarator properties are now available directly | +| `JassNullLiteralExpressionSyntax.Value` | `JassSyntaxFactory.Literal(null)` | +| `JassTypeSyntax.TypeName` | `JassTypeSyntaxExtensions.GetToken()` | + +
+ +
Other (breaking) changes + +- JassTypeSyntax is now abstract and inherits JassExpressionSyntax +- JassIdentifierNameSyntax now inherits JassTypeSyntax -> JassExpressionSyntax +- Properties that used to be of type `JassParameterListSyntax` are now `JassParameterListOrEmptyParameterListSyntax` +- The constructor of syntax node classes is now internal +- Properties have been changed from `{ get; init; }` to `{ get; }` +- `JassSymbol` now contains both `char` and `string` constants, existing `char` constants got the `Char` suffix +- `JassSyntaxFacts.IsWhitespaceCharacter(char)` now only considers spaces and tabs to be whitespace (previously used `char.IsWhitespace` and excluded \r and \n) + +
diff --git a/src/Directory.Build.props b/src/Directory.Build.props index ccc2aceb..30bfe7ae 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -17,7 +17,7 @@ - + diff --git a/src/Directory.Build.targets b/src/Directory.Build.targets index 19ee7535..2311b170 100644 --- a/src/Directory.Build.targets +++ b/src/Directory.Build.targets @@ -14,4 +14,13 @@ $(PackageVersion) + + + + + + + %(ThisProjectPackageVersion.Version) + + \ No newline at end of file diff --git a/src/War3Net.Build.Core/Common/W3MathF.cs b/src/War3Net.Build.Core/Common/W3MathF.cs new file mode 100644 index 00000000..f43586ca --- /dev/null +++ b/src/War3Net.Build.Core/Common/W3MathF.cs @@ -0,0 +1,15 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +namespace War3Net.Build.Common +{ + public static class W3MathF + { + public const float Deg2Rad = 3.141593f / 180f; + public const float Rad2Deg = 180f / 3.141593f; + } +} \ No newline at end of file diff --git a/src/War3Net.Build.Core/Serialization/Json/Info/MapInfo.cs b/src/War3Net.Build.Core/Serialization/Json/Info/MapInfo.cs index f275779a..6cc88f53 100644 --- a/src/War3Net.Build.Core/Serialization/Json/Info/MapInfo.cs +++ b/src/War3Net.Build.Core/Serialization/Json/Info/MapInfo.cs @@ -34,7 +34,7 @@ internal void GetFrom(JsonElement jsonElement) if (FormatVersion >= MapInfoFormatVersion.v18) { MapVersion = jsonElement.GetInt32(nameof(MapVersion)); - EditorVersion = (EditorVersion)jsonElement.GetInt32(nameof(EditorVersion)); + EditorVersion = jsonElement.GetInt32Raw(nameof(EditorVersion)); if (FormatVersion >= MapInfoFormatVersion.v27) { diff --git a/src/War3Net.Build/CompileResult.cs b/src/War3Net.Build/CompileResult.cs index dd90e358..6aa562b1 100644 --- a/src/War3Net.Build/CompileResult.cs +++ b/src/War3Net.Build/CompileResult.cs @@ -24,7 +24,7 @@ internal CompileResult(EmitResult emitResult) _diagnostics = emitResult.Diagnostics; } - internal CompileResult(bool success, IEnumerable diagnostics) + internal CompileResult(bool success, IEnumerable? diagnostics) { _success = success; _diagnostics = diagnostics?.ToImmutableArray() ?? ImmutableArray.Empty; diff --git a/src/War3Net.Build/Extensions/MapExtensions.cs b/src/War3Net.Build/Extensions/MapExtensions.cs index 890c5fe8..ba531618 100644 --- a/src/War3Net.Build/Extensions/MapExtensions.cs +++ b/src/War3Net.Build/Extensions/MapExtensions.cs @@ -53,17 +53,7 @@ public static void CompileScript(this Map map, MapScriptBuilder mapScriptBuilder throw new ArgumentException($"The map's script language must be set to jass in order to use the jass compiler.", nameof(map)); } - var compilationUnit = mapScriptBuilder.Build(map); - - using var stream = new MemoryStream(); - using (var writer = new StreamWriter(stream, _defaultEncoding, leaveOpen: true)) - { - var renderer = new JassRenderer(writer); - renderer.Render(compilationUnit); - } - - stream.Position = 0; - map.SetScriptFile(stream); + map.Script = mapScriptBuilder.Build(map); } public static CompileResult CompileScript(this Map map, Compiler compiler, IEnumerable luaSystemLibs) @@ -133,7 +123,9 @@ public static CompileResult CompileScript(this Map map, Compiler compiler, MapSc transpiler.RegisterJassFile(JassSyntaxFactory.ParseCompilationUnit(File.ReadAllText(commonJPath))); transpiler.RegisterJassFile(JassSyntaxFactory.ParseCompilationUnit(File.ReadAllText(blizzardJPath))); - var luaCompilationUnit = transpiler.Transpile(mapScriptBuilder.Build(map)); + var mapScript = mapScriptBuilder.Build(map); + var jassCompilationUnit = JassSyntaxFactory.ParseCompilationUnit(mapScript); + var luaCompilationUnit = transpiler.Transpile(jassCompilationUnit); using (var writer = new StreamWriter(stream, _defaultEncoding, leaveOpen: true)) { var luaRenderOptions = new LuaSyntaxGenerator.SettingInfo diff --git a/src/War3Net.Build/Extensions/ModifiedAbilityDataExtensions.cs b/src/War3Net.Build/Extensions/ModifiedAbilityDataExtensions.cs index d4079fec..f7bcbe10 100644 --- a/src/War3Net.Build/Extensions/ModifiedAbilityDataExtensions.cs +++ b/src/War3Net.Build/Extensions/ModifiedAbilityDataExtensions.cs @@ -16,13 +16,32 @@ namespace War3Net.Build.Extensions { public static class ModifiedAbilityDataExtensions { + private static readonly Lazy> _abilityOrderOnStrings = new(GetAbilityOrderOnStrings); private static readonly Lazy> _abilityOrderOffStrings = new(GetAbilityOrderOffStrings); + public static bool TryGetOrderOnString(this ModifiedAbilityData abilityData, [NotNullWhen(true)] out string? orderOnString) + { + return _abilityOrderOnStrings.Value.TryGetValue(abilityData.AbilityId, out orderOnString); + } + public static bool TryGetOrderOffString(this ModifiedAbilityData abilityData, [NotNullWhen(true)] out string? orderOffString) { return _abilityOrderOffStrings.Value.TryGetValue(abilityData.AbilityId, out orderOffString); } + private static Dictionary GetAbilityOrderOnStrings() + { + return new Dictionary + { + { "Ahea".FromRawcode(), "healon" }, + { "ACsa".FromRawcode(), "flamingarrows" }, + { "ANth".FromRawcode(), "Thornyshield" }, + { "AEim".FromRawcode(), "immolation" }, + { "ANba".FromRawcode(), "blackarrowon" }, + { "AHds".FromRawcode(), "divineshield" }, + }; + } + private static Dictionary GetAbilityOrderOffStrings() { return new Dictionary diff --git a/src/War3Net.Build/Extensions/TriggerDefinitionExtensions.cs b/src/War3Net.Build/Extensions/TriggerItemExtensions.cs similarity index 75% rename from src/War3Net.Build/Extensions/TriggerDefinitionExtensions.cs rename to src/War3Net.Build/Extensions/TriggerItemExtensions.cs index 2b2900a1..0c3f50d4 100644 --- a/src/War3Net.Build/Extensions/TriggerDefinitionExtensions.cs +++ b/src/War3Net.Build/Extensions/TriggerItemExtensions.cs @@ -1,5 +1,5 @@ -// ------------------------------------------------------------------------------ -// +// ------------------------------------------------------------------------------ +// // Licensed under the MIT license. // See the LICENSE file in the project root for more information. // @@ -12,34 +12,34 @@ namespace War3Net.Build.Extensions { - public static class TriggerDefinitionExtensions + public static class TriggerItemExtensions { - public static string GetVariableName(this TriggerDefinition trigger) + public static string GetVariableName(this TriggerItem trigger) { return $"gg_trg_{trigger.GetTriggerIdentifierName()}"; } - public static string GetInitTrigFunctionName(this TriggerDefinition trigger) + public static string GetInitTrigFunctionName(this TriggerItem trigger) { return $"InitTrig_{trigger.GetTriggerIdentifierName()}"; } - public static string GetTrigConditionsFunctionName(this TriggerDefinition trigger) + public static string GetTrigConditionsFunctionName(this TriggerItem trigger) { return $"Trig_{trigger.GetTriggerIdentifierName()}_Conditions"; } - public static string GetTrigActionsFunctionName(this TriggerDefinition trigger) + public static string GetTrigActionsFunctionName(this TriggerItem trigger) { return $"Trig_{trigger.GetTriggerIdentifierName()}_Actions"; } - public static string GetTrigIdentifierBaseName(this TriggerDefinition trigger) + public static string GetTrigIdentifierBaseName(this TriggerItem trigger) { return $"Trig_{trigger.GetTriggerIdentifierName()}_"; } - public static string GetTriggerIdentifierName(this TriggerDefinition trigger) + public static string GetTriggerIdentifierName(this TriggerItem trigger) { return Regex.Replace(trigger.Name, "[^A-Za-z0-9_]", match => new string('_', Encoding.UTF8.GetBytes(match.Value).Length)); } diff --git a/src/War3Net.Build/Extensions/VariableDefinitionExtensions.cs b/src/War3Net.Build/Extensions/VariableDefinitionExtensions.cs index 881e8a69..7e67ada0 100644 --- a/src/War3Net.Build/Extensions/VariableDefinitionExtensions.cs +++ b/src/War3Net.Build/Extensions/VariableDefinitionExtensions.cs @@ -19,7 +19,7 @@ public static string GetVariableName(this VariableDefinition variable) return $"udg_{variable.Name}"; } - public static IExpressionSyntax GetInitialValueExpression(this VariableDefinition variable) + public static JassExpressionSyntax GetInitialValueExpression(this VariableDefinition variable) { throw new NotImplementedException(); } diff --git a/src/War3Net.Build/Extensions/WidgetDataExtensions.cs b/src/War3Net.Build/Extensions/WidgetDataExtensions.cs index 356e9e91..1ab190ee 100644 --- a/src/War3Net.Build/Extensions/WidgetDataExtensions.cs +++ b/src/War3Net.Build/Extensions/WidgetDataExtensions.cs @@ -20,7 +20,7 @@ public static bool HasItemTable(this WidgetData widgetData) public static bool HasItemTableSets(this WidgetData widgetData) { - return widgetData.ItemTableSets.Any(itemTableSet => itemTableSet.Items.Any()); + return widgetData.ItemTableSets.Any(itemTableSet => itemTableSet.Items.Count > 0); } } } \ No newline at end of file diff --git a/src/War3Net.Build/MapScriptBuilder.cs b/src/War3Net.Build/MapScriptBuilder.cs index 7857c9a5..d33619a0 100644 --- a/src/War3Net.Build/MapScriptBuilder.cs +++ b/src/War3Net.Build/MapScriptBuilder.cs @@ -7,15 +7,15 @@ using System; using System.Collections.Generic; +using System.IO; using System.Linq; using System.Reflection; using War3Net.Build.Extensions; using War3Net.Build.Info; using War3Net.Build.Script; -using War3Net.CodeAnalysis.Jass.Syntax; - -using SyntaxFactory = War3Net.CodeAnalysis.Jass.JassSyntaxFactory; +using War3Net.CodeAnalysis; +using War3Net.CodeAnalysis.Jass; namespace War3Net.Build { @@ -97,224 +97,242 @@ public virtual void SetDefaultOptionsForMap(Map map) UseWeatherEffectVariable = true; } - public virtual JassCompilationUnitSyntax Build(Map map) + public string Build(Map map) { if (map is null) { throw new ArgumentNullException(nameof(map)); } - JassCommentSyntax commentLine1 = new("==========================================================================="); - JassCommentSyntax commentLine2 = new("***************************************************************************"); - JassCommentSyntax commentLine3 = new("*"); + using var stringWriter = new StringWriter(); + stringWriter.NewLine = JassSymbol.CarriageReturnLineFeed; + using var writer = new IndentedTextWriter(stringWriter); + + Build(map, writer); - List declarations = new(); + return stringWriter.ToString(); + } + + public virtual void Build(Map map, IndentedTextWriter writer) + { + if (map is null) + { + throw new ArgumentNullException(nameof(map)); + } - void AppendBanner(string bannerText) + if (writer is null) { - declarations.Add(commentLine2); - declarations.Add(commentLine3); - declarations.Add(new JassCommentSyntax($"* {bannerText}")); - declarations.Add(commentLine3); - declarations.Add(commentLine2); - declarations.Add(JassEmptySyntax.Value); + throw new ArgumentNullException(nameof(writer)); } - void AppendBannerAndFunction(string bannerText, Func function, Func condition, bool includeCommentLine = false) + var commentLine1 = "//==========================================================================="; + var commentLine2 = "//***************************************************************************"; + var commentLine3 = "//*"; + + void WriteBanner(string bannerText) + { + writer.WriteLine(commentLine2); + writer.WriteLine(commentLine3); + writer.WriteLine($"{commentLine3} {bannerText}"); + writer.WriteLine(commentLine3); + writer.WriteLine(commentLine2); + writer.WriteLine(); + } + + void WriteBannerAndFunction(string bannerText, Action function, Func condition, bool includeCommentLine = false) { if (condition(map)) { - AppendBanner(bannerText); + WriteBanner(bannerText); if (includeCommentLine) { - declarations.Add(commentLine1); + writer.WriteLine(commentLine1); } - declarations.Add(function(map)); - declarations.Add(JassEmptySyntax.Value); + function.Invoke(map, writer); + writer.WriteLine(); } } - void AppendBannerAndFunctions(string bannerText, Func> functions, Func condition) + void WriteBannerAndFunctions(string bannerText, Action functions, Func condition) { if (condition(map)) { - AppendBanner(bannerText); - foreach (var function in functions(map)) - { - declarations.Add(function); - declarations.Add(JassEmptySyntax.Value); - } + WriteBanner(bannerText); + functions.Invoke(map, writer); } } - void AppendFunction(Func function, Func condition) + void WriteFunction(Action function, Func condition) { if (condition(map)) { - declarations.Add(commentLine1); - declarations.Add(function(map)); - declarations.Add(JassEmptySyntax.Value); + writer.WriteLine(commentLine1); + function.Invoke(map, writer); + writer.WriteLine(); } } - void AppendFunctionForIndex(int index, Func function, Func condition) + void WriteFunctionForIndex(int index, Action function, Func condition) { if (condition(map, index)) { - declarations.Add(commentLine1); - declarations.Add(function(map, index)); - declarations.Add(JassEmptySyntax.Value); + writer.WriteLine(commentLine1); + function.Invoke(map, index, writer); + writer.WriteLine(); } } - declarations.AddRange(GetMapScriptHeader(map)); - declarations.Add(JassEmptySyntax.Value); + WriteMapScriptHeader(map, writer); + writer.WriteLine(); - AppendBanner("Global Variables"); + WriteBanner("Global Variables"); - declarations.Add(Globals(map)); - declarations.Add(JassEmptySyntax.Value); + GenerateGlobals(map, writer); + writer.WriteLine(); - if (InitGlobalsCondition(map)) + if (ShouldGenerateInitGlobals(map)) { - declarations.Add(InitGlobals(map)); - declarations.Add(JassEmptySyntax.Value); + GenerateInitGlobals(map, writer); + writer.WriteLine(); } - AppendBanner("Custom Script Code"); - AppendBannerAndFunction("Random Groups", InitRandomGroups, InitRandomGroupsCondition); - AppendBannerAndFunctions("Map Item Tables", MapItemTables, MapItemTablesCondition); - AppendBannerAndFunction("Items", CreateAllItems, CreateAllItemsCondition); - AppendBannerAndFunctions("Unit Item Tables", UnitItemTables, UnitItemTablesCondition); - AppendBannerAndFunctions("Destructable Item Tables", DestructableItemTables, DestructableItemTablesCondition); - AppendBannerAndFunction("Sounds", InitSounds, InitSoundsCondition); - AppendBannerAndFunction("Destructable Objects", CreateAllDestructables, CreateAllDestructablesCondition); + WriteBanner("Custom Script Code"); + WriteBannerAndFunction("Random Groups", GenerateInitRandomGroups, ShouldGenerateInitRandomGroups); + WriteBannerAndFunctions("Map Item Tables", GenerateMapItemTables, ShouldGenerateMapItemTables); + WriteBannerAndFunction("Items", GenerateCreateAllItems, ShouldGenerateCreateAllItems); + WriteBannerAndFunctions("Unit Item Tables", GenerateUnitItemTables, ShouldGenerateUnitItemTables); + WriteBannerAndFunctions("Destructable Item Tables", GenerateDestructableItemTables, ShouldGenerateDestructableItemTables); + WriteBannerAndFunction("Sounds", GenerateInitSounds, ShouldGenerateInitSounds); + WriteBannerAndFunction("Destructable Objects", GenerateCreateAllDestructables, ShouldGenerateCreateAllDestructables); - if (CreateAllUnitsCondition(map)) + if (ShouldGenerateCreateAllUnits(map)) { - AppendBanner("Unit Creation"); + WriteBanner("Unit Creation"); foreach (var i in Enumerable.Range(0, MaxPlayerSlots)) { - AppendFunctionForIndex(i, CreateBuildingsForPlayer, CreateBuildingsForPlayerCondition); - AppendFunctionForIndex(i, CreateUnitsForPlayer, CreateUnitsForPlayerCondition); + WriteFunctionForIndex(i, GenerateCreateBuildingsForPlayer, ShouldGenerateCreateBuildingsForPlayer); + WriteFunctionForIndex(i, GenerateCreateUnitsForPlayer, ShouldGenerateCreateUnitsForPlayer); } - AppendFunction(CreateNeutralHostile, CreateNeutralHostileCondition); - AppendFunction(CreateNeutralPassiveBuildings, CreateNeutralPassiveBuildingsCondition); - AppendFunction(CreateNeutralPassive, CreateNeutralPassiveCondition); - AppendFunction(CreatePlayerBuildings, CreatePlayerBuildingsCondition); - AppendFunction(CreatePlayerUnits, CreatePlayerUnitsCondition); - AppendFunction(CreateNeutralUnits, CreateNeutralUnitsCondition); - AppendFunction(CreateAllUnits, (map) => true); + WriteFunction(GenerateCreateNeutralHostile, ShouldGenerateCreateNeutralHostile); + WriteFunction(GenerateCreateNeutralPassiveBuildings, ShouldGenerateCreateNeutralPassiveBuildings); + WriteFunction(GenerateCreateNeutralPassive, ShouldGenerateCreateNeutralPassive); + WriteFunction(GenerateCreatePlayerBuildings, ShouldGenerateCreatePlayerBuildings); + WriteFunction(GenerateCreatePlayerUnits, ShouldGenerateCreatePlayerUnits); + WriteFunction(GenerateCreateNeutralUnits, ShouldGenerateCreateNeutralUnits); + WriteFunction(GenerateCreateAllUnits, (map) => true); } - AppendBannerAndFunction("Regions", CreateRegions, CreateRegionsCondition); - AppendBannerAndFunction("Cameras", CreateCameras, CreateCamerasCondition); + WriteBannerAndFunction("Regions", GenerateCreateRegions, ShouldGenerateCreateRegions); + WriteBannerAndFunction("Cameras", GenerateCreateCameras, ShouldGenerateCreateCameras); - AppendBanner("Triggers"); + WriteBanner("Triggers"); if (map.Triggers is not null) { foreach (var trigger in map.Triggers.TriggerItems) { if (trigger is TriggerDefinition triggerDefinition && - InitTrigCondition(map, triggerDefinition)) + ShouldRenderTrigger(map, triggerDefinition)) { - declarations.Add(commentLine1); - declarations.Add(InitTrig(map, triggerDefinition)); - declarations.Add(JassEmptySyntax.Value); + var triggerRenderer = new TriggerRenderer(writer, TriggerData, map.Triggers.Variables, isLuaTrigger: false); + triggerRenderer.RenderTrigger(triggerDefinition); + writer.WriteLine(); } } - AppendFunction(InitCustomTriggers, InitCustomTriggersCondition); - AppendFunction(RunInitializationTriggers, RunInitializationTriggersCondition); + WriteFunction(GenerateInitCustomTriggers, ShouldGenerateInitCustomTriggers); + WriteFunction(GenerateRunInitializationTriggers, ShouldGenerateRunInitializationTriggers); } - if (InitUpgradesCondition(map)) + if (ShouldGenerateInitUpgrades(map)) { - AppendBanner("Upgrades"); + WriteBanner("Upgrades"); foreach (var i in Enumerable.Range(0, MaxPlayerSlots)) { - if (InitUpgrades_PlayerCondition(map, i)) + if (ShouldGenerateInitUpgradesForPlayer(map, i)) { - declarations.Add(InitUpgrades_Player(map, i)); - declarations.Add(JassEmptySyntax.Value); + GenerateInitUpgradesForPlayer(map, i, writer); + writer.WriteLine(); } } - declarations.Add(InitUpgrades(map)); - declarations.Add(JassEmptySyntax.Value); + GenerateInitUpgrades(map, writer); + writer.WriteLine(); } - if (InitTechTreeCondition(map)) + if (ShouldGenerateInitTechTree(map)) { - AppendBanner("TechTree"); + WriteBanner("TechTree"); foreach (var i in Enumerable.Range(0, MaxPlayerSlots)) { - if (InitTechTree_PlayerCondition(map, i)) + if (ShouldGenerateInitTechTreeForPlayer(map, i)) { - declarations.Add(InitTechTree_Player(map, i)); - declarations.Add(JassEmptySyntax.Value); + GenerateInitTechTreeForPlayer(map, i, writer); + writer.WriteLine(); } } - declarations.Add(InitTechTree(map)); - declarations.Add(JassEmptySyntax.Value); + GenerateInitTechTree(map, writer); + writer.WriteLine(); } - AppendBanner("Players"); + WriteBanner("Players"); - if (InitCustomPlayerSlotsCondition(map)) + if (ShouldGenerateInitCustomPlayerSlots(map)) { - declarations.Add(InitCustomPlayerSlots(map)); - declarations.Add(JassEmptySyntax.Value); + GenerateInitCustomPlayerSlots(map, writer); + writer.WriteLine(); } - if (InitCustomTeamsCondition(map)) + if (ShouldGenerateInitCustomTeams(map)) { - declarations.Add(InitCustomTeams(map)); - declarations.Add(JassEmptySyntax.Value); + GenerateInitCustomTeams(map, writer); + writer.WriteLine(); } - if (InitAllyPrioritiesCondition(map)) + if (ShouldGenerateInitAllyPriorities(map)) { var ids = Enumerable.Range(0, MaxPlayerSlots).ToArray(); if (map.Info.Players.Any(p => ids.Any(id => p.AllyLowPriorityFlags[id] || p.AllyHighPriorityFlags[id]))) { - declarations.Add(InitAllyPriorities(map)); - declarations.Add(JassEmptySyntax.Value); + GenerateInitAllyPriorities(map, writer); + writer.WriteLine(); } } - AppendBannerAndFunction("Main Initialization", main, mainCondition, true); - AppendBannerAndFunction("Map Configuration", config, configCondition); - - return SyntaxFactory.CompilationUnit(declarations); + WriteBannerAndFunction("Main Initialization", GenerateMain, ShouldGenerateMain, true); + WriteBannerAndFunction("Map Configuration", GenerateConfig, ShouldGenerateConfig); } - protected internal virtual IEnumerable GetMapScriptHeader(Map map) + protected internal virtual void WriteMapScriptHeader(Map map, IndentedTextWriter writer) { if (map is null) { throw new ArgumentNullException(nameof(map)); } + if (writer is null) + { + throw new ArgumentNullException(nameof(writer)); + } + var mapInfo = map.Info; var mapTriggerStrings = map.TriggerStrings; - yield return new JassCommentSyntax($"==========================================================================="); - yield return new JassCommentSyntax($" "); - yield return new JassCommentSyntax($" {mapInfo.MapName.Localize(mapTriggerStrings)}"); - yield return new JassCommentSyntax($" "); - yield return new JassCommentSyntax($" Warcraft III map script"); - yield return new JassCommentSyntax($" Generated by {Assembly.GetExecutingAssembly().GetName().Name}"); - yield return new JassCommentSyntax($" Date: {DateTime.Now:ddd MMM dd HH:mm:ss yyyy}"); - yield return new JassCommentSyntax($" Map Author: {mapInfo.MapAuthor.Localize(mapTriggerStrings)}"); - yield return new JassCommentSyntax($" "); - yield return new JassCommentSyntax($"==========================================================================="); + writer.WriteLine("//==========================================================================="); + writer.WriteLine("// "); + writer.WriteLine($"// {mapInfo.MapName.Localize(mapTriggerStrings)}"); + writer.WriteLine("// "); + writer.WriteLine("// Warcraft III map script"); + writer.WriteLine($"// Generated by {Assembly.GetExecutingAssembly().GetName().Name}"); + writer.WriteLine($"// Date: {DateTime.Now:ddd MMM dd HH:mm:ss yyyy}"); + writer.WriteLine($"// Map Author: {mapInfo.MapAuthor.Localize(mapTriggerStrings)}"); + writer.WriteLine("// "); + writer.WriteLine("//==========================================================================="); } } } \ No newline at end of file diff --git a/src/War3Net.Build/MapScriptBuilder/Audio/InitSounds.cs b/src/War3Net.Build/MapScriptBuilder/Audio/InitSounds.cs index 5915206e..471ba0d3 100644 --- a/src/War3Net.Build/MapScriptBuilder/Audio/InitSounds.cs +++ b/src/War3Net.Build/MapScriptBuilder/Audio/InitSounds.cs @@ -6,40 +6,43 @@ // ------------------------------------------------------------------------------ using System; -using System.Collections.Generic; using War3Net.Build.Audio; -using War3Net.Build.Providers; -using War3Net.CodeAnalysis.Jass.Syntax; - -using SyntaxFactory = War3Net.CodeAnalysis.Jass.JassSyntaxFactory; +using War3Net.CodeAnalysis; +using War3Net.CodeAnalysis.Jass; +using War3Net.CodeAnalysis.Jass.Extensions; namespace War3Net.Build { public partial class MapScriptBuilder { - protected internal virtual JassFunctionDeclarationSyntax InitSounds(Map map) + protected internal virtual void GenerateInitSounds(Map map, IndentedTextWriter writer) { if (map is null) { throw new ArgumentNullException(nameof(map)); } + if (writer is null) + { + throw new ArgumentNullException(nameof(writer)); + } + var mapSounds = map.Sounds; if (mapSounds is null) { - throw new ArgumentException($"Function '{nameof(InitSounds)}' cannot be generated without {nameof(MapSounds)}.", nameof(map)); + throw new ArgumentException($"Function '{GeneratedFunctionName.InitSounds}' cannot be generated without {nameof(MapSounds)}.", nameof(map)); } - var statements = new List(); + writer.WriteFunction(GeneratedFunctionName.InitSounds); foreach (var sound in mapSounds.Sounds) { if (sound.Flags.HasFlag(SoundFlags.Music)) { - statements.Add(SyntaxFactory.SetStatement( + writer.WriteSet( sound.Name, - SyntaxFactory.LiteralExpression(EscapedStringProvider.GetEscapedString(sound.FilePath)))); + JassLiteral.String(sound.FilePath)); } else { @@ -48,115 +51,115 @@ protected internal virtual JassFunctionDeclarationSyntax InitSounds(Map map) && sound.Channel != SoundChannel.Music && sound.Channel != SoundChannel.UserInterface; - statements.Add(SyntaxFactory.SetStatement( + writer.WriteSet( sound.Name, - SyntaxFactory.InvocationExpression( + JassExpression.InvokeSpaced( NativeName.CreateSound, - SyntaxFactory.LiteralExpression(EscapedStringProvider.GetEscapedString(sound.FilePath)), - SyntaxFactory.LiteralExpression(sound.Flags.HasFlag(SoundFlags.Looping)), - SyntaxFactory.LiteralExpression(is3DSound), - SyntaxFactory.LiteralExpression(sound.Flags.HasFlag(SoundFlags.StopWhenOutOfRange)), - SyntaxFactory.LiteralExpression(sound.FadeInRate), - SyntaxFactory.LiteralExpression(sound.FadeOutRate), - SyntaxFactory.LiteralExpression(EscapedStringProvider.GetEscapedString(sound.EaxSetting))))); + JassLiteral.String(sound.FilePath), + JassLiteral.Bool(sound.Flags.HasFlag(SoundFlags.Looping)), + JassLiteral.Bool(is3DSound), + JassLiteral.Bool(sound.Flags.HasFlag(SoundFlags.StopWhenOutOfRange)), + JassLiteral.Int(sound.FadeInRate), + JassLiteral.Int(sound.FadeOutRate), + JassLiteral.String(sound.EaxSetting))); if (!string.IsNullOrEmpty(sound.FacialAnimationLabel)) { - statements.Add(SyntaxFactory.CallStatement( + writer.WriteCall( NativeName.SetSoundFacialAnimationLabel, - SyntaxFactory.VariableReferenceExpression(sound.Name), - SyntaxFactory.LiteralExpression(sound.FacialAnimationLabel))); + sound.Name, + JassLiteral.String(sound.FacialAnimationLabel)); } if (!string.IsNullOrEmpty(sound.FacialAnimationGroupLabel)) { - statements.Add(SyntaxFactory.CallStatement( + writer.WriteCall( NativeName.SetSoundFacialAnimationGroupLabel, - SyntaxFactory.VariableReferenceExpression(sound.Name), - SyntaxFactory.LiteralExpression(sound.FacialAnimationGroupLabel))); + sound.Name, + JassLiteral.String(sound.FacialAnimationGroupLabel)); } if (!string.IsNullOrEmpty(sound.FacialAnimationSetFilepath)) { - statements.Add(SyntaxFactory.CallStatement( + writer.WriteCall( NativeName.SetSoundFacialAnimationSetFilepath, - SyntaxFactory.VariableReferenceExpression(sound.Name), - SyntaxFactory.LiteralExpression(sound.FacialAnimationSetFilepath))); + sound.Name, + JassLiteral.String(sound.FacialAnimationSetFilepath)); } if (sound.DialogueSpeakerNameKey > 0) { - statements.Add(SyntaxFactory.CallStatement( + writer.WriteCall( NativeName.SetDialogueSpeakerNameKey, - SyntaxFactory.VariableReferenceExpression(sound.Name), - SyntaxFactory.LiteralExpression($"TRIGSTR_{sound.DialogueSpeakerNameKey}"))); + sound.Name, + JassLiteral.String($"TRIGSTR_{sound.DialogueSpeakerNameKey}")); } if (sound.DialogueTextKey > 0) { - statements.Add(SyntaxFactory.CallStatement( + writer.WriteCall( NativeName.SetDialogueTextKey, - SyntaxFactory.VariableReferenceExpression(sound.Name), - SyntaxFactory.LiteralExpression($"TRIGSTR_{sound.DialogueTextKey}"))); + sound.Name, + JassLiteral.String($"TRIGSTR_{sound.DialogueTextKey}")); } if (sound.DistanceCutoff != 3000f) { var distanceCutoff = sound.DistanceCutoff == uint.MaxValue ? 3000f : sound.DistanceCutoff; - statements.Add(SyntaxFactory.CallStatement( + writer.WriteCall( NativeName.SetSoundDistanceCutoff, - SyntaxFactory.VariableReferenceExpression(sound.Name), - SyntaxFactory.LiteralExpression(distanceCutoff, precision: 1))); + sound.Name, + JassLiteral.Real(distanceCutoff)); } if ((int)sound.Channel != -1) { var channel = sound.Channel == SoundChannel.Undefined ? SoundChannel.General : sound.Channel; - statements.Add(SyntaxFactory.CallStatement( + writer.WriteCall( NativeName.SetSoundChannel, - SyntaxFactory.VariableReferenceExpression(sound.Name), - SyntaxFactory.LiteralExpression((int)channel))); + sound.Name, + JassLiteral.Int((int)channel)); } - statements.Add(SyntaxFactory.CallStatement( + writer.WriteCall( NativeName.SetSoundVolume, - SyntaxFactory.VariableReferenceExpression(sound.Name), - SyntaxFactory.LiteralExpression(sound.Volume == -1 ? 127 : sound.Volume))); + sound.Name, + JassLiteral.Int(sound.Volume == -1 ? 127 : sound.Volume)); - statements.Add(SyntaxFactory.CallStatement( + writer.WriteCall( NativeName.SetSoundPitch, - SyntaxFactory.VariableReferenceExpression(sound.Name), - SyntaxFactory.LiteralExpression(sound.Pitch == uint.MaxValue ? 1f : sound.Pitch, precision: 1))); + sound.Name, + JassLiteral.Real(sound.Pitch == uint.MaxValue ? 1f : sound.Pitch)); if (is3DSound) { - statements.Add(SyntaxFactory.CallStatement( + writer.WriteCall( NativeName.SetSoundDistances, - SyntaxFactory.VariableReferenceExpression(sound.Name), - SyntaxFactory.LiteralExpression(sound.MinDistance == uint.MaxValue ? 0f : sound.MinDistance, precision: 1), - SyntaxFactory.LiteralExpression(sound.MaxDistance == uint.MaxValue ? 10000f : sound.MaxDistance, precision: 1))); + sound.Name, + JassLiteral.Real(sound.MinDistance == uint.MaxValue ? 0f : sound.MinDistance), + JassLiteral.Real(sound.MaxDistance == uint.MaxValue ? 10000f : sound.MaxDistance)); - statements.Add(SyntaxFactory.CallStatement( + writer.WriteCall( NativeName.SetSoundConeAngles, - SyntaxFactory.VariableReferenceExpression(sound.Name), - SyntaxFactory.LiteralExpression(sound.ConeAngleInside == uint.MaxValue ? 0f : sound.ConeAngleInside, precision: 1), - SyntaxFactory.LiteralExpression(sound.ConeAngleOutside == uint.MaxValue ? 0f : sound.ConeAngleOutside, precision: 1), - SyntaxFactory.LiteralExpression(sound.ConeOutsideVolume == -1 ? 127 : sound.ConeOutsideVolume))); + sound.Name, + JassLiteral.Real(sound.ConeAngleInside == uint.MaxValue ? 0f : sound.ConeAngleInside), + JassLiteral.Real(sound.ConeAngleOutside == uint.MaxValue ? 0f : sound.ConeAngleOutside), + JassLiteral.Int(sound.ConeOutsideVolume == -1 ? 127 : sound.ConeOutsideVolume)); - statements.Add(SyntaxFactory.CallStatement( + writer.WriteCall( NativeName.SetSoundConeOrientation, - SyntaxFactory.VariableReferenceExpression(sound.Name), - SyntaxFactory.LiteralExpression(sound.ConeOrientation.X == uint.MaxValue ? 0f : sound.ConeOrientation.X, precision: 1), - SyntaxFactory.LiteralExpression(sound.ConeOrientation.Y == uint.MaxValue ? 0f : sound.ConeOrientation.Y, precision: 1), - SyntaxFactory.LiteralExpression(sound.ConeOrientation.Z == uint.MaxValue ? 0f : sound.ConeOrientation.Z, precision: 1))); + sound.Name, + JassLiteral.Real(sound.ConeOrientation.X == uint.MaxValue ? 0f : sound.ConeOrientation.X), + JassLiteral.Real(sound.ConeOrientation.Y == uint.MaxValue ? 0f : sound.ConeOrientation.Y), + JassLiteral.Real(sound.ConeOrientation.Z == uint.MaxValue ? 0f : sound.ConeOrientation.Z)); } } } - return SyntaxFactory.FunctionDeclaration(SyntaxFactory.FunctionDeclarator(nameof(InitSounds)), statements); + writer.EndFunction(); } - protected internal virtual bool InitSoundsCondition(Map map) + protected internal virtual bool ShouldGenerateInitSounds(Map map) { if (map is null) { diff --git a/src/War3Net.Build/MapScriptBuilder/Audio/Sounds.cs b/src/War3Net.Build/MapScriptBuilder/Audio/Sounds.cs index 85373d96..3e063784 100644 --- a/src/War3Net.Build/MapScriptBuilder/Audio/Sounds.cs +++ b/src/War3Net.Build/MapScriptBuilder/Audio/Sounds.cs @@ -6,60 +6,61 @@ // ------------------------------------------------------------------------------ using System; -using System.Collections.Generic; -using System.Linq; - -using Microsoft.CodeAnalysis.CSharp.Syntax; using War3Net.Build.Audio; -using War3Net.CodeAnalysis.Jass.Syntax; -using War3Net.CodeAnalysis.Transpilers; - -using SyntaxFactory = War3Net.CodeAnalysis.Jass.JassSyntaxFactory; +using War3Net.CodeAnalysis; +using War3Net.CodeAnalysis.Jass; +using War3Net.CodeAnalysis.Jass.Extensions; namespace War3Net.Build { public partial class MapScriptBuilder { - public virtual IEnumerable SoundsApi(Map map, JassToCSharpTranspiler transpiler) + protected internal virtual void GenerateSoundVariables(Map map, IndentedTextWriter writer) { - if (transpiler is null) + if (map is null) { - throw new ArgumentNullException(nameof(transpiler)); + throw new ArgumentNullException(nameof(map)); } - return Sounds(map).Select(sound => transpiler.Transpile(sound)); - } - - protected internal virtual IEnumerable Sounds(Map map) - { - if (map is null) + if (writer is null) { - throw new ArgumentNullException(nameof(map)); + throw new ArgumentNullException(nameof(writer)); } var mapSounds = map.Sounds; if (mapSounds is null) { - yield break; + return; } foreach (var sound in mapSounds.Sounds) { if (sound.Flags.HasFlag(SoundFlags.Music)) { - yield return SyntaxFactory.GlobalDeclaration( - JassTypeSyntax.String, + writer.WriteAlignedGlobal( + JassKeyword.String, sound.Name); } else { - yield return SyntaxFactory.GlobalDeclaration( - SyntaxFactory.ParseTypeName(TypeName.Sound), + writer.WriteAlignedGlobal( + TypeName.Sound, sound.Name, - JassNullLiteralExpressionSyntax.Value); + JassKeyword.Null); } } } + + protected internal virtual bool ShouldGenerateSoundVariables(Map map) + { + if (map is null) + { + throw new ArgumentNullException(nameof(map)); + } + + return map.Sounds is not null + && map.Sounds.Sounds.Count > 0; + } } } \ No newline at end of file diff --git a/src/War3Net.Build/MapScriptBuilder/Common/Config.cs b/src/War3Net.Build/MapScriptBuilder/Common/Config.cs index d1ecd765..13739767 100644 --- a/src/War3Net.Build/MapScriptBuilder/Common/Config.cs +++ b/src/War3Net.Build/MapScriptBuilder/Common/Config.cs @@ -1,127 +1,124 @@ -// ------------------------------------------------------------------------------ +// ------------------------------------------------------------------------------ // // Licensed under the MIT license. // See the LICENSE file in the project root for more information. // // ------------------------------------------------------------------------------ -#pragma warning disable IDE1006, SA1300 - using System; -using System.Collections.Generic; using System.Linq; using War3Net.Build.Info; -using War3Net.Build.Providers; -using War3Net.CodeAnalysis.Jass.Syntax; - -using SyntaxFactory = War3Net.CodeAnalysis.Jass.JassSyntaxFactory; +using War3Net.CodeAnalysis; +using War3Net.CodeAnalysis.Jass; +using War3Net.CodeAnalysis.Jass.Extensions; namespace War3Net.Build { public partial class MapScriptBuilder { - protected internal virtual JassFunctionDeclarationSyntax config(Map map) + protected internal virtual void GenerateConfig(Map map, IndentedTextWriter writer) { if (map is null) { throw new ArgumentNullException(nameof(map)); } + if (writer is null) + { + throw new ArgumentNullException(nameof(writer)); + } + var mapInfo = map.Info; if (mapInfo is null) { - throw new ArgumentException($"Function '{nameof(config)}' cannot be generated without {nameof(MapInfo)}.", nameof(map)); + throw new ArgumentException($"Function '{GeneratedFunctionName.Config}' cannot be generated without {nameof(MapInfo)}.", nameof(map)); } - var statements = new List(); + writer.WriteFunction(GeneratedFunctionName.Config); var playerDataCount = mapInfo.Players.Count; - var forceDataCount = mapInfo.Forces.Count; - statements.Add(SyntaxFactory.CallStatement(NativeName.SetMapName, SyntaxFactory.LiteralExpression(EscapedStringProvider.GetEscapedString(mapInfo.MapName)))); - statements.Add(SyntaxFactory.CallStatement(NativeName.SetMapDescription, SyntaxFactory.LiteralExpression(EscapedStringProvider.GetEscapedString(mapInfo.MapDescription)))); - statements.Add(SyntaxFactory.CallStatement(NativeName.SetPlayers, SyntaxFactory.LiteralExpression(playerDataCount))); - statements.Add(SyntaxFactory.CallStatement(NativeName.SetTeams, SyntaxFactory.LiteralExpression(playerDataCount))); + writer.WriteCall(NativeName.SetMapName, JassLiteral.String(mapInfo.MapName)); + writer.WriteCall(NativeName.SetMapDescription, JassLiteral.String(mapInfo.MapDescription)); + writer.WriteCall(NativeName.SetPlayers, JassLiteral.Int(playerDataCount)); + writer.WriteCall(NativeName.SetTeams, JassLiteral.Int(playerDataCount)); + var placement = mapInfo.Players.Any(player => player.AllyHighPriorityFlags != 0 || player.AllyLowPriorityFlags != 0) + ? PlacementName.TeamsTogether + : PlacementName.UseMapSettings; - if (mapInfo.Players.Any(player => player.AllyHighPriorityFlags != 0 || player.AllyLowPriorityFlags != 0)) - { - statements.Add(SyntaxFactory.CallStatement(NativeName.SetGamePlacement, SyntaxFactory.VariableReferenceExpression(PlacementName.TeamsTogether))); - } - else - { - statements.Add(SyntaxFactory.CallStatement(NativeName.SetGamePlacement, SyntaxFactory.VariableReferenceExpression(PlacementName.UseMapSettings))); - } + writer.WriteCall(NativeName.SetGamePlacement, placement); - statements.Add(JassEmptySyntax.Value); + writer.WriteLine(); if (!string.IsNullOrEmpty(LobbyMusic)) { - statements.Add(SyntaxFactory.CallStatement( + writer.WriteCall( NativeName.PlayMusic, - SyntaxFactory.LiteralExpression(EscapedStringProvider.GetEscapedString(LobbyMusic)))); + JassLiteral.String(LobbyMusic)); } for (var i = 0; i < playerDataCount; i++) { var location = mapInfo.Players[i].StartPosition; - statements.Add(SyntaxFactory.CallStatement( + writer.WriteCall( NativeName.DefineStartLocation, - SyntaxFactory.LiteralExpression(i), - SyntaxFactory.LiteralExpression(location.X, precision: 1), - SyntaxFactory.LiteralExpression(location.Y, precision: 1))); + JassLiteral.Int(i), + JassLiteral.Real(location.X), + JassLiteral.Real(location.Y)); } - statements.Add(JassEmptySyntax.Value); - statements.Add(new JassCommentSyntax(" Player setup")); + writer.WriteLine(); + writer.WriteComment("Player setup"); - if (InitCustomPlayerSlotsCondition(map)) + if (ShouldGenerateInitCustomPlayerSlots(map)) { - statements.Add(SyntaxFactory.CallStatement(nameof(InitCustomPlayerSlots))); + writer.WriteCall(GeneratedFunctionName.InitCustomPlayerSlots); } - var elseStatements = new List(); if (!mapInfo.MapFlags.HasFlag(MapFlags.UseCustomForces)) { + if (mapInfo.FormatVersion < MapInfoFormatVersion.v15) + { + var condition = JassExpression.Equal( + JassExpression.InvokeSpaced(NativeName.GetGameTypeSelected), + GameType.UseMapSettings); + + writer.WriteIf(JassExpression.ParenthesizedCompact(condition)); + writer.WriteCall(GeneratedFunctionName.InitCustomTeams); + writer.WriteElse(); + } + for (var i = 0; i < playerDataCount; i++) { - elseStatements.Add(SyntaxFactory.CallStatement( + writer.WriteCall( FunctionName.SetPlayerSlotAvailable, - SyntaxFactory.InvocationExpression(NativeName.Player, SyntaxFactory.LiteralExpression(mapInfo.Players[i].Id)), - SyntaxFactory.VariableReferenceExpression(MapControlName.User))); + JassExpression.Invoke(NativeName.Player, JassLiteral.Int(mapInfo.Players[i].Id)), + MapControlName.User); } - elseStatements.Add(SyntaxFactory.CallStatement(FunctionName.InitGenericPlayerSlots)); - } + writer.WriteCall(FunctionName.InitGenericPlayerSlots); - if (mapInfo.FormatVersion < MapInfoFormatVersion.v15) - { - statements.Add(SyntaxFactory.IfStatement( - SyntaxFactory.ParenthesizedExpression(SyntaxFactory.BinaryEqualsExpression( - SyntaxFactory.InvocationExpression(NativeName.GetGameTypeSelected), - SyntaxFactory.VariableReferenceExpression(GameType.UseMapSettings))), - SyntaxFactory.StatementList(SyntaxFactory.CallStatement(nameof(InitCustomTeams))), - new JassElseClauseSyntax(SyntaxFactory.StatementList(elseStatements)))); - } - else - { - statements.AddRange(elseStatements); + if (mapInfo.FormatVersion < MapInfoFormatVersion.v15) + { + writer.WriteEndIf(); + } } - if (InitCustomTeamsCondition(map) && InitCustomTeamsInvokeCondition(map)) + if (ShouldGenerateInitCustomTeams(map) && ShouldCallInitCustomTeams(map)) { - statements.Add(SyntaxFactory.CallStatement(nameof(InitCustomTeams))); + writer.WriteCall(GeneratedFunctionName.InitCustomTeams); } - if (InitAllyPrioritiesCondition(map)) + if (ShouldGenerateInitAllyPriorities(map)) { - statements.Add(SyntaxFactory.CallStatement(nameof(InitAllyPriorities))); + writer.WriteCall(GeneratedFunctionName.InitAllyPriorities); } - return SyntaxFactory.FunctionDeclaration(SyntaxFactory.FunctionDeclarator(nameof(config)), statements); + writer.EndFunction(); } - protected internal virtual bool configCondition(Map map) + protected internal virtual bool ShouldGenerateConfig(Map map) { if (map is null) { diff --git a/src/War3Net.Build/MapScriptBuilder/Common/ItemTableDropItems.cs b/src/War3Net.Build/MapScriptBuilder/Common/ItemTableDropItems.cs index ac6ec451..e69cf6da 100644 --- a/src/War3Net.Build/MapScriptBuilder/Common/ItemTableDropItems.cs +++ b/src/War3Net.Build/MapScriptBuilder/Common/ItemTableDropItems.cs @@ -1,4 +1,4 @@ -// ------------------------------------------------------------------------------ +// ------------------------------------------------------------------------------ // // Licensed under the MIT license. // See the LICENSE file in the project root for more information. @@ -13,15 +13,15 @@ using War3Net.Build.Info; using War3Net.Build.Providers; using War3Net.Build.Widget; -using War3Net.CodeAnalysis.Jass.Syntax; - -using SyntaxFactory = War3Net.CodeAnalysis.Jass.JassSyntaxFactory; +using War3Net.CodeAnalysis; +using War3Net.CodeAnalysis.Jass; +using War3Net.CodeAnalysis.Jass.Extensions; namespace War3Net.Build { public partial class MapScriptBuilder { - protected internal virtual JassFunctionDeclarationSyntax ItemTableDropItems(Map map, RandomItemTable table) + protected internal virtual void GenerateItemTableDropItems(Map map, RandomItemTable table, IndentedTextWriter writer) { if (map is null) { @@ -33,10 +33,20 @@ protected internal virtual JassFunctionDeclarationSyntax ItemTableDropItems(Map throw new ArgumentNullException(nameof(table)); } - return SyntaxFactory.FunctionDeclaration(SyntaxFactory.FunctionDeclarator(table.GetDropItemsFunctionName()), GetItemTableDropItemsStatements(map, table.ItemSets, false)); + if (writer is null) + { + throw new ArgumentNullException(nameof(writer)); + } + + writer.WriteFunction(table.GetDropItemsFunctionName()); + + WriteItemTableDropItemsStatements(map, table.ItemSets, false, writer); + + writer.EndFunction(); + writer.WriteLine(); } - protected internal virtual JassFunctionDeclarationSyntax ItemTableDropItems(Map map, WidgetData widgetData, int id) + protected internal virtual void GenerateItemTableDropItems(Map map, WidgetData widgetData, int id, IndentedTextWriter writer) { if (map is null) { @@ -48,16 +58,26 @@ protected internal virtual JassFunctionDeclarationSyntax ItemTableDropItems(Map throw new ArgumentNullException(nameof(widgetData)); } + if (writer is null) + { + throw new ArgumentNullException(nameof(writer)); + } + var funcName = widgetData switch { DoodadData doodadData => doodadData.GetDropItemsFunctionName(id), UnitData unitData => unitData.GetDropItemsFunctionName(id), }; - return SyntaxFactory.FunctionDeclaration(SyntaxFactory.FunctionDeclarator(funcName), GetItemTableDropItemsStatements(map, widgetData.ItemTableSets, true)); + writer.WriteFunction(funcName); + + WriteItemTableDropItemsStatements(map, widgetData.ItemTableSets, true, writer); + + writer.EndFunction(); + writer.WriteLine(); } - protected internal virtual IEnumerable GetItemTableDropItemsStatements(Map map, IEnumerable itemSets, bool chooseItemClass) + protected internal virtual void WriteItemTableDropItemsStatements(Map map, IEnumerable itemSets, bool chooseItemClass, IndentedTextWriter writer) { if (map is null) { @@ -69,123 +89,136 @@ protected internal virtual IEnumerable GetItemTableDropItemsSt throw new ArgumentNullException(nameof(itemSets)); } - var statements = new List(); - statements.Add(SyntaxFactory.LocalVariableDeclarationStatement(SyntaxFactory.ParseTypeName(TypeName.Widget), VariableName.TrigWidget, JassNullLiteralExpressionSyntax.Value)); - statements.Add(SyntaxFactory.LocalVariableDeclarationStatement(SyntaxFactory.ParseTypeName(TypeName.Unit), VariableName.TrigUnit, JassNullLiteralExpressionSyntax.Value)); - statements.Add(SyntaxFactory.LocalVariableDeclarationStatement(JassTypeSyntax.Integer, VariableName.ItemId, SyntaxFactory.LiteralExpression(0))); - statements.Add(SyntaxFactory.LocalVariableDeclarationStatement(JassTypeSyntax.Boolean, VariableName.CanDrop, SyntaxFactory.LiteralExpression(true))); - statements.Add(JassEmptySyntax.Value); + if (writer is null) + { + throw new ArgumentNullException(nameof(writer)); + } + + writer.WriteAlignedLocal(TypeName.Widget, VariableName.TrigWidget, JassKeyword.Null); + writer.WriteAlignedLocal(TypeName.Unit, VariableName.TrigUnit, JassKeyword.Null); + writer.WriteAlignedLocal(JassKeyword.Integer, VariableName.ItemId, "0"); + writer.WriteAlignedLocal(JassKeyword.Boolean, VariableName.CanDrop, JassKeyword.True); + writer.WriteLine(); - statements.Add(SyntaxFactory.SetStatement( + writer.WriteSet( VariableName.TrigWidget, - SyntaxFactory.VariableReferenceExpression(VariableName.BJLastDyingWidget))); + VariableName.BJLastDyingWidget); - statements.Add(SyntaxFactory.IfStatement( - new JassParenthesizedExpressionSyntax(SyntaxFactory.BinaryEqualsExpression(SyntaxFactory.VariableReferenceExpression(VariableName.TrigWidget), JassNullLiteralExpressionSyntax.Value)), - SyntaxFactory.SetStatement(VariableName.TrigUnit, SyntaxFactory.InvocationExpression(NativeName.GetTriggerUnit)))); + writer.WriteIf(JassExpression.ParenthesizedCompact(JassExpression.Equal( + VariableName.TrigWidget, + JassKeyword.Null))); - statements.Add(JassEmptySyntax.Value); + writer.WriteSet(VariableName.TrigUnit, JassExpression.Invoke(NativeName.GetTriggerUnit)); + writer.WriteEndIf(); - var canDropConditionExpression = SyntaxFactory.UnaryNotExpression(SyntaxFactory.InvocationExpression(NativeName.IsUnitHidden, SyntaxFactory.VariableReferenceExpression(VariableName.TrigUnit))); + writer.WriteLine(); - var ifBody = new List() - { - SyntaxFactory.SetStatement(VariableName.CanDrop, canDropConditionExpression), - }; + var trigUnitNotNullExpression = JassExpression.ParenthesizedCompact(JassExpression.NotEqual( + VariableName.TrigUnit, + JassKeyword.Null)); + + var changingUnitNotNullExpression = JassExpression.NotEqual( + JassExpression.Invoke(NativeName.GetChangingUnit), + JassKeyword.Null); + + writer.WriteIf(trigUnitNotNullExpression); + + writer.WriteSet( + VariableName.CanDrop, + JassExpression.Not(JassExpression.Invoke( + NativeName.IsUnitHidden, + VariableName.TrigUnit))); + + writer.WriteIf(JassExpression.ParenthesizedCompact(JassExpression.And( + VariableName.CanDrop, + changingUnitNotNullExpression))); - ifBody.Add(SyntaxFactory.IfStatement( - new JassParenthesizedExpressionSyntax(SyntaxFactory.BinaryAndExpression( - SyntaxFactory.VariableReferenceExpression(VariableName.CanDrop), - SyntaxFactory.BinaryNotEqualsExpression(SyntaxFactory.InvocationExpression(NativeName.GetChangingUnit), JassNullLiteralExpressionSyntax.Value))), - SyntaxFactory.SetStatement( - VariableName.CanDrop, - new JassParenthesizedExpressionSyntax(SyntaxFactory.BinaryEqualsExpression( - SyntaxFactory.InvocationExpression(NativeName.GetChangingUnitPrevOwner), - SyntaxFactory.InvocationExpression(NativeName.Player, SyntaxFactory.VariableReferenceExpression(GlobalVariableName.PlayerNeutralHostile))))))); - - statements.Add(SyntaxFactory.IfStatement( - new JassParenthesizedExpressionSyntax(SyntaxFactory.BinaryNotEqualsExpression(SyntaxFactory.VariableReferenceExpression(VariableName.TrigUnit), JassNullLiteralExpressionSyntax.Value)), - SyntaxFactory.StatementList(ifBody))); - statements.Add(JassEmptySyntax.Value); + writer.WriteSet( + VariableName.CanDrop, + JassExpression.ParenthesizedCompact(JassExpression.Equal( + JassExpression.Invoke(NativeName.GetChangingUnitPrevOwner), + JassExpression.Invoke(NativeName.Player, GlobalVariableName.PlayerNeutralHostile)))); + + writer.WriteEndIf(); + writer.WriteEndIf(); + writer.WriteLine(); + + writer.WriteIf(JassExpression.ParenthesizedCompact(VariableName.CanDrop)); var i = 0; - var randomDistStatements = new List(); foreach (var itemSet in itemSets) { - randomDistStatements.Add(new JassCommentSyntax($" Item set {i}")); - randomDistStatements.Add(SyntaxFactory.CallStatement(FunctionName.RandomDistReset)); + writer.WriteComment($"Item set {i}"); + writer.WriteCall(FunctionName.RandomDistReset); var summedChance = 0; foreach (var item in itemSet.Items) { + string itemIdExpression; if (RandomItemProvider.IsRandomItem(item.ItemId, out var itemClass, out var level)) { if (chooseItemClass) { - randomDistStatements.Add(SyntaxFactory.CallStatement( - FunctionName.RandomDistAddItem, - SyntaxFactory.InvocationExpression( - NativeName.ChooseRandomItemEx, - SyntaxFactory.VariableReferenceExpression(itemClass.GetVariableName()), - SyntaxFactory.LiteralExpression(level)), - SyntaxFactory.LiteralExpression(item.Chance))); + itemIdExpression = JassExpression.InvokeSpaced( + NativeName.ChooseRandomItemEx, + itemClass.GetVariableName(), + JassLiteral.Int(level)); } else { - randomDistStatements.Add(SyntaxFactory.CallStatement( - FunctionName.RandomDistAddItem, - SyntaxFactory.InvocationExpression(NativeName.ChooseRandomItem, SyntaxFactory.LiteralExpression(level)), - SyntaxFactory.LiteralExpression(item.Chance))); + itemIdExpression = JassExpression.InvokeSpaced( + NativeName.ChooseRandomItem, + JassLiteral.Int(level)); } } else { - randomDistStatements.Add(SyntaxFactory.CallStatement( - FunctionName.RandomDistAddItem, - SyntaxFactory.FourCCLiteralExpression(item.ItemId), - SyntaxFactory.LiteralExpression(item.Chance))); + itemIdExpression = JassLiteral.FourCC(item.ItemId); } + writer.WriteCall( + FunctionName.RandomDistAddItem, + itemIdExpression, + JassLiteral.Int(item.Chance)); + summedChance += item.Chance; } if (summedChance < 100) { - randomDistStatements.Add(SyntaxFactory.CallStatement( + writer.WriteCall( FunctionName.RandomDistAddItem, - SyntaxFactory.LiteralExpression(-1), - SyntaxFactory.LiteralExpression(100 - summedChance))); + "-1", + JassLiteral.Int(100 - summedChance)); } - var unitDropItemStatement = SyntaxFactory.CallStatement( - FunctionName.UnitDropItem, - SyntaxFactory.VariableReferenceExpression(VariableName.TrigUnit), - SyntaxFactory.VariableReferenceExpression(VariableName.ItemId)); + writer.WriteSet( + VariableName.ItemId, + JassExpression.InvokeSpaced(FunctionName.RandomDistChoose)); - randomDistStatements.Add(SyntaxFactory.SetStatement(VariableName.ItemId, SyntaxFactory.InvocationExpression(FunctionName.RandomDistChoose))); + writer.WriteIf(trigUnitNotNullExpression); + writer.WriteCall( + FunctionName.UnitDropItem, + VariableName.TrigUnit, + VariableName.ItemId); - randomDistStatements.Add(SyntaxFactory.IfStatement( - new JassParenthesizedExpressionSyntax(SyntaxFactory.BinaryNotEqualsExpression(SyntaxFactory.VariableReferenceExpression(VariableName.TrigUnit), JassNullLiteralExpressionSyntax.Value)), - SyntaxFactory.StatementList(unitDropItemStatement), - new JassElseClauseSyntax(SyntaxFactory.StatementList(SyntaxFactory.CallStatement( - FunctionName.WidgetDropItem, - SyntaxFactory.VariableReferenceExpression(VariableName.TrigWidget), - SyntaxFactory.VariableReferenceExpression(VariableName.ItemId)))))); + writer.WriteElse(); + writer.WriteCall( + FunctionName.WidgetDropItem, + VariableName.TrigWidget, + VariableName.ItemId); - randomDistStatements.Add(JassEmptySyntax.Value); + writer.WriteEndIf(); + writer.WriteLine(); i++; } - statements.Add(SyntaxFactory.IfStatement( - new JassParenthesizedExpressionSyntax(SyntaxFactory.VariableReferenceExpression(VariableName.CanDrop)), - randomDistStatements.ToArray())); - statements.Add(JassEmptySyntax.Value); - - statements.Add(SyntaxFactory.SetStatement(VariableName.BJLastDyingWidget, JassNullLiteralExpressionSyntax.Value)); - statements.Add(SyntaxFactory.CallStatement(NativeName.DestroyTrigger, SyntaxFactory.InvocationExpression(NativeName.GetTriggeringTrigger))); + writer.WriteEndIf(); + writer.WriteLine(); - return statements; + writer.WriteSet(VariableName.BJLastDyingWidget, JassKeyword.Null); + writer.WriteCallCompact(NativeName.DestroyTrigger, JassExpression.Invoke(NativeName.GetTriggeringTrigger)); } } } \ No newline at end of file diff --git a/src/War3Net.Build/MapScriptBuilder/Common/Main.cs b/src/War3Net.Build/MapScriptBuilder/Common/Main.cs index 528d4cde..ba40f76c 100644 --- a/src/War3Net.Build/MapScriptBuilder/Common/Main.cs +++ b/src/War3Net.Build/MapScriptBuilder/Common/Main.cs @@ -1,268 +1,277 @@ -// ------------------------------------------------------------------------------ +// ------------------------------------------------------------------------------ // // Licensed under the MIT license. // See the LICENSE file in the project root for more information. // // ------------------------------------------------------------------------------ -#pragma warning disable IDE1006, SA1300 - using System; -using System.Collections.Generic; using War3Net.Build.Common; using War3Net.Build.Environment; using War3Net.Build.Info; using War3Net.Build.Providers; -using War3Net.CodeAnalysis.Jass.Syntax; - -using SyntaxFactory = War3Net.CodeAnalysis.Jass.JassSyntaxFactory; +using War3Net.CodeAnalysis; +using War3Net.CodeAnalysis.Jass; +using War3Net.CodeAnalysis.Jass.Extensions; namespace War3Net.Build { public partial class MapScriptBuilder { - protected internal virtual JassFunctionDeclarationSyntax main(Map map) + protected internal virtual void GenerateMain(Map map, IndentedTextWriter writer) { if (map is null) { throw new ArgumentNullException(nameof(map)); } + if (writer is null) + { + throw new ArgumentNullException(nameof(writer)); + } + var mapEnvironment = map.Environment; if (mapEnvironment is null) { - throw new ArgumentException($"Function '{nameof(main)}' cannot be generated without {nameof(MapEnvironment)}.", nameof(map)); + throw new ArgumentException($"Function '{GeneratedFunctionName.Main}' cannot be generated without {nameof(MapEnvironment)}.", nameof(map)); } var mapInfo = map.Info; if (mapInfo is null) { - throw new ArgumentException($"Function '{nameof(main)}' cannot be generated without {nameof(MapInfo)}.", nameof(map)); + throw new ArgumentException($"Function '{GeneratedFunctionName.Main}' cannot be generated without {nameof(MapInfo)}.", nameof(map)); } - var statements = new List(); + writer.WriteFunction(GeneratedFunctionName.Main); - if (UseWeatherEffectVariable && EnableGlobalWeatherEffectCondition(map)) + if (UseWeatherEffectVariable && ShouldCallEnableGlobalWeatherEffect(map)) { - statements.Add(SyntaxFactory.LocalVariableDeclarationStatement(SyntaxFactory.ParseTypeName(TypeName.WeatherEffect), VariableName.WeatherEffect)); + writer.WriteLocal(TypeName.WeatherEffect, VariableName.WeatherEffect); } if (mapInfo.CameraBoundsComplements is null) { - statements.Add(SyntaxFactory.CallStatement( + writer.WriteCall( NativeName.SetCameraBounds, - SyntaxFactory.LiteralExpression(mapInfo.CameraBounds.BottomLeft.X, precision: 1), - SyntaxFactory.LiteralExpression(mapInfo.CameraBounds.BottomLeft.Y, precision: 1), - SyntaxFactory.LiteralExpression(mapInfo.CameraBounds.TopRight.X, precision: 1), - SyntaxFactory.LiteralExpression(mapInfo.CameraBounds.TopRight.Y, precision: 1), - SyntaxFactory.LiteralExpression(mapInfo.CameraBounds.TopLeft.X, precision: 1), - SyntaxFactory.LiteralExpression(mapInfo.CameraBounds.TopLeft.Y, precision: 1), - SyntaxFactory.LiteralExpression(mapInfo.CameraBounds.BottomRight.X, precision: 1), - SyntaxFactory.LiteralExpression(mapInfo.CameraBounds.BottomRight.Y, precision: 1))); + JassLiteral.Real(mapInfo.CameraBounds.BottomLeft.X), + JassLiteral.Real(mapInfo.CameraBounds.BottomLeft.Y), + JassLiteral.Real(mapInfo.CameraBounds.TopRight.X), + JassLiteral.Real(mapInfo.CameraBounds.TopRight.Y), + JassLiteral.Real(mapInfo.CameraBounds.TopLeft.X), + JassLiteral.Real(mapInfo.CameraBounds.TopLeft.Y), + JassLiteral.Real(mapInfo.CameraBounds.BottomRight.X), + JassLiteral.Real(mapInfo.CameraBounds.BottomRight.Y)); } else { - statements.Add(SyntaxFactory.CallStatement( + var left = JassLiteral.Real(mapEnvironment.Left + (128 * mapInfo.CameraBoundsComplements.Left)); + var bottom = JassLiteral.Real(mapEnvironment.Bottom + (128 * mapInfo.CameraBoundsComplements.Bottom)); + var right = JassLiteral.Real(mapEnvironment.Right - (128 * mapInfo.CameraBoundsComplements.Right)); + var top = JassLiteral.Real(mapEnvironment.Top - (128 * mapInfo.CameraBoundsComplements.Top)); + + var marginLeft = JassExpression.Invoke(NativeName.GetCameraMargin, CameraMarginName.Left); + var marginBottom = JassExpression.Invoke(NativeName.GetCameraMargin, CameraMarginName.Bottom); + var marginRight = JassExpression.Invoke(NativeName.GetCameraMargin, CameraMarginName.Right); + var marginTop = JassExpression.Invoke(NativeName.GetCameraMargin, CameraMarginName.Top); + + writer.WriteCall( NativeName.SetCameraBounds, - SyntaxFactory.BinaryAdditionExpression( - SyntaxFactory.LiteralExpression(mapEnvironment.Left + (128 * mapInfo.CameraBoundsComplements.Left), precision: 1), - SyntaxFactory.InvocationExpression(NativeName.GetCameraMargin, SyntaxFactory.VariableReferenceExpression(CameraMarginName.Left))), - SyntaxFactory.BinaryAdditionExpression( - SyntaxFactory.LiteralExpression(mapEnvironment.Bottom + (128 * mapInfo.CameraBoundsComplements.Bottom), precision: 1), - SyntaxFactory.InvocationExpression(NativeName.GetCameraMargin, SyntaxFactory.VariableReferenceExpression(CameraMarginName.Bottom))), - SyntaxFactory.BinarySubtractionExpression( - SyntaxFactory.LiteralExpression(mapEnvironment.Right - (128 * mapInfo.CameraBoundsComplements.Right), precision: 1), - SyntaxFactory.InvocationExpression(NativeName.GetCameraMargin, SyntaxFactory.VariableReferenceExpression(CameraMarginName.Right))), - SyntaxFactory.BinarySubtractionExpression( - SyntaxFactory.LiteralExpression(mapEnvironment.Top - (128 * mapInfo.CameraBoundsComplements.Top), precision: 1), - SyntaxFactory.InvocationExpression(NativeName.GetCameraMargin, SyntaxFactory.VariableReferenceExpression(CameraMarginName.Top))), - SyntaxFactory.BinaryAdditionExpression( - SyntaxFactory.LiteralExpression(mapEnvironment.Left + (128 * mapInfo.CameraBoundsComplements.Left), precision: 1), - SyntaxFactory.InvocationExpression(NativeName.GetCameraMargin, SyntaxFactory.VariableReferenceExpression(CameraMarginName.Left))), - SyntaxFactory.BinarySubtractionExpression( - SyntaxFactory.LiteralExpression(mapEnvironment.Top - (128 * mapInfo.CameraBoundsComplements.Top), precision: 1), - SyntaxFactory.InvocationExpression(NativeName.GetCameraMargin, SyntaxFactory.VariableReferenceExpression(CameraMarginName.Top))), - SyntaxFactory.BinarySubtractionExpression( - SyntaxFactory.LiteralExpression(mapEnvironment.Right - (128 * mapInfo.CameraBoundsComplements.Right), precision: 1), - SyntaxFactory.InvocationExpression(NativeName.GetCameraMargin, SyntaxFactory.VariableReferenceExpression(CameraMarginName.Right))), - SyntaxFactory.BinaryAdditionExpression( - SyntaxFactory.LiteralExpression(mapEnvironment.Bottom + (128 * mapInfo.CameraBoundsComplements.Bottom), precision: 1), - SyntaxFactory.InvocationExpression(NativeName.GetCameraMargin, SyntaxFactory.VariableReferenceExpression(CameraMarginName.Bottom))))); + JassExpression.Add(left, marginLeft), + JassExpression.Add(bottom, marginBottom), + JassExpression.Subtract(right, marginRight), + JassExpression.Subtract(top, marginTop), + JassExpression.Add(left, marginLeft), + JassExpression.Subtract(top, marginTop), + JassExpression.Subtract(right, marginRight), + JassExpression.Add(bottom, marginBottom)); } - if (SetDayNightModelsCondition(map)) + if (ShouldCallSetDayNightModels(map)) { var lightEnvironment = mapInfo.LightEnvironment == Tileset.Unspecified ? mapInfo.Tileset : mapInfo.LightEnvironment; - statements.Add(SyntaxFactory.CallStatement( + writer.WriteCall( NativeName.SetDayNightModels, - SyntaxFactory.LiteralExpression(EscapedStringProvider.GetEscapedString(LightEnvironmentProvider.GetTerrainLightEnvironmentModel(lightEnvironment))), - SyntaxFactory.LiteralExpression(EscapedStringProvider.GetEscapedString(LightEnvironmentProvider.GetUnitLightEnvironmentModel(lightEnvironment))))); + JassLiteral.String(LightEnvironmentProvider.GetTerrainLightEnvironmentModel(lightEnvironment)), + JassLiteral.String(LightEnvironmentProvider.GetUnitLightEnvironmentModel(lightEnvironment))); } - if (SetTerrainFogExCondition(map)) + if (ShouldCallSetTerrainFogEx(map)) { var precision = mapInfo.FormatVersion >= MapInfoFormatVersion.v31 ? 3 : 1; - statements.Add(SyntaxFactory.CallStatement( + writer.WriteCall( NativeName.SetTerrainFogEx, - SyntaxFactory.LiteralExpression((int)mapInfo.FogStyle), - SyntaxFactory.LiteralExpression(mapInfo.FogStartZ), - SyntaxFactory.LiteralExpression(mapInfo.FogEndZ), - SyntaxFactory.LiteralExpression(mapInfo.FogDensity, precision), - SyntaxFactory.LiteralExpression(mapInfo.FogColor.R / 255f, precision), - SyntaxFactory.LiteralExpression(mapInfo.FogColor.G / 255f, precision), - SyntaxFactory.LiteralExpression(mapInfo.FogColor.B / 255f, precision))); + JassLiteral.Int((int)mapInfo.FogStyle), + JassLiteral.Real(mapInfo.FogStartZ), + JassLiteral.Real(mapInfo.FogEndZ), + JassLiteral.Real(mapInfo.FogDensity, precision), + JassLiteral.Real(mapInfo.FogColor.R / 255f, precision), + JassLiteral.Real(mapInfo.FogColor.G / 255f, precision), + JassLiteral.Real(mapInfo.FogColor.B / 255f, precision)); } - if (SetWaterBaseColorCondition(map)) + if (ShouldCallSetWaterBaseColor(map)) { - statements.Add(SyntaxFactory.CallStatement( + writer.WriteCall( NativeName.SetWaterBaseColor, - SyntaxFactory.LiteralExpression(mapInfo.WaterTintingColor.R), - SyntaxFactory.LiteralExpression(mapInfo.WaterTintingColor.G), - SyntaxFactory.LiteralExpression(mapInfo.WaterTintingColor.B), - SyntaxFactory.LiteralExpression(mapInfo.WaterTintingColor.A))); + JassLiteral.Int(mapInfo.WaterTintingColor.R), + JassLiteral.Int(mapInfo.WaterTintingColor.G), + JassLiteral.Int(mapInfo.WaterTintingColor.B), + JassLiteral.Int(mapInfo.WaterTintingColor.A)); } - if (EnableGlobalWeatherEffectCondition(map)) + if (ShouldCallEnableGlobalWeatherEffect(map)) { - var createWeather = SyntaxFactory.InvocationExpression( - NativeName.AddWeatherEffect, - SyntaxFactory.InvocationExpression( - NativeName.Rect, - SyntaxFactory.LiteralExpression(mapEnvironment.Left, precision: 1), - SyntaxFactory.LiteralExpression(mapEnvironment.Bottom, precision: 1), - SyntaxFactory.LiteralExpression(mapEnvironment.Right, precision: 1), - SyntaxFactory.LiteralExpression(mapEnvironment.Top, precision: 1)), - SyntaxFactory.FourCCLiteralExpression((int)mapInfo.GlobalWeather)); + var weatherType = JassLiteral.FourCC((int)mapInfo.GlobalWeather); + var weatherRegion = JassExpression.InvokeCompact( + NativeName.Rect, + JassLiteral.Real(mapEnvironment.Left), + JassLiteral.Real(mapEnvironment.Bottom), + JassLiteral.Real(mapEnvironment.Right), + JassLiteral.Real(mapEnvironment.Top)); if (UseWeatherEffectVariable) { - statements.Add(SyntaxFactory.SetStatement(VariableName.WeatherEffect, createWeather)); - statements.Add(SyntaxFactory.CallStatement(NativeName.EnableWeatherEffect, SyntaxFactory.VariableReferenceExpression(VariableName.WeatherEffect), SyntaxFactory.LiteralExpression(true))); + writer.WriteSet( + VariableName.WeatherEffect, + JassExpression.InvokeSpaced( + NativeName.AddWeatherEffect, + weatherRegion, + weatherType)); + + writer.WriteCall( + NativeName.EnableWeatherEffect, + VariableName.WeatherEffect, + JassKeyword.True); } else { - statements.Add(SyntaxFactory.CallStatement(NativeName.EnableWeatherEffect, createWeather, SyntaxFactory.LiteralExpression(true))); + writer.WriteCall( + NativeName.EnableWeatherEffect, + JassExpression.Invoke( + NativeName.AddWeatherEffect, + weatherRegion, + weatherType), + JassKeyword.True); } } - if (NewSoundEnvironmentCondition(map)) + if (ShouldCallNewSoundEnvironment(map)) { - statements.Add(SyntaxFactory.CallStatement( + writer.WriteCall( NativeName.NewSoundEnvironment, - SyntaxFactory.LiteralExpression(EscapedStringProvider.GetEscapedString(string.IsNullOrEmpty(mapInfo.SoundEnvironment) ? "Default" : mapInfo.SoundEnvironment)))); + JassLiteral.String(string.IsNullOrEmpty(mapInfo.SoundEnvironment) ? "Default" : mapInfo.SoundEnvironment)); } - if (SetAmbientSoundCondition(map)) + if (ShouldCallSetAmbientSound(map)) { - statements.Add(SyntaxFactory.CallStatement( + writer.WriteCall( FunctionName.SetAmbientDaySound, - SyntaxFactory.LiteralExpression(EscapedStringProvider.GetEscapedString(SoundEnvironmentProvider.GetAmbientDaySound(mapInfo.Tileset))))); + JassLiteral.String(SoundEnvironmentProvider.GetAmbientDaySound(mapInfo.Tileset))); - statements.Add(SyntaxFactory.CallStatement( + writer.WriteCall( FunctionName.SetAmbientNightSound, - SyntaxFactory.LiteralExpression(EscapedStringProvider.GetEscapedString(SoundEnvironmentProvider.GetAmbientNightSound(mapInfo.Tileset))))); + JassLiteral.String(SoundEnvironmentProvider.GetAmbientNightSound(mapInfo.Tileset))); } - if (SetMapMusicCondition(map)) + if (ShouldCallSetMapMusic(map)) { - statements.Add(SyntaxFactory.CallStatement( + writer.WriteCall( NativeName.SetMapMusic, - SyntaxFactory.LiteralExpression("Music"), - SyntaxFactory.LiteralExpression(true), - SyntaxFactory.LiteralExpression(0))); + JassLiteral.String("Music"), + JassKeyword.True, + "0"); } - if (InitSoundsCondition(map)) + if (ShouldGenerateInitSounds(map)) { - statements.Add(SyntaxFactory.CallStatement(nameof(InitSounds))); + writer.WriteCall(GeneratedFunctionName.InitSounds); } - if (CreateRegionsCondition(map)) + if (ShouldGenerateCreateRegions(map)) { - statements.Add(SyntaxFactory.CallStatement(nameof(CreateRegions))); + writer.WriteCall(GeneratedFunctionName.CreateRegions); } - if (CreateCamerasCondition(map)) + if (ShouldGenerateCreateCameras(map)) { - statements.Add(SyntaxFactory.CallStatement(nameof(CreateCameras))); + writer.WriteCall(GeneratedFunctionName.CreateCameras); } - if (InitUpgradesCondition(map)) + if (ShouldGenerateInitUpgrades(map)) { - statements.Add(SyntaxFactory.CallStatement(nameof(InitUpgrades))); + writer.WriteCall(GeneratedFunctionName.InitUpgrades); } - if (InitTechTreeCondition(map)) + if (ShouldGenerateInitTechTree(map)) { - statements.Add(SyntaxFactory.CallStatement(nameof(InitTechTree))); + writer.WriteCall(GeneratedFunctionName.InitTechTree); } - if (CreateAllDestructablesCondition(map)) + if (ShouldGenerateCreateAllDestructables(map)) { - statements.Add(SyntaxFactory.CallStatement(nameof(CreateAllDestructables))); + writer.WriteCall(GeneratedFunctionName.CreateAllDestructables); } - if (CreateAllItemsCondition(map)) + if (ShouldGenerateCreateAllItems(map)) { - statements.Add(SyntaxFactory.CallStatement(nameof(CreateAllItems))); + writer.WriteCall(GeneratedFunctionName.CreateAllItems); } - if (InitRandomGroupsCondition(map)) + if (ShouldGenerateInitRandomGroups(map)) { - statements.Add(SyntaxFactory.CallStatement(nameof(InitRandomGroups))); + writer.WriteCall(GeneratedFunctionName.InitRandomGroups); } - if (CreateAllUnitsCondition(map)) + if (ShouldGenerateCreateAllUnits(map)) { - statements.Add(SyntaxFactory.CallStatement(nameof(CreateAllUnits))); + writer.WriteCall(GeneratedFunctionName.CreateAllUnits); } else { - if (CreateNeutralUnitsCondition(map)) + if (ShouldGenerateCreateNeutralUnits(map)) { - statements.Add(SyntaxFactory.CallStatement(nameof(CreateNeutralUnits))); + writer.WriteCall(GeneratedFunctionName.CreateNeutralUnits); } - if (CreatePlayerUnitsCondition(map)) + if (ShouldGenerateCreatePlayerUnits(map)) { - statements.Add(SyntaxFactory.CallStatement(nameof(CreatePlayerUnits))); + writer.WriteCall(GeneratedFunctionName.CreatePlayerUnits); } } - if (InitBlizzardCondition(map)) + if (ShouldCallInitBlizzard(map)) { - statements.Add(SyntaxFactory.CallStatement(FunctionName.InitBlizzard)); + writer.WriteCall(FunctionName.InitBlizzard); } - if (InitGlobalsCondition(map)) + if (ShouldGenerateInitGlobals(map)) { - statements.Add(SyntaxFactory.CallStatement(nameof(InitGlobals))); + writer.WriteCall(GeneratedFunctionName.InitGlobals); } - if (InitCustomTriggersCondition(map)) + if (ShouldGenerateInitCustomTriggers(map)) { - statements.Add(SyntaxFactory.CallStatement(nameof(InitCustomTriggers))); + writer.WriteCall(GeneratedFunctionName.InitCustomTriggers); } - if (RunInitializationTriggersCondition(map)) + if (ShouldGenerateRunInitializationTriggers(map)) { - statements.Add(SyntaxFactory.CallStatement(nameof(RunInitializationTriggers))); + writer.WriteCall(GeneratedFunctionName.RunInitializationTriggers); } if (UseCSharpLua) { - statements.Add(SyntaxFactory.CallStatement(CSharpLua.LuaSyntaxGenerator.kManifestFuncName)); + writer.WriteCall(CSharpLua.LuaSyntaxGenerator.kManifestFuncName); } - statements.Add(JassEmptySyntax.Value); + writer.WriteLine(); - return SyntaxFactory.FunctionDeclaration(SyntaxFactory.FunctionDeclarator(nameof(main)), statements); + writer.EndFunction(); } - protected internal virtual bool mainCondition(Map map) + protected internal virtual bool ShouldGenerateMain(Map map) { if (map is null) { diff --git a/src/War3Net.Build/MapScriptBuilder/Common/NativeConditions.cs b/src/War3Net.Build/MapScriptBuilder/Common/NativeConditions.cs index eaae347e..cf3e9a04 100644 --- a/src/War3Net.Build/MapScriptBuilder/Common/NativeConditions.cs +++ b/src/War3Net.Build/MapScriptBuilder/Common/NativeConditions.cs @@ -14,7 +14,7 @@ namespace War3Net.Build { public partial class MapScriptBuilder { - protected internal virtual bool SetDayNightModelsCondition(Map map) + protected internal virtual bool ShouldCallSetDayNightModels(Map map) { if (map is null) { @@ -25,7 +25,7 @@ protected internal virtual bool SetDayNightModelsCondition(Map map) && map.Info.FormatVersion >= MapInfoFormatVersion.v15; } - protected internal virtual bool SetTerrainFogExCondition(Map map) + protected internal virtual bool ShouldCallSetTerrainFogEx(Map map) { if (map is null) { @@ -36,7 +36,7 @@ protected internal virtual bool SetTerrainFogExCondition(Map map) && map.Info.MapFlags.HasFlag(MapFlags.HasTerrainFog); } - protected internal virtual bool SetWaterBaseColorCondition(Map map) + protected internal virtual bool ShouldCallSetWaterBaseColor(Map map) { if (map is null) { @@ -47,7 +47,7 @@ protected internal virtual bool SetWaterBaseColorCondition(Map map) && map.Info.MapFlags.HasFlag(MapFlags.HasWaterTintingColor); } - protected internal virtual bool EnableGlobalWeatherEffectCondition(Map map) + protected internal virtual bool ShouldCallEnableGlobalWeatherEffect(Map map) { if (map is null) { @@ -58,7 +58,7 @@ protected internal virtual bool EnableGlobalWeatherEffectCondition(Map map) && map.Info.GlobalWeather != WeatherType.None; } - protected internal virtual bool NewSoundEnvironmentCondition(Map map) + protected internal virtual bool ShouldCallNewSoundEnvironment(Map map) { if (map is null) { @@ -69,7 +69,7 @@ protected internal virtual bool NewSoundEnvironmentCondition(Map map) && map.Info.FormatVersion > MapInfoFormatVersion.v15; } - protected internal virtual bool SetAmbientSoundCondition(Map map) + protected internal virtual bool ShouldCallSetAmbientSound(Map map) { if (map is null) { @@ -80,7 +80,7 @@ protected internal virtual bool SetAmbientSoundCondition(Map map) && map.Info.FormatVersion >= MapInfoFormatVersion.v15; } - protected internal virtual bool SetMapMusicCondition(Map map) + protected internal virtual bool ShouldCallSetMapMusic(Map map) { if (map is null) { @@ -91,7 +91,7 @@ protected internal virtual bool SetMapMusicCondition(Map map) && map.Info.FormatVersion >= MapInfoFormatVersion.v15; } - protected internal virtual bool InitBlizzardCondition(Map map) + protected internal virtual bool ShouldCallInitBlizzard(Map map) { if (map is null) { diff --git a/src/War3Net.Build/MapScriptBuilder/Constants/GeneratedFunctionName.cs b/src/War3Net.Build/MapScriptBuilder/Constants/GeneratedFunctionName.cs new file mode 100644 index 00000000..01f5e759 --- /dev/null +++ b/src/War3Net.Build/MapScriptBuilder/Constants/GeneratedFunctionName.cs @@ -0,0 +1,48 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +namespace War3Net.Build +{ + public partial class MapScriptBuilder + { + internal static class GeneratedFunctionName + { + internal const string Config = "config"; + internal const string CreateAllDestructables = "CreateAllDestructables"; + internal const string CreateAllItems = "CreateAllItems"; + internal const string CreateAllUnits = "CreateAllUnits"; + internal const string CreateCameras = "CreateCameras"; + internal const string CreateNeutralHostile = "CreateNeutralHostile"; + internal const string CreateNeutralHostileBuildings = "CreateNeutralHostileBuildings"; + internal const string CreateNeutralPassive = "CreateNeutralPassive"; + internal const string CreateNeutralPassiveBuildings = "CreateNeutralPassiveBuildings"; + internal const string CreateNeutralUnits = "CreateNeutralUnits"; + internal const string CreatePlayerBuildings = "CreatePlayerBuildings"; + internal const string CreatePlayerUnits = "CreatePlayerUnits"; + internal const string CreateRegions = "CreateRegions"; + internal const string InitAllyPriorities = "InitAllyPriorities"; + internal const string InitCustomPlayerSlots = "InitCustomPlayerSlots"; + internal const string InitCustomTeams = "InitCustomTeams"; + internal const string InitCustomTriggers = "InitCustomTriggers"; + internal const string InitGlobals = "InitGlobals"; + internal const string InitRandomGroups = "InitRandomGroups"; + internal const string InitSounds = "InitSounds"; + internal const string InitTechTree = "InitTechTree"; + internal const string InitUpgrades = "InitUpgrades"; + internal const string Main = "main"; + internal const string RunInitializationTriggers = "RunInitializationTriggers"; + + internal static string CreateBuildingsForPlayer(int playerId) => $"CreateBuildingsForPlayer{playerId}"; + + internal static string CreateUnitsForPlayer(int playerId) => $"CreateUnitsForPlayer{playerId}"; + + internal static string InitTechTreeForPlayer(int playerId) => $"InitTechTree_Player{playerId}"; + + internal static string InitUpgradesForPlayer(int playerId) => $"InitUpgrades_Player{playerId}"; + } + } +} \ No newline at end of file diff --git a/src/War3Net.Build/MapScriptBuilder/Constants/NativeName.cs b/src/War3Net.Build/MapScriptBuilder/Constants/NativeName.cs index 0e3132a5..c4673c0f 100644 --- a/src/War3Net.Build/MapScriptBuilder/Constants/NativeName.cs +++ b/src/War3Net.Build/MapScriptBuilder/Constants/NativeName.cs @@ -9,7 +9,7 @@ namespace War3Net.Build { public partial class MapScriptBuilder { - private class NativeName + internal static class NativeName { internal const string AddWeatherEffect = "AddWeatherEffect"; internal const string BlzCreateDeadDestructableWithSkin = "BlzCreateDeadDestructableWithSkin"; diff --git a/src/War3Net.Build/MapScriptBuilder/Environment/Cameras.cs b/src/War3Net.Build/MapScriptBuilder/Environment/Cameras.cs index 0ae97527..edba2b86 100644 --- a/src/War3Net.Build/MapScriptBuilder/Environment/Cameras.cs +++ b/src/War3Net.Build/MapScriptBuilder/Environment/Cameras.cs @@ -6,51 +6,52 @@ // ------------------------------------------------------------------------------ using System; -using System.Collections.Generic; -using System.Linq; - -using Microsoft.CodeAnalysis.CSharp.Syntax; using War3Net.Build.Extensions; -using War3Net.CodeAnalysis.Jass.Syntax; -using War3Net.CodeAnalysis.Transpilers; - -using SyntaxFactory = War3Net.CodeAnalysis.Jass.JassSyntaxFactory; +using War3Net.CodeAnalysis; +using War3Net.CodeAnalysis.Jass; +using War3Net.CodeAnalysis.Jass.Extensions; namespace War3Net.Build { public partial class MapScriptBuilder { - public virtual IEnumerable CamerasApi(Map map, JassToCSharpTranspiler transpiler) + protected internal virtual void GenerateCameraVariables(Map map, IndentedTextWriter writer) { - if (transpiler is null) + if (map is null) { - throw new ArgumentNullException(nameof(transpiler)); + throw new ArgumentNullException(nameof(map)); } - return Cameras(map).Select(camera => transpiler.Transpile(camera)); - } - - protected internal virtual IEnumerable Cameras(Map map) - { - if (map is null) + if (writer is null) { - throw new ArgumentNullException(nameof(map)); + throw new ArgumentNullException(nameof(writer)); } var mapCameras = map.Cameras; if (mapCameras is null) { - yield break; + return; } foreach (var camera in mapCameras.Cameras) { - yield return SyntaxFactory.GlobalDeclaration( - SyntaxFactory.ParseTypeName(TypeName.CameraSetup), + writer.WriteAlignedGlobal( + TypeName.CameraSetup, camera.GetVariableName(), - JassNullLiteralExpressionSyntax.Value); + JassKeyword.Null); } } + + protected internal virtual bool ShouldGenerateCameraVariables(Map map) + { + if (map is null) + { + throw new ArgumentNullException(nameof(map)); + } + + return map.Cameras is not null + && map.Cameras.Cameras.Count > 0; + } } } \ No newline at end of file diff --git a/src/War3Net.Build/MapScriptBuilder/Environment/CreateCameras.cs b/src/War3Net.Build/MapScriptBuilder/Environment/CreateCameras.cs index 575d95e5..5f65d040 100644 --- a/src/War3Net.Build/MapScriptBuilder/Environment/CreateCameras.cs +++ b/src/War3Net.Build/MapScriptBuilder/Environment/CreateCameras.cs @@ -6,66 +6,67 @@ // ------------------------------------------------------------------------------ using System; -using System.Collections.Generic; -using System.Linq; using War3Net.Build.Environment; using War3Net.Build.Extensions; using War3Net.Build.Info; -using War3Net.CodeAnalysis.Jass.Syntax; - -using SyntaxFactory = War3Net.CodeAnalysis.Jass.JassSyntaxFactory; +using War3Net.CodeAnalysis; +using War3Net.CodeAnalysis.Jass; +using War3Net.CodeAnalysis.Jass.Extensions; namespace War3Net.Build { public partial class MapScriptBuilder { - protected internal virtual JassFunctionDeclarationSyntax CreateCameras(Map map) + protected internal virtual void GenerateCreateCameras(Map map, IndentedTextWriter writer) { if (map is null) { throw new ArgumentNullException(nameof(map)); } + if (writer is null) + { + throw new ArgumentNullException(nameof(writer)); + } + var mapCameras = map.Cameras; if (mapCameras is null) { - throw new ArgumentException($"Function '{nameof(CreateCameras)}' cannot be generated without {nameof(MapCameras)}.", nameof(map)); + throw new ArgumentException($"Function '{GeneratedFunctionName.CreateCameras}' cannot be generated without {nameof(MapCameras)}.", nameof(map)); } - var statements = new List(); - statements.Add(JassEmptySyntax.Value); - - var zero = SyntaxFactory.LiteralExpression(0f); + writer.WriteFunction(GeneratedFunctionName.CreateCameras); + writer.WriteLine(); foreach (var camera in mapCameras.Cameras) { var cameraName = camera.GetVariableName(); - statements.Add(SyntaxFactory.SetStatement(cameraName, SyntaxFactory.InvocationExpression(NativeName.CreateCameraSetup))); - statements.Add(SyntaxFactory.CallStatement(NativeName.CameraSetupSetField, SyntaxFactory.VariableReferenceExpression(cameraName), SyntaxFactory.VariableReferenceExpression(CameraFieldName.ZOffset), SyntaxFactory.LiteralExpression(camera.ZOffset), zero)); - statements.Add(SyntaxFactory.CallStatement(NativeName.CameraSetupSetField, SyntaxFactory.VariableReferenceExpression(cameraName), SyntaxFactory.VariableReferenceExpression(CameraFieldName.Rotation), SyntaxFactory.LiteralExpression(camera.Rotation), zero)); - statements.Add(SyntaxFactory.CallStatement(NativeName.CameraSetupSetField, SyntaxFactory.VariableReferenceExpression(cameraName), SyntaxFactory.VariableReferenceExpression(CameraFieldName.AngleOfAttack), SyntaxFactory.LiteralExpression(camera.AngleOfAttack), zero)); - statements.Add(SyntaxFactory.CallStatement(NativeName.CameraSetupSetField, SyntaxFactory.VariableReferenceExpression(cameraName), SyntaxFactory.VariableReferenceExpression(CameraFieldName.TargetDistance), SyntaxFactory.LiteralExpression(camera.TargetDistance), zero)); - statements.Add(SyntaxFactory.CallStatement(NativeName.CameraSetupSetField, SyntaxFactory.VariableReferenceExpression(cameraName), SyntaxFactory.VariableReferenceExpression(CameraFieldName.Roll), SyntaxFactory.LiteralExpression(camera.Roll), zero)); - statements.Add(SyntaxFactory.CallStatement(NativeName.CameraSetupSetField, SyntaxFactory.VariableReferenceExpression(cameraName), SyntaxFactory.VariableReferenceExpression(CameraFieldName.FieldOfView), SyntaxFactory.LiteralExpression(camera.FieldOfView), zero)); - statements.Add(SyntaxFactory.CallStatement(NativeName.CameraSetupSetField, SyntaxFactory.VariableReferenceExpression(cameraName), SyntaxFactory.VariableReferenceExpression(CameraFieldName.FarZ), SyntaxFactory.LiteralExpression(camera.FarClippingPlane), zero)); + writer.WriteSet(cameraName, JassExpression.InvokeSpaced(NativeName.CreateCameraSetup)); + writer.WriteCall(NativeName.CameraSetupSetField, cameraName, CameraFieldName.ZOffset, JassLiteral.Real(camera.ZOffset), "0.0"); + writer.WriteCall(NativeName.CameraSetupSetField, cameraName, CameraFieldName.Rotation, JassLiteral.Real(camera.Rotation), "0.0"); + writer.WriteCall(NativeName.CameraSetupSetField, cameraName, CameraFieldName.AngleOfAttack, JassLiteral.Real(camera.AngleOfAttack), "0.0"); + writer.WriteCall(NativeName.CameraSetupSetField, cameraName, CameraFieldName.TargetDistance, JassLiteral.Real(camera.TargetDistance), "0.0"); + writer.WriteCall(NativeName.CameraSetupSetField, cameraName, CameraFieldName.Roll, JassLiteral.Real(camera.Roll), "0.0"); + writer.WriteCall(NativeName.CameraSetupSetField, cameraName, CameraFieldName.FieldOfView, JassLiteral.Real(camera.FieldOfView), "0.0"); + writer.WriteCall(NativeName.CameraSetupSetField, cameraName, CameraFieldName.FarZ, JassLiteral.Real(camera.FarClippingPlane), "0.0"); if (mapCameras.UseNewFormat) { - statements.Add(SyntaxFactory.CallStatement(NativeName.CameraSetupSetField, SyntaxFactory.VariableReferenceExpression(cameraName), SyntaxFactory.VariableReferenceExpression(CameraFieldName.NearZ), SyntaxFactory.LiteralExpression(camera.NearClippingPlane), zero)); - statements.Add(SyntaxFactory.CallStatement(NativeName.CameraSetupSetField, SyntaxFactory.VariableReferenceExpression(cameraName), SyntaxFactory.VariableReferenceExpression(CameraFieldName.LocalPitch), SyntaxFactory.LiteralExpression(camera.LocalPitch), zero)); - statements.Add(SyntaxFactory.CallStatement(NativeName.CameraSetupSetField, SyntaxFactory.VariableReferenceExpression(cameraName), SyntaxFactory.VariableReferenceExpression(CameraFieldName.LocalYaw), SyntaxFactory.LiteralExpression(camera.LocalYaw), zero)); - statements.Add(SyntaxFactory.CallStatement(NativeName.CameraSetupSetField, SyntaxFactory.VariableReferenceExpression(cameraName), SyntaxFactory.VariableReferenceExpression(CameraFieldName.LocalRoll), SyntaxFactory.LiteralExpression(camera.LocalRoll), zero)); + writer.WriteCall(NativeName.CameraSetupSetField, cameraName, CameraFieldName.NearZ, JassLiteral.Real(camera.NearClippingPlane), "0.0"); + writer.WriteCall(NativeName.CameraSetupSetField, cameraName, CameraFieldName.LocalPitch, JassLiteral.Real(camera.LocalPitch), "0.0"); + writer.WriteCall(NativeName.CameraSetupSetField, cameraName, CameraFieldName.LocalYaw, JassLiteral.Real(camera.LocalYaw), "0.0"); + writer.WriteCall(NativeName.CameraSetupSetField, cameraName, CameraFieldName.LocalRoll, JassLiteral.Real(camera.LocalRoll), "0.0"); } - statements.Add(SyntaxFactory.CallStatement(NativeName.CameraSetupSetDestPosition, SyntaxFactory.VariableReferenceExpression(cameraName), SyntaxFactory.LiteralExpression(camera.TargetPosition.X), SyntaxFactory.LiteralExpression(camera.TargetPosition.Y), zero)); - statements.Add(JassEmptySyntax.Value); + writer.WriteCall(NativeName.CameraSetupSetDestPosition, cameraName, JassLiteral.Real(camera.TargetPosition.X), JassLiteral.Real(camera.TargetPosition.Y), "0.0"); + writer.WriteLine(); } - return SyntaxFactory.FunctionDeclaration(SyntaxFactory.FunctionDeclarator(nameof(CreateCameras)), statements); + writer.EndFunction(); } - protected internal virtual bool CreateCamerasCondition(Map map) + protected internal virtual bool ShouldGenerateCreateCameras(Map map) { if (map is null) { @@ -78,7 +79,7 @@ protected internal virtual bool CreateCamerasCondition(Map map) } return map.Cameras is not null - && map.Cameras.Cameras.Any(); + && map.Cameras.Cameras.Count > 0; } } } \ No newline at end of file diff --git a/src/War3Net.Build/MapScriptBuilder/Environment/CreateRegions.cs b/src/War3Net.Build/MapScriptBuilder/Environment/CreateRegions.cs index 554640f4..7d524895 100644 --- a/src/War3Net.Build/MapScriptBuilder/Environment/CreateRegions.cs +++ b/src/War3Net.Build/MapScriptBuilder/Environment/CreateRegions.cs @@ -6,105 +6,108 @@ // ------------------------------------------------------------------------------ using System; -using System.Collections.Generic; -using System.Linq; using War3Net.Build.Common; using War3Net.Build.Environment; using War3Net.Build.Extensions; using War3Net.Build.Info; -using War3Net.CodeAnalysis.Jass.Syntax; - -using SyntaxFactory = War3Net.CodeAnalysis.Jass.JassSyntaxFactory; +using War3Net.CodeAnalysis; +using War3Net.CodeAnalysis.Jass; +using War3Net.CodeAnalysis.Jass.Extensions; namespace War3Net.Build { public partial class MapScriptBuilder { - protected internal virtual JassFunctionDeclarationSyntax CreateRegions(Map map) + protected internal virtual void GenerateCreateRegions(Map map, IndentedTextWriter writer) { if (map is null) { throw new ArgumentNullException(nameof(map)); } + if (writer is null) + { + throw new ArgumentNullException(nameof(writer)); + } + var mapRegions = map.Regions; if (mapRegions is null) { - throw new ArgumentException($"Function '{nameof(CreateRegions)}' cannot be generated without {nameof(MapRegions)}.", nameof(map)); + throw new ArgumentException($"Function '{GeneratedFunctionName.CreateRegions}' cannot be generated without {nameof(MapRegions)}.", nameof(map)); } - var statements = new List(); + writer.WriteFunction(GeneratedFunctionName.CreateRegions); if (UseWeatherEffectVariable) { - statements.Add(SyntaxFactory.LocalVariableDeclarationStatement(SyntaxFactory.ParseTypeName(TypeName.WeatherEffect), VariableName.WeatherEffect)); - statements.Add(JassEmptySyntax.Value); + writer.WriteLocal(TypeName.WeatherEffect, VariableName.WeatherEffect); + writer.WriteLine(); } foreach (var region in mapRegions.Regions) { var regionName = region.GetVariableName(); - statements.Add(SyntaxFactory.SetStatement( + writer.WriteSet( regionName, - SyntaxFactory.InvocationExpression( + JassExpression.InvokeSpaced( NativeName.Rect, - SyntaxFactory.LiteralExpression(region.Left, precision: 1), - SyntaxFactory.LiteralExpression(region.Bottom, precision: 1), - SyntaxFactory.LiteralExpression(region.Right, precision: 1), - SyntaxFactory.LiteralExpression(region.Top, precision: 1)))); + JassLiteral.Real(region.Left), + JassLiteral.Real(region.Bottom), + JassLiteral.Real(region.Right), + JassLiteral.Real(region.Top))); if (region.WeatherType != WeatherType.None) { if (UseWeatherEffectVariable) { - statements.Add(SyntaxFactory.SetStatement( + writer.WriteSet( VariableName.WeatherEffect, - SyntaxFactory.InvocationExpression( + JassExpression.InvokeSpaced( NativeName.AddWeatherEffect, - SyntaxFactory.VariableReferenceExpression(regionName), - SyntaxFactory.FourCCLiteralExpression((int)region.WeatherType)))); + regionName, + JassLiteral.FourCC((int)region.WeatherType))); - statements.Add(SyntaxFactory.CallStatement( + writer.WriteCall( NativeName.EnableWeatherEffect, - SyntaxFactory.VariableReferenceExpression(VariableName.WeatherEffect), - JassBooleanLiteralExpressionSyntax.True)); + VariableName.WeatherEffect, + JassKeyword.True); } else { - statements.Add(SyntaxFactory.CallStatement( + writer.WriteCall( NativeName.EnableWeatherEffect, - SyntaxFactory.InvocationExpression( + JassExpression.Invoke( NativeName.AddWeatherEffect, - SyntaxFactory.VariableReferenceExpression(regionName), - SyntaxFactory.FourCCLiteralExpression((int)region.WeatherType)), - JassBooleanLiteralExpressionSyntax.True)); + regionName, + JassLiteral.FourCC((int)region.WeatherType)), + JassKeyword.True); } } if (!string.IsNullOrEmpty(region.AmbientSound)) { - statements.Add(SyntaxFactory.CallStatement( + writer.WriteCall( NativeName.SetSoundPosition, - SyntaxFactory.VariableReferenceExpression(region.AmbientSound), - SyntaxFactory.LiteralExpression(region.CenterX), - SyntaxFactory.LiteralExpression(region.CenterY), - SyntaxFactory.LiteralExpression(0f))); + region.AmbientSound, + JassLiteral.Real(region.CenterX), + JassLiteral.Real(region.CenterY), + "0.0"); - statements.Add(SyntaxFactory.CallStatement( + writer.WriteCall( NativeName.RegisterStackedSound, - SyntaxFactory.VariableReferenceExpression(region.AmbientSound), - SyntaxFactory.LiteralExpression(true), - SyntaxFactory.LiteralExpression(region.Width), - SyntaxFactory.LiteralExpression(region.Height))); + region.AmbientSound, + JassKeyword.True, + JassLiteral.Real(region.Width), + JassLiteral.Real(region.Height)); } } - return SyntaxFactory.FunctionDeclaration(SyntaxFactory.FunctionDeclarator(nameof(CreateRegions)), statements); + writer.EndFunction(); } - protected internal virtual bool CreateRegionsCondition(Map map) + protected internal virtual bool ShouldGenerateCreateRegions(Map map) { if (map is null) { @@ -117,7 +120,7 @@ protected internal virtual bool CreateRegionsCondition(Map map) } return map.Regions is not null - && map.Regions.Regions.Any(); + && map.Regions.Regions.Count > 0; } } } \ No newline at end of file diff --git a/src/War3Net.Build/MapScriptBuilder/Environment/Regions.cs b/src/War3Net.Build/MapScriptBuilder/Environment/Regions.cs index 5be30ea1..3b5df4f1 100644 --- a/src/War3Net.Build/MapScriptBuilder/Environment/Regions.cs +++ b/src/War3Net.Build/MapScriptBuilder/Environment/Regions.cs @@ -6,51 +6,52 @@ // ------------------------------------------------------------------------------ using System; -using System.Collections.Generic; -using System.Linq; - -using Microsoft.CodeAnalysis.CSharp.Syntax; using War3Net.Build.Extensions; -using War3Net.CodeAnalysis.Jass.Syntax; -using War3Net.CodeAnalysis.Transpilers; - -using SyntaxFactory = War3Net.CodeAnalysis.Jass.JassSyntaxFactory; +using War3Net.CodeAnalysis; +using War3Net.CodeAnalysis.Jass; +using War3Net.CodeAnalysis.Jass.Extensions; namespace War3Net.Build { public partial class MapScriptBuilder { - public virtual IEnumerable RegionsApi(Map map, JassToCSharpTranspiler transpiler) + protected internal virtual void GenerateRegionVariables(Map map, IndentedTextWriter writer) { - if (transpiler is null) + if (map is null) { - throw new ArgumentNullException(nameof(transpiler)); + throw new ArgumentNullException(nameof(map)); } - return Regions(map).Select(region => transpiler.Transpile(region)); - } - - protected internal virtual IEnumerable Regions(Map map) - { - if (map is null) + if (writer is null) { - throw new ArgumentNullException(nameof(map)); + throw new ArgumentNullException(nameof(writer)); } var mapRegions = map.Regions; if (mapRegions is null) { - yield break; + return; } foreach (var region in mapRegions.Regions) { - yield return SyntaxFactory.GlobalDeclaration( - SyntaxFactory.ParseTypeName(TypeName.Rect), + writer.WriteAlignedGlobal( + TypeName.Rect, region.GetVariableName(), - JassNullLiteralExpressionSyntax.Value); + JassKeyword.Null); } } + + protected internal virtual bool ShouldGenerateRegionVariables(Map map) + { + if (map is null) + { + throw new ArgumentNullException(nameof(map)); + } + + return map.Regions is not null + && map.Regions.Regions.Count > 0; + } } } \ No newline at end of file diff --git a/src/War3Net.Build/MapScriptBuilder/Info/InitAllyPriorities.cs b/src/War3Net.Build/MapScriptBuilder/Info/InitAllyPriorities.cs index 38ed4998..817ca312 100644 --- a/src/War3Net.Build/MapScriptBuilder/Info/InitAllyPriorities.cs +++ b/src/War3Net.Build/MapScriptBuilder/Info/InitAllyPriorities.cs @@ -1,4 +1,4 @@ -// ------------------------------------------------------------------------------ +// ------------------------------------------------------------------------------ // // Licensed under the MIT license. // See the LICENSE file in the project root for more information. @@ -6,55 +6,73 @@ // ------------------------------------------------------------------------------ using System; -using System.Collections.Generic; using System.Linq; using War3Net.Build.Info; -using War3Net.CodeAnalysis.Jass.Syntax; - -using SyntaxFactory = War3Net.CodeAnalysis.Jass.JassSyntaxFactory; +using War3Net.CodeAnalysis; +using War3Net.CodeAnalysis.Jass; +using War3Net.CodeAnalysis.Jass.Extensions; namespace War3Net.Build { public partial class MapScriptBuilder { - protected internal virtual JassFunctionDeclarationSyntax InitAllyPriorities(Map map) + protected internal virtual void GenerateInitAllyPriorities(Map map, IndentedTextWriter writer) { if (map is null) { throw new ArgumentNullException(nameof(map)); } + if (writer is null) + { + throw new ArgumentNullException(nameof(writer)); + } + var mapInfo = map.Info; if (mapInfo is null) { - throw new ArgumentException($"Function '{nameof(InitAllyPriorities)}' cannot be generated without {nameof(MapInfo)}.", nameof(map)); + throw new ArgumentException($"Function '{GeneratedFunctionName.InitAllyPriorities}' cannot be generated without {nameof(MapInfo)}.", nameof(map)); } - var statements = new List(); + writer.WriteFunction(GeneratedFunctionName.InitAllyPriorities); var playerDataCount = mapInfo.Players.Count; for (var i = 0; i < playerDataCount; i++) { var playerData = mapInfo.Players[i]; - var allyStartLocPrioStatements = new List(); - var enemyStartLocPrioStatements = new List(); - var allySlotIndex = 0; var enemySlotIndex = 0; + + writer.WriteLine(); + + var allyCountIndex = 0; + for (var j = 0; j < MaxPlayerSlots; j++) + { + if (playerData.AllyLowPriorityFlags[j] || playerData.AllyHighPriorityFlags[j]) + { + allyCountIndex++; + } + } + + writer.WriteCall( + NativeName.SetStartLocPrioCount, + JassLiteral.Int(i), + JassLiteral.Int(allyCountIndex)); + for (var j = 0; j < MaxPlayerSlots; j++) { var hasLowFlag = playerData.AllyLowPriorityFlags[j]; var hasHighFlag = playerData.AllyHighPriorityFlags[j]; if (hasLowFlag || hasHighFlag) { - allyStartLocPrioStatements.Add(SyntaxFactory.CallStatement( + writer.WriteCall( NativeName.SetStartLocPrio, - SyntaxFactory.LiteralExpression(i), - SyntaxFactory.LiteralExpression(allySlotIndex++), - SyntaxFactory.LiteralExpression(j), - SyntaxFactory.VariableReferenceExpression(hasHighFlag ? StartLocPrioName.High : StartLocPrioName.Low))); + JassLiteral.Int(i), + JassLiteral.Int(allySlotIndex++), + JassLiteral.Int(j), + hasHighFlag ? StartLocPrioName.High : StartLocPrioName.Low); } if (mapInfo.FormatVersion >= MapInfoFormatVersion.v31) @@ -63,42 +81,40 @@ protected internal virtual JassFunctionDeclarationSyntax InitAllyPriorities(Map hasHighFlag = playerData.EnemyHighPriorityFlags[j]; if (hasLowFlag || hasHighFlag) { - enemyStartLocPrioStatements.Add(SyntaxFactory.CallStatement( + if (enemySlotIndex == 0) + { + writer.WriteLine(); + + var enemyCountIndex = 0; + for (var k = 0; k < MaxPlayerSlots; k++) + { + if (playerData.EnemyLowPriorityFlags[k] || playerData.EnemyHighPriorityFlags[k]) + { + enemyCountIndex++; + } + } + + writer.WriteCall( + NativeName.SetEnemyStartLocPrioCount, + JassLiteral.Int(i), + JassLiteral.Int(enemyCountIndex)); + } + + writer.WriteCall( NativeName.SetEnemyStartLocPrio, - SyntaxFactory.LiteralExpression(i), - SyntaxFactory.LiteralExpression(enemySlotIndex++), - SyntaxFactory.LiteralExpression(j), - SyntaxFactory.VariableReferenceExpression(hasHighFlag ? StartLocPrioName.High : StartLocPrioName.Low))); + JassLiteral.Int(i), + JassLiteral.Int(enemySlotIndex++), + JassLiteral.Int(j), + hasHighFlag ? StartLocPrioName.High : StartLocPrioName.Low); } } } - - statements.Add(JassEmptySyntax.Value); - - statements.Add(SyntaxFactory.CallStatement( - NativeName.SetStartLocPrioCount, - SyntaxFactory.LiteralExpression(i), - SyntaxFactory.LiteralExpression(allySlotIndex))); - - statements.AddRange(allyStartLocPrioStatements); - - if (enemyStartLocPrioStatements.Count > 0) - { - statements.Add(JassEmptySyntax.Value); - - statements.Add(SyntaxFactory.CallStatement( - NativeName.SetEnemyStartLocPrioCount, - SyntaxFactory.LiteralExpression(i), - SyntaxFactory.LiteralExpression(enemySlotIndex))); - - statements.AddRange(enemyStartLocPrioStatements); - } } - return SyntaxFactory.FunctionDeclaration(SyntaxFactory.FunctionDeclarator(nameof(InitAllyPriorities)), statements); + writer.EndFunction(); } - protected internal virtual bool InitAllyPrioritiesCondition(Map map) + protected internal virtual bool ShouldGenerateInitAllyPriorities(Map map) { if (map is null) { diff --git a/src/War3Net.Build/MapScriptBuilder/Info/InitCustomPlayerSlots.cs b/src/War3Net.Build/MapScriptBuilder/Info/InitCustomPlayerSlots.cs index 986e939e..9ce95686 100644 --- a/src/War3Net.Build/MapScriptBuilder/Info/InitCustomPlayerSlots.cs +++ b/src/War3Net.Build/MapScriptBuilder/Info/InitCustomPlayerSlots.cs @@ -1,4 +1,4 @@ -// ------------------------------------------------------------------------------ +// ------------------------------------------------------------------------------ // // Licensed under the MIT license. // See the LICENSE file in the project root for more information. @@ -6,32 +6,36 @@ // ------------------------------------------------------------------------------ using System; -using System.Collections.Generic; using War3Net.Build.Extensions; using War3Net.Build.Info; -using War3Net.CodeAnalysis.Jass.Syntax; - -using SyntaxFactory = War3Net.CodeAnalysis.Jass.JassSyntaxFactory; +using War3Net.CodeAnalysis; +using War3Net.CodeAnalysis.Jass; +using War3Net.CodeAnalysis.Jass.Extensions; namespace War3Net.Build { public partial class MapScriptBuilder { - protected internal virtual JassFunctionDeclarationSyntax InitCustomPlayerSlots(Map map) + protected internal virtual void GenerateInitCustomPlayerSlots(Map map, IndentedTextWriter writer) { if (map is null) { throw new ArgumentNullException(nameof(map)); } + if (writer is null) + { + throw new ArgumentNullException(nameof(writer)); + } + var mapInfo = map.Info; if (mapInfo is null) { - throw new ArgumentException($"Function '{nameof(InitCustomPlayerSlots)}' cannot be generated without {nameof(MapInfo)}.", nameof(map)); + throw new ArgumentException($"Function '{GeneratedFunctionName.InitCustomPlayerSlots}' cannot be generated without {nameof(MapInfo)}.", nameof(map)); } - var statements = new List(); + writer.WriteFunction(GeneratedFunctionName.InitCustomPlayerSlots); var playerDataCount = mapInfo.Players.Count; @@ -39,41 +43,44 @@ protected internal virtual JassFunctionDeclarationSyntax InitCustomPlayerSlots(M { var playerData = mapInfo.Players[i]; - statements.Add(JassEmptySyntax.Value); - statements.Add(new JassCommentSyntax($" Player {playerData.Id}")); + writer.WriteLine(); + writer.WriteComment($"Player {playerData.Id}"); + + var playerExpr = JassExpression.Invoke(NativeName.Player, JassLiteral.Int(playerData.Id)); + var playerColor = JassExpression.Invoke(NativeName.ConvertPlayerColor, JassLiteral.Int(playerData.Id)); - statements.Add(SyntaxFactory.CallStatement( + writer.WriteCall( NativeName.SetPlayerStartLocation, - SyntaxFactory.InvocationExpression(NativeName.Player, SyntaxFactory.LiteralExpression(playerData.Id)), - SyntaxFactory.LiteralExpression(i))); + playerExpr, + JassLiteral.Int(i)); if (playerData.Flags.HasFlag(PlayerFlags.FixedStartPosition)) { - statements.Add(SyntaxFactory.CallStatement( + writer.WriteCall( NativeName.ForcePlayerStartLocation, - SyntaxFactory.InvocationExpression(NativeName.Player, SyntaxFactory.LiteralExpression(playerData.Id)), - SyntaxFactory.LiteralExpression(i))); + playerExpr, + JassLiteral.Int(i)); } - statements.Add(SyntaxFactory.CallStatement( + writer.WriteCall( NativeName.SetPlayerColor, - SyntaxFactory.InvocationExpression(NativeName.Player, SyntaxFactory.LiteralExpression(playerData.Id)), - SyntaxFactory.InvocationExpression(NativeName.ConvertPlayerColor, SyntaxFactory.LiteralExpression(playerData.Id)))); + playerExpr, + playerColor); - statements.Add(SyntaxFactory.CallStatement( + writer.WriteCall( NativeName.SetPlayerRacePreference, - SyntaxFactory.InvocationExpression(NativeName.Player, SyntaxFactory.LiteralExpression(playerData.Id)), - SyntaxFactory.VariableReferenceExpression(playerData.Race.GetVariableName()))); + playerExpr, + playerData.Race.GetVariableName()); - statements.Add(SyntaxFactory.CallStatement( + writer.WriteCall( NativeName.SetPlayerRaceSelectable, - SyntaxFactory.InvocationExpression(NativeName.Player, SyntaxFactory.LiteralExpression(playerData.Id)), - SyntaxFactory.LiteralExpression(playerData.Flags.HasFlag(PlayerFlags.RaceSelectable) || !mapInfo.MapFlags.HasFlag(MapFlags.FixedPlayerSettingsForCustomForces)))); + playerExpr, + JassLiteral.Bool(playerData.Flags.HasFlag(PlayerFlags.RaceSelectable) || !mapInfo.MapFlags.HasFlag(MapFlags.FixedPlayerSettingsForCustomForces))); - statements.Add(SyntaxFactory.CallStatement( + writer.WriteCall( NativeName.SetPlayerController, - SyntaxFactory.InvocationExpression(NativeName.Player, SyntaxFactory.LiteralExpression(playerData.Id)), - SyntaxFactory.VariableReferenceExpression(playerData.Controller.GetVariableName()))); + playerExpr, + playerData.Controller.GetVariableName()); if (playerData.Controller == PlayerController.Rescuable) { @@ -82,23 +89,23 @@ protected internal virtual JassFunctionDeclarationSyntax InitCustomPlayerSlots(M var otherPlayerData = mapInfo.Players[j]; if (otherPlayerData.Controller == PlayerController.User) { - statements.Add(SyntaxFactory.CallStatement( + writer.WriteCall( NativeName.SetPlayerAlliance, - SyntaxFactory.InvocationExpression(NativeName.Player, SyntaxFactory.LiteralExpression(playerData.Id)), - SyntaxFactory.InvocationExpression(NativeName.Player, SyntaxFactory.LiteralExpression(otherPlayerData.Id)), - SyntaxFactory.VariableReferenceExpression(AllianceTypeName.Rescuable), - SyntaxFactory.LiteralExpression(true))); + playerExpr, + JassExpression.Invoke(NativeName.Player, JassLiteral.Int(otherPlayerData.Id)), + AllianceTypeName.Rescuable, + JassKeyword.True); } } } } - statements.Add(JassEmptySyntax.Value); + writer.WriteLine(); - return SyntaxFactory.FunctionDeclaration(SyntaxFactory.FunctionDeclarator(nameof(InitCustomPlayerSlots)), statements); + writer.EndFunction(); } - protected internal virtual bool InitCustomPlayerSlotsCondition(Map map) + protected internal virtual bool ShouldGenerateInitCustomPlayerSlots(Map map) { if (map is null) { diff --git a/src/War3Net.Build/MapScriptBuilder/Info/InitCustomTeams.cs b/src/War3Net.Build/MapScriptBuilder/Info/InitCustomTeams.cs index ce91d1a5..9eda1eb1 100644 --- a/src/War3Net.Build/MapScriptBuilder/Info/InitCustomTeams.cs +++ b/src/War3Net.Build/MapScriptBuilder/Info/InitCustomTeams.cs @@ -1,4 +1,4 @@ -// ------------------------------------------------------------------------------ +// ------------------------------------------------------------------------------ // // Licensed under the MIT license. // See the LICENSE file in the project root for more information. @@ -6,32 +6,36 @@ // ------------------------------------------------------------------------------ using System; -using System.Collections.Generic; using System.Linq; using War3Net.Build.Info; -using War3Net.CodeAnalysis.Jass.Syntax; - -using SyntaxFactory = War3Net.CodeAnalysis.Jass.JassSyntaxFactory; +using War3Net.CodeAnalysis; +using War3Net.CodeAnalysis.Jass; +using War3Net.CodeAnalysis.Jass.Extensions; namespace War3Net.Build { public partial class MapScriptBuilder { - protected internal virtual JassFunctionDeclarationSyntax InitCustomTeams(Map map) + protected internal virtual void GenerateInitCustomTeams(Map map, IndentedTextWriter writer) { if (map is null) { throw new ArgumentNullException(nameof(map)); } + if (writer is null) + { + throw new ArgumentNullException(nameof(writer)); + } + var mapInfo = map.Info; if (mapInfo is null) { - throw new ArgumentException($"Function '{nameof(InitCustomTeams)}' cannot be generated without {nameof(MapInfo)}.", nameof(map)); + throw new ArgumentException($"Function '{GeneratedFunctionName.InitCustomTeams}' cannot be generated without {nameof(MapInfo)}.", nameof(map)); } - var statements = new List(); + writer.WriteFunction(GeneratedFunctionName.InitCustomTeams); var forceDataCount = mapInfo.Forces.Count; var useBlizzardAllianceFunctions = mapInfo.FormatVersion > MapInfoFormatVersion.v15; @@ -45,28 +49,30 @@ protected internal virtual JassFunctionDeclarationSyntax InitCustomTeams(Map map .Select(player => player.Id) .ToList(); - if (!playerSlots.Any()) + if (playerSlots.Count == 0) { continue; } - statements.Add(new JassCommentSyntax($" Force: {forceData.Name}")); + writer.WriteComment($"Force: {forceData.Name}"); var alliedVictory = forceData.Flags.HasFlag(ForceFlags.AlliedVictory); foreach (var playerSlot in playerSlots) { - statements.Add(SyntaxFactory.CallStatement( + var playerExpr = JassExpression.Invoke(NativeName.Player, JassLiteral.Int(playerSlot)); + + writer.WriteCall( NativeName.SetPlayerTeam, - SyntaxFactory.InvocationExpression(NativeName.Player, SyntaxFactory.LiteralExpression(playerSlot)), - SyntaxFactory.LiteralExpression(i))); + playerExpr, + JassLiteral.Int(i)); if (alliedVictory) { - statements.Add(SyntaxFactory.CallStatement( + writer.WriteCall( NativeName.SetPlayerState, - SyntaxFactory.InvocationExpression(NativeName.Player, SyntaxFactory.LiteralExpression(playerSlot)), - SyntaxFactory.VariableReferenceExpression(PlayerStateName.AlliedVictory), - SyntaxFactory.LiteralExpression(1))); + playerExpr, + PlayerStateName.AlliedVictory, + "1"); } } @@ -74,15 +80,15 @@ protected internal virtual JassFunctionDeclarationSyntax InitCustomTeams(Map map if (useBlizzardAllianceFunctions) { - void AddSetAllianceStateStatement(string statementName) + void WriteSetAllianceStateStatement(string statementName) { foreach (var (playerSlot1, playerSlot2) in playerSlotPairs) { - statements.Add(SyntaxFactory.CallStatement( + writer.WriteCall( statementName, - SyntaxFactory.InvocationExpression(NativeName.Player, SyntaxFactory.LiteralExpression(playerSlot1)), - SyntaxFactory.InvocationExpression(NativeName.Player, SyntaxFactory.LiteralExpression(playerSlot2)), - SyntaxFactory.LiteralExpression(true))); + JassExpression.Invoke(NativeName.Player, JassLiteral.Int(playerSlot1)), + JassExpression.Invoke(NativeName.Player, JassLiteral.Int(playerSlot2)), + JassKeyword.True); } } @@ -90,74 +96,74 @@ void AddSetAllianceStateStatement(string statementName) { if (mapInfo.FormatVersion >= MapInfoFormatVersion.v31) { - statements.Add(JassEmptySyntax.Value); - statements.Add(new JassCommentSyntax(" Allied")); + writer.WriteLine(); + writer.WriteComment(" Allied"); } - AddSetAllianceStateStatement(FunctionName.SetPlayerAllianceStateAllyBJ); + WriteSetAllianceStateStatement(FunctionName.SetPlayerAllianceStateAllyBJ); } if (forceData.Flags.HasFlag(ForceFlags.ShareVision)) { - AddSetAllianceStateStatement(FunctionName.SetPlayerAllianceStateVisionBJ); + WriteSetAllianceStateStatement(FunctionName.SetPlayerAllianceStateVisionBJ); } if (forceData.Flags.HasFlag(ForceFlags.ShareUnitControl)) { - AddSetAllianceStateStatement(FunctionName.SetPlayerAllianceStateControlBJ); + WriteSetAllianceStateStatement(FunctionName.SetPlayerAllianceStateControlBJ); } if (forceData.Flags.HasFlag(ForceFlags.ShareAdvancedUnitControl)) { - AddSetAllianceStateStatement(FunctionName.SetPlayerAllianceStateFullControlBJ); + WriteSetAllianceStateStatement(FunctionName.SetPlayerAllianceStateFullControlBJ); } } else { - void AddSetAllianceStateStatement(string variableName, string comment) + void WriteSetAllianceStateStatement(string variableName, string comment) { - statements.Add(JassEmptySyntax.Value); - statements.Add(new JassCommentSyntax(comment)); + writer.WriteLine(); + writer.WriteComment(comment); foreach (var (playerSlot1, playerSlot2) in playerSlotPairs) { - statements.Add(SyntaxFactory.CallStatement( + writer.WriteCall( NativeName.SetPlayerAlliance, - SyntaxFactory.InvocationExpression(NativeName.Player, SyntaxFactory.LiteralExpression(playerSlot1)), - SyntaxFactory.InvocationExpression(NativeName.Player, SyntaxFactory.LiteralExpression(playerSlot2)), - SyntaxFactory.VariableReferenceExpression(variableName), - SyntaxFactory.LiteralExpression(true))); + JassExpression.Invoke(NativeName.Player, JassLiteral.Int(playerSlot1)), + JassExpression.Invoke(NativeName.Player, JassLiteral.Int(playerSlot2)), + variableName, + JassKeyword.True); } } if (forceData.Flags.HasFlag(ForceFlags.Allied)) { - AddSetAllianceStateStatement(AllianceTypeName.Passive, " Allied"); + WriteSetAllianceStateStatement(AllianceTypeName.Passive, " Allied"); } if (forceData.Flags.HasFlag(ForceFlags.ShareVision)) { - AddSetAllianceStateStatement(AllianceTypeName.SharedVision, " Shared Vision"); + WriteSetAllianceStateStatement(AllianceTypeName.SharedVision, " Shared Vision"); } if (forceData.Flags.HasFlag(ForceFlags.ShareUnitControl)) { - AddSetAllianceStateStatement(AllianceTypeName.SharedControl, " Shared Control"); + WriteSetAllianceStateStatement(AllianceTypeName.SharedControl, " Shared Control"); } if (forceData.Flags.HasFlag(ForceFlags.ShareAdvancedUnitControl)) { - AddSetAllianceStateStatement(AllianceTypeName.SharedAdvancedControl, " Advanced Control"); + WriteSetAllianceStateStatement(AllianceTypeName.SharedAdvancedControl, " Advanced Control"); } } - statements.Add(JassEmptySyntax.Value); + writer.WriteLine(); } - return SyntaxFactory.FunctionDeclaration(SyntaxFactory.FunctionDeclarator(nameof(InitCustomTeams)), statements); + writer.EndFunction(); } - protected internal virtual bool InitCustomTeamsCondition(Map map) + protected internal virtual bool ShouldGenerateInitCustomTeams(Map map) { if (map is null) { @@ -167,7 +173,7 @@ protected internal virtual bool InitCustomTeamsCondition(Map map) return map.Info is not null; } - protected internal virtual bool InitCustomTeamsInvokeCondition(Map map) + protected internal virtual bool ShouldCallInitCustomTeams(Map map) { if (map is null) { diff --git a/src/War3Net.Build/MapScriptBuilder/Info/InitRandomGroups.cs b/src/War3Net.Build/MapScriptBuilder/Info/InitRandomGroups.cs index 0e321765..dc10e166 100644 --- a/src/War3Net.Build/MapScriptBuilder/Info/InitRandomGroups.cs +++ b/src/War3Net.Build/MapScriptBuilder/Info/InitRandomGroups.cs @@ -1,4 +1,4 @@ -// ------------------------------------------------------------------------------ +// ------------------------------------------------------------------------------ // // Licensed under the MIT license. // See the LICENSE file in the project root for more information. @@ -6,101 +6,105 @@ // ------------------------------------------------------------------------------ using System; -using System.Collections.Generic; -using System.Linq; using War3Net.Build.Extensions; using War3Net.Build.Info; using War3Net.Build.Providers; -using War3Net.CodeAnalysis.Jass.Syntax; - -using SyntaxFactory = War3Net.CodeAnalysis.Jass.JassSyntaxFactory; +using War3Net.CodeAnalysis; +using War3Net.CodeAnalysis.Jass; +using War3Net.CodeAnalysis.Jass.Extensions; namespace War3Net.Build { public partial class MapScriptBuilder { - protected internal virtual JassFunctionDeclarationSyntax InitRandomGroups(Map map) + protected internal virtual void GenerateInitRandomGroups(Map map, IndentedTextWriter writer) { if (map is null) { throw new ArgumentNullException(nameof(map)); } + if (writer is null) + { + throw new ArgumentNullException(nameof(writer)); + } + var randomUnitTables = map.Info?.RandomUnitTables; if (randomUnitTables is null) { - throw new ArgumentException($"Function '{nameof(InitRandomGroups)}' cannot be generated without {nameof(MapInfo.RandomUnitTables)}.", nameof(map)); + throw new ArgumentException($"Function '{GeneratedFunctionName.InitRandomGroups}' cannot be generated without {nameof(MapInfo.RandomUnitTables)}.", nameof(map)); } - var statements = new List(); + writer.WriteFunction(GeneratedFunctionName.InitRandomGroups); - statements.Add(SyntaxFactory.LocalVariableDeclarationStatement(JassTypeSyntax.Integer, VariableName.CurrentSet)); - statements.Add(JassEmptySyntax.Value); + writer.WriteLocal(JassKeyword.Integer, VariableName.CurrentSet); + writer.WriteLine(); foreach (var unitTable in randomUnitTables) { - statements.Add(new JassCommentSyntax($" Group {unitTable.Index} - {unitTable.Name}")); - statements.Add(SyntaxFactory.CallStatement(FunctionName.RandomDistReset)); + writer.WriteComment($"Group {unitTable.Index} - {unitTable.Name}"); + writer.WriteCall(FunctionName.RandomDistReset); for (var i = 0; i < unitTable.UnitSets.Count; i++) { - statements.Add(SyntaxFactory.CallStatement( + writer.WriteCall( FunctionName.RandomDistAddItem, - SyntaxFactory.LiteralExpression(i), - SyntaxFactory.LiteralExpression(unitTable.UnitSets[i].Chance))); + JassLiteral.Int(i), + JassLiteral.Int(unitTable.UnitSets[i].Chance)); } - statements.Add(SyntaxFactory.SetStatement(VariableName.CurrentSet, SyntaxFactory.InvocationExpression(FunctionName.RandomDistChoose))); - statements.Add(JassEmptySyntax.Value); + writer.WriteSet(VariableName.CurrentSet, JassExpression.Invoke(FunctionName.RandomDistChoose)); + writer.WriteLine(); var groupVarName = unitTable.GetVariableName(); - var ifElseifBlocks = new List<(IExpressionSyntax Condition, IStatementSyntax[] Body)>(); for (var setIndex = 0; setIndex < unitTable.UnitSets.Count; setIndex++) { var set = unitTable.UnitSets[setIndex]; - var condition = SyntaxFactory.BinaryEqualsExpression(SyntaxFactory.VariableReferenceExpression(VariableName.CurrentSet), SyntaxFactory.LiteralExpression(setIndex)); - var bodyStatements = new List(); + var condition = JassExpression.ParenthesizedCompact(JassExpression.EqualCompact( + VariableName.CurrentSet, + JassLiteral.Int(setIndex))); + + if (setIndex == 0) + { + writer.WriteIf(condition); + } + else + { + writer.WriteElseIf(condition); + } for (var position = 0; position < unitTable.Types.Count; position++) { var id = set?.UnitIds[position] ?? 0; var unitTypeExpression = RandomUnitProvider.IsRandomUnit(id, out var level) - ? SyntaxFactory.InvocationExpression(NativeName.ChooseRandomCreep, SyntaxFactory.LiteralExpression(level)) - : id == 0 ? SyntaxFactory.LiteralExpression(-1) : SyntaxFactory.FourCCLiteralExpression(id); + ? JassExpression.Invoke(NativeName.ChooseRandomCreep, JassLiteral.Int(level)) + : id == 0 ? "-1" : JassLiteral.FourCC(id); - bodyStatements.Add(SyntaxFactory.SetStatement( - groupVarName, - SyntaxFactory.LiteralExpression(position), - unitTypeExpression)); + writer.WriteSet( + JassExpression.ElementAccess(groupVarName, position), + unitTypeExpression); } - - ifElseifBlocks.Add((new JassParenthesizedExpressionSyntax(condition), bodyStatements.ToArray())); } - var elseClauseStatements = new List(); + writer.WriteElse(); + for (var position = 0; position < unitTable.Types.Count; position++) { - elseClauseStatements.Add(SyntaxFactory.SetStatement( - groupVarName, - SyntaxFactory.LiteralExpression(position), - SyntaxFactory.LiteralExpression(-1))); + writer.WriteSet( + JassExpression.ElementAccess(groupVarName, position), + "-1"); } - statements.Add(SyntaxFactory.IfStatement( - ifElseifBlocks.First().Condition, - SyntaxFactory.StatementList(ifElseifBlocks.First().Body), - ifElseifBlocks.Skip(1).Select(elseIf => new JassElseIfClauseSyntax(elseIf.Condition, SyntaxFactory.StatementList(elseIf.Body))), - new JassElseClauseSyntax(SyntaxFactory.StatementList(elseClauseStatements)))); - - statements.Add(JassEmptySyntax.Value); + writer.WriteEndIf(); + writer.WriteLine(); } - return SyntaxFactory.FunctionDeclaration(SyntaxFactory.FunctionDeclarator(nameof(InitRandomGroups)), statements); + writer.EndFunction(); } - protected internal virtual bool InitRandomGroupsCondition(Map map) + protected internal virtual bool ShouldGenerateInitRandomGroups(Map map) { if (map is null) { @@ -108,7 +112,7 @@ protected internal virtual bool InitRandomGroupsCondition(Map map) } return map.Info?.RandomUnitTables is not null - && map.Info.RandomUnitTables.Any(); + && map.Info.RandomUnitTables.Count > 0; } } } \ No newline at end of file diff --git a/src/War3Net.Build/MapScriptBuilder/Info/InitTechTree.cs b/src/War3Net.Build/MapScriptBuilder/Info/InitTechTree.cs index 855fb618..8adc4f75 100644 --- a/src/War3Net.Build/MapScriptBuilder/Info/InitTechTree.cs +++ b/src/War3Net.Build/MapScriptBuilder/Info/InitTechTree.cs @@ -1,4 +1,4 @@ -// ------------------------------------------------------------------------------ +// ------------------------------------------------------------------------------ // // Licensed under the MIT license. // See the LICENSE file in the project root for more information. @@ -6,38 +6,41 @@ // ------------------------------------------------------------------------------ using System; -using System.Collections.Generic; using System.Linq; -using War3Net.CodeAnalysis.Jass.Syntax; - -using SyntaxFactory = War3Net.CodeAnalysis.Jass.JassSyntaxFactory; +using War3Net.CodeAnalysis; +using War3Net.CodeAnalysis.Jass.Extensions; namespace War3Net.Build { public partial class MapScriptBuilder { - protected internal virtual JassFunctionDeclarationSyntax InitTechTree(Map map) + protected internal virtual void GenerateInitTechTree(Map map, IndentedTextWriter writer) { if (map is null) { throw new ArgumentNullException(nameof(map)); } - var statements = new List(); + if (writer is null) + { + throw new ArgumentNullException(nameof(writer)); + } + + writer.WriteFunction(GeneratedFunctionName.InitTechTree); for (var i = 0; i < MaxPlayerSlots; i++) { - if (InitTechTree_PlayerCondition(map, i)) + if (ShouldGenerateInitTechTreeForPlayer(map, i)) { - statements.Add(SyntaxFactory.CallStatement(nameof(InitTechTree_Player) + i)); + writer.WriteCall(GeneratedFunctionName.InitTechTreeForPlayer(i)); } } - return SyntaxFactory.FunctionDeclaration(SyntaxFactory.FunctionDeclarator(nameof(InitTechTree)), statements); + writer.EndFunction(); } - protected internal virtual bool InitTechTreeCondition(Map map) + protected internal virtual bool ShouldGenerateInitTechTree(Map map) { if (map is null) { @@ -45,7 +48,7 @@ protected internal virtual bool InitTechTreeCondition(Map map) } return map.Info is not null - && map.Info.TechData.Any(); + && map.Info.TechData.Count > 0; } } } \ No newline at end of file diff --git a/src/War3Net.Build/MapScriptBuilder/Info/InitTechTreeForPlayer.cs b/src/War3Net.Build/MapScriptBuilder/Info/InitTechTreeForPlayer.cs index 2de2ec00..2eb583e2 100644 --- a/src/War3Net.Build/MapScriptBuilder/Info/InitTechTreeForPlayer.cs +++ b/src/War3Net.Build/MapScriptBuilder/Info/InitTechTreeForPlayer.cs @@ -1,4 +1,4 @@ -// ------------------------------------------------------------------------------ +// ------------------------------------------------------------------------------ // // Licensed under the MIT license. // See the LICENSE file in the project root for more information. @@ -6,33 +6,39 @@ // ------------------------------------------------------------------------------ using System; -using System.Collections.Generic; using System.Linq; using War3Net.Build.Info; -using War3Net.CodeAnalysis.Jass.Syntax; +using War3Net.CodeAnalysis; +using War3Net.CodeAnalysis.Jass; +using War3Net.CodeAnalysis.Jass.Extensions; using War3Net.Common.Extensions; -using SyntaxFactory = War3Net.CodeAnalysis.Jass.JassSyntaxFactory; - namespace War3Net.Build { public partial class MapScriptBuilder { - protected internal virtual JassFunctionDeclarationSyntax InitTechTree_Player(Map map, int playerId) + protected internal virtual void GenerateInitTechTreeForPlayer(Map map, int playerId, IndentedTextWriter writer) { if (map is null) { throw new ArgumentNullException(nameof(map)); } + if (writer is null) + { + throw new ArgumentNullException(nameof(writer)); + } + + var functionName = GeneratedFunctionName.InitTechTreeForPlayer(playerId); + var mapInfo = map.Info; if (mapInfo is null) { - throw new ArgumentException($"Function '{nameof(InitTechTree_Player) + playerId}' cannot be generated without {nameof(MapInfo)}.", nameof(map)); + throw new ArgumentException($"Function '{functionName}' cannot be generated without {nameof(MapInfo)}.", nameof(map)); } - var statements = new List(); + writer.WriteFunction(functionName); foreach (var techData in mapInfo.TechData) { @@ -40,27 +46,27 @@ protected internal virtual JassFunctionDeclarationSyntax InitTechTree_Player(Map { if (techData.Id.ToRawcode()[0] == 'A') { - statements.Add(SyntaxFactory.CallStatement( + writer.WriteCall( NativeName.SetPlayerAbilityAvailable, - SyntaxFactory.InvocationExpression(NativeName.Player, SyntaxFactory.LiteralExpression(playerId)), - SyntaxFactory.FourCCLiteralExpression(techData.Id), - SyntaxFactory.LiteralExpression(false))); + JassExpression.Invoke(NativeName.Player, JassLiteral.Int(playerId)), + JassLiteral.FourCC(techData.Id), + JassKeyword.False); } else { - statements.Add(SyntaxFactory.CallStatement( + writer.WriteCall( NativeName.SetPlayerTechMaxAllowed, - SyntaxFactory.InvocationExpression(NativeName.Player, SyntaxFactory.LiteralExpression(playerId)), - SyntaxFactory.FourCCLiteralExpression(techData.Id), - SyntaxFactory.LiteralExpression(0))); + JassExpression.Invoke(NativeName.Player, JassLiteral.Int(playerId)), + JassLiteral.FourCC(techData.Id), + JassLiteral.Int(0)); } } } - return SyntaxFactory.FunctionDeclaration(SyntaxFactory.FunctionDeclarator(nameof(InitTechTree_Player) + playerId), statements); + writer.EndFunction(); } - protected internal virtual bool InitTechTree_PlayerCondition(Map map, int playerId) + protected internal virtual bool ShouldGenerateInitTechTreeForPlayer(Map map, int playerId) { if (map is null) { diff --git a/src/War3Net.Build/MapScriptBuilder/Info/InitUpgrades.cs b/src/War3Net.Build/MapScriptBuilder/Info/InitUpgrades.cs index 66dbc61c..f68f0556 100644 --- a/src/War3Net.Build/MapScriptBuilder/Info/InitUpgrades.cs +++ b/src/War3Net.Build/MapScriptBuilder/Info/InitUpgrades.cs @@ -1,4 +1,4 @@ -// ------------------------------------------------------------------------------ +// ------------------------------------------------------------------------------ // // Licensed under the MIT license. // See the LICENSE file in the project root for more information. @@ -6,38 +6,40 @@ // ------------------------------------------------------------------------------ using System; -using System.Collections.Generic; -using System.Linq; - -using War3Net.CodeAnalysis.Jass.Syntax; -using SyntaxFactory = War3Net.CodeAnalysis.Jass.JassSyntaxFactory; +using War3Net.CodeAnalysis; +using War3Net.CodeAnalysis.Jass.Extensions; namespace War3Net.Build { public partial class MapScriptBuilder { - protected internal virtual JassFunctionDeclarationSyntax InitUpgrades(Map map) + protected internal virtual void GenerateInitUpgrades(Map map, IndentedTextWriter writer) { if (map is null) { throw new ArgumentNullException(nameof(map)); } - var statements = new List(); + if (writer is null) + { + throw new ArgumentNullException(nameof(writer)); + } + + writer.WriteFunction(GeneratedFunctionName.InitUpgrades); for (var i = 0; i < MaxPlayerSlots; i++) { - if (InitUpgrades_PlayerCondition(map, i)) + if (ShouldGenerateInitUpgradesForPlayer(map, i)) { - statements.Add(SyntaxFactory.CallStatement(nameof(InitUpgrades_Player) + i)); + writer.WriteCall(GeneratedFunctionName.InitUpgradesForPlayer(i)); } } - return SyntaxFactory.FunctionDeclaration(SyntaxFactory.FunctionDeclarator(nameof(InitUpgrades)), statements); + writer.EndFunction(); } - protected internal virtual bool InitUpgradesCondition(Map map) + protected internal virtual bool ShouldGenerateInitUpgrades(Map map) { if (map is null) { @@ -45,7 +47,7 @@ protected internal virtual bool InitUpgradesCondition(Map map) } return map.Info is not null - && map.Info.UpgradeData.Any(); + && map.Info.UpgradeData.Count > 0; } } } \ No newline at end of file diff --git a/src/War3Net.Build/MapScriptBuilder/Info/InitUpgradesForPlayer.cs b/src/War3Net.Build/MapScriptBuilder/Info/InitUpgradesForPlayer.cs index 288070f7..c4162bb1 100644 --- a/src/War3Net.Build/MapScriptBuilder/Info/InitUpgradesForPlayer.cs +++ b/src/War3Net.Build/MapScriptBuilder/Info/InitUpgradesForPlayer.cs @@ -1,4 +1,4 @@ -// ------------------------------------------------------------------------------ +// ------------------------------------------------------------------------------ // // Licensed under the MIT license. // See the LICENSE file in the project root for more information. @@ -10,28 +10,35 @@ using System.Linq; using War3Net.Build.Info; -using War3Net.CodeAnalysis.Jass.Syntax; - -using SyntaxFactory = War3Net.CodeAnalysis.Jass.JassSyntaxFactory; +using War3Net.CodeAnalysis; +using War3Net.CodeAnalysis.Jass; +using War3Net.CodeAnalysis.Jass.Extensions; namespace War3Net.Build { public partial class MapScriptBuilder { - protected internal virtual JassFunctionDeclarationSyntax InitUpgrades_Player(Map map, int playerId) + protected internal virtual void GenerateInitUpgradesForPlayer(Map map, int playerId, IndentedTextWriter writer) { if (map is null) { throw new ArgumentNullException(nameof(map)); } + if (writer is null) + { + throw new ArgumentNullException(nameof(writer)); + } + + var functionName = GeneratedFunctionName.InitUpgradesForPlayer(playerId); + var mapInfo = map.Info; if (mapInfo is null) { - throw new ArgumentException($"Function '{nameof(InitUpgrades_Player) + playerId}' cannot be generated without {nameof(MapInfo)}.", nameof(map)); + throw new ArgumentException($"Function '{functionName}' cannot be generated without {nameof(MapInfo)}.", nameof(map)); } - var statements = new List(); + writer.WriteFunction(functionName); var maxLevel = new Dictionary(); var researched = new Dictionary(); @@ -73,26 +80,26 @@ protected internal virtual JassFunctionDeclarationSyntax InitUpgrades_Player(Map foreach (var tech in maxLevel) { - statements.Add(SyntaxFactory.CallStatement( + writer.WriteCall( NativeName.SetPlayerTechMaxAllowed, - SyntaxFactory.InvocationExpression(NativeName.Player, SyntaxFactory.LiteralExpression(playerId)), - SyntaxFactory.FourCCLiteralExpression(tech.Key), - SyntaxFactory.LiteralExpression(tech.Value))); + JassExpression.Invoke(NativeName.Player, JassLiteral.Int(playerId)), + JassLiteral.FourCC(tech.Key), + JassLiteral.Int(tech.Value)); } foreach (var tech in researched) { - statements.Add(SyntaxFactory.CallStatement( + writer.WriteCall( NativeName.SetPlayerTechResearched, - SyntaxFactory.InvocationExpression(NativeName.Player, SyntaxFactory.LiteralExpression(playerId)), - SyntaxFactory.FourCCLiteralExpression(tech.Key), - SyntaxFactory.LiteralExpression(tech.Value + 1))); + JassExpression.Invoke(NativeName.Player, JassLiteral.Int(playerId)), + JassLiteral.FourCC(tech.Key), + JassLiteral.Int(tech.Value + 1)); } - return SyntaxFactory.FunctionDeclaration(SyntaxFactory.FunctionDeclarator(nameof(InitUpgrades_Player) + playerId), statements); + writer.EndFunction(); } - protected internal virtual bool InitUpgrades_PlayerCondition(Map map, int playerId) + protected internal virtual bool ShouldGenerateInitUpgradesForPlayer(Map map, int playerId) { if (map is null) { diff --git a/src/War3Net.Build/MapScriptBuilder/Info/MapItemTables.cs b/src/War3Net.Build/MapScriptBuilder/Info/MapItemTables.cs index 51e36ff9..c8b8fabb 100644 --- a/src/War3Net.Build/MapScriptBuilder/Info/MapItemTables.cs +++ b/src/War3Net.Build/MapScriptBuilder/Info/MapItemTables.cs @@ -1,4 +1,4 @@ -// ------------------------------------------------------------------------------ +// ------------------------------------------------------------------------------ // // Licensed under the MIT license. // See the LICENSE file in the project root for more information. @@ -6,33 +6,42 @@ // ------------------------------------------------------------------------------ using System; -using System.Collections.Generic; using System.Linq; using War3Net.Build.Info; -using War3Net.CodeAnalysis.Jass.Syntax; +using War3Net.CodeAnalysis; namespace War3Net.Build { public partial class MapScriptBuilder { - protected internal virtual IEnumerable MapItemTables(Map map) + protected internal virtual void GenerateMapItemTables(Map map, IndentedTextWriter writer) { if (map is null) { throw new ArgumentNullException(nameof(map)); } + if (writer is null) + { + throw new ArgumentNullException(nameof(writer)); + } + var randomItemTables = map.Info?.RandomItemTables; if (randomItemTables is null) { - throw new ArgumentException($"'ItemTable_DropItems' functions cannot be generated without {nameof(MapInfo.RandomItemTables)}."); + throw new ArgumentException($"DropItems functions cannot be generated without {nameof(MapInfo.RandomItemTables)}."); + } + + foreach (var table in randomItemTables) + { + GenerateItemTableDropItems(map, table, writer); } - return randomItemTables.Select(table => ItemTableDropItems(map, table)); + writer.WriteLine(); } - protected internal virtual bool MapItemTablesCondition(Map map) + protected internal virtual bool ShouldGenerateMapItemTables(Map map) { if (map is null) { @@ -40,7 +49,7 @@ protected internal virtual bool MapItemTablesCondition(Map map) } return map.Info?.RandomItemTables is not null - && map.Info.RandomItemTables.Any(); + && map.Info.RandomItemTables.Count > 0; } } } \ No newline at end of file diff --git a/src/War3Net.Build/MapScriptBuilder/Info/RandomUnitTables.cs b/src/War3Net.Build/MapScriptBuilder/Info/RandomUnitTables.cs index 11417c5d..e8b58a83 100644 --- a/src/War3Net.Build/MapScriptBuilder/Info/RandomUnitTables.cs +++ b/src/War3Net.Build/MapScriptBuilder/Info/RandomUnitTables.cs @@ -6,53 +6,54 @@ // ------------------------------------------------------------------------------ using System; -using System.Collections.Generic; -using System.Linq; - -using Microsoft.CodeAnalysis.CSharp.Syntax; using War3Net.Build.Extensions; -using War3Net.CodeAnalysis.Jass.Syntax; -using War3Net.CodeAnalysis.Transpilers; - -using SyntaxFactory = War3Net.CodeAnalysis.Jass.JassSyntaxFactory; +using War3Net.CodeAnalysis; +using War3Net.CodeAnalysis.Jass; +using War3Net.CodeAnalysis.Jass.Extensions; namespace War3Net.Build { public partial class MapScriptBuilder { - public virtual IEnumerable RandomUnitTablesApi(Map map, JassToCSharpTranspiler transpiler) + protected internal virtual void GenerateRandomUnitTables(Map map, IndentedTextWriter writer) { - if (transpiler is null) + if (map is null) { - throw new ArgumentNullException(nameof(transpiler)); + throw new ArgumentNullException(nameof(map)); } - return RandomUnitTables(map).Select(randomUnitTable => transpiler.Transpile(randomUnitTable)); - } - - protected internal virtual IEnumerable RandomUnitTables(Map map) - { - if (map is null) + if (writer is null) { - throw new ArgumentNullException(nameof(map)); + throw new ArgumentNullException(nameof(writer)); } var randomUnitTables = map.Info?.RandomUnitTables; if (randomUnitTables is null) { - yield break; + return; } var id = 0; foreach (var randomUnitTable in randomUnitTables) { - yield return SyntaxFactory.GlobalArrayDeclaration( - JassTypeSyntax.Integer, + writer.WriteAlignedGlobal( + $"{JassKeyword.Integer} {JassKeyword.Array}", randomUnitTable.GetVariableName(id)); id++; } } + + protected internal virtual bool ShouldGenerateRandomUnitTables(Map map) + { + if (map is null) + { + throw new ArgumentNullException(nameof(map)); + } + + return map.Info?.RandomUnitTables is not null + && map.Info.RandomUnitTables.Count > 0; + } } } \ No newline at end of file diff --git a/src/War3Net.Build/MapScriptBuilder/Script/Globals.cs b/src/War3Net.Build/MapScriptBuilder/Script/Globals.cs index a0ccae6a..b50c2790 100644 --- a/src/War3Net.Build/MapScriptBuilder/Script/Globals.cs +++ b/src/War3Net.Build/MapScriptBuilder/Script/Globals.cs @@ -5,49 +5,99 @@ // // ------------------------------------------------------------------------------ -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Linq; +using System; -using War3Net.CodeAnalysis.Jass.Syntax; +using War3Net.CodeAnalysis; +using War3Net.CodeAnalysis.Jass; +using War3Net.CodeAnalysis.Jass.Extensions; namespace War3Net.Build { public partial class MapScriptBuilder { - protected internal virtual JassGlobalDeclarationListSyntax Globals(Map map) + public virtual void GenerateGlobals(Map map, IndentedTextWriter writer) { - var globalDeclarationList = new List(); - var generatedGlobals = new List(); + if (map is null) + { + throw new ArgumentNullException(nameof(map)); + } + + if (writer is null) + { + throw new ArgumentNullException(nameof(writer)); + } - generatedGlobals.AddRange(Regions(map)); - generatedGlobals.AddRange(Cameras(map)); - generatedGlobals.AddRange(Sounds(map)); - generatedGlobals.AddRange(Triggers(map)); - generatedGlobals.AddRange(Units(map)); - generatedGlobals.AddRange(Destructables(map)); - generatedGlobals.AddRange(RandomUnitTables(map)); + var shouldGenerateUserDefinedVariables = ShouldGenerateUserDefinedVariables(map); + var shouldGenerateRegionVariables = ShouldGenerateRegionVariables(map); + var shouldGenerateCameraVariables = ShouldGenerateCameraVariables(map); + var shouldGenerateSoundVariables = ShouldGenerateSoundVariables(map); + var shouldGenerateTriggerVariables = ShouldGenerateTriggerVariables(map); + var shouldGenerateUnitVariables = ShouldGenerateUnitVariables(map); + var shouldGenerateDestructableVariables = ShouldGenerateDestructableVariables(map); + var shouldGenerateRandomUnitTables = ShouldGenerateRandomUnitTables(map); - var userDefinedGlobals = new List(Variables(map)); + writer.WriteLine(JassKeyword.Globals); + writer.Indent(); - if (userDefinedGlobals.Any()) + if (shouldGenerateUserDefinedVariables) { - globalDeclarationList.Add(new JassCommentSyntax(" User-defined")); - globalDeclarationList.AddRange(userDefinedGlobals); + writer.WriteComment("User-defined"); + GenerateUserDefinedVariables(map, writer); + } - if (generatedGlobals.Any()) + if (shouldGenerateRegionVariables || + shouldGenerateCameraVariables || + shouldGenerateSoundVariables || + shouldGenerateTriggerVariables || + shouldGenerateUnitVariables || + shouldGenerateDestructableVariables || + shouldGenerateRandomUnitTables) + { + if (shouldGenerateUserDefinedVariables) { - globalDeclarationList.Add(JassEmptySyntax.Value); + writer.WriteLine(); } - } - if (generatedGlobals.Any()) - { - globalDeclarationList.Add(new JassCommentSyntax(" Generated")); - globalDeclarationList.AddRange(generatedGlobals); + writer.WriteComment("Generated"); + + if (shouldGenerateRegionVariables) + { + GenerateRegionVariables(map, writer); + } + + if (shouldGenerateCameraVariables) + { + GenerateCameraVariables(map, writer); + } + + if (shouldGenerateSoundVariables) + { + GenerateSoundVariables(map, writer); + } + + if (shouldGenerateTriggerVariables) + { + GenerateTriggerVariables(map, writer); + } + + if (shouldGenerateUnitVariables) + { + GenerateUnitVariables(map, writer); + } + + if (shouldGenerateDestructableVariables) + { + GenerateDestructableVariables(map, writer); + } + + if (shouldGenerateRandomUnitTables) + { + GenerateRandomUnitTables(map, writer); + } } - return new JassGlobalDeclarationListSyntax(globalDeclarationList.ToImmutableArray()); + writer.Unindent(); + writer.WriteLine(JassKeyword.EndGlobals); } } } \ No newline at end of file diff --git a/src/War3Net.Build/MapScriptBuilder/Script/InitCustomTriggers.cs b/src/War3Net.Build/MapScriptBuilder/Script/InitCustomTriggers.cs index 39653fa0..52f22d16 100644 --- a/src/War3Net.Build/MapScriptBuilder/Script/InitCustomTriggers.cs +++ b/src/War3Net.Build/MapScriptBuilder/Script/InitCustomTriggers.cs @@ -6,47 +6,50 @@ // ------------------------------------------------------------------------------ using System; -using System.Collections.Generic; using System.Linq; using War3Net.Build.Extensions; using War3Net.Build.Script; -using War3Net.CodeAnalysis.Jass.Syntax; - -using SyntaxFactory = War3Net.CodeAnalysis.Jass.JassSyntaxFactory; +using War3Net.CodeAnalysis; +using War3Net.CodeAnalysis.Jass.Extensions; namespace War3Net.Build { public partial class MapScriptBuilder { - protected internal virtual JassFunctionDeclarationSyntax InitCustomTriggers(Map map) + protected internal virtual void GenerateInitCustomTriggers(Map map, IndentedTextWriter writer) { if (map is null) { throw new ArgumentNullException(nameof(map)); } + if (writer is null) + { + throw new ArgumentNullException(nameof(writer)); + } + var mapTriggers = map.Triggers; if (mapTriggers is null) { - throw new ArgumentException($"Function '{nameof(InitCustomTriggers)}' cannot be generated without {nameof(MapTriggers)}.", nameof(map)); + throw new ArgumentException($"Function '{GeneratedFunctionName.InitCustomTriggers}' cannot be generated without {nameof(MapTriggers)}.", nameof(map)); } - var statements = new List(); + writer.WriteFunction(GeneratedFunctionName.InitCustomTriggers); foreach (var trigger in mapTriggers.TriggerItems) { if (trigger is TriggerDefinition triggerDefinition && triggerDefinition.IsEnabled) { - statements.Add(SyntaxFactory.CallStatement(triggerDefinition.GetInitTrigFunctionName())); + writer.WriteCall(triggerDefinition.GetInitTrigFunctionName()); } } - return SyntaxFactory.FunctionDeclaration(SyntaxFactory.FunctionDeclarator(nameof(InitCustomTriggers)), statements); + writer.EndFunction(); } - protected internal virtual bool InitCustomTriggersCondition(Map map) + protected internal virtual bool ShouldGenerateInitCustomTriggers(Map map) { if (map is null) { diff --git a/src/War3Net.Build/MapScriptBuilder/Script/InitGlobals.cs b/src/War3Net.Build/MapScriptBuilder/Script/InitGlobals.cs index 0cb74cdd..31aaa829 100644 --- a/src/War3Net.Build/MapScriptBuilder/Script/InitGlobals.cs +++ b/src/War3Net.Build/MapScriptBuilder/Script/InitGlobals.cs @@ -6,36 +6,38 @@ // ------------------------------------------------------------------------------ using System; -using System.Collections.Generic; using System.Linq; using War3Net.Build.Extensions; using War3Net.Build.Info; -using War3Net.Build.Providers; using War3Net.Build.Script; +using War3Net.CodeAnalysis; using War3Net.CodeAnalysis.Jass; -using War3Net.CodeAnalysis.Jass.Syntax; - -using SyntaxFactory = War3Net.CodeAnalysis.Jass.JassSyntaxFactory; +using War3Net.CodeAnalysis.Jass.Extensions; namespace War3Net.Build { public partial class MapScriptBuilder { - protected internal virtual JassFunctionDeclarationSyntax InitGlobals(Map map) + protected internal virtual void GenerateInitGlobals(Map map, IndentedTextWriter writer) { if (map is null) { throw new ArgumentNullException(nameof(map)); } + if (writer is null) + { + throw new ArgumentNullException(nameof(writer)); + } + var mapTriggers = map.Triggers; if (mapTriggers is null) { - throw new ArgumentException($"Function '{nameof(InitGlobals)}' cannot be generated without {nameof(MapTriggers)}.", nameof(map)); + throw new ArgumentException($"Function '{GeneratedFunctionName.InitGlobals}' cannot be generated without {nameof(MapTriggers)}.", nameof(map)); } - var statements = new List(); + writer.WriteFunction(GeneratedFunctionName.InitGlobals); if (mapTriggers.Variables.Any(variable => (variable.IsArray || @@ -44,75 +46,85 @@ protected internal virtual JassFunctionDeclarationSyntax InitGlobals(Map map) TriggerData.TriggerTypeDefaults.TryGetValue(variable.Type, out _) || string.Equals(variable.Type, JassKeyword.String, StringComparison.Ordinal)))) { - statements.Add(SyntaxFactory.LocalVariableDeclarationStatement( - JassTypeSyntax.Integer, + writer.WriteLocal( + JassKeyword.Integer, "i", - SyntaxFactory.LiteralExpression(0))); + "0"); } foreach (var variable in mapTriggers.Variables) { + string? initialValueExpression = null; + if (variable.IsInitialized) { - var initialValue = TriggerData.TriggerParams.TryGetValue(variable.InitialValue, out var triggerParam) && string.Equals(triggerParam.VariableType, variable.Type, StringComparison.Ordinal) + initialValueExpression = TriggerData.TriggerParams.TryGetValue(variable.InitialValue, out var triggerParam) && string.Equals(triggerParam.VariableType, variable.Type, StringComparison.Ordinal) ? triggerParam.ScriptText : string.Equals(variable.Type, JassKeyword.String, StringComparison.Ordinal) ? $"\"{EscapedStringProvider.GetEscapedString(variable.InitialValue)}\"" : variable.InitialValue; - - statements.AddRange(InitGlobal(variable, SyntaxFactory.ParseExpression(initialValue))); } else if (TriggerData.TriggerTypeDefaults.TryGetValue(variable.Type, out var triggerTypeDefault)) { - statements.AddRange(InitGlobal(variable, SyntaxFactory.ParseExpression(triggerTypeDefault.ScriptText))); + initialValueExpression = triggerTypeDefault.ScriptText; } else if (string.Equals(variable.Type, JassKeyword.String, StringComparison.Ordinal)) { - statements.AddRange(InitGlobal(variable, SyntaxFactory.LiteralExpression(string.Empty))); + initialValueExpression = "\"\""; + } + + if (initialValueExpression is not null) + { + WriteInitGlobal(variable, initialValueExpression, writer); } } - return SyntaxFactory.FunctionDeclaration(SyntaxFactory.FunctionDeclarator(nameof(InitGlobals)), statements); + writer.EndFunction(); } - protected internal virtual IEnumerable InitGlobal(VariableDefinition variable, IExpressionSyntax expression) + protected internal virtual void WriteInitGlobal(VariableDefinition variable, string initialValueExpression, IndentedTextWriter writer) { if (variable is null) { throw new ArgumentNullException(nameof(variable)); } + if (writer is null) + { + throw new ArgumentNullException(nameof(writer)); + } + if (variable.IsArray) { - yield return SyntaxFactory.SetStatement( + writer.WriteSet( + "i", + "0"); + + writer.WriteLoop(); + writer.WriteExitWhen(JassExpression.ParenthesizedCompact(JassExpression.GreaterThan( "i", - SyntaxFactory.LiteralExpression(0)); - - yield return SyntaxFactory.LoopStatement( - SyntaxFactory.ExitStatement(SyntaxFactory.ParenthesizedExpression(SyntaxFactory.BinaryGreaterThanExpression( - SyntaxFactory.VariableReferenceExpression("i"), - SyntaxFactory.LiteralExpression(variable.ArraySize)))), - SyntaxFactory.SetStatement( - variable.GetVariableName(), - SyntaxFactory.VariableReferenceExpression("i"), - expression), - SyntaxFactory.SetStatement( - "i", - SyntaxFactory.BinaryAdditionExpression( - SyntaxFactory.VariableReferenceExpression("i"), - SyntaxFactory.LiteralExpression(1)))); - - yield return JassEmptySyntax.Value; + JassLiteral.Int(variable.ArraySize)))); + + writer.WriteSet( + JassExpression.ElementAccess(variable.GetVariableName(), "i"), + initialValueExpression); + + writer.WriteSet( + "i", + JassExpression.Add("i", "1")); + + writer.WriteEndLoop(); + writer.WriteLine(); } else { - yield return SyntaxFactory.SetStatement( + writer.WriteSet( variable.GetVariableName(), - expression); + initialValueExpression); } } - protected internal virtual bool InitGlobalsCondition(Map map) + protected internal virtual bool ShouldGenerateInitGlobals(Map map) { if (map is null) { diff --git a/src/War3Net.Build/MapScriptBuilder/Script/InitTrig.cs b/src/War3Net.Build/MapScriptBuilder/Script/InitTrig.cs index 65131930..86fb22cd 100644 --- a/src/War3Net.Build/MapScriptBuilder/Script/InitTrig.cs +++ b/src/War3Net.Build/MapScriptBuilder/Script/InitTrig.cs @@ -1,4 +1,4 @@ -// ------------------------------------------------------------------------------ +// ------------------------------------------------------------------------------ // // Licensed under the MIT license. // See the LICENSE file in the project root for more information. @@ -6,49 +6,14 @@ // ------------------------------------------------------------------------------ using System; -using System.Collections.Generic; -using War3Net.Build.Extensions; using War3Net.Build.Script; -using War3Net.CodeAnalysis.Jass.Syntax; - -using SyntaxFactory = War3Net.CodeAnalysis.Jass.JassSyntaxFactory; namespace War3Net.Build { public partial class MapScriptBuilder { - protected internal virtual JassFunctionDeclarationSyntax InitTrig(Map map, TriggerDefinition triggerDefinition) - { - if (map is null) - { - throw new ArgumentNullException(nameof(map)); - } - - if (triggerDefinition is null) - { - throw new ArgumentNullException(nameof(triggerDefinition)); - } - - var triggerVariableName = triggerDefinition.GetVariableName(); - - var statements = new List(); - - statements.Add(SyntaxFactory.SetStatement( - triggerVariableName, - SyntaxFactory.InvocationExpression(NativeName.CreateTrigger))); - - if (!triggerDefinition.IsInitiallyOn) - { - statements.Add(SyntaxFactory.CallStatement( - NativeName.DisableTrigger, - SyntaxFactory.FunctionReferenceExpression(triggerVariableName))); - } - - return SyntaxFactory.FunctionDeclaration(SyntaxFactory.FunctionDeclarator(triggerDefinition.GetInitTrigFunctionName()), statements); - } - - protected internal virtual bool InitTrigCondition(Map map, TriggerDefinition triggerDefinition) + protected internal virtual bool ShouldRenderTrigger(Map map, TriggerDefinition triggerDefinition) { if (map is null) { diff --git a/src/War3Net.Build/MapScriptBuilder/Script/RunInitializationTriggers.cs b/src/War3Net.Build/MapScriptBuilder/Script/RunInitializationTriggers.cs index e4ccfc45..b641aa6a 100644 --- a/src/War3Net.Build/MapScriptBuilder/Script/RunInitializationTriggers.cs +++ b/src/War3Net.Build/MapScriptBuilder/Script/RunInitializationTriggers.cs @@ -6,49 +6,52 @@ // ------------------------------------------------------------------------------ using System; -using System.Collections.Generic; using System.Linq; using War3Net.Build.Extensions; using War3Net.Build.Script; -using War3Net.CodeAnalysis.Jass.Syntax; - -using SyntaxFactory = War3Net.CodeAnalysis.Jass.JassSyntaxFactory; +using War3Net.CodeAnalysis; +using War3Net.CodeAnalysis.Jass.Extensions; namespace War3Net.Build { public partial class MapScriptBuilder { - protected internal virtual JassFunctionDeclarationSyntax RunInitializationTriggers(Map map) + protected internal virtual void GenerateRunInitializationTriggers(Map map, IndentedTextWriter writer) { if (map is null) { throw new ArgumentNullException(nameof(map)); } + if (writer is null) + { + throw new ArgumentNullException(nameof(writer)); + } + var mapTriggers = map.Triggers; if (mapTriggers is null) { - throw new ArgumentException($"Function '{nameof(RunInitializationTriggers)}' cannot be generated without {nameof(MapTriggers)}.", nameof(map)); + throw new ArgumentException($"Function '{GeneratedFunctionName.RunInitializationTriggers}' cannot be generated without {nameof(MapTriggers)}.", nameof(map)); } - var statements = new List(); + writer.WriteFunction(GeneratedFunctionName.RunInitializationTriggers); foreach (var trigger in mapTriggers.TriggerItems) { if (trigger is TriggerDefinition triggerDefinition && - RunInitializationTriggersConditionSingleTrigger(map, triggerDefinition)) + ShouldGenerateRunInitializationTriggersForTrigger(map, triggerDefinition)) { - statements.Add(SyntaxFactory.CallStatement( + writer.WriteCall( NativeName.ConditionalTriggerExecute, - SyntaxFactory.VariableReferenceExpression(triggerDefinition.GetVariableName()))); + triggerDefinition.GetVariableName()); } } - return SyntaxFactory.FunctionDeclaration(SyntaxFactory.FunctionDeclarator(nameof(RunInitializationTriggers)), statements); + writer.EndFunction(); } - protected internal virtual bool RunInitializationTriggersCondition(Map map) + protected internal virtual bool ShouldGenerateRunInitializationTriggers(Map map) { if (map is null) { @@ -58,10 +61,10 @@ protected internal virtual bool RunInitializationTriggersCondition(Map map) return map.Triggers is not null && map.Triggers.TriggerItems.Any(trigger => trigger is TriggerDefinition triggerDefinition && - RunInitializationTriggersConditionSingleTrigger(map, triggerDefinition)); + ShouldGenerateRunInitializationTriggersForTrigger(map, triggerDefinition)); } - protected internal virtual bool RunInitializationTriggersConditionSingleTrigger(Map map, TriggerDefinition triggerDefinition) + protected internal virtual bool ShouldGenerateRunInitializationTriggersForTrigger(Map map, TriggerDefinition triggerDefinition) { if (map is null) { diff --git a/src/War3Net.Build/MapScriptBuilder/Script/Triggers.cs b/src/War3Net.Build/MapScriptBuilder/Script/Triggers.cs index 2666ccd5..d98360d6 100644 --- a/src/War3Net.Build/MapScriptBuilder/Script/Triggers.cs +++ b/src/War3Net.Build/MapScriptBuilder/Script/Triggers.cs @@ -6,42 +6,65 @@ // ------------------------------------------------------------------------------ using System; -using System.Collections.Generic; +using System.Linq; using War3Net.Build.Extensions; using War3Net.Build.Script; -using War3Net.CodeAnalysis.Jass.Syntax; - -using SyntaxFactory = War3Net.CodeAnalysis.Jass.JassSyntaxFactory; +using War3Net.CodeAnalysis; +using War3Net.CodeAnalysis.Jass; +using War3Net.CodeAnalysis.Jass.Extensions; namespace War3Net.Build { public partial class MapScriptBuilder { - protected internal virtual IEnumerable Triggers(Map map) + protected internal virtual void GenerateTriggerVariables(Map map, IndentedTextWriter writer) { if (map is null) { throw new ArgumentNullException(nameof(map)); } + if (writer is null) + { + throw new ArgumentNullException(nameof(writer)); + } + var mapTriggers = map.Triggers; if (mapTriggers is null) { - yield break; + return; + } + + foreach (var triggerItem in mapTriggers.TriggerItems.Where(ShouldGenerateTriggerVariable)) + { + writer.WriteAlignedGlobal( + TypeName.Trigger, + triggerItem.GetVariableName(), + JassKeyword.Null); + } + } + + protected internal virtual bool ShouldGenerateTriggerVariables(Map map) + { + if (map is null) + { + throw new ArgumentNullException(nameof(map)); } - foreach (var trigger in mapTriggers.TriggerItems) + return map.Triggers is not null + && map.Triggers.TriggerItems.Any(ShouldGenerateTriggerVariable); + } + + protected internal virtual bool ShouldGenerateTriggerVariable(TriggerItem triggerItem) + { + if (triggerItem is null) { - if (trigger is TriggerDefinition triggerDefinition && - !triggerDefinition.IsComment) - { - yield return SyntaxFactory.GlobalDeclaration( - SyntaxFactory.ParseTypeName(TypeName.Trigger), - triggerDefinition.GetVariableName(), - JassNullLiteralExpressionSyntax.Value); - } + throw new ArgumentNullException(nameof(triggerItem)); } + + return triggerItem is TriggerDefinition triggerDefinition + && !triggerDefinition.IsComment; } } } \ No newline at end of file diff --git a/src/War3Net.Build/MapScriptBuilder/Script/Variables.cs b/src/War3Net.Build/MapScriptBuilder/Script/Variables.cs index 4018cebb..6b01c888 100644 --- a/src/War3Net.Build/MapScriptBuilder/Script/Variables.cs +++ b/src/War3Net.Build/MapScriptBuilder/Script/Variables.cs @@ -6,66 +6,80 @@ // ------------------------------------------------------------------------------ using System; -using System.Collections.Generic; using War3Net.Build.Extensions; +using War3Net.CodeAnalysis; using War3Net.CodeAnalysis.Jass; -using War3Net.CodeAnalysis.Jass.Syntax; - -using SyntaxFactory = War3Net.CodeAnalysis.Jass.JassSyntaxFactory; +using War3Net.CodeAnalysis.Jass.Extensions; namespace War3Net.Build { public partial class MapScriptBuilder { - protected internal virtual IEnumerable Variables(Map map) + protected internal virtual void GenerateUserDefinedVariables(Map map, IndentedTextWriter writer) { if (map is null) { throw new ArgumentNullException(nameof(map)); } + if (writer is null) + { + throw new ArgumentNullException(nameof(writer)); + } + var mapTriggers = map.Triggers; if (mapTriggers is null) { - yield break; + return; } foreach (var variable in mapTriggers.Variables) { - var type = TriggerData.TriggerTypes.TryGetValue(variable.Type, out var triggerType) && !string.IsNullOrEmpty(triggerType.BaseType) + var variableType = TriggerData.TriggerTypes.TryGetValue(variable.Type, out var triggerType) && !string.IsNullOrEmpty(triggerType.BaseType) ? triggerType.BaseType : variable.Type; if (variable.IsArray) { - yield return SyntaxFactory.GlobalArrayDeclaration( - SyntaxFactory.ParseTypeName(type), + writer.WriteAlignedGlobal( + $"{variableType} {JassKeyword.Array}", variable.GetVariableName()); } else if (string.Equals(variable.Type, JassKeyword.String, StringComparison.Ordinal)) { - yield return SyntaxFactory.GlobalDeclaration( - JassTypeSyntax.String, + writer.WriteAlignedGlobal( + JassKeyword.String, variable.GetVariableName()); } else { - var value = variable.Type switch + var defaultValue = variable.Type switch { - JassKeyword.Integer => SyntaxFactory.LiteralExpression(0), - JassKeyword.Real => SyntaxFactory.LiteralExpression(0), - JassKeyword.Boolean => JassBooleanLiteralExpressionSyntax.False, + JassKeyword.Integer => "0", + JassKeyword.Real => "0", + JassKeyword.Boolean => JassKeyword.False, - _ => JassNullLiteralExpressionSyntax.Value, + _ => JassKeyword.Null, }; - yield return SyntaxFactory.GlobalDeclaration( - SyntaxFactory.ParseTypeName(type), + writer.WriteAlignedGlobal( + variableType, variable.GetVariableName(), - value); + defaultValue); } } } + + protected internal virtual bool ShouldGenerateUserDefinedVariables(Map map) + { + if (map is null) + { + throw new ArgumentNullException(nameof(map)); + } + + return map.Triggers?.Variables is not null + && map.Triggers.Variables.Count > 0; + } } } \ No newline at end of file diff --git a/src/War3Net.Build/MapScriptBuilder/Widget/CreateAllDestructables.cs b/src/War3Net.Build/MapScriptBuilder/Widget/CreateAllDestructables.cs index 809834c3..1c2b8387 100644 --- a/src/War3Net.Build/MapScriptBuilder/Widget/CreateAllDestructables.cs +++ b/src/War3Net.Build/MapScriptBuilder/Widget/CreateAllDestructables.cs @@ -9,41 +9,48 @@ using System.Collections.Generic; using System.Linq; +using War3Net.Build.Common; using War3Net.Build.Extensions; using War3Net.Build.Info; using War3Net.Build.Widget; -using War3Net.CodeAnalysis.Jass.Syntax; - -using SyntaxFactory = War3Net.CodeAnalysis.Jass.JassSyntaxFactory; +using War3Net.CodeAnalysis; +using War3Net.CodeAnalysis.Jass; +using War3Net.CodeAnalysis.Jass.Extensions; namespace War3Net.Build { public partial class MapScriptBuilder { - protected internal virtual JassFunctionDeclarationSyntax CreateAllDestructables(Map map) + protected internal virtual void GenerateCreateAllDestructables(Map map, IndentedTextWriter writer) { if (map is null) { throw new ArgumentNullException(nameof(map)); } + if (writer is null) + { + throw new ArgumentNullException(nameof(writer)); + } + var mapDoodads = map.Doodads; if (mapDoodads is null) { - throw new ArgumentException($"Function '{nameof(CreateAllDestructables)}' cannot be generated without {nameof(MapDoodads)}.", nameof(map)); + throw new ArgumentException($"Function '{GeneratedFunctionName.CreateAllDestructables}' cannot be generated without {nameof(MapDoodads)}.", nameof(map)); } - var statements = new List(); + writer.WriteFunction(GeneratedFunctionName.CreateAllDestructables); + if (!ForceGenerateGlobalDestructableVariable) { - statements.Add(SyntaxFactory.LocalVariableDeclarationStatement(SyntaxFactory.ParseTypeName(TypeName.Destructable), VariableName.Destructable)); + writer.WriteLocal(TypeName.Destructable, VariableName.Destructable); } - statements.Add(SyntaxFactory.LocalVariableDeclarationStatement(SyntaxFactory.ParseTypeName(TypeName.Trigger), VariableName.Trigger)); + writer.WriteLocal(TypeName.Trigger, VariableName.Trigger); if (UseLifeVariable) { - statements.Add(SyntaxFactory.LocalVariableDeclarationStatement(JassTypeSyntax.Real, VariableName.Life)); + writer.WriteLocal(JassKeyword.Real, VariableName.Life); } var createFunctions = new[] @@ -58,7 +65,7 @@ protected internal virtual JassFunctionDeclarationSyntax CreateAllDestructables( NativeName.BlzCreateDeadDestructableZWithSkin, }; - foreach (var (destructable, id) in mapDoodads.Doodads.IncludeId().Where(pair => CreateAllDestructablesConditionSingleDoodad(map, pair.Obj))) + foreach (var (destructable, id) in mapDoodads.Doodads.Where(ShouldGenerateDestructableVariable).IncludeId()) { var destructableVariableName = destructable.GetVariableName(); if (!ForceGenerateGlobalDestructableVariable && (!TriggerVariableReferences.TryGetValue(destructableVariableName, out var value) || !value)) @@ -72,84 +79,88 @@ protected internal virtual JassFunctionDeclarationSyntax CreateAllDestructables( var hasSkin = ForceGenerateUnitWithSkin || skinId != destructable.TypeId; var createFunctionIndex = isDead ? 1 : 0; - var arguments = new List(); - arguments.Add(SyntaxFactory.FourCCLiteralExpression(destructable.TypeId)); - arguments.Add(SyntaxFactory.LiteralExpression(destructable.Position.X)); - arguments.Add(SyntaxFactory.LiteralExpression(destructable.Position.Y)); + var arguments = new List(); + arguments.Add(JassLiteral.FourCC(destructable.TypeId)); + arguments.Add(JassLiteral.Real(destructable.Position.X)); + arguments.Add(JassLiteral.Real(destructable.Position.Y)); if (hasZ) { - arguments.Add(SyntaxFactory.LiteralExpression(destructable.Position.Z)); + arguments.Add(JassLiteral.Real(destructable.Position.Z)); createFunctionIndex += 2; } - arguments.Add(SyntaxFactory.LiteralExpression(destructable.Rotation * (180f / MathF.PI), precision: 3)); - arguments.Add(SyntaxFactory.LiteralExpression(destructable.Scale.X, precision: 3)); - arguments.Add(SyntaxFactory.LiteralExpression(destructable.Variation)); + arguments.Add(JassLiteral.Real(destructable.Rotation * W3MathF.Rad2Deg, 3)); + arguments.Add(JassLiteral.Real(destructable.Scale.X, 3)); + arguments.Add(JassLiteral.Int(destructable.Variation)); if (hasSkin) { - arguments.Add(SyntaxFactory.FourCCLiteralExpression(destructable.SkinId)); + arguments.Add(JassLiteral.FourCC(destructable.SkinId)); createFunctionIndex += 4; } - statements.Add(SyntaxFactory.SetStatement( + writer.WriteSet( destructableVariableName, - SyntaxFactory.InvocationExpression(createFunctions[createFunctionIndex], arguments.ToArray()))); + JassExpression.InvokeSpaced( + createFunctions[createFunctionIndex], + arguments.ToArray())); if (!isDead && destructable.Life != 100) { + var lifePercentLiteral = JassLiteral.Real(destructable.Life * 0.01f, 2); + if (UseLifeVariable) { - statements.Add(SyntaxFactory.SetStatement( + writer.WriteSet( VariableName.Life, - SyntaxFactory.InvocationExpression( + JassExpression.InvokeSpaced( NativeName.GetDestructableLife, - SyntaxFactory.VariableReferenceExpression(destructableVariableName)))); + destructableVariableName)); - statements.Add(SyntaxFactory.CallStatement( + writer.WriteCall( NativeName.SetDestructableLife, - SyntaxFactory.VariableReferenceExpression(destructableVariableName), - SyntaxFactory.BinaryMultiplicationExpression( - SyntaxFactory.LiteralExpression(destructable.Life * 0.01f, precision: 2), - SyntaxFactory.VariableReferenceExpression(VariableName.Life)))); + destructableVariableName, + JassExpression.Multiply( + lifePercentLiteral, + VariableName.Life)); } else { - statements.Add(SyntaxFactory.CallStatement( + writer.WriteCall( NativeName.SetDestructableLife, - SyntaxFactory.VariableReferenceExpression(destructableVariableName), - SyntaxFactory.BinaryMultiplicationExpression( - SyntaxFactory.LiteralExpression(destructable.Life * 0.01f, precision: 2), - SyntaxFactory.InvocationExpression(NativeName.GetDestructableLife, SyntaxFactory.VariableReferenceExpression(destructableVariableName))))); + destructableVariableName, + JassExpression.Multiply( + lifePercentLiteral, + JassExpression.Invoke(NativeName.GetDestructableLife, destructableVariableName))); } } if (destructable.HasItemTable()) { - statements.Add(SyntaxFactory.SetStatement( + writer.WriteSet( VariableName.Trigger, - SyntaxFactory.InvocationExpression(NativeName.CreateTrigger))); + JassExpression.InvokeSpaced(NativeName.CreateTrigger)); - statements.Add(SyntaxFactory.CallStatement( + writer.WriteCall( NativeName.TriggerRegisterDeathEvent, - SyntaxFactory.VariableReferenceExpression(VariableName.Trigger), - SyntaxFactory.VariableReferenceExpression(destructableVariableName))); + VariableName.Trigger, + destructableVariableName); - statements.Add(SyntaxFactory.CallStatement( + writer.WriteCall( NativeName.TriggerAddAction, - SyntaxFactory.VariableReferenceExpression(VariableName.Trigger), - SyntaxFactory.FunctionReferenceExpression(FunctionName.SaveDyingWidget))); + VariableName.Trigger, + JassExpression.FunctionRef(FunctionName.SaveDyingWidget)); - statements.Add(SyntaxFactory.CallStatement( + writer.WriteCall( NativeName.TriggerAddAction, - SyntaxFactory.VariableReferenceExpression(VariableName.Trigger), - SyntaxFactory.FunctionReferenceExpression(destructable.GetDropItemsFunctionName(id)))); + VariableName.Trigger, + JassExpression.FunctionRef(destructable.GetDropItemsFunctionName(id))); } } - return SyntaxFactory.FunctionDeclaration(SyntaxFactory.FunctionDeclarator(nameof(CreateAllDestructables)), statements); + writer.EndFunction(); } - protected internal virtual bool CreateAllDestructablesCondition(Map map) + protected internal virtual bool ShouldGenerateCreateAllDestructables(Map map) { if (map is null) { @@ -162,24 +173,7 @@ protected internal virtual bool CreateAllDestructablesCondition(Map map) } return map.Doodads is not null - && map.Doodads.Doodads.Any(doodad => CreateAllDestructablesConditionSingleDoodad(map, doodad)); - } - - protected internal virtual bool CreateAllDestructablesConditionSingleDoodad(Map map, DoodadData doodadData) - { - if (map is null) - { - throw new ArgumentNullException(nameof(map)); - } - - if (doodadData is null) - { - throw new ArgumentNullException(nameof(doodadData)); - } - - return ForceGenerateGlobalDestructableVariable - || (TriggerVariableReferences.TryGetValue(doodadData.GetVariableName(), out var value) && value) - || doodadData.HasItemTable(); + && map.Doodads.Doodads.Any(ShouldGenerateDestructableVariable); } } } \ No newline at end of file diff --git a/src/War3Net.Build/MapScriptBuilder/Widget/CreateAllItems.cs b/src/War3Net.Build/MapScriptBuilder/Widget/CreateAllItems.cs index b76343c3..5815452e 100644 --- a/src/War3Net.Build/MapScriptBuilder/Widget/CreateAllItems.cs +++ b/src/War3Net.Build/MapScriptBuilder/Widget/CreateAllItems.cs @@ -13,31 +13,37 @@ using War3Net.Build.Info; using War3Net.Build.Providers; using War3Net.Build.Widget; -using War3Net.CodeAnalysis.Jass.Syntax; - -using SyntaxFactory = War3Net.CodeAnalysis.Jass.JassSyntaxFactory; +using War3Net.CodeAnalysis; +using War3Net.CodeAnalysis.Jass; +using War3Net.CodeAnalysis.Jass.Extensions; namespace War3Net.Build { public partial class MapScriptBuilder { - protected internal virtual JassFunctionDeclarationSyntax CreateAllItems(Map map) + protected internal virtual void GenerateCreateAllItems(Map map, IndentedTextWriter writer) { if (map is null) { throw new ArgumentNullException(nameof(map)); } + if (writer is null) + { + throw new ArgumentNullException(nameof(writer)); + } + var mapUnits = map.Units; if (mapUnits is null) { - throw new ArgumentException($"Function '{nameof(CreateAllItems)}' cannot be generated without {nameof(MapUnits)}.", nameof(map)); + throw new ArgumentException($"Function '{GeneratedFunctionName.CreateAllItems}' cannot be generated without {nameof(MapUnits)}.", nameof(map)); } - var statements = new List(); - statements.Add(SyntaxFactory.LocalVariableDeclarationStatement(JassTypeSyntax.Integer, VariableName.ItemId)); + writer.WriteFunction(GeneratedFunctionName.CreateAllItems); - foreach (var item in mapUnits.Units.Where(item => CreateAllItemsConditionSingleItem(map, item))) + writer.WriteLocal(JassKeyword.Integer, VariableName.ItemId); + + foreach (var item in mapUnits.Units.Where(item => ShouldGenerateCreateAllItemsForItem(map, item))) { if (item.IsRandomItem()) { @@ -45,12 +51,12 @@ protected internal virtual JassFunctionDeclarationSyntax CreateAllItems(Map map) switch (randomData) { case RandomUnitAny randomUnitAny: - statements.Add(SyntaxFactory.SetStatement( + writer.WriteSet( VariableName.ItemId, - SyntaxFactory.InvocationExpression( + JassExpression.InvokeSpaced( NativeName.ChooseRandomItemEx, - SyntaxFactory.InvocationExpression(NativeName.ConvertItemType, SyntaxFactory.LiteralExpression((int)randomUnitAny.Class)), - SyntaxFactory.LiteralExpression(randomUnitAny.Level)))); + JassExpression.Invoke(NativeName.ConvertItemType, JassLiteral.Int((int)randomUnitAny.Class)), + JassLiteral.Int(randomUnitAny.Level))); break; @@ -58,37 +64,37 @@ protected internal virtual JassFunctionDeclarationSyntax CreateAllItems(Map map) break; case RandomUnitCustomTable randomUnitCustomTable: - statements.Add(SyntaxFactory.CallStatement(FunctionName.RandomDistReset)); + writer.WriteCall(FunctionName.RandomDistReset); var summedChance = 0; foreach (var randomItem in randomUnitCustomTable.RandomUnits) { - IExpressionSyntax id = RandomItemProvider.IsRandomItem(randomItem.UnitId, out var itemClass, out var level) - ? SyntaxFactory.InvocationExpression( + var itemIdExpression = RandomItemProvider.IsRandomItem(randomItem.UnitId, out var itemClass, out var level) + ? JassExpression.Invoke( NativeName.ChooseRandomItemEx, - SyntaxFactory.InvocationExpression(NativeName.ConvertItemType, SyntaxFactory.LiteralExpression((int)itemClass)), - SyntaxFactory.LiteralExpression(level)) - : SyntaxFactory.FourCCLiteralExpression(randomItem.UnitId); + JassExpression.Invoke(NativeName.ConvertItemType, JassLiteral.Int((int)itemClass)), + JassLiteral.Int(level)) + : JassLiteral.FourCC(randomItem.UnitId); - statements.Add(SyntaxFactory.CallStatement( + writer.WriteCall( FunctionName.RandomDistAddItem, - id, - SyntaxFactory.LiteralExpression(randomItem.Chance))); + itemIdExpression, + JassLiteral.Int(randomItem.Chance)); summedChance += randomItem.Chance; } if (summedChance < 100) { - statements.Add(SyntaxFactory.CallStatement( + writer.WriteCall( FunctionName.RandomDistAddItem, - SyntaxFactory.LiteralExpression(-1), - SyntaxFactory.LiteralExpression(100 - summedChance))); + "-1", + JassLiteral.Int(100 - summedChance)); } - statements.Add(SyntaxFactory.SetStatement( + writer.WriteSet( VariableName.ItemId, - SyntaxFactory.InvocationExpression(FunctionName.RandomDistChoose))); + JassExpression.InvokeSpaced(FunctionName.RandomDistChoose)); break; @@ -96,39 +102,43 @@ protected internal virtual JassFunctionDeclarationSyntax CreateAllItems(Map map) break; } - statements.Add(SyntaxFactory.IfStatement( - SyntaxFactory.BinaryNotEqualsExpression(SyntaxFactory.VariableReferenceExpression(VariableName.ItemId), SyntaxFactory.LiteralExpression(-1)), - SyntaxFactory.CallStatement( - NativeName.CreateItem, - SyntaxFactory.VariableReferenceExpression(VariableName.ItemId), - SyntaxFactory.LiteralExpression(item.Position.X), - SyntaxFactory.LiteralExpression(item.Position.Y)))); + writer.WriteIf(JassExpression.NotEqual(VariableName.ItemId, "-1")); + + writer.WriteCall( + NativeName.CreateItem, + VariableName.ItemId, + JassLiteral.Real(item.Position.X), + JassLiteral.Real(item.Position.Y)); + + writer.WriteEndIf(); } else { - var args = new List() + var args = new List { - SyntaxFactory.FourCCLiteralExpression(item.TypeId), - SyntaxFactory.LiteralExpression(item.Position.X), - SyntaxFactory.LiteralExpression(item.Position.Y), + JassLiteral.FourCC(item.TypeId), + JassLiteral.Real(item.Position.X), + JassLiteral.Real(item.Position.Y), }; var hasSkin = item.SkinId != 0 && item.SkinId != item.TypeId; if (hasSkin) { - args.Add(SyntaxFactory.FourCCLiteralExpression(item.SkinId)); + args.Add(JassLiteral.FourCC(item.SkinId)); } - statements.Add(SyntaxFactory.CallStatement(hasSkin ? NativeName.BlzCreateItemWithSkin : NativeName.CreateItem, args.ToArray())); + writer.WriteCall( + hasSkin ? NativeName.BlzCreateItemWithSkin : NativeName.CreateItem, + args.ToArray()); } } - statements.Add(JassEmptySyntax.Value); + writer.WriteLine(); - return SyntaxFactory.FunctionDeclaration(SyntaxFactory.FunctionDeclarator(nameof(CreateAllItems)), statements); + writer.EndFunction(); } - protected internal virtual bool CreateAllItemsCondition(Map map) + protected internal virtual bool ShouldGenerateCreateAllItems(Map map) { if (map is null) { @@ -141,10 +151,10 @@ protected internal virtual bool CreateAllItemsCondition(Map map) } return map.Units is not null - && map.Units.Units.Any(item => CreateAllItemsConditionSingleItem(map, item)); + && map.Units.Units.Any(item => ShouldGenerateCreateAllItemsForItem(map, item)); } - protected internal virtual bool CreateAllItemsConditionSingleItem(Map map, UnitData unitData) + protected internal virtual bool ShouldGenerateCreateAllItemsForItem(Map map, UnitData unitData) { if (map is null) { diff --git a/src/War3Net.Build/MapScriptBuilder/Widget/CreateAllUnits.cs b/src/War3Net.Build/MapScriptBuilder/Widget/CreateAllUnits.cs index 2ef0d476..a05954a9 100644 --- a/src/War3Net.Build/MapScriptBuilder/Widget/CreateAllUnits.cs +++ b/src/War3Net.Build/MapScriptBuilder/Widget/CreateAllUnits.cs @@ -6,58 +6,61 @@ // ------------------------------------------------------------------------------ using System; -using System.Collections.Generic; using System.Linq; using War3Net.Build.Extensions; using War3Net.Build.Info; using War3Net.Build.Widget; -using War3Net.CodeAnalysis.Jass.Syntax; - -using SyntaxFactory = War3Net.CodeAnalysis.Jass.JassSyntaxFactory; +using War3Net.CodeAnalysis; +using War3Net.CodeAnalysis.Jass.Extensions; namespace War3Net.Build { public partial class MapScriptBuilder { - protected internal virtual JassFunctionDeclarationSyntax CreateAllUnits(Map map) + protected internal virtual void GenerateCreateAllUnits(Map map, IndentedTextWriter writer) { if (map is null) { throw new ArgumentNullException(nameof(map)); } - var statements = new List(); + if (writer is null) + { + throw new ArgumentNullException(nameof(writer)); + } - if (CreateNeutralPassiveBuildingsCondition(map)) + writer.WriteFunction(GeneratedFunctionName.CreateAllUnits); + + if (ShouldGenerateCreateNeutralPassiveBuildings(map)) { - statements.Add(SyntaxFactory.CallStatement(nameof(CreateNeutralPassiveBuildings))); + writer.WriteCall(GeneratedFunctionName.CreateNeutralPassiveBuildings); } - if (CreatePlayerBuildingsCondition(map)) + if (ShouldGenerateCreatePlayerBuildings(map)) { - statements.Add(SyntaxFactory.CallStatement(nameof(CreatePlayerBuildings))); + writer.WriteCall(GeneratedFunctionName.CreatePlayerBuildings); } - if (CreateNeutralHostileCondition(map)) + if (ShouldGenerateCreateNeutralHostile(map)) { - statements.Add(SyntaxFactory.CallStatement(nameof(CreateNeutralHostile))); + writer.WriteCall(GeneratedFunctionName.CreateNeutralHostile); } - if (CreateNeutralPassiveCondition(map)) + if (ShouldGenerateCreateNeutralPassive(map)) { - statements.Add(SyntaxFactory.CallStatement(nameof(CreateNeutralPassive))); + writer.WriteCall(GeneratedFunctionName.CreateNeutralPassive); } - if (CreatePlayerUnitsCondition(map)) + if (ShouldGenerateCreatePlayerUnits(map)) { - statements.Add(SyntaxFactory.CallStatement(nameof(CreatePlayerUnits))); + writer.WriteCall(GeneratedFunctionName.CreatePlayerUnits); } - return SyntaxFactory.FunctionDeclaration(SyntaxFactory.FunctionDeclarator(nameof(CreateAllUnits)), statements); + writer.EndFunction(); } - protected internal virtual bool CreateAllUnitsCondition(Map map) + protected internal virtual bool ShouldGenerateCreateAllUnits(Map map) { if (map is null) { @@ -67,25 +70,21 @@ protected internal virtual bool CreateAllUnitsCondition(Map map) if (map.Info is null || map.Info.FormatVersion >= MapInfoFormatVersion.v28) { return map.Units is not null - && map.Units.Units.Any(unit => CreateAllUnitsConditionSingleUnit(map, unit)); + && map.Units.Units.Any(ShouldGenerateCreateAllUnitsForUnit); } return map.Info.FormatVersion >= MapInfoFormatVersion.v15; } - protected internal virtual bool CreateAllUnitsConditionSingleUnit(Map map, UnitData unitData) + protected internal virtual bool ShouldGenerateCreateAllUnitsForUnit(UnitData unitData) { - if (map is null) - { - throw new ArgumentNullException(nameof(map)); - } - if (unitData is null) { throw new ArgumentNullException(nameof(unitData)); } - return unitData.IsUnit() && !unitData.IsPlayerStartLocation(); + return unitData.IsUnit() + && !unitData.IsPlayerStartLocation(); } } } \ No newline at end of file diff --git a/src/War3Net.Build/MapScriptBuilder/Widget/CreateBuildingsForPlayer.cs b/src/War3Net.Build/MapScriptBuilder/Widget/CreateBuildingsForPlayer.cs index 674fc6c8..17694294 100644 --- a/src/War3Net.Build/MapScriptBuilder/Widget/CreateBuildingsForPlayer.cs +++ b/src/War3Net.Build/MapScriptBuilder/Widget/CreateBuildingsForPlayer.cs @@ -1,4 +1,4 @@ -// ------------------------------------------------------------------------------ +// ------------------------------------------------------------------------------ // // Licensed under the MIT license. // See the LICENSE file in the project root for more information. @@ -10,38 +10,46 @@ using War3Net.Build.Extensions; using War3Net.Build.Widget; -using War3Net.CodeAnalysis.Jass.Syntax; - -using SyntaxFactory = War3Net.CodeAnalysis.Jass.JassSyntaxFactory; +using War3Net.CodeAnalysis; +using War3Net.CodeAnalysis.Jass; +using War3Net.CodeAnalysis.Jass.Extensions; namespace War3Net.Build { public partial class MapScriptBuilder { - protected internal virtual JassFunctionDeclarationSyntax CreateBuildingsForPlayer(Map map, int playerId) + protected internal virtual void GenerateCreateBuildingsForPlayer(Map map, int playerId, IndentedTextWriter writer) { - var functionName = nameof(CreateBuildingsForPlayer) + playerId; - if (map is null) { throw new ArgumentNullException(nameof(map)); } + if (writer is null) + { + throw new ArgumentNullException(nameof(writer)); + } + + var functionName = GeneratedFunctionName.CreateBuildingsForPlayer(playerId); + var mapUnits = map.Units; if (mapUnits is null) { throw new ArgumentException($"Function '{functionName}' cannot be generated without {nameof(MapUnits)}.", nameof(map)); } - return SyntaxFactory.FunctionDeclaration( - SyntaxFactory.FunctionDeclarator(functionName), - CreateUnits( - map, - mapUnits.Units.IncludeId().Where(pair => CreateBuildingsForPlayerConditionSingleUnit(map, playerId, pair.Obj)), - SyntaxFactory.LiteralExpression(playerId))); + writer.WriteFunction(functionName); + + GenerateCreateUnits( + map, + mapUnits.Units.IncludeId().Where(pair => ShouldGenerateCreateBuildingsForPlayerAndUnit(map, playerId, pair.Obj)), + JassLiteral.Int(playerId), + writer); + + writer.EndFunction(); } - protected internal virtual bool CreateBuildingsForPlayerCondition(Map map, int playerId) + protected internal virtual bool ShouldGenerateCreateBuildingsForPlayer(Map map, int playerId) { if (map is null) { @@ -49,10 +57,10 @@ protected internal virtual bool CreateBuildingsForPlayerCondition(Map map, int p } return map.Units is not null - && map.Units.Units.Any(unit => CreateBuildingsForPlayerConditionSingleUnit(map, playerId, unit)); + && map.Units.Units.Any(unit => ShouldGenerateCreateBuildingsForPlayerAndUnit(map, playerId, unit)); } - protected internal virtual bool CreateBuildingsForPlayerConditionSingleUnit(Map map, int playerId, UnitData unitData) + protected internal virtual bool ShouldGenerateCreateBuildingsForPlayerAndUnit(Map map, int playerId, UnitData unitData) { if (map is null) { diff --git a/src/War3Net.Build/MapScriptBuilder/Widget/CreateNeutralHostile.cs b/src/War3Net.Build/MapScriptBuilder/Widget/CreateNeutralHostile.cs index 6cb75bef..c6cd8469 100644 --- a/src/War3Net.Build/MapScriptBuilder/Widget/CreateNeutralHostile.cs +++ b/src/War3Net.Build/MapScriptBuilder/Widget/CreateNeutralHostile.cs @@ -1,4 +1,4 @@ -// ------------------------------------------------------------------------------ +// ------------------------------------------------------------------------------ // // Licensed under the MIT license. // See the LICENSE file in the project root for more information. @@ -10,36 +10,43 @@ using War3Net.Build.Extensions; using War3Net.Build.Widget; -using War3Net.CodeAnalysis.Jass.Syntax; - -using SyntaxFactory = War3Net.CodeAnalysis.Jass.JassSyntaxFactory; +using War3Net.CodeAnalysis; +using War3Net.CodeAnalysis.Jass.Extensions; namespace War3Net.Build { public partial class MapScriptBuilder { - protected internal virtual JassFunctionDeclarationSyntax CreateNeutralHostile(Map map) + protected internal virtual void GenerateCreateNeutralHostile(Map map, IndentedTextWriter writer) { if (map is null) { throw new ArgumentNullException(nameof(map)); } + if (writer is null) + { + throw new ArgumentNullException(nameof(writer)); + } + var mapUnits = map.Units; if (mapUnits is null) { - throw new ArgumentException($"Function '{nameof(CreateNeutralHostile)}' cannot be generated without {nameof(MapUnits)}.", nameof(map)); + throw new ArgumentException($"Function '{GeneratedFunctionName.CreateNeutralHostile}' cannot be generated without {nameof(MapUnits)}.", nameof(map)); } - return SyntaxFactory.FunctionDeclaration( - SyntaxFactory.FunctionDeclarator(nameof(CreateNeutralHostile)), - CreateUnits( - map, - mapUnits.Units.IncludeId().Where(pair => CreateNeutralHostileConditionSingleUnit(map, pair.Obj)), - SyntaxFactory.VariableReferenceExpression(GlobalVariableName.PlayerNeutralHostile))); + writer.WriteFunction(GeneratedFunctionName.CreateNeutralHostile); + + GenerateCreateUnits( + map, + mapUnits.Units.IncludeId().Where(pair => ShouldGenerateCreateNeutralHostileForUnit(map, pair.Obj)), + GlobalVariableName.PlayerNeutralHostile, + writer); + + writer.EndFunction(); } - protected internal virtual bool CreateNeutralHostileCondition(Map map) + protected internal virtual bool ShouldGenerateCreateNeutralHostile(Map map) { if (map is null) { @@ -47,10 +54,10 @@ protected internal virtual bool CreateNeutralHostileCondition(Map map) } return map.Units is not null - && map.Units.Units.Any(unit => CreateNeutralHostileConditionSingleUnit(map, unit)); + && map.Units.Units.Any(unit => ShouldGenerateCreateNeutralHostileForUnit(map, unit)); } - protected internal virtual bool CreateNeutralHostileConditionSingleUnit(Map map, UnitData unitData) + protected internal virtual bool ShouldGenerateCreateNeutralHostileForUnit(Map map, UnitData unitData) { if (map is null) { diff --git a/src/War3Net.Build/MapScriptBuilder/Widget/CreateNeutralHostileBuildings.cs b/src/War3Net.Build/MapScriptBuilder/Widget/CreateNeutralHostileBuildings.cs new file mode 100644 index 00000000..1bcf6965 --- /dev/null +++ b/src/War3Net.Build/MapScriptBuilder/Widget/CreateNeutralHostileBuildings.cs @@ -0,0 +1,82 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using System; +using System.Linq; + +using War3Net.Build.Extensions; +using War3Net.Build.Info; +using War3Net.Build.Widget; +using War3Net.CodeAnalysis; +using War3Net.CodeAnalysis.Jass.Extensions; + +namespace War3Net.Build +{ + public partial class MapScriptBuilder + { + protected internal virtual void GenerateCreateNeutralHostileBuildings(Map map, IndentedTextWriter writer) + { + if (map is null) + { + throw new ArgumentNullException(nameof(map)); + } + + if (writer is null) + { + throw new ArgumentNullException(nameof(writer)); + } + + var mapUnits = map.Units; + if (mapUnits is null) + { + throw new ArgumentException($"Function '{GeneratedFunctionName.CreateNeutralHostileBuildings}' cannot be generated without {nameof(MapUnits)}.", nameof(map)); + } + + writer.WriteFunction(GeneratedFunctionName.CreateNeutralHostileBuildings); + + GenerateCreateUnits( + map, + mapUnits.Units.IncludeId().Where(pair => ShouldGenerateCreateNeutralHostileBuildingsForUnit(map, pair.Obj)), + GlobalVariableName.PlayerNeutralHostile, + writer); + + writer.EndFunction(); + } + + protected internal virtual bool ShouldGenerateCreateNeutralHostileBuildings(Map map) + { + if (map is null) + { + throw new ArgumentNullException(nameof(map)); + } + + if (map.Info is not null && map.Info.FormatVersion < MapInfoFormatVersion.v15) + { + return false; + } + + return map.Units is not null + && map.Units.Units.Any(unit => ShouldGenerateCreateNeutralHostileBuildingsForUnit(map, unit)); + } + + protected internal virtual bool ShouldGenerateCreateNeutralHostileBuildingsForUnit(Map map, UnitData unitData) + { + if (map is null) + { + throw new ArgumentNullException(nameof(map)); + } + + if (unitData is null) + { + throw new ArgumentNullException(nameof(unitData)); + } + + var neutralHostileId = MaxPlayerSlots; + return unitData.OwnerId == neutralHostileId && unitData.IsUnit() && unitData.IsBuilding(map.UnitObjectData); + } + } +} \ No newline at end of file diff --git a/src/War3Net.Build/MapScriptBuilder/Widget/CreateNeutralPassive.cs b/src/War3Net.Build/MapScriptBuilder/Widget/CreateNeutralPassive.cs index 3ef6fb76..9dc3c2fe 100644 --- a/src/War3Net.Build/MapScriptBuilder/Widget/CreateNeutralPassive.cs +++ b/src/War3Net.Build/MapScriptBuilder/Widget/CreateNeutralPassive.cs @@ -1,4 +1,4 @@ -// ------------------------------------------------------------------------------ +// ------------------------------------------------------------------------------ // // Licensed under the MIT license. // See the LICENSE file in the project root for more information. @@ -11,36 +11,43 @@ using War3Net.Build.Extensions; using War3Net.Build.Info; using War3Net.Build.Widget; -using War3Net.CodeAnalysis.Jass.Syntax; - -using SyntaxFactory = War3Net.CodeAnalysis.Jass.JassSyntaxFactory; +using War3Net.CodeAnalysis; +using War3Net.CodeAnalysis.Jass.Extensions; namespace War3Net.Build { public partial class MapScriptBuilder { - protected internal virtual JassFunctionDeclarationSyntax CreateNeutralPassive(Map map) + protected internal virtual void GenerateCreateNeutralPassive(Map map, IndentedTextWriter writer) { if (map is null) { throw new ArgumentNullException(nameof(map)); } + if (writer is null) + { + throw new ArgumentNullException(nameof(writer)); + } + var mapUnits = map.Units; if (mapUnits is null) { - throw new ArgumentException($"Function '{nameof(CreateNeutralPassive)}' cannot be generated without {nameof(MapUnits)}.", nameof(map)); + throw new ArgumentException($"Function '{GeneratedFunctionName.CreateNeutralPassive}' cannot be generated without {nameof(MapUnits)}.", nameof(map)); } - return SyntaxFactory.FunctionDeclaration( - SyntaxFactory.FunctionDeclarator(nameof(CreateNeutralPassive)), - CreateUnits( - map, - mapUnits.Units.IncludeId().Where(pair => CreateNeutralPassiveConditionSingleUnit(map, pair.Obj)), - SyntaxFactory.VariableReferenceExpression(GlobalVariableName.PlayerNeutralPassive))); + writer.WriteFunction(GeneratedFunctionName.CreateNeutralPassive); + + GenerateCreateUnits( + map, + mapUnits.Units.IncludeId().Where(pair => ShouldGenerateCreateNeutralPassiveForUnit(map, pair.Obj)), + GlobalVariableName.PlayerNeutralPassive, + writer); + + writer.EndFunction(); } - protected internal virtual bool CreateNeutralPassiveCondition(Map map) + protected internal virtual bool ShouldGenerateCreateNeutralPassive(Map map) { if (map is null) { @@ -53,10 +60,10 @@ protected internal virtual bool CreateNeutralPassiveCondition(Map map) } return map.Units is not null - && map.Units.Units.Any(unit => CreateNeutralPassiveConditionSingleUnit(map, unit)); + && map.Units.Units.Any(unit => ShouldGenerateCreateNeutralPassiveForUnit(map, unit)); } - protected internal virtual bool CreateNeutralPassiveConditionSingleUnit(Map map, UnitData unitData) + protected internal virtual bool ShouldGenerateCreateNeutralPassiveForUnit(Map map, UnitData unitData) { if (map is null) { diff --git a/src/War3Net.Build/MapScriptBuilder/Widget/CreateNeutralPassiveBuildings.cs b/src/War3Net.Build/MapScriptBuilder/Widget/CreateNeutralPassiveBuildings.cs index d24a5525..276c64b5 100644 --- a/src/War3Net.Build/MapScriptBuilder/Widget/CreateNeutralPassiveBuildings.cs +++ b/src/War3Net.Build/MapScriptBuilder/Widget/CreateNeutralPassiveBuildings.cs @@ -1,4 +1,4 @@ -// ------------------------------------------------------------------------------ +// ------------------------------------------------------------------------------ // // Licensed under the MIT license. // See the LICENSE file in the project root for more information. @@ -11,36 +11,43 @@ using War3Net.Build.Extensions; using War3Net.Build.Info; using War3Net.Build.Widget; -using War3Net.CodeAnalysis.Jass.Syntax; - -using SyntaxFactory = War3Net.CodeAnalysis.Jass.JassSyntaxFactory; +using War3Net.CodeAnalysis; +using War3Net.CodeAnalysis.Jass.Extensions; namespace War3Net.Build { public partial class MapScriptBuilder { - protected internal virtual JassFunctionDeclarationSyntax CreateNeutralPassiveBuildings(Map map) + protected internal virtual void GenerateCreateNeutralPassiveBuildings(Map map, IndentedTextWriter writer) { if (map is null) { throw new ArgumentNullException(nameof(map)); } + if (writer is null) + { + throw new ArgumentNullException(nameof(writer)); + } + var mapUnits = map.Units; if (mapUnits is null) { - throw new ArgumentException($"Function '{nameof(CreateNeutralPassiveBuildings)}' cannot be generated without {nameof(MapUnits)}.", nameof(map)); + throw new ArgumentException($"Function '{GeneratedFunctionName.CreateNeutralPassiveBuildings}' cannot be generated without {nameof(MapUnits)}.", nameof(map)); } - return SyntaxFactory.FunctionDeclaration( - SyntaxFactory.FunctionDeclarator(nameof(CreateNeutralPassiveBuildings)), - CreateUnits( - map, - mapUnits.Units.IncludeId().Where(pair => CreateNeutralPassiveBuildingsConditionSingleUnit(map, pair.Obj)), - SyntaxFactory.VariableReferenceExpression(GlobalVariableName.PlayerNeutralPassive))); + writer.WriteFunction(GeneratedFunctionName.CreateNeutralPassiveBuildings); + + GenerateCreateUnits( + map, + mapUnits.Units.IncludeId().Where(pair => ShouldGenerateCreateNeutralPassiveBuildingsForUnit(map, pair.Obj)), + GlobalVariableName.PlayerNeutralPassive, + writer); + + writer.EndFunction(); } - protected internal virtual bool CreateNeutralPassiveBuildingsCondition(Map map) + protected internal virtual bool ShouldGenerateCreateNeutralPassiveBuildings(Map map) { if (map is null) { @@ -53,10 +60,10 @@ protected internal virtual bool CreateNeutralPassiveBuildingsCondition(Map map) } return map.Units is not null - && map.Units.Units.Any(unit => CreateNeutralPassiveBuildingsConditionSingleUnit(map, unit)); + && map.Units.Units.Any(unit => ShouldGenerateCreateNeutralPassiveBuildingsForUnit(map, unit)); } - protected internal virtual bool CreateNeutralPassiveBuildingsConditionSingleUnit(Map map, UnitData unitData) + protected internal virtual bool ShouldGenerateCreateNeutralPassiveBuildingsForUnit(Map map, UnitData unitData) { if (map is null) { diff --git a/src/War3Net.Build/MapScriptBuilder/Widget/CreateNeutralUnits.cs b/src/War3Net.Build/MapScriptBuilder/Widget/CreateNeutralUnits.cs index 634f75fe..95fce222 100644 --- a/src/War3Net.Build/MapScriptBuilder/Widget/CreateNeutralUnits.cs +++ b/src/War3Net.Build/MapScriptBuilder/Widget/CreateNeutralUnits.cs @@ -1,4 +1,4 @@ -// ------------------------------------------------------------------------------ +// ------------------------------------------------------------------------------ // // Licensed under the MIT license. // See the LICENSE file in the project root for more information. @@ -6,40 +6,43 @@ // ------------------------------------------------------------------------------ using System; -using System.Collections.Generic; using War3Net.Build.Info; -using War3Net.CodeAnalysis.Jass.Syntax; - -using SyntaxFactory = War3Net.CodeAnalysis.Jass.JassSyntaxFactory; +using War3Net.CodeAnalysis; +using War3Net.CodeAnalysis.Jass.Extensions; namespace War3Net.Build { public partial class MapScriptBuilder { - protected internal virtual JassFunctionDeclarationSyntax CreateNeutralUnits(Map map) + protected internal virtual void GenerateCreateNeutralUnits(Map map, IndentedTextWriter writer) { if (map is null) { throw new ArgumentNullException(nameof(map)); } - var statements = new List(); + if (writer is null) + { + throw new ArgumentNullException(nameof(writer)); + } + + writer.WriteFunction(GeneratedFunctionName.CreateNeutralUnits); - if (CreateNeutralHostileCondition(map)) + if (ShouldGenerateCreateNeutralHostile(map)) { - statements.Add(SyntaxFactory.CallStatement(nameof(CreateNeutralHostile))); + writer.WriteCall(GeneratedFunctionName.CreateNeutralHostile); } - if (CreateNeutralPassiveCondition(map)) + if (ShouldGenerateCreateNeutralPassive(map)) { - statements.Add(SyntaxFactory.CallStatement(nameof(CreateNeutralPassive))); + writer.WriteCall(GeneratedFunctionName.CreateNeutralPassive); } - return SyntaxFactory.FunctionDeclaration(SyntaxFactory.FunctionDeclarator(nameof(CreateNeutralUnits)), statements); + writer.EndFunction(); } - protected internal virtual bool CreateNeutralUnitsCondition(Map map) + protected internal virtual bool ShouldGenerateCreateNeutralUnits(Map map) { if (map is null) { diff --git a/src/War3Net.Build/MapScriptBuilder/Widget/CreatePlayerBuildings.cs b/src/War3Net.Build/MapScriptBuilder/Widget/CreatePlayerBuildings.cs index e59b20a8..f5194d7d 100644 --- a/src/War3Net.Build/MapScriptBuilder/Widget/CreatePlayerBuildings.cs +++ b/src/War3Net.Build/MapScriptBuilder/Widget/CreatePlayerBuildings.cs @@ -1,4 +1,4 @@ -// ------------------------------------------------------------------------------ +// ------------------------------------------------------------------------------ // // Licensed under the MIT license. // See the LICENSE file in the project root for more information. @@ -6,41 +6,44 @@ // ------------------------------------------------------------------------------ using System; -using System.Collections.Generic; using System.Linq; using War3Net.Build.Extensions; using War3Net.Build.Info; using War3Net.Build.Widget; -using War3Net.CodeAnalysis.Jass.Syntax; - -using SyntaxFactory = War3Net.CodeAnalysis.Jass.JassSyntaxFactory; +using War3Net.CodeAnalysis; +using War3Net.CodeAnalysis.Jass.Extensions; namespace War3Net.Build { public partial class MapScriptBuilder { - protected internal virtual JassFunctionDeclarationSyntax CreatePlayerBuildings(Map map) + protected internal virtual void GenerateCreatePlayerBuildings(Map map, IndentedTextWriter writer) { if (map is null) { throw new ArgumentNullException(nameof(map)); } - var statements = new List(); + if (writer is null) + { + throw new ArgumentNullException(nameof(writer)); + } + + writer.WriteFunction(GeneratedFunctionName.CreatePlayerBuildings); for (var i = 0; i < MaxPlayerSlots; i++) { - if (CreateBuildingsForPlayerCondition(map, i)) + if (ShouldGenerateCreateBuildingsForPlayer(map, i)) { - statements.Add(SyntaxFactory.CallStatement(nameof(CreateBuildingsForPlayer) + i)); + writer.WriteCall(GeneratedFunctionName.CreateBuildingsForPlayer(i)); } } - return SyntaxFactory.FunctionDeclaration(SyntaxFactory.FunctionDeclarator(nameof(CreatePlayerBuildings)), statements); + writer.EndFunction(); } - protected internal virtual bool CreatePlayerBuildingsCondition(Map map) + protected internal virtual bool ShouldGenerateCreatePlayerBuildings(Map map) { if (map is null) { @@ -53,10 +56,10 @@ protected internal virtual bool CreatePlayerBuildingsCondition(Map map) } return map.Units is not null - && map.Units.Units.Any(unit => CreatePlayerBuildingsConditionSingleUnit(map, unit)); + && map.Units.Units.Any(unit => ShouldGenerateCreatePlayerBuildingsForUnit(map, unit)); } - protected internal virtual bool CreatePlayerBuildingsConditionSingleUnit(Map map, UnitData unitData) + protected internal virtual bool ShouldGenerateCreatePlayerBuildingsForUnit(Map map, UnitData unitData) { if (map is null) { diff --git a/src/War3Net.Build/MapScriptBuilder/Widget/CreatePlayerUnits.cs b/src/War3Net.Build/MapScriptBuilder/Widget/CreatePlayerUnits.cs index fa5fece5..6db895e8 100644 --- a/src/War3Net.Build/MapScriptBuilder/Widget/CreatePlayerUnits.cs +++ b/src/War3Net.Build/MapScriptBuilder/Widget/CreatePlayerUnits.cs @@ -1,4 +1,4 @@ -// ------------------------------------------------------------------------------ +// ------------------------------------------------------------------------------ // // Licensed under the MIT license. // See the LICENSE file in the project root for more information. @@ -6,41 +6,44 @@ // ------------------------------------------------------------------------------ using System; -using System.Collections.Generic; using System.Linq; using War3Net.Build.Extensions; using War3Net.Build.Info; using War3Net.Build.Widget; -using War3Net.CodeAnalysis.Jass.Syntax; - -using SyntaxFactory = War3Net.CodeAnalysis.Jass.JassSyntaxFactory; +using War3Net.CodeAnalysis; +using War3Net.CodeAnalysis.Jass.Extensions; namespace War3Net.Build { public partial class MapScriptBuilder { - protected internal virtual JassFunctionDeclarationSyntax CreatePlayerUnits(Map map) + protected internal virtual void GenerateCreatePlayerUnits(Map map, IndentedTextWriter writer) { if (map is null) { throw new ArgumentNullException(nameof(map)); } - var statements = new List(); + if (writer is null) + { + throw new ArgumentNullException(nameof(writer)); + } + + writer.WriteFunction(GeneratedFunctionName.CreatePlayerUnits); for (var i = 0; i < MaxPlayerSlots; i++) { - if (CreateUnitsForPlayerCondition(map, i)) + if (ShouldGenerateCreateUnitsForPlayer(map, i)) { - statements.Add(SyntaxFactory.CallStatement(nameof(CreateUnitsForPlayer) + i)); + writer.WriteCall(GeneratedFunctionName.CreateUnitsForPlayer(i)); } } - return SyntaxFactory.FunctionDeclaration(SyntaxFactory.FunctionDeclarator(nameof(CreatePlayerUnits)), statements); + writer.EndFunction(); } - protected internal virtual bool CreatePlayerUnitsCondition(Map map) + protected internal virtual bool ShouldGenerateCreatePlayerUnits(Map map) { if (map is null) { diff --git a/src/War3Net.Build/MapScriptBuilder/Widget/CreateUnits.cs b/src/War3Net.Build/MapScriptBuilder/Widget/CreateUnits.cs index c3e22938..8e976325 100644 --- a/src/War3Net.Build/MapScriptBuilder/Widget/CreateUnits.cs +++ b/src/War3Net.Build/MapScriptBuilder/Widget/CreateUnits.cs @@ -1,4 +1,4 @@ -// ------------------------------------------------------------------------------ +// ------------------------------------------------------------------------------ // // Licensed under the MIT license. // See the LICENSE file in the project root for more information. @@ -9,19 +9,20 @@ using System.Collections.Generic; using System.Linq; +using War3Net.Build.Common; using War3Net.Build.Extensions; using War3Net.Build.Info; using War3Net.Build.Providers; using War3Net.Build.Widget; -using War3Net.CodeAnalysis.Jass.Syntax; - -using SyntaxFactory = War3Net.CodeAnalysis.Jass.JassSyntaxFactory; +using War3Net.CodeAnalysis; +using War3Net.CodeAnalysis.Jass; +using War3Net.CodeAnalysis.Jass.Extensions; namespace War3Net.Build { public partial class MapScriptBuilder { - protected internal virtual IEnumerable CreateUnits(Map map, IEnumerable<(UnitData Unit, int Id)> units, IExpressionSyntax playerNumber) + protected internal virtual void GenerateCreateUnits(Map map, IEnumerable<(UnitData Unit, int Id)> units, string playerNumberExpression, IndentedTextWriter writer) { if (map is null) { @@ -33,26 +34,34 @@ protected internal virtual IEnumerable CreateUnits(Map map, IE throw new ArgumentNullException(nameof(units)); } - if (playerNumber is null) + if (playerNumberExpression is null) + { + throw new ArgumentNullException(nameof(playerNumberExpression)); + } + + if (writer is null) { - throw new ArgumentNullException(nameof(playerNumber)); + throw new ArgumentNullException(nameof(writer)); } - var statements = new List(); - statements.Add(SyntaxFactory.LocalVariableDeclarationStatement(SyntaxFactory.ParseTypeName(TypeName.Player), VariableName.Player, SyntaxFactory.InvocationExpression(NativeName.Player, playerNumber))); + writer.WriteLocal( + TypeName.Player, + VariableName.Player, + JassExpression.Invoke(NativeName.Player, playerNumberExpression)); + if (!ForceGenerateGlobalUnitVariable) { - statements.Add(SyntaxFactory.LocalVariableDeclarationStatement(SyntaxFactory.ParseTypeName(TypeName.Unit), VariableName.Unit)); + writer.WriteLocal(TypeName.Unit, VariableName.Unit); } - statements.Add(SyntaxFactory.LocalVariableDeclarationStatement(JassTypeSyntax.Integer, VariableName.UnitId)); - statements.Add(SyntaxFactory.LocalVariableDeclarationStatement(SyntaxFactory.ParseTypeName(TypeName.Trigger), VariableName.Trigger)); + writer.WriteLocal(JassKeyword.Integer, VariableName.UnitId); + writer.WriteLocal(TypeName.Trigger, VariableName.Trigger); if (UseLifeVariable) { - statements.Add(SyntaxFactory.LocalVariableDeclarationStatement(JassTypeSyntax.Real, VariableName.Life)); + writer.WriteLocal(JassKeyword.Real, VariableName.Life); } - statements.Add(JassEmptySyntax.Value); + writer.WriteLine(); foreach (var (unit, id) in units.OrderBy(pair => pair.Unit.CreationNumber)) { @@ -70,83 +79,84 @@ protected internal virtual IEnumerable CreateUnits(Map map, IE case RandomUnitAny randomUnitAny: if (unit.IsRandomBuilding()) { - statements.Add(SyntaxFactory.SetStatement( + writer.WriteSet( VariableName.UnitId, - SyntaxFactory.InvocationExpression(NativeName.ChooseRandomNPBuilding))); + JassExpression.InvokeSpaced(NativeName.ChooseRandomNPBuilding)); } else { - statements.Add(SyntaxFactory.SetStatement( + writer.WriteSet( VariableName.UnitId, - SyntaxFactory.InvocationExpression(NativeName.ChooseRandomCreep, SyntaxFactory.LiteralExpression(randomUnitAny.Level)))); + JassExpression.InvokeSpaced(NativeName.ChooseRandomCreep, JassLiteral.Int(randomUnitAny.Level))); } break; case RandomUnitGlobalTable randomUnitGlobalTable: - statements.Add(SyntaxFactory.SetStatement( + writer.WriteSet( VariableName.UnitId, - SyntaxFactory.ArrayReferenceExpression(randomUnitGlobalTable.GetVariableName(), SyntaxFactory.LiteralExpression(randomUnitGlobalTable.Column)))); + JassExpression.ElementAccess(randomUnitGlobalTable.GetVariableName(), randomUnitGlobalTable.Column)); break; case RandomUnitCustomTable randomUnitCustomTable: - statements.Add(SyntaxFactory.CallStatement(FunctionName.RandomDistReset)); + writer.WriteCall(FunctionName.RandomDistReset); var summedChance = 0; foreach (var randomUnit in randomUnitCustomTable.RandomUnits) { - statements.Add(SyntaxFactory.CallStatement( + writer.WriteCall( FunctionName.RandomDistAddItem, RandomUnitProvider.IsRandomUnit(randomUnit.UnitId, out var level) - ? SyntaxFactory.InvocationExpression(NativeName.ChooseRandomCreep, SyntaxFactory.LiteralExpression(level)) - : SyntaxFactory.FourCCLiteralExpression(randomUnit.UnitId), - SyntaxFactory.LiteralExpression(randomUnit.Chance))); + ? JassExpression.Invoke(NativeName.ChooseRandomCreep, JassLiteral.Int(level)) + : JassLiteral.FourCC(randomUnit.UnitId), + JassLiteral.Int(randomUnit.Chance)); summedChance += randomUnit.Chance; } if (summedChance < 100) { - statements.Add(SyntaxFactory.CallStatement( + writer.WriteCall( FunctionName.RandomDistAddItem, - SyntaxFactory.LiteralExpression(-1), - SyntaxFactory.LiteralExpression(100 - summedChance))); + "-1", + JassLiteral.Int(100 - summedChance)); } - statements.Add(SyntaxFactory.SetStatement( + writer.WriteSet( VariableName.UnitId, - SyntaxFactory.InvocationExpression(FunctionName.RandomDistChoose))); + JassExpression.InvokeSpaced(FunctionName.RandomDistChoose)); break; } - var ifBodyStatements = new List(); - ifBodyStatements.Add(SyntaxFactory.SetStatement( + writer.WriteIf(JassExpression.Parenthesized(JassExpression.NotEqual( + VariableName.UnitId, + "-1"))); + + writer.WriteSet( unitVariableName, - SyntaxFactory.InvocationExpression( + JassExpression.InvokeSpaced( NativeName.CreateUnit, - SyntaxFactory.VariableReferenceExpression(VariableName.Player), - SyntaxFactory.VariableReferenceExpression(VariableName.UnitId), - SyntaxFactory.LiteralExpression(unit.Position.X, precision: 1), - SyntaxFactory.LiteralExpression(unit.Position.Y, precision: 1), - SyntaxFactory.LiteralExpression(unit.Rotation * (180f / MathF.PI), precision: 3)))); + VariableName.Player, + VariableName.UnitId, + JassLiteral.Real(unit.Position.X), + JassLiteral.Real(unit.Position.Y), + JassLiteral.Real(unit.Rotation * W3MathF.Rad2Deg, 3))); - ifBodyStatements.AddRange(GetCreateUnitStatements(map, unit, id)); + WriteCreateUnitStatements(map, unit, id, writer); - statements.Add(SyntaxFactory.IfStatement( - new JassParenthesizedExpressionSyntax(SyntaxFactory.BinaryNotEqualsExpression(SyntaxFactory.VariableReferenceExpression(VariableName.UnitId), SyntaxFactory.LiteralExpression(-1))), - ifBodyStatements.ToArray())); + writer.WriteEndIf(); } else { - var args = new List() + var args = new List { - SyntaxFactory.VariableReferenceExpression(VariableName.Player), - SyntaxFactory.FourCCLiteralExpression(unit.TypeId), - SyntaxFactory.LiteralExpression(unit.Position.X, precision: 1), - SyntaxFactory.LiteralExpression(unit.Position.Y, precision: 1), - SyntaxFactory.LiteralExpression(unit.Rotation * (180f / MathF.PI), precision: 3), + VariableName.Player, + JassLiteral.FourCC(unit.TypeId), + JassLiteral.Real(unit.Position.X), + JassLiteral.Real(unit.Position.Y), + JassLiteral.Real(unit.Rotation * W3MathF.Rad2Deg, 3), }; var skinId = unit.SkinId == 0 ? unit.TypeId : unit.SkinId; @@ -154,57 +164,57 @@ protected internal virtual IEnumerable CreateUnits(Map map, IE var hasSkin = ForceGenerateUnitWithSkin || skinId != unit.TypeId; if (hasSkin) { - args.Add(SyntaxFactory.FourCCLiteralExpression(skinId)); + args.Add(JassLiteral.FourCC(skinId)); } - statements.Add(SyntaxFactory.SetStatement( + writer.WriteSet( unitVariableName, - SyntaxFactory.InvocationExpression(hasSkin ? NativeName.BlzCreateUnitWithSkin : NativeName.CreateUnit, args.ToArray()))); + JassExpression.InvokeSpaced( + hasSkin ? NativeName.BlzCreateUnitWithSkin : NativeName.CreateUnit, + args.ToArray())); if (unit.HeroLevel > 1) { - statements.Add(SyntaxFactory.CallStatement( + writer.WriteCall( NativeName.SetHeroLevel, - SyntaxFactory.VariableReferenceExpression(unitVariableName), - SyntaxFactory.LiteralExpression(unit.HeroLevel), - SyntaxFactory.LiteralExpression(false))); + unitVariableName, + JassLiteral.Int(unit.HeroLevel), + JassKeyword.False); } if (unit.HeroStrength > 0) { - statements.Add(SyntaxFactory.CallStatement( + writer.WriteCall( NativeName.SetHeroStr, - SyntaxFactory.VariableReferenceExpression(unitVariableName), - SyntaxFactory.LiteralExpression(unit.HeroStrength), - SyntaxFactory.LiteralExpression(true))); + unitVariableName, + JassLiteral.Int(unit.HeroStrength), + JassKeyword.True); } if (unit.HeroAgility > 0) { - statements.Add(SyntaxFactory.CallStatement( + writer.WriteCall( NativeName.SetHeroAgi, - SyntaxFactory.VariableReferenceExpression(unitVariableName), - SyntaxFactory.LiteralExpression(unit.HeroAgility), - SyntaxFactory.LiteralExpression(true))); + unitVariableName, + JassLiteral.Int(unit.HeroAgility), + JassKeyword.True); } if (unit.HeroIntelligence > 0) { - statements.Add(SyntaxFactory.CallStatement( + writer.WriteCall( NativeName.SetHeroInt, - SyntaxFactory.VariableReferenceExpression(unitVariableName), - SyntaxFactory.LiteralExpression(unit.HeroIntelligence), - SyntaxFactory.LiteralExpression(true))); + unitVariableName, + JassLiteral.Int(unit.HeroIntelligence), + JassKeyword.True); } - statements.AddRange(GetCreateUnitStatements(map, unit, id)); + WriteCreateUnitStatements(map, unit, id, writer); } } - - return statements; } - protected internal virtual IEnumerable GetCreateUnitStatements(Map map, UnitData unit, int id) + protected internal virtual void WriteCreateUnitStatements(Map map, UnitData unit, int id, IndentedTextWriter writer) { if (map is null) { @@ -216,9 +226,10 @@ protected internal virtual IEnumerable GetCreateUnitStatements throw new ArgumentNullException(nameof(unit)); } - var randomItemTables = map.Info?.RandomItemTables; - - var statements = new List(); + if (writer is null) + { + throw new ArgumentNullException(nameof(writer)); + } var unitVariableName = unit.GetVariableName(); if (!ForceGenerateGlobalUnitVariable && (!TriggerVariableReferences.TryGetValue(unitVariableName, out var value) || !value)) @@ -228,53 +239,55 @@ protected internal virtual IEnumerable GetCreateUnitStatements if (unit.HP != -1) { + var lifePercentLiteral = JassLiteral.Real(unit.HP * 0.01f, 2); + if (UseLifeVariable) { - statements.Add(SyntaxFactory.SetStatement( + writer.WriteSet( VariableName.Life, - SyntaxFactory.InvocationExpression( + JassExpression.InvokeSpaced( NativeName.GetUnitState, - SyntaxFactory.VariableReferenceExpression(unitVariableName), - SyntaxFactory.VariableReferenceExpression(UnitStateName.Life)))); + unitVariableName, + UnitStateName.Life)); - statements.Add(SyntaxFactory.CallStatement( + writer.WriteCall( NativeName.SetUnitState, - SyntaxFactory.VariableReferenceExpression(unitVariableName), - SyntaxFactory.VariableReferenceExpression(UnitStateName.Life), - SyntaxFactory.BinaryMultiplicationExpression( - SyntaxFactory.LiteralExpression(unit.HP * 0.01f, precision: 2), - SyntaxFactory.VariableReferenceExpression(VariableName.Life)))); + unitVariableName, + UnitStateName.Life, + JassExpression.Multiply( + lifePercentLiteral, + VariableName.Life)); } else { - statements.Add(SyntaxFactory.CallStatement( + writer.WriteCall( NativeName.SetUnitState, - SyntaxFactory.VariableReferenceExpression(unitVariableName), - SyntaxFactory.VariableReferenceExpression(UnitStateName.Life), - SyntaxFactory.BinaryMultiplicationExpression( - SyntaxFactory.LiteralExpression(unit.HP * 0.01f, precision: 2), - SyntaxFactory.InvocationExpression( + unitVariableName, + UnitStateName.Life, + JassExpression.Multiply( + lifePercentLiteral, + JassExpression.Invoke( NativeName.GetUnitState, - SyntaxFactory.VariableReferenceExpression(unitVariableName), - SyntaxFactory.VariableReferenceExpression(UnitStateName.Life))))); + unitVariableName, + UnitStateName.Life))); } } if (unit.MP != -1) { - statements.Add(SyntaxFactory.CallStatement( + writer.WriteCall( NativeName.SetUnitState, - SyntaxFactory.VariableReferenceExpression(unitVariableName), - SyntaxFactory.VariableReferenceExpression(UnitStateName.Mana), - SyntaxFactory.LiteralExpression(unit.MP))); + unitVariableName, + UnitStateName.Mana, + JassLiteral.Int(unit.MP)); } if (unit.IsGoldMine()) { - statements.Add(SyntaxFactory.CallStatement( + writer.WriteCall( NativeName.SetResourceAmount, - SyntaxFactory.VariableReferenceExpression(unitVariableName), - SyntaxFactory.LiteralExpression(unit.GoldAmount))); + unitVariableName, + JassLiteral.Int(unit.GoldAmount)); } var playerColorId = unit.CustomPlayerColorId; @@ -285,19 +298,20 @@ protected internal virtual IEnumerable GetCreateUnitStatements if (playerColorId != -1) { - statements.Add(SyntaxFactory.CallStatement( + writer.WriteCall( NativeName.SetUnitColor, - SyntaxFactory.VariableReferenceExpression(unitVariableName), - SyntaxFactory.InvocationExpression(NativeName.ConvertPlayerColor, SyntaxFactory.LiteralExpression(playerColorId)))); + unitVariableName, + JassExpression.Invoke(NativeName.ConvertPlayerColor, JassLiteral.Int(playerColorId))); } if (unit.TargetAcquisition != -1f) { const float CampAcquisitionRange = 200f; - statements.Add(SyntaxFactory.CallStatement( + var acquisitionRange = unit.TargetAcquisition == -2f ? CampAcquisitionRange : unit.TargetAcquisition; + writer.WriteCall( NativeName.SetUnitAcquireRange, - SyntaxFactory.VariableReferenceExpression(unitVariableName), - SyntaxFactory.LiteralExpression(unit.TargetAcquisition == -2f ? CampAcquisitionRange : unit.TargetAcquisition, precision: 1))); + unitVariableName, + JassLiteral.Real(acquisitionRange)); } if (unit.WaygateDestinationRegionId != -1) @@ -305,16 +319,16 @@ protected internal virtual IEnumerable GetCreateUnitStatements var destinationRect = map.Regions?.Regions.Where(region => region.CreationNumber == unit.WaygateDestinationRegionId).SingleOrDefault(); if (destinationRect is not null) { - statements.Add(SyntaxFactory.CallStatement( + writer.WriteCall( NativeName.WaygateSetDestination, - SyntaxFactory.VariableReferenceExpression(unitVariableName), - SyntaxFactory.LiteralExpression(destinationRect.CenterX), - SyntaxFactory.LiteralExpression(destinationRect.CenterY))); + unitVariableName, + JassLiteral.Real(destinationRect.CenterX, 0), + JassLiteral.Real(destinationRect.CenterY, 0)); - statements.Add(SyntaxFactory.CallStatement( + writer.WriteCall( NativeName.WaygateActivate, - SyntaxFactory.VariableReferenceExpression(unitVariableName), - SyntaxFactory.LiteralExpression(true))); + unitVariableName, + JassKeyword.True); } } @@ -322,64 +336,64 @@ protected internal virtual IEnumerable GetCreateUnitStatements { for (var i = 0; i < ability.HeroAbilityLevel; i++) { - statements.Add(SyntaxFactory.CallStatement( + writer.WriteCall( NativeName.SelectHeroSkill, - SyntaxFactory.VariableReferenceExpression(unitVariableName), - SyntaxFactory.FourCCLiteralExpression(ability.AbilityId))); + unitVariableName, + JassLiteral.FourCC(ability.AbilityId)); } if (ability.IsAutocastActive) { - statements.Add(SyntaxFactory.CallStatement( + writer.WriteCall( NativeName.IssueImmediateOrderById, - SyntaxFactory.VariableReferenceExpression(unitVariableName), - SyntaxFactory.FourCCLiteralExpression(ability.AbilityId))); + unitVariableName, + JassLiteral.FourCC(ability.AbilityId)); } if (ability.TryGetOrderOffString(out var orderOffString)) { - statements.Add(SyntaxFactory.CallStatement( + writer.WriteCall( NativeName.IssueImmediateOrder, - SyntaxFactory.VariableReferenceExpression(unitVariableName), - SyntaxFactory.LiteralExpression(orderOffString))); + unitVariableName, + JassLiteral.String(orderOffString)); } } foreach (var item in unit.InventoryData) { - statements.Add(SyntaxFactory.CallStatement( + writer.WriteCall( NativeName.UnitAddItemToSlotById, - SyntaxFactory.VariableReferenceExpression(unitVariableName), - SyntaxFactory.FourCCLiteralExpression(item.ItemId), - SyntaxFactory.LiteralExpression(item.Slot))); + unitVariableName, + JassLiteral.FourCC(item.ItemId), + JassLiteral.Int(item.Slot)); } if (unit.HasItemTable()) { - statements.Add(SyntaxFactory.SetStatement(VariableName.Trigger, SyntaxFactory.InvocationExpression(NativeName.CreateTrigger))); + writer.WriteSet( + VariableName.Trigger, + JassExpression.InvokeSpaced(NativeName.CreateTrigger)); - statements.Add(SyntaxFactory.CallStatement( + writer.WriteCall( NativeName.TriggerRegisterUnitEvent, - SyntaxFactory.VariableReferenceExpression(VariableName.Trigger), - SyntaxFactory.VariableReferenceExpression(unitVariableName), - SyntaxFactory.VariableReferenceExpression(UnitEventName.Death))); + VariableName.Trigger, + unitVariableName, + UnitEventName.Death); if (map.Info is null || map.Info.FormatVersion >= MapInfoFormatVersion.v24) { - statements.Add(SyntaxFactory.CallStatement( + writer.WriteCall( NativeName.TriggerRegisterUnitEvent, - SyntaxFactory.VariableReferenceExpression(VariableName.Trigger), - SyntaxFactory.VariableReferenceExpression(unitVariableName), - SyntaxFactory.VariableReferenceExpression(UnitEventName.ChangeOwner))); + VariableName.Trigger, + unitVariableName, + UnitEventName.ChangeOwner); } - statements.Add(SyntaxFactory.CallStatement( + writer.WriteCall( NativeName.TriggerAddAction, - SyntaxFactory.VariableReferenceExpression(VariableName.Trigger), - SyntaxFactory.FunctionReferenceExpression(unit.GetDropItemsFunctionName(id)))); + VariableName.Trigger, + JassExpression.FunctionRef(unit.GetDropItemsFunctionName(id))); } - - return statements; } } } \ No newline at end of file diff --git a/src/War3Net.Build/MapScriptBuilder/Widget/CreateUnitsForPlayer.cs b/src/War3Net.Build/MapScriptBuilder/Widget/CreateUnitsForPlayer.cs index bb2dec50..8d4855f8 100644 --- a/src/War3Net.Build/MapScriptBuilder/Widget/CreateUnitsForPlayer.cs +++ b/src/War3Net.Build/MapScriptBuilder/Widget/CreateUnitsForPlayer.cs @@ -10,38 +10,46 @@ using War3Net.Build.Extensions; using War3Net.Build.Widget; -using War3Net.CodeAnalysis.Jass.Syntax; - -using SyntaxFactory = War3Net.CodeAnalysis.Jass.JassSyntaxFactory; +using War3Net.CodeAnalysis; +using War3Net.CodeAnalysis.Jass; +using War3Net.CodeAnalysis.Jass.Extensions; namespace War3Net.Build { public partial class MapScriptBuilder { - protected internal virtual JassFunctionDeclarationSyntax CreateUnitsForPlayer(Map map, int playerId) + protected internal virtual void GenerateCreateUnitsForPlayer(Map map, int playerId, IndentedTextWriter writer) { - var functionName = nameof(CreateUnitsForPlayer) + playerId; - if (map is null) { throw new ArgumentNullException(nameof(map)); } + if (writer is null) + { + throw new ArgumentNullException(nameof(writer)); + } + + var functionName = GeneratedFunctionName.CreateUnitsForPlayer(playerId); + var mapUnits = map.Units; if (mapUnits is null) { throw new ArgumentException($"Function '{functionName}' cannot be generated without {nameof(MapUnits)}.", nameof(map)); } - return SyntaxFactory.FunctionDeclaration( - SyntaxFactory.FunctionDeclarator(functionName), - CreateUnits( - map, - mapUnits.Units.IncludeId().Where(pair => CreateUnitsForPlayerConditionSingleUnit(map, playerId, pair.Obj)), - SyntaxFactory.LiteralExpression(playerId))); + writer.WriteFunction(functionName); + + GenerateCreateUnits( + map, + mapUnits.Units.IncludeId().Where(pair => ShouldGenerateCreateUnitsForPlayerAndUnit(map, playerId, pair.Obj)), + JassLiteral.Int(playerId), + writer); + + writer.EndFunction(); } - protected internal virtual bool CreateUnitsForPlayerCondition(Map map, int playerId) + protected internal virtual bool ShouldGenerateCreateUnitsForPlayer(Map map, int playerId) { if (map is null) { @@ -49,10 +57,10 @@ protected internal virtual bool CreateUnitsForPlayerCondition(Map map, int playe } return map.Units is not null - && map.Units.Units.Any(unit => CreateUnitsForPlayerConditionSingleUnit(map, playerId, unit)); + && map.Units.Units.Any(unit => ShouldGenerateCreateUnitsForPlayerAndUnit(map, playerId, unit)); } - protected internal virtual bool CreateUnitsForPlayerConditionSingleUnit(Map map, int playerId, UnitData unitData) + protected internal virtual bool ShouldGenerateCreateUnitsForPlayerAndUnit(Map map, int playerId, UnitData unitData) { if (map is null) { diff --git a/src/War3Net.Build/MapScriptBuilder/Widget/DestructableItemTables.cs b/src/War3Net.Build/MapScriptBuilder/Widget/DestructableItemTables.cs index 18395c77..eda0576d 100644 --- a/src/War3Net.Build/MapScriptBuilder/Widget/DestructableItemTables.cs +++ b/src/War3Net.Build/MapScriptBuilder/Widget/DestructableItemTables.cs @@ -1,4 +1,4 @@ -// ------------------------------------------------------------------------------ +// ------------------------------------------------------------------------------ // // Licensed under the MIT license. // See the LICENSE file in the project root for more information. @@ -6,41 +6,47 @@ // ------------------------------------------------------------------------------ using System; -using System.Collections.Generic; using System.Linq; using War3Net.Build.Extensions; using War3Net.Build.Widget; -using War3Net.CodeAnalysis.Jass.Syntax; +using War3Net.CodeAnalysis; namespace War3Net.Build { public partial class MapScriptBuilder { - protected internal virtual IEnumerable DestructableItemTables(Map map) + protected internal virtual void GenerateDestructableItemTables(Map map, IndentedTextWriter writer) { if (map is null) { throw new ArgumentNullException(nameof(map)); } + if (writer is null) + { + throw new ArgumentNullException(nameof(writer)); + } + var mapDoodads = map.Doodads; if (mapDoodads is null) { - throw new ArgumentException($"Function '{nameof(DestructableItemTables)}' cannot be generated without {nameof(MapDoodads)}.", nameof(map)); + throw new ArgumentException($"DropItems functions cannot be generated without {nameof(MapDoodads)}.", nameof(map)); } for (var i = 0; i < mapDoodads.Doodads.Count; i++) { var doodad = mapDoodads.Doodads[i]; - if (DestructableItemTablesConditionSingleDoodad(map, doodad)) + if (ShouldGenerateDestructableItemTablesForDoodad(map, doodad)) { - yield return ItemTableDropItems(map, doodad, i); + GenerateItemTableDropItems(map, doodad, i, writer); } } + + writer.WriteLine(); } - protected internal virtual bool DestructableItemTablesCondition(Map map) + protected internal virtual bool ShouldGenerateDestructableItemTables(Map map) { if (map is null) { @@ -48,10 +54,10 @@ protected internal virtual bool DestructableItemTablesCondition(Map map) } return map.Doodads is not null - && map.Doodads.Doodads.Any(doodad => DestructableItemTablesConditionSingleDoodad(map, doodad)); + && map.Doodads.Doodads.Any(doodad => ShouldGenerateDestructableItemTablesForDoodad(map, doodad)); } - protected internal virtual bool DestructableItemTablesConditionSingleDoodad(Map map, DoodadData doodadData) + protected internal virtual bool ShouldGenerateDestructableItemTablesForDoodad(Map map, DoodadData doodadData) { if (map is null) { diff --git a/src/War3Net.Build/MapScriptBuilder/Widget/Destructables.cs b/src/War3Net.Build/MapScriptBuilder/Widget/Destructables.cs index 54402493..e5aea8f0 100644 --- a/src/War3Net.Build/MapScriptBuilder/Widget/Destructables.cs +++ b/src/War3Net.Build/MapScriptBuilder/Widget/Destructables.cs @@ -6,55 +6,66 @@ // ------------------------------------------------------------------------------ using System; -using System.Collections.Generic; using System.Linq; -using Microsoft.CodeAnalysis.CSharp.Syntax; - using War3Net.Build.Extensions; -using War3Net.CodeAnalysis.Jass.Syntax; -using War3Net.CodeAnalysis.Transpilers; - -using SyntaxFactory = War3Net.CodeAnalysis.Jass.JassSyntaxFactory; +using War3Net.Build.Widget; +using War3Net.CodeAnalysis; +using War3Net.CodeAnalysis.Jass; +using War3Net.CodeAnalysis.Jass.Extensions; namespace War3Net.Build { public partial class MapScriptBuilder { - public virtual IEnumerable DestructablesApi(Map map, JassToCSharpTranspiler transpiler) + protected internal virtual void GenerateDestructableVariables(Map map, IndentedTextWriter writer) { - if (transpiler is null) + if (map is null) { - throw new ArgumentNullException(nameof(transpiler)); + throw new ArgumentNullException(nameof(map)); } - return Destructables(map).Select(destructable => transpiler.Transpile(destructable)); + if (writer is null) + { + throw new ArgumentNullException(nameof(writer)); + } + + var mapDoodads = map.Doodads; + if (mapDoodads is null) + { + return; + } + + foreach (var destructable in mapDoodads.Doodads.Where(ShouldGenerateDestructableVariable)) + { + writer.WriteAlignedGlobal( + TypeName.Destructable, + destructable.GetVariableName(), + JassKeyword.Null); + } } - protected internal virtual IEnumerable Destructables(Map map) + protected internal virtual bool ShouldGenerateDestructableVariables(Map map) { if (map is null) { throw new ArgumentNullException(nameof(map)); } - var mapDoodads = map.Doodads; - if (mapDoodads is null) - { - yield break; - } + return map.Doodads is not null + && map.Doodads.Doodads.Any(ShouldGenerateDestructableVariable); + } - foreach (var destructable in mapDoodads.Doodads.Where(destructable => CreateAllDestructablesConditionSingleDoodad(map, destructable))) + protected internal virtual bool ShouldGenerateDestructableVariable(DoodadData doodadData) + { + if (doodadData is null) { - var destructableVariableName = destructable.GetVariableName(); - if (ForceGenerateGlobalDestructableVariable || TriggerVariableReferences.ContainsKey(destructableVariableName)) - { - yield return SyntaxFactory.GlobalDeclaration( - SyntaxFactory.ParseTypeName(TypeName.Destructable), - destructableVariableName, - JassNullLiteralExpressionSyntax.Value); - } + throw new ArgumentNullException(nameof(doodadData)); } + + return ForceGenerateGlobalDestructableVariable + || (TriggerVariableReferences.TryGetValue(doodadData.GetVariableName(), out var value) && value) + || doodadData.HasItemTable(); } } } \ No newline at end of file diff --git a/src/War3Net.Build/MapScriptBuilder/Widget/UnitItemTables.cs b/src/War3Net.Build/MapScriptBuilder/Widget/UnitItemTables.cs index 81fd2150..629c3195 100644 --- a/src/War3Net.Build/MapScriptBuilder/Widget/UnitItemTables.cs +++ b/src/War3Net.Build/MapScriptBuilder/Widget/UnitItemTables.cs @@ -1,4 +1,4 @@ -// ------------------------------------------------------------------------------ +// ------------------------------------------------------------------------------ // // Licensed under the MIT license. // See the LICENSE file in the project root for more information. @@ -6,41 +6,47 @@ // ------------------------------------------------------------------------------ using System; -using System.Collections.Generic; using System.Linq; using War3Net.Build.Extensions; using War3Net.Build.Widget; -using War3Net.CodeAnalysis.Jass.Syntax; +using War3Net.CodeAnalysis; namespace War3Net.Build { public partial class MapScriptBuilder { - protected internal virtual IEnumerable UnitItemTables(Map map) + protected internal virtual void GenerateUnitItemTables(Map map, IndentedTextWriter writer) { if (map is null) { throw new ArgumentNullException(nameof(map)); } + if (writer is null) + { + throw new ArgumentNullException(nameof(writer)); + } + var mapUnits = map.Units; if (mapUnits is null) { - throw new ArgumentException($"Function '{nameof(UnitItemTables)}' cannot be generated without {nameof(MapUnits)}.", nameof(map)); + throw new ArgumentException($"DropItems functions cannot be generated without {nameof(MapUnits)}.", nameof(map)); } for (var i = 0; i < mapUnits.Units.Count; i++) { var unit = mapUnits.Units[i]; - if (UnitItemTablesConditionSingleUnit(map, unit)) + if (ShouldGenerateUnitItemTablesForUnit(map, unit)) { - yield return ItemTableDropItems(map, unit, i); + GenerateItemTableDropItems(map, unit, i, writer); } } + + writer.WriteLine(); } - protected internal virtual bool UnitItemTablesCondition(Map map) + protected internal virtual bool ShouldGenerateUnitItemTables(Map map) { if (map is null) { @@ -48,10 +54,10 @@ protected internal virtual bool UnitItemTablesCondition(Map map) } return map.Units is not null - && map.Units.Units.Any(unit => UnitItemTablesConditionSingleUnit(map, unit)); + && map.Units.Units.Any(unit => ShouldGenerateUnitItemTablesForUnit(map, unit)); } - protected internal virtual bool UnitItemTablesConditionSingleUnit(Map map, UnitData unitData) + protected internal virtual bool ShouldGenerateUnitItemTablesForUnit(Map map, UnitData unitData) { if (map is null) { diff --git a/src/War3Net.Build/MapScriptBuilder/Widget/Units.cs b/src/War3Net.Build/MapScriptBuilder/Widget/Units.cs index aa0d5193..02ee49f1 100644 --- a/src/War3Net.Build/MapScriptBuilder/Widget/Units.cs +++ b/src/War3Net.Build/MapScriptBuilder/Widget/Units.cs @@ -6,55 +6,72 @@ // ------------------------------------------------------------------------------ using System; -using System.Collections.Generic; using System.Linq; -using Microsoft.CodeAnalysis.CSharp.Syntax; - using War3Net.Build.Extensions; -using War3Net.CodeAnalysis.Jass.Syntax; -using War3Net.CodeAnalysis.Transpilers; - -using SyntaxFactory = War3Net.CodeAnalysis.Jass.JassSyntaxFactory; +using War3Net.Build.Widget; +using War3Net.CodeAnalysis; +using War3Net.CodeAnalysis.Jass; +using War3Net.CodeAnalysis.Jass.Extensions; namespace War3Net.Build { public partial class MapScriptBuilder { - public virtual IEnumerable UnitsApi(Map map, JassToCSharpTranspiler transpiler) + protected internal virtual void GenerateUnitVariables(Map map, IndentedTextWriter writer) { - if (transpiler is null) + if (map is null) { - throw new ArgumentNullException(nameof(transpiler)); + throw new ArgumentNullException(nameof(map)); } - return Units(map).Select(unit => transpiler.Transpile(unit)); + if (writer is null) + { + throw new ArgumentNullException(nameof(writer)); + } + + var mapUnits = map.Units; + if (mapUnits is null) + { + return; + } + + foreach (var unit in mapUnits.Units.Where(ShouldGenerateUnitVariable)) + { + writer.WriteAlignedGlobal( + TypeName.Unit, + unit.GetVariableName(), + JassKeyword.Null); + } } - protected internal virtual IEnumerable Units(Map map) + protected internal virtual bool ShouldGenerateUnitVariables(Map map) { if (map is null) { throw new ArgumentNullException(nameof(map)); } - var mapUnits = map.Units; - if (mapUnits is null) + return map.Units is not null + && map.Units.Units.Any(ShouldGenerateUnitVariable); + } + + protected internal virtual bool ShouldGenerateUnitVariable(UnitData unitData) + { + if (unitData is null) { - yield break; + throw new ArgumentNullException(nameof(unitData)); } - foreach (var unit in mapUnits.Units.Where(unit => CreateAllUnitsConditionSingleUnit(map, unit))) + if (!unitData.IsUnit() || unitData.IsPlayerStartLocation()) { - var unitVariableName = unit.GetVariableName(); - if (ForceGenerateGlobalUnitVariable || TriggerVariableReferences.ContainsKey(unitVariableName)) - { - yield return SyntaxFactory.GlobalDeclaration( - SyntaxFactory.ParseTypeName(TypeName.Unit), - unitVariableName, - JassNullLiteralExpressionSyntax.Value); - } + return false; } + + var unitVariableName = unitData.GetVariableName(); + + return ForceGenerateGlobalUnitVariable + || TriggerVariableReferences.ContainsKey(unitVariableName); } } } \ No newline at end of file diff --git a/src/War3Net.Build/TriggerRenderer.cs b/src/War3Net.Build/TriggerRenderer.cs index 7309710d..57a1c1e9 100644 --- a/src/War3Net.Build/TriggerRenderer.cs +++ b/src/War3Net.Build/TriggerRenderer.cs @@ -11,27 +11,24 @@ using System.ComponentModel; using System.IO; using System.Linq; -using System.Text; using War3Net.Build.Extensions; -using War3Net.Build.Providers; using War3Net.Build.Script; +using War3Net.CodeAnalysis; using War3Net.CodeAnalysis.Jass; -using War3Net.CodeAnalysis.Jass.Syntax; - -using SyntaxFactory = War3Net.CodeAnalysis.Jass.JassSyntaxFactory; +using War3Net.CodeAnalysis.Jass.Extensions; namespace War3Net.Build { public partial class TriggerRenderer { - private readonly TextWriter _writer; + private readonly IndentedTextWriter _writer; private readonly TriggerData _triggerData; private readonly ImmutableDictionary _variableTypes; private readonly bool _isLuaTrigger; public TriggerRenderer( - TextWriter writer, + IndentedTextWriter writer, TriggerData triggerData, IEnumerable variables, bool isLuaTrigger = false) @@ -49,14 +46,14 @@ public void RenderTrigger(TriggerDefinition triggerDefinition) throw new ArgumentNullException(nameof(triggerDefinition)); } - var commentLine = new JassCommentSyntax("===========================================================================").ToString(); + var commentLine = "//==========================================================================="; _writer.WriteLine(commentLine); - _writer.WriteLine(new JassCommentSyntax($" Trigger: {triggerDefinition.Name}").ToString()); + _writer.WriteComment($"Trigger: {triggerDefinition.Name}"); if (!string.IsNullOrEmpty(triggerDefinition.Description)) { - _writer.WriteLine(new JassCommentSyntax(string.Empty)); + _writer.WriteLine(JassSymbol.SlashSlash); using var stringReader = new StringReader(triggerDefinition.Description); while (true) @@ -67,7 +64,7 @@ public void RenderTrigger(TriggerDefinition triggerDefinition) break; } - _writer.WriteLine(new JassCommentSyntax($" {line}").ToString()); + _writer.WriteComment(line); } } @@ -88,25 +85,23 @@ public void RenderTrigger(TriggerDefinition triggerDefinition) private void RenderInitTrig(TriggerDefinition triggerDefinition) { - var stringBuilder = new StringBuilder(); - using var stringWriter = new StringWriter(stringBuilder); - var renderer = new JassRenderer(stringWriter); - var triggerVariableName = triggerDefinition.GetVariableName(); - var statements = new List(); + _writer.WriteFunction(triggerDefinition.GetInitTrigFunctionName()); - statements.Add(SyntaxFactory.SetStatement( + _writer.WriteSet( triggerVariableName, - SyntaxFactory.InvocationExpression(WellKnownNatives.CreateTrigger))); + JassExpression.InvokeSpaced(WellKnownNatives.CreateTrigger)); if (!triggerDefinition.IsInitiallyOn) { - statements.Add(SyntaxFactory.CallStatement( + _writer.WriteCall( WellKnownNatives.DisableTrigger, - SyntaxFactory.VariableReferenceExpression(triggerVariableName))); + triggerVariableName); } + var identifierBuilder = new TrigFunctionIdentifierBuilder(triggerDefinition.GetTriggerIdentifierName() + "_Func"); + foreach (var function in triggerDefinition.Functions.Where(function => function.Type == TriggerFunctionType.Event && function.IsEnabled)) { if (string.Equals(function.Name, "MapInitializationEvent", StringComparison.Ordinal)) @@ -114,37 +109,31 @@ private void RenderInitTrig(TriggerDefinition triggerDefinition) continue; } - var stringBuilder2 = new StringBuilder(); - using var stringWriter2 = new StringWriter(stringBuilder2); - var renderer2 = new JassRenderer(stringWriter2); - - var identifierBuilder = new TrigFunctionIdentifierBuilder(triggerDefinition.GetTriggerIdentifierName() + "_Func"); + var arguments = GetParameters(function, identifierBuilder) + .Prepend(triggerVariableName) + .ToArray(); - var context = new TriggerRendererContext(renderer2, identifierBuilder); - - var argumentListBuilder = ImmutableArray.CreateBuilder(); - argumentListBuilder.Add(SyntaxFactory.VariableReferenceExpression(triggerVariableName)); - BuildParameters(function, context, argumentListBuilder); - - statements.Add(SyntaxFactory.CallStatement(function.Name, new JassArgumentListSyntax(argumentListBuilder.ToImmutable()))); + _writer.WriteCall( + function.Name, + arguments); } if (triggerDefinition.Functions.Any(function => function.Type == TriggerFunctionType.Condition && function.IsEnabled)) { - statements.Add(SyntaxFactory.CallStatement( + _writer.WriteCall( WellKnownNatives.TriggerAddCondition, - SyntaxFactory.VariableReferenceExpression(triggerVariableName), - SyntaxFactory.InvocationExpression(WellKnownNatives.Condition, SyntaxFactory.FunctionReferenceExpression(triggerDefinition.GetTrigConditionsFunctionName())))); + triggerVariableName, + JassExpression.InvokeSpaced( + WellKnownNatives.Condition, + JassExpression.FunctionRef(triggerDefinition.GetTrigConditionsFunctionName()))); } - statements.Add(SyntaxFactory.CallStatement( + _writer.WriteCall( WellKnownNatives.TriggerAddAction, - SyntaxFactory.VariableReferenceExpression(triggerVariableName), - SyntaxFactory.FunctionReferenceExpression(triggerDefinition.GetTrigActionsFunctionName()))); - - renderer.Render(SyntaxFactory.FunctionDeclaration(SyntaxFactory.FunctionDeclarator(triggerDefinition.GetInitTrigFunctionName()), statements)); + triggerVariableName, + JassExpression.FunctionRef(triggerDefinition.GetTrigActionsFunctionName())); - _writer.WriteLine(stringBuilder.ToString()); + _writer.EndFunction(); } private ImmutableArray GetArgumentTypes(TriggerFunction function) @@ -167,47 +156,19 @@ private ImmutableArray GetArgumentTypes(TriggerFunction function) return argumentTypes; } - private void BuildParameters(TriggerFunction function, TriggerRendererContext context, ImmutableArray.Builder argumentListBuilder) - { - var argumentTypes = GetArgumentTypes(function); - - for (var i = 0; i < argumentTypes.Length; i++) - { - argumentListBuilder.Add(GetParameter(function.Parameters[i], argumentTypes[i], i, context)); - } - } - - private void BuildParametersSkipLast(TriggerFunction function, TriggerRendererContext context, ImmutableArray.Builder argumentListBuilder) - { - var argumentTypes = GetArgumentTypes(function); - - for (var i = 0; i + 1 < argumentTypes.Length; i++) - { - argumentListBuilder.Add(GetParameter(function.Parameters[i], argumentTypes[i], i, context)); - } - } - - private ImmutableArray.Builder BuildParameters(TriggerFunction function, TriggerRendererContext context) + private IEnumerable GetParameters(TriggerFunction function, TrigFunctionIdentifierBuilder identifierBuilder) { var argumentTypes = GetArgumentTypes(function); - var argumentListBuilder = ImmutableArray.CreateBuilder(); for (var i = 0; i < argumentTypes.Length; i++) { - argumentListBuilder.Add(GetParameter(function.Parameters[i], argumentTypes[i], i, context)); + yield return GetParameter(function.Parameters[i], argumentTypes[i], i, identifierBuilder); } - - return argumentListBuilder; } - private JassArgumentListSyntax GetParameters(TriggerFunction function, TriggerRendererContext context) + private string GetParameter(TriggerFunctionParameter parameter, string type, int parameterIndex, TrigFunctionIdentifierBuilder identifierBuilder) { - return new JassArgumentListSyntax(BuildParameters(function, context).ToImmutable()); - } - - private IExpressionSyntax GetParameter(TriggerFunctionParameter parameter, string type, int parameterIndex, TriggerRendererContext context) - { - context.TrigFunctionIdentifierBuilder.Append(parameterIndex + 1); + identifierBuilder.Append(parameterIndex + 1); try { switch (parameter.Type) @@ -216,19 +177,19 @@ private IExpressionSyntax GetParameter(TriggerFunctionParameter parameter, strin var triggerParam = _triggerData.TriggerParams[parameter.Value]; if (triggerParam.ScriptText.StartsWith('`') && triggerParam.ScriptText.EndsWith('`')) { - return SyntaxFactory.ParseExpression($"\"{triggerParam.ScriptText[1..^1]}\""); + return $"{JassSymbol.DoubleQuoteChar}{triggerParam.ScriptText[1..^1]}{JassSymbol.DoubleQuoteChar}"; } - return SyntaxFactory.ParseExpression(triggerParam.ScriptText); + return triggerParam.ScriptText; case TriggerFunctionParameterType.Variable: - var variableeName = parameter.Value.StartsWith("gg_", StringComparison.Ordinal) + var variableName = parameter.Value.StartsWith("gg_", StringComparison.Ordinal) ? parameter.Value : $"udg_{parameter.Value}"; return parameter.ArrayIndexer is null - ? SyntaxFactory.VariableReferenceExpression(variableeName) - : SyntaxFactory.ArrayReferenceExpression(variableeName, GetParameter(parameter.ArrayIndexer, JassKeyword.Integer, 0, context)); + ? variableName + : JassExpression.ElementAccess(variableName, GetParameter(parameter.ArrayIndexer, JassKeyword.Integer, 0, identifierBuilder)); case TriggerFunctionParameterType.Function: if (parameter.Function is null) @@ -238,10 +199,10 @@ private IExpressionSyntax GetParameter(TriggerFunctionParameter parameter, strin if (type == "boolexpr") { - var conditionFunctionName = context.TrigFunctionIdentifierBuilder.ToString(); - RenderConditionFunction(context.TrigFunctionIdentifierBuilder, conditionFunctionName, parameter); + var conditionFunctionName = identifierBuilder.ToString(); + RenderConditionFunction(identifierBuilder, conditionFunctionName, parameter); - return SyntaxFactory.InvocationExpression(WellKnownNatives.Condition, SyntaxFactory.FunctionReferenceExpression(conditionFunctionName)); + return JassExpression.Invoke(WellKnownNatives.Condition, JassExpression.FunctionRef(conditionFunctionName)); } var scriptName = GetScriptName(parameter.Function); @@ -249,25 +210,32 @@ private IExpressionSyntax GetParameter(TriggerFunctionParameter parameter, strin if (string.Equals(scriptName, "OperatorInt", StringComparison.Ordinal) || string.Equals(scriptName, "OperatorReal", StringComparison.Ordinal)) { - var parameters = GetParameters(parameter.Function, context); - - return SyntaxFactory.ParenthesizedExpression(SyntaxFactory.BinaryExpression( - parameters.Arguments[0], - parameters.Arguments[2], - SyntaxFactory.ParseBinaryOperator(((JassStringLiteralExpressionSyntax)parameters.Arguments[1]).Value))); + var parameters = GetParameters(parameter.Function, identifierBuilder).ToArray(); + + var @operator = parameters[1]; + if (@operator.StartsWith('"') && @operator.EndsWith('"')) + { + @operator = @operator[1..^1]; + } + + return JassExpression.ParenthesizedCompact(JassExpression.Binary( + parameters[0], + @operator, + parameters[2])); } else if (string.Equals(scriptName, "OperatorString", StringComparison.Ordinal)) { - var parameters = GetParameters(parameter.Function, context); + var parameters = GetParameters(parameter.Function, identifierBuilder).ToArray(); - return SyntaxFactory.ParenthesizedExpression(SyntaxFactory.BinaryExpression( - parameters.Arguments[0], - parameters.Arguments[1], - BinaryOperatorType.Add)); + return JassExpression.ParenthesizedCompact(JassExpression.Add( + parameters[0], + parameters[1])); } else { - return SyntaxFactory.InvocationExpression(scriptName, GetParameters(parameter.Function, context)); + return JassExpression.Invoke( + scriptName, + GetParameters(parameter.Function, identifierBuilder).ToArray()); } case TriggerFunctionParameterType.String: @@ -293,15 +261,15 @@ private IExpressionSyntax GetParameter(TriggerFunctionParameter parameter, strin if (knownStringTypes.Contains(type)) { - return SyntaxFactory.ParseExpression($"\"{EscapedStringProvider.GetEscapedString(parameter.Value)}\""); + return JassLiteral.String(parameter.Value); } else if (knownFourCCTypes.Contains(type)) { - return SyntaxFactory.ParseExpression($"'{parameter.Value}'"); + return JassLiteral.FourCC(parameter.Value); } else { - return SyntaxFactory.ParseExpression(parameter.Value); + return parameter.Value; } default: @@ -310,7 +278,7 @@ private IExpressionSyntax GetParameter(TriggerFunctionParameter parameter, strin } finally { - context.TrigFunctionIdentifierBuilder.Remove(); + identifierBuilder.Remove(); } } diff --git a/src/War3Net.Build/TriggerRenderer/Constants/TriggerConditionConstants.cs b/src/War3Net.Build/TriggerRenderer/Constants/TriggerConditionConstants.cs new file mode 100644 index 00000000..4c344cc9 --- /dev/null +++ b/src/War3Net.Build/TriggerRenderer/Constants/TriggerConditionConstants.cs @@ -0,0 +1,21 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +namespace War3Net.Build +{ + public partial class TriggerRenderer + { + // [TriggerConditions] in triggerdata.txt + private static class TriggerConditionConstants + { + internal const string GetBooleanAnd = "GetBooleanAnd"; + internal const string GetBooleanOr = "GetBooleanOr"; + internal const string AndMultiple = "AndMultiple"; + internal const string OrMultiple = "OrMultiple"; + } + } +} \ No newline at end of file diff --git a/src/War3Net.Build/TriggerRenderer/ForLoopRenderer.cs b/src/War3Net.Build/TriggerRenderer/ForLoopRenderer.cs index ebdee305..80471c8d 100644 --- a/src/War3Net.Build/TriggerRenderer/ForLoopRenderer.cs +++ b/src/War3Net.Build/TriggerRenderer/ForLoopRenderer.cs @@ -8,9 +8,8 @@ using System; using War3Net.Build.Script; -using War3Net.CodeAnalysis.Jass.Syntax; - -using SyntaxFactory = War3Net.CodeAnalysis.Jass.JassSyntaxFactory; +using War3Net.CodeAnalysis.Jass; +using War3Net.CodeAnalysis.Jass.Extensions; namespace War3Net.Build { @@ -30,30 +29,21 @@ private void RenderForLoop(TriggerFunction function, TriggerRendererContext cont { var argumentTypes = GetArgumentTypes(function); - context.Renderer.Render(SyntaxFactory.SetStatement(indexName, GetParameter(function.Parameters[0], argumentTypes[0], 0, context))); - context.Renderer.RenderNewLine(); - context.Renderer.Render(SyntaxFactory.SetStatement(indexEndName, GetParameter(function.Parameters[1], argumentTypes[1], 1, context))); - context.Renderer.RenderNewLine(); - - context.Renderer.Render(JassLoopCustomScriptAction.Value); - context.Renderer.RenderNewLine(); + context.Writer.WriteSet(indexName, GetParameter(function.Parameters[0], argumentTypes[0], 0, context.TrigFunctionIdentifierBuilder)); + context.Writer.WriteSet(indexEndName, GetParameter(function.Parameters[1], argumentTypes[1], 1, context.TrigFunctionIdentifierBuilder)); - context.Renderer.Render(SyntaxFactory.ExitStatement(SyntaxFactory.BinaryGreaterThanExpression( - SyntaxFactory.VariableReferenceExpression(indexName), - SyntaxFactory.VariableReferenceExpression(indexEndName)))); - context.Renderer.RenderNewLine(); + context.Writer.WriteLoop(); + context.Writer.WriteExitWhen(JassExpression.GreaterThan( + indexName, + indexEndName)); RenderTriggerAction(function.Parameters[2].Function, context); - context.Renderer.Render(SyntaxFactory.SetStatement( + context.Writer.WriteSet( indexName, - SyntaxFactory.BinaryAdditionExpression( - SyntaxFactory.VariableReferenceExpression(indexName), - SyntaxFactory.LiteralExpression(1)))); - context.Renderer.RenderNewLine(); + JassExpression.Add(indexName, "1")); - context.Renderer.Render(JassEndLoopCustomScriptAction.Value); - context.Renderer.RenderNewLine(); + context.Writer.WriteEndLoop(); } private void RenderForLoopVar(TriggerFunction function, TriggerRendererContext context) diff --git a/src/War3Net.Build/TriggerRenderer/ForeachLoopRenderer.cs b/src/War3Net.Build/TriggerRenderer/ForeachLoopRenderer.cs index be3bc276..1009bde9 100644 --- a/src/War3Net.Build/TriggerRenderer/ForeachLoopRenderer.cs +++ b/src/War3Net.Build/TriggerRenderer/ForeachLoopRenderer.cs @@ -5,12 +5,11 @@ // // ------------------------------------------------------------------------------ -using System.Collections.Immutable; +using System.Linq; using War3Net.Build.Script; -using War3Net.CodeAnalysis.Jass.Syntax; - -using SyntaxFactory = War3Net.CodeAnalysis.Jass.JassSyntaxFactory; +using War3Net.CodeAnalysis.Jass; +using War3Net.CodeAnalysis.Jass.Extensions; namespace War3Net.Build { @@ -23,12 +22,12 @@ private void RenderForeachLoop(TriggerFunction function, TriggerRendererContext RenderActionFunction(context.TrigFunctionIdentifierBuilder, actionFunctionName, function.Parameters[^1]); context.TrigFunctionIdentifierBuilder.Remove(); - var argumentListBuilder = ImmutableArray.CreateBuilder(); - BuildParametersSkipLast(function, context, argumentListBuilder); - argumentListBuilder.Add(SyntaxFactory.FunctionReferenceExpression(actionFunctionName)); - - context.Renderer.Render(SyntaxFactory.CallStatement(GetScriptName(function), new JassArgumentListSyntax(argumentListBuilder.ToImmutable()))); - context.Renderer.RenderNewLine(); + context.Writer.WriteCall( + GetScriptName(function), + GetParameters(function, context.TrigFunctionIdentifierBuilder) + .SkipLast(1) + .Append(JassExpression.FunctionRef(actionFunctionName)) + .ToArray()); } private void RenderForeachLoopMultiple(TriggerFunction function, TriggerRendererContext context) @@ -36,11 +35,11 @@ private void RenderForeachLoopMultiple(TriggerFunction function, TriggerRenderer var actionFunctionName = $"{context.TrigFunctionIdentifierBuilder}A"; RenderActionFunction(context.TrigFunctionIdentifierBuilder, actionFunctionName, function.ChildFunctions); - var argumentListBuilder = BuildParameters(function, context); - argumentListBuilder.Add(SyntaxFactory.FunctionReferenceExpression(actionFunctionName)); - - context.Renderer.Render(SyntaxFactory.CallStatement(GetScriptName(function), new JassArgumentListSyntax(argumentListBuilder.ToImmutable()))); - context.Renderer.RenderNewLine(); + context.Writer.WriteCall( + GetScriptName(function), + GetParameters(function, context.TrigFunctionIdentifierBuilder) + .Append(JassExpression.FunctionRef(actionFunctionName)) + .ToArray()); } } } \ No newline at end of file diff --git a/src/War3Net.Build/TriggerRenderer/IfThenElseRenderer.cs b/src/War3Net.Build/TriggerRenderer/IfThenElseRenderer.cs index c5ed196b..a5322bdf 100644 --- a/src/War3Net.Build/TriggerRenderer/IfThenElseRenderer.cs +++ b/src/War3Net.Build/TriggerRenderer/IfThenElseRenderer.cs @@ -6,9 +6,8 @@ // ------------------------------------------------------------------------------ using War3Net.Build.Script; -using War3Net.CodeAnalysis.Jass.Syntax; - -using SyntaxFactory = War3Net.CodeAnalysis.Jass.JassSyntaxFactory; +using War3Net.CodeAnalysis.Jass; +using War3Net.CodeAnalysis.Jass.Extensions; namespace War3Net.Build { @@ -21,22 +20,19 @@ private void RenderIfThenElse(TriggerFunction function, TriggerRendererContext c RenderConditionFunction(context.TrigFunctionIdentifierBuilder, conditionFunctionName, function.Parameters[0]); context.TrigFunctionIdentifierBuilder.Remove(); - context.Renderer.Render(new JassIfCustomScriptAction(SyntaxFactory.ParenthesizedExpression(SyntaxFactory.InvocationExpression(conditionFunctionName)))); - context.Renderer.RenderNewLine(); + context.Writer.WriteIf(JassExpression.Parenthesized(JassExpression.Invoke(conditionFunctionName))); context.TrigFunctionIdentifierBuilder.Append(2); RenderTriggerAction(function.Parameters[1].Function, context); context.TrigFunctionIdentifierBuilder.Remove(); - context.Renderer.Render(JassElseCustomScriptAction.Value); - context.Renderer.RenderNewLine(); + context.Writer.WriteElse(); context.TrigFunctionIdentifierBuilder.Append(3); RenderTriggerAction(function.Parameters[2].Function, context); context.TrigFunctionIdentifierBuilder.Remove(); - context.Renderer.Render(JassEndIfCustomScriptAction.Value); - context.Renderer.RenderNewLine(); + context.Writer.WriteEndIf(); } private void RenderIfThenElseMultiple(TriggerFunction function, TriggerRendererContext context) @@ -44,8 +40,7 @@ private void RenderIfThenElseMultiple(TriggerFunction function, TriggerRendererC var conditionFunctionName = $"{context.TrigFunctionIdentifierBuilder}C"; RenderConditionFunction(context.TrigFunctionIdentifierBuilder, conditionFunctionName, true, function.ChildFunctions); - context.Renderer.Render(new JassIfCustomScriptAction(SyntaxFactory.ParenthesizedExpression(SyntaxFactory.InvocationExpression(conditionFunctionName)))); - context.Renderer.RenderNewLine(); + context.Writer.WriteIf(JassExpression.Parenthesized(JassExpression.Invoke(conditionFunctionName))); context.TrigFunctionIdentifierBuilder.Append("Func"); @@ -60,8 +55,7 @@ private void RenderIfThenElseMultiple(TriggerFunction function, TriggerRendererC } } - context.Renderer.Render(JassElseCustomScriptAction.Value); - context.Renderer.RenderNewLine(); + context.Writer.WriteElse(); for (var i = 0; i < function.ChildFunctions.Count; i++) { @@ -76,8 +70,7 @@ private void RenderIfThenElseMultiple(TriggerFunction function, TriggerRendererC context.TrigFunctionIdentifierBuilder.Remove(); - context.Renderer.Render(JassEndIfCustomScriptAction.Value); - context.Renderer.RenderNewLine(); + context.Writer.WriteEndIf(); } } } \ No newline at end of file diff --git a/src/War3Net.Build/TriggerRenderer/SetVariableRenderer.cs b/src/War3Net.Build/TriggerRenderer/SetVariableRenderer.cs index 94ee58f5..36a7cd83 100644 --- a/src/War3Net.Build/TriggerRenderer/SetVariableRenderer.cs +++ b/src/War3Net.Build/TriggerRenderer/SetVariableRenderer.cs @@ -8,8 +8,8 @@ using System; using War3Net.Build.Script; - -using SyntaxFactory = War3Net.CodeAnalysis.Jass.JassSyntaxFactory; +using War3Net.CodeAnalysis.Jass; +using War3Net.CodeAnalysis.Jass.Extensions; namespace War3Net.Build { @@ -25,21 +25,17 @@ private void RenderSetVariable(TriggerFunction function, TriggerRendererContext throw new InvalidOperationException($"Unable to determine the type of the global variable '{variableParameter.Value}'."); } + var variableName = $"udg_{variableParameter.Value}"; if (variableParameter.ArrayIndexer is not null) { - context.Renderer.Render(SyntaxFactory.SetStatement( - $"udg_{variableParameter.Value}", - GetParameter(variableParameter.ArrayIndexer, "integer", 0, context), - GetParameter(valueParameter, type, 1, context))); - } - else - { - context.Renderer.Render(SyntaxFactory.SetStatement( - $"udg_{variableParameter.Value}", - GetParameter(valueParameter, type, 1, context))); + variableName = JassExpression.ElementAccess( + variableName, + GetParameter(variableParameter.ArrayIndexer, "integer", 0, context.TrigFunctionIdentifierBuilder)); } - context.Renderer.RenderNewLine(); + context.Writer.WriteSet( + variableName, + GetParameter(valueParameter, type, 1, context.TrigFunctionIdentifierBuilder)); } } } \ No newline at end of file diff --git a/src/War3Net.Build/TriggerRenderer/TriggerActionRenderer.cs b/src/War3Net.Build/TriggerRenderer/TriggerActionRenderer.cs index 85a7e3d7..e695412b 100644 --- a/src/War3Net.Build/TriggerRenderer/TriggerActionRenderer.cs +++ b/src/War3Net.Build/TriggerRenderer/TriggerActionRenderer.cs @@ -8,13 +8,12 @@ using System; using System.Collections.Generic; using System.IO; -using System.Text; +using System.Linq; using War3Net.Build.Script; +using War3Net.CodeAnalysis; using War3Net.CodeAnalysis.Jass; -using War3Net.CodeAnalysis.Jass.Syntax; - -using SyntaxFactory = War3Net.CodeAnalysis.Jass.JassSyntaxFactory; +using War3Net.CodeAnalysis.Jass.Extensions; namespace War3Net.Build { @@ -33,35 +32,26 @@ private void RenderActionFunction(TrigFunctionIdentifierBuilder identifierBuilde throw new ArgumentException("Parameter function must be enabled and of type 'Action'.", nameof(parameter)); } - var stringBuilder = new StringBuilder(); - using var stringWriter = new StringWriter(stringBuilder); - var renderer = new JassRenderer(stringWriter); - - var context = new TriggerRendererContext(renderer, identifierBuilder); + using var writer = IndentedTextWriter.New(_writer); - renderer.Render(new JassFunctionCustomScriptAction(SyntaxFactory.FunctionDeclarator(functionName))); - renderer.RenderNewLine(); + var context = new TriggerRendererContext(writer, identifierBuilder); + writer.WriteFunction(functionName); RenderTriggerAction(function, context); + writer.EndFunction(); - renderer.Render(JassEndFunctionCustomScriptAction.Value); - renderer.RenderNewLine(); - - _writer.WriteLine(stringBuilder.ToString()); + _writer.WriteLine(writer.ToString()); } private void RenderActionFunction(TrigFunctionIdentifierBuilder identifierBuilder, string functionName, List functions) { identifierBuilder.Append("Func"); - var stringBuilder = new StringBuilder(); - using var stringWriter = new StringWriter(stringBuilder); - var renderer = new JassRenderer(stringWriter); + using var writer = IndentedTextWriter.New(_writer); - var context = new TriggerRendererContext(renderer, identifierBuilder); + var context = new TriggerRendererContext(writer, identifierBuilder); - renderer.Render(new JassFunctionCustomScriptAction(SyntaxFactory.FunctionDeclarator(functionName))); - renderer.RenderNewLine(); + writer.WriteFunction(functionName); for (var i = 0; i < functions.Count; i++) { @@ -76,10 +66,9 @@ private void RenderActionFunction(TrigFunctionIdentifierBuilder identifierBuilde context.TrigFunctionIdentifierBuilder.Remove(); } - renderer.Render(JassEndFunctionCustomScriptAction.Value); - renderer.RenderNewLine(); + writer.EndFunction(); - _writer.WriteLine(stringBuilder.ToString()); + _writer.WriteLine(writer.ToString()); identifierBuilder.Remove(); } @@ -125,37 +114,32 @@ private void RenderTriggerAction(TriggerFunction function, TriggerRendererContex break; case "CommentString": - context.Renderer.Render(new JassCommentSyntax(" " + function.Parameters[0].Value)); - context.Renderer.RenderNewLine(); + context.Writer.WriteComment(function.Parameters[0].Value); break; case "CustomScriptCode": if (_isLuaTrigger) { - context.Renderer.Render(new JassCommentSyntax("! beginusercode")); - context.Renderer.RenderNewLine(); - - context.Renderer.RenderLine(function.Parameters[0].Value); - - context.Renderer.Render(new JassCommentSyntax("! endusercode")); - context.Renderer.RenderNewLine(); + context.Writer.WriteLine("//! beginusercode"); + context.Writer.WriteLine(function.Parameters[0].Value); + context.Writer.WriteLine("//! endusercode"); } else { - context.Renderer.Render(SyntaxFactory.ParseStatementLine(function.Parameters[0].Value)); - context.Renderer.RenderNewLine(); + context.Writer.WriteLine(function.Parameters[0].Value); } break; case "ReturnAction": - context.Renderer.Render(JassReturnStatementSyntax.Empty); - context.Renderer.RenderNewLine(); + context.Writer.WriteReturn(); break; default: - context.Renderer.Render(SyntaxFactory.CallStatement(GetScriptName(function), GetParameters(function, context))); - context.Renderer.RenderNewLine(); + context.Writer.WriteCall( + GetScriptName(function), + GetParameters(function, context.TrigFunctionIdentifierBuilder).ToArray()); + break; } } diff --git a/src/War3Net.Build/TriggerRenderer/TriggerConditionRenderer.cs b/src/War3Net.Build/TriggerRenderer/TriggerConditionRenderer.cs index e2a2d65a..d1a8ec41 100644 --- a/src/War3Net.Build/TriggerRenderer/TriggerConditionRenderer.cs +++ b/src/War3Net.Build/TriggerRenderer/TriggerConditionRenderer.cs @@ -8,13 +8,12 @@ using System; using System.Collections.Generic; using System.IO; -using System.Text; +using System.Linq; using War3Net.Build.Script; +using War3Net.CodeAnalysis; using War3Net.CodeAnalysis.Jass; -using War3Net.CodeAnalysis.Jass.Syntax; - -using SyntaxFactory = War3Net.CodeAnalysis.Jass.JassSyntaxFactory; +using War3Net.CodeAnalysis.Jass.Extensions; namespace War3Net.Build { @@ -33,38 +32,29 @@ private void RenderConditionFunction(TrigFunctionIdentifierBuilder identifierBui throw new ArgumentException("Parameter function must be enabled and of type 'Condition'.", nameof(parameter)); } - var stringBuilder = new StringBuilder(); - using var stringWriter = new StringWriter(stringBuilder); - var renderer = new JassRenderer(stringWriter); + using var writer = IndentedTextWriter.New(_writer); - var context = new TriggerRendererContext(renderer, identifierBuilder); + var context = new TriggerRendererContext(writer, identifierBuilder); - renderer.Render(new JassFunctionCustomScriptAction(SyntaxFactory.ConditionFunctionDeclarator(functionName))); - renderer.RenderNewLine(); + writer.WriteFilterFunction(functionName); var expression = GetTriggerConditionExpression(function, context); - renderer.Render(new JassReturnStatementSyntax(expression)); - renderer.RenderNewLine(); - - renderer.Render(JassEndFunctionCustomScriptAction.Value); - renderer.RenderNewLine(); + writer.WriteReturn(expression); + writer.EndFunction(); - _writer.WriteLine(stringBuilder.ToString()); + _writer.WriteLine(writer.ToString()); } private void RenderConditionFunction(TrigFunctionIdentifierBuilder identifierBuilder, string functionName, bool returnValue, List functions) { identifierBuilder.Append("Func"); - var stringBuilder = new StringBuilder(); - using var stringWriter = new StringWriter(stringBuilder); - var renderer = new JassRenderer(stringWriter); + using var writer = IndentedTextWriter.New(_writer); - var context = new TriggerRendererContext(renderer, identifierBuilder); + var context = new TriggerRendererContext(writer, identifierBuilder); - renderer.Render(new JassFunctionCustomScriptAction(SyntaxFactory.ConditionFunctionDeclarator(functionName))); - renderer.RenderNewLine(); + writer.WriteFilterFunction(functionName); for (var i = 0; i < functions.Count; i++) { @@ -80,46 +70,41 @@ private void RenderConditionFunction(TrigFunctionIdentifierBuilder identifierBui if (returnValue) { - context.Renderer.Render(SyntaxFactory.IfStatement( - SyntaxFactory.ParenthesizedExpression(SyntaxFactory.UnaryNotExpression(expression)), - new JassReturnStatementSyntax(JassBooleanLiteralExpressionSyntax.False))); - context.Renderer.RenderNewLine(); + writer.WriteIf(JassExpression.Parenthesized(JassExpression.Not(expression))); + writer.WriteReturn(JassKeyword.False); + writer.WriteEndIf(); } else { - context.Renderer.Render(SyntaxFactory.IfStatement( - SyntaxFactory.ParenthesizedExpression(expression), - new JassReturnStatementSyntax(JassBooleanLiteralExpressionSyntax.True))); - context.Renderer.RenderNewLine(); + writer.WriteIf(JassExpression.Parenthesized(expression)); + writer.WriteReturn(JassKeyword.True); + writer.WriteEndIf(); } } - context.Renderer.Render(new JassReturnStatementSyntax(SyntaxFactory.LiteralExpression(returnValue))); - context.Renderer.RenderNewLine(); - - renderer.Render(JassEndFunctionCustomScriptAction.Value); - renderer.RenderNewLine(); + writer.WriteReturn(JassLiteral.Bool(returnValue)); + writer.EndFunction(); - _writer.WriteLine(stringBuilder.ToString()); + _writer.WriteLine(writer.ToString()); identifierBuilder.Remove(); } - private IExpressionSyntax GetTriggerConditionExpression(TriggerFunction function, TriggerRendererContext context) + private string GetTriggerConditionExpression(TriggerFunction function, TriggerRendererContext context) { if (function.Type != TriggerFunctionType.Condition || !function.IsEnabled) { throw new ArgumentException("Function must be enabled and of type 'Condition'.", nameof(function)); } - if (function.Name == "OrMultiple" || function.Name == "AndMultiple") + if (function.Name == TriggerConditionConstants.OrMultiple || function.Name == TriggerConditionConstants.AndMultiple) { var conditionFunctionName = $"{context.TrigFunctionIdentifierBuilder}C"; - RenderConditionFunction(context.TrigFunctionIdentifierBuilder, conditionFunctionName, function.Name == "AndMultiple", function.ChildFunctions); + RenderConditionFunction(context.TrigFunctionIdentifierBuilder, conditionFunctionName, function.Name == TriggerConditionConstants.AndMultiple, function.ChildFunctions); - return SyntaxFactory.InvocationExpression(conditionFunctionName); + return JassExpression.Invoke(conditionFunctionName); } - else if (function.Name == "GetBooleanAnd" || function.Name == "GetBooleanOr") + else if (function.Name == TriggerConditionConstants.GetBooleanAnd || function.Name == TriggerConditionConstants.GetBooleanOr) { context.TrigFunctionIdentifierBuilder.Append(1); var conditionFunctionName1 = context.TrigFunctionIdentifierBuilder.ToString(); @@ -131,19 +116,25 @@ private IExpressionSyntax GetTriggerConditionExpression(TriggerFunction function RenderConditionFunction(context.TrigFunctionIdentifierBuilder, conditionFunctionName2, function.Parameters[1]); context.TrigFunctionIdentifierBuilder.Remove(); - return SyntaxFactory.InvocationExpression( + return JassExpression.InvokeSpaced( function.Name, - SyntaxFactory.InvocationExpression(conditionFunctionName1), - SyntaxFactory.InvocationExpression(conditionFunctionName2)); + JassExpression.Invoke(conditionFunctionName1), + JassExpression.Invoke(conditionFunctionName2)); } else { - var parameters = GetParameters(function, context); + var parameters = GetParameters(function, context.TrigFunctionIdentifierBuilder).ToArray(); + + var @operator = parameters[1]; + if (@operator.StartsWith('"') && @operator.EndsWith('"')) + { + @operator = @operator[1..^1]; + } - return SyntaxFactory.ParenthesizedExpression(SyntaxFactory.BinaryExpression( - parameters.Arguments[0], - parameters.Arguments[2], - SyntaxFactory.ParseBinaryOperator(((JassStringLiteralExpressionSyntax)parameters.Arguments[1]).Value))); + return JassExpression.Parenthesized(JassExpression.Binary( + parameters[0], + @operator, + parameters[2])); } } } diff --git a/src/War3Net.Build/TriggerRenderer/WaitForConditionRenderer.cs b/src/War3Net.Build/TriggerRenderer/WaitForConditionRenderer.cs index 92754706..38900f3a 100644 --- a/src/War3Net.Build/TriggerRenderer/WaitForConditionRenderer.cs +++ b/src/War3Net.Build/TriggerRenderer/WaitForConditionRenderer.cs @@ -6,8 +6,8 @@ // ------------------------------------------------------------------------------ using War3Net.Build.Script; - -using SyntaxFactory = War3Net.CodeAnalysis.Jass.JassSyntaxFactory; +using War3Net.CodeAnalysis.Jass; +using War3Net.CodeAnalysis.Jass.Extensions; namespace War3Net.Build { @@ -22,15 +22,17 @@ private void RenderWaitForCondition(TriggerFunction function, TriggerRendererCon RenderConditionFunction(context.TrigFunctionIdentifierBuilder, conditionFunctionName, function.Parameters[0]); context.TrigFunctionIdentifierBuilder.Remove(); - context.Renderer.Render(SyntaxFactory.LoopStatement( - SyntaxFactory.ExitStatement(SyntaxFactory.ParenthesizedExpression(SyntaxFactory.InvocationExpression(conditionFunctionName))), - SyntaxFactory.CallStatement( - WellKnownNatives.TriggerSleepAction, - SyntaxFactory.InvocationExpression( - WellKnownFunctions.RMaxBJ, - SyntaxFactory.VariableReferenceExpression("bj_WAIT_FOR_COND_MIN_INTERVAL"), - GetParameter(function.Parameters[1], argumentTypes[1], 1, context))))); - context.Renderer.RenderNewLine(); + context.Writer.WriteLoop(); + context.Writer.WriteExitWhen(JassExpression.Parenthesized(JassExpression.Invoke(conditionFunctionName))); + + context.Writer.WriteCallCompact( + WellKnownNatives.TriggerSleepAction, + JassExpression.Invoke( + WellKnownFunctions.RMaxBJ, + "bj_WAIT_FOR_COND_MIN_INTERVAL", + GetParameter(function.Parameters[1], argumentTypes[1], 1, context.TrigFunctionIdentifierBuilder))); + + context.Writer.WriteEndLoop(); } } } \ No newline at end of file diff --git a/src/War3Net.Build/TriggerRendererContext.cs b/src/War3Net.Build/TriggerRendererContext.cs index e1573bf5..932d88a9 100644 --- a/src/War3Net.Build/TriggerRendererContext.cs +++ b/src/War3Net.Build/TriggerRendererContext.cs @@ -5,22 +5,24 @@ // // ------------------------------------------------------------------------------ -using War3Net.CodeAnalysis.Jass; +using War3Net.CodeAnalysis; namespace War3Net.Build { public class TriggerRendererContext { - private readonly JassRenderer _renderer; + private readonly IndentedTextWriter _writer; private readonly TrigFunctionIdentifierBuilder _builder; - public TriggerRendererContext(JassRenderer renderer, TrigFunctionIdentifierBuilder builder) + public TriggerRendererContext( + IndentedTextWriter writer, + TrigFunctionIdentifierBuilder builder) { - _renderer = renderer; + _writer = writer; _builder = builder; } - public JassRenderer Renderer => _renderer; + public IndentedTextWriter Writer => _writer; public TrigFunctionIdentifierBuilder TrigFunctionIdentifierBuilder => _builder; } diff --git a/src/War3Net.CodeAnalysis.Decompilers/Audio/MapSoundsDecompiler.cs b/src/War3Net.CodeAnalysis.Decompilers/Audio/MapSoundsDecompiler.cs index d8297c7a..c9fe8ec2 100644 --- a/src/War3Net.CodeAnalysis.Decompilers/Audio/MapSoundsDecompiler.cs +++ b/src/War3Net.CodeAnalysis.Decompilers/Audio/MapSoundsDecompiler.cs @@ -45,59 +45,54 @@ public bool TryDecompileMapSounds(JassFunctionDeclarationSyntax functionDeclarat var sounds = new Dictionary(StringComparer.Ordinal); - foreach (var statement in functionDeclaration.Body.Statements) + foreach (var statement in functionDeclaration.Statements) { - if (statement is JassCommentSyntax || - statement is JassEmptySyntax) + if (statement is JassSetStatementSyntax setStatement) { - continue; - } - else if (statement is JassSetStatementSyntax setStatement) - { - if (setStatement.Indexer is null && - setStatement.IdentifierName.Name.StartsWith("gg_snd_", StringComparison.Ordinal)) + if (setStatement.ElementAccessClause is null && + setStatement.IdentifierName.Token.Text.StartsWith("gg_snd_", StringComparison.Ordinal)) { if (setStatement.Value.Expression is JassInvocationExpressionSyntax invocationExpression && - string.Equals(invocationExpression.IdentifierName.Name, "CreateSound", StringComparison.Ordinal)) + string.Equals(invocationExpression.IdentifierName.Token.Text, "CreateSound", StringComparison.Ordinal)) { - if (invocationExpression.Arguments.Arguments.Length == 7 && - invocationExpression.Arguments.Arguments[0] is JassStringLiteralExpressionSyntax fileNameLiteralExpression && - invocationExpression.Arguments.Arguments[1] is JassBooleanLiteralExpressionSyntax loopingLiteralExpression && - invocationExpression.Arguments.Arguments[2] is JassBooleanLiteralExpressionSyntax is3DLiteralExpression && - invocationExpression.Arguments.Arguments[3] is JassBooleanLiteralExpressionSyntax stopWhenOutOfRangeLiteralExpression && - invocationExpression.Arguments.Arguments[4].TryGetIntegerExpressionValue(out var fadeInRate) && - invocationExpression.Arguments.Arguments[5].TryGetIntegerExpressionValue(out var fadeOutRate) && - invocationExpression.Arguments.Arguments[6] is JassStringLiteralExpressionSyntax eaxSettingLiteralExpression) + if (invocationExpression.ArgumentList.ArgumentList.Items.Length == 7 && + invocationExpression.ArgumentList.ArgumentList.Items[0].TryGetNotNullStringExpressionValue(out var fileName) && + invocationExpression.ArgumentList.ArgumentList.Items[1].TryGetBooleanExpressionValue(out var looping) && + invocationExpression.ArgumentList.ArgumentList.Items[2].TryGetBooleanExpressionValue(out var is3D) && + invocationExpression.ArgumentList.ArgumentList.Items[3].TryGetBooleanExpressionValue(out var stopWhenOutOfRange) && + invocationExpression.ArgumentList.ArgumentList.Items[4].TryGetIntegerExpressionValue(out var fadeInRate) && + invocationExpression.ArgumentList.ArgumentList.Items[5].TryGetIntegerExpressionValue(out var fadeOutRate) && + invocationExpression.ArgumentList.ArgumentList.Items[6].TryGetNotNullStringExpressionValue(out var eaxSetting)) { var flags = (SoundFlags)0; - if (loopingLiteralExpression.Value) + if (looping) { flags |= SoundFlags.Looping; } - if (is3DLiteralExpression.Value) + if (is3D) { flags |= SoundFlags.Is3DSound; } - if (stopWhenOutOfRangeLiteralExpression.Value) + if (stopWhenOutOfRange) { flags |= SoundFlags.StopWhenOutOfRange; } - var filePath = Regex.Unescape(fileNameLiteralExpression.Value); + var filePath = Regex.Unescape(fileName); Context.ImportedFileNames.Add(filePath); - if (!is3DLiteralExpression.Value && !IsInternalSound(filePath)) + if (!is3D && !IsInternalSound(filePath)) { flags |= SoundFlags.UNK16; } - sounds.Add(setStatement.IdentifierName.Name, new Sound + sounds.Add(setStatement.IdentifierName.Token.Text, new Sound { - Name = setStatement.IdentifierName.Name, + Name = setStatement.IdentifierName.Token.Text, FilePath = filePath, - EaxSetting = eaxSettingLiteralExpression.Value, + EaxSetting = eaxSetting, Flags = flags, FadeInRate = fadeInRate, FadeOutRate = fadeOutRate, @@ -115,11 +110,11 @@ invocationExpression.Arguments.Arguments[3] is JassBooleanLiteralExpressionSynta return false; } } - else if (setStatement.Value.Expression is JassStringLiteralExpressionSyntax stringLiteralExpression) + else if (setStatement.Value.Expression.TryGetNotNullStringExpressionValue(out var musicFileName)) { var flags = SoundFlags.Music; - var filePath = Regex.Unescape(stringLiteralExpression.Value); + var filePath = Regex.Unescape(musicFileName); Context.ImportedFileNames.Add(filePath); if (!IsInternalSound(filePath)) @@ -127,9 +122,9 @@ invocationExpression.Arguments.Arguments[3] is JassBooleanLiteralExpressionSynta flags |= SoundFlags.UNK16; } - sounds.Add(setStatement.IdentifierName.Name, new Sound + sounds.Add(setStatement.IdentifierName.Token.Text, new Sound { - Name = setStatement.IdentifierName.Name, + Name = setStatement.IdentifierName.Token.Text, FilePath = filePath, EaxSetting = string.Empty, Flags = flags, @@ -149,18 +144,18 @@ invocationExpression.Arguments.Arguments[3] is JassBooleanLiteralExpressionSynta } else if (statement is JassCallStatementSyntax callStatement) { - if (string.Equals(callStatement.IdentifierName.Name, "SetSoundParamsFromLabel", StringComparison.Ordinal)) + if (string.Equals(callStatement.IdentifierName.Token.Text, "SetSoundParamsFromLabel", StringComparison.Ordinal)) { continue; } - else if (string.Equals(callStatement.IdentifierName.Name, "SetSoundFacialAnimationLabel", StringComparison.Ordinal)) + else if (string.Equals(callStatement.IdentifierName.Token.Text, "SetSoundFacialAnimationLabel", StringComparison.Ordinal)) { - if (callStatement.Arguments.Arguments.Length == 2 && - callStatement.Arguments.Arguments[0] is JassVariableReferenceExpressionSyntax variableReferenceExpression && - callStatement.Arguments.Arguments[1] is JassStringLiteralExpressionSyntax stringLiteralExpression && - sounds.TryGetValue(variableReferenceExpression.IdentifierName.Name, out var sound)) + if (callStatement.ArgumentList.ArgumentList.Items.Length == 2 && + callStatement.ArgumentList.ArgumentList.Items[0].TryGetIdentifierNameValue(out var variableName) && + callStatement.ArgumentList.ArgumentList.Items[1].TryGetNotNullStringExpressionValue(out var facialAnimationLabel) && + sounds.TryGetValue(variableName, out var sound)) { - sound.FacialAnimationLabel = stringLiteralExpression.Value; + sound.FacialAnimationLabel = facialAnimationLabel; } else { @@ -168,14 +163,14 @@ callStatement.Arguments.Arguments[1] is JassStringLiteralExpressionSyntax string return false; } } - else if (string.Equals(callStatement.IdentifierName.Name, "SetSoundFacialAnimationGroupLabel", StringComparison.Ordinal)) + else if (string.Equals(callStatement.IdentifierName.Token.Text, "SetSoundFacialAnimationGroupLabel", StringComparison.Ordinal)) { - if (callStatement.Arguments.Arguments.Length == 2 && - callStatement.Arguments.Arguments[0] is JassVariableReferenceExpressionSyntax variableReferenceExpression && - callStatement.Arguments.Arguments[1] is JassStringLiteralExpressionSyntax stringLiteralExpression && - sounds.TryGetValue(variableReferenceExpression.IdentifierName.Name, out var sound)) + if (callStatement.ArgumentList.ArgumentList.Items.Length == 2 && + callStatement.ArgumentList.ArgumentList.Items[0].TryGetIdentifierNameValue(out var variableName) && + callStatement.ArgumentList.ArgumentList.Items[1].TryGetNotNullStringExpressionValue(out var facialAnimationGroupLabel) && + sounds.TryGetValue(variableName, out var sound)) { - sound.FacialAnimationGroupLabel = stringLiteralExpression.Value; + sound.FacialAnimationGroupLabel = facialAnimationGroupLabel; } else { @@ -183,14 +178,14 @@ callStatement.Arguments.Arguments[1] is JassStringLiteralExpressionSyntax string return false; } } - else if (string.Equals(callStatement.IdentifierName.Name, "SetSoundFacialAnimationSetFilepath", StringComparison.Ordinal)) + else if (string.Equals(callStatement.IdentifierName.Token.Text, "SetSoundFacialAnimationSetFilepath", StringComparison.Ordinal)) { - if (callStatement.Arguments.Arguments.Length == 2 && - callStatement.Arguments.Arguments[0] is JassVariableReferenceExpressionSyntax variableReferenceExpression && - callStatement.Arguments.Arguments[1] is JassStringLiteralExpressionSyntax stringLiteralExpression && - sounds.TryGetValue(variableReferenceExpression.IdentifierName.Name, out var sound)) + if (callStatement.ArgumentList.ArgumentList.Items.Length == 2 && + callStatement.ArgumentList.ArgumentList.Items[0].TryGetIdentifierNameValue(out var variableName) && + callStatement.ArgumentList.ArgumentList.Items[1].TryGetNotNullStringExpressionValue(out var facialAnimationSetFilepath) && + sounds.TryGetValue(variableName, out var sound)) { - sound.FacialAnimationSetFilepath = stringLiteralExpression.Value; + sound.FacialAnimationSetFilepath = facialAnimationSetFilepath; } else { @@ -198,14 +193,14 @@ callStatement.Arguments.Arguments[1] is JassStringLiteralExpressionSyntax string return false; } } - else if (string.Equals(callStatement.IdentifierName.Name, "SetDialogueSpeakerNameKey", StringComparison.Ordinal)) + else if (string.Equals(callStatement.IdentifierName.Token.Text, "SetDialogueSpeakerNameKey", StringComparison.Ordinal)) { - if (callStatement.Arguments.Arguments.Length == 2 && - callStatement.Arguments.Arguments[0] is JassVariableReferenceExpressionSyntax variableReferenceExpression && - callStatement.Arguments.Arguments[1] is JassStringLiteralExpressionSyntax stringLiteralExpression && - sounds.TryGetValue(variableReferenceExpression.IdentifierName.Name, out var sound) && - stringLiteralExpression.Value.StartsWith("TRIGSTR_", StringComparison.Ordinal) && - int.TryParse(stringLiteralExpression.Value["TRIGSTR_".Length..], NumberStyles.None, CultureInfo.InvariantCulture, out var dialogueSpeakerNameKey)) + if (callStatement.ArgumentList.ArgumentList.Items.Length == 2 && + callStatement.ArgumentList.ArgumentList.Items[0].TryGetIdentifierNameValue(out var variableName) && + callStatement.ArgumentList.ArgumentList.Items[1].TryGetNotNullStringExpressionValue(out var dialogueSpeakerNameKeyString) && + sounds.TryGetValue(variableName, out var sound) && + dialogueSpeakerNameKeyString.StartsWith("TRIGSTR_", StringComparison.Ordinal) && + int.TryParse(dialogueSpeakerNameKeyString["TRIGSTR_".Length..], NumberStyles.None, CultureInfo.InvariantCulture, out var dialogueSpeakerNameKey)) { sound.DialogueSpeakerNameKey = dialogueSpeakerNameKey; } @@ -215,14 +210,14 @@ callStatement.Arguments.Arguments[1] is JassStringLiteralExpressionSyntax string return false; } } - else if (string.Equals(callStatement.IdentifierName.Name, "SetDialogueTextKey", StringComparison.Ordinal)) + else if (string.Equals(callStatement.IdentifierName.Token.Text, "SetDialogueTextKey", StringComparison.Ordinal)) { - if (callStatement.Arguments.Arguments.Length == 2 && - callStatement.Arguments.Arguments[0] is JassVariableReferenceExpressionSyntax variableReferenceExpression && - callStatement.Arguments.Arguments[1] is JassStringLiteralExpressionSyntax stringLiteralExpression && - sounds.TryGetValue(variableReferenceExpression.IdentifierName.Name, out var sound) && - stringLiteralExpression.Value.StartsWith("TRIGSTR_", StringComparison.Ordinal) && - int.TryParse(stringLiteralExpression.Value["TRIGSTR_".Length..], NumberStyles.None, CultureInfo.InvariantCulture, out var dialogueTextKey)) + if (callStatement.ArgumentList.ArgumentList.Items.Length == 2 && + callStatement.ArgumentList.ArgumentList.Items[0].TryGetIdentifierNameValue(out var variableName) && + callStatement.ArgumentList.ArgumentList.Items[1].TryGetNotNullStringExpressionValue(out var dialogueTextKeyString) && + sounds.TryGetValue(variableName, out var sound) && + dialogueTextKeyString.StartsWith("TRIGSTR_", StringComparison.Ordinal) && + int.TryParse(dialogueTextKeyString["TRIGSTR_".Length..], NumberStyles.None, CultureInfo.InvariantCulture, out var dialogueTextKey)) { sound.DialogueTextKey = dialogueTextKey; } @@ -232,16 +227,16 @@ callStatement.Arguments.Arguments[1] is JassStringLiteralExpressionSyntax string return false; } } - else if (string.Equals(callStatement.IdentifierName.Name, "SetSoundDuration", StringComparison.Ordinal)) + else if (string.Equals(callStatement.IdentifierName.Token.Text, "SetSoundDuration", StringComparison.Ordinal)) { continue; } - else if (string.Equals(callStatement.IdentifierName.Name, "SetSoundDistanceCutoff", StringComparison.Ordinal)) + else if (string.Equals(callStatement.IdentifierName.Token.Text, "SetSoundDistanceCutoff", StringComparison.Ordinal)) { - if (callStatement.Arguments.Arguments.Length == 2 && - callStatement.Arguments.Arguments[0] is JassVariableReferenceExpressionSyntax variableReferenceExpression && - callStatement.Arguments.Arguments[1].TryGetRealExpressionValue(out var cutoff) && - sounds.TryGetValue(variableReferenceExpression.IdentifierName.Name, out var sound)) + if (callStatement.ArgumentList.ArgumentList.Items.Length == 2 && + callStatement.ArgumentList.ArgumentList.Items[0].TryGetIdentifierNameValue(out var variableName) && + callStatement.ArgumentList.ArgumentList.Items[1].TryGetRealExpressionValue(out var cutoff) && + sounds.TryGetValue(variableName, out var sound)) { sound.DistanceCutoff = cutoff; } @@ -251,12 +246,12 @@ callStatement.Arguments.Arguments[0] is JassVariableReferenceExpressionSyntax va return false; } } - else if (string.Equals(callStatement.IdentifierName.Name, "SetSoundChannel", StringComparison.Ordinal)) + else if (string.Equals(callStatement.IdentifierName.Token.Text, "SetSoundChannel", StringComparison.Ordinal)) { - if (callStatement.Arguments.Arguments.Length == 2 && - callStatement.Arguments.Arguments[0] is JassVariableReferenceExpressionSyntax variableReferenceExpression && - callStatement.Arguments.Arguments[1].TryGetIntegerExpressionValue(out var channel) && - sounds.TryGetValue(variableReferenceExpression.IdentifierName.Name, out var sound)) + if (callStatement.ArgumentList.ArgumentList.Items.Length == 2 && + callStatement.ArgumentList.ArgumentList.Items[0].TryGetIdentifierNameValue(out var variableName) && + callStatement.ArgumentList.ArgumentList.Items[1].TryGetIntegerExpressionValue(out var channel) && + sounds.TryGetValue(variableName, out var sound)) { sound.Channel = (SoundChannel)channel; } @@ -266,12 +261,12 @@ callStatement.Arguments.Arguments[0] is JassVariableReferenceExpressionSyntax va return false; } } - else if (string.Equals(callStatement.IdentifierName.Name, "SetSoundVolume", StringComparison.Ordinal)) + else if (string.Equals(callStatement.IdentifierName.Token.Text, "SetSoundVolume", StringComparison.Ordinal)) { - if (callStatement.Arguments.Arguments.Length == 2 && - callStatement.Arguments.Arguments[0] is JassVariableReferenceExpressionSyntax variableReferenceExpression && - callStatement.Arguments.Arguments[1].TryGetIntegerExpressionValue(out var volume) && - sounds.TryGetValue(variableReferenceExpression.IdentifierName.Name, out var sound)) + if (callStatement.ArgumentList.ArgumentList.Items.Length == 2 && + callStatement.ArgumentList.ArgumentList.Items[0].TryGetIdentifierNameValue(out var variableName) && + callStatement.ArgumentList.ArgumentList.Items[1].TryGetIntegerExpressionValue(out var volume) && + sounds.TryGetValue(variableName, out var sound)) { sound.Volume = volume; } @@ -281,12 +276,12 @@ callStatement.Arguments.Arguments[0] is JassVariableReferenceExpressionSyntax va return false; } } - else if (string.Equals(callStatement.IdentifierName.Name, "SetSoundPitch", StringComparison.Ordinal)) + else if (string.Equals(callStatement.IdentifierName.Token.Text, "SetSoundPitch", StringComparison.Ordinal)) { - if (callStatement.Arguments.Arguments.Length == 2 && - callStatement.Arguments.Arguments[0] is JassVariableReferenceExpressionSyntax variableReferenceExpression && - callStatement.Arguments.Arguments[1].TryGetRealExpressionValue(out var pitch) && - sounds.TryGetValue(variableReferenceExpression.IdentifierName.Name, out var sound)) + if (callStatement.ArgumentList.ArgumentList.Items.Length == 2 && + callStatement.ArgumentList.ArgumentList.Items[0].TryGetIdentifierNameValue(out var variableName) && + callStatement.ArgumentList.ArgumentList.Items[1].TryGetRealExpressionValue(out var pitch) && + sounds.TryGetValue(variableName, out var sound)) { sound.Pitch = pitch; } @@ -296,13 +291,13 @@ callStatement.Arguments.Arguments[0] is JassVariableReferenceExpressionSyntax va return false; } } - else if (string.Equals(callStatement.IdentifierName.Name, "SetSoundDistances", StringComparison.Ordinal)) + else if (string.Equals(callStatement.IdentifierName.Token.Text, "SetSoundDistances", StringComparison.Ordinal)) { - if (callStatement.Arguments.Arguments.Length == 3 && - callStatement.Arguments.Arguments[0] is JassVariableReferenceExpressionSyntax variableReferenceExpression && - callStatement.Arguments.Arguments[1].TryGetRealExpressionValue(out var minDist) && - callStatement.Arguments.Arguments[2].TryGetRealExpressionValue(out var maxDist) && - sounds.TryGetValue(variableReferenceExpression.IdentifierName.Name, out var sound) && + if (callStatement.ArgumentList.ArgumentList.Items.Length == 3 && + callStatement.ArgumentList.ArgumentList.Items[0].TryGetIdentifierNameValue(out var variableName) && + callStatement.ArgumentList.ArgumentList.Items[1].TryGetRealExpressionValue(out var minDist) && + callStatement.ArgumentList.ArgumentList.Items[2].TryGetRealExpressionValue(out var maxDist) && + sounds.TryGetValue(variableName, out var sound) && sound.Flags.HasFlag(SoundFlags.Is3DSound)) { sound.MinDistance = minDist; @@ -314,14 +309,14 @@ callStatement.Arguments.Arguments[0] is JassVariableReferenceExpressionSyntax va return false; } } - else if (string.Equals(callStatement.IdentifierName.Name, "SetSoundConeAngles", StringComparison.Ordinal)) + else if (string.Equals(callStatement.IdentifierName.Token.Text, "SetSoundConeAngles", StringComparison.Ordinal)) { - if (callStatement.Arguments.Arguments.Length == 4 && - callStatement.Arguments.Arguments[0] is JassVariableReferenceExpressionSyntax variableReferenceExpression && - callStatement.Arguments.Arguments[1].TryGetRealExpressionValue(out var inside) && - callStatement.Arguments.Arguments[2].TryGetRealExpressionValue(out var outside) && - callStatement.Arguments.Arguments[3].TryGetIntegerExpressionValue(out var outsideVolume) && - sounds.TryGetValue(variableReferenceExpression.IdentifierName.Name, out var sound) && + if (callStatement.ArgumentList.ArgumentList.Items.Length == 4 && + callStatement.ArgumentList.ArgumentList.Items[0].TryGetIdentifierNameValue(out var variableName) && + callStatement.ArgumentList.ArgumentList.Items[1].TryGetRealExpressionValue(out var inside) && + callStatement.ArgumentList.ArgumentList.Items[2].TryGetRealExpressionValue(out var outside) && + callStatement.ArgumentList.ArgumentList.Items[3].TryGetIntegerExpressionValue(out var outsideVolume) && + sounds.TryGetValue(variableName, out var sound) && sound.Flags.HasFlag(SoundFlags.Is3DSound)) { sound.ConeAngleInside = inside; @@ -334,14 +329,14 @@ callStatement.Arguments.Arguments[0] is JassVariableReferenceExpressionSyntax va return false; } } - else if (string.Equals(callStatement.IdentifierName.Name, "SetSoundConeOrientation", StringComparison.Ordinal)) + else if (string.Equals(callStatement.IdentifierName.Token.Text, "SetSoundConeOrientation", StringComparison.Ordinal)) { - if (callStatement.Arguments.Arguments.Length == 4 && - callStatement.Arguments.Arguments[0] is JassVariableReferenceExpressionSyntax variableReferenceExpression && - callStatement.Arguments.Arguments[1].TryGetRealExpressionValue(out var x) && - callStatement.Arguments.Arguments[2].TryGetRealExpressionValue(out var y) && - callStatement.Arguments.Arguments[3].TryGetRealExpressionValue(out var z) && - sounds.TryGetValue(variableReferenceExpression.IdentifierName.Name, out var sound) && + if (callStatement.ArgumentList.ArgumentList.Items.Length == 4 && + callStatement.ArgumentList.ArgumentList.Items[0].TryGetIdentifierNameValue(out var variableName) && + callStatement.ArgumentList.ArgumentList.Items[1].TryGetRealExpressionValue(out var x) && + callStatement.ArgumentList.ArgumentList.Items[2].TryGetRealExpressionValue(out var y) && + callStatement.ArgumentList.ArgumentList.Items[3].TryGetRealExpressionValue(out var z) && + sounds.TryGetValue(variableName, out var sound) && sound.Flags.HasFlag(SoundFlags.Is3DSound)) { sound.ConeOrientation = new(x, y, z); @@ -365,7 +360,7 @@ callStatement.Arguments.Arguments[0] is JassVariableReferenceExpressionSyntax va } } - if (sounds.Any()) + if (sounds.Count > 0) { mapSounds = new MapSounds(formatVersion); mapSounds.Sounds.AddRange(sounds.Values); diff --git a/src/War3Net.CodeAnalysis.Decompilers/DecompilationContext.cs b/src/War3Net.CodeAnalysis.Decompilers/DecompilationContext.cs index e26efe73..e9564aa6 100644 --- a/src/War3Net.CodeAnalysis.Decompilers/DecompilationContext.cs +++ b/src/War3Net.CodeAnalysis.Decompilers/DecompilationContext.cs @@ -13,6 +13,7 @@ using War3Net.Build.Info; using War3Net.Build.Script; using War3Net.CodeAnalysis.Jass; +using War3Net.CodeAnalysis.Jass.Extensions; using War3Net.CodeAnalysis.Jass.Syntax; namespace War3Net.CodeAnalysis.Decompilers @@ -46,34 +47,24 @@ public DecompilationContext(Map map, Campaign? campaign, TriggerData? triggerDat var compilationUnit = JassSyntaxFactory.ParseCompilationUnit(map.Script); - var comments = new List(); var functionDeclarationsBuilder = ImmutableDictionary.CreateBuilder(StringComparer.Ordinal); var variableDeclarationsBuilder = ImmutableDictionary.CreateBuilder(StringComparer.Ordinal); foreach (var declaration in compilationUnit.Declarations) { - if (declaration is JassCommentSyntax comment) + if (declaration is JassFunctionDeclarationSyntax functionDeclaration) { - comments.Add(comment); + functionDeclarationsBuilder.Add(functionDeclaration.FunctionDeclarator.IdentifierName.Token.Text, new FunctionDeclarationContext(functionDeclaration)); } - else + else if (declaration is JassGlobalsDeclarationSyntax globalsDeclaration) { - if (declaration is JassFunctionDeclarationSyntax functionDeclaration) + foreach (var globalDeclaration in globalsDeclaration.GlobalDeclarations) { - functionDeclarationsBuilder.Add(functionDeclaration.FunctionDeclarator.IdentifierName.Name, new FunctionDeclarationContext(functionDeclaration, comments)); - } - else if (declaration is JassGlobalDeclarationListSyntax globalDeclarationList) - { - foreach (var declaration2 in globalDeclarationList.Globals) + if (globalDeclaration is JassGlobalVariableDeclarationSyntax globalVariableDeclaration) { - if (declaration2 is JassGlobalDeclarationSyntax globalDeclaration) - { - variableDeclarationsBuilder.Add(globalDeclaration.Declarator.IdentifierName.Name, new VariableDeclarationContext(globalDeclaration)); - } + variableDeclarationsBuilder.Add(globalVariableDeclaration.Declarator.GetIdentifierName().Token.Text, new VariableDeclarationContext(globalVariableDeclaration)); } } - - comments.Clear(); } } diff --git a/src/War3Net.CodeAnalysis.Decompilers/Environment/MapCamerasDecompiler.cs b/src/War3Net.CodeAnalysis.Decompilers/Environment/MapCamerasDecompiler.cs index b8e4dc26..d2b38c5d 100644 --- a/src/War3Net.CodeAnalysis.Decompilers/Environment/MapCamerasDecompiler.cs +++ b/src/War3Net.CodeAnalysis.Decompilers/Environment/MapCamerasDecompiler.cs @@ -43,25 +43,20 @@ public bool TryDecompileMapCameras(JassFunctionDeclarationSyntax functionDeclara var cameras = new Dictionary(StringComparer.Ordinal); - foreach (var statement in functionDeclaration.Body.Statements) + foreach (var statement in functionDeclaration.Statements) { - if (statement is JassCommentSyntax || - statement is JassEmptySyntax) + if (statement is JassSetStatementSyntax setStatement) { - continue; - } - else if (statement is JassSetStatementSyntax setStatement) - { - if (setStatement.Indexer is null && - setStatement.IdentifierName.Name.StartsWith("gg_cam_", StringComparison.Ordinal) && + if (setStatement.ElementAccessClause is null && + setStatement.IdentifierName.Token.Text.StartsWith("gg_cam_", StringComparison.Ordinal) && setStatement.Value.Expression is JassInvocationExpressionSyntax invocationExpression && - string.Equals(invocationExpression.IdentifierName.Name, "CreateCameraSetup", StringComparison.Ordinal)) + string.Equals(invocationExpression.IdentifierName.Token.Text, "CreateCameraSetup", StringComparison.Ordinal)) { - if (invocationExpression.Arguments.Arguments.IsEmpty) + if (invocationExpression.ArgumentList.ArgumentList.Items.IsEmpty) { - cameras.Add(setStatement.IdentifierName.Name, new Camera + cameras.Add(setStatement.IdentifierName.Token.Text, new Camera { - Name = setStatement.IdentifierName.Name["gg_cam_".Length..].Replace('_', ' '), + Name = setStatement.IdentifierName.Token.Text["gg_cam_".Length..].Replace('_', ' '), NearClippingPlane = useNewFormat ? default : 100f, }); } @@ -78,17 +73,17 @@ setStatement.Value.Expression is JassInvocationExpressionSyntax invocationExpres } else if (statement is JassCallStatementSyntax callStatement) { - if (string.Equals(callStatement.IdentifierName.Name, "CameraSetupSetField", StringComparison.Ordinal)) + if (string.Equals(callStatement.IdentifierName.Token.Text, "CameraSetupSetField", StringComparison.Ordinal)) { - if (callStatement.Arguments.Arguments.Length == 4 && - callStatement.Arguments.Arguments[0] is JassVariableReferenceExpressionSyntax cameraVariableReferenceExpression && - callStatement.Arguments.Arguments[1] is JassVariableReferenceExpressionSyntax cameraFieldVariableReferenceExpression && - callStatement.Arguments.Arguments[2].TryGetRealExpressionValue(out var value) && - callStatement.Arguments.Arguments[3].TryGetRealExpressionValue(out var duration) && + if (callStatement.ArgumentList.ArgumentList.Items.Length == 4 && + callStatement.ArgumentList.ArgumentList.Items[0].TryGetIdentifierNameValue(out var cameraVariableName) && + callStatement.ArgumentList.ArgumentList.Items[1].TryGetIdentifierNameValue(out var cameraField) && + callStatement.ArgumentList.ArgumentList.Items[2].TryGetRealExpressionValue(out var value) && + callStatement.ArgumentList.ArgumentList.Items[3].TryGetRealExpressionValue(out var duration) && duration == 0f && - cameras.TryGetValue(cameraVariableReferenceExpression.IdentifierName.Name, out var camera)) + cameras.TryGetValue(cameraVariableName, out var camera)) { - switch (cameraFieldVariableReferenceExpression.IdentifierName.Name) + switch (cameraField) { case "CAMERA_FIELD_ZOFFSET": camera.ZOffset = value; @@ -131,15 +126,15 @@ callStatement.Arguments.Arguments[1] is JassVariableReferenceExpressionSyntax ca return false; } } - else if (string.Equals(callStatement.IdentifierName.Name, "CameraSetupSetDestPosition", StringComparison.Ordinal)) + else if (string.Equals(callStatement.IdentifierName.Token.Text, "CameraSetupSetDestPosition", StringComparison.Ordinal)) { - if (callStatement.Arguments.Arguments.Length == 4 && - callStatement.Arguments.Arguments[0] is JassVariableReferenceExpressionSyntax cameraVariableReferenceExpression && - callStatement.Arguments.Arguments[1].TryGetRealExpressionValue(out var x) && - callStatement.Arguments.Arguments[2].TryGetRealExpressionValue(out var y) && - callStatement.Arguments.Arguments[3].TryGetRealExpressionValue(out var duration) && + if (callStatement.ArgumentList.ArgumentList.Items.Length == 4 && + callStatement.ArgumentList.ArgumentList.Items[0].TryGetIdentifierNameValue(out var cameraVariableName) && + callStatement.ArgumentList.ArgumentList.Items[1].TryGetRealExpressionValue(out var x) && + callStatement.ArgumentList.ArgumentList.Items[2].TryGetRealExpressionValue(out var y) && + callStatement.ArgumentList.ArgumentList.Items[3].TryGetRealExpressionValue(out var duration) && duration == 0f && - cameras.TryGetValue(cameraVariableReferenceExpression.IdentifierName.Name, out var camera)) + cameras.TryGetValue(cameraVariableName, out var camera)) { camera.TargetPosition = new(x, y); } @@ -162,7 +157,7 @@ callStatement.Arguments.Arguments[0] is JassVariableReferenceExpressionSyntax ca } } - if (cameras.Any()) + if (cameras.Count > 0) { mapCameras = new MapCameras(formatVersion, useNewFormat); mapCameras.Cameras.AddRange(cameras.Values); diff --git a/src/War3Net.CodeAnalysis.Decompilers/Environment/MapRegionsDecompiler.cs b/src/War3Net.CodeAnalysis.Decompilers/Environment/MapRegionsDecompiler.cs index c4569433..6dee22b7 100644 --- a/src/War3Net.CodeAnalysis.Decompilers/Environment/MapRegionsDecompiler.cs +++ b/src/War3Net.CodeAnalysis.Decompilers/Environment/MapRegionsDecompiler.cs @@ -46,31 +46,29 @@ public bool TryDecompileMapRegions(JassFunctionDeclarationSyntax functionDeclara var createdRegions = new List(); var regions = new Dictionary(StringComparer.Ordinal); - foreach (var statement in functionDeclaration.Body.Statements) + foreach (var statement in functionDeclaration.Statements) { - if (statement is JassLocalVariableDeclarationStatementSyntax || - statement is JassCommentSyntax || - statement is JassEmptySyntax) + if (statement is JassLocalVariableDeclarationStatementSyntax) { continue; } else if (statement is JassSetStatementSyntax setStatement) { - if (setStatement.Indexer is null && + if (setStatement.ElementAccessClause is null && setStatement.Value.Expression is JassInvocationExpressionSyntax invocationExpression) { - if (setStatement.IdentifierName.Name.StartsWith("gg_rct_", StringComparison.Ordinal) && - string.Equals(invocationExpression.IdentifierName.Name, "Rect", StringComparison.Ordinal)) + if (setStatement.IdentifierName.Token.Text.StartsWith("gg_rct_", StringComparison.Ordinal) && + string.Equals(invocationExpression.IdentifierName.Token.Text, "Rect", StringComparison.Ordinal)) { - if (invocationExpression.Arguments.Arguments.Length == 4 && - invocationExpression.Arguments.Arguments[0].TryGetRealExpressionValue(out var minx) && - invocationExpression.Arguments.Arguments[1].TryGetRealExpressionValue(out var miny) && - invocationExpression.Arguments.Arguments[2].TryGetRealExpressionValue(out var maxx) && - invocationExpression.Arguments.Arguments[3].TryGetRealExpressionValue(out var maxy)) + if (invocationExpression.ArgumentList.ArgumentList.Items.Length == 4 && + invocationExpression.ArgumentList.ArgumentList.Items[0].TryGetRealExpressionValue(out var minx) && + invocationExpression.ArgumentList.ArgumentList.Items[1].TryGetRealExpressionValue(out var miny) && + invocationExpression.ArgumentList.ArgumentList.Items[2].TryGetRealExpressionValue(out var maxx) && + invocationExpression.ArgumentList.ArgumentList.Items[3].TryGetRealExpressionValue(out var maxy)) { currentRegion = new Region { - Name = setStatement.IdentifierName.Name["gg_rct_".Length..].Replace('_', ' '), + Name = setStatement.IdentifierName.Token.Text["gg_rct_".Length..].Replace('_', ' '), Left = minx, Bottom = miny, Right = maxx, @@ -82,9 +80,9 @@ statement is JassCommentSyntax || createdRegions.Add(currentRegion); - if (!regions.TryAdd(setStatement.IdentifierName.Name, currentRegion)) + if (!regions.TryAdd(setStatement.IdentifierName.Token.Text, currentRegion)) { - regions[setStatement.IdentifierName.Name] = currentRegion; + regions[setStatement.IdentifierName.Token.Text] = currentRegion; } } else @@ -93,15 +91,15 @@ statement is JassCommentSyntax || return false; } } - else if (string.Equals(setStatement.IdentifierName.Name, "we", StringComparison.Ordinal) && - string.Equals(invocationExpression.IdentifierName.Name, "AddWeatherEffect", StringComparison.Ordinal)) + else if (string.Equals(setStatement.IdentifierName.Token.Text, "we", StringComparison.Ordinal) && + string.Equals(invocationExpression.IdentifierName.Token.Text, "AddWeatherEffect", StringComparison.Ordinal)) { - if (invocationExpression.Arguments.Arguments.Length == 2 && - invocationExpression.Arguments.Arguments[0] is JassVariableReferenceExpressionSyntax regionVariableReferenceExpression && - invocationExpression.Arguments.Arguments[1] is JassFourCCLiteralExpressionSyntax fourCCLiteralExpression && - regions.TryGetValue(regionVariableReferenceExpression.IdentifierName.Name, out var region)) + if (invocationExpression.ArgumentList.ArgumentList.Items.Length == 2 && + invocationExpression.ArgumentList.ArgumentList.Items[0].TryGetIdentifierNameValue(out var regionVariableName) && + invocationExpression.ArgumentList.ArgumentList.Items[1].TryGetIntegerExpressionValue(out var weatherTypeValue) && + regions.TryGetValue(regionVariableName, out var region)) { - region.WeatherType = (WeatherType)fourCCLiteralExpression.Value.InvertEndianness(); + region.WeatherType = (WeatherType)weatherTypeValue.InvertEndianness(); } else { @@ -121,19 +119,19 @@ invocationExpression.Arguments.Arguments[1] is JassFourCCLiteralExpressionSyntax } else if (statement is JassCallStatementSyntax callStatement) { - if (string.Equals(callStatement.IdentifierName.Name, "SetSoundPosition", StringComparison.Ordinal)) + if (string.Equals(callStatement.IdentifierName.Token.Text, "SetSoundPosition", StringComparison.Ordinal)) { - if (callStatement.Arguments.Arguments.Length == 4 && - callStatement.Arguments.Arguments[0] is JassVariableReferenceExpressionSyntax soundVariableReferenceExpression && - callStatement.Arguments.Arguments[1].TryGetRealExpressionValue(out var x) && - callStatement.Arguments.Arguments[2].TryGetRealExpressionValue(out var y) && + if (callStatement.ArgumentList.ArgumentList.Items.Length == 4 && + callStatement.ArgumentList.ArgumentList.Items[0].TryGetIdentifierNameValue(out var soundVariableName) && + callStatement.ArgumentList.ArgumentList.Items[1].TryGetRealExpressionValue(out var x) && + callStatement.ArgumentList.ArgumentList.Items[2].TryGetRealExpressionValue(out var y) && currentRegion is not null && currentRegion.CenterX == x && currentRegion.CenterY == y && (string.IsNullOrEmpty(currentRegion.AmbientSound) || - string.Equals(currentRegion.AmbientSound, soundVariableReferenceExpression.IdentifierName.Name, StringComparison.Ordinal))) + string.Equals(currentRegion.AmbientSound, soundVariableName, StringComparison.Ordinal))) { - currentRegion.AmbientSound = soundVariableReferenceExpression.IdentifierName.Name; + currentRegion.AmbientSound = soundVariableName; } else { @@ -141,19 +139,19 @@ currentRegion is not null && return false; } } - else if (string.Equals(callStatement.IdentifierName.Name, "RegisterStackedSound", StringComparison.Ordinal)) + else if (string.Equals(callStatement.IdentifierName.Token.Text, "RegisterStackedSound", StringComparison.Ordinal)) { - if (callStatement.Arguments.Arguments.Length == 4 && - callStatement.Arguments.Arguments[0] is JassVariableReferenceExpressionSyntax soundVariableReferenceExpression && - callStatement.Arguments.Arguments[2].TryGetRealExpressionValue(out var rectWidth) && - callStatement.Arguments.Arguments[3].TryGetRealExpressionValue(out var rectHeight) && + if (callStatement.ArgumentList.ArgumentList.Items.Length == 4 && + callStatement.ArgumentList.ArgumentList.Items[0].TryGetIdentifierNameValue(out var soundVariableName) && + callStatement.ArgumentList.ArgumentList.Items[2].TryGetRealExpressionValue(out var rectWidth) && + callStatement.ArgumentList.ArgumentList.Items[3].TryGetRealExpressionValue(out var rectHeight) && currentRegion is not null && currentRegion.Width == rectWidth && currentRegion.Height == rectHeight && (string.IsNullOrEmpty(currentRegion.AmbientSound) || - string.Equals(currentRegion.AmbientSound, soundVariableReferenceExpression.IdentifierName.Name, StringComparison.Ordinal))) + string.Equals(currentRegion.AmbientSound, soundVariableName, StringComparison.Ordinal))) { - currentRegion.AmbientSound = soundVariableReferenceExpression.IdentifierName.Name; + currentRegion.AmbientSound = soundVariableName; } else { @@ -161,7 +159,7 @@ currentRegion is not null && return false; } } - else if (string.Equals(callStatement.IdentifierName.Name, "EnableWeatherEffect", StringComparison.Ordinal)) + else if (string.Equals(callStatement.IdentifierName.Token.Text, "EnableWeatherEffect", StringComparison.Ordinal)) { continue; } @@ -178,7 +176,7 @@ currentRegion is not null && } } - if (regions.Any()) + if (regions.Count > 0) { mapRegions = new MapRegions(formatVersion); mapRegions.Regions.AddRange(createdRegions); diff --git a/src/War3Net.CodeAnalysis.Decompilers/FunctionDeclarationContext.cs b/src/War3Net.CodeAnalysis.Decompilers/FunctionDeclarationContext.cs index 7a3b155e..6b3d8f3a 100644 --- a/src/War3Net.CodeAnalysis.Decompilers/FunctionDeclarationContext.cs +++ b/src/War3Net.CodeAnalysis.Decompilers/FunctionDeclarationContext.cs @@ -5,24 +5,23 @@ // // ------------------------------------------------------------------------------ -using System.Collections.Generic; -using System.Collections.Immutable; - +using War3Net.CodeAnalysis.Jass; +using War3Net.CodeAnalysis.Jass.Extensions; using War3Net.CodeAnalysis.Jass.Syntax; namespace War3Net.CodeAnalysis.Decompilers { internal sealed class FunctionDeclarationContext { - public FunctionDeclarationContext(JassFunctionDeclarationSyntax functionDeclaration, IEnumerable comments) + public FunctionDeclarationContext(JassFunctionDeclarationSyntax functionDeclaration) { FunctionDeclaration = functionDeclaration; - Comments = comments.ToImmutableList(); - if (functionDeclaration.FunctionDeclarator.ParameterList.Parameters.IsEmpty) + if (functionDeclaration.FunctionDeclarator.ParameterList is JassEmptyParameterListSyntax) { - IsActionsFunction = functionDeclaration.FunctionDeclarator.ReturnType == JassTypeSyntax.Nothing; - IsConditionsFunction = functionDeclaration.FunctionDeclarator.ReturnType == JassTypeSyntax.Boolean; + var returnTypeToken = functionDeclaration.FunctionDeclarator.ReturnClause.ReturnType.GetToken(); + IsActionsFunction = returnTypeToken.SyntaxKind == JassSyntaxKind.NothingKeyword; + IsConditionsFunction = returnTypeToken.SyntaxKind == JassSyntaxKind.BooleanKeyword; } Handled = false; @@ -30,8 +29,6 @@ public FunctionDeclarationContext(JassFunctionDeclarationSyntax functionDeclarat public JassFunctionDeclarationSyntax FunctionDeclaration { get; } - public ImmutableList Comments { get; } - public bool IsActionsFunction { get; } public bool IsConditionsFunction { get; } diff --git a/src/War3Net.CodeAnalysis.Decompilers/JassScriptDecompiler.cs b/src/War3Net.CodeAnalysis.Decompilers/JassScriptDecompiler.cs index 1ccf7fe4..313251d7 100644 --- a/src/War3Net.CodeAnalysis.Decompilers/JassScriptDecompiler.cs +++ b/src/War3Net.CodeAnalysis.Decompilers/JassScriptDecompiler.cs @@ -67,16 +67,16 @@ private IEnumerable GetCandidateFunctions(string? ex yield return expectedFunction; } - foreach (var statement in mainFunction.FunctionDeclaration.Body.Statements) + foreach (var statement in mainFunction.FunctionDeclaration.Statements) { - if (statement is JassCallStatementSyntax callStatement && callStatement.Arguments.Arguments.IsEmpty) + if (statement is JassCallStatementSyntax callStatement && callStatement.ArgumentList.ArgumentList.Items.IsEmpty) { - if (string.Equals(callStatement.IdentifierName.Name, expectedFunctionName, StringComparison.Ordinal)) + if (string.Equals(callStatement.IdentifierName.Token.Text, expectedFunctionName, StringComparison.Ordinal)) { continue; } - if (Context.FunctionDeclarations.TryGetValue(callStatement.IdentifierName.Name, out var candidateFunction) && + if (Context.FunctionDeclarations.TryGetValue(callStatement.IdentifierName.Token.Text, out var candidateFunction) && candidateFunction.IsActionsFunction && !candidateFunction.Handled) { diff --git a/src/War3Net.CodeAnalysis.Decompilers/Script/MapTriggersDecompiler.cs b/src/War3Net.CodeAnalysis.Decompilers/Script/MapTriggersDecompiler.cs index ef32be5c..654d6ffc 100644 --- a/src/War3Net.CodeAnalysis.Decompilers/Script/MapTriggersDecompiler.cs +++ b/src/War3Net.CodeAnalysis.Decompilers/Script/MapTriggersDecompiler.cs @@ -11,6 +11,7 @@ using System.Linq; using War3Net.Build.Script; +using War3Net.CodeAnalysis.Jass; using War3Net.CodeAnalysis.Jass.Extensions; using War3Net.CodeAnalysis.Jass.Syntax; @@ -83,7 +84,7 @@ public bool TryDecompileMapTriggers( }); } - if (Context.VariableDeclarations.Any(declaration => declaration.Value.GlobalDeclaration.Declarator.IdentifierName.Name.StartsWith("udg_", StringComparison.Ordinal))) + if (Context.VariableDeclarations.Any(declaration => declaration.Value.GlobalVariableDeclaration.Declarator.GetIdentifierName().Token.Text.StartsWith("udg_", StringComparison.Ordinal))) { var variablesCategoryId = categoryId++; @@ -96,13 +97,13 @@ public bool TryDecompileMapTriggers( foreach (var declaration in Context.VariableDeclarations) { - var globalDeclaration = declaration.Value.GlobalDeclaration; - if (globalDeclaration.Declarator.IdentifierName.Name.StartsWith("udg_", StringComparison.Ordinal)) + var globalVariableDeclaration = declaration.Value.GlobalVariableDeclaration; + if (globalVariableDeclaration.Declarator.GetIdentifierName().Token.Text.StartsWith("udg_", StringComparison.Ordinal)) { var variableDefinition = new VariableDefinition { - Name = globalDeclaration.Declarator.IdentifierName.Name["udg_".Length..], - Type = globalDeclaration.Declarator.Type.TypeName.Name, + Name = globalVariableDeclaration.Declarator.GetIdentifierName().Token.Text["udg_".Length..], + Type = globalVariableDeclaration.Declarator.GetVariableType().GetToken().Text, Unk = 1, IsArray = declaration.Value.IsArray, ArraySize = 1, @@ -114,7 +115,7 @@ public bool TryDecompileMapTriggers( declaration.Value.VariableDefinition = variableDefinition; - if (globalDeclaration.Declarator is JassVariableDeclaratorSyntax variableDeclarator) + if (globalVariableDeclaration.Declarator is JassVariableDeclaratorSyntax variableDeclarator) { if (variableDeclarator.Value is not null && TryDecompileVariableDefinitionInitialValue(variableDeclarator.Value.Expression, variableDefinition.Type, out var initialValue)) @@ -140,19 +141,21 @@ public bool TryDecompileMapTriggers( if (initGlobalsFunction is not null) { - foreach (var statement in initGlobalsFunction.Body.Statements) + foreach (var statement in initGlobalsFunction.Statements) { if (statement is JassLoopStatementSyntax loopStatement) { - if (loopStatement.Body.Statements.Length == 3 && - loopStatement.Body.Statements[0] is JassExitStatementSyntax exitStatement && - loopStatement.Body.Statements[1] is JassSetStatementSyntax setVariableStatement && - loopStatement.Body.Statements[2] is JassSetStatementSyntax && - setVariableStatement.Indexer is JassVariableReferenceExpressionSyntax i && - string.Equals(i.IdentifierName.Name, "i", StringComparison.Ordinal)) + if (loopStatement.Statements.Length == 3 && + loopStatement.Statements[0] is JassExitStatementSyntax exitStatement && + loopStatement.Statements[1] is JassSetStatementSyntax setVariableStatement && + loopStatement.Statements[2] is JassSetStatementSyntax && + exitStatement.Condition.Deparenthesize() is JassBinaryExpressionSyntax binaryExpression && + binaryExpression.Right.TryGetIntegerExpressionValue(out var arraySize) && + setVariableStatement.ElementAccessClause is not null && + setVariableStatement.ElementAccessClause.Expression.TryGetIdentifierNameValue(out var indexVariableName) && + string.Equals(indexVariableName, "i", StringComparison.Ordinal)) { - var variableName = setVariableStatement.IdentifierName.Name["udg_".Length..]; - var arraySize = ((JassDecimalLiteralExpressionSyntax)((JassBinaryExpressionSyntax)exitStatement.Condition.Deparenthesize()).Right).Value; + var variableName = setVariableStatement.IdentifierName.Token.Text["udg_".Length..]; var variableDefinition = mapTriggers.Variables.Single(v => string.Equals(v.Name, variableName, StringComparison.Ordinal)); @@ -180,11 +183,11 @@ setVariableStatement.Indexer is JassVariableReferenceExpressionSyntax i && }); var triggers = new Dictionary(StringComparer.Ordinal); - foreach (var statement in initCustomTriggersFunction.Body.Statements) + foreach (var statement in initCustomTriggersFunction.Statements) { if (statement is JassCallStatementSyntax callStatement && - callStatement.Arguments.Arguments.IsEmpty && - Context.FunctionDeclarations.TryGetValue(callStatement.IdentifierName.Name, out var initTrigFunction) && + callStatement.ArgumentList.ArgumentList.Items.IsEmpty && + Context.FunctionDeclarations.TryGetValue(callStatement.IdentifierName.Token.Text, out var initTrigFunction) && TryDecompileTriggerDefinition(initTrigFunction, out var trigger)) { trigger.Id = triggerId++; @@ -201,14 +204,14 @@ setVariableStatement.Indexer is JassVariableReferenceExpressionSyntax i && if (runInitializationTriggersFunction is not null) { - foreach (var statement in runInitializationTriggersFunction.Body.Statements) + foreach (var statement in runInitializationTriggersFunction.Statements) { if (statement is JassCallStatementSyntax callStatement && - callStatement.Arguments.Arguments.Length == 1 && - string.Equals(callStatement.IdentifierName.Name, "ConditionalTriggerExecute", StringComparison.Ordinal) && - callStatement.Arguments.Arguments[0] is JassVariableReferenceExpressionSyntax triggerVariableReferenceExpression && - triggerVariableReferenceExpression.IdentifierName.Name.StartsWith("gg_trg_", StringComparison.Ordinal) && - triggers.TryGetValue(triggerVariableReferenceExpression.IdentifierName.Name["gg_trg_".Length..].Replace('_', ' '), out var triggerDefinition)) + callStatement.ArgumentList.ArgumentList.Items.Length == 1 && + string.Equals(callStatement.IdentifierName.Token.Text, "ConditionalTriggerExecute", StringComparison.Ordinal) && + callStatement.ArgumentList.ArgumentList.Items[0].TryGetIdentifierNameValue(out var triggerVariableName) && + triggerVariableName.StartsWith("gg_trg_", StringComparison.Ordinal) && + triggers.TryGetValue(triggerVariableName["gg_trg_".Length..].Replace('_', ' '), out var triggerDefinition)) { triggerDefinition.Functions.Add(new TriggerFunction { @@ -235,19 +238,19 @@ private bool TryDecompileTriggerDefinition(FunctionDeclarationContext initTrigFu string? triggerVariableName = null; - var triggerFunctionName = initTrigFunction.FunctionDeclaration.FunctionDeclarator.IdentifierName.Name["InitTrig_".Length..]; + var triggerFunctionName = initTrigFunction.FunctionDeclaration.FunctionDeclarator.IdentifierName.Token.Text["InitTrig_".Length..]; - foreach (var statement in initTrigFunction.FunctionDeclaration.Body.Statements) + foreach (var statement in initTrigFunction.FunctionDeclaration.Statements) { if (statement is JassSetStatementSyntax setStatement) { if (setStatement.Value.Expression is JassInvocationExpressionSyntax invocationExpression && - invocationExpression.Arguments.Arguments.IsEmpty && - setStatement.Indexer is null && - string.Equals(setStatement.IdentifierName.Name, $"gg_trg_{triggerFunctionName}", StringComparison.Ordinal) && - string.Equals(invocationExpression.IdentifierName.Name, "CreateTrigger", StringComparison.Ordinal)) + invocationExpression.ArgumentList.ArgumentList.Items.IsEmpty && + setStatement.ElementAccessClause is null && + string.Equals(setStatement.IdentifierName.Token.Text, $"gg_trg_{triggerFunctionName}", StringComparison.Ordinal) && + string.Equals(invocationExpression.IdentifierName.Token.Text, "CreateTrigger", StringComparison.Ordinal)) { - triggerVariableName = setStatement.IdentifierName.Name; + triggerVariableName = setStatement.IdentifierName.Token.Text; trigger = new TriggerDefinition(); trigger.IsEnabled = true; @@ -263,20 +266,22 @@ setStatement.Indexer is null && } else if (statement is JassCallStatementSyntax callStatement) { - if (string.Equals(callStatement.IdentifierName.Name, "TriggerAddAction", StringComparison.Ordinal)) + if (string.Equals(callStatement.IdentifierName.Token.Text, "TriggerAddAction", StringComparison.Ordinal)) { - if (callStatement.Arguments.Arguments.Length == 2 && - callStatement.Arguments.Arguments[0] is JassVariableReferenceExpressionSyntax variableReferenceExpression && - callStatement.Arguments.Arguments[1] is JassFunctionReferenceExpressionSyntax functionReferenceExpression && - string.Equals(variableReferenceExpression.IdentifierName.Name, triggerVariableName, StringComparison.Ordinal) && - Context.FunctionDeclarations.TryGetValue(functionReferenceExpression.IdentifierName.Name, out var actionsFunctionDeclaration) && + if (callStatement.ArgumentList.ArgumentList.Items.Length == 2 && + callStatement.ArgumentList.ArgumentList.Items[0].TryGetIdentifierNameValue(out var variableName) && + callStatement.ArgumentList.ArgumentList.Items[1] is JassFunctionReferenceExpressionSyntax functionReferenceExpression && + string.Equals(variableName, triggerVariableName, StringComparison.Ordinal) && + Context.FunctionDeclarations.TryGetValue(functionReferenceExpression.IdentifierName.Token.Text, out var actionsFunctionDeclaration) && actionsFunctionDeclaration.IsActionsFunction) { var actionsFunction = actionsFunctionDeclaration.FunctionDeclaration; - if (TryDecompileActionStatementList(actionsFunction.Body, out var actionFunctions)) + if (TryDecompileActionStatements(actionsFunction.Statements, out var actionFunctions)) { - trigger.Functions.AddRange(actionFunctions); + var functions = trigger.Functions; + functions.AddRange(actionFunctions); + DecompileLeadingTrivia(actionsFunction.EndFunctionToken.LeadingTrivia, ref functions); } else { @@ -290,23 +295,25 @@ callStatement.Arguments.Arguments[1] is JassFunctionReferenceExpressionSyntax fu return false; } } - else if (string.Equals(callStatement.IdentifierName.Name, "TriggerAddCondition", StringComparison.Ordinal)) + else if (string.Equals(callStatement.IdentifierName.Token.Text, "TriggerAddCondition", StringComparison.Ordinal)) { - if (callStatement.Arguments.Arguments.Length == 2 && - callStatement.Arguments.Arguments[0] is JassVariableReferenceExpressionSyntax variableReferenceExpression && - callStatement.Arguments.Arguments[1] is JassInvocationExpressionSyntax conditionInvocationExpression && - string.Equals(conditionInvocationExpression.IdentifierName.Name, "Condition", StringComparison.Ordinal) && - conditionInvocationExpression.Arguments.Arguments.Length == 1 && - conditionInvocationExpression.Arguments.Arguments[0] is JassFunctionReferenceExpressionSyntax functionReferenceExpression && - string.Equals(variableReferenceExpression.IdentifierName.Name, triggerVariableName, StringComparison.Ordinal) && - Context.FunctionDeclarations.TryGetValue(functionReferenceExpression.IdentifierName.Name, out var conditionsFunctionDeclaration) && + if (callStatement.ArgumentList.ArgumentList.Items.Length == 2 && + callStatement.ArgumentList.ArgumentList.Items[0].TryGetIdentifierNameValue(out var variableName) && + callStatement.ArgumentList.ArgumentList.Items[1] is JassInvocationExpressionSyntax conditionInvocationExpression && + string.Equals(conditionInvocationExpression.IdentifierName.Token.Text, "Condition", StringComparison.Ordinal) && + conditionInvocationExpression.ArgumentList.ArgumentList.Items.Length == 1 && + conditionInvocationExpression.ArgumentList.ArgumentList.Items[0] is JassFunctionReferenceExpressionSyntax functionReferenceExpression && + string.Equals(variableName, triggerVariableName, StringComparison.Ordinal) && + Context.FunctionDeclarations.TryGetValue(functionReferenceExpression.IdentifierName.Token.Text, out var conditionsFunctionDeclaration) && conditionsFunctionDeclaration.IsConditionsFunction) { var conditionsFunction = conditionsFunctionDeclaration.FunctionDeclaration; - if (TryDecompileConditionStatementList(conditionsFunction.Body, out var conditionFunctions)) + if (TryDecompileConditionStatements(conditionsFunction.Statements, out var conditionFunctions)) { - trigger.Functions.AddRange(conditionFunctions); + var functions = trigger.Functions; + functions.AddRange(conditionFunctions); + DecompileLeadingTrivia(conditionsFunction.EndFunctionToken.LeadingTrivia, ref functions); } else { @@ -318,11 +325,11 @@ conditionInvocationExpression.Arguments.Arguments[0] is JassFunctionReferenceExp return false; } } - else if (string.Equals(callStatement.IdentifierName.Name, "DisableTrigger", StringComparison.Ordinal)) + else if (string.Equals(callStatement.IdentifierName.Token.Text, "DisableTrigger", StringComparison.Ordinal)) { - if (callStatement.Arguments.Arguments.Length == 1 && - callStatement.Arguments.Arguments[0] is JassVariableReferenceExpressionSyntax variableReferenceExpression && - string.Equals(variableReferenceExpression.IdentifierName.Name, triggerVariableName, StringComparison.Ordinal)) + if (callStatement.ArgumentList.ArgumentList.Items.Length == 1 && + callStatement.ArgumentList.ArgumentList.Items[0].TryGetIdentifierNameValue(out var variableName) && + string.Equals(variableName, triggerVariableName, StringComparison.Ordinal)) { trigger.IsInitiallyOn = false; @@ -335,21 +342,21 @@ callStatement.Arguments.Arguments[0] is JassVariableReferenceExpressionSyntax va } else { - if (Context.TriggerData.TriggerData.TriggerEvents.TryGetValue(callStatement.IdentifierName.Name, out var triggerEvent) && - callStatement.Arguments.Arguments.Length == triggerEvent.ArgumentTypes.Length + 1 && - callStatement.Arguments.Arguments[0] is JassVariableReferenceExpressionSyntax variableReferenceExpression && - string.Equals(variableReferenceExpression.IdentifierName.Name, triggerVariableName, StringComparison.Ordinal)) + if (Context.TriggerData.TriggerData.TriggerEvents.TryGetValue(callStatement.IdentifierName.Token.Text, out var triggerEvent) && + callStatement.ArgumentList.ArgumentList.Items.Length == triggerEvent.ArgumentTypes.Length + 1 && + callStatement.ArgumentList.ArgumentList.Items[0].TryGetIdentifierNameValue(out var variableName) && + string.Equals(variableName, triggerVariableName, StringComparison.Ordinal)) { var function = new TriggerFunction { Type = TriggerFunctionType.Event, IsEnabled = true, - Name = callStatement.IdentifierName.Name, + Name = callStatement.IdentifierName.Token.Text, }; - for (var i = 1; i < callStatement.Arguments.Arguments.Length; i++) + for (var i = 1; i < callStatement.ArgumentList.ArgumentList.Items.Length; i++) { - if (TryDecompileTriggerFunctionParameter(callStatement.Arguments.Arguments[i], triggerEvent.ArgumentTypes[i - 1], out var functionParameter)) + if (TryDecompileTriggerFunctionParameter(callStatement.ArgumentList.ArgumentList.Items[i], triggerEvent.ArgumentTypes[i - 1], out var functionParameter)) { function.Parameters.Add(functionParameter); } @@ -376,9 +383,9 @@ callStatement.Arguments.Arguments[0] is JassVariableReferenceExpressionSyntax va return trigger is not null; } - private bool TryDecompileVariableDefinitionInitialValue(IExpressionSyntax expression, string type, [NotNullWhen(true)] out string? initialValue) + private bool TryDecompileVariableDefinitionInitialValue(JassExpressionSyntax expression, string type, [NotNullWhen(true)] out string? initialValue) { - if (expression is not JassNullLiteralExpressionSyntax && + if (expression.SyntaxKind != JassSyntaxKind.NullLiteralExpression && (!Context.TriggerData.TriggerData.TriggerTypeDefaults.TryGetValue(type, out var typeDefault) || !string.Equals(typeDefault.ScriptText, expression.ToString(), StringComparison.Ordinal)) && TryDecompileTriggerFunctionParameter(expression, type, out var functionParameter)) diff --git a/src/War3Net.CodeAnalysis.Decompilers/Script/Special/CustomScriptCodeDecompiler.cs b/src/War3Net.CodeAnalysis.Decompilers/Script/Special/CustomScriptCodeDecompiler.cs index 697fa58c..f6fe5dd2 100644 --- a/src/War3Net.CodeAnalysis.Decompilers/Script/Special/CustomScriptCodeDecompiler.cs +++ b/src/War3Net.CodeAnalysis.Decompilers/Script/Special/CustomScriptCodeDecompiler.cs @@ -12,21 +12,6 @@ namespace War3Net.CodeAnalysis.Decompilers { public partial class JassScriptDecompiler { - private TriggerFunction DecompileCustomScriptAction(IDeclarationLineSyntax declarationLine) - { - return DecompileCustomScriptAction(declarationLine.ToString()); - } - - private TriggerFunction DecompileCustomScriptAction(IGlobalLineSyntax globalLine) - { - return DecompileCustomScriptAction(globalLine.ToString()); - } - - private TriggerFunction DecompileCustomScriptAction(IStatementLineSyntax statementLine) - { - return DecompileCustomScriptAction(statementLine.ToString()); - } - private TriggerFunction DecompileCustomScriptAction(string customScriptCode) { return new TriggerFunction diff --git a/src/War3Net.CodeAnalysis.Decompilers/Script/Special/ForEachLoopDecompiler.cs b/src/War3Net.CodeAnalysis.Decompilers/Script/Special/ForEachLoopDecompiler.cs index 3a8a8b33..2f19d337 100644 --- a/src/War3Net.CodeAnalysis.Decompilers/Script/Special/ForEachLoopDecompiler.cs +++ b/src/War3Net.CodeAnalysis.Decompilers/Script/Special/ForEachLoopDecompiler.cs @@ -22,14 +22,14 @@ private bool TryDecompileForEachLoopActionFunction(JassCallStatementSyntax callS { if (parameters.Length > 0 && string.Equals(parameters[^1], JassKeyword.Code, StringComparison.Ordinal) && - Context.TriggerData.TriggerActions.TryGetValue(callStatement.IdentifierName.Name, out var actions)) + Context.TriggerData.TriggerActions.TryGetValue(callStatement.IdentifierName.Token.Text, out var actions)) { - var action = actions.First(action => action.ArgumentTypes.Length == callStatement.Arguments.Arguments.Length - 1); + var action = actions.First(action => action.ArgumentTypes.Length == callStatement.ArgumentList.ArgumentList.Items.Length - 1); - if (callStatement.Arguments.Arguments[^1] is JassFunctionReferenceExpressionSyntax functionReferenceExpression && - Context.FunctionDeclarations.TryGetValue(functionReferenceExpression.IdentifierName.Name, out var actionsFunctionDeclaration) && + if (callStatement.ArgumentList.ArgumentList.Items[^1] is JassFunctionReferenceExpressionSyntax functionReferenceExpression && + Context.FunctionDeclarations.TryGetValue(functionReferenceExpression.IdentifierName.Token.Text, out var actionsFunctionDeclaration) && actionsFunctionDeclaration.IsActionsFunction && - TryDecompileActionStatementList(actionsFunctionDeclaration.FunctionDeclaration.Body, out var loopActionFunctions)) + TryDecompileActionStatements(actionsFunctionDeclaration.FunctionDeclaration.Statements, out var loopActionFunctions)) { var function = new TriggerFunction { @@ -38,9 +38,9 @@ private bool TryDecompileForEachLoopActionFunction(JassCallStatementSyntax callS Name = action.FunctionName, }; - for (var i = 0; i < callStatement.Arguments.Arguments.Length - 1; i++) + for (var i = 0; i < callStatement.ArgumentList.ArgumentList.Items.Length - 1; i++) { - if (TryDecompileTriggerFunctionParameter(callStatement.Arguments.Arguments[i], action.ArgumentTypes[i], out var functionParameter)) + if (TryDecompileTriggerFunctionParameter(callStatement.ArgumentList.ArgumentList.Items[i], action.ArgumentTypes[i], out var functionParameter)) { function.Parameters.Add(functionParameter); } diff --git a/src/War3Net.CodeAnalysis.Decompilers/Script/Special/ForLoopDecompiler.cs b/src/War3Net.CodeAnalysis.Decompilers/Script/Special/ForLoopDecompiler.cs index 1fa6e1db..22ce2fab 100644 --- a/src/War3Net.CodeAnalysis.Decompilers/Script/Special/ForLoopDecompiler.cs +++ b/src/War3Net.CodeAnalysis.Decompilers/Script/Special/ForLoopDecompiler.cs @@ -20,28 +20,28 @@ public partial class JassScriptDecompiler { private bool TryDecompileForLoopActionFunction( JassSetStatementSyntax setStatement, - IStatementSyntax? statement2, - IStatementSyntax? statement3, + JassStatementSyntax? statement2, + JassStatementSyntax? statement3, ref List functions) { if (statement2 is JassSetStatementSyntax setIndexEndStatement && statement3 is JassLoopStatementSyntax loopStatement && - loopStatement.Body.Statements.Length >= 2 && - loopStatement.Body.Statements[0] is JassExitStatementSyntax exitStatement && + loopStatement.Statements.Length >= 2 && + loopStatement.Statements[0] is JassExitStatementSyntax exitStatement && exitStatement.Condition.Deparenthesize() is JassBinaryExpressionSyntax exitExpression && - exitExpression.Operator == BinaryOperatorType.GreaterThan && - exitExpression.Left is JassVariableReferenceExpressionSyntax exitLeftVariableReferenceExpression && - exitExpression.Right is JassVariableReferenceExpressionSyntax exitRightVariableReferenceExpression && - loopStatement.Body.Statements[^1] is JassSetStatementSyntax incrementStatement && + exitExpression.SyntaxKind == JassSyntaxKind.GreaterThanExpression && + exitExpression.Left.TryGetIdentifierNameValue(out var exitLeftVariableName) && + exitExpression.Right.TryGetIdentifierNameValue(out var exitRightVariableName) && + loopStatement.Statements[^1] is JassSetStatementSyntax incrementStatement && incrementStatement.Value.Expression is JassBinaryExpressionSyntax incrementExpression && - incrementExpression.Operator == BinaryOperatorType.Add && - incrementExpression.Left is JassVariableReferenceExpressionSyntax incrementVariableReferenceExpression && - incrementExpression.Right is JassDecimalLiteralExpressionSyntax incrementLiteralExpression && - incrementLiteralExpression.Value == 1 && - string.Equals(setStatement.IdentifierName.Name, exitLeftVariableReferenceExpression.IdentifierName.Name, StringComparison.Ordinal) && - string.Equals(setIndexEndStatement.IdentifierName.Name, exitRightVariableReferenceExpression.IdentifierName.Name, StringComparison.Ordinal) && - string.Equals(setStatement.IdentifierName.Name, incrementStatement.IdentifierName.Name, StringComparison.Ordinal) && - string.Equals(setStatement.IdentifierName.Name, incrementVariableReferenceExpression.IdentifierName.Name, StringComparison.Ordinal)) + incrementExpression.SyntaxKind == JassSyntaxKind.AddExpression && + incrementExpression.Left.TryGetIdentifierNameValue(out var incrementVariableName) && + incrementExpression.Right.TryGetIntegerExpressionValue(out var incrementValue) && + incrementValue == 1 && + string.Equals(setStatement.IdentifierName.Token.Text, exitLeftVariableName, StringComparison.Ordinal) && + string.Equals(setIndexEndStatement.IdentifierName.Token.Text, exitRightVariableName, StringComparison.Ordinal) && + string.Equals(setStatement.IdentifierName.Token.Text, incrementStatement.IdentifierName.Token.Text, StringComparison.Ordinal) && + string.Equals(setStatement.IdentifierName.Token.Text, incrementVariableName, StringComparison.Ordinal)) { var loopFunction = new TriggerFunction { @@ -49,10 +49,10 @@ incrementExpression.Right is JassDecimalLiteralExpressionSyntax incrementLiteral IsEnabled = true, }; - var loopBody = new JassStatementListSyntax(ImmutableArray.CreateRange(loopStatement.Body.Statements, 1, loopStatement.Body.Statements.Length - 2, statement => statement)); + var loopBody = ImmutableArray.CreateRange(loopStatement.Statements, 1, loopStatement.Statements.Length - 2, statement => statement); if (TryDecompileTriggerFunctionParameter(setStatement.Value.Expression, JassKeyword.Integer, out var indexFunctionParameter) && TryDecompileTriggerFunctionParameter(setIndexEndStatement.Value.Expression, JassKeyword.Integer, out var indexEndFunctionParameter) && - TryDecompileActionStatementList(loopBody, out var loopActionFunctions)) + TryDecompileActionStatements(loopBody, out var loopActionFunctions)) { loopFunction.Parameters.Add(indexFunctionParameter); loopFunction.Parameters.Add(indexEndFunctionParameter); @@ -68,13 +68,13 @@ incrementExpression.Right is JassDecimalLiteralExpressionSyntax incrementLiteral return false; } - if (string.Equals(setStatement.IdentifierName.Name, "bj_forLoopAIndex", StringComparison.Ordinal) && - string.Equals(setIndexEndStatement.IdentifierName.Name, "bj_forLoopAIndexEnd", StringComparison.Ordinal)) + if (string.Equals(setStatement.IdentifierName.Token.Text, "bj_forLoopAIndex", StringComparison.Ordinal) && + string.Equals(setIndexEndStatement.IdentifierName.Token.Text, "bj_forLoopAIndexEnd", StringComparison.Ordinal)) { loopFunction.Name = "ForLoopAMultiple"; } - else if (string.Equals(setStatement.IdentifierName.Name, "bj_forLoopBIndex", StringComparison.Ordinal) && - string.Equals(setIndexEndStatement.IdentifierName.Name, "bj_forLoopBIndexEnd", StringComparison.Ordinal)) + else if (string.Equals(setStatement.IdentifierName.Token.Text, "bj_forLoopBIndex", StringComparison.Ordinal) && + string.Equals(setIndexEndStatement.IdentifierName.Token.Text, "bj_forLoopBIndexEnd", StringComparison.Ordinal)) { loopFunction.Name = "ForLoopBMultiple"; } @@ -94,32 +94,32 @@ incrementExpression.Right is JassDecimalLiteralExpressionSyntax incrementLiteral private bool TryDecompileForLoopVarActionFunction( JassSetStatementSyntax setStatement, - IStatementSyntax? statement2, + JassStatementSyntax? statement2, ref List functions) { if (statement2 is JassLoopStatementSyntax loopStatement && - loopStatement.Body.Statements.Length >= 2 && - loopStatement.Body.Statements[0] is JassExitStatementSyntax exitStatement && + loopStatement.Statements.Length >= 2 && + loopStatement.Statements[0] is JassExitStatementSyntax exitStatement && exitStatement.Condition.Deparenthesize() is JassBinaryExpressionSyntax exitExpression && - exitExpression.Operator == BinaryOperatorType.GreaterThan && - loopStatement.Body.Statements[^1] is JassSetStatementSyntax incrementStatement && + exitExpression.SyntaxKind == JassSyntaxKind.GreaterThanExpression && + loopStatement.Statements[^1] is JassSetStatementSyntax incrementStatement && incrementStatement.Value.Expression is JassBinaryExpressionSyntax incrementExpression && - incrementExpression.Operator == BinaryOperatorType.Add && - incrementExpression.Right is JassDecimalLiteralExpressionSyntax incrementLiteralExpression && - incrementLiteralExpression.Value == 1 && + incrementExpression.SyntaxKind == JassSyntaxKind.AddExpression && + incrementExpression.Right.TryGetIntegerExpressionValue(out var incrementValue) && + incrementValue == 1 && TryDecompileTriggerFunctionParameterVariable(setStatement, out var variableFunctionParameter, out var variableType) && string.Equals(variableType, JassKeyword.Integer, StringComparison.Ordinal) && TryDecompileTriggerFunctionParameter(setStatement.Value.Expression, JassKeyword.Integer, out var indexFunctionParameter) && TryDecompileTriggerFunctionParameter(exitExpression.Right, JassKeyword.Integer, out var indexEndFunctionParameter)) { - if (setStatement.Indexer is null) + if (setStatement.ElementAccessClause is null) { - if (incrementStatement.Indexer is null && - exitExpression.Left is JassVariableReferenceExpressionSyntax exitLeftVariableReferenceExpression && - incrementExpression.Left is JassVariableReferenceExpressionSyntax incrementVariableReferenceExpression && - string.Equals(setStatement.IdentifierName.Name, incrementStatement.IdentifierName.Name, StringComparison.Ordinal) && - string.Equals(setStatement.IdentifierName.Name, exitLeftVariableReferenceExpression.IdentifierName.Name, StringComparison.Ordinal) && - string.Equals(setStatement.IdentifierName.Name, incrementVariableReferenceExpression.IdentifierName.Name, StringComparison.Ordinal)) + if (incrementStatement.ElementAccessClause is null && + exitExpression.Left.TryGetIdentifierNameValue(out var exitLeftVariableName) && + incrementExpression.Left.TryGetIdentifierNameValue(out var incrementVariableName) && + string.Equals(setStatement.IdentifierName.Token.Text, incrementStatement.IdentifierName.Token.Text, StringComparison.Ordinal) && + string.Equals(setStatement.IdentifierName.Token.Text, exitLeftVariableName, StringComparison.Ordinal) && + string.Equals(setStatement.IdentifierName.Token.Text, incrementVariableName, StringComparison.Ordinal)) { } else @@ -129,16 +129,16 @@ incrementExpression.Left is JassVariableReferenceExpressionSyntax incrementVaria } else { - var indexer = setStatement.Indexer.ToString(); - if (incrementStatement.Indexer is not null && - exitExpression.Left is JassArrayReferenceExpressionSyntax exitLeftArrayReferenceExpression && - incrementExpression.Left is JassArrayReferenceExpressionSyntax incrementArrayReferenceExpression && - string.Equals(setStatement.IdentifierName.Name, incrementStatement.IdentifierName.Name, StringComparison.Ordinal) && - string.Equals(setStatement.IdentifierName.Name, exitLeftArrayReferenceExpression.IdentifierName.Name, StringComparison.Ordinal) && - string.Equals(setStatement.IdentifierName.Name, incrementArrayReferenceExpression.IdentifierName.Name, StringComparison.Ordinal) && - string.Equals(indexer, incrementStatement.Indexer.ToString(), StringComparison.Ordinal) && - string.Equals(indexer, exitLeftArrayReferenceExpression.Indexer.ToString(), StringComparison.Ordinal) && - string.Equals(indexer, incrementArrayReferenceExpression.Indexer.ToString(), StringComparison.Ordinal)) + var elementAccess = setStatement.ElementAccessClause.ToString(); + if (incrementStatement.ElementAccessClause is not null && + exitExpression.Left is JassElementAccessExpressionSyntax exitLeftElementAccessExpression && + incrementExpression.Left is JassElementAccessExpressionSyntax incrementElementAccessExpression && + string.Equals(setStatement.IdentifierName.Token.Text, incrementStatement.IdentifierName.Token.Text, StringComparison.Ordinal) && + string.Equals(setStatement.IdentifierName.Token.Text, exitLeftElementAccessExpression.IdentifierName.Token.Text, StringComparison.Ordinal) && + string.Equals(setStatement.IdentifierName.Token.Text, incrementElementAccessExpression.IdentifierName.Token.Text, StringComparison.Ordinal) && + string.Equals(elementAccess, incrementStatement.ElementAccessClause.ToString(), StringComparison.Ordinal) && + string.Equals(elementAccess, exitLeftElementAccessExpression.ElementAccessClause.ToString(), StringComparison.Ordinal) && + string.Equals(elementAccess, incrementElementAccessExpression.ElementAccessClause.ToString(), StringComparison.Ordinal)) { } else @@ -158,8 +158,8 @@ incrementExpression.Left is JassArrayReferenceExpressionSyntax incrementArrayRef loopFunction.Parameters.Add(indexFunctionParameter); loopFunction.Parameters.Add(indexEndFunctionParameter); - var loopBody = new JassStatementListSyntax(ImmutableArray.CreateRange(loopStatement.Body.Statements, 1, loopStatement.Body.Statements.Length - 2, statement => statement)); - if (TryDecompileActionStatementList(loopBody, out var loopActionFunctions)) + var loopBody = ImmutableArray.CreateRange(loopStatement.Statements, 1, loopStatement.Statements.Length - 2, statement => statement); + if (TryDecompileActionStatements(loopBody, out var loopActionFunctions)) { foreach (var loopActionFunction in loopActionFunctions) { diff --git a/src/War3Net.CodeAnalysis.Decompilers/Script/Special/IfThenElseDecompiler.cs b/src/War3Net.CodeAnalysis.Decompilers/Script/Special/IfThenElseDecompiler.cs index 96c81dec..0f3c716f 100644 --- a/src/War3Net.CodeAnalysis.Decompilers/Script/Special/IfThenElseDecompiler.cs +++ b/src/War3Net.CodeAnalysis.Decompilers/Script/Special/IfThenElseDecompiler.cs @@ -27,25 +27,25 @@ private bool TryDecompileIfThenElseActionFunction(JassIfStatementSyntax ifStatem Name = "IfThenElseMultiple", }; - if (!TryDecompileActionStatementList(ifStatement.Body, out var thenActions) || - !TryDecompileActionStatementList(ifStatement.ElseClause.Body, out var elseActions)) + if (!TryDecompileActionStatements(ifStatement.IfClause.Statements, out var thenActions) || + !TryDecompileActionStatements(ifStatement.ElseClause.Statements, out var elseActions)) { actionFunction = null; return false; } - var conditionExpression = ifStatement.Condition.Deparenthesize(); + var conditionExpression = ifStatement.IfClause.IfClauseDeclarator.Condition.Deparenthesize(); if (conditionExpression is JassInvocationExpressionSyntax conditionInvocationExpression && - Context.FunctionDeclarations.TryGetValue(conditionInvocationExpression.IdentifierName.Name, out var conditionsFunctionDeclaration) && + Context.FunctionDeclarations.TryGetValue(conditionInvocationExpression.IdentifierName.Token.Text, out var conditionsFunctionDeclaration) && conditionsFunctionDeclaration.IsConditionsFunction) { var conditionsFunction = conditionsFunctionDeclaration.FunctionDeclaration; - if (conditionsFunction.Body.Statements.Length == 1 && + if (conditionsFunction.Statements.Length == 1 && thenActions.Count == 1 && elseActions.Count == 1 && - TryDecompileConditionStatement(conditionsFunction.Body.Statements[0], true, out var conditionFunction)) + TryDecompileConditionStatement(conditionsFunction.Statements[0], true, out var conditionFunction)) { function.Name = "IfThenElse"; @@ -75,7 +75,7 @@ private bool TryDecompileIfThenElseActionFunction(JassIfStatementSyntax ifStatem } else { - if (TryDecompileConditionStatementList(conditionsFunction.Body, out var conditionFunctions)) + if (TryDecompileConditionStatements(conditionsFunction.Statements, out var conditionFunctions)) { foreach (var condition in conditionFunctions) { diff --git a/src/War3Net.CodeAnalysis.Decompilers/Script/Special/WaitForConditionDecompiler.cs b/src/War3Net.CodeAnalysis.Decompilers/Script/Special/WaitForConditionDecompiler.cs index 70082ad7..c48003ab 100644 --- a/src/War3Net.CodeAnalysis.Decompilers/Script/Special/WaitForConditionDecompiler.cs +++ b/src/War3Net.CodeAnalysis.Decompilers/Script/Special/WaitForConditionDecompiler.cs @@ -19,26 +19,26 @@ public partial class JassScriptDecompiler { private bool TryDecompileWaitForConditionActionFunction(JassLoopStatementSyntax loopStatement, [NotNullWhen(true)] out TriggerFunction? actionFunction) { - if (loopStatement.Body.Statements.Length == 2 && - loopStatement.Body.Statements[0] is JassExitStatementSyntax exitStatement && + if (loopStatement.Statements.Length == 2 && + loopStatement.Statements[0] is JassExitStatementSyntax exitStatement && exitStatement.Condition.Deparenthesize() is JassInvocationExpressionSyntax exitInvocationExpression && - exitInvocationExpression.Arguments.Arguments.IsEmpty && - loopStatement.Body.Statements[1] is JassCallStatementSyntax callStatement && - string.Equals(callStatement.IdentifierName.Name, "TriggerSleepAction", StringComparison.Ordinal) && - callStatement.Arguments.Arguments.Length == 1 && - callStatement.Arguments.Arguments[0] is JassInvocationExpressionSyntax callInvocationExpression && - string.Equals(callInvocationExpression.IdentifierName.Name, "RMaxBJ", StringComparison.Ordinal) && - callInvocationExpression.Arguments.Arguments.Length == 2 && - callInvocationExpression.Arguments.Arguments[0] is JassVariableReferenceExpressionSyntax variableReferenceExpression && - string.Equals(variableReferenceExpression.IdentifierName.Name, "bj_WAIT_FOR_COND_MIN_INTERVAL", StringComparison.Ordinal) && - Context.FunctionDeclarations.TryGetValue(exitInvocationExpression.IdentifierName.Name, out var exitFunctionDeclaration) && + exitInvocationExpression.ArgumentList.ArgumentList.Items.IsEmpty && + loopStatement.Statements[1] is JassCallStatementSyntax callStatement && + string.Equals(callStatement.IdentifierName.Token.Text, "TriggerSleepAction", StringComparison.Ordinal) && + callStatement.ArgumentList.ArgumentList.Items.Length == 1 && + callStatement.ArgumentList.ArgumentList.Items[0] is JassInvocationExpressionSyntax callInvocationExpression && + string.Equals(callInvocationExpression.IdentifierName.Token.Text, "RMaxBJ", StringComparison.Ordinal) && + callInvocationExpression.ArgumentList.ArgumentList.Items.Length == 2 && + callInvocationExpression.ArgumentList.ArgumentList.Items[0].TryGetIdentifierNameValue(out var variableName) && + string.Equals(variableName, "bj_WAIT_FOR_COND_MIN_INTERVAL", StringComparison.Ordinal) && + Context.FunctionDeclarations.TryGetValue(exitInvocationExpression.IdentifierName.Token.Text, out var exitFunctionDeclaration) && exitFunctionDeclaration.IsConditionsFunction) { var exitFunction = exitFunctionDeclaration.FunctionDeclaration; - if (exitFunction.Body.Statements.Length == 1 && - TryDecompileConditionStatement(exitFunction.Body.Statements[0], true, out var conditionFunction) && - TryDecompileTriggerFunctionParameter(callInvocationExpression.Arguments.Arguments[1], JassKeyword.Real, out var intervalParameter)) + if (exitFunction.Statements.Length == 1 && + TryDecompileConditionStatement(exitFunction.Statements[0], true, out var conditionFunction) && + TryDecompileTriggerFunctionParameter(callInvocationExpression.ArgumentList.ArgumentList.Items[1], JassKeyword.Real, out var intervalParameter)) { actionFunction = new TriggerFunction { diff --git a/src/War3Net.CodeAnalysis.Decompilers/Script/SyntaxDecompilers/BinaryExpressionDecompiler.cs b/src/War3Net.CodeAnalysis.Decompilers/Script/SyntaxDecompilers/BinaryExpressionDecompiler.cs index 6217c588..be3e2701 100644 --- a/src/War3Net.CodeAnalysis.Decompilers/Script/SyntaxDecompilers/BinaryExpressionDecompiler.cs +++ b/src/War3Net.CodeAnalysis.Decompilers/Script/SyntaxDecompilers/BinaryExpressionDecompiler.cs @@ -98,8 +98,8 @@ private bool TryDecompileBinaryExpression( } else { - if (TryDecompileBinaryOperatorType( - binaryExpression.Operator, + if (TryDecompileBinaryOperator( + binaryExpression.OperatorToken, leftOptions[left].Type, leftOptions[left].Parameter, rightOptions[right].Parameter, diff --git a/src/War3Net.CodeAnalysis.Decompilers/Script/SyntaxDecompilers/BinaryOperatorTypeDecompiler.cs b/src/War3Net.CodeAnalysis.Decompilers/Script/SyntaxDecompilers/BinaryOperatorDecompiler.cs similarity index 86% rename from src/War3Net.CodeAnalysis.Decompilers/Script/SyntaxDecompilers/BinaryOperatorTypeDecompiler.cs rename to src/War3Net.CodeAnalysis.Decompilers/Script/SyntaxDecompilers/BinaryOperatorDecompiler.cs index 83498e98..b8f3e1dc 100644 --- a/src/War3Net.CodeAnalysis.Decompilers/Script/SyntaxDecompilers/BinaryOperatorTypeDecompiler.cs +++ b/src/War3Net.CodeAnalysis.Decompilers/Script/SyntaxDecompilers/BinaryOperatorDecompiler.cs @@ -1,5 +1,5 @@ // ------------------------------------------------------------------------------ -// +// // Licensed under the MIT license. // See the LICENSE file in the project root for more information. // @@ -16,8 +16,8 @@ namespace War3Net.CodeAnalysis.Decompilers { public partial class JassScriptDecompiler { - private bool TryDecompileBinaryOperatorType( - BinaryOperatorType binaryOperatorType, + private bool TryDecompileBinaryOperator( + JassSyntaxToken operatorToken, string expectedType, TriggerFunctionParameter leftOperandParameter, TriggerFunctionParameter rightOperandParameter, @@ -36,7 +36,7 @@ private bool TryDecompileBinaryOperatorType( { if (triggerCall.ArgumentTypes.Length == 2) { - if (binaryOperatorType == BinaryOperatorType.Add) + if (operatorToken.SyntaxKind == JassSyntaxKind.PlusToken) { function = new TriggerFunction { @@ -53,7 +53,7 @@ private bool TryDecompileBinaryOperatorType( } else if (triggerCall.ArgumentTypes.Length == 3) { - if (TryDecompileTriggerFunctionParameter(binaryOperatorType, triggerCall.ArgumentTypes[1], out var operatorFunctionParameter)) + if (TryDecompileTriggerFunctionParameter(operatorToken, triggerCall.ArgumentTypes[1], out var operatorFunctionParameter)) { function = new TriggerFunction { diff --git a/src/War3Net.CodeAnalysis.Decompilers/Script/SyntaxDecompilers/BooleanLiteralExpressionDecompiler.cs b/src/War3Net.CodeAnalysis.Decompilers/Script/SyntaxDecompilers/BooleanLiteralExpressionDecompiler.cs index a441177f..5412d7bd 100644 --- a/src/War3Net.CodeAnalysis.Decompilers/Script/SyntaxDecompilers/BooleanLiteralExpressionDecompiler.cs +++ b/src/War3Net.CodeAnalysis.Decompilers/Script/SyntaxDecompilers/BooleanLiteralExpressionDecompiler.cs @@ -18,7 +18,7 @@ namespace War3Net.CodeAnalysis.Decompilers public partial class JassScriptDecompiler { private bool TryDecompileBooleanLiteralExpression( - JassBooleanLiteralExpressionSyntax booleanLiteralExpression, + JassLiteralExpressionSyntax booleanLiteralExpression, string expectedType, [NotNullWhen(true)] out TriggerFunctionParameter? functionParameter) { @@ -27,12 +27,12 @@ private bool TryDecompileBooleanLiteralExpression( functionParameter = new TriggerFunctionParameter { Type = TriggerFunctionParameterType.String, - Value = booleanLiteralExpression.ToString(), + Value = booleanLiteralExpression.Token.Text, }; return true; } - else if (TryDecompileTriggerFunctionParameterPreset(booleanLiteralExpression.ToString(), expectedType, out _, out functionParameter)) + else if (TryDecompileTriggerFunctionParameterPreset(booleanLiteralExpression.Token.Text, expectedType, out _, out functionParameter)) { return true; } @@ -42,7 +42,7 @@ private bool TryDecompileBooleanLiteralExpression( } private bool TryDecompileBooleanLiteralExpression( - JassBooleanLiteralExpressionSyntax booleanLiteralExpression, + JassLiteralExpressionSyntax booleanLiteralExpression, [NotNullWhen(true)] out List? decompileOptions) { if (TryDecompileBooleanLiteralExpression(booleanLiteralExpression, JassKeyword.Boolean, out var functionParameter)) diff --git a/src/War3Net.CodeAnalysis.Decompilers/Script/SyntaxDecompilers/CallStatementDecompiler.cs b/src/War3Net.CodeAnalysis.Decompilers/Script/SyntaxDecompilers/CallStatementDecompiler.cs index a4ac27f8..31fe1905 100644 --- a/src/War3Net.CodeAnalysis.Decompilers/Script/SyntaxDecompilers/CallStatementDecompiler.cs +++ b/src/War3Net.CodeAnalysis.Decompilers/Script/SyntaxDecompilers/CallStatementDecompiler.cs @@ -19,12 +19,12 @@ private bool TryDecompileCallStatement( JassCallStatementSyntax callStatement, ref List functions) { - if (!Context.TriggerData.TriggerActions.TryGetValue(callStatement.IdentifierName.Name, out var actions)) + if (!Context.TriggerData.TriggerActions.TryGetValue(callStatement.IdentifierName.Token.Text, out var actions)) { return false; } - var action = actions.First(action => action.ArgumentTypes.Length == callStatement.Arguments.Arguments.Length); + var action = actions.First(action => action.ArgumentTypes.Length == callStatement.ArgumentList.ArgumentList.Items.Length); if (TryDecompileForEachLoopActionFunction(callStatement, action.ArgumentTypes, out var loopActionFunction)) { @@ -39,9 +39,9 @@ private bool TryDecompileCallStatement( Name = action.FunctionName, }; - for (var j = 0; j < callStatement.Arguments.Arguments.Length; j++) + for (var j = 0; j < callStatement.ArgumentList.ArgumentList.Items.Length; j++) { - if (TryDecompileTriggerFunctionParameter(callStatement.Arguments.Arguments[j], action.ArgumentTypes[j], out var functionParameter)) + if (TryDecompileTriggerFunctionParameter(callStatement.ArgumentList.ArgumentList.Items[j], action.ArgumentTypes[j], out var functionParameter)) { function.Parameters.Add(functionParameter); } diff --git a/src/War3Net.CodeAnalysis.Decompilers/Script/SyntaxDecompilers/CharacterLiteralExpressionDecompiler.cs b/src/War3Net.CodeAnalysis.Decompilers/Script/SyntaxDecompilers/CharacterLiteralExpressionDecompiler.cs index fd1e05d6..d20c1992 100644 --- a/src/War3Net.CodeAnalysis.Decompilers/Script/SyntaxDecompilers/CharacterLiteralExpressionDecompiler.cs +++ b/src/War3Net.CodeAnalysis.Decompilers/Script/SyntaxDecompilers/CharacterLiteralExpressionDecompiler.cs @@ -19,7 +19,7 @@ namespace War3Net.CodeAnalysis.Decompilers public partial class JassScriptDecompiler { private bool TryDecompileCharacterLiteralExpression( - JassCharacterLiteralExpressionSyntax characterLiteralExpression, + JassLiteralExpressionSyntax characterLiteralExpression, string expectedType, [NotNullWhen(true)] out TriggerFunctionParameter? functionParameter) { @@ -29,7 +29,7 @@ private bool TryDecompileCharacterLiteralExpression( functionParameter = new TriggerFunctionParameter { Type = TriggerFunctionParameterType.String, - Value = ((int)characterLiteralExpression.Value).ToString(CultureInfo.InvariantCulture), + Value = ((int)JassLiteral.ParseChar(characterLiteralExpression.Token.Text)).ToString(CultureInfo.InvariantCulture), }; return true; @@ -40,10 +40,10 @@ private bool TryDecompileCharacterLiteralExpression( } private bool TryDecompileCharacterLiteralExpression( - JassCharacterLiteralExpressionSyntax characterLiteralExpression, + JassLiteralExpressionSyntax characterLiteralExpression, [NotNullWhen(true)] out List? decompileOptions) { - var value = ((int)characterLiteralExpression.Value).ToString(CultureInfo.InvariantCulture); + var value = ((int)JassLiteral.ParseChar(characterLiteralExpression.Token.Text)).ToString(CultureInfo.InvariantCulture); decompileOptions = new(); diff --git a/src/War3Net.CodeAnalysis.Decompilers/Script/SyntaxDecompilers/CommentDecompiler.cs b/src/War3Net.CodeAnalysis.Decompilers/Script/SyntaxDecompilers/CommentDecompiler.cs deleted file mode 100644 index abedc8e6..00000000 --- a/src/War3Net.CodeAnalysis.Decompilers/Script/SyntaxDecompilers/CommentDecompiler.cs +++ /dev/null @@ -1,44 +0,0 @@ -// ------------------------------------------------------------------------------ -// -// Licensed under the MIT license. -// See the LICENSE file in the project root for more information. -// -// ------------------------------------------------------------------------------ - -using System.Collections.Generic; - -using War3Net.Build.Script; -using War3Net.CodeAnalysis.Jass.Syntax; - -namespace War3Net.CodeAnalysis.Decompilers -{ - public partial class JassScriptDecompiler - { - private bool TryDecompileComment( - JassCommentSyntax comment, - ref List functions) - { - if (comment.Comment.Length > 1 && comment.Comment.StartsWith(' ')) - { - functions.Add(new TriggerFunction - { - Type = TriggerFunctionType.Action, - IsEnabled = true, - Name = "CommentString", - Parameters = new() - { - new TriggerFunctionParameter - { - Type = TriggerFunctionParameterType.String, - Value = comment.Comment[1..], - }, - }, - }); - - return true; - } - - return false; - } - } -} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Decompilers/Script/SyntaxDecompilers/DecimalLiteralExpressionDecompiler.cs b/src/War3Net.CodeAnalysis.Decompilers/Script/SyntaxDecompilers/DecimalLiteralExpressionDecompiler.cs index 45f41b3f..d6cb8b01 100644 --- a/src/War3Net.CodeAnalysis.Decompilers/Script/SyntaxDecompilers/DecimalLiteralExpressionDecompiler.cs +++ b/src/War3Net.CodeAnalysis.Decompilers/Script/SyntaxDecompilers/DecimalLiteralExpressionDecompiler.cs @@ -18,7 +18,7 @@ namespace War3Net.CodeAnalysis.Decompilers public partial class JassScriptDecompiler { private bool TryDecompileDecimalLiteralExpression( - JassDecimalLiteralExpressionSyntax decimalLiteralExpression, + JassLiteralExpressionSyntax decimalLiteralExpression, string expectedType, [NotNullWhen(true)] out TriggerFunctionParameter? functionParameter) { @@ -28,7 +28,7 @@ private bool TryDecompileDecimalLiteralExpression( functionParameter = new TriggerFunctionParameter { Type = TriggerFunctionParameterType.String, - Value = decimalLiteralExpression.ToString(), + Value = decimalLiteralExpression.Token.Text, }; return true; @@ -39,10 +39,10 @@ private bool TryDecompileDecimalLiteralExpression( } private bool TryDecompileDecimalLiteralExpression( - JassDecimalLiteralExpressionSyntax decimalLiteralExpression, + JassLiteralExpressionSyntax decimalLiteralExpression, [NotNullWhen(true)] out List? decompileOptions) { - var value = decimalLiteralExpression.ToString(); + var value = decimalLiteralExpression.Token.Text; decompileOptions = new(); diff --git a/src/War3Net.CodeAnalysis.Decompilers/Script/SyntaxDecompilers/ArrayReferenceExpressionDecompiler.cs b/src/War3Net.CodeAnalysis.Decompilers/Script/SyntaxDecompilers/ElementAccessExpressionDecompiler.cs similarity index 72% rename from src/War3Net.CodeAnalysis.Decompilers/Script/SyntaxDecompilers/ArrayReferenceExpressionDecompiler.cs rename to src/War3Net.CodeAnalysis.Decompilers/Script/SyntaxDecompilers/ElementAccessExpressionDecompiler.cs index 8e863080..b4a640e5 100644 --- a/src/War3Net.CodeAnalysis.Decompilers/Script/SyntaxDecompilers/ArrayReferenceExpressionDecompiler.cs +++ b/src/War3Net.CodeAnalysis.Decompilers/Script/SyntaxDecompilers/ElementAccessExpressionDecompiler.cs @@ -1,5 +1,5 @@ // ------------------------------------------------------------------------------ -// +// // Licensed under the MIT license. // See the LICENSE file in the project root for more information. // @@ -18,13 +18,13 @@ namespace War3Net.CodeAnalysis.Decompilers { public partial class JassScriptDecompiler { - private bool TryDecompileArrayReferenceExpression( - JassArrayReferenceExpressionSyntax arrayReferenceExpression, + private bool TryDecompileElementAccessExpression( + JassElementAccessExpressionSyntax elementAccessExpression, string expectedType, [NotNullWhen(true)] out TriggerFunctionParameter? functionParameter) { if (Context.TriggerData.TriggerParams.TryGetValue(string.Empty, out var triggerParamsForAllTypes) && - triggerParamsForAllTypes.TryGetValue(arrayReferenceExpression.ToString(), out var triggerParams)) + triggerParamsForAllTypes.TryGetValue(elementAccessExpression.ToString(), out var triggerParams)) { var triggerParam = triggerParams.SingleOrDefault(param => string.Equals(param.VariableType, expectedType, StringComparison.Ordinal)); if (triggerParam is not null) @@ -39,10 +39,10 @@ private bool TryDecompileArrayReferenceExpression( } } - if (TryDecompileTriggerFunctionParameter(arrayReferenceExpression.Indexer, JassKeyword.Integer, out var arrayIndexer)) + if (TryDecompileTriggerFunctionParameter(elementAccessExpression.ElementAccessClause.Expression, JassKeyword.Integer, out var arrayIndexer)) { return TryDecompileVariableDeclarationReference( - arrayReferenceExpression.IdentifierName.Name, + elementAccessExpression.IdentifierName.Token.Text, arrayIndexer, expectedType, out functionParameter); @@ -52,12 +52,12 @@ private bool TryDecompileArrayReferenceExpression( return false; } - private bool TryDecompileArrayReferenceExpression( - JassArrayReferenceExpressionSyntax arrayReferenceExpression, + private bool TryDecompileElementAccessExpression( + JassElementAccessExpressionSyntax elementAccessExpression, [NotNullWhen(true)] out List? decompileOptions) { if (Context.TriggerData.TriggerParams.TryGetValue(string.Empty, out var triggerParamsForAllTypes) && - triggerParamsForAllTypes.TryGetValue(arrayReferenceExpression.ToString(), out var triggerParams) && + triggerParamsForAllTypes.TryGetValue(elementAccessExpression.ToString(), out var triggerParams) && triggerParams.Length == 1) { var triggerParam = triggerParams[0]; @@ -76,10 +76,10 @@ private bool TryDecompileArrayReferenceExpression( return true; } - if (TryDecompileTriggerFunctionParameter(arrayReferenceExpression.Indexer, JassKeyword.Integer, out var arrayIndexer)) + if (TryDecompileTriggerFunctionParameter(elementAccessExpression.ElementAccessClause.Expression, JassKeyword.Integer, out var arrayIndexer)) { return TryDecompileVariableDeclarationReference( - arrayReferenceExpression.IdentifierName.Name, + elementAccessExpression.IdentifierName.Token.Text, arrayIndexer, out decompileOptions); } diff --git a/src/War3Net.CodeAnalysis.Decompilers/Script/SyntaxDecompilers/FourCCLiteralExpressionDecompiler.cs b/src/War3Net.CodeAnalysis.Decompilers/Script/SyntaxDecompilers/FourCCLiteralExpressionDecompiler.cs index b5bb667c..8bf63920 100644 --- a/src/War3Net.CodeAnalysis.Decompilers/Script/SyntaxDecompilers/FourCCLiteralExpressionDecompiler.cs +++ b/src/War3Net.CodeAnalysis.Decompilers/Script/SyntaxDecompilers/FourCCLiteralExpressionDecompiler.cs @@ -19,11 +19,11 @@ namespace War3Net.CodeAnalysis.Decompilers public partial class JassScriptDecompiler { private bool TryDecompileFourCCLiteralExpression( - JassFourCCLiteralExpressionSyntax fourCCLiteralExpression, + JassLiteralExpressionSyntax fourCCLiteralExpression, string expectedType, [NotNullWhen(true)] out TriggerFunctionParameter? functionParameter) { - if (TryDecompileTriggerFunctionParameterPreset(fourCCLiteralExpression.ToString(), expectedType, out _, out functionParameter)) + if (TryDecompileTriggerFunctionParameterPreset(fourCCLiteralExpression.Token.Text, expectedType, out _, out functionParameter)) { return true; } @@ -33,7 +33,7 @@ private bool TryDecompileFourCCLiteralExpression( functionParameter = new TriggerFunctionParameter { Type = TriggerFunctionParameterType.String, - Value = fourCCLiteralExpression.Value.ToJassRawcode(), + Value = fourCCLiteralExpression.Token.Text[1..^1], }; return true; @@ -43,7 +43,7 @@ private bool TryDecompileFourCCLiteralExpression( functionParameter = new TriggerFunctionParameter { Type = TriggerFunctionParameterType.String, - Value = fourCCLiteralExpression.Value.ToJassRawcode(), + Value = fourCCLiteralExpression.Token.Text[1..^1], }; return true; @@ -54,7 +54,7 @@ private bool TryDecompileFourCCLiteralExpression( } private bool TryDecompileFourCCLiteralExpression( - JassFourCCLiteralExpressionSyntax fourCCLiteralExpression, + JassLiteralExpressionSyntax fourCCLiteralExpression, [NotNullWhen(true)] out List? decompileOptions) { if (Context.TriggerData.TriggerTypes.TryGetValue(JassKeyword.Integer, out var customTypes)) @@ -68,7 +68,7 @@ private bool TryDecompileFourCCLiteralExpression( Parameter = new TriggerFunctionParameter { Type = TriggerFunctionParameterType.String, - Value = fourCCLiteralExpression.Value.ToJassRawcode(), + Value = fourCCLiteralExpression.Token.Text[1..^1], }, }); } diff --git a/src/War3Net.CodeAnalysis.Decompilers/Script/SyntaxDecompilers/HexadecimalLiteralExpressionDecompiler.cs b/src/War3Net.CodeAnalysis.Decompilers/Script/SyntaxDecompilers/HexadecimalLiteralExpressionDecompiler.cs index c2860d28..c00c7734 100644 --- a/src/War3Net.CodeAnalysis.Decompilers/Script/SyntaxDecompilers/HexadecimalLiteralExpressionDecompiler.cs +++ b/src/War3Net.CodeAnalysis.Decompilers/Script/SyntaxDecompilers/HexadecimalLiteralExpressionDecompiler.cs @@ -19,17 +19,19 @@ namespace War3Net.CodeAnalysis.Decompilers public partial class JassScriptDecompiler { private bool TryDecompileHexadecimalLiteralExpression( - JassHexadecimalLiteralExpressionSyntax hexadecimalLiteralExpression, + JassLiteralExpressionSyntax hexadecimalLiteralExpression, string expectedType, [NotNullWhen(true)] out TriggerFunctionParameter? functionParameter) { if (string.Equals(expectedType, JassKeyword.Integer, StringComparison.Ordinal) || string.Equals(expectedType, JassKeyword.Real, StringComparison.Ordinal)) { + var value = JassLiteral.ParseHex(hexadecimalLiteralExpression.Token.Text); + functionParameter = new TriggerFunctionParameter { Type = TriggerFunctionParameterType.String, - Value = hexadecimalLiteralExpression.Value.ToString(CultureInfo.InvariantCulture), + Value = value.ToString(CultureInfo.InvariantCulture), }; return true; @@ -40,10 +42,10 @@ private bool TryDecompileHexadecimalLiteralExpression( } private bool TryDecompileHexadecimalLiteralExpression( - JassHexadecimalLiteralExpressionSyntax hexadecimalLiteralExpression, + JassLiteralExpressionSyntax hexadecimalLiteralExpression, [NotNullWhen(true)] out List? decompileOptions) { - var value = hexadecimalLiteralExpression.Value.ToString(CultureInfo.InvariantCulture); + var value = JassLiteral.ParseHex(hexadecimalLiteralExpression.Token.Text).ToString(CultureInfo.InvariantCulture); decompileOptions = new(); diff --git a/src/War3Net.CodeAnalysis.Decompilers/Script/SyntaxDecompilers/VariableReferenceExpressionDecompiler.cs b/src/War3Net.CodeAnalysis.Decompilers/Script/SyntaxDecompilers/IdentifierNameDecompiler.cs similarity index 91% rename from src/War3Net.CodeAnalysis.Decompilers/Script/SyntaxDecompilers/VariableReferenceExpressionDecompiler.cs rename to src/War3Net.CodeAnalysis.Decompilers/Script/SyntaxDecompilers/IdentifierNameDecompiler.cs index 2d76b9e3..61785b9d 100644 --- a/src/War3Net.CodeAnalysis.Decompilers/Script/SyntaxDecompilers/VariableReferenceExpressionDecompiler.cs +++ b/src/War3Net.CodeAnalysis.Decompilers/Script/SyntaxDecompilers/IdentifierNameDecompiler.cs @@ -1,5 +1,5 @@ // ------------------------------------------------------------------------------ -// +// // Licensed under the MIT license. // See the LICENSE file in the project root for more information. // @@ -17,13 +17,13 @@ namespace War3Net.CodeAnalysis.Decompilers { public partial class JassScriptDecompiler { - private bool TryDecompileVariableReferenceExpression( - JassVariableReferenceExpressionSyntax variableReferenceExpression, + private bool TryDecompileIdentifierName( + JassIdentifierNameSyntax identifierName, string expectedType, [NotNullWhen(true)] out TriggerFunctionParameter? functionParameter) { if (Context.TriggerData.TriggerParams.TryGetValue(string.Empty, out var triggerParamsForAllTypes) && - triggerParamsForAllTypes.TryGetValue(variableReferenceExpression.ToString(), out var triggerParams)) + triggerParamsForAllTypes.TryGetValue(identifierName.Token.Text, out var triggerParams)) { var triggerParam = triggerParams.SingleOrDefault(param => string.Equals(param.VariableType, expectedType, StringComparison.Ordinal)); if (triggerParam is not null) @@ -39,18 +39,18 @@ private bool TryDecompileVariableReferenceExpression( } return TryDecompileVariableDeclarationReference( - variableReferenceExpression.IdentifierName.Name, + identifierName.Token.Text, null, expectedType, out functionParameter); } - private bool TryDecompileVariableReferenceExpression( - JassVariableReferenceExpressionSyntax variableReferenceExpression, + private bool TryDecompileIdentifierName( + JassIdentifierNameSyntax identifierName, [NotNullWhen(true)] out List? decompileOptions) { if (Context.TriggerData.TriggerParams.TryGetValue(string.Empty, out var triggerParamsForAllTypes) && - triggerParamsForAllTypes.TryGetValue(variableReferenceExpression.ToString(), out var triggerParams) && + triggerParamsForAllTypes.TryGetValue(identifierName.Token.Text, out var triggerParams) && triggerParams.Length == 1) { var triggerParam = triggerParams[0]; @@ -70,7 +70,7 @@ private bool TryDecompileVariableReferenceExpression( } return TryDecompileVariableDeclarationReference( - variableReferenceExpression.IdentifierName.Name, + identifierName.Token.Text, null, out decompileOptions); } diff --git a/src/War3Net.CodeAnalysis.Decompilers/Script/SyntaxDecompilers/IfStatementDecompiler.cs b/src/War3Net.CodeAnalysis.Decompilers/Script/SyntaxDecompilers/IfStatementDecompiler.cs index a2b161c1..e84d2dd0 100644 --- a/src/War3Net.CodeAnalysis.Decompilers/Script/SyntaxDecompilers/IfStatementDecompiler.cs +++ b/src/War3Net.CodeAnalysis.Decompilers/Script/SyntaxDecompilers/IfStatementDecompiler.cs @@ -9,6 +9,7 @@ using System.Diagnostics.CodeAnalysis; using War3Net.Build.Script; +using War3Net.CodeAnalysis.Jass; using War3Net.CodeAnalysis.Jass.Extensions; using War3Net.CodeAnalysis.Jass.Syntax; @@ -26,9 +27,9 @@ private bool TryDecompileIfStatement( return true; } - functions.Add(DecompileCustomScriptAction(new JassIfCustomScriptAction(ifStatement.Condition))); + functions.Add(DecompileCustomScriptAction(ifStatement.IfClause.IfClauseDeclarator.ToString())); - if (TryDecompileActionStatementList(ifStatement.Body, out var thenActions)) + if (TryDecompileActionStatements(ifStatement.IfClause.Statements, out var thenActions)) { functions.AddRange(thenActions); } @@ -39,9 +40,10 @@ private bool TryDecompileIfStatement( foreach (var elseIfClause in ifStatement.ElseIfClauses) { - functions.Add(DecompileCustomScriptAction(new JassElseIfCustomScriptAction(elseIfClause.Condition))); + DecompileLeadingTrivia(elseIfClause.ElseIfClauseDeclarator.ElseIfToken.LeadingTrivia, ref functions); + functions.Add(DecompileCustomScriptAction(elseIfClause.ElseIfClauseDeclarator.ToString())); - if (TryDecompileActionStatementList(elseIfClause.Body, out var elseIfActions)) + if (TryDecompileActionStatements(elseIfClause.Statements, out var elseIfActions)) { functions.AddRange(elseIfActions); } @@ -53,9 +55,10 @@ private bool TryDecompileIfStatement( if (ifStatement.ElseClause is not null) { - functions.Add(DecompileCustomScriptAction(JassElseCustomScriptAction.Value)); + DecompileLeadingTrivia(ifStatement.ElseClause.ElseToken.LeadingTrivia, ref functions); + functions.Add(DecompileCustomScriptAction(ifStatement.ElseClause.ElseToken.ToString())); - if (TryDecompileActionStatementList(ifStatement.ElseClause.Body, out var elseActions)) + if (TryDecompileActionStatements(ifStatement.ElseClause.Statements, out var elseActions)) { functions.AddRange(elseActions); } @@ -65,7 +68,8 @@ private bool TryDecompileIfStatement( } } - functions.Add(DecompileCustomScriptAction(JassEndIfCustomScriptAction.Value)); + DecompileLeadingTrivia(ifStatement.EndIfToken.LeadingTrivia, ref functions); + functions.Add(DecompileCustomScriptAction(ifStatement.EndIfToken.ToString())); return true; } @@ -78,17 +82,18 @@ private bool TryDecompileIfStatement( { if (ifStatement.ElseIfClauses.IsEmpty && ifStatement.ElseClause is null && - ifStatement.Body.Statements.Length == 1 && - ifStatement.Body.Statements[0] is JassReturnStatementSyntax returnStatement && - returnStatement.Value is JassBooleanLiteralExpressionSyntax booleanLiteralExpression && - booleanLiteralExpression.Value != returnValue) + ifStatement.IfClause.Statements.Length == 1 && + ifStatement.IfClause.Statements[0] is JassReturnStatementSyntax returnStatement && + returnStatement.Value is not null && + returnStatement.Value.TryGetBooleanExpressionValue(out var returnStatementValue) && + returnStatementValue != returnValue) { - var conditionExpression = ifStatement.Condition.Deparenthesize(); + var conditionExpression = ifStatement.IfClause.IfClauseDeclarator.Condition.Deparenthesize(); if (returnValue) { if (conditionExpression is JassUnaryExpressionSyntax unaryExpression && - unaryExpression.Operator == UnaryOperatorType.Not) + unaryExpression.SyntaxKind == JassSyntaxKind.LogicalNotExpression) { conditionExpression = unaryExpression.Expression.Deparenthesize(); } diff --git a/src/War3Net.CodeAnalysis.Decompilers/Script/SyntaxDecompilers/InvocationExpressionDecompiler.cs b/src/War3Net.CodeAnalysis.Decompilers/Script/SyntaxDecompilers/InvocationExpressionDecompiler.cs index 85a67899..74192db8 100644 --- a/src/War3Net.CodeAnalysis.Decompilers/Script/SyntaxDecompilers/InvocationExpressionDecompiler.cs +++ b/src/War3Net.CodeAnalysis.Decompilers/Script/SyntaxDecompilers/InvocationExpressionDecompiler.cs @@ -26,7 +26,7 @@ private bool TryDecompileInvocationExpression( return true; } - if (Context.TriggerData.TriggerData.TriggerCalls.TryGetValue(invocationExpression.IdentifierName.Name, out var triggerCall)) + if (Context.TriggerData.TriggerData.TriggerCalls.TryGetValue(invocationExpression.IdentifierName.Token.Text, out var triggerCall)) { if (string.Equals(triggerCall.ReturnType, expectedType, StringComparison.Ordinal) && TryDecompileTriggerCallFunction(invocationExpression, triggerCall, out var callFunction)) @@ -34,7 +34,7 @@ private bool TryDecompileInvocationExpression( functionParameter = new TriggerFunctionParameter { Type = TriggerFunctionParameterType.Function, - Value = invocationExpression.IdentifierName.Name, + Value = invocationExpression.IdentifierName.Token.Text, Function = callFunction, }; @@ -42,17 +42,17 @@ private bool TryDecompileInvocationExpression( } } - if (string.Equals(invocationExpression.IdentifierName.Name, "Condition", StringComparison.Ordinal)) + if (string.Equals(invocationExpression.IdentifierName.Token.Text, "Condition", StringComparison.Ordinal)) { - if (invocationExpression.Arguments.Arguments.Length == 1 && - invocationExpression.Arguments.Arguments[0] is JassFunctionReferenceExpressionSyntax functionReferenceExpression && - Context.FunctionDeclarations.TryGetValue(functionReferenceExpression.IdentifierName.Name, out var conditionFunctionDeclaration) && + if (invocationExpression.ArgumentList.ArgumentList.Items.Length == 1 && + invocationExpression.ArgumentList.ArgumentList.Items[0] is JassFunctionReferenceExpressionSyntax functionReferenceExpression && + Context.FunctionDeclarations.TryGetValue(functionReferenceExpression.IdentifierName.Token.Text, out var conditionFunctionDeclaration) && conditionFunctionDeclaration.IsConditionsFunction) { var conditionFunction = conditionFunctionDeclaration.FunctionDeclaration; - if (conditionFunction.Body.Statements.Length == 1 && - TryDecompileConditionStatement(conditionFunction.Body.Statements[0], true, out var function)) + if (conditionFunction.Statements.Length == 1 && + TryDecompileConditionStatement(conditionFunction.Statements[0], true, out var function)) { functionParameter = new TriggerFunctionParameter { @@ -93,7 +93,7 @@ private bool TryDecompileInvocationExpression( } } - if (Context.TriggerData.TriggerData.TriggerCalls.TryGetValue(invocationExpression.IdentifierName.Name, out var triggerCall) && + if (Context.TriggerData.TriggerData.TriggerCalls.TryGetValue(invocationExpression.IdentifierName.Token.Text, out var triggerCall) && TryDecompileTriggerCallFunction(invocationExpression, triggerCall, out var callFunction)) { result.Add(new DecompileOption @@ -102,7 +102,7 @@ private bool TryDecompileInvocationExpression( Parameter = new TriggerFunctionParameter { Type = TriggerFunctionParameterType.Function, - Value = invocationExpression.IdentifierName.Name, + Value = invocationExpression.IdentifierName.Token.Text, Function = callFunction, }, }); diff --git a/src/War3Net.CodeAnalysis.Decompilers/Script/SyntaxDecompilers/LiteralExpressionDecompiler.cs b/src/War3Net.CodeAnalysis.Decompilers/Script/SyntaxDecompilers/LiteralExpressionDecompiler.cs new file mode 100644 index 00000000..08b125c2 --- /dev/null +++ b/src/War3Net.CodeAnalysis.Decompilers/Script/SyntaxDecompilers/LiteralExpressionDecompiler.cs @@ -0,0 +1,63 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; + +using War3Net.Build.Script; +using War3Net.CodeAnalysis.Jass; +using War3Net.CodeAnalysis.Jass.Syntax; + +namespace War3Net.CodeAnalysis.Decompilers +{ + public partial class JassScriptDecompiler + { + private bool TryDecompileLiteralExpression( + JassLiteralExpressionSyntax literalExpression, + string expectedType, + [NotNullWhen(true)] out TriggerFunctionParameter? functionParameter) + { + return literalExpression.SyntaxKind switch + { + JassSyntaxKind.CharacterLiteralExpression => TryDecompileCharacterLiteralExpression(literalExpression, expectedType, out functionParameter), + JassSyntaxKind.FourCCLiteralExpression => TryDecompileFourCCLiteralExpression(literalExpression, expectedType, out functionParameter), + JassSyntaxKind.HexadecimalLiteralExpression => TryDecompileHexadecimalLiteralExpression(literalExpression, expectedType, out functionParameter), + JassSyntaxKind.RealLiteralExpression => TryDecompileRealLiteralExpression(literalExpression, expectedType, out functionParameter), + JassSyntaxKind.OctalLiteralExpression => TryDecompileOctalLiteralExpression(literalExpression, expectedType, out functionParameter), + JassSyntaxKind.DecimalLiteralExpression => TryDecompileDecimalLiteralExpression(literalExpression, expectedType, out functionParameter), + JassSyntaxKind.TrueLiteralExpression => TryDecompileBooleanLiteralExpression(literalExpression, expectedType, out functionParameter), + JassSyntaxKind.FalseLiteralExpression => TryDecompileBooleanLiteralExpression(literalExpression, expectedType, out functionParameter), + JassSyntaxKind.StringLiteralExpression => TryDecompileStringLiteralExpression(literalExpression, expectedType, out functionParameter), + JassSyntaxKind.NullLiteralExpression => TryDecompileNullLiteralExpression(literalExpression, expectedType, out functionParameter), + + _ => throw new NotSupportedException($"Unsupported literal expression kind: {literalExpression.SyntaxKind}"), + }; + } + + private bool TryDecompileLiteralExpression( + JassLiteralExpressionSyntax literalExpression, + [NotNullWhen(true)] out List? decompileOptions) + { + return literalExpression.SyntaxKind switch + { + JassSyntaxKind.CharacterLiteralExpression => TryDecompileCharacterLiteralExpression(literalExpression, out decompileOptions), + JassSyntaxKind.FourCCLiteralExpression => TryDecompileFourCCLiteralExpression(literalExpression, out decompileOptions), + JassSyntaxKind.HexadecimalLiteralExpression => TryDecompileHexadecimalLiteralExpression(literalExpression, out decompileOptions), + JassSyntaxKind.RealLiteralExpression => TryDecompileRealLiteralExpression(literalExpression, out decompileOptions), + JassSyntaxKind.OctalLiteralExpression => TryDecompileOctalLiteralExpression(literalExpression, out decompileOptions), + JassSyntaxKind.DecimalLiteralExpression => TryDecompileDecimalLiteralExpression(literalExpression, out decompileOptions), + JassSyntaxKind.TrueLiteralExpression => TryDecompileBooleanLiteralExpression(literalExpression, out decompileOptions), + JassSyntaxKind.FalseLiteralExpression => TryDecompileBooleanLiteralExpression(literalExpression, out decompileOptions), + JassSyntaxKind.StringLiteralExpression => TryDecompileStringLiteralExpression(literalExpression, out decompileOptions), + JassSyntaxKind.NullLiteralExpression => TryDecompileNullLiteralExpression(literalExpression, out decompileOptions), + + _ => throw new NotSupportedException($"Unsupported literal expression kind: {literalExpression.SyntaxKind}"), + }; + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Decompilers/Script/SyntaxDecompilers/LoopStatementDecompiler.cs b/src/War3Net.CodeAnalysis.Decompilers/Script/SyntaxDecompilers/LoopStatementDecompiler.cs index 3a5d48a2..e02ba45d 100644 --- a/src/War3Net.CodeAnalysis.Decompilers/Script/SyntaxDecompilers/LoopStatementDecompiler.cs +++ b/src/War3Net.CodeAnalysis.Decompilers/Script/SyntaxDecompilers/LoopStatementDecompiler.cs @@ -24,16 +24,17 @@ private bool TryDecompileLoopStatement( return true; } - if (!TryDecompileActionStatementList(loopStatement.Body, out var loopActions)) + if (!TryDecompileActionStatements(loopStatement.Statements, out var loopActions)) { return false; } - functions.Add(DecompileCustomScriptAction(JassLoopCustomScriptAction.Value)); + functions.Add(DecompileCustomScriptAction(loopStatement.LoopToken.ToString())); functions.AddRange(loopActions); - functions.Add(DecompileCustomScriptAction(JassEndLoopCustomScriptAction.Value)); + DecompileLeadingTrivia(loopStatement.EndLoopToken.LeadingTrivia, ref functions); + functions.Add(DecompileCustomScriptAction(loopStatement.EndLoopToken.ToString())); - return false; + return true; } } } \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Decompilers/Script/SyntaxDecompilers/NullLiteralExpressionDecompiler.cs b/src/War3Net.CodeAnalysis.Decompilers/Script/SyntaxDecompilers/NullLiteralExpressionDecompiler.cs index c60ef2d0..8faa74fc 100644 --- a/src/War3Net.CodeAnalysis.Decompilers/Script/SyntaxDecompilers/NullLiteralExpressionDecompiler.cs +++ b/src/War3Net.CodeAnalysis.Decompilers/Script/SyntaxDecompilers/NullLiteralExpressionDecompiler.cs @@ -16,19 +16,19 @@ namespace War3Net.CodeAnalysis.Decompilers public partial class JassScriptDecompiler { private bool TryDecompileNullLiteralExpression( - JassNullLiteralExpressionSyntax nullLiteralExpression, + JassLiteralExpressionSyntax nullLiteralExpression, string expectedType, [NotNullWhen(true)] out TriggerFunctionParameter? functionParameter) { - return TryDecompileTriggerFunctionParameterPreset(nullLiteralExpression.ToString(), expectedType, out _, out functionParameter); + return TryDecompileTriggerFunctionParameterPreset(nullLiteralExpression.Token.Text, expectedType, out _, out functionParameter); } private bool TryDecompileNullLiteralExpression( - JassNullLiteralExpressionSyntax nullLiteralExpression, + JassLiteralExpressionSyntax nullLiteralExpression, [NotNullWhen(true)] out List? decompileOptions) { if (Context.TriggerData.TriggerParams.TryGetValue(string.Empty, out var triggerParamsForAllTypes) && - triggerParamsForAllTypes.TryGetValue(nullLiteralExpression.ToString(), out var triggerParams)) + triggerParamsForAllTypes.TryGetValue(nullLiteralExpression.Token.Text, out var triggerParams)) { decompileOptions = new(); diff --git a/src/War3Net.CodeAnalysis.Decompilers/Script/SyntaxDecompilers/OctalLiteralExpressionDecompiler.cs b/src/War3Net.CodeAnalysis.Decompilers/Script/SyntaxDecompilers/OctalLiteralExpressionDecompiler.cs index 787d751a..163ed4eb 100644 --- a/src/War3Net.CodeAnalysis.Decompilers/Script/SyntaxDecompilers/OctalLiteralExpressionDecompiler.cs +++ b/src/War3Net.CodeAnalysis.Decompilers/Script/SyntaxDecompilers/OctalLiteralExpressionDecompiler.cs @@ -19,7 +19,7 @@ namespace War3Net.CodeAnalysis.Decompilers public partial class JassScriptDecompiler { private bool TryDecompileOctalLiteralExpression( - JassOctalLiteralExpressionSyntax octalLiteralExpression, + JassLiteralExpressionSyntax octalLiteralExpression, string expectedType, [NotNullWhen(true)] out TriggerFunctionParameter? functionParameter) { @@ -29,7 +29,7 @@ private bool TryDecompileOctalLiteralExpression( functionParameter = new TriggerFunctionParameter { Type = TriggerFunctionParameterType.String, - Value = octalLiteralExpression.Value.ToString(CultureInfo.InvariantCulture), + Value = JassLiteral.ParseOctal(octalLiteralExpression.Token.Text).ToString(CultureInfo.InvariantCulture), }; return true; @@ -40,10 +40,10 @@ private bool TryDecompileOctalLiteralExpression( } private bool TryDecompileOctalLiteralExpression( - JassOctalLiteralExpressionSyntax octalLiteralExpression, + JassLiteralExpressionSyntax octalLiteralExpression, [NotNullWhen(true)] out List? decompileOptions) { - var value = octalLiteralExpression.Value.ToString(CultureInfo.InvariantCulture); + var value = JassLiteral.ParseOctal(octalLiteralExpression.Token.Text).ToString(CultureInfo.InvariantCulture); decompileOptions = new(); diff --git a/src/War3Net.CodeAnalysis.Decompilers/Script/SyntaxDecompilers/RealLiteralExpressionDecompiler.cs b/src/War3Net.CodeAnalysis.Decompilers/Script/SyntaxDecompilers/RealLiteralExpressionDecompiler.cs index 0b468f16..f6e6dc82 100644 --- a/src/War3Net.CodeAnalysis.Decompilers/Script/SyntaxDecompilers/RealLiteralExpressionDecompiler.cs +++ b/src/War3Net.CodeAnalysis.Decompilers/Script/SyntaxDecompilers/RealLiteralExpressionDecompiler.cs @@ -18,7 +18,7 @@ namespace War3Net.CodeAnalysis.Decompilers public partial class JassScriptDecompiler { private bool TryDecompileRealLiteralExpression( - JassRealLiteralExpressionSyntax realLiteralExpression, + JassLiteralExpressionSyntax realLiteralExpression, string expectedType, [NotNullWhen(true)] out TriggerFunctionParameter? functionParameter) { @@ -27,7 +27,7 @@ private bool TryDecompileRealLiteralExpression( functionParameter = new TriggerFunctionParameter { Type = TriggerFunctionParameterType.String, - Value = realLiteralExpression.ToString(), + Value = realLiteralExpression.Token.Text, }; return true; @@ -38,7 +38,7 @@ private bool TryDecompileRealLiteralExpression( } private bool TryDecompileRealLiteralExpression( - JassRealLiteralExpressionSyntax realLiteralExpression, + JassLiteralExpressionSyntax realLiteralExpression, [NotNullWhen(true)] out List? decompileOptions) { decompileOptions = new(); @@ -49,7 +49,7 @@ private bool TryDecompileRealLiteralExpression( Parameter = new TriggerFunctionParameter { Type = TriggerFunctionParameterType.String, - Value = realLiteralExpression.ToString(), + Value = realLiteralExpression.Token.Text, }, }); diff --git a/src/War3Net.CodeAnalysis.Decompilers/Script/SyntaxDecompilers/SetStatementDecompiler.cs b/src/War3Net.CodeAnalysis.Decompilers/Script/SyntaxDecompilers/SetStatementDecompiler.cs index 4c690bf6..ab9203be 100644 --- a/src/War3Net.CodeAnalysis.Decompilers/Script/SyntaxDecompilers/SetStatementDecompiler.cs +++ b/src/War3Net.CodeAnalysis.Decompilers/Script/SyntaxDecompilers/SetStatementDecompiler.cs @@ -6,6 +6,7 @@ // ------------------------------------------------------------------------------ using System.Collections.Generic; +using System.Collections.Immutable; using War3Net.Build.Script; using War3Net.CodeAnalysis.Jass.Syntax; @@ -16,12 +17,12 @@ public partial class JassScriptDecompiler { private bool TryDecompileSetStatement( JassSetStatementSyntax setStatement, - JassStatementListSyntax statementList, + ImmutableArray statements, ref int i, ref List functions) { - var lookaheadStatement1 = i + 1 < statementList.Statements.Length ? statementList.Statements[i + 1] : null; - var lookaheadStatement2 = i + 2 < statementList.Statements.Length ? statementList.Statements[i + 2] : null; + var lookaheadStatement1 = i + 1 < statements.Length ? statements[i + 1] : null; + var lookaheadStatement2 = i + 2 < statements.Length ? statements[i + 2] : null; if (TryDecompileForLoopActionFunction(setStatement, lookaheadStatement1, lookaheadStatement2, ref functions)) { diff --git a/src/War3Net.CodeAnalysis.Decompilers/Script/SyntaxDecompilers/StatementDecompiler.cs b/src/War3Net.CodeAnalysis.Decompilers/Script/SyntaxDecompilers/StatementDecompiler.cs index e5fae454..15259aa7 100644 --- a/src/War3Net.CodeAnalysis.Decompilers/Script/SyntaxDecompilers/StatementDecompiler.cs +++ b/src/War3Net.CodeAnalysis.Decompilers/Script/SyntaxDecompilers/StatementDecompiler.cs @@ -6,6 +6,7 @@ // ------------------------------------------------------------------------------ using System.Collections.Generic; +using System.Collections.Immutable; using System.Diagnostics.CodeAnalysis; using War3Net.Build.Script; @@ -16,14 +17,13 @@ namespace War3Net.CodeAnalysis.Decompilers public partial class JassScriptDecompiler { private bool TryDecompileActionStatement( - JassStatementListSyntax statementList, + ImmutableArray statements, ref int i, ref List functions) { - return statementList.Statements[i] switch + return statements[i] switch { - JassCommentSyntax comment => TryDecompileComment(comment, ref functions), - JassSetStatementSyntax setStatement => TryDecompileSetStatement(setStatement, statementList, ref i, ref functions), + JassSetStatementSyntax setStatement => TryDecompileSetStatement(setStatement, statements, ref i, ref functions), JassCallStatementSyntax callStatement => TryDecompileCallStatement(callStatement, ref functions), JassIfStatementSyntax ifStatement => TryDecompileIfStatement(ifStatement, ref functions), JassLoopStatementSyntax loopStatement => TryDecompileLoopStatement(loopStatement, ref functions), @@ -35,7 +35,7 @@ private bool TryDecompileActionStatement( /// for AND conditions, for OR conditions. private bool TryDecompileConditionStatement( - IStatementSyntax statement, + JassStatementSyntax statement, bool returnValue, [NotNullWhen(true)] out TriggerFunction? function) { diff --git a/src/War3Net.CodeAnalysis.Decompilers/Script/SyntaxDecompilers/StatementListDecompiler.cs b/src/War3Net.CodeAnalysis.Decompilers/Script/SyntaxDecompilers/StatementListDecompiler.cs index 9004c789..e1101106 100644 --- a/src/War3Net.CodeAnalysis.Decompilers/Script/SyntaxDecompilers/StatementListDecompiler.cs +++ b/src/War3Net.CodeAnalysis.Decompilers/Script/SyntaxDecompilers/StatementListDecompiler.cs @@ -6,33 +6,29 @@ // ------------------------------------------------------------------------------ using System.Collections.Generic; +using System.Collections.Immutable; using System.Diagnostics.CodeAnalysis; using System.Linq; using War3Net.Build.Script; +using War3Net.CodeAnalysis.Jass.Extensions; using War3Net.CodeAnalysis.Jass.Syntax; namespace War3Net.CodeAnalysis.Decompilers { public partial class JassScriptDecompiler { - private bool TryDecompileActionStatementList(JassStatementListSyntax statementList, [NotNullWhen(true)] out List? actionFunctions) + private bool TryDecompileActionStatements(ImmutableArray statements, [NotNullWhen(true)] out List? actionFunctions) { var result = new List(); - for (var i = 0; i < statementList.Statements.Length; i++) + for (var i = 0; i < statements.Length; i++) { - if (!TryDecompileActionStatement(statementList, ref i, ref result)) + DecompileLeadingTrivia(statements[i].GetLeadingTrivia(), ref result); + + if (!TryDecompileActionStatement(statements, ref i, ref result)) { - if (statementList.Statements[i] is IStatementLineSyntax statementLine) - { - result.Add(DecompileCustomScriptAction(statementLine)); - } - else - { - actionFunctions = null; - return false; - } + result.Add(DecompileCustomScriptAction(statements[i].ToString())); } } @@ -40,13 +36,13 @@ private bool TryDecompileActionStatementList(JassStatementListSyntax statementLi return true; } - private bool TryDecompileConditionStatementList( - JassStatementListSyntax statementList, + private bool TryDecompileConditionStatements( + ImmutableArray statements, [NotNullWhen(true)] out List? conditionFunctions) { var result = new List(); - foreach (var conditionStatement in statementList.Statements.SkipLast(1)) + foreach (var conditionStatement in statements.SkipLast(1)) { if (TryDecompileConditionStatement(conditionStatement, true, out var conditionFunction)) { @@ -60,9 +56,10 @@ private bool TryDecompileConditionStatementList( } // Last statement must be "return true" - if (statementList.Statements[^1] is not JassReturnStatementSyntax finalReturnStatement || - finalReturnStatement.Value is not JassBooleanLiteralExpressionSyntax returnBooleanLiteralExpression || - !returnBooleanLiteralExpression.Value) + if (statements[^1] is not JassReturnStatementSyntax finalReturnStatement || + finalReturnStatement.Value is null || + !finalReturnStatement.Value.TryGetBooleanExpressionValue(out var returnStatementValue) || + !returnStatementValue) { conditionFunctions = null; return false; @@ -72,8 +69,8 @@ finalReturnStatement.Value is not JassBooleanLiteralExpressionSyntax returnBoole return true; } - private bool TryDecompileAndOrMultipleStatementList( - JassStatementListSyntax statementList, + private bool TryDecompileAndOrMultipleStatements( + ImmutableArray statements, [NotNullWhen(true)] out TriggerFunction? conditionFunction) { var result = new TriggerFunction @@ -83,18 +80,19 @@ private bool TryDecompileAndOrMultipleStatementList( }; // Last statement must be "return true" or "return false" - if (statementList.Statements[^1] is not JassReturnStatementSyntax finalReturnStatement || - finalReturnStatement.Value is not JassBooleanLiteralExpressionSyntax returnBooleanLiteralExpression) + if (statements[^1] is not JassReturnStatementSyntax finalReturnStatement || + finalReturnStatement.Value is null || + !finalReturnStatement.Value.TryGetBooleanExpressionValue(out var returnStatementValue)) { conditionFunction = null; return false; } - result.Name = returnBooleanLiteralExpression.Value ? "AndMultiple" : "OrMultiple"; + result.Name = returnStatementValue ? "AndMultiple" : "OrMultiple"; - foreach (var conditionStatement in statementList.Statements.SkipLast(1)) + foreach (var conditionStatement in statements.SkipLast(1)) { - if (TryDecompileConditionStatement(conditionStatement, returnBooleanLiteralExpression.Value, out var function)) + if (TryDecompileConditionStatement(conditionStatement, returnStatementValue, out var function)) { function.Branch = 0; result.ChildFunctions.Add(function); diff --git a/src/War3Net.CodeAnalysis.Decompilers/Script/SyntaxDecompilers/StringLiteralExpressionDecompiler.cs b/src/War3Net.CodeAnalysis.Decompilers/Script/SyntaxDecompilers/StringLiteralExpressionDecompiler.cs index 42adf42a..4b68b228 100644 --- a/src/War3Net.CodeAnalysis.Decompilers/Script/SyntaxDecompilers/StringLiteralExpressionDecompiler.cs +++ b/src/War3Net.CodeAnalysis.Decompilers/Script/SyntaxDecompilers/StringLiteralExpressionDecompiler.cs @@ -19,11 +19,13 @@ namespace War3Net.CodeAnalysis.Decompilers public partial class JassScriptDecompiler { private bool TryDecompileStringLiteralExpression( - JassStringLiteralExpressionSyntax stringLiteralExpression, + JassLiteralExpressionSyntax stringLiteralExpression, string expectedType, [NotNullWhen(true)] out TriggerFunctionParameter? functionParameter) { - if (TryDecompileTriggerFunctionParameterPreset($"`{stringLiteralExpression.Value}`", expectedType, out _, out functionParameter)) + var stringValue = JassLiteral.ParseString(stringLiteralExpression.Token.Text); + + if (TryDecompileTriggerFunctionParameterPreset($"`{stringValue}`", expectedType, out _, out functionParameter)) { return true; } @@ -35,7 +37,7 @@ private bool TryDecompileStringLiteralExpression( functionParameter = new TriggerFunctionParameter { Type = TriggerFunctionParameterType.String, - Value = Regex.Unescape(stringLiteralExpression.Value), + Value = Regex.Unescape(stringValue), }; return true; @@ -46,10 +48,11 @@ private bool TryDecompileStringLiteralExpression( } private bool TryDecompileStringLiteralExpression( - JassStringLiteralExpressionSyntax stringLiteralExpression, + JassLiteralExpressionSyntax stringLiteralExpression, [NotNullWhen(true)] out List? decompileOptions) { - var value = Regex.Unescape(stringLiteralExpression.Value); + var stringValue = JassLiteral.ParseString(stringLiteralExpression.Token.Text); + var value = Regex.Unescape(stringValue); decompileOptions = new(); decompileOptions.Add(new DecompileOption @@ -66,7 +69,7 @@ private bool TryDecompileStringLiteralExpression( { foreach (var customType in customTypes) { - if (TryDecompileTriggerFunctionParameterPreset($"`{stringLiteralExpression.Value}`", customType.Key, out _, out var functionParameter)) + if (TryDecompileTriggerFunctionParameterPreset($"`{stringValue}`", customType.Key, out _, out var functionParameter)) { decompileOptions.Add(new DecompileOption { diff --git a/src/War3Net.CodeAnalysis.Decompilers/Script/SyntaxDecompilers/TriviaDecompiler.cs b/src/War3Net.CodeAnalysis.Decompilers/Script/SyntaxDecompilers/TriviaDecompiler.cs new file mode 100644 index 00000000..120291bb --- /dev/null +++ b/src/War3Net.CodeAnalysis.Decompilers/Script/SyntaxDecompilers/TriviaDecompiler.cs @@ -0,0 +1,62 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using System; +using System.Collections.Generic; + +using War3Net.Build.Script; +using War3Net.CodeAnalysis.Jass; +using War3Net.CodeAnalysis.Jass.Syntax; + +namespace War3Net.CodeAnalysis.Decompilers +{ + public partial class JassScriptDecompiler + { + private void DecompileLeadingTrivia( + JassSyntaxTriviaList leadingTrivia, + ref List functions) + { + foreach (var trivia in leadingTrivia.Trivia) + { + _ = TryDecompileComment(trivia, ref functions); + } + } + + private bool TryDecompileComment( + JassSyntaxTrivia trivia, + ref List functions) + { + if (trivia.Text.StartsWith(JassSymbol.SlashSlash, StringComparison.Ordinal)) + { + if (trivia.Text.Length > 3 && trivia.Text[2] == JassSymbol.SpaceChar) + { + functions.Add(new TriggerFunction + { + Type = TriggerFunctionType.Action, + IsEnabled = true, + Name = "CommentString", + Parameters = new() + { + new TriggerFunctionParameter + { + Type = TriggerFunctionParameterType.String, + Value = trivia.Text[3..], + }, + }, + }); + + return true; + } + + functions.Add(DecompileCustomScriptAction(trivia.Text)); + return true; + } + + return false; + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Decompilers/Script/SyntaxDecompilers/UnaryExpressionDecompiler.cs b/src/War3Net.CodeAnalysis.Decompilers/Script/SyntaxDecompilers/UnaryExpressionDecompiler.cs index 90b6ff0b..cfca76d8 100644 --- a/src/War3Net.CodeAnalysis.Decompilers/Script/SyntaxDecompilers/UnaryExpressionDecompiler.cs +++ b/src/War3Net.CodeAnalysis.Decompilers/Script/SyntaxDecompilers/UnaryExpressionDecompiler.cs @@ -11,7 +11,6 @@ using War3Net.Build.Script; using War3Net.CodeAnalysis.Jass; -using War3Net.CodeAnalysis.Jass.Extensions; using War3Net.CodeAnalysis.Jass.Syntax; namespace War3Net.CodeAnalysis.Decompilers @@ -23,16 +22,16 @@ private bool TryDecompileUnaryExpression( string expectedType, [NotNullWhen(true)] out TriggerFunctionParameter? functionParameter) { - switch (unaryExpression.Operator) + switch (unaryExpression.SyntaxKind) { - case UnaryOperatorType.Plus: - case UnaryOperatorType.Minus: + case JassSyntaxKind.UnaryPlusExpression: + case JassSyntaxKind.UnaryMinusExpression: if (string.Equals(expectedType, JassKeyword.Integer, StringComparison.Ordinal) || string.Equals(expectedType, JassKeyword.Real, StringComparison.Ordinal)) { if (TryDecompileTriggerFunctionParameter(unaryExpression.Expression, expectedType, out functionParameter)) { - functionParameter.Value = unaryExpression.Operator.GetSymbol() + functionParameter.Value; + functionParameter.Value = unaryExpression.OperatorToken.Text + functionParameter.Value; return true; } } @@ -48,10 +47,10 @@ private bool TryDecompileUnaryExpression( JassUnaryExpressionSyntax unaryExpression, [NotNullWhen(true)] out List? decompileOptions) { - switch (unaryExpression.Operator) + switch (unaryExpression.SyntaxKind) { - case UnaryOperatorType.Plus: - case UnaryOperatorType.Minus: + case JassSyntaxKind.UnaryPlusExpression: + case JassSyntaxKind.UnaryMinusExpression: var result = new List(); if (TryDecompileUnaryExpression(unaryExpression, JassKeyword.Integer, out var functionParameterInt)) diff --git a/src/War3Net.CodeAnalysis.Decompilers/Script/TriggerCallFunctionDecompiler.cs b/src/War3Net.CodeAnalysis.Decompilers/Script/TriggerCallFunctionDecompiler.cs index 92481f48..6e75a635 100644 --- a/src/War3Net.CodeAnalysis.Decompilers/Script/TriggerCallFunctionDecompiler.cs +++ b/src/War3Net.CodeAnalysis.Decompilers/Script/TriggerCallFunctionDecompiler.cs @@ -19,7 +19,7 @@ public partial class JassScriptDecompiler [Obsolete] private bool TryDecompileTriggerCallFunction(JassInvocationExpressionSyntax invocationExpression, [NotNullWhen(true)] out TriggerFunction? callFunction) { - if (Context.TriggerData.TriggerData.TriggerCalls.TryGetValue(invocationExpression.IdentifierName.Name, out var triggerCall) && + if (Context.TriggerData.TriggerData.TriggerCalls.TryGetValue(invocationExpression.IdentifierName.Token.Text, out var triggerCall) && TryDecompileTriggerCallFunction(invocationExpression, triggerCall, out callFunction)) { return true; @@ -34,18 +34,18 @@ private bool TryDecompileTriggerCallFunction( TriggerData.TriggerCall triggerCall, [NotNullWhen(true)] out TriggerFunction? callFunction) { - if (triggerCall.ArgumentTypes.Length == invocationExpression.Arguments.Arguments.Length) + if (triggerCall.ArgumentTypes.Length == invocationExpression.ArgumentList.ArgumentList.Items.Length) { var function = new TriggerFunction { Type = TriggerFunctionType.Call, IsEnabled = true, - Name = invocationExpression.IdentifierName.Name, + Name = invocationExpression.IdentifierName.Token.Text, }; - for (var i = 0; i < invocationExpression.Arguments.Arguments.Length; i++) + for (var i = 0; i < invocationExpression.ArgumentList.ArgumentList.Items.Length; i++) { - if (TryDecompileTriggerFunctionParameter(invocationExpression.Arguments.Arguments[i], triggerCall.ArgumentTypes[i], out var functionParameter)) + if (TryDecompileTriggerFunctionParameter(invocationExpression.ArgumentList.ArgumentList.Items[i], triggerCall.ArgumentTypes[i], out var functionParameter)) { function.Parameters.Add(functionParameter); } @@ -79,7 +79,7 @@ private bool TryDecompileTriggerCallFunction(JassBinaryExpressionSyntax binaryEx if (argumentTypes.Length == 2) { if (string.Equals(type, JassKeyword.String, StringComparison.Ordinal) && - binaryExpression.Operator == BinaryOperatorType.Add && + binaryExpression.SyntaxKind == JassSyntaxKind.AddExpression && TryDecompileTriggerFunctionParameter(binaryExpression.Left, argumentTypes[0], out var leftFunctionParameter) && TryDecompileTriggerFunctionParameter(binaryExpression.Right, argumentTypes[1], out var rightFunctionParameter)) { @@ -100,7 +100,7 @@ private bool TryDecompileTriggerCallFunction(JassBinaryExpressionSyntax binaryEx else if (argumentTypes.Length == 3) { if (TryDecompileTriggerFunctionParameter(binaryExpression.Left, argumentTypes[0], out var leftFunctionParameter) && - TryDecompileTriggerFunctionParameter(binaryExpression.Operator, argumentTypes[1], out var operatorFunctionParameter) && + TryDecompileTriggerFunctionParameter(binaryExpression.OperatorToken, argumentTypes[1], out var operatorFunctionParameter) && TryDecompileTriggerFunctionParameter(binaryExpression.Right, argumentTypes[2], out var rightFunctionParameter)) { var function = new TriggerFunction diff --git a/src/War3Net.CodeAnalysis.Decompilers/Script/TriggerConditionFunctionDecompiler.cs b/src/War3Net.CodeAnalysis.Decompilers/Script/TriggerConditionFunctionDecompiler.cs index c6be4a1d..ea85802a 100644 --- a/src/War3Net.CodeAnalysis.Decompilers/Script/TriggerConditionFunctionDecompiler.cs +++ b/src/War3Net.CodeAnalysis.Decompilers/Script/TriggerConditionFunctionDecompiler.cs @@ -16,25 +16,25 @@ namespace War3Net.CodeAnalysis.Decompilers { public partial class JassScriptDecompiler { - private bool TryDecompileConditionExpression(IExpressionSyntax expression, [NotNullWhen(true)] out TriggerFunction? conditionFunction) + private bool TryDecompileConditionExpression(JassExpressionSyntax expression, [NotNullWhen(true)] out TriggerFunction? conditionFunction) { expression = expression.Deparenthesize(); if (expression is JassInvocationExpressionSyntax invocationExpression) { - if (string.Equals(invocationExpression.IdentifierName.Name, "GetBooleanAnd", StringComparison.Ordinal) || - string.Equals(invocationExpression.IdentifierName.Name, "GetBooleanOr", StringComparison.Ordinal)) + if (string.Equals(invocationExpression.IdentifierName.Token.Text, "GetBooleanAnd", StringComparison.Ordinal) || + string.Equals(invocationExpression.IdentifierName.Token.Text, "GetBooleanOr", StringComparison.Ordinal)) { - if (invocationExpression.Arguments.Arguments.Length == 2) + if (invocationExpression.ArgumentList.ArgumentList.Items.Length == 2) { var function = new TriggerFunction { Type = TriggerFunctionType.Condition, IsEnabled = true, - Name = invocationExpression.IdentifierName.Name, + Name = invocationExpression.IdentifierName.Token.Text, }; - foreach (var argument in invocationExpression.Arguments.Arguments) + foreach (var argument in invocationExpression.ArgumentList.ArgumentList.Items) { if (TryDecompileConditionExpression(argument, out var conditionSubFunction)) { @@ -63,20 +63,20 @@ private bool TryDecompileConditionExpression(IExpressionSyntax expression, [NotN } else { - if (invocationExpression.Arguments.Arguments.IsEmpty && - Context.FunctionDeclarations.TryGetValue(invocationExpression.IdentifierName.Name, out var conditionsFunctionDeclaration) && + if (invocationExpression.ArgumentList.ArgumentList.Items.IsEmpty && + Context.FunctionDeclarations.TryGetValue(invocationExpression.IdentifierName.Token.Text, out var conditionsFunctionDeclaration) && conditionsFunctionDeclaration.IsConditionsFunction) { var conditionsFunction = conditionsFunctionDeclaration.FunctionDeclaration; - if (TryDecompileAndOrMultipleStatementList(conditionsFunction.Body, out conditionFunction)) + if (TryDecompileAndOrMultipleStatements(conditionsFunction.Statements, out conditionFunction)) { return true; } - if (conditionsFunction.Body.Statements.Length == 1) + if (conditionsFunction.Statements.Length == 1) { - if (conditionsFunction.Body.Statements[0] is JassReturnStatementSyntax singleReturnStatement) + if (conditionsFunction.Statements[0] is JassReturnStatementSyntax singleReturnStatement) { return TryDecompileReturnStatement(singleReturnStatement, out conditionFunction); } @@ -116,7 +116,7 @@ private bool TryDecompileConditionExpression(IExpressionSyntax expression, [NotN if (Context.TriggerData.TriggerConditions.TryGetValue(decompileOption.Type, out var triggerCondition) && triggerCondition.ArgumentTypes.Length == 3) { - if (!TryDecompileTriggerFunctionParameter(binaryExpression.Operator, triggerCondition.ArgumentTypes[1], out var operatorFunctionParameter)) + if (!TryDecompileTriggerFunctionParameter(binaryExpression.OperatorToken, triggerCondition.ArgumentTypes[1], out var operatorFunctionParameter)) { continue; } diff --git a/src/War3Net.CodeAnalysis.Decompilers/Script/TriggerFunctionParameterDecompiler.cs b/src/War3Net.CodeAnalysis.Decompilers/Script/TriggerFunctionParameterDecompiler.cs index 09be9d47..6c50412b 100644 --- a/src/War3Net.CodeAnalysis.Decompilers/Script/TriggerFunctionParameterDecompiler.cs +++ b/src/War3Net.CodeAnalysis.Decompilers/Script/TriggerFunctionParameterDecompiler.cs @@ -12,7 +12,6 @@ using War3Net.Build.Script; using War3Net.CodeAnalysis.Jass; -using War3Net.CodeAnalysis.Jass.Extensions; using War3Net.CodeAnalysis.Jass.Syntax; namespace War3Net.CodeAnalysis.Decompilers @@ -20,25 +19,17 @@ namespace War3Net.CodeAnalysis.Decompilers public partial class JassScriptDecompiler { private bool TryDecompileTriggerFunctionParameter( - IExpressionSyntax expression, + JassExpressionSyntax expression, string expectedType, [NotNullWhen(true)] out TriggerFunctionParameter? functionParameter) { return expression switch { - JassCharacterLiteralExpressionSyntax characterLiteralExpression => TryDecompileCharacterLiteralExpression(characterLiteralExpression, expectedType, out functionParameter), - JassFourCCLiteralExpressionSyntax fourCCLiteralExpression => TryDecompileFourCCLiteralExpression(fourCCLiteralExpression, expectedType, out functionParameter), - JassHexadecimalLiteralExpressionSyntax hexadecimalLiteralExpression => TryDecompileHexadecimalLiteralExpression(hexadecimalLiteralExpression, expectedType, out functionParameter), - JassRealLiteralExpressionSyntax realLiteralExpression => TryDecompileRealLiteralExpression(realLiteralExpression, expectedType, out functionParameter), - JassOctalLiteralExpressionSyntax octalLiteralExpression => TryDecompileOctalLiteralExpression(octalLiteralExpression, expectedType, out functionParameter), - JassDecimalLiteralExpressionSyntax decimalLiteralExpression => TryDecompileDecimalLiteralExpression(decimalLiteralExpression, expectedType, out functionParameter), - JassBooleanLiteralExpressionSyntax booleanLiteralExpression => TryDecompileBooleanLiteralExpression(booleanLiteralExpression, expectedType, out functionParameter), - JassStringLiteralExpressionSyntax stringLiteralExpression => TryDecompileStringLiteralExpression(stringLiteralExpression, expectedType, out functionParameter), - JassNullLiteralExpressionSyntax nullLiteralExpression => TryDecompileNullLiteralExpression(nullLiteralExpression, expectedType, out functionParameter), + JassLiteralExpressionSyntax literalExpression => TryDecompileLiteralExpression(literalExpression, expectedType, out functionParameter), JassFunctionReferenceExpressionSyntax functionReferenceExpression => TryDecompileFunctionReferenceExpression(functionReferenceExpression, expectedType, out functionParameter), JassInvocationExpressionSyntax invocationExpression => TryDecompileInvocationExpression(invocationExpression, expectedType, out functionParameter), - JassArrayReferenceExpressionSyntax arrayReferenceExpression => TryDecompileArrayReferenceExpression(arrayReferenceExpression, expectedType, out functionParameter), - JassVariableReferenceExpressionSyntax variableReferenceExpression => TryDecompileVariableReferenceExpression(variableReferenceExpression, expectedType, out functionParameter), + JassElementAccessExpressionSyntax elementAccessExpression => TryDecompileElementAccessExpression(elementAccessExpression, expectedType, out functionParameter), + JassIdentifierNameSyntax identifierName => TryDecompileIdentifierName(identifierName, expectedType, out functionParameter), JassParenthesizedExpressionSyntax parenthesizedExpression => TryDecompileParenthesizedExpression(parenthesizedExpression, expectedType, out functionParameter), JassUnaryExpressionSyntax unaryExpression => TryDecompileUnaryExpression(unaryExpression, expectedType, out functionParameter), JassBinaryExpressionSyntax binaryExpression => TryDecompileBinaryExpression(binaryExpression, expectedType, out functionParameter), @@ -48,24 +39,16 @@ private bool TryDecompileTriggerFunctionParameter( } private bool TryDecompileTriggerFunctionParameter( - IExpressionSyntax expression, + JassExpressionSyntax expression, [NotNullWhen(true)] out List? decompileOptions) { return expression switch { - JassCharacterLiteralExpressionSyntax characterLiteralExpression => TryDecompileCharacterLiteralExpression(characterLiteralExpression, out decompileOptions), - JassFourCCLiteralExpressionSyntax fourCCLiteralExpression => TryDecompileFourCCLiteralExpression(fourCCLiteralExpression, out decompileOptions), - JassHexadecimalLiteralExpressionSyntax hexadecimalLiteralExpression => TryDecompileHexadecimalLiteralExpression(hexadecimalLiteralExpression, out decompileOptions), - JassRealLiteralExpressionSyntax realLiteralExpression => TryDecompileRealLiteralExpression(realLiteralExpression, out decompileOptions), - JassOctalLiteralExpressionSyntax octalLiteralExpression => TryDecompileOctalLiteralExpression(octalLiteralExpression, out decompileOptions), - JassDecimalLiteralExpressionSyntax decimalLiteralExpression => TryDecompileDecimalLiteralExpression(decimalLiteralExpression, out decompileOptions), - JassBooleanLiteralExpressionSyntax booleanLiteralExpression => TryDecompileBooleanLiteralExpression(booleanLiteralExpression, out decompileOptions), - JassStringLiteralExpressionSyntax stringLiteralExpression => TryDecompileStringLiteralExpression(stringLiteralExpression, out decompileOptions), - JassNullLiteralExpressionSyntax nullLiteralExpression => TryDecompileNullLiteralExpression(nullLiteralExpression, out decompileOptions), + JassLiteralExpressionSyntax literalExpression => TryDecompileLiteralExpression(literalExpression, out decompileOptions), JassFunctionReferenceExpressionSyntax functionReferenceExpression => TryDecompileFunctionReferenceExpression(functionReferenceExpression, out decompileOptions), JassInvocationExpressionSyntax invocationExpression => TryDecompileInvocationExpression(invocationExpression, out decompileOptions), - JassArrayReferenceExpressionSyntax arrayReferenceExpression => TryDecompileArrayReferenceExpression(arrayReferenceExpression, out decompileOptions), - JassVariableReferenceExpressionSyntax variableReferenceExpression => TryDecompileVariableReferenceExpression(variableReferenceExpression, out decompileOptions), + JassElementAccessExpressionSyntax elementAccessExpression => TryDecompileElementAccessExpression(elementAccessExpression, out decompileOptions), + JassIdentifierNameSyntax identifierName => TryDecompileIdentifierName(identifierName, out decompileOptions), JassParenthesizedExpressionSyntax parenthesizedExpression => TryDecompileParenthesizedExpression(parenthesizedExpression, out decompileOptions), JassUnaryExpressionSyntax unaryExpression => TryDecompileUnaryExpression(unaryExpression, out decompileOptions), JassBinaryExpressionSyntax binaryExpression => TryDecompileBinaryExpression(binaryExpression, out decompileOptions), @@ -74,10 +57,10 @@ private bool TryDecompileTriggerFunctionParameter( }; } - private bool TryDecompileTriggerFunctionParameter(BinaryOperatorType binaryOperatorType, string type, [NotNullWhen(true)] out TriggerFunctionParameter? functionParameter) + private bool TryDecompileTriggerFunctionParameter(JassSyntaxToken operatorToken, string type, [NotNullWhen(true)] out TriggerFunctionParameter? functionParameter) { if (Context.TriggerData.TriggerParams.TryGetValue(type, out var triggerParamsForType) && - triggerParamsForType.TryGetValue($"\"{binaryOperatorType.GetSymbol()}\"", out var triggerParams)) + triggerParamsForType.TryGetValue($"\"{operatorToken.Text}\"", out var triggerParams)) { functionParameter = new TriggerFunctionParameter { @@ -132,17 +115,17 @@ private bool TryDecompileTriggerFunctionParameterPreset( private bool TryDecompileTriggerFunctionParameterVariable(JassSetStatementSyntax setStatement, [NotNullWhen(true)] out TriggerFunctionParameter? functionParameter, [NotNullWhen(true)] out string? type) { - if (setStatement.IdentifierName.Name.StartsWith("udg_", StringComparison.Ordinal) && - Context.VariableDeclarations.TryGetValue(setStatement.IdentifierName.Name, out var variableDeclaration)) + if (setStatement.IdentifierName.Token.Text.StartsWith("udg_", StringComparison.Ordinal) && + Context.VariableDeclarations.TryGetValue(setStatement.IdentifierName.Token.Text, out var variableDeclaration)) { - functionParameter = DecompileVariableTriggerFunctionParameter(setStatement.IdentifierName.Name); + functionParameter = DecompileVariableTriggerFunctionParameter(setStatement.IdentifierName.Token.Text); type = variableDeclaration.Type; - if (setStatement.Indexer is null) + if (setStatement.ElementAccessClause is null) { return true; } - else if (TryDecompileTriggerFunctionParameter(setStatement.Indexer, JassKeyword.Integer, out var arrayIndexer)) + else if (TryDecompileTriggerFunctionParameter(setStatement.ElementAccessClause.Expression, JassKeyword.Integer, out var arrayIndexer)) { functionParameter.ArrayIndexer = arrayIndexer; diff --git a/src/War3Net.CodeAnalysis.Decompilers/VariableDeclarationContext.cs b/src/War3Net.CodeAnalysis.Decompilers/VariableDeclarationContext.cs index d37bb87e..739b79c8 100644 --- a/src/War3Net.CodeAnalysis.Decompilers/VariableDeclarationContext.cs +++ b/src/War3Net.CodeAnalysis.Decompilers/VariableDeclarationContext.cs @@ -6,21 +6,22 @@ // ------------------------------------------------------------------------------ using War3Net.Build.Script; +using War3Net.CodeAnalysis.Jass.Extensions; using War3Net.CodeAnalysis.Jass.Syntax; namespace War3Net.CodeAnalysis.Decompilers { internal sealed class VariableDeclarationContext { - public VariableDeclarationContext(JassGlobalDeclarationSyntax globalDeclaration) + public VariableDeclarationContext(JassGlobalVariableDeclarationSyntax globalVariableDeclaration) { - GlobalDeclaration = globalDeclaration; - IsArray = globalDeclaration.Declarator is JassArrayDeclaratorSyntax; + GlobalVariableDeclaration = globalVariableDeclaration; + IsArray = globalVariableDeclaration.Declarator is JassArrayDeclaratorSyntax; - Type = globalDeclaration.Declarator.Type.TypeName.Name; + Type = globalVariableDeclaration.Declarator.GetVariableType().GetToken().Text; } - public JassGlobalDeclarationSyntax GlobalDeclaration { get; } + public JassGlobalVariableDeclarationSyntax GlobalVariableDeclaration { get; } public bool IsArray { get; } diff --git a/src/War3Net.CodeAnalysis.Decompilers/Widget/MapUnitsDecompiler.cs b/src/War3Net.CodeAnalysis.Decompilers/Widget/MapUnitsDecompiler.cs index aa480b99..af7a08cd 100644 --- a/src/War3Net.CodeAnalysis.Decompilers/Widget/MapUnitsDecompiler.cs +++ b/src/War3Net.CodeAnalysis.Decompilers/Widget/MapUnitsDecompiler.cs @@ -10,7 +10,9 @@ using System.Diagnostics.CodeAnalysis; using System.Numerics; +using War3Net.Build.Common; using War3Net.Build.Widget; +using War3Net.CodeAnalysis.Jass; using War3Net.CodeAnalysis.Jass.Extensions; using War3Net.CodeAnalysis.Jass.Syntax; using War3Net.Common.Extensions; @@ -117,27 +119,22 @@ private bool TryDecompileCreateUnitsFunction(JassFunctionDeclarationSyntax creat var result = new List(); - foreach (var statement in createUnitsFunction.Body.Statements) + foreach (var statement in createUnitsFunction.Statements) { - if (statement is JassCommentSyntax || - statement is JassEmptySyntax) + if (statement is JassLocalVariableDeclarationStatementSyntax localVariableDeclarationStatement) { - continue; - } - else if (statement is JassLocalVariableDeclarationStatementSyntax localVariableDeclarationStatement) - { - var typeName = localVariableDeclarationStatement.Declarator.Type.TypeName.Name; + var typeName = localVariableDeclarationStatement.Declarator.GetVariableType().GetToken().Text; if (string.Equals(typeName, "player", StringComparison.Ordinal)) { if (localVariableDeclarationStatement.Declarator is JassVariableDeclaratorSyntax variableDeclarator && variableDeclarator.Value is not null && variableDeclarator.Value.Expression is JassInvocationExpressionSyntax playerInvocationExpression && - string.Equals(playerInvocationExpression.IdentifierName.Name, "Player", StringComparison.Ordinal) && - playerInvocationExpression.Arguments.Arguments.Length == 1 && - playerInvocationExpression.Arguments.Arguments[0].TryGetPlayerIdExpressionValue(Context.MaxPlayerSlots, out var playerId)) + string.Equals(playerInvocationExpression.IdentifierName.Token.Text, "Player", StringComparison.Ordinal) && + playerInvocationExpression.ArgumentList.ArgumentList.Items.Length == 1 && + playerInvocationExpression.ArgumentList.ArgumentList.Items[0].TryGetPlayerIdExpressionValue(Context.MaxPlayerSlots, out var playerId)) { - localPlayerVariableName = variableDeclarator.IdentifierName.Name; + localPlayerVariableName = variableDeclarator.IdentifierName.Token.Text; localPlayerVariableValue = playerId; } else @@ -168,31 +165,31 @@ variableDeclarator.Value.Expression is JassInvocationExpressionSyntax playerInvo } else if (statement is JassSetStatementSyntax setStatement) { - if (setStatement.Indexer is null) + if (setStatement.ElementAccessClause is null) { if (setStatement.Value.Expression is JassInvocationExpressionSyntax invocationExpression) { - if (string.Equals(invocationExpression.IdentifierName.Name, "CreateUnit", StringComparison.Ordinal)) + if (string.Equals(invocationExpression.IdentifierName.Token.Text, "CreateUnit", StringComparison.Ordinal)) { - if (invocationExpression.Arguments.Arguments.Length == 5 && - invocationExpression.Arguments.Arguments[0] is JassVariableReferenceExpressionSyntax playerVariableReferenceExpression && - invocationExpression.Arguments.Arguments[1].TryGetIntegerExpressionValue(out var unitId) && - invocationExpression.Arguments.Arguments[2].TryGetRealExpressionValue(out var x) && - invocationExpression.Arguments.Arguments[3].TryGetRealExpressionValue(out var y) && - invocationExpression.Arguments.Arguments[4].TryGetRealExpressionValue(out var face) && - string.Equals(playerVariableReferenceExpression.IdentifierName.Name, localPlayerVariableName, StringComparison.Ordinal)) + if (invocationExpression.ArgumentList.ArgumentList.Items.Length == 5 && + invocationExpression.ArgumentList.ArgumentList.Items[0].TryGetIdentifierNameValue(out var playerVariableName) && + invocationExpression.ArgumentList.ArgumentList.Items[1].TryGetIntegerExpressionValue(out var unitId) && + invocationExpression.ArgumentList.ArgumentList.Items[2].TryGetRealExpressionValue(out var x) && + invocationExpression.ArgumentList.ArgumentList.Items[3].TryGetRealExpressionValue(out var y) && + invocationExpression.ArgumentList.ArgumentList.Items[4].TryGetRealExpressionValue(out var face) && + string.Equals(playerVariableName, localPlayerVariableName, StringComparison.Ordinal)) { var unit = new UnitData { OwnerId = localPlayerVariableValue.Value, TypeId = unitId.InvertEndianness(), Position = new Vector3(x, y, 0f), - Rotation = face * (MathF.PI / 180f), + Rotation = face * W3MathF.Deg2Rad, Scale = Vector3.One, Flags = 2, GoldAmount = 12500, HeroLevel = 1, - CreationNumber = CreationNumber++ + CreationNumber = CreationNumber++, }; unit.SkinId = unit.TypeId; @@ -205,29 +202,29 @@ invocationExpression.Arguments.Arguments[0] is JassVariableReferenceExpressionSy return false; } } - else if (string.Equals(invocationExpression.IdentifierName.Name, "BlzCreateUnitWithSkin", StringComparison.Ordinal)) + else if (string.Equals(invocationExpression.IdentifierName.Token.Text, "BlzCreateUnitWithSkin", StringComparison.Ordinal)) { - if (invocationExpression.Arguments.Arguments.Length == 6 && - invocationExpression.Arguments.Arguments[0] is JassVariableReferenceExpressionSyntax playerVariableReferenceExpression && - invocationExpression.Arguments.Arguments[1].TryGetIntegerExpressionValue(out var unitId) && - invocationExpression.Arguments.Arguments[2].TryGetRealExpressionValue(out var x) && - invocationExpression.Arguments.Arguments[3].TryGetRealExpressionValue(out var y) && - invocationExpression.Arguments.Arguments[4].TryGetRealExpressionValue(out var face) && - invocationExpression.Arguments.Arguments[5].TryGetIntegerExpressionValue(out var skinId) && - string.Equals(playerVariableReferenceExpression.IdentifierName.Name, localPlayerVariableName, StringComparison.Ordinal)) + if (invocationExpression.ArgumentList.ArgumentList.Items.Length == 6 && + invocationExpression.ArgumentList.ArgumentList.Items[0].TryGetIdentifierNameValue(out var playerVariableName) && + invocationExpression.ArgumentList.ArgumentList.Items[1].TryGetIntegerExpressionValue(out var unitId) && + invocationExpression.ArgumentList.ArgumentList.Items[2].TryGetRealExpressionValue(out var x) && + invocationExpression.ArgumentList.ArgumentList.Items[3].TryGetRealExpressionValue(out var y) && + invocationExpression.ArgumentList.ArgumentList.Items[4].TryGetRealExpressionValue(out var face) && + invocationExpression.ArgumentList.ArgumentList.Items[5].TryGetIntegerExpressionValue(out var skinId) && + string.Equals(playerVariableName, localPlayerVariableName, StringComparison.Ordinal)) { var unit = new UnitData { OwnerId = localPlayerVariableValue.Value, TypeId = unitId.InvertEndianness(), Position = new Vector3(x, y, 0f), - Rotation = face * (MathF.PI / 180f), + Rotation = face * W3MathF.Deg2Rad, Scale = Vector3.One, SkinId = skinId.InvertEndianness(), Flags = 2, GoldAmount = 12500, HeroLevel = 1, - CreationNumber = CreationNumber++ + CreationNumber = CreationNumber++, }; result.Add(unit); @@ -238,17 +235,17 @@ invocationExpression.Arguments.Arguments[0] is JassVariableReferenceExpressionSy return false; } } - else if (string.Equals(invocationExpression.IdentifierName.Name, "CreateTrigger", StringComparison.Ordinal)) + else if (string.Equals(invocationExpression.IdentifierName.Token.Text, "CreateTrigger", StringComparison.Ordinal)) { // TODO continue; } - else if (string.Equals(invocationExpression.IdentifierName.Name, "GetUnitState", StringComparison.Ordinal)) + else if (string.Equals(invocationExpression.IdentifierName.Token.Text, "GetUnitState", StringComparison.Ordinal)) { // TODO continue; } - else if (string.Equals(invocationExpression.IdentifierName.Name, "RandomDistChoose", StringComparison.Ordinal)) + else if (string.Equals(invocationExpression.IdentifierName.Token.Text, "RandomDistChoose", StringComparison.Ordinal)) { // TODO continue; @@ -259,7 +256,7 @@ invocationExpression.Arguments.Arguments[0] is JassVariableReferenceExpressionSy return false; } } - else if (setStatement.Value.Expression is JassArrayReferenceExpressionSyntax) + else if (setStatement.Value.Expression is JassElementAccessExpressionSyntax) { // TODO continue; @@ -278,11 +275,11 @@ invocationExpression.Arguments.Arguments[0] is JassVariableReferenceExpressionSy } else if (statement is JassCallStatementSyntax callStatement) { - if (string.Equals(callStatement.IdentifierName.Name, "SetResourceAmount", StringComparison.Ordinal)) + if (string.Equals(callStatement.IdentifierName.Token.Text, "SetResourceAmount", StringComparison.Ordinal)) { - if (callStatement.Arguments.Arguments.Length == 2 && - callStatement.Arguments.Arguments[0] is JassVariableReferenceExpressionSyntax unitVariableReferenceExpression && - callStatement.Arguments.Arguments[1].TryGetIntegerExpressionValue(out var amount)) + if (callStatement.ArgumentList.ArgumentList.Items.Length == 2 && + callStatement.ArgumentList.ArgumentList.Items[0] is JassIdentifierNameSyntax && + callStatement.ArgumentList.ArgumentList.Items[1].TryGetIntegerExpressionValue(out var amount)) { result[^1].GoldAmount = amount; } @@ -292,14 +289,14 @@ callStatement.Arguments.Arguments[0] is JassVariableReferenceExpressionSyntax un return false; } } - else if (string.Equals(callStatement.IdentifierName.Name, "SetUnitColor", StringComparison.Ordinal)) + else if (string.Equals(callStatement.IdentifierName.Token.Text, "SetUnitColor", StringComparison.Ordinal)) { - if (callStatement.Arguments.Arguments.Length == 2 && - callStatement.Arguments.Arguments[0] is JassVariableReferenceExpressionSyntax unitVariableReferenceExpression && - callStatement.Arguments.Arguments[1] is JassInvocationExpressionSyntax convertPlayerColorInvocationExpression && - string.Equals(convertPlayerColorInvocationExpression.IdentifierName.Name, "ConvertPlayerColor", StringComparison.Ordinal) && - convertPlayerColorInvocationExpression.Arguments.Arguments.Length == 1 && - convertPlayerColorInvocationExpression.Arguments.Arguments[0].TryGetIntegerExpressionValue(out var playerColorId)) + if (callStatement.ArgumentList.ArgumentList.Items.Length == 2 && + callStatement.ArgumentList.ArgumentList.Items[0] is JassIdentifierNameSyntax && + callStatement.ArgumentList.ArgumentList.Items[1] is JassInvocationExpressionSyntax convertPlayerColorInvocationExpression && + string.Equals(convertPlayerColorInvocationExpression.IdentifierName.Token.Text, "ConvertPlayerColor", StringComparison.Ordinal) && + convertPlayerColorInvocationExpression.ArgumentList.ArgumentList.Items.Length == 1 && + convertPlayerColorInvocationExpression.ArgumentList.ArgumentList.Items[0].TryGetIntegerExpressionValue(out var playerColorId)) { result[^1].CustomPlayerColorId = playerColorId; } @@ -309,11 +306,11 @@ callStatement.Arguments.Arguments[1] is JassInvocationExpressionSyntax convertPl return false; } } - else if (string.Equals(callStatement.IdentifierName.Name, "SetUnitAcquireRange", StringComparison.Ordinal)) + else if (string.Equals(callStatement.IdentifierName.Token.Text, "SetUnitAcquireRange", StringComparison.Ordinal)) { - if (callStatement.Arguments.Arguments.Length == 2 && - callStatement.Arguments.Arguments[0] is JassVariableReferenceExpressionSyntax unitVariableReferenceExpression && - callStatement.Arguments.Arguments[1].TryGetRealExpressionValue(out var acquireRange)) + if (callStatement.ArgumentList.ArgumentList.Items.Length == 2 && + callStatement.ArgumentList.ArgumentList.Items[0] is JassIdentifierNameSyntax && + callStatement.ArgumentList.ArgumentList.Items[1].TryGetRealExpressionValue(out var acquireRange)) { const float CampAcquireRange = 200f; result[^1].TargetAcquisition = acquireRange == CampAcquireRange ? -2f : acquireRange; @@ -324,18 +321,18 @@ callStatement.Arguments.Arguments[0] is JassVariableReferenceExpressionSyntax un return false; } } - else if (string.Equals(callStatement.IdentifierName.Name, "SetUnitState", StringComparison.Ordinal)) + else if (string.Equals(callStatement.IdentifierName.Token.Text, "SetUnitState", StringComparison.Ordinal)) { - if (callStatement.Arguments.Arguments.Length == 3 && - callStatement.Arguments.Arguments[0] is JassVariableReferenceExpressionSyntax unitVariableReferenceExpression && - callStatement.Arguments.Arguments[1] is JassVariableReferenceExpressionSyntax unitStateVariableReferenceExpression) + if (callStatement.ArgumentList.ArgumentList.Items.Length == 3 && + callStatement.ArgumentList.ArgumentList.Items[0] is JassIdentifierNameSyntax && + callStatement.ArgumentList.ArgumentList.Items[1].TryGetIdentifierNameValue(out var unitState)) { - if (string.Equals(unitStateVariableReferenceExpression.IdentifierName.Name, "UNIT_STATE_LIFE", StringComparison.Ordinal)) + if (string.Equals(unitState, "UNIT_STATE_LIFE", StringComparison.Ordinal)) { - if (callStatement.Arguments.Arguments[2] is JassBinaryExpressionSyntax binaryExpression && + if (callStatement.ArgumentList.ArgumentList.Items[2] is JassBinaryExpressionSyntax binaryExpression && binaryExpression.Left.TryGetRealExpressionValue(out var hp) && - binaryExpression.Operator == BinaryOperatorType.Multiplication && - binaryExpression.Right is JassVariableReferenceExpressionSyntax) + binaryExpression.SyntaxKind == JassSyntaxKind.MultiplyExpression && + binaryExpression.Right is JassIdentifierNameSyntax) { result[^1].HP = (int)(100 * hp); } @@ -345,9 +342,9 @@ callStatement.Arguments.Arguments[0] is JassVariableReferenceExpressionSyntax un return false; } } - else if (string.Equals(unitStateVariableReferenceExpression.IdentifierName.Name, "UNIT_STATE_MANA", StringComparison.Ordinal)) + else if (string.Equals(unitState, "UNIT_STATE_MANA", StringComparison.Ordinal)) { - if (callStatement.Arguments.Arguments[2].TryGetIntegerExpressionValue(out var mp)) + if (callStatement.ArgumentList.ArgumentList.Items[2].TryGetIntegerExpressionValue(out var mp)) { result[^1].MP = mp; } @@ -369,12 +366,12 @@ callStatement.Arguments.Arguments[0] is JassVariableReferenceExpressionSyntax un return false; } } - else if (string.Equals(callStatement.IdentifierName.Name, "UnitAddItemToSlotById", StringComparison.Ordinal)) + else if (string.Equals(callStatement.IdentifierName.Token.Text, "UnitAddItemToSlotById", StringComparison.Ordinal)) { - if (callStatement.Arguments.Arguments.Length == 3 && - callStatement.Arguments.Arguments[0] is JassVariableReferenceExpressionSyntax && - callStatement.Arguments.Arguments[1].TryGetIntegerExpressionValue(out var itemId) && - callStatement.Arguments.Arguments[2].TryGetIntegerExpressionValue(out var slot)) + if (callStatement.ArgumentList.ArgumentList.Items.Length == 3 && + callStatement.ArgumentList.ArgumentList.Items[0] is JassIdentifierNameSyntax && + callStatement.ArgumentList.ArgumentList.Items[1].TryGetIntegerExpressionValue(out var itemId) && + callStatement.ArgumentList.ArgumentList.Items[2].TryGetIntegerExpressionValue(out var slot)) { result[^1].InventoryData.Add(new InventoryItemData { @@ -388,12 +385,12 @@ callStatement.Arguments.Arguments[0] is JassVariableReferenceExpressionSyntax && return false; } } - else if (string.Equals(callStatement.IdentifierName.Name, "SetHeroLevel", StringComparison.Ordinal)) + else if (string.Equals(callStatement.IdentifierName.Token.Text, "SetHeroLevel", StringComparison.Ordinal)) { - if (callStatement.Arguments.Arguments.Length == 3 && - callStatement.Arguments.Arguments[0] is JassVariableReferenceExpressionSyntax unitVariableReferenceExpression && - callStatement.Arguments.Arguments[1].TryGetIntegerExpressionValue(out var level) && - callStatement.Arguments.Arguments[2] is JassBooleanLiteralExpressionSyntax) + if (callStatement.ArgumentList.ArgumentList.Items.Length == 3 && + callStatement.ArgumentList.ArgumentList.Items[0] is JassIdentifierNameSyntax && + callStatement.ArgumentList.ArgumentList.Items[1].TryGetIntegerExpressionValue(out var level) && + callStatement.ArgumentList.ArgumentList.Items[2].TryGetBooleanExpressionValue(out _)) { result[^1].HeroLevel = level; } @@ -403,12 +400,12 @@ callStatement.Arguments.Arguments[0] is JassVariableReferenceExpressionSyntax un return false; } } - else if (string.Equals(callStatement.IdentifierName.Name, "SetHeroStr", StringComparison.Ordinal)) + else if (string.Equals(callStatement.IdentifierName.Token.Text, "SetHeroStr", StringComparison.Ordinal)) { - if (callStatement.Arguments.Arguments.Length == 3 && - callStatement.Arguments.Arguments[0] is JassVariableReferenceExpressionSyntax unitVariableReferenceExpression && - callStatement.Arguments.Arguments[1].TryGetIntegerExpressionValue(out var value) && - callStatement.Arguments.Arguments[2] is JassBooleanLiteralExpressionSyntax) + if (callStatement.ArgumentList.ArgumentList.Items.Length == 3 && + callStatement.ArgumentList.ArgumentList.Items[0] is JassIdentifierNameSyntax && + callStatement.ArgumentList.ArgumentList.Items[1].TryGetIntegerExpressionValue(out var value) && + callStatement.ArgumentList.ArgumentList.Items[2].TryGetBooleanExpressionValue(out _)) { result[^1].HeroStrength = value; } @@ -418,12 +415,12 @@ callStatement.Arguments.Arguments[0] is JassVariableReferenceExpressionSyntax un return false; } } - else if (string.Equals(callStatement.IdentifierName.Name, "SetHeroAgi", StringComparison.Ordinal)) + else if (string.Equals(callStatement.IdentifierName.Token.Text, "SetHeroAgi", StringComparison.Ordinal)) { - if (callStatement.Arguments.Arguments.Length == 3 && - callStatement.Arguments.Arguments[0] is JassVariableReferenceExpressionSyntax unitVariableReferenceExpression && - callStatement.Arguments.Arguments[1].TryGetIntegerExpressionValue(out var value) && - callStatement.Arguments.Arguments[2] is JassBooleanLiteralExpressionSyntax) + if (callStatement.ArgumentList.ArgumentList.Items.Length == 3 && + callStatement.ArgumentList.ArgumentList.Items[0] is JassIdentifierNameSyntax && + callStatement.ArgumentList.ArgumentList.Items[1].TryGetIntegerExpressionValue(out var value) && + callStatement.ArgumentList.ArgumentList.Items[2].TryGetBooleanExpressionValue(out _)) { result[^1].HeroAgility = value; } @@ -433,12 +430,12 @@ callStatement.Arguments.Arguments[0] is JassVariableReferenceExpressionSyntax un return false; } } - else if (string.Equals(callStatement.IdentifierName.Name, "SetHeroInt", StringComparison.Ordinal)) + else if (string.Equals(callStatement.IdentifierName.Token.Text, "SetHeroInt", StringComparison.Ordinal)) { - if (callStatement.Arguments.Arguments.Length == 3 && - callStatement.Arguments.Arguments[0] is JassVariableReferenceExpressionSyntax unitVariableReferenceExpression && - callStatement.Arguments.Arguments[1].TryGetIntegerExpressionValue(out var value) && - callStatement.Arguments.Arguments[2] is JassBooleanLiteralExpressionSyntax) + if (callStatement.ArgumentList.ArgumentList.Items.Length == 3 && + callStatement.ArgumentList.ArgumentList.Items[0] is JassIdentifierNameSyntax && + callStatement.ArgumentList.ArgumentList.Items[1].TryGetIntegerExpressionValue(out var value) && + callStatement.ArgumentList.ArgumentList.Items[2].TryGetBooleanExpressionValue(out _)) { result[^1].HeroIntelligence = value; } @@ -448,39 +445,39 @@ callStatement.Arguments.Arguments[0] is JassVariableReferenceExpressionSyntax un return false; } } - else if (string.Equals(callStatement.IdentifierName.Name, "SelectHeroSkill", StringComparison.Ordinal)) + else if (string.Equals(callStatement.IdentifierName.Token.Text, "SelectHeroSkill", StringComparison.Ordinal)) { // TODO continue; } - else if (string.Equals(callStatement.IdentifierName.Name, "IssueImmediateOrder", StringComparison.Ordinal)) + else if (string.Equals(callStatement.IdentifierName.Token.Text, "IssueImmediateOrder", StringComparison.Ordinal)) { // TODO continue; } - else if (string.Equals(callStatement.IdentifierName.Name, "RandomDistReset", StringComparison.Ordinal)) + else if (string.Equals(callStatement.IdentifierName.Token.Text, "RandomDistReset", StringComparison.Ordinal)) { // TODO continue; } - else if (string.Equals(callStatement.IdentifierName.Name, "RandomDistAddItem", StringComparison.Ordinal)) + else if (string.Equals(callStatement.IdentifierName.Token.Text, "RandomDistAddItem", StringComparison.Ordinal)) { // TODO continue; } - else if (string.Equals(callStatement.IdentifierName.Name, "TriggerRegisterUnitEvent", StringComparison.Ordinal)) + else if (string.Equals(callStatement.IdentifierName.Token.Text, "TriggerRegisterUnitEvent", StringComparison.Ordinal)) { // TODO continue; } - else if (string.Equals(callStatement.IdentifierName.Name, "TriggerAddAction", StringComparison.Ordinal)) + else if (string.Equals(callStatement.IdentifierName.Token.Text, "TriggerAddAction", StringComparison.Ordinal)) { // TODO continue; } - else if (callStatement.Arguments.Arguments.IsEmpty) + else if (callStatement.ArgumentList.ArgumentList.Items.IsEmpty) { - if (Context.FunctionDeclarations.TryGetValue(callStatement.IdentifierName.Name, out var subFunction) && + if (Context.FunctionDeclarations.TryGetValue(callStatement.IdentifierName.Token.Text, out var subFunction) && TryDecompileCreateUnitsFunction(subFunction.FunctionDeclaration, out var subFunctionResult)) { result.AddRange(subFunctionResult); @@ -499,9 +496,9 @@ callStatement.Arguments.Arguments[0] is JassVariableReferenceExpressionSyntax un } else if (statement is JassIfStatementSyntax ifStatement) { - if (ifStatement.Condition.Deparenthesize() is JassBinaryExpressionSyntax binaryExpression && - binaryExpression.Left is JassVariableReferenceExpressionSyntax && - binaryExpression.Operator == BinaryOperatorType.NotEquals && + if (ifStatement.IfClause.IfClauseDeclarator.Condition.Deparenthesize() is JassBinaryExpressionSyntax binaryExpression && + binaryExpression.Left is JassIdentifierNameSyntax && + binaryExpression.SyntaxKind == JassSyntaxKind.NotEqualsExpression && binaryExpression.Right.TryGetIntegerExpressionValue(out var value) && value == -1) { @@ -529,23 +526,18 @@ private bool TryDecompileCreateItemsFunction(JassFunctionDeclarationSyntax creat { var result = new List(); - foreach (var statement in createItemsFunction.Body.Statements) + foreach (var statement in createItemsFunction.Statements) { - if (statement is JassCommentSyntax || - statement is JassEmptySyntax) - { - continue; - } - else if (statement is JassSetStatementSyntax setStatement) + if (statement is JassSetStatementSyntax setStatement) { if (setStatement.Value.Expression is JassInvocationExpressionSyntax invocationExpression) { - if (string.Equals(invocationExpression.IdentifierName.Name, "CreateItem", StringComparison.Ordinal)) + if (string.Equals(invocationExpression.IdentifierName.Token.Text, "CreateItem", StringComparison.Ordinal)) { - if (invocationExpression.Arguments.Arguments.Length == 3 && - invocationExpression.Arguments.Arguments[0].TryGetIntegerExpressionValue(out var unitId) && - invocationExpression.Arguments.Arguments[1].TryGetRealExpressionValue(out var x) && - invocationExpression.Arguments.Arguments[2].TryGetRealExpressionValue(out var y)) + if (invocationExpression.ArgumentList.ArgumentList.Items.Length == 3 && + invocationExpression.ArgumentList.ArgumentList.Items[0].TryGetIntegerExpressionValue(out var unitId) && + invocationExpression.ArgumentList.ArgumentList.Items[1].TryGetRealExpressionValue(out var x) && + invocationExpression.ArgumentList.ArgumentList.Items[2].TryGetRealExpressionValue(out var y)) { var unit = new UnitData { @@ -557,7 +549,7 @@ private bool TryDecompileCreateItemsFunction(JassFunctionDeclarationSyntax creat Flags = 2, GoldAmount = 12500, HeroLevel = 1, - CreationNumber = CreationNumber++ + CreationNumber = CreationNumber++, }; unit.SkinId = unit.TypeId; @@ -565,13 +557,13 @@ private bool TryDecompileCreateItemsFunction(JassFunctionDeclarationSyntax creat result.Add(unit); } } - else if (string.Equals(invocationExpression.IdentifierName.Name, "BlzCreateItemWithSkin", StringComparison.Ordinal)) + else if (string.Equals(invocationExpression.IdentifierName.Token.Text, "BlzCreateItemWithSkin", StringComparison.Ordinal)) { - if (invocationExpression.Arguments.Arguments.Length == 4 && - invocationExpression.Arguments.Arguments[0].TryGetIntegerExpressionValue(out var unitId) && - invocationExpression.Arguments.Arguments[1].TryGetRealExpressionValue(out var x) && - invocationExpression.Arguments.Arguments[2].TryGetRealExpressionValue(out var y) && - invocationExpression.Arguments.Arguments[3].TryGetIntegerExpressionValue(out var skinId)) + if (invocationExpression.ArgumentList.ArgumentList.Items.Length == 4 && + invocationExpression.ArgumentList.ArgumentList.Items[0].TryGetIntegerExpressionValue(out var unitId) && + invocationExpression.ArgumentList.ArgumentList.Items[1].TryGetRealExpressionValue(out var x) && + invocationExpression.ArgumentList.ArgumentList.Items[2].TryGetRealExpressionValue(out var y) && + invocationExpression.ArgumentList.ArgumentList.Items[3].TryGetIntegerExpressionValue(out var skinId)) { var unit = new UnitData { @@ -584,7 +576,7 @@ private bool TryDecompileCreateItemsFunction(JassFunctionDeclarationSyntax creat Flags = 2, GoldAmount = 12500, HeroLevel = 1, - CreationNumber = CreationNumber++ + CreationNumber = CreationNumber++, }; result.Add(unit); @@ -594,12 +586,12 @@ private bool TryDecompileCreateItemsFunction(JassFunctionDeclarationSyntax creat } else if (statement is JassCallStatementSyntax callStatement) { - if (string.Equals(callStatement.IdentifierName.Name, "CreateItem", StringComparison.Ordinal)) + if (string.Equals(callStatement.IdentifierName.Token.Text, "CreateItem", StringComparison.Ordinal)) { - if (callStatement.Arguments.Arguments.Length == 3 && - callStatement.Arguments.Arguments[0].TryGetIntegerExpressionValue(out var itemId) && - callStatement.Arguments.Arguments[1].TryGetRealExpressionValue(out var x) && - callStatement.Arguments.Arguments[2].TryGetRealExpressionValue(out var y)) + if (callStatement.ArgumentList.ArgumentList.Items.Length == 3 && + callStatement.ArgumentList.ArgumentList.Items[0].TryGetIntegerExpressionValue(out var itemId) && + callStatement.ArgumentList.ArgumentList.Items[1].TryGetRealExpressionValue(out var x) && + callStatement.ArgumentList.ArgumentList.Items[2].TryGetRealExpressionValue(out var y)) { var item = new UnitData { @@ -611,7 +603,7 @@ private bool TryDecompileCreateItemsFunction(JassFunctionDeclarationSyntax creat Flags = 2, GoldAmount = 12500, HeroLevel = 1, - CreationNumber = CreationNumber++ + CreationNumber = CreationNumber++, }; item.SkinId = item.TypeId; @@ -619,13 +611,13 @@ private bool TryDecompileCreateItemsFunction(JassFunctionDeclarationSyntax creat result.Add(item); } } - else if (string.Equals(callStatement.IdentifierName.Name, "BlzCreateItemWithSkin", StringComparison.Ordinal)) + else if (string.Equals(callStatement.IdentifierName.Token.Text, "BlzCreateItemWithSkin", StringComparison.Ordinal)) { - if (callStatement.Arguments.Arguments.Length == 4 && - callStatement.Arguments.Arguments[0].TryGetIntegerExpressionValue(out var itemId) && - callStatement.Arguments.Arguments[1].TryGetRealExpressionValue(out var x) && - callStatement.Arguments.Arguments[2].TryGetRealExpressionValue(out var y) && - callStatement.Arguments.Arguments[3].TryGetIntegerExpressionValue(out var skinId)) + if (callStatement.ArgumentList.ArgumentList.Items.Length == 4 && + callStatement.ArgumentList.ArgumentList.Items[0].TryGetIntegerExpressionValue(out var itemId) && + callStatement.ArgumentList.ArgumentList.Items[1].TryGetRealExpressionValue(out var x) && + callStatement.ArgumentList.ArgumentList.Items[2].TryGetRealExpressionValue(out var y) && + callStatement.ArgumentList.ArgumentList.Items[3].TryGetIntegerExpressionValue(out var skinId)) { var item = new UnitData { @@ -638,7 +630,7 @@ private bool TryDecompileCreateItemsFunction(JassFunctionDeclarationSyntax creat Flags = 2, GoldAmount = 12500, HeroLevel = 1, - CreationNumber = CreationNumber++ + CreationNumber = CreationNumber++, }; result.Add(item); @@ -655,15 +647,15 @@ private bool TryDecompileStartLocationPositionsConfigFunction(JassFunctionDeclar { var result = new Dictionary(); - foreach (var statement in configFunction.Body.Statements) + foreach (var statement in configFunction.Statements) { if (statement is JassCallStatementSyntax callStatement && - string.Equals(callStatement.IdentifierName.Name, "DefineStartLocation", StringComparison.Ordinal)) + string.Equals(callStatement.IdentifierName.Token.Text, "DefineStartLocation", StringComparison.Ordinal)) { - if (callStatement.Arguments.Arguments.Length == 3 && - callStatement.Arguments.Arguments[0].TryGetIntegerExpressionValue(out var index) && - callStatement.Arguments.Arguments[1].TryGetRealExpressionValue(out var x) && - callStatement.Arguments.Arguments[2].TryGetRealExpressionValue(out var y)) + if (callStatement.ArgumentList.ArgumentList.Items.Length == 3 && + callStatement.ArgumentList.ArgumentList.Items[0].TryGetIntegerExpressionValue(out var index) && + callStatement.ArgumentList.ArgumentList.Items[1].TryGetRealExpressionValue(out var x) && + callStatement.ArgumentList.ArgumentList.Items[2].TryGetRealExpressionValue(out var y)) { result.Add(index, new Vector2(x, y)); } @@ -690,23 +682,18 @@ private bool TryDecompileInitCustomPlayerSlotsFunction( { var result = new List(); - foreach (var statement in initCustomPlayerSlotsFunction.Body.Statements) + foreach (var statement in initCustomPlayerSlotsFunction.Statements) { - if (statement is JassCommentSyntax || - statement is JassEmptySyntax) - { - continue; - } - else if (statement is JassCallStatementSyntax callStatement) + if (statement is JassCallStatementSyntax callStatement) { - if (string.Equals(callStatement.IdentifierName.Name, "SetPlayerStartLocation", StringComparison.Ordinal)) + if (string.Equals(callStatement.IdentifierName.Token.Text, "SetPlayerStartLocation", StringComparison.Ordinal)) { - if (callStatement.Arguments.Arguments.Length == 2 && - callStatement.Arguments.Arguments[0] is JassInvocationExpressionSyntax playerInvocationExpression && - string.Equals(playerInvocationExpression.IdentifierName.Name, "Player", StringComparison.Ordinal) && - playerInvocationExpression.Arguments.Arguments.Length == 1 && - playerInvocationExpression.Arguments.Arguments[0].TryGetPlayerIdExpressionValue(Context.MaxPlayerSlots, out var playerId) && - callStatement.Arguments.Arguments[1].TryGetIntegerExpressionValue(out var startLocationNumber) && + if (callStatement.ArgumentList.ArgumentList.Items.Length == 2 && + callStatement.ArgumentList.ArgumentList.Items[0] is JassInvocationExpressionSyntax playerInvocationExpression && + string.Equals(playerInvocationExpression.IdentifierName.Token.Text, "Player", StringComparison.Ordinal) && + playerInvocationExpression.ArgumentList.ArgumentList.Items.Length == 1 && + playerInvocationExpression.ArgumentList.ArgumentList.Items[0].TryGetPlayerIdExpressionValue(Context.MaxPlayerSlots, out var playerId) && + callStatement.ArgumentList.ArgumentList.Items[1].TryGetIntegerExpressionValue(out var startLocationNumber) && startLocationPositions.TryGetValue(startLocationNumber, out var startLocationPosition)) { var unit = new UnitData @@ -720,7 +707,7 @@ callStatement.Arguments.Arguments[0] is JassInvocationExpressionSyntax playerInv GoldAmount = 12500, HeroLevel = 0, TargetAcquisition = 0, - CreationNumber = CreationNumber++ + CreationNumber = CreationNumber++, }; unit.SkinId = unit.TypeId; diff --git a/src/War3Net.CodeAnalysis.Jass/Builders/JassCompilationUnitBuilder.cs b/src/War3Net.CodeAnalysis.Jass/Builders/JassCompilationUnitBuilder.cs new file mode 100644 index 00000000..6f067dd0 --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/Builders/JassCompilationUnitBuilder.cs @@ -0,0 +1,96 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using System; +using System.Collections.Immutable; + +using War3Net.CodeAnalysis.Jass.Extensions; +using War3Net.CodeAnalysis.Jass.Syntax; + +namespace War3Net.CodeAnalysis.Jass.Builders +{ + public class JassCompilationUnitBuilder : JassSyntaxBuilder + { + private readonly ImmutableArray.Builder _declarationsBuilder; + + private JassGlobalsDeclarationBuilder? _globalsDeclarationBuilder; + private JassFunctionDeclarationBuilder? _functionDeclarationBuilder; + + public JassCompilationUnitBuilder() + { + _declarationsBuilder = ImmutableArray.CreateBuilder(); + } + + public JassCompilationUnitSyntax ToCompilationUnit(JassSyntaxToken endOfFileToken) + { + if (_globalsDeclarationBuilder is not null || + _functionDeclarationBuilder is not null) + { + throw new InvalidOperationException(); + } + + JassSyntaxFactory.ThrowHelper.ThrowIfInvalidToken(endOfFileToken, JassSyntaxKind.EndOfFileToken); + + return new JassCompilationUnitSyntax( + _declarationsBuilder.ToImmutable(), + endOfFileToken.PrependLeadingTrivia(BuildTriviaList())); + } + + public void AddDeclaration(JassTopLevelDeclarationSyntax declaration) + { + if (_globalsDeclarationBuilder is not null || + _functionDeclarationBuilder is not null) + { + throw new InvalidOperationException(); + } + + _declarationsBuilder.Add(declaration.PrependLeadingTrivia(BuildTriviaList())); + } + + public JassGlobalsDeclarationBuilder BeginGlobalsDeclaration(JassSyntaxToken globalsToken) + { + if (_functionDeclarationBuilder is not null) + { + throw new InvalidOperationException(); + } + + return _globalsDeclarationBuilder = new JassGlobalsDeclarationBuilder(globalsToken.PrependLeadingTrivia(BuildTriviaList())); + } + + public void EndGlobalsDeclaration(JassSyntaxToken endGlobalsToken) + { + if (_globalsDeclarationBuilder is null) + { + throw new InvalidOperationException(); + } + + _declarationsBuilder.Add(_globalsDeclarationBuilder.ToGlobalsDeclaration(endGlobalsToken)); + _globalsDeclarationBuilder = null; + } + + public JassFunctionDeclarationBuilder BeginFunctionDeclaration(JassFunctionDeclaratorSyntax functionDeclarator) + { + if (_globalsDeclarationBuilder is not null) + { + throw new InvalidOperationException(); + } + + return _functionDeclarationBuilder = new JassFunctionDeclarationBuilder(functionDeclarator.PrependLeadingTrivia(BuildTriviaList())); + } + + public void EndFunctionDeclaration(JassSyntaxToken endFunctionToken) + { + if (_functionDeclarationBuilder is null) + { + throw new InvalidOperationException(); + } + + _declarationsBuilder.Add(_functionDeclarationBuilder.ToFunctionDeclaration(endFunctionToken)); + _functionDeclarationBuilder = null; + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Builders/JassFunctionDeclarationBuilder.cs b/src/War3Net.CodeAnalysis.Jass/Builders/JassFunctionDeclarationBuilder.cs new file mode 100644 index 00000000..8a1ece30 --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/Builders/JassFunctionDeclarationBuilder.cs @@ -0,0 +1,32 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using War3Net.CodeAnalysis.Jass.Extensions; +using War3Net.CodeAnalysis.Jass.Syntax; + +namespace War3Net.CodeAnalysis.Jass.Builders +{ + public class JassFunctionDeclarationBuilder : JassStatementListSyntaxBuilder + { + private readonly JassFunctionDeclaratorSyntax _functionDeclarator; + + public JassFunctionDeclarationBuilder(JassFunctionDeclaratorSyntax functionDeclarator) + { + _functionDeclarator = functionDeclarator; + } + + public JassFunctionDeclarationSyntax ToFunctionDeclaration(JassSyntaxToken endFunctionToken) + { + JassSyntaxFactory.ThrowHelper.ThrowIfInvalidToken(endFunctionToken, JassSyntaxKind.EndFunctionKeyword); + + return new JassFunctionDeclarationSyntax( + _functionDeclarator, + BuildStatementList(), + endFunctionToken.PrependLeadingTrivia(BuildTriviaList())); + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Builders/JassGlobalsDeclarationBuilder.cs b/src/War3Net.CodeAnalysis.Jass/Builders/JassGlobalsDeclarationBuilder.cs new file mode 100644 index 00000000..db826265 --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/Builders/JassGlobalsDeclarationBuilder.cs @@ -0,0 +1,43 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using System.Collections.Immutable; + +using War3Net.CodeAnalysis.Jass.Extensions; +using War3Net.CodeAnalysis.Jass.Syntax; + +namespace War3Net.CodeAnalysis.Jass.Builders +{ + public class JassGlobalsDeclarationBuilder : JassSyntaxBuilder + { + private readonly ImmutableArray.Builder _globalDeclarationsBuilder; + private readonly JassSyntaxToken _globalsToken; + + public JassGlobalsDeclarationBuilder(JassSyntaxToken globalsToken) + { + JassSyntaxFactory.ThrowHelper.ThrowIfInvalidToken(globalsToken, JassSyntaxKind.GlobalsKeyword); + + _globalDeclarationsBuilder = ImmutableArray.CreateBuilder(); + _globalsToken = globalsToken; + } + + public JassGlobalsDeclarationSyntax ToGlobalsDeclaration(JassSyntaxToken endGlobalsToken) + { + JassSyntaxFactory.ThrowHelper.ThrowIfInvalidToken(endGlobalsToken, JassSyntaxKind.EndGlobalsKeyword); + + return new JassGlobalsDeclarationSyntax( + _globalsToken, + _globalDeclarationsBuilder.ToImmutable(), + endGlobalsToken.PrependLeadingTrivia(BuildTriviaList())); + } + + public void AddGlobalDeclaration(JassGlobalDeclarationSyntax globalDeclaration) + { + _globalDeclarationsBuilder.Add(globalDeclaration.PrependLeadingTrivia(BuildTriviaList())); + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Builders/JassIfStatementBuilder.cs b/src/War3Net.CodeAnalysis.Jass/Builders/JassIfStatementBuilder.cs new file mode 100644 index 00000000..3205180c --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/Builders/JassIfStatementBuilder.cs @@ -0,0 +1,92 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using System; +using System.Collections.Immutable; +using System.Diagnostics.CodeAnalysis; + +using War3Net.CodeAnalysis.Jass.Extensions; +using War3Net.CodeAnalysis.Jass.Syntax; + +namespace War3Net.CodeAnalysis.Jass.Builders +{ + public class JassIfStatementBuilder : JassStatementListSyntaxBuilder + { + private readonly ImmutableArray.Builder _elseIfClausesBuilder; + private readonly JassIfClauseDeclaratorSyntax _ifClauseDeclarator; + + private JassIfClauseSyntax? _ifClause; + private JassElseIfClauseDeclaratorSyntax? _elseIfClauseDeclarator; + private JassSyntaxToken? _elseToken; + private JassElseClauseSyntax? _elseClause; + + public JassIfStatementBuilder(JassIfClauseDeclaratorSyntax ifClauseDeclarator) + { + _elseIfClausesBuilder = ImmutableArray.CreateBuilder(); + _ifClauseDeclarator = ifClauseDeclarator; + } + + public JassIfStatementSyntax ToIfStatement(JassSyntaxToken endIfToken) + { + JassSyntaxFactory.ThrowHelper.ThrowIfInvalidToken(endIfToken, JassSyntaxKind.EndIfKeyword); + + EndCurrentClause(); + + return new JassIfStatementSyntax( + _ifClause, + _elseIfClausesBuilder.ToImmutable(), + _elseClause, + endIfToken); + } + + public void BeginElseIfClause(JassElseIfClauseDeclaratorSyntax elseIfClauseDeclarator) + { + EndCurrentClause(); + if (_elseClause is not null) + { + throw new InvalidOperationException(); + } + + _elseIfClauseDeclarator = elseIfClauseDeclarator.PrependLeadingTrivia(BuildTriviaList()); + } + + public void BeginElseClause(JassSyntaxToken elseToken) + { + JassSyntaxFactory.ThrowHelper.ThrowIfInvalidToken(elseToken, JassSyntaxKind.ElseKeyword); + + EndCurrentClause(); + if (_elseClause is not null) + { + throw new InvalidOperationException(); + } + + _elseIfClauseDeclarator = null; + _elseToken = elseToken.PrependLeadingTrivia(BuildTriviaList()); + } + + [MemberNotNull(nameof(_ifClause))] + private void EndCurrentClause() + { + if (_ifClause is null) + { + _ifClause = new JassIfClauseSyntax(_ifClauseDeclarator, BuildStatementList()); + } + else if (_elseIfClauseDeclarator is not null) + { + _elseIfClausesBuilder.Add(new JassElseIfClauseSyntax(_elseIfClauseDeclarator, BuildStatementList())); + } + else if (_elseToken is not null) + { + _elseClause = new JassElseClauseSyntax(_elseToken, BuildStatementList()); + } + else + { + throw new InvalidOperationException(); + } + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Builders/JassLoopStatementBuilder.cs b/src/War3Net.CodeAnalysis.Jass/Builders/JassLoopStatementBuilder.cs new file mode 100644 index 00000000..f39e32f1 --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/Builders/JassLoopStatementBuilder.cs @@ -0,0 +1,34 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using War3Net.CodeAnalysis.Jass.Extensions; +using War3Net.CodeAnalysis.Jass.Syntax; + +namespace War3Net.CodeAnalysis.Jass.Builders +{ + public class JassLoopStatementBuilder : JassStatementListSyntaxBuilder + { + private readonly JassSyntaxToken _loopToken; + + public JassLoopStatementBuilder(JassSyntaxToken loopToken) + { + JassSyntaxFactory.ThrowHelper.ThrowIfInvalidToken(loopToken, JassSyntaxKind.LoopKeyword); + + _loopToken = loopToken; + } + + public JassLoopStatementSyntax ToLoopStatement(JassSyntaxToken endLoopToken) + { + JassSyntaxFactory.ThrowHelper.ThrowIfInvalidToken(endLoopToken, JassSyntaxKind.EndLoopKeyword); + + return new JassLoopStatementSyntax( + _loopToken, + BuildStatementList(), + endLoopToken.PrependLeadingTrivia(BuildTriviaList())); + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Builders/JassStatementListSyntaxBuilder.cs b/src/War3Net.CodeAnalysis.Jass/Builders/JassStatementListSyntaxBuilder.cs new file mode 100644 index 00000000..ec427949 --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/Builders/JassStatementListSyntaxBuilder.cs @@ -0,0 +1,99 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using System; +using System.Collections.Immutable; + +using War3Net.CodeAnalysis.Jass.Extensions; +using War3Net.CodeAnalysis.Jass.Syntax; + +namespace War3Net.CodeAnalysis.Jass.Builders +{ + public abstract class JassStatementListSyntaxBuilder : JassSyntaxBuilder + { + private readonly ImmutableArray.Builder _statementsBuilder; + + private JassIfStatementBuilder? _ifStatementBuilder; + private JassLoopStatementBuilder? _loopStatementBuilder; + + public JassStatementListSyntaxBuilder() + { + _statementsBuilder = ImmutableArray.CreateBuilder(); + } + + public void AddStatement(JassStatementSyntax statement) + { + if (_ifStatementBuilder is not null || + _loopStatementBuilder is not null) + { + throw new InvalidOperationException(); + } + + _statementsBuilder.Add(statement.PrependLeadingTrivia(BuildTriviaList())); + } + + public JassIfStatementBuilder BeginIfStatement(JassIfClauseDeclaratorSyntax ifClauseDeclarator) + { + if (_loopStatementBuilder is not null) + { + throw new InvalidOperationException(); + } + + return _ifStatementBuilder = new JassIfStatementBuilder(ifClauseDeclarator.PrependLeadingTrivia(BuildTriviaList())); + } + + public void EndIfStatement(JassSyntaxToken endifToken) + { + if (_ifStatementBuilder is null) + { + throw new InvalidOperationException(); + } + + _statementsBuilder.Add(_ifStatementBuilder.ToIfStatement(endifToken)); + _ifStatementBuilder = null; + } + + public JassLoopStatementBuilder BeginLoopStatement(JassSyntaxToken loopToken) + { + if (_ifStatementBuilder is not null) + { + throw new InvalidOperationException(); + } + + return _loopStatementBuilder = new JassLoopStatementBuilder(loopToken.PrependLeadingTrivia(BuildTriviaList())); + } + + public void EndLoopStatement(JassSyntaxToken endLoopToken) + { + if (_loopStatementBuilder is null) + { + throw new InvalidOperationException(); + } + + _statementsBuilder.Add(_loopStatementBuilder.ToLoopStatement(endLoopToken)); + _loopStatementBuilder = null; + } + + protected ImmutableArray BuildStatementList() + { + if (_ifStatementBuilder is not null || + _loopStatementBuilder is not null) + { + throw new InvalidOperationException(); + } + + if (_statementsBuilder.Count == 0) + { + return ImmutableArray.Empty; + } + + var result = _statementsBuilder.ToImmutable(); + _statementsBuilder.Clear(); + return result; + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Builders/JassSyntaxBuilder.cs b/src/War3Net.CodeAnalysis.Jass/Builders/JassSyntaxBuilder.cs new file mode 100644 index 00000000..93fb64ac --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/Builders/JassSyntaxBuilder.cs @@ -0,0 +1,51 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using System.Collections.Generic; +using System.Collections.Immutable; + +using War3Net.CodeAnalysis.Jass.Syntax; + +namespace War3Net.CodeAnalysis.Jass.Builders +{ + public abstract class JassSyntaxBuilder + { + private readonly ImmutableArray.Builder _triviaBuilder; + + public JassSyntaxBuilder() + { + _triviaBuilder = ImmutableArray.CreateBuilder(); + } + + public void AddTrivia(JassSyntaxTrivia trivia) + { + _triviaBuilder.Add(trivia); + } + + public void AddTrivia(params JassSyntaxTrivia[] trivia) + { + _triviaBuilder.AddRange(trivia); + } + + public void AddTrivia(IEnumerable trivia) + { + _triviaBuilder.AddRange(trivia); + } + + protected JassSyntaxTriviaList BuildTriviaList() + { + if (_triviaBuilder.Count == 0) + { + return JassSyntaxTriviaList.Empty; + } + + var result = new JassSyntaxTriviaList(_triviaBuilder.ToImmutable()); + _triviaBuilder.Clear(); + return result; + } + } +} \ No newline at end of file diff --git a/src/War3Net.Build.Core/Providers/EscapedStringProvider.cs b/src/War3Net.CodeAnalysis.Jass/EscapedStringProvider.cs similarity index 98% rename from src/War3Net.Build.Core/Providers/EscapedStringProvider.cs rename to src/War3Net.CodeAnalysis.Jass/EscapedStringProvider.cs index 92990a96..367ed29f 100644 --- a/src/War3Net.Build.Core/Providers/EscapedStringProvider.cs +++ b/src/War3Net.CodeAnalysis.Jass/EscapedStringProvider.cs @@ -8,7 +8,7 @@ using System.Diagnostics.CodeAnalysis; using System.Text; -namespace War3Net.Build.Providers +namespace War3Net.CodeAnalysis.Jass { // Based on https://referencesource.microsoft.com/#System/regex/system/text/regularexpressions/RegexParser.cs,845bf727ea7a0421 public static class EscapedStringProvider diff --git a/src/War3Net.CodeAnalysis.Jass/Extensions/BinaryOperatorTypeExtensions.cs b/src/War3Net.CodeAnalysis.Jass/Extensions/BinaryOperatorTypeExtensions.cs deleted file mode 100644 index df09cfa6..00000000 --- a/src/War3Net.CodeAnalysis.Jass/Extensions/BinaryOperatorTypeExtensions.cs +++ /dev/null @@ -1,37 +0,0 @@ -// ------------------------------------------------------------------------------ -// -// Licensed under the MIT license. -// See the LICENSE file in the project root for more information. -// -// ------------------------------------------------------------------------------ - -using System.ComponentModel; - -using War3Net.CodeAnalysis.Jass.Syntax; - -namespace War3Net.CodeAnalysis.Jass.Extensions -{ - public static class BinaryOperatorTypeExtensions - { - public static string GetSymbol(this BinaryOperatorType binaryOperator) - { - return binaryOperator switch - { - BinaryOperatorType.Add => $"{JassSymbol.PlusSign}", - BinaryOperatorType.Subtract => $"{JassSymbol.MinusSign}", - BinaryOperatorType.Multiplication => $"{JassSymbol.Asterisk}", - BinaryOperatorType.Division => $"{JassSymbol.Slash}", - BinaryOperatorType.GreaterThan => $"{JassSymbol.GreaterThanSign}", - BinaryOperatorType.LessThan => $"{JassSymbol.LessThanSign}", - BinaryOperatorType.Equals => $"{JassSymbol.EqualsSign}{JassSymbol.EqualsSign}", - BinaryOperatorType.NotEquals => $"{JassSymbol.ExclamationMark}{JassSymbol.EqualsSign}", - BinaryOperatorType.GreaterOrEqual => $"{JassSymbol.GreaterThanSign}{JassSymbol.EqualsSign}", - BinaryOperatorType.LessOrEqual => $"{JassSymbol.LessThanSign}{JassSymbol.EqualsSign}", - BinaryOperatorType.And => JassKeyword.And, - BinaryOperatorType.Or => JassKeyword.Or, - - _ => throw new InvalidEnumArgumentException(nameof(binaryOperator), (int)binaryOperator, typeof(BinaryOperatorType)), - }; - } - } -} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Extensions/ExpressionSyntaxExtensions.cs b/src/War3Net.CodeAnalysis.Jass/Extensions/ExpressionSyntaxExtensions.cs deleted file mode 100644 index 620a3cc1..00000000 --- a/src/War3Net.CodeAnalysis.Jass/Extensions/ExpressionSyntaxExtensions.cs +++ /dev/null @@ -1,106 +0,0 @@ -// ------------------------------------------------------------------------------ -// -// Licensed under the MIT license. -// See the LICENSE file in the project root for more information. -// -// ------------------------------------------------------------------------------ - -using System; -using System.Globalization; - -using War3Net.CodeAnalysis.Jass.Syntax; - -namespace War3Net.CodeAnalysis.Jass.Extensions -{ - public static class ExpressionSyntaxExtensions - { - public static IExpressionSyntax Deparenthesize(this IExpressionSyntax expression) - { - while (expression is JassParenthesizedExpressionSyntax parenthesizedExpression) - { - expression = parenthesizedExpression.Expression; - } - - return expression; - } - - public static bool TryGetIntegerExpressionValue(this IExpressionSyntax expression, out int value) - { - switch (expression) - { - case JassDecimalLiteralExpressionSyntax decimalLiteralExpression: - value = decimalLiteralExpression.Value; - return true; - - case JassOctalLiteralExpressionSyntax octalLiteralExpression: - value = octalLiteralExpression.Value; - return true; - - case JassFourCCLiteralExpressionSyntax fourCCLiteralExpression: - value = fourCCLiteralExpression.Value; - return true; - - case JassUnaryExpressionSyntax unaryExpression: - return int.TryParse(unaryExpression.ToString(), out value); - - case JassHexadecimalLiteralExpressionSyntax hexLiteralExpression: - value = hexLiteralExpression.Value; - return true; - - default: - value = default; - return false; - } - } - - public static bool TryGetPlayerIdExpressionValue(this IExpressionSyntax expression, int maxPlayerSlots, out int value) - { - if (expression is JassVariableReferenceExpressionSyntax variableReferenceExpression) - { - if (string.Equals(variableReferenceExpression.IdentifierName.Name, "PLAYER_NEUTRAL_AGGRESSIVE", StringComparison.Ordinal)) - { - value = maxPlayerSlots; - return true; - } - else if (string.Equals(variableReferenceExpression.IdentifierName.Name, "PLAYER_NEUTRAL_PASSIVE", StringComparison.Ordinal)) - { - value = maxPlayerSlots + 3; - return true; - } - else - { - value = default; - return false; - } - } - else - { - return expression.TryGetIntegerExpressionValue(out value); - } - } - - public static bool TryGetRealExpressionValue(this IExpressionSyntax expression, out float value) - { - if (expression.TryGetIntegerExpressionValue(out var intValue)) - { - value = intValue; - return true; - } - - if (expression is JassRealLiteralExpressionSyntax realLiteralExpression && - float.TryParse(realLiteralExpression.ToString(), NumberStyles.AllowDecimalPoint, CultureInfo.InvariantCulture, out value)) - { - return true; - } - - if (expression is JassUnaryExpressionSyntax unaryExpression && - float.TryParse(unaryExpression.ToString(), NumberStyles.AllowLeadingSign | NumberStyles.AllowDecimalPoint, CultureInfo.InvariantCulture, out value)) - { - return true; - } - - value = default; - return false; - } - } -} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Extensions/ImmutableArrayExtensions.cs b/src/War3Net.CodeAnalysis.Jass/Extensions/ImmutableArrayExtensions.cs new file mode 100644 index 00000000..34476122 --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/Extensions/ImmutableArrayExtensions.cs @@ -0,0 +1,80 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using System.Collections.Generic; +using System.Collections.Immutable; +using System.IO; +using System.Linq; +using System.Text; + +using War3Net.CodeAnalysis.Jass.Syntax; + +namespace War3Net.CodeAnalysis.Jass.Extensions +{ + public static class ImmutableArrayExtensions + { + public static bool IsEquivalentTo(this ImmutableArray array, ImmutableArray other) + where TSyntaxNode : JassSyntaxNode + { + if (array.Length != other.Length) + { + return false; + } + + for (var i = 0; i < array.Length; i++) + { + if (!array[i].IsEquivalentTo(other[i])) + { + return false; + } + } + + return true; + } + + public static void WriteTo(this ImmutableArray array, TextWriter writer) + where TSyntaxNode : JassSyntaxNode + { + for (var i = 0; i < array.Length; i++) + { + array[i].WriteTo(writer); + } + } + + public static IEnumerable GetDescendantNodes(this ImmutableArray array) + where TSyntaxNode : JassSyntaxNode + { + return array.SelectMany(item => item.GetDescendantNodes()); + } + + public static IEnumerable GetDescendantTokens(this ImmutableArray array) + where TSyntaxNode : JassSyntaxNode + { + return array.SelectMany(item => item.GetDescendantTokens()); + } + + public static IEnumerable GetDescendantNodesAndTokens(this ImmutableArray array) + where TSyntaxNode : JassSyntaxNode + { + return array.SelectMany(item => item.GetDescendantNodesAndTokens()); + } + + internal static ImmutableArray ReplaceFirstItem(this ImmutableArray array, T newItem) + { + var builder = array.ToBuilder(); + builder[0] = newItem; + return builder.ToImmutable(); + } + + internal static ImmutableArray ReplaceLastItem(this ImmutableArray array, T newItem) + { + var builder = array.ToBuilder(); + builder[^1] = newItem; + return builder.ToImmutable(); + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Extensions/IndentedTextWriterExtensions.cs b/src/War3Net.CodeAnalysis.Jass/Extensions/IndentedTextWriterExtensions.cs new file mode 100644 index 00000000..c9fec4f1 --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/Extensions/IndentedTextWriterExtensions.cs @@ -0,0 +1,296 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +namespace War3Net.CodeAnalysis.Jass.Extensions +{ + public static class IndentedTextWriterExtensions + { + public static void WriteComment(this IndentedTextWriter writer, string comment) + { + writer.Write("// "); + writer.WriteLine(comment); + } + + public static void WriteFunction(this IndentedTextWriter writer, string functionName) + { + writer.Write(JassKeyword.Function); + writer.Write(' '); + writer.Write(functionName); + writer.Write(' '); + writer.Write(JassKeyword.Takes); + writer.Write(' '); + writer.Write(JassKeyword.Nothing); + writer.Write(' '); + writer.Write(JassKeyword.Returns); + writer.Write(' '); + writer.Write(JassKeyword.Nothing); + writer.WriteLine(); + writer.Indent(); + } + + public static void WriteFilterFunction(this IndentedTextWriter writer, string functionName) + { + writer.Write(JassKeyword.Function); + writer.Write(' '); + writer.Write(functionName); + writer.Write(' '); + writer.Write(JassKeyword.Takes); + writer.Write(' '); + writer.Write(JassKeyword.Nothing); + writer.Write(' '); + writer.Write(JassKeyword.Returns); + writer.Write(' '); + writer.Write(JassKeyword.Boolean); + writer.WriteLine(); + writer.Indent(); + } + + public static void EndFunction(this IndentedTextWriter writer) + { + writer.Unindent(); + writer.WriteLine(JassKeyword.EndFunction); + } + + public static void WriteLocal(this IndentedTextWriter writer, string typeName, string variableName) + { + writer.Write(JassKeyword.Local); + writer.Write(' '); + writer.Write(typeName); + writer.Write(' '); + writer.WriteLine(variableName); + } + + public static void WriteLocal(this IndentedTextWriter writer, string typeName, string variableName, string initialValue) + { + writer.Write(JassKeyword.Local); + writer.Write(' '); + writer.Write(typeName); + writer.Write(' '); + writer.Write(variableName); + writer.Write(" = "); + writer.WriteLine(initialValue); + } + + public static void WriteAlignedLocal( + this IndentedTextWriter writer, + string typeName, + string variableName, + string initialValue, + int typeColumnWidth = 8, + int variableNameColumnWidth = 11) + { + writer.Write(JassKeyword.Local); + writer.Write(' '); + writer.Write(typeName); + var typePadding = typeColumnWidth - typeName.Length; + if (typePadding > 0) + { + writer.Write(new string(' ', typePadding)); + } + else + { + writer.Write(' '); + } + + writer.Write(variableName); + + var namePadding = variableNameColumnWidth - variableName.Length; + if (namePadding > 0) + { + writer.Write(new string(' ', namePadding)); + } + else + { + writer.Write(' '); + } + + writer.Write("= "); + writer.WriteLine(initialValue); + } + + public static void WriteAlignedGlobal( + this IndentedTextWriter writer, + string typeName, + string variableName, + int typeColumnWidth = 24) + { + writer.Write(typeName); + var typePadding = typeColumnWidth - typeName.Length; + if (typePadding > 0) + { + writer.Write(new string(' ', typePadding)); + } + else + { + writer.Write(' '); + } + + writer.WriteLine(variableName); + } + + public static void WriteAlignedGlobal( + this IndentedTextWriter writer, + string typeName, + string variableName, + string value, + int typeColumnWidth = 24, + int variableNameColumnWidth = 27) + { + writer.Write(typeName); + var typePadding = typeColumnWidth - typeName.Length; + if (typePadding > 0) + { + writer.Write(new string(' ', typePadding)); + } + else + { + writer.Write(' '); + } + + writer.Write(variableName); + + var namePadding = variableNameColumnWidth - variableName.Length; + if (namePadding > 0) + { + writer.Write(new string(' ', namePadding)); + } + else + { + writer.Write(' '); + } + + writer.Write("= "); + writer.WriteLine(value); + } + + public static void WriteCall(this IndentedTextWriter writer, string functionName) + { + writer.Write(JassKeyword.Call); + writer.Write(' '); + writer.Write(functionName); + writer.Write("( )"); + writer.WriteLine(); + } + + public static void WriteCall(this IndentedTextWriter writer, string functionName, params string[] arguments) + { + writer.Write(JassKeyword.Call); + writer.Write(' '); + writer.Write(functionName); + writer.Write("( "); + for (var i = 0; i < arguments.Length; i++) + { + if (i > 0) + { + writer.Write(", "); + } + + writer.Write(arguments[i]); + } + + writer.Write(" )"); + writer.WriteLine(); + } + + public static void WriteCallCompact(this IndentedTextWriter writer, string functionName, params string[] arguments) + { + writer.Write(JassKeyword.Call); + writer.Write(' '); + writer.Write(functionName); + writer.Write('('); + for (var i = 0; i < arguments.Length; i++) + { + if (i > 0) + { + writer.Write(", "); + } + + writer.Write(arguments[i]); + } + + writer.Write(')'); + writer.WriteLine(); + } + + public static void WriteSet(this IndentedTextWriter writer, string variableName, string valueExpression) + { + writer.Write(JassKeyword.Set); + writer.Write(' '); + writer.Write(variableName); + writer.Write(" = "); + writer.WriteLine(valueExpression); + } + + public static void WriteIf(this IndentedTextWriter writer, string condition) + { + writer.Write(JassKeyword.If); + writer.Write(' '); + writer.Write(condition); + writer.Write(' '); + writer.Write(JassKeyword.Then); + writer.WriteLine(); + writer.Indent(); + } + + public static void WriteElseIf(this IndentedTextWriter writer, string condition) + { + writer.Unindent(); + writer.Write(JassKeyword.ElseIf); + writer.Write(' '); + writer.Write(condition); + writer.Write(' '); + writer.Write(JassKeyword.Then); + writer.WriteLine(); + writer.Indent(); + } + + public static void WriteElse(this IndentedTextWriter writer) + { + writer.Unindent(); + writer.WriteLine(JassKeyword.Else); + writer.Indent(); + } + + public static void WriteEndIf(this IndentedTextWriter writer) + { + writer.Unindent(); + writer.Write(JassKeyword.EndIf); + writer.WriteLine(); + } + + public static void WriteLoop(this IndentedTextWriter writer) + { + writer.WriteLine(JassKeyword.Loop); + writer.Indent(); + } + + public static void WriteExitWhen(this IndentedTextWriter writer, string condition) + { + writer.Write(JassKeyword.ExitWhen); + writer.Write(' '); + writer.WriteLine(condition); + } + + public static void WriteEndLoop(this IndentedTextWriter writer) + { + writer.Unindent(); + writer.WriteLine(JassKeyword.EndLoop); + } + + public static void WriteReturn(this IndentedTextWriter writer) + { + writer.WriteLine(JassKeyword.Return); + } + + public static void WriteReturn(this IndentedTextWriter writer, string expression) + { + writer.Write(JassKeyword.Return); + writer.Write(' '); + writer.WriteLine(expression); + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Renderer/VariableReferenceExpressionRenderer.cs b/src/War3Net.CodeAnalysis.Jass/Extensions/JassCompilationUnitSyntaxExtensions.cs similarity index 50% rename from src/War3Net.CodeAnalysis.Jass/Renderer/VariableReferenceExpressionRenderer.cs rename to src/War3Net.CodeAnalysis.Jass/Extensions/JassCompilationUnitSyntaxExtensions.cs index 8f048ec5..dc64fc7e 100644 --- a/src/War3Net.CodeAnalysis.Jass/Renderer/VariableReferenceExpressionRenderer.cs +++ b/src/War3Net.CodeAnalysis.Jass/Extensions/JassCompilationUnitSyntaxExtensions.cs @@ -1,5 +1,5 @@ // ------------------------------------------------------------------------------ -// +// // Licensed under the MIT license. // See the LICENSE file in the project root for more information. // @@ -7,13 +7,13 @@ using War3Net.CodeAnalysis.Jass.Syntax; -namespace War3Net.CodeAnalysis.Jass +namespace War3Net.CodeAnalysis.Jass.Extensions { - public partial class JassRenderer + public static class JassCompilationUnitSyntaxExtensions { - public void Render(JassVariableReferenceExpressionSyntax variableReferenceExpressionSyntax) + public static JassCompilationUnitSyntax NormalizeWhitespace(this JassCompilationUnitSyntax compilationUnit) { - Render(variableReferenceExpressionSyntax.IdentifierName); + return new JassSyntaxNormalizer().NormalizeWhitespace(compilationUnit); } } } \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Extensions/JassExpressionSyntaxExtensions.cs b/src/War3Net.CodeAnalysis.Jass/Extensions/JassExpressionSyntaxExtensions.cs new file mode 100644 index 00000000..e1627f99 --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/Extensions/JassExpressionSyntaxExtensions.cs @@ -0,0 +1,209 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using System; +using System.Diagnostics.CodeAnalysis; +using System.Globalization; + +using War3Net.CodeAnalysis.Jass.Syntax; + +namespace War3Net.CodeAnalysis.Jass.Extensions +{ + public static class JassExpressionSyntaxExtensions + { + public static JassExpressionSyntax Deparenthesize(this JassExpressionSyntax expression) + { + while (expression is JassParenthesizedExpressionSyntax parenthesizedExpression) + { + expression = parenthesizedExpression.Expression; + } + + return expression; + } + + public static bool TryGetBooleanExpressionValue(this JassExpressionSyntax expression, out bool value) + { + if (expression is JassLiteralExpressionSyntax literalExpression) + { + switch (literalExpression.Token.SyntaxKind) + { + case JassSyntaxKind.TrueKeyword: + value = true; + return true; + + case JassSyntaxKind.FalseKeyword: + value = false; + return true; + } + } + + value = default; + return false; + } + + public static bool TryGetCharacterExpressionValue(this JassExpressionSyntax expression, out char value) + { + if (expression is JassLiteralExpressionSyntax literalExpression && + literalExpression.Token.SyntaxKind == JassSyntaxKind.CharacterLiteralToken) + { + value = JassLiteral.ParseChar(literalExpression.Token.Text); + return true; + } + + value = default; + return false; + } + + public static bool TryGetIdentifierNameValue(this JassExpressionSyntax expression, [NotNullWhen(true)] out string? value) + { + if (expression is JassIdentifierNameSyntax identifierName) + { + value = identifierName.Token.Text; + return true; + } + + value = null; + return false; + } + + public static bool TryGetIntegerExpressionValue(this JassExpressionSyntax expression, out int value) + { + if (expression is JassLiteralExpressionSyntax literalExpression) + { + switch (literalExpression.Token.SyntaxKind) + { + case JassSyntaxKind.DecimalLiteralToken: + value = JassLiteral.ParseInt(literalExpression.Token.Text); + return true; + + case JassSyntaxKind.HexadecimalLiteralToken: + value = JassLiteral.ParseHex(literalExpression.Token.Text); + return true; + + case JassSyntaxKind.OctalLiteralToken: + value = JassLiteral.ParseOctal(literalExpression.Token.Text); + return true; + + case JassSyntaxKind.FourCCLiteralToken: + value = JassLiteral.ParseFourCC(literalExpression.Token.Text); + return true; + + default: + value = default; + return false; + } + } + else if (expression is JassUnaryExpressionSyntax unaryExpression) + { + switch (unaryExpression.SyntaxKind) + { + case JassSyntaxKind.UnaryPlusExpression: + return unaryExpression.Expression.TryGetIntegerExpressionValue(out value); + + case JassSyntaxKind.UnaryMinusExpression: + if (unaryExpression.Expression.TryGetIntegerExpressionValue(out var result)) + { + value = -result; + return true; + } + + break; + + default: + value = default; + return false; + } + } + + value = default; + return false; + } + + public static bool TryGetPlayerIdExpressionValue(this JassExpressionSyntax expression, int maxPlayerSlots, out int value) + { + if (expression is JassIdentifierNameSyntax identifierName) + { + if (string.Equals(identifierName.Token.Text, "PLAYER_NEUTRAL_AGGRESSIVE", StringComparison.Ordinal)) + { + value = maxPlayerSlots; + return true; + } + else if (string.Equals(identifierName.Token.Text, "PLAYER_NEUTRAL_PASSIVE", StringComparison.Ordinal)) + { + value = maxPlayerSlots + 3; + return true; + } + else + { + value = default; + return false; + } + } + else + { + return expression.TryGetIntegerExpressionValue(out value); + } + } + + public static bool TryGetRealExpressionValue(this JassExpressionSyntax expression, out float value) + { + if (expression.TryGetIntegerExpressionValue(out var intValue)) + { + value = intValue; + return true; + } + + if (expression is JassLiteralExpressionSyntax literalExpression && + float.TryParse(literalExpression.Token.Text, NumberStyles.AllowDecimalPoint, CultureInfo.InvariantCulture, out value)) + { + return true; + } + + if (expression is JassUnaryExpressionSyntax unaryExpression && + float.TryParse(unaryExpression.ToString(), NumberStyles.AllowLeadingSign | NumberStyles.AllowDecimalPoint, CultureInfo.InvariantCulture, out value)) + { + return true; + } + + value = default; + return false; + } + + public static bool TryGetStringExpressionValue(this JassExpressionSyntax expression, out string? value) + { + if (expression is JassLiteralExpressionSyntax literalExpression) + { + switch (literalExpression.Token.SyntaxKind) + { + case JassSyntaxKind.StringLiteralToken: + value = JassLiteral.ParseString(literalExpression.Token.Text); + return true; + + case JassSyntaxKind.NullKeyword: + value = null; + return true; + } + } + + value = null; + return false; + } + + public static bool TryGetNotNullStringExpressionValue(this JassExpressionSyntax expression, [NotNullWhen(true)] out string? value) + { + if (expression is JassLiteralExpressionSyntax literalExpression && + literalExpression.Token.SyntaxKind == JassSyntaxKind.StringLiteralToken) + { + value = JassLiteral.ParseString(literalExpression.Token.Text); + return true; + } + + value = null; + return false; + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Extensions/JassParameterListOrEmptyParameterListSyntaxExtensions.cs b/src/War3Net.CodeAnalysis.Jass/Extensions/JassParameterListOrEmptyParameterListSyntaxExtensions.cs new file mode 100644 index 00000000..e5ceb4d8 --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/Extensions/JassParameterListOrEmptyParameterListSyntaxExtensions.cs @@ -0,0 +1,49 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using System; +using System.Collections.Immutable; + +using War3Net.CodeAnalysis.Jass.Syntax; + +namespace War3Net.CodeAnalysis.Jass.Extensions +{ + public static class JassParameterListOrEmptyParameterListSyntaxExtensions + { + public static JassSyntaxToken GetTakesToken(this JassParameterListOrEmptyParameterListSyntax parameterListOrEmptyParameterList) + { + if (parameterListOrEmptyParameterList is JassParameterListSyntax parameterList) + { + return parameterList.TakesToken; + } + else if (parameterListOrEmptyParameterList is JassEmptyParameterListSyntax emptyParameterList) + { + return emptyParameterList.TakesToken; + } + else + { + throw new ArgumentException("Unknown parameter list type.", nameof(parameterListOrEmptyParameterList)); + } + } + + public static ImmutableArray GetParameters(this JassParameterListOrEmptyParameterListSyntax parameterListOrEmptyParameterList) + { + if (parameterListOrEmptyParameterList is JassParameterListSyntax parameterList) + { + return parameterList.ParameterList.Items; + } + else if (parameterListOrEmptyParameterList is JassEmptyParameterListSyntax) + { + return ImmutableArray.Empty; + } + else + { + throw new ArgumentException("Unknown parameter list type.", nameof(parameterListOrEmptyParameterList)); + } + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Extensions/EquatableExtensions.cs b/src/War3Net.CodeAnalysis.Jass/Extensions/JassSyntaxNodeExtensions.cs similarity index 51% rename from src/War3Net.CodeAnalysis.Jass/Extensions/EquatableExtensions.cs rename to src/War3Net.CodeAnalysis.Jass/Extensions/JassSyntaxNodeExtensions.cs index 8fa626bb..cd3b1edb 100644 --- a/src/War3Net.CodeAnalysis.Jass/Extensions/EquatableExtensions.cs +++ b/src/War3Net.CodeAnalysis.Jass/Extensions/JassSyntaxNodeExtensions.cs @@ -1,19 +1,19 @@ // ------------------------------------------------------------------------------ -// +// // Licensed under the MIT license. // See the LICENSE file in the project root for more information. // // ------------------------------------------------------------------------------ -using System; +using War3Net.CodeAnalysis.Jass.Syntax; namespace War3Net.CodeAnalysis.Jass.Extensions { - public static class EquatableExtensions + public static class JassSyntaxNodeExtensions { - public static bool NullableEquals(this IEquatable? objA, T? objB) + public static bool NullableEquivalentTo(this JassSyntaxNode? objA, JassSyntaxNode? objB) { - return ReferenceEquals(objA, objB) || objA?.Equals(objB) == true; + return ReferenceEquals(objA, objB) || objA?.IsEquivalentTo(objB) == true; } } } \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Extensions/JassSyntaxTokenExtensions.cs b/src/War3Net.CodeAnalysis.Jass/Extensions/JassSyntaxTokenExtensions.cs new file mode 100644 index 00000000..77be4596 --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/Extensions/JassSyntaxTokenExtensions.cs @@ -0,0 +1,132 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using System.Collections.Generic; +using System.Collections.Immutable; + +using War3Net.CodeAnalysis.Jass.Syntax; + +namespace War3Net.CodeAnalysis.Jass.Extensions +{ + public static class JassSyntaxTokenExtensions + { + public static JassSyntaxToken WithLeadingTrivia(this JassSyntaxToken token, JassSyntaxTriviaList triviaList) + { + return new JassSyntaxToken(triviaList, token.SyntaxKind, token.Text, token.TrailingTrivia); + } + + public static JassSyntaxToken WithTrailingTrivia(this JassSyntaxToken token, JassSyntaxTriviaList triviaList) + { + return new JassSyntaxToken(token.LeadingTrivia, token.SyntaxKind, token.Text, triviaList); + } + + public static JassSyntaxToken AppendLeadingTrivia(this JassSyntaxToken token, JassSyntaxTrivia trivia) + { + return token.WithLeadingTrivia(JassSyntaxFactory.AppendTriviaToList(token.LeadingTrivia.Trivia, trivia)); + } + + public static JassSyntaxToken AppendLeadingTrivia(this JassSyntaxToken token, JassSyntaxTriviaList triviaList) + { + return token.WithLeadingTrivia(JassSyntaxFactory.ConcatTriviaLists(token.LeadingTrivia.Trivia, triviaList.Trivia)); + } + + public static JassSyntaxToken AppendLeadingTrivia(this JassSyntaxToken token, params JassSyntaxTrivia[] triviaList) + { + return token.WithLeadingTrivia(JassSyntaxFactory.ConcatTriviaLists(token.LeadingTrivia.Trivia, triviaList)); + } + + public static JassSyntaxToken AppendLeadingTrivia(this JassSyntaxToken token, IEnumerable triviaList) + { + return token.WithLeadingTrivia(JassSyntaxFactory.ConcatTriviaLists(token.LeadingTrivia.Trivia, triviaList)); + } + + public static JassSyntaxToken AppendLeadingTrivia(this JassSyntaxToken token, ImmutableArray triviaList) + { + return token.WithLeadingTrivia(JassSyntaxFactory.ConcatTriviaLists(token.LeadingTrivia.Trivia, triviaList)); + } + + public static JassSyntaxToken PrependLeadingTrivia(this JassSyntaxToken token, JassSyntaxTrivia trivia) + { + return token.WithLeadingTrivia(JassSyntaxFactory.PrependTriviaToList(trivia, token.LeadingTrivia.Trivia)); + } + + public static JassSyntaxToken PrependLeadingTrivia(this JassSyntaxToken token, JassSyntaxTriviaList triviaList) + { + return token.WithLeadingTrivia(JassSyntaxFactory.ConcatTriviaLists(triviaList.Trivia, token.LeadingTrivia.Trivia)); + } + + public static JassSyntaxToken PrependLeadingTrivia(this JassSyntaxToken token, params JassSyntaxTrivia[] triviaList) + { + return token.WithLeadingTrivia(JassSyntaxFactory.ConcatTriviaLists(triviaList, token.LeadingTrivia.Trivia)); + } + + public static JassSyntaxToken PrependLeadingTrivia(this JassSyntaxToken token, IEnumerable triviaList) + { + return token.WithLeadingTrivia(JassSyntaxFactory.ConcatTriviaLists(triviaList, token.LeadingTrivia.Trivia)); + } + + public static JassSyntaxToken PrependLeadingTrivia(this JassSyntaxToken token, ImmutableArray triviaList) + { + return token.WithLeadingTrivia(JassSyntaxFactory.ConcatTriviaLists(triviaList, token.LeadingTrivia.Trivia)); + } + + public static JassSyntaxToken AppendTrailingTrivia(this JassSyntaxToken token, JassSyntaxTrivia trivia) + { + return token.WithTrailingTrivia(JassSyntaxFactory.AppendTriviaToList(token.TrailingTrivia.Trivia, trivia)); + } + + public static JassSyntaxToken AppendTrailingTrivia(this JassSyntaxToken token, JassSyntaxTriviaList triviaList) + { + return token.WithTrailingTrivia(JassSyntaxFactory.ConcatTriviaLists(token.TrailingTrivia.Trivia, triviaList.Trivia)); + } + + public static JassSyntaxToken AppendTrailingTrivia(this JassSyntaxToken token, params JassSyntaxTrivia[] triviaList) + { + return token.WithTrailingTrivia(JassSyntaxFactory.ConcatTriviaLists(token.TrailingTrivia.Trivia, triviaList)); + } + + public static JassSyntaxToken AppendTrailingTrivia(this JassSyntaxToken token, IEnumerable triviaList) + { + return token.WithTrailingTrivia(JassSyntaxFactory.ConcatTriviaLists(token.TrailingTrivia.Trivia, triviaList)); + } + + public static JassSyntaxToken AppendTrailingTrivia(this JassSyntaxToken token, ImmutableArray triviaList) + { + return token.WithTrailingTrivia(JassSyntaxFactory.ConcatTriviaLists(token.TrailingTrivia.Trivia, triviaList)); + } + + public static JassSyntaxToken PrependTrailingTrivia(this JassSyntaxToken token, JassSyntaxTrivia trivia) + { + return token.WithTrailingTrivia(JassSyntaxFactory.PrependTriviaToList(trivia, token.TrailingTrivia.Trivia)); + } + + public static JassSyntaxToken PrependTrailingTrivia(this JassSyntaxToken token, JassSyntaxTriviaList triviaList) + { + return token.WithTrailingTrivia(JassSyntaxFactory.ConcatTriviaLists(triviaList.Trivia, token.TrailingTrivia.Trivia)); + } + + public static JassSyntaxToken PrependTrailingTrivia(this JassSyntaxToken token, params JassSyntaxTrivia[] triviaList) + { + return token.WithTrailingTrivia(JassSyntaxFactory.ConcatTriviaLists(triviaList, token.TrailingTrivia.Trivia)); + } + + public static JassSyntaxToken PrependTrailingTrivia(this JassSyntaxToken token, IEnumerable triviaList) + { + return token.WithTrailingTrivia(JassSyntaxFactory.ConcatTriviaLists(triviaList, token.TrailingTrivia.Trivia)); + } + + public static JassSyntaxToken PrependTrailingTrivia(this JassSyntaxToken token, ImmutableArray triviaList) + { + return token.WithTrailingTrivia(JassSyntaxFactory.ConcatTriviaLists(triviaList, token.TrailingTrivia.Trivia)); + } + + internal static bool NullableEquals(this JassSyntaxToken? objA, JassSyntaxToken? objB) + { + return (objA is null) == (objB is null); + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Extensions/JassSyntaxTriviaListExtensions.cs b/src/War3Net.CodeAnalysis.Jass/Extensions/JassSyntaxTriviaListExtensions.cs new file mode 100644 index 00000000..bdb1b469 --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/Extensions/JassSyntaxTriviaListExtensions.cs @@ -0,0 +1,30 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using War3Net.CodeAnalysis.Jass.Syntax; + +namespace War3Net.CodeAnalysis.Jass.Extensions +{ + public static class JassSyntaxTriviaListExtensions + { + public static string GetIndentationString(this JassSyntaxTriviaList triviaList) + { + if (triviaList.Trivia.IsEmpty) + { + return string.Empty; + } + + var lastTrivia = triviaList.Trivia[^1]; + if (lastTrivia.SyntaxKind != JassSyntaxKind.WhitespaceTrivia) + { + return string.Empty; + } + + return lastTrivia.Text; + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Extensions/JassTypeSyntaxExtensions.cs b/src/War3Net.CodeAnalysis.Jass/Extensions/JassTypeSyntaxExtensions.cs new file mode 100644 index 00000000..394bd979 --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/Extensions/JassTypeSyntaxExtensions.cs @@ -0,0 +1,32 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using System; + +using War3Net.CodeAnalysis.Jass.Syntax; + +namespace War3Net.CodeAnalysis.Jass.Extensions +{ + public static class JassTypeSyntaxExtensions + { + public static JassSyntaxToken GetToken(this JassTypeSyntax type) + { + if (type is JassIdentifierNameSyntax identifierName) + { + return identifierName.Token; + } + else if (type is JassPredefinedTypeSyntax predefinedType) + { + return predefinedType.Token; + } + else + { + throw new ArgumentException("Unknown type.", nameof(type)); + } + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Extensions/JassVariableOrArrayDeclaratorSyntaxExtensions.cs b/src/War3Net.CodeAnalysis.Jass/Extensions/JassVariableOrArrayDeclaratorSyntaxExtensions.cs new file mode 100644 index 00000000..d217bb0e --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/Extensions/JassVariableOrArrayDeclaratorSyntaxExtensions.cs @@ -0,0 +1,48 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using System; + +using War3Net.CodeAnalysis.Jass.Syntax; + +namespace War3Net.CodeAnalysis.Jass.Extensions +{ + public static class JassVariableOrArrayDeclaratorSyntaxExtensions + { + public static JassIdentifierNameSyntax GetIdentifierName(this JassVariableOrArrayDeclaratorSyntax declarator) + { + if (declarator is JassVariableDeclaratorSyntax variableDeclarator) + { + return variableDeclarator.IdentifierName; + } + else if (declarator is JassArrayDeclaratorSyntax arrayDeclarator) + { + return arrayDeclarator.IdentifierName; + } + else + { + throw new ArgumentException("Unknown declarator type.", nameof(declarator)); + } + } + + public static JassTypeSyntax GetVariableType(this JassVariableOrArrayDeclaratorSyntax declarator) + { + if (declarator is JassVariableDeclaratorSyntax variableDeclarator) + { + return variableDeclarator.Type; + } + else if (declarator is JassArrayDeclaratorSyntax arrayDeclarator) + { + return arrayDeclarator.Type; + } + else + { + throw new ArgumentException("Unknown declarator type.", nameof(declarator)); + } + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Extensions/ObjectExtensions.cs b/src/War3Net.CodeAnalysis.Jass/Extensions/ObjectExtensions.cs new file mode 100644 index 00000000..fd9db04a --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/Extensions/ObjectExtensions.cs @@ -0,0 +1,37 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +namespace War3Net.CodeAnalysis.Jass.Extensions +{ + internal static class ObjectExtensions + { + internal static string Optional(this object? obj) + { + return obj?.ToString() ?? string.Empty; + } + + internal static string OptionalPrefixed(this object? obj, string prefix = " ") + { + if (obj is null) + { + return string.Empty; + } + + return $"{prefix}{obj}"; + } + + internal static string OptionalSuffixed(this object? obj, string suffix = " ") + { + if (obj is null) + { + return string.Empty; + } + + return $"{obj}{suffix}"; + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Extensions/ParserExtensions.cs b/src/War3Net.CodeAnalysis.Jass/Extensions/ParserExtensions.cs index 3ef55e85..c475619b 100644 --- a/src/War3Net.CodeAnalysis.Jass/Extensions/ParserExtensions.cs +++ b/src/War3Net.CodeAnalysis.Jass/Extensions/ParserExtensions.cs @@ -11,20 +11,56 @@ using War3Net.CodeAnalysis.Jass.Syntax; +using static Pidgin.Parser; + namespace War3Net.CodeAnalysis.Jass.Extensions { internal static class ParserExtensions { - internal static Parser> Prefix( - this Parser parser) + internal static Parser> Prefix( + this Parser operatorTokenParser) + { + return operatorTokenParser.Select>(operatorToken => expression => new JassUnaryExpressionSyntax( + operatorToken, + expression)); + } + + internal static Parser> Infix( + this Parser operatorTokenParser) + { + return operatorTokenParser.Select>(operatorToken => (left, right) => new JassBinaryExpressionSyntax( + left, + operatorToken, + right)); + } + + internal static Parser AsToken( + this Parser tokenSymbolParser, + Parser trailingTriviaParser, + JassSyntaxKind syntaxKind, + string symbol) { - return parser.Select>(@operator => expression => new JassUnaryExpressionSyntax(@operator, expression)); + return Map( + (_, trailingTrivia) => new JassSyntaxToken( + syntaxKind, + symbol, + trailingTrivia), + tokenSymbolParser, + trailingTriviaParser); } - internal static Parser> Infix( - this Parser parser) + internal static Parser AsToken( + this Parser tokenTextParser, + Parser trailingTriviaParser, + JassSyntaxKind syntaxKind) { - return parser.Select>(@operator => (left, right) => new JassBinaryExpressionSyntax(@operator, left, right)); + return Map( + (text, trailingTrivia) => new JassSyntaxToken( + syntaxKind, + text, + trailingTrivia), + tokenTextParser, + trailingTriviaParser); } } } \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Extensions/SeparatedSyntaxListExtensions.cs b/src/War3Net.CodeAnalysis.Jass/Extensions/SeparatedSyntaxListExtensions.cs new file mode 100644 index 00000000..98178263 --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/Extensions/SeparatedSyntaxListExtensions.cs @@ -0,0 +1,142 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using System.Collections.Generic; +using System.IO; +using System.Linq; + +using War3Net.CodeAnalysis.Jass.Syntax; + +namespace War3Net.CodeAnalysis.Jass.Extensions +{ + public static class SeparatedSyntaxListExtensions + { + public static bool IsEquivalentTo(this SeparatedSyntaxList list, SeparatedSyntaxList other) + where TSyntaxNode : JassSyntaxNode + { + return list.Items.IsEquivalentTo(other.Items); + } + + public static void WriteTo(this SeparatedSyntaxList list, TextWriter writer) + where TSyntaxNode : JassSyntaxNode + { + if (list.Items.IsEmpty) + { + return; + } + + list.Items[0].WriteTo(writer); + for (var i = 1; i < list.Items.Length; i++) + { + list.Separators[i - 1].WriteTo(writer); + list.Items[i].WriteTo(writer); + } + } + + public static IEnumerable GetChildNodesAndTokens(this SeparatedSyntaxList list) + where TSyntaxNode : JassSyntaxNode + { + if (list.Items.IsEmpty) + { + yield break; + } + + yield return list.Items[0]; + for (var i = 1; i < list.Items.Length; i++) + { + yield return list.Separators[i - 1]; + yield return list.Items[i]; + } + } + + public static IEnumerable GetDescendantNodes(this SeparatedSyntaxList list) + where TSyntaxNode : JassSyntaxNode + { + return list.Items.SelectMany(syntaxNode => syntaxNode.GetDescendantNodes()); + } + + public static IEnumerable GetDescendantTokens(this SeparatedSyntaxList list) + where TSyntaxNode : JassSyntaxNode + { + if (list.Items.IsEmpty) + { + yield break; + } + + foreach (var descendant in list.Items[0].GetDescendantTokens()) + { + yield return descendant; + } + + for (var i = 1; i < list.Items.Length; i++) + { + yield return list.Separators[i - 1]; + foreach (var descendant in list.Items[i].GetDescendantTokens()) + { + yield return descendant; + } + } + } + + public static IEnumerable GetDescendantNodesAndTokens(this SeparatedSyntaxList list) + where TSyntaxNode : JassSyntaxNode + { + if (list.Items.IsEmpty) + { + yield break; + } + + foreach (var descendant in list.Items[0].GetDescendantNodesAndTokens()) + { + yield return descendant; + } + + for (var i = 1; i < list.Items.Length; i++) + { + yield return list.Separators[i - 1]; + foreach (var descendant in list.Items[i].GetDescendantNodesAndTokens()) + { + yield return descendant; + } + } + } + + public static JassSyntaxTriviaList GetLeadingTrivia(this SeparatedSyntaxList list) + where TItem : JassSyntaxNode + { + if (list.IsEmpty) + { + return JassSyntaxTriviaList.Empty; + } + + return list.Items[0].GetLeadingTrivia(); + } + + public static JassSyntaxTriviaList GetTrailingTrivia(this SeparatedSyntaxList list) + where TItem : JassSyntaxNode + { + if (list.IsEmpty) + { + return JassSyntaxTriviaList.Empty; + } + + return list.Items[^1].GetTrailingTrivia(); + } + + internal static SeparatedSyntaxList ReplaceFirstItem(this SeparatedSyntaxList list, TItem newItem) + { + var items = list.Items.ReplaceFirstItem(newItem); + return SeparatedSyntaxList.Create(items, list.Separators); + } + + internal static SeparatedSyntaxList ReplaceLastItem(this SeparatedSyntaxList list, TItem newItem) + { + var items = list.Items.ReplaceLastItem(newItem); + return SeparatedSyntaxList.Create(items, list.Separators); + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Extensions/SyntaxNodeExtensions.cs b/src/War3Net.CodeAnalysis.Jass/Extensions/SyntaxNodeExtensions.cs new file mode 100644 index 00000000..537446d2 --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/Extensions/SyntaxNodeExtensions.cs @@ -0,0 +1,203 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using System.Collections.Generic; +using System.Collections.Immutable; + +using War3Net.CodeAnalysis.Jass.Syntax; + +namespace War3Net.CodeAnalysis.Jass.Extensions +{ + public static class SyntaxNodeExtensions + { + public static JassSyntaxTriviaList GetLeadingTrivia(this JassSyntaxNode syntaxNode) + { + return syntaxNode.GetFirstToken().LeadingTrivia; + } + + public static JassSyntaxTriviaList GetTrailingTrivia(this JassSyntaxNode syntaxNode) + { + return syntaxNode.GetLastToken().TrailingTrivia; + } + + public static TSyntaxNode WithLeadingTrivia(this TSyntaxNode syntaxNode, JassSyntaxTriviaList triviaList) + where TSyntaxNode : JassSyntaxNode + { + var oldToken = syntaxNode.GetFirstToken(); + var newToken = oldToken.WithLeadingTrivia(triviaList); + return (TSyntaxNode)syntaxNode.ReplaceFirstToken(newToken); + } + + public static TSyntaxNode WithTrailingTrivia(this TSyntaxNode syntaxNode, JassSyntaxTriviaList triviaList) + where TSyntaxNode : JassSyntaxNode + { + var oldToken = syntaxNode.GetLastToken(); + var newToken = oldToken.WithTrailingTrivia(triviaList); + return (TSyntaxNode)syntaxNode.ReplaceLastToken(newToken); + } + + public static TSyntaxNode AppendLeadingTrivia(this TSyntaxNode syntaxNode, JassSyntaxTrivia trivia) + where TSyntaxNode : JassSyntaxNode + { + var oldToken = syntaxNode.GetFirstToken(); + var newToken = oldToken.AppendLeadingTrivia(trivia); + return (TSyntaxNode)syntaxNode.ReplaceFirstToken(newToken); + } + + public static TSyntaxNode AppendLeadingTrivia(this TSyntaxNode syntaxNode, JassSyntaxTriviaList triviaList) + where TSyntaxNode : JassSyntaxNode + { + var oldToken = syntaxNode.GetFirstToken(); + var newToken = oldToken.AppendLeadingTrivia(triviaList); + return (TSyntaxNode)syntaxNode.ReplaceFirstToken(newToken); + } + + public static TSyntaxNode AppendLeadingTrivia(this TSyntaxNode syntaxNode, params JassSyntaxTrivia[] triviaList) + where TSyntaxNode : JassSyntaxNode + { + var oldToken = syntaxNode.GetFirstToken(); + var newToken = oldToken.AppendLeadingTrivia(triviaList); + return (TSyntaxNode)syntaxNode.ReplaceFirstToken(newToken); + } + + public static TSyntaxNode AppendLeadingTrivia(this TSyntaxNode syntaxNode, IEnumerable triviaList) + where TSyntaxNode : JassSyntaxNode + { + var oldToken = syntaxNode.GetFirstToken(); + var newToken = oldToken.AppendLeadingTrivia(triviaList); + return (TSyntaxNode)syntaxNode.ReplaceFirstToken(newToken); + } + + public static TSyntaxNode AppendLeadingTrivia(this TSyntaxNode syntaxNode, ImmutableArray triviaList) + where TSyntaxNode : JassSyntaxNode + { + var oldToken = syntaxNode.GetFirstToken(); + var newToken = oldToken.AppendLeadingTrivia(triviaList); + return (TSyntaxNode)syntaxNode.ReplaceFirstToken(newToken); + } + + public static TSyntaxNode PrependLeadingTrivia(this TSyntaxNode syntaxNode, JassSyntaxTrivia trivia) + where TSyntaxNode : JassSyntaxNode + { + var oldToken = syntaxNode.GetFirstToken(); + var newToken = oldToken.PrependLeadingTrivia(trivia); + return (TSyntaxNode)syntaxNode.ReplaceFirstToken(newToken); + } + + public static TSyntaxNode PrependLeadingTrivia(this TSyntaxNode syntaxNode, JassSyntaxTriviaList triviaList) + where TSyntaxNode : JassSyntaxNode + { + var oldToken = syntaxNode.GetFirstToken(); + var newToken = oldToken.PrependLeadingTrivia(triviaList); + return (TSyntaxNode)syntaxNode.ReplaceFirstToken(newToken); + } + + public static TSyntaxNode PrependLeadingTrivia(this TSyntaxNode syntaxNode, params JassSyntaxTrivia[] triviaList) + where TSyntaxNode : JassSyntaxNode + { + var oldToken = syntaxNode.GetFirstToken(); + var newToken = oldToken.PrependLeadingTrivia(triviaList); + return (TSyntaxNode)syntaxNode.ReplaceFirstToken(newToken); + } + + public static TSyntaxNode PrependLeadingTrivia(this TSyntaxNode syntaxNode, IEnumerable triviaList) + where TSyntaxNode : JassSyntaxNode + { + var oldToken = syntaxNode.GetFirstToken(); + var newToken = oldToken.PrependLeadingTrivia(triviaList); + return (TSyntaxNode)syntaxNode.ReplaceFirstToken(newToken); + } + + public static TSyntaxNode PrependLeadingTrivia(this TSyntaxNode syntaxNode, ImmutableArray triviaList) + where TSyntaxNode : JassSyntaxNode + { + var oldToken = syntaxNode.GetFirstToken(); + var newToken = oldToken.PrependLeadingTrivia(triviaList); + return (TSyntaxNode)syntaxNode.ReplaceFirstToken(newToken); + } + + public static TSyntaxNode AppendTrailingTrivia(this TSyntaxNode syntaxNode, JassSyntaxTrivia trivia) + where TSyntaxNode : JassSyntaxNode + { + var oldToken = syntaxNode.GetLastToken(); + var newToken = oldToken.AppendTrailingTrivia(trivia); + return (TSyntaxNode)syntaxNode.ReplaceLastToken(newToken); + } + + public static TSyntaxNode AppendTrailingTrivia(this TSyntaxNode syntaxNode, JassSyntaxTriviaList triviaList) + where TSyntaxNode : JassSyntaxNode + { + var oldToken = syntaxNode.GetLastToken(); + var newToken = oldToken.AppendTrailingTrivia(triviaList); + return (TSyntaxNode)syntaxNode.ReplaceLastToken(newToken); + } + + public static TSyntaxNode AppendTrailingTrivia(this TSyntaxNode syntaxNode, params JassSyntaxTrivia[] triviaList) + where TSyntaxNode : JassSyntaxNode + { + var oldToken = syntaxNode.GetLastToken(); + var newToken = oldToken.AppendTrailingTrivia(triviaList); + return (TSyntaxNode)syntaxNode.ReplaceLastToken(newToken); + } + + public static TSyntaxNode AppendTrailingTrivia(this TSyntaxNode syntaxNode, IEnumerable triviaList) + where TSyntaxNode : JassSyntaxNode + { + var oldToken = syntaxNode.GetLastToken(); + var newToken = oldToken.AppendTrailingTrivia(triviaList); + return (TSyntaxNode)syntaxNode.ReplaceLastToken(newToken); + } + + public static TSyntaxNode AppendTrailingTrivia(this TSyntaxNode syntaxNode, ImmutableArray triviaList) + where TSyntaxNode : JassSyntaxNode + { + var oldToken = syntaxNode.GetLastToken(); + var newToken = oldToken.AppendTrailingTrivia(triviaList); + return (TSyntaxNode)syntaxNode.ReplaceLastToken(newToken); + } + + public static TSyntaxNode PrependTrailingTrivia(this TSyntaxNode syntaxNode, JassSyntaxTrivia trivia) + where TSyntaxNode : JassSyntaxNode + { + var oldToken = syntaxNode.GetLastToken(); + var newToken = oldToken.PrependTrailingTrivia(trivia); + return (TSyntaxNode)syntaxNode.ReplaceLastToken(newToken); + } + + public static TSyntaxNode PrependTrailingTrivia(this TSyntaxNode syntaxNode, JassSyntaxTriviaList triviaList) + where TSyntaxNode : JassSyntaxNode + { + var oldToken = syntaxNode.GetLastToken(); + var newToken = oldToken.PrependTrailingTrivia(triviaList); + return (TSyntaxNode)syntaxNode.ReplaceLastToken(newToken); + } + + public static TSyntaxNode PrependTrailingTrivia(this TSyntaxNode syntaxNode, params JassSyntaxTrivia[] triviaList) + where TSyntaxNode : JassSyntaxNode + { + var oldToken = syntaxNode.GetLastToken(); + var newToken = oldToken.PrependTrailingTrivia(triviaList); + return (TSyntaxNode)syntaxNode.ReplaceLastToken(newToken); + } + + public static TSyntaxNode PrependTrailingTrivia(this TSyntaxNode syntaxNode, IEnumerable triviaList) + where TSyntaxNode : JassSyntaxNode + { + var oldToken = syntaxNode.GetLastToken(); + var newToken = oldToken.PrependTrailingTrivia(triviaList); + return (TSyntaxNode)syntaxNode.ReplaceLastToken(newToken); + } + + public static TSyntaxNode PrependTrailingTrivia(this TSyntaxNode syntaxNode, ImmutableArray triviaList) + where TSyntaxNode : JassSyntaxNode + { + var oldToken = syntaxNode.GetLastToken(); + var newToken = oldToken.PrependTrailingTrivia(triviaList); + return (TSyntaxNode)syntaxNode.ReplaceLastToken(newToken); + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Extensions/TypeExtensions.cs b/src/War3Net.CodeAnalysis.Jass/Extensions/TypeExtensions.cs index 27ef3515..d3fa07e2 100644 --- a/src/War3Net.CodeAnalysis.Jass/Extensions/TypeExtensions.cs +++ b/src/War3Net.CodeAnalysis.Jass/Extensions/TypeExtensions.cs @@ -19,22 +19,22 @@ public static JassTypeSyntax ToJassType(this Type type) var typeCode = Type.GetTypeCode(type); return typeCode switch { - TypeCode.Empty => JassTypeSyntax.Nothing, - TypeCode.Object => JassTypeSyntax.Handle, - TypeCode.Boolean => JassTypeSyntax.Boolean, - TypeCode.Char => JassTypeSyntax.Integer, - TypeCode.SByte => JassTypeSyntax.Integer, - TypeCode.Byte => JassTypeSyntax.Integer, - TypeCode.Int16 => JassTypeSyntax.Integer, - TypeCode.UInt16 => JassTypeSyntax.Integer, - TypeCode.Int32 => JassTypeSyntax.Integer, - TypeCode.UInt32 => JassTypeSyntax.Integer, - TypeCode.Int64 => JassTypeSyntax.Integer, - TypeCode.UInt64 => JassTypeSyntax.Integer, - TypeCode.Single => JassTypeSyntax.Real, - TypeCode.Double => JassTypeSyntax.Real, - TypeCode.Decimal => JassTypeSyntax.Real, - TypeCode.String => JassTypeSyntax.String, + TypeCode.Empty => JassPredefinedTypeSyntax.Nothing, + TypeCode.Object => JassPredefinedTypeSyntax.Handle, + TypeCode.Boolean => JassPredefinedTypeSyntax.Boolean, + TypeCode.Char => JassPredefinedTypeSyntax.Integer, + TypeCode.SByte => JassPredefinedTypeSyntax.Integer, + TypeCode.Byte => JassPredefinedTypeSyntax.Integer, + TypeCode.Int16 => JassPredefinedTypeSyntax.Integer, + TypeCode.UInt16 => JassPredefinedTypeSyntax.Integer, + TypeCode.Int32 => JassPredefinedTypeSyntax.Integer, + TypeCode.UInt32 => JassPredefinedTypeSyntax.Integer, + TypeCode.Int64 => JassPredefinedTypeSyntax.Integer, + TypeCode.UInt64 => JassPredefinedTypeSyntax.Integer, + TypeCode.Single => JassPredefinedTypeSyntax.Real, + TypeCode.Double => JassPredefinedTypeSyntax.Real, + TypeCode.Decimal => JassPredefinedTypeSyntax.Real, + TypeCode.String => JassPredefinedTypeSyntax.String, _ => throw new InvalidEnumArgumentException(nameof(typeCode), (int)typeCode, typeof(TypeCode)), }; diff --git a/src/War3Net.CodeAnalysis.Jass/Extensions/UnaryOperatorTypeExtensions.cs b/src/War3Net.CodeAnalysis.Jass/Extensions/UnaryOperatorTypeExtensions.cs deleted file mode 100644 index 66dc6e8a..00000000 --- a/src/War3Net.CodeAnalysis.Jass/Extensions/UnaryOperatorTypeExtensions.cs +++ /dev/null @@ -1,28 +0,0 @@ -// ------------------------------------------------------------------------------ -// -// Licensed under the MIT license. -// See the LICENSE file in the project root for more information. -// -// ------------------------------------------------------------------------------ - -using System.ComponentModel; - -using War3Net.CodeAnalysis.Jass.Syntax; - -namespace War3Net.CodeAnalysis.Jass.Extensions -{ - public static class UnaryOperatorTypeExtensions - { - public static string GetSymbol(this UnaryOperatorType unaryOperator) - { - return unaryOperator switch - { - UnaryOperatorType.Plus => $"{JassSymbol.PlusSign}", - UnaryOperatorType.Minus => $"{JassSymbol.MinusSign}", - UnaryOperatorType.Not => JassKeyword.Not, - - _ => throw new InvalidEnumArgumentException(nameof(unaryOperator), (int)unaryOperator, typeof(UnaryOperatorType)), - }; - } - } -} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/JassExpression.cs b/src/War3Net.CodeAnalysis.Jass/JassExpression.cs new file mode 100644 index 00000000..b263f0f0 --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/JassExpression.cs @@ -0,0 +1,137 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +namespace War3Net.CodeAnalysis.Jass +{ + public static class JassExpression + { + public static string FunctionRef(string functionName) + { + return $"{JassKeyword.Function} {functionName}"; + } + + public static string Invoke(string functionName) + { + return $"{functionName}()"; + } + + public static string Invoke(string functionName, params string[] arguments) + { + return $"{functionName}({string.Join(", ", arguments)})"; + } + + public static string InvokeCompact(string functionName, params string[] arguments) + { + return $"{functionName}({string.Join(",", arguments)})"; + } + + public static string InvokeSpaced(string functionName, params string[] arguments) + { + return $"{functionName}( {string.Join(", ", arguments)} )"; + } + + public static string Not(string expression) + { + return $"{JassKeyword.Not} {expression}"; + } + + public static string Negate(string expression) + { + return $"-{expression}"; + } + + public static string And(string left, string right) + { + return $"{left} {JassKeyword.And} {right}"; + } + + public static string Or(string left, string right) + { + return $"{left} {JassKeyword.Or} {right}"; + } + + public static string Equal(string left, string right) + { + return $"{left} == {right}"; + } + + public static string EqualCompact(string left, string right) + { + return $"{left}=={right}"; + } + + public static string NotEqual(string left, string right) + { + return $"{left} != {right}"; + } + + public static string Add(string left, string right) + { + return $"{left} + {right}"; + } + + public static string Subtract(string left, string right) + { + return $"{left} - {right}"; + } + + public static string Multiply(string left, string right) + { + return $"{left} * {right}"; + } + + public static string Divide(string left, string right) + { + return $"{left} / {right}"; + } + + public static string LessThan(string left, string right) + { + return $"{left} < {right}"; + } + + public static string LessThanOrEqual(string left, string right) + { + return $"{left} <= {right}"; + } + + public static string GreaterThan(string left, string right) + { + return $"{left} > {right}"; + } + + public static string GreaterThanOrEqual(string left, string right) + { + return $"{left} >= {right}"; + } + + public static string Binary(string left, string @operator, string right) + { + return $"{left} {@operator} {right}"; + } + + public static string Parenthesized(string expression) + { + return $"( {expression} )"; + } + + public static string ParenthesizedCompact(string expression) + { + return $"({expression})"; + } + + public static string ElementAccess(string arrayName, int index) + { + return $"{arrayName}[{index}]"; + } + + public static string ElementAccess(string arrayName, string index) + { + return $"{arrayName}[{index}]"; + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/JassLiteral.cs b/src/War3Net.CodeAnalysis.Jass/JassLiteral.cs new file mode 100644 index 00000000..791b7637 --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/JassLiteral.cs @@ -0,0 +1,102 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using System; +using System.Globalization; + +using War3Net.CodeAnalysis.Jass.Extensions; +using War3Net.Common.Extensions; + +namespace War3Net.CodeAnalysis.Jass +{ + public static class JassLiteral + { + public static string Int(int value) + { + return value.ToString(CultureInfo.InvariantCulture); + } + + public static string Real(float value, int decimals = 1) + { + return value.ToString($"F{decimals}", CultureInfo.InvariantCulture); + } + + public static string Real(double value, int decimals = 1) + { + return value.ToString($"F{decimals}", CultureInfo.InvariantCulture); + } + + public static string String(string? value) + { + return $"{JassSymbol.DoubleQuoteChar}{EscapedStringProvider.GetEscapedString(value ?? string.Empty)}{JassSymbol.DoubleQuoteChar}"; + } + + public static string Bool(bool value) + { + return value ? JassKeyword.True : JassKeyword.False; + } + + public static string FourCC(int value) + { + return $"{JassSymbol.SingleQuoteChar}{value.ToRawcode()}{JassSymbol.SingleQuoteChar}"; + } + + public static string FourCC(string value) + { + return $"{JassSymbol.SingleQuoteChar}{value}{JassSymbol.SingleQuoteChar}"; + } + + public static float ParseReal(string value) + { + return float.Parse(value, NumberStyles.AllowDecimalPoint, CultureInfo.InvariantCulture); + } + + public static char ParseChar(string value) + { + if (value[1] == '\\') + { + return value[2] switch + { + 'r' => '\r', + 'n' => '\n', + 't' => '\t', + 'b' => '\b', + 'f' => '\f', + _ => value[2], + }; + } + + return value[1]; + } + + public static string ParseString(string value) + { + return value[1..^1]; + } + + public static int ParseInt(string value) + { + return int.Parse(value, NumberStyles.None, CultureInfo.InvariantCulture); + } + + public static int ParseHex(string value) + { + var hexDigits = value.StartsWith(JassSymbol.DollarChar) ? value[1..] : value[2..]; + return Convert.ToInt32(hexDigits, 16); + } + + public static int ParseOctal(string value) + { + return Convert.ToInt32(value, 8); + } + + public static int ParseFourCC(string value) + { + return value[1..^1].FromJassRawcode(); + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/JassParser.cs b/src/War3Net.CodeAnalysis.Jass/JassParser.cs index f1e678af..72cbcaa7 100644 --- a/src/War3Net.CodeAnalysis.Jass/JassParser.cs +++ b/src/War3Net.CodeAnalysis.Jass/JassParser.cs @@ -5,6 +5,8 @@ // // ------------------------------------------------------------------------------ +using System; + using Pidgin; using War3Net.CodeAnalysis.Jass.Syntax; @@ -19,191 +21,168 @@ internal partial class JassParser private static readonly JassParser _parser = new JassParser(); private readonly Parser _argumentListParser; - private readonly Parser _binaryOperatorParser; - private readonly Parser _commentParser; + private readonly Parser _binaryOperatorParser; private readonly Parser _compilationUnitParser; - private readonly Parser _declarationParser; - private readonly Parser _declarationLineParser; - private readonly Parser _expressionParser; - private readonly Parser _functionDeclarationParser; - private readonly Parser _globalDeclarationParser; - private readonly Parser _globalLineParser; + private readonly Parser _expressionParser; + private readonly Parser _globalDeclarationParser; private readonly Parser _identifierNameParser; - private readonly Parser _parameterListParser; - private readonly Parser _statementParser; - private readonly Parser _statementLineParser; + private readonly Parser _leadingTriviaListParser; + private readonly Parser _parameterListParser; + private readonly Parser _statementParser; + private readonly Parser _topLevelDeclarationParser; + private readonly Parser _trailingTriviaListParser; private readonly Parser _typeParser; - private readonly Parser _unaryOperatorParser; + private readonly Parser _unaryOperatorParser; private JassParser() { - var whitespaceParser = GetWhitespaceParser(); - var identifierNameParser = GetIdentifierNameParser(whitespaceParser); - var typeParser = GetTypeParser(identifierNameParser, whitespaceParser); - var expressionParser = GetExpressionParser(whitespaceParser, identifierNameParser); - var equalsValueClauseParser = GetEqualsValueClauseParser(whitespaceParser, expressionParser); - - var argumentListParser = GetArgumentListParser(whitespaceParser, expressionParser); - var parameterListParser = GetParameterListParser(whitespaceParser, GetParameterParser(identifierNameParser, typeParser)); - var functionDeclaratorParser = GetFunctionDeclaratorParser(identifierNameParser, parameterListParser, typeParser, whitespaceParser); - var variableDeclaratorParser = GetVariableDeclaratorParser(equalsValueClauseParser, identifierNameParser, typeParser, whitespaceParser); - - var commentStringParser = GetCommentStringParser(); - var newLineParser = GetNewLineParser(whitespaceParser); - var endOfLineParser = GetEndOfLineParser(commentStringParser, newLineParser); - var emptyParser = GetEmptyParser(); - var emptyLineParser = GetEmptyLineParser(); - var commentParser = GetCommentParser(commentStringParser); - - var setStatementParser = GetSetStatementParser(whitespaceParser, expressionParser, equalsValueClauseParser, identifierNameParser); - var callStatementParser = GetCallStatementParser(whitespaceParser, argumentListParser, identifierNameParser); - var exitStatementParser = GetExitStatementParser(expressionParser, whitespaceParser); - var localVariableDeclarationStatementParser = GetLocalVariableDeclarationStatementParser(variableDeclaratorParser, whitespaceParser); - var returnStatementParser = GetReturnStatementParser(expressionParser, whitespaceParser); + var whitespaceTriviaParser = GetWhitespaceTriviaParser(); + var newLineTriviaParser = GetNewLineTriviaParser(); + var singleNewLineTriviaParser = GetSingleNewLineTriviaParser(); + var singleLineCommentTriviaParser = GetSingleLineCommentTriviaParser(); + + var simpleTriviaListParser = GetSimpleTriviaListParser( + whitespaceTriviaParser); + + var leadingTriviaListParser = GetLeadingTriviaListParser( + whitespaceTriviaParser, + newLineTriviaParser, + singleLineCommentTriviaParser); + + var trailingTriviaListParser = GetTrailingTriviaListParser( + whitespaceTriviaParser, + singleNewLineTriviaParser, + singleLineCommentTriviaParser); + + var identifierParser = GetIdentifierParser(); + var identifierNameParser = GetIdentifierNameParser(identifierParser, simpleTriviaListParser); + var typeParser = GetTypeParser(identifierParser, simpleTriviaListParser); + + var expressionParser = GetExpressionParser(identifierParser, identifierNameParser, simpleTriviaListParser); + var equalsValueClauseParser = GetEqualsValueClauseParser(simpleTriviaListParser, expressionParser); + + var parameterParser = GetParameterParser(identifierNameParser, typeParser); + var parameterListParser = GetParameterListParser(simpleTriviaListParser, parameterParser); + var argumentListParser = GetArgumentListParser(simpleTriviaListParser, expressionParser); + var returnClauseParser = GetReturnClauseParser(typeParser, simpleTriviaListParser); + var functionDeclaratorParser = GetFunctionDeclaratorParser(identifierNameParser, parameterListParser, returnClauseParser, simpleTriviaListParser, trailingTriviaListParser); + var variableOrArrayDeclaratorParser = GetVariableOrArrayDeclaratorParser(equalsValueClauseParser, identifierNameParser, typeParser, simpleTriviaListParser); + var elementAccessClauseParser = GetElementAccessClauseParser(simpleTriviaListParser, expressionParser); + + var setStatementParser = GetSetStatementParser(identifierNameParser, elementAccessClauseParser, equalsValueClauseParser, simpleTriviaListParser, trailingTriviaListParser); + var callStatementParser = GetCallStatementParser(identifierNameParser, argumentListParser, simpleTriviaListParser, trailingTriviaListParser); + var exitStatementParser = GetExitStatementParser(expressionParser, simpleTriviaListParser, trailingTriviaListParser); + var localVariableDeclarationStatementParser = GetLocalVariableDeclarationStatementParser(variableOrArrayDeclaratorParser, simpleTriviaListParser, trailingTriviaListParser); + var returnStatementParser = GetReturnStatementParser(expressionParser, simpleTriviaListParser, trailingTriviaListParser); + + var ifClauseDeclaratorParser = GetIfClauseDeclaratorParser(expressionParser, simpleTriviaListParser, trailingTriviaListParser); + var elseIfClauseDeclaratorParser = GetElseIfClauseDeclaratorParser(expressionParser, simpleTriviaListParser, trailingTriviaListParser); var statementParser = GetStatementParser( - emptyParser, - commentParser, localVariableDeclarationStatementParser, exitStatementParser, returnStatementParser, setStatementParser, callStatementParser, - expressionParser, - whitespaceParser, - endOfLineParser); + ifClauseDeclaratorParser, + elseIfClauseDeclaratorParser, + simpleTriviaListParser, + leadingTriviaListParser, + trailingTriviaListParser); - var statementLineParser = GetStatementLineParser( - emptyLineParser, - commentParser, - localVariableDeclarationStatementParser, - setStatementParser, - callStatementParser, - exitStatementParser, - returnStatementParser, - expressionParser, - whitespaceParser); - - var constantDeclarationParser = GetConstantDeclarationParser( + var globalConstantDeclarationParser = GetGlobalConstantDeclarationParser( equalsValueClauseParser, identifierNameParser, typeParser, - whitespaceParser); + simpleTriviaListParser, + trailingTriviaListParser); - var variableDeclarationParser = GetVariableDeclarationParser( - equalsValueClauseParser, - identifierNameParser, - typeParser, - whitespaceParser); + var globalVariableDeclarationParser = GetGlobalVariableDeclarationParser( + variableOrArrayDeclaratorParser, + trailingTriviaListParser); var globalDeclarationParser = GetGlobalDeclarationParser( - emptyParser, - commentParser, - constantDeclarationParser, - variableDeclarationParser); - - var globalLineParser = GetGlobalLineParser( - emptyLineParser, - commentParser, - constantDeclarationParser, - variableDeclarationParser, - whitespaceParser); - - var statementListParser = GetStatementListParser( - statementParser, - endOfLineParser); + globalConstantDeclarationParser, + globalVariableDeclarationParser); var functionDeclarationParser = GetFunctionDeclarationParser( functionDeclaratorParser, - statementListParser, - whitespaceParser, - endOfLineParser); + statementParser, + leadingTriviaListParser, + trailingTriviaListParser); var typeDeclarationParser = GetTypeDeclarationParser( identifierNameParser, typeParser, - whitespaceParser); + simpleTriviaListParser, + trailingTriviaListParser); var nativeFunctionDeclarationParser = GetNativeFunctionDeclarationParser( - functionDeclaratorParser, - whitespaceParser); + identifierNameParser, + parameterListParser, + returnClauseParser, + simpleTriviaListParser, + trailingTriviaListParser); - var declarationParser = GetDeclarationParser( - emptyParser, - commentParser, + var topLevelDeclarationParser = GetTopLevelDeclarationParser( typeDeclarationParser, nativeFunctionDeclarationParser, functionDeclarationParser, - globalDeclarationParser.Before(endOfLineParser), - whitespaceParser, - endOfLineParser); - - var declarationLineParser = GetDeclarationLineParser( - emptyLineParser, - commentParser, - typeDeclarationParser, - nativeFunctionDeclarationParser, - functionDeclaratorParser, - whitespaceParser); + globalDeclarationParser, + simpleTriviaListParser, + leadingTriviaListParser, + trailingTriviaListParser); var compilationUnitParser = GetCompilationUnitParser( - declarationParser, - commentStringParser, - newLineParser); + topLevelDeclarationParser, + leadingTriviaListParser); - Parser Create(Parser parser) => whitespaceParser.Then(parser).Before(End); + Parser Create(Parser parser) => simpleTriviaListParser.Then(parser).Before(End); _argumentListParser = Create(argumentListParser); - _binaryOperatorParser = Create(GetBinaryOperatorParser(whitespaceParser)); - _commentParser = Create(commentParser); + _binaryOperatorParser = Create(GetBinaryOperatorParser(simpleTriviaListParser)); _compilationUnitParser = Create(compilationUnitParser); - _declarationParser = Create(declarationParser); - _declarationLineParser = Create(declarationLineParser); _expressionParser = Create(expressionParser); - _functionDeclarationParser = Create(functionDeclarationParser); _globalDeclarationParser = Create(globalDeclarationParser); - _globalLineParser = Create(globalLineParser); _identifierNameParser = Create(identifierNameParser); + _leadingTriviaListParser = leadingTriviaListParser.Before(End); _parameterListParser = Create(parameterListParser); _statementParser = Create(statementParser); - _statementLineParser = Create(statementLineParser.Before(commentStringParser.Optional())); + _topLevelDeclarationParser = Create(topLevelDeclarationParser); + _trailingTriviaListParser = trailingTriviaListParser.Before(End); _typeParser = Create(typeParser); - _unaryOperatorParser = Create(GetUnaryOperatorParser(whitespaceParser)); + _unaryOperatorParser = Create(GetUnaryOperatorParser(simpleTriviaListParser)); } internal static JassParser Instance => _parser; internal Parser ArgumentListParser => _argumentListParser; - internal Parser BinaryOperatorParser => _binaryOperatorParser; - - internal Parser CommentParser => _commentParser; + internal Parser BinaryOperatorParser => _binaryOperatorParser; internal Parser CompilationUnitParser => _compilationUnitParser; - internal Parser DeclarationParser => _declarationParser; - - internal Parser DeclarationLineParser => _declarationLineParser; + internal Parser ExpressionParser => _expressionParser; - internal Parser ExpressionParser => _expressionParser; + internal Parser FunctionDeclarationParser => throw new NotImplementedException("FunctionDeclarationParser is not supported in the new syntax system. Use TopLevelDeclarationParser instead."); - internal Parser FunctionDeclarationParser => _functionDeclarationParser; + internal Parser GlobalDeclarationParser => _globalDeclarationParser; - internal Parser GlobalDeclarationParser => _globalDeclarationParser; + internal Parser IdentifierNameParser => _identifierNameParser; - internal Parser GlobalLineParser => _globalLineParser; + internal Parser LeadingTriviaListParser => _leadingTriviaListParser; - internal Parser IdentifierNameParser => _identifierNameParser; + internal Parser ParameterListParser => _parameterListParser; - internal Parser ParameterListParser => _parameterListParser; + internal Parser StatementParser => _statementParser; - internal Parser StatementParser => _statementParser; + internal Parser TopLevelDeclarationParser => _topLevelDeclarationParser; - internal Parser StatementLineParser => _statementLineParser; + internal Parser TrailingTriviaListParser => _trailingTriviaListParser; internal Parser TypeParser => _typeParser; - internal Parser UnaryOperatorParser => _unaryOperatorParser; + internal Parser UnaryOperatorParser => _unaryOperatorParser; private static class Keyword { @@ -298,31 +277,35 @@ private static Parser GetKeywordParser(string keyword, Parser LineFeed = Char(JassSymbol.LineFeed); - internal static readonly Parser CarriageReturn = Char(JassSymbol.CarriageReturn); - internal static readonly Parser QuotationMark = Char(JassSymbol.QuotationMark); - internal static readonly Parser DollarSign = Char(JassSymbol.DollarSign); - internal static readonly Parser Apostrophe = Char(JassSymbol.Apostrophe); - internal static readonly Parser LeftParenthesis = Char(JassSymbol.LeftParenthesis); - internal static readonly Parser RightParenthesis = Char(JassSymbol.RightParenthesis); - internal static readonly Parser Asterisk = Char(JassSymbol.Asterisk); - internal static readonly Parser PlusSign = Char(JassSymbol.PlusSign); - internal static readonly Parser Comma = Char(JassSymbol.Comma); - internal static readonly Parser MinusSign = Char(JassSymbol.MinusSign); - internal static readonly Parser FullStop = Char(JassSymbol.FullStop); - internal static readonly Parser Slash = Char(JassSymbol.Slash); - internal static readonly Parser Zero = Char(JassSymbol.Zero); - internal static readonly Parser LessThanSign = Char(JassSymbol.LessThanSign); - internal static readonly Parser EqualsSign = Char(JassSymbol.EqualsSign); - internal static readonly Parser GreaterThanSign = Char(JassSymbol.GreaterThanSign); - internal static readonly Parser LeftSquareBracket = Char(JassSymbol.LeftSquareBracket); - internal static readonly Parser RightSquareBracket = Char(JassSymbol.RightSquareBracket); - internal static readonly Parser X = CIChar(JassSymbol.X); - - internal static readonly Parser EqualsEquals = String($"{JassSymbol.EqualsSign}{JassSymbol.EqualsSign}"); - internal static readonly Parser LessOrEquals = String($"{JassSymbol.LessThanSign}{JassSymbol.EqualsSign}"); - internal static readonly Parser GreaterOrEquals = String($"{JassSymbol.GreaterThanSign}{JassSymbol.EqualsSign}"); - internal static readonly Parser NotEquals = String($"{JassSymbol.ExclamationMark}{JassSymbol.EqualsSign}"); + internal static readonly Parser LineFeed = Char(JassSymbol.LineFeedChar); + internal static readonly Parser CarriageReturn = Char(JassSymbol.CarriageReturnChar); + internal static readonly Parser DoubleQuote = Char(JassSymbol.DoubleQuoteChar); + internal static readonly Parser Dollar = Char(JassSymbol.DollarChar); + internal static readonly Parser SingleQuote = Char(JassSymbol.SingleQuoteChar); + internal static readonly Parser OpenParen = Char(JassSymbol.OpenParenChar); + internal static readonly Parser CloseParen = Char(JassSymbol.CloseParenChar); + internal static readonly Parser Asterisk = Char(JassSymbol.AsteriskChar); + internal static readonly Parser Plus = Char(JassSymbol.PlusChar); + internal static readonly Parser Comma = Char(JassSymbol.CommaChar); + internal static readonly Parser Minus = Char(JassSymbol.MinusChar); + internal static readonly Parser Dot = Char(JassSymbol.DotChar); + internal static readonly Parser Slash = Char(JassSymbol.SlashChar); + internal static readonly Parser Zero = Char(JassSymbol.ZeroChar); + internal static readonly Parser LessThan = Char(JassSymbol.LessThanChar); + internal static new readonly Parser Equals = Char(JassSymbol.EqualsChar); + internal static readonly Parser GreaterThan = Char(JassSymbol.GreaterThanChar); + internal static readonly Parser OpenBracket = Char(JassSymbol.OpenBracketChar); + internal static readonly Parser CloseBracket = Char(JassSymbol.CloseBracketChar); + internal static readonly Parser X = CIChar(JassSymbol.XChar); + + internal static readonly Parser CarriageReturnLineFeed = String(JassSymbol.CarriageReturnLineFeed); + internal static readonly Parser CarriageReturnString = String(JassSymbol.CarriageReturn); + internal static readonly Parser LineFeedString = String(JassSymbol.LineFeed); + internal static readonly Parser EqualsEquals = String(JassSymbol.EqualsEquals); + internal static readonly Parser LessThanEquals = String(JassSymbol.LessThanEquals); + internal static readonly Parser GreaterThanEquals = String(JassSymbol.GreaterThanEquals); + internal static readonly Parser ExclamationEquals = String(JassSymbol.ExclamationEquals); + internal static readonly Parser SlashSlash = String(JassSymbol.SlashSlash); } } } \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/JassRenamer.cs b/src/War3Net.CodeAnalysis.Jass/JassRenamer.cs index 8d435559..250494d7 100644 --- a/src/War3Net.CodeAnalysis.Jass/JassRenamer.cs +++ b/src/War3Net.CodeAnalysis.Jass/JassRenamer.cs @@ -8,19 +8,17 @@ using System; using System.Collections.Generic; -using War3Net.CodeAnalysis.Jass.Syntax; - namespace War3Net.CodeAnalysis.Jass { public partial class JassRenamer { - private readonly Dictionary _functionDeclarationRenames; - private readonly Dictionary _globalVariableRenames; + private readonly Dictionary _functionDeclarationRenames; + private readonly Dictionary _globalVariableRenames; private readonly HashSet _localVariableNames; public JassRenamer( - Dictionary functionDeclarationRenames, - Dictionary globalVariableRenames) + Dictionary functionDeclarationRenames, + Dictionary globalVariableRenames) { _functionDeclarationRenames = functionDeclarationRenames; _globalVariableRenames = globalVariableRenames; diff --git a/src/War3Net.CodeAnalysis.Jass/JassRenderer.cs b/src/War3Net.CodeAnalysis.Jass/JassRenderer.cs index 5ebd51c7..9d15b780 100644 --- a/src/War3Net.CodeAnalysis.Jass/JassRenderer.cs +++ b/src/War3Net.CodeAnalysis.Jass/JassRenderer.cs @@ -5,11 +5,13 @@ // // ------------------------------------------------------------------------------ +using System; using System.IO; using System.Linq; namespace War3Net.CodeAnalysis.Jass { + [Obsolete("Use NormalizeWhitespace() and JassSyntaxNode.WriteTo()")] public partial class JassRenderer { private readonly TextWriter _writer; @@ -34,14 +36,12 @@ public JassRenderer(TextWriter writer, JassRendererOptions options) public void RenderNewLine() => WriteLine(); - private void Write(char c) + private void WriteSpace() { - if (!_currentLineIndented) + if (_currentLineIndented) { - WriteIndentation(); + _writer.Write(' '); } - - _writer.Write(c); } private void Write(string s) @@ -60,12 +60,6 @@ private void WriteLine() _currentLineIndented = false; } - private void WriteLine(string s) - { - Write(s); - WriteLine(); - } - private void WriteIndentation() { _currentLineIndented = true; diff --git a/src/War3Net.CodeAnalysis.Jass/JassSymbol.cs b/src/War3Net.CodeAnalysis.Jass/JassSymbol.cs index 8b116c9b..9d01c3ae 100644 --- a/src/War3Net.CodeAnalysis.Jass/JassSymbol.cs +++ b/src/War3Net.CodeAnalysis.Jass/JassSymbol.cs @@ -11,26 +11,54 @@ namespace War3Net.CodeAnalysis.Jass { public static class JassSymbol { - public const char LineFeed = '\n'; - public const char CarriageReturn = '\r'; - public const char ExclamationMark = '!'; - public const char QuotationMark = '"'; - public const char DollarSign = '$'; - public const char Apostrophe = '\''; - public const char LeftParenthesis = '('; - public const char RightParenthesis = ')'; - public const char Asterisk = '*'; - public const char PlusSign = '+'; - public const char Comma = ','; - public const char MinusSign = '-'; - public const char FullStop = '.'; - public const char Slash = '/'; - public const char Zero = '0'; - public const char LessThanSign = '<'; - public const char EqualsSign = '='; - public const char GreaterThanSign = '>'; - public const char LeftSquareBracket = '['; - public const char RightSquareBracket = ']'; - public const char X = 'x'; + public const char LineFeedChar = '\n'; + public const char CarriageReturnChar = '\r'; + public const char ExclamationChar = '!'; + public const char DoubleQuoteChar = '"'; + public const char DollarChar = '$'; + public const char SingleQuoteChar = '\''; + public const char OpenParenChar = '('; + public const char CloseParenChar = ')'; + public const char AsteriskChar = '*'; + public const char PlusChar = '+'; + public const char CommaChar = ','; + public const char MinusChar = '-'; + public const char DotChar = '.'; + public const char SlashChar = '/'; + public const char ZeroChar = '0'; + public const char LessThanChar = '<'; + public const char EqualsChar = '='; + public const char GreaterThanChar = '>'; + public const char OpenBracketChar = '['; + public const char CloseBracketChar = ']'; + public const char XChar = 'x'; + public const char SpaceChar = ' '; + public const char TabChar = '\t'; + + public const string LineFeed = "\n"; + public const string CarriageReturn = "\r"; + public const string Dollar = "$"; + public const string OpenParen = "("; + public const string CloseParen = ")"; + public const string Asterisk = "*"; + public const string Plus = "+"; + public const string Comma = ","; + public const string Minus = "-"; + public const string Dot = "."; + public const string Slash = "/"; + public const string Zero = "0"; + public const string LessThan = "<"; + public new const string Equals = "="; + public const string GreaterThan = ">"; + public const string OpenBracket = "["; + public const string CloseBracket = "]"; + public const string Space = " "; + + public const string CarriageReturnLineFeed = "\r\n"; + public const string SlashSlash = "//"; + public const string ExclamationEquals = "!="; + public const string LessThanEquals = "<="; + public const string GreaterThanEquals = ">="; + public const string EqualsEquals = "=="; } } \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/JassSyntaxFactory.cs b/src/War3Net.CodeAnalysis.Jass/JassSyntaxFactory.cs index ace6c7d8..87dd64c4 100644 --- a/src/War3Net.CodeAnalysis.Jass/JassSyntaxFactory.cs +++ b/src/War3Net.CodeAnalysis.Jass/JassSyntaxFactory.cs @@ -5,7 +5,10 @@ // // ------------------------------------------------------------------------------ +using System; +using System.Collections.Immutable; using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; using Pidgin; @@ -20,32 +23,22 @@ public static JassArgumentListSyntax ParseArgumentList(string argumentList) return JassParser.Instance.ArgumentListParser.ParseOrThrow(argumentList); } - public static BinaryOperatorType ParseBinaryOperator(string binaryOperator) + public static JassSyntaxToken ParseBinaryOperator(string binaryOperator) { return JassParser.Instance.BinaryOperatorParser.ParseOrThrow(binaryOperator); } - public static JassCommentSyntax ParseComment(string comment) - { - return JassParser.Instance.CommentParser.ParseOrThrow(comment); - } - public static JassCompilationUnitSyntax ParseCompilationUnit(string compilationUnit) { return JassParser.Instance.CompilationUnitParser.ParseOrThrow(compilationUnit); } - public static ITopLevelDeclarationSyntax ParseDeclaration(string declaration) - { - return JassParser.Instance.DeclarationParser.ParseOrThrow(declaration); - } - - public static IDeclarationLineSyntax ParseDeclarationLine(string declarationLine) + public static JassSyntaxNodeOrToken ParseCustomScriptAction(string customScriptAction) { - return JassParser.Instance.DeclarationLineParser.ParseOrThrow(declarationLine); + throw new NotImplementedException(); } - public static IExpressionSyntax ParseExpression(string expression) + public static JassExpressionSyntax ParseExpression(string expression) { return JassParser.Instance.ExpressionParser.ParseOrThrow(expression); } @@ -55,34 +48,29 @@ public static JassFunctionDeclarationSyntax ParseFunctionDeclaration(string func return JassParser.Instance.FunctionDeclarationParser.ParseOrThrow(functionDeclaration); } - public static IGlobalDeclarationSyntax ParseGlobalDeclaration(string globalDeclaration) + public static JassGlobalDeclarationSyntax ParseGlobalDeclaration(string globalDeclaration) { return JassParser.Instance.GlobalDeclarationParser.ParseOrThrow(globalDeclaration); } - public static IGlobalLineSyntax ParseGlobalLine(string globalLine) - { - return JassParser.Instance.GlobalLineParser.ParseOrThrow(globalLine); - } - public static JassIdentifierNameSyntax ParseIdentifierName(string identifierName) { return JassParser.Instance.IdentifierNameParser.ParseOrThrow(identifierName); } - public static JassParameterListSyntax ParseParameterList(string parameterList) + public static JassParameterListOrEmptyParameterListSyntax ParseParameterList(string parameterList) { return JassParser.Instance.ParameterListParser.ParseOrThrow(parameterList); } - public static IStatementSyntax ParseStatement(string statement) + public static JassStatementSyntax ParseStatement(string statement) { return JassParser.Instance.StatementParser.ParseOrThrow(statement); } - public static IStatementLineSyntax ParseStatementLine(string statementLine) + public static JassTopLevelDeclarationSyntax ParseTopLevelDeclaration(string topLevelDeclaration) { - return JassParser.Instance.StatementLineParser.ParseOrThrow(statementLine); + return JassParser.Instance.TopLevelDeclarationParser.ParseOrThrow(topLevelDeclaration); } public static JassTypeSyntax ParseTypeName(string typeName) @@ -90,7 +78,7 @@ public static JassTypeSyntax ParseTypeName(string typeName) return JassParser.Instance.TypeParser.ParseOrThrow(typeName); } - public static UnaryOperatorType ParseUnaryOperator(string unaryOperator) + public static JassSyntaxToken ParseUnaryOperator(string unaryOperator) { return JassParser.Instance.UnaryOperatorParser.ParseOrThrow(unaryOperator); } @@ -100,32 +88,17 @@ public static bool TryParseArgumentList(string argumentList, [NotNullWhen(true)] return TryParse(argumentList, JassParser.Instance.ArgumentListParser, out result); } - public static bool TryParseBinaryOperator(string binaryOperator, [NotNullWhen(true)] out BinaryOperatorType? result) + public static bool TryParseBinaryOperator(string binaryOperator, [NotNullWhen(true)] out JassSyntaxToken? result) { return TryParse(binaryOperator, JassParser.Instance.BinaryOperatorParser, out result); } - public static bool TryParseComment(string comment, [NotNullWhen(true)] out JassCommentSyntax? result) - { - return TryParse(comment, JassParser.Instance.CommentParser, out result); - } - public static bool TryParseCompilationUnit(string compilationUnit, [NotNullWhen(true)] out JassCompilationUnitSyntax? result) { return TryParse(compilationUnit, JassParser.Instance.CompilationUnitParser, out result); } - public static bool TryParseDeclaration(string declaration, [NotNullWhen(true)] out ITopLevelDeclarationSyntax? result) - { - return TryParse(declaration, JassParser.Instance.DeclarationParser, out result); - } - - public static bool TryParseDeclarationLine(string declarationLine, [NotNullWhen(true)] out IDeclarationLineSyntax? result) - { - return TryParse(declarationLine, JassParser.Instance.DeclarationLineParser, out result); - } - - public static bool TryParseExpression(string expression, [NotNullWhen(true)] out IExpressionSyntax? result) + public static bool TryParseExpression(string expression, [NotNullWhen(true)] out JassExpressionSyntax? result) { return TryParse(expression, JassParser.Instance.ExpressionParser, out result); } @@ -135,34 +108,34 @@ public static bool TryParseFunctionDeclaration(string functionDeclaration, [NotN return TryParse(functionDeclaration, JassParser.Instance.FunctionDeclarationParser, out result); } - public static bool TryParseGlobalDeclaration(string globalDeclaration, [NotNullWhen(true)] out IGlobalDeclarationSyntax? result) + public static bool TryParseGlobalDeclaration(string globalDeclaration, [NotNullWhen(true)] out JassGlobalDeclarationSyntax? result) { return TryParse(globalDeclaration, JassParser.Instance.GlobalDeclarationParser, out result); } - public static bool TryParseGlobalLine(string globalLine, [NotNullWhen(true)] out IGlobalLineSyntax? result) - { - return TryParse(globalLine, JassParser.Instance.GlobalLineParser, out result); - } - public static bool TryParseIdentifierName(string identifierName, [NotNullWhen(true)] out JassIdentifierNameSyntax? result) { return TryParse(identifierName, JassParser.Instance.IdentifierNameParser, out result); } - public static bool TryParseParameterList(string parameterList, [NotNullWhen(true)] out JassParameterListSyntax? result) + public static bool TryParseParameterList(string parameterList, [NotNullWhen(true)] out JassParameterListOrEmptyParameterListSyntax? result) { return TryParse(parameterList, JassParser.Instance.ParameterListParser, out result); } - public static bool TryParseStatement(string statement, [NotNullWhen(true)] out IStatementSyntax? result) + public static bool TryParseScriptLine(string scriptLine, [NotNullWhen(true)] out JassSyntaxNodeOrToken? result) + { + throw new NotImplementedException(); + } + + public static bool TryParseStatement(string statement, [NotNullWhen(true)] out JassStatementSyntax? result) { return TryParse(statement, JassParser.Instance.StatementParser, out result); } - public static bool TryParseStatementLine(string statementLine, [NotNullWhen(true)] out IStatementLineSyntax? result) + public static bool TryParseTopLevelDeclaration(string topLevelDeclaration, [NotNullWhen(true)] out JassTopLevelDeclarationSyntax? result) { - return TryParse(statementLine, JassParser.Instance.StatementLineParser, out result); + return TryParse(topLevelDeclaration, JassParser.Instance.TopLevelDeclarationParser, out result); } public static bool TryParseTypeName(string typeName, [NotNullWhen(true)] out JassTypeSyntax? result) @@ -170,7 +143,7 @@ public static bool TryParseTypeName(string typeName, [NotNullWhen(true)] out Jas return TryParse(typeName, JassParser.Instance.TypeParser, out result); } - public static bool TryParseUnaryOperator(string unaryOperator, [NotNullWhen(true)] out UnaryOperatorType? result) + public static bool TryParseUnaryOperator(string unaryOperator, [NotNullWhen(true)] out JassSyntaxToken? result) { return TryParse(unaryOperator, JassParser.Instance.UnaryOperatorParser, out result); } @@ -202,5 +175,52 @@ private static bool TryParse(string input, Parser parser result = null; return false; } + + internal static class ThrowHelper + { + public static void ThrowIfInvalidToken(JassSyntaxToken token, JassSyntaxKind expectedSyntaxKind, [CallerArgumentExpression("token")] string? paramName = null) + { + if (token is null) + { + throw new ArgumentNullException(paramName); + } + + if (token.SyntaxKind != expectedSyntaxKind) + { + throw new ArgumentException("Invalid SyntaxKind.", paramName); + } + } + + public static void ThrowIfInvalidSeparatedSyntaxList(SeparatedSyntaxList separatedSyntaxList, JassSyntaxKind expectedSyntaxKind, [CallerArgumentExpression("separatedSyntaxList")] string? paramName = null) + { + if (separatedSyntaxList is null) + { + throw new ArgumentNullException(paramName); + } + + for (var i = 0; i < separatedSyntaxList.Items.Length; i++) + { + var item = separatedSyntaxList.Items[i]; + if (item is null) + { + throw new ArgumentException("Items in list may not be null.", paramName); + } + } + + for (var i = 0; i < separatedSyntaxList.Separators.Length; i++) + { + var separator = separatedSyntaxList.Separators[i]; + if (separator is null) + { + throw new ArgumentException("Separators in list may not be null.", paramName); + } + + if (separator.SyntaxKind != expectedSyntaxKind) + { + throw new ArgumentException("Invalid SyntaxKind.", paramName); + } + } + } + } } } \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/JassSyntaxFacts.cs b/src/War3Net.CodeAnalysis.Jass/JassSyntaxFacts.cs index b81f4592..cc8a8165 100644 --- a/src/War3Net.CodeAnalysis.Jass/JassSyntaxFacts.cs +++ b/src/War3Net.CodeAnalysis.Jass/JassSyntaxFacts.cs @@ -9,13 +9,18 @@ namespace War3Net.CodeAnalysis.Jass { - public static class JassSyntaxFacts + public static partial class JassSyntaxFacts { public static bool IsWhitespaceCharacter(char ch) { - return char.IsWhiteSpace(ch) - && ch != JassSymbol.CarriageReturn - && ch != JassSymbol.LineFeed; + return ch == JassSymbol.SpaceChar + || ch == JassSymbol.TabChar; + } + + public static bool IsNewLineCharacter(char ch) + { + return ch == JassSymbol.CarriageReturnChar + || ch == JassSymbol.LineFeedChar; } /// @@ -77,5 +82,10 @@ public static bool IsValidIdentifier([NotNullWhen(true)] string? name) return IsIdentifierEndCharacter(name[^1]); } + + internal static bool IsNotReservedKeyword(string? name) + { + return !IsReservedKeyword(GetSyntaxKind(name)); + } } } \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/JassSyntaxKind.cs b/src/War3Net.CodeAnalysis.Jass/JassSyntaxKind.cs new file mode 100644 index 00000000..b117f54c --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/JassSyntaxKind.cs @@ -0,0 +1,447 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using War3Net.CodeAnalysis.Jass.Syntax; + +namespace War3Net.CodeAnalysis.Jass +{ + public enum JassSyntaxKind + { + None = 0, + + /// Represents * token. + /// + AsteriskToken = 8199, + + /// Represents ( token. + /// + OpenParenToken = 8200, + + /// Represents ) token. + /// + CloseParenToken = 8201, + + /// Represents - token. + /// + MinusToken = 8202, + + /// Represents + token. + /// + PlusToken = 8203, + + /// Represents = token. + /// + EqualsToken = 8204, + + /// Represents [ token. + /// + OpenBracketToken = 8207, + + /// Represents ] token. + /// + CloseBracketToken = 8208, + + /// Represents < token. + /// + LessThanToken = 8215, + + /// Represents , token. + /// + CommaToken = 8216, + + /// Represents > token. + /// + GreaterThanToken = 8217, + + /// Represents / token. + /// + SlashToken = 8221, + + /// Represents != token. + /// + ExclamationEqualsToken = 8267, + + /// Represents == token. + /// + EqualsEqualsToken = 8268, + + /// Represents <= token. + /// + LessThanEqualsToken = 8270, + + /// Represents >= token. + /// + GreaterThanEqualsToken = 8273, + + /// Represents boolean keyword. + /// + BooleanKeyword = 8304, + + /// Represents integer keyword. + /// + IntegerKeyword = 8309, + + /// Represents real keyword. + /// + RealKeyword = 8314, + + /// Represents string keyword. + /// + StringKeyword = 8316, + + /// Represents nothing keyword. + /// + NothingKeyword = 8318, + + /// Represents handle keyword. + /// + HandleKeyword = 8319, + + /// Represents code keyword. + /// + CodeKeyword = 8320, + + /// Represents null keyword. + /// + NullKeyword = 8322, + + /// Represents true keyword. + /// + TrueKeyword = 8323, + + /// Represents false keyword. + /// + FalseKeyword = 8324, + + /// Represents if keyword. + /// + IfKeyword = 8325, + + /// Represents elseif keyword. + /// + ElseIfKeyword = 8326, + + /// Represents then keyword. + /// + ThenKeyword = 8327, + + /// Represents else keyword. + /// + ElseKeyword = 8328, + + /// Represents endif keyword. + /// + EndIfKeyword = 8329, + + /// Represents loop keyword. + /// + LoopKeyword = 8330, + + /// Represents exitwhen keyword. + /// + ExitWhenKeyword = 8339, + + /// Represents endloop keyword. + /// + EndLoopKeyword = 8340, + + /// Represents return keyword. + /// + ReturnKeyword = 8341, + + /// Represents call keyword. + /// + CallKeyword = 8342, + + /// Represents set keyword. + /// + SetKeyword = 8343, + + /// Represents local keyword. + /// + LocalKeyword = 8344, + + /// Represents debug keyword. + /// + DebugKeyword = 8345, + + /// Represents constant keyword. + /// + ConstantKeyword = 8350, + + /// Represents function keyword. + /// + FunctionKeyword = 8351, + + /// Represents takes keyword. + /// + TakesKeyword = 8352, + + /// Represents returns keyword. + /// + ReturnsKeyword = 8353, + + /// Represents endfunction keyword. + /// + EndFunctionKeyword = 8354, + + /// Represents native keyword. + /// + NativeKeyword = 8359, + + /// Represents extends keyword. + /// + ExtendsKeyword = 8371, + + /// Represents alias keyword. + /// + AliasKeyword = 8407, + + /// Represents array keyword. + /// + ArrayKeyword = 8408, + + /// Represents globals keyword. + /// + GlobalsKeyword = 8409, + + /// Represents endglobals keyword. + /// + EndGlobalsKeyword = 8410, + + /// Represents type keyword. + /// + TypeKeyword = 8411, + + /// Represents or keyword. + /// + OrKeyword = 8438, + + /// Represents and keyword. + /// + AndKeyword = 8439, + + /// Represents not keyword. + /// + NotKeyword = 8440, + + /// Represents the end of a file. + EndOfFileToken = 8496, + + IdentifierToken = 8508, + + RealLiteralToken = 8509, + CharacterLiteralToken = 8510, + StringLiteralToken = 8511, + DecimalLiteralToken = 8512, + HexadecimalLiteralToken = 8513, + OctalLiteralToken = 8514, + FourCCLiteralToken = 8515, + + NewLineTrivia = 8539, + WhitespaceTrivia = 8540, + SingleLineCommentTrivia = 8541, + + /// Represents . + IdentifierName = 8616, + + /// Represents . + PredefinedType = 8621, + + /// Represents . + ParenthesizedExpression = 8632, + + /// Represents . + FunctionReferenceExpression = 8633, + + /// Represents . + InvocationExpression = 8634, + + /// Represents . + ElementAccessExpression = 8635, + + /// Represents . + ArgumentList = 8636, + + /// Represents . + ElementAccessClause = 8637, + + /// Represents with as operator token kind. + AddExpression = 8668, + + /// Represents with as operator token kind. + SubtractExpression = 8669, + + /// Represents with as operator token kind. + MultiplyExpression = 8670, + + /// Represents with as operator token kind. + DivideExpression = 8671, + + /// Represents with as operator token kind. + LogicalOrExpression = 8675, + + /// Represents with as operator token kind. + LogicalAndExpression = 8676, + + /// Represents with as operator token kind. + EqualsExpression = 8680, + + /// Represents with as operator token kind. + NotEqualsExpression = 8681, + + /// Represents with as operator token kind. + LessThanExpression = 8682, + + /// Represents with as operator token kind. + LessThanOrEqualExpression = 8683, + + /// Represents with as operator token kind. + GreaterThanExpression = 8684, + + /// Represents with as operator token kind. + GreaterThanOrEqualExpression = 8685, + + /// Represents . + SetStatement = 8714, + + /// Represents with as operator token kind. + UnaryPlusExpression = 8730, + + /// Represents with as operator token kind. + UnaryMinusExpression = 8731, + + /// Represents with as operator token kind. + LogicalNotExpression = 8733, + + /// Represents with as token kind. + RealLiteralExpression = 8749, + + /// Represents with as token kind. + StringLiteralExpression = 8750, + + /// Represents with as token kind. + CharacterLiteralExpression = 8751, + + /// Represents with as token kind. + TrueLiteralExpression = 8752, + + /// Represents with as token kind. + FalseLiteralExpression = 8753, + + /// Represents with as token kind. + NullLiteralExpression = 8754, + + /// Represents with as token kind. + DecimalLiteralExpression = 8755, + + /// Represents with as token kind. + HexadecimalLiteralExpression = 8756, + + /// Represents with as token kind. + OctalLiteralExpression = 8757, + + /// Represents with as token kind. + FourCCLiteralExpression = 8758, + + /// Represents with as declarator kind. + LocalVariableDeclarationStatement = 8793, + + /// Represents with as declarator kind. + LocalArrayDeclarationStatement = 8794, + + /// Represents . + VariableDeclarator = 8795, + + /// Represents . + EqualsValueClause = 8796, + + /// Represents . + ArrayDeclarator = 8797, + + /// Represents . + ExitStatement = 8803, + + /// Represents . + ReturnStatement = 8805, + + /// Represents . + CallStatement = 8808, + + /// Represents . + LoopStatement = 8809, + + /// Represents . + IfStatement = 8819, + + /// Represents . + IfClause = 8820, + + /// Represents . + IfClauseDeclarator = 8821, + + /// Represents . + ElseIfClause = 8822, + + /// Represents . + ElseIfClauseDeclarator = 8823, + + /// Represents . + ElseClause = 8824, + + /// Represents with as statement kind. + DebugSetStatement = 8825, + + /// Represents with as statement kind. + DebugCallStatement = 8826, + + /// Represents with as statement kind. + DebugLoopStatement = 8827, + + /// Represents with as statement kind. + DebugIfStatement = 8828, + + /// Represents . + CompilationUnit = 8840, + + /// Represents . + TypeDeclaration = 8855, + + /// Represents . + NativeFunctionDeclaration = 8859, + + /// Represents . + FunctionDeclaration = 8875, + + /// Represents . + FunctionDeclarator = 8876, + + /// Represents . + ParameterList = 8906, + + /// Represents . + EmptyParameterList = 8907, + + /// Represents . + Parameter = 8908, + + /// Represents . + ReturnClause = 8909, + + /// Represents . + GlobalsDeclaration = 8911, + + /// Represents . + GlobalConstantDeclaration = 8912, + + /// Represents with as declarator kind. + GlobalVariableDeclaration = 8913, + + /// Represents with as declarator kind. + GlobalArrayDeclaration = 8914, + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/JassSyntaxKindFacts.cs b/src/War3Net.CodeAnalysis.Jass/JassSyntaxKindFacts.cs new file mode 100644 index 00000000..3cdd72c7 --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/JassSyntaxKindFacts.cs @@ -0,0 +1,340 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using System; +using System.Collections.Generic; +using System.Linq; + +namespace War3Net.CodeAnalysis.Jass +{ + public static partial class JassSyntaxFacts + { + private static readonly Dictionary _defaultTokenText = GetDefaultTokenText(); + private static readonly Dictionary _textToKind = _defaultTokenText.ToDictionary(pair => pair.Value, pair => pair.Key, StringComparer.Ordinal); + + public static bool IsPunctuation(JassSyntaxKind syntaxKind) + { + return syntaxKind >= JassSyntaxKind.AsteriskToken && syntaxKind <= JassSyntaxKind.GreaterThanEqualsToken; + } + + public static bool IsKeyword(JassSyntaxKind syntaxKind) + { + return syntaxKind >= JassSyntaxKind.BooleanKeyword && syntaxKind <= JassSyntaxKind.NotKeyword; + } + + public static bool IsPredefinedTypeKeyword(JassSyntaxKind syntaxKind) + { + return syntaxKind >= JassSyntaxKind.BooleanKeyword && syntaxKind <= JassSyntaxKind.CodeKeyword; + } + + public static bool IsReservedKeyword(JassSyntaxKind syntaxKind) + { + return syntaxKind >= JassSyntaxKind.NullKeyword && syntaxKind <= JassSyntaxKind.NotKeyword; + } + + internal static bool IsLiteralToken(JassSyntaxKind syntaxKind) + { + return syntaxKind >= JassSyntaxKind.RealLiteralToken && syntaxKind <= JassSyntaxKind.FourCCLiteralToken; + } + + public static bool IsAnyToken(JassSyntaxKind syntaxKind) + { + return syntaxKind >= JassSyntaxKind.AsteriskToken && syntaxKind <= JassSyntaxKind.FourCCLiteralToken; + } + + public static bool IsTrivia(JassSyntaxKind syntaxKind) + { + return syntaxKind >= JassSyntaxKind.NewLineTrivia && syntaxKind <= JassSyntaxKind.SingleLineCommentTrivia; + } + + public static bool IsBinaryExpressionToken(JassSyntaxKind binaryOperatorTokenSyntaxKind) + { + return GetBinaryExpressionKind(binaryOperatorTokenSyntaxKind) != JassSyntaxKind.None; + } + + public static JassSyntaxKind GetBinaryExpressionKind(JassSyntaxKind binaryOperatorTokenSyntaxKind) + { + return binaryOperatorTokenSyntaxKind switch + { + JassSyntaxKind.PlusToken => JassSyntaxKind.AddExpression, + JassSyntaxKind.MinusToken => JassSyntaxKind.SubtractExpression, + JassSyntaxKind.AsteriskToken => JassSyntaxKind.MultiplyExpression, + JassSyntaxKind.SlashToken => JassSyntaxKind.DivideExpression, + JassSyntaxKind.OrKeyword => JassSyntaxKind.LogicalOrExpression, + JassSyntaxKind.AndKeyword => JassSyntaxKind.LogicalAndExpression, + JassSyntaxKind.EqualsEqualsToken => JassSyntaxKind.EqualsExpression, + JassSyntaxKind.ExclamationEqualsToken => JassSyntaxKind.NotEqualsExpression, + JassSyntaxKind.LessThanToken => JassSyntaxKind.LessThanExpression, + JassSyntaxKind.LessThanEqualsToken => JassSyntaxKind.LessThanOrEqualExpression, + JassSyntaxKind.GreaterThanToken => JassSyntaxKind.GreaterThanExpression, + JassSyntaxKind.GreaterThanEqualsToken => JassSyntaxKind.GreaterThanOrEqualExpression, + + _ => JassSyntaxKind.None, + }; + } + + public static bool IsLiteralExpressionToken(JassSyntaxKind literalExpressionTokenSyntaxKind) + { + return GetLiteralExpressionKind(literalExpressionTokenSyntaxKind) != JassSyntaxKind.None; + } + + public static JassSyntaxKind GetLiteralExpressionKind(JassSyntaxKind literalExpressionTokenSyntaxKind) + { + return literalExpressionTokenSyntaxKind switch + { + JassSyntaxKind.RealLiteralToken => JassSyntaxKind.RealLiteralExpression, + JassSyntaxKind.StringLiteralToken => JassSyntaxKind.StringLiteralExpression, + JassSyntaxKind.CharacterLiteralToken => JassSyntaxKind.CharacterLiteralExpression, + JassSyntaxKind.TrueKeyword => JassSyntaxKind.TrueLiteralExpression, + JassSyntaxKind.FalseKeyword => JassSyntaxKind.FalseLiteralExpression, + JassSyntaxKind.NullKeyword => JassSyntaxKind.NullLiteralExpression, + JassSyntaxKind.DecimalLiteralToken => JassSyntaxKind.DecimalLiteralExpression, + JassSyntaxKind.HexadecimalLiteralToken => JassSyntaxKind.HexadecimalLiteralExpression, + JassSyntaxKind.OctalLiteralToken => JassSyntaxKind.OctalLiteralExpression, + JassSyntaxKind.FourCCLiteralToken => JassSyntaxKind.FourCCLiteralExpression, + + _ => JassSyntaxKind.None, + }; + } + + public static bool IsUnaryExpressionToken(JassSyntaxKind unaryOperatorTokenSyntaxKind) + { + return GetUnaryExpressionKind(unaryOperatorTokenSyntaxKind) != JassSyntaxKind.None; + } + + public static JassSyntaxKind GetUnaryExpressionKind(JassSyntaxKind unaryOperatorTokenSyntaxKind) + { + return unaryOperatorTokenSyntaxKind switch + { + JassSyntaxKind.PlusToken => JassSyntaxKind.UnaryPlusExpression, + JassSyntaxKind.MinusToken => JassSyntaxKind.UnaryMinusExpression, + JassSyntaxKind.NotKeyword => JassSyntaxKind.LogicalNotExpression, + + _ => JassSyntaxKind.None, + }; + } + + public static bool IsDebugStatementNode(JassSyntaxKind statementSyntaxKind) + { + return GetDebugStatementKind(statementSyntaxKind) != JassSyntaxKind.None; + } + + public static JassSyntaxKind GetDebugStatementKind(JassSyntaxKind statementSyntaxKind) + { + return statementSyntaxKind switch + { + JassSyntaxKind.SetStatement => JassSyntaxKind.DebugSetStatement, + JassSyntaxKind.CallStatement => JassSyntaxKind.DebugCallStatement, + JassSyntaxKind.LoopStatement => JassSyntaxKind.DebugLoopStatement, + JassSyntaxKind.IfStatement => JassSyntaxKind.DebugIfStatement, + + _ => JassSyntaxKind.None, + }; + } + + public static JassSyntaxKind GetGlobalDeclarationKind(JassSyntaxKind declaratorSyntaxKind) + { + return declaratorSyntaxKind switch + { + JassSyntaxKind.VariableDeclarator => JassSyntaxKind.GlobalVariableDeclaration, + JassSyntaxKind.ArrayDeclarator => JassSyntaxKind.GlobalArrayDeclaration, + + _ => JassSyntaxKind.None, + }; + } + + public static JassSyntaxKind GetLocalDeclarationStatementKind(JassSyntaxKind declaratorSyntaxKind) + { + return declaratorSyntaxKind switch + { + JassSyntaxKind.VariableDeclarator => JassSyntaxKind.LocalVariableDeclarationStatement, + JassSyntaxKind.ArrayDeclarator => JassSyntaxKind.LocalArrayDeclarationStatement, + + _ => JassSyntaxKind.None, + }; + } + + public static IEnumerable GetPunctuationKinds() + { + yield return JassSyntaxKind.AsteriskToken; + yield return JassSyntaxKind.OpenParenToken; + yield return JassSyntaxKind.CloseParenToken; + yield return JassSyntaxKind.MinusToken; + yield return JassSyntaxKind.PlusToken; + yield return JassSyntaxKind.EqualsToken; + yield return JassSyntaxKind.OpenBracketToken; + yield return JassSyntaxKind.CloseBracketToken; + yield return JassSyntaxKind.LessThanToken; + yield return JassSyntaxKind.CommaToken; + yield return JassSyntaxKind.GreaterThanToken; + yield return JassSyntaxKind.SlashToken; + yield return JassSyntaxKind.ExclamationEqualsToken; + yield return JassSyntaxKind.EqualsEqualsToken; + yield return JassSyntaxKind.LessThanEqualsToken; + yield return JassSyntaxKind.GreaterThanEqualsToken; + } + + public static IEnumerable GetKeywordKinds() + { + yield return JassSyntaxKind.BooleanKeyword; + yield return JassSyntaxKind.IntegerKeyword; + yield return JassSyntaxKind.RealKeyword; + yield return JassSyntaxKind.StringKeyword; + yield return JassSyntaxKind.NothingKeyword; + yield return JassSyntaxKind.HandleKeyword; + yield return JassSyntaxKind.NullKeyword; + yield return JassSyntaxKind.TrueKeyword; + yield return JassSyntaxKind.FalseKeyword; + yield return JassSyntaxKind.IfKeyword; + yield return JassSyntaxKind.ElseIfKeyword; + yield return JassSyntaxKind.ThenKeyword; + yield return JassSyntaxKind.ElseKeyword; + yield return JassSyntaxKind.EndIfKeyword; + yield return JassSyntaxKind.LoopKeyword; + yield return JassSyntaxKind.ExitWhenKeyword; + yield return JassSyntaxKind.EndLoopKeyword; + yield return JassSyntaxKind.ReturnKeyword; + yield return JassSyntaxKind.CallKeyword; + yield return JassSyntaxKind.SetKeyword; + yield return JassSyntaxKind.LocalKeyword; + yield return JassSyntaxKind.DebugKeyword; + yield return JassSyntaxKind.ConstantKeyword; + yield return JassSyntaxKind.FunctionKeyword; + yield return JassSyntaxKind.TakesKeyword; + yield return JassSyntaxKind.ReturnsKeyword; + yield return JassSyntaxKind.EndFunctionKeyword; + yield return JassSyntaxKind.NativeKeyword; + yield return JassSyntaxKind.ExtendsKeyword; + yield return JassSyntaxKind.CodeKeyword; + yield return JassSyntaxKind.AliasKeyword; + yield return JassSyntaxKind.ArrayKeyword; + yield return JassSyntaxKind.GlobalsKeyword; + yield return JassSyntaxKind.EndGlobalsKeyword; + yield return JassSyntaxKind.TypeKeyword; + yield return JassSyntaxKind.OrKeyword; + yield return JassSyntaxKind.AndKeyword; + yield return JassSyntaxKind.NotKeyword; + } + + public static IEnumerable GetPredefinedTypeKeywordKinds() + { + yield return JassSyntaxKind.BooleanKeyword; + yield return JassSyntaxKind.IntegerKeyword; + yield return JassSyntaxKind.RealKeyword; + yield return JassSyntaxKind.StringKeyword; + yield return JassSyntaxKind.NothingKeyword; + yield return JassSyntaxKind.HandleKeyword; + yield return JassSyntaxKind.NullKeyword; + } + + public static IEnumerable GetReservedKeywordKinds() + { + yield return JassSyntaxKind.TrueKeyword; + yield return JassSyntaxKind.FalseKeyword; + yield return JassSyntaxKind.IfKeyword; + yield return JassSyntaxKind.ElseIfKeyword; + yield return JassSyntaxKind.ThenKeyword; + yield return JassSyntaxKind.ElseKeyword; + yield return JassSyntaxKind.EndIfKeyword; + yield return JassSyntaxKind.LoopKeyword; + yield return JassSyntaxKind.ExitWhenKeyword; + yield return JassSyntaxKind.EndLoopKeyword; + yield return JassSyntaxKind.ReturnKeyword; + yield return JassSyntaxKind.CallKeyword; + yield return JassSyntaxKind.SetKeyword; + yield return JassSyntaxKind.LocalKeyword; + yield return JassSyntaxKind.DebugKeyword; + yield return JassSyntaxKind.ConstantKeyword; + yield return JassSyntaxKind.FunctionKeyword; + yield return JassSyntaxKind.TakesKeyword; + yield return JassSyntaxKind.ReturnsKeyword; + yield return JassSyntaxKind.EndFunctionKeyword; + yield return JassSyntaxKind.NativeKeyword; + yield return JassSyntaxKind.ExtendsKeyword; + yield return JassSyntaxKind.CodeKeyword; + yield return JassSyntaxKind.AliasKeyword; + yield return JassSyntaxKind.ArrayKeyword; + yield return JassSyntaxKind.GlobalsKeyword; + yield return JassSyntaxKind.EndGlobalsKeyword; + yield return JassSyntaxKind.TypeKeyword; + yield return JassSyntaxKind.OrKeyword; + yield return JassSyntaxKind.AndKeyword; + yield return JassSyntaxKind.NotKeyword; + } + + public static string GetText(JassSyntaxKind syntaxKind) + { + return _defaultTokenText.GetValueOrDefault(syntaxKind, string.Empty); + } + + public static JassSyntaxKind GetSyntaxKind(string text) + { + return _textToKind.GetValueOrDefault(text, JassSyntaxKind.None); + } + + private static Dictionary GetDefaultTokenText() + { + return new Dictionary + { + { JassSyntaxKind.AsteriskToken, JassSymbol.Asterisk }, + { JassSyntaxKind.OpenParenToken, JassSymbol.OpenParen }, + { JassSyntaxKind.CloseParenToken, JassSymbol.CloseParen }, + { JassSyntaxKind.MinusToken, JassSymbol.Minus }, + { JassSyntaxKind.PlusToken, JassSymbol.Plus }, + { JassSyntaxKind.EqualsToken, JassSymbol.Equals }, + { JassSyntaxKind.OpenBracketToken, JassSymbol.OpenBracket }, + { JassSyntaxKind.CloseBracketToken, JassSymbol.CloseBracket }, + { JassSyntaxKind.LessThanToken, JassSymbol.LessThan }, + { JassSyntaxKind.CommaToken, JassSymbol.Comma }, + { JassSyntaxKind.GreaterThanToken, JassSymbol.GreaterThan }, + { JassSyntaxKind.SlashToken, JassSymbol.Slash }, + { JassSyntaxKind.ExclamationEqualsToken, JassSymbol.ExclamationEquals }, + { JassSyntaxKind.EqualsEqualsToken, JassSymbol.EqualsEquals }, + { JassSyntaxKind.LessThanEqualsToken, JassSymbol.LessThanEquals }, + { JassSyntaxKind.GreaterThanEqualsToken, JassSymbol.GreaterThanEquals }, + { JassSyntaxKind.BooleanKeyword, JassKeyword.Boolean }, + { JassSyntaxKind.IntegerKeyword, JassKeyword.Integer }, + { JassSyntaxKind.RealKeyword, JassKeyword.Real }, + { JassSyntaxKind.StringKeyword, JassKeyword.String }, + { JassSyntaxKind.NothingKeyword, JassKeyword.Nothing }, + { JassSyntaxKind.HandleKeyword, JassKeyword.Handle }, + { JassSyntaxKind.NullKeyword, JassKeyword.Null }, + { JassSyntaxKind.TrueKeyword, JassKeyword.True }, + { JassSyntaxKind.FalseKeyword, JassKeyword.False }, + { JassSyntaxKind.IfKeyword, JassKeyword.If }, + { JassSyntaxKind.ElseIfKeyword, JassKeyword.ElseIf }, + { JassSyntaxKind.ThenKeyword, JassKeyword.Then }, + { JassSyntaxKind.ElseKeyword, JassKeyword.Else }, + { JassSyntaxKind.EndIfKeyword, JassKeyword.EndIf }, + { JassSyntaxKind.LoopKeyword, JassKeyword.Loop }, + { JassSyntaxKind.ExitWhenKeyword, JassKeyword.ExitWhen }, + { JassSyntaxKind.EndLoopKeyword, JassKeyword.EndLoop }, + { JassSyntaxKind.ReturnKeyword, JassKeyword.Return }, + { JassSyntaxKind.CallKeyword, JassKeyword.Call }, + { JassSyntaxKind.SetKeyword, JassKeyword.Set }, + { JassSyntaxKind.LocalKeyword, JassKeyword.Local }, + { JassSyntaxKind.DebugKeyword, JassKeyword.Debug }, + { JassSyntaxKind.ConstantKeyword, JassKeyword.Constant }, + { JassSyntaxKind.FunctionKeyword, JassKeyword.Function }, + { JassSyntaxKind.TakesKeyword, JassKeyword.Takes }, + { JassSyntaxKind.ReturnsKeyword, JassKeyword.Returns }, + { JassSyntaxKind.EndFunctionKeyword, JassKeyword.EndFunction }, + { JassSyntaxKind.NativeKeyword, JassKeyword.Native }, + { JassSyntaxKind.ExtendsKeyword, JassKeyword.Extends }, + { JassSyntaxKind.CodeKeyword, JassKeyword.Code }, + { JassSyntaxKind.AliasKeyword, JassKeyword.Alias }, + { JassSyntaxKind.ArrayKeyword, JassKeyword.Array }, + { JassSyntaxKind.GlobalsKeyword, JassKeyword.Globals }, + { JassSyntaxKind.EndGlobalsKeyword, JassKeyword.EndGlobals }, + { JassSyntaxKind.TypeKeyword, JassKeyword.Type }, + { JassSyntaxKind.OrKeyword, JassKeyword.Or }, + { JassSyntaxKind.AndKeyword, JassKeyword.And }, + { JassSyntaxKind.NotKeyword, JassKeyword.Not }, + }; + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/JassSyntaxNormalizer.cs b/src/War3Net.CodeAnalysis.Jass/JassSyntaxNormalizer.cs new file mode 100644 index 00000000..ff2f275b --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/JassSyntaxNormalizer.cs @@ -0,0 +1,52 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using System.Collections.Generic; + +using War3Net.CodeAnalysis.Jass.Syntax; + +namespace War3Net.CodeAnalysis.Jass +{ + internal sealed partial class JassSyntaxNormalizer : JassSyntaxRewriter + { + private readonly List _nodes; + private readonly bool _addSpacesToOuterInvocation; + private readonly bool _trimComments; + private readonly string _indentationString; + + private JassSyntaxToken _previousToken; + private JassSyntaxToken _currentToken; + private JassSyntaxNode? _previousNode; + private JassSyntaxNode? _previousNodeParent; + private JassSyntaxNode? _previousNodeGrandParent; + + private int _currentLevelOfIndentation; + private bool _encounteredAnyTextOnCurrentLine; + private bool _requireNewLineTrivia; + + public JassSyntaxNormalizer( + bool addSpacesToOuterInvocation = true, + bool trimComments = false, + string indentationString = " ") + { + _nodes = new List(); + _addSpacesToOuterInvocation = addSpacesToOuterInvocation; + _trimComments = trimComments; + _indentationString = indentationString; + + _previousToken = new JassSyntaxToken(JassSyntaxTriviaList.Empty, JassSyntaxKind.None, string.Empty, JassSyntaxTriviaList.Empty); + _currentToken = _previousToken; + _previousNode = null; + _previousNodeParent = null; + _previousNodeGrandParent = null; + + _currentLevelOfIndentation = 0; + _encounteredAnyTextOnCurrentLine = false; + _requireNewLineTrivia = false; + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Normalizer/ArgumentListNormalizer.cs b/src/War3Net.CodeAnalysis.Jass/Normalizer/ArgumentListNormalizer.cs new file mode 100644 index 00000000..f33d8655 --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/Normalizer/ArgumentListNormalizer.cs @@ -0,0 +1,24 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using War3Net.CodeAnalysis.Jass.Syntax; + +namespace War3Net.CodeAnalysis.Jass +{ + partial class JassSyntaxNormalizer + { + /// + protected override bool RewriteArgumentList(JassArgumentListSyntax argumentList, out JassArgumentListSyntax result) + { + _nodes.Add(argumentList); + var normalized = base.RewriteArgumentList(argumentList, out result); + _nodes.RemoveAt(_nodes.Count - 1); + + return normalized; + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Normalizer/ArrayDeclaratorNormalizer.cs b/src/War3Net.CodeAnalysis.Jass/Normalizer/ArrayDeclaratorNormalizer.cs new file mode 100644 index 00000000..ddce3ede --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/Normalizer/ArrayDeclaratorNormalizer.cs @@ -0,0 +1,24 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using War3Net.CodeAnalysis.Jass.Syntax; + +namespace War3Net.CodeAnalysis.Jass +{ + partial class JassSyntaxNormalizer + { + /// + protected override bool RewriteArrayDeclarator(JassArrayDeclaratorSyntax arrayDeclarator, out JassVariableOrArrayDeclaratorSyntax result) + { + _nodes.Add(arrayDeclarator); + var normalized = base.RewriteArrayDeclarator(arrayDeclarator, out result); + _nodes.RemoveAt(_nodes.Count - 1); + + return normalized; + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Normalizer/BinaryExpressionNormalizer.cs b/src/War3Net.CodeAnalysis.Jass/Normalizer/BinaryExpressionNormalizer.cs new file mode 100644 index 00000000..a53f9746 --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/Normalizer/BinaryExpressionNormalizer.cs @@ -0,0 +1,24 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using War3Net.CodeAnalysis.Jass.Syntax; + +namespace War3Net.CodeAnalysis.Jass +{ + partial class JassSyntaxNormalizer + { + /// + protected override bool RewriteBinaryExpression(JassBinaryExpressionSyntax binaryExpression, out JassExpressionSyntax result) + { + _nodes.Add(binaryExpression); + var normalized = base.RewriteBinaryExpression(binaryExpression, out result); + _nodes.RemoveAt(_nodes.Count - 1); + + return normalized; + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Normalizer/CallStatementNormalizer.cs b/src/War3Net.CodeAnalysis.Jass/Normalizer/CallStatementNormalizer.cs new file mode 100644 index 00000000..d7230856 --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/Normalizer/CallStatementNormalizer.cs @@ -0,0 +1,26 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using War3Net.CodeAnalysis.Jass.Syntax; + +namespace War3Net.CodeAnalysis.Jass +{ + partial class JassSyntaxNormalizer + { + /// + protected override bool RewriteCallStatement(JassCallStatementSyntax callStatement, out JassStatementSyntax result) + { + _nodes.Add(callStatement); + var normalized = base.RewriteCallStatement(callStatement, out result); + _nodes.RemoveAt(_nodes.Count - 1); + + _requireNewLineTrivia = _encounteredAnyTextOnCurrentLine; + + return normalized; + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Normalizer/CompilationUnitNormalizer.cs b/src/War3Net.CodeAnalysis.Jass/Normalizer/CompilationUnitNormalizer.cs new file mode 100644 index 00000000..9a2e079d --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/Normalizer/CompilationUnitNormalizer.cs @@ -0,0 +1,30 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using War3Net.CodeAnalysis.Jass.Syntax; + +namespace War3Net.CodeAnalysis.Jass +{ + partial class JassSyntaxNormalizer + { + public JassCompilationUnitSyntax NormalizeWhitespace(JassCompilationUnitSyntax compilationUnit) + { + RewriteCompilationUnit(compilationUnit, out var result); + return result; + } + + /// + protected override bool RewriteCompilationUnit(JassCompilationUnitSyntax compilationUnit, out JassCompilationUnitSyntax result) + { + _nodes.Add(compilationUnit); + var normalized = base.RewriteCompilationUnit(compilationUnit, out result); + _nodes.RemoveAt(_nodes.Count - 1); + + return normalized; + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Normalizer/DebugStatementNormalizer.cs b/src/War3Net.CodeAnalysis.Jass/Normalizer/DebugStatementNormalizer.cs new file mode 100644 index 00000000..0f893ea0 --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/Normalizer/DebugStatementNormalizer.cs @@ -0,0 +1,24 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using War3Net.CodeAnalysis.Jass.Syntax; + +namespace War3Net.CodeAnalysis.Jass +{ + partial class JassSyntaxNormalizer + { + /// + protected override bool RewriteDebugStatement(JassDebugStatementSyntax debugStatement, out JassStatementSyntax result) + { + _nodes.Add(debugStatement); + var normalized = base.RewriteDebugStatement(debugStatement, out result); + _nodes.RemoveAt(_nodes.Count - 1); + + return normalized; + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Normalizer/ElementAccessClauseNormalizer.cs b/src/War3Net.CodeAnalysis.Jass/Normalizer/ElementAccessClauseNormalizer.cs new file mode 100644 index 00000000..813ff1ba --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/Normalizer/ElementAccessClauseNormalizer.cs @@ -0,0 +1,32 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using System.Diagnostics.CodeAnalysis; + +using War3Net.CodeAnalysis.Jass.Syntax; + +namespace War3Net.CodeAnalysis.Jass +{ + partial class JassSyntaxNormalizer + { + /// + protected override bool RewriteElementAccessClause(JassElementAccessClauseSyntax? elementAccessClause, [NotNullIfNotNull("elementAccessClause")] out JassElementAccessClauseSyntax? result) + { + if (elementAccessClause is null) + { + result = null; + return false; + } + + _nodes.Add(elementAccessClause); + var normalized = base.RewriteElementAccessClause(elementAccessClause, out result); + _nodes.RemoveAt(_nodes.Count - 1); + + return normalized; + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Normalizer/ElementAccessExpressionNormalizer.cs b/src/War3Net.CodeAnalysis.Jass/Normalizer/ElementAccessExpressionNormalizer.cs new file mode 100644 index 00000000..7371f438 --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/Normalizer/ElementAccessExpressionNormalizer.cs @@ -0,0 +1,24 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using War3Net.CodeAnalysis.Jass.Syntax; + +namespace War3Net.CodeAnalysis.Jass +{ + partial class JassSyntaxNormalizer + { + /// + protected override bool RewriteElementAccessExpression(JassElementAccessExpressionSyntax elementAccessExpression, out JassExpressionSyntax result) + { + _nodes.Add(elementAccessExpression); + var normalized = base.RewriteElementAccessExpression(elementAccessExpression, out result); + _nodes.RemoveAt(_nodes.Count - 1); + + return normalized; + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Normalizer/ElseClauseNormalizer.cs b/src/War3Net.CodeAnalysis.Jass/Normalizer/ElseClauseNormalizer.cs new file mode 100644 index 00000000..f241a55b --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/Normalizer/ElseClauseNormalizer.cs @@ -0,0 +1,32 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using System.Diagnostics.CodeAnalysis; + +using War3Net.CodeAnalysis.Jass.Syntax; + +namespace War3Net.CodeAnalysis.Jass +{ + partial class JassSyntaxNormalizer + { + /// + protected override bool RewriteElseClause(JassElseClauseSyntax? elseClause, [NotNullIfNotNull("elseClause")] out JassElseClauseSyntax? result) + { + if (elseClause is null) + { + result = null; + return false; + } + + _nodes.Add(elseClause); + var normalized = base.RewriteElseClause(elseClause, out result); + _nodes.RemoveAt(_nodes.Count - 1); + + return normalized; + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Normalizer/ElseIfClauseDeclaratorNormalizer.cs b/src/War3Net.CodeAnalysis.Jass/Normalizer/ElseIfClauseDeclaratorNormalizer.cs new file mode 100644 index 00000000..9a0dd3c2 --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/Normalizer/ElseIfClauseDeclaratorNormalizer.cs @@ -0,0 +1,24 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using War3Net.CodeAnalysis.Jass.Syntax; + +namespace War3Net.CodeAnalysis.Jass +{ + partial class JassSyntaxNormalizer + { + /// + protected override bool RewriteElseIfClauseDeclarator(JassElseIfClauseDeclaratorSyntax elseIfClauseDeclarator, out JassElseIfClauseDeclaratorSyntax result) + { + _nodes.Add(elseIfClauseDeclarator); + var normalized = base.RewriteElseIfClauseDeclarator(elseIfClauseDeclarator, out result); + _nodes.RemoveAt(_nodes.Count - 1); + + return normalized; + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Normalizer/ElseIfClauseNormalizer.cs b/src/War3Net.CodeAnalysis.Jass/Normalizer/ElseIfClauseNormalizer.cs new file mode 100644 index 00000000..2e284252 --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/Normalizer/ElseIfClauseNormalizer.cs @@ -0,0 +1,24 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using War3Net.CodeAnalysis.Jass.Syntax; + +namespace War3Net.CodeAnalysis.Jass +{ + partial class JassSyntaxNormalizer + { + /// + protected override bool RewriteElseIfClause(JassElseIfClauseSyntax elseIfClause, out JassElseIfClauseSyntax result) + { + _nodes.Add(elseIfClause); + var normalized = base.RewriteElseIfClause(elseIfClause, out result); + _nodes.RemoveAt(_nodes.Count - 1); + + return normalized; + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Normalizer/EmptyParameterListNormalizer.cs b/src/War3Net.CodeAnalysis.Jass/Normalizer/EmptyParameterListNormalizer.cs new file mode 100644 index 00000000..b83f348c --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/Normalizer/EmptyParameterListNormalizer.cs @@ -0,0 +1,24 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using War3Net.CodeAnalysis.Jass.Syntax; + +namespace War3Net.CodeAnalysis.Jass +{ + partial class JassSyntaxNormalizer + { + /// + protected override bool RewriteEmptyParameterList(JassEmptyParameterListSyntax emptyParameterList, out JassParameterListOrEmptyParameterListSyntax result) + { + _nodes.Add(emptyParameterList); + var normalized = base.RewriteEmptyParameterList(emptyParameterList, out result); + _nodes.RemoveAt(_nodes.Count - 1); + + return normalized; + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Normalizer/EqualsValueClauseNormalizer.cs b/src/War3Net.CodeAnalysis.Jass/Normalizer/EqualsValueClauseNormalizer.cs new file mode 100644 index 00000000..801c31dc --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/Normalizer/EqualsValueClauseNormalizer.cs @@ -0,0 +1,32 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using System.Diagnostics.CodeAnalysis; + +using War3Net.CodeAnalysis.Jass.Syntax; + +namespace War3Net.CodeAnalysis.Jass +{ + partial class JassSyntaxNormalizer + { + /// + protected override bool RewriteEqualsValueClause(JassEqualsValueClauseSyntax? equalsValueClause, [NotNullIfNotNull("equalsValueClause")] out JassEqualsValueClauseSyntax? result) + { + if (equalsValueClause is null) + { + result = null; + return false; + } + + _nodes.Add(equalsValueClause); + var normalized = base.RewriteEqualsValueClause(equalsValueClause, out result); + _nodes.RemoveAt(_nodes.Count - 1); + + return normalized; + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Normalizer/ExitStatementNormalizer.cs b/src/War3Net.CodeAnalysis.Jass/Normalizer/ExitStatementNormalizer.cs new file mode 100644 index 00000000..2b845d05 --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/Normalizer/ExitStatementNormalizer.cs @@ -0,0 +1,26 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using War3Net.CodeAnalysis.Jass.Syntax; + +namespace War3Net.CodeAnalysis.Jass +{ + partial class JassSyntaxNormalizer + { + /// + protected override bool RewriteExitStatement(JassExitStatementSyntax exitStatement, out JassStatementSyntax result) + { + _nodes.Add(exitStatement); + var normalized = base.RewriteExitStatement(exitStatement, out result); + _nodes.RemoveAt(_nodes.Count - 1); + + _requireNewLineTrivia = _encounteredAnyTextOnCurrentLine; + + return normalized; + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Normalizer/FunctionDeclarationNormalizer.cs b/src/War3Net.CodeAnalysis.Jass/Normalizer/FunctionDeclarationNormalizer.cs new file mode 100644 index 00000000..194a39eb --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/Normalizer/FunctionDeclarationNormalizer.cs @@ -0,0 +1,24 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using War3Net.CodeAnalysis.Jass.Syntax; + +namespace War3Net.CodeAnalysis.Jass +{ + partial class JassSyntaxNormalizer + { + /// + protected override bool RewriteFunctionDeclaration(JassFunctionDeclarationSyntax functionDeclaration, out JassTopLevelDeclarationSyntax result) + { + _nodes.Add(functionDeclaration); + var normalized = base.RewriteFunctionDeclaration(functionDeclaration, out result); + _nodes.RemoveAt(_nodes.Count - 1); + + return normalized; + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Normalizer/FunctionDeclaratorNormalizer.cs b/src/War3Net.CodeAnalysis.Jass/Normalizer/FunctionDeclaratorNormalizer.cs new file mode 100644 index 00000000..76fa8cc5 --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/Normalizer/FunctionDeclaratorNormalizer.cs @@ -0,0 +1,27 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using War3Net.CodeAnalysis.Jass.Syntax; + +namespace War3Net.CodeAnalysis.Jass +{ + partial class JassSyntaxNormalizer + { + /// + protected override bool RewriteFunctionDeclarator(JassFunctionDeclaratorSyntax functionDeclarator, out JassFunctionDeclaratorSyntax result) + { + _nodes.Add(functionDeclarator); + var normalized = base.RewriteFunctionDeclarator(functionDeclarator, out result); + _nodes.RemoveAt(_nodes.Count - 1); + + _currentLevelOfIndentation++; + _requireNewLineTrivia = _encounteredAnyTextOnCurrentLine; + + return normalized; + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Normalizer/FunctionReferenceExpressionNormalizer.cs b/src/War3Net.CodeAnalysis.Jass/Normalizer/FunctionReferenceExpressionNormalizer.cs new file mode 100644 index 00000000..d5eed392 --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/Normalizer/FunctionReferenceExpressionNormalizer.cs @@ -0,0 +1,24 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using War3Net.CodeAnalysis.Jass.Syntax; + +namespace War3Net.CodeAnalysis.Jass +{ + partial class JassSyntaxNormalizer + { + /// + protected override bool RewriteFunctionReferenceExpression(JassFunctionReferenceExpressionSyntax functionReferenceExpression, out JassExpressionSyntax result) + { + _nodes.Add(functionReferenceExpression); + var normalized = base.RewriteFunctionReferenceExpression(functionReferenceExpression, out result); + _nodes.RemoveAt(_nodes.Count - 1); + + return normalized; + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Normalizer/GlobalConstantDeclarationNormalizer.cs b/src/War3Net.CodeAnalysis.Jass/Normalizer/GlobalConstantDeclarationNormalizer.cs new file mode 100644 index 00000000..2942ec3f --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/Normalizer/GlobalConstantDeclarationNormalizer.cs @@ -0,0 +1,26 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using War3Net.CodeAnalysis.Jass.Syntax; + +namespace War3Net.CodeAnalysis.Jass +{ + partial class JassSyntaxNormalizer + { + /// + protected override bool RewriteGlobalConstantDeclaration(JassGlobalConstantDeclarationSyntax globalConstantDeclaration, out JassGlobalDeclarationSyntax result) + { + _nodes.Add(globalConstantDeclaration); + var normalized = base.RewriteGlobalConstantDeclaration(globalConstantDeclaration, out result); + _nodes.RemoveAt(_nodes.Count - 1); + + _requireNewLineTrivia = _encounteredAnyTextOnCurrentLine; + + return normalized; + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Normalizer/GlobalVariableDeclarationNormalizer.cs b/src/War3Net.CodeAnalysis.Jass/Normalizer/GlobalVariableDeclarationNormalizer.cs new file mode 100644 index 00000000..d76fa00d --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/Normalizer/GlobalVariableDeclarationNormalizer.cs @@ -0,0 +1,26 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using War3Net.CodeAnalysis.Jass.Syntax; + +namespace War3Net.CodeAnalysis.Jass +{ + partial class JassSyntaxNormalizer + { + /// + protected override bool RewriteGlobalVariableDeclaration(JassGlobalVariableDeclarationSyntax globalVariableDeclaration, out JassGlobalDeclarationSyntax result) + { + _nodes.Add(globalVariableDeclaration); + var normalized = base.RewriteGlobalVariableDeclaration(globalVariableDeclaration, out result); + _nodes.RemoveAt(_nodes.Count - 1); + + _requireNewLineTrivia = _encounteredAnyTextOnCurrentLine; + + return normalized; + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Normalizer/GlobalsDeclarationNormalizer.cs b/src/War3Net.CodeAnalysis.Jass/Normalizer/GlobalsDeclarationNormalizer.cs new file mode 100644 index 00000000..cfb385d4 --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/Normalizer/GlobalsDeclarationNormalizer.cs @@ -0,0 +1,24 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using War3Net.CodeAnalysis.Jass.Syntax; + +namespace War3Net.CodeAnalysis.Jass +{ + partial class JassSyntaxNormalizer + { + /// + protected override bool RewriteGlobalsDeclaration(JassGlobalsDeclarationSyntax globalsDeclaration, out JassTopLevelDeclarationSyntax result) + { + _nodes.Add(globalsDeclaration); + var normalized = base.RewriteGlobalsDeclaration(globalsDeclaration, out result); + _nodes.RemoveAt(_nodes.Count - 1); + + return normalized; + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Normalizer/IdentifierNameNormalizer.cs b/src/War3Net.CodeAnalysis.Jass/Normalizer/IdentifierNameNormalizer.cs new file mode 100644 index 00000000..60338e59 --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/Normalizer/IdentifierNameNormalizer.cs @@ -0,0 +1,44 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using War3Net.CodeAnalysis.Jass.Syntax; + +namespace War3Net.CodeAnalysis.Jass +{ + partial class JassSyntaxNormalizer + { + /// + protected override bool RewriteIdentifierName(JassIdentifierNameSyntax identifierName, out JassIdentifierNameSyntax result) + { + _nodes.Add(identifierName); + var normalized = base.RewriteIdentifierName(identifierName, out result); + _nodes.RemoveAt(_nodes.Count - 1); + + return normalized; + } + + /// + protected override bool RewriteIdentifierNameAsExpression(JassIdentifierNameSyntax identifierName, out JassExpressionSyntax result) + { + _nodes.Add(identifierName); + var normalized = base.RewriteIdentifierNameAsExpression(identifierName, out result); + _nodes.RemoveAt(_nodes.Count - 1); + + return normalized; + } + + /// + protected override bool RewriteIdentifierNameAsType(JassIdentifierNameSyntax identifierName, out JassTypeSyntax result) + { + _nodes.Add(identifierName); + var normalized = base.RewriteIdentifierNameAsType(identifierName, out result); + _nodes.RemoveAt(_nodes.Count - 1); + + return normalized; + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Normalizer/IfClauseDeclaratorNormalizer.cs b/src/War3Net.CodeAnalysis.Jass/Normalizer/IfClauseDeclaratorNormalizer.cs new file mode 100644 index 00000000..1a46828d --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/Normalizer/IfClauseDeclaratorNormalizer.cs @@ -0,0 +1,24 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using War3Net.CodeAnalysis.Jass.Syntax; + +namespace War3Net.CodeAnalysis.Jass +{ + partial class JassSyntaxNormalizer + { + /// + protected override bool RewriteIfClauseDeclarator(JassIfClauseDeclaratorSyntax ifClauseDeclarator, out JassIfClauseDeclaratorSyntax result) + { + _nodes.Add(ifClauseDeclarator); + var normalized = base.RewriteIfClauseDeclarator(ifClauseDeclarator, out result); + _nodes.RemoveAt(_nodes.Count - 1); + + return normalized; + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Normalizer/IfClauseNormalizer.cs b/src/War3Net.CodeAnalysis.Jass/Normalizer/IfClauseNormalizer.cs new file mode 100644 index 00000000..40c2c78e --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/Normalizer/IfClauseNormalizer.cs @@ -0,0 +1,24 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using War3Net.CodeAnalysis.Jass.Syntax; + +namespace War3Net.CodeAnalysis.Jass +{ + partial class JassSyntaxNormalizer + { + /// + protected override bool RewriteIfClause(JassIfClauseSyntax ifClause, out JassIfClauseSyntax result) + { + _nodes.Add(ifClause); + var normalized = base.RewriteIfClause(ifClause, out result); + _nodes.RemoveAt(_nodes.Count - 1); + + return normalized; + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Normalizer/IfStatementNormalizer.cs b/src/War3Net.CodeAnalysis.Jass/Normalizer/IfStatementNormalizer.cs new file mode 100644 index 00000000..ffdb9233 --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/Normalizer/IfStatementNormalizer.cs @@ -0,0 +1,24 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using War3Net.CodeAnalysis.Jass.Syntax; + +namespace War3Net.CodeAnalysis.Jass +{ + partial class JassSyntaxNormalizer + { + /// + protected override bool RewriteIfStatement(JassIfStatementSyntax ifStatement, out JassStatementSyntax result) + { + _nodes.Add(ifStatement); + var normalized = base.RewriteIfStatement(ifStatement, out result); + _nodes.RemoveAt(_nodes.Count - 1); + + return normalized; + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Normalizer/InvocationExpressionNormalizer.cs b/src/War3Net.CodeAnalysis.Jass/Normalizer/InvocationExpressionNormalizer.cs new file mode 100644 index 00000000..db2818bd --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/Normalizer/InvocationExpressionNormalizer.cs @@ -0,0 +1,24 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using War3Net.CodeAnalysis.Jass.Syntax; + +namespace War3Net.CodeAnalysis.Jass +{ + partial class JassSyntaxNormalizer + { + /// + protected override bool RewriteInvocationExpression(JassInvocationExpressionSyntax invocationExpression, out JassExpressionSyntax result) + { + _nodes.Add(invocationExpression); + var normalized = base.RewriteInvocationExpression(invocationExpression, out result); + _nodes.RemoveAt(_nodes.Count - 1); + + return normalized; + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Normalizer/LiteralExpressionNormalizer.cs b/src/War3Net.CodeAnalysis.Jass/Normalizer/LiteralExpressionNormalizer.cs new file mode 100644 index 00000000..32219209 --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/Normalizer/LiteralExpressionNormalizer.cs @@ -0,0 +1,24 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using War3Net.CodeAnalysis.Jass.Syntax; + +namespace War3Net.CodeAnalysis.Jass +{ + partial class JassSyntaxNormalizer + { + /// + protected override bool RewriteLiteralExpression(JassLiteralExpressionSyntax literalExpression, out JassExpressionSyntax result) + { + _nodes.Add(literalExpression); + var normalized = base.RewriteLiteralExpression(literalExpression, out result); + _nodes.RemoveAt(_nodes.Count - 1); + + return normalized; + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Normalizer/LocalVariableDeclarationStatementNormalizer.cs b/src/War3Net.CodeAnalysis.Jass/Normalizer/LocalVariableDeclarationStatementNormalizer.cs new file mode 100644 index 00000000..ca891893 --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/Normalizer/LocalVariableDeclarationStatementNormalizer.cs @@ -0,0 +1,26 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using War3Net.CodeAnalysis.Jass.Syntax; + +namespace War3Net.CodeAnalysis.Jass +{ + partial class JassSyntaxNormalizer + { + /// + protected override bool RewriteLocalVariableDeclarationStatement(JassLocalVariableDeclarationStatementSyntax localVariableDeclarationStatement, out JassStatementSyntax result) + { + _nodes.Add(localVariableDeclarationStatement); + var normalized = base.RewriteLocalVariableDeclarationStatement(localVariableDeclarationStatement, out result); + _nodes.RemoveAt(_nodes.Count - 1); + + _requireNewLineTrivia = _encounteredAnyTextOnCurrentLine; + + return normalized; + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Normalizer/LoopStatementNormalizer.cs b/src/War3Net.CodeAnalysis.Jass/Normalizer/LoopStatementNormalizer.cs new file mode 100644 index 00000000..3b48e1f9 --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/Normalizer/LoopStatementNormalizer.cs @@ -0,0 +1,24 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using War3Net.CodeAnalysis.Jass.Syntax; + +namespace War3Net.CodeAnalysis.Jass +{ + partial class JassSyntaxNormalizer + { + /// + protected override bool RewriteLoopStatement(JassLoopStatementSyntax loopStatement, out JassStatementSyntax result) + { + _nodes.Add(loopStatement); + var normalized = base.RewriteLoopStatement(loopStatement, out result); + _nodes.RemoveAt(_nodes.Count - 1); + + return normalized; + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Normalizer/NativeFunctionDeclarationNormalizer.cs b/src/War3Net.CodeAnalysis.Jass/Normalizer/NativeFunctionDeclarationNormalizer.cs new file mode 100644 index 00000000..0592bc5e --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/Normalizer/NativeFunctionDeclarationNormalizer.cs @@ -0,0 +1,26 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using War3Net.CodeAnalysis.Jass.Syntax; + +namespace War3Net.CodeAnalysis.Jass +{ + partial class JassSyntaxNormalizer + { + /// + protected override bool RewriteNativeFunctionDeclaration(JassNativeFunctionDeclarationSyntax nativeFunctionDeclaration, out JassTopLevelDeclarationSyntax result) + { + _nodes.Add(nativeFunctionDeclaration); + var normalized = base.RewriteNativeFunctionDeclaration(nativeFunctionDeclaration, out result); + _nodes.RemoveAt(_nodes.Count - 1); + + _requireNewLineTrivia = _encounteredAnyTextOnCurrentLine; + + return normalized; + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Normalizer/ParameterListNormalizer.cs b/src/War3Net.CodeAnalysis.Jass/Normalizer/ParameterListNormalizer.cs new file mode 100644 index 00000000..c034da41 --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/Normalizer/ParameterListNormalizer.cs @@ -0,0 +1,24 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using War3Net.CodeAnalysis.Jass.Syntax; + +namespace War3Net.CodeAnalysis.Jass +{ + partial class JassSyntaxNormalizer + { + /// + protected override bool RewriteParameterList(JassParameterListSyntax parameterList, out JassParameterListOrEmptyParameterListSyntax result) + { + _nodes.Add(parameterList); + var normalized = base.RewriteParameterList(parameterList, out result); + _nodes.RemoveAt(_nodes.Count - 1); + + return normalized; + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Normalizer/ParameterNormalizer.cs b/src/War3Net.CodeAnalysis.Jass/Normalizer/ParameterNormalizer.cs new file mode 100644 index 00000000..17198a68 --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/Normalizer/ParameterNormalizer.cs @@ -0,0 +1,24 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using War3Net.CodeAnalysis.Jass.Syntax; + +namespace War3Net.CodeAnalysis.Jass +{ + partial class JassSyntaxNormalizer + { + /// + protected override bool RewriteParameter(JassParameterSyntax parameter, out JassParameterSyntax result) + { + _nodes.Add(parameter); + var normalized = base.RewriteParameter(parameter, out result); + _nodes.RemoveAt(_nodes.Count - 1); + + return normalized; + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Normalizer/ParenthesizedExpressionNormalizer.cs b/src/War3Net.CodeAnalysis.Jass/Normalizer/ParenthesizedExpressionNormalizer.cs new file mode 100644 index 00000000..523a8cb8 --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/Normalizer/ParenthesizedExpressionNormalizer.cs @@ -0,0 +1,24 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using War3Net.CodeAnalysis.Jass.Syntax; + +namespace War3Net.CodeAnalysis.Jass +{ + partial class JassSyntaxNormalizer + { + /// + protected override bool RewriteParenthesizedExpression(JassParenthesizedExpressionSyntax parenthesizedExpression, out JassExpressionSyntax result) + { + _nodes.Add(parenthesizedExpression); + var normalized = base.RewriteParenthesizedExpression(parenthesizedExpression, out result); + _nodes.RemoveAt(_nodes.Count - 1); + + return normalized; + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Normalizer/PredefinedTypeNormalizer.cs b/src/War3Net.CodeAnalysis.Jass/Normalizer/PredefinedTypeNormalizer.cs new file mode 100644 index 00000000..d638c3f6 --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/Normalizer/PredefinedTypeNormalizer.cs @@ -0,0 +1,24 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using War3Net.CodeAnalysis.Jass.Syntax; + +namespace War3Net.CodeAnalysis.Jass +{ + partial class JassSyntaxNormalizer + { + /// + protected override bool RewritePredefinedType(JassPredefinedTypeSyntax predefinedType, out JassTypeSyntax result) + { + _nodes.Add(predefinedType); + var normalized = base.RewritePredefinedType(predefinedType, out result); + _nodes.RemoveAt(_nodes.Count - 1); + + return normalized; + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Normalizer/ReturnClauseNormalizer.cs b/src/War3Net.CodeAnalysis.Jass/Normalizer/ReturnClauseNormalizer.cs new file mode 100644 index 00000000..640f5e12 --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/Normalizer/ReturnClauseNormalizer.cs @@ -0,0 +1,24 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using War3Net.CodeAnalysis.Jass.Syntax; + +namespace War3Net.CodeAnalysis.Jass +{ + partial class JassSyntaxNormalizer + { + /// + protected override bool RewriteReturnClause(JassReturnClauseSyntax returnClause, out JassReturnClauseSyntax result) + { + _nodes.Add(returnClause); + var normalized = base.RewriteReturnClause(returnClause, out result); + _nodes.RemoveAt(_nodes.Count - 1); + + return normalized; + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Normalizer/ReturnStatementNormalizer.cs b/src/War3Net.CodeAnalysis.Jass/Normalizer/ReturnStatementNormalizer.cs new file mode 100644 index 00000000..b9fd90a1 --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/Normalizer/ReturnStatementNormalizer.cs @@ -0,0 +1,26 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using War3Net.CodeAnalysis.Jass.Syntax; + +namespace War3Net.CodeAnalysis.Jass +{ + partial class JassSyntaxNormalizer + { + /// + protected override bool RewriteReturnStatement(JassReturnStatementSyntax returnStatement, out JassStatementSyntax result) + { + _nodes.Add(returnStatement); + var normalized = base.RewriteReturnStatement(returnStatement, out result); + _nodes.RemoveAt(_nodes.Count - 1); + + _requireNewLineTrivia = _encounteredAnyTextOnCurrentLine; + + return normalized; + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Normalizer/SetStatementNormalizer.cs b/src/War3Net.CodeAnalysis.Jass/Normalizer/SetStatementNormalizer.cs new file mode 100644 index 00000000..5b5ba385 --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/Normalizer/SetStatementNormalizer.cs @@ -0,0 +1,26 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using War3Net.CodeAnalysis.Jass.Syntax; + +namespace War3Net.CodeAnalysis.Jass +{ + partial class JassSyntaxNormalizer + { + /// + protected override bool RewriteSetStatement(JassSetStatementSyntax setStatement, out JassStatementSyntax result) + { + _nodes.Add(setStatement); + var normalized = base.RewriteSetStatement(setStatement, out result); + _nodes.RemoveAt(_nodes.Count - 1); + + _requireNewLineTrivia = _encounteredAnyTextOnCurrentLine; + + return normalized; + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Normalizer/SyntaxTokenNormalizer.cs b/src/War3Net.CodeAnalysis.Jass/Normalizer/SyntaxTokenNormalizer.cs new file mode 100644 index 00000000..0f7e59f9 --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/Normalizer/SyntaxTokenNormalizer.cs @@ -0,0 +1,116 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; + +using War3Net.CodeAnalysis.Jass.Syntax; + +namespace War3Net.CodeAnalysis.Jass +{ + partial class JassSyntaxNormalizer + { + private static readonly HashSet _increaseIndentationSyntaxKinds = GetIncreaseIndentationSyntaxKinds(); + private static readonly HashSet _decreaseIndentationSyntaxKinds = GetDecreaseIndentationSyntaxKinds(); + private static readonly HashSet _requireNewLineSyntaxKinds = GetRequireNewLineSyntaxKinds(); + + /// + protected override bool RewriteToken(JassSyntaxToken? token, [NotNullIfNotNull("token")] out JassSyntaxToken? result) + { + if (token is null) + { + result = null; + return false; + } + + _currentToken = token; + + if (_decreaseIndentationSyntaxKinds.Contains(_currentToken.SyntaxKind)) + { + _currentLevelOfIndentation--; + } + + var normalizedLeadingTrivia = RewriteLeadingTrivia(token.LeadingTrivia, out var leadingTrivia); + + if (_requireNewLineSyntaxKinds.Contains(_currentToken.SyntaxKind)) + { + _requireNewLineTrivia = true; + } + + if (_increaseIndentationSyntaxKinds.Contains(_currentToken.SyntaxKind)) + { + _currentLevelOfIndentation++; + } + + var normalizedTrailingTrivia = RewriteTrailingTrivia(token.TrailingTrivia, out var trailingTrivia); + + if (normalizedLeadingTrivia || normalizedTrailingTrivia) + { + result = new JassSyntaxToken( + leadingTrivia, + token.SyntaxKind, + token.Text, + trailingTrivia); + + _previousToken = result; + _previousNode = _nodes[^1]; + _previousNodeParent = _nodes.Count > 1 ? _nodes[^2] : null; + _previousNodeGrandParent = _nodes.Count > 2 ? _nodes[^3] : null; + + return true; + } + + result = token; + + _previousToken = token; + _previousNode = _nodes[^1]; + _previousNodeParent = _nodes.Count > 1 ? _nodes[^2] : null; + _previousNodeGrandParent = _nodes.Count > 2 ? _nodes[^3] : null; + + return false; + } + + private static HashSet GetIncreaseIndentationSyntaxKinds() + { + return new HashSet + { + JassSyntaxKind.ElseKeyword, + JassSyntaxKind.GlobalsKeyword, + JassSyntaxKind.LoopKeyword, + JassSyntaxKind.ThenKeyword, + }; + } + + private static HashSet GetDecreaseIndentationSyntaxKinds() + { + return new HashSet + { + JassSyntaxKind.ElseIfKeyword, + JassSyntaxKind.ElseKeyword, + JassSyntaxKind.EndFunctionKeyword, + JassSyntaxKind.EndGlobalsKeyword, + JassSyntaxKind.EndIfKeyword, + JassSyntaxKind.EndLoopKeyword, + }; + } + + private static HashSet GetRequireNewLineSyntaxKinds() + { + return new HashSet + { + JassSyntaxKind.ElseKeyword, + JassSyntaxKind.EndFunctionKeyword, + JassSyntaxKind.EndGlobalsKeyword, + JassSyntaxKind.EndIfKeyword, + JassSyntaxKind.EndLoopKeyword, + JassSyntaxKind.GlobalsKeyword, + JassSyntaxKind.LoopKeyword, + JassSyntaxKind.ThenKeyword, + }; + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Normalizer/SyntaxTriviaListNormalizer.cs b/src/War3Net.CodeAnalysis.Jass/Normalizer/SyntaxTriviaListNormalizer.cs new file mode 100644 index 00000000..ec0b14e3 --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/Normalizer/SyntaxTriviaListNormalizer.cs @@ -0,0 +1,202 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using System.Collections.Immutable; +using System.Linq; + +using War3Net.CodeAnalysis.Jass.Syntax; + +namespace War3Net.CodeAnalysis.Jass +{ + partial class JassSyntaxNormalizer + { + /// + protected override bool RewriteLeadingTrivia(JassSyntaxTriviaList triviaList, out JassSyntaxTriviaList result) + { + var triviaBuilder = ImmutableArray.CreateBuilder(); + + if (_requireNewLineTrivia) + { + triviaBuilder.Add(JassSyntaxTrivia.NewLine); + _encounteredAnyTextOnCurrentLine = false; + _requireNewLineTrivia = false; + } + + HandleExistingTrivia(triviaList, triviaBuilder); + + if (_encounteredAnyTextOnCurrentLine) + { + var requireSpace = true; + + if (_previousToken.SyntaxKind == JassSyntaxKind.OpenBracketToken || + _currentToken.SyntaxKind == JassSyntaxKind.OpenBracketToken || + _currentToken.SyntaxKind == JassSyntaxKind.CloseBracketToken || + _currentToken.SyntaxKind == JassSyntaxKind.CommaToken) + { + requireSpace = false; + } + else + { + var currentNode = _nodes[^1]; + if (currentNode is not null) + { + if (_currentToken.SyntaxKind == JassSyntaxKind.OpenParenToken) + { + requireSpace = currentNode.SyntaxKind == JassSyntaxKind.ParenthesizedExpression; + } + else if (_currentToken.SyntaxKind == JassSyntaxKind.CloseParenToken) + { + if (_addSpacesToOuterInvocation && + currentNode.SyntaxKind == JassSyntaxKind.ArgumentList && + _nodes.Count > 1) + { + var currentNodeParent = _nodes[^2]; + + if (currentNodeParent.SyntaxKind == JassSyntaxKind.CallStatement) + { + requireSpace = true; + } + else if (currentNodeParent.SyntaxKind == JassSyntaxKind.InvocationExpression && _nodes.Count > 2) + { + var currentNodeGrandParent = _nodes[^3]; + requireSpace = currentNodeGrandParent.SyntaxKind == JassSyntaxKind.EqualsValueClause; + } + else + { + requireSpace = false; + } + } + else + { + requireSpace = false; + } + } + } + + if (_previousNode is not null) + { + if (_previousNode.SyntaxKind == JassSyntaxKind.UnaryPlusExpression || + _previousNode.SyntaxKind == JassSyntaxKind.UnaryMinusExpression) + { + requireSpace = false; + } + else if (_previousToken.SyntaxKind == JassSyntaxKind.OpenParenToken) + { + if (_addSpacesToOuterInvocation && + _previousNode.SyntaxKind == JassSyntaxKind.ArgumentList && + _previousNodeParent is not null) + { + if (_previousNodeParent.SyntaxKind == JassSyntaxKind.CallStatement) + { + requireSpace = true; + if (_currentToken.SyntaxKind == JassSyntaxKind.CloseParenToken) + { + requireSpace = false; + triviaBuilder.Add(JassSyntaxFactory.WhitespaceTrivia(" ")); + } + } + else if (_previousNodeParent.SyntaxKind == JassSyntaxKind.InvocationExpression && + _previousNodeGrandParent is not null && + _previousNodeGrandParent.SyntaxKind == JassSyntaxKind.EqualsValueClause) + { + requireSpace = true; + if (_currentToken.SyntaxKind == JassSyntaxKind.CloseParenToken) + { + requireSpace = false; + triviaBuilder.Add(JassSyntaxFactory.WhitespaceTrivia(" ")); + } + } + else + { + requireSpace = false; + } + } + else + { + requireSpace = false; + } + } + } + } + + if (requireSpace) + { + triviaBuilder.Add(JassSyntaxTrivia.SingleSpace); + } + } + else if (!string.IsNullOrEmpty(_currentToken.Text)) + { + _encounteredAnyTextOnCurrentLine = true; + if (_currentLevelOfIndentation > 0) + { + triviaBuilder.Add(JassSyntaxFactory.WhitespaceTrivia(string.Concat(Enumerable.Repeat(_indentationString, _currentLevelOfIndentation)))); + } + } + + result = JassSyntaxFactory.SyntaxTriviaList(triviaBuilder.ToImmutable()); + return true; + } + + /// + protected override bool RewriteTrailingTrivia(JassSyntaxTriviaList triviaList, out JassSyntaxTriviaList result) + { + var triviaBuilder = ImmutableArray.CreateBuilder(); + + HandleExistingTrivia(triviaList, triviaBuilder); + + result = JassSyntaxFactory.SyntaxTriviaList(triviaBuilder.ToImmutable()); + return true; + } + + private void HandleExistingTrivia(JassSyntaxTriviaList triviaList, ImmutableArray.Builder triviaBuilder) + { + for (var i = 0; i < triviaList.Trivia.Length; i++) + { + var trivia = triviaList.Trivia[i]; + if (trivia.SyntaxKind == JassSyntaxKind.NewLineTrivia) + { + triviaBuilder.Add(trivia); + _encounteredAnyTextOnCurrentLine = false; + _requireNewLineTrivia = false; + } + else if (trivia.SyntaxKind == JassSyntaxKind.SingleLineCommentTrivia) + { + if (!_encounteredAnyTextOnCurrentLine) + { + _encounteredAnyTextOnCurrentLine = true; + if (_currentLevelOfIndentation > 0) + { + triviaBuilder.Add(JassSyntaxFactory.WhitespaceTrivia(string.Concat(Enumerable.Repeat(_indentationString, _currentLevelOfIndentation)))); + } + } + else if (_previousToken.TrailingTrivia.Trivia.IsEmpty || _previousToken.TrailingTrivia.Trivia[^1].SyntaxKind != JassSyntaxKind.WhitespaceTrivia) + { + triviaBuilder.Add(JassSyntaxTrivia.SingleSpace); + } + + if (_trimComments && char.IsWhiteSpace(trivia.Text[^1])) + { + triviaBuilder.Add(JassSyntaxFactory.SingleLineCommentTrivia(trivia.Text.TrimEnd())); + } + else + { + triviaBuilder.Add(trivia); + } + + _requireNewLineTrivia = true; + } + } + + if (_requireNewLineTrivia) + { + triviaBuilder.Add(JassSyntaxTrivia.NewLine); + _encounteredAnyTextOnCurrentLine = false; + _requireNewLineTrivia = false; + } + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Normalizer/TypeDeclarationNormalizer.cs b/src/War3Net.CodeAnalysis.Jass/Normalizer/TypeDeclarationNormalizer.cs new file mode 100644 index 00000000..6728b78e --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/Normalizer/TypeDeclarationNormalizer.cs @@ -0,0 +1,26 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using War3Net.CodeAnalysis.Jass.Syntax; + +namespace War3Net.CodeAnalysis.Jass +{ + partial class JassSyntaxNormalizer + { + /// + protected override bool RewriteTypeDeclaration(JassTypeDeclarationSyntax typeDeclaration, out JassTopLevelDeclarationSyntax result) + { + _nodes.Add(typeDeclaration); + var normalized = base.RewriteTypeDeclaration(typeDeclaration, out result); + _nodes.RemoveAt(_nodes.Count - 1); + + _requireNewLineTrivia = _encounteredAnyTextOnCurrentLine; + + return normalized; + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Normalizer/UnaryExpressionNormalizer.cs b/src/War3Net.CodeAnalysis.Jass/Normalizer/UnaryExpressionNormalizer.cs new file mode 100644 index 00000000..5b694104 --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/Normalizer/UnaryExpressionNormalizer.cs @@ -0,0 +1,24 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using War3Net.CodeAnalysis.Jass.Syntax; + +namespace War3Net.CodeAnalysis.Jass +{ + partial class JassSyntaxNormalizer + { + /// + protected override bool RewriteUnaryExpression(JassUnaryExpressionSyntax unaryExpression, out JassExpressionSyntax result) + { + _nodes.Add(unaryExpression); + var normalized = base.RewriteUnaryExpression(unaryExpression, out result); + _nodes.RemoveAt(_nodes.Count - 1); + + return normalized; + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Normalizer/VariableDeclaratorNormalizer.cs b/src/War3Net.CodeAnalysis.Jass/Normalizer/VariableDeclaratorNormalizer.cs new file mode 100644 index 00000000..0b8d9b74 --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/Normalizer/VariableDeclaratorNormalizer.cs @@ -0,0 +1,24 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using War3Net.CodeAnalysis.Jass.Syntax; + +namespace War3Net.CodeAnalysis.Jass +{ + partial class JassSyntaxNormalizer + { + /// + protected override bool RewriteVariableDeclarator(JassVariableDeclaratorSyntax variableDeclarator, out JassVariableOrArrayDeclaratorSyntax result) + { + _nodes.Add(variableDeclarator); + var normalized = base.RewriteVariableDeclarator(variableDeclarator, out result); + _nodes.RemoveAt(_nodes.Count - 1); + + return normalized; + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Parser/ArgumentListParser.cs b/src/War3Net.CodeAnalysis.Jass/Parser/ArgumentListParser.cs index 8115d92e..9e90566d 100644 --- a/src/War3Net.CodeAnalysis.Jass/Parser/ArgumentListParser.cs +++ b/src/War3Net.CodeAnalysis.Jass/Parser/ArgumentListParser.cs @@ -1,26 +1,33 @@ -// ------------------------------------------------------------------------------ +// ------------------------------------------------------------------------------ // // Licensed under the MIT license. // See the LICENSE file in the project root for more information. // // ------------------------------------------------------------------------------ -using System.Collections.Immutable; - using Pidgin; +using War3Net.CodeAnalysis.Jass.Extensions; using War3Net.CodeAnalysis.Jass.Syntax; +using static Pidgin.Parser; + namespace War3Net.CodeAnalysis.Jass { internal partial class JassParser { internal static Parser GetArgumentListParser( - Parser whitespaceParser, - Parser expressionParser) + Parser triviaParser, + Parser expressionParser) { - return expressionParser.Separated(Symbol.Comma.Then(whitespaceParser)) - .Select(arguments => new JassArgumentListSyntax(arguments.ToImmutableArray())); + return Map( + (openParenToken, argumentList, closeParenToken) => new JassArgumentListSyntax( + openParenToken, + argumentList, + closeParenToken), + Symbol.OpenParen.AsToken(triviaParser, JassSyntaxKind.OpenParenToken, JassSymbol.OpenParen), + expressionParser.SeparatedList(Symbol.Comma.AsToken(triviaParser, JassSyntaxKind.CommaToken, JassSymbol.Comma)), + Symbol.CloseParen.AsToken(triviaParser, JassSyntaxKind.CloseParenToken, JassSymbol.CloseParen)); } } } \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Parser/ArrayReferenceExpressionParser.cs b/src/War3Net.CodeAnalysis.Jass/Parser/ArrayReferenceExpressionParser.cs deleted file mode 100644 index e50fce34..00000000 --- a/src/War3Net.CodeAnalysis.Jass/Parser/ArrayReferenceExpressionParser.cs +++ /dev/null @@ -1,28 +0,0 @@ -// ------------------------------------------------------------------------------ -// -// Licensed under the MIT license. -// See the LICENSE file in the project root for more information. -// -// ------------------------------------------------------------------------------ - -using Pidgin; - -using War3Net.CodeAnalysis.Jass.Syntax; - -using static Pidgin.Parser; - -namespace War3Net.CodeAnalysis.Jass -{ - internal partial class JassParser - { - internal static Parser GetArrayReferenceExpressionParser( - Parser whitespaceParser, - Parser expressionParser, - Parser identifierNameParser) - { - return Try(identifierNameParser.Before(Symbol.LeftSquareBracket.Then(whitespaceParser))) - .Then(expressionParser, (id, indexer) => (IExpressionSyntax)new JassArrayReferenceExpressionSyntax(id, indexer)) - .Before(Symbol.RightSquareBracket.Then(whitespaceParser)); - } - } -} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Parser/BinaryOperatorParser.cs b/src/War3Net.CodeAnalysis.Jass/Parser/BinaryOperatorParser.cs index 58960378..2e8d81f3 100644 --- a/src/War3Net.CodeAnalysis.Jass/Parser/BinaryOperatorParser.cs +++ b/src/War3Net.CodeAnalysis.Jass/Parser/BinaryOperatorParser.cs @@ -7,6 +7,7 @@ using Pidgin; +using War3Net.CodeAnalysis.Jass.Extensions; using War3Net.CodeAnalysis.Jass.Syntax; using static Pidgin.Parser; @@ -15,81 +16,94 @@ namespace War3Net.CodeAnalysis.Jass { internal partial class JassParser { - internal static Parser GetBinaryOperatorParser(Parser whitespaceParser) + internal static Parser GetBinaryOperatorParser( + Parser triviaParser) { return OneOf( - GetBinaryAddOperatorParser(whitespaceParser), - GetBinarySubtractOperatorParser(whitespaceParser), - GetBinaryMultiplicationOperatorParser(whitespaceParser), - GetBinaryDivisionOperatorParser(whitespaceParser), - GetBinaryGreaterOrEqualOperatorParser(whitespaceParser), - GetBinaryLessOrEqualOperatorParser(whitespaceParser), - GetBinaryEqualsOperatorParser(whitespaceParser), - GetBinaryNotEqualsOperatorParser(whitespaceParser), - GetBinaryGreaterThanOperatorParser(whitespaceParser), - GetBinaryLessThanOperatorParser(whitespaceParser), - GetBinaryAndOperatorParser(whitespaceParser), - GetBinaryOrOperatorParser(whitespaceParser)); + GetBinaryAddOperatorParser(triviaParser), + GetBinarySubtractOperatorParser(triviaParser), + GetBinaryMultiplicationOperatorParser(triviaParser), + GetBinaryDivisionOperatorParser(triviaParser), + GetBinaryGreaterOrEqualOperatorParser(triviaParser), + GetBinaryLessOrEqualOperatorParser(triviaParser), + GetBinaryEqualsOperatorParser(triviaParser), + GetBinaryNotEqualsOperatorParser(triviaParser), + GetBinaryGreaterThanOperatorParser(triviaParser), + GetBinaryLessThanOperatorParser(triviaParser), + GetBinaryAndOperatorParser(triviaParser), + GetBinaryOrOperatorParser(triviaParser)); } - internal static Parser GetBinaryAddOperatorParser(Parser whitespaceParser) + internal static Parser GetBinaryAddOperatorParser( + Parser triviaParser) { - return Symbol.PlusSign.Then(whitespaceParser).ThenReturn(BinaryOperatorType.Add); + return Symbol.Plus.AsToken(triviaParser, JassSyntaxKind.PlusToken, JassSymbol.Plus); } - internal static Parser GetBinarySubtractOperatorParser(Parser whitespaceParser) + internal static Parser GetBinarySubtractOperatorParser( + Parser triviaParser) { - return Symbol.MinusSign.Then(whitespaceParser).ThenReturn(BinaryOperatorType.Subtract); + return Symbol.Minus.AsToken(triviaParser, JassSyntaxKind.MinusToken, JassSymbol.Minus); } - internal static Parser GetBinaryMultiplicationOperatorParser(Parser whitespaceParser) + internal static Parser GetBinaryMultiplicationOperatorParser( + Parser triviaParser) { - return Symbol.Asterisk.Then(whitespaceParser).ThenReturn(BinaryOperatorType.Multiplication); + return Symbol.Asterisk.AsToken(triviaParser, JassSyntaxKind.AsteriskToken, JassSymbol.Asterisk); } - internal static Parser GetBinaryDivisionOperatorParser(Parser whitespaceParser) + internal static Parser GetBinaryDivisionOperatorParser( + Parser triviaParser) { - return Try(Symbol.Slash.Then(Not(Lookahead(Symbol.Slash)))).Then(whitespaceParser).ThenReturn(BinaryOperatorType.Division); + return Try(Symbol.Slash.Before(Not(Lookahead(Symbol.Slash)))).AsToken(triviaParser, JassSyntaxKind.SlashToken, JassSymbol.Slash); } - internal static Parser GetBinaryGreaterOrEqualOperatorParser(Parser whitespaceParser) + internal static Parser GetBinaryGreaterOrEqualOperatorParser( + Parser triviaParser) { - return Try(Symbol.GreaterOrEquals).Then(whitespaceParser).ThenReturn(BinaryOperatorType.GreaterOrEqual); + return Try(Symbol.GreaterThanEquals).AsToken(triviaParser, JassSyntaxKind.GreaterThanEqualsToken); } - internal static Parser GetBinaryLessOrEqualOperatorParser(Parser whitespaceParser) + internal static Parser GetBinaryLessOrEqualOperatorParser( + Parser triviaParser) { - return Try(Symbol.LessOrEquals).Then(whitespaceParser).ThenReturn(BinaryOperatorType.LessOrEqual); + return Try(Symbol.LessThanEquals).AsToken(triviaParser, JassSyntaxKind.LessThanEqualsToken); } - internal static Parser GetBinaryEqualsOperatorParser(Parser whitespaceParser) + internal static Parser GetBinaryEqualsOperatorParser( + Parser triviaParser) { - return Symbol.EqualsEquals.Then(whitespaceParser).ThenReturn(BinaryOperatorType.Equals); + return Symbol.EqualsEquals.AsToken(triviaParser, JassSyntaxKind.EqualsEqualsToken); } - internal static Parser GetBinaryNotEqualsOperatorParser(Parser whitespaceParser) + internal static Parser GetBinaryNotEqualsOperatorParser( + Parser triviaParser) { - return Symbol.NotEquals.Then(whitespaceParser).ThenReturn(BinaryOperatorType.NotEquals); + return Symbol.ExclamationEquals.AsToken(triviaParser, JassSyntaxKind.ExclamationEqualsToken); } - internal static Parser GetBinaryGreaterThanOperatorParser(Parser whitespaceParser) + internal static Parser GetBinaryGreaterThanOperatorParser( + Parser triviaParser) { - return Symbol.GreaterThanSign.Then(whitespaceParser).ThenReturn(BinaryOperatorType.GreaterThan); + return Symbol.GreaterThan.AsToken(triviaParser, JassSyntaxKind.GreaterThanToken, JassSymbol.GreaterThan); } - internal static Parser GetBinaryLessThanOperatorParser(Parser whitespaceParser) + internal static Parser GetBinaryLessThanOperatorParser( + Parser triviaParser) { - return Symbol.LessThanSign.Then(whitespaceParser).ThenReturn(BinaryOperatorType.LessThan); + return Symbol.LessThan.AsToken(triviaParser, JassSyntaxKind.LessThanToken, JassSymbol.LessThan); } - internal static Parser GetBinaryAndOperatorParser(Parser whitespaceParser) + internal static Parser GetBinaryAndOperatorParser( + Parser triviaParser) { - return Keyword.And.Then(whitespaceParser).ThenReturn(BinaryOperatorType.And); + return Keyword.And.AsToken(triviaParser, JassSyntaxKind.AndKeyword); } - internal static Parser GetBinaryOrOperatorParser(Parser whitespaceParser) + internal static Parser GetBinaryOrOperatorParser( + Parser triviaParser) { - return Keyword.Or.Then(whitespaceParser).ThenReturn(BinaryOperatorType.Or); + return Keyword.Or.AsToken(triviaParser, JassSyntaxKind.OrKeyword); } } } \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Parser/BooleanLiteralExpressionParser.cs b/src/War3Net.CodeAnalysis.Jass/Parser/BooleanLiteralExpressionParser.cs deleted file mode 100644 index 5f6578eb..00000000 --- a/src/War3Net.CodeAnalysis.Jass/Parser/BooleanLiteralExpressionParser.cs +++ /dev/null @@ -1,22 +0,0 @@ -// ------------------------------------------------------------------------------ -// -// Licensed under the MIT license. -// See the LICENSE file in the project root for more information. -// -// ------------------------------------------------------------------------------ - -using Pidgin; - -using War3Net.CodeAnalysis.Jass.Syntax; - -namespace War3Net.CodeAnalysis.Jass -{ - internal partial class JassParser - { - internal static Parser GetBooleanLiteralExpressionParser(Parser whitespaceParser) - { - return Keyword.True.Then(whitespaceParser).ThenReturn(JassBooleanLiteralExpressionSyntax.True) - .Or(Keyword.False.Then(whitespaceParser).ThenReturn(JassBooleanLiteralExpressionSyntax.False)); - } - } -} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Parser/CallStatementParser.cs b/src/War3Net.CodeAnalysis.Jass/Parser/CallStatementParser.cs index 2de6885d..fd86241f 100644 --- a/src/War3Net.CodeAnalysis.Jass/Parser/CallStatementParser.cs +++ b/src/War3Net.CodeAnalysis.Jass/Parser/CallStatementParser.cs @@ -1,4 +1,4 @@ -// ------------------------------------------------------------------------------ +// ------------------------------------------------------------------------------ // // Licensed under the MIT license. // See the LICENSE file in the project root for more information. @@ -7,20 +7,30 @@ using Pidgin; +using War3Net.CodeAnalysis.Jass.Extensions; using War3Net.CodeAnalysis.Jass.Syntax; +using static Pidgin.Parser; + namespace War3Net.CodeAnalysis.Jass { internal partial class JassParser { - internal static Parser GetCallStatementParser( - Parser whitespaceParser, + internal static Parser GetCallStatementParser( + Parser identifierNameParser, Parser argumentListParser, - Parser identifierNameParser) + Parser triviaParser, + Parser trailingTriviaParser) { - return Keyword.Call.Then(whitespaceParser).Then(identifierNameParser).Then( - Symbol.LeftParenthesis.Then(whitespaceParser).Then(argumentListParser).Before(Symbol.RightParenthesis.Then(whitespaceParser)), - (id, arguments) => new JassCallStatementSyntax(id, arguments)); + return Map( + (callToken, identifierName, argumentList, trailingTrivia) => (JassStatementSyntax)new JassCallStatementSyntax( + callToken, + identifierName, + argumentList.AppendTrailingTrivia(trailingTrivia)), + Keyword.Call.AsToken(triviaParser, JassSyntaxKind.CallKeyword), + identifierNameParser, + argumentListParser, + trailingTriviaParser); } } } \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Parser/CharacterLiteralExpressionParser.cs b/src/War3Net.CodeAnalysis.Jass/Parser/CharacterLiteralExpressionParser.cs index e6e0d5b5..18afaaf2 100644 --- a/src/War3Net.CodeAnalysis.Jass/Parser/CharacterLiteralExpressionParser.cs +++ b/src/War3Net.CodeAnalysis.Jass/Parser/CharacterLiteralExpressionParser.cs @@ -1,4 +1,4 @@ -// ------------------------------------------------------------------------------ +// ------------------------------------------------------------------------------ // // Licensed under the MIT license. // See the LICENSE file in the project root for more information. @@ -15,11 +15,12 @@ namespace War3Net.CodeAnalysis.Jass { internal partial class JassParser { - internal static Parser GetCharacterLiteralExpressionParser() + internal static Parser GetCharacterLiteralExpressionParser( + Parser triviaParser) { var escapeCharacterParser = OneOf( - Symbol.QuotationMark.ThenReturn(JassSymbol.QuotationMark), - Symbol.Apostrophe.ThenReturn(JassSymbol.Apostrophe), + Symbol.DoubleQuote, + Symbol.SingleQuote, Char('r').ThenReturn('\r'), Char('n').ThenReturn('\n'), Char('t').ThenReturn('\t'), @@ -27,8 +28,12 @@ internal static Parser GetCharacterLiteralExpressionPar Char('f').ThenReturn('\f'), Char('\\').ThenReturn('\\')); - return Try(Char('\\').Then(escapeCharacterParser).Or(AnyCharExcept(JassSymbol.Apostrophe)).Between(Symbol.Apostrophe)) - .Select(value => new JassCharacterLiteralExpressionSyntax(value)) + return Map( + (value, trivia) => (JassExpressionSyntax)new JassLiteralExpressionSyntax(new JassSyntaxToken(JassSyntaxKind.CharacterLiteralToken, $"'{value}'", trivia)), + Try(OneOf( + Char('\\').Then(escapeCharacterParser), + AnyCharExcept(JassSymbol.SingleQuoteChar)).Between(Symbol.SingleQuote)), + triviaParser) .Labelled("character literal"); } } diff --git a/src/War3Net.CodeAnalysis.Jass/Parser/CommentParser.cs b/src/War3Net.CodeAnalysis.Jass/Parser/CommentParser.cs deleted file mode 100644 index a50b93a4..00000000 --- a/src/War3Net.CodeAnalysis.Jass/Parser/CommentParser.cs +++ /dev/null @@ -1,28 +0,0 @@ -// ------------------------------------------------------------------------------ -// -// Licensed under the MIT license. -// See the LICENSE file in the project root for more information. -// -// ------------------------------------------------------------------------------ - -using Pidgin; - -using War3Net.CodeAnalysis.Jass.Syntax; - -using static Pidgin.Parser; - -namespace War3Net.CodeAnalysis.Jass -{ - internal partial class JassParser - { - internal static Parser GetCommentStringParser() - { - return Try(String($"{JassSymbol.Slash}{JassSymbol.Slash}")).Then(AnyCharExcept(JassSymbol.CarriageReturn, JassSymbol.LineFeed).ManyString()); - } - - internal static Parser GetCommentParser(Parser commentStringParser) - { - return commentStringParser.Select(comment => new JassCommentSyntax(comment)); - } - } -} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Parser/CompilationUnitParser.cs b/src/War3Net.CodeAnalysis.Jass/Parser/CompilationUnitParser.cs index c4c6d01c..b1685921 100644 --- a/src/War3Net.CodeAnalysis.Jass/Parser/CompilationUnitParser.cs +++ b/src/War3Net.CodeAnalysis.Jass/Parser/CompilationUnitParser.cs @@ -1,4 +1,4 @@ -// ------------------------------------------------------------------------------ +// ------------------------------------------------------------------------------ // // Licensed under the MIT license. // See the LICENSE file in the project root for more information. @@ -9,9 +9,9 @@ using Pidgin; +using War3Net.CodeAnalysis.Jass.Extensions; using War3Net.CodeAnalysis.Jass.Syntax; -using static Pidgin.Parser; using static Pidgin.Parser; namespace War3Net.CodeAnalysis.Jass @@ -19,12 +19,17 @@ namespace War3Net.CodeAnalysis.Jass internal partial class JassParser { internal static Parser GetCompilationUnitParser( - Parser declarationParser, - Parser commentStringParser, - Parser newlineParser) + Parser declarationParser, + Parser leadingTriviaParser) { - return declarationParser.Before(commentStringParser.Optional().Then(newlineParser.Or(Lookahead(End)))).Many() - .Select(declarations => new JassCompilationUnitSyntax(declarations.ToImmutableArray())); + return declarationParser.UntilWithLeading( + leadingTriviaParser, + leadingTriviaParser, + End, + (leadingTrivia, declaration) => declaration.WithLeadingTrivia(leadingTrivia), + (firstTrivia, declarations, lastTrivia, _) => new JassCompilationUnitSyntax( + declarations.ToImmutableArray(), + new JassSyntaxToken(lastTrivia, JassSyntaxKind.EndOfFileToken, string.Empty, JassSyntaxTriviaList.Empty)).WithLeadingTrivia(firstTrivia)); } } } \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Parser/ConstantDeclarationParser.cs b/src/War3Net.CodeAnalysis.Jass/Parser/ConstantDeclarationParser.cs deleted file mode 100644 index 83df7537..00000000 --- a/src/War3Net.CodeAnalysis.Jass/Parser/ConstantDeclarationParser.cs +++ /dev/null @@ -1,31 +0,0 @@ -// ------------------------------------------------------------------------------ -// -// Licensed under the MIT license. -// See the LICENSE file in the project root for more information. -// -// ------------------------------------------------------------------------------ - -using Pidgin; - -using War3Net.CodeAnalysis.Jass.Syntax; - -using static Pidgin.Parser; - -namespace War3Net.CodeAnalysis.Jass -{ - internal partial class JassParser - { - internal static Parser GetConstantDeclarationParser( - Parser equalsValueClauseParser, - Parser identifierNameParser, - Parser typeParser, - Parser whitespaceParser) - { - return Map( - (type, id, value) => new JassGlobalDeclarationSyntax(new JassVariableDeclaratorSyntax(type, id, value)), - Keyword.Constant.Then(whitespaceParser).Then(typeParser), - identifierNameParser, - equalsValueClauseParser); - } - } -} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Parser/DebugCustomScriptActionParser.cs b/src/War3Net.CodeAnalysis.Jass/Parser/DebugCustomScriptActionParser.cs deleted file mode 100644 index efc70cc3..00000000 --- a/src/War3Net.CodeAnalysis.Jass/Parser/DebugCustomScriptActionParser.cs +++ /dev/null @@ -1,34 +0,0 @@ -// ------------------------------------------------------------------------------ -// -// Licensed under the MIT license. -// See the LICENSE file in the project root for more information. -// -// ------------------------------------------------------------------------------ - -using Pidgin; - -using War3Net.CodeAnalysis.Jass.Syntax; - -using static Pidgin.Parser; - -namespace War3Net.CodeAnalysis.Jass -{ - internal partial class JassParser - { - internal static Parser GetDebugCustomScriptActionParser( - Parser setCustomScriptActionParser, - Parser callCustomScriptActionParser, - Parser ifCustomScriptActionParser, - Parser loopCustomScriptActionParser, - Parser whitespaceParser) - { - return Keyword.Debug.Then(whitespaceParser).Then( - OneOf( - setCustomScriptActionParser, - callCustomScriptActionParser, - ifCustomScriptActionParser) - .Select(action => new JassDebugCustomScriptAction(action)) - .Or(loopCustomScriptActionParser.ThenReturn(JassDebugCustomScriptAction.DebugLoop))); - } - } -} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Parser/DebugStatementParser.cs b/src/War3Net.CodeAnalysis.Jass/Parser/DebugStatementParser.cs index 72fb3a65..dbec8cd7 100644 --- a/src/War3Net.CodeAnalysis.Jass/Parser/DebugStatementParser.cs +++ b/src/War3Net.CodeAnalysis.Jass/Parser/DebugStatementParser.cs @@ -1,4 +1,4 @@ -// ------------------------------------------------------------------------------ +// ------------------------------------------------------------------------------ // // Licensed under the MIT license. // See the LICENSE file in the project root for more information. @@ -7,6 +7,7 @@ using Pidgin; +using War3Net.CodeAnalysis.Jass.Extensions; using War3Net.CodeAnalysis.Jass.Syntax; using static Pidgin.Parser; @@ -15,21 +16,23 @@ namespace War3Net.CodeAnalysis.Jass { internal partial class JassParser { - internal static Parser GetDebugStatementParser( - Parser expressionParser, - Parser statementListParser, - Parser setStatementParser, - Parser callStatementParser, - Parser whitespaceParser, - Parser endOfLineParser) + internal static Parser GetDebugStatementParser( + Parser setStatementParser, + Parser callStatementParser, + Parser ifStatementParser, + Parser loopStatementParser, + Parser triviaParser) { - return Keyword.Debug.Then(whitespaceParser).Then( + return Map( + (debugToken, statement) => (JassStatementSyntax)new JassDebugStatementSyntax( + debugToken, + statement), + Keyword.Debug.AsToken(triviaParser, JassSyntaxKind.DebugKeyword), OneOf( setStatementParser, callStatementParser, - GetIfStatementParser(expressionParser, statementListParser, whitespaceParser, endOfLineParser), - GetLoopStatementParser(statementListParser, whitespaceParser, endOfLineParser)) - .Select(statement => new JassDebugStatementSyntax(statement))); + ifStatementParser, + loopStatementParser)); } } } \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Parser/DecimalLiteralExpressionParser.cs b/src/War3Net.CodeAnalysis.Jass/Parser/DecimalLiteralExpressionParser.cs index cdae40e6..69c4e3f8 100644 --- a/src/War3Net.CodeAnalysis.Jass/Parser/DecimalLiteralExpressionParser.cs +++ b/src/War3Net.CodeAnalysis.Jass/Parser/DecimalLiteralExpressionParser.cs @@ -1,4 +1,4 @@ -// ------------------------------------------------------------------------------ +// ------------------------------------------------------------------------------ // // Licensed under the MIT license. // See the LICENSE file in the project root for more information. @@ -7,6 +7,7 @@ using Pidgin; +using War3Net.CodeAnalysis.Jass.Extensions; using War3Net.CodeAnalysis.Jass.Syntax; using static Pidgin.Parser; @@ -15,10 +16,13 @@ namespace War3Net.CodeAnalysis.Jass { internal partial class JassParser { - internal static Parser GetDecimalLiteralExpressionParser() + internal static Parser GetDecimalLiteralExpressionParser( + Parser triviaParser) { return Try(UnsignedInt(10)) - .Select(value => new JassDecimalLiteralExpressionSyntax(value)) + .MapWithInput((s, _) => s.ToString()) + .AsToken(triviaParser, JassSyntaxKind.DecimalLiteralToken) + .Map(token => (JassExpressionSyntax)new JassLiteralExpressionSyntax(token)) .Labelled("decimal literal"); } } diff --git a/src/War3Net.CodeAnalysis.Jass/Parser/DeclarationLineParser.cs b/src/War3Net.CodeAnalysis.Jass/Parser/DeclarationLineParser.cs deleted file mode 100644 index f6077551..00000000 --- a/src/War3Net.CodeAnalysis.Jass/Parser/DeclarationLineParser.cs +++ /dev/null @@ -1,35 +0,0 @@ -// ------------------------------------------------------------------------------ -// -// Licensed under the MIT license. -// See the LICENSE file in the project root for more information. -// -// ------------------------------------------------------------------------------ - -using Pidgin; - -using War3Net.CodeAnalysis.Jass.Syntax; - -using static Pidgin.Parser; - -namespace War3Net.CodeAnalysis.Jass -{ - internal partial class JassParser - { - internal static Parser GetDeclarationLineParser( - Parser emptyLineParser, - Parser commentParser, - Parser typeDeclarationParser, - Parser nativeFunctionDeclarationParser, - Parser functionDeclaratorParser, - Parser whitespaceParser) - { - return OneOf( - emptyLineParser.Cast(), - commentParser.Cast(), - typeDeclarationParser.Cast(), - GetGlobalsCustomScriptActionParser(whitespaceParser), - nativeFunctionDeclarationParser.Cast(), - GetFunctionCustomScriptActionParser(functionDeclaratorParser, whitespaceParser)); - } - } -} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Parser/DeclarationParser.cs b/src/War3Net.CodeAnalysis.Jass/Parser/DeclarationParser.cs deleted file mode 100644 index 824ee5bf..00000000 --- a/src/War3Net.CodeAnalysis.Jass/Parser/DeclarationParser.cs +++ /dev/null @@ -1,37 +0,0 @@ -// ------------------------------------------------------------------------------ -// -// Licensed under the MIT license. -// See the LICENSE file in the project root for more information. -// -// ------------------------------------------------------------------------------ - -using Pidgin; - -using War3Net.CodeAnalysis.Jass.Syntax; - -using static Pidgin.Parser; - -namespace War3Net.CodeAnalysis.Jass -{ - internal partial class JassParser - { - internal static Parser GetDeclarationParser( - Parser emptyParser, - Parser commentParser, - Parser typeDeclarationParser, - Parser nativeFunctionDeclarationParser, - Parser functionDeclarationParser, - Parser globalDeclarationParser, - Parser whitespaceParser, - Parser endOfLineParser) - { - return OneOf( - emptyParser.Cast(), - commentParser.Cast(), - typeDeclarationParser.Cast(), - GetGlobalDeclarationListParser(globalDeclarationParser, whitespaceParser, endOfLineParser), - nativeFunctionDeclarationParser.Cast(), - functionDeclarationParser.Cast()); - } - } -} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Parser/ElementAccessClauseParser.cs b/src/War3Net.CodeAnalysis.Jass/Parser/ElementAccessClauseParser.cs new file mode 100644 index 00000000..8676021e --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/Parser/ElementAccessClauseParser.cs @@ -0,0 +1,33 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using Pidgin; + +using War3Net.CodeAnalysis.Jass.Extensions; +using War3Net.CodeAnalysis.Jass.Syntax; + +using static Pidgin.Parser; + +namespace War3Net.CodeAnalysis.Jass +{ + internal partial class JassParser + { + internal static Parser GetElementAccessClauseParser( + Parser triviaParser, + Parser expressionParser) + { + return Map( + (openBracketToken, expression, closeBracketToken) => new JassElementAccessClauseSyntax( + openBracketToken, + expression, + closeBracketToken), + Symbol.OpenBracket.AsToken(triviaParser, JassSyntaxKind.OpenBracketToken, JassSymbol.OpenBracket), + expressionParser, + Symbol.CloseBracket.AsToken(triviaParser, JassSyntaxKind.CloseBracketToken, JassSymbol.CloseBracket)); + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Parser/ElseClauseParser.cs b/src/War3Net.CodeAnalysis.Jass/Parser/ElseClauseParser.cs deleted file mode 100644 index 46d32d45..00000000 --- a/src/War3Net.CodeAnalysis.Jass/Parser/ElseClauseParser.cs +++ /dev/null @@ -1,26 +0,0 @@ -// ------------------------------------------------------------------------------ -// -// Licensed under the MIT license. -// See the LICENSE file in the project root for more information. -// -// ------------------------------------------------------------------------------ - -using Pidgin; - -using War3Net.CodeAnalysis.Jass.Syntax; - -namespace War3Net.CodeAnalysis.Jass -{ - internal partial class JassParser - { - internal static Parser GetElseClauseParser( - Parser statementListParser, - Parser whitespaceParser, - Parser endOfLineParser) - { - return Keyword.Else.Then(whitespaceParser).Before(endOfLineParser) - .Then(statementListParser) - .Select(statementList => new JassElseClauseSyntax(statementList)); - } - } -} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Parser/ElseCustomScriptActionParser.cs b/src/War3Net.CodeAnalysis.Jass/Parser/ElseCustomScriptActionParser.cs deleted file mode 100644 index 56d6a871..00000000 --- a/src/War3Net.CodeAnalysis.Jass/Parser/ElseCustomScriptActionParser.cs +++ /dev/null @@ -1,21 +0,0 @@ -// ------------------------------------------------------------------------------ -// -// Licensed under the MIT license. -// See the LICENSE file in the project root for more information. -// -// ------------------------------------------------------------------------------ - -using Pidgin; - -using War3Net.CodeAnalysis.Jass.Syntax; - -namespace War3Net.CodeAnalysis.Jass -{ - internal partial class JassParser - { - internal static Parser GetElseCustomScriptActionParser(Parser whitespaceParser) - { - return Keyword.Else.Then(whitespaceParser).ThenReturn(JassElseCustomScriptAction.Value); - } - } -} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Parser/ElseIfClauseDeclaratorParser.cs b/src/War3Net.CodeAnalysis.Jass/Parser/ElseIfClauseDeclaratorParser.cs new file mode 100644 index 00000000..2a5a8572 --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/Parser/ElseIfClauseDeclaratorParser.cs @@ -0,0 +1,34 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using Pidgin; + +using War3Net.CodeAnalysis.Jass.Extensions; +using War3Net.CodeAnalysis.Jass.Syntax; + +using static Pidgin.Parser; + +namespace War3Net.CodeAnalysis.Jass +{ + internal partial class JassParser + { + internal static Parser GetElseIfClauseDeclaratorParser( + Parser expressionParser, + Parser triviaParser, + Parser trailingTriviaParser) + { + return Map( + (elseIfToken, condition, thenToken) => new JassElseIfClauseDeclaratorSyntax( + elseIfToken, + condition, + thenToken), + Keyword.ElseIf.AsToken(triviaParser, JassSyntaxKind.ElseIfKeyword), + expressionParser, + Keyword.Then.AsToken(trailingTriviaParser, JassSyntaxKind.ThenKeyword)); + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Parser/ElseIfClauseParser.cs b/src/War3Net.CodeAnalysis.Jass/Parser/ElseIfClauseParser.cs deleted file mode 100644 index cf5b35f2..00000000 --- a/src/War3Net.CodeAnalysis.Jass/Parser/ElseIfClauseParser.cs +++ /dev/null @@ -1,30 +0,0 @@ -// ------------------------------------------------------------------------------ -// -// Licensed under the MIT license. -// See the LICENSE file in the project root for more information. -// -// ------------------------------------------------------------------------------ - -using Pidgin; - -using War3Net.CodeAnalysis.Jass.Syntax; - -using static Pidgin.Parser; - -namespace War3Net.CodeAnalysis.Jass -{ - internal partial class JassParser - { - internal static Parser GetElseIfClauseParser( - Parser expressionParser, - Parser statementListParser, - Parser whitespaceParser, - Parser endOfLineParser) - { - return Map( - (condition, statementList) => new JassElseIfClauseSyntax(condition, statementList), - Keyword.ElseIf.Then(whitespaceParser).Then(expressionParser).Before(Keyword.Then.Then(whitespaceParser)).Before(endOfLineParser), - statementListParser); - } - } -} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Parser/ElseIfCustomScriptActionParser.cs b/src/War3Net.CodeAnalysis.Jass/Parser/ElseIfCustomScriptActionParser.cs deleted file mode 100644 index f0acde85..00000000 --- a/src/War3Net.CodeAnalysis.Jass/Parser/ElseIfCustomScriptActionParser.cs +++ /dev/null @@ -1,24 +0,0 @@ -// ------------------------------------------------------------------------------ -// -// Licensed under the MIT license. -// See the LICENSE file in the project root for more information. -// -// ------------------------------------------------------------------------------ - -using Pidgin; - -using War3Net.CodeAnalysis.Jass.Syntax; - -namespace War3Net.CodeAnalysis.Jass -{ - internal partial class JassParser - { - internal static Parser GetElseIfCustomScriptActionParser( - Parser expressionParser, - Parser whitespaceParser) - { - return Keyword.ElseIf.Then(whitespaceParser).Then(expressionParser).Before(Keyword.Then.Then(whitespaceParser)) - .Select(expression => new JassElseIfCustomScriptAction(expression)); - } - } -} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Parser/EmptyParser.cs b/src/War3Net.CodeAnalysis.Jass/Parser/EmptyParser.cs deleted file mode 100644 index 33d4aa21..00000000 --- a/src/War3Net.CodeAnalysis.Jass/Parser/EmptyParser.cs +++ /dev/null @@ -1,29 +0,0 @@ -// ------------------------------------------------------------------------------ -// -// Licensed under the MIT license. -// See the LICENSE file in the project root for more information. -// -// ------------------------------------------------------------------------------ - -using Pidgin; - -using War3Net.CodeAnalysis.Jass.Syntax; - -using static Pidgin.Parser; -using static Pidgin.Parser; - -namespace War3Net.CodeAnalysis.Jass -{ - internal partial class JassParser - { - internal static Parser GetEmptyParser() - { - return Lookahead(Symbol.CarriageReturn.Or(Symbol.LineFeed)).ThenReturn(JassEmptySyntax.Value); - } - - internal static Parser GetEmptyLineParser() - { - return Lookahead(End).ThenReturn(JassEmptySyntax.Value); - } - } -} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Parser/EndFunctionCustomScriptActionParser.cs b/src/War3Net.CodeAnalysis.Jass/Parser/EndFunctionCustomScriptActionParser.cs deleted file mode 100644 index 073c8569..00000000 --- a/src/War3Net.CodeAnalysis.Jass/Parser/EndFunctionCustomScriptActionParser.cs +++ /dev/null @@ -1,21 +0,0 @@ -// ------------------------------------------------------------------------------ -// -// Licensed under the MIT license. -// See the LICENSE file in the project root for more information. -// -// ------------------------------------------------------------------------------ - -using Pidgin; - -using War3Net.CodeAnalysis.Jass.Syntax; - -namespace War3Net.CodeAnalysis.Jass -{ - internal partial class JassParser - { - internal static Parser GetEndFunctionCustomScriptActionParser(Parser whitespaceParser) - { - return Keyword.EndFunction.Then(whitespaceParser).ThenReturn(JassEndFunctionCustomScriptAction.Value); - } - } -} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Parser/EndGlobalsCustomScriptActionParser.cs b/src/War3Net.CodeAnalysis.Jass/Parser/EndGlobalsCustomScriptActionParser.cs deleted file mode 100644 index 91b14ce0..00000000 --- a/src/War3Net.CodeAnalysis.Jass/Parser/EndGlobalsCustomScriptActionParser.cs +++ /dev/null @@ -1,21 +0,0 @@ -// ------------------------------------------------------------------------------ -// -// Licensed under the MIT license. -// See the LICENSE file in the project root for more information. -// -// ------------------------------------------------------------------------------ - -using Pidgin; - -using War3Net.CodeAnalysis.Jass.Syntax; - -namespace War3Net.CodeAnalysis.Jass -{ - internal partial class JassParser - { - internal static Parser GetEndGlobalsCustomScriptActionParser(Parser whitespaceParser) - { - return Keyword.EndGlobals.Then(whitespaceParser).ThenReturn(JassEndGlobalsCustomScriptAction.Value); - } - } -} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Parser/EndIfCustomScriptActionParser.cs b/src/War3Net.CodeAnalysis.Jass/Parser/EndIfCustomScriptActionParser.cs deleted file mode 100644 index aecfd4ee..00000000 --- a/src/War3Net.CodeAnalysis.Jass/Parser/EndIfCustomScriptActionParser.cs +++ /dev/null @@ -1,21 +0,0 @@ -// ------------------------------------------------------------------------------ -// -// Licensed under the MIT license. -// See the LICENSE file in the project root for more information. -// -// ------------------------------------------------------------------------------ - -using Pidgin; - -using War3Net.CodeAnalysis.Jass.Syntax; - -namespace War3Net.CodeAnalysis.Jass -{ - internal partial class JassParser - { - internal static Parser GetEndIfCustomScriptActionParser(Parser whitespaceParser) - { - return Keyword.EndIf.Then(whitespaceParser).ThenReturn(JassEndIfCustomScriptAction.Value); - } - } -} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Parser/EndLoopCustomScriptActionParser.cs b/src/War3Net.CodeAnalysis.Jass/Parser/EndLoopCustomScriptActionParser.cs deleted file mode 100644 index 236751ee..00000000 --- a/src/War3Net.CodeAnalysis.Jass/Parser/EndLoopCustomScriptActionParser.cs +++ /dev/null @@ -1,21 +0,0 @@ -// ------------------------------------------------------------------------------ -// -// Licensed under the MIT license. -// See the LICENSE file in the project root for more information. -// -// ------------------------------------------------------------------------------ - -using Pidgin; - -using War3Net.CodeAnalysis.Jass.Syntax; - -namespace War3Net.CodeAnalysis.Jass -{ - internal partial class JassParser - { - internal static Parser GetEndLoopCustomScriptActionParser(Parser whitespaceParser) - { - return Keyword.EndLoop.Then(whitespaceParser).ThenReturn(JassEndLoopCustomScriptAction.Value); - } - } -} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Parser/EndOfLineParser.cs b/src/War3Net.CodeAnalysis.Jass/Parser/EndOfLineParser.cs deleted file mode 100644 index cf683ed8..00000000 --- a/src/War3Net.CodeAnalysis.Jass/Parser/EndOfLineParser.cs +++ /dev/null @@ -1,21 +0,0 @@ -// ------------------------------------------------------------------------------ -// -// Licensed under the MIT license. -// See the LICENSE file in the project root for more information. -// -// ------------------------------------------------------------------------------ - -using Pidgin; - -namespace War3Net.CodeAnalysis.Jass -{ - internal partial class JassParser - { - internal static Parser GetEndOfLineParser( - Parser commentStringParser, - Parser newLineParser) - { - return commentStringParser.Optional().Then(newLineParser); - } - } -} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Parser/EqualsValueClauseParser.cs b/src/War3Net.CodeAnalysis.Jass/Parser/EqualsValueClauseParser.cs index 10ea5d65..f7f0514d 100644 --- a/src/War3Net.CodeAnalysis.Jass/Parser/EqualsValueClauseParser.cs +++ b/src/War3Net.CodeAnalysis.Jass/Parser/EqualsValueClauseParser.cs @@ -1,4 +1,4 @@ -// ------------------------------------------------------------------------------ +// ------------------------------------------------------------------------------ // // Licensed under the MIT license. // See the LICENSE file in the project root for more information. @@ -7,18 +7,25 @@ using Pidgin; +using War3Net.CodeAnalysis.Jass.Extensions; using War3Net.CodeAnalysis.Jass.Syntax; +using static Pidgin.Parser; + namespace War3Net.CodeAnalysis.Jass { internal partial class JassParser { internal static Parser GetEqualsValueClauseParser( - Parser whitespaceParser, - Parser expressionParser) + Parser triviaParser, + Parser expressionParser) { - return Symbol.EqualsSign.Then(whitespaceParser).Then(expressionParser) - .Select(expression => new JassEqualsValueClauseSyntax(expression)); + return Map( + (equalsToken, expression) => new JassEqualsValueClauseSyntax( + equalsToken, + expression), + Symbol.Equals.AsToken(triviaParser, JassSyntaxKind.EqualsToken, JassSymbol.Equals), + expressionParser); } } } \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Parser/ExitStatementParser.cs b/src/War3Net.CodeAnalysis.Jass/Parser/ExitStatementParser.cs index ea887726..d63d5a70 100644 --- a/src/War3Net.CodeAnalysis.Jass/Parser/ExitStatementParser.cs +++ b/src/War3Net.CodeAnalysis.Jass/Parser/ExitStatementParser.cs @@ -1,4 +1,4 @@ -// ------------------------------------------------------------------------------ +// ------------------------------------------------------------------------------ // // Licensed under the MIT license. // See the LICENSE file in the project root for more information. @@ -7,18 +7,27 @@ using Pidgin; +using War3Net.CodeAnalysis.Jass.Extensions; using War3Net.CodeAnalysis.Jass.Syntax; +using static Pidgin.Parser; + namespace War3Net.CodeAnalysis.Jass { internal partial class JassParser { - internal static Parser GetExitStatementParser( - Parser expressionParser, - Parser whitespaceParser) + internal static Parser GetExitStatementParser( + Parser expressionParser, + Parser triviaParser, + Parser trailingTriviaParser) { - return Keyword.ExitWhen.Then(whitespaceParser).Then(expressionParser) - .Select(expression => new JassExitStatementSyntax(expression)); + return Map( + (exitWhenToken, expression, trailingTrivia) => (JassStatementSyntax)new JassExitStatementSyntax( + exitWhenToken, + expression.AppendTrailingTrivia(trailingTrivia)), + Keyword.ExitWhen.AsToken(triviaParser, JassSyntaxKind.ExitWhenKeyword), + expressionParser, + trailingTriviaParser); } } } \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Parser/ExpressionParser.cs b/src/War3Net.CodeAnalysis.Jass/Parser/ExpressionParser.cs index 13f6ac8f..bbc21815 100644 --- a/src/War3Net.CodeAnalysis.Jass/Parser/ExpressionParser.cs +++ b/src/War3Net.CodeAnalysis.Jass/Parser/ExpressionParser.cs @@ -1,4 +1,4 @@ -// ------------------------------------------------------------------------------ +// ------------------------------------------------------------------------------ // // Licensed under the MIT license. // See the LICENSE file in the project root for more information. @@ -9,6 +9,7 @@ using Pidgin.Expression; using War3Net.CodeAnalysis.Jass.Extensions; +using War3Net.CodeAnalysis.Jass.Parsers; using War3Net.CodeAnalysis.Jass.Syntax; using static Pidgin.Parser; @@ -17,46 +18,51 @@ namespace War3Net.CodeAnalysis.Jass { internal partial class JassParser { - internal static Parser GetExpressionParser( - Parser whitespaceParser, - Parser identifierNameParser) + internal static Parser GetExpressionParser( + Parser identifierParser, + Parser identifierNameParser, + Parser triviaParser) { - return Pidgin.Expression.ExpressionParser.Build( + return Pidgin.Expression.ExpressionParser.Build( expressionParser => - ( - OneOf( - GetCharacterLiteralExpressionParser().Before(whitespaceParser), - GetFourCCLiteralExpressionParser().Before(whitespaceParser), - GetHexadecimalLiteralExpressionParser().Before(whitespaceParser), - GetRealLiteralExpressionParser().Before(whitespaceParser), - GetOctalLiteralExpressionParser().Before(whitespaceParser), - GetDecimalLiteralExpressionParser().Before(whitespaceParser), - GetBooleanLiteralExpressionParser(whitespaceParser), - GetStringLiteralExpressionParser().Before(whitespaceParser), - GetNullLiteralExpressionParser(whitespaceParser), - GetFunctionReferenceExpressionParser(identifierNameParser, whitespaceParser), - GetInvocationExpressionParser(whitespaceParser, expressionParser, identifierNameParser), - GetArrayReferenceExpressionParser(whitespaceParser, expressionParser, identifierNameParser), - GetVariableReferenceExpressionParser(identifierNameParser), - GetParenthesizedExpressionParser(whitespaceParser, expressionParser)), + { + var argumentListParser = GetArgumentListParser(triviaParser, expressionParser); + var elementAccessClauseParser = GetElementAccessClauseParser(triviaParser, expressionParser); + + return (OneOf( + GetCharacterLiteralExpressionParser(triviaParser), + GetFourCCLiteralExpressionParser(triviaParser), + GetHexadecimalLiteralExpressionParser(triviaParser), + GetRealLiteralExpressionParser(triviaParser), + GetOctalLiteralExpressionParser(triviaParser), + GetDecimalLiteralExpressionParser(triviaParser), + GetStringLiteralExpressionParser(triviaParser), + new IdentifierExpressionParser( + identifierParser, + identifierNameParser, + argumentListParser, + elementAccessClauseParser, + triviaParser), + GetParenthesizedExpressionParser(triviaParser, expressionParser)), new[] { // https://www.hiveworkshop.com/threads/precedence-in-jass.43500/#post-378439 - Operator.PrefixChainable(GetUnaryNotOperatorParser(whitespaceParser).Prefix()), - Operator.PrefixChainable(GetUnaryPlusOperatorParser(whitespaceParser).Prefix(), GetUnaryMinusOperatorParser(whitespaceParser).Prefix()), - Operator.InfixL(GetBinaryMultiplicationOperatorParser(whitespaceParser).Infix()) - .And(Operator.InfixL(GetBinaryDivisionOperatorParser(whitespaceParser).Infix())), - Operator.InfixL(GetBinaryAddOperatorParser(whitespaceParser).Infix()) - .And(Operator.InfixL(GetBinarySubtractOperatorParser(whitespaceParser).Infix())), - Operator.InfixL(GetBinaryGreaterOrEqualOperatorParser(whitespaceParser).Infix()) - .And(Operator.InfixL(GetBinaryLessOrEqualOperatorParser(whitespaceParser).Infix())) - .And(Operator.InfixL(GetBinaryEqualsOperatorParser(whitespaceParser).Infix())) - .And(Operator.InfixL(GetBinaryNotEqualsOperatorParser(whitespaceParser).Infix())) - .And(Operator.InfixL(GetBinaryGreaterThanOperatorParser(whitespaceParser).Infix())) - .And(Operator.InfixL(GetBinaryLessThanOperatorParser(whitespaceParser).Infix())), - Operator.InfixL(GetBinaryAndOperatorParser(whitespaceParser).Infix()) - .And(Operator.InfixL(GetBinaryOrOperatorParser(whitespaceParser).Infix())), - })); + Operator.PrefixChainable(GetUnaryNotOperatorParser(triviaParser).Prefix()), + Operator.PrefixChainable(GetUnaryPlusOperatorParser(triviaParser).Prefix(), GetUnaryMinusOperatorParser(triviaParser).Prefix()), + Operator.InfixL(GetBinaryMultiplicationOperatorParser(triviaParser).Infix()) + .And(Operator.InfixL(GetBinaryDivisionOperatorParser(triviaParser).Infix())), + Operator.InfixL(GetBinaryAddOperatorParser(triviaParser).Infix()) + .And(Operator.InfixL(GetBinarySubtractOperatorParser(triviaParser).Infix())), + Operator.InfixL(GetBinaryGreaterOrEqualOperatorParser(triviaParser).Infix()) + .And(Operator.InfixL(GetBinaryLessOrEqualOperatorParser(triviaParser).Infix())) + .And(Operator.InfixL(GetBinaryEqualsOperatorParser(triviaParser).Infix())) + .And(Operator.InfixL(GetBinaryNotEqualsOperatorParser(triviaParser).Infix())) + .And(Operator.InfixL(GetBinaryGreaterThanOperatorParser(triviaParser).Infix())) + .And(Operator.InfixL(GetBinaryLessThanOperatorParser(triviaParser).Infix())), + Operator.InfixL(GetBinaryAndOperatorParser(triviaParser).Infix()) + .And(Operator.InfixL(GetBinaryOrOperatorParser(triviaParser).Infix())), + }); + }); } } } \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Parser/FourCCLiteralExpressionParser.cs b/src/War3Net.CodeAnalysis.Jass/Parser/FourCCLiteralExpressionParser.cs index 0b3191c5..94b5cd84 100644 --- a/src/War3Net.CodeAnalysis.Jass/Parser/FourCCLiteralExpressionParser.cs +++ b/src/War3Net.CodeAnalysis.Jass/Parser/FourCCLiteralExpressionParser.cs @@ -1,4 +1,4 @@ -// ------------------------------------------------------------------------------ +// ------------------------------------------------------------------------------ // // Licensed under the MIT license. // See the LICENSE file in the project root for more information. @@ -16,11 +16,14 @@ namespace War3Net.CodeAnalysis.Jass { internal partial class JassParser { - internal static Parser GetFourCCLiteralExpressionParser() + internal static Parser GetFourCCLiteralExpressionParser( + Parser triviaParser) { - return Symbol.Apostrophe.Then(AnyCharExcept(JassSymbol.Apostrophe).ManyString()).Before(Symbol.Apostrophe) + return Symbol.SingleQuote.Then(AnyCharExcept(JassSymbol.SingleQuoteChar).ManyString()).Before(Symbol.SingleQuote) .Assert(value => value.IsJassRawcode()) - .Select(value => new JassFourCCLiteralExpressionSyntax(value.FromJassRawcode())) + .MapWithInput((s, _) => s.ToString()) + .AsToken(triviaParser, JassSyntaxKind.FourCCLiteralToken) + .Map(token => (JassExpressionSyntax)new JassLiteralExpressionSyntax(token)) .Labelled("fourCC literal"); } } diff --git a/src/War3Net.CodeAnalysis.Jass/Parser/FunctionCustomScriptActionParser.cs b/src/War3Net.CodeAnalysis.Jass/Parser/FunctionCustomScriptActionParser.cs deleted file mode 100644 index 987aa31f..00000000 --- a/src/War3Net.CodeAnalysis.Jass/Parser/FunctionCustomScriptActionParser.cs +++ /dev/null @@ -1,24 +0,0 @@ -// ------------------------------------------------------------------------------ -// -// Licensed under the MIT license. -// See the LICENSE file in the project root for more information. -// -// ------------------------------------------------------------------------------ - -using Pidgin; - -using War3Net.CodeAnalysis.Jass.Syntax; - -namespace War3Net.CodeAnalysis.Jass -{ - internal partial class JassParser - { - internal static Parser GetFunctionCustomScriptActionParser( - Parser functionDeclaratorParser, - Parser whitespaceParser) - { - return Keyword.Function.Then(whitespaceParser).Then(functionDeclaratorParser) - .Select(functionDeclarator => new JassFunctionCustomScriptAction(functionDeclarator)); - } - } -} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Parser/FunctionDeclarationParser.cs b/src/War3Net.CodeAnalysis.Jass/Parser/FunctionDeclarationParser.cs index 55bc8a43..b428ffd0 100644 --- a/src/War3Net.CodeAnalysis.Jass/Parser/FunctionDeclarationParser.cs +++ b/src/War3Net.CodeAnalysis.Jass/Parser/FunctionDeclarationParser.cs @@ -1,30 +1,37 @@ -// ------------------------------------------------------------------------------ +// ------------------------------------------------------------------------------ // // Licensed under the MIT license. // See the LICENSE file in the project root for more information. // // ------------------------------------------------------------------------------ +using System; +using System.Collections.Immutable; + using Pidgin; +using War3Net.CodeAnalysis.Jass.Extensions; using War3Net.CodeAnalysis.Jass.Syntax; -using static Pidgin.Parser; - namespace War3Net.CodeAnalysis.Jass { internal partial class JassParser { - internal static Parser GetFunctionDeclarationParser( - Parser functionDeclaratorParser, - Parser statementListParser, - Parser whitespaceParser, - Parser endOfLineParser) + internal static Parser, JassTopLevelDeclarationSyntax>> GetFunctionDeclarationParser( + Parser, JassFunctionDeclaratorSyntax>> functionDeclaratorParser, + Parser statementParser, + Parser leadingTriviaParser, + Parser trailingTriviaParser) { - return Map( - (declarator, body) => new JassFunctionDeclarationSyntax(declarator, body), - Keyword.Constant.Then(whitespaceParser).Optional().Then(Keyword.Function.Then(whitespaceParser)).Then(functionDeclaratorParser).Before(endOfLineParser), - statementListParser.Before(Keyword.EndFunction.Then(whitespaceParser))); + return statementParser.UntilWithLeading, JassFunctionDeclaratorSyntax>, JassStatementSyntax, JassSyntaxToken, Func, JassTopLevelDeclarationSyntax>>( + leadingTriviaParser, + functionDeclaratorParser, + Keyword.EndFunction.AsToken(trailingTriviaParser, JassSyntaxKind.EndFunctionKeyword), + (leadingTrivia, statement) => statement.WithLeadingTrivia(leadingTrivia), + (declaratorFunc, statements, leadingTrivia, endFunctionToken) => constantToken => new JassFunctionDeclarationSyntax( + declaratorFunc(constantToken), + statements.ToImmutableArray(), + endFunctionToken.WithLeadingTrivia(leadingTrivia))); } } } \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Parser/FunctionDeclaratorParser.cs b/src/War3Net.CodeAnalysis.Jass/Parser/FunctionDeclaratorParser.cs index 6fffffee..ec7ba2df 100644 --- a/src/War3Net.CodeAnalysis.Jass/Parser/FunctionDeclaratorParser.cs +++ b/src/War3Net.CodeAnalysis.Jass/Parser/FunctionDeclaratorParser.cs @@ -1,12 +1,15 @@ -// ------------------------------------------------------------------------------ +// ------------------------------------------------------------------------------ // // Licensed under the MIT license. // See the LICENSE file in the project root for more information. // // ------------------------------------------------------------------------------ +using System; + using Pidgin; +using War3Net.CodeAnalysis.Jass.Extensions; using War3Net.CodeAnalysis.Jass.Syntax; using static Pidgin.Parser; @@ -15,17 +18,25 @@ namespace War3Net.CodeAnalysis.Jass { internal partial class JassParser { - internal static Parser GetFunctionDeclaratorParser( + internal static Parser, JassFunctionDeclaratorSyntax>> GetFunctionDeclaratorParser( Parser identifierNameParser, - Parser parameterListParser, - Parser typeParser, - Parser whitespaceParser) + Parser parameterListParser, + Parser returnClauseParser, + Parser triviaParser, + Parser trailingTriviaParser) { - return Map( - (id, parameterList, returnType) => new JassFunctionDeclaratorSyntax(id, parameterList, returnType), + return Map, JassFunctionDeclaratorSyntax>>( + (functionToken, identifierName, parameterList, returnClause, trailingTrivia) => constantToken => new JassFunctionDeclaratorSyntax( + constantToken.GetValueOrDefault(), + functionToken, + identifierName, + parameterList, + returnClause.AppendTrailingTrivia(trailingTrivia)), + Keyword.Function.AsToken(triviaParser, JassSyntaxKind.FunctionKeyword), identifierNameParser, - Keyword.Takes.Then(whitespaceParser).Then(Keyword.Nothing.Then(whitespaceParser).ThenReturn(JassParameterListSyntax.Empty).Or(parameterListParser)), - Keyword.Returns.Then(whitespaceParser).Then(Keyword.Nothing.Then(whitespaceParser).ThenReturn(JassTypeSyntax.Nothing).Or(typeParser))); + parameterListParser, + returnClauseParser, + trailingTriviaParser); } } } \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Parser/FunctionReferenceExpressionParser.cs b/src/War3Net.CodeAnalysis.Jass/Parser/FunctionReferenceExpressionParser.cs deleted file mode 100644 index 340b4860..00000000 --- a/src/War3Net.CodeAnalysis.Jass/Parser/FunctionReferenceExpressionParser.cs +++ /dev/null @@ -1,25 +0,0 @@ -// ------------------------------------------------------------------------------ -// -// Licensed under the MIT license. -// See the LICENSE file in the project root for more information. -// -// ------------------------------------------------------------------------------ - -using Pidgin; - -using War3Net.CodeAnalysis.Jass.Syntax; - -namespace War3Net.CodeAnalysis.Jass -{ - internal partial class JassParser - { - internal static Parser GetFunctionReferenceExpressionParser( - Parser identifierNameParser, - Parser whitespaceParser) - { - return Keyword.Function.Then(whitespaceParser).Then(identifierNameParser) - .Select(name => new JassFunctionReferenceExpressionSyntax(name)) - .Labelled("function reference"); - } - } -} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Parser/GlobalConstantDeclarationParser.cs b/src/War3Net.CodeAnalysis.Jass/Parser/GlobalConstantDeclarationParser.cs new file mode 100644 index 00000000..b7401b7b --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/Parser/GlobalConstantDeclarationParser.cs @@ -0,0 +1,39 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using Pidgin; + +using War3Net.CodeAnalysis.Jass.Extensions; +using War3Net.CodeAnalysis.Jass.Syntax; + +using static Pidgin.Parser; + +namespace War3Net.CodeAnalysis.Jass +{ + internal partial class JassParser + { + internal static Parser GetGlobalConstantDeclarationParser( + Parser equalsValueClauseParser, + Parser identifierNameParser, + Parser typeParser, + Parser triviaParser, + Parser trailingTriviaParser) + { + return Map( + (constantToken, type, identifierName, value, trailingTrivia) => (JassGlobalDeclarationSyntax)new JassGlobalConstantDeclarationSyntax( + constantToken, + type, + identifierName, + value.AppendTrailingTrivia(trailingTrivia)), + Keyword.Constant.AsToken(triviaParser, JassSyntaxKind.ConstantKeyword), + typeParser, + identifierNameParser, + equalsValueClauseParser, + trailingTriviaParser); + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Parser/GlobalDeclarationListParser.cs b/src/War3Net.CodeAnalysis.Jass/Parser/GlobalDeclarationListParser.cs deleted file mode 100644 index d1287aa7..00000000 --- a/src/War3Net.CodeAnalysis.Jass/Parser/GlobalDeclarationListParser.cs +++ /dev/null @@ -1,27 +0,0 @@ -// ------------------------------------------------------------------------------ -// -// Licensed under the MIT license. -// See the LICENSE file in the project root for more information. -// -// ------------------------------------------------------------------------------ - -using System.Collections.Immutable; - -using Pidgin; - -using War3Net.CodeAnalysis.Jass.Syntax; - -namespace War3Net.CodeAnalysis.Jass -{ - internal partial class JassParser - { - internal static Parser GetGlobalDeclarationListParser( - Parser globalDeclarationParser, - Parser whitespaceParser, - Parser endOfLineParser) - { - return Keyword.Globals.Then(whitespaceParser).Then(endOfLineParser).Then(globalDeclarationParser.Many()).Before(Keyword.EndGlobals.Then(whitespaceParser)) - .Select(globals => new JassGlobalDeclarationListSyntax(globals.ToImmutableArray())); - } - } -} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Parser/GlobalDeclarationParser.cs b/src/War3Net.CodeAnalysis.Jass/Parser/GlobalDeclarationParser.cs index 80a43d75..009143f6 100644 --- a/src/War3Net.CodeAnalysis.Jass/Parser/GlobalDeclarationParser.cs +++ b/src/War3Net.CodeAnalysis.Jass/Parser/GlobalDeclarationParser.cs @@ -1,4 +1,4 @@ -// ------------------------------------------------------------------------------ +// ------------------------------------------------------------------------------ // // Licensed under the MIT license. // See the LICENSE file in the project root for more information. @@ -15,17 +15,13 @@ namespace War3Net.CodeAnalysis.Jass { internal partial class JassParser { - internal static Parser GetGlobalDeclarationParser( - Parser emptyParser, - Parser commentParser, - Parser constantDeclarationParser, - Parser variableDeclarationParser) + internal static Parser GetGlobalDeclarationParser( + Parser globalConstantDeclarationParser, + Parser globalVariableDeclarationParser) { return OneOf( - emptyParser.Cast(), - commentParser.Cast(), - constantDeclarationParser.Cast(), - variableDeclarationParser.Cast()); + globalConstantDeclarationParser, + globalVariableDeclarationParser); } } } \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Parser/GlobalLineParser.cs b/src/War3Net.CodeAnalysis.Jass/Parser/GlobalLineParser.cs deleted file mode 100644 index 0fbabc15..00000000 --- a/src/War3Net.CodeAnalysis.Jass/Parser/GlobalLineParser.cs +++ /dev/null @@ -1,33 +0,0 @@ -// ------------------------------------------------------------------------------ -// -// Licensed under the MIT license. -// See the LICENSE file in the project root for more information. -// -// ------------------------------------------------------------------------------ - -using Pidgin; - -using War3Net.CodeAnalysis.Jass.Syntax; - -using static Pidgin.Parser; - -namespace War3Net.CodeAnalysis.Jass -{ - internal partial class JassParser - { - internal static Parser GetGlobalLineParser( - Parser emptyLineParser, - Parser commentParser, - Parser constantDeclarationParser, - Parser variableDeclarationParser, - Parser whitespaceParser) - { - return OneOf( - emptyLineParser.Cast(), - commentParser.Cast(), - constantDeclarationParser.Cast(), - variableDeclarationParser.Cast(), - GetEndGlobalsCustomScriptActionParser(whitespaceParser)); - } - } -} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Parser/GlobalVariableDeclarationParser.cs b/src/War3Net.CodeAnalysis.Jass/Parser/GlobalVariableDeclarationParser.cs new file mode 100644 index 00000000..dfa3bb4b --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/Parser/GlobalVariableDeclarationParser.cs @@ -0,0 +1,29 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using Pidgin; + +using War3Net.CodeAnalysis.Jass.Extensions; +using War3Net.CodeAnalysis.Jass.Syntax; + +using static Pidgin.Parser; + +namespace War3Net.CodeAnalysis.Jass +{ + internal partial class JassParser + { + internal static Parser GetGlobalVariableDeclarationParser( + Parser variableOrArrayDeclaratorParser, + Parser trailingTriviaParser) + { + return Map( + (declarator, trailingTrivia) => (JassGlobalDeclarationSyntax)new JassGlobalVariableDeclarationSyntax(declarator.AppendTrailingTrivia(trailingTrivia)), + variableOrArrayDeclaratorParser, + trailingTriviaParser); + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Parser/GlobalsCustomScriptActionParser.cs b/src/War3Net.CodeAnalysis.Jass/Parser/GlobalsCustomScriptActionParser.cs deleted file mode 100644 index 0b9d89c3..00000000 --- a/src/War3Net.CodeAnalysis.Jass/Parser/GlobalsCustomScriptActionParser.cs +++ /dev/null @@ -1,21 +0,0 @@ -// ------------------------------------------------------------------------------ -// -// Licensed under the MIT license. -// See the LICENSE file in the project root for more information. -// -// ------------------------------------------------------------------------------ - -using Pidgin; - -using War3Net.CodeAnalysis.Jass.Syntax; - -namespace War3Net.CodeAnalysis.Jass -{ - internal partial class JassParser - { - internal static Parser GetGlobalsCustomScriptActionParser(Parser whitespaceParser) - { - return Keyword.Globals.Then(whitespaceParser).ThenReturn(JassGlobalsCustomScriptAction.Value); - } - } -} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Parser/GlobalsDeclarationParser.cs b/src/War3Net.CodeAnalysis.Jass/Parser/GlobalsDeclarationParser.cs new file mode 100644 index 00000000..04413f9c --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/Parser/GlobalsDeclarationParser.cs @@ -0,0 +1,35 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using System.Collections.Immutable; + +using Pidgin; + +using War3Net.CodeAnalysis.Jass.Extensions; +using War3Net.CodeAnalysis.Jass.Syntax; + +namespace War3Net.CodeAnalysis.Jass +{ + internal partial class JassParser + { + internal static Parser GetGlobalsDeclarationParser( + Parser globalDeclarationParser, + Parser leadingTriviaParser, + Parser trailingTriviaParser) + { + return globalDeclarationParser.UntilWithLeading( + leadingTriviaParser, + Keyword.Globals.AsToken(trailingTriviaParser, JassSyntaxKind.GlobalsKeyword), + Keyword.EndGlobals.AsToken(trailingTriviaParser, JassSyntaxKind.EndGlobalsKeyword), + (leadingTrivia, global) => global.WithLeadingTrivia(leadingTrivia), + (globalsToken, globalDeclarations, leadingTrivia, endGlobalsToken) => (JassTopLevelDeclarationSyntax)new JassGlobalsDeclarationSyntax( + globalsToken, + globalDeclarations.ToImmutableArray(), + endGlobalsToken.WithLeadingTrivia(leadingTrivia))); + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Parser/HexadecimalLiteralExpressionParser.cs b/src/War3Net.CodeAnalysis.Jass/Parser/HexadecimalLiteralExpressionParser.cs index 561873b0..bc4f86b9 100644 --- a/src/War3Net.CodeAnalysis.Jass/Parser/HexadecimalLiteralExpressionParser.cs +++ b/src/War3Net.CodeAnalysis.Jass/Parser/HexadecimalLiteralExpressionParser.cs @@ -1,4 +1,4 @@ -// ------------------------------------------------------------------------------ +// ------------------------------------------------------------------------------ // // Licensed under the MIT license. // See the LICENSE file in the project root for more information. @@ -7,6 +7,7 @@ using Pidgin; +using War3Net.CodeAnalysis.Jass.Extensions; using War3Net.CodeAnalysis.Jass.Syntax; using static Pidgin.Parser; @@ -15,10 +16,13 @@ namespace War3Net.CodeAnalysis.Jass { internal partial class JassParser { - internal static Parser GetHexadecimalLiteralExpressionParser() + internal static Parser GetHexadecimalLiteralExpressionParser( + Parser triviaParser) { - return Symbol.DollarSign.Or(Try(Symbol.Zero.Then(Symbol.X))).Then(UnsignedInt(16)) - .Select(value => new JassHexadecimalLiteralExpressionSyntax(value)) + return Symbol.Dollar.Or(Try(Symbol.Zero.Then(Symbol.X))).Then(UnsignedInt(16)) + .MapWithInput((s, _) => s.ToString()) + .AsToken(triviaParser, JassSyntaxKind.HexadecimalLiteralToken) + .Map(token => (JassExpressionSyntax)new JassLiteralExpressionSyntax(token)) .Labelled("hexadecimal literal"); } } diff --git a/src/War3Net.CodeAnalysis.Jass/Parser/IdentifierNameParser.cs b/src/War3Net.CodeAnalysis.Jass/Parser/IdentifierNameParser.cs index 6ad7e44b..9a93c30a 100644 --- a/src/War3Net.CodeAnalysis.Jass/Parser/IdentifierNameParser.cs +++ b/src/War3Net.CodeAnalysis.Jass/Parser/IdentifierNameParser.cs @@ -7,6 +7,7 @@ using Pidgin; +using War3Net.CodeAnalysis.Jass.Extensions; using War3Net.CodeAnalysis.Jass.Syntax; using static Pidgin.Parser; @@ -16,13 +17,19 @@ namespace War3Net.CodeAnalysis.Jass { internal partial class JassParser { - internal static Parser GetIdentifierNameParser(Parser whitespaceParser) + internal static Parser GetIdentifierParser() { return Try(Token(c => char.IsLetterOrDigit(c) || c == '_').AtLeastOnceString().Assert(value => !char.IsDigit(value[0]))) - .Then(value => JassSyntaxFacts.IsValidIdentifier(value) && !JassKeyword.IsKeyword(value) - ? Return(new JassIdentifierNameSyntax(value)) - : Fail($"'{value}' is not a valid identifier name")) - .Before(whitespaceParser) + .Assert(JassSyntaxFacts.IsValidIdentifier, value => $"'{value}' is not a valid identifier name"); + } + + internal static Parser GetIdentifierNameParser( + Parser identifierParser, + Parser triviaParser) + { + return identifierParser + .AsToken(triviaParser, JassSyntaxKind.IdentifierToken) + .Map(token => new JassIdentifierNameSyntax(token)) .Labelled("identifier name"); } } diff --git a/src/War3Net.CodeAnalysis.Jass/Parser/IfClauseDeclaratorParser.cs b/src/War3Net.CodeAnalysis.Jass/Parser/IfClauseDeclaratorParser.cs new file mode 100644 index 00000000..89c446a4 --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/Parser/IfClauseDeclaratorParser.cs @@ -0,0 +1,34 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using Pidgin; + +using War3Net.CodeAnalysis.Jass.Extensions; +using War3Net.CodeAnalysis.Jass.Syntax; + +using static Pidgin.Parser; + +namespace War3Net.CodeAnalysis.Jass +{ + internal partial class JassParser + { + internal static Parser GetIfClauseDeclaratorParser( + Parser expressionParser, + Parser triviaParser, + Parser trailingTriviaParser) + { + return Map( + (ifToken, condition, thenToken) => new JassIfClauseDeclaratorSyntax( + ifToken, + condition, + thenToken), + Keyword.If.AsToken(triviaParser, JassSyntaxKind.IfKeyword), + expressionParser, + Keyword.Then.AsToken(trailingTriviaParser, JassSyntaxKind.ThenKeyword)); + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Parser/IfCustomScriptActionParser.cs b/src/War3Net.CodeAnalysis.Jass/Parser/IfCustomScriptActionParser.cs deleted file mode 100644 index 5c98523f..00000000 --- a/src/War3Net.CodeAnalysis.Jass/Parser/IfCustomScriptActionParser.cs +++ /dev/null @@ -1,24 +0,0 @@ -// ------------------------------------------------------------------------------ -// -// Licensed under the MIT license. -// See the LICENSE file in the project root for more information. -// -// ------------------------------------------------------------------------------ - -using Pidgin; - -using War3Net.CodeAnalysis.Jass.Syntax; - -namespace War3Net.CodeAnalysis.Jass -{ - internal partial class JassParser - { - internal static Parser GetIfCustomScriptActionParser( - Parser expressionParser, - Parser whitespaceParser) - { - return Keyword.If.Then(whitespaceParser).Then(expressionParser).Before(Keyword.Then.Then(whitespaceParser)) - .Select(expression => new JassIfCustomScriptAction(expression)); - } - } -} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Parser/IfStatementParser.cs b/src/War3Net.CodeAnalysis.Jass/Parser/IfStatementParser.cs index c0237f29..e5dc442d 100644 --- a/src/War3Net.CodeAnalysis.Jass/Parser/IfStatementParser.cs +++ b/src/War3Net.CodeAnalysis.Jass/Parser/IfStatementParser.cs @@ -1,4 +1,4 @@ -// ------------------------------------------------------------------------------ +// ------------------------------------------------------------------------------ // // Licensed under the MIT license. // See the LICENSE file in the project root for more information. @@ -9,27 +9,37 @@ using Pidgin; +using War3Net.CodeAnalysis.Jass.Extensions; using War3Net.CodeAnalysis.Jass.Syntax; -using static Pidgin.Parser; - namespace War3Net.CodeAnalysis.Jass { internal partial class JassParser { - internal static Parser GetIfStatementParser( - Parser expressionParser, - Parser statementListParser, - Parser whitespaceParser, - Parser endOfLineParser) + internal static Parser GetIfStatementParser( + Parser statementParser, + Parser ifClauseDeclaratorParser, + Parser elseIfClauseDeclaratorParser, + Parser leadingTriviaParser, + Parser trailingTriviaParser) { - return Map( - (condition, statementList, elseIfClauses, elseClause, _) => (IStatementSyntax)new JassIfStatementSyntax(condition, statementList, elseIfClauses.ToImmutableArray(), elseClause.GetValueOrDefault()), - Keyword.If.Then(whitespaceParser).Then(expressionParser).Before(Keyword.Then.Then(whitespaceParser)).Before(endOfLineParser), - statementListParser, - GetElseIfClauseParser(expressionParser, statementListParser, whitespaceParser, endOfLineParser).Many(), - GetElseClauseParser(statementListParser, whitespaceParser, endOfLineParser).Optional(), - Keyword.EndIf.Then(whitespaceParser)); + return statementParser.IfThenElse( + leadingTriviaParser, + ifClauseDeclaratorParser, + elseIfClauseDeclaratorParser, + Keyword.Else.AsToken(trailingTriviaParser, JassSyntaxKind.ElseKeyword), + Keyword.EndIf.AsToken(trailingTriviaParser, JassSyntaxKind.EndIfKeyword), + (declarator, statements) => new JassIfClauseSyntax(declarator, statements.ToImmutableArray()), + (declarator, statements) => new JassElseIfClauseSyntax(declarator, statements.ToImmutableArray()), + (elseToken, statements) => new JassElseClauseSyntax(elseToken, statements.ToImmutableArray()), + (trivia, statement) => statement.WithLeadingTrivia(trivia), + (trivia, elseIfDeclarator) => elseIfDeclarator.WithLeadingTrivia(trivia), + (trivia, elseDeclarator) => elseDeclarator.WithLeadingTrivia(trivia), + (ifClause, elseIfClauses, elseClause, trivia, endIfToken) => (JassStatementSyntax)new JassIfStatementSyntax( + ifClause, + elseIfClauses.ToImmutableArray(), + elseClause, + endIfToken.WithLeadingTrivia(trivia))); } } } \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Parser/InvocationExpressionParser.cs b/src/War3Net.CodeAnalysis.Jass/Parser/InvocationExpressionParser.cs deleted file mode 100644 index a1057e65..00000000 --- a/src/War3Net.CodeAnalysis.Jass/Parser/InvocationExpressionParser.cs +++ /dev/null @@ -1,27 +0,0 @@ -// ------------------------------------------------------------------------------ -// -// Licensed under the MIT license. -// See the LICENSE file in the project root for more information. -// -// ------------------------------------------------------------------------------ - -using Pidgin; - -using War3Net.CodeAnalysis.Jass.Syntax; - -using static Pidgin.Parser; - -namespace War3Net.CodeAnalysis.Jass -{ - internal partial class JassParser - { - internal static Parser GetInvocationExpressionParser( - Parser whitespaceParser, - Parser expressionParser, - Parser identifierNameParser) - { - return Try(identifierNameParser.Before(Symbol.LeftParenthesis.Then(whitespaceParser))) - .Then(GetArgumentListParser(whitespaceParser, expressionParser).Before(Symbol.RightParenthesis.Then(whitespaceParser)), (id, arguments) => (IExpressionSyntax)new JassInvocationExpressionSyntax(id, arguments)); - } - } -} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Parser/LocalVariableDeclarationStatementParser.cs b/src/War3Net.CodeAnalysis.Jass/Parser/LocalVariableDeclarationStatementParser.cs index eef1310e..0344445e 100644 --- a/src/War3Net.CodeAnalysis.Jass/Parser/LocalVariableDeclarationStatementParser.cs +++ b/src/War3Net.CodeAnalysis.Jass/Parser/LocalVariableDeclarationStatementParser.cs @@ -1,4 +1,4 @@ -// ------------------------------------------------------------------------------ +// ------------------------------------------------------------------------------ // // Licensed under the MIT license. // See the LICENSE file in the project root for more information. @@ -7,18 +7,27 @@ using Pidgin; +using War3Net.CodeAnalysis.Jass.Extensions; using War3Net.CodeAnalysis.Jass.Syntax; +using static Pidgin.Parser; + namespace War3Net.CodeAnalysis.Jass { internal partial class JassParser { - internal static Parser GetLocalVariableDeclarationStatementParser( - Parser variableDeclaratorParser, - Parser whitespaceParser) + internal static Parser GetLocalVariableDeclarationStatementParser( + Parser variableOrArrayDeclaratorParser, + Parser triviaParser, + Parser trailingTriviaParser) { - return Keyword.Local.Then(whitespaceParser).Then(variableDeclaratorParser) - .Select(declarator => new JassLocalVariableDeclarationStatementSyntax(declarator)); + return Map( + (localToken, declarator, trailingTrivia) => (JassStatementSyntax)new JassLocalVariableDeclarationStatementSyntax( + localToken, + declarator.AppendTrailingTrivia(trailingTrivia)), + Keyword.Local.AsToken(triviaParser, JassSyntaxKind.LocalKeyword), + variableOrArrayDeclaratorParser, + trailingTriviaParser); } } } \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Parser/LoopCustomScriptActionParser.cs b/src/War3Net.CodeAnalysis.Jass/Parser/LoopCustomScriptActionParser.cs deleted file mode 100644 index 79b9f337..00000000 --- a/src/War3Net.CodeAnalysis.Jass/Parser/LoopCustomScriptActionParser.cs +++ /dev/null @@ -1,21 +0,0 @@ -// ------------------------------------------------------------------------------ -// -// Licensed under the MIT license. -// See the LICENSE file in the project root for more information. -// -// ------------------------------------------------------------------------------ - -using Pidgin; - -using War3Net.CodeAnalysis.Jass.Syntax; - -namespace War3Net.CodeAnalysis.Jass -{ - internal partial class JassParser - { - internal static Parser GetLoopCustomScriptActionParser(Parser whitespaceParser) - { - return Keyword.Loop.Then(whitespaceParser).ThenReturn(JassLoopCustomScriptAction.Value); - } - } -} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Parser/LoopStatementParser.cs b/src/War3Net.CodeAnalysis.Jass/Parser/LoopStatementParser.cs index 52f6b53f..9b2fb2ef 100644 --- a/src/War3Net.CodeAnalysis.Jass/Parser/LoopStatementParser.cs +++ b/src/War3Net.CodeAnalysis.Jass/Parser/LoopStatementParser.cs @@ -1,26 +1,35 @@ -// ------------------------------------------------------------------------------ +// ------------------------------------------------------------------------------ // // Licensed under the MIT license. // See the LICENSE file in the project root for more information. // // ------------------------------------------------------------------------------ +using System.Collections.Immutable; + using Pidgin; +using War3Net.CodeAnalysis.Jass.Extensions; using War3Net.CodeAnalysis.Jass.Syntax; namespace War3Net.CodeAnalysis.Jass { internal partial class JassParser { - internal static Parser GetLoopStatementParser( - Parser statementListParser, - Parser whitespaceParser, - Parser endOfLineParser) + internal static Parser GetLoopStatementParser( + Parser statementParser, + Parser leadingTriviaParser, + Parser trailingTriviaParser) { - return Keyword.Loop.Then(whitespaceParser).Then(endOfLineParser) - .Then(statementListParser).Before(Keyword.EndLoop.Then(whitespaceParser)) - .Select(statementList => new JassLoopStatementSyntax(statementList)); + return statementParser.UntilWithLeading( + leadingTriviaParser, + Keyword.Loop.AsToken(trailingTriviaParser, JassSyntaxKind.LoopKeyword), + Keyword.EndLoop.AsToken(trailingTriviaParser, JassSyntaxKind.EndLoopKeyword), + (leadingTrivia, statement) => statement.WithLeadingTrivia(leadingTrivia), + (loopToken, statements, leadingTrivia, endLoopToken) => (JassStatementSyntax)new JassLoopStatementSyntax( + loopToken, + statements.ToImmutableArray(), + endLoopToken.WithLeadingTrivia(leadingTrivia))); } } } \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Parser/NativeFunctionDeclarationParser.cs b/src/War3Net.CodeAnalysis.Jass/Parser/NativeFunctionDeclarationParser.cs index 1d23d8d4..b2fbd75e 100644 --- a/src/War3Net.CodeAnalysis.Jass/Parser/NativeFunctionDeclarationParser.cs +++ b/src/War3Net.CodeAnalysis.Jass/Parser/NativeFunctionDeclarationParser.cs @@ -1,12 +1,15 @@ -// ------------------------------------------------------------------------------ +// ------------------------------------------------------------------------------ // // Licensed under the MIT license. // See the LICENSE file in the project root for more information. // // ------------------------------------------------------------------------------ +using System; + using Pidgin; +using War3Net.CodeAnalysis.Jass.Extensions; using War3Net.CodeAnalysis.Jass.Syntax; using static Pidgin.Parser; @@ -15,12 +18,25 @@ namespace War3Net.CodeAnalysis.Jass { internal partial class JassParser { - internal static Parser GetNativeFunctionDeclarationParser( - Parser functionDeclaratorParser, - Parser whitespaceParser) + internal static Parser, JassTopLevelDeclarationSyntax>> GetNativeFunctionDeclarationParser( + Parser identifierNameParser, + Parser parameterListParser, + Parser returnClauseParser, + Parser triviaParser, + Parser trailingTriviaParser) { - return Try(Keyword.Constant.Then(whitespaceParser).Optional().Then(Keyword.Native.Then(whitespaceParser))).Then(functionDeclaratorParser) - .Select(functionDeclaration => new JassNativeFunctionDeclarationSyntax(functionDeclaration)); + return Map, JassTopLevelDeclarationSyntax>>( + (nativeToken, identifierName, parameterList, returnClause, trailingTrivia) => constantToken => new JassNativeFunctionDeclarationSyntax( + constantToken.GetValueOrDefault(), + nativeToken, + identifierName, + parameterList, + returnClause.AppendTrailingTrivia(trailingTrivia)), + Keyword.Native.AsToken(triviaParser, JassSyntaxKind.NativeKeyword), + identifierNameParser, + parameterListParser, + returnClauseParser, + trailingTriviaParser); } } } \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Parser/NewLineParser.cs b/src/War3Net.CodeAnalysis.Jass/Parser/NewLineParser.cs deleted file mode 100644 index e8a4a927..00000000 --- a/src/War3Net.CodeAnalysis.Jass/Parser/NewLineParser.cs +++ /dev/null @@ -1,23 +0,0 @@ -// ------------------------------------------------------------------------------ -// -// Licensed under the MIT license. -// See the LICENSE file in the project root for more information. -// -// ------------------------------------------------------------------------------ - -using Pidgin; - -using static Pidgin.Parser; - -namespace War3Net.CodeAnalysis.Jass -{ - internal partial class JassParser - { - internal static Parser GetNewLineParser(Parser whitespaceParser) - { - return OneOf( - Symbol.CarriageReturn.Before(Symbol.LineFeed.Optional()), - Symbol.LineFeed).Then(whitespaceParser).Labelled("newline"); - } - } -} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Parser/NullLiteralExpressionParser.cs b/src/War3Net.CodeAnalysis.Jass/Parser/NullLiteralExpressionParser.cs deleted file mode 100644 index b0b06978..00000000 --- a/src/War3Net.CodeAnalysis.Jass/Parser/NullLiteralExpressionParser.cs +++ /dev/null @@ -1,22 +0,0 @@ -// ------------------------------------------------------------------------------ -// -// Licensed under the MIT license. -// See the LICENSE file in the project root for more information. -// -// ------------------------------------------------------------------------------ - -using Pidgin; - -using War3Net.CodeAnalysis.Jass.Syntax; - -namespace War3Net.CodeAnalysis.Jass -{ - internal partial class JassParser - { - internal static Parser GetNullLiteralExpressionParser(Parser whitespaceParser) - { - return Keyword.Null.Then(whitespaceParser).ThenReturn(JassNullLiteralExpressionSyntax.Value) - .Labelled("null literal"); - } - } -} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Parser/OctalLiteralExpressionParser.cs b/src/War3Net.CodeAnalysis.Jass/Parser/OctalLiteralExpressionParser.cs index c09710b4..a164adb2 100644 --- a/src/War3Net.CodeAnalysis.Jass/Parser/OctalLiteralExpressionParser.cs +++ b/src/War3Net.CodeAnalysis.Jass/Parser/OctalLiteralExpressionParser.cs @@ -1,4 +1,4 @@ -// ------------------------------------------------------------------------------ +// ------------------------------------------------------------------------------ // // Licensed under the MIT license. // See the LICENSE file in the project root for more information. @@ -7,6 +7,7 @@ using Pidgin; +using War3Net.CodeAnalysis.Jass.Extensions; using War3Net.CodeAnalysis.Jass.Syntax; using static Pidgin.Parser; @@ -15,10 +16,13 @@ namespace War3Net.CodeAnalysis.Jass { internal partial class JassParser { - internal static Parser GetOctalLiteralExpressionParser() + internal static Parser GetOctalLiteralExpressionParser( + Parser triviaParser) { - return Try(Symbol.Zero.Then(UnsignedInt(8).Optional())) - .Select(value => new JassOctalLiteralExpressionSyntax(value.GetValueOrDefault())) + return Try(Symbol.Zero.Then(UnsignedInt(8))) + .MapWithInput((s, _) => s.ToString()) + .AsToken(triviaParser, JassSyntaxKind.OctalLiteralToken) + .Map(token => (JassExpressionSyntax)new JassLiteralExpressionSyntax(token)) .Labelled("octal literal"); } } diff --git a/src/War3Net.CodeAnalysis.Jass/Parser/ParameterListParser.cs b/src/War3Net.CodeAnalysis.Jass/Parser/ParameterListParser.cs index 4c91f2a1..f1a0c688 100644 --- a/src/War3Net.CodeAnalysis.Jass/Parser/ParameterListParser.cs +++ b/src/War3Net.CodeAnalysis.Jass/Parser/ParameterListParser.cs @@ -1,26 +1,41 @@ -// ------------------------------------------------------------------------------ +// ------------------------------------------------------------------------------ // // Licensed under the MIT license. // See the LICENSE file in the project root for more information. // // ------------------------------------------------------------------------------ -using System.Collections.Immutable; +using System; using Pidgin; +using War3Net.CodeAnalysis.Jass.Extensions; using War3Net.CodeAnalysis.Jass.Syntax; +using static Pidgin.Parser; + namespace War3Net.CodeAnalysis.Jass { internal partial class JassParser { - internal static Parser GetParameterListParser( - Parser whitespaceParser, + internal static Parser GetParameterListParser( + Parser triviaParser, Parser parameterParser) { - return parameterParser.Separated(Symbol.Comma.Then(whitespaceParser)) - .Select(parameters => new JassParameterListSyntax(parameters.ToImmutableArray())); + return Map( + (takesToken, parameterListFunc) => parameterListFunc(takesToken), + Keyword.Takes.AsToken(triviaParser, JassSyntaxKind.TakesKeyword), + OneOf( + Map>( + (nothingToken) => takesToken => new JassEmptyParameterListSyntax( + takesToken, + nothingToken), + Keyword.Nothing.AsToken(triviaParser, JassSyntaxKind.NothingKeyword)), + Map, Func>( + (parameterList) => takesToken => new JassParameterListSyntax( + takesToken, + parameterList), + parameterParser.SeparatedList(Symbol.Comma.AsToken(triviaParser, JassSyntaxKind.CommaToken, JassSymbol.Comma))))); } } } \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Parser/ParameterParser.cs b/src/War3Net.CodeAnalysis.Jass/Parser/ParameterParser.cs index 09c8c669..c94f0fe5 100644 --- a/src/War3Net.CodeAnalysis.Jass/Parser/ParameterParser.cs +++ b/src/War3Net.CodeAnalysis.Jass/Parser/ParameterParser.cs @@ -1,4 +1,4 @@ -// ------------------------------------------------------------------------------ +// ------------------------------------------------------------------------------ // // Licensed under the MIT license. // See the LICENSE file in the project root for more information. @@ -9,6 +9,8 @@ using War3Net.CodeAnalysis.Jass.Syntax; +using static Pidgin.Parser; + namespace War3Net.CodeAnalysis.Jass { internal partial class JassParser @@ -17,7 +19,10 @@ internal static Parser GetParameterParser( Parser identifierNameParser, Parser typeParser) { - return typeParser.Then(identifierNameParser, (type, id) => new JassParameterSyntax(type, id)); + return Map( + (type, identifierName) => new JassParameterSyntax(type, identifierName), + typeParser, + identifierNameParser.Labelled("parameter identifier name")); } } } \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Parser/ParenthesizedExpressionParser.cs b/src/War3Net.CodeAnalysis.Jass/Parser/ParenthesizedExpressionParser.cs index b9709b13..f55e5aa4 100644 --- a/src/War3Net.CodeAnalysis.Jass/Parser/ParenthesizedExpressionParser.cs +++ b/src/War3Net.CodeAnalysis.Jass/Parser/ParenthesizedExpressionParser.cs @@ -1,4 +1,4 @@ -// ------------------------------------------------------------------------------ +// ------------------------------------------------------------------------------ // // Licensed under the MIT license. // See the LICENSE file in the project root for more information. @@ -7,19 +7,27 @@ using Pidgin; +using War3Net.CodeAnalysis.Jass.Extensions; using War3Net.CodeAnalysis.Jass.Syntax; +using static Pidgin.Parser; + namespace War3Net.CodeAnalysis.Jass { internal partial class JassParser { - internal static Parser GetParenthesizedExpressionParser( - Parser whitespaceParser, - Parser expressionParser) + internal static Parser GetParenthesizedExpressionParser( + Parser triviaParser, + Parser expressionParser) { - return Symbol.LeftParenthesis.Then(whitespaceParser).Then(expressionParser).Before(Symbol.RightParenthesis.Then(whitespaceParser)) - .Select(expression => new JassParenthesizedExpressionSyntax(expression)) - .Labelled("parenthesized expression"); + return Map( + (openParenToken, expression, closeParenToken) => (JassExpressionSyntax)new JassParenthesizedExpressionSyntax( + openParenToken, + expression, + closeParenToken), + Symbol.OpenParen.AsToken(triviaParser, JassSyntaxKind.OpenParenToken, JassSymbol.OpenParen), + expressionParser, + Symbol.CloseParen.AsToken(triviaParser, JassSyntaxKind.CloseParenToken, JassSymbol.CloseParen)); } } } \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Parser/RealLiteralExpressionParser.cs b/src/War3Net.CodeAnalysis.Jass/Parser/RealLiteralExpressionParser.cs index c5cb8567..982a42a6 100644 --- a/src/War3Net.CodeAnalysis.Jass/Parser/RealLiteralExpressionParser.cs +++ b/src/War3Net.CodeAnalysis.Jass/Parser/RealLiteralExpressionParser.cs @@ -1,4 +1,4 @@ -// ------------------------------------------------------------------------------ +// ------------------------------------------------------------------------------ // // Licensed under the MIT license. // See the LICENSE file in the project root for more information. @@ -7,6 +7,7 @@ using Pidgin; +using War3Net.CodeAnalysis.Jass.Extensions; using War3Net.CodeAnalysis.Jass.Syntax; using static Pidgin.Parser; @@ -16,21 +17,16 @@ namespace War3Net.CodeAnalysis.Jass { internal partial class JassParser { - internal static Parser GetRealLiteralExpressionParser() + internal static Parser GetRealLiteralExpressionParser( + Parser triviaParser) { -#if true - return Try(Token(char.IsDigit).AtLeastOnceString().Before(Symbol.FullStop)) - .Then(Token(char.IsDigit).ManyString(), (intPart, fracPart) => (IExpressionSyntax)new JassRealLiteralExpressionSyntax(intPart, fracPart)) - .Or(Symbol.FullStop.Then(Token(char.IsDigit).AtLeastOnceString()) - .Select(fracPart => new JassRealLiteralExpressionSyntax($"{JassSymbol.Zero}", fracPart))) - .Labelled("real literal"); -#else - return Try(Token(char.IsDigit).AtLeastOnceString().Before(Symbol.FullStop)) - .Then(Token(char.IsDigit).ManyString(), (intPart, fracPart) => (IExpressionSyntax)new JassRealLiteralExpressionSyntax(float.Parse($"{intPart}{JassSymbol.FullStop}{fracPart}", CultureInfo.InvariantCulture))) - .Or(Symbol.FullStop.Then(Token(char.IsDigit).AtLeastOnceString()) - .Select(fracPart => new JassRealLiteralExpressionSyntax(float.Parse($"{JassSymbol.Zero}{JassSymbol.FullStop}{fracPart}", CultureInfo.InvariantCulture)))) + return OneOf( + Try(Token(char.IsDigit).AtLeastOnce().Before(Symbol.Dot)).Then(Token(char.IsDigit).Many()), + Symbol.Dot.Then(Token(char.IsDigit).AtLeastOnce())) + .MapWithInput((s, _) => s.ToString()) + .AsToken(triviaParser, JassSyntaxKind.RealLiteralToken) + .Map(token => (JassExpressionSyntax)new JassLiteralExpressionSyntax(token)) .Labelled("real literal"); -#endif } } } \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Parser/ReturnClauseParser.cs b/src/War3Net.CodeAnalysis.Jass/Parser/ReturnClauseParser.cs new file mode 100644 index 00000000..b064cfcf --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/Parser/ReturnClauseParser.cs @@ -0,0 +1,31 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using Pidgin; + +using War3Net.CodeAnalysis.Jass.Extensions; +using War3Net.CodeAnalysis.Jass.Syntax; + +using static Pidgin.Parser; + +namespace War3Net.CodeAnalysis.Jass +{ + internal partial class JassParser + { + internal static Parser GetReturnClauseParser( + Parser typeParser, + Parser triviaParser) + { + return Map( + (returnsToken, type) => new JassReturnClauseSyntax( + returnsToken, + type), + Keyword.Returns.AsToken(triviaParser, JassSyntaxKind.ReturnsKeyword), + typeParser); + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Parser/ReturnStatementParser.cs b/src/War3Net.CodeAnalysis.Jass/Parser/ReturnStatementParser.cs index 754b33ef..678774a9 100644 --- a/src/War3Net.CodeAnalysis.Jass/Parser/ReturnStatementParser.cs +++ b/src/War3Net.CodeAnalysis.Jass/Parser/ReturnStatementParser.cs @@ -1,4 +1,4 @@ -// ------------------------------------------------------------------------------ +// ------------------------------------------------------------------------------ // // Licensed under the MIT license. // See the LICENSE file in the project root for more information. @@ -7,18 +7,27 @@ using Pidgin; +using War3Net.CodeAnalysis.Jass.Extensions; using War3Net.CodeAnalysis.Jass.Syntax; +using static Pidgin.Parser; + namespace War3Net.CodeAnalysis.Jass { internal partial class JassParser { - internal static Parser GetReturnStatementParser( - Parser expressionParser, - Parser whitespaceParser) + internal static Parser GetReturnStatementParser( + Parser expressionParser, + Parser triviaParser, + Parser trailingTriviaParser) { - return Keyword.Return.Then(whitespaceParser).Then(expressionParser.Optional()) - .Select(expression => expression.HasValue ? new JassReturnStatementSyntax(expression.Value) : JassReturnStatementSyntax.Empty); + return Map( + (returnToken, expression, trailingTrivia) => (JassStatementSyntax)new JassReturnStatementSyntax( + returnToken, + expression.GetValueOrDefault()).AppendTrailingTrivia(trailingTrivia), + Keyword.Return.AsToken(triviaParser, JassSyntaxKind.ReturnKeyword), + expressionParser.Optional(), + trailingTriviaParser); } } } \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Parser/SetStatementParser.cs b/src/War3Net.CodeAnalysis.Jass/Parser/SetStatementParser.cs index 6493fdf1..f1623cc9 100644 --- a/src/War3Net.CodeAnalysis.Jass/Parser/SetStatementParser.cs +++ b/src/War3Net.CodeAnalysis.Jass/Parser/SetStatementParser.cs @@ -1,4 +1,4 @@ -// ------------------------------------------------------------------------------ +// ------------------------------------------------------------------------------ // // Licensed under the MIT license. // See the LICENSE file in the project root for more information. @@ -7,6 +7,7 @@ using Pidgin; +using War3Net.CodeAnalysis.Jass.Extensions; using War3Net.CodeAnalysis.Jass.Syntax; using static Pidgin.Parser; @@ -15,17 +16,24 @@ namespace War3Net.CodeAnalysis.Jass { internal partial class JassParser { - internal static Parser GetSetStatementParser( - Parser whitespaceParser, - Parser expressionParser, + internal static Parser GetSetStatementParser( + Parser identifierNameParser, + Parser elementAccessClauseParser, Parser equalsValueClauseParser, - Parser identifierNameParser) + Parser triviaParser, + Parser trailingTriviaParser) { return Map( - (id, indexer, equals) => new JassSetStatementSyntax(id, indexer.GetValueOrDefault(), equals), - Keyword.Set.Then(whitespaceParser).Then(identifierNameParser), - Symbol.LeftSquareBracket.Then(whitespaceParser).Then(expressionParser).Before(Symbol.RightSquareBracket.Then(whitespaceParser)).Optional(), - equalsValueClauseParser); + (setToken, identifierName, elementAccessClause, value, trailingTrivia) => (JassStatementSyntax)new JassSetStatementSyntax( + setToken, + identifierName, + elementAccessClause.GetValueOrDefault(), + value.AppendTrailingTrivia(trailingTrivia)), + Keyword.Set.AsToken(triviaParser, JassSyntaxKind.SetKeyword), + identifierNameParser, + elementAccessClauseParser.Optional(), + equalsValueClauseParser, + trailingTriviaParser); } } } \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Parser/StatementLineParser.cs b/src/War3Net.CodeAnalysis.Jass/Parser/StatementLineParser.cs deleted file mode 100644 index 07506f73..00000000 --- a/src/War3Net.CodeAnalysis.Jass/Parser/StatementLineParser.cs +++ /dev/null @@ -1,52 +0,0 @@ -// ------------------------------------------------------------------------------ -// -// Licensed under the MIT license. -// See the LICENSE file in the project root for more information. -// -// ------------------------------------------------------------------------------ - -using Pidgin; - -using War3Net.CodeAnalysis.Jass.Syntax; - -using static Pidgin.Parser; - -namespace War3Net.CodeAnalysis.Jass -{ - internal partial class JassParser - { - internal static Parser GetStatementLineParser( - Parser emptyLineParser, - Parser commentParser, - Parser localVariableDeclarationStatementParser, - Parser setCustomScriptActionParser, - Parser callCustomScriptActionParser, - Parser exitStatementParser, - Parser returnStatementParser, - Parser expressionParser, - Parser whitespaceParser) - { - var setParser = setCustomScriptActionParser.Cast(); - var callParser = callCustomScriptActionParser.Cast(); - var ifCustomScriptActionParser = GetIfCustomScriptActionParser(expressionParser, whitespaceParser); - var loopCustomScriptActionParser = GetLoopCustomScriptActionParser(whitespaceParser); - - return OneOf( - emptyLineParser.Cast(), - commentParser.Cast(), - localVariableDeclarationStatementParser.Cast(), - setParser, - callParser, - ifCustomScriptActionParser, - GetElseIfCustomScriptActionParser(expressionParser, whitespaceParser), - GetElseCustomScriptActionParser(whitespaceParser), - GetEndIfCustomScriptActionParser(whitespaceParser), - loopCustomScriptActionParser, - GetEndLoopCustomScriptActionParser(whitespaceParser), - exitStatementParser.Cast(), - returnStatementParser.Cast(), - GetEndFunctionCustomScriptActionParser(whitespaceParser), - GetDebugCustomScriptActionParser(setParser, callParser, ifCustomScriptActionParser, loopCustomScriptActionParser, whitespaceParser)); - } - } -} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Parser/StatementListParser.cs b/src/War3Net.CodeAnalysis.Jass/Parser/StatementListParser.cs deleted file mode 100644 index 54585424..00000000 --- a/src/War3Net.CodeAnalysis.Jass/Parser/StatementListParser.cs +++ /dev/null @@ -1,26 +0,0 @@ -// ------------------------------------------------------------------------------ -// -// Licensed under the MIT license. -// See the LICENSE file in the project root for more information. -// -// ------------------------------------------------------------------------------ - -using System.Collections.Immutable; - -using Pidgin; - -using War3Net.CodeAnalysis.Jass.Syntax; - -namespace War3Net.CodeAnalysis.Jass -{ - internal partial class JassParser - { - internal static Parser GetStatementListParser( - Parser statementParser, - Parser endOfLineParser) - { - return statementParser.Before(endOfLineParser).Many() - .Select(statements => new JassStatementListSyntax(statements.ToImmutableArray())); - } - } -} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Parser/StatementParser.cs b/src/War3Net.CodeAnalysis.Jass/Parser/StatementParser.cs index 0342ef4e..8b7c8f14 100644 --- a/src/War3Net.CodeAnalysis.Jass/Parser/StatementParser.cs +++ b/src/War3Net.CodeAnalysis.Jass/Parser/StatementParser.cs @@ -1,4 +1,4 @@ -// ------------------------------------------------------------------------------ +// ------------------------------------------------------------------------------ // // Licensed under the MIT license. // See the LICENSE file in the project root for more information. @@ -15,39 +15,38 @@ namespace War3Net.CodeAnalysis.Jass { internal partial class JassParser { - internal static Parser GetStatementParser( - Parser emptyParser, - Parser commentParser, - Parser localVariableDeclarationStatementParser, - Parser exitStatementParser, - Parser returnStatementParser, - Parser setStatementParser, - Parser callStatementParser, - Parser expressionParser, - Parser whitespaceParser, - Parser endOfLineParser) + internal static Parser GetStatementParser( + Parser localVariableDeclarationStatementParser, + Parser exitStatementParser, + Parser returnStatementParser, + Parser setStatementParser, + Parser callStatementParser, + Parser ifClauseDeclaratorParser, + Parser elseIfClauseDeclaratorParser, + Parser triviaParser, + Parser leadingTriviaParser, + Parser trailingTriviaParser) { - var setParser = setStatementParser.Cast(); - var callParser = callStatementParser.Cast(); - - return Rec( + return Rec( statementParser => { - var statementListParser = GetStatementListParser( - statementParser, - endOfLineParser); + var ifStatementParser = GetIfStatementParser(statementParser, ifClauseDeclaratorParser, elseIfClauseDeclaratorParser, leadingTriviaParser, trailingTriviaParser); + var loopStatementParser = GetLoopStatementParser(statementParser, leadingTriviaParser, trailingTriviaParser); return OneOf( - emptyParser.Cast(), - commentParser.Cast(), - localVariableDeclarationStatementParser.Cast(), - setParser, - callParser, - GetIfStatementParser(expressionParser, statementListParser, whitespaceParser, endOfLineParser), - GetLoopStatementParser(statementListParser, whitespaceParser, endOfLineParser), - exitStatementParser.Cast(), - returnStatementParser.Cast(), - GetDebugStatementParser(expressionParser, statementListParser, setParser, callParser, whitespaceParser, endOfLineParser)); + localVariableDeclarationStatementParser, + setStatementParser, + callStatementParser, + ifStatementParser, + loopStatementParser, + exitStatementParser, + returnStatementParser, + GetDebugStatementParser( + setStatementParser, + callStatementParser, + ifStatementParser, + loopStatementParser, + triviaParser)); }); } } diff --git a/src/War3Net.CodeAnalysis.Jass/Parser/StringLiteralExpressionParser.cs b/src/War3Net.CodeAnalysis.Jass/Parser/StringLiteralExpressionParser.cs index 926899aa..0780208e 100644 --- a/src/War3Net.CodeAnalysis.Jass/Parser/StringLiteralExpressionParser.cs +++ b/src/War3Net.CodeAnalysis.Jass/Parser/StringLiteralExpressionParser.cs @@ -16,11 +16,12 @@ namespace War3Net.CodeAnalysis.Jass { internal partial class JassParser { - internal static Parser GetStringLiteralExpressionParser() + internal static Parser GetStringLiteralExpressionParser( + Parser triviaParser) { var escapeSequenceParser = OneOf( - Symbol.QuotationMark.ThenReturn($"\\{JassSymbol.QuotationMark}"), - Symbol.Apostrophe.ThenReturn($"\\{JassSymbol.Apostrophe}"), + Symbol.DoubleQuote.ThenReturn($"\\{JassSymbol.DoubleQuoteChar}"), + Symbol.SingleQuote.ThenReturn($"\\{JassSymbol.SingleQuoteChar}"), Char('r').ThenReturn("\\r"), Char('n').ThenReturn("\\n"), Char('t').ThenReturn("\\t"), @@ -30,8 +31,14 @@ internal static Parser GetStringLiteralExpressionParser Any.Then(c => Fail($"\"\\{c}\" is not a valid escape sequence"))) .Labelled("escape sequence"); - return Char('\\').Then(escapeSequenceParser).Or(AnyCharExcept(JassSymbol.QuotationMark).Map(char.ToString)).ManyString().Between(Symbol.QuotationMark) - .Select(value => new JassStringLiteralExpressionSyntax(value)) + var stringLiteralParser = Char('\\').Then(escapeSequenceParser).Or(AnyCharExcept(JassSymbol.DoubleQuoteChar).Map(char.ToString)).ManyString().Between(Symbol.DoubleQuote) + .Labelled("string literal"); + + return Map( + (value, trivia) => (JassExpressionSyntax)new JassLiteralExpressionSyntax( + new JassSyntaxToken(JassSyntaxKind.StringLiteralToken, $"{JassSymbol.DoubleQuoteChar}{value}{JassSymbol.DoubleQuoteChar}", trivia)), + stringLiteralParser, + triviaParser) .Labelled("string literal"); } } diff --git a/src/War3Net.CodeAnalysis.Jass/Parser/SyntaxTriviaListParser.cs b/src/War3Net.CodeAnalysis.Jass/Parser/SyntaxTriviaListParser.cs new file mode 100644 index 00000000..f9276b0c --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/Parser/SyntaxTriviaListParser.cs @@ -0,0 +1,63 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using System.Collections.Immutable; +using System.Linq; + +using Pidgin; + +using War3Net.CodeAnalysis.Jass.Syntax; + +using static Pidgin.Parser; +using static Pidgin.Parser; + +namespace War3Net.CodeAnalysis.Jass +{ + internal partial class JassParser + { + internal static Parser GetSimpleTriviaListParser( + Parser whitespaceTriviaParser) + { + return OneOf( + whitespaceTriviaParser.Many().Select(trivia => new JassSyntaxTriviaList(trivia.ToImmutableArray())), + Return(JassSyntaxTriviaList.Empty)); + } + + internal static Parser GetLeadingTriviaListParser( + Parser whitespaceTriviaParser, + Parser newLineTriviaParser, + Parser singleLineCommentTriviaParser) + { + return OneOf( + OneOf( + whitespaceTriviaParser, + newLineTriviaParser, + singleLineCommentTriviaParser) + .Many() + .Select(trivia => new JassSyntaxTriviaList(trivia.ToImmutableArray())), + Return(JassSyntaxTriviaList.Empty)); + } + + internal static Parser GetTrailingTriviaListParser( + Parser whitespaceTriviaParser, + Parser singleNewLineTriviaParser, + Parser singleLineCommentTriviaParser) + { + return OneOf( + OneOf( + whitespaceTriviaParser, + singleLineCommentTriviaParser) + .Many() + .Then( + OneOf( + singleNewLineTriviaParser.Select(newLine => Maybe.Just(newLine)), + End.ThenReturn(Maybe.Nothing())), + (trivia, newLine) => new JassSyntaxTriviaList((newLine.HasValue ? trivia.Append(newLine.Value) : trivia).ToImmutableArray())), + Return(JassSyntaxTriviaList.Empty)); + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Parser/SyntaxTriviaParser.cs b/src/War3Net.CodeAnalysis.Jass/Parser/SyntaxTriviaParser.cs new file mode 100644 index 00000000..43cc76f6 --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/Parser/SyntaxTriviaParser.cs @@ -0,0 +1,50 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using Pidgin; + +using War3Net.CodeAnalysis.Jass.Syntax; + +using static Pidgin.Parser; +using static Pidgin.Parser; + +namespace War3Net.CodeAnalysis.Jass +{ + internal partial class JassParser + { + internal static Parser GetWhitespaceTriviaParser() + { + return Token(JassSyntaxFacts.IsWhitespaceCharacter) + .AtLeastOnceString() + .Select(whitespace => new JassSyntaxTrivia(JassSyntaxKind.WhitespaceTrivia, whitespace)); + } + + internal static Parser GetNewLineTriviaParser() + { + return OneOf(Symbol.CarriageReturn, Symbol.LineFeed) + .AtLeastOnceString() + .Select(newLine => new JassSyntaxTrivia(JassSyntaxKind.NewLineTrivia, newLine)); + } + + internal static Parser GetSingleNewLineTriviaParser() + { + return OneOf( + Try(Symbol.CarriageReturnLineFeed), + Symbol.CarriageReturnString, + Symbol.LineFeedString) + .Select(newLine => new JassSyntaxTrivia(JassSyntaxKind.NewLineTrivia, newLine)); + } + + internal static Parser GetSingleLineCommentTriviaParser() + { + return Map( + (_, commentString) => new JassSyntaxTrivia(JassSyntaxKind.SingleLineCommentTrivia, $"{JassSymbol.SlashSlash}{commentString}"), + Try(Symbol.SlashSlash), + AnyCharExcept(JassSymbol.CarriageReturnChar, JassSymbol.LineFeedChar).ManyString()); + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Parser/TopLevelDeclarationParser.cs b/src/War3Net.CodeAnalysis.Jass/Parser/TopLevelDeclarationParser.cs new file mode 100644 index 00000000..4587d124 --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/Parser/TopLevelDeclarationParser.cs @@ -0,0 +1,41 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using System; + +using Pidgin; + +using War3Net.CodeAnalysis.Jass.Extensions; +using War3Net.CodeAnalysis.Jass.Syntax; + +using static Pidgin.Parser; + +namespace War3Net.CodeAnalysis.Jass +{ + internal partial class JassParser + { + internal static Parser GetTopLevelDeclarationParser( + Parser typeDeclarationParser, + Parser, JassTopLevelDeclarationSyntax>> nativeFunctionDeclarationParser, + Parser, JassTopLevelDeclarationSyntax>> functionDeclarationParser, + Parser globalDeclarationParser, + Parser triviaParser, + Parser leadingTriviaParser, + Parser trailingTriviaParser) + { + return OneOf( + typeDeclarationParser, + GetGlobalsDeclarationParser(globalDeclarationParser, leadingTriviaParser, trailingTriviaParser), + Map( + (constantToken, declarationFunc) => declarationFunc(constantToken), + Keyword.Constant.AsToken(triviaParser, JassSyntaxKind.ConstantKeyword).Optional(), + OneOf( + nativeFunctionDeclarationParser, + functionDeclarationParser))); + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Parser/TypeDeclarationParser.cs b/src/War3Net.CodeAnalysis.Jass/Parser/TypeDeclarationParser.cs index 2fc9e60c..127e30c0 100644 --- a/src/War3Net.CodeAnalysis.Jass/Parser/TypeDeclarationParser.cs +++ b/src/War3Net.CodeAnalysis.Jass/Parser/TypeDeclarationParser.cs @@ -1,4 +1,4 @@ -// ------------------------------------------------------------------------------ +// ------------------------------------------------------------------------------ // // Licensed under the MIT license. // See the LICENSE file in the project root for more information. @@ -7,20 +7,32 @@ using Pidgin; +using War3Net.CodeAnalysis.Jass.Extensions; using War3Net.CodeAnalysis.Jass.Syntax; +using static Pidgin.Parser; + namespace War3Net.CodeAnalysis.Jass { internal partial class JassParser { - internal static Parser GetTypeDeclarationParser( + internal static Parser GetTypeDeclarationParser( Parser identifierNameParser, Parser typeParser, - Parser whitespaceParser) + Parser triviaParser, + Parser trailingTriviaParser) { - return Keyword.Type.Then(whitespaceParser).Then(identifierNameParser).Then( - Keyword.Extends.Then(whitespaceParser).Then(typeParser), - (@new, @base) => new JassTypeDeclarationSyntax(@new, @base)); + return Map( + (typeToken, identifierName, extendsToken, type, trailingTrivia) => (JassTopLevelDeclarationSyntax)new JassTypeDeclarationSyntax( + typeToken, + identifierName, + extendsToken, + type.AppendTrailingTrivia(trailingTrivia)), + Keyword.Type.AsToken(triviaParser, JassSyntaxKind.TypeKeyword), + identifierNameParser, + Keyword.Extends.AsToken(triviaParser, JassSyntaxKind.ExtendsKeyword), + typeParser, + trailingTriviaParser); } } } \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Parser/TypeParser.cs b/src/War3Net.CodeAnalysis.Jass/Parser/TypeParser.cs index 7f39d22f..30e316f1 100644 --- a/src/War3Net.CodeAnalysis.Jass/Parser/TypeParser.cs +++ b/src/War3Net.CodeAnalysis.Jass/Parser/TypeParser.cs @@ -5,6 +5,8 @@ // // ------------------------------------------------------------------------------ +using System.Collections.Generic; + using Pidgin; using War3Net.CodeAnalysis.Jass.Syntax; @@ -15,19 +17,35 @@ namespace War3Net.CodeAnalysis.Jass { internal partial class JassParser { + private static readonly Dictionary _predefinedTypeSyntaxKinds = GetPredefinedTypeSyntaxKinds(); + internal static Parser GetTypeParser( - Parser identifierNameParser, - Parser whitespaceParser) + Parser identifierParser, + Parser triviaParser) + { + return Map( + (text, trivia) => new JassSyntaxToken( + _predefinedTypeSyntaxKinds.GetValueOrDefault(text, JassSyntaxKind.IdentifierToken), + text, + trivia), + identifierParser.Assert(JassSyntaxFacts.IsNotReservedKeyword), + triviaParser).Select(token => token.SyntaxKind == JassSyntaxKind.IdentifierToken + ? new JassIdentifierNameSyntax(token) + : new JassPredefinedTypeSyntax(token)); + } + + private static Dictionary GetPredefinedTypeSyntaxKinds() { - return OneOf( - Keyword.Code.Then(whitespaceParser).ThenReturn(JassTypeSyntax.Code), - Keyword.Handle.Then(whitespaceParser).ThenReturn(JassTypeSyntax.Handle), - Keyword.Integer.Then(whitespaceParser).ThenReturn(JassTypeSyntax.Integer), - Keyword.Real.Then(whitespaceParser).ThenReturn(JassTypeSyntax.Real), - Keyword.Boolean.Then(whitespaceParser).ThenReturn(JassTypeSyntax.Boolean), - Keyword.String.Then(whitespaceParser).ThenReturn(JassTypeSyntax.String), - identifierNameParser.Map(id => new JassTypeSyntax(id))) - .Labelled("type"); + return new Dictionary + { + { JassKeyword.Boolean, JassSyntaxKind.BooleanKeyword }, + { JassKeyword.Code, JassSyntaxKind.CodeKeyword }, + { JassKeyword.Handle, JassSyntaxKind.HandleKeyword }, + { JassKeyword.Integer, JassSyntaxKind.IntegerKeyword }, + { JassKeyword.Nothing, JassSyntaxKind.NothingKeyword }, + { JassKeyword.Real, JassSyntaxKind.RealKeyword }, + { JassKeyword.String, JassSyntaxKind.StringKeyword }, + }; } } } \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Parser/UnaryOperatorParser.cs b/src/War3Net.CodeAnalysis.Jass/Parser/UnaryOperatorParser.cs index e5dca025..7de241d4 100644 --- a/src/War3Net.CodeAnalysis.Jass/Parser/UnaryOperatorParser.cs +++ b/src/War3Net.CodeAnalysis.Jass/Parser/UnaryOperatorParser.cs @@ -1,4 +1,4 @@ -// ------------------------------------------------------------------------------ +// ------------------------------------------------------------------------------ // // Licensed under the MIT license. // See the LICENSE file in the project root for more information. @@ -7,6 +7,7 @@ using Pidgin; +using War3Net.CodeAnalysis.Jass.Extensions; using War3Net.CodeAnalysis.Jass.Syntax; using static Pidgin.Parser; @@ -15,27 +16,31 @@ namespace War3Net.CodeAnalysis.Jass { internal partial class JassParser { - internal static Parser GetUnaryOperatorParser(Parser whitespaceParser) + internal static Parser GetUnaryOperatorParser( + Parser triviaParser) { return OneOf( - GetUnaryPlusOperatorParser(whitespaceParser), - GetUnaryMinusOperatorParser(whitespaceParser), - GetUnaryNotOperatorParser(whitespaceParser)); + GetUnaryPlusOperatorParser(triviaParser), + GetUnaryMinusOperatorParser(triviaParser), + GetUnaryNotOperatorParser(triviaParser)); } - internal static Parser GetUnaryPlusOperatorParser(Parser whitespaceParser) + internal static Parser GetUnaryPlusOperatorParser( + Parser triviaParser) { - return Symbol.PlusSign.Then(whitespaceParser).ThenReturn(UnaryOperatorType.Plus); + return Symbol.Plus.AsToken(triviaParser, JassSyntaxKind.PlusToken, JassSymbol.Plus); } - internal static Parser GetUnaryMinusOperatorParser(Parser whitespaceParser) + internal static Parser GetUnaryMinusOperatorParser( + Parser triviaParser) { - return Symbol.MinusSign.Then(whitespaceParser).ThenReturn(UnaryOperatorType.Minus); + return Symbol.Minus.AsToken(triviaParser, JassSyntaxKind.MinusToken, JassSymbol.Minus); } - internal static Parser GetUnaryNotOperatorParser(Parser whitespaceParser) + internal static Parser GetUnaryNotOperatorParser( + Parser triviaParser) { - return Keyword.Not.Then(whitespaceParser).ThenReturn(UnaryOperatorType.Not); + return Keyword.Not.AsToken(triviaParser, JassSyntaxKind.NotKeyword); } } } \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Parser/VariableDeclarationParser.cs b/src/War3Net.CodeAnalysis.Jass/Parser/VariableDeclarationParser.cs deleted file mode 100644 index f5d9a736..00000000 --- a/src/War3Net.CodeAnalysis.Jass/Parser/VariableDeclarationParser.cs +++ /dev/null @@ -1,28 +0,0 @@ -// ------------------------------------------------------------------------------ -// -// Licensed under the MIT license. -// See the LICENSE file in the project root for more information. -// -// ------------------------------------------------------------------------------ - -using Pidgin; - -using War3Net.CodeAnalysis.Jass.Syntax; - -using static Pidgin.Parser; - -namespace War3Net.CodeAnalysis.Jass -{ - internal partial class JassParser - { - internal static Parser GetVariableDeclarationParser( - Parser equalsValueClauseParser, - Parser identifierNameParser, - Parser typeParser, - Parser whitespaceParser) - { - return GetVariableDeclaratorParser(equalsValueClauseParser, identifierNameParser, Try(typeParser), whitespaceParser) - .Select(declarator => new JassGlobalDeclarationSyntax(declarator)); - } - } -} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Parser/VariableDeclaratorParser.cs b/src/War3Net.CodeAnalysis.Jass/Parser/VariableDeclaratorParser.cs deleted file mode 100644 index 6c23918b..00000000 --- a/src/War3Net.CodeAnalysis.Jass/Parser/VariableDeclaratorParser.cs +++ /dev/null @@ -1,36 +0,0 @@ -// ------------------------------------------------------------------------------ -// -// Licensed under the MIT license. -// See the LICENSE file in the project root for more information. -// -// ------------------------------------------------------------------------------ - -using Pidgin; - -using War3Net.CodeAnalysis.Jass.Syntax; - -using static Pidgin.Parser; - -namespace War3Net.CodeAnalysis.Jass -{ - internal partial class JassParser - { - internal static Parser GetVariableDeclaratorParser( - Parser equalsValueClauseParser, - Parser identifierNameParser, - Parser typeParser, - Parser whitespaceParser) - { - return OneOf( - Map( - (type, id) => (IVariableDeclaratorSyntax)new JassArrayDeclaratorSyntax(type, id), - Try(typeParser.Before(Keyword.Array)).Before(whitespaceParser), - identifierNameParser), - Map( - (type, id, value) => (IVariableDeclaratorSyntax)new JassVariableDeclaratorSyntax(type, id, value.GetValueOrDefault()), - typeParser, - identifierNameParser, - equalsValueClauseParser.Optional())); - } - } -} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Parser/VariableOrArrayDeclaratorParser.cs b/src/War3Net.CodeAnalysis.Jass/Parser/VariableOrArrayDeclaratorParser.cs new file mode 100644 index 00000000..819c7169 --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/Parser/VariableOrArrayDeclaratorParser.cs @@ -0,0 +1,47 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using System; + +using Pidgin; + +using War3Net.CodeAnalysis.Jass.Extensions; +using War3Net.CodeAnalysis.Jass.Syntax; + +using static Pidgin.Parser; + +namespace War3Net.CodeAnalysis.Jass +{ + internal partial class JassParser + { + internal static Parser GetVariableOrArrayDeclaratorParser( + Parser equalsValueClauseParser, + Parser identifierNameParser, + Parser typeParser, + Parser triviaParser) + { + return Map( + (type, declaratorFunc) => declaratorFunc(type), + typeParser, + OneOf( + Map>( + (arrayToken, identifierName) => type => new JassArrayDeclaratorSyntax( + type, + arrayToken, + identifierName), + Keyword.Array.AsToken(triviaParser, JassSyntaxKind.ArrayKeyword), + identifierNameParser), + Map, Func>( + (identifierName, value) => type => new JassVariableDeclaratorSyntax( + type, + identifierName, + value.GetValueOrDefault()), + identifierNameParser, + equalsValueClauseParser.Optional()))); + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Parser/VariableReferenceExpressionParser.cs b/src/War3Net.CodeAnalysis.Jass/Parser/VariableReferenceExpressionParser.cs deleted file mode 100644 index 77197793..00000000 --- a/src/War3Net.CodeAnalysis.Jass/Parser/VariableReferenceExpressionParser.cs +++ /dev/null @@ -1,23 +0,0 @@ -// ------------------------------------------------------------------------------ -// -// Licensed under the MIT license. -// See the LICENSE file in the project root for more information. -// -// ------------------------------------------------------------------------------ - -using Pidgin; - -using War3Net.CodeAnalysis.Jass.Syntax; - -namespace War3Net.CodeAnalysis.Jass -{ - internal partial class JassParser - { - internal static Parser GetVariableReferenceExpressionParser(Parser identifierNameParser) - { - return identifierNameParser - .Select(name => new JassVariableReferenceExpressionSyntax(name)) - .Labelled("variable reference"); - } - } -} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Parser/WhitespaceParser.cs b/src/War3Net.CodeAnalysis.Jass/Parser/WhitespaceParser.cs deleted file mode 100644 index 538bf544..00000000 --- a/src/War3Net.CodeAnalysis.Jass/Parser/WhitespaceParser.cs +++ /dev/null @@ -1,21 +0,0 @@ -// ------------------------------------------------------------------------------ -// -// Licensed under the MIT license. -// See the LICENSE file in the project root for more information. -// -// ------------------------------------------------------------------------------ - -using Pidgin; - -using static Pidgin.Parser; - -namespace War3Net.CodeAnalysis.Jass -{ - internal partial class JassParser - { - internal static Parser GetWhitespaceParser() - { - return Token(JassSyntaxFacts.IsWhitespaceCharacter).SkipMany(); - } - } -} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Parsers/IdentifierExpressionParser.cs b/src/War3Net.CodeAnalysis.Jass/Parsers/IdentifierExpressionParser.cs new file mode 100644 index 00000000..53f9f1bb --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/Parsers/IdentifierExpressionParser.cs @@ -0,0 +1,147 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; + +using Pidgin; + +using War3Net.CodeAnalysis.Jass.Syntax; + +namespace War3Net.CodeAnalysis.Jass.Parsers +{ + internal sealed class IdentifierExpressionParser : Parser + { + private static readonly Dictionary _keywordLiteralExpressionKinds = GetKeywordLiteralExpressionKinds(); + + private readonly Parser _identifierParser; + private readonly Parser _identifierNameParser; + private readonly Parser _argumentListParser; + private readonly Parser _elementAccessClauseParser; + private readonly Parser _triviaParser; + + public IdentifierExpressionParser( + Parser identifierParser, + Parser identifierNameParser, + Parser argumentListParser, + Parser elementAccessClauseParser, + Parser triviaParser) + { + _identifierParser = identifierParser; + _identifierNameParser = identifierNameParser; + _argumentListParser = argumentListParser; + _elementAccessClauseParser = elementAccessClauseParser; + _triviaParser = triviaParser; + } + + public override bool TryParse( + ref ParseState state, + ref PooledList> expecteds, + [MaybeNullWhen(false)] out JassExpressionSyntax result) + { + var childExpecteds = new PooledList>(state.Configuration.ArrayPoolProvider.GetArrayPool>()); + + if (!_identifierParser.TryParse(ref state, ref childExpecteds, out var identifierResult)) + { + expecteds.AddRange(childExpecteds.AsSpan()); + childExpecteds.Dispose(); + result = null; + return false; + } + + childExpecteds.Clear(); + + if (!_triviaParser.TryParse(ref state, ref childExpecteds, out var identifierTriviaResult)) + { + expecteds.AddRange(childExpecteds.AsSpan()); + childExpecteds.Dispose(); + result = null; + return false; + } + + childExpecteds.Clear(); + + if (_keywordLiteralExpressionKinds.TryGetValue(identifierResult, out var keywordLiteralExpressionKind)) + { + childExpecteds.Dispose(); + result = new JassLiteralExpressionSyntax(new JassSyntaxToken(keywordLiteralExpressionKind, identifierResult, identifierTriviaResult)); + return true; + } + + if (string.Equals(identifierResult, JassKeyword.Function, StringComparison.Ordinal)) + { + if (!_identifierNameParser.TryParse(ref state, ref childExpecteds, out var identifierNameResult)) + { + expecteds.AddRange(childExpecteds.AsSpan()); + childExpecteds.Dispose(); + result = null; + return false; + } + + childExpecteds.Dispose(); + result = new JassFunctionReferenceExpressionSyntax( + new JassSyntaxToken(JassSyntaxKind.FunctionKeyword, JassKeyword.Function, identifierTriviaResult), + identifierNameResult); + + return true; + } + + var identifierStartLoc = state.Location; + + if (_argumentListParser.TryParse(ref state, ref childExpecteds, out var argumentListResult)) + { + childExpecteds.Dispose(); + result = new JassInvocationExpressionSyntax( + new JassIdentifierNameSyntax(new JassSyntaxToken(JassSyntaxKind.IdentifierToken, identifierResult, identifierTriviaResult)), + argumentListResult); + + return true; + } + else if (state.Location > identifierStartLoc) + { + expecteds.AddRange(childExpecteds.AsSpan()); + childExpecteds.Dispose(); + result = null; + return false; + } + + childExpecteds.Clear(); + + if (_elementAccessClauseParser.TryParse(ref state, ref childExpecteds, out var elementAccessClauseResult)) + { + childExpecteds.Dispose(); + result = new JassElementAccessExpressionSyntax( + new JassIdentifierNameSyntax(new JassSyntaxToken(JassSyntaxKind.IdentifierToken, identifierResult, identifierTriviaResult)), + elementAccessClauseResult); + + return true; + } + else if (state.Location > identifierStartLoc) + { + expecteds.AddRange(childExpecteds.AsSpan()); + childExpecteds.Dispose(); + result = null; + return false; + } + + childExpecteds.Dispose(); + result = new JassIdentifierNameSyntax(new JassSyntaxToken(JassSyntaxKind.IdentifierToken, identifierResult, identifierTriviaResult)); + return true; + } + + private static Dictionary GetKeywordLiteralExpressionKinds() + { + return new Dictionary(StringComparer.Ordinal) + { + { JassKeyword.True, JassSyntaxKind.TrueKeyword }, + { JassKeyword.False, JassSyntaxKind.FalseKeyword }, + { JassKeyword.Null, JassSyntaxKind.NullKeyword }, + }; + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Renamer/ArgumentListRenamer.cs b/src/War3Net.CodeAnalysis.Jass/Renamer/ArgumentListRenamer.cs index 5bc8a01f..9e86f842 100644 --- a/src/War3Net.CodeAnalysis.Jass/Renamer/ArgumentListRenamer.cs +++ b/src/War3Net.CodeAnalysis.Jass/Renamer/ArgumentListRenamer.cs @@ -5,7 +5,6 @@ // // ------------------------------------------------------------------------------ -using System.Collections.Immutable; using System.Diagnostics.CodeAnalysis; using War3Net.CodeAnalysis.Jass.Syntax; @@ -16,26 +15,45 @@ public partial class JassRenamer { private bool TryRenameArgumentList(JassArgumentListSyntax argumentList, [NotNullWhen(true)] out JassArgumentListSyntax? renamedArgumentList) { - var isRenamed = false; - - var argumentsBuilder = ImmutableArray.CreateBuilder(); - foreach (var argument in argumentList.Arguments) + for (var i = 0; i < argumentList.ArgumentList.Items.Length; i++) { - if (TryRenameExpression(argument, out var renamedArgument)) - { - argumentsBuilder.Add(renamedArgument); - isRenamed = true; - } - else + if (TryRenameExpression(argumentList.ArgumentList.Items[i], out var renamedArgument)) { - argumentsBuilder.Add(argument); - } - } + SeparatedSyntaxList.Builder argumentListBuilder; + if (i == 0) + { + argumentListBuilder = SeparatedSyntaxList.CreateBuilder(renamedArgument, argumentList.ArgumentList.Items.Length); + } + else + { + argumentListBuilder = SeparatedSyntaxList.CreateBuilder(argumentList.ArgumentList.Items[0], argumentList.ArgumentList.Items.Length); + for (var j = 0; j < i; j++) + { + argumentListBuilder.Add(argumentList.ArgumentList.Separators[j], argumentList.ArgumentList.Items[j + 1]); + } - if (isRenamed) - { - renamedArgumentList = new JassArgumentListSyntax(argumentsBuilder.ToImmutable()); - return true; + argumentListBuilder.Add(argumentList.ArgumentList.Separators[i - 1], renamedArgument); + } + + while (++i < argumentList.ArgumentList.Items.Length) + { + if (TryRenameExpression(argumentList.ArgumentList.Items[i], out renamedArgument)) + { + argumentListBuilder.Add(argumentList.ArgumentList.Separators[i - 1], renamedArgument); + } + else + { + argumentListBuilder.Add(argumentList.ArgumentList.Separators[i - 1], argumentList.ArgumentList.Items[i]); + } + } + + renamedArgumentList = new JassArgumentListSyntax( + argumentList.OpenParenToken, + argumentListBuilder.ToSeparatedSyntaxList(), + argumentList.CloseParenToken); + + return true; + } } renamedArgumentList = null; diff --git a/src/War3Net.CodeAnalysis.Jass/Renamer/ArrayDeclaratorRenamer.cs b/src/War3Net.CodeAnalysis.Jass/Renamer/ArrayDeclaratorRenamer.cs index e6a0dfef..e3a42497 100644 --- a/src/War3Net.CodeAnalysis.Jass/Renamer/ArrayDeclaratorRenamer.cs +++ b/src/War3Net.CodeAnalysis.Jass/Renamer/ArrayDeclaratorRenamer.cs @@ -13,12 +13,13 @@ namespace War3Net.CodeAnalysis.Jass { public partial class JassRenamer { - private bool TryRenameArrayDeclarator(JassArrayDeclaratorSyntax arrayDeclarator, [NotNullWhen(true)] out IVariableDeclaratorSyntax? renamedArrayDeclarator) + private bool TryRenameArrayDeclarator(JassArrayDeclaratorSyntax arrayDeclarator, [NotNullWhen(true)] out JassVariableOrArrayDeclaratorSyntax? renamedArrayDeclarator) { if (TryRenameVariableIdentifierName(arrayDeclarator.IdentifierName, out var renamedIdentifierName)) { renamedArrayDeclarator = new JassArrayDeclaratorSyntax( arrayDeclarator.Type, + arrayDeclarator.ArrayToken, renamedIdentifierName); return true; diff --git a/src/War3Net.CodeAnalysis.Jass/Renamer/ArrayReferenceExpressionRenamer.cs b/src/War3Net.CodeAnalysis.Jass/Renamer/ArrayReferenceExpressionRenamer.cs deleted file mode 100644 index d6a2a367..00000000 --- a/src/War3Net.CodeAnalysis.Jass/Renamer/ArrayReferenceExpressionRenamer.cs +++ /dev/null @@ -1,32 +0,0 @@ -// ------------------------------------------------------------------------------ -// -// Licensed under the MIT license. -// See the LICENSE file in the project root for more information. -// -// ------------------------------------------------------------------------------ - -using System.Diagnostics.CodeAnalysis; - -using War3Net.CodeAnalysis.Jass.Syntax; - -namespace War3Net.CodeAnalysis.Jass -{ - public partial class JassRenamer - { - private bool TryRenameArrayReferenceExpression(JassArrayReferenceExpressionSyntax arrayReferenceExpression, [NotNullWhen(true)] out IExpressionSyntax? renamedArrayReferenceExpression) - { - if (TryRenameVariableIdentifierName(arrayReferenceExpression.IdentifierName, out var renamedIdentifierName) | - TryRenameExpression(arrayReferenceExpression.Indexer, out var renamedIndexer)) - { - renamedArrayReferenceExpression = new JassArrayReferenceExpressionSyntax( - renamedIdentifierName ?? arrayReferenceExpression.IdentifierName, - renamedIndexer ?? arrayReferenceExpression.Indexer); - - return true; - } - - renamedArrayReferenceExpression = null; - return false; - } - } -} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Renamer/BinaryExpressionRenamer.cs b/src/War3Net.CodeAnalysis.Jass/Renamer/BinaryExpressionRenamer.cs index 046f7c63..68016eb5 100644 --- a/src/War3Net.CodeAnalysis.Jass/Renamer/BinaryExpressionRenamer.cs +++ b/src/War3Net.CodeAnalysis.Jass/Renamer/BinaryExpressionRenamer.cs @@ -13,14 +13,14 @@ namespace War3Net.CodeAnalysis.Jass { public partial class JassRenamer { - private bool TryRenameBinaryExpression(JassBinaryExpressionSyntax binaryExpression, [NotNullWhen(true)] out IExpressionSyntax? renamedBinaryExpression) + private bool TryRenameBinaryExpression(JassBinaryExpressionSyntax binaryExpression, [NotNullWhen(true)] out JassExpressionSyntax? renamedBinaryExpression) { if (TryRenameExpression(binaryExpression.Left, out var renamedLeftExpression) | TryRenameExpression(binaryExpression.Right, out var renamedRightExpression)) { renamedBinaryExpression = new JassBinaryExpressionSyntax( - binaryExpression.Operator, renamedLeftExpression ?? binaryExpression.Left, + binaryExpression.OperatorToken, renamedRightExpression ?? binaryExpression.Right); return true; diff --git a/src/War3Net.CodeAnalysis.Jass/Renamer/CallStatementRenamer.cs b/src/War3Net.CodeAnalysis.Jass/Renamer/CallStatementRenamer.cs index 6186fe21..b2978eb1 100644 --- a/src/War3Net.CodeAnalysis.Jass/Renamer/CallStatementRenamer.cs +++ b/src/War3Net.CodeAnalysis.Jass/Renamer/CallStatementRenamer.cs @@ -6,7 +6,6 @@ // ------------------------------------------------------------------------------ using System; -using System.Collections.Immutable; using System.Diagnostics.CodeAnalysis; using War3Net.CodeAnalysis.Jass.Syntax; @@ -15,18 +14,23 @@ namespace War3Net.CodeAnalysis.Jass { public partial class JassRenamer { - private bool TryRenameCallStatement(JassCallStatementSyntax callStatement, [NotNullWhen(true)] out IStatementSyntax? renamedCallStatement) + private bool TryRenameCallStatement(JassCallStatementSyntax callStatement, [NotNullWhen(true)] out JassStatementSyntax? renamedCallStatement) { if (RenameExecuteFuncArgument && - string.Equals(callStatement.IdentifierName.Name, "ExecuteFunc", StringComparison.Ordinal)) + string.Equals(callStatement.IdentifierName.Token.Text, "ExecuteFunc", StringComparison.Ordinal)) { - if (callStatement.Arguments.Arguments.Length == 1 && - callStatement.Arguments.Arguments[0] is JassStringLiteralExpressionSyntax stringLiteralExpression && - _functionDeclarationRenames.TryGetValue(stringLiteralExpression.Value, out var renamedValue)) + if (callStatement.ArgumentList.ArgumentList.Items.Length == 1 && + callStatement.ArgumentList.ArgumentList.Items[0] is JassLiteralExpressionSyntax stringLiteralExpression && + stringLiteralExpression.Token.SyntaxKind == JassSyntaxKind.StringLiteralToken && + TryRenameFunctionIdentifierToken(stringLiteralExpression.Token, out var renamedToken)) { renamedCallStatement = new JassCallStatementSyntax( + callStatement.CallToken, callStatement.IdentifierName, - new JassArgumentListSyntax(new IExpressionSyntax[] { new JassStringLiteralExpressionSyntax(renamedValue.Name) }.ToImmutableArray())); + new JassArgumentListSyntax( + callStatement.ArgumentList.OpenParenToken, + SeparatedSyntaxList.Create(new JassLiteralExpressionSyntax(renamedToken)), + callStatement.ArgumentList.CloseParenToken)); return true; } @@ -36,11 +40,12 @@ callStatement.Arguments.Arguments[0] is JassStringLiteralExpressionSyntax string } if (TryRenameFunctionIdentifierName(callStatement.IdentifierName, out var renamedIdentifierName) | - TryRenameArgumentList(callStatement.Arguments, out var renamedArguments)) + TryRenameArgumentList(callStatement.ArgumentList, out var renamedArguments)) { renamedCallStatement = new JassCallStatementSyntax( + callStatement.CallToken, renamedIdentifierName ?? callStatement.IdentifierName, - renamedArguments ?? callStatement.Arguments); + renamedArguments ?? callStatement.ArgumentList); return true; } diff --git a/src/War3Net.CodeAnalysis.Jass/Renamer/CompilationUnitRenamer.cs b/src/War3Net.CodeAnalysis.Jass/Renamer/CompilationUnitRenamer.cs index 965e116c..4b0762f7 100644 --- a/src/War3Net.CodeAnalysis.Jass/Renamer/CompilationUnitRenamer.cs +++ b/src/War3Net.CodeAnalysis.Jass/Renamer/CompilationUnitRenamer.cs @@ -22,26 +22,36 @@ public bool TryRenameCompilationUnit(JassCompilationUnitSyntax compilationUnit, throw new ArgumentNullException(nameof(compilationUnit)); } - var isRenamed = false; - - var declarationsBuilder = ImmutableArray.CreateBuilder(); - foreach (var declaration in compilationUnit.Declarations) + for (var i = 0; i < compilationUnit.Declarations.Length; i++) { - if (TryRenameDeclaration(declaration, out var renamedDeclaration)) - { - declarationsBuilder.Add(renamedDeclaration); - isRenamed = true; - } - else + if (TryRenameDeclaration(compilationUnit.Declarations[i], out var renamedDeclaration)) { - declarationsBuilder.Add(declaration); - } - } + var builder = ImmutableArray.CreateBuilder(compilationUnit.Declarations.Length); + for (var j = 0; j < i; j++) + { + builder.Add(compilationUnit.Declarations[j]); + } - if (isRenamed) - { - renamedCompilationUnit = new JassCompilationUnitSyntax(declarationsBuilder.ToImmutable()); - return true; + builder.Add(renamedDeclaration); + + while (++i < compilationUnit.Declarations.Length) + { + if (TryRenameDeclaration(compilationUnit.Declarations[i], out renamedDeclaration)) + { + builder.Add(renamedDeclaration); + } + else + { + builder.Add(compilationUnit.Declarations[i]); + } + } + + renamedCompilationUnit = new JassCompilationUnitSyntax( + builder.ToImmutable(), + compilationUnit.EndOfFileToken); + + return true; + } } renamedCompilationUnit = null; diff --git a/src/War3Net.CodeAnalysis.Jass/Renamer/DebugStatementRenamer.cs b/src/War3Net.CodeAnalysis.Jass/Renamer/DebugStatementRenamer.cs index dfe20233..c8aed527 100644 --- a/src/War3Net.CodeAnalysis.Jass/Renamer/DebugStatementRenamer.cs +++ b/src/War3Net.CodeAnalysis.Jass/Renamer/DebugStatementRenamer.cs @@ -13,11 +13,14 @@ namespace War3Net.CodeAnalysis.Jass { public partial class JassRenamer { - private bool TryRenameDebugStatement(JassDebugStatementSyntax debugStatement, [NotNullWhen(true)] out IStatementSyntax? renamedDebugStatement) + private bool TryRenameDebugStatement(JassDebugStatementSyntax debugStatement, [NotNullWhen(true)] out JassStatementSyntax? renamedDebugStatement) { if (TryRenameStatement(debugStatement.Statement, out var renamedStatement)) { - renamedDebugStatement = new JassDebugStatementSyntax(renamedStatement); + renamedDebugStatement = new JassDebugStatementSyntax( + debugStatement.DebugToken, + renamedStatement); + return true; } diff --git a/src/War3Net.CodeAnalysis.Jass/Renamer/DeclarationRenamer.cs b/src/War3Net.CodeAnalysis.Jass/Renamer/DeclarationRenamer.cs index 7bf04034..2e2506c4 100644 --- a/src/War3Net.CodeAnalysis.Jass/Renamer/DeclarationRenamer.cs +++ b/src/War3Net.CodeAnalysis.Jass/Renamer/DeclarationRenamer.cs @@ -13,11 +13,11 @@ namespace War3Net.CodeAnalysis.Jass { public partial class JassRenamer { - private bool TryRenameDeclaration(ITopLevelDeclarationSyntax declaration, [NotNullWhen(true)] out ITopLevelDeclarationSyntax? renamedDeclaration) + private bool TryRenameDeclaration(JassTopLevelDeclarationSyntax declaration, [NotNullWhen(true)] out JassTopLevelDeclarationSyntax? renamedDeclaration) { return declaration switch { - JassGlobalDeclarationListSyntax globalDeclarationList => TryRenameGlobalDeclarationList(globalDeclarationList, out renamedDeclaration), + JassGlobalsDeclarationSyntax globalsDeclaration => TryRenameGlobalsDeclaration(globalsDeclaration, out renamedDeclaration), JassNativeFunctionDeclarationSyntax nativeFunctionDeclaration => TryRenameNativeFunctionDeclaration(nativeFunctionDeclaration, out renamedDeclaration), JassFunctionDeclarationSyntax functionDeclaration => TryRenameFunctionDeclaration(functionDeclaration, out renamedDeclaration), diff --git a/src/War3Net.CodeAnalysis.Jass/Renamer/ElementAccessClauseRenamer.cs b/src/War3Net.CodeAnalysis.Jass/Renamer/ElementAccessClauseRenamer.cs new file mode 100644 index 00000000..38bf81bf --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/Renamer/ElementAccessClauseRenamer.cs @@ -0,0 +1,33 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using System.Diagnostics.CodeAnalysis; + +using War3Net.CodeAnalysis.Jass.Syntax; + +namespace War3Net.CodeAnalysis.Jass +{ + public partial class JassRenamer + { + private bool TryRenameElementAccessClause(JassElementAccessClauseSyntax? elementAccessClause, [NotNullWhen(true)] out JassElementAccessClauseSyntax? renamedElementAccessClause) + { + if (elementAccessClause is not null && + TryRenameExpression(elementAccessClause.Expression, out var renamedExpression)) + { + renamedElementAccessClause = new JassElementAccessClauseSyntax( + elementAccessClause.OpenBracketToken, + renamedExpression, + elementAccessClause.CloseBracketToken); + + return true; + } + + renamedElementAccessClause = null; + return false; + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Renamer/ElementAccessExpressionRenamer.cs b/src/War3Net.CodeAnalysis.Jass/Renamer/ElementAccessExpressionRenamer.cs new file mode 100644 index 00000000..296176e3 --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/Renamer/ElementAccessExpressionRenamer.cs @@ -0,0 +1,32 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using System.Diagnostics.CodeAnalysis; + +using War3Net.CodeAnalysis.Jass.Syntax; + +namespace War3Net.CodeAnalysis.Jass +{ + public partial class JassRenamer + { + private bool TryRenameElementAccessExpression(JassElementAccessExpressionSyntax elementAccessExpression, [NotNullWhen(true)] out JassExpressionSyntax? renamedElementAccessExpression) + { + if (TryRenameVariableIdentifierName(elementAccessExpression.IdentifierName, out var renamedIdentifierName) | + TryRenameElementAccessClause(elementAccessExpression.ElementAccessClause, out var renamedElementAccessClause)) + { + renamedElementAccessExpression = new JassElementAccessExpressionSyntax( + renamedIdentifierName ?? elementAccessExpression.IdentifierName, + renamedElementAccessClause ?? elementAccessExpression.ElementAccessClause); + + return true; + } + + renamedElementAccessExpression = null; + return false; + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Renamer/ElseClauseRenamer.cs b/src/War3Net.CodeAnalysis.Jass/Renamer/ElseClauseRenamer.cs index 586a0e14..e2cc3339 100644 --- a/src/War3Net.CodeAnalysis.Jass/Renamer/ElseClauseRenamer.cs +++ b/src/War3Net.CodeAnalysis.Jass/Renamer/ElseClauseRenamer.cs @@ -16,9 +16,12 @@ public partial class JassRenamer private bool TryRenameElseClause(JassElseClauseSyntax? elseClause, [NotNullWhen(true)] out JassElseClauseSyntax? renamedElseClause) { if (elseClause is not null && - TryRenameStatementList(elseClause.Body, out var renamedBody)) + TryRenameStatementList(elseClause.Statements, out var renamedStatements)) { - renamedElseClause = new JassElseClauseSyntax(renamedBody); + renamedElseClause = new JassElseClauseSyntax( + elseClause.ElseToken, + renamedStatements.Value); + return true; } diff --git a/src/War3Net.CodeAnalysis.Jass/Renamer/ElseIfClauseDeclaratorRenamer.cs b/src/War3Net.CodeAnalysis.Jass/Renamer/ElseIfClauseDeclaratorRenamer.cs new file mode 100644 index 00000000..ee798102 --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/Renamer/ElseIfClauseDeclaratorRenamer.cs @@ -0,0 +1,32 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using System.Diagnostics.CodeAnalysis; + +using War3Net.CodeAnalysis.Jass.Syntax; + +namespace War3Net.CodeAnalysis.Jass +{ + public partial class JassRenamer + { + private bool TryRenameElseIfClauseDeclarator(JassElseIfClauseDeclaratorSyntax elseIfClauseDeclarator, [NotNullWhen(true)] out JassElseIfClauseDeclaratorSyntax? renamedElseIfClauseDeclarator) + { + if (TryRenameExpression(elseIfClauseDeclarator.Condition, out var renamedCondition)) + { + renamedElseIfClauseDeclarator = new JassElseIfClauseDeclaratorSyntax( + elseIfClauseDeclarator.ElseIfToken, + renamedCondition, + elseIfClauseDeclarator.ThenToken); + + return true; + } + + renamedElseIfClauseDeclarator = null; + return false; + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Renamer/ElseIfClauseListRenamer.cs b/src/War3Net.CodeAnalysis.Jass/Renamer/ElseIfClauseListRenamer.cs new file mode 100644 index 00000000..f6241209 --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/Renamer/ElseIfClauseListRenamer.cs @@ -0,0 +1,52 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using System.Collections.Immutable; +using System.Diagnostics.CodeAnalysis; + +using War3Net.CodeAnalysis.Jass.Syntax; + +namespace War3Net.CodeAnalysis.Jass +{ + public partial class JassRenamer + { + private bool TryRenameElseIfClauseList(ImmutableArray elseIfClauses, [NotNullWhen(true)] out ImmutableArray? renamedElseIfClauses) + { + for (var i = 0; i < elseIfClauses.Length; i++) + { + if (TryRenameElseIfClause(elseIfClauses[i], out var renamedElseIfClause)) + { + var builder = ImmutableArray.CreateBuilder(elseIfClauses.Length); + for (var j = 0; j < i; j++) + { + builder.Add(elseIfClauses[j]); + } + + builder.Add(renamedElseIfClause); + + while (++i < elseIfClauses.Length) + { + if (TryRenameElseIfClause(elseIfClauses[i], out renamedElseIfClause)) + { + builder.Add(renamedElseIfClause); + } + else + { + builder.Add(elseIfClauses[i]); + } + } + + renamedElseIfClauses = builder.ToImmutable(); + return true; + } + } + + renamedElseIfClauses = null; + return false; + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Renamer/ElseIfClauseRenamer.cs b/src/War3Net.CodeAnalysis.Jass/Renamer/ElseIfClauseRenamer.cs index 865f505d..8a4728c8 100644 --- a/src/War3Net.CodeAnalysis.Jass/Renamer/ElseIfClauseRenamer.cs +++ b/src/War3Net.CodeAnalysis.Jass/Renamer/ElseIfClauseRenamer.cs @@ -15,12 +15,12 @@ public partial class JassRenamer { private bool TryRenameElseIfClause(JassElseIfClauseSyntax elseIfClause, [NotNullWhen(true)] out JassElseIfClauseSyntax? renamedElseIfClause) { - if (TryRenameExpression(elseIfClause.Condition, out var renamedCondition) | - TryRenameStatementList(elseIfClause.Body, out var renamedBody)) + if (TryRenameElseIfClauseDeclarator(elseIfClause.ElseIfClauseDeclarator, out var renamedDeclarator) | + TryRenameStatementList(elseIfClause.Statements, out var renamedStatements)) { renamedElseIfClause = new JassElseIfClauseSyntax( - renamedCondition ?? elseIfClause.Condition, - renamedBody ?? elseIfClause.Body); + renamedDeclarator ?? elseIfClause.ElseIfClauseDeclarator, + renamedStatements ?? elseIfClause.Statements); return true; } diff --git a/src/War3Net.CodeAnalysis.Jass/Renamer/EqualsValueClauseRenamer.cs b/src/War3Net.CodeAnalysis.Jass/Renamer/EqualsValueClauseRenamer.cs index 2f341972..4204933b 100644 --- a/src/War3Net.CodeAnalysis.Jass/Renamer/EqualsValueClauseRenamer.cs +++ b/src/War3Net.CodeAnalysis.Jass/Renamer/EqualsValueClauseRenamer.cs @@ -18,7 +18,10 @@ private bool TryRenameEqualsValueClause(JassEqualsValueClauseSyntax? equalsValue if (equalsValueClause is not null && TryRenameExpression(equalsValueClause.Expression, out var renamedExpression)) { - renamedEqualsValueClause = new JassEqualsValueClauseSyntax(renamedExpression); + renamedEqualsValueClause = new JassEqualsValueClauseSyntax( + equalsValueClause.EqualsToken, + renamedExpression); + return true; } diff --git a/src/War3Net.CodeAnalysis.Jass/Renamer/ExitStatementRenamer.cs b/src/War3Net.CodeAnalysis.Jass/Renamer/ExitStatementRenamer.cs index 4dd741f3..207234e2 100644 --- a/src/War3Net.CodeAnalysis.Jass/Renamer/ExitStatementRenamer.cs +++ b/src/War3Net.CodeAnalysis.Jass/Renamer/ExitStatementRenamer.cs @@ -13,11 +13,14 @@ namespace War3Net.CodeAnalysis.Jass { public partial class JassRenamer { - private bool TryRenameExitStatement(JassExitStatementSyntax exitStatement, [NotNullWhen(true)] out IStatementSyntax? renamedExitStatement) + private bool TryRenameExitStatement(JassExitStatementSyntax exitStatement, [NotNullWhen(true)] out JassStatementSyntax? renamedExitStatement) { if (TryRenameExpression(exitStatement.Condition, out var renamedCondition)) { - renamedExitStatement = new JassExitStatementSyntax(renamedCondition); + renamedExitStatement = new JassExitStatementSyntax( + exitStatement.ExitWhenToken, + renamedCondition); + return true; } diff --git a/src/War3Net.CodeAnalysis.Jass/Renamer/ExpressionRenamer.cs b/src/War3Net.CodeAnalysis.Jass/Renamer/ExpressionRenamer.cs index f1257773..3cbb023d 100644 --- a/src/War3Net.CodeAnalysis.Jass/Renamer/ExpressionRenamer.cs +++ b/src/War3Net.CodeAnalysis.Jass/Renamer/ExpressionRenamer.cs @@ -13,14 +13,14 @@ namespace War3Net.CodeAnalysis.Jass { public partial class JassRenamer { - private bool TryRenameExpression(IExpressionSyntax? expression, [NotNullWhen(true)] out IExpressionSyntax? renamedExpression) + private bool TryRenameExpression(JassExpressionSyntax? expression, [NotNullWhen(true)] out JassExpressionSyntax? renamedExpression) { return expression switch { JassFunctionReferenceExpressionSyntax functionReferenceExpression => TryRenameFunctionReferenceExpression(functionReferenceExpression, out renamedExpression), JassInvocationExpressionSyntax invocationExpression => TryRenameInvocationExpression(invocationExpression, out renamedExpression), - JassArrayReferenceExpressionSyntax arrayReferenceExpression => TryRenameArrayReferenceExpression(arrayReferenceExpression, out renamedExpression), - JassVariableReferenceExpressionSyntax variableReferenceExpression => TryRenameVariableReferenceExpression(variableReferenceExpression, out renamedExpression), + JassElementAccessExpressionSyntax elementAccessExpression => TryRenameElementAccessExpression(elementAccessExpression, out renamedExpression), + JassIdentifierNameSyntax identifierName => TryRenameIdentifierName(identifierName, out renamedExpression), JassParenthesizedExpressionSyntax parenthesizedExpression => TryRenameParenthesizedExpression(parenthesizedExpression, out renamedExpression), JassUnaryExpressionSyntax unaryExpression => TryRenameUnaryExpression(unaryExpression, out renamedExpression), JassBinaryExpressionSyntax binaryExpression => TryRenameBinaryExpression(binaryExpression, out renamedExpression), diff --git a/src/War3Net.CodeAnalysis.Jass/Renamer/FunctionDeclarationRenamer.cs b/src/War3Net.CodeAnalysis.Jass/Renamer/FunctionDeclarationRenamer.cs index fb3b056b..c567f378 100644 --- a/src/War3Net.CodeAnalysis.Jass/Renamer/FunctionDeclarationRenamer.cs +++ b/src/War3Net.CodeAnalysis.Jass/Renamer/FunctionDeclarationRenamer.cs @@ -7,27 +7,29 @@ using System.Diagnostics.CodeAnalysis; +using War3Net.CodeAnalysis.Jass.Extensions; using War3Net.CodeAnalysis.Jass.Syntax; namespace War3Net.CodeAnalysis.Jass { public partial class JassRenamer { - private bool TryRenameFunctionDeclaration(JassFunctionDeclarationSyntax functionDeclaration, [NotNullWhen(true)] out ITopLevelDeclarationSyntax? renamedFunctionDeclaration) + private bool TryRenameFunctionDeclaration(JassFunctionDeclarationSyntax functionDeclaration, [NotNullWhen(true)] out JassTopLevelDeclarationSyntax? renamedFunctionDeclaration) { - foreach (var parameter in functionDeclaration.FunctionDeclarator.ParameterList.Parameters) + foreach (var parameter in functionDeclaration.FunctionDeclarator.ParameterList.GetParameters()) { - _localVariableNames.Add(parameter.IdentifierName.Name); + _localVariableNames.Add(parameter.IdentifierName.Token.Text); } if (TryRenameFunctionDeclarator(functionDeclaration.FunctionDeclarator, out var renamedFunctionDeclarator) | - TryRenameStatementList(functionDeclaration.Body, out var renamedBody)) + TryRenameStatementList(functionDeclaration.Statements, out var renamedStatements)) { _localVariableNames.Clear(); renamedFunctionDeclaration = new JassFunctionDeclarationSyntax( renamedFunctionDeclarator ?? functionDeclaration.FunctionDeclarator, - renamedBody ?? functionDeclaration.Body); + renamedStatements ?? functionDeclaration.Statements, + functionDeclaration.EndFunctionToken); return true; } diff --git a/src/War3Net.CodeAnalysis.Jass/Renamer/FunctionDeclaratorRenamer.cs b/src/War3Net.CodeAnalysis.Jass/Renamer/FunctionDeclaratorRenamer.cs index 013169b2..39735d0a 100644 --- a/src/War3Net.CodeAnalysis.Jass/Renamer/FunctionDeclaratorRenamer.cs +++ b/src/War3Net.CodeAnalysis.Jass/Renamer/FunctionDeclaratorRenamer.cs @@ -18,9 +18,11 @@ private bool TryRenameFunctionDeclarator(JassFunctionDeclaratorSyntax functionDe if (TryRenameFunctionIdentifierName(functionDeclarator.IdentifierName, out var renamedIdentifierName)) { renamedFunctionDeclarator = new JassFunctionDeclaratorSyntax( + functionDeclarator.ConstantToken, + functionDeclarator.FunctionToken, renamedIdentifierName, functionDeclarator.ParameterList, - functionDeclarator.ReturnType); + functionDeclarator.ReturnClause); return true; } diff --git a/src/War3Net.CodeAnalysis.Jass/Renamer/FunctionReferenceExpressionRenamer.cs b/src/War3Net.CodeAnalysis.Jass/Renamer/FunctionReferenceExpressionRenamer.cs index af8d7d8c..aea18ec5 100644 --- a/src/War3Net.CodeAnalysis.Jass/Renamer/FunctionReferenceExpressionRenamer.cs +++ b/src/War3Net.CodeAnalysis.Jass/Renamer/FunctionReferenceExpressionRenamer.cs @@ -13,11 +13,14 @@ namespace War3Net.CodeAnalysis.Jass { public partial class JassRenamer { - private bool TryRenameFunctionReferenceExpression(JassFunctionReferenceExpressionSyntax functionReferenceExpression, [NotNullWhen(true)] out IExpressionSyntax? renamedFunctionReferenceExpression) + private bool TryRenameFunctionReferenceExpression(JassFunctionReferenceExpressionSyntax functionReferenceExpression, [NotNullWhen(true)] out JassExpressionSyntax? renamedFunctionReferenceExpression) { if (TryRenameFunctionIdentifierName(functionReferenceExpression.IdentifierName, out var renamedIdentifierName)) { - renamedFunctionReferenceExpression = new JassFunctionReferenceExpressionSyntax(renamedIdentifierName); + renamedFunctionReferenceExpression = new JassFunctionReferenceExpressionSyntax( + functionReferenceExpression.FunctionToken, + renamedIdentifierName); + return true; } diff --git a/src/War3Net.CodeAnalysis.Jass/Renamer/GlobalConstantDeclarationRenamer.cs b/src/War3Net.CodeAnalysis.Jass/Renamer/GlobalConstantDeclarationRenamer.cs new file mode 100644 index 00000000..ac26af4c --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/Renamer/GlobalConstantDeclarationRenamer.cs @@ -0,0 +1,34 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using System.Diagnostics.CodeAnalysis; + +using War3Net.CodeAnalysis.Jass.Syntax; + +namespace War3Net.CodeAnalysis.Jass +{ + public partial class JassRenamer + { + private bool TryRenameGlobalConstantDeclaration(JassGlobalConstantDeclarationSyntax globalConstantDeclaration, [NotNullWhen(true)] out JassGlobalDeclarationSyntax? renamedGlobalConstantDeclaration) + { + if (TryRenameVariableIdentifierName(globalConstantDeclaration.IdentifierName, out var renamedIdentifierName) | + TryRenameEqualsValueClause(globalConstantDeclaration.Value, out var renamedValue)) + { + renamedGlobalConstantDeclaration = new JassGlobalConstantDeclarationSyntax( + globalConstantDeclaration.ConstantToken, + globalConstantDeclaration.Type, + renamedIdentifierName ?? globalConstantDeclaration.IdentifierName, + renamedValue ?? globalConstantDeclaration.Value); + + return false; + } + + renamedGlobalConstantDeclaration = null; + return false; + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Renamer/GlobalDeclarationListRenamer.cs b/src/War3Net.CodeAnalysis.Jass/Renamer/GlobalDeclarationListRenamer.cs deleted file mode 100644 index 57877360..00000000 --- a/src/War3Net.CodeAnalysis.Jass/Renamer/GlobalDeclarationListRenamer.cs +++ /dev/null @@ -1,45 +0,0 @@ -// ------------------------------------------------------------------------------ -// -// Licensed under the MIT license. -// See the LICENSE file in the project root for more information. -// -// ------------------------------------------------------------------------------ - -using System.Collections.Immutable; -using System.Diagnostics.CodeAnalysis; - -using War3Net.CodeAnalysis.Jass.Syntax; - -namespace War3Net.CodeAnalysis.Jass -{ - public partial class JassRenamer - { - private bool TryRenameGlobalDeclarationList(JassGlobalDeclarationListSyntax globalDeclarationList, [NotNullWhen(true)] out ITopLevelDeclarationSyntax? renamedGlobalDeclarationList) - { - var isRenamed = false; - - var declarationsBuilder = ImmutableArray.CreateBuilder(); - foreach (var declaration in globalDeclarationList.Globals) - { - if (TryRenameGlobalDeclaration(declaration, out var renamedDeclaration)) - { - declarationsBuilder.Add(renamedDeclaration); - isRenamed = true; - } - else - { - declarationsBuilder.Add(declaration); - } - } - - if (isRenamed) - { - renamedGlobalDeclarationList = new JassGlobalDeclarationListSyntax(declarationsBuilder.ToImmutable()); - return true; - } - - renamedGlobalDeclarationList = null; - return false; - } - } -} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Renamer/GlobalDeclarationRenamer.cs b/src/War3Net.CodeAnalysis.Jass/Renamer/GlobalDeclarationRenamer.cs index 67db7962..cdcb78c4 100644 --- a/src/War3Net.CodeAnalysis.Jass/Renamer/GlobalDeclarationRenamer.cs +++ b/src/War3Net.CodeAnalysis.Jass/Renamer/GlobalDeclarationRenamer.cs @@ -13,25 +13,14 @@ namespace War3Net.CodeAnalysis.Jass { public partial class JassRenamer { - private bool TryRenameGlobalDeclaration(JassGlobalDeclarationSyntax globalDeclaration, [NotNullWhen(true)] out IGlobalDeclarationSyntax? renamedGlobalDeclaration) + private bool TryRenameGlobalDeclaration(JassGlobalDeclarationSyntax globalDeclaration, [NotNullWhen(true)] out JassGlobalDeclarationSyntax? renamedDeclaration) { - if (TryRenameVariableDeclarator(globalDeclaration.Declarator, out var renamedDeclarator)) + return globalDeclaration switch { - renamedGlobalDeclaration = new JassGlobalDeclarationSyntax(renamedDeclarator); - return true; - } + JassGlobalConstantDeclarationSyntax globalConstantDeclaration => TryRenameGlobalConstantDeclaration(globalConstantDeclaration, out renamedDeclaration), + JassGlobalVariableDeclarationSyntax globalVariableDeclaration => TryRenameGlobalVariableDeclaration(globalVariableDeclaration, out renamedDeclaration), - renamedGlobalDeclaration = null; - return false; - } - - private bool TryRenameGlobalDeclaration(IGlobalDeclarationSyntax declaration, [NotNullWhen(true)] out IGlobalDeclarationSyntax? renamedDeclaration) - { - return declaration switch - { - JassGlobalDeclarationSyntax globalDeclaration => TryRenameGlobalDeclaration(globalDeclaration, out renamedDeclaration), - - _ => TryRenameDummy(declaration, out renamedDeclaration), + _ => TryRenameDummy(globalDeclaration, out renamedDeclaration), }; } } diff --git a/src/War3Net.CodeAnalysis.Jass/Renamer/GlobalVariableDeclarationRenamer.cs b/src/War3Net.CodeAnalysis.Jass/Renamer/GlobalVariableDeclarationRenamer.cs new file mode 100644 index 00000000..fafdf7d9 --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/Renamer/GlobalVariableDeclarationRenamer.cs @@ -0,0 +1,28 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using System.Diagnostics.CodeAnalysis; + +using War3Net.CodeAnalysis.Jass.Syntax; + +namespace War3Net.CodeAnalysis.Jass +{ + public partial class JassRenamer + { + private bool TryRenameGlobalVariableDeclaration(JassGlobalVariableDeclarationSyntax globalVariableDeclaration, [NotNullWhen(true)] out JassGlobalDeclarationSyntax? renamedGlobalVariableDeclaration) + { + if (TryRenameVariableOrArrayDeclarator(globalVariableDeclaration.Declarator, out var renamedDeclarator)) + { + renamedGlobalVariableDeclaration = new JassGlobalVariableDeclarationSyntax(renamedDeclarator); + return true; + } + + renamedGlobalVariableDeclaration = null; + return false; + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Renamer/GlobalsDeclarationRenamer.cs b/src/War3Net.CodeAnalysis.Jass/Renamer/GlobalsDeclarationRenamer.cs new file mode 100644 index 00000000..a2d6c6e9 --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/Renamer/GlobalsDeclarationRenamer.cs @@ -0,0 +1,56 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using System.Collections.Immutable; +using System.Diagnostics.CodeAnalysis; + +using War3Net.CodeAnalysis.Jass.Syntax; + +namespace War3Net.CodeAnalysis.Jass +{ + public partial class JassRenamer + { + private bool TryRenameGlobalsDeclaration(JassGlobalsDeclarationSyntax globalsDeclaration, [NotNullWhen(true)] out JassTopLevelDeclarationSyntax? renamedGlobalsDeclaration) + { + for (var i = 0; i < globalsDeclaration.GlobalDeclarations.Length; i++) + { + if (TryRenameGlobalDeclaration(globalsDeclaration.GlobalDeclarations[i], out var renamedGlobal)) + { + var builder = ImmutableArray.CreateBuilder(globalsDeclaration.GlobalDeclarations.Length); + for (var j = 0; j < i; j++) + { + builder.Add(globalsDeclaration.GlobalDeclarations[j]); + } + + builder.Add(renamedGlobal); + + while (++i < globalsDeclaration.GlobalDeclarations.Length) + { + if (TryRenameGlobalDeclaration(globalsDeclaration.GlobalDeclarations[i], out renamedGlobal)) + { + builder.Add(renamedGlobal); + } + else + { + builder.Add(globalsDeclaration.GlobalDeclarations[i]); + } + } + + renamedGlobalsDeclaration = new JassGlobalsDeclarationSyntax( + globalsDeclaration.GlobalsToken, + builder.ToImmutable(), + globalsDeclaration.EndGlobalsToken); + + return true; + } + } + + renamedGlobalsDeclaration = null; + return false; + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Renamer/IdentifierNameRenamer.cs b/src/War3Net.CodeAnalysis.Jass/Renamer/IdentifierNameRenamer.cs index 3274cab6..7c7a6969 100644 --- a/src/War3Net.CodeAnalysis.Jass/Renamer/IdentifierNameRenamer.cs +++ b/src/War3Net.CodeAnalysis.Jass/Renamer/IdentifierNameRenamer.cs @@ -13,20 +13,40 @@ namespace War3Net.CodeAnalysis.Jass { public partial class JassRenamer { + private bool TryRenameIdentifierName(JassIdentifierNameSyntax identifierName, [NotNullWhen(true)] out JassExpressionSyntax? renamedIdentifierName) + { + if (TryRenameVariableIdentifierToken(identifierName.Token, out var renamedToken)) + { + renamedIdentifierName = new JassIdentifierNameSyntax(renamedToken); + return true; + } + + renamedIdentifierName = null; + return false; + } + private bool TryRenameFunctionIdentifierName(JassIdentifierNameSyntax identifierName, [NotNullWhen(true)] out JassIdentifierNameSyntax? renamedIdentifierName) { - return _functionDeclarationRenames.TryGetValue(identifierName.Name, out renamedIdentifierName); + if (TryRenameFunctionIdentifierToken(identifierName.Token, out var renamedToken)) + { + renamedIdentifierName = new JassIdentifierNameSyntax(renamedToken); + return true; + } + + renamedIdentifierName = null; + return false; } private bool TryRenameVariableIdentifierName(JassIdentifierNameSyntax identifierName, [NotNullWhen(true)] out JassIdentifierNameSyntax? renamedIdentifierName) { - if (_localVariableNames.Contains(identifierName.Name)) + if (TryRenameVariableIdentifierToken(identifierName.Token, out var renamedToken)) { - renamedIdentifierName = null; - return false; + renamedIdentifierName = new JassIdentifierNameSyntax(renamedToken); + return true; } - return _globalVariableRenames.TryGetValue(identifierName.Name, out renamedIdentifierName); + renamedIdentifierName = null; + return false; } } } \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Renamer/IfClauseDeclaratorRenamer.cs b/src/War3Net.CodeAnalysis.Jass/Renamer/IfClauseDeclaratorRenamer.cs new file mode 100644 index 00000000..a759dee8 --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/Renamer/IfClauseDeclaratorRenamer.cs @@ -0,0 +1,32 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using System.Diagnostics.CodeAnalysis; + +using War3Net.CodeAnalysis.Jass.Syntax; + +namespace War3Net.CodeAnalysis.Jass +{ + public partial class JassRenamer + { + private bool TryRenameIfClauseDeclarator(JassIfClauseDeclaratorSyntax ifClauseDeclarator, [NotNullWhen(true)] out JassIfClauseDeclaratorSyntax? renamedIfClauseDeclarator) + { + if (TryRenameExpression(ifClauseDeclarator.Condition, out var renamedCondition)) + { + renamedIfClauseDeclarator = new JassIfClauseDeclaratorSyntax( + ifClauseDeclarator.IfToken, + renamedCondition, + ifClauseDeclarator.ThenToken); + + return true; + } + + renamedIfClauseDeclarator = null; + return false; + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Renamer/IfClauseRenamer.cs b/src/War3Net.CodeAnalysis.Jass/Renamer/IfClauseRenamer.cs new file mode 100644 index 00000000..f5e1b2df --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/Renamer/IfClauseRenamer.cs @@ -0,0 +1,32 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using System.Diagnostics.CodeAnalysis; + +using War3Net.CodeAnalysis.Jass.Syntax; + +namespace War3Net.CodeAnalysis.Jass +{ + public partial class JassRenamer + { + private bool TryRenameIfClause(JassIfClauseSyntax ifClause, [NotNullWhen(true)] out JassIfClauseSyntax? renamedIfClause) + { + if (TryRenameIfClauseDeclarator(ifClause.IfClauseDeclarator, out var renamedDeclarator) | + TryRenameStatementList(ifClause.Statements, out var renamedStatements)) + { + renamedIfClause = new JassIfClauseSyntax( + renamedDeclarator ?? ifClause.IfClauseDeclarator, + renamedStatements ?? ifClause.Statements); + + return true; + } + + renamedIfClause = null; + return false; + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Renamer/IfStatementRenamer.cs b/src/War3Net.CodeAnalysis.Jass/Renamer/IfStatementRenamer.cs index 75079d81..ae2f4759 100644 --- a/src/War3Net.CodeAnalysis.Jass/Renamer/IfStatementRenamer.cs +++ b/src/War3Net.CodeAnalysis.Jass/Renamer/IfStatementRenamer.cs @@ -5,7 +5,6 @@ // // ------------------------------------------------------------------------------ -using System.Collections.Immutable; using System.Diagnostics.CodeAnalysis; using War3Net.CodeAnalysis.Jass.Syntax; @@ -14,34 +13,17 @@ namespace War3Net.CodeAnalysis.Jass { public partial class JassRenamer { - private bool TryRenameIfStatement(JassIfStatementSyntax ifStatement, [NotNullWhen(true)] out IStatementSyntax? renamedIfStatement) + private bool TryRenameIfStatement(JassIfStatementSyntax ifStatement, [NotNullWhen(true)] out JassStatementSyntax? renamedIfStatement) { - var isRenamed = false; - - var elseIfClausesBuilder = ImmutableArray.CreateBuilder(); - foreach (var elseIfClause in ifStatement.ElseIfClauses) - { - if (TryRenameElseIfClause(elseIfClause, out var renamedElseIfClause)) - { - elseIfClausesBuilder.Add(renamedElseIfClause); - isRenamed = true; - } - else - { - elseIfClausesBuilder.Add(elseIfClause); - } - } - - if (TryRenameExpression(ifStatement.Condition, out var renamedCondition) | - TryRenameStatementList(ifStatement.Body, out var renamedBody) | - isRenamed | + if (TryRenameIfClause(ifStatement.IfClause, out var renamedIfClause) | + TryRenameElseIfClauseList(ifStatement.ElseIfClauses, out var renamedElseIfClauses) | TryRenameElseClause(ifStatement.ElseClause, out var renamedElseClause)) { renamedIfStatement = new JassIfStatementSyntax( - renamedCondition ?? ifStatement.Condition, - renamedBody ?? ifStatement.Body, - isRenamed ? elseIfClausesBuilder.ToImmutable() : ifStatement.ElseIfClauses, - renamedElseClause ?? ifStatement.ElseClause); + renamedIfClause ?? ifStatement.IfClause, + renamedElseIfClauses ?? ifStatement.ElseIfClauses, + renamedElseClause ?? ifStatement.ElseClause, + ifStatement.EndIfToken); return true; } diff --git a/src/War3Net.CodeAnalysis.Jass/Renamer/InvocationExpressionRenamer.cs b/src/War3Net.CodeAnalysis.Jass/Renamer/InvocationExpressionRenamer.cs index de54d763..fb815921 100644 --- a/src/War3Net.CodeAnalysis.Jass/Renamer/InvocationExpressionRenamer.cs +++ b/src/War3Net.CodeAnalysis.Jass/Renamer/InvocationExpressionRenamer.cs @@ -13,14 +13,14 @@ namespace War3Net.CodeAnalysis.Jass { public partial class JassRenamer { - private bool TryRenameInvocationExpression(JassInvocationExpressionSyntax invocationExpression, [NotNullWhen(true)] out IExpressionSyntax? renamedInvocationExpression) + private bool TryRenameInvocationExpression(JassInvocationExpressionSyntax invocationExpression, [NotNullWhen(true)] out JassExpressionSyntax? renamedInvocationExpression) { if (TryRenameFunctionIdentifierName(invocationExpression.IdentifierName, out var renamedIdentifierName) | - TryRenameArgumentList(invocationExpression.Arguments, out var renamedArguments)) + TryRenameArgumentList(invocationExpression.ArgumentList, out var renamedArguments)) { renamedInvocationExpression = new JassInvocationExpressionSyntax( renamedIdentifierName ?? invocationExpression.IdentifierName, - renamedArguments ?? invocationExpression.Arguments); + renamedArguments ?? invocationExpression.ArgumentList); return true; } diff --git a/src/War3Net.CodeAnalysis.Jass/Renamer/LocalVariableDeclarationStatementRenamer.cs b/src/War3Net.CodeAnalysis.Jass/Renamer/LocalVariableDeclarationStatementRenamer.cs index d8620672..ef526efb 100644 --- a/src/War3Net.CodeAnalysis.Jass/Renamer/LocalVariableDeclarationStatementRenamer.cs +++ b/src/War3Net.CodeAnalysis.Jass/Renamer/LocalVariableDeclarationStatementRenamer.cs @@ -7,19 +7,23 @@ using System.Diagnostics.CodeAnalysis; +using War3Net.CodeAnalysis.Jass.Extensions; using War3Net.CodeAnalysis.Jass.Syntax; namespace War3Net.CodeAnalysis.Jass { public partial class JassRenamer { - private bool TryRenameLocalVariableDeclarationStatement(JassLocalVariableDeclarationStatementSyntax localVariableDeclarationStatement, [NotNullWhen(true)] out IStatementSyntax? renamedLocalVariableDeclarationStatement) + private bool TryRenameLocalVariableDeclarationStatement(JassLocalVariableDeclarationStatementSyntax localVariableDeclarationStatement, [NotNullWhen(true)] out JassStatementSyntax? renamedLocalVariableDeclarationStatement) { - _localVariableNames.Add(localVariableDeclarationStatement.Declarator.IdentifierName.Name); + _localVariableNames.Add(localVariableDeclarationStatement.Declarator.GetIdentifierName().Token.Text); - if (TryRenameVariableDeclarator(localVariableDeclarationStatement.Declarator, out var renamedDeclarator)) + if (TryRenameVariableOrArrayDeclarator(localVariableDeclarationStatement.Declarator, out var renamedDeclarator)) { - renamedLocalVariableDeclarationStatement = new JassLocalVariableDeclarationStatementSyntax(renamedDeclarator); + renamedLocalVariableDeclarationStatement = new JassLocalVariableDeclarationStatementSyntax( + localVariableDeclarationStatement.LocalToken, + renamedDeclarator); + return true; } diff --git a/src/War3Net.CodeAnalysis.Jass/Renamer/LoopStatementRenamer.cs b/src/War3Net.CodeAnalysis.Jass/Renamer/LoopStatementRenamer.cs index a11b1498..28b301e4 100644 --- a/src/War3Net.CodeAnalysis.Jass/Renamer/LoopStatementRenamer.cs +++ b/src/War3Net.CodeAnalysis.Jass/Renamer/LoopStatementRenamer.cs @@ -13,11 +13,15 @@ namespace War3Net.CodeAnalysis.Jass { public partial class JassRenamer { - private bool TryRenameLoopStatement(JassLoopStatementSyntax loopStatement, [NotNullWhen(true)] out IStatementSyntax? renamedLoopStatement) + private bool TryRenameLoopStatement(JassLoopStatementSyntax loopStatement, [NotNullWhen(true)] out JassStatementSyntax? renamedLoopStatement) { - if (TryRenameStatementList(loopStatement.Body, out var renamedBody)) + if (TryRenameStatementList(loopStatement.Statements, out var renamedStatements)) { - renamedLoopStatement = new JassLoopStatementSyntax(renamedBody); + renamedLoopStatement = new JassLoopStatementSyntax( + loopStatement.LoopToken, + renamedStatements.Value, + loopStatement.EndLoopToken); + return true; } diff --git a/src/War3Net.CodeAnalysis.Jass/Renamer/NativeFunctionDeclarationRenamer.cs b/src/War3Net.CodeAnalysis.Jass/Renamer/NativeFunctionDeclarationRenamer.cs index b42567eb..bdce7da1 100644 --- a/src/War3Net.CodeAnalysis.Jass/Renamer/NativeFunctionDeclarationRenamer.cs +++ b/src/War3Net.CodeAnalysis.Jass/Renamer/NativeFunctionDeclarationRenamer.cs @@ -13,11 +13,17 @@ namespace War3Net.CodeAnalysis.Jass { public partial class JassRenamer { - private bool TryRenameNativeFunctionDeclaration(JassNativeFunctionDeclarationSyntax nativeFunctionDeclaration, [NotNullWhen(true)] out ITopLevelDeclarationSyntax? renamedNativeFunctionDeclaration) + private bool TryRenameNativeFunctionDeclaration(JassNativeFunctionDeclarationSyntax nativeFunctionDeclaration, [NotNullWhen(true)] out JassTopLevelDeclarationSyntax? renamedNativeFunctionDeclaration) { - if (TryRenameFunctionDeclarator(nativeFunctionDeclaration.FunctionDeclarator, out var renamedFunctionDeclarator)) + if (TryRenameFunctionIdentifierName(nativeFunctionDeclaration.IdentifierName, out var renamedIdentifierName)) { - renamedNativeFunctionDeclaration = new JassNativeFunctionDeclarationSyntax(renamedFunctionDeclarator); + renamedNativeFunctionDeclaration = new JassNativeFunctionDeclarationSyntax( + nativeFunctionDeclaration.ConstantToken, + nativeFunctionDeclaration.NativeToken, + renamedIdentifierName, + nativeFunctionDeclaration.ParameterList, + nativeFunctionDeclaration.ReturnClause); + return true; } diff --git a/src/War3Net.CodeAnalysis.Jass/Renamer/ParenthesizedExpressionRenamer.cs b/src/War3Net.CodeAnalysis.Jass/Renamer/ParenthesizedExpressionRenamer.cs index 8495df89..70f2f4ec 100644 --- a/src/War3Net.CodeAnalysis.Jass/Renamer/ParenthesizedExpressionRenamer.cs +++ b/src/War3Net.CodeAnalysis.Jass/Renamer/ParenthesizedExpressionRenamer.cs @@ -13,11 +13,15 @@ namespace War3Net.CodeAnalysis.Jass { public partial class JassRenamer { - private bool TryRenameParenthesizedExpression(JassParenthesizedExpressionSyntax parenthesizedExpression, [NotNullWhen(true)] out IExpressionSyntax? renamedParenthesizedExpression) + private bool TryRenameParenthesizedExpression(JassParenthesizedExpressionSyntax parenthesizedExpression, [NotNullWhen(true)] out JassExpressionSyntax? renamedParenthesizedExpression) { if (TryRenameExpression(parenthesizedExpression.Expression, out var renamedExpression)) { - renamedParenthesizedExpression = new JassParenthesizedExpressionSyntax(renamedExpression); + renamedParenthesizedExpression = new JassParenthesizedExpressionSyntax( + parenthesizedExpression.OpenParenToken, + renamedExpression, + parenthesizedExpression.CloseParenToken); + return true; } diff --git a/src/War3Net.CodeAnalysis.Jass/Renamer/ReturnStatementRenamer.cs b/src/War3Net.CodeAnalysis.Jass/Renamer/ReturnStatementRenamer.cs index 516084c8..d6423c18 100644 --- a/src/War3Net.CodeAnalysis.Jass/Renamer/ReturnStatementRenamer.cs +++ b/src/War3Net.CodeAnalysis.Jass/Renamer/ReturnStatementRenamer.cs @@ -13,11 +13,14 @@ namespace War3Net.CodeAnalysis.Jass { public partial class JassRenamer { - private bool TryRenameReturnStatement(JassReturnStatementSyntax returnStatement, [NotNullWhen(true)] out IStatementSyntax? renamedReturnStatement) + private bool TryRenameReturnStatement(JassReturnStatementSyntax returnStatement, [NotNullWhen(true)] out JassStatementSyntax? renamedReturnStatement) { if (TryRenameExpression(returnStatement.Value, out var renamedValue)) { - renamedReturnStatement = new JassReturnStatementSyntax(renamedValue); + renamedReturnStatement = new JassReturnStatementSyntax( + returnStatement.ReturnToken, + renamedValue); + return true; } diff --git a/src/War3Net.CodeAnalysis.Jass/Renamer/SetStatementRenamer.cs b/src/War3Net.CodeAnalysis.Jass/Renamer/SetStatementRenamer.cs index b3c5237a..804b0068 100644 --- a/src/War3Net.CodeAnalysis.Jass/Renamer/SetStatementRenamer.cs +++ b/src/War3Net.CodeAnalysis.Jass/Renamer/SetStatementRenamer.cs @@ -13,15 +13,16 @@ namespace War3Net.CodeAnalysis.Jass { public partial class JassRenamer { - private bool TryRenameSetStatement(JassSetStatementSyntax setStatement, [NotNullWhen(true)] out IStatementSyntax? renamedSetStatement) + private bool TryRenameSetStatement(JassSetStatementSyntax setStatement, [NotNullWhen(true)] out JassStatementSyntax? renamedSetStatement) { if (TryRenameVariableIdentifierName(setStatement.IdentifierName, out var renamedIdentifierName) | - TryRenameExpression(setStatement.Indexer, out var renamedIndexer) | + TryRenameElementAccessClause(setStatement.ElementAccessClause, out var renamedElementAccessClause) | TryRenameEqualsValueClause(setStatement.Value, out var renamedValue)) { renamedSetStatement = new JassSetStatementSyntax( + setStatement.SetToken, renamedIdentifierName ?? setStatement.IdentifierName, - renamedIndexer ?? setStatement.Indexer, + renamedElementAccessClause ?? setStatement.ElementAccessClause, renamedValue ?? setStatement.Value); return true; diff --git a/src/War3Net.CodeAnalysis.Jass/Renamer/StatementListRenamer.cs b/src/War3Net.CodeAnalysis.Jass/Renamer/StatementListRenamer.cs index df75e9fb..a15553ef 100644 --- a/src/War3Net.CodeAnalysis.Jass/Renamer/StatementListRenamer.cs +++ b/src/War3Net.CodeAnalysis.Jass/Renamer/StatementListRenamer.cs @@ -14,31 +14,38 @@ namespace War3Net.CodeAnalysis.Jass { public partial class JassRenamer { - private bool TryRenameStatementList(JassStatementListSyntax statementList, [NotNullWhen(true)] out JassStatementListSyntax? renamedStatementList) + private bool TryRenameStatementList(ImmutableArray statements, [NotNullWhen(true)] out ImmutableArray? renamedStatements) { - var isRenamed = false; - - var statementsBuilder = ImmutableArray.CreateBuilder(); - foreach (var statement in statementList.Statements) + for (var i = 0; i < statements.Length; i++) { - if (TryRenameStatement(statement, out var renamedStatement)) - { - statementsBuilder.Add(renamedStatement); - isRenamed = true; - } - else + if (TryRenameStatement(statements[i], out var renamedStatement)) { - statementsBuilder.Add(statement); - } - } + var builder = ImmutableArray.CreateBuilder(statements.Length); + for (var j = 0; j < i; j++) + { + builder.Add(statements[j]); + } - if (isRenamed) - { - renamedStatementList = new JassStatementListSyntax(statementsBuilder.ToImmutable()); - return true; + builder.Add(renamedStatement); + + while (++i < statements.Length) + { + if (TryRenameStatement(statements[i], out renamedStatement)) + { + builder.Add(renamedStatement); + } + else + { + builder.Add(statements[i]); + } + } + + renamedStatements = builder.ToImmutable(); + return true; + } } - renamedStatementList = null; + renamedStatements = null; return false; } } diff --git a/src/War3Net.CodeAnalysis.Jass/Renamer/StatementRenamer.cs b/src/War3Net.CodeAnalysis.Jass/Renamer/StatementRenamer.cs index 1e784336..a5b32e7a 100644 --- a/src/War3Net.CodeAnalysis.Jass/Renamer/StatementRenamer.cs +++ b/src/War3Net.CodeAnalysis.Jass/Renamer/StatementRenamer.cs @@ -13,7 +13,7 @@ namespace War3Net.CodeAnalysis.Jass { public partial class JassRenamer { - private bool TryRenameStatement(IStatementSyntax statement, [NotNullWhen(true)] out IStatementSyntax? renamedStatement) + private bool TryRenameStatement(JassStatementSyntax statement, [NotNullWhen(true)] out JassStatementSyntax? renamedStatement) { return statement switch { diff --git a/src/War3Net.CodeAnalysis.Jass/Renamer/SyntaxTokenRenamer.cs b/src/War3Net.CodeAnalysis.Jass/Renamer/SyntaxTokenRenamer.cs new file mode 100644 index 00000000..ce2c6cd4 --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/Renamer/SyntaxTokenRenamer.cs @@ -0,0 +1,56 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using System.Diagnostics.CodeAnalysis; + +using War3Net.CodeAnalysis.Jass.Syntax; + +namespace War3Net.CodeAnalysis.Jass +{ + public partial class JassRenamer + { + private bool TryRenameFunctionIdentifierToken(JassSyntaxToken token, [NotNullWhen(true)] out JassSyntaxToken? renamedToken) + { + if (_functionDeclarationRenames.TryGetValue(token.Text, out var newName)) + { + renamedToken = new JassSyntaxToken( + token.LeadingTrivia, + token.SyntaxKind, + newName, + token.TrailingTrivia); + + return true; + } + + renamedToken = null; + return false; + } + + private bool TryRenameVariableIdentifierToken(JassSyntaxToken token, [NotNullWhen(true)] out JassSyntaxToken? renamedToken) + { + if (_localVariableNames.Contains(token.Text)) + { + renamedToken = null; + return false; + } + + if (_globalVariableRenames.TryGetValue(token.Text, out var newName)) + { + renamedToken = new JassSyntaxToken( + token.LeadingTrivia, + token.SyntaxKind, + newName, + token.TrailingTrivia); + + return true; + } + + renamedToken = null; + return false; + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Renamer/UnaryExpressionRenamer.cs b/src/War3Net.CodeAnalysis.Jass/Renamer/UnaryExpressionRenamer.cs index 52d8618c..d195cb0b 100644 --- a/src/War3Net.CodeAnalysis.Jass/Renamer/UnaryExpressionRenamer.cs +++ b/src/War3Net.CodeAnalysis.Jass/Renamer/UnaryExpressionRenamer.cs @@ -13,12 +13,12 @@ namespace War3Net.CodeAnalysis.Jass { public partial class JassRenamer { - private bool TryRenameUnaryExpression(JassUnaryExpressionSyntax unaryExpression, [NotNullWhen(true)] out IExpressionSyntax? renamedUnaryExpression) + private bool TryRenameUnaryExpression(JassUnaryExpressionSyntax unaryExpression, [NotNullWhen(true)] out JassExpressionSyntax? renamedUnaryExpression) { if (TryRenameExpression(unaryExpression.Expression, out var renamedExpression)) { renamedUnaryExpression = new JassUnaryExpressionSyntax( - unaryExpression.Operator, + unaryExpression.OperatorToken, renamedExpression); return true; diff --git a/src/War3Net.CodeAnalysis.Jass/Renamer/VariableDeclaratorRenamer.cs b/src/War3Net.CodeAnalysis.Jass/Renamer/VariableDeclaratorRenamer.cs index 84fc44b5..1b51c14d 100644 --- a/src/War3Net.CodeAnalysis.Jass/Renamer/VariableDeclaratorRenamer.cs +++ b/src/War3Net.CodeAnalysis.Jass/Renamer/VariableDeclaratorRenamer.cs @@ -13,18 +13,7 @@ namespace War3Net.CodeAnalysis.Jass { public partial class JassRenamer { - private bool TryRenameVariableDeclarator(IVariableDeclaratorSyntax declarator, [NotNullWhen(true)] out IVariableDeclaratorSyntax? renamedDeclarator) - { - return declarator switch - { - JassArrayDeclaratorSyntax arrayDeclarator => TryRenameArrayDeclarator(arrayDeclarator, out renamedDeclarator), - JassVariableDeclaratorSyntax variableDeclarator => TryRenameVariableDeclarator(variableDeclarator, out renamedDeclarator), - - _ => TryRenameDummy(declarator, out renamedDeclarator), - }; - } - - private bool TryRenameVariableDeclarator(JassVariableDeclaratorSyntax variableDeclarator, [NotNullWhen(true)] out IVariableDeclaratorSyntax? renamedVariableDeclarator) + private bool TryRenameVariableDeclarator(JassVariableDeclaratorSyntax variableDeclarator, [NotNullWhen(true)] out JassVariableOrArrayDeclaratorSyntax? renamedVariableDeclarator) { if (TryRenameVariableIdentifierName(variableDeclarator.IdentifierName, out var renamedIdentifierName) | TryRenameEqualsValueClause(variableDeclarator.Value, out var renamedValue)) diff --git a/src/War3Net.CodeAnalysis.Jass/Renamer/VariableOrArrayDeclaratorRenamer.cs b/src/War3Net.CodeAnalysis.Jass/Renamer/VariableOrArrayDeclaratorRenamer.cs new file mode 100644 index 00000000..14e11d42 --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/Renamer/VariableOrArrayDeclaratorRenamer.cs @@ -0,0 +1,27 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using System.Diagnostics.CodeAnalysis; + +using War3Net.CodeAnalysis.Jass.Syntax; + +namespace War3Net.CodeAnalysis.Jass +{ + public partial class JassRenamer + { + private bool TryRenameVariableOrArrayDeclarator(JassVariableOrArrayDeclaratorSyntax declarator, [NotNullWhen(true)] out JassVariableOrArrayDeclaratorSyntax? renamedDeclarator) + { + return declarator switch + { + JassArrayDeclaratorSyntax arrayDeclarator => TryRenameArrayDeclarator(arrayDeclarator, out renamedDeclarator), + JassVariableDeclaratorSyntax variableDeclarator => TryRenameVariableDeclarator(variableDeclarator, out renamedDeclarator), + + _ => TryRenameDummy(declarator, out renamedDeclarator), + }; + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Renamer/VariableReferenceExpressionRenamer.cs b/src/War3Net.CodeAnalysis.Jass/Renamer/VariableReferenceExpressionRenamer.cs deleted file mode 100644 index b39ddfd9..00000000 --- a/src/War3Net.CodeAnalysis.Jass/Renamer/VariableReferenceExpressionRenamer.cs +++ /dev/null @@ -1,28 +0,0 @@ -// ------------------------------------------------------------------------------ -// -// Licensed under the MIT license. -// See the LICENSE file in the project root for more information. -// -// ------------------------------------------------------------------------------ - -using System.Diagnostics.CodeAnalysis; - -using War3Net.CodeAnalysis.Jass.Syntax; - -namespace War3Net.CodeAnalysis.Jass -{ - public partial class JassRenamer - { - private bool TryRenameVariableReferenceExpression(JassVariableReferenceExpressionSyntax variableReferenceExpression, [NotNullWhen(true)] out IExpressionSyntax? renamedVariableReferenceExpression) - { - if (TryRenameVariableIdentifierName(variableReferenceExpression.IdentifierName, out var renamedIdentifierName)) - { - renamedVariableReferenceExpression = new JassVariableReferenceExpressionSyntax(renamedIdentifierName); - return true; - } - - renamedVariableReferenceExpression = null; - return false; - } - } -} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Renderer/ArgumentListRenderer.cs b/src/War3Net.CodeAnalysis.Jass/Renderer/ArgumentListRenderer.cs index b31a3f77..f4a602aa 100644 --- a/src/War3Net.CodeAnalysis.Jass/Renderer/ArgumentListRenderer.cs +++ b/src/War3Net.CodeAnalysis.Jass/Renderer/ArgumentListRenderer.cs @@ -5,8 +5,6 @@ // // ------------------------------------------------------------------------------ -using System.Linq; - using War3Net.CodeAnalysis.Jass.Syntax; namespace War3Net.CodeAnalysis.Jass @@ -15,15 +13,20 @@ public partial class JassRenderer { public void Render(JassArgumentListSyntax argumentList) { - if (argumentList.Arguments.Any()) + Render(argumentList.OpenParenToken); + + if (!argumentList.ArgumentList.Items.IsEmpty) { - Render(argumentList.Arguments.First()); - foreach (var argument in argumentList.Arguments.Skip(1)) + Render(argumentList.ArgumentList.Items[0]); + for (var i = 1; i < argumentList.ArgumentList.Items.Length; i++) { - Write($"{JassSymbol.Comma} "); - Render(argument); + Render(argumentList.ArgumentList.Separators[i - 1]); + WriteSpace(); + Render(argumentList.ArgumentList.Items[i]); } } + + Render(argumentList.CloseParenToken); } } } \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Renderer/ArrayDeclaratorRenderer.cs b/src/War3Net.CodeAnalysis.Jass/Renderer/ArrayDeclaratorRenderer.cs index 0ef1ba53..6d1964ef 100644 --- a/src/War3Net.CodeAnalysis.Jass/Renderer/ArrayDeclaratorRenderer.cs +++ b/src/War3Net.CodeAnalysis.Jass/Renderer/ArrayDeclaratorRenderer.cs @@ -14,7 +14,9 @@ public partial class JassRenderer public void Render(JassArrayDeclaratorSyntax arrayDeclarator) { Render(arrayDeclarator.Type); - Write($" {JassKeyword.Array} "); + WriteSpace(); + Render(arrayDeclarator.ArrayToken); + WriteSpace(); Render(arrayDeclarator.IdentifierName); } } diff --git a/src/War3Net.CodeAnalysis.Jass/Renderer/ArrayReferenceExpressionRenderer.cs b/src/War3Net.CodeAnalysis.Jass/Renderer/ArrayReferenceExpressionRenderer.cs deleted file mode 100644 index 8997e33f..00000000 --- a/src/War3Net.CodeAnalysis.Jass/Renderer/ArrayReferenceExpressionRenderer.cs +++ /dev/null @@ -1,22 +0,0 @@ -// ------------------------------------------------------------------------------ -// -// Licensed under the MIT license. -// See the LICENSE file in the project root for more information. -// -// ------------------------------------------------------------------------------ - -using War3Net.CodeAnalysis.Jass.Syntax; - -namespace War3Net.CodeAnalysis.Jass -{ - public partial class JassRenderer - { - public void Render(JassArrayReferenceExpressionSyntax arrayReferenceExpression) - { - Render(arrayReferenceExpression.IdentifierName); - Write(JassSymbol.LeftSquareBracket); - Render(arrayReferenceExpression.Indexer); - Write(JassSymbol.RightSquareBracket); - } - } -} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Renderer/BinaryExpressionRenderer.cs b/src/War3Net.CodeAnalysis.Jass/Renderer/BinaryExpressionRenderer.cs index 3fed0971..e780749a 100644 --- a/src/War3Net.CodeAnalysis.Jass/Renderer/BinaryExpressionRenderer.cs +++ b/src/War3Net.CodeAnalysis.Jass/Renderer/BinaryExpressionRenderer.cs @@ -5,7 +5,6 @@ // // ------------------------------------------------------------------------------ -using War3Net.CodeAnalysis.Jass.Extensions; using War3Net.CodeAnalysis.Jass.Syntax; namespace War3Net.CodeAnalysis.Jass @@ -15,7 +14,9 @@ public partial class JassRenderer public void Render(JassBinaryExpressionSyntax binaryExpression) { Render(binaryExpression.Left); - Write($" {binaryExpression.Operator.GetSymbol()} "); + WriteSpace(); + Render(binaryExpression.OperatorToken); + WriteSpace(); Render(binaryExpression.Right); } } diff --git a/src/War3Net.CodeAnalysis.Jass/Renderer/CallStatementRenderer.cs b/src/War3Net.CodeAnalysis.Jass/Renderer/CallStatementRenderer.cs index 4190deac..623ac6e0 100644 --- a/src/War3Net.CodeAnalysis.Jass/Renderer/CallStatementRenderer.cs +++ b/src/War3Net.CodeAnalysis.Jass/Renderer/CallStatementRenderer.cs @@ -13,11 +13,10 @@ public partial class JassRenderer { public void Render(JassCallStatementSyntax callStatement) { - Write($"{JassKeyword.Call} "); + Render(callStatement.CallToken); + WriteSpace(); Render(callStatement.IdentifierName); - Write(JassSymbol.LeftParenthesis); - Render(callStatement.Arguments); - Write(JassSymbol.RightParenthesis); + Render(callStatement.ArgumentList); } } } \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Renderer/CompilationUnitRenderer.cs b/src/War3Net.CodeAnalysis.Jass/Renderer/CompilationUnitRenderer.cs index 6f81b0e5..379880cd 100644 --- a/src/War3Net.CodeAnalysis.Jass/Renderer/CompilationUnitRenderer.cs +++ b/src/War3Net.CodeAnalysis.Jass/Renderer/CompilationUnitRenderer.cs @@ -16,8 +16,9 @@ public void Render(JassCompilationUnitSyntax compilationUnit) foreach (var declaration in compilationUnit.Declarations) { Render(declaration); - WriteLine(); } + + Render(compilationUnit.EndOfFileToken.LeadingTrivia); } } } \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Renderer/DebugStatementRenderer.cs b/src/War3Net.CodeAnalysis.Jass/Renderer/DebugStatementRenderer.cs index 28050239..783e645a 100644 --- a/src/War3Net.CodeAnalysis.Jass/Renderer/DebugStatementRenderer.cs +++ b/src/War3Net.CodeAnalysis.Jass/Renderer/DebugStatementRenderer.cs @@ -13,7 +13,8 @@ public partial class JassRenderer { public void Render(JassDebugStatementSyntax debugStatement) { - Write($"{JassKeyword.Debug} "); + Render(debugStatement.DebugToken); + WriteSpace(); Render(debugStatement.Statement); } } diff --git a/src/War3Net.CodeAnalysis.Jass/Renderer/DecimalLiteralExpressionRenderer.cs b/src/War3Net.CodeAnalysis.Jass/Renderer/DecimalLiteralExpressionRenderer.cs deleted file mode 100644 index 4d6f7640..00000000 --- a/src/War3Net.CodeAnalysis.Jass/Renderer/DecimalLiteralExpressionRenderer.cs +++ /dev/null @@ -1,19 +0,0 @@ -// ------------------------------------------------------------------------------ -// -// Licensed under the MIT license. -// See the LICENSE file in the project root for more information. -// -// ------------------------------------------------------------------------------ - -using War3Net.CodeAnalysis.Jass.Syntax; - -namespace War3Net.CodeAnalysis.Jass -{ - public partial class JassRenderer - { - public void Render(JassDecimalLiteralExpressionSyntax decimalLiteralExpression) - { - Write(decimalLiteralExpression.ToString()); - } - } -} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Renderer/DeclarationLineRenderer.cs b/src/War3Net.CodeAnalysis.Jass/Renderer/DeclarationLineRenderer.cs deleted file mode 100644 index 4f6a0eac..00000000 --- a/src/War3Net.CodeAnalysis.Jass/Renderer/DeclarationLineRenderer.cs +++ /dev/null @@ -1,31 +0,0 @@ -// ------------------------------------------------------------------------------ -// -// Licensed under the MIT license. -// See the LICENSE file in the project root for more information. -// -// ------------------------------------------------------------------------------ - -using System; - -using War3Net.CodeAnalysis.Jass.Syntax; - -namespace War3Net.CodeAnalysis.Jass -{ - public partial class JassRenderer - { - public void Render(IDeclarationLineSyntax declarationLine) - { - switch (declarationLine) - { - case JassEmptySyntax empty: Render(empty); break; - case JassCommentSyntax comment: Render(comment); break; - case JassFunctionCustomScriptAction functionDeclaration: Render(functionDeclaration); break; - case JassGlobalsCustomScriptAction globalsDeclaration: Render(globalsDeclaration); break; - case JassNativeFunctionDeclarationSyntax nativeFunctionDeclaration: Render(nativeFunctionDeclaration); break; - case JassTypeDeclarationSyntax typeDeclaration: Render(typeDeclaration); break; - - default: throw new NotSupportedException(); - } - } - } -} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Renderer/ElementAccessClauseRenderer.cs b/src/War3Net.CodeAnalysis.Jass/Renderer/ElementAccessClauseRenderer.cs new file mode 100644 index 00000000..639cf834 --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/Renderer/ElementAccessClauseRenderer.cs @@ -0,0 +1,21 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using War3Net.CodeAnalysis.Jass.Syntax; + +namespace War3Net.CodeAnalysis.Jass +{ + public partial class JassRenderer + { + public void Render(JassElementAccessClauseSyntax elementAccessClause) + { + Render(elementAccessClause.OpenBracketToken); + Render(elementAccessClause.Expression); + Render(elementAccessClause.CloseBracketToken); + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Renderer/EndIfCustomScriptActionRenderer.cs b/src/War3Net.CodeAnalysis.Jass/Renderer/ElementAccessExpressionRenderer.cs similarity index 61% rename from src/War3Net.CodeAnalysis.Jass/Renderer/EndIfCustomScriptActionRenderer.cs rename to src/War3Net.CodeAnalysis.Jass/Renderer/ElementAccessExpressionRenderer.cs index 079ee90f..424c671f 100644 --- a/src/War3Net.CodeAnalysis.Jass/Renderer/EndIfCustomScriptActionRenderer.cs +++ b/src/War3Net.CodeAnalysis.Jass/Renderer/ElementAccessExpressionRenderer.cs @@ -1,5 +1,5 @@ // ------------------------------------------------------------------------------ -// +// // Licensed under the MIT license. // See the LICENSE file in the project root for more information. // @@ -11,10 +11,10 @@ namespace War3Net.CodeAnalysis.Jass { public partial class JassRenderer { - public void Render(JassEndIfCustomScriptAction endIfCustomScriptAction) + public void Render(JassElementAccessExpressionSyntax elementAccessExpression) { - Outdent(); - Write(JassKeyword.EndIf); + Render(elementAccessExpression.IdentifierName); + Render(elementAccessExpression.ElementAccessClause); } } } \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Renderer/ElseClauseRenderer.cs b/src/War3Net.CodeAnalysis.Jass/Renderer/ElseClauseRenderer.cs index d38f3814..6f717958 100644 --- a/src/War3Net.CodeAnalysis.Jass/Renderer/ElseClauseRenderer.cs +++ b/src/War3Net.CodeAnalysis.Jass/Renderer/ElseClauseRenderer.cs @@ -13,9 +13,9 @@ public partial class JassRenderer { public void Render(JassElseClauseSyntax elseClause) { - WriteLine(JassKeyword.Else); + Render(elseClause.ElseToken); Indent(); - Render(elseClause.Body); + Render(elseClause.Statements); Outdent(); } } diff --git a/src/War3Net.CodeAnalysis.Jass/Renderer/LoopCustomScriptActionRenderer.cs b/src/War3Net.CodeAnalysis.Jass/Renderer/ElseIfClauseDeclaratorRenderer.cs similarity index 55% rename from src/War3Net.CodeAnalysis.Jass/Renderer/LoopCustomScriptActionRenderer.cs rename to src/War3Net.CodeAnalysis.Jass/Renderer/ElseIfClauseDeclaratorRenderer.cs index 3f2918fb..f5d5883c 100644 --- a/src/War3Net.CodeAnalysis.Jass/Renderer/LoopCustomScriptActionRenderer.cs +++ b/src/War3Net.CodeAnalysis.Jass/Renderer/ElseIfClauseDeclaratorRenderer.cs @@ -1,5 +1,5 @@ // ------------------------------------------------------------------------------ -// +// // Licensed under the MIT license. // See the LICENSE file in the project root for more information. // @@ -11,10 +11,13 @@ namespace War3Net.CodeAnalysis.Jass { public partial class JassRenderer { - public void Render(JassLoopCustomScriptAction loopCustomScriptAction) + public void Render(JassElseIfClauseDeclaratorSyntax elseIfClauseDeclarator) { - Write(JassKeyword.Loop); - Indent(); + Render(elseIfClauseDeclarator.ElseIfToken); + WriteSpace(); + Render(elseIfClauseDeclarator.Condition); + WriteSpace(); + Render(elseIfClauseDeclarator.ThenToken); } } } \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Renderer/ElseIfClauseRenderer.cs b/src/War3Net.CodeAnalysis.Jass/Renderer/ElseIfClauseRenderer.cs index c8c9d6f5..5082e577 100644 --- a/src/War3Net.CodeAnalysis.Jass/Renderer/ElseIfClauseRenderer.cs +++ b/src/War3Net.CodeAnalysis.Jass/Renderer/ElseIfClauseRenderer.cs @@ -13,11 +13,9 @@ public partial class JassRenderer { public void Render(JassElseIfClauseSyntax elseIfClause) { - Write($"{JassKeyword.ElseIf} "); - Render(elseIfClause.Condition); - WriteLine($" {JassKeyword.Then}"); + Render(elseIfClause.ElseIfClauseDeclarator); Indent(); - Render(elseIfClause.Body); + Render(elseIfClause.Statements); Outdent(); } } diff --git a/src/War3Net.CodeAnalysis.Jass/Renderer/ElseIfCustomScriptActionRenderer.cs b/src/War3Net.CodeAnalysis.Jass/Renderer/ElseIfCustomScriptActionRenderer.cs deleted file mode 100644 index ba041053..00000000 --- a/src/War3Net.CodeAnalysis.Jass/Renderer/ElseIfCustomScriptActionRenderer.cs +++ /dev/null @@ -1,23 +0,0 @@ -// ------------------------------------------------------------------------------ -// -// Licensed under the MIT license. -// See the LICENSE file in the project root for more information. -// -// ------------------------------------------------------------------------------ - -using War3Net.CodeAnalysis.Jass.Syntax; - -namespace War3Net.CodeAnalysis.Jass -{ - public partial class JassRenderer - { - public void Render(JassElseIfCustomScriptAction elseIfCustomScriptAction) - { - Outdent(); - Write($"{JassKeyword.ElseIf} "); - Render(elseIfCustomScriptAction.Condition); - Write($" {JassKeyword.Then}"); - Indent(); - } - } -} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Renderer/EmptyParameterListRenderer.cs b/src/War3Net.CodeAnalysis.Jass/Renderer/EmptyParameterListRenderer.cs new file mode 100644 index 00000000..ece2e7f2 --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/Renderer/EmptyParameterListRenderer.cs @@ -0,0 +1,21 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using War3Net.CodeAnalysis.Jass.Syntax; + +namespace War3Net.CodeAnalysis.Jass +{ + public partial class JassRenderer + { + public void Render(JassEmptyParameterListSyntax emptyParameterList) + { + Render(emptyParameterList.TakesToken); + WriteSpace(); + Render(emptyParameterList.NothingToken); + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Renderer/EndFunctionCustomScriptActionRenderer.cs b/src/War3Net.CodeAnalysis.Jass/Renderer/EndFunctionCustomScriptActionRenderer.cs deleted file mode 100644 index 6015b488..00000000 --- a/src/War3Net.CodeAnalysis.Jass/Renderer/EndFunctionCustomScriptActionRenderer.cs +++ /dev/null @@ -1,20 +0,0 @@ -// ------------------------------------------------------------------------------ -// -// Licensed under the MIT license. -// See the LICENSE file in the project root for more information. -// -// ------------------------------------------------------------------------------ - -using War3Net.CodeAnalysis.Jass.Syntax; - -namespace War3Net.CodeAnalysis.Jass -{ - public partial class JassRenderer - { - public void Render(JassEndFunctionCustomScriptAction endFunctionCustomScriptAction) - { - Outdent(); - Write(JassKeyword.EndFunction); - } - } -} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Renderer/EndGlobalsCustomScriptActionRenderer.cs b/src/War3Net.CodeAnalysis.Jass/Renderer/EndGlobalsCustomScriptActionRenderer.cs deleted file mode 100644 index 87b4117a..00000000 --- a/src/War3Net.CodeAnalysis.Jass/Renderer/EndGlobalsCustomScriptActionRenderer.cs +++ /dev/null @@ -1,20 +0,0 @@ -// ------------------------------------------------------------------------------ -// -// Licensed under the MIT license. -// See the LICENSE file in the project root for more information. -// -// ------------------------------------------------------------------------------ - -using War3Net.CodeAnalysis.Jass.Syntax; - -namespace War3Net.CodeAnalysis.Jass -{ - public partial class JassRenderer - { - public void Render(JassEndGlobalsCustomScriptAction endGlobalsCustomScriptAction) - { - Outdent(); - Write(JassKeyword.EndGlobals); - } - } -} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Renderer/EqualsValueClauseRenderer.cs b/src/War3Net.CodeAnalysis.Jass/Renderer/EqualsValueClauseRenderer.cs index 1a4d256a..942426d9 100644 --- a/src/War3Net.CodeAnalysis.Jass/Renderer/EqualsValueClauseRenderer.cs +++ b/src/War3Net.CodeAnalysis.Jass/Renderer/EqualsValueClauseRenderer.cs @@ -13,7 +13,8 @@ public partial class JassRenderer { public void Render(JassEqualsValueClauseSyntax equalsValueClause) { - Write($"{JassSymbol.EqualsSign} "); + Render(equalsValueClause.EqualsToken); + WriteSpace(); Render(equalsValueClause.Expression); } } diff --git a/src/War3Net.CodeAnalysis.Jass/Renderer/ExitStatementRenderer.cs b/src/War3Net.CodeAnalysis.Jass/Renderer/ExitStatementRenderer.cs index cc90c5ad..0d914f59 100644 --- a/src/War3Net.CodeAnalysis.Jass/Renderer/ExitStatementRenderer.cs +++ b/src/War3Net.CodeAnalysis.Jass/Renderer/ExitStatementRenderer.cs @@ -13,7 +13,8 @@ public partial class JassRenderer { public void Render(JassExitStatementSyntax exitStatement) { - Write($"{JassKeyword.ExitWhen} "); + Render(exitStatement.ExitWhenToken); + WriteSpace(); Render(exitStatement.Condition); } } diff --git a/src/War3Net.CodeAnalysis.Jass/Renderer/ExpressionRenderer.cs b/src/War3Net.CodeAnalysis.Jass/Renderer/ExpressionRenderer.cs index 6b9907ea..84caefde 100644 --- a/src/War3Net.CodeAnalysis.Jass/Renderer/ExpressionRenderer.cs +++ b/src/War3Net.CodeAnalysis.Jass/Renderer/ExpressionRenderer.cs @@ -13,23 +13,15 @@ namespace War3Net.CodeAnalysis.Jass { public partial class JassRenderer { - public void Render(IExpressionSyntax expression) + public void Render(JassExpressionSyntax expression) { switch (expression) { - case JassCharacterLiteralExpressionSyntax characterLiteralExpression: Render(characterLiteralExpression); break; - case JassFourCCLiteralExpressionSyntax fourCCLiteralExpression: Render(fourCCLiteralExpression); break; - case JassHexadecimalLiteralExpressionSyntax hexadecimalLiteralExpression: Render(hexadecimalLiteralExpression); break; - case JassRealLiteralExpressionSyntax realLiteralExpression: Render(realLiteralExpression); break; - case JassOctalLiteralExpressionSyntax octalLiteralExpression: Render(octalLiteralExpression); break; - case JassDecimalLiteralExpressionSyntax decimalLiteralExpression: Render(decimalLiteralExpression); break; - case JassBooleanLiteralExpressionSyntax booleanLiteralExpression: Render(booleanLiteralExpression); break; - case JassStringLiteralExpressionSyntax stringLiteralExpression: Render(stringLiteralExpression); break; - case JassNullLiteralExpressionSyntax nullLiteralExpression: Render(nullLiteralExpression); break; + case JassLiteralExpressionSyntax literalExpression: Render(literalExpression); break; case JassFunctionReferenceExpressionSyntax functionReferenceExpression: Render(functionReferenceExpression); break; case JassInvocationExpressionSyntax invocationExpression: Render(invocationExpression); break; - case JassArrayReferenceExpressionSyntax arrayReferenceExpression: Render(arrayReferenceExpression); break; - case JassVariableReferenceExpressionSyntax variableReferenceExpression: Render(variableReferenceExpression); break; + case JassElementAccessExpressionSyntax elementAccessExpression: Render(elementAccessExpression); break; + case JassIdentifierNameSyntax identifierName: Render(identifierName); break; case JassParenthesizedExpressionSyntax parenthesizedExpression: Render(parenthesizedExpression); break; case JassUnaryExpressionSyntax unaryExpression: Render(unaryExpression); break; case JassBinaryExpressionSyntax binaryExpression: Render(binaryExpression); break; diff --git a/src/War3Net.CodeAnalysis.Jass/Renderer/FourCCLiteralExpressionRenderer.cs b/src/War3Net.CodeAnalysis.Jass/Renderer/FourCCLiteralExpressionRenderer.cs deleted file mode 100644 index 27976c6c..00000000 --- a/src/War3Net.CodeAnalysis.Jass/Renderer/FourCCLiteralExpressionRenderer.cs +++ /dev/null @@ -1,19 +0,0 @@ -// ------------------------------------------------------------------------------ -// -// Licensed under the MIT license. -// See the LICENSE file in the project root for more information. -// -// ------------------------------------------------------------------------------ - -using War3Net.CodeAnalysis.Jass.Syntax; - -namespace War3Net.CodeAnalysis.Jass -{ - public partial class JassRenderer - { - public void Render(JassFourCCLiteralExpressionSyntax fourCCLiteralExpression) - { - Write(fourCCLiteralExpression.ToString()); - } - } -} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Renderer/FunctionCustomScriptActionRenderer.cs b/src/War3Net.CodeAnalysis.Jass/Renderer/FunctionCustomScriptActionRenderer.cs deleted file mode 100644 index 402a0ea6..00000000 --- a/src/War3Net.CodeAnalysis.Jass/Renderer/FunctionCustomScriptActionRenderer.cs +++ /dev/null @@ -1,21 +0,0 @@ -// ------------------------------------------------------------------------------ -// -// Licensed under the MIT license. -// See the LICENSE file in the project root for more information. -// -// ------------------------------------------------------------------------------ - -using War3Net.CodeAnalysis.Jass.Syntax; - -namespace War3Net.CodeAnalysis.Jass -{ - public partial class JassRenderer - { - public void Render(JassFunctionCustomScriptAction functionCustomScriptAction) - { - Write($"{JassKeyword.Function} "); - Render(functionCustomScriptAction.FunctionDeclarator); - Indent(); - } - } -} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Renderer/FunctionDeclarationRenderer.cs b/src/War3Net.CodeAnalysis.Jass/Renderer/FunctionDeclarationRenderer.cs index faf01ffa..a155b43e 100644 --- a/src/War3Net.CodeAnalysis.Jass/Renderer/FunctionDeclarationRenderer.cs +++ b/src/War3Net.CodeAnalysis.Jass/Renderer/FunctionDeclarationRenderer.cs @@ -13,13 +13,11 @@ public partial class JassRenderer { public void Render(JassFunctionDeclarationSyntax functionDeclaration) { - Write($"{JassKeyword.Function} "); Render(functionDeclaration.FunctionDeclarator); - WriteLine(); Indent(); - Render(functionDeclaration.Body); + Render(functionDeclaration.Statements); Outdent(); - Write(JassKeyword.EndFunction); + Render(functionDeclaration.EndFunctionToken); } } } \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Renderer/FunctionDeclaratorRenderer.cs b/src/War3Net.CodeAnalysis.Jass/Renderer/FunctionDeclaratorRenderer.cs index ba19279a..7c714d33 100644 --- a/src/War3Net.CodeAnalysis.Jass/Renderer/FunctionDeclaratorRenderer.cs +++ b/src/War3Net.CodeAnalysis.Jass/Renderer/FunctionDeclaratorRenderer.cs @@ -13,11 +13,19 @@ public partial class JassRenderer { public void Render(JassFunctionDeclaratorSyntax functionDeclarator) { + if (functionDeclarator.ConstantToken is not null) + { + Render(functionDeclarator.ConstantToken); + WriteSpace(); + } + + Render(functionDeclarator.FunctionToken); + WriteSpace(); Render(functionDeclarator.IdentifierName); - Write($" {JassKeyword.Takes} "); + WriteSpace(); Render(functionDeclarator.ParameterList); - Write($" {JassKeyword.Returns} "); - Render(functionDeclarator.ReturnType); + WriteSpace(); + Render(functionDeclarator.ReturnClause); } } } \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Renderer/FunctionReferenceExpressionRenderer.cs b/src/War3Net.CodeAnalysis.Jass/Renderer/FunctionReferenceExpressionRenderer.cs index 4c867aca..0293900a 100644 --- a/src/War3Net.CodeAnalysis.Jass/Renderer/FunctionReferenceExpressionRenderer.cs +++ b/src/War3Net.CodeAnalysis.Jass/Renderer/FunctionReferenceExpressionRenderer.cs @@ -13,7 +13,8 @@ public partial class JassRenderer { public void Render(JassFunctionReferenceExpressionSyntax functionReferenceExpression) { - Write($"{JassKeyword.Function} "); + Render(functionReferenceExpression.FunctionToken); + WriteSpace(); Render(functionReferenceExpression.IdentifierName); } } diff --git a/src/War3Net.CodeAnalysis.Jass/Renderer/GlobalConstantDeclarationRenderer.cs b/src/War3Net.CodeAnalysis.Jass/Renderer/GlobalConstantDeclarationRenderer.cs new file mode 100644 index 00000000..284b4cea --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/Renderer/GlobalConstantDeclarationRenderer.cs @@ -0,0 +1,25 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using War3Net.CodeAnalysis.Jass.Syntax; + +namespace War3Net.CodeAnalysis.Jass +{ + public partial class JassRenderer + { + public void Render(JassGlobalConstantDeclarationSyntax globalConstantDeclaration) + { + Render(globalConstantDeclaration.ConstantToken); + WriteSpace(); + Render(globalConstantDeclaration.Type); + WriteSpace(); + Render(globalConstantDeclaration.IdentifierName); + WriteSpace(); + Render(globalConstantDeclaration.Value); + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Renderer/GlobalDeclarationRenderer.cs b/src/War3Net.CodeAnalysis.Jass/Renderer/GlobalDeclarationRenderer.cs index 0b670e56..a3fdd710 100644 --- a/src/War3Net.CodeAnalysis.Jass/Renderer/GlobalDeclarationRenderer.cs +++ b/src/War3Net.CodeAnalysis.Jass/Renderer/GlobalDeclarationRenderer.cs @@ -15,16 +15,10 @@ public partial class JassRenderer { public void Render(JassGlobalDeclarationSyntax globalDeclaration) { - Render(globalDeclaration.Declarator); - } - - public void Render(IGlobalDeclarationSyntax declaration) - { - switch (declaration) + switch (globalDeclaration) { - case JassEmptySyntax empty: Render(empty); break; - case JassCommentSyntax comment: Render(comment); break; - case JassGlobalDeclarationSyntax globalDeclaration: Render(globalDeclaration); break; + case JassGlobalConstantDeclarationSyntax globalConstantDeclaration: Render(globalConstantDeclaration); break; + case JassGlobalVariableDeclarationSyntax globalVariableDeclaration: Render(globalVariableDeclaration); break; default: throw new NotSupportedException(); } diff --git a/src/War3Net.CodeAnalysis.Jass/Renderer/EndLoopCustomScriptActionRenderer.cs b/src/War3Net.CodeAnalysis.Jass/Renderer/GlobalVariableDeclarationRenderer.cs similarity index 67% rename from src/War3Net.CodeAnalysis.Jass/Renderer/EndLoopCustomScriptActionRenderer.cs rename to src/War3Net.CodeAnalysis.Jass/Renderer/GlobalVariableDeclarationRenderer.cs index 976eaae6..47226d1b 100644 --- a/src/War3Net.CodeAnalysis.Jass/Renderer/EndLoopCustomScriptActionRenderer.cs +++ b/src/War3Net.CodeAnalysis.Jass/Renderer/GlobalVariableDeclarationRenderer.cs @@ -1,5 +1,5 @@ // ------------------------------------------------------------------------------ -// +// // Licensed under the MIT license. // See the LICENSE file in the project root for more information. // @@ -11,10 +11,9 @@ namespace War3Net.CodeAnalysis.Jass { public partial class JassRenderer { - public void Render(JassEndLoopCustomScriptAction endLoopCustomScriptAction) + public void Render(JassGlobalVariableDeclarationSyntax globalVariableDeclaration) { - Outdent(); - Write(JassKeyword.EndLoop); + Render(globalVariableDeclaration.Declarator); } } } \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Renderer/GlobalsCustomScriptActionRenderer.cs b/src/War3Net.CodeAnalysis.Jass/Renderer/GlobalsCustomScriptActionRenderer.cs deleted file mode 100644 index 23cadf12..00000000 --- a/src/War3Net.CodeAnalysis.Jass/Renderer/GlobalsCustomScriptActionRenderer.cs +++ /dev/null @@ -1,20 +0,0 @@ -// ------------------------------------------------------------------------------ -// -// Licensed under the MIT license. -// See the LICENSE file in the project root for more information. -// -// ------------------------------------------------------------------------------ - -using War3Net.CodeAnalysis.Jass.Syntax; - -namespace War3Net.CodeAnalysis.Jass -{ - public partial class JassRenderer - { - public void Render(JassGlobalsCustomScriptAction globalsCustomScriptAction) - { - Write(JassKeyword.Globals); - Indent(); - } - } -} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Renderer/GlobalDeclarationListRenderer.cs b/src/War3Net.CodeAnalysis.Jass/Renderer/GlobalsDeclarationRenderer.cs similarity index 61% rename from src/War3Net.CodeAnalysis.Jass/Renderer/GlobalDeclarationListRenderer.cs rename to src/War3Net.CodeAnalysis.Jass/Renderer/GlobalsDeclarationRenderer.cs index fb53997a..4d2d0652 100644 --- a/src/War3Net.CodeAnalysis.Jass/Renderer/GlobalDeclarationListRenderer.cs +++ b/src/War3Net.CodeAnalysis.Jass/Renderer/GlobalsDeclarationRenderer.cs @@ -1,5 +1,5 @@ // ------------------------------------------------------------------------------ -// +// // Licensed under the MIT license. // See the LICENSE file in the project root for more information. // @@ -11,19 +11,18 @@ namespace War3Net.CodeAnalysis.Jass { public partial class JassRenderer { - public void Render(JassGlobalDeclarationListSyntax globalDeclarationList) + public void Render(JassGlobalsDeclarationSyntax globalsDeclaration) { - WriteLine(JassKeyword.Globals); + Render(globalsDeclaration.GlobalsToken); Indent(); - foreach (var globalDeclaration in globalDeclarationList.Globals) + foreach (var globalDeclaration in globalsDeclaration.GlobalDeclarations) { Render(globalDeclaration); - WriteLine(); } Outdent(); - Write(JassKeyword.EndGlobals); + Render(globalsDeclaration.EndGlobalsToken); } } } \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Renderer/HexadecimalLiteralExpressionRenderer.cs b/src/War3Net.CodeAnalysis.Jass/Renderer/HexadecimalLiteralExpressionRenderer.cs deleted file mode 100644 index 39461542..00000000 --- a/src/War3Net.CodeAnalysis.Jass/Renderer/HexadecimalLiteralExpressionRenderer.cs +++ /dev/null @@ -1,19 +0,0 @@ -// ------------------------------------------------------------------------------ -// -// Licensed under the MIT license. -// See the LICENSE file in the project root for more information. -// -// ------------------------------------------------------------------------------ - -using War3Net.CodeAnalysis.Jass.Syntax; - -namespace War3Net.CodeAnalysis.Jass -{ - public partial class JassRenderer - { - public void Render(JassHexadecimalLiteralExpressionSyntax hexadecimalLiteralExpression) - { - Write(hexadecimalLiteralExpression.ToString()); - } - } -} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Renderer/IdentifierNameRenderer.cs b/src/War3Net.CodeAnalysis.Jass/Renderer/IdentifierNameRenderer.cs index f45b94c1..5669bf3f 100644 --- a/src/War3Net.CodeAnalysis.Jass/Renderer/IdentifierNameRenderer.cs +++ b/src/War3Net.CodeAnalysis.Jass/Renderer/IdentifierNameRenderer.cs @@ -13,7 +13,7 @@ public partial class JassRenderer { public void Render(JassIdentifierNameSyntax identifierName) { - Write(identifierName.Name); + Render(identifierName.Token); } } } \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Renderer/IfClauseDeclaratorRenderer.cs b/src/War3Net.CodeAnalysis.Jass/Renderer/IfClauseDeclaratorRenderer.cs new file mode 100644 index 00000000..8021fe0e --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/Renderer/IfClauseDeclaratorRenderer.cs @@ -0,0 +1,23 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using War3Net.CodeAnalysis.Jass.Syntax; + +namespace War3Net.CodeAnalysis.Jass +{ + public partial class JassRenderer + { + public void Render(JassIfClauseDeclaratorSyntax ifClauseDeclarator) + { + Render(ifClauseDeclarator.IfToken); + WriteSpace(); + Render(ifClauseDeclarator.Condition); + WriteSpace(); + Render(ifClauseDeclarator.ThenToken); + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Renderer/ElseCustomScriptActionRenderer.cs b/src/War3Net.CodeAnalysis.Jass/Renderer/IfClauseRenderer.cs similarity index 69% rename from src/War3Net.CodeAnalysis.Jass/Renderer/ElseCustomScriptActionRenderer.cs rename to src/War3Net.CodeAnalysis.Jass/Renderer/IfClauseRenderer.cs index 5b711ee3..1821b0ef 100644 --- a/src/War3Net.CodeAnalysis.Jass/Renderer/ElseCustomScriptActionRenderer.cs +++ b/src/War3Net.CodeAnalysis.Jass/Renderer/IfClauseRenderer.cs @@ -1,5 +1,5 @@ // ------------------------------------------------------------------------------ -// +// // Licensed under the MIT license. // See the LICENSE file in the project root for more information. // @@ -11,11 +11,12 @@ namespace War3Net.CodeAnalysis.Jass { public partial class JassRenderer { - public void Render(JassElseCustomScriptAction elseCustomScriptAction) + public void Render(JassIfClauseSyntax ifClause) { - Outdent(); - Write(JassKeyword.Else); + Render(ifClause.IfClauseDeclarator); Indent(); + Render(ifClause.Statements); + Outdent(); } } } \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Renderer/IfCustomScriptActionRenderer.cs b/src/War3Net.CodeAnalysis.Jass/Renderer/IfCustomScriptActionRenderer.cs deleted file mode 100644 index 0739e01f..00000000 --- a/src/War3Net.CodeAnalysis.Jass/Renderer/IfCustomScriptActionRenderer.cs +++ /dev/null @@ -1,22 +0,0 @@ -// ------------------------------------------------------------------------------ -// -// Licensed under the MIT license. -// See the LICENSE file in the project root for more information. -// -// ------------------------------------------------------------------------------ - -using War3Net.CodeAnalysis.Jass.Syntax; - -namespace War3Net.CodeAnalysis.Jass -{ - public partial class JassRenderer - { - public void Render(JassIfCustomScriptAction ifCustomScriptAction) - { - Write($"{JassKeyword.If} "); - Render(ifCustomScriptAction.Condition); - Write($" {JassKeyword.Then}"); - Indent(); - } - } -} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Renderer/IfStatementRenderer.cs b/src/War3Net.CodeAnalysis.Jass/Renderer/IfStatementRenderer.cs index 0a3fbbf8..66386a5c 100644 --- a/src/War3Net.CodeAnalysis.Jass/Renderer/IfStatementRenderer.cs +++ b/src/War3Net.CodeAnalysis.Jass/Renderer/IfStatementRenderer.cs @@ -13,12 +13,7 @@ public partial class JassRenderer { public void Render(JassIfStatementSyntax ifStatement) { - Write($"{JassKeyword.If} "); - Render(ifStatement.Condition); - WriteLine($" {JassKeyword.Then}"); - Indent(); - Render(ifStatement.Body); - Outdent(); + Render(ifStatement.IfClause); foreach (var elseIfClause in ifStatement.ElseIfClauses) { @@ -30,7 +25,7 @@ public void Render(JassIfStatementSyntax ifStatement) Render(ifStatement.ElseClause); } - Write(JassKeyword.EndIf); + Render(ifStatement.EndIfToken); } } } \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Renderer/InvocationExpressionRenderer.cs b/src/War3Net.CodeAnalysis.Jass/Renderer/InvocationExpressionRenderer.cs index 3c905037..78c52ca0 100644 --- a/src/War3Net.CodeAnalysis.Jass/Renderer/InvocationExpressionRenderer.cs +++ b/src/War3Net.CodeAnalysis.Jass/Renderer/InvocationExpressionRenderer.cs @@ -14,9 +14,7 @@ public partial class JassRenderer public void Render(JassInvocationExpressionSyntax invocationExpression) { Render(invocationExpression.IdentifierName); - Write(JassSymbol.LeftParenthesis); - Render(invocationExpression.Arguments); - Write(JassSymbol.RightParenthesis); + Render(invocationExpression.ArgumentList); } } } \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Renderer/EmptyRenderer.cs b/src/War3Net.CodeAnalysis.Jass/Renderer/LiteralExpressionRenderer.cs similarity index 69% rename from src/War3Net.CodeAnalysis.Jass/Renderer/EmptyRenderer.cs rename to src/War3Net.CodeAnalysis.Jass/Renderer/LiteralExpressionRenderer.cs index e90414d4..861a66d6 100644 --- a/src/War3Net.CodeAnalysis.Jass/Renderer/EmptyRenderer.cs +++ b/src/War3Net.CodeAnalysis.Jass/Renderer/LiteralExpressionRenderer.cs @@ -1,5 +1,5 @@ // ------------------------------------------------------------------------------ -// +// // Licensed under the MIT license. // See the LICENSE file in the project root for more information. // @@ -11,8 +11,9 @@ namespace War3Net.CodeAnalysis.Jass { public partial class JassRenderer { - public void Render(JassEmptySyntax empty) + public void Render(JassLiteralExpressionSyntax literalExpression) { + Render(literalExpression.Token); } } } \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Renderer/LocalVariableDeclarationStatementRenderer.cs b/src/War3Net.CodeAnalysis.Jass/Renderer/LocalVariableDeclarationStatementRenderer.cs index 485852c5..bf4358ec 100644 --- a/src/War3Net.CodeAnalysis.Jass/Renderer/LocalVariableDeclarationStatementRenderer.cs +++ b/src/War3Net.CodeAnalysis.Jass/Renderer/LocalVariableDeclarationStatementRenderer.cs @@ -13,7 +13,8 @@ public partial class JassRenderer { public void Render(JassLocalVariableDeclarationStatementSyntax localVariableDeclarationStatement) { - Write($"{JassKeyword.Local} "); + Render(localVariableDeclarationStatement.LocalToken); + WriteSpace(); Render(localVariableDeclarationStatement.Declarator); } } diff --git a/src/War3Net.CodeAnalysis.Jass/Renderer/LoopStatementRenderer.cs b/src/War3Net.CodeAnalysis.Jass/Renderer/LoopStatementRenderer.cs index 0a2825b2..007b1137 100644 --- a/src/War3Net.CodeAnalysis.Jass/Renderer/LoopStatementRenderer.cs +++ b/src/War3Net.CodeAnalysis.Jass/Renderer/LoopStatementRenderer.cs @@ -13,11 +13,11 @@ public partial class JassRenderer { public void Render(JassLoopStatementSyntax loopStatement) { - WriteLine(JassKeyword.Loop); + Render(loopStatement.LoopToken); Indent(); - Render(loopStatement.Body); + Render(loopStatement.Statements); Outdent(); - Write(JassKeyword.EndLoop); + Render(loopStatement.EndLoopToken); } } } \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Renderer/NativeFunctionDeclarationRenderer.cs b/src/War3Net.CodeAnalysis.Jass/Renderer/NativeFunctionDeclarationRenderer.cs index 462e3156..6a37d49a 100644 --- a/src/War3Net.CodeAnalysis.Jass/Renderer/NativeFunctionDeclarationRenderer.cs +++ b/src/War3Net.CodeAnalysis.Jass/Renderer/NativeFunctionDeclarationRenderer.cs @@ -13,8 +13,19 @@ public partial class JassRenderer { public void Render(JassNativeFunctionDeclarationSyntax nativeFunctionDeclaration) { - Write($"{JassKeyword.Native} "); - Render(nativeFunctionDeclaration.FunctionDeclarator); + if (nativeFunctionDeclaration.ConstantToken is not null) + { + Render(nativeFunctionDeclaration.ConstantToken); + WriteSpace(); + } + + Render(nativeFunctionDeclaration.NativeToken); + WriteSpace(); + Render(nativeFunctionDeclaration.IdentifierName); + WriteSpace(); + Render(nativeFunctionDeclaration.ParameterList); + WriteSpace(); + Render(nativeFunctionDeclaration.ReturnClause); } } } \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Renderer/NullLiteralExpressionRenderer.cs b/src/War3Net.CodeAnalysis.Jass/Renderer/NullLiteralExpressionRenderer.cs deleted file mode 100644 index cf49415d..00000000 --- a/src/War3Net.CodeAnalysis.Jass/Renderer/NullLiteralExpressionRenderer.cs +++ /dev/null @@ -1,19 +0,0 @@ -// ------------------------------------------------------------------------------ -// -// Licensed under the MIT license. -// See the LICENSE file in the project root for more information. -// -// ------------------------------------------------------------------------------ - -using War3Net.CodeAnalysis.Jass.Syntax; - -namespace War3Net.CodeAnalysis.Jass -{ - public partial class JassRenderer - { - public void Render(JassNullLiteralExpressionSyntax nullLiteralExpression) - { - Write(JassKeyword.Null); - } - } -} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Renderer/OctalLiteralExpressionRenderer.cs b/src/War3Net.CodeAnalysis.Jass/Renderer/OctalLiteralExpressionRenderer.cs deleted file mode 100644 index 8002c613..00000000 --- a/src/War3Net.CodeAnalysis.Jass/Renderer/OctalLiteralExpressionRenderer.cs +++ /dev/null @@ -1,19 +0,0 @@ -// ------------------------------------------------------------------------------ -// -// Licensed under the MIT license. -// See the LICENSE file in the project root for more information. -// -// ------------------------------------------------------------------------------ - -using War3Net.CodeAnalysis.Jass.Syntax; - -namespace War3Net.CodeAnalysis.Jass -{ - public partial class JassRenderer - { - public void Render(JassOctalLiteralExpressionSyntax octalLiteralExpression) - { - Write(octalLiteralExpression.ToString()); - } - } -} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Renderer/GlobalLineRenderer.cs b/src/War3Net.CodeAnalysis.Jass/Renderer/ParameterListOrEmptyParameterListRenderer.cs similarity index 52% rename from src/War3Net.CodeAnalysis.Jass/Renderer/GlobalLineRenderer.cs rename to src/War3Net.CodeAnalysis.Jass/Renderer/ParameterListOrEmptyParameterListRenderer.cs index f1b6c7a6..90fecb5b 100644 --- a/src/War3Net.CodeAnalysis.Jass/Renderer/GlobalLineRenderer.cs +++ b/src/War3Net.CodeAnalysis.Jass/Renderer/ParameterListOrEmptyParameterListRenderer.cs @@ -1,5 +1,5 @@ // ------------------------------------------------------------------------------ -// +// // Licensed under the MIT license. // See the LICENSE file in the project root for more information. // @@ -13,14 +13,12 @@ namespace War3Net.CodeAnalysis.Jass { public partial class JassRenderer { - public void Render(IGlobalLineSyntax globalLine) + public void Render(JassParameterListOrEmptyParameterListSyntax parameterListOrEmptyParameterList) { - switch (globalLine) + switch (parameterListOrEmptyParameterList) { - case JassEmptySyntax empty: Render(empty); break; - case JassCommentSyntax comment: Render(comment); break; - case JassGlobalDeclarationSyntax globalDeclaration: Render(globalDeclaration); break; - case JassEndGlobalsCustomScriptAction endGlobals: Render(endGlobals); break; + case JassParameterListSyntax parameterList: Render(parameterList); break; + case JassEmptyParameterListSyntax emptyParameterList: Render(emptyParameterList); break; default: throw new NotSupportedException(); } diff --git a/src/War3Net.CodeAnalysis.Jass/Renderer/ParameterListRenderer.cs b/src/War3Net.CodeAnalysis.Jass/Renderer/ParameterListRenderer.cs index b9116b73..6bc840ce 100644 --- a/src/War3Net.CodeAnalysis.Jass/Renderer/ParameterListRenderer.cs +++ b/src/War3Net.CodeAnalysis.Jass/Renderer/ParameterListRenderer.cs @@ -5,8 +5,6 @@ // // ------------------------------------------------------------------------------ -using System.Linq; - using War3Net.CodeAnalysis.Jass.Syntax; namespace War3Net.CodeAnalysis.Jass @@ -15,18 +13,15 @@ public partial class JassRenderer { public void Render(JassParameterListSyntax parameterList) { - if (parameterList.Parameters.Any()) - { - Render(parameterList.Parameters.First()); - foreach (var parameter in parameterList.Parameters.Skip(1)) - { - Write($"{JassSymbol.Comma} "); - Render(parameter); - } - } - else + Render(parameterList.TakesToken); + WriteSpace(); + + Render(parameterList.ParameterList.Items[0]); + for (var i = 1; i < parameterList.ParameterList.Items.Length; i++) { - Render(JassTypeSyntax.Nothing); + Render(parameterList.ParameterList.Separators[i - 1]); + WriteSpace(); + Render(parameterList.ParameterList.Items[i]); } } } diff --git a/src/War3Net.CodeAnalysis.Jass/Renderer/ParameterRenderer.cs b/src/War3Net.CodeAnalysis.Jass/Renderer/ParameterRenderer.cs index c4519850..d1885b1e 100644 --- a/src/War3Net.CodeAnalysis.Jass/Renderer/ParameterRenderer.cs +++ b/src/War3Net.CodeAnalysis.Jass/Renderer/ParameterRenderer.cs @@ -14,7 +14,7 @@ public partial class JassRenderer public void Render(JassParameterSyntax parameter) { Render(parameter.Type); - Write(' '); + WriteSpace(); Render(parameter.IdentifierName); } } diff --git a/src/War3Net.CodeAnalysis.Jass/Renderer/ParenthesizedExpressionRenderer.cs b/src/War3Net.CodeAnalysis.Jass/Renderer/ParenthesizedExpressionRenderer.cs index b25c776b..3ae0c383 100644 --- a/src/War3Net.CodeAnalysis.Jass/Renderer/ParenthesizedExpressionRenderer.cs +++ b/src/War3Net.CodeAnalysis.Jass/Renderer/ParenthesizedExpressionRenderer.cs @@ -13,9 +13,9 @@ public partial class JassRenderer { public void Render(JassParenthesizedExpressionSyntax parenthesizedExpression) { - Write(JassSymbol.LeftParenthesis); + Render(parenthesizedExpression.OpenParenToken); Render(parenthesizedExpression.Expression); - Write(JassSymbol.RightParenthesis); + Render(parenthesizedExpression.CloseParenToken); } } } \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Renderer/CommentRenderer.cs b/src/War3Net.CodeAnalysis.Jass/Renderer/PredefinedTypeRenderer.cs similarity index 71% rename from src/War3Net.CodeAnalysis.Jass/Renderer/CommentRenderer.cs rename to src/War3Net.CodeAnalysis.Jass/Renderer/PredefinedTypeRenderer.cs index 4c2f0288..2af47e57 100644 --- a/src/War3Net.CodeAnalysis.Jass/Renderer/CommentRenderer.cs +++ b/src/War3Net.CodeAnalysis.Jass/Renderer/PredefinedTypeRenderer.cs @@ -1,5 +1,5 @@ // ------------------------------------------------------------------------------ -// +// // Licensed under the MIT license. // See the LICENSE file in the project root for more information. // @@ -11,9 +11,9 @@ namespace War3Net.CodeAnalysis.Jass { public partial class JassRenderer { - public void Render(JassCommentSyntax comment) + public void Render(JassPredefinedTypeSyntax predefinedType) { - Write(comment.ToString()); + Render(predefinedType.Token); } } } \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Renderer/RealLiteralExpressionRenderer.cs b/src/War3Net.CodeAnalysis.Jass/Renderer/RealLiteralExpressionRenderer.cs deleted file mode 100644 index 50c3e977..00000000 --- a/src/War3Net.CodeAnalysis.Jass/Renderer/RealLiteralExpressionRenderer.cs +++ /dev/null @@ -1,19 +0,0 @@ -// ------------------------------------------------------------------------------ -// -// Licensed under the MIT license. -// See the LICENSE file in the project root for more information. -// -// ------------------------------------------------------------------------------ - -using War3Net.CodeAnalysis.Jass.Syntax; - -namespace War3Net.CodeAnalysis.Jass -{ - public partial class JassRenderer - { - public void Render(JassRealLiteralExpressionSyntax realLiteralExpression) - { - Write(realLiteralExpression.ToString()); - } - } -} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Renderer/CharacterLiteralExpressionRenderer.cs b/src/War3Net.CodeAnalysis.Jass/Renderer/ReturnClauseRenderer.cs similarity index 63% rename from src/War3Net.CodeAnalysis.Jass/Renderer/CharacterLiteralExpressionRenderer.cs rename to src/War3Net.CodeAnalysis.Jass/Renderer/ReturnClauseRenderer.cs index 32de6c83..087a941f 100644 --- a/src/War3Net.CodeAnalysis.Jass/Renderer/CharacterLiteralExpressionRenderer.cs +++ b/src/War3Net.CodeAnalysis.Jass/Renderer/ReturnClauseRenderer.cs @@ -1,5 +1,5 @@ // ------------------------------------------------------------------------------ -// +// // Licensed under the MIT license. // See the LICENSE file in the project root for more information. // @@ -11,9 +11,11 @@ namespace War3Net.CodeAnalysis.Jass { public partial class JassRenderer { - public void Render(JassCharacterLiteralExpressionSyntax characterLiteralExpression) + public void Render(JassReturnClauseSyntax returnClause) { - Write(characterLiteralExpression.ToString()); + Render(returnClause.ReturnsToken); + WriteSpace(); + Render(returnClause.ReturnType); } } } \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Renderer/ReturnStatementRenderer.cs b/src/War3Net.CodeAnalysis.Jass/Renderer/ReturnStatementRenderer.cs index 7cd13752..62bcc8b6 100644 --- a/src/War3Net.CodeAnalysis.Jass/Renderer/ReturnStatementRenderer.cs +++ b/src/War3Net.CodeAnalysis.Jass/Renderer/ReturnStatementRenderer.cs @@ -13,13 +13,10 @@ public partial class JassRenderer { public void Render(JassReturnStatementSyntax returnStatement) { - if (returnStatement.Value is null) + Render(returnStatement.ReturnToken); + if (returnStatement.Value is not null) { - Write(JassKeyword.Return); - } - else - { - Write($"{JassKeyword.Return} "); + WriteSpace(); Render(returnStatement.Value); } } diff --git a/src/War3Net.CodeAnalysis.Jass/Renderer/SetStatementRenderer.cs b/src/War3Net.CodeAnalysis.Jass/Renderer/SetStatementRenderer.cs index 6fc94969..66e79e7b 100644 --- a/src/War3Net.CodeAnalysis.Jass/Renderer/SetStatementRenderer.cs +++ b/src/War3Net.CodeAnalysis.Jass/Renderer/SetStatementRenderer.cs @@ -13,17 +13,16 @@ public partial class JassRenderer { public void Render(JassSetStatementSyntax setStatement) { - Write($"{JassKeyword.Set} "); + Render(setStatement.SetToken); + WriteSpace(); Render(setStatement.IdentifierName); - if (setStatement.Indexer is not null) + if (setStatement.ElementAccessClause is not null) { - Write(JassSymbol.LeftSquareBracket); - Render(setStatement.Indexer); - Write(JassSymbol.RightSquareBracket); + Render(setStatement.ElementAccessClause); } - Write(' '); + WriteSpace(); Render(setStatement.Value); } } diff --git a/src/War3Net.CodeAnalysis.Jass/Renderer/StatementLineRenderer.cs b/src/War3Net.CodeAnalysis.Jass/Renderer/StatementLineRenderer.cs deleted file mode 100644 index 148e1a9a..00000000 --- a/src/War3Net.CodeAnalysis.Jass/Renderer/StatementLineRenderer.cs +++ /dev/null @@ -1,40 +0,0 @@ -// ------------------------------------------------------------------------------ -// -// Licensed under the MIT license. -// See the LICENSE file in the project root for more information. -// -// ------------------------------------------------------------------------------ - -using System; - -using War3Net.CodeAnalysis.Jass.Syntax; - -namespace War3Net.CodeAnalysis.Jass -{ - public partial class JassRenderer - { - public void Render(IStatementLineSyntax statementLine) - { - switch (statementLine) - { - case JassEmptySyntax empty: Render(empty); break; - case JassCommentSyntax comment: Render(comment); break; - case JassLocalVariableDeclarationStatementSyntax localVariableDeclarationStatement: Render(localVariableDeclarationStatement); break; - case JassSetStatementSyntax setStatement: Render(setStatement); break; - case JassCallStatementSyntax callStatement: Render(callStatement); break; - case JassIfCustomScriptAction ifCustomScriptAction: Render(ifCustomScriptAction); break; - case JassElseIfCustomScriptAction elseIfCustomScriptAction: Render(elseIfCustomScriptAction); break; - case JassElseCustomScriptAction elseCustomScriptAction: Render(elseCustomScriptAction); break; - case JassEndIfCustomScriptAction endIfCustomScriptAction: Render(endIfCustomScriptAction); break; - case JassLoopCustomScriptAction loopCustomScriptAction: Render(loopCustomScriptAction); break; - case JassEndLoopCustomScriptAction endLoopCustomScriptAction: Render(endLoopCustomScriptAction); break; - case JassExitStatementSyntax exitStatement: Render(exitStatement); break; - case JassReturnStatementSyntax returnStatement: Render(returnStatement); break; - case JassEndFunctionCustomScriptAction functionCustomScriptAction: Render(functionCustomScriptAction); break; - case JassDebugCustomScriptAction debugCustomScriptAction: Render(debugCustomScriptAction); break; - - default: throw new NotSupportedException(); - } - } - } -} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Renderer/StatementListRenderer.cs b/src/War3Net.CodeAnalysis.Jass/Renderer/StatementListRenderer.cs index 5fc880d9..ed32fe10 100644 --- a/src/War3Net.CodeAnalysis.Jass/Renderer/StatementListRenderer.cs +++ b/src/War3Net.CodeAnalysis.Jass/Renderer/StatementListRenderer.cs @@ -5,18 +5,19 @@ // // ------------------------------------------------------------------------------ +using System.Collections.Immutable; + using War3Net.CodeAnalysis.Jass.Syntax; namespace War3Net.CodeAnalysis.Jass { public partial class JassRenderer { - public void Render(JassStatementListSyntax statementList) + public void Render(ImmutableArray statementList) { - foreach (var statement in statementList.Statements) + foreach (var statement in statementList) { Render(statement); - WriteLine(); } } } diff --git a/src/War3Net.CodeAnalysis.Jass/Renderer/StatementRenderer.cs b/src/War3Net.CodeAnalysis.Jass/Renderer/StatementRenderer.cs index 44f15bca..3e15c508 100644 --- a/src/War3Net.CodeAnalysis.Jass/Renderer/StatementRenderer.cs +++ b/src/War3Net.CodeAnalysis.Jass/Renderer/StatementRenderer.cs @@ -13,12 +13,10 @@ namespace War3Net.CodeAnalysis.Jass { public partial class JassRenderer { - public void Render(IStatementSyntax statement) + public void Render(JassStatementSyntax statement) { switch (statement) { - case JassEmptySyntax empty: Render(empty); break; - case JassCommentSyntax comment: Render(comment); break; case JassLocalVariableDeclarationStatementSyntax localVariableDeclarationStatement: Render(localVariableDeclarationStatement); break; case JassSetStatementSyntax setStatement: Render(setStatement); break; case JassCallStatementSyntax callStatement: Render(callStatement); break; diff --git a/src/War3Net.CodeAnalysis.Jass/Renderer/StringLiteralExpressionRenderer.cs b/src/War3Net.CodeAnalysis.Jass/Renderer/StringLiteralExpressionRenderer.cs deleted file mode 100644 index 12b54766..00000000 --- a/src/War3Net.CodeAnalysis.Jass/Renderer/StringLiteralExpressionRenderer.cs +++ /dev/null @@ -1,19 +0,0 @@ -// ------------------------------------------------------------------------------ -// -// Licensed under the MIT license. -// See the LICENSE file in the project root for more information. -// -// ------------------------------------------------------------------------------ - -using War3Net.CodeAnalysis.Jass.Syntax; - -namespace War3Net.CodeAnalysis.Jass -{ - public partial class JassRenderer - { - public void Render(JassStringLiteralExpressionSyntax stringLiteralExpression) - { - Write(stringLiteralExpression.ToString()); - } - } -} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Renderer/DebugCustomScriptActionRenderer.cs b/src/War3Net.CodeAnalysis.Jass/Renderer/SyntaxTokenRenderer.cs similarity index 63% rename from src/War3Net.CodeAnalysis.Jass/Renderer/DebugCustomScriptActionRenderer.cs rename to src/War3Net.CodeAnalysis.Jass/Renderer/SyntaxTokenRenderer.cs index 0f8bda0a..b9c5a298 100644 --- a/src/War3Net.CodeAnalysis.Jass/Renderer/DebugCustomScriptActionRenderer.cs +++ b/src/War3Net.CodeAnalysis.Jass/Renderer/SyntaxTokenRenderer.cs @@ -1,5 +1,5 @@ // ------------------------------------------------------------------------------ -// +// // Licensed under the MIT license. // See the LICENSE file in the project root for more information. // @@ -11,10 +11,11 @@ namespace War3Net.CodeAnalysis.Jass { public partial class JassRenderer { - public void Render(JassDebugCustomScriptAction debugCustomScriptAction) + public void Render(JassSyntaxToken syntaxToken) { - Write($"{JassKeyword.Debug} "); - Render(debugCustomScriptAction.Action); + Render(syntaxToken.LeadingTrivia); + Write(syntaxToken.Text); + Render(syntaxToken.TrailingTrivia); } } } \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Renderer/BooleanLiteralExpressionRenderer.cs b/src/War3Net.CodeAnalysis.Jass/Renderer/SyntaxTriviaListRenderer.cs similarity index 64% rename from src/War3Net.CodeAnalysis.Jass/Renderer/BooleanLiteralExpressionRenderer.cs rename to src/War3Net.CodeAnalysis.Jass/Renderer/SyntaxTriviaListRenderer.cs index a632e34f..e2268f02 100644 --- a/src/War3Net.CodeAnalysis.Jass/Renderer/BooleanLiteralExpressionRenderer.cs +++ b/src/War3Net.CodeAnalysis.Jass/Renderer/SyntaxTriviaListRenderer.cs @@ -1,5 +1,5 @@ // ------------------------------------------------------------------------------ -// +// // Licensed under the MIT license. // See the LICENSE file in the project root for more information. // @@ -11,9 +11,12 @@ namespace War3Net.CodeAnalysis.Jass { public partial class JassRenderer { - public void Render(JassBooleanLiteralExpressionSyntax booleanLiteralExpression) + public void Render(JassSyntaxTriviaList triviaList) { - Write(booleanLiteralExpression.ToString()); + foreach (var trivia in triviaList.Trivia) + { + Render(trivia); + } } } } \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Renderer/SyntaxTriviaRenderer.cs b/src/War3Net.CodeAnalysis.Jass/Renderer/SyntaxTriviaRenderer.cs new file mode 100644 index 00000000..bc4a4923 --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/Renderer/SyntaxTriviaRenderer.cs @@ -0,0 +1,57 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using War3Net.CodeAnalysis.Jass.Syntax; + +namespace War3Net.CodeAnalysis.Jass +{ + public partial class JassRenderer + { + public void Render(JassSyntaxTrivia trivia) + { + if (trivia.SyntaxKind == JassSyntaxKind.SingleLineCommentTrivia) + { + WriteSpace(); + Write(trivia.Text.TrimEnd()); + } + else if (trivia.SyntaxKind == JassSyntaxKind.NewLineTrivia) + { + var lines = 0; + var isCarriageReturn = false; + for (var i = 0; i < trivia.Text.Length; i++) + { + if (trivia.Text[i] == '\r') + { + if (isCarriageReturn) + { + lines++; + } + else + { + isCarriageReturn = true; + } + } + else + { + lines++; + isCarriageReturn = false; + } + } + + if (isCarriageReturn) + { + lines++; + } + + for (var i = 0; i < lines; i++) + { + WriteLine(); + } + } + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Renderer/DeclarationRenderer.cs b/src/War3Net.CodeAnalysis.Jass/Renderer/TopLevelDeclarationRenderer.cs similarity index 65% rename from src/War3Net.CodeAnalysis.Jass/Renderer/DeclarationRenderer.cs rename to src/War3Net.CodeAnalysis.Jass/Renderer/TopLevelDeclarationRenderer.cs index e7bd31e0..b1f65444 100644 --- a/src/War3Net.CodeAnalysis.Jass/Renderer/DeclarationRenderer.cs +++ b/src/War3Net.CodeAnalysis.Jass/Renderer/TopLevelDeclarationRenderer.cs @@ -1,5 +1,5 @@ // ------------------------------------------------------------------------------ -// +// // Licensed under the MIT license. // See the LICENSE file in the project root for more information. // @@ -13,15 +13,12 @@ namespace War3Net.CodeAnalysis.Jass { public partial class JassRenderer { - public void Render(ITopLevelDeclarationSyntax declaration) + public void Render(JassTopLevelDeclarationSyntax declaration) { switch (declaration) { - case JassEmptySyntax empty: Render(empty); break; - case JassCommentSyntax comment: Render(comment); break; case JassTypeDeclarationSyntax typeDeclaration: Render(typeDeclaration); break; - case JassGlobalDeclarationListSyntax globalDeclarationList: Render(globalDeclarationList); break; - case JassGlobalDeclarationSyntax globalDeclaration: Render(globalDeclaration); break; + case JassGlobalsDeclarationSyntax globalsDeclaration: Render(globalsDeclaration); break; case JassNativeFunctionDeclarationSyntax nativeFunctionDeclaration: Render(nativeFunctionDeclaration); break; case JassFunctionDeclarationSyntax functionDeclaration: Render(functionDeclaration); break; diff --git a/src/War3Net.CodeAnalysis.Jass/Renderer/TypeDeclarationRenderer.cs b/src/War3Net.CodeAnalysis.Jass/Renderer/TypeDeclarationRenderer.cs index 15efad05..b50856f3 100644 --- a/src/War3Net.CodeAnalysis.Jass/Renderer/TypeDeclarationRenderer.cs +++ b/src/War3Net.CodeAnalysis.Jass/Renderer/TypeDeclarationRenderer.cs @@ -13,9 +13,12 @@ public partial class JassRenderer { public void Render(JassTypeDeclarationSyntax typeDeclaration) { - Write($"{JassKeyword.Type} "); + Render(typeDeclaration.TypeToken); + WriteSpace(); Render(typeDeclaration.IdentifierName); - Write($" {JassKeyword.Extends} "); + WriteSpace(); + Render(typeDeclaration.ExtendsToken); + WriteSpace(); Render(typeDeclaration.BaseType); } } diff --git a/src/War3Net.CodeAnalysis.Jass/Renderer/TypeRenderer.cs b/src/War3Net.CodeAnalysis.Jass/Renderer/TypeRenderer.cs index 4dee306d..d889d72e 100644 --- a/src/War3Net.CodeAnalysis.Jass/Renderer/TypeRenderer.cs +++ b/src/War3Net.CodeAnalysis.Jass/Renderer/TypeRenderer.cs @@ -5,6 +5,8 @@ // // ------------------------------------------------------------------------------ +using System; + using War3Net.CodeAnalysis.Jass.Syntax; namespace War3Net.CodeAnalysis.Jass @@ -13,7 +15,13 @@ public partial class JassRenderer { public void Render(JassTypeSyntax type) { - Render(type.TypeName); + switch (type) + { + case JassIdentifierNameSyntax identifierName: Render(identifierName); break; + case JassPredefinedTypeSyntax predefinedType: Render(predefinedType); break; + + default: throw new NotSupportedException(); + } } } } \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Renderer/UnaryExpressionRenderer.cs b/src/War3Net.CodeAnalysis.Jass/Renderer/UnaryExpressionRenderer.cs index 7b478ce5..f683fd80 100644 --- a/src/War3Net.CodeAnalysis.Jass/Renderer/UnaryExpressionRenderer.cs +++ b/src/War3Net.CodeAnalysis.Jass/Renderer/UnaryExpressionRenderer.cs @@ -5,7 +5,6 @@ // // ------------------------------------------------------------------------------ -using War3Net.CodeAnalysis.Jass.Extensions; using War3Net.CodeAnalysis.Jass.Syntax; namespace War3Net.CodeAnalysis.Jass @@ -14,13 +13,10 @@ public partial class JassRenderer { public void Render(JassUnaryExpressionSyntax unaryExpression) { - if (unaryExpression.Operator == UnaryOperatorType.Not) + Render(unaryExpression.OperatorToken); + if (unaryExpression.SyntaxKind == JassSyntaxKind.LogicalNotExpression) { - Write($"{unaryExpression.Operator.GetSymbol()} "); - } - else - { - Write(unaryExpression.Operator.GetSymbol()); + WriteSpace(); } Render(unaryExpression.Expression); diff --git a/src/War3Net.CodeAnalysis.Jass/Renderer/VariableDeclaratorRenderer.cs b/src/War3Net.CodeAnalysis.Jass/Renderer/VariableDeclaratorRenderer.cs index 45723f0e..14cc8620 100644 --- a/src/War3Net.CodeAnalysis.Jass/Renderer/VariableDeclaratorRenderer.cs +++ b/src/War3Net.CodeAnalysis.Jass/Renderer/VariableDeclaratorRenderer.cs @@ -5,34 +5,21 @@ // // ------------------------------------------------------------------------------ -using System; - using War3Net.CodeAnalysis.Jass.Syntax; namespace War3Net.CodeAnalysis.Jass { public partial class JassRenderer { - public void Render(IVariableDeclaratorSyntax declarator) - { - switch (declarator) - { - case JassArrayDeclaratorSyntax arrayDeclarator: Render(arrayDeclarator); break; - case JassVariableDeclaratorSyntax variableDeclarator: Render(variableDeclarator); break; - - default: throw new NotSupportedException(); - } - } - public void Render(JassVariableDeclaratorSyntax variableDeclarator) { Render(variableDeclarator.Type); - Write(' '); + WriteSpace(); Render(variableDeclarator.IdentifierName); if (variableDeclarator.Value is not null) { - Write(' '); + WriteSpace(); Render(variableDeclarator.Value); } } diff --git a/src/War3Net.CodeAnalysis.Jass/Renderer/VariableOrArrayDeclaratorRenderer.cs b/src/War3Net.CodeAnalysis.Jass/Renderer/VariableOrArrayDeclaratorRenderer.cs new file mode 100644 index 00000000..0536c926 --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/Renderer/VariableOrArrayDeclaratorRenderer.cs @@ -0,0 +1,27 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using System; + +using War3Net.CodeAnalysis.Jass.Syntax; + +namespace War3Net.CodeAnalysis.Jass +{ + public partial class JassRenderer + { + public void Render(JassVariableOrArrayDeclaratorSyntax declarator) + { + switch (declarator) + { + case JassArrayDeclaratorSyntax arrayDeclarator: Render(arrayDeclarator); break; + case JassVariableDeclaratorSyntax variableDeclarator: Render(variableDeclarator); break; + + default: throw new NotSupportedException(); + } + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Syntax/BinaryOperatorType.cs b/src/War3Net.CodeAnalysis.Jass/Syntax/BinaryOperatorType.cs deleted file mode 100644 index d0bf2ffe..00000000 --- a/src/War3Net.CodeAnalysis.Jass/Syntax/BinaryOperatorType.cs +++ /dev/null @@ -1,25 +0,0 @@ -// ------------------------------------------------------------------------------ -// -// Licensed under the MIT license. -// See the LICENSE file in the project root for more information. -// -// ------------------------------------------------------------------------------ - -namespace War3Net.CodeAnalysis.Jass.Syntax -{ - public enum BinaryOperatorType - { - Add, - Subtract, - Multiplication, - Division, - GreaterThan, - LessThan, - Equals, - NotEquals, - GreaterOrEqual, - LessOrEqual, - And, - Or, - } -} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Syntax/IDeclarationLineSyntax.cs b/src/War3Net.CodeAnalysis.Jass/Syntax/IDeclarationLineSyntax.cs deleted file mode 100644 index ff4111ca..00000000 --- a/src/War3Net.CodeAnalysis.Jass/Syntax/IDeclarationLineSyntax.cs +++ /dev/null @@ -1,15 +0,0 @@ -// ------------------------------------------------------------------------------ -// -// Licensed under the MIT license. -// See the LICENSE file in the project root for more information. -// -// ------------------------------------------------------------------------------ - -using System; - -namespace War3Net.CodeAnalysis.Jass.Syntax -{ - public interface IDeclarationLineSyntax : IEquatable - { - } -} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Syntax/IExpressionSyntax.cs b/src/War3Net.CodeAnalysis.Jass/Syntax/IExpressionSyntax.cs deleted file mode 100644 index 4c015ece..00000000 --- a/src/War3Net.CodeAnalysis.Jass/Syntax/IExpressionSyntax.cs +++ /dev/null @@ -1,15 +0,0 @@ -// ------------------------------------------------------------------------------ -// -// Licensed under the MIT license. -// See the LICENSE file in the project root for more information. -// -// ------------------------------------------------------------------------------ - -using System; - -namespace War3Net.CodeAnalysis.Jass.Syntax -{ - public interface IExpressionSyntax : IEquatable - { - } -} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Syntax/IGlobalDeclarationSyntax.cs b/src/War3Net.CodeAnalysis.Jass/Syntax/IGlobalDeclarationSyntax.cs deleted file mode 100644 index 685ac3e3..00000000 --- a/src/War3Net.CodeAnalysis.Jass/Syntax/IGlobalDeclarationSyntax.cs +++ /dev/null @@ -1,15 +0,0 @@ -// ------------------------------------------------------------------------------ -// -// Licensed under the MIT license. -// See the LICENSE file in the project root for more information. -// -// ------------------------------------------------------------------------------ - -using System; - -namespace War3Net.CodeAnalysis.Jass.Syntax -{ - public interface IGlobalDeclarationSyntax : IEquatable - { - } -} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Syntax/IGlobalLineSyntax.cs b/src/War3Net.CodeAnalysis.Jass/Syntax/IGlobalLineSyntax.cs deleted file mode 100644 index 221f11c9..00000000 --- a/src/War3Net.CodeAnalysis.Jass/Syntax/IGlobalLineSyntax.cs +++ /dev/null @@ -1,15 +0,0 @@ -// ------------------------------------------------------------------------------ -// -// Licensed under the MIT license. -// See the LICENSE file in the project root for more information. -// -// ------------------------------------------------------------------------------ - -using System; - -namespace War3Net.CodeAnalysis.Jass.Syntax -{ - public interface IGlobalLineSyntax : IEquatable - { - } -} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Syntax/IInvocationSyntax.cs b/src/War3Net.CodeAnalysis.Jass/Syntax/IInvocationSyntax.cs deleted file mode 100644 index 39d087a4..00000000 --- a/src/War3Net.CodeAnalysis.Jass/Syntax/IInvocationSyntax.cs +++ /dev/null @@ -1,16 +0,0 @@ -// ------------------------------------------------------------------------------ -// -// Licensed under the MIT license. -// See the LICENSE file in the project root for more information. -// -// ------------------------------------------------------------------------------ - -namespace War3Net.CodeAnalysis.Jass.Syntax -{ - public interface IInvocationSyntax - { - public JassIdentifierNameSyntax IdentifierName { get; init; } - - public JassArgumentListSyntax Arguments { get; init; } - } -} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Syntax/IMemberDeclarationSyntax.cs b/src/War3Net.CodeAnalysis.Jass/Syntax/IMemberDeclarationSyntax.cs deleted file mode 100644 index 953e24a4..00000000 --- a/src/War3Net.CodeAnalysis.Jass/Syntax/IMemberDeclarationSyntax.cs +++ /dev/null @@ -1,15 +0,0 @@ -// ------------------------------------------------------------------------------ -// -// Licensed under the MIT license. -// See the LICENSE file in the project root for more information. -// -// ------------------------------------------------------------------------------ - -using System; - -namespace War3Net.CodeAnalysis.Jass.Syntax -{ - public interface IMemberDeclarationSyntax : IEquatable - { - } -} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Syntax/IScopedDeclarationSyntax.cs b/src/War3Net.CodeAnalysis.Jass/Syntax/IScopedDeclarationSyntax.cs deleted file mode 100644 index 5b3b67ac..00000000 --- a/src/War3Net.CodeAnalysis.Jass/Syntax/IScopedDeclarationSyntax.cs +++ /dev/null @@ -1,15 +0,0 @@ -// ------------------------------------------------------------------------------ -// -// Licensed under the MIT license. -// See the LICENSE file in the project root for more information. -// -// ------------------------------------------------------------------------------ - -using System; - -namespace War3Net.CodeAnalysis.Jass.Syntax -{ - public interface IScopedDeclarationSyntax : IEquatable - { - } -} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Syntax/IScopedGlobalDeclarationSyntax.cs b/src/War3Net.CodeAnalysis.Jass/Syntax/IScopedGlobalDeclarationSyntax.cs deleted file mode 100644 index 6ac2795f..00000000 --- a/src/War3Net.CodeAnalysis.Jass/Syntax/IScopedGlobalDeclarationSyntax.cs +++ /dev/null @@ -1,15 +0,0 @@ -// ------------------------------------------------------------------------------ -// -// Licensed under the MIT license. -// See the LICENSE file in the project root for more information. -// -// ------------------------------------------------------------------------------ - -using System; - -namespace War3Net.CodeAnalysis.Jass.Syntax -{ - public interface IScopedGlobalDeclarationSyntax : IEquatable - { - } -} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Syntax/IStatementLineSyntax.cs b/src/War3Net.CodeAnalysis.Jass/Syntax/IStatementLineSyntax.cs deleted file mode 100644 index 6e5a531f..00000000 --- a/src/War3Net.CodeAnalysis.Jass/Syntax/IStatementLineSyntax.cs +++ /dev/null @@ -1,15 +0,0 @@ -// ------------------------------------------------------------------------------ -// -// Licensed under the MIT license. -// See the LICENSE file in the project root for more information. -// -// ------------------------------------------------------------------------------ - -using System; - -namespace War3Net.CodeAnalysis.Jass.Syntax -{ - public interface IStatementLineSyntax : IEquatable - { - } -} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Syntax/IStatementSyntax.cs b/src/War3Net.CodeAnalysis.Jass/Syntax/IStatementSyntax.cs deleted file mode 100644 index b618f868..00000000 --- a/src/War3Net.CodeAnalysis.Jass/Syntax/IStatementSyntax.cs +++ /dev/null @@ -1,15 +0,0 @@ -// ------------------------------------------------------------------------------ -// -// Licensed under the MIT license. -// See the LICENSE file in the project root for more information. -// -// ------------------------------------------------------------------------------ - -using System; - -namespace War3Net.CodeAnalysis.Jass.Syntax -{ - public interface IStatementSyntax : IEquatable - { - } -} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Syntax/ITopLevelDeclarationSyntax.cs b/src/War3Net.CodeAnalysis.Jass/Syntax/ITopLevelDeclarationSyntax.cs deleted file mode 100644 index 70abd3ed..00000000 --- a/src/War3Net.CodeAnalysis.Jass/Syntax/ITopLevelDeclarationSyntax.cs +++ /dev/null @@ -1,15 +0,0 @@ -// ------------------------------------------------------------------------------ -// -// Licensed under the MIT license. -// See the LICENSE file in the project root for more information. -// -// ------------------------------------------------------------------------------ - -using System; - -namespace War3Net.CodeAnalysis.Jass.Syntax -{ - public interface ITopLevelDeclarationSyntax : IEquatable - { - } -} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Syntax/IVariableDeclaratorSyntax.cs b/src/War3Net.CodeAnalysis.Jass/Syntax/IVariableDeclaratorSyntax.cs deleted file mode 100644 index 455f261d..00000000 --- a/src/War3Net.CodeAnalysis.Jass/Syntax/IVariableDeclaratorSyntax.cs +++ /dev/null @@ -1,18 +0,0 @@ -// ------------------------------------------------------------------------------ -// -// Licensed under the MIT license. -// See the LICENSE file in the project root for more information. -// -// ------------------------------------------------------------------------------ - -using System; - -namespace War3Net.CodeAnalysis.Jass.Syntax -{ - public interface IVariableDeclaratorSyntax : IEquatable - { - JassTypeSyntax Type { get; init; } - - JassIdentifierNameSyntax IdentifierName { get; init; } - } -} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Syntax/JassArgumentListSyntax.cs b/src/War3Net.CodeAnalysis.Jass/Syntax/JassArgumentListSyntax.cs index 7f5d431a..2972903f 100644 --- a/src/War3Net.CodeAnalysis.Jass/Syntax/JassArgumentListSyntax.cs +++ b/src/War3Net.CodeAnalysis.Jass/Syntax/JassArgumentListSyntax.cs @@ -5,27 +5,126 @@ // // ------------------------------------------------------------------------------ -using System; -using System.Collections.Immutable; -using System.Linq; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.IO; + +using War3Net.CodeAnalysis.Jass.Extensions; namespace War3Net.CodeAnalysis.Jass.Syntax { - public class JassArgumentListSyntax : IEquatable + public class JassArgumentListSyntax : JassSyntaxNode { - public JassArgumentListSyntax(ImmutableArray arguments) + public static readonly JassArgumentListSyntax Empty = new( + new JassSyntaxToken(JassSyntaxKind.OpenParenToken, JassSymbol.OpenParen, JassSyntaxTriviaList.Empty), + SeparatedSyntaxList.Empty, + new JassSyntaxToken(JassSyntaxKind.CloseParenToken, JassSymbol.CloseParen, JassSyntaxTriviaList.Empty)); + + internal JassArgumentListSyntax( + JassSyntaxToken openParenToken, + SeparatedSyntaxList argumentList, + JassSyntaxToken closeParenToken) + { + OpenParenToken = openParenToken; + ArgumentList = argumentList; + CloseParenToken = closeParenToken; + } + + public JassSyntaxToken OpenParenToken { get; } + + public SeparatedSyntaxList ArgumentList { get; } + + public JassSyntaxToken CloseParenToken { get; } + + public override JassSyntaxKind SyntaxKind => JassSyntaxKind.ArgumentList; + + public override bool IsEquivalentTo([NotNullWhen(true)] JassSyntaxNode? other) + { + return other is JassArgumentListSyntax argumentList + && ArgumentList.IsEquivalentTo(argumentList.ArgumentList); + } + + public override void WriteTo(TextWriter writer) + { + OpenParenToken.WriteTo(writer); + ArgumentList.WriteTo(writer); + CloseParenToken.WriteTo(writer); + } + + public override IEnumerable GetChildNodes() + { + return ArgumentList.Items; + } + + public override IEnumerable GetChildTokens() + { + yield return OpenParenToken; + foreach (var child in ArgumentList.Separators) + { + yield return child; + } + + yield return CloseParenToken; + } + + public override IEnumerable GetChildNodesAndTokens() { - Arguments = arguments; + yield return OpenParenToken; + foreach (var child in ArgumentList.GetChildNodesAndTokens()) + { + yield return child; + } + + yield return CloseParenToken; } - public ImmutableArray Arguments { get; init; } + public override IEnumerable GetDescendantNodes() + { + return ArgumentList.GetDescendantNodes(); + } - public bool Equals(JassArgumentListSyntax? other) + public override IEnumerable GetDescendantTokens() { - return other is not null - && Arguments.SequenceEqual(other.Arguments); + yield return OpenParenToken; + foreach (var descendant in ArgumentList.GetDescendantTokens()) + { + yield return descendant; + } + + yield return CloseParenToken; } - public override string ToString() => string.Join($"{JassSymbol.Comma} ", Arguments); + public override IEnumerable GetDescendantNodesAndTokens() + { + yield return OpenParenToken; + foreach (var descendant in ArgumentList.GetDescendantNodesAndTokens()) + { + yield return descendant; + } + + yield return CloseParenToken; + } + + public override string ToString() => $"{OpenParenToken}{ArgumentList}{CloseParenToken}"; + + public override JassSyntaxToken GetFirstToken() => OpenParenToken; + + public override JassSyntaxToken GetLastToken() => CloseParenToken; + + protected internal override JassArgumentListSyntax ReplaceFirstToken(JassSyntaxToken newToken) + { + return new JassArgumentListSyntax( + newToken, + ArgumentList, + CloseParenToken); + } + + protected internal override JassArgumentListSyntax ReplaceLastToken(JassSyntaxToken newToken) + { + return new JassArgumentListSyntax( + OpenParenToken, + ArgumentList, + newToken); + } } } \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Syntax/JassArrayDeclaratorSyntax.cs b/src/War3Net.CodeAnalysis.Jass/Syntax/JassArrayDeclaratorSyntax.cs index ed4d9c6e..ce8cc2a7 100644 --- a/src/War3Net.CodeAnalysis.Jass/Syntax/JassArrayDeclaratorSyntax.cs +++ b/src/War3Net.CodeAnalysis.Jass/Syntax/JassArrayDeclaratorSyntax.cs @@ -5,27 +5,131 @@ // // ------------------------------------------------------------------------------ +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.IO; + namespace War3Net.CodeAnalysis.Jass.Syntax { - public class JassArrayDeclaratorSyntax : IVariableDeclaratorSyntax + public class JassArrayDeclaratorSyntax : JassVariableOrArrayDeclaratorSyntax { - public JassArrayDeclaratorSyntax(JassTypeSyntax type, JassIdentifierNameSyntax identifierName) + internal JassArrayDeclaratorSyntax( + JassTypeSyntax type, + JassSyntaxToken arrayToken, + JassIdentifierNameSyntax identifierName) { Type = type; + ArrayToken = arrayToken; IdentifierName = identifierName; } - public JassTypeSyntax Type { get; init; } + public JassTypeSyntax Type { get; } + + public JassSyntaxToken ArrayToken { get; } + + public JassIdentifierNameSyntax IdentifierName { get; } - public JassIdentifierNameSyntax IdentifierName { get; init; } + public override JassSyntaxKind SyntaxKind => JassSyntaxKind.ArrayDeclarator; - public bool Equals(IVariableDeclaratorSyntax? other) + public override bool IsEquivalentTo([NotNullWhen(true)] JassSyntaxNode? other) { return other is JassArrayDeclaratorSyntax arrayDeclarator - && Type.Equals(arrayDeclarator.Type) - && IdentifierName.Equals(arrayDeclarator.IdentifierName); + && Type.IsEquivalentTo(arrayDeclarator.Type) + && IdentifierName.IsEquivalentTo(arrayDeclarator.IdentifierName); + } + + public override void WriteTo(TextWriter writer) + { + Type.WriteTo(writer); + ArrayToken.WriteTo(writer); + IdentifierName.WriteTo(writer); + } + + public override IEnumerable GetChildNodes() + { + yield return Type; + yield return IdentifierName; + } + + public override IEnumerable GetChildTokens() + { + yield return ArrayToken; + } + + public override IEnumerable GetChildNodesAndTokens() + { + yield return Type; + yield return ArrayToken; + yield return IdentifierName; + } + + public override IEnumerable GetDescendantNodes() + { + yield return Type; + foreach (var descendant in Type.GetDescendantNodes()) + { + yield return descendant; + } + + yield return IdentifierName; + foreach (var descendant in IdentifierName.GetDescendantNodes()) + { + yield return descendant; + } } - public override string ToString() => $"{Type} {JassKeyword.Array} {IdentifierName}"; + public override IEnumerable GetDescendantTokens() + { + foreach (var descendant in Type.GetDescendantTokens()) + { + yield return descendant; + } + + yield return ArrayToken; + + foreach (var descendant in IdentifierName.GetDescendantTokens()) + { + yield return descendant; + } + } + + public override IEnumerable GetDescendantNodesAndTokens() + { + yield return Type; + foreach (var descendant in Type.GetDescendantNodesAndTokens()) + { + yield return descendant; + } + + yield return ArrayToken; + + yield return IdentifierName; + foreach (var descendant in IdentifierName.GetDescendantNodesAndTokens()) + { + yield return descendant; + } + } + + public override string ToString() => $"{Type} {ArrayToken} {IdentifierName}"; + + public override JassSyntaxToken GetFirstToken() => Type.GetFirstToken(); + + public override JassSyntaxToken GetLastToken() => IdentifierName.GetLastToken(); + + protected internal override JassArrayDeclaratorSyntax ReplaceFirstToken(JassSyntaxToken newToken) + { + return new JassArrayDeclaratorSyntax( + Type.ReplaceFirstToken(newToken), + ArrayToken, + IdentifierName); + } + + protected internal override JassArrayDeclaratorSyntax ReplaceLastToken(JassSyntaxToken newToken) + { + return new JassArrayDeclaratorSyntax( + Type, + ArrayToken, + IdentifierName.ReplaceLastToken(newToken)); + } } } \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Syntax/JassArrayReferenceExpressionSyntax.cs b/src/War3Net.CodeAnalysis.Jass/Syntax/JassArrayReferenceExpressionSyntax.cs deleted file mode 100644 index da61f525..00000000 --- a/src/War3Net.CodeAnalysis.Jass/Syntax/JassArrayReferenceExpressionSyntax.cs +++ /dev/null @@ -1,31 +0,0 @@ -// ------------------------------------------------------------------------------ -// -// Licensed under the MIT license. -// See the LICENSE file in the project root for more information. -// -// ------------------------------------------------------------------------------ - -namespace War3Net.CodeAnalysis.Jass.Syntax -{ - public class JassArrayReferenceExpressionSyntax : IExpressionSyntax - { - public JassArrayReferenceExpressionSyntax(JassIdentifierNameSyntax identifierName, IExpressionSyntax indexer) - { - IdentifierName = identifierName; - Indexer = indexer; - } - - public JassIdentifierNameSyntax IdentifierName { get; init; } - - public IExpressionSyntax Indexer { get; init; } - - public bool Equals(IExpressionSyntax? other) - { - return other is JassArrayReferenceExpressionSyntax arrayReferenceExpression - && IdentifierName.Equals(arrayReferenceExpression.IdentifierName) - && Indexer.Equals(arrayReferenceExpression.Indexer); - } - - public override string ToString() => $"{IdentifierName}{JassSymbol.LeftSquareBracket}{Indexer}{JassSymbol.RightSquareBracket}"; - } -} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Syntax/JassBinaryExpressionSyntax.cs b/src/War3Net.CodeAnalysis.Jass/Syntax/JassBinaryExpressionSyntax.cs index 3bc5517f..fe0df0f5 100644 --- a/src/War3Net.CodeAnalysis.Jass/Syntax/JassBinaryExpressionSyntax.cs +++ b/src/War3Net.CodeAnalysis.Jass/Syntax/JassBinaryExpressionSyntax.cs @@ -5,33 +5,132 @@ // // ------------------------------------------------------------------------------ -using War3Net.CodeAnalysis.Jass.Extensions; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.IO; namespace War3Net.CodeAnalysis.Jass.Syntax { - public class JassBinaryExpressionSyntax : IExpressionSyntax + public class JassBinaryExpressionSyntax : JassExpressionSyntax { - public JassBinaryExpressionSyntax(BinaryOperatorType @operator, IExpressionSyntax left, IExpressionSyntax right) + internal JassBinaryExpressionSyntax( + JassExpressionSyntax left, + JassSyntaxToken operatorToken, + JassExpressionSyntax right) { - Operator = @operator; Left = left; + OperatorToken = operatorToken; Right = right; } - public BinaryOperatorType Operator { get; init; } + public JassExpressionSyntax Left { get; } - public IExpressionSyntax Left { get; init; } + public JassSyntaxToken OperatorToken { get; } - public IExpressionSyntax Right { get; init; } + public JassExpressionSyntax Right { get; } - public bool Equals(IExpressionSyntax? other) + public override JassSyntaxKind SyntaxKind => JassSyntaxFacts.GetBinaryExpressionKind(OperatorToken.SyntaxKind); + + public override bool IsEquivalentTo([NotNullWhen(true)] JassSyntaxNode? other) { return other is JassBinaryExpressionSyntax binaryExpression - && Operator == binaryExpression.Operator - && Left.Equals(binaryExpression.Left) - && Right.Equals(binaryExpression.Right); + && Left.IsEquivalentTo(binaryExpression.Left) + && OperatorToken.IsEquivalentTo(binaryExpression.OperatorToken) + && Right.IsEquivalentTo(binaryExpression.Right); + } + + public override void WriteTo(TextWriter writer) + { + Left.WriteTo(writer); + OperatorToken.WriteTo(writer); + Right.WriteTo(writer); + } + + public override IEnumerable GetChildNodes() + { + yield return Left; + yield return Right; + } + + public override IEnumerable GetChildTokens() + { + yield return OperatorToken; } - public override string ToString() => $"{Left} {Operator.GetSymbol()} {Right}"; + public override IEnumerable GetChildNodesAndTokens() + { + yield return Left; + yield return OperatorToken; + yield return Right; + } + + public override IEnumerable GetDescendantNodes() + { + yield return Left; + foreach (var descendant in Left.GetDescendantNodes()) + { + yield return descendant; + } + + yield return Right; + foreach (var descendant in Right.GetDescendantNodes()) + { + yield return descendant; + } + } + + public override IEnumerable GetDescendantTokens() + { + foreach (var descendant in Left.GetDescendantTokens()) + { + yield return descendant; + } + + yield return OperatorToken; + + foreach (var descendant in Right.GetDescendantTokens()) + { + yield return descendant; + } + } + + public override IEnumerable GetDescendantNodesAndTokens() + { + yield return Left; + foreach (var descendant in Left.GetDescendantNodesAndTokens()) + { + yield return descendant; + } + + yield return OperatorToken; + + yield return Right; + foreach (var descendant in Right.GetDescendantNodesAndTokens()) + { + yield return descendant; + } + } + + public override string ToString() => $"{Left} {OperatorToken} {Right}"; + + public override JassSyntaxToken GetFirstToken() => Left.GetFirstToken(); + + public override JassSyntaxToken GetLastToken() => Right.GetLastToken(); + + protected internal override JassBinaryExpressionSyntax ReplaceFirstToken(JassSyntaxToken newToken) + { + return new JassBinaryExpressionSyntax( + Left.ReplaceFirstToken(newToken), + OperatorToken, + Right); + } + + protected internal override JassBinaryExpressionSyntax ReplaceLastToken(JassSyntaxToken newToken) + { + return new JassBinaryExpressionSyntax( + Left, + OperatorToken, + Right.ReplaceLastToken(newToken)); + } } } \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Syntax/JassBooleanLiteralExpressionSyntax.cs b/src/War3Net.CodeAnalysis.Jass/Syntax/JassBooleanLiteralExpressionSyntax.cs deleted file mode 100644 index 90569d5d..00000000 --- a/src/War3Net.CodeAnalysis.Jass/Syntax/JassBooleanLiteralExpressionSyntax.cs +++ /dev/null @@ -1,30 +0,0 @@ -// ------------------------------------------------------------------------------ -// -// Licensed under the MIT license. -// See the LICENSE file in the project root for more information. -// -// ------------------------------------------------------------------------------ - -namespace War3Net.CodeAnalysis.Jass.Syntax -{ - public class JassBooleanLiteralExpressionSyntax : IExpressionSyntax - { - public static readonly JassBooleanLiteralExpressionSyntax True = new JassBooleanLiteralExpressionSyntax(true); - public static readonly JassBooleanLiteralExpressionSyntax False = new JassBooleanLiteralExpressionSyntax(false); - - private JassBooleanLiteralExpressionSyntax(bool value) - { - Value = value; - } - - public bool Value { get; init; } - - public bool Equals(IExpressionSyntax? other) - { - return other is JassBooleanLiteralExpressionSyntax booleanLiteralExpression - && Value == booleanLiteralExpression.Value; - } - - public override string ToString() => Value ? JassKeyword.True : JassKeyword.False; - } -} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Syntax/JassCallStatementSyntax.cs b/src/War3Net.CodeAnalysis.Jass/Syntax/JassCallStatementSyntax.cs index 8d42270f..990b905d 100644 --- a/src/War3Net.CodeAnalysis.Jass/Syntax/JassCallStatementSyntax.cs +++ b/src/War3Net.CodeAnalysis.Jass/Syntax/JassCallStatementSyntax.cs @@ -5,34 +5,131 @@ // // ------------------------------------------------------------------------------ +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.IO; + namespace War3Net.CodeAnalysis.Jass.Syntax { - public class JassCallStatementSyntax : IStatementSyntax, IStatementLineSyntax, IInvocationSyntax + public class JassCallStatementSyntax : JassStatementSyntax { - public JassCallStatementSyntax(JassIdentifierNameSyntax identifierName, JassArgumentListSyntax arguments) + internal JassCallStatementSyntax( + JassSyntaxToken callToken, + JassIdentifierNameSyntax identifierName, + JassArgumentListSyntax argumentList) { + CallToken = callToken; IdentifierName = identifierName; - Arguments = arguments; + ArgumentList = argumentList; } - public JassIdentifierNameSyntax IdentifierName { get; init; } + public JassSyntaxToken CallToken { get; } + + public JassIdentifierNameSyntax IdentifierName { get; } + + public JassArgumentListSyntax ArgumentList { get; } - public JassArgumentListSyntax Arguments { get; init; } + public override JassSyntaxKind SyntaxKind => JassSyntaxKind.CallStatement; - public bool Equals(IStatementSyntax? other) + public override bool IsEquivalentTo([NotNullWhen(true)] JassSyntaxNode? other) { return other is JassCallStatementSyntax callStatement - && IdentifierName.Equals(callStatement.IdentifierName) - && Arguments.Equals(callStatement.Arguments); + && IdentifierName.IsEquivalentTo(callStatement.IdentifierName) + && ArgumentList.IsEquivalentTo(callStatement.ArgumentList); } - public bool Equals(IStatementLineSyntax? other) + public override void WriteTo(TextWriter writer) { - return other is JassCallStatementSyntax callStatement - && IdentifierName.Equals(callStatement.IdentifierName) - && Arguments.Equals(callStatement.Arguments); + CallToken.WriteTo(writer); + IdentifierName.WriteTo(writer); + ArgumentList.WriteTo(writer); + } + + public override IEnumerable GetChildNodes() + { + yield return IdentifierName; + yield return ArgumentList; + } + + public override IEnumerable GetChildTokens() + { + yield return CallToken; } - public override string ToString() => $"{JassKeyword.Call} {IdentifierName}{JassSymbol.LeftParenthesis}{Arguments}{JassSymbol.RightParenthesis}"; + public override IEnumerable GetChildNodesAndTokens() + { + yield return CallToken; + yield return IdentifierName; + yield return ArgumentList; + } + + public override IEnumerable GetDescendantNodes() + { + yield return IdentifierName; + foreach (var descendant in IdentifierName.GetDescendantNodes()) + { + yield return descendant; + } + + yield return ArgumentList; + foreach (var descendant in ArgumentList.GetDescendantNodes()) + { + yield return descendant; + } + } + + public override IEnumerable GetDescendantTokens() + { + yield return CallToken; + + foreach (var descendant in IdentifierName.GetDescendantTokens()) + { + yield return descendant; + } + + foreach (var descendant in ArgumentList.GetDescendantTokens()) + { + yield return descendant; + } + } + + public override IEnumerable GetDescendantNodesAndTokens() + { + yield return CallToken; + + yield return IdentifierName; + foreach (var descendant in IdentifierName.GetDescendantNodesAndTokens()) + { + yield return descendant; + } + + yield return ArgumentList; + foreach (var descendant in ArgumentList.GetDescendantNodesAndTokens()) + { + yield return descendant; + } + } + + public override string ToString() => $"{CallToken} {IdentifierName}{ArgumentList}"; + + public override JassSyntaxToken GetFirstToken() => CallToken; + + public override JassSyntaxToken GetLastToken() => ArgumentList.GetLastToken(); + + protected internal override JassCallStatementSyntax ReplaceFirstToken(JassSyntaxToken newToken) + { + return new JassCallStatementSyntax( + newToken, + IdentifierName, + ArgumentList); + } + + protected internal override JassCallStatementSyntax ReplaceLastToken(JassSyntaxToken newToken) + { + return new JassCallStatementSyntax( + CallToken, + IdentifierName, + ArgumentList.ReplaceLastToken(newToken)); + } } } \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Syntax/JassCharacterLiteralExpressionSyntax.cs b/src/War3Net.CodeAnalysis.Jass/Syntax/JassCharacterLiteralExpressionSyntax.cs deleted file mode 100644 index 52d5461c..00000000 --- a/src/War3Net.CodeAnalysis.Jass/Syntax/JassCharacterLiteralExpressionSyntax.cs +++ /dev/null @@ -1,27 +0,0 @@ -// ------------------------------------------------------------------------------ -// -// Licensed under the MIT license. -// See the LICENSE file in the project root for more information. -// -// ------------------------------------------------------------------------------ - -namespace War3Net.CodeAnalysis.Jass.Syntax -{ - public class JassCharacterLiteralExpressionSyntax : IExpressionSyntax - { - public JassCharacterLiteralExpressionSyntax(char value) - { - Value = value; - } - - public char Value { get; init; } - - public bool Equals(IExpressionSyntax? other) - { - return other is JassCharacterLiteralExpressionSyntax characterLiteralExpression - && Value == characterLiteralExpression.Value; - } - - public override string ToString() => $"{JassSymbol.Apostrophe}{Value}{JassSymbol.Apostrophe}"; - } -} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Syntax/JassCommentSyntax.cs b/src/War3Net.CodeAnalysis.Jass/Syntax/JassCommentSyntax.cs deleted file mode 100644 index 9a048603..00000000 --- a/src/War3Net.CodeAnalysis.Jass/Syntax/JassCommentSyntax.cs +++ /dev/null @@ -1,77 +0,0 @@ -// ------------------------------------------------------------------------------ -// -// Licensed under the MIT license. -// See the LICENSE file in the project root for more information. -// -// ------------------------------------------------------------------------------ - -using System; - -namespace War3Net.CodeAnalysis.Jass.Syntax -{ - public class JassCommentSyntax : ITopLevelDeclarationSyntax, IScopedDeclarationSyntax, IGlobalDeclarationSyntax, IScopedGlobalDeclarationSyntax, IMemberDeclarationSyntax, IStatementSyntax, IDeclarationLineSyntax, IGlobalLineSyntax, IStatementLineSyntax - { - public JassCommentSyntax(string comment) - { - Comment = comment; - } - - public string Comment { get; init; } - - public bool Equals(ITopLevelDeclarationSyntax? other) - { - return other is JassCommentSyntax comment - && string.Equals(Comment, comment.Comment, StringComparison.Ordinal); - } - - public bool Equals(IScopedDeclarationSyntax? other) - { - return other is JassCommentSyntax comment - && string.Equals(Comment, comment.Comment, StringComparison.Ordinal); - } - - public bool Equals(IGlobalDeclarationSyntax? other) - { - return other is JassCommentSyntax comment - && string.Equals(Comment, comment.Comment, StringComparison.Ordinal); - } - - public bool Equals(IScopedGlobalDeclarationSyntax? other) - { - return other is JassCommentSyntax comment - && string.Equals(Comment, comment.Comment, StringComparison.Ordinal); - } - - public bool Equals(IMemberDeclarationSyntax? other) - { - return other is JassCommentSyntax comment - && string.Equals(Comment, comment.Comment, StringComparison.Ordinal); - } - - public bool Equals(IStatementSyntax? other) - { - return other is JassCommentSyntax comment - && string.Equals(Comment, comment.Comment, StringComparison.Ordinal); - } - - public bool Equals(IDeclarationLineSyntax? other) - { - return other is JassCommentSyntax comment - && string.Equals(Comment, comment.Comment, StringComparison.Ordinal); - } - - public bool Equals(IGlobalLineSyntax? other) - { - return other is JassCommentSyntax comment - && string.Equals(Comment, comment.Comment, StringComparison.Ordinal); - } - - public bool Equals(IStatementLineSyntax? other) - { - return other is JassCommentSyntax comment - && string.Equals(Comment, comment.Comment, StringComparison.Ordinal); - } - - public override string ToString() => $"{JassSymbol.Slash}{JassSymbol.Slash}{Comment}"; - } -} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Syntax/JassCompilationUnitSyntax.cs b/src/War3Net.CodeAnalysis.Jass/Syntax/JassCompilationUnitSyntax.cs index 0c1e458b..b4b43c3d 100644 --- a/src/War3Net.CodeAnalysis.Jass/Syntax/JassCompilationUnitSyntax.cs +++ b/src/War3Net.CodeAnalysis.Jass/Syntax/JassCompilationUnitSyntax.cs @@ -5,27 +5,111 @@ // // ------------------------------------------------------------------------------ -using System; +using System.Collections.Generic; using System.Collections.Immutable; -using System.Linq; +using System.Diagnostics.CodeAnalysis; +using System.IO; + +using War3Net.CodeAnalysis.Jass.Extensions; namespace War3Net.CodeAnalysis.Jass.Syntax { - public class JassCompilationUnitSyntax : IEquatable + public class JassCompilationUnitSyntax : JassSyntaxNode { - public JassCompilationUnitSyntax(ImmutableArray declarations) + internal JassCompilationUnitSyntax( + ImmutableArray declarations, + JassSyntaxToken endOfFileToken) { Declarations = declarations; + EndOfFileToken = endOfFileToken; + } + + public ImmutableArray Declarations { get; } + + public JassSyntaxToken EndOfFileToken { get; } + + public override JassSyntaxKind SyntaxKind => JassSyntaxKind.CompilationUnit; + + public override bool IsEquivalentTo([NotNullWhen(true)] JassSyntaxNode? other) + { + return other is JassCompilationUnitSyntax compilationUnit + && Declarations.IsEquivalentTo(compilationUnit.Declarations); + } + + public override void WriteTo(TextWriter writer) + { + Declarations.WriteTo(writer); + EndOfFileToken.WriteTo(writer); } - public ImmutableArray Declarations { get; init; } + public override IEnumerable GetChildNodes() + { + return Declarations; + } - public bool Equals(JassCompilationUnitSyntax? other) + public override IEnumerable GetChildTokens() { - return other is not null - && Declarations.SequenceEqual(other.Declarations); + yield return EndOfFileToken; } - public override string ToString() => $"<{base.ToString()}> [{Declarations.Length}]"; + public override IEnumerable GetChildNodesAndTokens() + { + foreach (var child in Declarations) + { + yield return child; + } + + yield return EndOfFileToken; + } + + public override IEnumerable GetDescendantNodes() + { + return Declarations.GetDescendantNodes(); + } + + public override IEnumerable GetDescendantTokens() + { + foreach (var descendant in Declarations.GetDescendantTokens()) + { + yield return descendant; + } + + yield return EndOfFileToken; + } + + public override IEnumerable GetDescendantNodesAndTokens() + { + foreach (var descendant in Declarations.GetDescendantNodesAndTokens()) + { + yield return descendant; + } + + yield return EndOfFileToken; + } + + public override JassSyntaxToken GetFirstToken() => Declarations.IsEmpty ? EndOfFileToken : Declarations[0].GetFirstToken(); + + public override JassSyntaxToken GetLastToken() => EndOfFileToken; + + protected internal override JassCompilationUnitSyntax ReplaceFirstToken(JassSyntaxToken newToken) + { + if (!Declarations.IsEmpty) + { + return new JassCompilationUnitSyntax( + Declarations.ReplaceFirstItem(Declarations[0].ReplaceFirstToken(newToken)), + EndOfFileToken); + } + + return new JassCompilationUnitSyntax( + Declarations, + newToken); + } + + protected internal override JassCompilationUnitSyntax ReplaceLastToken(JassSyntaxToken newToken) + { + return new JassCompilationUnitSyntax( + Declarations, + newToken); + } } } \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Syntax/JassDebugCustomScriptAction.cs b/src/War3Net.CodeAnalysis.Jass/Syntax/JassDebugCustomScriptAction.cs deleted file mode 100644 index 1d46f2ea..00000000 --- a/src/War3Net.CodeAnalysis.Jass/Syntax/JassDebugCustomScriptAction.cs +++ /dev/null @@ -1,29 +0,0 @@ -// ------------------------------------------------------------------------------ -// -// Licensed under the MIT license. -// See the LICENSE file in the project root for more information. -// -// ------------------------------------------------------------------------------ - -namespace War3Net.CodeAnalysis.Jass.Syntax -{ - public class JassDebugCustomScriptAction : IStatementLineSyntax - { - public static readonly JassDebugCustomScriptAction DebugLoop = new JassDebugCustomScriptAction(JassLoopCustomScriptAction.Value); - - public JassDebugCustomScriptAction(IStatementLineSyntax action) - { - Action = action; - } - - public IStatementLineSyntax Action { get; init; } - - public bool Equals(IStatementLineSyntax? other) - { - return other is JassDebugCustomScriptAction debug - && Action.Equals(debug.Action); - } - - public override string ToString() => $"{JassKeyword.Debug} {Action}"; - } -} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Syntax/JassDebugStatementSyntax.cs b/src/War3Net.CodeAnalysis.Jass/Syntax/JassDebugStatementSyntax.cs index a403a987..db60586f 100644 --- a/src/War3Net.CodeAnalysis.Jass/Syntax/JassDebugStatementSyntax.cs +++ b/src/War3Net.CodeAnalysis.Jass/Syntax/JassDebugStatementSyntax.cs @@ -5,23 +5,104 @@ // // ------------------------------------------------------------------------------ +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.IO; + namespace War3Net.CodeAnalysis.Jass.Syntax { - public class JassDebugStatementSyntax : IStatementSyntax + public class JassDebugStatementSyntax : JassStatementSyntax { - public JassDebugStatementSyntax(IStatementSyntax statement) + internal JassDebugStatementSyntax( + JassSyntaxToken debugToken, + JassStatementSyntax statement) { + DebugToken = debugToken; Statement = statement; } - public IStatementSyntax Statement { get; init; } + public JassSyntaxToken DebugToken { get; } + + public JassStatementSyntax Statement { get; } + + public override JassSyntaxKind SyntaxKind => JassSyntaxFacts.GetDebugStatementKind(Statement.SyntaxKind); - public bool Equals(IStatementSyntax? other) + public override bool IsEquivalentTo([NotNullWhen(true)] JassSyntaxNode? other) { return other is JassDebugStatementSyntax debugStatement - && Statement.Equals(debugStatement.Statement); + && Statement.IsEquivalentTo(debugStatement.Statement); + } + + public override void WriteTo(TextWriter writer) + { + DebugToken.WriteTo(writer); + Statement.WriteTo(writer); + } + + public override IEnumerable GetChildNodes() + { + yield return Statement; + } + + public override IEnumerable GetChildTokens() + { + yield return DebugToken; + } + + public override IEnumerable GetChildNodesAndTokens() + { + yield return DebugToken; + yield return Statement; + } + + public override IEnumerable GetDescendantNodes() + { + yield return Statement; + foreach (var descendant in Statement.GetDescendantNodes()) + { + yield return descendant; + } + } + + public override IEnumerable GetDescendantTokens() + { + yield return DebugToken; + + foreach (var descendant in Statement.GetDescendantTokens()) + { + yield return descendant; + } + } + + public override IEnumerable GetDescendantNodesAndTokens() + { + yield return DebugToken; + + yield return Statement; + foreach (var descendant in Statement.GetDescendantNodesAndTokens()) + { + yield return descendant; + } } - public override string ToString() => $"{JassKeyword.Debug} {Statement}"; + public override string ToString() => $"{DebugToken} {Statement}"; + + public override JassSyntaxToken GetFirstToken() => DebugToken; + + public override JassSyntaxToken GetLastToken() => Statement.GetLastToken(); + + protected internal override JassDebugStatementSyntax ReplaceFirstToken(JassSyntaxToken newToken) + { + return new JassDebugStatementSyntax( + newToken, + Statement); + } + + protected internal override JassDebugStatementSyntax ReplaceLastToken(JassSyntaxToken newToken) + { + return new JassDebugStatementSyntax( + DebugToken, + Statement.ReplaceLastToken(newToken)); + } } } \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Syntax/JassDecimalLiteralExpressionSyntax.cs b/src/War3Net.CodeAnalysis.Jass/Syntax/JassDecimalLiteralExpressionSyntax.cs deleted file mode 100644 index 2f5a6b36..00000000 --- a/src/War3Net.CodeAnalysis.Jass/Syntax/JassDecimalLiteralExpressionSyntax.cs +++ /dev/null @@ -1,29 +0,0 @@ -// ------------------------------------------------------------------------------ -// -// Licensed under the MIT license. -// See the LICENSE file in the project root for more information. -// -// ------------------------------------------------------------------------------ - -using System.Globalization; - -namespace War3Net.CodeAnalysis.Jass.Syntax -{ - public class JassDecimalLiteralExpressionSyntax : IExpressionSyntax - { - public JassDecimalLiteralExpressionSyntax(int value) - { - Value = value; - } - - public int Value { get; init; } - - public bool Equals(IExpressionSyntax? other) - { - return other is JassDecimalLiteralExpressionSyntax decimalLiteralExpression - && Value == decimalLiteralExpression.Value; - } - - public override string ToString() => Value.ToString(CultureInfo.InvariantCulture); - } -} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Syntax/JassElementAccessClauseSyntax.cs b/src/War3Net.CodeAnalysis.Jass/Syntax/JassElementAccessClauseSyntax.cs new file mode 100644 index 00000000..39c619ba --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/Syntax/JassElementAccessClauseSyntax.cs @@ -0,0 +1,121 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.IO; + +namespace War3Net.CodeAnalysis.Jass.Syntax +{ + public class JassElementAccessClauseSyntax : JassSyntaxNode + { + internal JassElementAccessClauseSyntax( + JassSyntaxToken openBracketToken, + JassExpressionSyntax expression, + JassSyntaxToken closeBracketToken) + { + OpenBracketToken = openBracketToken; + Expression = expression; + CloseBracketToken = closeBracketToken; + } + + public JassSyntaxToken OpenBracketToken { get; } + + public JassExpressionSyntax Expression { get; } + + public JassSyntaxToken CloseBracketToken { get; } + + public override JassSyntaxKind SyntaxKind => JassSyntaxKind.ElementAccessClause; + + public override bool IsEquivalentTo([NotNullWhen(true)] JassSyntaxNode? other) + { + return other is JassElementAccessClauseSyntax elementAccessClause + && Expression.IsEquivalentTo(elementAccessClause.Expression); + } + + public override void WriteTo(TextWriter writer) + { + OpenBracketToken.WriteTo(writer); + Expression.WriteTo(writer); + CloseBracketToken.WriteTo(writer); + } + + public override IEnumerable GetChildNodes() + { + yield return Expression; + } + + public override IEnumerable GetChildTokens() + { + yield return OpenBracketToken; + yield return CloseBracketToken; + } + + public override IEnumerable GetChildNodesAndTokens() + { + yield return OpenBracketToken; + yield return Expression; + yield return CloseBracketToken; + } + + public override IEnumerable GetDescendantNodes() + { + yield return Expression; + foreach (var descendant in Expression.GetDescendantNodes()) + { + yield return descendant; + } + } + + public override IEnumerable GetDescendantTokens() + { + yield return OpenBracketToken; + + foreach (var descendant in Expression.GetDescendantTokens()) + { + yield return descendant; + } + + yield return CloseBracketToken; + } + + public override IEnumerable GetDescendantNodesAndTokens() + { + yield return OpenBracketToken; + + yield return Expression; + foreach (var descendant in Expression.GetDescendantNodesAndTokens()) + { + yield return descendant; + } + + yield return CloseBracketToken; + } + + public override string ToString() => $"{OpenBracketToken}{Expression}{CloseBracketToken}"; + + public override JassSyntaxToken GetFirstToken() => OpenBracketToken; + + public override JassSyntaxToken GetLastToken() => CloseBracketToken; + + protected internal override JassElementAccessClauseSyntax ReplaceFirstToken(JassSyntaxToken newToken) + { + return new JassElementAccessClauseSyntax( + newToken, + Expression, + CloseBracketToken); + } + + protected internal override JassElementAccessClauseSyntax ReplaceLastToken(JassSyntaxToken newToken) + { + return new JassElementAccessClauseSyntax( + OpenBracketToken, + Expression, + newToken); + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Syntax/JassElementAccessExpressionSyntax.cs b/src/War3Net.CodeAnalysis.Jass/Syntax/JassElementAccessExpressionSyntax.cs new file mode 100644 index 00000000..0ab5dc72 --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/Syntax/JassElementAccessExpressionSyntax.cs @@ -0,0 +1,123 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.IO; + +namespace War3Net.CodeAnalysis.Jass.Syntax +{ + public class JassElementAccessExpressionSyntax : JassExpressionSyntax + { + internal JassElementAccessExpressionSyntax( + JassIdentifierNameSyntax identifierName, + JassElementAccessClauseSyntax elementAccessClause) + { + IdentifierName = identifierName; + ElementAccessClause = elementAccessClause; + } + + public JassIdentifierNameSyntax IdentifierName { get; } + + public JassElementAccessClauseSyntax ElementAccessClause { get; } + + public override JassSyntaxKind SyntaxKind => JassSyntaxKind.ElementAccessExpression; + + public override bool IsEquivalentTo([NotNullWhen(true)] JassSyntaxNode? other) + { + return other is JassElementAccessExpressionSyntax elementAccessExpression + && IdentifierName.IsEquivalentTo(elementAccessExpression.IdentifierName) + && ElementAccessClause.IsEquivalentTo(elementAccessExpression.ElementAccessClause); + } + + public override void WriteTo(TextWriter writer) + { + IdentifierName.WriteTo(writer); + ElementAccessClause.WriteTo(writer); + } + + public override IEnumerable GetChildNodes() + { + yield return IdentifierName; + yield return ElementAccessClause; + } + + public override IEnumerable GetChildTokens() + { + yield break; + } + + public override IEnumerable GetChildNodesAndTokens() + { + yield return IdentifierName; + yield return ElementAccessClause; + } + + public override IEnumerable GetDescendantNodes() + { + yield return IdentifierName; + foreach (var descendant in IdentifierName.GetDescendantNodes()) + { + yield return descendant; + } + + yield return ElementAccessClause; + foreach (var descendant in ElementAccessClause.GetDescendantNodes()) + { + yield return descendant; + } + } + + public override IEnumerable GetDescendantTokens() + { + foreach (var descendant in IdentifierName.GetDescendantTokens()) + { + yield return descendant; + } + + foreach (var descendant in ElementAccessClause.GetDescendantTokens()) + { + yield return descendant; + } + } + + public override IEnumerable GetDescendantNodesAndTokens() + { + yield return IdentifierName; + foreach (var descendant in IdentifierName.GetDescendantNodesAndTokens()) + { + yield return descendant; + } + + yield return ElementAccessClause; + foreach (var descendant in ElementAccessClause.GetDescendantNodesAndTokens()) + { + yield return descendant; + } + } + + public override string ToString() => $"{IdentifierName}{ElementAccessClause}"; + + public override JassSyntaxToken GetFirstToken() => IdentifierName.GetFirstToken(); + + public override JassSyntaxToken GetLastToken() => ElementAccessClause.GetLastToken(); + + protected internal override JassElementAccessExpressionSyntax ReplaceFirstToken(JassSyntaxToken newToken) + { + return new JassElementAccessExpressionSyntax( + IdentifierName.ReplaceFirstToken(newToken), + ElementAccessClause); + } + + protected internal override JassElementAccessExpressionSyntax ReplaceLastToken(JassSyntaxToken newToken) + { + return new JassElementAccessExpressionSyntax( + IdentifierName, + ElementAccessClause.ReplaceLastToken(newToken)); + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Syntax/JassElseClauseSyntax.cs b/src/War3Net.CodeAnalysis.Jass/Syntax/JassElseClauseSyntax.cs index 46e279d1..e30ad577 100644 --- a/src/War3Net.CodeAnalysis.Jass/Syntax/JassElseClauseSyntax.cs +++ b/src/War3Net.CodeAnalysis.Jass/Syntax/JassElseClauseSyntax.cs @@ -5,25 +5,113 @@ // // ------------------------------------------------------------------------------ -using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Diagnostics.CodeAnalysis; +using System.IO; + +using War3Net.CodeAnalysis.Jass.Extensions; namespace War3Net.CodeAnalysis.Jass.Syntax { - public class JassElseClauseSyntax : IEquatable + public class JassElseClauseSyntax : JassSyntaxNode { - public JassElseClauseSyntax(JassStatementListSyntax body) + internal JassElseClauseSyntax( + JassSyntaxToken elseToken, + ImmutableArray statements) + { + ElseToken = elseToken; + Statements = statements; + } + + public JassSyntaxToken ElseToken { get; } + + public ImmutableArray Statements { get; } + + public override JassSyntaxKind SyntaxKind => JassSyntaxKind.ElseClause; + + public override bool IsEquivalentTo([NotNullWhen(true)] JassSyntaxNode? other) + { + return other is JassElseClauseSyntax elseClause + && Statements.IsEquivalentTo(elseClause.Statements); + } + + public override void WriteTo(TextWriter writer) + { + ElseToken.WriteTo(writer); + Statements.WriteTo(writer); + } + + public override IEnumerable GetChildNodes() + { + return Statements; + } + + public override IEnumerable GetChildTokens() + { + yield return ElseToken; + } + + public override IEnumerable GetChildNodesAndTokens() + { + yield return ElseToken; + + foreach (var child in Statements) + { + yield return child; + } + } + + public override IEnumerable GetDescendantNodes() + { + return Statements.GetDescendantNodes(); + } + + public override IEnumerable GetDescendantTokens() + { + yield return ElseToken; + + foreach (var descendant in Statements.GetDescendantTokens()) + { + yield return descendant; + } + } + + public override IEnumerable GetDescendantNodesAndTokens() { - Body = body; + yield return ElseToken; + + foreach (var descendant in Statements.GetDescendantNodesAndTokens()) + { + yield return descendant; + } } - public JassStatementListSyntax Body { get; init; } + public override string ToString() => $"{ElseToken} [...]"; + + public override JassSyntaxToken GetFirstToken() => ElseToken; - public bool Equals(JassElseClauseSyntax? other) + public override JassSyntaxToken GetLastToken() => Statements.IsEmpty ? ElseToken : Statements[^1].GetLastToken(); + + protected internal override JassElseClauseSyntax ReplaceFirstToken(JassSyntaxToken newToken) { - return other is not null - && Body.Equals(other.Body); + return new JassElseClauseSyntax( + newToken, + Statements); } - public override string ToString() => $"{JassKeyword.Else} [{Body.Statements.Length}]"; + protected internal override JassElseClauseSyntax ReplaceLastToken(JassSyntaxToken newToken) + { + if (!Statements.IsEmpty) + { + return new JassElseClauseSyntax( + ElseToken, + Statements.ReplaceLastItem(Statements[^1].ReplaceLastToken(newToken))); + } + + return new JassElseClauseSyntax( + newToken, + Statements); + } } } \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Syntax/JassElseCustomScriptAction.cs b/src/War3Net.CodeAnalysis.Jass/Syntax/JassElseCustomScriptAction.cs deleted file mode 100644 index 4e6b693a..00000000 --- a/src/War3Net.CodeAnalysis.Jass/Syntax/JassElseCustomScriptAction.cs +++ /dev/null @@ -1,22 +0,0 @@ -// ------------------------------------------------------------------------------ -// -// Licensed under the MIT license. -// See the LICENSE file in the project root for more information. -// -// ------------------------------------------------------------------------------ - -namespace War3Net.CodeAnalysis.Jass.Syntax -{ - public class JassElseCustomScriptAction : IStatementLineSyntax - { - public static readonly JassElseCustomScriptAction Value = new JassElseCustomScriptAction(); - - private JassElseCustomScriptAction() - { - } - - public bool Equals(IStatementLineSyntax? other) => other is JassElseCustomScriptAction; - - public override string ToString() => JassKeyword.Else; - } -} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Syntax/JassElseIfClauseDeclaratorSyntax.cs b/src/War3Net.CodeAnalysis.Jass/Syntax/JassElseIfClauseDeclaratorSyntax.cs new file mode 100644 index 00000000..b73553e6 --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/Syntax/JassElseIfClauseDeclaratorSyntax.cs @@ -0,0 +1,121 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.IO; + +namespace War3Net.CodeAnalysis.Jass.Syntax +{ + public class JassElseIfClauseDeclaratorSyntax : JassSyntaxNode + { + internal JassElseIfClauseDeclaratorSyntax( + JassSyntaxToken elseIfToken, + JassExpressionSyntax condition, + JassSyntaxToken thenToken) + { + ElseIfToken = elseIfToken; + Condition = condition; + ThenToken = thenToken; + } + + public JassSyntaxToken ElseIfToken { get; } + + public JassExpressionSyntax Condition { get; } + + public JassSyntaxToken ThenToken { get; } + + public override JassSyntaxKind SyntaxKind => JassSyntaxKind.ElseIfClauseDeclarator; + + public override bool IsEquivalentTo([NotNullWhen(true)] JassSyntaxNode? other) + { + return other is JassElseIfClauseDeclaratorSyntax elseIfClauseDeclarator + && Condition.IsEquivalentTo(elseIfClauseDeclarator.Condition); + } + + public override void WriteTo(TextWriter writer) + { + ElseIfToken.WriteTo(writer); + Condition.WriteTo(writer); + ThenToken.WriteTo(writer); + } + + public override IEnumerable GetChildNodes() + { + yield return Condition; + } + + public override IEnumerable GetChildTokens() + { + yield return ElseIfToken; + yield return ThenToken; + } + + public override IEnumerable GetChildNodesAndTokens() + { + yield return ElseIfToken; + yield return Condition; + yield return ThenToken; + } + + public override IEnumerable GetDescendantNodes() + { + yield return Condition; + foreach (var descendant in Condition.GetDescendantNodes()) + { + yield return descendant; + } + } + + public override IEnumerable GetDescendantTokens() + { + yield return ElseIfToken; + + foreach (var descendant in Condition.GetDescendantTokens()) + { + yield return descendant; + } + + yield return ThenToken; + } + + public override IEnumerable GetDescendantNodesAndTokens() + { + yield return ElseIfToken; + + yield return Condition; + foreach (var descendant in Condition.GetDescendantNodesAndTokens()) + { + yield return descendant; + } + + yield return ThenToken; + } + + public override string ToString() => $"{ElseIfToken} {Condition} {ThenToken}"; + + public override JassSyntaxToken GetFirstToken() => ElseIfToken; + + public override JassSyntaxToken GetLastToken() => ThenToken; + + protected internal override JassElseIfClauseDeclaratorSyntax ReplaceFirstToken(JassSyntaxToken newToken) + { + return new JassElseIfClauseDeclaratorSyntax( + newToken, + Condition, + ThenToken); + } + + protected internal override JassElseIfClauseDeclaratorSyntax ReplaceLastToken(JassSyntaxToken newToken) + { + return new JassElseIfClauseDeclaratorSyntax( + ElseIfToken, + Condition, + newToken); + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Syntax/JassElseIfClauseSyntax.cs b/src/War3Net.CodeAnalysis.Jass/Syntax/JassElseIfClauseSyntax.cs index d5393202..2293687b 100644 --- a/src/War3Net.CodeAnalysis.Jass/Syntax/JassElseIfClauseSyntax.cs +++ b/src/War3Net.CodeAnalysis.Jass/Syntax/JassElseIfClauseSyntax.cs @@ -5,29 +5,133 @@ // // ------------------------------------------------------------------------------ -using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Diagnostics.CodeAnalysis; +using System.IO; + +using War3Net.CodeAnalysis.Jass.Extensions; namespace War3Net.CodeAnalysis.Jass.Syntax { - public class JassElseIfClauseSyntax : IEquatable + public class JassElseIfClauseSyntax : JassSyntaxNode { - public JassElseIfClauseSyntax(IExpressionSyntax condition, JassStatementListSyntax body) + internal JassElseIfClauseSyntax( + JassElseIfClauseDeclaratorSyntax elseIfClauseDeclarator, + ImmutableArray statements) + { + ElseIfClauseDeclarator = elseIfClauseDeclarator; + Statements = statements; + } + + public JassElseIfClauseDeclaratorSyntax ElseIfClauseDeclarator { get; } + + public ImmutableArray Statements { get; } + + public override JassSyntaxKind SyntaxKind => JassSyntaxKind.ElseIfClause; + + public override bool IsEquivalentTo([NotNullWhen(true)] JassSyntaxNode? other) + { + return other is JassElseIfClauseSyntax elseIfClause + && ElseIfClauseDeclarator.IsEquivalentTo(elseIfClause.ElseIfClauseDeclarator) + && Statements.IsEquivalentTo(elseIfClause.Statements); + } + + public override void WriteTo(TextWriter writer) { - Condition = condition; - Body = body; + ElseIfClauseDeclarator.WriteTo(writer); + Statements.WriteTo(writer); } - public IExpressionSyntax Condition { get; init; } + public override IEnumerable GetChildNodes() + { + yield return ElseIfClauseDeclarator; + foreach (var child in Statements) + { + yield return child; + } + } - public JassStatementListSyntax Body { get; init; } + public override IEnumerable GetChildTokens() + { + yield break; + } - public bool Equals(JassElseIfClauseSyntax? other) + public override IEnumerable GetChildNodesAndTokens() { - return other is not null - && Condition.Equals(other.Condition) - && Body.Equals(other.Body); + yield return ElseIfClauseDeclarator; + foreach (var child in Statements) + { + yield return child; + } + } + + public override IEnumerable GetDescendantNodes() + { + yield return ElseIfClauseDeclarator; + foreach (var descendant in ElseIfClauseDeclarator.GetDescendantNodes()) + { + yield return descendant; + } + + foreach (var descendant in Statements.GetDescendantNodes()) + { + yield return descendant; + } } - public override string ToString() => $"{JassKeyword.ElseIf} {Condition} {JassKeyword.Then} [{Body.Statements.Length}]"; + public override IEnumerable GetDescendantTokens() + { + foreach (var descendant in ElseIfClauseDeclarator.GetDescendantTokens()) + { + yield return descendant; + } + + foreach (var descendant in Statements.GetDescendantTokens()) + { + yield return descendant; + } + } + + public override IEnumerable GetDescendantNodesAndTokens() + { + yield return ElseIfClauseDeclarator; + foreach (var descendant in ElseIfClauseDeclarator.GetDescendantNodesAndTokens()) + { + yield return descendant; + } + + foreach (var descendant in Statements.GetDescendantNodesAndTokens()) + { + yield return descendant; + } + } + + public override string ToString() => $"{ElseIfClauseDeclarator} [...]"; + + public override JassSyntaxToken GetFirstToken() => ElseIfClauseDeclarator.GetFirstToken(); + + public override JassSyntaxToken GetLastToken() => Statements.IsEmpty ? ElseIfClauseDeclarator.GetLastToken() : Statements[^1].GetLastToken(); + + protected internal override JassElseIfClauseSyntax ReplaceFirstToken(JassSyntaxToken newToken) + { + return new JassElseIfClauseSyntax( + ElseIfClauseDeclarator.ReplaceFirstToken(newToken), + Statements); + } + + protected internal override JassElseIfClauseSyntax ReplaceLastToken(JassSyntaxToken newToken) + { + if (!Statements.IsEmpty) + { + return new JassElseIfClauseSyntax( + ElseIfClauseDeclarator, + Statements.ReplaceLastItem(Statements[^1].ReplaceLastToken(newToken))); + } + + return new JassElseIfClauseSyntax( + ElseIfClauseDeclarator.ReplaceLastToken(newToken), + Statements); + } } } \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Syntax/JassElseIfCustomScriptAction.cs b/src/War3Net.CodeAnalysis.Jass/Syntax/JassElseIfCustomScriptAction.cs deleted file mode 100644 index 2aa2b35f..00000000 --- a/src/War3Net.CodeAnalysis.Jass/Syntax/JassElseIfCustomScriptAction.cs +++ /dev/null @@ -1,27 +0,0 @@ -// ------------------------------------------------------------------------------ -// -// Licensed under the MIT license. -// See the LICENSE file in the project root for more information. -// -// ------------------------------------------------------------------------------ - -namespace War3Net.CodeAnalysis.Jass.Syntax -{ - public class JassElseIfCustomScriptAction : IStatementLineSyntax - { - public JassElseIfCustomScriptAction(IExpressionSyntax condition) - { - Condition = condition; - } - - public IExpressionSyntax Condition { get; init; } - - public bool Equals(IStatementLineSyntax? other) - { - return other is JassElseIfCustomScriptAction elseIf - && Condition.Equals(elseIf.Condition); - } - - public override string ToString() => $"{JassKeyword.ElseIf} {Condition} {JassKeyword.Then}"; - } -} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Syntax/JassEmptyParameterListSyntax.cs b/src/War3Net.CodeAnalysis.Jass/Syntax/JassEmptyParameterListSyntax.cs new file mode 100644 index 00000000..9d86eb93 --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/Syntax/JassEmptyParameterListSyntax.cs @@ -0,0 +1,99 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.IO; + +namespace War3Net.CodeAnalysis.Jass.Syntax +{ + public class JassEmptyParameterListSyntax : JassParameterListOrEmptyParameterListSyntax + { + public static readonly JassEmptyParameterListSyntax Value = new( + new JassSyntaxToken(JassSyntaxKind.TakesKeyword, JassKeyword.Takes, JassSyntaxTriviaList.SingleSpace), + new JassSyntaxToken(JassSyntaxKind.NothingKeyword, JassKeyword.Nothing, JassSyntaxTriviaList.SingleSpace)); + + internal JassEmptyParameterListSyntax( + JassSyntaxToken takesToken, + JassSyntaxToken nothingToken) + { + TakesToken = takesToken; + NothingToken = nothingToken; + } + + public JassSyntaxToken TakesToken { get; } + + public JassSyntaxToken NothingToken { get; } + + public override JassSyntaxKind SyntaxKind => JassSyntaxKind.EmptyParameterList; + + public override bool IsEquivalentTo([NotNullWhen(true)] JassSyntaxNode? other) + { + return other is JassEmptyParameterListSyntax; + } + + public override void WriteTo(TextWriter writer) + { + TakesToken.WriteTo(writer); + NothingToken.WriteTo(writer); + } + + public override IEnumerable GetChildNodes() + { + yield break; + } + + public override IEnumerable GetChildTokens() + { + yield return TakesToken; + yield return NothingToken; + } + + public override IEnumerable GetChildNodesAndTokens() + { + yield return TakesToken; + yield return NothingToken; + } + + public override IEnumerable GetDescendantNodes() + { + yield break; + } + + public override IEnumerable GetDescendantTokens() + { + yield return TakesToken; + yield return NothingToken; + } + + public override IEnumerable GetDescendantNodesAndTokens() + { + yield return TakesToken; + yield return NothingToken; + } + + public override string ToString() => $"{TakesToken} {NothingToken}"; + + public override JassSyntaxToken GetFirstToken() => TakesToken; + + public override JassSyntaxToken GetLastToken() => NothingToken; + + protected internal override JassEmptyParameterListSyntax ReplaceFirstToken(JassSyntaxToken newToken) + { + return new JassEmptyParameterListSyntax( + newToken, + NothingToken); + } + + protected internal override JassEmptyParameterListSyntax ReplaceLastToken(JassSyntaxToken newToken) + { + return new JassEmptyParameterListSyntax( + TakesToken, + newToken); + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Syntax/JassEmptySyntax.cs b/src/War3Net.CodeAnalysis.Jass/Syntax/JassEmptySyntax.cs deleted file mode 100644 index 5b13cec4..00000000 --- a/src/War3Net.CodeAnalysis.Jass/Syntax/JassEmptySyntax.cs +++ /dev/null @@ -1,38 +0,0 @@ -// ------------------------------------------------------------------------------ -// -// Licensed under the MIT license. -// See the LICENSE file in the project root for more information. -// -// ------------------------------------------------------------------------------ - -namespace War3Net.CodeAnalysis.Jass.Syntax -{ - public class JassEmptySyntax : ITopLevelDeclarationSyntax, IScopedDeclarationSyntax, IGlobalDeclarationSyntax, IScopedGlobalDeclarationSyntax, IMemberDeclarationSyntax, IStatementSyntax, IDeclarationLineSyntax, IGlobalLineSyntax, IStatementLineSyntax - { - public static readonly JassEmptySyntax Value = new JassEmptySyntax(); - - private JassEmptySyntax() - { - } - - public bool Equals(ITopLevelDeclarationSyntax? other) => other is JassEmptySyntax; - - public bool Equals(IScopedDeclarationSyntax? other) => other is JassEmptySyntax; - - public bool Equals(IGlobalDeclarationSyntax? other) => other is JassEmptySyntax; - - public bool Equals(IScopedGlobalDeclarationSyntax? other) => other is JassEmptySyntax; - - public bool Equals(IMemberDeclarationSyntax? other) => other is JassEmptySyntax; - - public bool Equals(IStatementSyntax? other) => other is JassEmptySyntax; - - public bool Equals(IDeclarationLineSyntax? other) => other is JassEmptySyntax; - - public bool Equals(IGlobalLineSyntax? other) => other is JassEmptySyntax; - - public bool Equals(IStatementLineSyntax? other) => other is JassEmptySyntax; - - public override string ToString() => string.Empty; - } -} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Syntax/JassEndFunctionCustomScriptAction.cs b/src/War3Net.CodeAnalysis.Jass/Syntax/JassEndFunctionCustomScriptAction.cs deleted file mode 100644 index b5ddffec..00000000 --- a/src/War3Net.CodeAnalysis.Jass/Syntax/JassEndFunctionCustomScriptAction.cs +++ /dev/null @@ -1,22 +0,0 @@ -// ------------------------------------------------------------------------------ -// -// Licensed under the MIT license. -// See the LICENSE file in the project root for more information. -// -// ------------------------------------------------------------------------------ - -namespace War3Net.CodeAnalysis.Jass.Syntax -{ - public class JassEndFunctionCustomScriptAction : IStatementLineSyntax - { - public static readonly JassEndFunctionCustomScriptAction Value = new JassEndFunctionCustomScriptAction(); - - private JassEndFunctionCustomScriptAction() - { - } - - public bool Equals(IStatementLineSyntax? other) => other is JassEndFunctionCustomScriptAction; - - public override string ToString() => JassKeyword.EndFunction; - } -} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Syntax/JassEndGlobalsCustomScriptAction.cs b/src/War3Net.CodeAnalysis.Jass/Syntax/JassEndGlobalsCustomScriptAction.cs deleted file mode 100644 index dbf1de22..00000000 --- a/src/War3Net.CodeAnalysis.Jass/Syntax/JassEndGlobalsCustomScriptAction.cs +++ /dev/null @@ -1,22 +0,0 @@ -// ------------------------------------------------------------------------------ -// -// Licensed under the MIT license. -// See the LICENSE file in the project root for more information. -// -// ------------------------------------------------------------------------------ - -namespace War3Net.CodeAnalysis.Jass.Syntax -{ - public class JassEndGlobalsCustomScriptAction : IGlobalLineSyntax - { - public static readonly JassEndGlobalsCustomScriptAction Value = new JassEndGlobalsCustomScriptAction(); - - private JassEndGlobalsCustomScriptAction() - { - } - - public bool Equals(IGlobalLineSyntax? other) => other is JassEndGlobalsCustomScriptAction; - - public override string ToString() => JassKeyword.EndGlobals; - } -} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Syntax/JassEndIfCustomScriptAction.cs b/src/War3Net.CodeAnalysis.Jass/Syntax/JassEndIfCustomScriptAction.cs deleted file mode 100644 index c27ed66c..00000000 --- a/src/War3Net.CodeAnalysis.Jass/Syntax/JassEndIfCustomScriptAction.cs +++ /dev/null @@ -1,22 +0,0 @@ -// ------------------------------------------------------------------------------ -// -// Licensed under the MIT license. -// See the LICENSE file in the project root for more information. -// -// ------------------------------------------------------------------------------ - -namespace War3Net.CodeAnalysis.Jass.Syntax -{ - public class JassEndIfCustomScriptAction : IStatementLineSyntax - { - public static readonly JassEndIfCustomScriptAction Value = new JassEndIfCustomScriptAction(); - - private JassEndIfCustomScriptAction() - { - } - - public bool Equals(IStatementLineSyntax? other) => other is JassEndIfCustomScriptAction; - - public override string ToString() => JassKeyword.EndIf; - } -} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Syntax/JassEndLoopCustomScriptAction.cs b/src/War3Net.CodeAnalysis.Jass/Syntax/JassEndLoopCustomScriptAction.cs deleted file mode 100644 index 4c293da2..00000000 --- a/src/War3Net.CodeAnalysis.Jass/Syntax/JassEndLoopCustomScriptAction.cs +++ /dev/null @@ -1,22 +0,0 @@ -// ------------------------------------------------------------------------------ -// -// Licensed under the MIT license. -// See the LICENSE file in the project root for more information. -// -// ------------------------------------------------------------------------------ - -namespace War3Net.CodeAnalysis.Jass.Syntax -{ - public class JassEndLoopCustomScriptAction : IStatementLineSyntax - { - public static readonly JassEndLoopCustomScriptAction Value = new JassEndLoopCustomScriptAction(); - - private JassEndLoopCustomScriptAction() - { - } - - public bool Equals(IStatementLineSyntax? other) => other is JassEndLoopCustomScriptAction; - - public override string ToString() => JassKeyword.EndLoop; - } -} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Syntax/JassEqualsValueClauseSyntax.cs b/src/War3Net.CodeAnalysis.Jass/Syntax/JassEqualsValueClauseSyntax.cs index 5601ff8c..90dd4960 100644 --- a/src/War3Net.CodeAnalysis.Jass/Syntax/JassEqualsValueClauseSyntax.cs +++ b/src/War3Net.CodeAnalysis.Jass/Syntax/JassEqualsValueClauseSyntax.cs @@ -5,25 +5,104 @@ // // ------------------------------------------------------------------------------ -using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.IO; namespace War3Net.CodeAnalysis.Jass.Syntax { - public class JassEqualsValueClauseSyntax : IEquatable + public class JassEqualsValueClauseSyntax : JassSyntaxNode { - public JassEqualsValueClauseSyntax(IExpressionSyntax expression) + internal JassEqualsValueClauseSyntax( + JassSyntaxToken equalsToken, + JassExpressionSyntax expression) { + EqualsToken = equalsToken; Expression = expression; } - public IExpressionSyntax Expression { get; init; } + public JassSyntaxToken EqualsToken { get; } - public bool Equals(JassEqualsValueClauseSyntax? other) + public JassExpressionSyntax Expression { get; } + + public override JassSyntaxKind SyntaxKind => JassSyntaxKind.EqualsValueClause; + + public override bool IsEquivalentTo([NotNullWhen(true)] JassSyntaxNode? other) + { + return other is JassEqualsValueClauseSyntax equalsValueClause + && Expression.IsEquivalentTo(equalsValueClause.Expression); + } + + public override void WriteTo(TextWriter writer) + { + EqualsToken.WriteTo(writer); + Expression.WriteTo(writer); + } + + public override IEnumerable GetChildNodes() + { + yield return Expression; + } + + public override IEnumerable GetChildTokens() + { + yield return EqualsToken; + } + + public override IEnumerable GetChildNodesAndTokens() + { + yield return EqualsToken; + yield return Expression; + } + + public override IEnumerable GetDescendantNodes() { - return other is not null - && Expression.Equals(other.Expression); + yield return Expression; + foreach (var descendant in Expression.GetDescendantNodes()) + { + yield return descendant; + } } - public override string ToString() => $"{JassSymbol.EqualsSign} {Expression}"; + public override IEnumerable GetDescendantTokens() + { + yield return EqualsToken; + + foreach (var descendant in Expression.GetDescendantTokens()) + { + yield return descendant; + } + } + + public override IEnumerable GetDescendantNodesAndTokens() + { + yield return EqualsToken; + + yield return Expression; + foreach (var descendant in Expression.GetDescendantNodesAndTokens()) + { + yield return descendant; + } + } + + public override string ToString() => $"{EqualsToken} {Expression}"; + + public override JassSyntaxToken GetFirstToken() => EqualsToken; + + public override JassSyntaxToken GetLastToken() => Expression.GetLastToken(); + + protected internal override JassEqualsValueClauseSyntax ReplaceFirstToken(JassSyntaxToken newToken) + { + return new JassEqualsValueClauseSyntax( + newToken, + Expression); + } + + protected internal override JassEqualsValueClauseSyntax ReplaceLastToken(JassSyntaxToken newToken) + { + return new JassEqualsValueClauseSyntax( + EqualsToken, + Expression.ReplaceLastToken(newToken)); + } } } \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Syntax/JassExitStatementSyntax.cs b/src/War3Net.CodeAnalysis.Jass/Syntax/JassExitStatementSyntax.cs index 0ce16e9f..9b78fbd4 100644 --- a/src/War3Net.CodeAnalysis.Jass/Syntax/JassExitStatementSyntax.cs +++ b/src/War3Net.CodeAnalysis.Jass/Syntax/JassExitStatementSyntax.cs @@ -5,29 +5,104 @@ // // ------------------------------------------------------------------------------ +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.IO; + namespace War3Net.CodeAnalysis.Jass.Syntax { - public class JassExitStatementSyntax : IStatementSyntax, IStatementLineSyntax + public class JassExitStatementSyntax : JassStatementSyntax { - public JassExitStatementSyntax(IExpressionSyntax condition) + internal JassExitStatementSyntax( + JassSyntaxToken exitWhenToken, + JassExpressionSyntax condition) { + ExitWhenToken = exitWhenToken; Condition = condition; } - public IExpressionSyntax Condition { get; init; } + public JassSyntaxToken ExitWhenToken { get; } + + public JassExpressionSyntax Condition { get; } - public bool Equals(IStatementSyntax? other) + public override JassSyntaxKind SyntaxKind => JassSyntaxKind.ExitStatement; + + public override bool IsEquivalentTo([NotNullWhen(true)] JassSyntaxNode? other) { return other is JassExitStatementSyntax exitStatement - && Condition.Equals(exitStatement.Condition); + && Condition.IsEquivalentTo(exitStatement.Condition); } - public bool Equals(IStatementLineSyntax? other) + public override void WriteTo(TextWriter writer) { - return other is JassExitStatementSyntax exitStatement - && Condition.Equals(exitStatement.Condition); + ExitWhenToken.WriteTo(writer); + Condition.WriteTo(writer); + } + + public override IEnumerable GetChildNodes() + { + yield return Condition; + } + + public override IEnumerable GetChildTokens() + { + yield return ExitWhenToken; + } + + public override IEnumerable GetChildNodesAndTokens() + { + yield return ExitWhenToken; + yield return Condition; + } + + public override IEnumerable GetDescendantNodes() + { + yield return Condition; + foreach (var descendant in Condition.GetDescendantNodes()) + { + yield return descendant; + } } - public override string ToString() => $"{JassKeyword.ExitWhen} {Condition}"; + public override IEnumerable GetDescendantTokens() + { + yield return ExitWhenToken; + + foreach (var descendant in Condition.GetDescendantTokens()) + { + yield return descendant; + } + } + + public override IEnumerable GetDescendantNodesAndTokens() + { + yield return ExitWhenToken; + + yield return Condition; + foreach (var descendant in Condition.GetDescendantNodesAndTokens()) + { + yield return descendant; + } + } + + public override string ToString() => $"{ExitWhenToken} {Condition}"; + + public override JassSyntaxToken GetFirstToken() => ExitWhenToken; + + public override JassSyntaxToken GetLastToken() => Condition.GetLastToken(); + + protected internal override JassExitStatementSyntax ReplaceFirstToken(JassSyntaxToken newToken) + { + return new JassExitStatementSyntax( + newToken, + Condition); + } + + protected internal override JassExitStatementSyntax ReplaceLastToken(JassSyntaxToken newToken) + { + return new JassExitStatementSyntax( + ExitWhenToken, + Condition.ReplaceLastToken(newToken)); + } } } \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Syntax/JassExpressionSyntax.cs b/src/War3Net.CodeAnalysis.Jass/Syntax/JassExpressionSyntax.cs new file mode 100644 index 00000000..c24943a9 --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/Syntax/JassExpressionSyntax.cs @@ -0,0 +1,16 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +namespace War3Net.CodeAnalysis.Jass.Syntax +{ + public abstract class JassExpressionSyntax : JassSyntaxNode + { + protected internal override abstract JassExpressionSyntax ReplaceFirstToken(JassSyntaxToken newToken); + + protected internal override abstract JassExpressionSyntax ReplaceLastToken(JassSyntaxToken newToken); + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Syntax/JassFourCCLiteralExpressionSyntax.cs b/src/War3Net.CodeAnalysis.Jass/Syntax/JassFourCCLiteralExpressionSyntax.cs deleted file mode 100644 index 55031196..00000000 --- a/src/War3Net.CodeAnalysis.Jass/Syntax/JassFourCCLiteralExpressionSyntax.cs +++ /dev/null @@ -1,29 +0,0 @@ -// ------------------------------------------------------------------------------ -// -// Licensed under the MIT license. -// See the LICENSE file in the project root for more information. -// -// ------------------------------------------------------------------------------ - -using War3Net.CodeAnalysis.Jass.Extensions; - -namespace War3Net.CodeAnalysis.Jass.Syntax -{ - public class JassFourCCLiteralExpressionSyntax : IExpressionSyntax - { - public JassFourCCLiteralExpressionSyntax(int value) - { - Value = value; - } - - public int Value { get; init; } - - public bool Equals(IExpressionSyntax? other) - { - return other is JassFourCCLiteralExpressionSyntax fourCCLiteralExpression - && Value == fourCCLiteralExpression.Value; - } - - public override string ToString() => $"{JassSymbol.Apostrophe}{Value.ToJassRawcode()}{JassSymbol.Apostrophe}"; - } -} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Syntax/JassFunctionCustomScriptAction.cs b/src/War3Net.CodeAnalysis.Jass/Syntax/JassFunctionCustomScriptAction.cs deleted file mode 100644 index 139210ee..00000000 --- a/src/War3Net.CodeAnalysis.Jass/Syntax/JassFunctionCustomScriptAction.cs +++ /dev/null @@ -1,27 +0,0 @@ -// ------------------------------------------------------------------------------ -// -// Licensed under the MIT license. -// See the LICENSE file in the project root for more information. -// -// ------------------------------------------------------------------------------ - -namespace War3Net.CodeAnalysis.Jass.Syntax -{ - public class JassFunctionCustomScriptAction : IDeclarationLineSyntax - { - public JassFunctionCustomScriptAction(JassFunctionDeclaratorSyntax functionDeclarator) - { - FunctionDeclarator = functionDeclarator; - } - - public JassFunctionDeclaratorSyntax FunctionDeclarator { get; init; } - - public bool Equals(IDeclarationLineSyntax? other) - { - return other is JassFunctionCustomScriptAction function - && FunctionDeclarator.Equals(function.FunctionDeclarator); - } - - public override string ToString() => $"{JassKeyword.Function} {FunctionDeclarator}"; - } -} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Syntax/JassFunctionDeclarationSyntax.cs b/src/War3Net.CodeAnalysis.Jass/Syntax/JassFunctionDeclarationSyntax.cs index a0f9a8e7..b00c1a2b 100644 --- a/src/War3Net.CodeAnalysis.Jass/Syntax/JassFunctionDeclarationSyntax.cs +++ b/src/War3Net.CodeAnalysis.Jass/Syntax/JassFunctionDeclarationSyntax.cs @@ -5,27 +5,141 @@ // // ------------------------------------------------------------------------------ +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Diagnostics.CodeAnalysis; +using System.IO; + +using War3Net.CodeAnalysis.Jass.Extensions; + namespace War3Net.CodeAnalysis.Jass.Syntax { - public class JassFunctionDeclarationSyntax : ITopLevelDeclarationSyntax + public class JassFunctionDeclarationSyntax : JassTopLevelDeclarationSyntax { - public JassFunctionDeclarationSyntax(JassFunctionDeclaratorSyntax functionDeclarator, JassStatementListSyntax body) + internal JassFunctionDeclarationSyntax( + JassFunctionDeclaratorSyntax functionDeclarator, + ImmutableArray statements, + JassSyntaxToken endFunctionToken) { FunctionDeclarator = functionDeclarator; - Body = body; + Statements = statements; + EndFunctionToken = endFunctionToken; } - public JassFunctionDeclaratorSyntax FunctionDeclarator { get; init; } + public JassFunctionDeclaratorSyntax FunctionDeclarator { get; } + + public ImmutableArray Statements { get; } - public JassStatementListSyntax Body { get; init; } + public JassSyntaxToken EndFunctionToken { get; } - public bool Equals(ITopLevelDeclarationSyntax? other) + public override JassSyntaxKind SyntaxKind => JassSyntaxKind.FunctionDeclaration; + + public override bool IsEquivalentTo([NotNullWhen(true)] JassSyntaxNode? other) { return other is JassFunctionDeclarationSyntax functionDeclaration - && FunctionDeclarator.Equals(functionDeclaration.FunctionDeclarator) - && Body.Equals(functionDeclaration.Body); + && FunctionDeclarator.IsEquivalentTo(functionDeclaration.FunctionDeclarator) + && Statements.IsEquivalentTo(functionDeclaration.Statements); + } + + public override void WriteTo(TextWriter writer) + { + FunctionDeclarator.WriteTo(writer); + Statements.WriteTo(writer); + EndFunctionToken.WriteTo(writer); + } + + public override IEnumerable GetChildNodes() + { + yield return FunctionDeclarator; + + foreach (var child in Statements) + { + yield return child; + } } - public override string ToString() => $"{FunctionDeclarator} [{Body.Statements.Length}]"; + public override IEnumerable GetChildTokens() + { + yield return EndFunctionToken; + } + + public override IEnumerable GetChildNodesAndTokens() + { + yield return FunctionDeclarator; + + foreach (var child in Statements) + { + yield return child; + } + + yield return EndFunctionToken; + } + + public override IEnumerable GetDescendantNodes() + { + yield return FunctionDeclarator; + foreach (var descendant in FunctionDeclarator.GetDescendantNodes()) + { + yield return descendant; + } + + foreach (var descendant in Statements.GetDescendantNodes()) + { + yield return descendant; + } + } + + public override IEnumerable GetDescendantTokens() + { + foreach (var descendant in FunctionDeclarator.GetDescendantTokens()) + { + yield return descendant; + } + + foreach (var descendant in Statements.GetDescendantTokens()) + { + yield return descendant; + } + + yield return EndFunctionToken; + } + + public override IEnumerable GetDescendantNodesAndTokens() + { + yield return FunctionDeclarator; + foreach (var descendant in FunctionDeclarator.GetDescendantNodesAndTokens()) + { + yield return descendant; + } + + foreach (var descendant in Statements.GetDescendantNodesAndTokens()) + { + yield return descendant; + } + + yield return EndFunctionToken; + } + + public override string ToString() => $"{FunctionDeclarator} [...]"; + + public override JassSyntaxToken GetFirstToken() => FunctionDeclarator.GetFirstToken(); + + public override JassSyntaxToken GetLastToken() => EndFunctionToken; + + protected internal override JassFunctionDeclarationSyntax ReplaceFirstToken(JassSyntaxToken newToken) + { + return new JassFunctionDeclarationSyntax( + FunctionDeclarator.ReplaceFirstToken(newToken), + Statements, + EndFunctionToken); + } + + protected internal override JassFunctionDeclarationSyntax ReplaceLastToken(JassSyntaxToken newToken) + { + return new JassFunctionDeclarationSyntax( + FunctionDeclarator, + Statements, + newToken); + } } } \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Syntax/JassFunctionDeclaratorSyntax.cs b/src/War3Net.CodeAnalysis.Jass/Syntax/JassFunctionDeclaratorSyntax.cs index 4abf03fa..d9abc2a0 100644 --- a/src/War3Net.CodeAnalysis.Jass/Syntax/JassFunctionDeclaratorSyntax.cs +++ b/src/War3Net.CodeAnalysis.Jass/Syntax/JassFunctionDeclaratorSyntax.cs @@ -5,33 +5,198 @@ // // ------------------------------------------------------------------------------ -using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.IO; + +using War3Net.CodeAnalysis.Jass.Extensions; namespace War3Net.CodeAnalysis.Jass.Syntax { - public class JassFunctionDeclaratorSyntax : IEquatable + public class JassFunctionDeclaratorSyntax : JassSyntaxNode { - public JassFunctionDeclaratorSyntax(JassIdentifierNameSyntax identifierName, JassParameterListSyntax parameterList, JassTypeSyntax returnType) + internal JassFunctionDeclaratorSyntax( + JassSyntaxToken? constantToken, + JassSyntaxToken functionToken, + JassIdentifierNameSyntax identifierName, + JassParameterListOrEmptyParameterListSyntax parameterList, + JassReturnClauseSyntax returnClause) { + ConstantToken = constantToken; + FunctionToken = functionToken; IdentifierName = identifierName; ParameterList = parameterList; - ReturnType = returnType; + ReturnClause = returnClause; + } + + public JassSyntaxToken? ConstantToken { get; } + + public JassSyntaxToken FunctionToken { get; } + + public JassIdentifierNameSyntax IdentifierName { get; } + + public JassParameterListOrEmptyParameterListSyntax ParameterList { get; } + + public JassReturnClauseSyntax ReturnClause { get; } + + public override JassSyntaxKind SyntaxKind => JassSyntaxKind.FunctionDeclarator; + + public override bool IsEquivalentTo([NotNullWhen(true)] JassSyntaxNode? other) + { + return other is JassFunctionDeclaratorSyntax functionDeclarator + && ConstantToken.NullableEquals(functionDeclarator.ConstantToken) + && IdentifierName.IsEquivalentTo(functionDeclarator.IdentifierName) + && ParameterList.IsEquivalentTo(functionDeclarator.ParameterList) + && ReturnClause.IsEquivalentTo(functionDeclarator.ReturnClause); + } + + public override void WriteTo(TextWriter writer) + { + ConstantToken?.WriteTo(writer); + FunctionToken.WriteTo(writer); + IdentifierName.WriteTo(writer); + ParameterList.WriteTo(writer); + ReturnClause.WriteTo(writer); + } + + public override IEnumerable GetChildNodes() + { + yield return IdentifierName; + yield return ParameterList; + yield return ReturnClause; + } + + public override IEnumerable GetChildTokens() + { + if (ConstantToken is not null) + { + yield return ConstantToken; + } + + yield return FunctionToken; + } + + public override IEnumerable GetChildNodesAndTokens() + { + if (ConstantToken is not null) + { + yield return ConstantToken; + } + + yield return FunctionToken; + yield return IdentifierName; + yield return ParameterList; + yield return ReturnClause; } - public JassIdentifierNameSyntax IdentifierName { get; init; } + public override IEnumerable GetDescendantNodes() + { + yield return IdentifierName; + foreach (var descendant in IdentifierName.GetDescendantNodes()) + { + yield return descendant; + } - public JassParameterListSyntax ParameterList { get; init; } + yield return ParameterList; + foreach (var descendant in ParameterList.GetDescendantNodes()) + { + yield return descendant; + } - public JassTypeSyntax ReturnType { get; init; } + yield return ReturnClause; + foreach (var descendant in ReturnClause.GetDescendantNodes()) + { + yield return descendant; + } + } - public bool Equals(JassFunctionDeclaratorSyntax? other) + public override IEnumerable GetDescendantTokens() { - return other is not null - && IdentifierName.Equals(other.IdentifierName) - && ParameterList.Equals(other.ParameterList) - && ReturnType.Equals(other.ReturnType); + if (ConstantToken is not null) + { + yield return ConstantToken; + } + + yield return FunctionToken; + + foreach (var descendant in IdentifierName.GetDescendantTokens()) + { + yield return descendant; + } + + foreach (var descendant in ParameterList.GetDescendantTokens()) + { + yield return descendant; + } + + foreach (var descendant in ReturnClause.GetDescendantTokens()) + { + yield return descendant; + } } - public override string ToString() => $"{IdentifierName} {JassKeyword.Takes} {ParameterList} {JassKeyword.Returns} {ReturnType}"; + public override IEnumerable GetDescendantNodesAndTokens() + { + if (ConstantToken is not null) + { + yield return ConstantToken; + } + + yield return FunctionToken; + + yield return IdentifierName; + foreach (var descendant in IdentifierName.GetDescendantNodesAndTokens()) + { + yield return descendant; + } + + yield return ParameterList; + foreach (var descendant in ParameterList.GetDescendantNodesAndTokens()) + { + yield return descendant; + } + + yield return ReturnClause; + foreach (var descendant in ReturnClause.GetDescendantNodesAndTokens()) + { + yield return descendant; + } + } + + public override string ToString() => $"{ConstantToken.OptionalSuffixed()}{FunctionToken} {IdentifierName} {ParameterList} {ReturnClause}"; + + public override JassSyntaxToken GetFirstToken() => ConstantToken ?? FunctionToken; + + public override JassSyntaxToken GetLastToken() => ReturnClause.GetLastToken(); + + protected internal override JassFunctionDeclaratorSyntax ReplaceFirstToken(JassSyntaxToken newToken) + { + if (ConstantToken is not null) + { + return new JassFunctionDeclaratorSyntax( + newToken, + FunctionToken, + IdentifierName, + ParameterList, + ReturnClause); + } + + return new JassFunctionDeclaratorSyntax( + null, + newToken, + IdentifierName, + ParameterList, + ReturnClause); + } + + protected internal override JassFunctionDeclaratorSyntax ReplaceLastToken(JassSyntaxToken newToken) + { + return new JassFunctionDeclaratorSyntax( + ConstantToken, + FunctionToken, + IdentifierName, + ParameterList, + ReturnClause.ReplaceLastToken(newToken)); + } } } \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Syntax/JassFunctionReferenceExpressionSyntax.cs b/src/War3Net.CodeAnalysis.Jass/Syntax/JassFunctionReferenceExpressionSyntax.cs index 55104b34..87373a22 100644 --- a/src/War3Net.CodeAnalysis.Jass/Syntax/JassFunctionReferenceExpressionSyntax.cs +++ b/src/War3Net.CodeAnalysis.Jass/Syntax/JassFunctionReferenceExpressionSyntax.cs @@ -5,23 +5,104 @@ // // ------------------------------------------------------------------------------ +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.IO; + namespace War3Net.CodeAnalysis.Jass.Syntax { - public class JassFunctionReferenceExpressionSyntax : IExpressionSyntax + public class JassFunctionReferenceExpressionSyntax : JassExpressionSyntax { - public JassFunctionReferenceExpressionSyntax(JassIdentifierNameSyntax identifierName) + internal JassFunctionReferenceExpressionSyntax( + JassSyntaxToken functionToken, + JassIdentifierNameSyntax identifierName) { + FunctionToken = functionToken; IdentifierName = identifierName; } - public JassIdentifierNameSyntax IdentifierName { get; init; } + public JassSyntaxToken FunctionToken { get; } + + public JassIdentifierNameSyntax IdentifierName { get; } + + public override JassSyntaxKind SyntaxKind => JassSyntaxKind.FunctionReferenceExpression; - public bool Equals(IExpressionSyntax? other) + public override bool IsEquivalentTo([NotNullWhen(true)] JassSyntaxNode? other) { return other is JassFunctionReferenceExpressionSyntax functionReferenceExpression - && IdentifierName.Equals(functionReferenceExpression.IdentifierName); + && IdentifierName.IsEquivalentTo(functionReferenceExpression.IdentifierName); + } + + public override void WriteTo(TextWriter writer) + { + FunctionToken.WriteTo(writer); + IdentifierName.WriteTo(writer); + } + + public override IEnumerable GetChildNodes() + { + yield return IdentifierName; + } + + public override IEnumerable GetChildTokens() + { + yield return FunctionToken; + } + + public override IEnumerable GetChildNodesAndTokens() + { + yield return FunctionToken; + yield return IdentifierName; + } + + public override IEnumerable GetDescendantNodes() + { + yield return IdentifierName; + foreach (var descendant in IdentifierName.GetDescendantNodes()) + { + yield return descendant; + } + } + + public override IEnumerable GetDescendantTokens() + { + yield return FunctionToken; + + foreach (var descendant in IdentifierName.GetDescendantTokens()) + { + yield return descendant; + } + } + + public override IEnumerable GetDescendantNodesAndTokens() + { + yield return FunctionToken; + + yield return IdentifierName; + foreach (var descendant in IdentifierName.GetDescendantNodesAndTokens()) + { + yield return descendant; + } } - public override string ToString() => $"{JassKeyword.Function} {IdentifierName}"; + public override string ToString() => $"{FunctionToken} {IdentifierName}"; + + public override JassSyntaxToken GetFirstToken() => FunctionToken; + + public override JassSyntaxToken GetLastToken() => IdentifierName.GetLastToken(); + + protected internal override JassFunctionReferenceExpressionSyntax ReplaceFirstToken(JassSyntaxToken newToken) + { + return new JassFunctionReferenceExpressionSyntax( + newToken, + IdentifierName); + } + + protected internal override JassFunctionReferenceExpressionSyntax ReplaceLastToken(JassSyntaxToken newToken) + { + return new JassFunctionReferenceExpressionSyntax( + FunctionToken, + IdentifierName.ReplaceLastToken(newToken)); + } } } \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Syntax/JassGlobalConstantDeclarationSyntax.cs b/src/War3Net.CodeAnalysis.Jass/Syntax/JassGlobalConstantDeclarationSyntax.cs new file mode 100644 index 00000000..09644177 --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/Syntax/JassGlobalConstantDeclarationSyntax.cs @@ -0,0 +1,162 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.IO; + +namespace War3Net.CodeAnalysis.Jass.Syntax +{ + public class JassGlobalConstantDeclarationSyntax : JassGlobalDeclarationSyntax + { + internal JassGlobalConstantDeclarationSyntax( + JassSyntaxToken constantToken, + JassTypeSyntax type, + JassIdentifierNameSyntax identifierName, + JassEqualsValueClauseSyntax value) + { + ConstantToken = constantToken; + Type = type; + IdentifierName = identifierName; + Value = value; + } + + public JassSyntaxToken ConstantToken { get; } + + public JassTypeSyntax Type { get; } + + public JassIdentifierNameSyntax IdentifierName { get; } + + public JassEqualsValueClauseSyntax Value { get; } + + public override JassSyntaxKind SyntaxKind => JassSyntaxKind.GlobalConstantDeclaration; + + public override bool IsEquivalentTo([NotNullWhen(true)] JassSyntaxNode? other) + { + return other is JassGlobalConstantDeclarationSyntax globalConstantDeclaration + && Type.IsEquivalentTo(globalConstantDeclaration.Type) + && IdentifierName.IsEquivalentTo(globalConstantDeclaration.IdentifierName) + && Value.IsEquivalentTo(globalConstantDeclaration.Value); + } + + public override void WriteTo(TextWriter writer) + { + ConstantToken.WriteTo(writer); + Type.WriteTo(writer); + IdentifierName.WriteTo(writer); + Value.WriteTo(writer); + } + + public override IEnumerable GetChildNodes() + { + yield return Type; + yield return IdentifierName; + yield return Value; + } + + public override IEnumerable GetChildTokens() + { + yield return ConstantToken; + } + + public override IEnumerable GetChildNodesAndTokens() + { + yield return ConstantToken; + yield return Type; + yield return IdentifierName; + yield return Value; + } + + public override IEnumerable GetDescendantNodes() + { + yield return Type; + foreach (var descendant in Type.GetDescendantNodes()) + { + yield return descendant; + } + + yield return IdentifierName; + foreach (var descendant in IdentifierName.GetDescendantNodes()) + { + yield return descendant; + } + + yield return Value; + foreach (var descendant in Value.GetDescendantNodes()) + { + yield return descendant; + } + } + + public override IEnumerable GetDescendantTokens() + { + yield return ConstantToken; + + foreach (var descendant in Type.GetDescendantTokens()) + { + yield return descendant; + } + + foreach (var descendant in IdentifierName.GetDescendantTokens()) + { + yield return descendant; + } + + foreach (var descendant in Value.GetDescendantTokens()) + { + yield return descendant; + } + } + + public override IEnumerable GetDescendantNodesAndTokens() + { + yield return ConstantToken; + + yield return Type; + foreach (var descendant in Type.GetDescendantNodesAndTokens()) + { + yield return descendant; + } + + yield return IdentifierName; + foreach (var descendant in IdentifierName.GetDescendantNodesAndTokens()) + { + yield return descendant; + } + + yield return Value; + foreach (var descendant in Value.GetDescendantNodesAndTokens()) + { + yield return descendant; + } + } + + public override string ToString() => $"{ConstantToken} {Type} {IdentifierName} {Value}"; + + public override JassSyntaxToken GetFirstToken() => ConstantToken; + + public override JassSyntaxToken GetLastToken() => Value.GetLastToken(); + + protected internal override JassGlobalConstantDeclarationSyntax ReplaceFirstToken(JassSyntaxToken newToken) + { + return new JassGlobalConstantDeclarationSyntax( + newToken, + Type, + IdentifierName, + Value); + } + + protected internal override JassGlobalConstantDeclarationSyntax ReplaceLastToken(JassSyntaxToken newToken) + { + return new JassGlobalConstantDeclarationSyntax( + ConstantToken, + Type, + IdentifierName, + Value.ReplaceLastToken(newToken)); + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Syntax/JassGlobalDeclarationListSyntax.cs b/src/War3Net.CodeAnalysis.Jass/Syntax/JassGlobalDeclarationListSyntax.cs deleted file mode 100644 index 3474a5ac..00000000 --- a/src/War3Net.CodeAnalysis.Jass/Syntax/JassGlobalDeclarationListSyntax.cs +++ /dev/null @@ -1,30 +0,0 @@ -// ------------------------------------------------------------------------------ -// -// Licensed under the MIT license. -// See the LICENSE file in the project root for more information. -// -// ------------------------------------------------------------------------------ - -using System.Collections.Immutable; -using System.Linq; - -namespace War3Net.CodeAnalysis.Jass.Syntax -{ - public class JassGlobalDeclarationListSyntax : ITopLevelDeclarationSyntax - { - public JassGlobalDeclarationListSyntax(ImmutableArray globals) - { - Globals = globals; - } - - public ImmutableArray Globals { get; init; } - - public bool Equals(ITopLevelDeclarationSyntax? other) - { - return other is JassGlobalDeclarationListSyntax globalDeclarationList - && Globals.SequenceEqual(globalDeclarationList.Globals); - } - - public override string ToString() => $"{JassKeyword.Globals} [{Globals.Length}]"; - } -} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Syntax/JassGlobalDeclarationSyntax.cs b/src/War3Net.CodeAnalysis.Jass/Syntax/JassGlobalDeclarationSyntax.cs index 3d432e0b..249f6ba8 100644 --- a/src/War3Net.CodeAnalysis.Jass/Syntax/JassGlobalDeclarationSyntax.cs +++ b/src/War3Net.CodeAnalysis.Jass/Syntax/JassGlobalDeclarationSyntax.cs @@ -7,27 +7,10 @@ namespace War3Net.CodeAnalysis.Jass.Syntax { - public class JassGlobalDeclarationSyntax : IGlobalDeclarationSyntax, IGlobalLineSyntax + public abstract class JassGlobalDeclarationSyntax : JassSyntaxNode { - public JassGlobalDeclarationSyntax(IVariableDeclaratorSyntax declarator) - { - Declarator = declarator; - } + protected internal override abstract JassGlobalDeclarationSyntax ReplaceFirstToken(JassSyntaxToken newToken); - public IVariableDeclaratorSyntax Declarator { get; init; } - - public bool Equals(IGlobalDeclarationSyntax? other) - { - return other is JassGlobalDeclarationSyntax globalDeclaration - && Declarator.Equals(globalDeclaration.Declarator); - } - - public bool Equals(IGlobalLineSyntax? other) - { - return other is JassGlobalDeclarationSyntax globalDeclaration - && Declarator.Equals(globalDeclaration.Declarator); - } - - public override string ToString() => Declarator.ToString(); + protected internal override abstract JassGlobalDeclarationSyntax ReplaceLastToken(JassSyntaxToken newToken); } } \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Syntax/JassGlobalVariableDeclarationSyntax.cs b/src/War3Net.CodeAnalysis.Jass/Syntax/JassGlobalVariableDeclarationSyntax.cs new file mode 100644 index 00000000..b185463e --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/Syntax/JassGlobalVariableDeclarationSyntax.cs @@ -0,0 +1,91 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.IO; + +namespace War3Net.CodeAnalysis.Jass.Syntax +{ + public class JassGlobalVariableDeclarationSyntax : JassGlobalDeclarationSyntax + { + internal JassGlobalVariableDeclarationSyntax( + JassVariableOrArrayDeclaratorSyntax declarator) + { + Declarator = declarator; + } + + public JassVariableOrArrayDeclaratorSyntax Declarator { get; } + + public override JassSyntaxKind SyntaxKind => JassSyntaxFacts.GetGlobalDeclarationKind(Declarator.SyntaxKind); + + public override bool IsEquivalentTo([NotNullWhen(true)] JassSyntaxNode? other) + { + return other is JassGlobalVariableDeclarationSyntax globalVariableDeclaration + && Declarator.IsEquivalentTo(globalVariableDeclaration.Declarator); + } + + public override void WriteTo(TextWriter writer) + { + Declarator.WriteTo(writer); + } + + public override IEnumerable GetChildNodes() + { + yield return Declarator; + } + + public override IEnumerable GetChildTokens() + { + yield break; + } + + public override IEnumerable GetChildNodesAndTokens() + { + yield return Declarator; + } + + public override IEnumerable GetDescendantNodes() + { + yield return Declarator; + foreach (var descendant in Declarator.GetDescendantNodes()) + { + yield return descendant; + } + } + + public override IEnumerable GetDescendantTokens() + { + return Declarator.GetDescendantTokens(); + } + + public override IEnumerable GetDescendantNodesAndTokens() + { + yield return Declarator; + foreach (var descendant in Declarator.GetDescendantNodesAndTokens()) + { + yield return descendant; + } + } + + public override string ToString() => Declarator.ToString(); + + public override JassSyntaxToken GetFirstToken() => Declarator.GetFirstToken(); + + public override JassSyntaxToken GetLastToken() => Declarator.GetLastToken(); + + protected internal override JassGlobalVariableDeclarationSyntax ReplaceFirstToken(JassSyntaxToken newToken) + { + return new JassGlobalVariableDeclarationSyntax(Declarator.ReplaceFirstToken(newToken)); + } + + protected internal override JassGlobalVariableDeclarationSyntax ReplaceLastToken(JassSyntaxToken newToken) + { + return new JassGlobalVariableDeclarationSyntax(Declarator.ReplaceLastToken(newToken)); + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Syntax/JassGlobalsCustomScriptAction.cs b/src/War3Net.CodeAnalysis.Jass/Syntax/JassGlobalsCustomScriptAction.cs deleted file mode 100644 index 1b4331ba..00000000 --- a/src/War3Net.CodeAnalysis.Jass/Syntax/JassGlobalsCustomScriptAction.cs +++ /dev/null @@ -1,22 +0,0 @@ -// ------------------------------------------------------------------------------ -// -// Licensed under the MIT license. -// See the LICENSE file in the project root for more information. -// -// ------------------------------------------------------------------------------ - -namespace War3Net.CodeAnalysis.Jass.Syntax -{ - public class JassGlobalsCustomScriptAction : IDeclarationLineSyntax - { - public static readonly JassGlobalsCustomScriptAction Value = new JassGlobalsCustomScriptAction(); - - private JassGlobalsCustomScriptAction() - { - } - - public bool Equals(IDeclarationLineSyntax? other) => other is JassGlobalsCustomScriptAction; - - public override string ToString() => JassKeyword.Globals; - } -} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Syntax/JassGlobalsDeclarationSyntax.cs b/src/War3Net.CodeAnalysis.Jass/Syntax/JassGlobalsDeclarationSyntax.cs new file mode 100644 index 00000000..5933385e --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/Syntax/JassGlobalsDeclarationSyntax.cs @@ -0,0 +1,124 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Diagnostics.CodeAnalysis; +using System.IO; + +using War3Net.CodeAnalysis.Jass.Extensions; + +namespace War3Net.CodeAnalysis.Jass.Syntax +{ + public class JassGlobalsDeclarationSyntax : JassTopLevelDeclarationSyntax + { + internal JassGlobalsDeclarationSyntax( + JassSyntaxToken globalsToken, + ImmutableArray globalDeclarations, + JassSyntaxToken endGlobalsToken) + { + GlobalsToken = globalsToken; + GlobalDeclarations = globalDeclarations; + EndGlobalsToken = endGlobalsToken; + } + + public JassSyntaxToken GlobalsToken { get; } + + public ImmutableArray GlobalDeclarations { get; } + + public JassSyntaxToken EndGlobalsToken { get; } + + public override JassSyntaxKind SyntaxKind => JassSyntaxKind.GlobalsDeclaration; + + public override bool IsEquivalentTo([NotNullWhen(true)] JassSyntaxNode? other) + { + return other is JassGlobalsDeclarationSyntax globalsDeclaration + && GlobalDeclarations.IsEquivalentTo(globalsDeclaration.GlobalDeclarations); + } + + public override void WriteTo(TextWriter writer) + { + GlobalsToken.WriteTo(writer); + GlobalDeclarations.WriteTo(writer); + EndGlobalsToken.WriteTo(writer); + } + + public override IEnumerable GetChildNodes() + { + return GlobalDeclarations; + } + + public override IEnumerable GetChildTokens() + { + yield return GlobalsToken; + yield return EndGlobalsToken; + } + + public override IEnumerable GetChildNodesAndTokens() + { + yield return GlobalsToken; + + foreach (var child in GlobalDeclarations) + { + yield return child; + } + + yield return EndGlobalsToken; + } + + public override IEnumerable GetDescendantNodes() + { + return GlobalDeclarations.GetDescendantNodes(); + } + + public override IEnumerable GetDescendantTokens() + { + yield return GlobalsToken; + + foreach (var descendant in GlobalDeclarations.GetDescendantTokens()) + { + yield return descendant; + } + + yield return EndGlobalsToken; + } + + public override IEnumerable GetDescendantNodesAndTokens() + { + yield return GlobalsToken; + + foreach (var descendant in GlobalDeclarations.GetDescendantNodesAndTokens()) + { + yield return descendant; + } + + yield return EndGlobalsToken; + } + + public override string ToString() => $"{GlobalsToken} [...]"; + + public override JassSyntaxToken GetFirstToken() => GlobalsToken; + + public override JassSyntaxToken GetLastToken() => EndGlobalsToken; + + protected internal override JassGlobalsDeclarationSyntax ReplaceFirstToken(JassSyntaxToken newToken) + { + return new JassGlobalsDeclarationSyntax( + newToken, + GlobalDeclarations, + EndGlobalsToken); + } + + protected internal override JassGlobalsDeclarationSyntax ReplaceLastToken(JassSyntaxToken newToken) + { + return new JassGlobalsDeclarationSyntax( + GlobalsToken, + GlobalDeclarations, + newToken); + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Syntax/JassHexadecimalLiteralExpressionSyntax.cs b/src/War3Net.CodeAnalysis.Jass/Syntax/JassHexadecimalLiteralExpressionSyntax.cs deleted file mode 100644 index d3a88dab..00000000 --- a/src/War3Net.CodeAnalysis.Jass/Syntax/JassHexadecimalLiteralExpressionSyntax.cs +++ /dev/null @@ -1,29 +0,0 @@ -// ------------------------------------------------------------------------------ -// -// Licensed under the MIT license. -// See the LICENSE file in the project root for more information. -// -// ------------------------------------------------------------------------------ - -using System; - -namespace War3Net.CodeAnalysis.Jass.Syntax -{ - public class JassHexadecimalLiteralExpressionSyntax : IExpressionSyntax - { - public JassHexadecimalLiteralExpressionSyntax(int value) - { - Value = value; - } - - public int Value { get; init; } - - public bool Equals(IExpressionSyntax? other) - { - return other is JassHexadecimalLiteralExpressionSyntax hexadecimalLiteralExpression - && Value == hexadecimalLiteralExpression.Value; - } - - public override string ToString() => $"{JassSymbol.Zero}{JassSymbol.X}" + Convert.ToString(Value, 16).ToUpperInvariant(); - } -} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Syntax/JassIdentifierNameSyntax.cs b/src/War3Net.CodeAnalysis.Jass/Syntax/JassIdentifierNameSyntax.cs index 8ca91e87..da06ba96 100644 --- a/src/War3Net.CodeAnalysis.Jass/Syntax/JassIdentifierNameSyntax.cs +++ b/src/War3Net.CodeAnalysis.Jass/Syntax/JassIdentifierNameSyntax.cs @@ -5,25 +5,79 @@ // // ------------------------------------------------------------------------------ -using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.IO; namespace War3Net.CodeAnalysis.Jass.Syntax { - public class JassIdentifierNameSyntax : IEquatable + public class JassIdentifierNameSyntax : JassTypeSyntax { - public JassIdentifierNameSyntax(string name) + internal JassIdentifierNameSyntax( + JassSyntaxToken token) { - Name = name; + Token = token; } - public string Name { get; init; } + public JassSyntaxToken Token { get; } - public virtual bool Equals(JassIdentifierNameSyntax? other) + public override JassSyntaxKind SyntaxKind => JassSyntaxKind.IdentifierName; + + public override bool IsEquivalentTo([NotNullWhen(true)] JassSyntaxNode? other) + { + return other is JassIdentifierNameSyntax identifierName + && Token.IsEquivalentTo(identifierName.Token); + } + + public override void WriteTo(TextWriter writer) + { + Token.WriteTo(writer); + } + + public override IEnumerable GetChildNodes() + { + yield break; + } + + public override IEnumerable GetChildTokens() + { + yield return Token; + } + + public override IEnumerable GetChildNodesAndTokens() + { + yield return Token; + } + + public override IEnumerable GetDescendantNodes() + { + yield break; + } + + public override IEnumerable GetDescendantTokens() { - return other is not null - && string.Equals(Name, other.Name, StringComparison.Ordinal); + yield return Token; } - public override string ToString() => Name; + public override IEnumerable GetDescendantNodesAndTokens() + { + yield return Token; + } + + public override string ToString() => Token.ToString(); + + public override JassSyntaxToken GetFirstToken() => Token; + + public override JassSyntaxToken GetLastToken() => Token; + + protected internal override JassIdentifierNameSyntax ReplaceFirstToken(JassSyntaxToken newToken) + { + return new JassIdentifierNameSyntax(newToken); + } + + protected internal override JassIdentifierNameSyntax ReplaceLastToken(JassSyntaxToken newToken) + { + return new JassIdentifierNameSyntax(newToken); + } } } \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Syntax/JassIfClauseDeclaratorSyntax.cs b/src/War3Net.CodeAnalysis.Jass/Syntax/JassIfClauseDeclaratorSyntax.cs new file mode 100644 index 00000000..73d815c7 --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/Syntax/JassIfClauseDeclaratorSyntax.cs @@ -0,0 +1,121 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.IO; + +namespace War3Net.CodeAnalysis.Jass.Syntax +{ + public class JassIfClauseDeclaratorSyntax : JassSyntaxNode + { + internal JassIfClauseDeclaratorSyntax( + JassSyntaxToken ifToken, + JassExpressionSyntax condition, + JassSyntaxToken thenToken) + { + IfToken = ifToken; + Condition = condition; + ThenToken = thenToken; + } + + public JassSyntaxToken IfToken { get; } + + public JassExpressionSyntax Condition { get; } + + public JassSyntaxToken ThenToken { get; } + + public override JassSyntaxKind SyntaxKind => JassSyntaxKind.IfClauseDeclarator; + + public override bool IsEquivalentTo([NotNullWhen(true)] JassSyntaxNode? other) + { + return other is JassIfClauseDeclaratorSyntax ifClauseDeclarator + && Condition.IsEquivalentTo(ifClauseDeclarator.Condition); + } + + public override void WriteTo(TextWriter writer) + { + IfToken.WriteTo(writer); + Condition.WriteTo(writer); + ThenToken.WriteTo(writer); + } + + public override IEnumerable GetChildNodes() + { + yield return Condition; + } + + public override IEnumerable GetChildTokens() + { + yield return IfToken; + yield return ThenToken; + } + + public override IEnumerable GetChildNodesAndTokens() + { + yield return IfToken; + yield return Condition; + yield return ThenToken; + } + + public override IEnumerable GetDescendantNodes() + { + yield return Condition; + foreach (var descendant in Condition.GetDescendantNodes()) + { + yield return descendant; + } + } + + public override IEnumerable GetDescendantTokens() + { + yield return IfToken; + + foreach (var descendant in Condition.GetDescendantTokens()) + { + yield return descendant; + } + + yield return ThenToken; + } + + public override IEnumerable GetDescendantNodesAndTokens() + { + yield return IfToken; + + yield return Condition; + foreach (var descendant in Condition.GetDescendantNodesAndTokens()) + { + yield return descendant; + } + + yield return ThenToken; + } + + public override string ToString() => $"{IfToken} {Condition} {ThenToken}"; + + public override JassSyntaxToken GetFirstToken() => IfToken; + + public override JassSyntaxToken GetLastToken() => ThenToken; + + protected internal override JassIfClauseDeclaratorSyntax ReplaceFirstToken(JassSyntaxToken newToken) + { + return new JassIfClauseDeclaratorSyntax( + newToken, + Condition, + ThenToken); + } + + protected internal override JassIfClauseDeclaratorSyntax ReplaceLastToken(JassSyntaxToken newToken) + { + return new JassIfClauseDeclaratorSyntax( + IfToken, + Condition, + newToken); + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Syntax/JassIfClauseSyntax.cs b/src/War3Net.CodeAnalysis.Jass/Syntax/JassIfClauseSyntax.cs new file mode 100644 index 00000000..c27594a8 --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/Syntax/JassIfClauseSyntax.cs @@ -0,0 +1,137 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Diagnostics.CodeAnalysis; +using System.IO; + +using War3Net.CodeAnalysis.Jass.Extensions; + +namespace War3Net.CodeAnalysis.Jass.Syntax +{ + public class JassIfClauseSyntax : JassSyntaxNode + { + internal JassIfClauseSyntax( + JassIfClauseDeclaratorSyntax ifClauseDeclarator, + ImmutableArray statements) + { + IfClauseDeclarator = ifClauseDeclarator; + Statements = statements; + } + + public JassIfClauseDeclaratorSyntax IfClauseDeclarator { get; } + + public ImmutableArray Statements { get; } + + public override JassSyntaxKind SyntaxKind => JassSyntaxKind.IfClause; + + public override bool IsEquivalentTo([NotNullWhen(true)] JassSyntaxNode? other) + { + return other is JassIfClauseSyntax ifClause + && IfClauseDeclarator.IsEquivalentTo(ifClause.IfClauseDeclarator) + && Statements.IsEquivalentTo(ifClause.Statements); + } + + public override void WriteTo(TextWriter writer) + { + IfClauseDeclarator.WriteTo(writer); + Statements.WriteTo(writer); + } + + public override IEnumerable GetChildNodes() + { + yield return IfClauseDeclarator; + foreach (var child in Statements) + { + yield return child; + } + } + + public override IEnumerable GetChildTokens() + { + yield break; + } + + public override IEnumerable GetChildNodesAndTokens() + { + yield return IfClauseDeclarator; + foreach (var child in Statements) + { + yield return child; + } + } + + public override IEnumerable GetDescendantNodes() + { + yield return IfClauseDeclarator; + foreach (var descendant in IfClauseDeclarator.GetDescendantNodes()) + { + yield return descendant; + } + + foreach (var descendant in Statements.GetDescendantNodes()) + { + yield return descendant; + } + } + + public override IEnumerable GetDescendantTokens() + { + foreach (var descendant in IfClauseDeclarator.GetDescendantTokens()) + { + yield return descendant; + } + + foreach (var descendant in Statements.GetDescendantTokens()) + { + yield return descendant; + } + } + + public override IEnumerable GetDescendantNodesAndTokens() + { + yield return IfClauseDeclarator; + foreach (var descendant in IfClauseDeclarator.GetDescendantNodesAndTokens()) + { + yield return descendant; + } + + foreach (var descendant in Statements.GetDescendantNodesAndTokens()) + { + yield return descendant; + } + } + + public override string ToString() => $"{IfClauseDeclarator} [...]"; + + public override JassSyntaxToken GetFirstToken() => IfClauseDeclarator.GetFirstToken(); + + public override JassSyntaxToken GetLastToken() => Statements.IsEmpty ? IfClauseDeclarator.GetLastToken() : Statements[^1].GetLastToken(); + + protected internal override JassIfClauseSyntax ReplaceFirstToken(JassSyntaxToken newToken) + { + return new JassIfClauseSyntax( + IfClauseDeclarator.ReplaceFirstToken(newToken), + Statements); + } + + protected internal override JassIfClauseSyntax ReplaceLastToken(JassSyntaxToken newToken) + { + if (!Statements.IsEmpty) + { + return new JassIfClauseSyntax( + IfClauseDeclarator, + Statements.ReplaceLastItem(Statements[^1].ReplaceLastToken(newToken))); + } + + return new JassIfClauseSyntax( + IfClauseDeclarator.ReplaceLastToken(newToken), + Statements); + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Syntax/JassIfCustomScriptAction.cs b/src/War3Net.CodeAnalysis.Jass/Syntax/JassIfCustomScriptAction.cs deleted file mode 100644 index a8e66c6d..00000000 --- a/src/War3Net.CodeAnalysis.Jass/Syntax/JassIfCustomScriptAction.cs +++ /dev/null @@ -1,27 +0,0 @@ -// ------------------------------------------------------------------------------ -// -// Licensed under the MIT license. -// See the LICENSE file in the project root for more information. -// -// ------------------------------------------------------------------------------ - -namespace War3Net.CodeAnalysis.Jass.Syntax -{ - public class JassIfCustomScriptAction : IStatementLineSyntax - { - public JassIfCustomScriptAction(IExpressionSyntax condition) - { - Condition = condition; - } - - public IExpressionSyntax Condition { get; init; } - - public bool Equals(IStatementLineSyntax? other) - { - return other is JassIfCustomScriptAction @if - && Condition.Equals(@if.Condition); - } - - public override string ToString() => $"{JassKeyword.If} {Condition} {JassKeyword.Then}"; - } -} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Syntax/JassIfStatementSyntax.cs b/src/War3Net.CodeAnalysis.Jass/Syntax/JassIfStatementSyntax.cs index 51aa2129..39ae4216 100644 --- a/src/War3Net.CodeAnalysis.Jass/Syntax/JassIfStatementSyntax.cs +++ b/src/War3Net.CodeAnalysis.Jass/Syntax/JassIfStatementSyntax.cs @@ -5,41 +5,185 @@ // // ------------------------------------------------------------------------------ -using System; +using System.Collections.Generic; using System.Collections.Immutable; -using System.Linq; +using System.Diagnostics.CodeAnalysis; +using System.IO; using War3Net.CodeAnalysis.Jass.Extensions; namespace War3Net.CodeAnalysis.Jass.Syntax { - public class JassIfStatementSyntax : IStatementSyntax + public class JassIfStatementSyntax : JassStatementSyntax { - public JassIfStatementSyntax(IExpressionSyntax condition, JassStatementListSyntax body, ImmutableArray elseIfClauses, JassElseClauseSyntax? elseClause) + internal JassIfStatementSyntax( + JassIfClauseSyntax ifClause, + ImmutableArray elseIfClauses, + JassElseClauseSyntax? elseClause, + JassSyntaxToken endIfToken) { - Condition = condition; - Body = body; + IfClause = ifClause; ElseIfClauses = elseIfClauses; ElseClause = elseClause; + EndIfToken = endIfToken; } - public IExpressionSyntax Condition { get; init; } + public JassIfClauseSyntax IfClause { get; } - public JassStatementListSyntax Body { get; init; } + public ImmutableArray ElseIfClauses { get; } - public ImmutableArray ElseIfClauses { get; init; } + public JassElseClauseSyntax? ElseClause { get; } - public JassElseClauseSyntax? ElseClause { get; init; } + public JassSyntaxToken EndIfToken { get; } - public bool Equals(IStatementSyntax? other) + public override JassSyntaxKind SyntaxKind => JassSyntaxKind.IfStatement; + + public override bool IsEquivalentTo([NotNullWhen(true)] JassSyntaxNode? other) { return other is JassIfStatementSyntax ifStatement - && Condition.Equals(ifStatement.Condition) - && Body.Equals(ifStatement.Body) - && ElseIfClauses.SequenceEqual(ifStatement.ElseIfClauses) - && ElseClause.NullableEquals(ifStatement.ElseClause); + && IfClause.IsEquivalentTo(ifStatement.IfClause) + && ElseIfClauses.IsEquivalentTo(ifStatement.ElseIfClauses) + && ElseClause.NullableEquivalentTo(ifStatement.ElseClause); + } + + public override void WriteTo(TextWriter writer) + { + IfClause.WriteTo(writer); + ElseIfClauses.WriteTo(writer); + ElseClause?.WriteTo(writer); + EndIfToken.WriteTo(writer); + } + + public override IEnumerable GetChildNodes() + { + yield return IfClause; + + foreach (var child in ElseIfClauses) + { + yield return child; + } + + if (ElseClause is not null) + { + yield return ElseClause; + } + } + + public override IEnumerable GetChildTokens() + { + yield return EndIfToken; } - public override string ToString() => $"{JassKeyword.If} {Condition} {JassKeyword.Then} [{Body.Statements.Length}]"; + public override IEnumerable GetChildNodesAndTokens() + { + yield return IfClause; + + foreach (var child in ElseIfClauses) + { + yield return child; + } + + if (ElseClause is not null) + { + yield return ElseClause; + } + + yield return EndIfToken; + } + + public override IEnumerable GetDescendantNodes() + { + yield return IfClause; + foreach (var descendant in IfClause.GetDescendantNodes()) + { + yield return descendant; + } + + foreach (var descendant in ElseIfClauses.GetDescendantNodes()) + { + yield return descendant; + } + + if (ElseClause is not null) + { + yield return ElseClause; + foreach (var descendant in ElseClause.GetDescendantNodes()) + { + yield return descendant; + } + } + } + + public override IEnumerable GetDescendantTokens() + { + foreach (var descendant in IfClause.GetDescendantTokens()) + { + yield return descendant; + } + + foreach (var descendant in ElseIfClauses.GetDescendantTokens()) + { + yield return descendant; + } + + if (ElseClause is not null) + { + foreach (var descendant in ElseClause.GetDescendantTokens()) + { + yield return descendant; + } + } + + yield return EndIfToken; + } + + public override IEnumerable GetDescendantNodesAndTokens() + { + yield return IfClause; + foreach (var descendant in IfClause.GetDescendantNodesAndTokens()) + { + yield return descendant; + } + + foreach (var descendant in ElseIfClauses.GetDescendantNodesAndTokens()) + { + yield return descendant; + } + + if (ElseClause is not null) + { + yield return ElseClause; + foreach (var descendant in ElseClause.GetDescendantNodesAndTokens()) + { + yield return descendant; + } + } + + yield return EndIfToken; + } + + public override string ToString() => IfClause.ToString(); + + public override JassSyntaxToken GetFirstToken() => IfClause.GetFirstToken(); + + public override JassSyntaxToken GetLastToken() => EndIfToken; + + protected internal override JassIfStatementSyntax ReplaceFirstToken(JassSyntaxToken newToken) + { + return new JassIfStatementSyntax( + IfClause.ReplaceFirstToken(newToken), + ElseIfClauses, + ElseClause, + EndIfToken); + } + + protected internal override JassIfStatementSyntax ReplaceLastToken(JassSyntaxToken newToken) + { + return new JassIfStatementSyntax( + IfClause, + ElseIfClauses, + ElseClause, + newToken); + } } } \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Syntax/JassInvocationExpressionSyntax.cs b/src/War3Net.CodeAnalysis.Jass/Syntax/JassInvocationExpressionSyntax.cs index 8b6d1c91..7bde353e 100644 --- a/src/War3Net.CodeAnalysis.Jass/Syntax/JassInvocationExpressionSyntax.cs +++ b/src/War3Net.CodeAnalysis.Jass/Syntax/JassInvocationExpressionSyntax.cs @@ -5,27 +5,119 @@ // // ------------------------------------------------------------------------------ +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.IO; + namespace War3Net.CodeAnalysis.Jass.Syntax { - public class JassInvocationExpressionSyntax : IExpressionSyntax, IInvocationSyntax + public class JassInvocationExpressionSyntax : JassExpressionSyntax { - public JassInvocationExpressionSyntax(JassIdentifierNameSyntax identifierName, JassArgumentListSyntax arguments) + internal JassInvocationExpressionSyntax( + JassIdentifierNameSyntax identifierName, + JassArgumentListSyntax argumentList) { IdentifierName = identifierName; - Arguments = arguments; + ArgumentList = argumentList; } - public JassIdentifierNameSyntax IdentifierName { get; init; } + public JassIdentifierNameSyntax IdentifierName { get; } + + public JassArgumentListSyntax ArgumentList { get; } - public JassArgumentListSyntax Arguments { get; init; } + public override JassSyntaxKind SyntaxKind => JassSyntaxKind.InvocationExpression; - public bool Equals(IExpressionSyntax? other) + public override bool IsEquivalentTo([NotNullWhen(true)] JassSyntaxNode? other) { return other is JassInvocationExpressionSyntax invocationExpression - && IdentifierName.Equals(invocationExpression.IdentifierName) - && Arguments.Equals(invocationExpression.Arguments); + && IdentifierName.IsEquivalentTo(invocationExpression.IdentifierName) + && ArgumentList.IsEquivalentTo(invocationExpression.ArgumentList); + } + + public override void WriteTo(TextWriter writer) + { + IdentifierName.WriteTo(writer); + ArgumentList.WriteTo(writer); + } + + public override IEnumerable GetChildNodes() + { + yield return IdentifierName; + yield return ArgumentList; + } + + public override IEnumerable GetChildTokens() + { + yield break; + } + + public override IEnumerable GetChildNodesAndTokens() + { + yield return IdentifierName; + yield return ArgumentList; + } + + public override IEnumerable GetDescendantNodes() + { + yield return IdentifierName; + foreach (var descendant in IdentifierName.GetDescendantNodes()) + { + yield return descendant; + } + + yield return ArgumentList; + foreach (var descendant in ArgumentList.GetDescendantNodes()) + { + yield return descendant; + } + } + + public override IEnumerable GetDescendantTokens() + { + foreach (var descendant in IdentifierName.GetDescendantTokens()) + { + yield return descendant; + } + + foreach (var descendant in ArgumentList.GetDescendantTokens()) + { + yield return descendant; + } + } + + public override IEnumerable GetDescendantNodesAndTokens() + { + yield return IdentifierName; + foreach (var descendant in IdentifierName.GetDescendantNodesAndTokens()) + { + yield return descendant; + } + + yield return ArgumentList; + foreach (var descendant in ArgumentList.GetDescendantNodesAndTokens()) + { + yield return descendant; + } } - public override string ToString() => $"{IdentifierName}{JassSymbol.LeftParenthesis}{Arguments}{JassSymbol.RightParenthesis}"; + public override string ToString() => $"{IdentifierName}{ArgumentList}"; + + public override JassSyntaxToken GetFirstToken() => IdentifierName.GetFirstToken(); + + public override JassSyntaxToken GetLastToken() => ArgumentList.GetLastToken(); + + protected internal override JassInvocationExpressionSyntax ReplaceFirstToken(JassSyntaxToken newToken) + { + return new JassInvocationExpressionSyntax( + IdentifierName.ReplaceFirstToken(newToken), + ArgumentList); + } + + protected internal override JassInvocationExpressionSyntax ReplaceLastToken(JassSyntaxToken newToken) + { + return new JassInvocationExpressionSyntax( + IdentifierName, + ArgumentList.ReplaceLastToken(newToken)); + } } } \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Syntax/JassLiteralExpressionSyntax.cs b/src/War3Net.CodeAnalysis.Jass/Syntax/JassLiteralExpressionSyntax.cs new file mode 100644 index 00000000..eb043296 --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/Syntax/JassLiteralExpressionSyntax.cs @@ -0,0 +1,83 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.IO; + +namespace War3Net.CodeAnalysis.Jass.Syntax +{ + public class JassLiteralExpressionSyntax : JassExpressionSyntax + { + internal JassLiteralExpressionSyntax( + JassSyntaxToken token) + { + Token = token; + } + + public JassSyntaxToken Token { get; } + + public override JassSyntaxKind SyntaxKind => JassSyntaxFacts.GetLiteralExpressionKind(Token.SyntaxKind); + + public override bool IsEquivalentTo([NotNullWhen(true)] JassSyntaxNode? other) + { + return other is JassLiteralExpressionSyntax literalExpression + && Token.IsEquivalentTo(literalExpression.Token); + } + + public override void WriteTo(TextWriter writer) + { + Token.WriteTo(writer); + } + + public override IEnumerable GetChildNodes() + { + yield break; + } + + public override IEnumerable GetChildTokens() + { + yield return Token; + } + + public override IEnumerable GetChildNodesAndTokens() + { + yield return Token; + } + + public override IEnumerable GetDescendantNodes() + { + yield break; + } + + public override IEnumerable GetDescendantTokens() + { + yield return Token; + } + + public override IEnumerable GetDescendantNodesAndTokens() + { + yield return Token; + } + + public override string ToString() => Token.ToString(); + + public override JassSyntaxToken GetFirstToken() => Token; + + public override JassSyntaxToken GetLastToken() => Token; + + protected internal override JassLiteralExpressionSyntax ReplaceFirstToken(JassSyntaxToken newToken) + { + return new JassLiteralExpressionSyntax(newToken); + } + + protected internal override JassLiteralExpressionSyntax ReplaceLastToken(JassSyntaxToken newToken) + { + return new JassLiteralExpressionSyntax(newToken); + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Syntax/JassLocalVariableDeclarationStatementSyntax.cs b/src/War3Net.CodeAnalysis.Jass/Syntax/JassLocalVariableDeclarationStatementSyntax.cs index ea62a8d3..71ef3738 100644 --- a/src/War3Net.CodeAnalysis.Jass/Syntax/JassLocalVariableDeclarationStatementSyntax.cs +++ b/src/War3Net.CodeAnalysis.Jass/Syntax/JassLocalVariableDeclarationStatementSyntax.cs @@ -5,29 +5,104 @@ // // ------------------------------------------------------------------------------ +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.IO; + namespace War3Net.CodeAnalysis.Jass.Syntax { - public class JassLocalVariableDeclarationStatementSyntax : IStatementSyntax, IStatementLineSyntax + public class JassLocalVariableDeclarationStatementSyntax : JassStatementSyntax { - public JassLocalVariableDeclarationStatementSyntax(IVariableDeclaratorSyntax declarator) + internal JassLocalVariableDeclarationStatementSyntax( + JassSyntaxToken localToken, + JassVariableOrArrayDeclaratorSyntax declarator) { + LocalToken = localToken; Declarator = declarator; } - public IVariableDeclaratorSyntax Declarator { get; init; } + public JassSyntaxToken LocalToken { get; } + + public JassVariableOrArrayDeclaratorSyntax Declarator { get; } - public bool Equals(IStatementSyntax? other) + public override JassSyntaxKind SyntaxKind => JassSyntaxFacts.GetLocalDeclarationStatementKind(Declarator.SyntaxKind); + + public override bool IsEquivalentTo([NotNullWhen(true)] JassSyntaxNode? other) { return other is JassLocalVariableDeclarationStatementSyntax localVariableDeclarationStatement - && Declarator.Equals(localVariableDeclarationStatement.Declarator); + && Declarator.IsEquivalentTo(localVariableDeclarationStatement.Declarator); } - public bool Equals(IStatementLineSyntax? other) + public override void WriteTo(TextWriter writer) { - return other is JassLocalVariableDeclarationStatementSyntax localVariableDeclarationStatement - && Declarator.Equals(localVariableDeclarationStatement.Declarator); + LocalToken.WriteTo(writer); + Declarator.WriteTo(writer); + } + + public override IEnumerable GetChildNodes() + { + yield return Declarator; + } + + public override IEnumerable GetChildTokens() + { + yield return LocalToken; + } + + public override IEnumerable GetChildNodesAndTokens() + { + yield return LocalToken; + yield return Declarator; + } + + public override IEnumerable GetDescendantNodes() + { + yield return Declarator; + foreach (var descendant in Declarator.GetDescendantNodes()) + { + yield return descendant; + } } - public override string ToString() => $"{JassKeyword.Local} {Declarator}"; + public override IEnumerable GetDescendantTokens() + { + yield return LocalToken; + + foreach (var descendant in Declarator.GetDescendantTokens()) + { + yield return descendant; + } + } + + public override IEnumerable GetDescendantNodesAndTokens() + { + yield return LocalToken; + + yield return Declarator; + foreach (var descendant in Declarator.GetDescendantNodesAndTokens()) + { + yield return descendant; + } + } + + public override string ToString() => $"{LocalToken} {Declarator}"; + + public override JassSyntaxToken GetFirstToken() => LocalToken; + + public override JassSyntaxToken GetLastToken() => Declarator.GetLastToken(); + + protected internal override JassLocalVariableDeclarationStatementSyntax ReplaceFirstToken(JassSyntaxToken newToken) + { + return new JassLocalVariableDeclarationStatementSyntax( + newToken, + Declarator); + } + + protected internal override JassLocalVariableDeclarationStatementSyntax ReplaceLastToken(JassSyntaxToken newToken) + { + return new JassLocalVariableDeclarationStatementSyntax( + LocalToken, + Declarator.ReplaceLastToken(newToken)); + } } } \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Syntax/JassLoopCustomScriptAction.cs b/src/War3Net.CodeAnalysis.Jass/Syntax/JassLoopCustomScriptAction.cs deleted file mode 100644 index 8b621a3b..00000000 --- a/src/War3Net.CodeAnalysis.Jass/Syntax/JassLoopCustomScriptAction.cs +++ /dev/null @@ -1,22 +0,0 @@ -// ------------------------------------------------------------------------------ -// -// Licensed under the MIT license. -// See the LICENSE file in the project root for more information. -// -// ------------------------------------------------------------------------------ - -namespace War3Net.CodeAnalysis.Jass.Syntax -{ - public class JassLoopCustomScriptAction : IStatementLineSyntax - { - public static readonly JassLoopCustomScriptAction Value = new JassLoopCustomScriptAction(); - - private JassLoopCustomScriptAction() - { - } - - public bool Equals(IStatementLineSyntax? other) => other is JassLoopCustomScriptAction; - - public override string ToString() => JassKeyword.Loop; - } -} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Syntax/JassLoopStatementSyntax.cs b/src/War3Net.CodeAnalysis.Jass/Syntax/JassLoopStatementSyntax.cs index b07bcc1e..6b471f86 100644 --- a/src/War3Net.CodeAnalysis.Jass/Syntax/JassLoopStatementSyntax.cs +++ b/src/War3Net.CodeAnalysis.Jass/Syntax/JassLoopStatementSyntax.cs @@ -5,23 +5,120 @@ // // ------------------------------------------------------------------------------ +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Diagnostics.CodeAnalysis; +using System.IO; + +using War3Net.CodeAnalysis.Jass.Extensions; + namespace War3Net.CodeAnalysis.Jass.Syntax { - public class JassLoopStatementSyntax : IStatementSyntax + public class JassLoopStatementSyntax : JassStatementSyntax { - public JassLoopStatementSyntax(JassStatementListSyntax body) + internal JassLoopStatementSyntax( + JassSyntaxToken loopToken, + ImmutableArray statements, + JassSyntaxToken endLoopToken) { - Body = body; + LoopToken = loopToken; + Statements = statements; + EndLoopToken = endLoopToken; } - public JassStatementListSyntax Body { get; init; } + public JassSyntaxToken LoopToken { get; } + + public ImmutableArray Statements { get; } + + public JassSyntaxToken EndLoopToken { get; } + + public override JassSyntaxKind SyntaxKind => JassSyntaxKind.LoopStatement; - public bool Equals(IStatementSyntax? other) + public override bool IsEquivalentTo([NotNullWhen(true)] JassSyntaxNode? other) { return other is JassLoopStatementSyntax loopStatement - && Body.Equals(loopStatement.Body); + && Statements.IsEquivalentTo(loopStatement.Statements); + } + + public override void WriteTo(TextWriter writer) + { + LoopToken.WriteTo(writer); + Statements.WriteTo(writer); + EndLoopToken.WriteTo(writer); + } + + public override IEnumerable GetChildNodes() + { + return Statements; + } + + public override IEnumerable GetChildTokens() + { + yield return LoopToken; + yield return EndLoopToken; + } + + public override IEnumerable GetChildNodesAndTokens() + { + yield return LoopToken; + + foreach (var child in Statements) + { + yield return child; + } + + yield return EndLoopToken; + } + + public override IEnumerable GetDescendantNodes() + { + return Statements.GetDescendantNodes(); + } + + public override IEnumerable GetDescendantTokens() + { + yield return LoopToken; + + foreach (var descendant in Statements.GetDescendantTokens()) + { + yield return descendant; + } + + yield return EndLoopToken; + } + + public override IEnumerable GetDescendantNodesAndTokens() + { + yield return LoopToken; + + foreach (var descendant in Statements.GetDescendantNodesAndTokens()) + { + yield return descendant; + } + + yield return EndLoopToken; + } + + public override string ToString() => $"{LoopToken} [...]"; + + public override JassSyntaxToken GetFirstToken() => LoopToken; + + public override JassSyntaxToken GetLastToken() => EndLoopToken; + + protected internal override JassLoopStatementSyntax ReplaceFirstToken(JassSyntaxToken newToken) + { + return new JassLoopStatementSyntax( + newToken, + Statements, + EndLoopToken); } - public override string ToString() => $"{JassKeyword.Loop} [{Body.Statements.Length}]"; + protected internal override JassLoopStatementSyntax ReplaceLastToken(JassSyntaxToken newToken) + { + return new JassLoopStatementSyntax( + LoopToken, + Statements, + newToken); + } } } \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Syntax/JassNativeFunctionDeclarationSyntax.cs b/src/War3Net.CodeAnalysis.Jass/Syntax/JassNativeFunctionDeclarationSyntax.cs index fa2ed743..1dcc5f26 100644 --- a/src/War3Net.CodeAnalysis.Jass/Syntax/JassNativeFunctionDeclarationSyntax.cs +++ b/src/War3Net.CodeAnalysis.Jass/Syntax/JassNativeFunctionDeclarationSyntax.cs @@ -5,29 +5,198 @@ // // ------------------------------------------------------------------------------ +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.IO; + +using War3Net.CodeAnalysis.Jass.Extensions; + namespace War3Net.CodeAnalysis.Jass.Syntax { - public class JassNativeFunctionDeclarationSyntax : ITopLevelDeclarationSyntax, IDeclarationLineSyntax + public class JassNativeFunctionDeclarationSyntax : JassTopLevelDeclarationSyntax { - public JassNativeFunctionDeclarationSyntax(JassFunctionDeclaratorSyntax functionDeclarator) + internal JassNativeFunctionDeclarationSyntax( + JassSyntaxToken? constantToken, + JassSyntaxToken nativeToken, + JassIdentifierNameSyntax identifierName, + JassParameterListOrEmptyParameterListSyntax parameterList, + JassReturnClauseSyntax returnClause) { - FunctionDeclarator = functionDeclarator; + ConstantToken = constantToken; + NativeToken = nativeToken; + IdentifierName = identifierName; + ParameterList = parameterList; + ReturnClause = returnClause; } - public JassFunctionDeclaratorSyntax FunctionDeclarator { get; init; } + public JassSyntaxToken? ConstantToken { get; } + + public JassSyntaxToken NativeToken { get; } + + public JassIdentifierNameSyntax IdentifierName { get; } + + public JassParameterListOrEmptyParameterListSyntax ParameterList { get; } + + public JassReturnClauseSyntax ReturnClause { get; } - public bool Equals(ITopLevelDeclarationSyntax? other) + public override JassSyntaxKind SyntaxKind => JassSyntaxKind.NativeFunctionDeclaration; + + public override bool IsEquivalentTo([NotNullWhen(true)] JassSyntaxNode? other) { return other is JassNativeFunctionDeclarationSyntax nativeFunctionDeclaration - && FunctionDeclarator.Equals(nativeFunctionDeclaration.FunctionDeclarator); + && ConstantToken.NullableEquals(nativeFunctionDeclaration.ConstantToken) + && IdentifierName.IsEquivalentTo(nativeFunctionDeclaration.IdentifierName) + && ParameterList.IsEquivalentTo(nativeFunctionDeclaration.ParameterList) + && ReturnClause.IsEquivalentTo(nativeFunctionDeclaration.ReturnClause); } - public bool Equals(IDeclarationLineSyntax? other) + public override void WriteTo(TextWriter writer) { - return other is JassNativeFunctionDeclarationSyntax nativeFunctionDeclaration - && FunctionDeclarator.Equals(nativeFunctionDeclaration.FunctionDeclarator); + ConstantToken?.WriteTo(writer); + NativeToken.WriteTo(writer); + IdentifierName.WriteTo(writer); + ParameterList.WriteTo(writer); + ReturnClause.WriteTo(writer); + } + + public override IEnumerable GetChildNodes() + { + yield return IdentifierName; + yield return ParameterList; + yield return ReturnClause; + } + + public override IEnumerable GetChildTokens() + { + if (ConstantToken is not null) + { + yield return ConstantToken; + } + + yield return NativeToken; + } + + public override IEnumerable GetChildNodesAndTokens() + { + if (ConstantToken is not null) + { + yield return ConstantToken; + } + + yield return NativeToken; + yield return IdentifierName; + yield return ParameterList; + yield return ReturnClause; + } + + public override IEnumerable GetDescendantNodes() + { + yield return IdentifierName; + foreach (var descendant in IdentifierName.GetDescendantNodes()) + { + yield return descendant; + } + + yield return ParameterList; + foreach (var descendant in ParameterList.GetDescendantNodes()) + { + yield return descendant; + } + + yield return ReturnClause; + foreach (var descendant in ReturnClause.GetDescendantNodes()) + { + yield return descendant; + } } - public override string ToString() => $"{JassKeyword.Native} {FunctionDeclarator}"; + public override IEnumerable GetDescendantTokens() + { + if (ConstantToken is not null) + { + yield return ConstantToken; + } + + yield return NativeToken; + + foreach (var descendant in IdentifierName.GetDescendantTokens()) + { + yield return descendant; + } + + foreach (var descendant in ParameterList.GetDescendantTokens()) + { + yield return descendant; + } + + foreach (var descendant in ReturnClause.GetDescendantTokens()) + { + yield return descendant; + } + } + + public override IEnumerable GetDescendantNodesAndTokens() + { + if (ConstantToken is not null) + { + yield return ConstantToken; + } + + yield return NativeToken; + + yield return IdentifierName; + foreach (var descendant in IdentifierName.GetDescendantNodesAndTokens()) + { + yield return descendant; + } + + yield return ParameterList; + foreach (var descendant in ParameterList.GetDescendantNodesAndTokens()) + { + yield return descendant; + } + + yield return ReturnClause; + foreach (var descendant in ReturnClause.GetDescendantNodesAndTokens()) + { + yield return descendant; + } + } + + public override string ToString() => $"{ConstantToken.OptionalSuffixed()}{NativeToken} {IdentifierName} {ParameterList} {ReturnClause}"; + + public override JassSyntaxToken GetFirstToken() => ConstantToken ?? NativeToken; + + public override JassSyntaxToken GetLastToken() => ReturnClause.GetLastToken(); + + protected internal override JassNativeFunctionDeclarationSyntax ReplaceFirstToken(JassSyntaxToken newToken) + { + if (ConstantToken is not null) + { + return new JassNativeFunctionDeclarationSyntax( + newToken, + NativeToken, + IdentifierName, + ParameterList, + ReturnClause); + } + + return new JassNativeFunctionDeclarationSyntax( + null, + newToken, + IdentifierName, + ParameterList, + ReturnClause); + } + + protected internal override JassNativeFunctionDeclarationSyntax ReplaceLastToken(JassSyntaxToken newToken) + { + return new JassNativeFunctionDeclarationSyntax( + ConstantToken, + NativeToken, + IdentifierName, + ParameterList, + ReturnClause.ReplaceLastToken(newToken)); + } } } \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Syntax/JassNullLiteralExpressionSyntax.cs b/src/War3Net.CodeAnalysis.Jass/Syntax/JassNullLiteralExpressionSyntax.cs deleted file mode 100644 index d130e7ee..00000000 --- a/src/War3Net.CodeAnalysis.Jass/Syntax/JassNullLiteralExpressionSyntax.cs +++ /dev/null @@ -1,22 +0,0 @@ -// ------------------------------------------------------------------------------ -// -// Licensed under the MIT license. -// See the LICENSE file in the project root for more information. -// -// ------------------------------------------------------------------------------ - -namespace War3Net.CodeAnalysis.Jass.Syntax -{ - public class JassNullLiteralExpressionSyntax : IExpressionSyntax - { - public static readonly JassNullLiteralExpressionSyntax Value = new JassNullLiteralExpressionSyntax(); - - private JassNullLiteralExpressionSyntax() - { - } - - public bool Equals(IExpressionSyntax? other) => other is JassNullLiteralExpressionSyntax; - - public override string ToString() => JassKeyword.Null; - } -} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Syntax/JassOctalLiteralExpressionSyntax.cs b/src/War3Net.CodeAnalysis.Jass/Syntax/JassOctalLiteralExpressionSyntax.cs deleted file mode 100644 index e2f2630f..00000000 --- a/src/War3Net.CodeAnalysis.Jass/Syntax/JassOctalLiteralExpressionSyntax.cs +++ /dev/null @@ -1,29 +0,0 @@ -// ------------------------------------------------------------------------------ -// -// Licensed under the MIT license. -// See the LICENSE file in the project root for more information. -// -// ------------------------------------------------------------------------------ - -using System; - -namespace War3Net.CodeAnalysis.Jass.Syntax -{ - public class JassOctalLiteralExpressionSyntax : IExpressionSyntax - { - public JassOctalLiteralExpressionSyntax(int value) - { - Value = value; - } - - public int Value { get; init; } - - public bool Equals(IExpressionSyntax? other) - { - return other is JassOctalLiteralExpressionSyntax octalLiteralExpression - && Value == octalLiteralExpression.Value; - } - - public override string ToString() => Value == 0 ? $"{JassSymbol.Zero}" : $"{JassSymbol.Zero}{Convert.ToString(Value, 8)}"; - } -} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Syntax/JassParameterListOrEmptyParameterListSyntax.cs b/src/War3Net.CodeAnalysis.Jass/Syntax/JassParameterListOrEmptyParameterListSyntax.cs new file mode 100644 index 00000000..0c109518 --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/Syntax/JassParameterListOrEmptyParameterListSyntax.cs @@ -0,0 +1,16 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +namespace War3Net.CodeAnalysis.Jass.Syntax +{ + public abstract class JassParameterListOrEmptyParameterListSyntax : JassSyntaxNode + { + protected internal override abstract JassParameterListOrEmptyParameterListSyntax ReplaceFirstToken(JassSyntaxToken newToken); + + protected internal override abstract JassParameterListOrEmptyParameterListSyntax ReplaceLastToken(JassSyntaxToken newToken); + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Syntax/JassParameterListSyntax.cs b/src/War3Net.CodeAnalysis.Jass/Syntax/JassParameterListSyntax.cs index 212b22d3..dd5a0f49 100644 --- a/src/War3Net.CodeAnalysis.Jass/Syntax/JassParameterListSyntax.cs +++ b/src/War3Net.CodeAnalysis.Jass/Syntax/JassParameterListSyntax.cs @@ -5,29 +5,110 @@ // // ------------------------------------------------------------------------------ -using System; -using System.Collections.Immutable; -using System.Linq; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.IO; + +using War3Net.CodeAnalysis.Jass.Extensions; namespace War3Net.CodeAnalysis.Jass.Syntax { - public class JassParameterListSyntax : IEquatable + public class JassParameterListSyntax : JassParameterListOrEmptyParameterListSyntax { - public static readonly JassParameterListSyntax Empty = new JassParameterListSyntax(ImmutableArray.Empty); + internal JassParameterListSyntax( + JassSyntaxToken takesToken, + SeparatedSyntaxList parameterList) + { + TakesToken = takesToken; + ParameterList = parameterList; + } + + public JassSyntaxToken TakesToken { get; } + + public SeparatedSyntaxList ParameterList { get; } + + public override JassSyntaxKind SyntaxKind => JassSyntaxKind.ParameterList; + + public override bool IsEquivalentTo([NotNullWhen(true)] JassSyntaxNode? other) + { + return other is JassParameterListSyntax parameterList + && ParameterList.IsEquivalentTo(parameterList.ParameterList); + } - public JassParameterListSyntax(ImmutableArray parameters) + public override void WriteTo(TextWriter writer) { - Parameters = parameters; + TakesToken.WriteTo(writer); + ParameterList.WriteTo(writer); } - public ImmutableArray Parameters { get; init; } + public override IEnumerable GetChildNodes() + { + return ParameterList.Items; + } - public bool Equals(JassParameterListSyntax? other) + public override IEnumerable GetChildTokens() { - return other is not null - && Parameters.SequenceEqual(other.Parameters); + yield return TakesToken; + + foreach (var child in ParameterList.Separators) + { + yield return child; + } } - public override string ToString() => Parameters.Any() ? string.Join($"{JassSymbol.Comma} ", Parameters) : JassKeyword.Nothing; + public override IEnumerable GetChildNodesAndTokens() + { + yield return TakesToken; + + foreach (var child in ParameterList.GetChildNodesAndTokens()) + { + yield return child; + } + } + + public override IEnumerable GetDescendantNodes() + { + return ParameterList.GetDescendantNodes(); + } + + public override IEnumerable GetDescendantTokens() + { + yield return TakesToken; + + foreach (var descendant in ParameterList.GetDescendantTokens()) + { + yield return descendant; + } + } + + public override IEnumerable GetDescendantNodesAndTokens() + { + yield return TakesToken; + + foreach (var descendant in ParameterList.GetDescendantNodesAndTokens()) + { + yield return descendant; + } + } + + public override string ToString() => $"{TakesToken} {ParameterList}"; + + public override JassSyntaxToken GetFirstToken() => TakesToken; + + public override JassSyntaxToken GetLastToken() => ParameterList.Items[^1].GetLastToken(); + + protected internal override JassParameterListSyntax ReplaceFirstToken(JassSyntaxToken newToken) + { + return new JassParameterListSyntax( + newToken, + ParameterList); + } + + protected internal override JassParameterListSyntax ReplaceLastToken(JassSyntaxToken newToken) + { + return new JassParameterListSyntax( + TakesToken, + ParameterList.ReplaceLastItem(ParameterList.Items[^1].ReplaceLastToken(newToken))); + } } } \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Syntax/JassParameterSyntax.cs b/src/War3Net.CodeAnalysis.Jass/Syntax/JassParameterSyntax.cs index 054949d4..cd9db787 100644 --- a/src/War3Net.CodeAnalysis.Jass/Syntax/JassParameterSyntax.cs +++ b/src/War3Net.CodeAnalysis.Jass/Syntax/JassParameterSyntax.cs @@ -5,29 +5,119 @@ // // ------------------------------------------------------------------------------ -using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.IO; namespace War3Net.CodeAnalysis.Jass.Syntax { - public class JassParameterSyntax : IEquatable + public class JassParameterSyntax : JassSyntaxNode { - public JassParameterSyntax(JassTypeSyntax type, JassIdentifierNameSyntax identifierName) + internal JassParameterSyntax( + JassTypeSyntax type, + JassIdentifierNameSyntax identifierName) { Type = type; IdentifierName = identifierName; } - public JassTypeSyntax Type { get; init; } + public JassTypeSyntax Type { get; } - public JassIdentifierNameSyntax IdentifierName { get; init; } + public JassIdentifierNameSyntax IdentifierName { get; } - public bool Equals(JassParameterSyntax? other) + public override JassSyntaxKind SyntaxKind => JassSyntaxKind.Parameter; + + public override bool IsEquivalentTo([NotNullWhen(true)] JassSyntaxNode? other) { - return other is not null - && Type.Equals(other.Type) - && IdentifierName.Equals(other.IdentifierName); + return other is JassParameterSyntax parameter + && Type.IsEquivalentTo(parameter.Type) + && IdentifierName.IsEquivalentTo(parameter.IdentifierName); + } + + public override void WriteTo(TextWriter writer) + { + Type.WriteTo(writer); + IdentifierName.WriteTo(writer); + } + + public override IEnumerable GetChildNodes() + { + yield return Type; + yield return IdentifierName; + } + + public override IEnumerable GetChildTokens() + { + yield break; + } + + public override IEnumerable GetChildNodesAndTokens() + { + yield return Type; + yield return IdentifierName; + } + + public override IEnumerable GetDescendantNodes() + { + yield return Type; + foreach (var descendant in Type.GetDescendantNodes()) + { + yield return descendant; + } + + yield return IdentifierName; + foreach (var descendant in IdentifierName.GetDescendantNodes()) + { + yield return descendant; + } + } + + public override IEnumerable GetDescendantTokens() + { + foreach (var descendant in Type.GetDescendantTokens()) + { + yield return descendant; + } + + foreach (var descendant in IdentifierName.GetDescendantTokens()) + { + yield return descendant; + } + } + + public override IEnumerable GetDescendantNodesAndTokens() + { + yield return Type; + foreach (var descendant in Type.GetDescendantNodesAndTokens()) + { + yield return descendant; + } + + yield return IdentifierName; + foreach (var descendant in IdentifierName.GetDescendantNodesAndTokens()) + { + yield return descendant; + } } public override string ToString() => $"{Type} {IdentifierName}"; + + public override JassSyntaxToken GetFirstToken() => Type.GetFirstToken(); + + public override JassSyntaxToken GetLastToken() => IdentifierName.GetLastToken(); + + protected internal override JassParameterSyntax ReplaceFirstToken(JassSyntaxToken newToken) + { + return new JassParameterSyntax( + Type.ReplaceFirstToken(newToken), + IdentifierName); + } + + protected internal override JassParameterSyntax ReplaceLastToken(JassSyntaxToken newToken) + { + return new JassParameterSyntax( + Type, + IdentifierName.ReplaceLastToken(newToken)); + } } } \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Syntax/JassParenthesizedExpressionSyntax.cs b/src/War3Net.CodeAnalysis.Jass/Syntax/JassParenthesizedExpressionSyntax.cs index 3ea77c4b..651ba31e 100644 --- a/src/War3Net.CodeAnalysis.Jass/Syntax/JassParenthesizedExpressionSyntax.cs +++ b/src/War3Net.CodeAnalysis.Jass/Syntax/JassParenthesizedExpressionSyntax.cs @@ -5,23 +5,117 @@ // // ------------------------------------------------------------------------------ +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.IO; + namespace War3Net.CodeAnalysis.Jass.Syntax { - public class JassParenthesizedExpressionSyntax : IExpressionSyntax + public class JassParenthesizedExpressionSyntax : JassExpressionSyntax { - public JassParenthesizedExpressionSyntax(IExpressionSyntax expression) + internal JassParenthesizedExpressionSyntax( + JassSyntaxToken openParenToken, + JassExpressionSyntax expression, + JassSyntaxToken closeParenToken) { + OpenParenToken = openParenToken; Expression = expression; + CloseParenToken = closeParenToken; } - public IExpressionSyntax Expression { get; init; } + public JassSyntaxToken OpenParenToken { get; } + + public JassExpressionSyntax Expression { get; } + + public JassSyntaxToken CloseParenToken { get; } - public bool Equals(IExpressionSyntax? other) + public override JassSyntaxKind SyntaxKind => JassSyntaxKind.ParenthesizedExpression; + + public override bool IsEquivalentTo([NotNullWhen(true)] JassSyntaxNode? other) { return other is JassParenthesizedExpressionSyntax parenthesizedExpression - && Expression.Equals(parenthesizedExpression.Expression); + && Expression.IsEquivalentTo(parenthesizedExpression.Expression); + } + + public override void WriteTo(TextWriter writer) + { + OpenParenToken.WriteTo(writer); + Expression.WriteTo(writer); + CloseParenToken.WriteTo(writer); + } + + public override IEnumerable GetChildNodes() + { + yield return Expression; + } + + public override IEnumerable GetChildTokens() + { + yield return OpenParenToken; + yield return CloseParenToken; + } + + public override IEnumerable GetChildNodesAndTokens() + { + yield return OpenParenToken; + yield return Expression; + yield return CloseParenToken; + } + + public override IEnumerable GetDescendantNodes() + { + yield return Expression; + foreach (var descendant in Expression.GetDescendantNodes()) + { + yield return descendant; + } } - public override string ToString() => $"{JassSymbol.LeftParenthesis}{Expression}{JassSymbol.RightParenthesis}"; + public override IEnumerable GetDescendantTokens() + { + yield return OpenParenToken; + + foreach (var descendant in Expression.GetDescendantTokens()) + { + yield return descendant; + } + + yield return CloseParenToken; + } + + public override IEnumerable GetDescendantNodesAndTokens() + { + yield return OpenParenToken; + + yield return Expression; + foreach (var descendant in Expression.GetDescendantNodesAndTokens()) + { + yield return descendant; + } + + yield return CloseParenToken; + } + + public override string ToString() => $"{OpenParenToken}{Expression}{CloseParenToken}"; + + public override JassSyntaxToken GetFirstToken() => OpenParenToken; + + public override JassSyntaxToken GetLastToken() => CloseParenToken; + + protected internal override JassParenthesizedExpressionSyntax ReplaceFirstToken(JassSyntaxToken newToken) + { + return new JassParenthesizedExpressionSyntax( + newToken, + Expression, + CloseParenToken); + } + + protected internal override JassParenthesizedExpressionSyntax ReplaceLastToken(JassSyntaxToken newToken) + { + return new JassParenthesizedExpressionSyntax( + OpenParenToken, + Expression, + newToken); + } } } \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Syntax/JassPredefinedTypeSyntax.cs b/src/War3Net.CodeAnalysis.Jass/Syntax/JassPredefinedTypeSyntax.cs new file mode 100644 index 00000000..78d00ea5 --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/Syntax/JassPredefinedTypeSyntax.cs @@ -0,0 +1,91 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.IO; + +namespace War3Net.CodeAnalysis.Jass.Syntax +{ + public class JassPredefinedTypeSyntax : JassTypeSyntax + { + public static readonly JassPredefinedTypeSyntax Boolean = new(JassSyntaxFactory.Token(JassSyntaxKind.BooleanKeyword)); + public static readonly JassPredefinedTypeSyntax Code = new(JassSyntaxFactory.Token(JassSyntaxKind.CodeKeyword)); + public static readonly JassPredefinedTypeSyntax Handle = new(JassSyntaxFactory.Token(JassSyntaxKind.HandleKeyword)); + public static readonly JassPredefinedTypeSyntax Integer = new(JassSyntaxFactory.Token(JassSyntaxKind.IntegerKeyword)); + public static readonly JassPredefinedTypeSyntax Nothing = new(JassSyntaxFactory.Token(JassSyntaxKind.NothingKeyword)); + public static readonly JassPredefinedTypeSyntax Real = new(JassSyntaxFactory.Token(JassSyntaxKind.RealKeyword)); + public static readonly JassPredefinedTypeSyntax String = new(JassSyntaxFactory.Token(JassSyntaxKind.StringKeyword)); + + internal JassPredefinedTypeSyntax( + JassSyntaxToken token) + { + Token = token; + } + + public JassSyntaxToken Token { get; } + + public override JassSyntaxKind SyntaxKind => JassSyntaxKind.PredefinedType; + + public override bool IsEquivalentTo([NotNullWhen(true)] JassSyntaxNode? other) + { + return other is JassPredefinedTypeSyntax predefinedType + && Token.IsEquivalentTo(predefinedType.Token); + } + + public override void WriteTo(TextWriter writer) + { + Token.WriteTo(writer); + } + + public override IEnumerable GetChildNodes() + { + yield break; + } + + public override IEnumerable GetChildTokens() + { + yield return Token; + } + + public override IEnumerable GetChildNodesAndTokens() + { + yield return Token; + } + + public override IEnumerable GetDescendantNodes() + { + yield break; + } + + public override IEnumerable GetDescendantTokens() + { + yield return Token; + } + + public override IEnumerable GetDescendantNodesAndTokens() + { + yield return Token; + } + + public override string ToString() => Token.ToString(); + + public override JassSyntaxToken GetFirstToken() => Token; + + public override JassSyntaxToken GetLastToken() => Token; + + protected internal override JassPredefinedTypeSyntax ReplaceFirstToken(JassSyntaxToken newToken) + { + return new JassPredefinedTypeSyntax(newToken); + } + + protected internal override JassPredefinedTypeSyntax ReplaceLastToken(JassSyntaxToken newToken) + { + return new JassPredefinedTypeSyntax(newToken); + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Syntax/JassRealLiteralExpressionSyntax.cs b/src/War3Net.CodeAnalysis.Jass/Syntax/JassRealLiteralExpressionSyntax.cs deleted file mode 100644 index a16a06b0..00000000 --- a/src/War3Net.CodeAnalysis.Jass/Syntax/JassRealLiteralExpressionSyntax.cs +++ /dev/null @@ -1,69 +0,0 @@ -// ------------------------------------------------------------------------------ -// -// Licensed under the MIT license. -// See the LICENSE file in the project root for more information. -// -// ------------------------------------------------------------------------------ - -using System; -using System.Globalization; - -namespace War3Net.CodeAnalysis.Jass.Syntax -{ - public class JassRealLiteralExpressionSyntax : IExpressionSyntax - { -#if true - public JassRealLiteralExpressionSyntax(string intPart, string fracPart) - { - IntPart = intPart; - FracPart = fracPart; - } - - public string IntPart { get; init; } - - public string FracPart { get; init; } - - public bool Equals(IExpressionSyntax? other) - { - if (other is JassRealLiteralExpressionSyntax realLiteralExpression && FracPart.Length == realLiteralExpression.FracPart.Length) - { - var left = float.Parse(ToString(), CultureInfo.InvariantCulture); - var right = float.Parse(realLiteralExpression.ToString(), CultureInfo.InvariantCulture); - - if (left == right) - { - return true; - } - - var difference = MathF.Abs(left - right); - var maxDifference = MathF.Pow(10, -FracPart.Length) * 1.01f; - return difference <= maxDifference; - } - - return false; - } - - public override string ToString() - { - return string.IsNullOrEmpty(FracPart) - ? $"{IntPart}{JassSymbol.FullStop}" - : $"{IntPart}{JassSymbol.FullStop}{FracPart}"; - } -#else - public JassRealLiteralExpressionSyntax(float value) - { - Value = value; - } - - public float Value { get; init; } - - public bool Equals(IExpressionSyntax? other) - { - return other is JassRealLiteralExpressionSyntax realLiteralExpression - && Value == realLiteralExpression.Value; - } - - public override string ToString() => Value.ToString(CultureInfo.InvariantCulture); -#endif - } -} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Syntax/JassReturnClauseSyntax.cs b/src/War3Net.CodeAnalysis.Jass/Syntax/JassReturnClauseSyntax.cs new file mode 100644 index 00000000..a330228d --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/Syntax/JassReturnClauseSyntax.cs @@ -0,0 +1,108 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.IO; + +namespace War3Net.CodeAnalysis.Jass.Syntax +{ + public class JassReturnClauseSyntax : JassSyntaxNode + { + internal JassReturnClauseSyntax( + JassSyntaxToken returnsToken, + JassTypeSyntax returnType) + { + ReturnsToken = returnsToken; + ReturnType = returnType; + } + + public JassSyntaxToken ReturnsToken { get; } + + public JassTypeSyntax ReturnType { get; } + + public override JassSyntaxKind SyntaxKind => JassSyntaxKind.ReturnClause; + + public override bool IsEquivalentTo([NotNullWhen(true)] JassSyntaxNode? other) + { + return other is JassReturnClauseSyntax returnClause + && ReturnType.IsEquivalentTo(returnClause.ReturnType); + } + + public override void WriteTo(TextWriter writer) + { + ReturnsToken.WriteTo(writer); + ReturnType.WriteTo(writer); + } + + public override IEnumerable GetChildNodes() + { + yield return ReturnType; + } + + public override IEnumerable GetChildTokens() + { + yield return ReturnsToken; + } + + public override IEnumerable GetChildNodesAndTokens() + { + yield return ReturnsToken; + yield return ReturnType; + } + + public override IEnumerable GetDescendantNodes() + { + yield return ReturnType; + foreach (var descendant in ReturnType.GetDescendantNodes()) + { + yield return descendant; + } + } + + public override IEnumerable GetDescendantTokens() + { + yield return ReturnsToken; + + foreach (var descendant in ReturnType.GetDescendantTokens()) + { + yield return descendant; + } + } + + public override IEnumerable GetDescendantNodesAndTokens() + { + yield return ReturnsToken; + + yield return ReturnType; + foreach (var descendant in ReturnType.GetDescendantNodesAndTokens()) + { + yield return descendant; + } + } + + public override string ToString() => $"{ReturnsToken} {ReturnType}"; + + public override JassSyntaxToken GetFirstToken() => ReturnsToken; + + public override JassSyntaxToken GetLastToken() => ReturnType.GetLastToken(); + + protected internal override JassReturnClauseSyntax ReplaceFirstToken(JassSyntaxToken newToken) + { + return new JassReturnClauseSyntax( + newToken, + ReturnType); + } + + protected internal override JassReturnClauseSyntax ReplaceLastToken(JassSyntaxToken newToken) + { + return new JassReturnClauseSyntax( + ReturnsToken, + ReturnType.ReplaceLastToken(newToken)); + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Syntax/JassReturnStatementSyntax.cs b/src/War3Net.CodeAnalysis.Jass/Syntax/JassReturnStatementSyntax.cs index 4756f94f..7649a96a 100644 --- a/src/War3Net.CodeAnalysis.Jass/Syntax/JassReturnStatementSyntax.cs +++ b/src/War3Net.CodeAnalysis.Jass/Syntax/JassReturnStatementSyntax.cs @@ -5,33 +5,133 @@ // // ------------------------------------------------------------------------------ +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.IO; + using War3Net.CodeAnalysis.Jass.Extensions; namespace War3Net.CodeAnalysis.Jass.Syntax { - public class JassReturnStatementSyntax : IStatementSyntax, IStatementLineSyntax + public class JassReturnStatementSyntax : JassStatementSyntax { - public static readonly JassReturnStatementSyntax Empty = new JassReturnStatementSyntax(null); + public static readonly JassReturnStatementSyntax Empty = new( + new JassSyntaxToken(JassSyntaxKind.ReturnKeyword, JassKeyword.Return, JassSyntaxTriviaList.Empty), + null); - public JassReturnStatementSyntax(IExpressionSyntax? value) + internal JassReturnStatementSyntax( + JassSyntaxToken returnToken, + JassExpressionSyntax? value) { + ReturnToken = returnToken; Value = value; } - public IExpressionSyntax? Value { get; init; } + public JassSyntaxToken ReturnToken { get; } + + public JassExpressionSyntax? Value { get; } + + public override JassSyntaxKind SyntaxKind => JassSyntaxKind.ReturnStatement; - public bool Equals(IStatementSyntax? other) + public override bool IsEquivalentTo([NotNullWhen(true)] JassSyntaxNode? other) { return other is JassReturnStatementSyntax returnStatement - && Value.NullableEquals(returnStatement.Value); + && Value.NullableEquivalentTo(returnStatement.Value); } - public bool Equals(IStatementLineSyntax? other) + public override void WriteTo(TextWriter writer) { - return other is JassReturnStatementSyntax returnStatement - && Value.NullableEquals(returnStatement.Value); + ReturnToken.WriteTo(writer); + Value?.WriteTo(writer); + } + + public override IEnumerable GetChildNodes() + { + if (Value is not null) + { + yield return Value; + } + } + + public override IEnumerable GetChildTokens() + { + yield return ReturnToken; + } + + public override IEnumerable GetChildNodesAndTokens() + { + yield return ReturnToken; + + if (Value is not null) + { + yield return Value; + } + } + + public override IEnumerable GetDescendantNodes() + { + if (Value is not null) + { + yield return Value; + foreach (var descendant in Value.GetDescendantNodes()) + { + yield return descendant; + } + } } - public override string ToString() => Value is null ? JassKeyword.Return : $"{JassKeyword.Return} {Value}"; + public override IEnumerable GetDescendantTokens() + { + yield return ReturnToken; + + if (Value is not null) + { + foreach (var descendant in Value.GetDescendantTokens()) + { + yield return descendant; + } + } + } + + public override IEnumerable GetDescendantNodesAndTokens() + { + yield return ReturnToken; + + if (Value is not null) + { + yield return Value; + foreach (var descendant in Value.GetDescendantNodesAndTokens()) + { + yield return descendant; + } + } + } + + public override string ToString() => $"{ReturnToken}{Value.OptionalPrefixed()}"; + + public override JassSyntaxToken GetFirstToken() => ReturnToken; + + public override JassSyntaxToken GetLastToken() => Value?.GetLastToken() ?? ReturnToken; + + protected internal override JassReturnStatementSyntax ReplaceFirstToken(JassSyntaxToken newToken) + { + return new JassReturnStatementSyntax( + newToken, + Value); + } + + protected internal override JassReturnStatementSyntax ReplaceLastToken(JassSyntaxToken newToken) + { + if (Value is not null) + { + return new JassReturnStatementSyntax( + ReturnToken, + Value.ReplaceLastToken(newToken)); + } + + return new JassReturnStatementSyntax( + newToken, + null); + } } } \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Syntax/JassSetStatementSyntax.cs b/src/War3Net.CodeAnalysis.Jass/Syntax/JassSetStatementSyntax.cs index 006dd56a..c466e80b 100644 --- a/src/War3Net.CodeAnalysis.Jass/Syntax/JassSetStatementSyntax.cs +++ b/src/War3Net.CodeAnalysis.Jass/Syntax/JassSetStatementSyntax.cs @@ -5,46 +5,179 @@ // // ------------------------------------------------------------------------------ +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.IO; + using War3Net.CodeAnalysis.Jass.Extensions; namespace War3Net.CodeAnalysis.Jass.Syntax { - public class JassSetStatementSyntax : IStatementSyntax, IStatementLineSyntax + public class JassSetStatementSyntax : JassStatementSyntax { - public JassSetStatementSyntax(JassIdentifierNameSyntax identifierName, IExpressionSyntax? indexer, JassEqualsValueClauseSyntax value) + internal JassSetStatementSyntax( + JassSyntaxToken setToken, + JassIdentifierNameSyntax identifierName, + JassElementAccessClauseSyntax? elementAccessClause, + JassEqualsValueClauseSyntax value) { + SetToken = setToken; IdentifierName = identifierName; - Indexer = indexer; + ElementAccessClause = elementAccessClause; Value = value; } - public JassIdentifierNameSyntax IdentifierName { get; init; } + public JassSyntaxToken SetToken { get; } + + public JassIdentifierNameSyntax IdentifierName { get; } + + public JassElementAccessClauseSyntax? ElementAccessClause { get; } - public IExpressionSyntax? Indexer { get; init; } + public JassEqualsValueClauseSyntax Value { get; } - public JassEqualsValueClauseSyntax Value { get; init; } + public override JassSyntaxKind SyntaxKind => JassSyntaxKind.SetStatement; - public bool Equals(IStatementSyntax? other) + public override bool IsEquivalentTo([NotNullWhen(true)] JassSyntaxNode? other) { return other is JassSetStatementSyntax setStatement - && IdentifierName.Equals(setStatement.IdentifierName) - && Indexer.NullableEquals(setStatement.Indexer) - && Value.Equals(setStatement.Value); + && IdentifierName.IsEquivalentTo(setStatement.IdentifierName) + && ElementAccessClause.NullableEquivalentTo(setStatement.ElementAccessClause) + && Value.IsEquivalentTo(setStatement.Value); } - public bool Equals(IStatementLineSyntax? other) + public override void WriteTo(TextWriter writer) { - return other is JassSetStatementSyntax setStatement - && IdentifierName.Equals(setStatement.IdentifierName) - && Indexer.NullableEquals(setStatement.Indexer) - && Value.Equals(setStatement.Value); + SetToken.WriteTo(writer); + IdentifierName.WriteTo(writer); + ElementAccessClause?.WriteTo(writer); + Value.WriteTo(writer); + } + + public override IEnumerable GetChildNodes() + { + yield return IdentifierName; + + if (ElementAccessClause is not null) + { + yield return ElementAccessClause; + } + + yield return Value; + } + + public override IEnumerable GetChildTokens() + { + yield return SetToken; + } + + public override IEnumerable GetChildNodesAndTokens() + { + yield return SetToken; + yield return IdentifierName; + + if (ElementAccessClause is not null) + { + yield return ElementAccessClause; + } + + yield return Value; + } + + public override IEnumerable GetDescendantNodes() + { + yield return IdentifierName; + foreach (var descendant in IdentifierName.GetDescendantNodes()) + { + yield return descendant; + } + + if (ElementAccessClause is not null) + { + yield return ElementAccessClause; + foreach (var descendant in ElementAccessClause.GetDescendantNodes()) + { + yield return descendant; + } + } + + yield return Value; + foreach (var descendant in Value.GetDescendantNodes()) + { + yield return descendant; + } + } + + public override IEnumerable GetDescendantTokens() + { + yield return SetToken; + + foreach (var descendant in IdentifierName.GetDescendantTokens()) + { + yield return descendant; + } + + if (ElementAccessClause is not null) + { + foreach (var descendant in ElementAccessClause.GetDescendantTokens()) + { + yield return descendant; + } + } + + foreach (var descendant in Value.GetDescendantTokens()) + { + yield return descendant; + } + } + + public override IEnumerable GetDescendantNodesAndTokens() + { + yield return SetToken; + + yield return IdentifierName; + foreach (var descendant in IdentifierName.GetDescendantNodesAndTokens()) + { + yield return descendant; + } + + if (ElementAccessClause is not null) + { + yield return ElementAccessClause; + foreach (var descendant in ElementAccessClause.GetDescendantNodesAndTokens()) + { + yield return descendant; + } + } + + yield return Value; + foreach (var descendant in Value.GetDescendantNodesAndTokens()) + { + yield return descendant; + } + } + + public override string ToString() => $"{SetToken} {IdentifierName}{ElementAccessClause.Optional()} {Value}"; + + public override JassSyntaxToken GetFirstToken() => SetToken; + + public override JassSyntaxToken GetLastToken() => Value.GetLastToken(); + + protected internal override JassSetStatementSyntax ReplaceFirstToken(JassSyntaxToken newToken) + { + return new JassSetStatementSyntax( + newToken, + IdentifierName, + ElementAccessClause, + Value); } - public override string ToString() + protected internal override JassSetStatementSyntax ReplaceLastToken(JassSyntaxToken newToken) { - return Indexer is null - ? $"{JassKeyword.Set} {IdentifierName} {Value}" - : $"{JassKeyword.Set} {IdentifierName}{JassSymbol.LeftSquareBracket}{Indexer}{JassSymbol.RightSquareBracket} {Value}"; + return new JassSetStatementSyntax( + SetToken, + IdentifierName, + ElementAccessClause, + Value.ReplaceLastToken(newToken)); } } } \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Syntax/JassStatementListSyntax.cs b/src/War3Net.CodeAnalysis.Jass/Syntax/JassStatementListSyntax.cs deleted file mode 100644 index 4b73caac..00000000 --- a/src/War3Net.CodeAnalysis.Jass/Syntax/JassStatementListSyntax.cs +++ /dev/null @@ -1,31 +0,0 @@ -// ------------------------------------------------------------------------------ -// -// Licensed under the MIT license. -// See the LICENSE file in the project root for more information. -// -// ------------------------------------------------------------------------------ - -using System; -using System.Collections.Immutable; -using System.Linq; - -namespace War3Net.CodeAnalysis.Jass.Syntax -{ - public class JassStatementListSyntax : IEquatable - { - public JassStatementListSyntax(ImmutableArray statements) - { - Statements = statements; - } - - public ImmutableArray Statements { get; init; } - - public bool Equals(JassStatementListSyntax? other) - { - return other is not null - && Statements.SequenceEqual(other.Statements); - } - - public override string ToString() => $"<{base.ToString()}> [{Statements.Length}]"; - } -} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Syntax/JassStatementSyntax.cs b/src/War3Net.CodeAnalysis.Jass/Syntax/JassStatementSyntax.cs new file mode 100644 index 00000000..da32de14 --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/Syntax/JassStatementSyntax.cs @@ -0,0 +1,16 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +namespace War3Net.CodeAnalysis.Jass.Syntax +{ + public abstract class JassStatementSyntax : JassSyntaxNode + { + protected internal override abstract JassStatementSyntax ReplaceFirstToken(JassSyntaxToken newToken); + + protected internal override abstract JassStatementSyntax ReplaceLastToken(JassSyntaxToken newToken); + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Syntax/JassStringLiteralExpressionSyntax.cs b/src/War3Net.CodeAnalysis.Jass/Syntax/JassStringLiteralExpressionSyntax.cs deleted file mode 100644 index 5fdf9433..00000000 --- a/src/War3Net.CodeAnalysis.Jass/Syntax/JassStringLiteralExpressionSyntax.cs +++ /dev/null @@ -1,29 +0,0 @@ -// ------------------------------------------------------------------------------ -// -// Licensed under the MIT license. -// See the LICENSE file in the project root for more information. -// -// ------------------------------------------------------------------------------ - -using System; - -namespace War3Net.CodeAnalysis.Jass.Syntax -{ - public class JassStringLiteralExpressionSyntax : IExpressionSyntax - { - public JassStringLiteralExpressionSyntax(string value) - { - Value = value; - } - - public string Value { get; init; } - - public bool Equals(IExpressionSyntax? other) - { - return other is JassStringLiteralExpressionSyntax stringLiteralExpression - && string.Equals(Value, stringLiteralExpression.Value, StringComparison.Ordinal); - } - - public override string ToString() => $"{JassSymbol.QuotationMark}{Value}{JassSymbol.QuotationMark}"; - } -} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Syntax/JassSyntaxNode.cs b/src/War3Net.CodeAnalysis.Jass/Syntax/JassSyntaxNode.cs new file mode 100644 index 00000000..46de4df2 --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/Syntax/JassSyntaxNode.cs @@ -0,0 +1,56 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.IO; + +namespace War3Net.CodeAnalysis.Jass.Syntax +{ + public abstract class JassSyntaxNode + { + internal JassSyntaxNode() + { + } + + public abstract JassSyntaxKind SyntaxKind { get; } + + /// + /// Determines if two nodes are the same, disregarding trivia differences. + /// + public abstract bool IsEquivalentTo([NotNullWhen(true)] JassSyntaxNode? other); + + public abstract void WriteTo(TextWriter writer); + + public abstract IEnumerable GetChildNodes(); + + public abstract IEnumerable GetChildTokens(); + + public abstract IEnumerable GetChildNodesAndTokens(); + + public abstract IEnumerable GetDescendantNodes(); + + public abstract IEnumerable GetDescendantTokens(); + + public abstract IEnumerable GetDescendantNodesAndTokens(); + + public abstract JassSyntaxToken GetFirstToken(); + + public abstract JassSyntaxToken GetLastToken(); + + protected internal abstract JassSyntaxNode ReplaceFirstToken(JassSyntaxToken newToken); + + protected internal abstract JassSyntaxNode ReplaceLastToken(JassSyntaxToken newToken); + + public string ToFullString() + { + using var writer = new StringWriter(); + WriteTo(writer); + return writer.ToString(); + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Syntax/JassSyntaxNodeOrToken.cs b/src/War3Net.CodeAnalysis.Jass/Syntax/JassSyntaxNodeOrToken.cs new file mode 100644 index 00000000..a3eef98c --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/Syntax/JassSyntaxNodeOrToken.cs @@ -0,0 +1,146 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using System; +using System.Diagnostics.CodeAnalysis; + +namespace War3Net.CodeAnalysis.Jass.Syntax +{ + // Based on https://github.com/mcintyre321/OneOf/blob/master/OneOf/OneOfT1.generated.cs + public readonly struct JassSyntaxNodeOrToken + { + private readonly JassSyntaxNode? _node; + private readonly JassSyntaxToken? _token; + private readonly bool _isNode; + + private JassSyntaxNodeOrToken(JassSyntaxNode node) + { + if (node is null) + { + throw new ArgumentNullException(nameof(node)); + } + + _node = node; + _token = null; + _isNode = true; + } + + private JassSyntaxNodeOrToken(JassSyntaxToken token) + { + if (token is null) + { + throw new ArgumentNullException(nameof(token)); + } + + _node = null; + _token = token; + _isNode = false; + } + + public object Value => _isNode ? _node! : _token!; + + public bool IsNode => _isNode; + + public bool IsToken => !_isNode; + + public JassSyntaxNode AsNode => _node ?? throw new InvalidOperationException($"Cannot return as node, because result is token."); + + public JassSyntaxToken AsToken => _token ?? throw new InvalidOperationException($"Cannot return as token, because result is node."); + + public static implicit operator JassSyntaxNodeOrToken(JassSyntaxNode node) => new(node); + + public static implicit operator JassSyntaxNodeOrToken(JassSyntaxToken token) => new(token); + + public static JassSyntaxNodeOrToken FromJassSyntaxNode(JassSyntaxNode node) => node; + + public static JassSyntaxNodeOrToken FromJassSyntaxToken(JassSyntaxToken token) => token; + + public void Switch(Action? nodeFunc, Action? tokenFunc) + { + if (_isNode) + { + if (nodeFunc is null) + { + throw new ArgumentNullException(nameof(nodeFunc)); + } + + nodeFunc.Invoke(_node!); + } + else + { + if (tokenFunc is null) + { + throw new ArgumentNullException(nameof(tokenFunc)); + } + + tokenFunc.Invoke(_token!); + } + } + + public TResult Match(Func? nodeFunc, Func? tokenFunc) + { + if (_isNode) + { + if (nodeFunc is null) + { + throw new ArgumentNullException(nameof(nodeFunc)); + } + + return nodeFunc.Invoke(_node!); + } + else + { + if (tokenFunc is null) + { + throw new ArgumentNullException(nameof(tokenFunc)); + } + + return tokenFunc.Invoke(_token!); + } + } + + public bool TryGetNode([NotNullWhen(true)] out JassSyntaxNode? node) + { + node = _node; + return _isNode; + } + + public bool TryGetToken([NotNullWhen(true)] out JassSyntaxToken? token) + { + token = _token; + return !_isNode; + } + + public bool TryPickNode([NotNullWhen(true)] out JassSyntaxNode? node, [NotNullWhen(false)] out JassSyntaxToken? token) + { + node = _node; + token = _token; + return _isNode; + } + + public bool TryPickToken([NotNullWhen(true)] out JassSyntaxToken? token, [NotNullWhen(false)] out JassSyntaxNode? node) + { + node = _node; + token = _token; + return !_isNode; + } + + public override string ToString() + { + return _isNode + ? $"Node: {_node}" + : $"Token: {_token}"; + } + + public string ToFullString() + { + return TryPickNode(out var node, out var token) + ? node.ToFullString() + : token.ToFullString(); + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Syntax/JassSyntaxToken.cs b/src/War3Net.CodeAnalysis.Jass/Syntax/JassSyntaxToken.cs new file mode 100644 index 00000000..697329cb --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/Syntax/JassSyntaxToken.cs @@ -0,0 +1,65 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using System; +using System.Diagnostics.CodeAnalysis; +using System.IO; + +namespace War3Net.CodeAnalysis.Jass.Syntax +{ + public class JassSyntaxToken + { + internal JassSyntaxToken( + JassSyntaxKind syntaxKind, + string text, + JassSyntaxTriviaList trailingTrivia) + { + LeadingTrivia = JassSyntaxTriviaList.Empty; + SyntaxKind = syntaxKind; + Text = text; + TrailingTrivia = trailingTrivia; + } + + internal JassSyntaxToken( + JassSyntaxTriviaList leadingTrivia, + JassSyntaxKind syntaxKind, + string text, + JassSyntaxTriviaList trailingTrivia) + { + LeadingTrivia = leadingTrivia; + SyntaxKind = syntaxKind; + Text = text; + TrailingTrivia = trailingTrivia; + } + + public JassSyntaxTriviaList LeadingTrivia { get; } + + public JassSyntaxKind SyntaxKind { get; } + + public string Text { get; } + + public JassSyntaxTriviaList TrailingTrivia { get; } + + public bool IsEquivalentTo([NotNullWhen(true)] JassSyntaxToken? other) + { + return other is not null + && SyntaxKind == other.SyntaxKind + && string.Equals(Text, other.Text, StringComparison.Ordinal); + } + + public void WriteTo(TextWriter writer) + { + LeadingTrivia.WriteTo(writer); + writer.Write(Text); + TrailingTrivia.WriteTo(writer); + } + + public override string ToString() => Text; + + public string ToFullString() => $"{LeadingTrivia}{Text}{TrailingTrivia}"; + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Syntax/JassSyntaxTrivia.cs b/src/War3Net.CodeAnalysis.Jass/Syntax/JassSyntaxTrivia.cs new file mode 100644 index 00000000..8d21d354 --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/Syntax/JassSyntaxTrivia.cs @@ -0,0 +1,45 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using System; +using System.Diagnostics.CodeAnalysis; +using System.IO; + +namespace War3Net.CodeAnalysis.Jass.Syntax +{ + public class JassSyntaxTrivia + { + public static readonly JassSyntaxTrivia SingleSpace = new(JassSyntaxKind.WhitespaceTrivia, JassSymbol.Space); + public static readonly JassSyntaxTrivia NewLine = new(JassSyntaxKind.NewLineTrivia, JassSymbol.CarriageReturnLineFeed); + + internal JassSyntaxTrivia( + JassSyntaxKind syntaxKind, + string text) + { + SyntaxKind = syntaxKind; + Text = text; + } + + public JassSyntaxKind SyntaxKind { get; } + + public string Text { get; } + + public bool IsEquivalentTo([NotNullWhen(true)] JassSyntaxTrivia? other) + { + return other is not null + && SyntaxKind == other.SyntaxKind + && string.Equals(Text, other.Text, StringComparison.Ordinal); + } + + public void WriteTo(TextWriter writer) + { + writer.Write(Text); + } + + public override string ToString() => Text; + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Syntax/JassSyntaxTriviaList.cs b/src/War3Net.CodeAnalysis.Jass/Syntax/JassSyntaxTriviaList.cs new file mode 100644 index 00000000..8ae869f5 --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/Syntax/JassSyntaxTriviaList.cs @@ -0,0 +1,37 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using System.Collections.Immutable; +using System.IO; + +namespace War3Net.CodeAnalysis.Jass.Syntax +{ + public class JassSyntaxTriviaList + { + public static readonly JassSyntaxTriviaList Empty = new(ImmutableArray.Empty); + public static readonly JassSyntaxTriviaList SingleSpace = new(ImmutableArray.Create(JassSyntaxTrivia.SingleSpace)); + public static readonly JassSyntaxTriviaList NewLine = new(ImmutableArray.Create(JassSyntaxTrivia.NewLine)); + + internal JassSyntaxTriviaList( + ImmutableArray trivia) + { + Trivia = trivia; + } + + public ImmutableArray Trivia { get; } + + public void WriteTo(TextWriter writer) + { + for (var i = 0; i < Trivia.Length; i++) + { + Trivia[i].WriteTo(writer); + } + } + + public override string ToString() => string.Concat(Trivia); + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Syntax/JassTopLevelDeclarationSyntax.cs b/src/War3Net.CodeAnalysis.Jass/Syntax/JassTopLevelDeclarationSyntax.cs new file mode 100644 index 00000000..b8e8e156 --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/Syntax/JassTopLevelDeclarationSyntax.cs @@ -0,0 +1,16 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +namespace War3Net.CodeAnalysis.Jass.Syntax +{ + public abstract class JassTopLevelDeclarationSyntax : JassSyntaxNode + { + protected internal override abstract JassTopLevelDeclarationSyntax ReplaceFirstToken(JassSyntaxToken newToken); + + protected internal override abstract JassTopLevelDeclarationSyntax ReplaceLastToken(JassSyntaxToken newToken); + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Syntax/JassTypeDeclarationSyntax.cs b/src/War3Net.CodeAnalysis.Jass/Syntax/JassTypeDeclarationSyntax.cs index 1bbc1481..ec6de54d 100644 --- a/src/War3Net.CodeAnalysis.Jass/Syntax/JassTypeDeclarationSyntax.cs +++ b/src/War3Net.CodeAnalysis.Jass/Syntax/JassTypeDeclarationSyntax.cs @@ -5,34 +5,144 @@ // // ------------------------------------------------------------------------------ +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.IO; + namespace War3Net.CodeAnalysis.Jass.Syntax { - public class JassTypeDeclarationSyntax : ITopLevelDeclarationSyntax, IDeclarationLineSyntax + public class JassTypeDeclarationSyntax : JassTopLevelDeclarationSyntax { - public JassTypeDeclarationSyntax(JassIdentifierNameSyntax identifierName, JassTypeSyntax baseType) + internal JassTypeDeclarationSyntax( + JassSyntaxToken typeToken, + JassIdentifierNameSyntax identifierName, + JassSyntaxToken extendsToken, + JassTypeSyntax baseType) { + TypeToken = typeToken; IdentifierName = identifierName; + ExtendsToken = extendsToken; BaseType = baseType; } - public JassIdentifierNameSyntax IdentifierName { get; init; } + public JassSyntaxToken TypeToken { get; } + + public JassIdentifierNameSyntax IdentifierName { get; } + + public JassSyntaxToken ExtendsToken { get; } - public JassTypeSyntax BaseType { get; init; } + public JassTypeSyntax BaseType { get; } - public bool Equals(ITopLevelDeclarationSyntax? other) + public override JassSyntaxKind SyntaxKind => JassSyntaxKind.TypeDeclaration; + + public override bool IsEquivalentTo([NotNullWhen(true)] JassSyntaxNode? other) { return other is JassTypeDeclarationSyntax typeDeclaration - && IdentifierName.Equals(typeDeclaration.IdentifierName) - && BaseType.Equals(typeDeclaration.BaseType); + && IdentifierName.IsEquivalentTo(typeDeclaration.IdentifierName) + && BaseType.IsEquivalentTo(typeDeclaration.BaseType); } - public bool Equals(IDeclarationLineSyntax? other) + public override void WriteTo(TextWriter writer) { - return other is JassTypeDeclarationSyntax typeDeclaration - && IdentifierName.Equals(typeDeclaration.IdentifierName) - && BaseType.Equals(typeDeclaration.BaseType); + TypeToken.WriteTo(writer); + IdentifierName.WriteTo(writer); + ExtendsToken.WriteTo(writer); + BaseType.WriteTo(writer); + } + + public override IEnumerable GetChildNodes() + { + yield return IdentifierName; + yield return BaseType; + } + + public override IEnumerable GetChildTokens() + { + yield return TypeToken; + yield return ExtendsToken; + } + + public override IEnumerable GetChildNodesAndTokens() + { + yield return TypeToken; + yield return IdentifierName; + yield return ExtendsToken; + yield return BaseType; + } + + public override IEnumerable GetDescendantNodes() + { + yield return IdentifierName; + foreach (var descendant in IdentifierName.GetDescendantNodes()) + { + yield return descendant; + } + + yield return BaseType; + foreach (var descendant in BaseType.GetDescendantNodes()) + { + yield return descendant; + } + } + + public override IEnumerable GetDescendantTokens() + { + yield return TypeToken; + + foreach (var descendant in IdentifierName.GetDescendantTokens()) + { + yield return descendant; + } + + yield return ExtendsToken; + + foreach (var descendant in BaseType.GetDescendantTokens()) + { + yield return descendant; + } } - public override string ToString() => $"{JassKeyword.Type} {IdentifierName} {JassKeyword.Extends} {BaseType}"; + public override IEnumerable GetDescendantNodesAndTokens() + { + yield return TypeToken; + + yield return IdentifierName; + foreach (var descendant in IdentifierName.GetDescendantNodesAndTokens()) + { + yield return descendant; + } + + yield return ExtendsToken; + + yield return BaseType; + foreach (var descendant in BaseType.GetDescendantNodesAndTokens()) + { + yield return descendant; + } + } + + public override string ToString() => $"{TypeToken} {IdentifierName} {ExtendsToken} {BaseType}"; + + public override JassSyntaxToken GetFirstToken() => TypeToken; + + public override JassSyntaxToken GetLastToken() => BaseType.GetLastToken(); + + protected internal override JassTypeDeclarationSyntax ReplaceFirstToken(JassSyntaxToken newToken) + { + return new JassTypeDeclarationSyntax( + newToken, + IdentifierName, + ExtendsToken, + BaseType); + } + + protected internal override JassTypeDeclarationSyntax ReplaceLastToken(JassSyntaxToken newToken) + { + return new JassTypeDeclarationSyntax( + TypeToken, + IdentifierName, + ExtendsToken, + BaseType.ReplaceLastToken(newToken)); + } } } \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Syntax/JassTypeSyntax.cs b/src/War3Net.CodeAnalysis.Jass/Syntax/JassTypeSyntax.cs index 049c7dca..341459dc 100644 --- a/src/War3Net.CodeAnalysis.Jass/Syntax/JassTypeSyntax.cs +++ b/src/War3Net.CodeAnalysis.Jass/Syntax/JassTypeSyntax.cs @@ -5,33 +5,12 @@ // // ------------------------------------------------------------------------------ -using System; - namespace War3Net.CodeAnalysis.Jass.Syntax { - public class JassTypeSyntax : IEquatable + public abstract class JassTypeSyntax : JassExpressionSyntax { - public static readonly JassTypeSyntax Boolean = new JassTypeSyntax(new JassIdentifierNameSyntax(JassKeyword.Boolean)); - public static readonly JassTypeSyntax Code = new JassTypeSyntax(new JassIdentifierNameSyntax(JassKeyword.Code)); - public static readonly JassTypeSyntax Handle = new JassTypeSyntax(new JassIdentifierNameSyntax(JassKeyword.Handle)); - public static readonly JassTypeSyntax Integer = new JassTypeSyntax(new JassIdentifierNameSyntax(JassKeyword.Integer)); - public static readonly JassTypeSyntax Nothing = new JassTypeSyntax(new JassIdentifierNameSyntax(JassKeyword.Nothing)); - public static readonly JassTypeSyntax Real = new JassTypeSyntax(new JassIdentifierNameSyntax(JassKeyword.Real)); - public static readonly JassTypeSyntax String = new JassTypeSyntax(new JassIdentifierNameSyntax(JassKeyword.String)); - - internal JassTypeSyntax(JassIdentifierNameSyntax typeName) - { - TypeName = typeName; - } - - public JassIdentifierNameSyntax TypeName { get; init; } - - public bool Equals(JassTypeSyntax? other) - { - return other is not null - && TypeName.Equals(other.TypeName); - } + protected internal override abstract JassTypeSyntax ReplaceFirstToken(JassSyntaxToken newToken); - public override string ToString() => TypeName.ToString(); + protected internal override abstract JassTypeSyntax ReplaceLastToken(JassSyntaxToken newToken); } } \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Syntax/JassUnaryExpressionSyntax.cs b/src/War3Net.CodeAnalysis.Jass/Syntax/JassUnaryExpressionSyntax.cs index 0d60e508..c2bc38ed 100644 --- a/src/War3Net.CodeAnalysis.Jass/Syntax/JassUnaryExpressionSyntax.cs +++ b/src/War3Net.CodeAnalysis.Jass/Syntax/JassUnaryExpressionSyntax.cs @@ -5,34 +5,105 @@ // // ------------------------------------------------------------------------------ -using War3Net.CodeAnalysis.Jass.Extensions; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.IO; namespace War3Net.CodeAnalysis.Jass.Syntax { - public class JassUnaryExpressionSyntax : IExpressionSyntax + public class JassUnaryExpressionSyntax : JassExpressionSyntax { - public JassUnaryExpressionSyntax(UnaryOperatorType @operator, IExpressionSyntax expression) + internal JassUnaryExpressionSyntax( + JassSyntaxToken operatorToken, + JassExpressionSyntax expression) { - Operator = @operator; + OperatorToken = operatorToken; Expression = expression; } - public UnaryOperatorType Operator { get; init; } + public JassSyntaxToken OperatorToken { get; } - public IExpressionSyntax Expression { get; init; } + public JassExpressionSyntax Expression { get; } - public bool Equals(IExpressionSyntax? other) + public override JassSyntaxKind SyntaxKind => JassSyntaxFacts.GetUnaryExpressionKind(OperatorToken.SyntaxKind); + + public override bool IsEquivalentTo([NotNullWhen(true)] JassSyntaxNode? other) { return other is JassUnaryExpressionSyntax unaryExpression - && Operator == unaryExpression.Operator - && Expression.Equals(unaryExpression.Expression); + && OperatorToken.IsEquivalentTo(unaryExpression.OperatorToken) + && Expression.IsEquivalentTo(unaryExpression.Expression); + } + + public override void WriteTo(TextWriter writer) + { + OperatorToken.WriteTo(writer); + Expression.WriteTo(writer); + } + + public override IEnumerable GetChildNodes() + { + yield return Expression; + } + + public override IEnumerable GetChildTokens() + { + yield return OperatorToken; + } + + public override IEnumerable GetChildNodesAndTokens() + { + yield return OperatorToken; + yield return Expression; + } + + public override IEnumerable GetDescendantNodes() + { + yield return Expression; + foreach (var descendant in Expression.GetDescendantNodes()) + { + yield return descendant; + } + } + + public override IEnumerable GetDescendantTokens() + { + yield return OperatorToken; + + foreach (var descendant in Expression.GetDescendantTokens()) + { + yield return descendant; + } + } + + public override IEnumerable GetDescendantNodesAndTokens() + { + yield return OperatorToken; + + yield return Expression; + foreach (var descendant in Expression.GetDescendantNodesAndTokens()) + { + yield return descendant; + } + } + + public override string ToString() => $"{OperatorToken}{(OperatorToken.SyntaxKind == JassSyntaxKind.NotKeyword ? JassSymbol.Space : string.Empty)}{Expression}"; + + public override JassSyntaxToken GetFirstToken() => OperatorToken; + + public override JassSyntaxToken GetLastToken() => Expression.GetLastToken(); + + protected internal override JassUnaryExpressionSyntax ReplaceFirstToken(JassSyntaxToken newToken) + { + return new JassUnaryExpressionSyntax( + newToken, + Expression); } - public override string ToString() + protected internal override JassUnaryExpressionSyntax ReplaceLastToken(JassSyntaxToken newToken) { - return Operator == UnaryOperatorType.Not - ? $"{Operator.GetSymbol()} {Expression}" - : $"{Operator.GetSymbol()}{Expression}"; + return new JassUnaryExpressionSyntax( + OperatorToken, + Expression.ReplaceLastToken(newToken)); } } } \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Syntax/JassVariableDeclaratorSyntax.cs b/src/War3Net.CodeAnalysis.Jass/Syntax/JassVariableDeclaratorSyntax.cs index 6d2e461d..bfba2660 100644 --- a/src/War3Net.CodeAnalysis.Jass/Syntax/JassVariableDeclaratorSyntax.cs +++ b/src/War3Net.CodeAnalysis.Jass/Syntax/JassVariableDeclaratorSyntax.cs @@ -5,38 +5,173 @@ // // ------------------------------------------------------------------------------ +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.IO; + using War3Net.CodeAnalysis.Jass.Extensions; namespace War3Net.CodeAnalysis.Jass.Syntax { - public class JassVariableDeclaratorSyntax : IVariableDeclaratorSyntax + public class JassVariableDeclaratorSyntax : JassVariableOrArrayDeclaratorSyntax { - public JassVariableDeclaratorSyntax(JassTypeSyntax type, JassIdentifierNameSyntax identifierName, JassEqualsValueClauseSyntax? value) + internal JassVariableDeclaratorSyntax( + JassTypeSyntax type, + JassIdentifierNameSyntax identifierName, + JassEqualsValueClauseSyntax? value) { Type = type; IdentifierName = identifierName; Value = value; } - public JassTypeSyntax Type { get; init; } + public JassTypeSyntax Type { get; } + + public JassIdentifierNameSyntax IdentifierName { get; } - public JassIdentifierNameSyntax IdentifierName { get; init; } + public JassEqualsValueClauseSyntax? Value { get; } - public JassEqualsValueClauseSyntax? Value { get; init; } + public override JassSyntaxKind SyntaxKind => JassSyntaxKind.VariableDeclarator; - public bool Equals(IVariableDeclaratorSyntax? other) + public override bool IsEquivalentTo([NotNullWhen(true)] JassSyntaxNode? other) { return other is JassVariableDeclaratorSyntax variableDeclarator - && Type.Equals(variableDeclarator.Type) - && IdentifierName.Equals(variableDeclarator.IdentifierName) - && Value.NullableEquals(variableDeclarator.Value); + && Type.IsEquivalentTo(variableDeclarator.Type) + && IdentifierName.IsEquivalentTo(variableDeclarator.IdentifierName) + && Value.NullableEquivalentTo(variableDeclarator.Value); + } + + public override void WriteTo(TextWriter writer) + { + Type.WriteTo(writer); + IdentifierName.WriteTo(writer); + Value?.WriteTo(writer); + } + + public override IEnumerable GetChildNodes() + { + yield return Type; + yield return IdentifierName; + + if (Value is not null) + { + yield return Value; + } + } + + public override IEnumerable GetChildTokens() + { + yield break; + } + + public override IEnumerable GetChildNodesAndTokens() + { + yield return Type; + yield return IdentifierName; + + if (Value is not null) + { + yield return Value; + } + } + + public override IEnumerable GetDescendantNodes() + { + yield return Type; + foreach (var descendant in Type.GetDescendantNodes()) + { + yield return descendant; + } + + yield return IdentifierName; + foreach (var descendant in IdentifierName.GetDescendantNodes()) + { + yield return descendant; + } + + if (Value is not null) + { + yield return Value; + foreach (var descendant in Value.GetDescendantNodes()) + { + yield return descendant; + } + } + } + + public override IEnumerable GetDescendantTokens() + { + foreach (var descendant in Type.GetDescendantTokens()) + { + yield return descendant; + } + + foreach (var descendant in IdentifierName.GetDescendantTokens()) + { + yield return descendant; + } + + if (Value is not null) + { + foreach (var descendant in Value.GetDescendantTokens()) + { + yield return descendant; + } + } } - public override string ToString() + public override IEnumerable GetDescendantNodesAndTokens() { - return Value is null - ? $"{Type} {IdentifierName}" - : $"{Type} {IdentifierName} {Value}"; + yield return Type; + foreach (var descendant in Type.GetDescendantNodesAndTokens()) + { + yield return descendant; + } + + yield return IdentifierName; + foreach (var descendant in IdentifierName.GetDescendantNodesAndTokens()) + { + yield return descendant; + } + + if (Value is not null) + { + yield return Value; + foreach (var descendant in Value.GetDescendantNodesAndTokens()) + { + yield return descendant; + } + } + } + + public override string ToString() => $"{Type} {IdentifierName}{Value.OptionalPrefixed()}"; + + public override JassSyntaxToken GetFirstToken() => Type.GetFirstToken(); + + public override JassSyntaxToken GetLastToken() => ((JassSyntaxNode?)Value ?? IdentifierName).GetLastToken(); + + protected internal override JassVariableDeclaratorSyntax ReplaceFirstToken(JassSyntaxToken newToken) + { + return new JassVariableDeclaratorSyntax( + Type.ReplaceFirstToken(newToken), + IdentifierName, + Value); + } + + protected internal override JassVariableDeclaratorSyntax ReplaceLastToken(JassSyntaxToken newToken) + { + if (Value is not null) + { + return new JassVariableDeclaratorSyntax( + Type, + IdentifierName, + Value.ReplaceLastToken(newToken)); + } + + return new JassVariableDeclaratorSyntax( + Type, + IdentifierName.ReplaceLastToken(newToken), + null); } } } \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Syntax/JassVariableOrArrayDeclaratorSyntax.cs b/src/War3Net.CodeAnalysis.Jass/Syntax/JassVariableOrArrayDeclaratorSyntax.cs new file mode 100644 index 00000000..f85e3936 --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/Syntax/JassVariableOrArrayDeclaratorSyntax.cs @@ -0,0 +1,16 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +namespace War3Net.CodeAnalysis.Jass.Syntax +{ + public abstract class JassVariableOrArrayDeclaratorSyntax : JassSyntaxNode + { + protected internal override abstract JassVariableOrArrayDeclaratorSyntax ReplaceFirstToken(JassSyntaxToken newToken); + + protected internal override abstract JassVariableOrArrayDeclaratorSyntax ReplaceLastToken(JassSyntaxToken newToken); + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Syntax/JassVariableReferenceExpressionSyntax.cs b/src/War3Net.CodeAnalysis.Jass/Syntax/JassVariableReferenceExpressionSyntax.cs deleted file mode 100644 index a95e523c..00000000 --- a/src/War3Net.CodeAnalysis.Jass/Syntax/JassVariableReferenceExpressionSyntax.cs +++ /dev/null @@ -1,27 +0,0 @@ -// ------------------------------------------------------------------------------ -// -// Licensed under the MIT license. -// See the LICENSE file in the project root for more information. -// -// ------------------------------------------------------------------------------ - -namespace War3Net.CodeAnalysis.Jass.Syntax -{ - public class JassVariableReferenceExpressionSyntax : IExpressionSyntax - { - public JassVariableReferenceExpressionSyntax(JassIdentifierNameSyntax identifierName) - { - IdentifierName = identifierName; - } - - public JassIdentifierNameSyntax IdentifierName { get; init; } - - public bool Equals(IExpressionSyntax? other) - { - return other is JassVariableReferenceExpressionSyntax variableReferenceExpression - && IdentifierName.Equals(variableReferenceExpression.IdentifierName); - } - - public override string ToString() => IdentifierName.ToString(); - } -} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/Syntax/UnaryOperatorType.cs b/src/War3Net.CodeAnalysis.Jass/Syntax/UnaryOperatorType.cs deleted file mode 100644 index 05d4cdab..00000000 --- a/src/War3Net.CodeAnalysis.Jass/Syntax/UnaryOperatorType.cs +++ /dev/null @@ -1,16 +0,0 @@ -// ------------------------------------------------------------------------------ -// -// Licensed under the MIT license. -// See the LICENSE file in the project root for more information. -// -// ------------------------------------------------------------------------------ - -namespace War3Net.CodeAnalysis.Jass.Syntax -{ - public enum UnaryOperatorType - { - Plus, - Minus, - Not, - } -} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/SyntaxFactory/ArgumentListFactory.cs b/src/War3Net.CodeAnalysis.Jass/SyntaxFactory/ArgumentListFactory.cs index 90d00a7f..d4e5baf1 100644 --- a/src/War3Net.CodeAnalysis.Jass/SyntaxFactory/ArgumentListFactory.cs +++ b/src/War3Net.CodeAnalysis.Jass/SyntaxFactory/ArgumentListFactory.cs @@ -5,7 +5,7 @@ // // ------------------------------------------------------------------------------ -using System.Collections.Immutable; +using System.Collections.Generic; using War3Net.CodeAnalysis.Jass.Syntax; @@ -13,9 +13,32 @@ namespace War3Net.CodeAnalysis.Jass { public static partial class JassSyntaxFactory { - public static JassArgumentListSyntax ArgumentList(params IExpressionSyntax[] arguments) + public static JassArgumentListSyntax ArgumentList(params JassExpressionSyntax[] arguments) { - return new JassArgumentListSyntax(arguments.ToImmutableArray()); + return new JassArgumentListSyntax( + Token(JassSyntaxKind.OpenParenToken), + SeparatedSyntaxList(JassSyntaxKind.CommaToken, arguments), + Token(JassSyntaxKind.CloseParenToken)); + } + + public static JassArgumentListSyntax ArgumentList(IEnumerable arguments) + { + return new JassArgumentListSyntax( + Token(JassSyntaxKind.OpenParenToken), + SeparatedSyntaxList(JassSyntaxKind.CommaToken, arguments), + Token(JassSyntaxKind.CloseParenToken)); + } + + public static JassArgumentListSyntax ArgumentList(JassSyntaxToken openParenToken, SeparatedSyntaxList argumentList, JassSyntaxToken closeParenToken) + { + ThrowHelper.ThrowIfInvalidToken(openParenToken, JassSyntaxKind.OpenParenToken); + ThrowHelper.ThrowIfInvalidSeparatedSyntaxList(argumentList, JassSyntaxKind.CommaToken); + ThrowHelper.ThrowIfInvalidToken(closeParenToken, JassSyntaxKind.CloseParenToken); + + return new JassArgumentListSyntax( + openParenToken, + argumentList, + closeParenToken); } } } \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/SyntaxFactory/ArrayDeclaratorFactory.cs b/src/War3Net.CodeAnalysis.Jass/SyntaxFactory/ArrayDeclaratorFactory.cs new file mode 100644 index 00000000..98c7e596 --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/SyntaxFactory/ArrayDeclaratorFactory.cs @@ -0,0 +1,56 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using War3Net.CodeAnalysis.Jass.Syntax; + +namespace War3Net.CodeAnalysis.Jass +{ + public static partial class JassSyntaxFactory + { + public static JassArrayDeclaratorSyntax ArrayDeclarator(JassTypeSyntax type, JassIdentifierNameSyntax identifierName) + { + return new JassArrayDeclaratorSyntax( + type, + Token(JassSyntaxKind.ArrayKeyword), + identifierName); + } + + public static JassArrayDeclaratorSyntax ArrayDeclarator(JassTypeSyntax type, string name) + { + return new JassArrayDeclaratorSyntax( + type, + Token(JassSyntaxKind.ArrayKeyword), + ParseIdentifierName(name)); + } + + public static JassArrayDeclaratorSyntax ArrayDeclarator(string type, JassIdentifierNameSyntax identifierName) + { + return new JassArrayDeclaratorSyntax( + ParseTypeName(type), + Token(JassSyntaxKind.ArrayKeyword), + identifierName); + } + + public static JassArrayDeclaratorSyntax ArrayDeclarator(string type, string name) + { + return new JassArrayDeclaratorSyntax( + ParseTypeName(type), + Token(JassSyntaxKind.ArrayKeyword), + ParseIdentifierName(name)); + } + + public static JassArrayDeclaratorSyntax ArrayDeclarator(JassTypeSyntax type, JassSyntaxToken arrayToken, JassIdentifierNameSyntax identifierName) + { + ThrowHelper.ThrowIfInvalidToken(arrayToken, JassSyntaxKind.ArrayKeyword); + + return new JassArrayDeclaratorSyntax( + type, + arrayToken, + identifierName); + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/SyntaxFactory/ArrayReferenceExpressionFactory.cs b/src/War3Net.CodeAnalysis.Jass/SyntaxFactory/ArrayReferenceExpressionFactory.cs deleted file mode 100644 index 288ef0f8..00000000 --- a/src/War3Net.CodeAnalysis.Jass/SyntaxFactory/ArrayReferenceExpressionFactory.cs +++ /dev/null @@ -1,21 +0,0 @@ -// ------------------------------------------------------------------------------ -// -// Licensed under the MIT license. -// See the LICENSE file in the project root for more information. -// -// ------------------------------------------------------------------------------ - -using War3Net.CodeAnalysis.Jass.Syntax; - -namespace War3Net.CodeAnalysis.Jass -{ - public static partial class JassSyntaxFactory - { - public static JassArrayReferenceExpressionSyntax ArrayReferenceExpression(string name, IExpressionSyntax indexer) - { - return new JassArrayReferenceExpressionSyntax( - ParseIdentifierName(name), - indexer); - } - } -} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/SyntaxFactory/BinaryExpressionFactory.cs b/src/War3Net.CodeAnalysis.Jass/SyntaxFactory/BinaryExpressionFactory.cs index 40de143d..4dd543cf 100644 --- a/src/War3Net.CodeAnalysis.Jass/SyntaxFactory/BinaryExpressionFactory.cs +++ b/src/War3Net.CodeAnalysis.Jass/SyntaxFactory/BinaryExpressionFactory.cs @@ -5,75 +5,122 @@ // // ------------------------------------------------------------------------------ +using System; + using War3Net.CodeAnalysis.Jass.Syntax; namespace War3Net.CodeAnalysis.Jass { public static partial class JassSyntaxFactory { - public static JassBinaryExpressionSyntax BinaryAdditionExpression(IExpressionSyntax left, IExpressionSyntax right) + public static JassBinaryExpressionSyntax BinaryAddExpression(JassExpressionSyntax left, JassExpressionSyntax right) { - return new JassBinaryExpressionSyntax(BinaryOperatorType.Add, left, right); + return new JassBinaryExpressionSyntax( + left, + Token(JassSyntaxKind.PlusToken), + right); } - public static JassBinaryExpressionSyntax BinarySubtractionExpression(IExpressionSyntax left, IExpressionSyntax right) + public static JassBinaryExpressionSyntax BinarySubtractExpression(JassExpressionSyntax left, JassExpressionSyntax right) { - return new JassBinaryExpressionSyntax(BinaryOperatorType.Subtract, left, right); + return new JassBinaryExpressionSyntax( + left, + Token(JassSyntaxKind.MinusToken), + right); } - public static JassBinaryExpressionSyntax BinaryMultiplicationExpression(IExpressionSyntax left, IExpressionSyntax right) + public static JassBinaryExpressionSyntax BinaryMultiplyExpression(JassExpressionSyntax left, JassExpressionSyntax right) { - return new JassBinaryExpressionSyntax(BinaryOperatorType.Multiplication, left, right); + return new JassBinaryExpressionSyntax( + left, + Token(JassSyntaxKind.AsteriskToken), + right); } - public static JassBinaryExpressionSyntax BinaryDivisionExpression(IExpressionSyntax left, IExpressionSyntax right) + public static JassBinaryExpressionSyntax BinaryDivideExpression(JassExpressionSyntax left, JassExpressionSyntax right) { - return new JassBinaryExpressionSyntax(BinaryOperatorType.Division, left, right); + return new JassBinaryExpressionSyntax( + left, + Token(JassSyntaxKind.SlashToken), + right); } - public static JassBinaryExpressionSyntax BinaryGreaterThanExpression(IExpressionSyntax left, IExpressionSyntax right) + public static JassBinaryExpressionSyntax BinaryGreaterThanExpression(JassExpressionSyntax left, JassExpressionSyntax right) { - return new JassBinaryExpressionSyntax(BinaryOperatorType.GreaterThan, left, right); + return new JassBinaryExpressionSyntax( + left, + Token(JassSyntaxKind.GreaterThanToken), + right); } - public static JassBinaryExpressionSyntax BinaryLessThanExpression(IExpressionSyntax left, IExpressionSyntax right) + public static JassBinaryExpressionSyntax BinaryLessThanExpression(JassExpressionSyntax left, JassExpressionSyntax right) { - return new JassBinaryExpressionSyntax(BinaryOperatorType.LessThan, left, right); + return new JassBinaryExpressionSyntax( + left, + Token(JassSyntaxKind.LessThanToken), + right); } - public static JassBinaryExpressionSyntax BinaryEqualsExpression(IExpressionSyntax left, IExpressionSyntax right) + public static JassBinaryExpressionSyntax BinaryEqualsExpression(JassExpressionSyntax left, JassExpressionSyntax right) { - return new JassBinaryExpressionSyntax(BinaryOperatorType.Equals, left, right); + return new JassBinaryExpressionSyntax( + left, + Token(JassSyntaxKind.EqualsEqualsToken), + right); } - public static JassBinaryExpressionSyntax BinaryNotEqualsExpression(IExpressionSyntax left, IExpressionSyntax right) + public static JassBinaryExpressionSyntax BinaryNotEqualsExpression(JassExpressionSyntax left, JassExpressionSyntax right) { - return new JassBinaryExpressionSyntax(BinaryOperatorType.NotEquals, left, right); + return new JassBinaryExpressionSyntax( + left, + Token(JassSyntaxKind.ExclamationEqualsToken), + right); } - public static JassBinaryExpressionSyntax BinaryGreaterOrEqualExpression(IExpressionSyntax left, IExpressionSyntax right) + public static JassBinaryExpressionSyntax BinaryGreaterThanOrEqualExpression(JassExpressionSyntax left, JassExpressionSyntax right) { - return new JassBinaryExpressionSyntax(BinaryOperatorType.GreaterOrEqual, left, right); + return new JassBinaryExpressionSyntax( + left, + Token(JassSyntaxKind.GreaterThanEqualsToken), + right); } - public static JassBinaryExpressionSyntax BinaryLessOrEqualExpression(IExpressionSyntax left, IExpressionSyntax right) + public static JassBinaryExpressionSyntax BinaryLessThanOrEqualExpression(JassExpressionSyntax left, JassExpressionSyntax right) { - return new JassBinaryExpressionSyntax(BinaryOperatorType.LessOrEqual, left, right); + return new JassBinaryExpressionSyntax( + left, + Token(JassSyntaxKind.LessThanEqualsToken), + right); } - public static JassBinaryExpressionSyntax BinaryAndExpression(IExpressionSyntax left, IExpressionSyntax right) + public static JassBinaryExpressionSyntax BinaryAndExpression(JassExpressionSyntax left, JassExpressionSyntax right) { - return new JassBinaryExpressionSyntax(BinaryOperatorType.And, left, right); + return new JassBinaryExpressionSyntax( + left, + Token(JassSyntaxKind.AndKeyword), + right); } - public static JassBinaryExpressionSyntax BinaryOrExpression(IExpressionSyntax left, IExpressionSyntax right) + public static JassBinaryExpressionSyntax BinaryOrExpression(JassExpressionSyntax left, JassExpressionSyntax right) { - return new JassBinaryExpressionSyntax(BinaryOperatorType.Or, left, right); + return new JassBinaryExpressionSyntax( + left, + Token(JassSyntaxKind.OrKeyword), + right); } - public static JassBinaryExpressionSyntax BinaryExpression(IExpressionSyntax left, IExpressionSyntax right, BinaryOperatorType @operator) + public static JassBinaryExpressionSyntax BinaryExpression(JassExpressionSyntax left, JassSyntaxToken operatorToken, JassExpressionSyntax right) { - return new JassBinaryExpressionSyntax(@operator, left, right); + var expressionKind = JassSyntaxFacts.GetBinaryExpressionKind(operatorToken.SyntaxKind); + if (expressionKind == JassSyntaxKind.None) + { + throw new ArgumentException($"'{operatorToken.SyntaxKind}' is not a valid operator kind for binary expressions.", nameof(operatorToken)); + } + + return new JassBinaryExpressionSyntax( + left, + operatorToken, + right); } } } \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/SyntaxFactory/CallStatementFactory.cs b/src/War3Net.CodeAnalysis.Jass/SyntaxFactory/CallStatementFactory.cs index c686a230..a653a435 100644 --- a/src/War3Net.CodeAnalysis.Jass/SyntaxFactory/CallStatementFactory.cs +++ b/src/War3Net.CodeAnalysis.Jass/SyntaxFactory/CallStatementFactory.cs @@ -5,24 +5,70 @@ // // ------------------------------------------------------------------------------ +using System.Collections.Generic; + using War3Net.CodeAnalysis.Jass.Syntax; namespace War3Net.CodeAnalysis.Jass { public static partial class JassSyntaxFactory { - public static JassCallStatementSyntax CallStatement(string name, JassArgumentListSyntax arguments) + public static JassCallStatementSyntax CallStatement(JassIdentifierNameSyntax identifierName, JassArgumentListSyntax argumentList) + { + return new JassCallStatementSyntax( + Token(JassSyntaxKind.CallKeyword), + identifierName, + argumentList); + } + + public static JassCallStatementSyntax CallStatement(JassIdentifierNameSyntax identifierName, params JassExpressionSyntax[] arguments) + { + return new JassCallStatementSyntax( + Token(JassSyntaxKind.CallKeyword), + identifierName, + ArgumentList(arguments)); + } + + public static JassCallStatementSyntax CallStatement(JassIdentifierNameSyntax identifierName, IEnumerable arguments) + { + return new JassCallStatementSyntax( + Token(JassSyntaxKind.CallKeyword), + identifierName, + ArgumentList(arguments)); + } + + public static JassCallStatementSyntax CallStatement(string name, JassArgumentListSyntax argumentList) { return new JassCallStatementSyntax( + Token(JassSyntaxKind.CallKeyword), ParseIdentifierName(name), - arguments); + argumentList); } - public static JassCallStatementSyntax CallStatement(string name, params IExpressionSyntax[] arguments) + public static JassCallStatementSyntax CallStatement(string name, params JassExpressionSyntax[] arguments) { return new JassCallStatementSyntax( + Token(JassSyntaxKind.CallKeyword), ParseIdentifierName(name), ArgumentList(arguments)); } + + public static JassCallStatementSyntax CallStatement(string name, IEnumerable arguments) + { + return new JassCallStatementSyntax( + Token(JassSyntaxKind.CallKeyword), + ParseIdentifierName(name), + ArgumentList(arguments)); + } + + public static JassCallStatementSyntax CallStatement(JassSyntaxToken callToken, JassIdentifierNameSyntax identifierName, JassArgumentListSyntax argumentList) + { + ThrowHelper.ThrowIfInvalidToken(callToken, JassSyntaxKind.CallKeyword); + + return new JassCallStatementSyntax( + callToken, + identifierName, + argumentList); + } } } \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/SyntaxFactory/CompilationUnitFactory.cs b/src/War3Net.CodeAnalysis.Jass/SyntaxFactory/CompilationUnitFactory.cs index e0717321..fbbe6c81 100644 --- a/src/War3Net.CodeAnalysis.Jass/SyntaxFactory/CompilationUnitFactory.cs +++ b/src/War3Net.CodeAnalysis.Jass/SyntaxFactory/CompilationUnitFactory.cs @@ -14,14 +14,34 @@ namespace War3Net.CodeAnalysis.Jass { public static partial class JassSyntaxFactory { - public static JassCompilationUnitSyntax CompilationUnit(IEnumerable declarations) + public static JassCompilationUnitSyntax CompilationUnit(params JassTopLevelDeclarationSyntax[] declarations) { - return new JassCompilationUnitSyntax(declarations.ToImmutableArray()); + return new JassCompilationUnitSyntax( + declarations.ToImmutableArray(), + Token(JassSyntaxKind.EndOfFileToken)); } - public static JassCompilationUnitSyntax CompilationUnit(params ITopLevelDeclarationSyntax[] declarations) + public static JassCompilationUnitSyntax CompilationUnit(IEnumerable declarations) { - return new JassCompilationUnitSyntax(declarations.ToImmutableArray()); + return new JassCompilationUnitSyntax( + declarations.ToImmutableArray(), + Token(JassSyntaxKind.EndOfFileToken)); + } + + public static JassCompilationUnitSyntax CompilationUnit(ImmutableArray declarations) + { + return new JassCompilationUnitSyntax( + declarations, + Token(JassSyntaxKind.EndOfFileToken)); + } + + public static JassCompilationUnitSyntax CompilationUnit(ImmutableArray declarations, JassSyntaxToken endOfFileToken) + { + ThrowHelper.ThrowIfInvalidToken(endOfFileToken, JassSyntaxKind.EndOfFileToken); + + return new JassCompilationUnitSyntax( + declarations, + endOfFileToken); } } } \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/SyntaxFactory/DebugStatementFactory.cs b/src/War3Net.CodeAnalysis.Jass/SyntaxFactory/DebugStatementFactory.cs new file mode 100644 index 00000000..dae98a17 --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/SyntaxFactory/DebugStatementFactory.cs @@ -0,0 +1,44 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using System; + +using War3Net.CodeAnalysis.Jass.Syntax; + +namespace War3Net.CodeAnalysis.Jass +{ + public static partial class JassSyntaxFactory + { + public static JassDebugStatementSyntax DebugStatement(JassStatementSyntax statement) + { + var statementKind = JassSyntaxFacts.GetDebugStatementKind(statement.SyntaxKind); + if (statementKind == JassSyntaxKind.None) + { + throw new ArgumentException($"'{statement.SyntaxKind}' is not a valid statement kind for debug statements.", nameof(statement)); + } + + return new JassDebugStatementSyntax( + Token(JassSyntaxKind.DebugKeyword), + statement); + } + + public static JassDebugStatementSyntax DebugStatement(JassSyntaxToken debugToken, JassStatementSyntax statement) + { + ThrowHelper.ThrowIfInvalidToken(debugToken, JassSyntaxKind.DebugKeyword); + + var statementKind = JassSyntaxFacts.GetDebugStatementKind(statement.SyntaxKind); + if (statementKind == JassSyntaxKind.None) + { + throw new ArgumentException($"'{statement.SyntaxKind}' is not a valid statement kind for debug statements.", nameof(statement)); + } + + return new JassDebugStatementSyntax( + debugToken, + statement); + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/SyntaxFactory/ElementAccessClauseFactory.cs b/src/War3Net.CodeAnalysis.Jass/SyntaxFactory/ElementAccessClauseFactory.cs new file mode 100644 index 00000000..d7225466 --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/SyntaxFactory/ElementAccessClauseFactory.cs @@ -0,0 +1,33 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using War3Net.CodeAnalysis.Jass.Syntax; + +namespace War3Net.CodeAnalysis.Jass +{ + public static partial class JassSyntaxFactory + { + public static JassElementAccessClauseSyntax ElementAccessClause(JassExpressionSyntax expression) + { + return new JassElementAccessClauseSyntax( + Token(JassSyntaxKind.OpenBracketToken), + expression, + Token(JassSyntaxKind.CloseBracketToken)); + } + + public static JassElementAccessClauseSyntax ElementAccessClause(JassSyntaxToken openBracketToken, JassExpressionSyntax expression, JassSyntaxToken closeBracketToken) + { + ThrowHelper.ThrowIfInvalidToken(openBracketToken, JassSyntaxKind.OpenBracketToken); + ThrowHelper.ThrowIfInvalidToken(closeBracketToken, JassSyntaxKind.CloseBracketToken); + + return new JassElementAccessClauseSyntax( + openBracketToken, + expression, + closeBracketToken); + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/SyntaxFactory/ElementAccessExpressionFactory.cs b/src/War3Net.CodeAnalysis.Jass/SyntaxFactory/ElementAccessExpressionFactory.cs new file mode 100644 index 00000000..1aab3e9f --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/SyntaxFactory/ElementAccessExpressionFactory.cs @@ -0,0 +1,42 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using War3Net.CodeAnalysis.Jass.Syntax; + +namespace War3Net.CodeAnalysis.Jass +{ + public static partial class JassSyntaxFactory + { + public static JassElementAccessExpressionSyntax ElementAccessExpression(JassIdentifierNameSyntax identifierName, JassElementAccessClauseSyntax elementAccessClause) + { + return new JassElementAccessExpressionSyntax( + identifierName, + elementAccessClause); + } + + public static JassElementAccessExpressionSyntax ElementAccessExpression(JassIdentifierNameSyntax identifierName, JassExpressionSyntax elementAccessClauseExpression) + { + return new JassElementAccessExpressionSyntax( + identifierName, + ElementAccessClause(elementAccessClauseExpression)); + } + + public static JassElementAccessExpressionSyntax ElementAccessExpression(string name, JassElementAccessClauseSyntax elementAccessClause) + { + return new JassElementAccessExpressionSyntax( + ParseIdentifierName(name), + elementAccessClause); + } + + public static JassElementAccessExpressionSyntax ElementAccessExpression(string name, JassExpressionSyntax elementAccessClauseExpression) + { + return new JassElementAccessExpressionSyntax( + ParseIdentifierName(name), + ElementAccessClause(elementAccessClauseExpression)); + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/SyntaxFactory/ElseClauseFactory.cs b/src/War3Net.CodeAnalysis.Jass/SyntaxFactory/ElseClauseFactory.cs new file mode 100644 index 00000000..31b4fba8 --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/SyntaxFactory/ElseClauseFactory.cs @@ -0,0 +1,47 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using System.Collections.Generic; +using System.Collections.Immutable; + +using War3Net.CodeAnalysis.Jass.Syntax; + +namespace War3Net.CodeAnalysis.Jass +{ + public static partial class JassSyntaxFactory + { + public static JassElseClauseSyntax ElseClause(params JassStatementSyntax[] statements) + { + return new JassElseClauseSyntax( + Token(JassSyntaxKind.ElseKeyword), + statements.ToImmutableArray()); + } + + public static JassElseClauseSyntax ElseClause(IEnumerable statements) + { + return new JassElseClauseSyntax( + Token(JassSyntaxKind.ElseKeyword), + statements.ToImmutableArray()); + } + + public static JassElseClauseSyntax ElseClause(ImmutableArray statements) + { + return new JassElseClauseSyntax( + Token(JassSyntaxKind.ElseKeyword), + statements); + } + + public static JassElseClauseSyntax ElseClause(JassSyntaxToken elseToken, ImmutableArray statements) + { + ThrowHelper.ThrowIfInvalidToken(elseToken, JassSyntaxKind.ElseKeyword); + + return new JassElseClauseSyntax( + elseToken, + statements); + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/SyntaxFactory/ElseIfClauseDeclaratorFactory.cs b/src/War3Net.CodeAnalysis.Jass/SyntaxFactory/ElseIfClauseDeclaratorFactory.cs new file mode 100644 index 00000000..488db1b1 --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/SyntaxFactory/ElseIfClauseDeclaratorFactory.cs @@ -0,0 +1,33 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using War3Net.CodeAnalysis.Jass.Syntax; + +namespace War3Net.CodeAnalysis.Jass +{ + public static partial class JassSyntaxFactory + { + public static JassElseIfClauseDeclaratorSyntax ElseIfClauseDeclarator(JassExpressionSyntax condition) + { + return new JassElseIfClauseDeclaratorSyntax( + Token(JassSyntaxKind.ElseIfKeyword), + condition, + Token(JassSyntaxKind.ThenKeyword)); + } + + public static JassElseIfClauseDeclaratorSyntax ElseIfClauseDeclarator(JassSyntaxToken elseIfToken, JassExpressionSyntax condition, JassSyntaxToken thenToken) + { + ThrowHelper.ThrowIfInvalidToken(elseIfToken, JassSyntaxKind.ElseIfKeyword); + ThrowHelper.ThrowIfInvalidToken(thenToken, JassSyntaxKind.ThenKeyword); + + return new JassElseIfClauseDeclaratorSyntax( + elseIfToken, + condition, + thenToken); + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/SyntaxFactory/ElseIfClauseFactory.cs b/src/War3Net.CodeAnalysis.Jass/SyntaxFactory/ElseIfClauseFactory.cs new file mode 100644 index 00000000..50110c1a --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/SyntaxFactory/ElseIfClauseFactory.cs @@ -0,0 +1,59 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using System.Collections.Generic; +using System.Collections.Immutable; + +using War3Net.CodeAnalysis.Jass.Syntax; + +namespace War3Net.CodeAnalysis.Jass +{ + public static partial class JassSyntaxFactory + { + public static JassElseIfClauseSyntax ElseIfClause(JassElseIfClauseDeclaratorSyntax elseIfClauseDeclarator, params JassStatementSyntax[] statements) + { + return new JassElseIfClauseSyntax( + elseIfClauseDeclarator, + statements.ToImmutableArray()); + } + + public static JassElseIfClauseSyntax ElseIfClause(JassElseIfClauseDeclaratorSyntax elseIfClauseDeclarator, IEnumerable statements) + { + return new JassElseIfClauseSyntax( + elseIfClauseDeclarator, + statements.ToImmutableArray()); + } + + public static JassElseIfClauseSyntax ElseIfClause(JassElseIfClauseDeclaratorSyntax elseIfClauseDeclarator, ImmutableArray statements) + { + return new JassElseIfClauseSyntax( + elseIfClauseDeclarator, + statements); + } + + public static JassElseIfClauseSyntax ElseIfClause(JassExpressionSyntax condition, params JassStatementSyntax[] statements) + { + return new JassElseIfClauseSyntax( + ElseIfClauseDeclarator(condition), + statements.ToImmutableArray()); + } + + public static JassElseIfClauseSyntax ElseIfClause(JassExpressionSyntax condition, IEnumerable statements) + { + return new JassElseIfClauseSyntax( + ElseIfClauseDeclarator(condition), + statements.ToImmutableArray()); + } + + public static JassElseIfClauseSyntax ElseIfClause(JassExpressionSyntax condition, ImmutableArray statements) + { + return new JassElseIfClauseSyntax( + ElseIfClauseDeclarator(condition), + statements); + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/SyntaxFactory/EqualsValueClauseFactory.cs b/src/War3Net.CodeAnalysis.Jass/SyntaxFactory/EqualsValueClauseFactory.cs new file mode 100644 index 00000000..9a8f2ea3 --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/SyntaxFactory/EqualsValueClauseFactory.cs @@ -0,0 +1,30 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using War3Net.CodeAnalysis.Jass.Syntax; + +namespace War3Net.CodeAnalysis.Jass +{ + public static partial class JassSyntaxFactory + { + public static JassEqualsValueClauseSyntax EqualsValueClause(JassExpressionSyntax expression) + { + return new JassEqualsValueClauseSyntax( + Token(JassSyntaxKind.EqualsToken), + expression); + } + + public static JassEqualsValueClauseSyntax EqualsValueClause(JassSyntaxToken equalsToken, JassExpressionSyntax expression) + { + ThrowHelper.ThrowIfInvalidToken(equalsToken, JassSyntaxKind.EqualsToken); + + return new JassEqualsValueClauseSyntax( + equalsToken, + expression); + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/SyntaxFactory/ExitStatementFactory.cs b/src/War3Net.CodeAnalysis.Jass/SyntaxFactory/ExitStatementFactory.cs index ec2d6eb7..e8e66d87 100644 --- a/src/War3Net.CodeAnalysis.Jass/SyntaxFactory/ExitStatementFactory.cs +++ b/src/War3Net.CodeAnalysis.Jass/SyntaxFactory/ExitStatementFactory.cs @@ -11,9 +11,20 @@ namespace War3Net.CodeAnalysis.Jass { public static partial class JassSyntaxFactory { - public static JassExitStatementSyntax ExitStatement(IExpressionSyntax expression) + public static JassExitStatementSyntax ExitStatement(JassExpressionSyntax expression) { - return new JassExitStatementSyntax(expression); + return new JassExitStatementSyntax( + Token(JassSyntaxKind.ExitWhenKeyword), + expression); + } + + public static JassExitStatementSyntax ExitStatement(JassSyntaxToken exitWhenToken, JassExpressionSyntax expression) + { + ThrowHelper.ThrowIfInvalidToken(exitWhenToken, JassSyntaxKind.ExitWhenKeyword); + + return new JassExitStatementSyntax( + exitWhenToken, + expression); } } } \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/SyntaxFactory/FourCCLiteralExpressionFactory.cs b/src/War3Net.CodeAnalysis.Jass/SyntaxFactory/FourCCLiteralExpressionFactory.cs deleted file mode 100644 index 1e8f1081..00000000 --- a/src/War3Net.CodeAnalysis.Jass/SyntaxFactory/FourCCLiteralExpressionFactory.cs +++ /dev/null @@ -1,20 +0,0 @@ -// ------------------------------------------------------------------------------ -// -// Licensed under the MIT license. -// See the LICENSE file in the project root for more information. -// -// ------------------------------------------------------------------------------ - -using War3Net.CodeAnalysis.Jass.Extensions; -using War3Net.CodeAnalysis.Jass.Syntax; - -namespace War3Net.CodeAnalysis.Jass -{ - public static partial class JassSyntaxFactory - { - public static JassFourCCLiteralExpressionSyntax FourCCLiteralExpression(int value) - { - return new JassFourCCLiteralExpressionSyntax(value.InvertEndianness()); - } - } -} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/SyntaxFactory/FunctionDeclarationFactory.cs b/src/War3Net.CodeAnalysis.Jass/SyntaxFactory/FunctionDeclarationFactory.cs index cfe71fd4..fae99904 100644 --- a/src/War3Net.CodeAnalysis.Jass/SyntaxFactory/FunctionDeclarationFactory.cs +++ b/src/War3Net.CodeAnalysis.Jass/SyntaxFactory/FunctionDeclarationFactory.cs @@ -6,6 +6,7 @@ // ------------------------------------------------------------------------------ using System.Collections.Generic; +using System.Collections.Immutable; using War3Net.CodeAnalysis.Jass.Syntax; @@ -13,14 +14,38 @@ namespace War3Net.CodeAnalysis.Jass { public static partial class JassSyntaxFactory { - public static JassFunctionDeclarationSyntax FunctionDeclaration(JassFunctionDeclaratorSyntax functionDeclarator, IEnumerable statements) + public static JassFunctionDeclarationSyntax FunctionDeclaration(JassFunctionDeclaratorSyntax functionDeclarator, params JassStatementSyntax[] statements) { - return new JassFunctionDeclarationSyntax(functionDeclarator, StatementList(statements)); + return new JassFunctionDeclarationSyntax( + functionDeclarator, + statements.ToImmutableArray(), + Token(JassSyntaxKind.EndFunctionKeyword)); } - public static JassFunctionDeclarationSyntax FunctionDeclaration(JassFunctionDeclaratorSyntax functionDeclarator, params IStatementSyntax[] statements) + public static JassFunctionDeclarationSyntax FunctionDeclaration(JassFunctionDeclaratorSyntax functionDeclarator, IEnumerable statements) { - return new JassFunctionDeclarationSyntax(functionDeclarator, StatementList(statements)); + return new JassFunctionDeclarationSyntax( + functionDeclarator, + statements.ToImmutableArray(), + Token(JassSyntaxKind.EndFunctionKeyword)); + } + + public static JassFunctionDeclarationSyntax FunctionDeclaration(JassFunctionDeclaratorSyntax functionDeclarator, ImmutableArray statements) + { + return new JassFunctionDeclarationSyntax( + functionDeclarator, + statements, + Token(JassSyntaxKind.EndFunctionKeyword)); + } + + public static JassFunctionDeclarationSyntax FunctionDeclaration(JassFunctionDeclaratorSyntax functionDeclarator, ImmutableArray statements, JassSyntaxToken endFunctionToken) + { + ThrowHelper.ThrowIfInvalidToken(endFunctionToken, JassSyntaxKind.EndFunctionKeyword); + + return new JassFunctionDeclarationSyntax( + functionDeclarator, + statements, + endFunctionToken); } } } \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/SyntaxFactory/FunctionDeclaratorFactory.cs b/src/War3Net.CodeAnalysis.Jass/SyntaxFactory/FunctionDeclaratorFactory.cs index f4e18cc1..8c7bd54f 100644 --- a/src/War3Net.CodeAnalysis.Jass/SyntaxFactory/FunctionDeclaratorFactory.cs +++ b/src/War3Net.CodeAnalysis.Jass/SyntaxFactory/FunctionDeclaratorFactory.cs @@ -5,26 +5,567 @@ // // ------------------------------------------------------------------------------ +using System.Collections.Generic; + using War3Net.CodeAnalysis.Jass.Syntax; namespace War3Net.CodeAnalysis.Jass { public static partial class JassSyntaxFactory { + public static JassFunctionDeclaratorSyntax FunctionDeclarator(JassIdentifierNameSyntax identifierName) + { + return new JassFunctionDeclaratorSyntax( + null, + Token(JassSyntaxKind.FunctionKeyword), + identifierName, + JassEmptyParameterListSyntax.Value, + ReturnClause(JassPredefinedTypeSyntax.Nothing)); + } + public static JassFunctionDeclaratorSyntax FunctionDeclarator(string name) { return new JassFunctionDeclaratorSyntax( + null, + Token(JassSyntaxKind.FunctionKeyword), + ParseIdentifierName(name), + JassEmptyParameterListSyntax.Value, + ReturnClause(JassPredefinedTypeSyntax.Nothing)); + } + + public static JassFunctionDeclaratorSyntax FunctionDeclarator(JassIdentifierNameSyntax identifierName, JassReturnClauseSyntax returnClause) + { + return new JassFunctionDeclaratorSyntax( + null, + Token(JassSyntaxKind.FunctionKeyword), + identifierName, + JassEmptyParameterListSyntax.Value, + returnClause); + } + + public static JassFunctionDeclaratorSyntax FunctionDeclarator(JassIdentifierNameSyntax identifierName, JassTypeSyntax returnType) + { + return new JassFunctionDeclaratorSyntax( + null, + Token(JassSyntaxKind.FunctionKeyword), + identifierName, + JassEmptyParameterListSyntax.Value, + ReturnClause(returnType)); + } + + public static JassFunctionDeclaratorSyntax FunctionDeclarator(JassIdentifierNameSyntax identifierName, string returnType) + { + return new JassFunctionDeclaratorSyntax( + null, + Token(JassSyntaxKind.FunctionKeyword), + identifierName, + JassEmptyParameterListSyntax.Value, + ReturnClause(ParseTypeName(returnType))); + } + + public static JassFunctionDeclaratorSyntax FunctionDeclarator(string name, JassReturnClauseSyntax returnClause) + { + return new JassFunctionDeclaratorSyntax( + null, + Token(JassSyntaxKind.FunctionKeyword), + ParseIdentifierName(name), + JassEmptyParameterListSyntax.Value, + returnClause); + } + + public static JassFunctionDeclaratorSyntax FunctionDeclarator(string name, JassTypeSyntax returnType) + { + return new JassFunctionDeclaratorSyntax( + null, + Token(JassSyntaxKind.FunctionKeyword), + ParseIdentifierName(name), + JassEmptyParameterListSyntax.Value, + ReturnClause(returnType)); + } + + public static JassFunctionDeclaratorSyntax FunctionDeclarator(string name, string returnType) + { + return new JassFunctionDeclaratorSyntax( + null, + Token(JassSyntaxKind.FunctionKeyword), + ParseIdentifierName(name), + JassEmptyParameterListSyntax.Value, + ReturnClause(ParseTypeName(returnType))); + } + + public static JassFunctionDeclaratorSyntax FunctionDeclarator(JassIdentifierNameSyntax identifierName, JassReturnClauseSyntax returnClause, JassParameterListOrEmptyParameterListSyntax parameterList) + { + return new JassFunctionDeclaratorSyntax( + null, + Token(JassSyntaxKind.FunctionKeyword), + identifierName, + parameterList, + returnClause); + } + + public static JassFunctionDeclaratorSyntax FunctionDeclarator(JassIdentifierNameSyntax identifierName, JassReturnClauseSyntax returnClause, params JassParameterSyntax[] parameters) + { + return new JassFunctionDeclaratorSyntax( + null, + Token(JassSyntaxKind.FunctionKeyword), + identifierName, + ParameterList(parameters), + returnClause); + } + + public static JassFunctionDeclaratorSyntax FunctionDeclarator(JassIdentifierNameSyntax identifierName, JassReturnClauseSyntax returnClause, IEnumerable parameters) + { + return new JassFunctionDeclaratorSyntax( + null, + Token(JassSyntaxKind.FunctionKeyword), + identifierName, + ParameterList(parameters), + returnClause); + } + + public static JassFunctionDeclaratorSyntax FunctionDeclarator(JassIdentifierNameSyntax identifierName, JassTypeSyntax returnType, JassParameterListOrEmptyParameterListSyntax parameterList) + { + return new JassFunctionDeclaratorSyntax( + null, + Token(JassSyntaxKind.FunctionKeyword), + identifierName, + parameterList, + ReturnClause(returnType)); + } + + public static JassFunctionDeclaratorSyntax FunctionDeclarator(JassIdentifierNameSyntax identifierName, JassTypeSyntax returnType, params JassParameterSyntax[] parameters) + { + return new JassFunctionDeclaratorSyntax( + null, + Token(JassSyntaxKind.FunctionKeyword), + identifierName, + ParameterList(parameters), + ReturnClause(returnType)); + } + + public static JassFunctionDeclaratorSyntax FunctionDeclarator(JassIdentifierNameSyntax identifierName, JassTypeSyntax returnType, IEnumerable parameters) + { + return new JassFunctionDeclaratorSyntax( + null, + Token(JassSyntaxKind.FunctionKeyword), + identifierName, + ParameterList(parameters), + ReturnClause(returnType)); + } + + public static JassFunctionDeclaratorSyntax FunctionDeclarator(JassIdentifierNameSyntax identifierName, string returnType, JassParameterListOrEmptyParameterListSyntax parameterList) + { + return new JassFunctionDeclaratorSyntax( + null, + Token(JassSyntaxKind.FunctionKeyword), + identifierName, + parameterList, + ReturnClause(ParseTypeName(returnType))); + } + + public static JassFunctionDeclaratorSyntax FunctionDeclarator(JassIdentifierNameSyntax identifierName, string returnType, params JassParameterSyntax[] parameters) + { + return new JassFunctionDeclaratorSyntax( + null, + Token(JassSyntaxKind.FunctionKeyword), + identifierName, + ParameterList(parameters), + ReturnClause(ParseTypeName(returnType))); + } + + public static JassFunctionDeclaratorSyntax FunctionDeclarator(JassIdentifierNameSyntax identifierName, string returnType, IEnumerable parameters) + { + return new JassFunctionDeclaratorSyntax( + null, + Token(JassSyntaxKind.FunctionKeyword), + identifierName, + ParameterList(parameters), + ReturnClause(ParseTypeName(returnType))); + } + + public static JassFunctionDeclaratorSyntax FunctionDeclarator(string name, JassReturnClauseSyntax returnClause, JassParameterListOrEmptyParameterListSyntax parameterList) + { + return new JassFunctionDeclaratorSyntax( + null, + Token(JassSyntaxKind.FunctionKeyword), + ParseIdentifierName(name), + parameterList, + returnClause); + } + + public static JassFunctionDeclaratorSyntax FunctionDeclarator(string name, JassReturnClauseSyntax returnClause, params JassParameterSyntax[] parameters) + { + return new JassFunctionDeclaratorSyntax( + null, + Token(JassSyntaxKind.FunctionKeyword), + ParseIdentifierName(name), + ParameterList(parameters), + returnClause); + } + + public static JassFunctionDeclaratorSyntax FunctionDeclarator(string name, JassReturnClauseSyntax returnClause, IEnumerable parameters) + { + return new JassFunctionDeclaratorSyntax( + null, + Token(JassSyntaxKind.FunctionKeyword), + ParseIdentifierName(name), + ParameterList(parameters), + returnClause); + } + + public static JassFunctionDeclaratorSyntax FunctionDeclarator(string name, JassTypeSyntax returnType, JassParameterListOrEmptyParameterListSyntax parameterList) + { + return new JassFunctionDeclaratorSyntax( + null, + Token(JassSyntaxKind.FunctionKeyword), + ParseIdentifierName(name), + parameterList, + ReturnClause(returnType)); + } + + public static JassFunctionDeclaratorSyntax FunctionDeclarator(string name, JassTypeSyntax returnType, params JassParameterSyntax[] parameters) + { + return new JassFunctionDeclaratorSyntax( + null, + Token(JassSyntaxKind.FunctionKeyword), + ParseIdentifierName(name), + ParameterList(parameters), + ReturnClause(returnType)); + } + + public static JassFunctionDeclaratorSyntax FunctionDeclarator(string name, JassTypeSyntax returnType, IEnumerable parameters) + { + return new JassFunctionDeclaratorSyntax( + null, + Token(JassSyntaxKind.FunctionKeyword), + ParseIdentifierName(name), + ParameterList(parameters), + ReturnClause(returnType)); + } + + public static JassFunctionDeclaratorSyntax FunctionDeclarator(string name, string returnType, JassParameterListOrEmptyParameterListSyntax parameterList) + { + return new JassFunctionDeclaratorSyntax( + null, + Token(JassSyntaxKind.FunctionKeyword), + ParseIdentifierName(name), + parameterList, + ReturnClause(ParseTypeName(returnType))); + } + + public static JassFunctionDeclaratorSyntax FunctionDeclarator(string name, string returnType, params JassParameterSyntax[] parameters) + { + return new JassFunctionDeclaratorSyntax( + null, + Token(JassSyntaxKind.FunctionKeyword), + ParseIdentifierName(name), + ParameterList(parameters), + ReturnClause(ParseTypeName(returnType))); + } + + public static JassFunctionDeclaratorSyntax FunctionDeclarator(string name, string returnType, IEnumerable parameters) + { + return new JassFunctionDeclaratorSyntax( + null, + Token(JassSyntaxKind.FunctionKeyword), + ParseIdentifierName(name), + ParameterList(parameters), + ReturnClause(ParseTypeName(returnType))); + } + + public static JassFunctionDeclaratorSyntax FunctionDeclarator(JassSyntaxToken functionToken, JassIdentifierNameSyntax identifierName, JassReturnClauseSyntax returnClause, JassParameterListOrEmptyParameterListSyntax parameterList) + { + ThrowHelper.ThrowIfInvalidToken(functionToken, JassSyntaxKind.FunctionKeyword); + + return new JassFunctionDeclaratorSyntax( + null, + functionToken, + identifierName, + parameterList, + returnClause); + } + + public static JassFunctionDeclaratorSyntax ConstantFunctionDeclarator(JassIdentifierNameSyntax identifierName) + { + return new JassFunctionDeclaratorSyntax( + Token(JassSyntaxKind.ConstantKeyword), + Token(JassSyntaxKind.FunctionKeyword), + identifierName, + JassEmptyParameterListSyntax.Value, + ReturnClause(JassPredefinedTypeSyntax.Nothing)); + } + + public static JassFunctionDeclaratorSyntax ConstantFunctionDeclarator(string name) + { + return new JassFunctionDeclaratorSyntax( + Token(JassSyntaxKind.ConstantKeyword), + Token(JassSyntaxKind.FunctionKeyword), + ParseIdentifierName(name), + JassEmptyParameterListSyntax.Value, + ReturnClause(JassPredefinedTypeSyntax.Nothing)); + } + + public static JassFunctionDeclaratorSyntax ConstantFunctionDeclarator(JassIdentifierNameSyntax identifierName, JassReturnClauseSyntax returnClause) + { + return new JassFunctionDeclaratorSyntax( + Token(JassSyntaxKind.ConstantKeyword), + Token(JassSyntaxKind.FunctionKeyword), + identifierName, + JassEmptyParameterListSyntax.Value, + returnClause); + } + + public static JassFunctionDeclaratorSyntax ConstantFunctionDeclarator(JassIdentifierNameSyntax identifierName, JassTypeSyntax returnType) + { + return new JassFunctionDeclaratorSyntax( + Token(JassSyntaxKind.ConstantKeyword), + Token(JassSyntaxKind.FunctionKeyword), + identifierName, + JassEmptyParameterListSyntax.Value, + ReturnClause(returnType)); + } + + public static JassFunctionDeclaratorSyntax ConstantFunctionDeclarator(JassIdentifierNameSyntax identifierName, string returnType) + { + return new JassFunctionDeclaratorSyntax( + Token(JassSyntaxKind.ConstantKeyword), + Token(JassSyntaxKind.FunctionKeyword), + identifierName, + JassEmptyParameterListSyntax.Value, + ReturnClause(ParseTypeName(returnType))); + } + + public static JassFunctionDeclaratorSyntax ConstantFunctionDeclarator(string name, JassReturnClauseSyntax returnClause) + { + return new JassFunctionDeclaratorSyntax( + Token(JassSyntaxKind.ConstantKeyword), + Token(JassSyntaxKind.FunctionKeyword), + ParseIdentifierName(name), + JassEmptyParameterListSyntax.Value, + returnClause); + } + + public static JassFunctionDeclaratorSyntax ConstantFunctionDeclarator(string name, JassTypeSyntax returnType) + { + return new JassFunctionDeclaratorSyntax( + Token(JassSyntaxKind.ConstantKeyword), + Token(JassSyntaxKind.FunctionKeyword), + ParseIdentifierName(name), + JassEmptyParameterListSyntax.Value, + ReturnClause(returnType)); + } + + public static JassFunctionDeclaratorSyntax ConstantFunctionDeclarator(string name, string returnType) + { + return new JassFunctionDeclaratorSyntax( + Token(JassSyntaxKind.ConstantKeyword), + Token(JassSyntaxKind.FunctionKeyword), + ParseIdentifierName(name), + JassEmptyParameterListSyntax.Value, + ReturnClause(ParseTypeName(returnType))); + } + + public static JassFunctionDeclaratorSyntax ConstantFunctionDeclarator(JassIdentifierNameSyntax identifierName, JassReturnClauseSyntax returnClause, JassParameterListOrEmptyParameterListSyntax parameterList) + { + return new JassFunctionDeclaratorSyntax( + Token(JassSyntaxKind.ConstantKeyword), + Token(JassSyntaxKind.FunctionKeyword), + identifierName, + parameterList, + returnClause); + } + + public static JassFunctionDeclaratorSyntax ConstantFunctionDeclarator(JassIdentifierNameSyntax identifierName, JassReturnClauseSyntax returnClause, params JassParameterSyntax[] parameters) + { + return new JassFunctionDeclaratorSyntax( + Token(JassSyntaxKind.ConstantKeyword), + Token(JassSyntaxKind.FunctionKeyword), + identifierName, + ParameterList(parameters), + returnClause); + } + + public static JassFunctionDeclaratorSyntax ConstantFunctionDeclarator(JassIdentifierNameSyntax identifierName, JassReturnClauseSyntax returnClause, IEnumerable parameters) + { + return new JassFunctionDeclaratorSyntax( + Token(JassSyntaxKind.ConstantKeyword), + Token(JassSyntaxKind.FunctionKeyword), + identifierName, + ParameterList(parameters), + returnClause); + } + + public static JassFunctionDeclaratorSyntax ConstantFunctionDeclarator(JassIdentifierNameSyntax identifierName, JassTypeSyntax returnType, JassParameterListOrEmptyParameterListSyntax parameterList) + { + return new JassFunctionDeclaratorSyntax( + Token(JassSyntaxKind.ConstantKeyword), + Token(JassSyntaxKind.FunctionKeyword), + identifierName, + parameterList, + ReturnClause(returnType)); + } + + public static JassFunctionDeclaratorSyntax ConstantFunctionDeclarator(JassIdentifierNameSyntax identifierName, JassTypeSyntax returnType, params JassParameterSyntax[] parameters) + { + return new JassFunctionDeclaratorSyntax( + Token(JassSyntaxKind.ConstantKeyword), + Token(JassSyntaxKind.FunctionKeyword), + identifierName, + ParameterList(parameters), + ReturnClause(returnType)); + } + + public static JassFunctionDeclaratorSyntax ConstantFunctionDeclarator(JassIdentifierNameSyntax identifierName, JassTypeSyntax returnType, IEnumerable parameters) + { + return new JassFunctionDeclaratorSyntax( + Token(JassSyntaxKind.ConstantKeyword), + Token(JassSyntaxKind.FunctionKeyword), + identifierName, + ParameterList(parameters), + ReturnClause(returnType)); + } + + public static JassFunctionDeclaratorSyntax ConstantFunctionDeclarator(JassIdentifierNameSyntax identifierName, string returnType, JassParameterListOrEmptyParameterListSyntax parameterList) + { + return new JassFunctionDeclaratorSyntax( + Token(JassSyntaxKind.ConstantKeyword), + Token(JassSyntaxKind.FunctionKeyword), + identifierName, + parameterList, + ReturnClause(ParseTypeName(returnType))); + } + + public static JassFunctionDeclaratorSyntax ConstantFunctionDeclarator(JassIdentifierNameSyntax identifierName, string returnType, params JassParameterSyntax[] parameters) + { + return new JassFunctionDeclaratorSyntax( + Token(JassSyntaxKind.ConstantKeyword), + Token(JassSyntaxKind.FunctionKeyword), + identifierName, + ParameterList(parameters), + ReturnClause(ParseTypeName(returnType))); + } + + public static JassFunctionDeclaratorSyntax ConstantFunctionDeclarator(JassIdentifierNameSyntax identifierName, string returnType, IEnumerable parameters) + { + return new JassFunctionDeclaratorSyntax( + Token(JassSyntaxKind.ConstantKeyword), + Token(JassSyntaxKind.FunctionKeyword), + identifierName, + ParameterList(parameters), + ReturnClause(ParseTypeName(returnType))); + } + + public static JassFunctionDeclaratorSyntax ConstantFunctionDeclarator(string name, JassReturnClauseSyntax returnClause, JassParameterListOrEmptyParameterListSyntax parameterList) + { + return new JassFunctionDeclaratorSyntax( + Token(JassSyntaxKind.ConstantKeyword), + Token(JassSyntaxKind.FunctionKeyword), + ParseIdentifierName(name), + parameterList, + returnClause); + } + + public static JassFunctionDeclaratorSyntax ConstantFunctionDeclarator(string name, JassReturnClauseSyntax returnClause, params JassParameterSyntax[] parameters) + { + return new JassFunctionDeclaratorSyntax( + Token(JassSyntaxKind.ConstantKeyword), + Token(JassSyntaxKind.FunctionKeyword), + ParseIdentifierName(name), + ParameterList(parameters), + returnClause); + } + + public static JassFunctionDeclaratorSyntax ConstantFunctionDeclarator(string name, JassReturnClauseSyntax returnClause, IEnumerable parameters) + { + return new JassFunctionDeclaratorSyntax( + Token(JassSyntaxKind.ConstantKeyword), + Token(JassSyntaxKind.FunctionKeyword), + ParseIdentifierName(name), + ParameterList(parameters), + returnClause); + } + + public static JassFunctionDeclaratorSyntax ConstantFunctionDeclarator(string name, JassTypeSyntax returnType, JassParameterListOrEmptyParameterListSyntax parameterList) + { + return new JassFunctionDeclaratorSyntax( + Token(JassSyntaxKind.ConstantKeyword), + Token(JassSyntaxKind.FunctionKeyword), ParseIdentifierName(name), - JassParameterListSyntax.Empty, - JassTypeSyntax.Nothing); + parameterList, + ReturnClause(returnType)); + } + + public static JassFunctionDeclaratorSyntax ConstantFunctionDeclarator(string name, JassTypeSyntax returnType, params JassParameterSyntax[] parameters) + { + return new JassFunctionDeclaratorSyntax( + Token(JassSyntaxKind.ConstantKeyword), + Token(JassSyntaxKind.FunctionKeyword), + ParseIdentifierName(name), + ParameterList(parameters), + ReturnClause(returnType)); + } + + public static JassFunctionDeclaratorSyntax ConstantFunctionDeclarator(string name, JassTypeSyntax returnType, IEnumerable parameters) + { + return new JassFunctionDeclaratorSyntax( + Token(JassSyntaxKind.ConstantKeyword), + Token(JassSyntaxKind.FunctionKeyword), + ParseIdentifierName(name), + ParameterList(parameters), + ReturnClause(returnType)); + } + + public static JassFunctionDeclaratorSyntax ConstantFunctionDeclarator(string name, string returnType, JassParameterListOrEmptyParameterListSyntax parameterList) + { + return new JassFunctionDeclaratorSyntax( + Token(JassSyntaxKind.ConstantKeyword), + Token(JassSyntaxKind.FunctionKeyword), + ParseIdentifierName(name), + parameterList, + ReturnClause(ParseTypeName(returnType))); + } + + public static JassFunctionDeclaratorSyntax ConstantFunctionDeclarator(string name, string returnType, params JassParameterSyntax[] parameters) + { + return new JassFunctionDeclaratorSyntax( + Token(JassSyntaxKind.ConstantKeyword), + Token(JassSyntaxKind.FunctionKeyword), + ParseIdentifierName(name), + ParameterList(parameters), + ReturnClause(ParseTypeName(returnType))); + } + + public static JassFunctionDeclaratorSyntax ConstantFunctionDeclarator(string name, string returnType, IEnumerable parameters) + { + return new JassFunctionDeclaratorSyntax( + Token(JassSyntaxKind.ConstantKeyword), + Token(JassSyntaxKind.FunctionKeyword), + ParseIdentifierName(name), + ParameterList(parameters), + ReturnClause(ParseTypeName(returnType))); + } + + public static JassFunctionDeclaratorSyntax ConstantFunctionDeclarator(JassSyntaxToken constantToken, JassSyntaxToken functionToken, JassIdentifierNameSyntax identifierName, JassTypeSyntax returnType, JassParameterListOrEmptyParameterListSyntax parameterList) + { + ThrowHelper.ThrowIfInvalidToken(constantToken, JassSyntaxKind.ConstantKeyword); + ThrowHelper.ThrowIfInvalidToken(functionToken, JassSyntaxKind.FunctionKeyword); + + return new JassFunctionDeclaratorSyntax( + constantToken, + functionToken, + identifierName, + parameterList, + ReturnClause(returnType)); } public static JassFunctionDeclaratorSyntax ConditionFunctionDeclarator(string name) { return new JassFunctionDeclaratorSyntax( + null, + Token(JassSyntaxKind.FunctionKeyword), ParseIdentifierName(name), - JassParameterListSyntax.Empty, - JassTypeSyntax.Boolean); + JassEmptyParameterListSyntax.Value, + ReturnClause(JassPredefinedTypeSyntax.Boolean)); } } } \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/SyntaxFactory/FunctionReferenceExpressionFactory.cs b/src/War3Net.CodeAnalysis.Jass/SyntaxFactory/FunctionReferenceExpressionFactory.cs index 7c2469da..dc8a6140 100644 --- a/src/War3Net.CodeAnalysis.Jass/SyntaxFactory/FunctionReferenceExpressionFactory.cs +++ b/src/War3Net.CodeAnalysis.Jass/SyntaxFactory/FunctionReferenceExpressionFactory.cs @@ -11,9 +11,27 @@ namespace War3Net.CodeAnalysis.Jass { public static partial class JassSyntaxFactory { + public static JassFunctionReferenceExpressionSyntax FunctionReferenceExpression(JassIdentifierNameSyntax identifierName) + { + return new JassFunctionReferenceExpressionSyntax( + Token(JassSyntaxKind.FunctionKeyword), + identifierName); + } + public static JassFunctionReferenceExpressionSyntax FunctionReferenceExpression(string name) { - return new JassFunctionReferenceExpressionSyntax(ParseIdentifierName(name)); + return new JassFunctionReferenceExpressionSyntax( + Token(JassSyntaxKind.FunctionKeyword), + ParseIdentifierName(name)); + } + + public static JassFunctionReferenceExpressionSyntax FunctionReferenceExpression(JassSyntaxToken functionToken, JassIdentifierNameSyntax identifierName) + { + ThrowHelper.ThrowIfInvalidToken(functionToken, JassSyntaxKind.FunctionKeyword); + + return new JassFunctionReferenceExpressionSyntax( + functionToken, + identifierName); } } } \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/SyntaxFactory/GlobalConstantDeclarationFactory.cs b/src/War3Net.CodeAnalysis.Jass/SyntaxFactory/GlobalConstantDeclarationFactory.cs new file mode 100644 index 00000000..17f37fea --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/SyntaxFactory/GlobalConstantDeclarationFactory.cs @@ -0,0 +1,97 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using War3Net.CodeAnalysis.Jass.Syntax; + +namespace War3Net.CodeAnalysis.Jass +{ + public static partial class JassSyntaxFactory + { + public static JassGlobalConstantDeclarationSyntax GlobalConstantDeclaration(JassTypeSyntax type, JassIdentifierNameSyntax identifierName, JassEqualsValueClauseSyntax value) + { + return new JassGlobalConstantDeclarationSyntax( + Token(JassSyntaxKind.ConstantKeyword), + type, + identifierName, + value); + } + + public static JassGlobalConstantDeclarationSyntax GlobalConstantDeclaration(JassTypeSyntax type, JassIdentifierNameSyntax identifierName, JassExpressionSyntax value) + { + return new JassGlobalConstantDeclarationSyntax( + Token(JassSyntaxKind.ConstantKeyword), + type, + identifierName, + EqualsValueClause(value)); + } + + public static JassGlobalConstantDeclarationSyntax GlobalConstantDeclaration(JassTypeSyntax type, string name, JassEqualsValueClauseSyntax value) + { + return new JassGlobalConstantDeclarationSyntax( + Token(JassSyntaxKind.ConstantKeyword), + type, + ParseIdentifierName(name), + value); + } + + public static JassGlobalConstantDeclarationSyntax GlobalConstantDeclaration(JassTypeSyntax type, string name, JassExpressionSyntax value) + { + return new JassGlobalConstantDeclarationSyntax( + Token(JassSyntaxKind.ConstantKeyword), + type, + ParseIdentifierName(name), + EqualsValueClause(value)); + } + + public static JassGlobalConstantDeclarationSyntax GlobalConstantDeclaration(string type, JassIdentifierNameSyntax identifierName, JassEqualsValueClauseSyntax value) + { + return new JassGlobalConstantDeclarationSyntax( + Token(JassSyntaxKind.ConstantKeyword), + ParseTypeName(type), + identifierName, + value); + } + + public static JassGlobalConstantDeclarationSyntax GlobalConstantDeclaration(string type, JassIdentifierNameSyntax identifierName, JassExpressionSyntax value) + { + return new JassGlobalConstantDeclarationSyntax( + Token(JassSyntaxKind.ConstantKeyword), + ParseTypeName(type), + identifierName, + EqualsValueClause(value)); + } + + public static JassGlobalConstantDeclarationSyntax GlobalConstantDeclaration(string type, string name, JassEqualsValueClauseSyntax value) + { + return new JassGlobalConstantDeclarationSyntax( + Token(JassSyntaxKind.ConstantKeyword), + ParseTypeName(type), + ParseIdentifierName(name), + value); + } + + public static JassGlobalConstantDeclarationSyntax GlobalConstantDeclaration(string type, string name, JassExpressionSyntax value) + { + return new JassGlobalConstantDeclarationSyntax( + Token(JassSyntaxKind.ConstantKeyword), + ParseTypeName(type), + ParseIdentifierName(name), + EqualsValueClause(value)); + } + + public static JassGlobalConstantDeclarationSyntax GlobalConstantDeclaration(JassSyntaxToken constantToken, JassTypeSyntax type, JassIdentifierNameSyntax identifierName, JassEqualsValueClauseSyntax value) + { + ThrowHelper.ThrowIfInvalidToken(constantToken, JassSyntaxKind.ConstantKeyword); + + return new JassGlobalConstantDeclarationSyntax( + constantToken, + type, + identifierName, + value); + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/SyntaxFactory/GlobalDeclarationFactory.cs b/src/War3Net.CodeAnalysis.Jass/SyntaxFactory/GlobalDeclarationFactory.cs deleted file mode 100644 index 30037a70..00000000 --- a/src/War3Net.CodeAnalysis.Jass/SyntaxFactory/GlobalDeclarationFactory.cs +++ /dev/null @@ -1,39 +0,0 @@ -// ------------------------------------------------------------------------------ -// -// Licensed under the MIT license. -// See the LICENSE file in the project root for more information. -// -// ------------------------------------------------------------------------------ - -using War3Net.CodeAnalysis.Jass.Syntax; - -namespace War3Net.CodeAnalysis.Jass -{ - public static partial class JassSyntaxFactory - { - public static JassGlobalDeclarationSyntax GlobalDeclaration(JassTypeSyntax type, string name) - { - return new JassGlobalDeclarationSyntax( - new JassVariableDeclaratorSyntax( - type, - ParseIdentifierName(name), - null)); - } - - public static JassGlobalDeclarationSyntax GlobalDeclaration(JassTypeSyntax type, string name, IExpressionSyntax value) - { - return new JassGlobalDeclarationSyntax( - new JassVariableDeclaratorSyntax( - type, - ParseIdentifierName(name), - new JassEqualsValueClauseSyntax(value))); - } - - public static JassGlobalDeclarationSyntax GlobalArrayDeclaration(JassTypeSyntax type, string name) - { - return new JassGlobalDeclarationSyntax( - new JassArrayDeclaratorSyntax( - type, ParseIdentifierName(name))); - } - } -} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/SyntaxFactory/GlobalVariableDeclarationFactory.cs b/src/War3Net.CodeAnalysis.Jass/SyntaxFactory/GlobalVariableDeclarationFactory.cs new file mode 100644 index 00000000..b5934b8f --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/SyntaxFactory/GlobalVariableDeclarationFactory.cs @@ -0,0 +1,155 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using War3Net.CodeAnalysis.Jass.Syntax; + +namespace War3Net.CodeAnalysis.Jass +{ + public static partial class JassSyntaxFactory + { + public static JassGlobalVariableDeclarationSyntax GlobalVariableDeclaration(JassTypeSyntax type, JassIdentifierNameSyntax identifierName) + { + return new JassGlobalVariableDeclarationSyntax( + VariableDeclarator( + type, + identifierName)); + } + + public static JassGlobalVariableDeclarationSyntax GlobalVariableDeclaration(JassTypeSyntax type, string name) + { + return new JassGlobalVariableDeclarationSyntax( + VariableDeclarator( + type, + ParseIdentifierName(name))); + } + + public static JassGlobalVariableDeclarationSyntax GlobalVariableDeclaration(string type, JassIdentifierNameSyntax identifierName) + { + return new JassGlobalVariableDeclarationSyntax( + VariableDeclarator( + ParseTypeName(type), + identifierName)); + } + + public static JassGlobalVariableDeclarationSyntax GlobalVariableDeclaration(string type, string name) + { + return new JassGlobalVariableDeclarationSyntax( + VariableDeclarator( + ParseTypeName(type), + ParseIdentifierName(name))); + } + + public static JassGlobalVariableDeclarationSyntax GlobalVariableDeclaration(JassTypeSyntax type, JassIdentifierNameSyntax identifierName, JassEqualsValueClauseSyntax value) + { + return new JassGlobalVariableDeclarationSyntax( + VariableDeclarator( + type, + identifierName, + value)); + } + + public static JassGlobalVariableDeclarationSyntax GlobalVariableDeclaration(JassTypeSyntax type, JassIdentifierNameSyntax identifierName, JassExpressionSyntax value) + { + return new JassGlobalVariableDeclarationSyntax( + VariableDeclarator( + type, + identifierName, + EqualsValueClause(value))); + } + + public static JassGlobalVariableDeclarationSyntax GlobalVariableDeclaration(JassTypeSyntax type, string name, JassEqualsValueClauseSyntax value) + { + return new JassGlobalVariableDeclarationSyntax( + VariableDeclarator( + type, + ParseIdentifierName(name), + value)); + } + + public static JassGlobalVariableDeclarationSyntax GlobalVariableDeclaration(JassTypeSyntax type, string name, JassExpressionSyntax value) + { + return new JassGlobalVariableDeclarationSyntax( + VariableDeclarator( + type, + ParseIdentifierName(name), + EqualsValueClause(value))); + } + + public static JassGlobalVariableDeclarationSyntax GlobalVariableDeclaration(string type, JassIdentifierNameSyntax identifierName, JassEqualsValueClauseSyntax value) + { + return new JassGlobalVariableDeclarationSyntax( + VariableDeclarator( + ParseTypeName(type), + identifierName, + value)); + } + + public static JassGlobalVariableDeclarationSyntax GlobalVariableDeclaration(string type, JassIdentifierNameSyntax identifierName, JassExpressionSyntax value) + { + return new JassGlobalVariableDeclarationSyntax( + VariableDeclarator( + ParseTypeName(type), + identifierName, + EqualsValueClause(value))); + } + + public static JassGlobalVariableDeclarationSyntax GlobalVariableDeclaration(string type, string name, JassEqualsValueClauseSyntax value) + { + return new JassGlobalVariableDeclarationSyntax( + VariableDeclarator( + ParseTypeName(type), + ParseIdentifierName(name), + value)); + } + + public static JassGlobalVariableDeclarationSyntax GlobalVariableDeclaration(string type, string name, JassExpressionSyntax value) + { + return new JassGlobalVariableDeclarationSyntax( + VariableDeclarator( + ParseTypeName(type), + ParseIdentifierName(name), + EqualsValueClause(value))); + } + + public static JassGlobalVariableDeclarationSyntax GlobalArrayDeclaration(JassTypeSyntax type, JassIdentifierNameSyntax identifierName) + { + return new JassGlobalVariableDeclarationSyntax( + ArrayDeclarator( + type, + identifierName)); + } + + public static JassGlobalVariableDeclarationSyntax GlobalArrayDeclaration(JassTypeSyntax type, string name) + { + return new JassGlobalVariableDeclarationSyntax( + ArrayDeclarator( + type, + ParseIdentifierName(name))); + } + + public static JassGlobalVariableDeclarationSyntax GlobalArrayDeclaration(string type, JassIdentifierNameSyntax identifierName) + { + return new JassGlobalVariableDeclarationSyntax( + ArrayDeclarator( + ParseTypeName(type), + identifierName)); + } + + public static JassGlobalVariableDeclarationSyntax GlobalArrayDeclaration(string type, string name) + { + return new JassGlobalVariableDeclarationSyntax( + ArrayDeclarator( + ParseTypeName(type), + ParseIdentifierName(name))); + } + + public static JassGlobalVariableDeclarationSyntax GlobalDeclaration(JassVariableOrArrayDeclaratorSyntax declarator) + { + return new JassGlobalVariableDeclarationSyntax(declarator); + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/SyntaxFactory/GlobalsDeclarationFactory.cs b/src/War3Net.CodeAnalysis.Jass/SyntaxFactory/GlobalsDeclarationFactory.cs new file mode 100644 index 00000000..d56ea85f --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/SyntaxFactory/GlobalsDeclarationFactory.cs @@ -0,0 +1,52 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using System.Collections.Generic; +using System.Collections.Immutable; + +using War3Net.CodeAnalysis.Jass.Syntax; + +namespace War3Net.CodeAnalysis.Jass +{ + public static partial class JassSyntaxFactory + { + public static JassGlobalsDeclarationSyntax GlobalsDeclaration(params JassGlobalDeclarationSyntax[] globalDeclarations) + { + return new JassGlobalsDeclarationSyntax( + Token(JassSyntaxKind.GlobalsKeyword), + globalDeclarations.ToImmutableArray(), + Token(JassSyntaxKind.EndGlobalsKeyword)); + } + + public static JassGlobalsDeclarationSyntax GlobalsDeclaration(IEnumerable globalDeclarations) + { + return new JassGlobalsDeclarationSyntax( + Token(JassSyntaxKind.GlobalsKeyword), + globalDeclarations.ToImmutableArray(), + Token(JassSyntaxKind.EndGlobalsKeyword)); + } + + public static JassGlobalsDeclarationSyntax GlobalsDeclaration(ImmutableArray globalDeclarations) + { + return new JassGlobalsDeclarationSyntax( + Token(JassSyntaxKind.GlobalsKeyword), + globalDeclarations, + Token(JassSyntaxKind.EndGlobalsKeyword)); + } + + public static JassGlobalsDeclarationSyntax GlobalsDeclaration(JassSyntaxToken globalsToken, ImmutableArray globalDeclarations, JassSyntaxToken endGlobalsToken) + { + ThrowHelper.ThrowIfInvalidToken(globalsToken, JassSyntaxKind.GlobalsKeyword); + ThrowHelper.ThrowIfInvalidToken(endGlobalsToken, JassSyntaxKind.EndGlobalsKeyword); + + return new JassGlobalsDeclarationSyntax( + globalsToken, + globalDeclarations, + endGlobalsToken); + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/SyntaxFactory/IdentifierNameFactory.cs b/src/War3Net.CodeAnalysis.Jass/SyntaxFactory/IdentifierNameFactory.cs new file mode 100644 index 00000000..65fa8099 --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/SyntaxFactory/IdentifierNameFactory.cs @@ -0,0 +1,46 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using System; + +using War3Net.CodeAnalysis.Jass.Syntax; + +namespace War3Net.CodeAnalysis.Jass +{ + public static partial class JassSyntaxFactory + { + public static JassIdentifierNameSyntax IdentifierName(JassSyntaxToken identifier) + { + if (identifier.SyntaxKind != JassSyntaxKind.IdentifierToken) + { + throw new ArgumentException("The token's syntax kind must be 'IdentifierToken'.", nameof(identifier)); + } + + return new JassIdentifierNameSyntax(identifier); + } + + public static JassIdentifierNameSyntax IdentifierName(string identifierName) + { + return new JassIdentifierNameSyntax(Identifier(identifierName)); + } + + public static JassSyntaxToken Identifier(string text) + { + return Identifier(JassSyntaxTriviaList.Empty, text, JassSyntaxTriviaList.Empty); + } + + public static JassSyntaxToken Identifier(JassSyntaxTriviaList leadingTrivia, string text, JassSyntaxTriviaList trailingTrivia) + { + if (!JassSyntaxFacts.IsValidIdentifier(text)) + { + throw new ArgumentException($"'{text}' is not a valid identifier.", nameof(text)); + } + + return new JassSyntaxToken(leadingTrivia, JassSyntaxKind.IdentifierToken, text, trailingTrivia); + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/SyntaxFactory/IfClauseDeclaratorFactory.cs b/src/War3Net.CodeAnalysis.Jass/SyntaxFactory/IfClauseDeclaratorFactory.cs new file mode 100644 index 00000000..f716a427 --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/SyntaxFactory/IfClauseDeclaratorFactory.cs @@ -0,0 +1,33 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using War3Net.CodeAnalysis.Jass.Syntax; + +namespace War3Net.CodeAnalysis.Jass +{ + public static partial class JassSyntaxFactory + { + public static JassIfClauseDeclaratorSyntax IfClauseDeclarator(JassExpressionSyntax condition) + { + return new JassIfClauseDeclaratorSyntax( + Token(JassSyntaxKind.IfKeyword), + condition, + Token(JassSyntaxKind.ThenKeyword)); + } + + public static JassIfClauseDeclaratorSyntax IfClauseDeclarator(JassSyntaxToken ifToken, JassExpressionSyntax condition, JassSyntaxToken thenToken) + { + ThrowHelper.ThrowIfInvalidToken(ifToken, JassSyntaxKind.IfKeyword); + ThrowHelper.ThrowIfInvalidToken(thenToken, JassSyntaxKind.ThenKeyword); + + return new JassIfClauseDeclaratorSyntax( + ifToken, + condition, + thenToken); + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/SyntaxFactory/IfClauseFactory.cs b/src/War3Net.CodeAnalysis.Jass/SyntaxFactory/IfClauseFactory.cs new file mode 100644 index 00000000..8f694457 --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/SyntaxFactory/IfClauseFactory.cs @@ -0,0 +1,59 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using System.Collections.Generic; +using System.Collections.Immutable; + +using War3Net.CodeAnalysis.Jass.Syntax; + +namespace War3Net.CodeAnalysis.Jass +{ + public static partial class JassSyntaxFactory + { + public static JassIfClauseSyntax IfClause(JassExpressionSyntax condition, params JassStatementSyntax[] statements) + { + return new JassIfClauseSyntax( + IfClauseDeclarator(condition), + statements.ToImmutableArray()); + } + + public static JassIfClauseSyntax IfClause(JassExpressionSyntax condition, IEnumerable statements) + { + return new JassIfClauseSyntax( + IfClauseDeclarator(condition), + statements.ToImmutableArray()); + } + + public static JassIfClauseSyntax IfClause(JassExpressionSyntax condition, ImmutableArray statements) + { + return new JassIfClauseSyntax( + IfClauseDeclarator(condition), + statements); + } + + public static JassIfClauseSyntax IfClause(JassIfClauseDeclaratorSyntax ifClauseDeclarator, params JassStatementSyntax[] statements) + { + return new JassIfClauseSyntax( + ifClauseDeclarator, + statements.ToImmutableArray()); + } + + public static JassIfClauseSyntax IfClause(JassIfClauseDeclaratorSyntax ifClauseDeclarator, IEnumerable statements) + { + return new JassIfClauseSyntax( + ifClauseDeclarator, + statements.ToImmutableArray()); + } + + public static JassIfClauseSyntax IfClause(JassIfClauseDeclaratorSyntax ifClauseDeclarator, ImmutableArray statements) + { + return new JassIfClauseSyntax( + ifClauseDeclarator, + statements); + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/SyntaxFactory/IfStatementFactory.cs b/src/War3Net.CodeAnalysis.Jass/SyntaxFactory/IfStatementFactory.cs index 20248c67..65f61974 100644 --- a/src/War3Net.CodeAnalysis.Jass/SyntaxFactory/IfStatementFactory.cs +++ b/src/War3Net.CodeAnalysis.Jass/SyntaxFactory/IfStatementFactory.cs @@ -14,49 +14,159 @@ namespace War3Net.CodeAnalysis.Jass { public static partial class JassSyntaxFactory { - public static JassIfStatementSyntax IfStatement(IExpressionSyntax condition, JassStatementListSyntax body) + public static JassIfStatementSyntax IfStatement(JassIfClauseSyntax ifClause) { return new JassIfStatementSyntax( - condition, - body, - ImmutableArray.Create(), - null); + ifClause, + ImmutableArray.Empty, + null, + Token(JassSyntaxKind.EndIfKeyword)); } - public static JassIfStatementSyntax IfStatement(IExpressionSyntax condition, params IStatementSyntax[] body) + public static JassIfStatementSyntax IfStatement(JassExpressionSyntax condition, params JassStatementSyntax[] statements) { return new JassIfStatementSyntax( - condition, - StatementList(body), - ImmutableArray.Create(), - null); + IfClause(condition, statements), + ImmutableArray.Empty, + null, + Token(JassSyntaxKind.EndIfKeyword)); } - public static JassIfStatementSyntax IfStatement(IExpressionSyntax condition, JassStatementListSyntax body, JassElseClauseSyntax elseClause) + public static JassIfStatementSyntax IfStatement(JassExpressionSyntax condition, IEnumerable statements) { return new JassIfStatementSyntax( - condition, - body, - ImmutableArray.Create(), - elseClause); + IfClause(condition, statements), + ImmutableArray.Empty, + null, + Token(JassSyntaxKind.EndIfKeyword)); } - public static JassIfStatementSyntax IfStatement(IExpressionSyntax condition, JassStatementListSyntax body, params JassElseIfClauseSyntax[] elseIfClauses) + public static JassIfStatementSyntax IfStatement(JassExpressionSyntax condition, ImmutableArray statements) { return new JassIfStatementSyntax( - condition, - body, + IfClause(condition, statements), + ImmutableArray.Empty, + null, + Token(JassSyntaxKind.EndIfKeyword)); + } + + public static JassIfStatementSyntax IfStatement(JassIfClauseSyntax ifClause, JassElseClauseSyntax? elseClause) + { + return new JassIfStatementSyntax( + ifClause, + ImmutableArray.Empty, + elseClause, + Token(JassSyntaxKind.EndIfKeyword)); + } + + public static JassIfStatementSyntax IfStatement(JassIfClauseSyntax ifClause, params JassElseIfClauseSyntax[] elseIfClauses) + { + return new JassIfStatementSyntax( + ifClause, + elseIfClauses.ToImmutableArray(), + null, + Token(JassSyntaxKind.EndIfKeyword)); + } + + public static JassIfStatementSyntax IfStatement(JassIfClauseSyntax ifClause, IEnumerable elseIfClauses) + { + return new JassIfStatementSyntax( + ifClause, + elseIfClauses.ToImmutableArray(), + null, + Token(JassSyntaxKind.EndIfKeyword)); + } + + public static JassIfStatementSyntax IfStatement(JassIfClauseSyntax ifClause, ImmutableArray elseIfClauses) + { + return new JassIfStatementSyntax( + ifClause, + elseIfClauses, + null, + Token(JassSyntaxKind.EndIfKeyword)); + } + + public static JassIfStatementSyntax IfStatement(JassExpressionSyntax condition, IEnumerable statements, JassElseClauseSyntax? elseClause) + { + return new JassIfStatementSyntax( + IfClause(condition, statements), + ImmutableArray.Empty, + elseClause, + Token(JassSyntaxKind.EndIfKeyword)); + } + + public static JassIfStatementSyntax IfStatement(JassExpressionSyntax condition, ImmutableArray statements, JassElseClauseSyntax? elseClause) + { + return new JassIfStatementSyntax( + IfClause(condition, statements), + ImmutableArray.Empty, + elseClause, + Token(JassSyntaxKind.EndIfKeyword)); + } + + public static JassIfStatementSyntax IfStatement(JassIfClauseSyntax ifClause, IEnumerable elseIfClauses, JassElseClauseSyntax? elseClause) + { + return new JassIfStatementSyntax( + ifClause, + elseIfClauses.ToImmutableArray(), + elseClause, + Token(JassSyntaxKind.EndIfKeyword)); + } + + public static JassIfStatementSyntax IfStatement(JassIfClauseSyntax ifClause, ImmutableArray elseIfClauses, JassElseClauseSyntax? elseClause) + { + return new JassIfStatementSyntax( + ifClause, + elseIfClauses, + elseClause, + Token(JassSyntaxKind.EndIfKeyword)); + } + + public static JassIfStatementSyntax IfStatement(JassExpressionSyntax condition, IEnumerable statements, IEnumerable elseIfClauses, JassElseClauseSyntax? elseClause) + { + return new JassIfStatementSyntax( + IfClause(condition, statements), elseIfClauses.ToImmutableArray(), - null); + elseClause, + Token(JassSyntaxKind.EndIfKeyword)); } - public static JassIfStatementSyntax IfStatement(IExpressionSyntax condition, JassStatementListSyntax body, IEnumerable elseIfClauses, JassElseClauseSyntax elseClause) + public static JassIfStatementSyntax IfStatement(JassExpressionSyntax condition, IEnumerable statements, ImmutableArray elseIfClauses, JassElseClauseSyntax? elseClause) { return new JassIfStatementSyntax( - condition, - body, + IfClause(condition, statements), + elseIfClauses, + elseClause, + Token(JassSyntaxKind.EndIfKeyword)); + } + + public static JassIfStatementSyntax IfStatement(JassExpressionSyntax condition, ImmutableArray statements, IEnumerable elseIfClauses, JassElseClauseSyntax? elseClause) + { + return new JassIfStatementSyntax( + IfClause(condition, statements), elseIfClauses.ToImmutableArray(), - elseClause); + elseClause, + Token(JassSyntaxKind.EndIfKeyword)); + } + + public static JassIfStatementSyntax IfStatement(JassExpressionSyntax condition, ImmutableArray statements, ImmutableArray elseIfClauses, JassElseClauseSyntax? elseClause) + { + return new JassIfStatementSyntax( + IfClause(condition, statements), + elseIfClauses, + elseClause, + Token(JassSyntaxKind.EndIfKeyword)); + } + + public static JassIfStatementSyntax IfStatement(JassIfClauseSyntax ifClause, ImmutableArray elseIfClauses, JassElseClauseSyntax? elseClause, JassSyntaxToken endIfToken) + { + ThrowHelper.ThrowIfInvalidToken(endIfToken, JassSyntaxKind.EndIfKeyword); + + return new JassIfStatementSyntax( + ifClause, + elseIfClauses, + elseClause, + endIfToken); } } } \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/SyntaxFactory/InvocationExpressionFactory.cs b/src/War3Net.CodeAnalysis.Jass/SyntaxFactory/InvocationExpressionFactory.cs index a3f71bb3..ea41eaf6 100644 --- a/src/War3Net.CodeAnalysis.Jass/SyntaxFactory/InvocationExpressionFactory.cs +++ b/src/War3Net.CodeAnalysis.Jass/SyntaxFactory/InvocationExpressionFactory.cs @@ -5,20 +5,50 @@ // // ------------------------------------------------------------------------------ +using System.Collections.Generic; + using War3Net.CodeAnalysis.Jass.Syntax; namespace War3Net.CodeAnalysis.Jass { public static partial class JassSyntaxFactory { - public static JassInvocationExpressionSyntax InvocationExpression(string name, JassArgumentListSyntax arguments) + public static JassInvocationExpressionSyntax InvocationExpression(JassIdentifierNameSyntax identifierName, JassArgumentListSyntax argumentList) + { + return new JassInvocationExpressionSyntax( + identifierName, + argumentList); + } + + public static JassInvocationExpressionSyntax InvocationExpression(JassIdentifierNameSyntax identifierName, params JassExpressionSyntax[] arguments) + { + return new JassInvocationExpressionSyntax( + identifierName, + ArgumentList(arguments)); + } + + public static JassInvocationExpressionSyntax InvocationExpression(JassIdentifierNameSyntax identifierName, IEnumerable arguments) + { + return new JassInvocationExpressionSyntax( + identifierName, + ArgumentList(arguments)); + } + + public static JassInvocationExpressionSyntax InvocationExpression(string name, JassArgumentListSyntax argumentList) + { + return new JassInvocationExpressionSyntax( + ParseIdentifierName(name), + argumentList); + } + + public static JassInvocationExpressionSyntax InvocationExpression(string name, params JassExpressionSyntax[] arguments) { return new JassInvocationExpressionSyntax( ParseIdentifierName(name), - arguments); + ArgumentList(arguments)); } - public static JassInvocationExpressionSyntax InvocationExpression(string name, params IExpressionSyntax[] arguments) + public static JassInvocationExpressionSyntax InvocationExpression(string name, IEnumerable arguments) { return new JassInvocationExpressionSyntax( ParseIdentifierName(name), diff --git a/src/War3Net.CodeAnalysis.Jass/SyntaxFactory/LiteralExpressionFactory.cs b/src/War3Net.CodeAnalysis.Jass/SyntaxFactory/LiteralExpressionFactory.cs index ea47df9e..d28e8afa 100644 --- a/src/War3Net.CodeAnalysis.Jass/SyntaxFactory/LiteralExpressionFactory.cs +++ b/src/War3Net.CodeAnalysis.Jass/SyntaxFactory/LiteralExpressionFactory.cs @@ -5,53 +5,84 @@ // // ------------------------------------------------------------------------------ +using System; using System.Globalization; +using War3Net.CodeAnalysis.Jass.Extensions; using War3Net.CodeAnalysis.Jass.Syntax; namespace War3Net.CodeAnalysis.Jass { public static partial class JassSyntaxFactory { - public static IExpressionSyntax LiteralExpression(string? value) + public static JassSyntaxToken Literal(string? value) { - return value is null ? JassNullLiteralExpressionSyntax.Value : new JassStringLiteralExpressionSyntax(value); + return Literal(JassSyntaxTriviaList.Empty, value, JassSyntaxTriviaList.Empty); } - public static IExpressionSyntax LiteralExpression(int value) + public static JassSyntaxToken Literal(JassSyntaxTriviaList leadingTrivia, string? value, JassSyntaxTriviaList trailingTrivia) { - if (value == 0) - { - return new JassOctalLiteralExpressionSyntax(0); - } + return value is null + ? Token(leadingTrivia, JassSyntaxKind.NullKeyword, trailingTrivia) + : Token(leadingTrivia, JassSyntaxKind.StringLiteralToken, $"\"{value}\"", trailingTrivia); + } - if (value < 0) - { - return UnaryMinusExpression(new JassDecimalLiteralExpressionSyntax(-value)); - } + public static JassSyntaxToken Literal(int value) + { + return Literal(JassSyntaxTriviaList.Empty, value, JassSyntaxTriviaList.Empty); + } - return new JassDecimalLiteralExpressionSyntax(value); + public static JassSyntaxToken Literal(JassSyntaxTriviaList leadingTrivia, int value, JassSyntaxTriviaList trailingTrivia) + { + return Token(leadingTrivia, JassSyntaxKind.DecimalLiteralToken, value.ToString(CultureInfo.InvariantCulture), trailingTrivia); } - public static IExpressionSyntax LiteralExpression(float value, int precision = 1) + public static JassSyntaxToken Literal(float value, int precision = 1) { - var valueAsString = value.ToString($"F{precision}", CultureInfo.InvariantCulture).Split('.', 2); - if (precision == 0 && valueAsString.Length == 1) - { - valueAsString = new string[] { valueAsString[0], string.Empty }; - } + return Literal(JassSyntaxTriviaList.Empty, value, precision, JassSyntaxTriviaList.Empty); + } - if (valueAsString[0].StartsWith('-')) + public static JassSyntaxToken Literal(JassSyntaxTriviaList leadingTrivia, float value, int precision, JassSyntaxTriviaList trailingTrivia) + { + var valueAsString = value.ToString($"F{precision}", CultureInfo.InvariantCulture); + if (precision == 0) { - return UnaryMinusExpression(new JassRealLiteralExpressionSyntax(valueAsString[0].TrimStart('-'), valueAsString[1])); + valueAsString += JassSymbol.Dot; } - return new JassRealLiteralExpressionSyntax(valueAsString[0], valueAsString[1]); + return Token(leadingTrivia, JassSyntaxKind.RealLiteralToken, valueAsString, trailingTrivia); } - public static IExpressionSyntax LiteralExpression(bool value) + public static JassSyntaxToken Literal(bool value) { - return value ? JassBooleanLiteralExpressionSyntax.True : JassBooleanLiteralExpressionSyntax.False; + return Literal(JassSyntaxTriviaList.Empty, value, JassSyntaxTriviaList.Empty); + } + + public static JassSyntaxToken Literal(JassSyntaxTriviaList leadingTrivia, bool value, JassSyntaxTriviaList trailingTrivia) + { + return value + ? Token(leadingTrivia, JassSyntaxKind.TrueKeyword, JassKeyword.True, trailingTrivia) + : Token(leadingTrivia, JassSyntaxKind.FalseKeyword, JassKeyword.False, trailingTrivia); + } + + public static JassSyntaxToken FourCCLiteral(int value) + { + return FourCCLiteral(JassSyntaxTriviaList.Empty, value, JassSyntaxTriviaList.Empty); + } + + public static JassSyntaxToken FourCCLiteral(JassSyntaxTriviaList leadingTrivia, int value, JassSyntaxTriviaList trailingTrivia) + { + return Token(leadingTrivia, JassSyntaxKind.FourCCLiteralToken, $"'{value.ToJassRawcode()}'", trailingTrivia); + } + + public static JassExpressionSyntax LiteralExpression(JassSyntaxToken token) + { + if (!JassSyntaxFacts.IsLiteralExpressionToken(token.SyntaxKind)) + { + throw new ArgumentException("Token kind must be a literal.", nameof(token)); + } + + return new JassLiteralExpressionSyntax(token); } } } \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/SyntaxFactory/LocalVariableDeclarationStatementFactory.cs b/src/War3Net.CodeAnalysis.Jass/SyntaxFactory/LocalVariableDeclarationStatementFactory.cs index 309c2413..d50c271e 100644 --- a/src/War3Net.CodeAnalysis.Jass/SyntaxFactory/LocalVariableDeclarationStatementFactory.cs +++ b/src/War3Net.CodeAnalysis.Jass/SyntaxFactory/LocalVariableDeclarationStatementFactory.cs @@ -11,29 +11,172 @@ namespace War3Net.CodeAnalysis.Jass { public static partial class JassSyntaxFactory { + public static JassLocalVariableDeclarationStatementSyntax LocalVariableDeclarationStatement(JassSyntaxToken localToken, JassVariableOrArrayDeclaratorSyntax declarator) + { + ThrowHelper.ThrowIfInvalidToken(localToken, JassSyntaxKind.LocalKeyword); + + return new JassLocalVariableDeclarationStatementSyntax( + localToken, + declarator); + } + + public static JassLocalVariableDeclarationStatementSyntax LocalVariableDeclarationStatement(JassTypeSyntax type, JassIdentifierNameSyntax identifierName) + { + return new JassLocalVariableDeclarationStatementSyntax( + Token(JassSyntaxKind.LocalKeyword), + VariableDeclarator( + type, + identifierName)); + } + public static JassLocalVariableDeclarationStatementSyntax LocalVariableDeclarationStatement(JassTypeSyntax type, string name) { return new JassLocalVariableDeclarationStatementSyntax( - new JassVariableDeclaratorSyntax( + Token(JassSyntaxKind.LocalKeyword), + VariableDeclarator( + type, + ParseIdentifierName(name))); + } + + public static JassLocalVariableDeclarationStatementSyntax LocalVariableDeclarationStatement(string type, JassIdentifierNameSyntax identifierName) + { + return new JassLocalVariableDeclarationStatementSyntax( + Token(JassSyntaxKind.LocalKeyword), + VariableDeclarator( + ParseTypeName(type), + identifierName)); + } + + public static JassLocalVariableDeclarationStatementSyntax LocalVariableDeclarationStatement(string type, string name) + { + return new JassLocalVariableDeclarationStatementSyntax( + Token(JassSyntaxKind.LocalKeyword), + VariableDeclarator( + ParseTypeName(type), + ParseIdentifierName(name))); + } + + public static JassLocalVariableDeclarationStatementSyntax LocalVariableDeclarationStatement(JassTypeSyntax type, JassIdentifierNameSyntax identifierName, JassEqualsValueClauseSyntax value) + { + return new JassLocalVariableDeclarationStatementSyntax( + Token(JassSyntaxKind.LocalKeyword), + VariableDeclarator( + type, + identifierName, + value)); + } + + public static JassLocalVariableDeclarationStatementSyntax LocalVariableDeclarationStatement(JassTypeSyntax type, JassIdentifierNameSyntax identifierName, JassExpressionSyntax value) + { + return new JassLocalVariableDeclarationStatementSyntax( + Token(JassSyntaxKind.LocalKeyword), + VariableDeclarator( + type, + identifierName, + EqualsValueClause(value))); + } + + public static JassLocalVariableDeclarationStatementSyntax LocalVariableDeclarationStatement(JassTypeSyntax type, string name, JassEqualsValueClauseSyntax value) + { + return new JassLocalVariableDeclarationStatementSyntax( + Token(JassSyntaxKind.LocalKeyword), + VariableDeclarator( type, ParseIdentifierName(name), - null)); + value)); } - public static JassLocalVariableDeclarationStatementSyntax LocalVariableDeclarationStatement(JassTypeSyntax type, string name, IExpressionSyntax value) + public static JassLocalVariableDeclarationStatementSyntax LocalVariableDeclarationStatement(JassTypeSyntax type, string name, JassExpressionSyntax value) { return new JassLocalVariableDeclarationStatementSyntax( - new JassVariableDeclaratorSyntax( + Token(JassSyntaxKind.LocalKeyword), + VariableDeclarator( type, ParseIdentifierName(name), - new JassEqualsValueClauseSyntax(value))); + EqualsValueClause(value))); + } + + public static JassLocalVariableDeclarationStatementSyntax LocalVariableDeclarationStatement(string type, JassIdentifierNameSyntax identifierName, JassEqualsValueClauseSyntax value) + { + return new JassLocalVariableDeclarationStatementSyntax( + Token(JassSyntaxKind.LocalKeyword), + VariableDeclarator( + ParseTypeName(type), + identifierName, + value)); + } + + public static JassLocalVariableDeclarationStatementSyntax LocalVariableDeclarationStatement(string type, JassIdentifierNameSyntax identifierName, JassExpressionSyntax value) + { + return new JassLocalVariableDeclarationStatementSyntax( + Token(JassSyntaxKind.LocalKeyword), + VariableDeclarator( + ParseTypeName(type), + identifierName, + EqualsValueClause(value))); + } + + public static JassLocalVariableDeclarationStatementSyntax LocalVariableDeclarationStatement(string type, string name, JassEqualsValueClauseSyntax value) + { + return new JassLocalVariableDeclarationStatementSyntax( + Token(JassSyntaxKind.LocalKeyword), + VariableDeclarator( + ParseTypeName(type), + ParseIdentifierName(name), + value)); + } + + public static JassLocalVariableDeclarationStatementSyntax LocalVariableDeclarationStatement(string type, string name, JassExpressionSyntax value) + { + return new JassLocalVariableDeclarationStatementSyntax( + Token(JassSyntaxKind.LocalKeyword), + VariableDeclarator( + ParseTypeName(type), + ParseIdentifierName(name), + EqualsValueClause(value))); + } + + public static JassLocalVariableDeclarationStatementSyntax LocalArrayDeclarationStatement(JassTypeSyntax type, JassIdentifierNameSyntax identifierName) + { + return new JassLocalVariableDeclarationStatementSyntax( + Token(JassSyntaxKind.LocalKeyword), + ArrayDeclarator( + type, + identifierName)); } public static JassLocalVariableDeclarationStatementSyntax LocalArrayDeclarationStatement(JassTypeSyntax type, string name) { return new JassLocalVariableDeclarationStatementSyntax( - new JassArrayDeclaratorSyntax( - type, ParseIdentifierName(name))); + Token(JassSyntaxKind.LocalKeyword), + ArrayDeclarator( + type, + ParseIdentifierName(name))); + } + + public static JassLocalVariableDeclarationStatementSyntax LocalArrayDeclarationStatement(string type, JassIdentifierNameSyntax identifierName) + { + return new JassLocalVariableDeclarationStatementSyntax( + Token(JassSyntaxKind.LocalKeyword), + ArrayDeclarator( + ParseTypeName(type), + identifierName)); + } + + public static JassLocalVariableDeclarationStatementSyntax LocalArrayDeclarationStatement(string type, string name) + { + return new JassLocalVariableDeclarationStatementSyntax( + Token(JassSyntaxKind.LocalKeyword), + ArrayDeclarator( + ParseTypeName(type), + ParseIdentifierName(name))); + } + + public static JassLocalVariableDeclarationStatementSyntax LocalDeclarationStatement(JassVariableOrArrayDeclaratorSyntax declarator) + { + return new JassLocalVariableDeclarationStatementSyntax( + Token(JassSyntaxKind.LocalKeyword), + declarator); } } } \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/SyntaxFactory/LoopStatementFactory.cs b/src/War3Net.CodeAnalysis.Jass/SyntaxFactory/LoopStatementFactory.cs index 491edae3..3b0bf847 100644 --- a/src/War3Net.CodeAnalysis.Jass/SyntaxFactory/LoopStatementFactory.cs +++ b/src/War3Net.CodeAnalysis.Jass/SyntaxFactory/LoopStatementFactory.cs @@ -5,20 +5,48 @@ // // ------------------------------------------------------------------------------ +using System.Collections.Generic; +using System.Collections.Immutable; + using War3Net.CodeAnalysis.Jass.Syntax; namespace War3Net.CodeAnalysis.Jass { public static partial class JassSyntaxFactory { - public static JassLoopStatementSyntax LoopStatement(JassStatementListSyntax body) + public static JassLoopStatementSyntax LoopStatement(params JassStatementSyntax[] statements) + { + return new JassLoopStatementSyntax( + Token(JassSyntaxKind.LoopKeyword), + statements.ToImmutableArray(), + Token(JassSyntaxKind.EndLoopKeyword)); + } + + public static JassLoopStatementSyntax LoopStatement(IEnumerable statements) { - return new JassLoopStatementSyntax(body); + return new JassLoopStatementSyntax( + Token(JassSyntaxKind.LoopKeyword), + statements.ToImmutableArray(), + Token(JassSyntaxKind.EndLoopKeyword)); } - public static JassLoopStatementSyntax LoopStatement(params IStatementSyntax[] body) + public static JassLoopStatementSyntax LoopStatement(ImmutableArray statements) { - return new JassLoopStatementSyntax(StatementList(body)); + return new JassLoopStatementSyntax( + Token(JassSyntaxKind.LoopKeyword), + statements, + Token(JassSyntaxKind.EndLoopKeyword)); + } + + public static JassLoopStatementSyntax LoopStatement(JassSyntaxToken loopToken, ImmutableArray statements, JassSyntaxToken endLoopToken) + { + ThrowHelper.ThrowIfInvalidToken(loopToken, JassSyntaxKind.LoopKeyword); + ThrowHelper.ThrowIfInvalidToken(endLoopToken, JassSyntaxKind.EndLoopKeyword); + + return new JassLoopStatementSyntax( + loopToken, + statements, + endLoopToken); } } } \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/SyntaxFactory/NativeFunctionDeclarationFactory.cs b/src/War3Net.CodeAnalysis.Jass/SyntaxFactory/NativeFunctionDeclarationFactory.cs new file mode 100644 index 00000000..5f6e3805 --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/SyntaxFactory/NativeFunctionDeclarationFactory.cs @@ -0,0 +1,561 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using System.Collections.Generic; + +using War3Net.CodeAnalysis.Jass.Syntax; + +namespace War3Net.CodeAnalysis.Jass +{ + public static partial class JassSyntaxFactory + { + public static JassNativeFunctionDeclarationSyntax NativeFunctionDeclaration(JassIdentifierNameSyntax identifierName) + { + return new JassNativeFunctionDeclarationSyntax( + null, + Token(JassSyntaxKind.NativeKeyword), + identifierName, + JassEmptyParameterListSyntax.Value, + ReturnClause(JassPredefinedTypeSyntax.Nothing)); + } + + public static JassNativeFunctionDeclarationSyntax NativeFunctionDeclaration(string name) + { + return new JassNativeFunctionDeclarationSyntax( + null, + Token(JassSyntaxKind.NativeKeyword), + ParseIdentifierName(name), + JassEmptyParameterListSyntax.Value, + ReturnClause(JassPredefinedTypeSyntax.Nothing)); + } + + public static JassNativeFunctionDeclarationSyntax NativeFunctionDeclaration(JassIdentifierNameSyntax identifierName, JassReturnClauseSyntax returnClause) + { + return new JassNativeFunctionDeclarationSyntax( + null, + Token(JassSyntaxKind.NativeKeyword), + identifierName, + JassEmptyParameterListSyntax.Value, + returnClause); + } + + public static JassNativeFunctionDeclarationSyntax NativeFunctionDeclaration(JassIdentifierNameSyntax identifierName, JassTypeSyntax returnType) + { + return new JassNativeFunctionDeclarationSyntax( + null, + Token(JassSyntaxKind.NativeKeyword), + identifierName, + JassEmptyParameterListSyntax.Value, + ReturnClause(returnType)); + } + + public static JassNativeFunctionDeclarationSyntax NativeFunctionDeclaration(JassIdentifierNameSyntax identifierName, string returnType) + { + return new JassNativeFunctionDeclarationSyntax( + null, + Token(JassSyntaxKind.NativeKeyword), + identifierName, + JassEmptyParameterListSyntax.Value, + ReturnClause(ParseTypeName(returnType))); + } + + public static JassNativeFunctionDeclarationSyntax NativeFunctionDeclaration(string name, JassReturnClauseSyntax returnClause) + { + return new JassNativeFunctionDeclarationSyntax( + null, + Token(JassSyntaxKind.NativeKeyword), + ParseIdentifierName(name), + JassEmptyParameterListSyntax.Value, + returnClause); + } + + public static JassNativeFunctionDeclarationSyntax NativeFunctionDeclaration(string name, JassTypeSyntax returnType) + { + return new JassNativeFunctionDeclarationSyntax( + null, + Token(JassSyntaxKind.NativeKeyword), + ParseIdentifierName(name), + JassEmptyParameterListSyntax.Value, + ReturnClause(returnType)); + } + + public static JassNativeFunctionDeclarationSyntax NativeFunctionDeclaration(string name, string returnType) + { + return new JassNativeFunctionDeclarationSyntax( + null, + Token(JassSyntaxKind.NativeKeyword), + ParseIdentifierName(name), + JassEmptyParameterListSyntax.Value, + ReturnClause(ParseTypeName(returnType))); + } + + public static JassNativeFunctionDeclarationSyntax NativeFunctionDeclaration(JassIdentifierNameSyntax identifierName, JassReturnClauseSyntax returnClause, JassParameterListOrEmptyParameterListSyntax parameterList) + { + return new JassNativeFunctionDeclarationSyntax( + null, + Token(JassSyntaxKind.NativeKeyword), + identifierName, + parameterList, + returnClause); + } + + public static JassNativeFunctionDeclarationSyntax NativeFunctionDeclaration(JassIdentifierNameSyntax identifierName, JassReturnClauseSyntax returnClause, params JassParameterSyntax[] parameters) + { + return new JassNativeFunctionDeclarationSyntax( + null, + Token(JassSyntaxKind.NativeKeyword), + identifierName, + ParameterList(parameters), + returnClause); + } + + public static JassNativeFunctionDeclarationSyntax NativeFunctionDeclaration(JassIdentifierNameSyntax identifierName, JassReturnClauseSyntax returnClause, IEnumerable parameters) + { + return new JassNativeFunctionDeclarationSyntax( + null, + Token(JassSyntaxKind.NativeKeyword), + identifierName, + ParameterList(parameters), + returnClause); + } + + public static JassNativeFunctionDeclarationSyntax NativeFunctionDeclaration(JassIdentifierNameSyntax identifierName, JassTypeSyntax returnType, JassParameterListOrEmptyParameterListSyntax parameterList) + { + return new JassNativeFunctionDeclarationSyntax( + null, + Token(JassSyntaxKind.NativeKeyword), + identifierName, + parameterList, + ReturnClause(returnType)); + } + + public static JassNativeFunctionDeclarationSyntax NativeFunctionDeclaration(JassIdentifierNameSyntax identifierName, JassTypeSyntax returnType, params JassParameterSyntax[] parameters) + { + return new JassNativeFunctionDeclarationSyntax( + null, + Token(JassSyntaxKind.NativeKeyword), + identifierName, + ParameterList(parameters), + ReturnClause(returnType)); + } + + public static JassNativeFunctionDeclarationSyntax NativeFunctionDeclaration(JassIdentifierNameSyntax identifierName, JassTypeSyntax returnType, IEnumerable parameters) + { + return new JassNativeFunctionDeclarationSyntax( + null, + Token(JassSyntaxKind.NativeKeyword), + identifierName, + ParameterList(parameters), + ReturnClause(returnType)); + } + + public static JassNativeFunctionDeclarationSyntax NativeFunctionDeclaration(JassIdentifierNameSyntax identifierName, string returnType, JassParameterListOrEmptyParameterListSyntax parameterList) + { + return new JassNativeFunctionDeclarationSyntax( + null, + Token(JassSyntaxKind.NativeKeyword), + identifierName, + parameterList, + ReturnClause(ParseTypeName(returnType))); + } + + public static JassNativeFunctionDeclarationSyntax NativeFunctionDeclaration(JassIdentifierNameSyntax identifierName, string returnType, params JassParameterSyntax[] parameters) + { + return new JassNativeFunctionDeclarationSyntax( + null, + Token(JassSyntaxKind.NativeKeyword), + identifierName, + ParameterList(parameters), + ReturnClause(ParseTypeName(returnType))); + } + + public static JassNativeFunctionDeclarationSyntax NativeFunctionDeclaration(JassIdentifierNameSyntax identifierName, string returnType, IEnumerable parameters) + { + return new JassNativeFunctionDeclarationSyntax( + null, + Token(JassSyntaxKind.NativeKeyword), + identifierName, + ParameterList(parameters), + ReturnClause(ParseTypeName(returnType))); + } + + public static JassNativeFunctionDeclarationSyntax NativeFunctionDeclaration(string name, JassReturnClauseSyntax returnClause, JassParameterListOrEmptyParameterListSyntax parameterList) + { + return new JassNativeFunctionDeclarationSyntax( + null, + Token(JassSyntaxKind.NativeKeyword), + ParseIdentifierName(name), + parameterList, + returnClause); + } + + public static JassNativeFunctionDeclarationSyntax NativeFunctionDeclaration(string name, JassReturnClauseSyntax returnClause, params JassParameterSyntax[] parameters) + { + return new JassNativeFunctionDeclarationSyntax( + null, + Token(JassSyntaxKind.NativeKeyword), + ParseIdentifierName(name), + ParameterList(parameters), + returnClause); + } + + public static JassNativeFunctionDeclarationSyntax NativeFunctionDeclaration(string name, JassReturnClauseSyntax returnClause, IEnumerable parameters) + { + return new JassNativeFunctionDeclarationSyntax( + null, + Token(JassSyntaxKind.NativeKeyword), + ParseIdentifierName(name), + ParameterList(parameters), + returnClause); + } + + public static JassNativeFunctionDeclarationSyntax NativeFunctionDeclaration(string name, JassTypeSyntax returnType, JassParameterListOrEmptyParameterListSyntax parameterList) + { + return new JassNativeFunctionDeclarationSyntax( + null, + Token(JassSyntaxKind.NativeKeyword), + ParseIdentifierName(name), + parameterList, + ReturnClause(returnType)); + } + + public static JassNativeFunctionDeclarationSyntax NativeFunctionDeclaration(string name, JassTypeSyntax returnType, params JassParameterSyntax[] parameters) + { + return new JassNativeFunctionDeclarationSyntax( + null, + Token(JassSyntaxKind.NativeKeyword), + ParseIdentifierName(name), + ParameterList(parameters), + ReturnClause(returnType)); + } + + public static JassNativeFunctionDeclarationSyntax NativeFunctionDeclaration(string name, JassTypeSyntax returnType, IEnumerable parameters) + { + return new JassNativeFunctionDeclarationSyntax( + null, + Token(JassSyntaxKind.NativeKeyword), + ParseIdentifierName(name), + ParameterList(parameters), + ReturnClause(returnType)); + } + + public static JassNativeFunctionDeclarationSyntax NativeFunctionDeclaration(string name, string returnType, JassParameterListOrEmptyParameterListSyntax parameterList) + { + return new JassNativeFunctionDeclarationSyntax( + null, + Token(JassSyntaxKind.NativeKeyword), + ParseIdentifierName(name), + parameterList, + ReturnClause(ParseTypeName(returnType))); + } + + public static JassNativeFunctionDeclarationSyntax NativeFunctionDeclaration(string name, string returnType, params JassParameterSyntax[] parameters) + { + return new JassNativeFunctionDeclarationSyntax( + null, + Token(JassSyntaxKind.NativeKeyword), + ParseIdentifierName(name), + ParameterList(parameters), + ReturnClause(ParseTypeName(returnType))); + } + + public static JassNativeFunctionDeclarationSyntax NativeFunctionDeclaration(string name, string returnType, IEnumerable parameters) + { + return new JassNativeFunctionDeclarationSyntax( + null, + Token(JassSyntaxKind.NativeKeyword), + ParseIdentifierName(name), + ParameterList(parameters), + ReturnClause(ParseTypeName(returnType))); + } + + public static JassNativeFunctionDeclarationSyntax NativeFunctionDeclaration(JassSyntaxToken nativeToken, JassIdentifierNameSyntax identifierName, JassReturnClauseSyntax returnClause, JassParameterListOrEmptyParameterListSyntax parameterList) + { + ThrowHelper.ThrowIfInvalidToken(nativeToken, JassSyntaxKind.NativeKeyword); + + return new JassNativeFunctionDeclarationSyntax( + null, + nativeToken, + identifierName, + parameterList, + returnClause); + } + + public static JassNativeFunctionDeclarationSyntax ConstantNativeFunctionDeclaration(JassIdentifierNameSyntax identifierName) + { + return new JassNativeFunctionDeclarationSyntax( + Token(JassSyntaxKind.ConstantKeyword), + Token(JassSyntaxKind.NativeKeyword), + identifierName, + JassEmptyParameterListSyntax.Value, + ReturnClause(JassPredefinedTypeSyntax.Nothing)); + } + + public static JassNativeFunctionDeclarationSyntax ConstantNativeFunctionDeclaration(string name) + { + return new JassNativeFunctionDeclarationSyntax( + Token(JassSyntaxKind.ConstantKeyword), + Token(JassSyntaxKind.NativeKeyword), + ParseIdentifierName(name), + JassEmptyParameterListSyntax.Value, + ReturnClause(JassPredefinedTypeSyntax.Nothing)); + } + + public static JassNativeFunctionDeclarationSyntax ConstantNativeFunctionDeclaration(JassIdentifierNameSyntax identifierName, JassReturnClauseSyntax returnClause) + { + return new JassNativeFunctionDeclarationSyntax( + Token(JassSyntaxKind.ConstantKeyword), + Token(JassSyntaxKind.NativeKeyword), + identifierName, + JassEmptyParameterListSyntax.Value, + returnClause); + } + + public static JassNativeFunctionDeclarationSyntax ConstantNativeFunctionDeclaration(JassIdentifierNameSyntax identifierName, JassTypeSyntax returnType) + { + return new JassNativeFunctionDeclarationSyntax( + Token(JassSyntaxKind.ConstantKeyword), + Token(JassSyntaxKind.NativeKeyword), + identifierName, + JassEmptyParameterListSyntax.Value, + ReturnClause(returnType)); + } + + public static JassNativeFunctionDeclarationSyntax ConstantNativeFunctionDeclaration(JassIdentifierNameSyntax identifierName, string returnType) + { + return new JassNativeFunctionDeclarationSyntax( + Token(JassSyntaxKind.ConstantKeyword), + Token(JassSyntaxKind.NativeKeyword), + identifierName, + JassEmptyParameterListSyntax.Value, + ReturnClause(ParseTypeName(returnType))); + } + + public static JassNativeFunctionDeclarationSyntax ConstantNativeFunctionDeclaration(string name, JassReturnClauseSyntax returnClause) + { + return new JassNativeFunctionDeclarationSyntax( + Token(JassSyntaxKind.ConstantKeyword), + Token(JassSyntaxKind.NativeKeyword), + ParseIdentifierName(name), + JassEmptyParameterListSyntax.Value, + returnClause); + } + + public static JassNativeFunctionDeclarationSyntax ConstantNativeFunctionDeclaration(string name, JassTypeSyntax returnType) + { + return new JassNativeFunctionDeclarationSyntax( + Token(JassSyntaxKind.ConstantKeyword), + Token(JassSyntaxKind.NativeKeyword), + ParseIdentifierName(name), + JassEmptyParameterListSyntax.Value, + ReturnClause(returnType)); + } + + public static JassNativeFunctionDeclarationSyntax ConstantNativeFunctionDeclaration(string name, string returnType) + { + return new JassNativeFunctionDeclarationSyntax( + Token(JassSyntaxKind.ConstantKeyword), + Token(JassSyntaxKind.NativeKeyword), + ParseIdentifierName(name), + JassEmptyParameterListSyntax.Value, + ReturnClause(ParseTypeName(returnType))); + } + + public static JassNativeFunctionDeclarationSyntax ConstantNativeFunctionDeclaration(JassIdentifierNameSyntax identifierName, JassReturnClauseSyntax returnClause, JassParameterListOrEmptyParameterListSyntax parameterList) + { + return new JassNativeFunctionDeclarationSyntax( + Token(JassSyntaxKind.ConstantKeyword), + Token(JassSyntaxKind.NativeKeyword), + identifierName, + parameterList, + returnClause); + } + + public static JassNativeFunctionDeclarationSyntax ConstantNativeFunctionDeclaration(JassIdentifierNameSyntax identifierName, JassReturnClauseSyntax returnClause, params JassParameterSyntax[] parameters) + { + return new JassNativeFunctionDeclarationSyntax( + Token(JassSyntaxKind.ConstantKeyword), + Token(JassSyntaxKind.NativeKeyword), + identifierName, + ParameterList(parameters), + returnClause); + } + + public static JassNativeFunctionDeclarationSyntax ConstantNativeFunctionDeclaration(JassIdentifierNameSyntax identifierName, JassReturnClauseSyntax returnClause, IEnumerable parameters) + { + return new JassNativeFunctionDeclarationSyntax( + Token(JassSyntaxKind.ConstantKeyword), + Token(JassSyntaxKind.NativeKeyword), + identifierName, + ParameterList(parameters), + returnClause); + } + + public static JassNativeFunctionDeclarationSyntax ConstantNativeFunctionDeclaration(JassIdentifierNameSyntax identifierName, JassTypeSyntax returnType, JassParameterListOrEmptyParameterListSyntax parameterList) + { + return new JassNativeFunctionDeclarationSyntax( + Token(JassSyntaxKind.ConstantKeyword), + Token(JassSyntaxKind.NativeKeyword), + identifierName, + parameterList, + ReturnClause(returnType)); + } + + public static JassNativeFunctionDeclarationSyntax ConstantNativeFunctionDeclaration(JassIdentifierNameSyntax identifierName, JassTypeSyntax returnType, params JassParameterSyntax[] parameters) + { + return new JassNativeFunctionDeclarationSyntax( + Token(JassSyntaxKind.ConstantKeyword), + Token(JassSyntaxKind.NativeKeyword), + identifierName, + ParameterList(parameters), + ReturnClause(returnType)); + } + + public static JassNativeFunctionDeclarationSyntax ConstantNativeFunctionDeclaration(JassIdentifierNameSyntax identifierName, JassTypeSyntax returnType, IEnumerable parameters) + { + return new JassNativeFunctionDeclarationSyntax( + Token(JassSyntaxKind.ConstantKeyword), + Token(JassSyntaxKind.NativeKeyword), + identifierName, + ParameterList(parameters), + ReturnClause(returnType)); + } + + public static JassNativeFunctionDeclarationSyntax ConstantNativeFunctionDeclaration(JassIdentifierNameSyntax identifierName, string returnType, JassParameterListOrEmptyParameterListSyntax parameterList) + { + return new JassNativeFunctionDeclarationSyntax( + Token(JassSyntaxKind.ConstantKeyword), + Token(JassSyntaxKind.NativeKeyword), + identifierName, + parameterList, + ReturnClause(ParseTypeName(returnType))); + } + + public static JassNativeFunctionDeclarationSyntax ConstantNativeFunctionDeclaration(JassIdentifierNameSyntax identifierName, string returnType, params JassParameterSyntax[] parameters) + { + return new JassNativeFunctionDeclarationSyntax( + Token(JassSyntaxKind.ConstantKeyword), + Token(JassSyntaxKind.NativeKeyword), + identifierName, + ParameterList(parameters), + ReturnClause(ParseTypeName(returnType))); + } + + public static JassNativeFunctionDeclarationSyntax ConstantNativeFunctionDeclaration(JassIdentifierNameSyntax identifierName, string returnType, IEnumerable parameters) + { + return new JassNativeFunctionDeclarationSyntax( + Token(JassSyntaxKind.ConstantKeyword), + Token(JassSyntaxKind.NativeKeyword), + identifierName, + ParameterList(parameters), + ReturnClause(ParseTypeName(returnType))); + } + + public static JassNativeFunctionDeclarationSyntax ConstantNativeFunctionDeclaration(string name, JassReturnClauseSyntax returnClause, JassParameterListOrEmptyParameterListSyntax parameterList) + { + return new JassNativeFunctionDeclarationSyntax( + Token(JassSyntaxKind.ConstantKeyword), + Token(JassSyntaxKind.NativeKeyword), + ParseIdentifierName(name), + parameterList, + returnClause); + } + + public static JassNativeFunctionDeclarationSyntax ConstantNativeFunctionDeclaration(string name, JassReturnClauseSyntax returnClause, params JassParameterSyntax[] parameters) + { + return new JassNativeFunctionDeclarationSyntax( + Token(JassSyntaxKind.ConstantKeyword), + Token(JassSyntaxKind.NativeKeyword), + ParseIdentifierName(name), + ParameterList(parameters), + returnClause); + } + + public static JassNativeFunctionDeclarationSyntax ConstantNativeFunctionDeclaration(string name, JassReturnClauseSyntax returnClause, IEnumerable parameters) + { + return new JassNativeFunctionDeclarationSyntax( + Token(JassSyntaxKind.ConstantKeyword), + Token(JassSyntaxKind.NativeKeyword), + ParseIdentifierName(name), + ParameterList(parameters), + returnClause); + } + + public static JassNativeFunctionDeclarationSyntax ConstantNativeFunctionDeclaration(string name, JassTypeSyntax returnType, JassParameterListOrEmptyParameterListSyntax parameterList) + { + return new JassNativeFunctionDeclarationSyntax( + Token(JassSyntaxKind.ConstantKeyword), + Token(JassSyntaxKind.NativeKeyword), + ParseIdentifierName(name), + parameterList, + ReturnClause(returnType)); + } + + public static JassNativeFunctionDeclarationSyntax ConstantNativeFunctionDeclaration(string name, JassTypeSyntax returnType, params JassParameterSyntax[] parameters) + { + return new JassNativeFunctionDeclarationSyntax( + Token(JassSyntaxKind.ConstantKeyword), + Token(JassSyntaxKind.NativeKeyword), + ParseIdentifierName(name), + ParameterList(parameters), + ReturnClause(returnType)); + } + + public static JassNativeFunctionDeclarationSyntax ConstantNativeFunctionDeclaration(string name, JassTypeSyntax returnType, IEnumerable parameters) + { + return new JassNativeFunctionDeclarationSyntax( + Token(JassSyntaxKind.ConstantKeyword), + Token(JassSyntaxKind.NativeKeyword), + ParseIdentifierName(name), + ParameterList(parameters), + ReturnClause(returnType)); + } + + public static JassNativeFunctionDeclarationSyntax ConstantNativeFunctionDeclaration(string name, string returnType, JassParameterListOrEmptyParameterListSyntax parameterList) + { + return new JassNativeFunctionDeclarationSyntax( + Token(JassSyntaxKind.ConstantKeyword), + Token(JassSyntaxKind.NativeKeyword), + ParseIdentifierName(name), + parameterList, + ReturnClause(ParseTypeName(returnType))); + } + + public static JassNativeFunctionDeclarationSyntax ConstantNativeFunctionDeclaration(string name, string returnType, params JassParameterSyntax[] parameters) + { + return new JassNativeFunctionDeclarationSyntax( + Token(JassSyntaxKind.ConstantKeyword), + Token(JassSyntaxKind.NativeKeyword), + ParseIdentifierName(name), + ParameterList(parameters), + ReturnClause(ParseTypeName(returnType))); + } + + public static JassNativeFunctionDeclarationSyntax ConstantNativeFunctionDeclaration(string name, string returnType, IEnumerable parameters) + { + return new JassNativeFunctionDeclarationSyntax( + Token(JassSyntaxKind.ConstantKeyword), + Token(JassSyntaxKind.NativeKeyword), + ParseIdentifierName(name), + ParameterList(parameters), + ReturnClause(ParseTypeName(returnType))); + } + + public static JassNativeFunctionDeclarationSyntax ConstantNativeFunctionDeclaration(JassSyntaxToken constantToken, JassSyntaxToken nativeToken, JassIdentifierNameSyntax identifierName, JassReturnClauseSyntax returnClause, JassParameterListOrEmptyParameterListSyntax parameterList) + { + ThrowHelper.ThrowIfInvalidToken(constantToken, JassSyntaxKind.ConstantKeyword); + ThrowHelper.ThrowIfInvalidToken(nativeToken, JassSyntaxKind.NativeKeyword); + + return new JassNativeFunctionDeclarationSyntax( + constantToken, + nativeToken, + identifierName, + parameterList, + returnClause); + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/SyntaxFactory/ParameterFactory.cs b/src/War3Net.CodeAnalysis.Jass/SyntaxFactory/ParameterFactory.cs index b8c9ebbd..9efceeca 100644 --- a/src/War3Net.CodeAnalysis.Jass/SyntaxFactory/ParameterFactory.cs +++ b/src/War3Net.CodeAnalysis.Jass/SyntaxFactory/ParameterFactory.cs @@ -11,6 +11,27 @@ namespace War3Net.CodeAnalysis.Jass { public static partial class JassSyntaxFactory { + public static JassParameterSyntax Parameter(JassTypeSyntax type, JassIdentifierNameSyntax identifierName) + { + return new JassParameterSyntax( + type, + identifierName); + } + + public static JassParameterSyntax Parameter(JassTypeSyntax type, string name) + { + return new JassParameterSyntax( + type, + ParseIdentifierName(name)); + } + + public static JassParameterSyntax Parameter(string type, JassIdentifierNameSyntax identifierName) + { + return new JassParameterSyntax( + ParseTypeName(type), + identifierName); + } + public static JassParameterSyntax Parameter(string type, string name) { return new JassParameterSyntax( diff --git a/src/War3Net.CodeAnalysis.Jass/SyntaxFactory/ParameterListFactory.cs b/src/War3Net.CodeAnalysis.Jass/SyntaxFactory/ParameterListFactory.cs index e3ff5a83..9847482b 100644 --- a/src/War3Net.CodeAnalysis.Jass/SyntaxFactory/ParameterListFactory.cs +++ b/src/War3Net.CodeAnalysis.Jass/SyntaxFactory/ParameterListFactory.cs @@ -5,7 +5,8 @@ // // ------------------------------------------------------------------------------ -using System.Collections.Immutable; +using System.Collections.Generic; +using System.Linq; using War3Net.CodeAnalysis.Jass.Syntax; @@ -13,9 +14,48 @@ namespace War3Net.CodeAnalysis.Jass { public static partial class JassSyntaxFactory { - public static JassParameterListSyntax ParameterList(params JassParameterSyntax[] parameters) + public static JassParameterListOrEmptyParameterListSyntax ParameterList(params JassParameterSyntax[] parameters) { - return new JassParameterListSyntax(parameters.ToImmutableArray()); + if (parameters.Length == 0) + { + return JassEmptyParameterListSyntax.Value; + } + + return new JassParameterListSyntax( + Token(JassSyntaxKind.TakesKeyword), + SeparatedSyntaxList(JassSyntaxKind.CommaToken, parameters)); + } + + public static JassParameterListOrEmptyParameterListSyntax ParameterList(IEnumerable parameters) + { + if (!parameters.Any()) + { + return JassEmptyParameterListSyntax.Value; + } + + return new JassParameterListSyntax( + Token(JassSyntaxKind.TakesKeyword), + SeparatedSyntaxList(JassSyntaxKind.CommaToken, parameters)); + } + + public static JassParameterListSyntax ParameterList(JassSyntaxToken takesToken, SeparatedSyntaxList parameterList) + { + ThrowHelper.ThrowIfInvalidToken(takesToken, JassSyntaxKind.TakesKeyword); + ThrowHelper.ThrowIfInvalidSeparatedSyntaxList(parameterList, JassSyntaxKind.CommaToken); + + return new JassParameterListSyntax( + takesToken, + parameterList); + } + + public static JassEmptyParameterListSyntax EmptyParameterList(JassSyntaxToken takesToken, JassSyntaxToken nothingToken) + { + ThrowHelper.ThrowIfInvalidToken(takesToken, JassSyntaxKind.TakesKeyword); + ThrowHelper.ThrowIfInvalidToken(nothingToken, JassSyntaxKind.NothingKeyword); + + return new JassEmptyParameterListSyntax( + takesToken, + nothingToken); } } } \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/SyntaxFactory/ParenthesizedExpressionFactory.cs b/src/War3Net.CodeAnalysis.Jass/SyntaxFactory/ParenthesizedExpressionFactory.cs index fa282891..240b583a 100644 --- a/src/War3Net.CodeAnalysis.Jass/SyntaxFactory/ParenthesizedExpressionFactory.cs +++ b/src/War3Net.CodeAnalysis.Jass/SyntaxFactory/ParenthesizedExpressionFactory.cs @@ -11,9 +11,23 @@ namespace War3Net.CodeAnalysis.Jass { public static partial class JassSyntaxFactory { - public static JassParenthesizedExpressionSyntax ParenthesizedExpression(IExpressionSyntax expression) + public static JassParenthesizedExpressionSyntax ParenthesizedExpression(JassExpressionSyntax expression) { - return new JassParenthesizedExpressionSyntax(expression); + return new JassParenthesizedExpressionSyntax( + Token(JassSyntaxKind.OpenParenToken), + expression, + Token(JassSyntaxKind.CloseParenToken)); + } + + public static JassParenthesizedExpressionSyntax ParenthesizedExpression(JassSyntaxToken openParenToken, JassExpressionSyntax expression, JassSyntaxToken closeParenToken) + { + ThrowHelper.ThrowIfInvalidToken(openParenToken, JassSyntaxKind.OpenParenToken); + ThrowHelper.ThrowIfInvalidToken(closeParenToken, JassSyntaxKind.CloseParenToken); + + return new JassParenthesizedExpressionSyntax( + openParenToken, + expression, + closeParenToken); } } } \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/SyntaxFactory/PredefinedTypeFactory.cs b/src/War3Net.CodeAnalysis.Jass/SyntaxFactory/PredefinedTypeFactory.cs new file mode 100644 index 00000000..99b4aada --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/SyntaxFactory/PredefinedTypeFactory.cs @@ -0,0 +1,27 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using System; +using System.Linq; + +using War3Net.CodeAnalysis.Jass.Syntax; + +namespace War3Net.CodeAnalysis.Jass +{ + public static partial class JassSyntaxFactory + { + public static JassPredefinedTypeSyntax PredefinedType(JassSyntaxToken keyword) + { + if (!JassSyntaxFacts.IsPredefinedTypeKeyword(keyword.SyntaxKind)) + { + throw new ArgumentException($"The token's syntax kind must be one of: [ {string.Join(", ", JassSyntaxFacts.GetPredefinedTypeKeywordKinds().Select(predefinedType => $"'{predefinedType}'"))} ].", nameof(keyword)); + } + + return new JassPredefinedTypeSyntax(keyword); + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/SyntaxFactory/ReturnClauseFactory.cs b/src/War3Net.CodeAnalysis.Jass/SyntaxFactory/ReturnClauseFactory.cs new file mode 100644 index 00000000..58bd86e5 --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/SyntaxFactory/ReturnClauseFactory.cs @@ -0,0 +1,30 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using War3Net.CodeAnalysis.Jass.Syntax; + +namespace War3Net.CodeAnalysis.Jass +{ + public static partial class JassSyntaxFactory + { + public static JassReturnClauseSyntax ReturnClause(JassTypeSyntax returnType) + { + return new JassReturnClauseSyntax( + Token(JassSyntaxKind.ReturnsKeyword), + returnType); + } + + public static JassReturnClauseSyntax ReturnClause(JassSyntaxToken returnsToken, JassTypeSyntax returnType) + { + ThrowHelper.ThrowIfInvalidToken(returnsToken, JassSyntaxKind.ReturnsKeyword); + + return new JassReturnClauseSyntax( + returnsToken, + returnType); + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/SyntaxFactory/ReturnStatementFactory.cs b/src/War3Net.CodeAnalysis.Jass/SyntaxFactory/ReturnStatementFactory.cs new file mode 100644 index 00000000..dbc81024 --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/SyntaxFactory/ReturnStatementFactory.cs @@ -0,0 +1,30 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using War3Net.CodeAnalysis.Jass.Syntax; + +namespace War3Net.CodeAnalysis.Jass +{ + public static partial class JassSyntaxFactory + { + public static JassReturnStatementSyntax ReturnStatement(JassExpressionSyntax expression) + { + return new JassReturnStatementSyntax( + Token(JassSyntaxKind.ReturnKeyword), + expression); + } + + public static JassReturnStatementSyntax ReturnStatement(JassSyntaxToken returnToken, JassExpressionSyntax expression) + { + ThrowHelper.ThrowIfInvalidToken(returnToken, JassSyntaxKind.ReturnKeyword); + + return new JassReturnStatementSyntax( + returnToken, + expression); + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/SyntaxFactory/SeparatedSyntaxListFactory.cs b/src/War3Net.CodeAnalysis.Jass/SyntaxFactory/SeparatedSyntaxListFactory.cs new file mode 100644 index 00000000..06e1aeb4 --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/SyntaxFactory/SeparatedSyntaxListFactory.cs @@ -0,0 +1,58 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using System.Collections.Generic; +using System.Collections.Immutable; + +using War3Net.CodeAnalysis.Jass.Syntax; + +namespace War3Net.CodeAnalysis.Jass +{ + public static partial class JassSyntaxFactory + { + public static SeparatedSyntaxList SeparatedSyntaxList(JassSyntaxKind syntaxKind, params TItem[] items) + where TItem : JassSyntaxNode + { + if (items.Length == 0) + { + return SeparatedSyntaxList.Empty; + } + + var builder = SeparatedSyntaxList.CreateBuilder(items[0], items.Length); + for (var i = 1; i < items.Length; i++) + { + builder.Add(Token(syntaxKind), items[i]); + } + + return builder.ToSeparatedSyntaxList(); + } + + public static SeparatedSyntaxList SeparatedSyntaxList(JassSyntaxKind syntaxKind, IEnumerable items) + where TItem : JassSyntaxNode + { + var enumerator = items.GetEnumerator(); + if (!enumerator.MoveNext()) + { + return SeparatedSyntaxList.Empty; + } + + var builder = SeparatedSyntaxList.CreateBuilder(enumerator.Current); + while (enumerator.MoveNext()) + { + builder.Add(Token(syntaxKind), enumerator.Current); + } + + return builder.ToSeparatedSyntaxList(); + } + + public static SeparatedSyntaxList SeparatedSyntaxList(ImmutableArray items, ImmutableArray separators) + where TItem : JassSyntaxNode + { + return SeparatedSyntaxList.Create(items, separators); + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/SyntaxFactory/SetStatementFactory.cs b/src/War3Net.CodeAnalysis.Jass/SyntaxFactory/SetStatementFactory.cs index 4189a8be..ec5c40e0 100644 --- a/src/War3Net.CodeAnalysis.Jass/SyntaxFactory/SetStatementFactory.cs +++ b/src/War3Net.CodeAnalysis.Jass/SyntaxFactory/SetStatementFactory.cs @@ -11,35 +11,122 @@ namespace War3Net.CodeAnalysis.Jass { public static partial class JassSyntaxFactory { - public static JassSetStatementSyntax SetStatement(string name, IExpressionSyntax value) + public static JassSetStatementSyntax SetStatement(JassIdentifierNameSyntax identifierName, JassEqualsValueClauseSyntax value) { return new JassSetStatementSyntax( - ParseIdentifierName(name), + Token(JassSyntaxKind.SetKeyword), + identifierName, + null, + value); + } + + public static JassSetStatementSyntax SetStatement(JassIdentifierNameSyntax identifierName, JassExpressionSyntax value) + { + return new JassSetStatementSyntax( + Token(JassSyntaxKind.SetKeyword), + identifierName, null, - new JassEqualsValueClauseSyntax(value)); + EqualsValueClause(value)); } public static JassSetStatementSyntax SetStatement(string name, JassEqualsValueClauseSyntax value) { return new JassSetStatementSyntax( + Token(JassSyntaxKind.SetKeyword), + ParseIdentifierName(name), + null, + value); + } + + public static JassSetStatementSyntax SetStatement(string name, JassExpressionSyntax value) + { + return new JassSetStatementSyntax( + Token(JassSyntaxKind.SetKeyword), ParseIdentifierName(name), null, + EqualsValueClause(value)); + } + + public static JassSetStatementSyntax SetStatement(JassIdentifierNameSyntax identifierName, JassElementAccessClauseSyntax elementAccessClause, JassEqualsValueClauseSyntax value) + { + return new JassSetStatementSyntax( + Token(JassSyntaxKind.SetKeyword), + identifierName, + elementAccessClause, + value); + } + + public static JassSetStatementSyntax SetStatement(JassIdentifierNameSyntax identifierName, JassElementAccessClauseSyntax elementAccessClause, JassExpressionSyntax value) + { + return new JassSetStatementSyntax( + Token(JassSyntaxKind.SetKeyword), + identifierName, + elementAccessClause, + EqualsValueClause(value)); + } + + public static JassSetStatementSyntax SetStatement(JassIdentifierNameSyntax identifierName, JassExpressionSyntax elementAccessExpression, JassEqualsValueClauseSyntax value) + { + return new JassSetStatementSyntax( + Token(JassSyntaxKind.SetKeyword), + identifierName, + ElementAccessClause(elementAccessExpression), + value); + } + + public static JassSetStatementSyntax SetStatement(JassIdentifierNameSyntax identifierName, JassExpressionSyntax elementAccessExpression, JassExpressionSyntax value) + { + return new JassSetStatementSyntax( + Token(JassSyntaxKind.SetKeyword), + identifierName, + ElementAccessClause(elementAccessExpression), + EqualsValueClause(value)); + } + + public static JassSetStatementSyntax SetStatement(string name, JassElementAccessClauseSyntax elementAccessClause, JassEqualsValueClauseSyntax value) + { + return new JassSetStatementSyntax( + Token(JassSyntaxKind.SetKeyword), + ParseIdentifierName(name), + elementAccessClause, value); } - public static JassSetStatementSyntax SetStatement(string name, IExpressionSyntax indexer, IExpressionSyntax value) + public static JassSetStatementSyntax SetStatement(string name, JassElementAccessClauseSyntax elementAccessClause, JassExpressionSyntax value) + { + return new JassSetStatementSyntax( + Token(JassSyntaxKind.SetKeyword), + ParseIdentifierName(name), + elementAccessClause, + EqualsValueClause(value)); + } + + public static JassSetStatementSyntax SetStatement(string name, JassExpressionSyntax elementAccessExpression, JassEqualsValueClauseSyntax value) { return new JassSetStatementSyntax( + Token(JassSyntaxKind.SetKeyword), ParseIdentifierName(name), - indexer, - new JassEqualsValueClauseSyntax(value)); + ElementAccessClause(elementAccessExpression), + value); } - public static JassSetStatementSyntax SetStatement(string name, IExpressionSyntax indexer, JassEqualsValueClauseSyntax value) + public static JassSetStatementSyntax SetStatement(string name, JassExpressionSyntax elementAccessExpression, JassExpressionSyntax value) { return new JassSetStatementSyntax( + Token(JassSyntaxKind.SetKeyword), ParseIdentifierName(name), - indexer, + ElementAccessClause(elementAccessExpression), + EqualsValueClause(value)); + } + + public static JassSetStatementSyntax SetStatement(JassSyntaxToken setToken, JassIdentifierNameSyntax identifierName, JassElementAccessClauseSyntax? elementAccessClause, JassEqualsValueClauseSyntax value) + { + ThrowHelper.ThrowIfInvalidToken(setToken, JassSyntaxKind.SetKeyword); + + return new JassSetStatementSyntax( + setToken, + identifierName, + elementAccessClause, value); } } diff --git a/src/War3Net.CodeAnalysis.Jass/SyntaxFactory/StatementListFactory.cs b/src/War3Net.CodeAnalysis.Jass/SyntaxFactory/StatementListFactory.cs deleted file mode 100644 index 41499e43..00000000 --- a/src/War3Net.CodeAnalysis.Jass/SyntaxFactory/StatementListFactory.cs +++ /dev/null @@ -1,27 +0,0 @@ -// ------------------------------------------------------------------------------ -// -// Licensed under the MIT license. -// See the LICENSE file in the project root for more information. -// -// ------------------------------------------------------------------------------ - -using System.Collections.Generic; -using System.Collections.Immutable; - -using War3Net.CodeAnalysis.Jass.Syntax; - -namespace War3Net.CodeAnalysis.Jass -{ - public static partial class JassSyntaxFactory - { - public static JassStatementListSyntax StatementList(IEnumerable statements) - { - return new JassStatementListSyntax(statements.ToImmutableArray()); - } - - public static JassStatementListSyntax StatementList(params IStatementSyntax[] statements) - { - return new JassStatementListSyntax(statements.ToImmutableArray()); - } - } -} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/SyntaxFactory/SyntaxTokenFactory.cs b/src/War3Net.CodeAnalysis.Jass/SyntaxFactory/SyntaxTokenFactory.cs new file mode 100644 index 00000000..3522cebb --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/SyntaxFactory/SyntaxTokenFactory.cs @@ -0,0 +1,83 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using System; +using System.Collections.Generic; +using System.ComponentModel; + +using War3Net.CodeAnalysis.Jass.Syntax; + +namespace War3Net.CodeAnalysis.Jass +{ + public static partial class JassSyntaxFactory + { + private static readonly Dictionary _defaultTokens = new(); + + public static JassSyntaxToken Token(JassSyntaxKind syntaxKind) + { + if (_defaultTokens.TryGetValue(syntaxKind, out var token)) + { + return token; + } + + var text = JassSyntaxFacts.GetText(syntaxKind); + if (text.Length == 0 && syntaxKind != JassSyntaxKind.EndOfFileToken) + { + throw new ArgumentException($"'{syntaxKind}' does not have default text.", nameof(syntaxKind)); + } + + token = new JassSyntaxToken(JassSyntaxTriviaList.Empty, syntaxKind, text, JassSyntaxTriviaList.Empty); + _defaultTokens.Add(syntaxKind, token); + return token; + } + + public static JassSyntaxToken Token(JassSyntaxTriviaList leadingTrivia, JassSyntaxKind syntaxKind) + { + return Token(leadingTrivia, syntaxKind, JassSyntaxTriviaList.Empty); + } + + public static JassSyntaxToken Token(JassSyntaxKind syntaxKind, JassSyntaxTriviaList trailingTrivia) + { + return Token(JassSyntaxTriviaList.Empty, syntaxKind, trailingTrivia); + } + + public static JassSyntaxToken Token(JassSyntaxKind syntaxKind, string text) + { + return Token(JassSyntaxTriviaList.Empty, syntaxKind, text, JassSyntaxTriviaList.Empty); + } + + public static JassSyntaxToken Token(JassSyntaxTriviaList leadingTrivia, JassSyntaxKind syntaxKind, JassSyntaxTriviaList trailingTrivia) + { + var text = JassSyntaxFacts.GetText(syntaxKind); + if (text.Length == 0 && syntaxKind != JassSyntaxKind.EndOfFileToken) + { + throw new ArgumentException($"'{syntaxKind}' does not have default text.", nameof(syntaxKind)); + } + + return new JassSyntaxToken(leadingTrivia, syntaxKind, text, trailingTrivia); + } + + public static JassSyntaxToken Token(JassSyntaxTriviaList leadingTrivia, JassSyntaxKind syntaxKind, string text, JassSyntaxTriviaList trailingTrivia) + { + var defaultText = JassSyntaxFacts.GetText(syntaxKind); + if (defaultText.Length == 0 && syntaxKind != JassSyntaxKind.EndOfFileToken) + { + var syntaxKindForText = JassSyntaxFacts.GetSyntaxKind(text); + if (syntaxKindForText != JassSyntaxKind.None) + { + throw new ArgumentException($"Text '{text}' is not valid for a token of type '{syntaxKind}', because this text is reserved for tokens of type '{syntaxKindForText}'.", nameof(text)); + } + } + else if (!string.Equals(text, defaultText, StringComparison.Ordinal)) + { + throw new ArgumentException($"Text '{text}' is not valid for a token of type '{syntaxKind}', because the text for this type must be '{defaultText}'.", nameof(text)); + } + + return new JassSyntaxToken(leadingTrivia, syntaxKind, text, trailingTrivia); + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/SyntaxFactory/SyntaxTriviaFactory.cs b/src/War3Net.CodeAnalysis.Jass/SyntaxFactory/SyntaxTriviaFactory.cs new file mode 100644 index 00000000..fb7ef6d9 --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/SyntaxFactory/SyntaxTriviaFactory.cs @@ -0,0 +1,81 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using System; +using System.ComponentModel; +using System.Globalization; +using System.Linq; + +using War3Net.CodeAnalysis.Jass.Syntax; + +namespace War3Net.CodeAnalysis.Jass +{ + public static partial class JassSyntaxFactory + { + public static JassSyntaxTrivia SyntaxTrivia(JassSyntaxKind syntaxKind, string text) + { + return syntaxKind switch + { + JassSyntaxKind.NewLineTrivia => NewLineTrivia(text), + JassSyntaxKind.WhitespaceTrivia => WhitespaceTrivia(text), + JassSyntaxKind.SingleLineCommentTrivia => SingleLineCommentTrivia(text), + + _ => throw new InvalidEnumArgumentException(nameof(syntaxKind), (int)syntaxKind, typeof(JassSyntaxKind)), + }; + } + + public static JassSyntaxTrivia NewLineTrivia(string text) + { + if (string.IsNullOrEmpty(text)) + { + throw new ArgumentNullException(nameof(text)); + } + + if (!text.All(c => c == JassSymbol.CarriageReturnChar || c == JassSymbol.LineFeedChar)) + { + throw new ArgumentException("Text may only contain '\\r' and '\\n' characters.", nameof(text)); + } + + return new JassSyntaxTrivia(JassSyntaxKind.NewLineTrivia, text); + } + + public static JassSyntaxTrivia WhitespaceTrivia(string text) + { + if (string.IsNullOrEmpty(text)) + { + throw new ArgumentNullException(nameof(text)); + } + + if (!text.All(JassSyntaxFacts.IsWhitespaceCharacter)) + { + throw new ArgumentException("Text may only contain ' ' and '\\t' characters.", nameof(text)); + } + + return new JassSyntaxTrivia(JassSyntaxKind.WhitespaceTrivia, text); + } + + public static JassSyntaxTrivia SingleLineCommentTrivia(string text) + { + if (string.IsNullOrEmpty(text)) + { + throw new ArgumentNullException(nameof(text)); + } + + if (!text.StartsWith(JassSymbol.SlashSlash, false, CultureInfo.InvariantCulture)) + { + throw new ArgumentException("Text must start with \"//\".", nameof(text)); + } + + if (text.Any(c => c == JassSymbol.CarriageReturnChar || c == JassSymbol.LineFeedChar)) + { + throw new ArgumentException("Text may not contain '\\r' or '\\n' characters.", nameof(text)); + } + + return new JassSyntaxTrivia(JassSyntaxKind.SingleLineCommentTrivia, text); + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/SyntaxFactory/SyntaxTriviaListFactory.cs b/src/War3Net.CodeAnalysis.Jass/SyntaxFactory/SyntaxTriviaListFactory.cs new file mode 100644 index 00000000..baaf46c0 --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/SyntaxFactory/SyntaxTriviaListFactory.cs @@ -0,0 +1,149 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Linq; + +using Pidgin; + +using War3Net.CodeAnalysis.Jass.Syntax; + +namespace War3Net.CodeAnalysis.Jass +{ + public static partial class JassSyntaxFactory + { + public static JassSyntaxTriviaList SyntaxTriviaList(JassSyntaxTrivia trivia) + { + return new JassSyntaxTriviaList(ImmutableArray.Create(trivia)); + } + + public static JassSyntaxTriviaList SyntaxTriviaList(params JassSyntaxTrivia[] trivia) + { + return new JassSyntaxTriviaList(trivia.ToImmutableArray()); + } + + public static JassSyntaxTriviaList SyntaxTriviaList(IEnumerable trivia) + { + return new JassSyntaxTriviaList(trivia.ToImmutableArray()); + } + + public static JassSyntaxTriviaList SyntaxTriviaList(ImmutableArray trivia) + { + return new JassSyntaxTriviaList(trivia); + } + + public static JassSyntaxTriviaList ParseLeadingTrivia(string text) + { + return JassParser.Instance.LeadingTriviaListParser.ParseOrThrow(text); + } + + public static JassSyntaxTriviaList ParseTrailingTrivia(string text) + { + return JassParser.Instance.TrailingTriviaListParser.ParseOrThrow(text); + } + + public static JassSyntaxTriviaList AppendTriviaToList(ImmutableArray triviaList, JassSyntaxTrivia trivia) + { + var builder = ImmutableArray.CreateBuilder(triviaList.Length + 1); + builder.AddRange(triviaList); + builder.Add(trivia); + return new JassSyntaxTriviaList(builder.MoveToImmutable()); + } + + public static JassSyntaxTriviaList PrependTriviaToList(JassSyntaxTrivia trivia, ImmutableArray triviaList) + { + var builder = ImmutableArray.CreateBuilder(triviaList.Length + 1); + builder.Add(trivia); + builder.AddRange(triviaList); + return new JassSyntaxTriviaList(builder.MoveToImmutable()); + } + + public static JassSyntaxTriviaList ConcatTriviaLists(ImmutableArray firstList, ImmutableArray secondList) + { + var builder = ImmutableArray.CreateBuilder(firstList.Length + secondList.Length); + builder.AddRange(firstList); + builder.AddRange(secondList); + return new JassSyntaxTriviaList(builder.MoveToImmutable()); + } + + public static JassSyntaxTriviaList ConcatTriviaLists(IEnumerable firstList, IEnumerable secondList) + { + var builder = ImmutableArray.CreateBuilder(); + builder.AddRange(firstList); + builder.AddRange(secondList); + return new JassSyntaxTriviaList(builder.ToImmutable()); + } + + public static JassSyntaxTriviaList ConcatTriviaLists(JassSyntaxTriviaList firstList, JassSyntaxTriviaList secondList) + { + return ConcatTriviaLists(firstList.Trivia, secondList.Trivia); + } + + public static JassSyntaxTriviaList ConcatTriviaLists(JassSyntaxTriviaList firstList, JassSyntaxTriviaList secondList, JassSyntaxTriviaList thirdList) + { + var builder = ImmutableArray.CreateBuilder( + firstList.Trivia.Length + + secondList.Trivia.Length + + thirdList.Trivia.Length); + + builder.AddRange(firstList.Trivia); + builder.AddRange(secondList.Trivia); + builder.AddRange(thirdList.Trivia); + + return SyntaxTriviaList(builder.MoveToImmutable()); + } + + public static JassSyntaxTriviaList MergeTriviaLists(JassSyntaxTriviaList firstList, JassSyntaxTriviaList secondList, JassSyntaxTriviaList thirdList) + { + return MergeTrivia(firstList.Trivia.Concat(secondList.Trivia).Concat(thirdList.Trivia).ToList()); + } + + public static JassSyntaxTriviaList MergeTrivia(List triviaList) + { + if (triviaList.Count == 0) + { + return JassSyntaxTriviaList.Empty; + } + + if (triviaList.Count == 1) + { + return SyntaxTriviaList(ImmutableArray.Create(triviaList[0])); + } + + var builder = ImmutableArray.CreateBuilder(); + var aggregatedList = new List(); + var kind = JassSyntaxKind.None; + + foreach (var trivia in triviaList) + { + if (trivia.SyntaxKind == kind) + { + aggregatedList.Add(trivia); + continue; + } + + if (aggregatedList.Count > 0) + { + builder.Add(SyntaxTrivia(kind, string.Concat(aggregatedList.Select(t => t.Text)))); + aggregatedList.Clear(); + } + + aggregatedList.Add(trivia); + kind = trivia.SyntaxKind; + } + + if (aggregatedList.Count > 0) + { + builder.Add(SyntaxTrivia(kind, string.Concat(aggregatedList.Select(t => t.Text)))); + aggregatedList.Clear(); + } + + return SyntaxTriviaList(builder.ToImmutable()); + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/SyntaxFactory/TypeDeclarationFactory.cs b/src/War3Net.CodeAnalysis.Jass/SyntaxFactory/TypeDeclarationFactory.cs new file mode 100644 index 00000000..449a9bfb --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/SyntaxFactory/TypeDeclarationFactory.cs @@ -0,0 +1,62 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using War3Net.CodeAnalysis.Jass.Syntax; + +namespace War3Net.CodeAnalysis.Jass +{ + public static partial class JassSyntaxFactory + { + public static JassTypeDeclarationSyntax TypeDeclaration(JassIdentifierNameSyntax identifierName, JassTypeSyntax baseType) + { + return new JassTypeDeclarationSyntax( + Token(JassSyntaxKind.TypeKeyword), + identifierName, + Token(JassSyntaxKind.ExtendsKeyword), + baseType); + } + + public static JassTypeDeclarationSyntax TypeDeclaration(JassIdentifierNameSyntax identifierName, string baseType) + { + return new JassTypeDeclarationSyntax( + Token(JassSyntaxKind.TypeKeyword), + identifierName, + Token(JassSyntaxKind.ExtendsKeyword), + ParseTypeName(baseType)); + } + + public static JassTypeDeclarationSyntax TypeDeclaration(string name, JassTypeSyntax baseType) + { + return new JassTypeDeclarationSyntax( + Token(JassSyntaxKind.TypeKeyword), + ParseIdentifierName(name), + Token(JassSyntaxKind.ExtendsKeyword), + baseType); + } + + public static JassTypeDeclarationSyntax TypeDeclaration(string name, string baseType) + { + return new JassTypeDeclarationSyntax( + Token(JassSyntaxKind.TypeKeyword), + ParseIdentifierName(name), + Token(JassSyntaxKind.ExtendsKeyword), + ParseTypeName(baseType)); + } + + public static JassTypeDeclarationSyntax TypeDeclaration(JassSyntaxToken typeToken, JassIdentifierNameSyntax identifierName, JassSyntaxToken extendsToken, JassTypeSyntax baseType) + { + ThrowHelper.ThrowIfInvalidToken(typeToken, JassSyntaxKind.TypeKeyword); + ThrowHelper.ThrowIfInvalidToken(extendsToken, JassSyntaxKind.ExtendsKeyword); + + return new JassTypeDeclarationSyntax( + typeToken, + identifierName, + extendsToken, + baseType); + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/SyntaxFactory/UnaryExpressionFactory.cs b/src/War3Net.CodeAnalysis.Jass/SyntaxFactory/UnaryExpressionFactory.cs index 866406dc..37a6b222 100644 --- a/src/War3Net.CodeAnalysis.Jass/SyntaxFactory/UnaryExpressionFactory.cs +++ b/src/War3Net.CodeAnalysis.Jass/SyntaxFactory/UnaryExpressionFactory.cs @@ -5,25 +5,46 @@ // // ------------------------------------------------------------------------------ +using System; + using War3Net.CodeAnalysis.Jass.Syntax; namespace War3Net.CodeAnalysis.Jass { public static partial class JassSyntaxFactory { - public static JassUnaryExpressionSyntax UnaryPlusExpression(IExpressionSyntax expression) + public static JassUnaryExpressionSyntax UnaryPlusExpression(JassExpressionSyntax expression) { - return new JassUnaryExpressionSyntax(UnaryOperatorType.Plus, expression); + return new JassUnaryExpressionSyntax( + Token(JassSyntaxKind.PlusToken), + expression); } - public static JassUnaryExpressionSyntax UnaryMinusExpression(IExpressionSyntax expression) + public static JassUnaryExpressionSyntax UnaryMinusExpression(JassExpressionSyntax expression) { - return new JassUnaryExpressionSyntax(UnaryOperatorType.Minus, expression); + return new JassUnaryExpressionSyntax( + Token(JassSyntaxKind.MinusToken), + expression); } - public static JassUnaryExpressionSyntax UnaryNotExpression(IExpressionSyntax expression) + public static JassUnaryExpressionSyntax UnaryNotExpression(JassExpressionSyntax expression) { - return new JassUnaryExpressionSyntax(UnaryOperatorType.Not, expression); + return new JassUnaryExpressionSyntax( + Token(JassSyntaxKind.NotKeyword), + expression); + } + + public static JassUnaryExpressionSyntax UnaryNotExpression(JassSyntaxToken operatorToken, JassExpressionSyntax expression) + { + var expressionKind = JassSyntaxFacts.GetUnaryExpressionKind(operatorToken.SyntaxKind); + if (expressionKind == JassSyntaxKind.None) + { + throw new ArgumentException($"'{operatorToken.SyntaxKind}' is not a valid operator kind for unary expressions.", nameof(operatorToken)); + } + + return new JassUnaryExpressionSyntax( + operatorToken, + expression); } } } \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/SyntaxFactory/VariableDeclaratorFactory.cs b/src/War3Net.CodeAnalysis.Jass/SyntaxFactory/VariableDeclaratorFactory.cs new file mode 100644 index 00000000..365cbb1b --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/SyntaxFactory/VariableDeclaratorFactory.cs @@ -0,0 +1,110 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using War3Net.CodeAnalysis.Jass.Syntax; + +namespace War3Net.CodeAnalysis.Jass +{ + public static partial class JassSyntaxFactory + { + public static JassVariableDeclaratorSyntax VariableDeclarator(JassTypeSyntax type, JassIdentifierNameSyntax identifierName) + { + return new JassVariableDeclaratorSyntax( + type, + identifierName, + null); + } + + public static JassVariableDeclaratorSyntax VariableDeclarator(JassTypeSyntax type, string name) + { + return new JassVariableDeclaratorSyntax( + type, + ParseIdentifierName(name), + null); + } + + public static JassVariableDeclaratorSyntax VariableDeclarator(string type, JassIdentifierNameSyntax identifierName) + { + return new JassVariableDeclaratorSyntax( + ParseTypeName(type), + identifierName, + null); + } + + public static JassVariableDeclaratorSyntax VariableDeclarator(string type, string name) + { + return new JassVariableDeclaratorSyntax( + ParseTypeName(type), + ParseIdentifierName(name), + null); + } + + public static JassVariableDeclaratorSyntax VariableDeclarator(JassTypeSyntax type, JassIdentifierNameSyntax identifierName, JassEqualsValueClauseSyntax? value) + { + return new JassVariableDeclaratorSyntax( + type, + identifierName, + value); + } + + public static JassVariableDeclaratorSyntax VariableDeclarator(JassTypeSyntax type, JassIdentifierNameSyntax identifierName, JassExpressionSyntax value) + { + return new JassVariableDeclaratorSyntax( + type, + identifierName, + EqualsValueClause(value)); + } + + public static JassVariableDeclaratorSyntax VariableDeclarator(JassTypeSyntax type, string name, JassEqualsValueClauseSyntax? value) + { + return new JassVariableDeclaratorSyntax( + type, + ParseIdentifierName(name), + value); + } + + public static JassVariableDeclaratorSyntax VariableDeclarator(JassTypeSyntax type, string name, JassExpressionSyntax value) + { + return new JassVariableDeclaratorSyntax( + type, + ParseIdentifierName(name), + EqualsValueClause(value)); + } + + public static JassVariableDeclaratorSyntax VariableDeclarator(string type, JassIdentifierNameSyntax identifierName, JassEqualsValueClauseSyntax? value) + { + return new JassVariableDeclaratorSyntax( + ParseTypeName(type), + identifierName, + value); + } + + public static JassVariableDeclaratorSyntax VariableDeclarator(string type, JassIdentifierNameSyntax identifierName, JassExpressionSyntax value) + { + return new JassVariableDeclaratorSyntax( + ParseTypeName(type), + identifierName, + EqualsValueClause(value)); + } + + public static JassVariableDeclaratorSyntax VariableDeclarator(string type, string name, JassEqualsValueClauseSyntax? value) + { + return new JassVariableDeclaratorSyntax( + ParseTypeName(type), + ParseIdentifierName(name), + value); + } + + public static JassVariableDeclaratorSyntax VariableDeclarator(string type, string name, JassExpressionSyntax value) + { + return new JassVariableDeclaratorSyntax( + ParseTypeName(type), + ParseIdentifierName(name), + EqualsValueClause(value)); + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/SyntaxFactory/VariableReferenceFactory.cs b/src/War3Net.CodeAnalysis.Jass/SyntaxFactory/VariableReferenceFactory.cs deleted file mode 100644 index 5c5506b9..00000000 --- a/src/War3Net.CodeAnalysis.Jass/SyntaxFactory/VariableReferenceFactory.cs +++ /dev/null @@ -1,19 +0,0 @@ -// ------------------------------------------------------------------------------ -// -// Licensed under the MIT license. -// See the LICENSE file in the project root for more information. -// -// ------------------------------------------------------------------------------ - -using War3Net.CodeAnalysis.Jass.Syntax; - -namespace War3Net.CodeAnalysis.Jass -{ - public static partial class JassSyntaxFactory - { - public static JassVariableReferenceExpressionSyntax VariableReferenceExpression(string name) - { - return new JassVariableReferenceExpressionSyntax(ParseIdentifierName(name)); - } - } -} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/SyntaxRewriter/ArgumentListRewriter.cs b/src/War3Net.CodeAnalysis.Jass/SyntaxRewriter/ArgumentListRewriter.cs new file mode 100644 index 00000000..e6ec1963 --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/SyntaxRewriter/ArgumentListRewriter.cs @@ -0,0 +1,35 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using War3Net.CodeAnalysis.Jass.Syntax; + +namespace War3Net.CodeAnalysis.Jass +{ + public abstract partial class JassSyntaxRewriter + { + /// The to rewrite. + /// The rewritten , or the input if it wasn't rewritten. + /// if the was rewritten to , otherwise . + protected virtual bool RewriteArgumentList(JassArgumentListSyntax argumentList, out JassArgumentListSyntax result) + { + if (RewriteToken(argumentList.OpenParenToken, out var openParenToken) | + RewriteSeparatedArgumentList(argumentList.ArgumentList, out var separatedArgumentList) | + RewriteToken(argumentList.CloseParenToken, out var closeParenToken)) + { + result = new JassArgumentListSyntax( + openParenToken, + separatedArgumentList, + closeParenToken); + + return true; + } + + result = argumentList; + return false; + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/SyntaxRewriter/ArrayDeclaratorRewriter.cs b/src/War3Net.CodeAnalysis.Jass/SyntaxRewriter/ArrayDeclaratorRewriter.cs new file mode 100644 index 00000000..a1360699 --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/SyntaxRewriter/ArrayDeclaratorRewriter.cs @@ -0,0 +1,35 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using War3Net.CodeAnalysis.Jass.Syntax; + +namespace War3Net.CodeAnalysis.Jass +{ + public abstract partial class JassSyntaxRewriter + { + /// The to rewrite. + /// The rewritten , or the input if it wasn't rewritten. + /// if the was rewritten to , otherwise . + protected virtual bool RewriteArrayDeclarator(JassArrayDeclaratorSyntax arrayDeclarator, out JassVariableOrArrayDeclaratorSyntax result) + { + if (RewriteType(arrayDeclarator.Type, out var type) | + RewriteToken(arrayDeclarator.ArrayToken, out var arrayToken) | + RewriteIdentifierName(arrayDeclarator.IdentifierName, out var identifierName)) + { + result = new JassArrayDeclaratorSyntax( + type, + arrayToken, + identifierName); + + return true; + } + + result = arrayDeclarator; + return false; + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/SyntaxRewriter/BinaryExpressionRewriter.cs b/src/War3Net.CodeAnalysis.Jass/SyntaxRewriter/BinaryExpressionRewriter.cs new file mode 100644 index 00000000..d01be03a --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/SyntaxRewriter/BinaryExpressionRewriter.cs @@ -0,0 +1,35 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using War3Net.CodeAnalysis.Jass.Syntax; + +namespace War3Net.CodeAnalysis.Jass +{ + public abstract partial class JassSyntaxRewriter + { + /// The to rewrite. + /// The rewritten , or the input if it wasn't rewritten. + /// if the was rewritten to , otherwise . + protected virtual bool RewriteBinaryExpression(JassBinaryExpressionSyntax binaryExpression, out JassExpressionSyntax result) + { + if (RewriteExpression(binaryExpression.Left, out var left) | + RewriteToken(binaryExpression.OperatorToken, out var operatorToken) | + RewriteExpression(binaryExpression.Right, out var right)) + { + result = new JassBinaryExpressionSyntax( + left, + operatorToken, + right); + + return true; + } + + result = binaryExpression; + return false; + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/SyntaxRewriter/CallStatementRewriter.cs b/src/War3Net.CodeAnalysis.Jass/SyntaxRewriter/CallStatementRewriter.cs new file mode 100644 index 00000000..b8465d08 --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/SyntaxRewriter/CallStatementRewriter.cs @@ -0,0 +1,35 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using War3Net.CodeAnalysis.Jass.Syntax; + +namespace War3Net.CodeAnalysis.Jass +{ + public abstract partial class JassSyntaxRewriter + { + /// The to rewrite. + /// The rewritten , or the input if it wasn't rewritten. + /// if the was rewritten to , otherwise . + protected virtual bool RewriteCallStatement(JassCallStatementSyntax callStatement, out JassStatementSyntax result) + { + if (RewriteToken(callStatement.CallToken, out var callToken) | + RewriteIdentifierName(callStatement.IdentifierName, out var identifierName) | + RewriteArgumentList(callStatement.ArgumentList, out var argumentList)) + { + result = new JassCallStatementSyntax( + callToken, + identifierName, + argumentList); + + return true; + } + + result = callStatement; + return false; + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/SyntaxRewriter/CompilationUnitRewriter.cs b/src/War3Net.CodeAnalysis.Jass/SyntaxRewriter/CompilationUnitRewriter.cs new file mode 100644 index 00000000..aa8accb7 --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/SyntaxRewriter/CompilationUnitRewriter.cs @@ -0,0 +1,33 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using War3Net.CodeAnalysis.Jass.Syntax; + +namespace War3Net.CodeAnalysis.Jass +{ + public abstract partial class JassSyntaxRewriter + { + /// The to rewrite. + /// The rewritten , or the input if it wasn't rewritten. + /// if the was rewritten to , otherwise . + protected virtual bool RewriteCompilationUnit(JassCompilationUnitSyntax compilationUnit, out JassCompilationUnitSyntax result) + { + if (RewriteDeclarationList(compilationUnit.Declarations, out var declarations) | + RewriteToken(compilationUnit.EndOfFileToken, out var endOfFileToken)) + { + result = new JassCompilationUnitSyntax( + declarations, + endOfFileToken); + + return true; + } + + result = compilationUnit; + return false; + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/SyntaxRewriter/DebugStatementRewriter.cs b/src/War3Net.CodeAnalysis.Jass/SyntaxRewriter/DebugStatementRewriter.cs new file mode 100644 index 00000000..ed74be7e --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/SyntaxRewriter/DebugStatementRewriter.cs @@ -0,0 +1,33 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using War3Net.CodeAnalysis.Jass.Syntax; + +namespace War3Net.CodeAnalysis.Jass +{ + public abstract partial class JassSyntaxRewriter + { + /// The to rewrite. + /// The rewritten , or the input if it wasn't rewritten. + /// if the was rewritten to , otherwise . + protected virtual bool RewriteDebugStatement(JassDebugStatementSyntax debugStatement, out JassStatementSyntax result) + { + if (RewriteToken(debugStatement.DebugToken, out var debugToken) | + RewriteStatement(debugStatement.Statement, out var statement)) + { + result = new JassDebugStatementSyntax( + debugToken, + statement); + + return true; + } + + result = debugStatement; + return false; + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/SyntaxRewriter/DeclarationListRewriter.cs b/src/War3Net.CodeAnalysis.Jass/SyntaxRewriter/DeclarationListRewriter.cs new file mode 100644 index 00000000..821e4525 --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/SyntaxRewriter/DeclarationListRewriter.cs @@ -0,0 +1,51 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using System.Collections.Immutable; + +using War3Net.CodeAnalysis.Jass.Syntax; + +namespace War3Net.CodeAnalysis.Jass +{ + public abstract partial class JassSyntaxRewriter + { + /// The to rewrite. + /// The rewritten , or the input if it wasn't rewritten. + /// if the was rewritten to , otherwise . + protected virtual bool RewriteDeclarationList(ImmutableArray declarationList, out ImmutableArray result) + { + if (declarationList.IsEmpty) + { + result = declarationList; + return false; + } + + for (var i = 0; i < declarationList.Length; i++) + { + if (RewriteTopLevelDeclaration(declarationList[i], out var declaration)) + { + var declarationListBuilder = declarationList.ToBuilder(); + declarationListBuilder[i] = declaration; + + while (++i < declarationList.Length) + { + if (RewriteTopLevelDeclaration(declarationList[i], out declaration)) + { + declarationListBuilder[i] = declaration; + } + } + + result = declarationListBuilder.ToImmutable(); + return true; + } + } + + result = declarationList; + return false; + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/SyntaxRewriter/ElementAccessClauseRewriter.cs b/src/War3Net.CodeAnalysis.Jass/SyntaxRewriter/ElementAccessClauseRewriter.cs new file mode 100644 index 00000000..ef07a3a7 --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/SyntaxRewriter/ElementAccessClauseRewriter.cs @@ -0,0 +1,43 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using System.Diagnostics.CodeAnalysis; + +using War3Net.CodeAnalysis.Jass.Syntax; + +namespace War3Net.CodeAnalysis.Jass +{ + public abstract partial class JassSyntaxRewriter + { + /// The to rewrite. + /// The rewritten , or the input if it wasn't rewritten. + /// if the was rewritten to , otherwise . + protected virtual bool RewriteElementAccessClause(JassElementAccessClauseSyntax? elementAccessClause, [NotNullIfNotNull("elementAccessClause")] out JassElementAccessClauseSyntax? result) + { + if (elementAccessClause is null) + { + result = null; + return false; + } + + if (RewriteToken(elementAccessClause.OpenBracketToken, out var openBracketToken) | + RewriteExpression(elementAccessClause.Expression, out var expression) | + RewriteToken(elementAccessClause.CloseBracketToken, out var closeBracketToken)) + { + result = new JassElementAccessClauseSyntax( + openBracketToken, + expression, + closeBracketToken); + + return true; + } + + result = elementAccessClause; + return false; + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/SyntaxRewriter/ElementAccessExpressionRewriter.cs b/src/War3Net.CodeAnalysis.Jass/SyntaxRewriter/ElementAccessExpressionRewriter.cs new file mode 100644 index 00000000..c1852b2e --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/SyntaxRewriter/ElementAccessExpressionRewriter.cs @@ -0,0 +1,33 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using War3Net.CodeAnalysis.Jass.Syntax; + +namespace War3Net.CodeAnalysis.Jass +{ + public abstract partial class JassSyntaxRewriter + { + /// The to rewrite. + /// The rewritten , or the input if it wasn't rewritten. + /// if the was rewritten to , otherwise . + protected virtual bool RewriteElementAccessExpression(JassElementAccessExpressionSyntax elementAccessExpression, out JassExpressionSyntax result) + { + if (RewriteIdentifierName(elementAccessExpression.IdentifierName, out var identifierName) | + RewriteElementAccessClause(elementAccessExpression.ElementAccessClause, out var elementAccessClause)) + { + result = new JassElementAccessExpressionSyntax( + identifierName, + elementAccessClause); + + return true; + } + + result = elementAccessExpression; + return false; + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/SyntaxRewriter/ElseClauseRewriter.cs b/src/War3Net.CodeAnalysis.Jass/SyntaxRewriter/ElseClauseRewriter.cs new file mode 100644 index 00000000..4f2f5faa --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/SyntaxRewriter/ElseClauseRewriter.cs @@ -0,0 +1,41 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using System.Diagnostics.CodeAnalysis; + +using War3Net.CodeAnalysis.Jass.Syntax; + +namespace War3Net.CodeAnalysis.Jass +{ + public abstract partial class JassSyntaxRewriter + { + /// The to rewrite. + /// The rewritten , or the input if it wasn't rewritten. + /// if the was rewritten to , otherwise . + protected virtual bool RewriteElseClause(JassElseClauseSyntax? elseClause, [NotNullIfNotNull("elseClause")] out JassElseClauseSyntax? result) + { + if (elseClause is null) + { + result = null; + return false; + } + + if (RewriteToken(elseClause.ElseToken, out var elseToken) | + RewriteStatementList(elseClause.Statements, out var statements)) + { + result = new JassElseClauseSyntax( + elseToken, + statements); + + return true; + } + + result = elseClause; + return false; + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/SyntaxRewriter/ElseIfClauseDeclaratorRewriter.cs b/src/War3Net.CodeAnalysis.Jass/SyntaxRewriter/ElseIfClauseDeclaratorRewriter.cs new file mode 100644 index 00000000..fdbe6e4b --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/SyntaxRewriter/ElseIfClauseDeclaratorRewriter.cs @@ -0,0 +1,35 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using War3Net.CodeAnalysis.Jass.Syntax; + +namespace War3Net.CodeAnalysis.Jass +{ + public abstract partial class JassSyntaxRewriter + { + /// The to rewrite. + /// The rewritten , or the input if it wasn't rewritten. + /// if the was rewritten to , otherwise . + protected virtual bool RewriteElseIfClauseDeclarator(JassElseIfClauseDeclaratorSyntax elseIfClauseDeclarator, out JassElseIfClauseDeclaratorSyntax result) + { + if (RewriteToken(elseIfClauseDeclarator.ElseIfToken, out var elseIfToken) | + RewriteExpression(elseIfClauseDeclarator.Condition, out var condition) | + RewriteToken(elseIfClauseDeclarator.ThenToken, out var thenToken)) + { + result = new JassElseIfClauseDeclaratorSyntax( + elseIfToken, + condition, + thenToken); + + return true; + } + + result = elseIfClauseDeclarator; + return false; + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/SyntaxRewriter/ElseIfClauseListRewriter.cs b/src/War3Net.CodeAnalysis.Jass/SyntaxRewriter/ElseIfClauseListRewriter.cs new file mode 100644 index 00000000..925f8a9c --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/SyntaxRewriter/ElseIfClauseListRewriter.cs @@ -0,0 +1,50 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using System.Collections.Immutable; + +using War3Net.CodeAnalysis.Jass.Syntax; + +namespace War3Net.CodeAnalysis.Jass +{ + public abstract partial class JassSyntaxRewriter + { + /// The to rewrite. + /// The rewritten , or the input if it wasn't rewritten. + /// if the was rewritten to , otherwise . + protected virtual bool RewriteElseIfClauseList(ImmutableArray elseIfClauseList, out ImmutableArray result) + { + if (elseIfClauseList.IsEmpty) + { + result = elseIfClauseList; + return false; + } + + for (var i = 0; i < elseIfClauseList.Length; i++) + { + if (RewriteElseIfClause(elseIfClauseList[i], out var elseIfClause)) + { + var elseIfClauseListBuilder = ImmutableArray.CreateBuilder(elseIfClauseList.Length); + elseIfClauseListBuilder.AddRange(elseIfClauseList, i); + elseIfClauseListBuilder.Add(elseIfClause); + + while (++i < elseIfClauseList.Length) + { + RewriteElseIfClause(elseIfClauseList[i], out elseIfClause); + elseIfClauseListBuilder.Add(elseIfClause); + } + + result = elseIfClauseListBuilder.ToImmutable(); + return true; + } + } + + result = elseIfClauseList; + return false; + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/SyntaxRewriter/ElseIfClauseRewriter.cs b/src/War3Net.CodeAnalysis.Jass/SyntaxRewriter/ElseIfClauseRewriter.cs new file mode 100644 index 00000000..fbb55dc4 --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/SyntaxRewriter/ElseIfClauseRewriter.cs @@ -0,0 +1,33 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using War3Net.CodeAnalysis.Jass.Syntax; + +namespace War3Net.CodeAnalysis.Jass +{ + public abstract partial class JassSyntaxRewriter + { + /// The to rewrite. + /// The rewritten , or the input if it wasn't rewritten. + /// if the was rewritten to , otherwise . + protected virtual bool RewriteElseIfClause(JassElseIfClauseSyntax elseIfClause, out JassElseIfClauseSyntax result) + { + if (RewriteElseIfClauseDeclarator(elseIfClause.ElseIfClauseDeclarator, out var elseIfClauseDeclarator) | + RewriteStatementList(elseIfClause.Statements, out var statements)) + { + result = new JassElseIfClauseSyntax( + elseIfClauseDeclarator, + statements); + + return true; + } + + result = elseIfClause; + return false; + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/SyntaxRewriter/EmptyParameterListRewriter.cs b/src/War3Net.CodeAnalysis.Jass/SyntaxRewriter/EmptyParameterListRewriter.cs new file mode 100644 index 00000000..6596df43 --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/SyntaxRewriter/EmptyParameterListRewriter.cs @@ -0,0 +1,33 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using War3Net.CodeAnalysis.Jass.Syntax; + +namespace War3Net.CodeAnalysis.Jass +{ + public abstract partial class JassSyntaxRewriter + { + /// The to rewrite. + /// The rewritten , or the input if it wasn't rewritten. + /// if the was rewritten to , otherwise . + protected virtual bool RewriteEmptyParameterList(JassEmptyParameterListSyntax emptyParameterList, out JassParameterListOrEmptyParameterListSyntax result) + { + if (RewriteToken(emptyParameterList.TakesToken, out var takesToken) | + RewriteToken(emptyParameterList.NothingToken, out var nothingToken)) + { + result = new JassEmptyParameterListSyntax( + takesToken, + nothingToken); + + return true; + } + + result = emptyParameterList; + return false; + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/SyntaxRewriter/EqualsValueClauseRewriter.cs b/src/War3Net.CodeAnalysis.Jass/SyntaxRewriter/EqualsValueClauseRewriter.cs new file mode 100644 index 00000000..4a81a2dd --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/SyntaxRewriter/EqualsValueClauseRewriter.cs @@ -0,0 +1,41 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using System.Diagnostics.CodeAnalysis; + +using War3Net.CodeAnalysis.Jass.Syntax; + +namespace War3Net.CodeAnalysis.Jass +{ + public abstract partial class JassSyntaxRewriter + { + /// The to rewrite. + /// The rewritten , or the input if it wasn't rewritten. + /// if the was rewritten to , otherwise . + protected virtual bool RewriteEqualsValueClause(JassEqualsValueClauseSyntax? equalsValueClause, [NotNullIfNotNull("equalsValueClause")] out JassEqualsValueClauseSyntax? result) + { + if (equalsValueClause is null) + { + result = null; + return false; + } + + if (RewriteToken(equalsValueClause.EqualsToken, out var equalsToken) | + RewriteExpression(equalsValueClause.Expression, out var expression)) + { + result = new JassEqualsValueClauseSyntax( + equalsToken, + expression); + + return true; + } + + result = equalsValueClause; + return false; + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/SyntaxRewriter/ExitStatementRewriter.cs b/src/War3Net.CodeAnalysis.Jass/SyntaxRewriter/ExitStatementRewriter.cs new file mode 100644 index 00000000..b721f446 --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/SyntaxRewriter/ExitStatementRewriter.cs @@ -0,0 +1,33 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using War3Net.CodeAnalysis.Jass.Syntax; + +namespace War3Net.CodeAnalysis.Jass +{ + public abstract partial class JassSyntaxRewriter + { + /// The to rewrite. + /// The rewritten , or the input if it wasn't rewritten. + /// if the was rewritten to , otherwise . + protected virtual bool RewriteExitStatement(JassExitStatementSyntax exitStatement, out JassStatementSyntax result) + { + if (RewriteToken(exitStatement.ExitWhenToken, out var exitWhenToken) | + RewriteExpression(exitStatement.Condition, out var condition)) + { + result = new JassExitStatementSyntax( + exitWhenToken, + condition); + + return true; + } + + result = exitStatement; + return false; + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/SyntaxRewriter/ExpressionRewriter.cs b/src/War3Net.CodeAnalysis.Jass/SyntaxRewriter/ExpressionRewriter.cs new file mode 100644 index 00000000..cde23ea9 --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/SyntaxRewriter/ExpressionRewriter.cs @@ -0,0 +1,40 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using System.Diagnostics.CodeAnalysis; + +using War3Net.CodeAnalysis.Jass.Syntax; + +namespace War3Net.CodeAnalysis.Jass +{ + public abstract partial class JassSyntaxRewriter + { + /// The to rewrite. + /// The rewritten , or the input if it wasn't rewritten. + /// if the was rewritten to , otherwise . + protected virtual bool RewriteExpression(JassExpressionSyntax? expression, [NotNullIfNotNull("expression")] out JassExpressionSyntax? result) + { + if (expression is null) + { + result = null; + return false; + } + + return expression switch + { + JassElementAccessExpressionSyntax elementAccessExpression => RewriteElementAccessExpression(elementAccessExpression, out result), + JassBinaryExpressionSyntax binaryExpression => RewriteBinaryExpression(binaryExpression, out result), + JassFunctionReferenceExpressionSyntax functionReferenceExpression => RewriteFunctionReferenceExpression(functionReferenceExpression, out result), + JassIdentifierNameSyntax identifierName => RewriteIdentifierNameAsExpression(identifierName, out result), + JassInvocationExpressionSyntax invocationExpression => RewriteInvocationExpression(invocationExpression, out result), + JassLiteralExpressionSyntax literalExpression => RewriteLiteralExpression(literalExpression, out result), + JassParenthesizedExpressionSyntax parenthesizedExpression => RewriteParenthesizedExpression(parenthesizedExpression, out result), + JassUnaryExpressionSyntax unaryExpression => RewriteUnaryExpression(unaryExpression, out result), + }; + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/SyntaxRewriter/FunctionDeclarationRewriter.cs b/src/War3Net.CodeAnalysis.Jass/SyntaxRewriter/FunctionDeclarationRewriter.cs new file mode 100644 index 00000000..ca5de1fa --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/SyntaxRewriter/FunctionDeclarationRewriter.cs @@ -0,0 +1,35 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using War3Net.CodeAnalysis.Jass.Syntax; + +namespace War3Net.CodeAnalysis.Jass +{ + public abstract partial class JassSyntaxRewriter + { + /// The to rewrite. + /// The rewritten , or the input if it wasn't rewritten. + /// if the was rewritten to , otherwise . + protected virtual bool RewriteFunctionDeclaration(JassFunctionDeclarationSyntax functionDeclaration, out JassTopLevelDeclarationSyntax result) + { + if (RewriteFunctionDeclarator(functionDeclaration.FunctionDeclarator, out var functionDeclarator) | + RewriteStatementList(functionDeclaration.Statements, out var statements) | + RewriteToken(functionDeclaration.EndFunctionToken, out var endFunctionToken)) + { + result = new JassFunctionDeclarationSyntax( + functionDeclarator, + statements, + endFunctionToken); + + return true; + } + + result = functionDeclaration; + return false; + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/SyntaxRewriter/FunctionDeclaratorRewriter.cs b/src/War3Net.CodeAnalysis.Jass/SyntaxRewriter/FunctionDeclaratorRewriter.cs new file mode 100644 index 00000000..87a44c9e --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/SyntaxRewriter/FunctionDeclaratorRewriter.cs @@ -0,0 +1,39 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using War3Net.CodeAnalysis.Jass.Syntax; + +namespace War3Net.CodeAnalysis.Jass +{ + public abstract partial class JassSyntaxRewriter + { + /// The to rewrite. + /// The rewritten , or the input if it wasn't rewritten. + /// if the was rewritten to , otherwise . + protected virtual bool RewriteFunctionDeclarator(JassFunctionDeclaratorSyntax functionDeclarator, out JassFunctionDeclaratorSyntax result) + { + if (RewriteToken(functionDeclarator.ConstantToken, out var constantToken) | + RewriteToken(functionDeclarator.FunctionToken, out var functionToken) | + RewriteIdentifierName(functionDeclarator.IdentifierName, out var identifierName) | + RewriteParameterListOrEmptyParameterList(functionDeclarator.ParameterList, out var parameterList) | + RewriteReturnClause(functionDeclarator.ReturnClause, out var returnClause)) + { + result = new JassFunctionDeclaratorSyntax( + constantToken, + functionToken, + identifierName, + parameterList, + returnClause); + + return true; + } + + result = functionDeclarator; + return false; + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/SyntaxRewriter/FunctionReferenceExpressionRewriter.cs b/src/War3Net.CodeAnalysis.Jass/SyntaxRewriter/FunctionReferenceExpressionRewriter.cs new file mode 100644 index 00000000..5cd136ab --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/SyntaxRewriter/FunctionReferenceExpressionRewriter.cs @@ -0,0 +1,33 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using War3Net.CodeAnalysis.Jass.Syntax; + +namespace War3Net.CodeAnalysis.Jass +{ + public abstract partial class JassSyntaxRewriter + { + /// The to rewrite. + /// The rewritten , or the input if it wasn't rewritten. + /// if the was rewritten to , otherwise . + protected virtual bool RewriteFunctionReferenceExpression(JassFunctionReferenceExpressionSyntax functionReferenceExpression, out JassExpressionSyntax result) + { + if (RewriteToken(functionReferenceExpression.FunctionToken, out var functionToken) | + RewriteIdentifierName(functionReferenceExpression.IdentifierName, out var identifierName)) + { + result = new JassFunctionReferenceExpressionSyntax( + functionToken, + identifierName); + + return true; + } + + result = functionReferenceExpression; + return false; + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/SyntaxRewriter/GlobalConstantDeclarationRewriter.cs b/src/War3Net.CodeAnalysis.Jass/SyntaxRewriter/GlobalConstantDeclarationRewriter.cs new file mode 100644 index 00000000..81cc612b --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/SyntaxRewriter/GlobalConstantDeclarationRewriter.cs @@ -0,0 +1,37 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using War3Net.CodeAnalysis.Jass.Syntax; + +namespace War3Net.CodeAnalysis.Jass +{ + public abstract partial class JassSyntaxRewriter + { + /// The to rewrite. + /// The rewritten , or the input if it wasn't rewritten. + /// if the was rewritten to , otherwise . + protected virtual bool RewriteGlobalConstantDeclaration(JassGlobalConstantDeclarationSyntax globalConstantDeclaration, out JassGlobalDeclarationSyntax result) + { + if (RewriteToken(globalConstantDeclaration.ConstantToken, out var constantToken) | + RewriteType(globalConstantDeclaration.Type, out var type) | + RewriteIdentifierName(globalConstantDeclaration.IdentifierName, out var identifierName) | + RewriteEqualsValueClause(globalConstantDeclaration.Value, out var value)) + { + result = new JassGlobalConstantDeclarationSyntax( + constantToken, + type, + identifierName, + value); + + return true; + } + + result = globalConstantDeclaration; + return false; + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/SyntaxRewriter/GlobalDeclarationListRewriter.cs b/src/War3Net.CodeAnalysis.Jass/SyntaxRewriter/GlobalDeclarationListRewriter.cs new file mode 100644 index 00000000..6dc27dab --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/SyntaxRewriter/GlobalDeclarationListRewriter.cs @@ -0,0 +1,50 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using System.Collections.Immutable; + +using War3Net.CodeAnalysis.Jass.Syntax; + +namespace War3Net.CodeAnalysis.Jass +{ + public abstract partial class JassSyntaxRewriter + { + /// The to rewrite. + /// The rewritten , or the input if it wasn't rewritten. + /// if the was rewritten to , otherwise . + protected virtual bool RewriteGlobalDeclarationList(ImmutableArray globalDeclarationList, out ImmutableArray result) + { + if (globalDeclarationList.IsEmpty) + { + result = globalDeclarationList; + return false; + } + + for (var i = 0; i < globalDeclarationList.Length; i++) + { + if (RewriteGlobalDeclaration(globalDeclarationList[i], out var globalDeclaration)) + { + var globalDeclarationListBuilder = ImmutableArray.CreateBuilder(globalDeclarationList.Length); + globalDeclarationListBuilder.AddRange(globalDeclarationList, i); + globalDeclarationListBuilder.Add(globalDeclaration); + + while (++i < globalDeclarationList.Length) + { + RewriteGlobalDeclaration(globalDeclarationList[i], out globalDeclaration); + globalDeclarationListBuilder.Add(globalDeclaration); + } + + result = globalDeclarationListBuilder.ToImmutable(); + return true; + } + } + + result = globalDeclarationList; + return false; + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/SyntaxRewriter/GlobalDeclarationRewriter.cs b/src/War3Net.CodeAnalysis.Jass/SyntaxRewriter/GlobalDeclarationRewriter.cs new file mode 100644 index 00000000..9f05e40e --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/SyntaxRewriter/GlobalDeclarationRewriter.cs @@ -0,0 +1,26 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using War3Net.CodeAnalysis.Jass.Syntax; + +namespace War3Net.CodeAnalysis.Jass +{ + public abstract partial class JassSyntaxRewriter + { + /// The to rewrite. + /// The rewritten , or the input if it wasn't rewritten. + /// if the was rewritten to , otherwise . + protected virtual bool RewriteGlobalDeclaration(JassGlobalDeclarationSyntax globalDeclaration, out JassGlobalDeclarationSyntax result) + { + return globalDeclaration switch + { + JassGlobalConstantDeclarationSyntax globalConstantDeclaration => RewriteGlobalConstantDeclaration(globalConstantDeclaration, out result), + JassGlobalVariableDeclarationSyntax globalVariableDeclaration => RewriteGlobalVariableDeclaration(globalVariableDeclaration, out result), + }; + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/SyntaxRewriter/GlobalVariableDeclarationRewriter.cs b/src/War3Net.CodeAnalysis.Jass/SyntaxRewriter/GlobalVariableDeclarationRewriter.cs new file mode 100644 index 00000000..e474a0b0 --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/SyntaxRewriter/GlobalVariableDeclarationRewriter.cs @@ -0,0 +1,29 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using War3Net.CodeAnalysis.Jass.Syntax; + +namespace War3Net.CodeAnalysis.Jass +{ + public abstract partial class JassSyntaxRewriter + { + /// The to rewrite. + /// The rewritten , or the input if it wasn't rewritten. + /// if the was rewritten to , otherwise . + protected virtual bool RewriteGlobalVariableDeclaration(JassGlobalVariableDeclarationSyntax globalVariableDeclaration, out JassGlobalDeclarationSyntax result) + { + if (RewriteVariableOrArrayDeclarator(globalVariableDeclaration.Declarator, out var declarator)) + { + result = new JassGlobalVariableDeclarationSyntax(declarator); + return true; + } + + result = globalVariableDeclaration; + return false; + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/SyntaxRewriter/GlobalsDeclarationRewriter.cs b/src/War3Net.CodeAnalysis.Jass/SyntaxRewriter/GlobalsDeclarationRewriter.cs new file mode 100644 index 00000000..40650032 --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/SyntaxRewriter/GlobalsDeclarationRewriter.cs @@ -0,0 +1,35 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using War3Net.CodeAnalysis.Jass.Syntax; + +namespace War3Net.CodeAnalysis.Jass +{ + public abstract partial class JassSyntaxRewriter + { + /// The to rewrite. + /// The rewritten , or the input if it wasn't rewritten. + /// if the was rewritten to , otherwise . + protected virtual bool RewriteGlobalsDeclaration(JassGlobalsDeclarationSyntax globalsDeclaration, out JassTopLevelDeclarationSyntax result) + { + if (RewriteToken(globalsDeclaration.GlobalsToken, out var globalsToken) | + RewriteGlobalDeclarationList(globalsDeclaration.GlobalDeclarations, out var globalDeclarations) | + RewriteToken(globalsDeclaration.EndGlobalsToken, out var endGlobalsToken)) + { + result = new JassGlobalsDeclarationSyntax( + globalsToken, + globalDeclarations, + endGlobalsToken); + + return true; + } + + result = globalsDeclaration; + return false; + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/SyntaxRewriter/IdentifierNameRewriter.cs b/src/War3Net.CodeAnalysis.Jass/SyntaxRewriter/IdentifierNameRewriter.cs new file mode 100644 index 00000000..86018ba6 --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/SyntaxRewriter/IdentifierNameRewriter.cs @@ -0,0 +1,59 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using War3Net.CodeAnalysis.Jass.Syntax; + +namespace War3Net.CodeAnalysis.Jass +{ + public abstract partial class JassSyntaxRewriter + { + /// The to rewrite. + /// The rewritten , or the input if it wasn't rewritten. + /// if the was rewritten to , otherwise . + protected virtual bool RewriteIdentifierName(JassIdentifierNameSyntax identifierName, out JassIdentifierNameSyntax result) + { + if (RewriteToken(identifierName.Token, out var token)) + { + result = new JassIdentifierNameSyntax(token); + return true; + } + + result = identifierName; + return false; + } + + /// The to rewrite. + /// The rewritten , or the input if it wasn't rewritten. + /// if the was rewritten to , otherwise . + protected virtual bool RewriteIdentifierNameAsExpression(JassIdentifierNameSyntax identifierName, out JassExpressionSyntax result) + { + if (RewriteToken(identifierName.Token, out var token)) + { + result = new JassIdentifierNameSyntax(token); + return true; + } + + result = identifierName; + return false; + } + + /// The to rewrite. + /// The rewritten , or the input if it wasn't rewritten. + /// if the was rewritten to , otherwise . + protected virtual bool RewriteIdentifierNameAsType(JassIdentifierNameSyntax identifierName, out JassTypeSyntax result) + { + if (RewriteToken(identifierName.Token, out var token)) + { + result = new JassIdentifierNameSyntax(token); + return true; + } + + result = identifierName; + return false; + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/SyntaxRewriter/IfClauseDeclaratorRewriter.cs b/src/War3Net.CodeAnalysis.Jass/SyntaxRewriter/IfClauseDeclaratorRewriter.cs new file mode 100644 index 00000000..c878c047 --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/SyntaxRewriter/IfClauseDeclaratorRewriter.cs @@ -0,0 +1,35 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using War3Net.CodeAnalysis.Jass.Syntax; + +namespace War3Net.CodeAnalysis.Jass +{ + public abstract partial class JassSyntaxRewriter + { + /// The to rewrite. + /// The rewritten , or the input if it wasn't rewritten. + /// if the was rewritten to , otherwise . + protected virtual bool RewriteIfClauseDeclarator(JassIfClauseDeclaratorSyntax ifClauseDeclarator, out JassIfClauseDeclaratorSyntax result) + { + if (RewriteToken(ifClauseDeclarator.IfToken, out var ifToken) | + RewriteExpression(ifClauseDeclarator.Condition, out var condition) | + RewriteToken(ifClauseDeclarator.ThenToken, out var thenToken)) + { + result = new JassIfClauseDeclaratorSyntax( + ifToken, + condition, + thenToken); + + return true; + } + + result = ifClauseDeclarator; + return false; + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/SyntaxRewriter/IfClauseRewriter.cs b/src/War3Net.CodeAnalysis.Jass/SyntaxRewriter/IfClauseRewriter.cs new file mode 100644 index 00000000..b0f1b9d3 --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/SyntaxRewriter/IfClauseRewriter.cs @@ -0,0 +1,33 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using War3Net.CodeAnalysis.Jass.Syntax; + +namespace War3Net.CodeAnalysis.Jass +{ + public abstract partial class JassSyntaxRewriter + { + /// The to rewrite. + /// The rewritten , or the input if it wasn't rewritten. + /// if the was rewritten to , otherwise . + protected virtual bool RewriteIfClause(JassIfClauseSyntax ifClause, out JassIfClauseSyntax result) + { + if (RewriteIfClauseDeclarator(ifClause.IfClauseDeclarator, out var ifClauseDeclarator) | + RewriteStatementList(ifClause.Statements, out var statements)) + { + result = new JassIfClauseSyntax( + ifClauseDeclarator, + statements); + + return true; + } + + result = ifClause; + return false; + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/SyntaxRewriter/IfStatementRewriter.cs b/src/War3Net.CodeAnalysis.Jass/SyntaxRewriter/IfStatementRewriter.cs new file mode 100644 index 00000000..60e091c6 --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/SyntaxRewriter/IfStatementRewriter.cs @@ -0,0 +1,37 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using War3Net.CodeAnalysis.Jass.Syntax; + +namespace War3Net.CodeAnalysis.Jass +{ + public abstract partial class JassSyntaxRewriter + { + /// The to rewrite. + /// The rewritten , or the input if it wasn't rewritten. + /// if the was rewritten to , otherwise . + protected virtual bool RewriteIfStatement(JassIfStatementSyntax ifStatement, out JassStatementSyntax result) + { + if (RewriteIfClause(ifStatement.IfClause, out var ifClause) | + RewriteElseIfClauseList(ifStatement.ElseIfClauses, out var elseIfClauses) | + RewriteElseClause(ifStatement.ElseClause, out var elseClause) | + RewriteToken(ifStatement.EndIfToken, out var endIfToken)) + { + result = new JassIfStatementSyntax( + ifClause, + elseIfClauses, + elseClause, + endIfToken); + + return true; + } + + result = ifStatement; + return false; + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/SyntaxRewriter/InvocationExpressionRewriter.cs b/src/War3Net.CodeAnalysis.Jass/SyntaxRewriter/InvocationExpressionRewriter.cs new file mode 100644 index 00000000..836a517e --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/SyntaxRewriter/InvocationExpressionRewriter.cs @@ -0,0 +1,33 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using War3Net.CodeAnalysis.Jass.Syntax; + +namespace War3Net.CodeAnalysis.Jass +{ + public abstract partial class JassSyntaxRewriter + { + /// The to rewrite. + /// The rewritten , or the input if it wasn't rewritten. + /// if the was rewritten to , otherwise . + protected virtual bool RewriteInvocationExpression(JassInvocationExpressionSyntax invocationExpression, out JassExpressionSyntax result) + { + if (RewriteIdentifierName(invocationExpression.IdentifierName, out var identifierName) | + RewriteArgumentList(invocationExpression.ArgumentList, out var argumentList)) + { + result = new JassInvocationExpressionSyntax( + identifierName, + argumentList); + + return true; + } + + result = invocationExpression; + return false; + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/SyntaxRewriter/LiteralExpressionRewriter.cs b/src/War3Net.CodeAnalysis.Jass/SyntaxRewriter/LiteralExpressionRewriter.cs new file mode 100644 index 00000000..3a850b3a --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/SyntaxRewriter/LiteralExpressionRewriter.cs @@ -0,0 +1,29 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using War3Net.CodeAnalysis.Jass.Syntax; + +namespace War3Net.CodeAnalysis.Jass +{ + public abstract partial class JassSyntaxRewriter + { + /// The to rewrite. + /// The rewritten , or the input if it wasn't rewritten. + /// if the was rewritten to , otherwise . + protected virtual bool RewriteLiteralExpression(JassLiteralExpressionSyntax literalExpression, out JassExpressionSyntax result) + { + if (RewriteToken(literalExpression.Token, out var token)) + { + result = new JassLiteralExpressionSyntax(token); + return true; + } + + result = literalExpression; + return false; + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/SyntaxRewriter/LocalVariableDeclarationStatementRewriter.cs b/src/War3Net.CodeAnalysis.Jass/SyntaxRewriter/LocalVariableDeclarationStatementRewriter.cs new file mode 100644 index 00000000..6c2814e2 --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/SyntaxRewriter/LocalVariableDeclarationStatementRewriter.cs @@ -0,0 +1,33 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using War3Net.CodeAnalysis.Jass.Syntax; + +namespace War3Net.CodeAnalysis.Jass +{ + public abstract partial class JassSyntaxRewriter + { + /// The to rewrite. + /// The rewritten , or the input if it wasn't rewritten. + /// if the was rewritten to , otherwise . + protected virtual bool RewriteLocalVariableDeclarationStatement(JassLocalVariableDeclarationStatementSyntax localVariableDeclarationStatement, out JassStatementSyntax result) + { + if (RewriteToken(localVariableDeclarationStatement.LocalToken, out var localToken) | + RewriteVariableOrArrayDeclarator(localVariableDeclarationStatement.Declarator, out var declarator)) + { + result = new JassLocalVariableDeclarationStatementSyntax( + localToken, + declarator); + + return true; + } + + result = localVariableDeclarationStatement; + return false; + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/SyntaxRewriter/LoopStatementRewriter.cs b/src/War3Net.CodeAnalysis.Jass/SyntaxRewriter/LoopStatementRewriter.cs new file mode 100644 index 00000000..79640b1e --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/SyntaxRewriter/LoopStatementRewriter.cs @@ -0,0 +1,35 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using War3Net.CodeAnalysis.Jass.Syntax; + +namespace War3Net.CodeAnalysis.Jass +{ + public abstract partial class JassSyntaxRewriter + { + /// The to rewrite. + /// The rewritten , or the input if it wasn't rewritten. + /// if the was rewritten to , otherwise . + protected virtual bool RewriteLoopStatement(JassLoopStatementSyntax loopStatement, out JassStatementSyntax result) + { + if (RewriteToken(loopStatement.LoopToken, out var loopToken) | + RewriteStatementList(loopStatement.Statements, out var statements) | + RewriteToken(loopStatement.EndLoopToken, out var endLoopToken)) + { + result = new JassLoopStatementSyntax( + loopToken, + statements, + endLoopToken); + + return true; + } + + result = loopStatement; + return false; + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/SyntaxRewriter/NativeFunctionDeclarationRewriter.cs b/src/War3Net.CodeAnalysis.Jass/SyntaxRewriter/NativeFunctionDeclarationRewriter.cs new file mode 100644 index 00000000..f682ef89 --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/SyntaxRewriter/NativeFunctionDeclarationRewriter.cs @@ -0,0 +1,39 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using War3Net.CodeAnalysis.Jass.Syntax; + +namespace War3Net.CodeAnalysis.Jass +{ + public abstract partial class JassSyntaxRewriter + { + /// The to rewrite. + /// The rewritten , or the input if it wasn't rewritten. + /// if the was rewritten to , otherwise . + protected virtual bool RewriteNativeFunctionDeclaration(JassNativeFunctionDeclarationSyntax nativeFunctionDeclaration, out JassTopLevelDeclarationSyntax result) + { + if (RewriteToken(nativeFunctionDeclaration.ConstantToken, out var constantToken) | + RewriteToken(nativeFunctionDeclaration.NativeToken, out var nativeToken) | + RewriteIdentifierName(nativeFunctionDeclaration.IdentifierName, out var identifierName) | + RewriteParameterListOrEmptyParameterList(nativeFunctionDeclaration.ParameterList, out var parameterList) | + RewriteReturnClause(nativeFunctionDeclaration.ReturnClause, out var returnClause)) + { + result = new JassNativeFunctionDeclarationSyntax( + constantToken, + nativeToken, + identifierName, + parameterList, + returnClause); + + return true; + } + + result = nativeFunctionDeclaration; + return false; + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/SyntaxRewriter/ParameterListOrEmptyParameterListRewriter.cs b/src/War3Net.CodeAnalysis.Jass/SyntaxRewriter/ParameterListOrEmptyParameterListRewriter.cs new file mode 100644 index 00000000..45caba94 --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/SyntaxRewriter/ParameterListOrEmptyParameterListRewriter.cs @@ -0,0 +1,26 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using War3Net.CodeAnalysis.Jass.Syntax; + +namespace War3Net.CodeAnalysis.Jass +{ + public abstract partial class JassSyntaxRewriter + { + /// The to rewrite. + /// The rewritten , or the input if it wasn't rewritten. + /// if the was rewritten to , otherwise . + protected virtual bool RewriteParameterListOrEmptyParameterList(JassParameterListOrEmptyParameterListSyntax parameterListOrEmptyParameterList, out JassParameterListOrEmptyParameterListSyntax result) + { + return parameterListOrEmptyParameterList switch + { + JassEmptyParameterListSyntax emptyParameterList => RewriteEmptyParameterList(emptyParameterList, out result), + JassParameterListSyntax parameterList => RewriteParameterList(parameterList, out result), + }; + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/SyntaxRewriter/ParameterListRewriter.cs b/src/War3Net.CodeAnalysis.Jass/SyntaxRewriter/ParameterListRewriter.cs new file mode 100644 index 00000000..da872480 --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/SyntaxRewriter/ParameterListRewriter.cs @@ -0,0 +1,33 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using War3Net.CodeAnalysis.Jass.Syntax; + +namespace War3Net.CodeAnalysis.Jass +{ + public abstract partial class JassSyntaxRewriter + { + /// The to rewrite. + /// The rewritten , or the input if it wasn't rewritten. + /// if the was rewritten to , otherwise . + protected virtual bool RewriteParameterList(JassParameterListSyntax parameterList, out JassParameterListOrEmptyParameterListSyntax result) + { + if (RewriteToken(parameterList.TakesToken, out var takesToken) | + RewriteSeparatedParameterList(parameterList.ParameterList, out var separatedParameterList)) + { + result = new JassParameterListSyntax( + takesToken, + separatedParameterList); + + return true; + } + + result = parameterList; + return false; + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/SyntaxRewriter/ParameterRewriter.cs b/src/War3Net.CodeAnalysis.Jass/SyntaxRewriter/ParameterRewriter.cs new file mode 100644 index 00000000..271d24cf --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/SyntaxRewriter/ParameterRewriter.cs @@ -0,0 +1,33 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using War3Net.CodeAnalysis.Jass.Syntax; + +namespace War3Net.CodeAnalysis.Jass +{ + public abstract partial class JassSyntaxRewriter + { + /// The to rewrite. + /// The rewritten , or the input if it wasn't rewritten. + /// if the was rewritten to , otherwise . + protected virtual bool RewriteParameter(JassParameterSyntax parameter, out JassParameterSyntax result) + { + if (RewriteType(parameter.Type, out var type) | + RewriteIdentifierName(parameter.IdentifierName, out var identifierName)) + { + result = new JassParameterSyntax( + type, + identifierName); + + return true; + } + + result = parameter; + return false; + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/SyntaxRewriter/ParenthesizedExpressionRewriter.cs b/src/War3Net.CodeAnalysis.Jass/SyntaxRewriter/ParenthesizedExpressionRewriter.cs new file mode 100644 index 00000000..ab12508f --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/SyntaxRewriter/ParenthesizedExpressionRewriter.cs @@ -0,0 +1,35 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using War3Net.CodeAnalysis.Jass.Syntax; + +namespace War3Net.CodeAnalysis.Jass +{ + public abstract partial class JassSyntaxRewriter + { + /// The to rewrite. + /// The rewritten , or the input if it wasn't rewritten. + /// if the was rewritten to , otherwise . + protected virtual bool RewriteParenthesizedExpression(JassParenthesizedExpressionSyntax parenthesizedExpression, out JassExpressionSyntax result) + { + if (RewriteToken(parenthesizedExpression.OpenParenToken, out var openParenToken) | + RewriteExpression(parenthesizedExpression.Expression, out var expression) | + RewriteToken(parenthesizedExpression.CloseParenToken, out var closeParenToken)) + { + result = new JassParenthesizedExpressionSyntax( + openParenToken, + expression, + closeParenToken); + + return true; + } + + result = parenthesizedExpression; + return false; + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/SyntaxRewriter/PredefinedTypeRewriter.cs b/src/War3Net.CodeAnalysis.Jass/SyntaxRewriter/PredefinedTypeRewriter.cs new file mode 100644 index 00000000..37a8f463 --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/SyntaxRewriter/PredefinedTypeRewriter.cs @@ -0,0 +1,29 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using War3Net.CodeAnalysis.Jass.Syntax; + +namespace War3Net.CodeAnalysis.Jass +{ + public abstract partial class JassSyntaxRewriter + { + /// The to rewrite. + /// The rewritten , or the input if it wasn't rewritten. + /// if the was rewritten to , otherwise . + protected virtual bool RewritePredefinedType(JassPredefinedTypeSyntax predefinedType, out JassTypeSyntax result) + { + if (RewriteToken(predefinedType.Token, out var token)) + { + result = new JassPredefinedTypeSyntax(token); + return true; + } + + result = predefinedType; + return false; + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/SyntaxRewriter/ReturnClauseRewriter.cs b/src/War3Net.CodeAnalysis.Jass/SyntaxRewriter/ReturnClauseRewriter.cs new file mode 100644 index 00000000..e7075f72 --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/SyntaxRewriter/ReturnClauseRewriter.cs @@ -0,0 +1,33 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using War3Net.CodeAnalysis.Jass.Syntax; + +namespace War3Net.CodeAnalysis.Jass +{ + public abstract partial class JassSyntaxRewriter + { + /// The to rewrite. + /// The rewritten , or the input if it wasn't rewritten. + /// if the was rewritten to , otherwise . + protected virtual bool RewriteReturnClause(JassReturnClauseSyntax returnClause, out JassReturnClauseSyntax result) + { + if (RewriteToken(returnClause.ReturnsToken, out var returnsToken) | + RewriteType(returnClause.ReturnType, out var returnType)) + { + result = new JassReturnClauseSyntax( + returnsToken, + returnType); + + return true; + } + + result = returnClause; + return false; + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/SyntaxRewriter/ReturnStatementRewriter.cs b/src/War3Net.CodeAnalysis.Jass/SyntaxRewriter/ReturnStatementRewriter.cs new file mode 100644 index 00000000..26072877 --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/SyntaxRewriter/ReturnStatementRewriter.cs @@ -0,0 +1,33 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using War3Net.CodeAnalysis.Jass.Syntax; + +namespace War3Net.CodeAnalysis.Jass +{ + public abstract partial class JassSyntaxRewriter + { + /// The to rewrite. + /// The rewritten , or the input if it wasn't rewritten. + /// if the was rewritten to , otherwise . + protected virtual bool RewriteReturnStatement(JassReturnStatementSyntax returnStatement, out JassStatementSyntax result) + { + if (RewriteToken(returnStatement.ReturnToken, out var returnToken) | + RewriteExpression(returnStatement.Value, out var value)) + { + result = new JassReturnStatementSyntax( + returnToken, + value); + + return true; + } + + result = returnStatement; + return false; + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/SyntaxRewriter/SeparatedArgumentListRewriter.cs b/src/War3Net.CodeAnalysis.Jass/SyntaxRewriter/SeparatedArgumentListRewriter.cs new file mode 100644 index 00000000..6bbbddf3 --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/SyntaxRewriter/SeparatedArgumentListRewriter.cs @@ -0,0 +1,70 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using War3Net.CodeAnalysis.Jass.Syntax; + +namespace War3Net.CodeAnalysis.Jass +{ + public abstract partial class JassSyntaxRewriter + { + /// The to rewrite. + /// The rewritten , or the input if it wasn't rewritten. + /// if the was rewritten to , otherwise . + protected virtual bool RewriteSeparatedArgumentList(SeparatedSyntaxList argumentList, out SeparatedSyntaxList result) + { + if (argumentList.IsEmpty) + { + result = argumentList; + return false; + } + + if (RewriteExpression(argumentList.Items[0], out var item)) + { + var argumentListBuilder = SeparatedSyntaxList.CreateBuilder(item, argumentList.Items.Length); + for (var i = 1; i < argumentList.Items.Length; i++) + { + RewriteToken(argumentList.Separators[i - 1], out var separator); + RewriteExpression(argumentList.Items[i], out item); + + argumentListBuilder.Add(separator, item); + } + + result = argumentListBuilder.ToSeparatedSyntaxList(); + return true; + } + + for (var i = 1; i < argumentList.Items.Length; i++) + { + if (RewriteToken(argumentList.Separators[i - 1], out var separator) | + RewriteExpression(argumentList.Items[i], out item)) + { + var argumentListBuilder = SeparatedSyntaxList.CreateBuilder(argumentList.Items[0], argumentList.Items.Length); + for (var j = 1; j < i; j++) + { + argumentListBuilder.Add(argumentList.Separators[j - 1], argumentList.Items[j]); + } + + argumentListBuilder.Add(separator, item); + + while (++i < argumentList.Items.Length) + { + RewriteToken(argumentList.Separators[i - 1], out separator); + RewriteExpression(argumentList.Items[i], out item); + + argumentListBuilder.Add(separator, item); + } + + result = argumentListBuilder.ToSeparatedSyntaxList(); + return true; + } + } + + result = argumentList; + return false; + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/SyntaxRewriter/SeparatedParameterListRewriter.cs b/src/War3Net.CodeAnalysis.Jass/SyntaxRewriter/SeparatedParameterListRewriter.cs new file mode 100644 index 00000000..89b9e2cb --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/SyntaxRewriter/SeparatedParameterListRewriter.cs @@ -0,0 +1,70 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using War3Net.CodeAnalysis.Jass.Syntax; + +namespace War3Net.CodeAnalysis.Jass +{ + public abstract partial class JassSyntaxRewriter + { + /// The to rewrite. + /// The rewritten , or the input if it wasn't rewritten. + /// if the was rewritten to , otherwise . + protected virtual bool RewriteSeparatedParameterList(SeparatedSyntaxList parameterList, out SeparatedSyntaxList result) + { + if (parameterList.IsEmpty) + { + result = parameterList; + return false; + } + + if (RewriteParameter(parameterList.Items[0], out var item)) + { + var parameterListBuilder = SeparatedSyntaxList.CreateBuilder(item, parameterList.Items.Length); + for (var i = 1; i < parameterList.Items.Length; i++) + { + RewriteToken(parameterList.Separators[i - 1], out var separator); + RewriteParameter(parameterList.Items[i], out item); + + parameterListBuilder.Add(separator, item); + } + + result = parameterListBuilder.ToSeparatedSyntaxList(); + return true; + } + + for (var i = 1; i < parameterList.Items.Length; i++) + { + if (RewriteToken(parameterList.Separators[i - 1], out var separator) | + RewriteParameter(parameterList.Items[i], out item)) + { + var parameterListBuilder = SeparatedSyntaxList.CreateBuilder(parameterList.Items[0], parameterList.Items.Length); + for (var j = 1; j < i; j++) + { + parameterListBuilder.Add(parameterList.Separators[j - 1], parameterList.Items[j]); + } + + parameterListBuilder.Add(separator, item); + + while (++i < parameterList.Items.Length) + { + RewriteToken(parameterList.Separators[i - 1], out separator); + RewriteParameter(parameterList.Items[i], out item); + + parameterListBuilder.Add(separator, item); + } + + result = parameterListBuilder.ToSeparatedSyntaxList(); + return true; + } + } + + result = parameterList; + return false; + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/SyntaxRewriter/SetStatementRewriter.cs b/src/War3Net.CodeAnalysis.Jass/SyntaxRewriter/SetStatementRewriter.cs new file mode 100644 index 00000000..77c9aac9 --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/SyntaxRewriter/SetStatementRewriter.cs @@ -0,0 +1,37 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using War3Net.CodeAnalysis.Jass.Syntax; + +namespace War3Net.CodeAnalysis.Jass +{ + public abstract partial class JassSyntaxRewriter + { + /// The to rewrite. + /// The rewritten , or the input if it wasn't rewritten. + /// if the was rewritten to , otherwise . + protected virtual bool RewriteSetStatement(JassSetStatementSyntax setStatement, out JassStatementSyntax result) + { + if (RewriteToken(setStatement.SetToken, out var setToken) | + RewriteIdentifierName(setStatement.IdentifierName, out var identifierName) | + RewriteElementAccessClause(setStatement.ElementAccessClause, out var elementAccessClause) | + RewriteEqualsValueClause(setStatement.Value, out var value)) + { + result = new JassSetStatementSyntax( + setToken, + identifierName, + elementAccessClause, + value); + + return true; + } + + result = setStatement; + return false; + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/SyntaxRewriter/StatementListRewriter.cs b/src/War3Net.CodeAnalysis.Jass/SyntaxRewriter/StatementListRewriter.cs new file mode 100644 index 00000000..04af3aba --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/SyntaxRewriter/StatementListRewriter.cs @@ -0,0 +1,50 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using System.Collections.Immutable; + +using War3Net.CodeAnalysis.Jass.Syntax; + +namespace War3Net.CodeAnalysis.Jass +{ + public abstract partial class JassSyntaxRewriter + { + /// The to rewrite. + /// The rewritten , or the input if it wasn't rewritten. + /// if the was rewritten to , otherwise . + protected virtual bool RewriteStatementList(ImmutableArray statementList, out ImmutableArray result) + { + if (statementList.IsEmpty) + { + result = statementList; + return false; + } + + for (var i = 0; i < statementList.Length; i++) + { + if (RewriteStatement(statementList[i], out var statement)) + { + var statementListBuilder = ImmutableArray.CreateBuilder(statementList.Length); + statementListBuilder.AddRange(statementList, i); + statementListBuilder.Add(statement); + + while (++i < statementList.Length) + { + RewriteStatement(statementList[i], out statement); + statementListBuilder.Add(statement); + } + + result = statementListBuilder.ToImmutable(); + return true; + } + } + + result = statementList; + return false; + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/SyntaxRewriter/StatementRewriter.cs b/src/War3Net.CodeAnalysis.Jass/SyntaxRewriter/StatementRewriter.cs new file mode 100644 index 00000000..062acefc --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/SyntaxRewriter/StatementRewriter.cs @@ -0,0 +1,32 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using War3Net.CodeAnalysis.Jass.Syntax; + +namespace War3Net.CodeAnalysis.Jass +{ + public abstract partial class JassSyntaxRewriter + { + /// The to rewrite. + /// The rewritten , or the input if it wasn't rewritten. + /// if the was rewritten to , otherwise . + protected virtual bool RewriteStatement(JassStatementSyntax statement, out JassStatementSyntax result) + { + return statement switch + { + JassCallStatementSyntax callStatement => RewriteCallStatement(callStatement, out result), + JassDebugStatementSyntax debugStatement => RewriteDebugStatement(debugStatement, out result), + JassExitStatementSyntax exitStatement => RewriteExitStatement(exitStatement, out result), + JassIfStatementSyntax ifStatement => RewriteIfStatement(ifStatement, out result), + JassLocalVariableDeclarationStatementSyntax localVariableDeclarationStatement => RewriteLocalVariableDeclarationStatement(localVariableDeclarationStatement, out result), + JassLoopStatementSyntax loopStatement => RewriteLoopStatement(loopStatement, out result), + JassReturnStatementSyntax returnStatement => RewriteReturnStatement(returnStatement, out result), + JassSetStatementSyntax setStatement => RewriteSetStatement(setStatement, out result), + }; + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/SyntaxRewriter/SyntaxTokenRewriter.cs b/src/War3Net.CodeAnalysis.Jass/SyntaxRewriter/SyntaxTokenRewriter.cs new file mode 100644 index 00000000..1b23e03b --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/SyntaxRewriter/SyntaxTokenRewriter.cs @@ -0,0 +1,43 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using System.Diagnostics.CodeAnalysis; + +using War3Net.CodeAnalysis.Jass.Syntax; + +namespace War3Net.CodeAnalysis.Jass +{ + public abstract partial class JassSyntaxRewriter + { + /// The to rewrite. + /// The rewritten , or the input if it wasn't rewritten. + /// if the was rewritten to , otherwise . + protected virtual bool RewriteToken(JassSyntaxToken? token, [NotNullIfNotNull("token")] out JassSyntaxToken? result) + { + if (token is null) + { + result = null; + return false; + } + + if (RewriteLeadingTrivia(token.LeadingTrivia, out var leadingTrivia) | + RewriteTrailingTrivia(token.TrailingTrivia, out var trailingTrivia)) + { + result = new JassSyntaxToken( + leadingTrivia, + token.SyntaxKind, + token.Text, + trailingTrivia); + + return true; + } + + result = token; + return false; + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/SyntaxRewriter/SyntaxTriviaListRewriter.cs b/src/War3Net.CodeAnalysis.Jass/SyntaxRewriter/SyntaxTriviaListRewriter.cs new file mode 100644 index 00000000..bb7773d8 --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/SyntaxRewriter/SyntaxTriviaListRewriter.cs @@ -0,0 +1,32 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using War3Net.CodeAnalysis.Jass.Syntax; + +namespace War3Net.CodeAnalysis.Jass +{ + public abstract partial class JassSyntaxRewriter + { + /// The to rewrite. + /// The rewritten , or the input if it wasn't rewritten. + /// if the was rewritten to , otherwise . + protected virtual bool RewriteLeadingTrivia(JassSyntaxTriviaList triviaList, out JassSyntaxTriviaList result) + { + result = triviaList; + return false; + } + + /// The to rewrite. + /// The rewritten , or the input if it wasn't rewritten. + /// if the was rewritten to , otherwise . + protected virtual bool RewriteTrailingTrivia(JassSyntaxTriviaList triviaList, out JassSyntaxTriviaList result) + { + result = triviaList; + return false; + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/SyntaxRewriter/TopLevelDeclarationRewriter.cs b/src/War3Net.CodeAnalysis.Jass/SyntaxRewriter/TopLevelDeclarationRewriter.cs new file mode 100644 index 00000000..1693baa9 --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/SyntaxRewriter/TopLevelDeclarationRewriter.cs @@ -0,0 +1,28 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using War3Net.CodeAnalysis.Jass.Syntax; + +namespace War3Net.CodeAnalysis.Jass +{ + public abstract partial class JassSyntaxRewriter + { + /// The to rewrite. + /// The rewritten , or the input if it wasn't rewritten. + /// if the was rewritten to , otherwise . + protected virtual bool RewriteTopLevelDeclaration(JassTopLevelDeclarationSyntax topLevelDeclaration, out JassTopLevelDeclarationSyntax result) + { + return topLevelDeclaration switch + { + JassFunctionDeclarationSyntax functionDeclaration => RewriteFunctionDeclaration(functionDeclaration, out result), + JassGlobalsDeclarationSyntax globalsDeclaration => RewriteGlobalsDeclaration(globalsDeclaration, out result), + JassNativeFunctionDeclarationSyntax nativeFunctionDeclaration => RewriteNativeFunctionDeclaration(nativeFunctionDeclaration, out result), + JassTypeDeclarationSyntax typeDeclaration => RewriteTypeDeclaration(typeDeclaration, out result), + }; + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/SyntaxRewriter/TypeDeclarationRewriter.cs b/src/War3Net.CodeAnalysis.Jass/SyntaxRewriter/TypeDeclarationRewriter.cs new file mode 100644 index 00000000..ff438385 --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/SyntaxRewriter/TypeDeclarationRewriter.cs @@ -0,0 +1,37 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using War3Net.CodeAnalysis.Jass.Syntax; + +namespace War3Net.CodeAnalysis.Jass +{ + public abstract partial class JassSyntaxRewriter + { + /// The to rewrite. + /// The rewritten , or the input if it wasn't rewritten. + /// if the was rewritten to , otherwise . + protected virtual bool RewriteTypeDeclaration(JassTypeDeclarationSyntax typeDeclaration, out JassTopLevelDeclarationSyntax result) + { + if (RewriteToken(typeDeclaration.TypeToken, out var typeToken) | + RewriteIdentifierName(typeDeclaration.IdentifierName, out var identifierName) | + RewriteToken(typeDeclaration.ExtendsToken, out var extendsToken) | + RewriteType(typeDeclaration.BaseType, out var baseType)) + { + result = new JassTypeDeclarationSyntax( + typeToken, + identifierName, + extendsToken, + baseType); + + return true; + } + + result = typeDeclaration; + return false; + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/SyntaxRewriter/TypeRewriter.cs b/src/War3Net.CodeAnalysis.Jass/SyntaxRewriter/TypeRewriter.cs new file mode 100644 index 00000000..35f990f3 --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/SyntaxRewriter/TypeRewriter.cs @@ -0,0 +1,26 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using War3Net.CodeAnalysis.Jass.Syntax; + +namespace War3Net.CodeAnalysis.Jass +{ + public abstract partial class JassSyntaxRewriter + { + /// The to rewrite. + /// The rewritten , or the input if it wasn't rewritten. + /// if the was rewritten to , otherwise . + protected virtual bool RewriteType(JassTypeSyntax type, out JassTypeSyntax result) + { + return type switch + { + JassIdentifierNameSyntax identifierName => RewriteIdentifierNameAsType(identifierName, out result), + JassPredefinedTypeSyntax predefinedType => RewritePredefinedType(predefinedType, out result), + }; + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/SyntaxRewriter/UnaryExpressionRewriter.cs b/src/War3Net.CodeAnalysis.Jass/SyntaxRewriter/UnaryExpressionRewriter.cs new file mode 100644 index 00000000..1efac5c6 --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/SyntaxRewriter/UnaryExpressionRewriter.cs @@ -0,0 +1,33 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using War3Net.CodeAnalysis.Jass.Syntax; + +namespace War3Net.CodeAnalysis.Jass +{ + public abstract partial class JassSyntaxRewriter + { + /// The to rewrite. + /// The rewritten , or the input if it wasn't rewritten. + /// if the was rewritten to , otherwise . + protected virtual bool RewriteUnaryExpression(JassUnaryExpressionSyntax unaryExpression, out JassExpressionSyntax result) + { + if (RewriteToken(unaryExpression.OperatorToken, out var operatorToken) | + RewriteExpression(unaryExpression.Expression, out var expression)) + { + result = new JassUnaryExpressionSyntax( + operatorToken, + expression); + + return true; + } + + result = unaryExpression; + return false; + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/SyntaxRewriter/VariableDeclaratorRewriter.cs b/src/War3Net.CodeAnalysis.Jass/SyntaxRewriter/VariableDeclaratorRewriter.cs new file mode 100644 index 00000000..58951625 --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/SyntaxRewriter/VariableDeclaratorRewriter.cs @@ -0,0 +1,35 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using War3Net.CodeAnalysis.Jass.Syntax; + +namespace War3Net.CodeAnalysis.Jass +{ + public abstract partial class JassSyntaxRewriter + { + /// The to rewrite. + /// The rewritten , or the input if it wasn't rewritten. + /// if the was rewritten to , otherwise . + protected virtual bool RewriteVariableDeclarator(JassVariableDeclaratorSyntax variableDeclarator, out JassVariableOrArrayDeclaratorSyntax result) + { + if (RewriteType(variableDeclarator.Type, out var type) | + RewriteIdentifierName(variableDeclarator.IdentifierName, out var identifierName) | + RewriteEqualsValueClause(variableDeclarator.Value, out var value)) + { + result = new JassVariableDeclaratorSyntax( + type, + identifierName, + value); + + return true; + } + + result = variableDeclarator; + return false; + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Jass/SyntaxRewriter/VariableOrArrayDeclaratorRewriter.cs b/src/War3Net.CodeAnalysis.Jass/SyntaxRewriter/VariableOrArrayDeclaratorRewriter.cs new file mode 100644 index 00000000..b50dda24 --- /dev/null +++ b/src/War3Net.CodeAnalysis.Jass/SyntaxRewriter/VariableOrArrayDeclaratorRewriter.cs @@ -0,0 +1,26 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using War3Net.CodeAnalysis.Jass.Syntax; + +namespace War3Net.CodeAnalysis.Jass +{ + public abstract partial class JassSyntaxRewriter + { + /// The to rewrite. + /// The rewritten , or the input if it wasn't rewritten. + /// if the was rewritten to , otherwise . + protected virtual bool RewriteVariableOrArrayDeclarator(JassVariableOrArrayDeclaratorSyntax variableOrArrayDeclarator, out JassVariableOrArrayDeclaratorSyntax result) + { + return variableOrArrayDeclarator switch + { + JassArrayDeclaratorSyntax arrayDeclarator => RewriteArrayDeclarator(arrayDeclarator, out result), + JassVariableDeclaratorSyntax variableDeclarator => RewriteVariableDeclarator(variableDeclarator, out result), + }; + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Transpilers/Extensions/SeparatedSyntaxListExtensions.cs b/src/War3Net.CodeAnalysis.Transpilers/Extensions/SeparatedSyntaxListExtensions.cs new file mode 100644 index 00000000..474a56b3 --- /dev/null +++ b/src/War3Net.CodeAnalysis.Transpilers/Extensions/SeparatedSyntaxListExtensions.cs @@ -0,0 +1,35 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; + +namespace War3Net.CodeAnalysis.Transpilers.Extensions +{ + public static class SeparatedSyntaxListExtensions + { + public static SeparatedSyntaxList WithoutTrivia(this SeparatedSyntaxList nodesAndTokens) + where TNode : SyntaxNode + { + if (nodesAndTokens.Count == 0) + { + return nodesAndTokens; + } + + if (nodesAndTokens.Count == 1) + { + return SyntaxFactory.SingletonSeparatedList(nodesAndTokens[0].WithoutTrivia()); + } + + var firstNode = nodesAndTokens[0]; + nodesAndTokens = nodesAndTokens.Replace(firstNode, firstNode.WithoutLeadingTrivia()); + var lastNode = nodesAndTokens[^1]; + nodesAndTokens = nodesAndTokens.Replace(lastNode, lastNode.WithoutTrailingTrivia()); + return nodesAndTokens; + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Transpilers/Extensions/SyntaxNodeExtensions.cs b/src/War3Net.CodeAnalysis.Transpilers/Extensions/SyntaxNodeExtensions.cs index ea990a5a..7a436a39 100644 --- a/src/War3Net.CodeAnalysis.Transpilers/Extensions/SyntaxNodeExtensions.cs +++ b/src/War3Net.CodeAnalysis.Transpilers/Extensions/SyntaxNodeExtensions.cs @@ -5,6 +5,9 @@ // // ------------------------------------------------------------------------------ +using System.Collections.Generic; +using System.Linq; + using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; @@ -13,27 +16,72 @@ namespace War3Net.CodeAnalysis.Transpilers.Extensions { public static class SyntaxNodeExtensions { + /// The amount of spaces that must be added (or removed) to keep the syntax aligned. + public static TSyntax WithAlignedWhitespace(this TSyntax node, int whitespaceDiff) + where TSyntax : SyntaxNode + { + if (whitespaceDiff == 0 || !node.HasTrailingTrivia) + { + return node; + } + + var trailingWhitespaceTrivia = node.GetTrailingTrivia()[0]; + if (!trailingWhitespaceTrivia.IsKind(SyntaxKind.WhitespaceTrivia)) + { + return node; + } + + if (trailingWhitespaceTrivia.Span.Length == 1 || + trailingWhitespaceTrivia.ToFullString().Any(c => c != ' ')) + { + return node; + } + + var newLength = trailingWhitespaceTrivia.Span.Length + whitespaceDiff; + if (newLength <= 0) + { + newLength = 1; + } + + return node.ReplaceTrivia(trailingWhitespaceTrivia, SyntaxFactory.Whitespace(new string(' ', newLength))); + } + public static TSyntax WithCSharpLuaTemplateAttribute(this TSyntax node, string template) where TSyntax : SyntaxNode { - return node.WithLeadingTrivia( - SyntaxFactory.Trivia( - SyntaxFactory.DocumentationCommentTrivia( - SyntaxKind.SingleLineDocumentationCommentTrivia, - SyntaxFactory.List(new XmlNodeSyntax[] - { - SyntaxFactory.XmlText( - SyntaxFactory.XmlTextLiteral( - SyntaxFactory.TriviaList( - SyntaxFactory.DocumentationCommentExterior("///")), - " ", - " ", - default)), - SyntaxFactory.XmlText( - $"@CSharpLua.Template = \"{template}\""), - SyntaxFactory.XmlText( - SyntaxFactory.XmlTextNewLine("\r\n", false)), - })))); + var newTrivia = SyntaxFactory.Trivia( + SyntaxFactory.DocumentationCommentTrivia( + SyntaxKind.SingleLineDocumentationCommentTrivia, + SyntaxFactory.List(new XmlNodeSyntax[] + { + SyntaxFactory.XmlText( + SyntaxFactory.XmlTextLiteral( + SyntaxFactory.TriviaList( + SyntaxFactory.DocumentationCommentExterior("///")), + " ", + " ", + default)), + SyntaxFactory.XmlText( + $"@CSharpLua.Template = \"{template}\""), + SyntaxFactory.XmlText( + SyntaxFactory.XmlTextNewLine("\r\n", false)), + }))); + + if (node.HasLeadingTrivia) + { + var newTriviaList = new List(2); + var leadingTrivia = node.GetLeadingTrivia(); + var lastLeadingTrivia = leadingTrivia[^1]; + if (lastLeadingTrivia.IsKind(SyntaxKind.WhitespaceTrivia)) + { + newTriviaList.Add(newTrivia); + newTriviaList.Add(lastLeadingTrivia); + } + + return node.InsertTriviaAfter(lastLeadingTrivia, newTriviaList); + } + + return node.WithLeadingTrivia(newTrivia); } } } \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Transpilers/Extensions/SyntaxTokenExtensions.cs b/src/War3Net.CodeAnalysis.Transpilers/Extensions/SyntaxTokenExtensions.cs new file mode 100644 index 00000000..77687def --- /dev/null +++ b/src/War3Net.CodeAnalysis.Transpilers/Extensions/SyntaxTokenExtensions.cs @@ -0,0 +1,30 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; + +namespace War3Net.CodeAnalysis.Transpilers.Extensions +{ + public static class SyntaxTokenExtensions + { + public static SyntaxToken WithSpace(this SyntaxToken token) + { + return token.WithTrailingTrivia(SyntaxTriviaList.Create(SyntaxFactory.ElasticSpace)); + } + + public static SyntaxToken WithoutLeadingTrivia(this SyntaxToken token) + { + return token.WithLeadingTrivia(SyntaxTriviaList.Empty); + } + + public static SyntaxToken WithoutTrailingTrivia(this SyntaxToken token) + { + return token.WithTrailingTrivia(SyntaxTriviaList.Empty); + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/ArgumentListTranspiler.cs b/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/ArgumentListTranspiler.cs index 6ca47f3e..5b1823d0 100644 --- a/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/ArgumentListTranspiler.cs +++ b/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/ArgumentListTranspiler.cs @@ -18,7 +18,12 @@ public partial class JassToCSharpTranspiler { public ArgumentListSyntax Transpile(JassArgumentListSyntax argumentList) { - return SyntaxFactory.ArgumentList(SyntaxFactory.SeparatedList(argumentList.Arguments.Select(argument => SyntaxFactory.Argument(Transpile(argument))))); + return SyntaxFactory.ArgumentList( + Transpile(SyntaxKind.OpenParenToken, argumentList.OpenParenToken), + SyntaxFactory.SeparatedList( + argumentList.ArgumentList.Items.Select(TranspileArgument), + argumentList.ArgumentList.Separators.Select(Transpile)), + Transpile(SyntaxKind.CloseParenToken, argumentList.CloseParenToken)); } } } \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/ArrayDeclaratorTranspiler.cs b/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/ArrayDeclaratorTranspiler.cs index b0b9dfbb..b90a3443 100644 --- a/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/ArrayDeclaratorTranspiler.cs +++ b/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/ArrayDeclaratorTranspiler.cs @@ -5,24 +5,107 @@ // // ------------------------------------------------------------------------------ +using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; +using War3Net.CodeAnalysis.Jass; +using War3Net.CodeAnalysis.Jass.Extensions; using War3Net.CodeAnalysis.Jass.Syntax; +using War3Net.CodeAnalysis.Transpilers.Extensions; namespace War3Net.CodeAnalysis.Transpilers { public partial class JassToCSharpTranspiler { - private VariableDeclarationSyntax Transpile(JassArrayDeclaratorSyntax arrayDeclarator) + private VariableDeclarationSyntax Transpile( + JassArrayDeclaratorSyntax arrayDeclarator, + bool isGlobalDeclaration) { - var type = Transpile(arrayDeclarator.Type); + return Transpile( + arrayDeclarator.GetLeadingTrivia(), + arrayDeclarator, + arrayDeclarator.GetTrailingTrivia(), + isGlobalDeclaration); + } + + private VariableDeclarationSyntax Transpile( + JassSyntaxTriviaList leadingTrivia, + JassArrayDeclaratorSyntax arrayDeclarator, + bool isGlobalDeclaration) + { + return Transpile( + leadingTrivia, + arrayDeclarator, + arrayDeclarator.GetTrailingTrivia(), + isGlobalDeclaration); + } + + private VariableDeclarationSyntax Transpile( + JassArrayDeclaratorSyntax arrayDeclarator, + JassSyntaxTriviaList trailingTrivia, + bool isGlobalDeclaration) + { + return Transpile( + arrayDeclarator.GetLeadingTrivia(), + arrayDeclarator, + trailingTrivia, + isGlobalDeclaration); + } + + private VariableDeclarationSyntax Transpile( + JassSyntaxTriviaList leadingTrivia, + JassArrayDeclaratorSyntax arrayDeclarator, + JassSyntaxTriviaList trailingTrivia, + bool isGlobalDeclaration) + { + var typeNode = isGlobalDeclaration + ? TranspileAligned( + leadingTrivia, + arrayDeclarator.Type, + JassSyntaxFactory.MergeTriviaLists( + arrayDeclarator.Type.GetTrailingTrivia(), + arrayDeclarator.ArrayToken.LeadingTrivia, + arrayDeclarator.ArrayToken.TrailingTrivia), + isArray: true) + : Transpile( + leadingTrivia, + arrayDeclarator.Type, + MergeTrivia( + arrayDeclarator.Type.GetTrailingTrivia(), + arrayDeclarator.ArrayToken)); + + var arrayTrivia = typeNode.GetTrailingTrivia(); + + var arrayType = SyntaxFactory.ArrayType( + typeNode.WithoutTrailingTrivia(), + SyntaxFactory.SingletonList( + SyntaxFactory.ArrayRankSpecifier( + SyntaxFactory.Token(SyntaxKind.OpenBracketToken), + SyntaxFactory.SeparatedList(), + SyntaxFactory.Token( + SyntaxTriviaList.Empty, + SyntaxKind.CloseBracketToken, + arrayTrivia)))); + return SyntaxFactory.VariableDeclaration( - SyntaxFactory.ArrayType(type), - SyntaxFactory.SingletonSeparatedList(SyntaxFactory.VariableDeclarator( - Transpile(arrayDeclarator.IdentifierName), - null, - SyntaxFactory.EqualsValueClause(SyntaxFactory.ArrayCreationExpression(SyntaxFactory.ArrayType(type)))))); + arrayType, + SyntaxFactory.SingletonSeparatedList( + SyntaxFactory.VariableDeclarator( + Transpile(arrayDeclarator.IdentifierName.Token, JassSyntaxTriviaList.SingleSpace), + null, + SyntaxFactory.EqualsValueClause( + TokenWithSpace(SyntaxKind.EqualsToken), + SyntaxFactory.ArrayCreationExpression( + TokenWithSpace(SyntaxKind.NewKeyword), + SyntaxFactory.ArrayType( + typeNode.WithoutTrivia(), + SyntaxFactory.SingletonList( + SyntaxFactory.ArrayRankSpecifier( + SyntaxFactory.SingletonSeparatedList( + SyntaxFactory.IdentifierName("JASS_MAX_ARRAY_SIZE"))))), + null))))) + .WithTrailingTrivia(Transpile(trailingTrivia)); } } } \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/ArrayReferenceExpressionTranspiler.cs b/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/ArrayReferenceExpressionTranspiler.cs deleted file mode 100644 index 5792ddfb..00000000 --- a/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/ArrayReferenceExpressionTranspiler.cs +++ /dev/null @@ -1,24 +0,0 @@ -// ------------------------------------------------------------------------------ -// -// Licensed under the MIT license. -// See the LICENSE file in the project root for more information. -// -// ------------------------------------------------------------------------------ - -using Microsoft.CodeAnalysis.CSharp; -using Microsoft.CodeAnalysis.CSharp.Syntax; - -using War3Net.CodeAnalysis.Jass.Syntax; - -namespace War3Net.CodeAnalysis.Transpilers -{ - public partial class JassToCSharpTranspiler - { - public ExpressionSyntax Transpile(JassArrayReferenceExpressionSyntax arrayReferenceExpression) - { - return SyntaxFactory.ElementAccessExpression( - SyntaxFactory.IdentifierName(Transpile(arrayReferenceExpression.IdentifierName)), - SyntaxFactory.BracketedArgumentList(SyntaxFactory.SingletonSeparatedList(SyntaxFactory.Argument(Transpile(arrayReferenceExpression.Indexer))))); - } - } -} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/BinaryExpressionTranspiler.cs b/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/BinaryExpressionTranspiler.cs index 11b9b1f6..0e4d6790 100644 --- a/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/BinaryExpressionTranspiler.cs +++ b/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/BinaryExpressionTranspiler.cs @@ -17,8 +17,11 @@ public partial class JassToCSharpTranspiler public ExpressionSyntax Transpile(JassBinaryExpressionSyntax binaryExpression) { return SyntaxFactory.BinaryExpression( - Transpile(binaryExpression.Operator), + TranspileBinaryExpressionKind(binaryExpression.SyntaxKind), Transpile(binaryExpression.Left), + Transpile( + TranspileBinaryOperatorKind(binaryExpression.OperatorToken.SyntaxKind), + binaryExpression.OperatorToken), Transpile(binaryExpression.Right)); } } diff --git a/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/BinaryOperatorTranspiler.cs b/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/BinaryOperatorTranspiler.cs deleted file mode 100644 index 1af69edb..00000000 --- a/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/BinaryOperatorTranspiler.cs +++ /dev/null @@ -1,35 +0,0 @@ -// ------------------------------------------------------------------------------ -// -// Licensed under the MIT license. -// See the LICENSE file in the project root for more information. -// -// ------------------------------------------------------------------------------ - -using Microsoft.CodeAnalysis.CSharp; - -using War3Net.CodeAnalysis.Jass.Syntax; - -namespace War3Net.CodeAnalysis.Transpilers -{ - public partial class JassToCSharpTranspiler - { - public SyntaxKind Transpile(BinaryOperatorType binaryOperator) - { - return binaryOperator switch - { - BinaryOperatorType.Add => SyntaxKind.AddExpression, - BinaryOperatorType.Subtract => SyntaxKind.SubtractExpression, - BinaryOperatorType.Multiplication => SyntaxKind.MultiplyExpression, - BinaryOperatorType.Division => SyntaxKind.DivideExpression, - BinaryOperatorType.GreaterThan => SyntaxKind.GreaterThanExpression, - BinaryOperatorType.LessThan => SyntaxKind.LessThanExpression, - BinaryOperatorType.Equals => SyntaxKind.EqualsExpression, - BinaryOperatorType.NotEquals => SyntaxKind.NotEqualsExpression, - BinaryOperatorType.GreaterOrEqual => SyntaxKind.GreaterThanOrEqualExpression, - BinaryOperatorType.LessOrEqual => SyntaxKind.LessThanOrEqualExpression, - BinaryOperatorType.And => SyntaxKind.LogicalAndExpression, - BinaryOperatorType.Or => SyntaxKind.LogicalOrExpression, - }; - } - } -} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/CallStatementTranspiler.cs b/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/CallStatementTranspiler.cs index 9f4f6c5c..eea36b1b 100644 --- a/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/CallStatementTranspiler.cs +++ b/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/CallStatementTranspiler.cs @@ -5,6 +5,7 @@ // // ------------------------------------------------------------------------------ +using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; @@ -16,9 +17,22 @@ public partial class JassToCSharpTranspiler { public StatementSyntax Transpile(JassCallStatementSyntax callStatement) { - return SyntaxFactory.ExpressionStatement(SyntaxFactory.InvocationExpression( - SyntaxFactory.IdentifierName(Transpile(callStatement.IdentifierName)), - Transpile(callStatement.Arguments))); + var leadingTrivia = MergeTrivia( + callStatement.CallToken, + callStatement.IdentifierName.Token.LeadingTrivia); + + var invocationExpression = SyntaxFactory.InvocationExpression( + Transpile(leadingTrivia, callStatement.IdentifierName), + Transpile(callStatement.ArgumentList)); + + var trailingTrivia = invocationExpression.GetTrailingTrivia(); + + return SyntaxFactory.ExpressionStatement( + invocationExpression.WithoutTrailingTrivia(), + SyntaxFactory.Token( + SyntaxTriviaList.Empty, + SyntaxKind.SemicolonToken, + trailingTrivia)); } } } \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/CharacterLiteralExpressionTranspiler.cs b/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/CharacterLiteralExpressionTranspiler.cs deleted file mode 100644 index 46b391fc..00000000 --- a/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/CharacterLiteralExpressionTranspiler.cs +++ /dev/null @@ -1,22 +0,0 @@ -// ------------------------------------------------------------------------------ -// -// Licensed under the MIT license. -// See the LICENSE file in the project root for more information. -// -// ------------------------------------------------------------------------------ - -using Microsoft.CodeAnalysis.CSharp; -using Microsoft.CodeAnalysis.CSharp.Syntax; - -using War3Net.CodeAnalysis.Jass.Syntax; - -namespace War3Net.CodeAnalysis.Transpilers -{ - public partial class JassToCSharpTranspiler - { - public ExpressionSyntax Transpile(JassCharacterLiteralExpressionSyntax characterLiteralExpression) - { - return SyntaxFactory.ParseExpression(characterLiteralExpression.ToString()); - } - } -} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/CommentTranspiler.cs b/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/CommentTranspiler.cs deleted file mode 100644 index 05b60db4..00000000 --- a/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/CommentTranspiler.cs +++ /dev/null @@ -1,22 +0,0 @@ -// ------------------------------------------------------------------------------ -// -// Licensed under the MIT license. -// See the LICENSE file in the project root for more information. -// -// ------------------------------------------------------------------------------ - -using Microsoft.CodeAnalysis.CSharp; -using Microsoft.CodeAnalysis.CSharp.Syntax; - -using War3Net.CodeAnalysis.Jass.Syntax; - -namespace War3Net.CodeAnalysis.Transpilers -{ - public partial class JassToCSharpTranspiler - { - public StatementSyntax Transpile(JassCommentSyntax comment) - { - return SyntaxFactory.ParseStatement(comment.ToString()); - } - } -} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/DebugStatementTranspiler.cs b/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/DebugStatementTranspiler.cs index 3efccfc3..624e9777 100644 --- a/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/DebugStatementTranspiler.cs +++ b/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/DebugStatementTranspiler.cs @@ -5,8 +5,11 @@ // // ------------------------------------------------------------------------------ +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; +using War3Net.CodeAnalysis.Jass.Extensions; using War3Net.CodeAnalysis.Jass.Syntax; namespace War3Net.CodeAnalysis.Transpilers @@ -15,7 +18,40 @@ public partial class JassToCSharpTranspiler { public StatementSyntax Transpile(JassDebugStatementSyntax debugStatement) { - return Transpile(debugStatement.Statement); + var leadingTrivia = Transpile(debugStatement.DebugToken.LeadingTrivia); + var statement = Transpile(debugStatement.Statement); + + var ifDebugDirective = SyntaxFactory.Trivia( + SyntaxFactory.IfDirectiveTrivia( + SyntaxFactory.Token(SyntaxKind.HashToken), + Transpile(SyntaxKind.IfKeyword, debugStatement.DebugToken.TrailingTrivia), + SyntaxFactory.IdentifierName("DEBUG"), + SyntaxFactory.Token(SyntaxKind.EndOfDirectiveToken), + isActive: true, + branchTaken: true, + conditionValue: true)); + + if (leadingTrivia.Count == 0 || leadingTrivia[^1].IsKind(SyntaxKind.EndOfLineTrivia)) + { + leadingTrivia = leadingTrivia.Add(ifDebugDirective); + leadingTrivia = leadingTrivia.Add(SyntaxFactory.CarriageReturnLineFeed); + } + else + { + leadingTrivia = leadingTrivia.Insert(leadingTrivia.Count - 1, ifDebugDirective); + leadingTrivia = leadingTrivia.Insert(leadingTrivia.Count - 1, SyntaxFactory.CarriageReturnLineFeed); + } + + leadingTrivia = leadingTrivia.AddRange(statement.GetLeadingTrivia()); + + var trailingTrivia = statement + .GetTrailingTrivia() + .Add(SyntaxFactory.Trivia(SyntaxFactory.EndIfDirectiveTrivia(isActive: true))) + .Add(SyntaxFactory.CarriageReturnLineFeed); + + return statement + .WithLeadingTrivia(leadingTrivia) + .WithTrailingTrivia(trailingTrivia); } } } \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/DecimalLiteralExpressionTranspiler.cs b/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/DecimalLiteralExpressionTranspiler.cs deleted file mode 100644 index 5bd6f5d2..00000000 --- a/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/DecimalLiteralExpressionTranspiler.cs +++ /dev/null @@ -1,22 +0,0 @@ -// ------------------------------------------------------------------------------ -// -// Licensed under the MIT license. -// See the LICENSE file in the project root for more information. -// -// ------------------------------------------------------------------------------ - -using Microsoft.CodeAnalysis.CSharp; -using Microsoft.CodeAnalysis.CSharp.Syntax; - -using War3Net.CodeAnalysis.Jass.Syntax; - -namespace War3Net.CodeAnalysis.Transpilers -{ - public partial class JassToCSharpTranspiler - { - public ExpressionSyntax Transpile(JassDecimalLiteralExpressionSyntax decimalLiteralExpression) - { - return SyntaxFactory.ParseExpression(decimalLiteralExpression.Value.ToString()); - } - } -} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/DeclarationTranspiler.cs b/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/DeclarationTranspiler.cs index 87a43b60..d624bfbf 100644 --- a/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/DeclarationTranspiler.cs +++ b/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/DeclarationTranspiler.cs @@ -16,13 +16,12 @@ namespace War3Net.CodeAnalysis.Transpilers { public partial class JassToCSharpTranspiler { - public IEnumerable Transpile(ITopLevelDeclarationSyntax declaration) + public IEnumerable Transpile(JassTopLevelDeclarationSyntax declaration) { return declaration switch { JassTypeDeclarationSyntax typeDeclaration => new[] { Transpile(typeDeclaration) }, - JassGlobalDeclarationListSyntax globalDeclarationList => Transpile(globalDeclarationList), - JassGlobalDeclarationSyntax globalDeclaration => new[] { Transpile(globalDeclaration) }, + JassGlobalsDeclarationSyntax globalsDeclaration => Transpile(globalsDeclaration), JassNativeFunctionDeclarationSyntax nativeFunctionDeclaration => new[] { Transpile(nativeFunctionDeclaration) }, JassFunctionDeclarationSyntax functionDeclaration => new[] { Transpile(functionDeclaration) }, _ => Array.Empty(), diff --git a/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/BooleanLiteralExpressionTranspiler.cs b/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/ElementAccessClauseTranspiler.cs similarity index 50% rename from src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/BooleanLiteralExpressionTranspiler.cs rename to src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/ElementAccessClauseTranspiler.cs index 59fa1a48..4e6479fb 100644 --- a/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/BooleanLiteralExpressionTranspiler.cs +++ b/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/ElementAccessClauseTranspiler.cs @@ -1,5 +1,5 @@ // ------------------------------------------------------------------------------ -// +// // Licensed under the MIT license. // See the LICENSE file in the project root for more information. // @@ -14,9 +14,12 @@ namespace War3Net.CodeAnalysis.Transpilers { public partial class JassToCSharpTranspiler { - public ExpressionSyntax Transpile(JassBooleanLiteralExpressionSyntax booleanLiteralExpression) + public BracketedArgumentListSyntax Transpile(JassElementAccessClauseSyntax elementAccessClause) { - return SyntaxFactory.LiteralExpression(booleanLiteralExpression.Value ? SyntaxKind.TrueLiteralExpression : SyntaxKind.FalseLiteralExpression); + return SyntaxFactory.BracketedArgumentList( + Transpile(SyntaxKind.OpenBracketToken, elementAccessClause.OpenBracketToken), + SyntaxFactory.SingletonSeparatedList(TranspileArgument(elementAccessClause.Expression)), + Transpile(SyntaxKind.CloseBracketToken, elementAccessClause.CloseBracketToken)); } } } \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/FourCCLiteralExpressionTranspiler.cs b/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/ElementAccessExpressionTranspiler.cs similarity index 60% rename from src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/FourCCLiteralExpressionTranspiler.cs rename to src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/ElementAccessExpressionTranspiler.cs index 4f642b20..1e615526 100644 --- a/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/FourCCLiteralExpressionTranspiler.cs +++ b/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/ElementAccessExpressionTranspiler.cs @@ -1,5 +1,5 @@ // ------------------------------------------------------------------------------ -// +// // Licensed under the MIT license. // See the LICENSE file in the project root for more information. // @@ -14,9 +14,11 @@ namespace War3Net.CodeAnalysis.Transpilers { public partial class JassToCSharpTranspiler { - public ExpressionSyntax Transpile(JassFourCCLiteralExpressionSyntax fourCCLiteralExpression) + public ExpressionSyntax Transpile(JassElementAccessExpressionSyntax elementAccessExpression) { - return SyntaxFactory.ParseExpression(fourCCLiteralExpression.Value.ToString()); + return SyntaxFactory.ElementAccessExpression( + Transpile(elementAccessExpression.IdentifierName), + Transpile(elementAccessExpression.ElementAccessClause)); } } } \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/ElseClauseTranspiler.cs b/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/ElseClauseTranspiler.cs index 64cf7675..5f8e7b81 100644 --- a/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/ElseClauseTranspiler.cs +++ b/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/ElseClauseTranspiler.cs @@ -5,6 +5,9 @@ // // ------------------------------------------------------------------------------ +using System.Linq; + +using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; @@ -14,9 +17,16 @@ namespace War3Net.CodeAnalysis.Transpilers { public partial class JassToCSharpTranspiler { - public ElseClauseSyntax Transpile(JassElseClauseSyntax elseClause) + public ElseClauseSyntax Transpile(JassElseClauseSyntax elseClause, JassSyntaxToken closingToken) { - return SyntaxFactory.ElseClause(SyntaxFactory.Block(Transpile(elseClause.Body))); + var elseBlock = SyntaxFactory.Block( + Transpile(SyntaxKind.OpenBraceToken, elseClause.ElseToken.TrailingTrivia), + SyntaxFactory.List(elseClause.Statements.Select(Transpile)), + Transpile(SyntaxKind.CloseBraceToken, closingToken)); + + return SyntaxFactory.ElseClause( + Token(SyntaxKind.ElseKeyword, SyntaxTriviaList.Create(SyntaxFactory.ElasticSpace)), + elseBlock); } } } \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/ElseIfClauseTranspiler.cs b/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/ElseIfClauseTranspiler.cs index 172f5d04..48f17a66 100644 --- a/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/ElseIfClauseTranspiler.cs +++ b/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/ElseIfClauseTranspiler.cs @@ -5,22 +5,72 @@ // // ------------------------------------------------------------------------------ +using System.Linq; + +using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; +using War3Net.CodeAnalysis.Jass.Extensions; using War3Net.CodeAnalysis.Jass.Syntax; +using War3Net.CodeAnalysis.Transpilers.Extensions; namespace War3Net.CodeAnalysis.Transpilers { public partial class JassToCSharpTranspiler { - public StatementSyntax Transpile(JassElseIfClauseSyntax elseIfClause, ElseClauseSyntax? elseClause) + public StatementSyntax Transpile( + JassElseIfClauseSyntax elseIfClause, + ElseClauseSyntax? elseClause, + JassSyntaxToken closingToken) { - return SyntaxFactory.IfStatement( - SyntaxFactory.List(), - Transpile(elseIfClause.Condition), - SyntaxFactory.Block(Transpile(elseIfClause.Body)), - elseClause); + var closeBraceToken = Transpile(SyntaxKind.CloseBraceToken, closingToken); + if (elseClause is not null) + { + closeBraceToken = closeBraceToken.WithSpace(); + } + + var elseIfBlock = SyntaxFactory.Block( + Transpile(SyntaxKind.OpenBraceToken, elseIfClause.ElseIfClauseDeclarator.ThenToken), + SyntaxFactory.List(elseIfClause.Statements.Select(Transpile)), + closeBraceToken); + + if (elseIfClause.ElseIfClauseDeclarator.Condition is JassParenthesizedExpressionSyntax parenthesizedExpression) + { + return SyntaxFactory.IfStatement( + SyntaxFactory.List(), + Transpile(SyntaxKind.IfKeyword, elseIfClause.ElseIfClauseDeclarator.ElseIfToken.TrailingTrivia), + Transpile(SyntaxKind.OpenParenToken, parenthesizedExpression.OpenParenToken), + Transpile(parenthesizedExpression.Expression), + Transpile(SyntaxKind.CloseParenToken, parenthesizedExpression.CloseParenToken), + elseIfBlock, + elseClause); + } + else + { + var trailingTrivia = MergeTrivia( + elseIfClause.ElseIfClauseDeclarator.ElseIfToken.TrailingTrivia, + elseIfClause.ElseIfClauseDeclarator.Condition.GetLeadingTrivia()); + + var leadingTrivia = MergeTrivia( + elseIfClause.ElseIfClauseDeclarator.Condition.GetTrailingTrivia(), + elseIfClause.ElseIfClauseDeclarator.ThenToken.LeadingTrivia); + + return SyntaxFactory.IfStatement( + SyntaxFactory.List(), + SyntaxFactory.Token( + SyntaxTriviaList.Empty, + SyntaxKind.IfKeyword, + SyntaxTriviaList.Create(SyntaxFactory.ElasticSpace)), + Transpile(SyntaxKind.OpenParenToken, trailingTrivia), + Transpile(elseIfClause.ElseIfClauseDeclarator.Condition).WithoutTrivia(), + SyntaxFactory.Token( + Transpile(leadingTrivia), + SyntaxKind.CloseParenToken, + SyntaxTriviaList.Create(SyntaxFactory.ElasticSpace)), + elseIfBlock, + elseClause); + } } } } \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/EmptyTranspiler.cs b/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/EmptyTranspiler.cs deleted file mode 100644 index bc7cd015..00000000 --- a/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/EmptyTranspiler.cs +++ /dev/null @@ -1,22 +0,0 @@ -// ------------------------------------------------------------------------------ -// -// Licensed under the MIT license. -// See the LICENSE file in the project root for more information. -// -// ------------------------------------------------------------------------------ - -using Microsoft.CodeAnalysis.CSharp; -using Microsoft.CodeAnalysis.CSharp.Syntax; - -using War3Net.CodeAnalysis.Jass.Syntax; - -namespace War3Net.CodeAnalysis.Transpilers -{ - public partial class JassToCSharpTranspiler - { - public StatementSyntax Transpile(JassEmptySyntax empty) - { - return SyntaxFactory.EmptyStatement(); - } - } -} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/EqualsValueClauseTranspiler.cs b/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/EqualsValueClauseTranspiler.cs index c4e5340b..40932100 100644 --- a/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/EqualsValueClauseTranspiler.cs +++ b/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/EqualsValueClauseTranspiler.cs @@ -16,7 +16,9 @@ public partial class JassToCSharpTranspiler { public EqualsValueClauseSyntax Transpile(JassEqualsValueClauseSyntax equalsValueClause) { - return SyntaxFactory.EqualsValueClause(Transpile(equalsValueClause.Expression)); + return SyntaxFactory.EqualsValueClause( + Transpile(SyntaxKind.EqualsToken, equalsValueClause.EqualsToken), + Transpile(equalsValueClause.Expression)); } } } \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/ExitStatementTranspiler.cs b/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/ExitStatementTranspiler.cs index 658fafa1..20acc919 100644 --- a/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/ExitStatementTranspiler.cs +++ b/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/ExitStatementTranspiler.cs @@ -5,10 +5,12 @@ // // ------------------------------------------------------------------------------ +using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; using War3Net.CodeAnalysis.Jass.Syntax; +using War3Net.CodeAnalysis.Transpilers.Extensions; namespace War3Net.CodeAnalysis.Transpilers { @@ -16,9 +18,41 @@ public partial class JassToCSharpTranspiler { public StatementSyntax Transpile(JassExitStatementSyntax exitStatement) { - return SyntaxFactory.IfStatement( - Transpile(exitStatement.Condition), - SyntaxFactory.BreakStatement()); + if (exitStatement.Condition is JassParenthesizedExpressionSyntax parenthesizedExpression) + { + return SyntaxFactory.IfStatement( + Transpile(SyntaxKind.IfKeyword, exitStatement.ExitWhenToken), + Transpile(SyntaxKind.OpenParenToken, parenthesizedExpression.OpenParenToken), + Transpile(parenthesizedExpression.Expression), + Transpile( + parenthesizedExpression.CloseParenToken.LeadingTrivia, + SyntaxKind.CloseParenToken, + JassSyntaxTriviaList.SingleSpace), + SyntaxFactory.BreakStatement( + SyntaxFactory.Token(SyntaxKind.BreakKeyword), + Transpile(SyntaxKind.SemicolonToken, parenthesizedExpression.CloseParenToken.TrailingTrivia)), + null); + } + else + { + var expression = Transpile(exitStatement.Condition); + var trailingTrivia = expression.GetTrailingTrivia(); + + return SyntaxFactory.IfStatement( + Transpile(SyntaxKind.IfKeyword, exitStatement.ExitWhenToken), + SyntaxFactory.Token(SyntaxKind.OpenParenToken), + expression.WithoutTrailingTrivia(), + Token( + SyntaxKind.CloseParenToken, + SyntaxTriviaList.Create(SyntaxFactory.ElasticSpace)), + SyntaxFactory.BreakStatement( + SyntaxFactory.Token(SyntaxKind.BreakKeyword), + SyntaxFactory.Token( + SyntaxTriviaList.Empty, + SyntaxKind.SemicolonToken, + trailingTrivia)), + null); + } } } } \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/ExpressionTranspiler.cs b/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/ExpressionTranspiler.cs index b1fc1d4c..645806ef 100644 --- a/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/ExpressionTranspiler.cs +++ b/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/ExpressionTranspiler.cs @@ -5,6 +5,7 @@ // // ------------------------------------------------------------------------------ +using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; using War3Net.CodeAnalysis.Jass.Syntax; @@ -13,27 +14,24 @@ namespace War3Net.CodeAnalysis.Transpilers { public partial class JassToCSharpTranspiler { - public ExpressionSyntax Transpile(IExpressionSyntax expression) + public ExpressionSyntax Transpile(JassExpressionSyntax expression) { return expression switch { - JassCharacterLiteralExpressionSyntax characterLiteralExpression => Transpile(characterLiteralExpression), - JassFourCCLiteralExpressionSyntax fourCCLiteralExpression => Transpile(fourCCLiteralExpression), - JassHexadecimalLiteralExpressionSyntax hexadecimalLiteralExpression => Transpile(hexadecimalLiteralExpression), - JassRealLiteralExpressionSyntax realLiteralExpression => Transpile(realLiteralExpression), - JassOctalLiteralExpressionSyntax octalLiteralExpression => Transpile(octalLiteralExpression), - JassDecimalLiteralExpressionSyntax decimalLiteralExpression => Transpile(decimalLiteralExpression), - JassBooleanLiteralExpressionSyntax booleanLiteralExpression => Transpile(booleanLiteralExpression), - JassStringLiteralExpressionSyntax stringLiteralExpression => Transpile(stringLiteralExpression), - JassNullLiteralExpressionSyntax nullLiteralExpression => Transpile(nullLiteralExpression), + JassLiteralExpressionSyntax literalExpression => Transpile(literalExpression), JassFunctionReferenceExpressionSyntax functionReferenceExpression => Transpile(functionReferenceExpression), JassInvocationExpressionSyntax invocationExpression => Transpile(invocationExpression), - JassArrayReferenceExpressionSyntax arrayReferenceExpression => Transpile(arrayReferenceExpression), - JassVariableReferenceExpressionSyntax variableReferenceExpression => Transpile(variableReferenceExpression), + JassElementAccessExpressionSyntax elementAccessExpression => Transpile(elementAccessExpression), + JassIdentifierNameSyntax identifierName => Transpile(identifierName), JassParenthesizedExpressionSyntax parenthesizedExpression => Transpile(parenthesizedExpression), JassUnaryExpressionSyntax unaryExpression => Transpile(unaryExpression), JassBinaryExpressionSyntax binaryExpression => Transpile(binaryExpression), }; } + + public ArgumentSyntax TranspileArgument(JassExpressionSyntax expression) + { + return SyntaxFactory.Argument(Transpile(expression)); + } } } \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/FunctionDeclarationTranspiler.cs b/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/FunctionDeclarationTranspiler.cs index 8932792a..9f06c6d8 100644 --- a/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/FunctionDeclarationTranspiler.cs +++ b/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/FunctionDeclarationTranspiler.cs @@ -5,11 +5,15 @@ // // ------------------------------------------------------------------------------ +using System.Linq; + using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; +using War3Net.CodeAnalysis.Jass.Extensions; using War3Net.CodeAnalysis.Jass.Syntax; +using War3Net.CodeAnalysis.Transpilers.Extensions; namespace War3Net.CodeAnalysis.Transpilers { @@ -17,18 +21,39 @@ public partial class JassToCSharpTranspiler { public MemberDeclarationSyntax Transpile(JassFunctionDeclarationSyntax functionDeclaration) { + var declarator = functionDeclaration.FunctionDeclarator; + + var firstToken = declarator.ConstantToken ?? declarator.FunctionToken; + var staticToken = declarator.ConstantToken is null + ? TokenWithSpace(SyntaxKind.StaticKeyword) + : Token(SyntaxKind.StaticKeyword, declarator.ConstantToken.TrailingTrivia); + + var functionNameToken = Transpile(declarator.IdentifierName.Token); + var discardTakesTokenLeadingTrivia = false; + if (IsSingleSpace(declarator.IdentifierName.Token.TrailingTrivia, declarator.ParameterList.GetTakesToken().LeadingTrivia)) + { + functionNameToken = functionNameToken.WithoutTrailingTrivia(); + discardTakesTokenLeadingTrivia = true; + } + return SyntaxFactory.MethodDeclaration( default, new SyntaxTokenList( - SyntaxFactory.Token(SyntaxKind.PublicKeyword), - SyntaxFactory.Token(SyntaxKind.StaticKeyword)), - Transpile(functionDeclaration.FunctionDeclarator.ReturnType), + TokenWithSpace(firstToken.LeadingTrivia, SyntaxKind.PublicKeyword), + staticToken), + Transpile( + declarator.ConstantToken is null ? JassSyntaxTriviaList.Empty : declarator.FunctionToken.LeadingTrivia, + declarator.ReturnClause.ReturnType, + declarator.FunctionToken.TrailingTrivia), null, - Transpile(functionDeclaration.FunctionDeclarator.IdentifierName), + functionNameToken, null, - SyntaxFactory.ParameterList(Transpile(functionDeclaration.FunctionDeclarator.ParameterList)), + Transpile(declarator.ParameterList, declarator.ReturnClause, discardTakesTokenLeadingTrivia), default, - SyntaxFactory.Block(Transpile(functionDeclaration.Body)), + SyntaxFactory.Block( + Transpile(SyntaxKind.OpenBraceToken, declarator.ReturnClause.ReturnType.GetToken()), + SyntaxFactory.List(functionDeclaration.Statements.Select(Transpile)), + Transpile(SyntaxKind.CloseBraceToken, functionDeclaration.EndFunctionToken)), null); } } diff --git a/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/FunctionReferenceExpressionTranspiler.cs b/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/FunctionReferenceExpressionTranspiler.cs index 0c24b104..5bc7fcc2 100644 --- a/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/FunctionReferenceExpressionTranspiler.cs +++ b/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/FunctionReferenceExpressionTranspiler.cs @@ -5,7 +5,6 @@ // // ------------------------------------------------------------------------------ -using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; using War3Net.CodeAnalysis.Jass.Syntax; @@ -16,7 +15,11 @@ public partial class JassToCSharpTranspiler { public ExpressionSyntax Transpile(JassFunctionReferenceExpressionSyntax functionReferenceExpression) { - return SyntaxFactory.IdentifierName(Transpile(functionReferenceExpression.IdentifierName)); + var leadingTrivia = MergeTrivia( + functionReferenceExpression.FunctionToken, + functionReferenceExpression.IdentifierName.Token.LeadingTrivia); + + return Transpile(leadingTrivia, functionReferenceExpression.IdentifierName); } } } \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/GlobalConstantDeclarationTranspiler.cs b/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/GlobalConstantDeclarationTranspiler.cs new file mode 100644 index 00000000..d8d64c65 --- /dev/null +++ b/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/GlobalConstantDeclarationTranspiler.cs @@ -0,0 +1,81 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; + +using War3Net.CodeAnalysis.Jass.Extensions; +using War3Net.CodeAnalysis.Jass.Syntax; +using War3Net.CodeAnalysis.Transpilers.Extensions; + +namespace War3Net.CodeAnalysis.Transpilers +{ + public partial class JassToCSharpTranspiler + { + public MemberDeclarationSyntax Transpile( + JassGlobalConstantDeclarationSyntax globalConstantDeclaration) + { + return Transpile( + globalConstantDeclaration.GetLeadingTrivia(), + globalConstantDeclaration, + globalConstantDeclaration.GetTrailingTrivia()); + } + + public MemberDeclarationSyntax Transpile( + JassSyntaxTriviaList leadingTrivia, + JassGlobalConstantDeclarationSyntax globalConstantDeclaration) + { + return Transpile( + leadingTrivia, + globalConstantDeclaration, + globalConstantDeclaration.GetTrailingTrivia()); + } + + public MemberDeclarationSyntax Transpile( + JassGlobalConstantDeclarationSyntax globalConstantDeclaration, + JassSyntaxTriviaList trailingTrivia) + { + return Transpile( + globalConstantDeclaration.GetLeadingTrivia(), + globalConstantDeclaration, + trailingTrivia); + } + + public MemberDeclarationSyntax Transpile( + JassSyntaxTriviaList leadingTrivia, + JassGlobalConstantDeclarationSyntax globalConstantDeclaration, + JassSyntaxTriviaList trailingTrivia) + { + var variableDeclaration = SyntaxFactory.VariableDeclaration( + TranspileAligned(globalConstantDeclaration.Type, isArray: false), + SyntaxFactory.SingletonSeparatedList( + SyntaxFactory.VariableDeclarator( + Transpile(globalConstantDeclaration.IdentifierName.Token), + null, + Transpile(globalConstantDeclaration.Value)))); + + var declaration = SyntaxFactory.FieldDeclaration( + default, + new SyntaxTokenList( + TokenWithSpace(leadingTrivia, SyntaxKind.PublicKeyword), + Transpile(SyntaxKind.ConstKeyword, globalConstantDeclaration.ConstantToken)), + variableDeclaration, + Transpile(SyntaxKind.SemicolonToken, globalConstantDeclaration.GetTrailingTrivia())); + + if (ApplyCSharpLuaTemplateAttribute) + { + var jassToLuaTranspiler = JassToLuaTranspiler ?? new JassToLuaTranspiler(); + + declaration = declaration.WithCSharpLuaTemplateAttribute( + jassToLuaTranspiler.Transpile(globalConstantDeclaration.IdentifierName.Token)); + } + + return declaration; + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/GlobalDeclarationListTranspiler.cs b/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/GlobalDeclarationListTranspiler.cs deleted file mode 100644 index bf852ca7..00000000 --- a/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/GlobalDeclarationListTranspiler.cs +++ /dev/null @@ -1,29 +0,0 @@ -// ------------------------------------------------------------------------------ -// -// Licensed under the MIT license. -// See the LICENSE file in the project root for more information. -// -// ------------------------------------------------------------------------------ - -using System.Collections.Generic; - -using Microsoft.CodeAnalysis.CSharp.Syntax; - -using War3Net.CodeAnalysis.Jass.Syntax; - -namespace War3Net.CodeAnalysis.Transpilers -{ - public partial class JassToCSharpTranspiler - { - public IEnumerable Transpile(JassGlobalDeclarationListSyntax globalDeclarationList) - { - foreach (var declaration in globalDeclarationList.Globals) - { - if (declaration is JassGlobalDeclarationSyntax globalDeclaration) - { - yield return Transpile(globalDeclaration); - } - } - } - } -} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/GlobalDeclarationTranspiler.cs b/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/GlobalDeclarationTranspiler.cs index 6d27cc20..cabbb3bc 100644 --- a/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/GlobalDeclarationTranspiler.cs +++ b/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/GlobalDeclarationTranspiler.cs @@ -5,33 +5,56 @@ // // ------------------------------------------------------------------------------ -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; using War3Net.CodeAnalysis.Jass.Syntax; -using War3Net.CodeAnalysis.Transpilers.Extensions; namespace War3Net.CodeAnalysis.Transpilers { public partial class JassToCSharpTranspiler { - public MemberDeclarationSyntax Transpile(JassGlobalDeclarationSyntax globalDeclaration) + public MemberDeclarationSyntax Transpile( + JassGlobalDeclarationSyntax globalDeclaration) { - var declaration = SyntaxFactory.FieldDeclaration( - default, - new SyntaxTokenList( - SyntaxFactory.Token(SyntaxKind.PublicKeyword), - SyntaxFactory.Token(SyntaxKind.StaticKeyword)), - Transpile(globalDeclaration.Declarator)); + return globalDeclaration switch + { + JassGlobalConstantDeclarationSyntax globalConstantDeclaration => Transpile(globalConstantDeclaration), + JassGlobalVariableDeclarationSyntax globalVariableDeclaration => Transpile(globalVariableDeclaration), + }; + } + + public MemberDeclarationSyntax Transpile( + JassSyntaxTriviaList leadingTrivia, + JassGlobalDeclarationSyntax globalDeclaration) + { + return globalDeclaration switch + { + JassGlobalConstantDeclarationSyntax globalConstantDeclaration => Transpile(leadingTrivia, globalConstantDeclaration), + JassGlobalVariableDeclarationSyntax globalVariableDeclaration => Transpile(leadingTrivia, globalVariableDeclaration), + }; + } - if (ApplyCSharpLuaTemplateAttribute) + public MemberDeclarationSyntax Transpile( + JassGlobalDeclarationSyntax globalDeclaration, + JassSyntaxTriviaList trailingTrivia) + { + return globalDeclaration switch { - var jassToLuaTranspiler = JassToLuaTranspiler ?? new JassToLuaTranspiler(); - declaration = declaration.WithCSharpLuaTemplateAttribute(jassToLuaTranspiler.Transpile(globalDeclaration.Declarator.IdentifierName)); - } + JassGlobalConstantDeclarationSyntax globalConstantDeclaration => Transpile(globalConstantDeclaration, trailingTrivia), + JassGlobalVariableDeclarationSyntax globalVariableDeclaration => Transpile(globalVariableDeclaration, trailingTrivia), + }; + } - return declaration; + public MemberDeclarationSyntax Transpile( + JassSyntaxTriviaList leadingTrivia, + JassGlobalDeclarationSyntax globalDeclaration, + JassSyntaxTriviaList trailingTrivia) + { + return globalDeclaration switch + { + JassGlobalConstantDeclarationSyntax globalConstantDeclaration => Transpile(leadingTrivia, globalConstantDeclaration, trailingTrivia), + JassGlobalVariableDeclarationSyntax globalVariableDeclaration => Transpile(leadingTrivia, globalVariableDeclaration, trailingTrivia), + }; } } } \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/GlobalVariableDeclarationTranspiler.cs b/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/GlobalVariableDeclarationTranspiler.cs new file mode 100644 index 00000000..242923fd --- /dev/null +++ b/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/GlobalVariableDeclarationTranspiler.cs @@ -0,0 +1,73 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; + +using War3Net.CodeAnalysis.Jass.Extensions; +using War3Net.CodeAnalysis.Jass.Syntax; +using War3Net.CodeAnalysis.Transpilers.Extensions; + +namespace War3Net.CodeAnalysis.Transpilers +{ + public partial class JassToCSharpTranspiler + { + public MemberDeclarationSyntax Transpile( + JassGlobalVariableDeclarationSyntax globalVariableDeclaration) + { + return Transpile( + globalVariableDeclaration.GetLeadingTrivia(), + globalVariableDeclaration, + globalVariableDeclaration.GetTrailingTrivia()); + } + + public MemberDeclarationSyntax Transpile( + JassSyntaxTriviaList leadingTrivia, + JassGlobalVariableDeclarationSyntax globalVariableDeclaration) + { + return Transpile( + leadingTrivia, + globalVariableDeclaration, + globalVariableDeclaration.GetTrailingTrivia()); + } + + public MemberDeclarationSyntax Transpile( + JassGlobalVariableDeclarationSyntax globalVariableDeclaration, + JassSyntaxTriviaList trailingTrivia) + { + return Transpile( + globalVariableDeclaration.GetLeadingTrivia(), + globalVariableDeclaration, + trailingTrivia); + } + + public MemberDeclarationSyntax Transpile( + JassSyntaxTriviaList leadingTrivia, + JassGlobalVariableDeclarationSyntax globalVariableDeclaration, + JassSyntaxTriviaList trailingTrivia) + { + var declaration = SyntaxFactory.FieldDeclaration( + default, + new SyntaxTokenList( + TokenWithSpace(leadingTrivia, SyntaxKind.PublicKeyword), + TokenWithSpace(SyntaxKind.StaticKeyword)), + Transpile(globalVariableDeclaration.Declarator, isGlobalDeclaration: true).WithoutTrivia(), + Transpile(SyntaxKind.SemicolonToken, trailingTrivia)); + + if (ApplyCSharpLuaTemplateAttribute) + { + var jassToLuaTranspiler = JassToLuaTranspiler ?? new JassToLuaTranspiler(); + + declaration = declaration.WithCSharpLuaTemplateAttribute( + jassToLuaTranspiler.Transpile(globalVariableDeclaration.Declarator.GetIdentifierName().Token)); + } + + return declaration; + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/GlobalsDeclarationTranspiler.cs b/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/GlobalsDeclarationTranspiler.cs new file mode 100644 index 00000000..9d7522f1 --- /dev/null +++ b/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/GlobalsDeclarationTranspiler.cs @@ -0,0 +1,53 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using System.Collections.Generic; + +using Microsoft.CodeAnalysis.CSharp.Syntax; + +using War3Net.CodeAnalysis.Jass.Extensions; +using War3Net.CodeAnalysis.Jass.Syntax; + +namespace War3Net.CodeAnalysis.Transpilers +{ + public partial class JassToCSharpTranspiler + { + public IEnumerable Transpile(JassGlobalsDeclarationSyntax globalsDeclaration) + { + var declarations = globalsDeclaration.GlobalDeclarations; + for (var i = 0; i < declarations.Length; i++) + { + if (i == 0) + { + if (i + 1 == declarations.Length) + { + yield return Transpile( + MergeTrivia(globalsDeclaration.GlobalsToken, declarations[i].GetLeadingTrivia()), + declarations[i], + MergeTrivia(declarations[i].GetTrailingTrivia(), globalsDeclaration.EndGlobalsToken)); + } + else + { + yield return Transpile( + MergeTrivia(globalsDeclaration.GlobalsToken, declarations[i].GetLeadingTrivia()), + declarations[i]); + } + } + else if (i + 1 == declarations.Length) + { + yield return Transpile( + declarations[i], + MergeTrivia(declarations[i].GetTrailingTrivia(), globalsDeclaration.EndGlobalsToken)); + } + else + { + yield return Transpile(declarations[i]); + } + } + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/HexadecimalLiteralExpressionTranspiler.cs b/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/HexadecimalLiteralExpressionTranspiler.cs deleted file mode 100644 index 1658d4f2..00000000 --- a/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/HexadecimalLiteralExpressionTranspiler.cs +++ /dev/null @@ -1,22 +0,0 @@ -// ------------------------------------------------------------------------------ -// -// Licensed under the MIT license. -// See the LICENSE file in the project root for more information. -// -// ------------------------------------------------------------------------------ - -using Microsoft.CodeAnalysis.CSharp; -using Microsoft.CodeAnalysis.CSharp.Syntax; - -using War3Net.CodeAnalysis.Jass.Syntax; - -namespace War3Net.CodeAnalysis.Transpilers -{ - public partial class JassToCSharpTranspiler - { - public ExpressionSyntax Transpile(JassHexadecimalLiteralExpressionSyntax hexadecimalLiteralExpression) - { - return SyntaxFactory.ParseExpression(hexadecimalLiteralExpression.ToString()); - } - } -} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/IdentifierNameTranspiler.cs b/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/IdentifierNameTranspiler.cs index c6dbd774..9b253f77 100644 --- a/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/IdentifierNameTranspiler.cs +++ b/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/IdentifierNameTranspiler.cs @@ -5,46 +5,89 @@ // // ------------------------------------------------------------------------------ -using System; -using System.Collections.Generic; -using System.Linq; - -using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; using War3Net.CodeAnalysis.Jass.Syntax; +using War3Net.CodeAnalysis.Transpilers.Extensions; namespace War3Net.CodeAnalysis.Transpilers { public partial class JassToCSharpTranspiler { - private const string AntiReservedKeywordConflictPrefix = "@"; + public IdentifierNameSyntax Transpile( + JassIdentifierNameSyntax identifierName) + { + return SyntaxFactory.IdentifierName(Transpile(identifierName.Token)); + } + + public IdentifierNameSyntax Transpile( + JassSyntaxTriviaList leadingTrivia, + JassIdentifierNameSyntax identifierName) + { + return SyntaxFactory.IdentifierName(Transpile(leadingTrivia, identifierName.Token)); + } - private static readonly Lazy> _reservedKeywords = new Lazy>(() => GetReservedKeywords().ToHashSet(StringComparer.Ordinal)); + public IdentifierNameSyntax Transpile( + JassIdentifierNameSyntax identifierName, + JassSyntaxTriviaList trailingTrivia) + { + return SyntaxFactory.IdentifierName(Transpile(identifierName.Token, trailingTrivia)); + } - public SyntaxToken Transpile(JassIdentifierNameSyntax identifierName) + public IdentifierNameSyntax Transpile( + JassSyntaxTriviaList leadingTrivia, + JassIdentifierNameSyntax identifierName, + JassSyntaxTriviaList trailingTrivia) { - var text = _reservedKeywords.Value.Contains(identifierName.Name) - ? $"{AntiReservedKeywordConflictPrefix}{identifierName.Name}" - : identifierName.Name; + return SyntaxFactory.IdentifierName(Transpile(leadingTrivia, identifierName.Token, trailingTrivia)); + } - return SyntaxFactory.Identifier( - SyntaxTriviaList.Empty, - SyntaxKind.IdentifierToken, - text, - identifierName.Name, - SyntaxTriviaList.Empty); + public IdentifierNameSyntax TranspileAligned( + JassIdentifierNameSyntax identifierName, + bool isArray) + { + return SyntaxFactory.IdentifierName(TranspileAligned(identifierName.Token, out var prefixAdded)) + .WithAlignedWhitespace(GetWhitespaceDiff(prefixAdded, isArray)); + } + + public IdentifierNameSyntax TranspileAligned( + JassSyntaxTriviaList leadingTrivia, + JassIdentifierNameSyntax identifierName, + bool isArray) + { + return SyntaxFactory.IdentifierName(TranspileAligned(leadingTrivia, identifierName.Token, out var prefixAdded)) + .WithAlignedWhitespace(GetWhitespaceDiff(prefixAdded, isArray)); + } + + public IdentifierNameSyntax TranspileAligned( + JassIdentifierNameSyntax identifierName, + JassSyntaxTriviaList trailingTrivia, + bool isArray) + { + return SyntaxFactory.IdentifierName(TranspileAligned(identifierName.Token, trailingTrivia, out var prefixAdded)) + .WithAlignedWhitespace(GetWhitespaceDiff(prefixAdded, isArray)); + } + + public IdentifierNameSyntax TranspileAligned( + JassSyntaxTriviaList leadingTrivia, + JassIdentifierNameSyntax identifierName, + JassSyntaxTriviaList trailingTrivia, + bool isArray) + { + return SyntaxFactory.IdentifierName(TranspileAligned(leadingTrivia, identifierName.Token, trailingTrivia, out var prefixAdded)) + .WithAlignedWhitespace(GetWhitespaceDiff(prefixAdded, isArray)); } - private static IEnumerable GetReservedKeywords() + private int GetWhitespaceDiff(bool prefixAdded, bool isArray) { - foreach (SyntaxKind syntaxKind in Enum.GetValues(typeof(SyntaxKind))) - { - if (SyntaxFacts.IsReservedKeyword(syntaxKind)) - { - yield return SyntaxFactory.Token(syntaxKind).ValueText; - } - } + return prefixAdded + ? isArray + ? ArrayWhitespaceDiff + PrefixWhitespaceDiff + : PrefixWhitespaceDiff + : isArray + ? ArrayWhitespaceDiff + : 0; } } } \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/IfStatementTranspiler.cs b/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/IfStatementTranspiler.cs index eca96590..463b460e 100644 --- a/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/IfStatementTranspiler.cs +++ b/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/IfStatementTranspiler.cs @@ -7,10 +7,13 @@ using System.Linq; +using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; +using War3Net.CodeAnalysis.Jass.Extensions; using War3Net.CodeAnalysis.Jass.Syntax; +using War3Net.CodeAnalysis.Transpilers.Extensions; namespace War3Net.CodeAnalysis.Transpilers { @@ -19,21 +22,72 @@ public partial class JassToCSharpTranspiler public StatementSyntax Transpile(JassIfStatementSyntax ifStatement) { ElseClauseSyntax? elseClause = null; + JassSyntaxToken closingToken = ifStatement.EndIfToken; + if (ifStatement.ElseClause is not null) { - elseClause = Transpile(ifStatement.ElseClause); + elseClause = Transpile(ifStatement.ElseClause, closingToken); + closingToken = ifStatement.ElseClause.ElseToken; } foreach (var elseIfClause in ifStatement.ElseIfClauses.Reverse()) { - elseClause = SyntaxFactory.ElseClause(Transpile(elseIfClause, elseClause)); + elseClause = SyntaxFactory.ElseClause( + Token( + SyntaxKind.ElseKeyword, + SyntaxTriviaList.Create(SyntaxFactory.ElasticSpace)), + Transpile(elseIfClause, elseClause, closingToken)); + + closingToken = elseIfClause.ElseIfClauseDeclarator.ElseIfToken; + } + + var closeBraceToken = Transpile(SyntaxKind.CloseBraceToken, closingToken); + if (elseClause is not null) + { + closeBraceToken = closeBraceToken.WithSpace(); + } + + var ifBlock = SyntaxFactory.Block( + Transpile(SyntaxKind.OpenBraceToken, ifStatement.IfClause.IfClauseDeclarator.ThenToken), + SyntaxFactory.List(ifStatement.IfClause.Statements.Select(Transpile)), + closeBraceToken); + + if (ifStatement.IfClause.IfClauseDeclarator.Condition is JassParenthesizedExpressionSyntax parenthesizedExpression) + { + return SyntaxFactory.IfStatement( + SyntaxFactory.List(), + Transpile(SyntaxKind.IfKeyword, ifStatement.IfClause.IfClauseDeclarator.IfToken), + Transpile(SyntaxKind.OpenParenToken, parenthesizedExpression.OpenParenToken), + Transpile(parenthesizedExpression.Expression), + Transpile(SyntaxKind.CloseParenToken, parenthesizedExpression.CloseParenToken), + ifBlock, + elseClause); } + else + { + var trailingTrivia = MergeTrivia( + ifStatement.IfClause.IfClauseDeclarator.IfToken.TrailingTrivia, + ifStatement.IfClause.IfClauseDeclarator.Condition.GetLeadingTrivia()); + + var leadingTrivia = MergeTrivia( + ifStatement.IfClause.IfClauseDeclarator.Condition.GetTrailingTrivia(), + ifStatement.IfClause.IfClauseDeclarator.ThenToken.LeadingTrivia); - return SyntaxFactory.IfStatement( - SyntaxFactory.List(), - Transpile(ifStatement.Condition), - SyntaxFactory.Block(Transpile(ifStatement.Body)), - elseClause); + return SyntaxFactory.IfStatement( + SyntaxFactory.List(), + SyntaxFactory.Token( + Transpile(ifStatement.IfClause.IfClauseDeclarator.IfToken.LeadingTrivia), + SyntaxKind.IfKeyword, + SyntaxTriviaList.Create(SyntaxFactory.ElasticSpace)), + Transpile(SyntaxKind.OpenParenToken, trailingTrivia), + Transpile(ifStatement.IfClause.IfClauseDeclarator.Condition).WithoutTrivia(), + SyntaxFactory.Token( + Transpile(leadingTrivia), + SyntaxKind.CloseParenToken, + SyntaxTriviaList.Create(SyntaxFactory.ElasticSpace)), + ifBlock, + elseClause); + } } } } \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/InvocationExpressionTranspiler.cs b/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/InvocationExpressionTranspiler.cs index e7cbe23e..51d4608e 100644 --- a/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/InvocationExpressionTranspiler.cs +++ b/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/InvocationExpressionTranspiler.cs @@ -17,8 +17,8 @@ public partial class JassToCSharpTranspiler public ExpressionSyntax Transpile(JassInvocationExpressionSyntax invocationExpression) { return SyntaxFactory.InvocationExpression( - SyntaxFactory.IdentifierName(Transpile(invocationExpression.IdentifierName)), - Transpile(invocationExpression.Arguments)); + Transpile(invocationExpression.IdentifierName), + Transpile(invocationExpression.ArgumentList)); } } } \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/LiteralExpressionTranspiler.cs b/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/LiteralExpressionTranspiler.cs new file mode 100644 index 00000000..035b0e15 --- /dev/null +++ b/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/LiteralExpressionTranspiler.cs @@ -0,0 +1,145 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using System; +using System.Globalization; + +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; + +using War3Net.CodeAnalysis.Jass; +using War3Net.CodeAnalysis.Jass.Syntax; +using War3Net.Common.Extensions; + +namespace War3Net.CodeAnalysis.Transpilers +{ + public partial class JassToCSharpTranspiler + { + public ExpressionSyntax Transpile(JassLiteralExpressionSyntax literalExpression) + { + return literalExpression.SyntaxKind switch + { + JassSyntaxKind.TrueLiteralExpression => TranspileLiteral(SyntaxKind.TrueLiteralExpression, SyntaxKind.TrueKeyword, literalExpression.Token), + JassSyntaxKind.FalseLiteralExpression => TranspileLiteral(SyntaxKind.FalseLiteralExpression, SyntaxKind.FalseKeyword, literalExpression.Token), + JassSyntaxKind.NullLiteralExpression => TranspileLiteral(SyntaxKind.NullLiteralExpression, SyntaxKind.NullKeyword, literalExpression.Token), + JassSyntaxKind.DecimalLiteralExpression => TranspileDecimalLiteral(literalExpression), + JassSyntaxKind.OctalLiteralExpression => TranspileOctalLiteral(literalExpression), + JassSyntaxKind.HexadecimalLiteralExpression => TranspileHexadecimalLiteral(literalExpression), + JassSyntaxKind.FourCCLiteralExpression => TranspileFourCCLiteral(literalExpression), + JassSyntaxKind.CharacterLiteralExpression => TranspileCharacterLiteral(literalExpression), + JassSyntaxKind.RealLiteralExpression => TranspileRealLiteral(literalExpression), + JassSyntaxKind.StringLiteralExpression => TranspileStringLiteral(literalExpression), + }; + } + + private ExpressionSyntax TranspileLiteral(SyntaxKind expressionKind, SyntaxKind tokenKind, JassSyntaxToken token) + { + return SyntaxFactory.LiteralExpression( + expressionKind, + Transpile(tokenKind, token)); + } + + private ExpressionSyntax TranspileDecimalLiteral(JassLiteralExpressionSyntax literalExpression) + { + var value = JassLiteral.ParseInt(literalExpression.Token.Text); + var text = value.ToString(CultureInfo.InvariantCulture); + + return SyntaxFactory.LiteralExpression( + SyntaxKind.NumericLiteralExpression, + SyntaxFactory.Literal( + Transpile(literalExpression.Token.LeadingTrivia), + text, + value, + Transpile(literalExpression.Token.TrailingTrivia))); + } + + private ExpressionSyntax TranspileOctalLiteral(JassLiteralExpressionSyntax literalExpression) + { + var value = JassLiteral.ParseOctal(literalExpression.Token.Text); + var text = value.ToString(CultureInfo.InvariantCulture); + + return SyntaxFactory.LiteralExpression( + SyntaxKind.NumericLiteralExpression, + SyntaxFactory.Literal( + Transpile(literalExpression.Token.LeadingTrivia), + text, + value, + Transpile(literalExpression.Token.TrailingTrivia))); + } + + private ExpressionSyntax TranspileHexadecimalLiteral(JassLiteralExpressionSyntax literalExpression) + { + var text = literalExpression.Token.Text.Replace(JassSymbol.Dollar, "0x", StringComparison.Ordinal); + var value = Convert.ToInt32(text[2..], 16); + + return SyntaxFactory.LiteralExpression( + SyntaxKind.NumericLiteralExpression, + SyntaxFactory.Literal( + Transpile(literalExpression.Token.LeadingTrivia), + text, + value, + Transpile(literalExpression.Token.TrailingTrivia))); + } + + private ExpressionSyntax TranspileFourCCLiteral(JassLiteralExpressionSyntax literalExpression) + { + var value = JassLiteral.ParseFourCC(literalExpression.Token.Text); + var text = value.ToString(CultureInfo.InvariantCulture); + + return SyntaxFactory.LiteralExpression( + SyntaxKind.NumericLiteralExpression, + SyntaxFactory.Literal( + Transpile(literalExpression.Token.LeadingTrivia), + text, + value, + Transpile(literalExpression.Token.TrailingTrivia))); + } + + private ExpressionSyntax TranspileCharacterLiteral(JassLiteralExpressionSyntax literalExpression) + { + var value = JassLiteral.ParseChar(literalExpression.Token.Text); + var text = $"(int){literalExpression.Token.Text}"; + + return SyntaxFactory.LiteralExpression( + SyntaxKind.NumericLiteralExpression, + SyntaxFactory.Literal( + Transpile(literalExpression.Token.LeadingTrivia), + text, + value, + Transpile(literalExpression.Token.TrailingTrivia))); + } + + private ExpressionSyntax TranspileRealLiteral(JassLiteralExpressionSyntax literalExpression) + { + var text = literalExpression.Token.Text.TrimEnd(JassSymbol.DotChar); + var value = float.Parse(text, NumberStyles.AllowDecimalPoint, CultureInfo.InvariantCulture); + + return SyntaxFactory.LiteralExpression( + SyntaxKind.NumericLiteralExpression, + SyntaxFactory.Literal( + Transpile(literalExpression.Token.LeadingTrivia), + $"{text}f", + value, + Transpile(literalExpression.Token.TrailingTrivia))); + } + + private ExpressionSyntax TranspileStringLiteral(JassLiteralExpressionSyntax literalExpression) + { + var text = literalExpression.Token.Text + .Replace(JassSymbol.CarriageReturn, @"\r", StringComparison.Ordinal) + .Replace(JassSymbol.LineFeed, @"\n", StringComparison.Ordinal); + + return SyntaxFactory.LiteralExpression( + SyntaxKind.StringLiteralExpression, + SyntaxFactory.Literal( + Transpile(literalExpression.Token.LeadingTrivia), + text, + text, + Transpile(literalExpression.Token.TrailingTrivia))); + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/LocalVariableDeclarationStatementTranspiler.cs b/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/LocalVariableDeclarationStatementTranspiler.cs index 5af76cd6..4d9f0cc8 100644 --- a/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/LocalVariableDeclarationStatementTranspiler.cs +++ b/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/LocalVariableDeclarationStatementTranspiler.cs @@ -5,9 +5,11 @@ // // ------------------------------------------------------------------------------ +using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; +using War3Net.CodeAnalysis.Jass.Extensions; using War3Net.CodeAnalysis.Jass.Syntax; namespace War3Net.CodeAnalysis.Transpilers @@ -16,7 +18,24 @@ public partial class JassToCSharpTranspiler { public StatementSyntax Transpile(JassLocalVariableDeclarationStatementSyntax localVariableDeclarationStatement) { - return SyntaxFactory.LocalDeclarationStatement(Transpile(localVariableDeclarationStatement.Declarator)); + var leadingTrivia = MergeTrivia( + localVariableDeclarationStatement.LocalToken, + localVariableDeclarationStatement.Declarator.GetLeadingTrivia()); + + var declaration = Transpile( + leadingTrivia, + localVariableDeclarationStatement.Declarator, + isGlobalDeclaration: false); + + var trailingTrivia = declaration.GetTrailingTrivia(); + + return SyntaxFactory.LocalDeclarationStatement( + SyntaxFactory.TokenList(), + declaration.WithoutTrailingTrivia(), + SyntaxFactory.Token( + SyntaxTriviaList.Empty, + SyntaxKind.SemicolonToken, + trailingTrivia)); } } } \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/LoopStatementTranspiler.cs b/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/LoopStatementTranspiler.cs index d323cd36..de9b4494 100644 --- a/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/LoopStatementTranspiler.cs +++ b/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/LoopStatementTranspiler.cs @@ -5,6 +5,9 @@ // // ------------------------------------------------------------------------------ +using System.Linq; + +using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; @@ -17,8 +20,20 @@ public partial class JassToCSharpTranspiler public StatementSyntax Transpile(JassLoopStatementSyntax loopStatement) { return SyntaxFactory.WhileStatement( + Transpile( + loopStatement.LoopToken.LeadingTrivia, + SyntaxKind.WhileKeyword, + JassSyntaxTriviaList.SingleSpace), + SyntaxFactory.Token(SyntaxKind.OpenParenToken), SyntaxFactory.LiteralExpression(SyntaxKind.TrueLiteralExpression), - SyntaxFactory.Block(Transpile(loopStatement.Body))); + SyntaxFactory.Token( + SyntaxTriviaList.Empty, + SyntaxKind.CloseParenToken, + SyntaxTriviaList.Create(SyntaxFactory.ElasticSpace)), + SyntaxFactory.Block( + Transpile(SyntaxKind.OpenBraceToken, loopStatement.LoopToken.TrailingTrivia), + SyntaxFactory.List(loopStatement.Statements.Select(Transpile)), + Transpile(SyntaxKind.CloseBraceToken, loopStatement.EndLoopToken))); } } } \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/NativeFunctionDeclarationTranspiler.cs b/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/NativeFunctionDeclarationTranspiler.cs index 7d1d9818..29afcd19 100644 --- a/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/NativeFunctionDeclarationTranspiler.cs +++ b/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/NativeFunctionDeclarationTranspiler.cs @@ -9,7 +9,9 @@ using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; +using War3Net.CodeAnalysis.Jass.Extensions; using War3Net.CodeAnalysis.Jass.Syntax; +using War3Net.CodeAnalysis.Transpilers.Extensions; namespace War3Net.CodeAnalysis.Transpilers { @@ -17,21 +19,45 @@ public partial class JassToCSharpTranspiler { public MemberDeclarationSyntax Transpile(JassNativeFunctionDeclarationSyntax nativeFunctionDeclaration) { + var firstToken = nativeFunctionDeclaration.ConstantToken ?? nativeFunctionDeclaration.NativeToken; + var externToken = nativeFunctionDeclaration.ConstantToken is null + ? TokenWithSpace(SyntaxKind.ExternKeyword) + : Token(SyntaxKind.ExternKeyword, nativeFunctionDeclaration.ConstantToken.TrailingTrivia); + + var functionNameToken = Transpile(nativeFunctionDeclaration.IdentifierName.Token); + var discardTakesTokenLeadingTrivia = false; + if (IsSingleSpace(nativeFunctionDeclaration.IdentifierName.Token.TrailingTrivia, nativeFunctionDeclaration.ParameterList.GetTakesToken().LeadingTrivia)) + { + functionNameToken = functionNameToken.WithoutTrailingTrivia(); + discardTakesTokenLeadingTrivia = true; + } + + var parameterList = Transpile( + nativeFunctionDeclaration.ParameterList, + nativeFunctionDeclaration.ReturnClause, + discardTakesTokenLeadingTrivia); + return SyntaxFactory.MethodDeclaration( default, new SyntaxTokenList( - SyntaxFactory.Token(SyntaxKind.PublicKeyword), - SyntaxFactory.Token(SyntaxKind.StaticKeyword), - SyntaxFactory.Token(SyntaxKind.ExternKeyword)), - Transpile(nativeFunctionDeclaration.FunctionDeclarator.ReturnType), + TokenWithSpace(firstToken.LeadingTrivia, SyntaxKind.PublicKeyword), + TokenWithSpace(SyntaxKind.StaticKeyword), + externToken), + Transpile( + nativeFunctionDeclaration.ConstantToken is null ? JassSyntaxTriviaList.Empty : nativeFunctionDeclaration.NativeToken.LeadingTrivia, + nativeFunctionDeclaration.ReturnClause.ReturnType, + nativeFunctionDeclaration.NativeToken.TrailingTrivia), null, - Transpile(nativeFunctionDeclaration.FunctionDeclarator.IdentifierName), + functionNameToken, null, - SyntaxFactory.ParameterList(Transpile(nativeFunctionDeclaration.FunctionDeclarator.ParameterList)), + parameterList.WithoutTrailingTrivia(), default, null, null, - SyntaxFactory.Token(SyntaxKind.SemicolonToken)); + SyntaxFactory.Token( + MergeTrivia(parameterList.GetTrailingTrivia(), Transpile(nativeFunctionDeclaration.ReturnClause.ReturnType.GetLeadingTrivia())), + SyntaxKind.SemicolonToken, + Transpile(nativeFunctionDeclaration.ReturnClause.ReturnType.GetTrailingTrivia()))); } } } \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/NullLiteralExpressionTranspiler.cs b/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/NullLiteralExpressionTranspiler.cs deleted file mode 100644 index cfd8933d..00000000 --- a/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/NullLiteralExpressionTranspiler.cs +++ /dev/null @@ -1,22 +0,0 @@ -// ------------------------------------------------------------------------------ -// -// Licensed under the MIT license. -// See the LICENSE file in the project root for more information. -// -// ------------------------------------------------------------------------------ - -using Microsoft.CodeAnalysis.CSharp; -using Microsoft.CodeAnalysis.CSharp.Syntax; - -using War3Net.CodeAnalysis.Jass.Syntax; - -namespace War3Net.CodeAnalysis.Transpilers -{ - public partial class JassToCSharpTranspiler - { - public ExpressionSyntax Transpile(JassNullLiteralExpressionSyntax nullLiteralExpression) - { - return SyntaxFactory.LiteralExpression(SyntaxKind.NullLiteralExpression); - } - } -} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/OctalLiteralExpressionTranspiler.cs b/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/OctalLiteralExpressionTranspiler.cs deleted file mode 100644 index 907e0108..00000000 --- a/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/OctalLiteralExpressionTranspiler.cs +++ /dev/null @@ -1,22 +0,0 @@ -// ------------------------------------------------------------------------------ -// -// Licensed under the MIT license. -// See the LICENSE file in the project root for more information. -// -// ------------------------------------------------------------------------------ - -using Microsoft.CodeAnalysis.CSharp; -using Microsoft.CodeAnalysis.CSharp.Syntax; - -using War3Net.CodeAnalysis.Jass.Syntax; - -namespace War3Net.CodeAnalysis.Transpilers -{ - public partial class JassToCSharpTranspiler - { - public ExpressionSyntax Transpile(JassOctalLiteralExpressionSyntax octalLiteralExpression) - { - return SyntaxFactory.ParseExpression(octalLiteralExpression.Value.ToString()); - } - } -} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/ParameterListTranspiler.cs b/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/ParameterListTranspiler.cs index 5ac1aeba..521974ce 100644 --- a/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/ParameterListTranspiler.cs +++ b/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/ParameterListTranspiler.cs @@ -11,15 +11,68 @@ using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; +using War3Net.CodeAnalysis.Jass.Extensions; using War3Net.CodeAnalysis.Jass.Syntax; +using War3Net.CodeAnalysis.Transpilers.Extensions; namespace War3Net.CodeAnalysis.Transpilers { public partial class JassToCSharpTranspiler { - public SeparatedSyntaxList Transpile(JassParameterListSyntax parameterList) + public ParameterListSyntax Transpile( + JassParameterListOrEmptyParameterListSyntax parameterListOrEmptyParameterList, + JassReturnClauseSyntax returnClause, + bool discardTakesTokenLeadingTrivia) { - return SyntaxFactory.SeparatedList(parameterList.Parameters.Select(Transpile)); + return parameterListOrEmptyParameterList switch + { + JassParameterListSyntax parameterList => Transpile(parameterList, returnClause, discardTakesTokenLeadingTrivia), + JassEmptyParameterListSyntax emptyParameterList => Transpile(emptyParameterList, returnClause, discardTakesTokenLeadingTrivia), + }; + } + + public ParameterListSyntax Transpile( + JassParameterListSyntax parameterList, + JassReturnClauseSyntax returnClause, + bool discardTakesTokenLeadingTrivia) + { + var openParenToken = Transpile( + discardTakesTokenLeadingTrivia ? JassSyntaxTriviaList.Empty : parameterList.TakesToken.LeadingTrivia, + SyntaxKind.OpenParenToken, + MergeTrivia(parameterList.TakesToken.TrailingTrivia, parameterList.GetLeadingTrivia())); + + var closeParenToken = Transpile( + MergeTrivia(parameterList.GetTrailingTrivia(), returnClause.ReturnsToken.LeadingTrivia), + SyntaxKind.CloseParenToken, + returnClause.ReturnsToken.TrailingTrivia); + + return SyntaxFactory.ParameterList( + openParenToken, + SyntaxFactory.SeparatedList( + parameterList.ParameterList.Items.Select(Transpile), + parameterList.ParameterList.Separators.Select(Transpile)).WithoutTrivia(), + closeParenToken); + } + + public ParameterListSyntax Transpile( + JassEmptyParameterListSyntax emptyParameterList, + JassReturnClauseSyntax returnClause, + bool discardTakesTokenLeadingTrivia) + { + var openParenToken = Transpile( + discardTakesTokenLeadingTrivia ? JassSyntaxTriviaList.Empty : emptyParameterList.TakesToken.LeadingTrivia, + SyntaxKind.OpenParenToken, + MergeTrivia(emptyParameterList.TakesToken.TrailingTrivia, emptyParameterList.NothingToken.LeadingTrivia)); + + var closeParenToken = Transpile( + MergeTrivia(emptyParameterList.NothingToken.TrailingTrivia, returnClause.ReturnsToken.LeadingTrivia), + SyntaxKind.CloseParenToken, + returnClause.ReturnsToken.TrailingTrivia); + + return SyntaxFactory.ParameterList( + openParenToken, + SyntaxFactory.SeparatedList(), + closeParenToken); } } } \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/ParameterTranspiler.cs b/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/ParameterTranspiler.cs index 95dce165..a1bef151 100644 --- a/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/ParameterTranspiler.cs +++ b/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/ParameterTranspiler.cs @@ -16,8 +16,12 @@ public partial class JassToCSharpTranspiler { public ParameterSyntax Transpile(JassParameterSyntax parameter) { - return SyntaxFactory.Parameter(Transpile(parameter.IdentifierName)) - .WithType(Transpile(parameter.Type)); + return SyntaxFactory.Parameter( + default, + SyntaxFactory.TokenList(), + Transpile(parameter.Type), + Transpile(parameter.IdentifierName.Token), + null); } } } \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/ParenthesizedExpressionTranspiler.cs b/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/ParenthesizedExpressionTranspiler.cs index b304d941..47b35100 100644 --- a/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/ParenthesizedExpressionTranspiler.cs +++ b/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/ParenthesizedExpressionTranspiler.cs @@ -16,7 +16,10 @@ public partial class JassToCSharpTranspiler { public ExpressionSyntax Transpile(JassParenthesizedExpressionSyntax parenthesizedExpression) { - return SyntaxFactory.ParenthesizedExpression(Transpile(parenthesizedExpression.Expression)); + return SyntaxFactory.ParenthesizedExpression( + Transpile(SyntaxKind.OpenParenToken, parenthesizedExpression.OpenParenToken), + Transpile(parenthesizedExpression.Expression), + Transpile(SyntaxKind.CloseParenToken, parenthesizedExpression.CloseParenToken)); } } } \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/PredefinedTypeTranspiler.cs b/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/PredefinedTypeTranspiler.cs new file mode 100644 index 00000000..5278f619 --- /dev/null +++ b/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/PredefinedTypeTranspiler.cs @@ -0,0 +1,107 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; + +using War3Net.CodeAnalysis.Jass; +using War3Net.CodeAnalysis.Jass.Syntax; +using War3Net.CodeAnalysis.Transpilers.Extensions; + +namespace War3Net.CodeAnalysis.Transpilers +{ + public partial class JassToCSharpTranspiler + { + private const string ActionTypeName = "System.Action"; + + public TypeSyntax Transpile( + JassPredefinedTypeSyntax type) + { + return type.Token.SyntaxKind == JassSyntaxKind.CodeKeyword + ? SyntaxFactory.IdentifierName(Transpile(ActionTypeName, type.Token)) + : SyntaxFactory.PredefinedType(Transpile(TranspileTypeKeyword(type.Token.SyntaxKind), type.Token)); + } + + public TypeSyntax Transpile( + JassSyntaxTriviaList leadingTrivia, + JassPredefinedTypeSyntax type) + { + return type.Token.SyntaxKind == JassSyntaxKind.CodeKeyword + ? SyntaxFactory.IdentifierName(Transpile(leadingTrivia, ActionTypeName, type.Token.TrailingTrivia)) + : SyntaxFactory.PredefinedType(Transpile(leadingTrivia, TranspileTypeKeyword(type.Token.SyntaxKind), type.Token.TrailingTrivia)); + } + + public TypeSyntax Transpile( + JassPredefinedTypeSyntax type, + JassSyntaxTriviaList trailingTrivia) + { + return type.Token.SyntaxKind == JassSyntaxKind.CodeKeyword + ? SyntaxFactory.IdentifierName(Transpile(type.Token.LeadingTrivia, ActionTypeName, trailingTrivia)) + : SyntaxFactory.PredefinedType(Transpile(type.Token.LeadingTrivia, TranspileTypeKeyword(type.Token.SyntaxKind), trailingTrivia)); + } + + public TypeSyntax Transpile( + JassSyntaxTriviaList leadingTrivia, + JassPredefinedTypeSyntax type, + JassSyntaxTriviaList trailingTrivia) + { + return type.Token.SyntaxKind == JassSyntaxKind.CodeKeyword + ? SyntaxFactory.IdentifierName(Transpile(leadingTrivia, ActionTypeName, trailingTrivia)) + : SyntaxFactory.PredefinedType(Transpile(leadingTrivia, TranspileTypeKeyword(type.Token.SyntaxKind), trailingTrivia)); + } + + public TypeSyntax TranspileAligned( + JassPredefinedTypeSyntax type, + bool isArray) + { + return Transpile(type) + .WithAlignedWhitespace(GetWhitespaceDiff(type.Token.SyntaxKind, isArray)); + } + + public TypeSyntax TranspileAligned( + JassSyntaxTriviaList leadingTrivia, + JassPredefinedTypeSyntax type, + bool isArray) + { + return Transpile(leadingTrivia, type) + .WithAlignedWhitespace(GetWhitespaceDiff(type.Token.SyntaxKind, isArray)); + } + + public TypeSyntax TranspileAligned( + JassPredefinedTypeSyntax type, + JassSyntaxTriviaList trailingTrivia, + bool isArray) + { + return Transpile(type, trailingTrivia) + .WithAlignedWhitespace(GetWhitespaceDiff(type.Token.SyntaxKind, isArray)); + } + + public TypeSyntax TranspileAligned( + JassSyntaxTriviaList leadingTrivia, + JassPredefinedTypeSyntax type, + JassSyntaxTriviaList trailingTrivia, + bool isArray) + { + return Transpile(leadingTrivia, type, trailingTrivia) + .WithAlignedWhitespace(GetWhitespaceDiff(type.Token.SyntaxKind, isArray)); + } + + private int GetWhitespaceDiff(JassSyntaxKind keyword, bool isArray) + { + return (isArray ? ArrayWhitespaceDiff : 0) + keyword switch + { + JassSyntaxKind.BooleanKeyword => 3, + JassSyntaxKind.CodeKeyword => -9, + JassSyntaxKind.HandleKeyword => 0, + JassSyntaxKind.IntegerKeyword => 4, + JassSyntaxKind.NothingKeyword => 3, + JassSyntaxKind.RealKeyword => -1, + JassSyntaxKind.StringKeyword => 0, + }; + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/RealLiteralExpressionTranspiler.cs b/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/RealLiteralExpressionTranspiler.cs deleted file mode 100644 index bb6f472e..00000000 --- a/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/RealLiteralExpressionTranspiler.cs +++ /dev/null @@ -1,25 +0,0 @@ -// ------------------------------------------------------------------------------ -// -// Licensed under the MIT license. -// See the LICENSE file in the project root for more information. -// -// ------------------------------------------------------------------------------ - -using Microsoft.CodeAnalysis.CSharp; -using Microsoft.CodeAnalysis.CSharp.Syntax; - -using War3Net.CodeAnalysis.Jass; -using War3Net.CodeAnalysis.Jass.Syntax; - -namespace War3Net.CodeAnalysis.Transpilers -{ - public partial class JassToCSharpTranspiler - { - public ExpressionSyntax Transpile(JassRealLiteralExpressionSyntax realLiteralExpression) - { - return SyntaxFactory.ParseExpression(string.IsNullOrEmpty(realLiteralExpression.FracPart) - ? $"{realLiteralExpression.IntPart}f" - : $"{realLiteralExpression.IntPart}{JassSymbol.FullStop}{realLiteralExpression.FracPart}f"); - } - } -} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/ReturnStatementTranspiler.cs b/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/ReturnStatementTranspiler.cs index e760e034..2cb21006 100644 --- a/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/ReturnStatementTranspiler.cs +++ b/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/ReturnStatementTranspiler.cs @@ -5,10 +5,12 @@ // // ------------------------------------------------------------------------------ +using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; using War3Net.CodeAnalysis.Jass.Syntax; +using War3Net.CodeAnalysis.Transpilers.Extensions; namespace War3Net.CodeAnalysis.Transpilers { @@ -16,9 +18,31 @@ public partial class JassToCSharpTranspiler { public StatementSyntax Transpile(JassReturnStatementSyntax returnStatement) { - return returnStatement.Value is null - ? SyntaxFactory.ReturnStatement() - : SyntaxFactory.ReturnStatement(Transpile(returnStatement.Value)); + var returnKeyword = Transpile(SyntaxKind.ReturnKeyword, returnStatement.ReturnToken); + + ExpressionSyntax? expression; + SyntaxTriviaList trailingTrivia; + + if (returnStatement.Value is null) + { + expression = null; + trailingTrivia = returnKeyword.TrailingTrivia; + returnKeyword = returnKeyword.WithoutTrailingTrivia(); + } + else + { + expression = Transpile(returnStatement.Value); + trailingTrivia = expression.GetTrailingTrivia(); + expression = expression.WithoutTrailingTrivia(); + } + + return SyntaxFactory.ReturnStatement( + returnKeyword, + expression, + SyntaxFactory.Token( + SyntaxTriviaList.Empty, + SyntaxKind.SemicolonToken, + trailingTrivia)); } } } \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/SetStatementTranspiler.cs b/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/SetStatementTranspiler.cs index c0e605ab..11dc264e 100644 --- a/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/SetStatementTranspiler.cs +++ b/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/SetStatementTranspiler.cs @@ -5,9 +5,11 @@ // // ------------------------------------------------------------------------------ +using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; +using War3Net.CodeAnalysis.Jass.Extensions; using War3Net.CodeAnalysis.Jass.Syntax; namespace War3Net.CodeAnalysis.Transpilers @@ -16,22 +18,28 @@ public partial class JassToCSharpTranspiler { public StatementSyntax Transpile(JassSetStatementSyntax setStatement) { - if (setStatement.Indexer is null) - { - return SyntaxFactory.ExpressionStatement(SyntaxFactory.AssignmentExpression( - SyntaxKind.SimpleAssignmentExpression, - SyntaxFactory.IdentifierName(Transpile(setStatement.IdentifierName)), - Transpile(setStatement.Value.Expression))); - } - else - { - return SyntaxFactory.ExpressionStatement(SyntaxFactory.AssignmentExpression( - SyntaxKind.SimpleAssignmentExpression, - SyntaxFactory.ElementAccessExpression( - SyntaxFactory.IdentifierName(Transpile(setStatement.IdentifierName)), - SyntaxFactory.BracketedArgumentList(SyntaxFactory.SingletonSeparatedList(SyntaxFactory.Argument(Transpile(setStatement.Indexer))))), - Transpile(setStatement.Value.Expression))); - } + var leadingTrivia = MergeTrivia(setStatement.SetToken, setStatement.IdentifierName.GetLeadingTrivia()); + + ExpressionSyntax left = setStatement.ElementAccessClause is null + ? Transpile(leadingTrivia, setStatement.IdentifierName) + : SyntaxFactory.ElementAccessExpression( + Transpile(leadingTrivia, setStatement.IdentifierName), + Transpile(setStatement.ElementAccessClause)); + + var assignmentExpression = SyntaxFactory.AssignmentExpression( + SyntaxKind.SimpleAssignmentExpression, + left, + Transpile(SyntaxKind.EqualsToken, setStatement.Value.EqualsToken), + Transpile(setStatement.Value.Expression)); + + var trailingTrivia = assignmentExpression.GetTrailingTrivia(); + + return SyntaxFactory.ExpressionStatement( + assignmentExpression.WithoutTrailingTrivia(), + SyntaxFactory.Token( + SyntaxTriviaList.Empty, + SyntaxKind.SemicolonToken, + trailingTrivia)); } } } \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/StatementListTranspiler.cs b/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/StatementListTranspiler.cs deleted file mode 100644 index ab8ee76b..00000000 --- a/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/StatementListTranspiler.cs +++ /dev/null @@ -1,24 +0,0 @@ -// ------------------------------------------------------------------------------ -// -// Licensed under the MIT license. -// See the LICENSE file in the project root for more information. -// -// ------------------------------------------------------------------------------ - -using System.Collections.Generic; -using System.Linq; - -using Microsoft.CodeAnalysis.CSharp.Syntax; - -using War3Net.CodeAnalysis.Jass.Syntax; - -namespace War3Net.CodeAnalysis.Transpilers -{ - public partial class JassToCSharpTranspiler - { - public IEnumerable Transpile(JassStatementListSyntax statementList) - { - return statementList.Statements.Select(Transpile); - } - } -} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/StatementTranspiler.cs b/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/StatementTranspiler.cs index cf219ec4..e4ee6cea 100644 --- a/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/StatementTranspiler.cs +++ b/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/StatementTranspiler.cs @@ -13,12 +13,10 @@ namespace War3Net.CodeAnalysis.Transpilers { public partial class JassToCSharpTranspiler { - public StatementSyntax Transpile(IStatementSyntax statement) + public StatementSyntax Transpile(JassStatementSyntax statement) { return statement switch { - JassEmptySyntax empty => Transpile(empty), - JassCommentSyntax comment => Transpile(comment), JassLocalVariableDeclarationStatementSyntax localVariableDeclarationStatement => Transpile(localVariableDeclarationStatement), JassSetStatementSyntax setStatement => Transpile(setStatement), JassCallStatementSyntax callStatement => Transpile(callStatement), diff --git a/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/StringLiteralExpressionTranspiler.cs b/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/StringLiteralExpressionTranspiler.cs deleted file mode 100644 index fa5aac87..00000000 --- a/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/StringLiteralExpressionTranspiler.cs +++ /dev/null @@ -1,23 +0,0 @@ -// ------------------------------------------------------------------------------ -// -// Licensed under the MIT license. -// See the LICENSE file in the project root for more information. -// -// ------------------------------------------------------------------------------ - -using Microsoft.CodeAnalysis.CSharp; -using Microsoft.CodeAnalysis.CSharp.Syntax; - -using War3Net.CodeAnalysis.Jass; -using War3Net.CodeAnalysis.Jass.Syntax; - -namespace War3Net.CodeAnalysis.Transpilers -{ - public partial class JassToCSharpTranspiler - { - public ExpressionSyntax Transpile(JassStringLiteralExpressionSyntax stringLiteralExpression) - { - return SyntaxFactory.ParseExpression($"\"{stringLiteralExpression.Value.Replace($"{JassSymbol.CarriageReturn}", @"\r").Replace($"{JassSymbol.LineFeed}", @"\n")}\""); - } - } -} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/SyntaxKindTranspiler.cs b/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/SyntaxKindTranspiler.cs new file mode 100644 index 00000000..966e19a2 --- /dev/null +++ b/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/SyntaxKindTranspiler.cs @@ -0,0 +1,87 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using Microsoft.CodeAnalysis.CSharp; + +using War3Net.CodeAnalysis.Jass; + +namespace War3Net.CodeAnalysis.Transpilers +{ + public partial class JassToCSharpTranspiler + { + public SyntaxKind TranspileBinaryExpressionKind(JassSyntaxKind expressionKind) + { + return expressionKind switch + { + JassSyntaxKind.AddExpression => SyntaxKind.AddExpression, + JassSyntaxKind.SubtractExpression => SyntaxKind.SubtractExpression, + JassSyntaxKind.MultiplyExpression => SyntaxKind.MultiplyExpression, + JassSyntaxKind.DivideExpression => SyntaxKind.DivideExpression, + JassSyntaxKind.GreaterThanExpression => SyntaxKind.GreaterThanExpression, + JassSyntaxKind.LessThanExpression => SyntaxKind.LessThanExpression, + JassSyntaxKind.EqualsExpression => SyntaxKind.EqualsExpression, + JassSyntaxKind.NotEqualsExpression => SyntaxKind.NotEqualsExpression, + JassSyntaxKind.GreaterThanOrEqualExpression => SyntaxKind.GreaterThanOrEqualExpression, + JassSyntaxKind.LessThanOrEqualExpression => SyntaxKind.LessThanOrEqualExpression, + JassSyntaxKind.LogicalAndExpression => SyntaxKind.LogicalAndExpression, + JassSyntaxKind.LogicalOrExpression => SyntaxKind.LogicalOrExpression, + }; + } + + public SyntaxKind TranspileBinaryOperatorKind(JassSyntaxKind operatorKind) + { + return operatorKind switch + { + JassSyntaxKind.PlusToken => SyntaxKind.PlusToken, + JassSyntaxKind.MinusToken => SyntaxKind.MinusToken, + JassSyntaxKind.AsteriskToken => SyntaxKind.AsteriskToken, + JassSyntaxKind.SlashToken => SyntaxKind.SlashToken, + JassSyntaxKind.GreaterThanToken => SyntaxKind.GreaterThanToken, + JassSyntaxKind.LessThanToken => SyntaxKind.LessThanToken, + JassSyntaxKind.EqualsEqualsToken => SyntaxKind.EqualsEqualsToken, + JassSyntaxKind.ExclamationEqualsToken => SyntaxKind.ExclamationEqualsToken, + JassSyntaxKind.GreaterThanEqualsToken => SyntaxKind.GreaterThanEqualsToken, + JassSyntaxKind.LessThanEqualsToken => SyntaxKind.LessThanEqualsToken, + JassSyntaxKind.AndKeyword => SyntaxKind.AmpersandAmpersandToken, + JassSyntaxKind.OrKeyword => SyntaxKind.BarBarToken, + }; + } + + public SyntaxKind TranspileTypeKeyword(JassSyntaxKind keyword) + { + return keyword switch + { + JassSyntaxKind.BooleanKeyword => SyntaxKind.BoolKeyword, + JassSyntaxKind.HandleKeyword => SyntaxKind.ObjectKeyword, + JassSyntaxKind.IntegerKeyword => SyntaxKind.IntKeyword, + JassSyntaxKind.NothingKeyword => SyntaxKind.VoidKeyword, + JassSyntaxKind.RealKeyword => SyntaxKind.FloatKeyword, + JassSyntaxKind.StringKeyword => SyntaxKind.StringKeyword, + }; + } + + public SyntaxKind TranspileUnaryExpressionKind(JassSyntaxKind expressionKind) + { + return expressionKind switch + { + JassSyntaxKind.UnaryPlusExpression => SyntaxKind.UnaryPlusExpression, + JassSyntaxKind.UnaryMinusExpression => SyntaxKind.UnaryMinusExpression, + JassSyntaxKind.LogicalNotExpression => SyntaxKind.LogicalNotExpression, + }; + } + + public SyntaxKind TranspileUnaryOperatorKind(JassSyntaxKind operatorKind) + { + return operatorKind switch + { + JassSyntaxKind.PlusToken => SyntaxKind.PlusToken, + JassSyntaxKind.MinusToken => SyntaxKind.MinusToken, + JassSyntaxKind.NotKeyword => SyntaxKind.ExclamationToken, + }; + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/SyntaxTokenTranspiler.cs b/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/SyntaxTokenTranspiler.cs new file mode 100644 index 00000000..b2915db8 --- /dev/null +++ b/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/SyntaxTokenTranspiler.cs @@ -0,0 +1,204 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using System; +using System.Collections.Generic; +using System.Linq; + +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; + +using War3Net.CodeAnalysis.Jass.Syntax; + +namespace War3Net.CodeAnalysis.Transpilers +{ + public partial class JassToCSharpTranspiler + { + private const string AntiReservedKeywordConflictPrefix = "@"; + + private static readonly Lazy> _reservedKeywords = new Lazy>(() => GetReservedKeywords().ToHashSet(StringComparer.Ordinal)); + + public string TranspileText( + string tokenText) + { + return _reservedKeywords.Value.Contains(tokenText) + ? $"{AntiReservedKeywordConflictPrefix}{tokenText}" + : tokenText; + } + + public string TranspileTextAligned( + string tokenText, + out bool prefixAdded) + { + prefixAdded = _reservedKeywords.Value.Contains(tokenText); + return prefixAdded + ? $"{AntiReservedKeywordConflictPrefix}{tokenText}" + : tokenText; + } + + public SyntaxToken Transpile( + JassSyntaxToken token) + { + return Transpile(token.LeadingTrivia, token, token.TrailingTrivia); + } + + public SyntaxToken Transpile( + JassSyntaxToken token, + JassSyntaxToken triviaFromToken) + { + return Transpile(triviaFromToken.LeadingTrivia, token, triviaFromToken.TrailingTrivia); + } + + public SyntaxToken Transpile( + JassSyntaxTriviaList leadingTrivia, + JassSyntaxToken token) + { + return Transpile(leadingTrivia, token, token.TrailingTrivia); + } + + public SyntaxToken Transpile( + JassSyntaxToken token, + JassSyntaxTriviaList trailingTrivia) + { + return Transpile(token.LeadingTrivia, token, trailingTrivia); + } + + public SyntaxToken Transpile( + JassSyntaxTriviaList leadingTrivia, + JassSyntaxToken token, + JassSyntaxTriviaList trailingTrivia) + { + return SyntaxFactory.Identifier( + Transpile(leadingTrivia), + SyntaxKind.IdentifierToken, + TranspileText(token.Text), + token.Text, + Transpile(trailingTrivia)); + } + + public SyntaxToken TranspileAligned( + JassSyntaxToken token, + out bool prefixAdded) + { + return TranspileAligned(token.LeadingTrivia, token, token.TrailingTrivia, out prefixAdded); + } + + public SyntaxToken TranspileAligned( + JassSyntaxTriviaList leadingTrivia, + JassSyntaxToken token, + out bool prefixAdded) + { + return TranspileAligned(leadingTrivia, token, token.TrailingTrivia, out prefixAdded); + } + + public SyntaxToken TranspileAligned( + JassSyntaxToken token, + JassSyntaxTriviaList trailingTrivia, + out bool prefixAdded) + { + return TranspileAligned(token.LeadingTrivia, token, trailingTrivia, out prefixAdded); + } + + public SyntaxToken TranspileAligned( + JassSyntaxTriviaList leadingTrivia, + JassSyntaxToken token, + JassSyntaxTriviaList trailingTrivia, + out bool prefixAdded) + { + return SyntaxFactory.Identifier( + Transpile(leadingTrivia), + SyntaxKind.IdentifierToken, + TranspileTextAligned(token.Text, out prefixAdded), + token.Text, + Transpile(trailingTrivia)); + } + + public SyntaxToken Transpile(string text) + { + return SyntaxFactory.Identifier( + SyntaxTriviaList.Empty, + SyntaxKind.IdentifierToken, + TranspileText(text), + text, + SyntaxTriviaList.Empty); + } + + public SyntaxToken Transpile( + string text, + JassSyntaxToken triviaFromToken) + { + return SyntaxFactory.Identifier( + Transpile(triviaFromToken.LeadingTrivia), + SyntaxKind.IdentifierToken, + TranspileText(text), + text, + Transpile(triviaFromToken.TrailingTrivia)); + } + + public SyntaxToken Transpile( + JassSyntaxTriviaList leadingTrivia, + string text, + JassSyntaxTriviaList trailingTrivia) + { + return SyntaxFactory.Identifier( + Transpile(leadingTrivia), + SyntaxKind.IdentifierToken, + TranspileText(text), + text, + Transpile(trailingTrivia)); + } + + public SyntaxToken Transpile( + SyntaxKind syntaxKind, + JassSyntaxToken triviaFromToken) + { + return Transpile(triviaFromToken.LeadingTrivia, syntaxKind, triviaFromToken.TrailingTrivia); + } + + public SyntaxToken Transpile( + JassSyntaxTriviaList leadingTrivia, + SyntaxKind syntaxKind) + { + return SyntaxFactory.Token( + Transpile(leadingTrivia), + syntaxKind, + SyntaxTriviaList.Empty); + } + + public SyntaxToken Transpile( + SyntaxKind syntaxKind, + JassSyntaxTriviaList trailingTrivia) + { + return SyntaxFactory.Token( + SyntaxTriviaList.Empty, + syntaxKind, + Transpile(trailingTrivia)); + } + + public SyntaxToken Transpile( + JassSyntaxTriviaList leadingTrivia, + SyntaxKind syntaxKind, + JassSyntaxTriviaList trailingTrivia) + { + return SyntaxFactory.Token( + Transpile(leadingTrivia), + syntaxKind, + Transpile(trailingTrivia)); + } + + private static IEnumerable GetReservedKeywords() + { + foreach (SyntaxKind syntaxKind in Enum.GetValues(typeof(SyntaxKind))) + { + if (SyntaxFacts.IsReservedKeyword(syntaxKind)) + { + yield return SyntaxFactory.Token(syntaxKind).Text; + } + } + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/SyntaxTriviaTranspiler.cs b/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/SyntaxTriviaTranspiler.cs new file mode 100644 index 00000000..4ee14918 --- /dev/null +++ b/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/SyntaxTriviaTranspiler.cs @@ -0,0 +1,40 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using System.Linq; + +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; + +using War3Net.CodeAnalysis.Jass; +using War3Net.CodeAnalysis.Jass.Syntax; + +namespace War3Net.CodeAnalysis.Transpilers +{ + public partial class JassToCSharpTranspiler + { + public SyntaxTriviaList Transpile(JassSyntaxTriviaList triviaList) + { + if (triviaList.Trivia.Length == 0) + { + return SyntaxTriviaList.Empty; + } + + return SyntaxFactory.TriviaList(triviaList.Trivia.Select(Transpile)); + } + + public SyntaxTrivia Transpile(JassSyntaxTrivia trivia) + { + return trivia.SyntaxKind switch + { + JassSyntaxKind.NewLineTrivia => SyntaxFactory.EndOfLine(trivia.Text), + JassSyntaxKind.WhitespaceTrivia => SyntaxFactory.Whitespace(trivia.Text), + JassSyntaxKind.SingleLineCommentTrivia => SyntaxFactory.Comment(trivia.Text), + }; + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/TypeDeclarationTranspiler.cs b/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/TypeDeclarationTranspiler.cs index 73bebdf4..3d51ce41 100644 --- a/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/TypeDeclarationTranspiler.cs +++ b/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/TypeDeclarationTranspiler.cs @@ -9,7 +9,9 @@ using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; +using War3Net.CodeAnalysis.Jass.Extensions; using War3Net.CodeAnalysis.Jass.Syntax; +using War3Net.CodeAnalysis.Transpilers.Extensions; namespace War3Net.CodeAnalysis.Transpilers { @@ -17,24 +19,47 @@ public partial class JassToCSharpTranspiler { public MemberDeclarationSyntax Transpile(JassTypeDeclarationSyntax typeDeclaration) { - var identifier = Transpile(typeDeclaration.IdentifierName); - var baseList = SyntaxFactory.BaseList(SyntaxFactory.SingletonSeparatedList(SyntaxFactory.SimpleBaseType(Transpile(typeDeclaration.BaseType)))); + var identifier = Transpile(typeDeclaration.IdentifierName.Token); + + var baseList = SyntaxFactory.BaseList( + Transpile(SyntaxKind.ColonToken, typeDeclaration.ExtendsToken), + SyntaxFactory.SingletonSeparatedList( + SyntaxFactory.SimpleBaseType(Transpile(typeDeclaration.BaseType)))); + + var indentationString = typeDeclaration.TypeToken.LeadingTrivia.GetIndentationString(); return SyntaxFactory.ClassDeclaration( default, - new SyntaxTokenList(SyntaxFactory.Token(SyntaxKind.PublicKeyword)), + new SyntaxTokenList(TokenWithSpace(typeDeclaration.TypeToken.LeadingTrivia, SyntaxKind.PublicKeyword)), + Transpile(SyntaxKind.ClassKeyword, typeDeclaration.TypeToken.TrailingTrivia), identifier, null, baseList, default, - SyntaxFactory.SingletonList( - (MemberDeclarationSyntax)SyntaxFactory.ConstructorDeclaration( + SyntaxFactory.Token( + SyntaxTriviaList.Create(SyntaxFactory.ElasticWhitespace(indentationString)), + SyntaxKind.OpenBraceToken, + SyntaxTriviaList.Create(SyntaxFactory.ElasticCarriageReturnLineFeed)), + SyntaxFactory.SingletonList( + SyntaxFactory.ConstructorDeclaration( default, - new SyntaxTokenList(SyntaxFactory.Token(SyntaxKind.InternalKeyword)), - identifier, - SyntaxFactory.ParameterList(), + new SyntaxTokenList( + SyntaxFactory.Token( + SyntaxTriviaList.Create(SyntaxFactory.ElasticWhitespace(indentationString)), + SyntaxKind.InternalKeyword, + SyntaxTriviaList.Create(SyntaxFactory.ElasticSpace))), + identifier.WithoutTrivia(), + SyntaxFactory.ParameterList( + SyntaxFactory.Token(SyntaxKind.OpenParenToken), + SyntaxFactory.SeparatedList(), + SyntaxFactory.Token(SyntaxKind.CloseParenToken).WithSpace()), null, - SyntaxFactory.Block()))); + SyntaxFactory.Block().WithTrailingTrivia(SyntaxFactory.ElasticCarriageReturnLineFeed))), + SyntaxFactory.Token( + SyntaxTriviaList.Create(SyntaxFactory.ElasticWhitespace(indentationString)), + SyntaxKind.CloseBraceToken, + SyntaxTriviaList.Create(SyntaxFactory.ElasticCarriageReturnLineFeed)), + default); } } } \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/TypeTranspiler.cs b/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/TypeTranspiler.cs index 4270efcc..9cf48dd3 100644 --- a/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/TypeTranspiler.cs +++ b/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/TypeTranspiler.cs @@ -5,9 +5,6 @@ // // ------------------------------------------------------------------------------ -using System; - -using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; using War3Net.CodeAnalysis.Jass.Syntax; @@ -16,44 +13,96 @@ namespace War3Net.CodeAnalysis.Transpilers { public partial class JassToCSharpTranspiler { - public TypeSyntax Transpile(JassTypeSyntax type) + public TypeSyntax Transpile( + JassTypeSyntax type) { - if (type.Equals(JassTypeSyntax.Boolean)) + return type switch { - return SyntaxFactory.PredefinedType(SyntaxFactory.Token(SyntaxKind.BoolKeyword)); - } + JassIdentifierNameSyntax identifierName => Transpile(identifierName), + JassPredefinedTypeSyntax predefinedType => Transpile(predefinedType), + }; + } - if (type.Equals(JassTypeSyntax.Code)) + public TypeSyntax Transpile( + JassSyntaxTriviaList leadingTrivia, + JassTypeSyntax type) + { + return type switch { - return SyntaxFactory.ParseTypeName(typeof(Action).FullName!); - } + JassIdentifierNameSyntax identifierName => Transpile(leadingTrivia, identifierName), + JassPredefinedTypeSyntax predefinedType => Transpile(leadingTrivia, predefinedType), + }; + } - if (type.Equals(JassTypeSyntax.Handle)) + public TypeSyntax Transpile( + JassTypeSyntax type, + JassSyntaxTriviaList trailingTrivia) + { + return type switch { - return SyntaxFactory.PredefinedType(SyntaxFactory.Token(SyntaxKind.ObjectKeyword)); - } + JassIdentifierNameSyntax identifierName => Transpile(identifierName, trailingTrivia), + JassPredefinedTypeSyntax predefinedType => Transpile(predefinedType, trailingTrivia), + }; + } - if (type.Equals(JassTypeSyntax.Integer)) + public TypeSyntax Transpile( + JassSyntaxTriviaList leadingTrivia, + JassTypeSyntax type, + JassSyntaxTriviaList trailingTrivia) + { + return type switch { - return SyntaxFactory.PredefinedType(SyntaxFactory.Token(SyntaxKind.IntKeyword)); - } + JassIdentifierNameSyntax identifierName => Transpile(leadingTrivia, identifierName, trailingTrivia), + JassPredefinedTypeSyntax predefinedType => Transpile(leadingTrivia, predefinedType, trailingTrivia), + }; + } - if (type.Equals(JassTypeSyntax.Nothing)) + public TypeSyntax TranspileAligned( + JassTypeSyntax type, + bool isArray) + { + return type switch { - return SyntaxFactory.PredefinedType(SyntaxFactory.Token(SyntaxKind.VoidKeyword)); - } + JassIdentifierNameSyntax identifierName => TranspileAligned(identifierName, isArray), + JassPredefinedTypeSyntax predefinedType => TranspileAligned(predefinedType, isArray), + }; + } - if (type.Equals(JassTypeSyntax.Real)) + public TypeSyntax TranspileAligned( + JassSyntaxTriviaList leadingTrivia, + JassTypeSyntax type, + bool isArray) + { + return type switch { - return SyntaxFactory.PredefinedType(SyntaxFactory.Token(SyntaxKind.FloatKeyword)); - } + JassIdentifierNameSyntax identifierName => TranspileAligned(leadingTrivia, identifierName, isArray), + JassPredefinedTypeSyntax predefinedType => TranspileAligned(leadingTrivia, predefinedType, isArray), + }; + } - if (type.Equals(JassTypeSyntax.String)) + public TypeSyntax TranspileAligned( + JassTypeSyntax type, + JassSyntaxTriviaList trailingTrivia, + bool isArray) + { + return type switch { - return SyntaxFactory.PredefinedType(SyntaxFactory.Token(SyntaxKind.StringKeyword)); - } + JassIdentifierNameSyntax identifierName => TranspileAligned(identifierName, trailingTrivia, isArray), + JassPredefinedTypeSyntax predefinedType => TranspileAligned(predefinedType, trailingTrivia, isArray), + }; + } - return SyntaxFactory.ParseTypeName(Transpile(type.TypeName).Text); + public TypeSyntax TranspileAligned( + JassSyntaxTriviaList leadingTrivia, + JassTypeSyntax type, + JassSyntaxTriviaList trailingTrivia, + bool isArray) + { + return type switch + { + JassIdentifierNameSyntax identifierName => TranspileAligned(leadingTrivia, identifierName, trailingTrivia, isArray), + JassPredefinedTypeSyntax predefinedType => TranspileAligned(leadingTrivia, predefinedType, trailingTrivia, isArray), + }; } } } \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/UnaryExpressionTranspiler.cs b/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/UnaryExpressionTranspiler.cs index ff4fc253..b20eb6ca 100644 --- a/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/UnaryExpressionTranspiler.cs +++ b/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/UnaryExpressionTranspiler.cs @@ -17,7 +17,10 @@ public partial class JassToCSharpTranspiler public ExpressionSyntax Transpile(JassUnaryExpressionSyntax unaryExpression) { return SyntaxFactory.PrefixUnaryExpression( - Transpile(unaryExpression.Operator), + TranspileUnaryExpressionKind(unaryExpression.SyntaxKind), + Transpile( + TranspileUnaryOperatorKind(unaryExpression.OperatorToken.SyntaxKind), + unaryExpression.OperatorToken), Transpile(unaryExpression.Expression)); } } diff --git a/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/UnaryOperatorTranspiler.cs b/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/UnaryOperatorTranspiler.cs deleted file mode 100644 index b28cd0e8..00000000 --- a/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/UnaryOperatorTranspiler.cs +++ /dev/null @@ -1,26 +0,0 @@ -// ------------------------------------------------------------------------------ -// -// Licensed under the MIT license. -// See the LICENSE file in the project root for more information. -// -// ------------------------------------------------------------------------------ - -using Microsoft.CodeAnalysis.CSharp; - -using War3Net.CodeAnalysis.Jass.Syntax; - -namespace War3Net.CodeAnalysis.Transpilers -{ - public partial class JassToCSharpTranspiler - { - public SyntaxKind Transpile(UnaryOperatorType unaryOperator) - { - return unaryOperator switch - { - UnaryOperatorType.Plus => SyntaxKind.UnaryPlusExpression, - UnaryOperatorType.Minus => SyntaxKind.UnaryPlusExpression, - UnaryOperatorType.Not => SyntaxKind.LogicalNotExpression, - }; - } - } -} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/VariableDeclaratorTranspiler.cs b/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/VariableDeclaratorTranspiler.cs index 2d615e2f..95a48b96 100644 --- a/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/VariableDeclaratorTranspiler.cs +++ b/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/VariableDeclaratorTranspiler.cs @@ -5,45 +5,86 @@ // // ------------------------------------------------------------------------------ +using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; +using War3Net.CodeAnalysis.Jass.Extensions; using War3Net.CodeAnalysis.Jass.Syntax; +using War3Net.CodeAnalysis.Transpilers.Extensions; namespace War3Net.CodeAnalysis.Transpilers { public partial class JassToCSharpTranspiler { - public VariableDeclarationSyntax Transpile(IVariableDeclaratorSyntax declarator) + public VariableDeclarationSyntax Transpile( + JassVariableDeclaratorSyntax variableDeclarator, + bool isGlobalDeclaration) { - return declarator switch - { - JassArrayDeclaratorSyntax arrayDeclarator => Transpile(arrayDeclarator), - JassVariableDeclaratorSyntax variableDeclarator => Transpile(variableDeclarator), - }; + return Transpile( + variableDeclarator.GetLeadingTrivia(), + variableDeclarator, + variableDeclarator.GetTrailingTrivia(), + isGlobalDeclaration); + } + + private VariableDeclarationSyntax Transpile( + JassSyntaxTriviaList leadingTrivia, + JassVariableDeclaratorSyntax variableDeclarator, + bool isGlobalDeclaration) + { + return Transpile( + leadingTrivia, + variableDeclarator, + variableDeclarator.GetTrailingTrivia(), + isGlobalDeclaration); + } + + private VariableDeclarationSyntax Transpile( + JassVariableDeclaratorSyntax variableDeclarator, + JassSyntaxTriviaList trailingTrivia, + bool isGlobalDeclaration) + { + return Transpile( + variableDeclarator.GetLeadingTrivia(), + variableDeclarator, + trailingTrivia, + isGlobalDeclaration); } - public VariableDeclarationSyntax Transpile(JassVariableDeclaratorSyntax variableDeclarator) + private VariableDeclarationSyntax Transpile( + JassSyntaxTriviaList leadingTrivia, + JassVariableDeclaratorSyntax variableDeclarator, + JassSyntaxTriviaList trailingTrivia, + bool isGlobalDeclaration) { - var type = Transpile(variableDeclarator.Type); + VariableDeclaratorSyntax declarator; + if (variableDeclarator.Value is null) { - return SyntaxFactory.VariableDeclaration( - type, - SyntaxFactory.SingletonSeparatedList(SyntaxFactory.VariableDeclarator( - Transpile(variableDeclarator.IdentifierName), - null, - SyntaxFactory.EqualsValueClause(SyntaxFactory.DefaultExpression(type))))); + declarator = SyntaxFactory.VariableDeclarator( + Transpile(variableDeclarator.IdentifierName.Token).WithSpace(), + null, + SyntaxFactory.EqualsValueClause( + TokenWithSpace(SyntaxKind.EqualsToken), + SyntaxFactory.LiteralExpression(SyntaxKind.DefaultLiteralExpression))); } else { - return SyntaxFactory.VariableDeclaration( - type, - SyntaxFactory.SingletonSeparatedList(SyntaxFactory.VariableDeclarator( - Transpile(variableDeclarator.IdentifierName), - null, - Transpile(variableDeclarator.Value)))); + declarator = SyntaxFactory.VariableDeclarator( + Transpile(variableDeclarator.IdentifierName.Token), + null, + Transpile(variableDeclarator.Value)); } + + var typeNode = isGlobalDeclaration + ? TranspileAligned(leadingTrivia, variableDeclarator.Type, isArray: false) + : Transpile(leadingTrivia, variableDeclarator.Type); + + return SyntaxFactory.VariableDeclaration( + typeNode, + SyntaxFactory.SingletonSeparatedList( + declarator.WithTrailingTrivia(Transpile(trailingTrivia)))); } } } \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/VariableOrArrayDeclaratorTranspiler.cs b/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/VariableOrArrayDeclaratorTranspiler.cs new file mode 100644 index 00000000..a3407c1f --- /dev/null +++ b/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/VariableOrArrayDeclaratorTranspiler.cs @@ -0,0 +1,64 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using Microsoft.CodeAnalysis.CSharp.Syntax; + +using War3Net.CodeAnalysis.Jass.Syntax; + +namespace War3Net.CodeAnalysis.Transpilers +{ + public partial class JassToCSharpTranspiler + { + public VariableDeclarationSyntax Transpile( + JassVariableOrArrayDeclaratorSyntax declarator, + bool isGlobalDeclaration) + { + return declarator switch + { + JassArrayDeclaratorSyntax arrayDeclarator => Transpile(arrayDeclarator, isGlobalDeclaration), + JassVariableDeclaratorSyntax variableDeclarator => Transpile(variableDeclarator, isGlobalDeclaration), + }; + } + + public VariableDeclarationSyntax Transpile( + JassSyntaxTriviaList leadingTrivia, + JassVariableOrArrayDeclaratorSyntax declarator, + bool isGlobalDeclaration) + { + return declarator switch + { + JassArrayDeclaratorSyntax arrayDeclarator => Transpile(leadingTrivia, arrayDeclarator, isGlobalDeclaration), + JassVariableDeclaratorSyntax variableDeclarator => Transpile(leadingTrivia, variableDeclarator, isGlobalDeclaration), + }; + } + + public VariableDeclarationSyntax Transpile( + JassVariableOrArrayDeclaratorSyntax declarator, + JassSyntaxTriviaList trailingTrivia, + bool isGlobalDeclaration) + { + return declarator switch + { + JassArrayDeclaratorSyntax arrayDeclarator => Transpile(arrayDeclarator, trailingTrivia, isGlobalDeclaration), + JassVariableDeclaratorSyntax variableDeclarator => Transpile(variableDeclarator, trailingTrivia, isGlobalDeclaration), + }; + } + + public VariableDeclarationSyntax Transpile( + JassSyntaxTriviaList leadingTrivia, + JassVariableOrArrayDeclaratorSyntax declarator, + JassSyntaxTriviaList trailingTrivia, + bool isGlobalDeclaration) + { + return declarator switch + { + JassArrayDeclaratorSyntax arrayDeclarator => Transpile(leadingTrivia, arrayDeclarator, trailingTrivia, isGlobalDeclaration), + JassVariableDeclaratorSyntax variableDeclarator => Transpile(leadingTrivia, variableDeclarator, trailingTrivia, isGlobalDeclaration), + }; + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/VariableReferenceExpressionTranspiler.cs b/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/VariableReferenceExpressionTranspiler.cs deleted file mode 100644 index 65a66e24..00000000 --- a/src/War3Net.CodeAnalysis.Transpilers/JassToCSharp/VariableReferenceExpressionTranspiler.cs +++ /dev/null @@ -1,22 +0,0 @@ -// ------------------------------------------------------------------------------ -// -// Licensed under the MIT license. -// See the LICENSE file in the project root for more information. -// -// ------------------------------------------------------------------------------ - -using Microsoft.CodeAnalysis.CSharp; -using Microsoft.CodeAnalysis.CSharp.Syntax; - -using War3Net.CodeAnalysis.Jass.Syntax; - -namespace War3Net.CodeAnalysis.Transpilers -{ - public partial class JassToCSharpTranspiler - { - public ExpressionSyntax Transpile(JassVariableReferenceExpressionSyntax variableReferenceExpression) - { - return SyntaxFactory.IdentifierName(Transpile(variableReferenceExpression.IdentifierName)); - } - } -} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Transpilers/JassToCSharpTranspiler.cs b/src/War3Net.CodeAnalysis.Transpilers/JassToCSharpTranspiler.cs index f326bc97..dca9bc5d 100644 --- a/src/War3Net.CodeAnalysis.Transpilers/JassToCSharpTranspiler.cs +++ b/src/War3Net.CodeAnalysis.Transpilers/JassToCSharpTranspiler.cs @@ -5,10 +5,22 @@ // // ------------------------------------------------------------------------------ +using System; +using System.Linq; + +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; + +using War3Net.CodeAnalysis.Jass; +using War3Net.CodeAnalysis.Jass.Syntax; + namespace War3Net.CodeAnalysis.Transpilers { public partial class JassToCSharpTranspiler { + private const int ArrayWhitespaceDiff = 3; + private const int PrefixWhitespaceDiff = -1; + public JassToCSharpTranspiler() { } @@ -19,5 +31,107 @@ public JassToCSharpTranspiler() public JassToLuaTranspiler? JassToLuaTranspiler { get; set; } public bool ApplyCSharpLuaTemplateAttribute { get; set; } + + private SyntaxToken TokenWithSpace(SyntaxKind tokenKind) + { + return SyntaxFactory.Token( + SyntaxTriviaList.Empty, + tokenKind, + SyntaxTriviaList.Create(SyntaxFactory.ElasticSpace)); + } + + private SyntaxToken TokenWithSpace(JassSyntaxTriviaList leadingTrivia, SyntaxKind tokenKind) + { + return SyntaxFactory.Token( + Transpile(leadingTrivia), + tokenKind, + SyntaxTriviaList.Create(SyntaxFactory.ElasticSpace)); + } + + private SyntaxToken Token(SyntaxKind tokenKind, JassSyntaxTriviaList trailingTrivia) + { + return SyntaxFactory.Token( + SyntaxTriviaList.Empty, + tokenKind, + Transpile(trailingTrivia)); + } + + private SyntaxToken Token(SyntaxKind tokenKind, SyntaxTriviaList trailingTrivia) + { + return SyntaxFactory.Token( + SyntaxTriviaList.Empty, + tokenKind, + trailingTrivia); + } + + private JassSyntaxTriviaList MergeTrivia(JassSyntaxToken discardedToken, JassSyntaxTriviaList leadingTrivia) + { + if (IsSingleSpace(discardedToken.TrailingTrivia, leadingTrivia)) + { + return discardedToken.LeadingTrivia; + } + + return JassSyntaxFactory.ConcatTriviaLists(discardedToken.LeadingTrivia, discardedToken.TrailingTrivia, leadingTrivia); + } + + private JassSyntaxTriviaList MergeTrivia(JassSyntaxTriviaList trailingTrivia, JassSyntaxToken discardedToken) + { + if (IsSingleSpace(trailingTrivia, discardedToken.LeadingTrivia)) + { + return discardedToken.TrailingTrivia; + } + + return JassSyntaxFactory.ConcatTriviaLists(trailingTrivia, discardedToken.LeadingTrivia, discardedToken.TrailingTrivia); + } + + private JassSyntaxTriviaList MergeTrivia(JassSyntaxTriviaList trailingTrivia, JassSyntaxTriviaList leadingTrivia) + { + if (IsSingleSpace(trailingTrivia, leadingTrivia)) + { + return JassSyntaxTriviaList.Empty; + } + + return JassSyntaxFactory.ConcatTriviaLists(trailingTrivia, leadingTrivia); + } + + private SyntaxTriviaList MergeTrivia(SyntaxTriviaList trailingTrivia, SyntaxTriviaList leadingTrivia) + { + if (IsSingleSpace(trailingTrivia, leadingTrivia)) + { + return SyntaxTriviaList.Empty; + } + + return SyntaxFactory.TriviaList(trailingTrivia.Concat(leadingTrivia)); + } + + private bool IsSingleSpace(JassSyntaxTriviaList trailingTrivia, JassSyntaxTriviaList leadingTrivia) + { + if (trailingTrivia.Trivia.Length == 1 && leadingTrivia.Trivia.Length == 0) + { + return string.Equals(trailingTrivia.Trivia[0].Text, JassSymbol.Space, StringComparison.Ordinal); + } + + if (trailingTrivia.Trivia.Length == 0 && leadingTrivia.Trivia.Length == 1) + { + return string.Equals(leadingTrivia.Trivia[0].Text, JassSymbol.Space, StringComparison.Ordinal); + } + + return false; + } + + private bool IsSingleSpace(SyntaxTriviaList trailingTrivia, SyntaxTriviaList leadingTrivia) + { + if (trailingTrivia.Count == 1 && leadingTrivia.Count == 0) + { + return string.Equals(trailingTrivia[0].ToFullString(), " ", StringComparison.Ordinal); + } + + if (trailingTrivia.Count == 0 && leadingTrivia.Count == 1) + { + return string.Equals(leadingTrivia[0].ToFullString(), " ", StringComparison.Ordinal); + } + + return false; + } } } \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Transpilers/JassToLua/ArgumentListTranspiler.cs b/src/War3Net.CodeAnalysis.Transpilers/JassToLua/ArgumentListTranspiler.cs index c5d9b34e..b3d8fa63 100644 --- a/src/War3Net.CodeAnalysis.Transpilers/JassToLua/ArgumentListTranspiler.cs +++ b/src/War3Net.CodeAnalysis.Transpilers/JassToLua/ArgumentListTranspiler.cs @@ -18,7 +18,7 @@ public partial class JassToLuaTranspiler { public IEnumerable Transpile(JassArgumentListSyntax argumentList) { - return argumentList.Arguments.Select(argument => Transpile(argument, out _)); + return argumentList.ArgumentList.Items.Select(argument => Transpile(argument, out _)); } } } \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Transpilers/JassToLua/ArrayDeclaratorTranspiler.cs b/src/War3Net.CodeAnalysis.Transpilers/JassToLua/ArrayDeclaratorTranspiler.cs index 0df8d1b5..d44bc1b9 100644 --- a/src/War3Net.CodeAnalysis.Transpilers/JassToLua/ArrayDeclaratorTranspiler.cs +++ b/src/War3Net.CodeAnalysis.Transpilers/JassToLua/ArrayDeclaratorTranspiler.cs @@ -7,6 +7,8 @@ using CSharpLua.LuaAst; +using War3Net.CodeAnalysis.Jass; +using War3Net.CodeAnalysis.Jass.Extensions; using War3Net.CodeAnalysis.Jass.Syntax; namespace War3Net.CodeAnalysis.Transpilers @@ -15,12 +17,12 @@ public partial class JassToLuaTranspiler { public LuaVariableDeclaratorSyntax Transpile(JassArrayDeclaratorSyntax arrayDeclarator) { - LuaExpressionSyntax expression = arrayDeclarator.Type switch + LuaExpressionSyntax expression = arrayDeclarator.Type.GetToken().SyntaxKind switch { - JassTypeSyntax type when type.Equals(JassTypeSyntax.Integer) => new LuaInvocationExpressionSyntax("__jarray", "0"), - JassTypeSyntax type when type.Equals(JassTypeSyntax.Real) => new LuaInvocationExpressionSyntax("__jarray", "0.0"), - JassTypeSyntax type when type.Equals(JassTypeSyntax.String) => new LuaInvocationExpressionSyntax("__jarray", LuaStringLiteralExpressionSyntax.Empty), - JassTypeSyntax type when type.Equals(JassTypeSyntax.Boolean) => new LuaInvocationExpressionSyntax("__jarray", LuaIdentifierLiteralExpressionSyntax.False), + JassSyntaxKind.IntegerKeyword => new LuaInvocationExpressionSyntax("__jarray", "0"), + JassSyntaxKind.RealKeyword => new LuaInvocationExpressionSyntax("__jarray", "0.0"), + JassSyntaxKind.StringKeyword => new LuaInvocationExpressionSyntax("__jarray", LuaStringLiteralExpressionSyntax.Empty), + JassSyntaxKind.BooleanKeyword => new LuaInvocationExpressionSyntax("__jarray", LuaIdentifierLiteralExpressionSyntax.False), _ => new LuaTableExpression(), }; diff --git a/src/War3Net.CodeAnalysis.Transpilers/JassToLua/BinaryExpressionTranspiler.cs b/src/War3Net.CodeAnalysis.Transpilers/JassToLua/BinaryExpressionTranspiler.cs index 87bd8466..80cb52cc 100644 --- a/src/War3Net.CodeAnalysis.Transpilers/JassToLua/BinaryExpressionTranspiler.cs +++ b/src/War3Net.CodeAnalysis.Transpilers/JassToLua/BinaryExpressionTranspiler.cs @@ -7,6 +7,7 @@ using CSharpLua.LuaAst; +using War3Net.CodeAnalysis.Jass; using War3Net.CodeAnalysis.Jass.Syntax; namespace War3Net.CodeAnalysis.Transpilers @@ -18,7 +19,48 @@ public LuaExpressionSyntax Transpile(JassBinaryExpressionSyntax binaryExpression var left = Transpile(binaryExpression.Left, out var leftType); var right = Transpile(binaryExpression.Right, out var rightType); - return new LuaBinaryExpressionSyntax(left, Transpile(binaryExpression.Operator, leftType, rightType, out type), right); + return new LuaBinaryExpressionSyntax(left, TranspileBinaryExpressionKind(binaryExpression.SyntaxKind, leftType, rightType, out type), right); + } + + public string TranspileBinaryExpressionKind(JassSyntaxKind expressionKind, JassTypeSyntax left, JassTypeSyntax right, out JassTypeSyntax type) + { + switch (expressionKind) + { + case JassSyntaxKind.GreaterThanExpression: + case JassSyntaxKind.LessThanExpression: + case JassSyntaxKind.EqualsExpression: + case JassSyntaxKind.NotEqualsExpression: + case JassSyntaxKind.GreaterThanOrEqualExpression: + case JassSyntaxKind.LessThanOrEqualExpression: + case JassSyntaxKind.LogicalAndExpression: + case JassSyntaxKind.LogicalOrExpression: + type = JassPredefinedTypeSyntax.Boolean; + break; + + default: + type = left.IsEquivalentTo(JassPredefinedTypeSyntax.String) || right.IsEquivalentTo(JassPredefinedTypeSyntax.String) + ? JassPredefinedTypeSyntax.String + : left.IsEquivalentTo(JassPredefinedTypeSyntax.Real) || right.IsEquivalentTo(JassPredefinedTypeSyntax.Real) + ? JassPredefinedTypeSyntax.Real + : left; + break; + } + + return expressionKind switch + { + JassSyntaxKind.AddExpression => type.IsEquivalentTo(JassPredefinedTypeSyntax.String) ? LuaSyntaxNode.Tokens.Concatenation : LuaSyntaxNode.Tokens.Plus, + JassSyntaxKind.SubtractExpression => LuaSyntaxNode.Tokens.Sub, + JassSyntaxKind.MultiplyExpression => LuaSyntaxNode.Tokens.Multiply, + JassSyntaxKind.DivideExpression => type.IsEquivalentTo(JassPredefinedTypeSyntax.Integer) ? LuaSyntaxNode.Tokens.IntegerDiv : LuaSyntaxNode.Tokens.Div, + JassSyntaxKind.GreaterThanExpression => ">", + JassSyntaxKind.LessThanExpression => "<", + JassSyntaxKind.EqualsExpression => LuaSyntaxNode.Tokens.EqualsEquals, + JassSyntaxKind.NotEqualsExpression => LuaSyntaxNode.Tokens.NotEquals, + JassSyntaxKind.GreaterThanOrEqualExpression => ">=", + JassSyntaxKind.LessThanOrEqualExpression => "<=", + JassSyntaxKind.LogicalAndExpression => LuaSyntaxNode.Keyword.And, + JassSyntaxKind.LogicalOrExpression => LuaSyntaxNode.Keyword.Or, + }; } } } \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Transpilers/JassToLua/BinaryOperatorTranspiler.cs b/src/War3Net.CodeAnalysis.Transpilers/JassToLua/BinaryOperatorTranspiler.cs deleted file mode 100644 index a8dcf716..00000000 --- a/src/War3Net.CodeAnalysis.Transpilers/JassToLua/BinaryOperatorTranspiler.cs +++ /dev/null @@ -1,57 +0,0 @@ -// ------------------------------------------------------------------------------ -// -// Licensed under the MIT license. -// See the LICENSE file in the project root for more information. -// -// ------------------------------------------------------------------------------ - -using CSharpLua.LuaAst; - -using War3Net.CodeAnalysis.Jass.Syntax; - -namespace War3Net.CodeAnalysis.Transpilers -{ - public partial class JassToLuaTranspiler - { - public string Transpile(BinaryOperatorType binaryOperator, JassTypeSyntax left, JassTypeSyntax right, out JassTypeSyntax type) - { - switch (binaryOperator) - { - case BinaryOperatorType.GreaterThan: - case BinaryOperatorType.LessThan: - case BinaryOperatorType.Equals: - case BinaryOperatorType.NotEquals: - case BinaryOperatorType.GreaterOrEqual: - case BinaryOperatorType.LessOrEqual: - case BinaryOperatorType.And: - case BinaryOperatorType.Or: - type = JassTypeSyntax.Boolean; - break; - - default: - type = left.Equals(JassTypeSyntax.String) || right.Equals(JassTypeSyntax.String) - ? JassTypeSyntax.String - : left.Equals(JassTypeSyntax.Real) || right.Equals(JassTypeSyntax.Real) - ? JassTypeSyntax.Real - : left; - break; - } - - return binaryOperator switch - { - BinaryOperatorType.Add => type.Equals(JassTypeSyntax.String) ? LuaSyntaxNode.Tokens.Concatenation : LuaSyntaxNode.Tokens.Plus, - BinaryOperatorType.Subtract => LuaSyntaxNode.Tokens.Sub, - BinaryOperatorType.Multiplication => LuaSyntaxNode.Tokens.Multiply, - BinaryOperatorType.Division => type.Equals(JassTypeSyntax.Integer) ? LuaSyntaxNode.Tokens.IntegerDiv : LuaSyntaxNode.Tokens.Div, - BinaryOperatorType.GreaterThan => ">", - BinaryOperatorType.LessThan => "<", - BinaryOperatorType.Equals => LuaSyntaxNode.Tokens.EqualsEquals, - BinaryOperatorType.NotEquals => LuaSyntaxNode.Tokens.NotEquals, - BinaryOperatorType.GreaterOrEqual => ">=", - BinaryOperatorType.LessOrEqual => "<=", - BinaryOperatorType.And => LuaSyntaxNode.Keyword.And, - BinaryOperatorType.Or => LuaSyntaxNode.Keyword.Or, - }; - } - } -} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Transpilers/JassToLua/BooleanLiteralExpressionTranspiler.cs b/src/War3Net.CodeAnalysis.Transpilers/JassToLua/BooleanLiteralExpressionTranspiler.cs deleted file mode 100644 index 693f0489..00000000 --- a/src/War3Net.CodeAnalysis.Transpilers/JassToLua/BooleanLiteralExpressionTranspiler.cs +++ /dev/null @@ -1,25 +0,0 @@ -// ------------------------------------------------------------------------------ -// -// Licensed under the MIT license. -// See the LICENSE file in the project root for more information. -// -// ------------------------------------------------------------------------------ - -using CSharpLua.LuaAst; - -using War3Net.CodeAnalysis.Jass.Syntax; - -namespace War3Net.CodeAnalysis.Transpilers -{ - public partial class JassToLuaTranspiler - { - public LuaExpressionSyntax Transpile(JassBooleanLiteralExpressionSyntax booleanLiteralExpression, out JassTypeSyntax type) - { - type = JassTypeSyntax.Boolean; - - return booleanLiteralExpression.Value - ? LuaIdentifierLiteralExpressionSyntax.True - : LuaIdentifierLiteralExpressionSyntax.False; - } - } -} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Transpilers/JassToLua/CallStatementTranspiler.cs b/src/War3Net.CodeAnalysis.Transpilers/JassToLua/CallStatementTranspiler.cs index 0f2324e0..2c4d6cd4 100644 --- a/src/War3Net.CodeAnalysis.Transpilers/JassToLua/CallStatementTranspiler.cs +++ b/src/War3Net.CodeAnalysis.Transpilers/JassToLua/CallStatementTranspiler.cs @@ -15,7 +15,9 @@ public partial class JassToLuaTranspiler { public LuaStatementSyntax Transpile(JassCallStatementSyntax callStatement) { - return new LuaInvocationExpressionSyntax(Transpile(callStatement.IdentifierName), Transpile(callStatement.Arguments)); + return new LuaInvocationExpressionSyntax( + Transpile(callStatement.IdentifierName), + Transpile(callStatement.ArgumentList)); } } } \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Transpilers/JassToLua/CharacterLiteralExpressionTranspiler.cs b/src/War3Net.CodeAnalysis.Transpilers/JassToLua/CharacterLiteralExpressionTranspiler.cs deleted file mode 100644 index 1f2caa73..00000000 --- a/src/War3Net.CodeAnalysis.Transpilers/JassToLua/CharacterLiteralExpressionTranspiler.cs +++ /dev/null @@ -1,23 +0,0 @@ -// ------------------------------------------------------------------------------ -// -// Licensed under the MIT license. -// See the LICENSE file in the project root for more information. -// -// ------------------------------------------------------------------------------ - -using CSharpLua.LuaAst; - -using War3Net.CodeAnalysis.Jass.Syntax; - -namespace War3Net.CodeAnalysis.Transpilers -{ - public partial class JassToLuaTranspiler - { - public LuaExpressionSyntax Transpile(JassCharacterLiteralExpressionSyntax characterLiteralExpression, out JassTypeSyntax type) - { - type = JassTypeSyntax.Integer; - - return new LuaCharacterLiteralExpression(characterLiteralExpression.Value); - } - } -} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Transpilers/JassToLua/CommentTranspiler.cs b/src/War3Net.CodeAnalysis.Transpilers/JassToLua/CommentTranspiler.cs deleted file mode 100644 index add5aefa..00000000 --- a/src/War3Net.CodeAnalysis.Transpilers/JassToLua/CommentTranspiler.cs +++ /dev/null @@ -1,21 +0,0 @@ -// ------------------------------------------------------------------------------ -// -// Licensed under the MIT license. -// See the LICENSE file in the project root for more information. -// -// ------------------------------------------------------------------------------ - -using CSharpLua.LuaAst; - -using War3Net.CodeAnalysis.Jass.Syntax; - -namespace War3Net.CodeAnalysis.Transpilers -{ - public partial class JassToLuaTranspiler - { - public LuaStatementSyntax Transpile(JassCommentSyntax comment) - { - return new LuaShortCommentStatement(comment.Comment); - } - } -} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Transpilers/JassToLua/DecimalLiteralExpressionTranspiler.cs b/src/War3Net.CodeAnalysis.Transpilers/JassToLua/DecimalLiteralExpressionTranspiler.cs deleted file mode 100644 index 8938eafe..00000000 --- a/src/War3Net.CodeAnalysis.Transpilers/JassToLua/DecimalLiteralExpressionTranspiler.cs +++ /dev/null @@ -1,23 +0,0 @@ -// ------------------------------------------------------------------------------ -// -// Licensed under the MIT license. -// See the LICENSE file in the project root for more information. -// -// ------------------------------------------------------------------------------ - -using CSharpLua.LuaAst; - -using War3Net.CodeAnalysis.Jass.Syntax; - -namespace War3Net.CodeAnalysis.Transpilers -{ - public partial class JassToLuaTranspiler - { - public LuaExpressionSyntax Transpile(JassDecimalLiteralExpressionSyntax decimalLiteralExpression, out JassTypeSyntax type) - { - type = JassTypeSyntax.Integer; - - return decimalLiteralExpression.Value; - } - } -} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Transpilers/JassToLua/DeclarationTranspiler.cs b/src/War3Net.CodeAnalysis.Transpilers/JassToLua/DeclarationTranspiler.cs index c1fcb93b..7f22f671 100644 --- a/src/War3Net.CodeAnalysis.Transpilers/JassToLua/DeclarationTranspiler.cs +++ b/src/War3Net.CodeAnalysis.Transpilers/JassToLua/DeclarationTranspiler.cs @@ -16,19 +16,16 @@ namespace War3Net.CodeAnalysis.Transpilers { public partial class JassToLuaTranspiler { - public IEnumerable Transpile(ITopLevelDeclarationSyntax declaration) + public IEnumerable Transpile(JassTopLevelDeclarationSyntax declaration) { if (declaration is JassNativeFunctionDeclarationSyntax nativeFunctionDeclaration) { - RegisterFunctionReturnType(nativeFunctionDeclaration.FunctionDeclarator); + RegisterFunctionReturnType(nativeFunctionDeclaration); } return declaration switch { - JassEmptySyntax empty => IgnoreEmptyDeclarations ? Array.Empty() : new[] { Transpile(empty) }, - JassCommentSyntax comment => IgnoreComments ? Array.Empty() : new[] { Transpile(comment) }, - JassGlobalDeclarationListSyntax globalDeclarationList => Transpile(globalDeclarationList), - JassGlobalDeclarationSyntax globalDeclaration => new[] { Transpile(globalDeclaration) }, + JassGlobalsDeclarationSyntax globalsDeclaration => Transpile(globalsDeclaration), JassFunctionDeclarationSyntax functionDeclaration => IgnoreEmptyDeclarations && KeepFunctionsSeparated ? new[] { Transpile(functionDeclaration), LuaBlankLinesStatement.One } : new[] { Transpile(functionDeclaration) }, _ => Array.Empty(), }; diff --git a/src/War3Net.CodeAnalysis.Transpilers/JassToLua/ArrayReferenceExpressionTranspiler.cs b/src/War3Net.CodeAnalysis.Transpilers/JassToLua/ElementAccessExpressionTranspiler.cs similarity index 54% rename from src/War3Net.CodeAnalysis.Transpilers/JassToLua/ArrayReferenceExpressionTranspiler.cs rename to src/War3Net.CodeAnalysis.Transpilers/JassToLua/ElementAccessExpressionTranspiler.cs index b36cf39b..90eed86e 100644 --- a/src/War3Net.CodeAnalysis.Transpilers/JassToLua/ArrayReferenceExpressionTranspiler.cs +++ b/src/War3Net.CodeAnalysis.Transpilers/JassToLua/ElementAccessExpressionTranspiler.cs @@ -1,5 +1,5 @@ // ------------------------------------------------------------------------------ -// +// // Licensed under the MIT license. // See the LICENSE file in the project root for more information. // @@ -13,13 +13,13 @@ namespace War3Net.CodeAnalysis.Transpilers { public partial class JassToLuaTranspiler { - public LuaExpressionSyntax Transpile(JassArrayReferenceExpressionSyntax arrayReferenceExpression, out JassTypeSyntax type) + public LuaExpressionSyntax Transpile(JassElementAccessExpressionSyntax elementAccessExpression, out JassTypeSyntax type) { - type = GetVariableType(arrayReferenceExpression.IdentifierName); + type = GetVariableType(elementAccessExpression.IdentifierName); return new LuaTableIndexAccessExpressionSyntax( - Transpile(arrayReferenceExpression.IdentifierName), - Transpile(arrayReferenceExpression.Indexer, out _)); + Transpile(elementAccessExpression.IdentifierName), + Transpile(elementAccessExpression.ElementAccessClause.Expression, out _)); } } } \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Transpilers/JassToLua/ElseClauseTranspiler.cs b/src/War3Net.CodeAnalysis.Transpilers/JassToLua/ElseClauseTranspiler.cs index 5d06ca4f..26963faf 100644 --- a/src/War3Net.CodeAnalysis.Transpilers/JassToLua/ElseClauseTranspiler.cs +++ b/src/War3Net.CodeAnalysis.Transpilers/JassToLua/ElseClauseTranspiler.cs @@ -5,6 +5,8 @@ // // ------------------------------------------------------------------------------ +using System.Linq; + using CSharpLua.LuaAst; using War3Net.CodeAnalysis.Jass.Syntax; @@ -17,7 +19,7 @@ public LuaElseClauseSyntax Transpile(JassElseClauseSyntax elseClause) { var luaElseClause = new LuaElseClauseSyntax(); - luaElseClause.Body.Statements.AddRange(Transpile(elseClause.Body)); + luaElseClause.Body.Statements.AddRange(elseClause.Statements.Select(Transpile)); return luaElseClause; } diff --git a/src/War3Net.CodeAnalysis.Transpilers/JassToLua/ElseIfClauseTranspiler.cs b/src/War3Net.CodeAnalysis.Transpilers/JassToLua/ElseIfClauseTranspiler.cs index 888abeab..5f761af8 100644 --- a/src/War3Net.CodeAnalysis.Transpilers/JassToLua/ElseIfClauseTranspiler.cs +++ b/src/War3Net.CodeAnalysis.Transpilers/JassToLua/ElseIfClauseTranspiler.cs @@ -5,6 +5,8 @@ // // ------------------------------------------------------------------------------ +using System.Linq; + using CSharpLua.LuaAst; using War3Net.CodeAnalysis.Jass.Syntax; @@ -15,9 +17,9 @@ public partial class JassToLuaTranspiler { public LuaElseIfStatementSyntax Transpile(JassElseIfClauseSyntax elseIfClause) { - var elseifStatement = new LuaElseIfStatementSyntax(Transpile(elseIfClause.Condition, out _)); + var elseifStatement = new LuaElseIfStatementSyntax(Transpile(elseIfClause.ElseIfClauseDeclarator.Condition, out _)); - elseifStatement.Body.Statements.AddRange(Transpile(elseIfClause.Body)); + elseifStatement.Body.Statements.AddRange(elseIfClause.Statements.Select(Transpile)); return elseifStatement; } diff --git a/src/War3Net.CodeAnalysis.Transpilers/JassToLua/EmptyTranspiler.cs b/src/War3Net.CodeAnalysis.Transpilers/JassToLua/EmptyTranspiler.cs deleted file mode 100644 index 9bf3c74f..00000000 --- a/src/War3Net.CodeAnalysis.Transpilers/JassToLua/EmptyTranspiler.cs +++ /dev/null @@ -1,21 +0,0 @@ -// ------------------------------------------------------------------------------ -// -// Licensed under the MIT license. -// See the LICENSE file in the project root for more information. -// -// ------------------------------------------------------------------------------ - -using CSharpLua.LuaAst; - -using War3Net.CodeAnalysis.Jass.Syntax; - -namespace War3Net.CodeAnalysis.Transpilers -{ - public partial class JassToLuaTranspiler - { - public LuaStatementSyntax Transpile(JassEmptySyntax empty) - { - return LuaBlankLinesStatement.One; - } - } -} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Transpilers/JassToLua/ExpressionTranspiler.cs b/src/War3Net.CodeAnalysis.Transpilers/JassToLua/ExpressionTranspiler.cs index 17b8211b..ddfdd176 100644 --- a/src/War3Net.CodeAnalysis.Transpilers/JassToLua/ExpressionTranspiler.cs +++ b/src/War3Net.CodeAnalysis.Transpilers/JassToLua/ExpressionTranspiler.cs @@ -13,23 +13,15 @@ namespace War3Net.CodeAnalysis.Transpilers { public partial class JassToLuaTranspiler { - public LuaExpressionSyntax Transpile(IExpressionSyntax expression, out JassTypeSyntax type) + public LuaExpressionSyntax Transpile(JassExpressionSyntax expression, out JassTypeSyntax type) { return expression switch { - JassCharacterLiteralExpressionSyntax characterLiteralExpression => Transpile(characterLiteralExpression, out type), - JassFourCCLiteralExpressionSyntax fourCCLiteralExpression => Transpile(fourCCLiteralExpression, out type), - JassHexadecimalLiteralExpressionSyntax hexadecimalLiteralExpression => Transpile(hexadecimalLiteralExpression, out type), - JassRealLiteralExpressionSyntax realLiteralExpression => Transpile(realLiteralExpression, out type), - JassOctalLiteralExpressionSyntax octalLiteralExpression => Transpile(octalLiteralExpression, out type), - JassDecimalLiteralExpressionSyntax decimalLiteralExpression => Transpile(decimalLiteralExpression, out type), - JassBooleanLiteralExpressionSyntax booleanLiteralExpression => Transpile(booleanLiteralExpression, out type), - JassStringLiteralExpressionSyntax stringLiteralExpression => Transpile(stringLiteralExpression, out type), - JassNullLiteralExpressionSyntax nullLiteralExpression => Transpile(nullLiteralExpression, out type), + JassLiteralExpressionSyntax literalExpression => Transpile(literalExpression, out type), JassFunctionReferenceExpressionSyntax functionReferenceExpression => Transpile(functionReferenceExpression, out type), JassInvocationExpressionSyntax invocationExpression => Transpile(invocationExpression, out type), - JassArrayReferenceExpressionSyntax arrayReferenceExpression => Transpile(arrayReferenceExpression, out type), - JassVariableReferenceExpressionSyntax variableReferenceExpression => Transpile(variableReferenceExpression, out type), + JassElementAccessExpressionSyntax elementAccessExpression => Transpile(elementAccessExpression, out type), + JassIdentifierNameSyntax identifierName => Transpile(identifierName, out type), JassParenthesizedExpressionSyntax parenthesizedExpression => Transpile(parenthesizedExpression, out type), JassUnaryExpressionSyntax unaryExpression => Transpile(unaryExpression, out type), JassBinaryExpressionSyntax binaryExpression => Transpile(binaryExpression, out type), diff --git a/src/War3Net.CodeAnalysis.Transpilers/JassToLua/FourCCLiteralExpressionTranspiler.cs b/src/War3Net.CodeAnalysis.Transpilers/JassToLua/FourCCLiteralExpressionTranspiler.cs deleted file mode 100644 index df843861..00000000 --- a/src/War3Net.CodeAnalysis.Transpilers/JassToLua/FourCCLiteralExpressionTranspiler.cs +++ /dev/null @@ -1,23 +0,0 @@ -// ------------------------------------------------------------------------------ -// -// Licensed under the MIT license. -// See the LICENSE file in the project root for more information. -// -// ------------------------------------------------------------------------------ - -using CSharpLua.LuaAst; - -using War3Net.CodeAnalysis.Jass.Syntax; - -namespace War3Net.CodeAnalysis.Transpilers -{ - public partial class JassToLuaTranspiler - { - public LuaExpressionSyntax Transpile(JassFourCCLiteralExpressionSyntax fourCCLiteralExpression, out JassTypeSyntax type) - { - type = JassTypeSyntax.Integer; - - return fourCCLiteralExpression.Value; - } - } -} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Transpilers/JassToLua/FunctionDeclarationTranspiler.cs b/src/War3Net.CodeAnalysis.Transpilers/JassToLua/FunctionDeclarationTranspiler.cs index 6b1a9b10..f0f53f6f 100644 --- a/src/War3Net.CodeAnalysis.Transpilers/JassToLua/FunctionDeclarationTranspiler.cs +++ b/src/War3Net.CodeAnalysis.Transpilers/JassToLua/FunctionDeclarationTranspiler.cs @@ -5,6 +5,8 @@ // // ------------------------------------------------------------------------------ +using System.Linq; + using CSharpLua.LuaAst; using War3Net.CodeAnalysis.Jass.Syntax; @@ -19,7 +21,7 @@ public LuaStatementSyntax Transpile(JassFunctionDeclarationSyntax functionDeclar var functionExpression = new LuaFunctionExpressionSyntax(); functionExpression.AddParameters(Transpile(functionDeclaration.FunctionDeclarator.ParameterList)); - functionExpression.Body.Statements.AddRange(Transpile(functionDeclaration.Body)); + functionExpression.Body.Statements.AddRange(functionDeclaration.Statements.Select(Transpile)); functionExpression.RenderAsFunctionDefinition = true; ClearLocalTypes(); diff --git a/src/War3Net.CodeAnalysis.Transpilers/JassToLua/FunctionReferenceExpressionTranspiler.cs b/src/War3Net.CodeAnalysis.Transpilers/JassToLua/FunctionReferenceExpressionTranspiler.cs index 1494e681..f000054f 100644 --- a/src/War3Net.CodeAnalysis.Transpilers/JassToLua/FunctionReferenceExpressionTranspiler.cs +++ b/src/War3Net.CodeAnalysis.Transpilers/JassToLua/FunctionReferenceExpressionTranspiler.cs @@ -15,7 +15,7 @@ public partial class JassToLuaTranspiler { public LuaExpressionSyntax Transpile(JassFunctionReferenceExpressionSyntax functionReferenceExpression, out JassTypeSyntax type) { - type = JassTypeSyntax.Code; + type = JassPredefinedTypeSyntax.Code; return Transpile(functionReferenceExpression.IdentifierName); } diff --git a/src/War3Net.CodeAnalysis.Transpilers/JassToLua/GlobalDeclarationListTranspiler.cs b/src/War3Net.CodeAnalysis.Transpilers/JassToLua/GlobalDeclarationListTranspiler.cs deleted file mode 100644 index 7e713df3..00000000 --- a/src/War3Net.CodeAnalysis.Transpilers/JassToLua/GlobalDeclarationListTranspiler.cs +++ /dev/null @@ -1,35 +0,0 @@ -// ------------------------------------------------------------------------------ -// -// Licensed under the MIT license. -// See the LICENSE file in the project root for more information. -// -// ------------------------------------------------------------------------------ - -using System; -using System.Collections.Generic; -using System.Linq; - -using CSharpLua.LuaAst; - -using War3Net.CodeAnalysis.Jass.Syntax; - -namespace War3Net.CodeAnalysis.Transpilers -{ - public partial class JassToLuaTranspiler - { - public IEnumerable Transpile(JassGlobalDeclarationListSyntax globalDeclarationList) - { - return globalDeclarationList.Globals - .Where(declaration => !(declaration is JassCommentSyntax && IgnoreComments)) - .Where(declaration => !(declaration is JassEmptySyntax && IgnoreEmptyDeclarations)) - .Select(declaration => declaration switch - { - JassEmptySyntax empty => Transpile(empty), - JassCommentSyntax comment => Transpile(comment), - JassGlobalDeclarationSyntax globalDeclaration => Transpile(globalDeclaration), - - _ => throw new NotSupportedException(), - }); - } - } -} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Transpilers/JassToLua/GlobalDeclarationTranspiler.cs b/src/War3Net.CodeAnalysis.Transpilers/JassToLua/GlobalDeclarationTranspiler.cs index 791f2d10..3c26fee2 100644 --- a/src/War3Net.CodeAnalysis.Transpilers/JassToLua/GlobalDeclarationTranspiler.cs +++ b/src/War3Net.CodeAnalysis.Transpilers/JassToLua/GlobalDeclarationTranspiler.cs @@ -15,7 +15,26 @@ public partial class JassToLuaTranspiler { public LuaStatementSyntax Transpile(JassGlobalDeclarationSyntax globalDeclaration) { - return Transpile(globalDeclaration.Declarator, false); + return globalDeclaration switch + { + JassGlobalConstantDeclarationSyntax globalConstantDeclaration => Transpile(globalConstantDeclaration), + JassGlobalVariableDeclarationSyntax globalVariableDeclaration => Transpile(globalVariableDeclaration.Declarator, false), + }; + } + + public LuaStatementSyntax Transpile(JassGlobalConstantDeclarationSyntax globalConstantDeclaration) + { + RegisterVariableType(globalConstantDeclaration); + + var expression = Transpile(globalConstantDeclaration.Value); + + var luaDeclarator = new LuaVariableDeclaratorSyntax(Transpile(globalConstantDeclaration.IdentifierName), expression); + luaDeclarator.IsLocalDeclaration = false; + + var declaration = new LuaVariableListDeclarationSyntax(); + declaration.Variables.Add(luaDeclarator); + + return new LuaLocalDeclarationStatementSyntax(declaration); } } } \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Transpilers/JassToLua/NullLiteralExpressionTranspiler.cs b/src/War3Net.CodeAnalysis.Transpilers/JassToLua/GlobalsDeclarationTranspiler.cs similarity index 60% rename from src/War3Net.CodeAnalysis.Transpilers/JassToLua/NullLiteralExpressionTranspiler.cs rename to src/War3Net.CodeAnalysis.Transpilers/JassToLua/GlobalsDeclarationTranspiler.cs index cb3874aa..f82c0fb9 100644 --- a/src/War3Net.CodeAnalysis.Transpilers/JassToLua/NullLiteralExpressionTranspiler.cs +++ b/src/War3Net.CodeAnalysis.Transpilers/JassToLua/GlobalsDeclarationTranspiler.cs @@ -1,10 +1,13 @@ // ------------------------------------------------------------------------------ -// +// // Licensed under the MIT license. // See the LICENSE file in the project root for more information. // // ------------------------------------------------------------------------------ +using System.Collections.Generic; +using System.Linq; + using CSharpLua.LuaAst; using War3Net.CodeAnalysis.Jass.Syntax; @@ -13,11 +16,9 @@ namespace War3Net.CodeAnalysis.Transpilers { public partial class JassToLuaTranspiler { - public LuaExpressionSyntax Transpile(JassNullLiteralExpressionSyntax nullLiteralExpression, out JassTypeSyntax type) + public IEnumerable Transpile(JassGlobalsDeclarationSyntax globalsDeclaration) { - type = JassTypeSyntax.Handle; - - return LuaIdentifierLiteralExpressionSyntax.Nil; + return globalsDeclaration.GlobalDeclarations.Select(Transpile); } } } \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Transpilers/JassToLua/HexadecimalLiteralExpressionTranspiler.cs b/src/War3Net.CodeAnalysis.Transpilers/JassToLua/HexadecimalLiteralExpressionTranspiler.cs deleted file mode 100644 index c393015f..00000000 --- a/src/War3Net.CodeAnalysis.Transpilers/JassToLua/HexadecimalLiteralExpressionTranspiler.cs +++ /dev/null @@ -1,23 +0,0 @@ -// ------------------------------------------------------------------------------ -// -// Licensed under the MIT license. -// See the LICENSE file in the project root for more information. -// -// ------------------------------------------------------------------------------ - -using CSharpLua.LuaAst; - -using War3Net.CodeAnalysis.Jass.Syntax; - -namespace War3Net.CodeAnalysis.Transpilers -{ - public partial class JassToLuaTranspiler - { - public LuaExpressionSyntax Transpile(JassHexadecimalLiteralExpressionSyntax hexadecimalLiteralExpression, out JassTypeSyntax type) - { - type = JassTypeSyntax.Integer; - - return hexadecimalLiteralExpression.ToString(); - } - } -} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Transpilers/JassToLua/IdentifierNameTranspiler.cs b/src/War3Net.CodeAnalysis.Transpilers/JassToLua/IdentifierNameTranspiler.cs index 1c2e2ede..576e7fe3 100644 --- a/src/War3Net.CodeAnalysis.Transpilers/JassToLua/IdentifierNameTranspiler.cs +++ b/src/War3Net.CodeAnalysis.Transpilers/JassToLua/IdentifierNameTranspiler.cs @@ -5,9 +5,7 @@ // // ------------------------------------------------------------------------------ -using System; -using System.Collections.Generic; -using System.Linq; +using CSharpLua.LuaAst; using War3Net.CodeAnalysis.Jass.Syntax; @@ -15,41 +13,15 @@ namespace War3Net.CodeAnalysis.Transpilers { public partial class JassToLuaTranspiler { - private const string AntiReservedKeywordConflictPrefix = "_"; - - private static readonly Lazy> _reservedKeywords = new Lazy>(() => GetLuaKeywords().ToHashSet(StringComparer.Ordinal)); - - public string Transpile(JassIdentifierNameSyntax identifierName) + public LuaIdentifierNameSyntax Transpile(JassIdentifierNameSyntax identifierName) { - return _reservedKeywords.Value.Contains(identifierName.Name) - ? $"{AntiReservedKeywordConflictPrefix}{identifierName.Name}" - : identifierName.Name; + return identifierName.Token.Text; } - private static IEnumerable GetLuaKeywords() + public LuaIdentifierNameSyntax Transpile(JassIdentifierNameSyntax identifierName, out JassTypeSyntax type) { - yield return "and"; - yield return "break"; - yield return "do"; - yield return "else"; - yield return "elseif"; - yield return "end"; - yield return "false"; - yield return "for"; - yield return "function"; - yield return "goto"; - yield return "if"; - yield return "in"; - yield return "local"; - yield return "nil"; - yield return "not"; - yield return "or"; - yield return "repeat"; - yield return "return"; - yield return "then"; - yield return "true"; - yield return "until"; - yield return "while"; + type = GetVariableType(identifierName); + return identifierName.Token.Text; } } } \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Transpilers/JassToLua/IfStatementTranspiler.cs b/src/War3Net.CodeAnalysis.Transpilers/JassToLua/IfStatementTranspiler.cs index fa07f463..e56b7d18 100644 --- a/src/War3Net.CodeAnalysis.Transpilers/JassToLua/IfStatementTranspiler.cs +++ b/src/War3Net.CodeAnalysis.Transpilers/JassToLua/IfStatementTranspiler.cs @@ -17,9 +17,9 @@ public partial class JassToLuaTranspiler { public LuaStatementSyntax Transpile(JassIfStatementSyntax ifStatement) { - var luaIfStatement = new LuaIfStatementSyntax(Transpile(ifStatement.Condition, out _)); + var luaIfStatement = new LuaIfStatementSyntax(Transpile(ifStatement.IfClause.IfClauseDeclarator.Condition, out _)); - luaIfStatement.Body.Statements.AddRange(Transpile(ifStatement.Body)); + luaIfStatement.Body.Statements.AddRange(ifStatement.IfClause.Statements.Select(Transpile)); luaIfStatement.ElseIfStatements.AddRange(ifStatement.ElseIfClauses.Select(Transpile)); luaIfStatement.Else = ifStatement.ElseClause is null ? null : Transpile(ifStatement.ElseClause); diff --git a/src/War3Net.CodeAnalysis.Transpilers/JassToLua/InvocationExpressionTranspiler.cs b/src/War3Net.CodeAnalysis.Transpilers/JassToLua/InvocationExpressionTranspiler.cs index 14deb848..c913743a 100644 --- a/src/War3Net.CodeAnalysis.Transpilers/JassToLua/InvocationExpressionTranspiler.cs +++ b/src/War3Net.CodeAnalysis.Transpilers/JassToLua/InvocationExpressionTranspiler.cs @@ -19,7 +19,7 @@ public LuaExpressionSyntax Transpile(JassInvocationExpressionSyntax invocationEx var luaInvocationExpression = new LuaInvocationExpressionSyntax(Transpile(invocationExpression.IdentifierName)); - luaInvocationExpression.AddArguments(Transpile(invocationExpression.Arguments)); + luaInvocationExpression.AddArguments(Transpile(invocationExpression.ArgumentList)); return luaInvocationExpression; } diff --git a/src/War3Net.CodeAnalysis.Transpilers/JassToLua/LiteralExpressionTranspiler.cs b/src/War3Net.CodeAnalysis.Transpilers/JassToLua/LiteralExpressionTranspiler.cs new file mode 100644 index 00000000..aae40b77 --- /dev/null +++ b/src/War3Net.CodeAnalysis.Transpilers/JassToLua/LiteralExpressionTranspiler.cs @@ -0,0 +1,109 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using System; +using System.Globalization; + +using CSharpLua.LuaAst; + +using War3Net.CodeAnalysis.Jass; +using War3Net.CodeAnalysis.Jass.Syntax; + +namespace War3Net.CodeAnalysis.Transpilers +{ + public partial class JassToLuaTranspiler + { + public LuaExpressionSyntax Transpile(JassLiteralExpressionSyntax literalExpression, out JassTypeSyntax type) + { + return literalExpression.SyntaxKind switch + { + JassSyntaxKind.TrueLiteralExpression => TranspileTrueLiteral(out type), + JassSyntaxKind.FalseLiteralExpression => TranspileFalseLiteral(out type), + JassSyntaxKind.NullLiteralExpression => TranspileNullLiteral(out type), + JassSyntaxKind.DecimalLiteralExpression => TranspileDecimalLiteral(literalExpression, out type), + JassSyntaxKind.OctalLiteralExpression => TranspileOctalLiteral(literalExpression, out type), + JassSyntaxKind.HexadecimalLiteralExpression => TranspileHexadecimalLiteral(literalExpression, out type), + JassSyntaxKind.FourCCLiteralExpression => TranspileFourCCLiteral(literalExpression, out type), + JassSyntaxKind.CharacterLiteralExpression => TranspileCharacterLiteral(literalExpression, out type), + JassSyntaxKind.RealLiteralExpression => TranspileRealLiteral(literalExpression, out type), + JassSyntaxKind.StringLiteralExpression => TranspileStringLiteral(literalExpression, out type), + }; + } + + private LuaExpressionSyntax TranspileTrueLiteral(out JassTypeSyntax type) + { + type = JassPredefinedTypeSyntax.Boolean; + return new LuaIdentifierLiteralExpressionSyntax(LuaIdentifierNameSyntax.True); + } + + private LuaExpressionSyntax TranspileFalseLiteral(out JassTypeSyntax type) + { + type = JassPredefinedTypeSyntax.Boolean; + return new LuaIdentifierLiteralExpressionSyntax(LuaIdentifierNameSyntax.False); + } + + private LuaExpressionSyntax TranspileNullLiteral(out JassTypeSyntax type) + { + type = JassPredefinedTypeSyntax.Handle; + return new LuaIdentifierLiteralExpressionSyntax(LuaIdentifierNameSyntax.Nil); + } + + private LuaExpressionSyntax TranspileDecimalLiteral(JassLiteralExpressionSyntax literalExpression, out JassTypeSyntax type) + { + type = JassPredefinedTypeSyntax.Integer; + return new LuaIdentifierLiteralExpressionSyntax(literalExpression.Token.Text); + } + + private LuaExpressionSyntax TranspileOctalLiteral(JassLiteralExpressionSyntax literalExpression, out JassTypeSyntax type) + { + type = JassPredefinedTypeSyntax.Integer; + var text = JassLiteral.ParseOctal(literalExpression.Token.Text).ToString(CultureInfo.InvariantCulture); + + return new LuaIdentifierLiteralExpressionSyntax(text); + } + + private LuaExpressionSyntax TranspileHexadecimalLiteral(JassLiteralExpressionSyntax literalExpression, out JassTypeSyntax type) + { + type = JassPredefinedTypeSyntax.Integer; + var text = literalExpression.Token.Text.Replace(JassSymbol.Dollar, "0x", StringComparison.Ordinal); + + return new LuaIdentifierLiteralExpressionSyntax(text); + } + + private LuaExpressionSyntax TranspileFourCCLiteral(JassLiteralExpressionSyntax literalExpression, out JassTypeSyntax type) + { + type = JassPredefinedTypeSyntax.Integer; + var text = $"FourCC(\"{literalExpression.Token.Text[1..^1]}\")"; + + return new LuaIdentifierLiteralExpressionSyntax(text); + } + + private LuaExpressionSyntax TranspileCharacterLiteral(JassLiteralExpressionSyntax literalExpression, out JassTypeSyntax type) + { + type = JassPredefinedTypeSyntax.Integer; + var text = ((int)JassLiteral.ParseChar(literalExpression.Token.Text)).ToString(CultureInfo.InvariantCulture); + + return new LuaIdentifierLiteralExpressionSyntax(text); + } + + private LuaExpressionSyntax TranspileRealLiteral(JassLiteralExpressionSyntax literalExpression, out JassTypeSyntax type) + { + type = JassPredefinedTypeSyntax.Real; + return new LuaIdentifierLiteralExpressionSyntax(literalExpression.Token.Text); + } + + private LuaExpressionSyntax TranspileStringLiteral(JassLiteralExpressionSyntax literalExpression, out JassTypeSyntax type) + { + type = JassPredefinedTypeSyntax.String; + var text = JassLiteral.ParseString(literalExpression.Token.Text) + .Replace(JassSymbol.CarriageReturn, @"\r", StringComparison.Ordinal) + .Replace(JassSymbol.LineFeed, @"\n", StringComparison.Ordinal); + + return new LuaStringLiteralExpressionSyntax(text); + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Transpilers/JassToLua/LoopStatementTranspiler.cs b/src/War3Net.CodeAnalysis.Transpilers/JassToLua/LoopStatementTranspiler.cs index 4ef3db44..7929de22 100644 --- a/src/War3Net.CodeAnalysis.Transpilers/JassToLua/LoopStatementTranspiler.cs +++ b/src/War3Net.CodeAnalysis.Transpilers/JassToLua/LoopStatementTranspiler.cs @@ -5,6 +5,8 @@ // // ------------------------------------------------------------------------------ +using System.Linq; + using CSharpLua.LuaAst; using War3Net.CodeAnalysis.Jass.Syntax; @@ -17,7 +19,7 @@ public LuaStatementSyntax Transpile(JassLoopStatementSyntax loopStatement) { var whileStatement = new LuaWhileStatementSyntax(LuaIdentifierLiteralExpressionSyntax.True); - whileStatement.Body.Statements.AddRange(Transpile(loopStatement.Body)); + whileStatement.Body.Statements.AddRange(loopStatement.Statements.Select(Transpile)); return whileStatement; } diff --git a/src/War3Net.CodeAnalysis.Transpilers/JassToLua/OctalLiteralExpressionTranspiler.cs b/src/War3Net.CodeAnalysis.Transpilers/JassToLua/OctalLiteralExpressionTranspiler.cs deleted file mode 100644 index c3fa63cb..00000000 --- a/src/War3Net.CodeAnalysis.Transpilers/JassToLua/OctalLiteralExpressionTranspiler.cs +++ /dev/null @@ -1,23 +0,0 @@ -// ------------------------------------------------------------------------------ -// -// Licensed under the MIT license. -// See the LICENSE file in the project root for more information. -// -// ------------------------------------------------------------------------------ - -using CSharpLua.LuaAst; - -using War3Net.CodeAnalysis.Jass.Syntax; - -namespace War3Net.CodeAnalysis.Transpilers -{ - public partial class JassToLuaTranspiler - { - public LuaExpressionSyntax Transpile(JassOctalLiteralExpressionSyntax octalLiteralExpression, out JassTypeSyntax type) - { - type = JassTypeSyntax.Integer; - - return octalLiteralExpression.Value; - } - } -} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Transpilers/JassToLua/ParameterListTranspiler.cs b/src/War3Net.CodeAnalysis.Transpilers/JassToLua/ParameterListTranspiler.cs index 87cea536..19ae202f 100644 --- a/src/War3Net.CodeAnalysis.Transpilers/JassToLua/ParameterListTranspiler.cs +++ b/src/War3Net.CodeAnalysis.Transpilers/JassToLua/ParameterListTranspiler.cs @@ -16,9 +16,18 @@ namespace War3Net.CodeAnalysis.Transpilers { public partial class JassToLuaTranspiler { + public IEnumerable Transpile(JassParameterListOrEmptyParameterListSyntax parameterListOrEmptyParameterList) + { + return parameterListOrEmptyParameterList switch + { + JassParameterListSyntax parameterList => Transpile(parameterList), + JassEmptyParameterListSyntax => Enumerable.Empty(), + }; + } + public IEnumerable Transpile(JassParameterListSyntax parameterList) { - return parameterList.Parameters.Select(Transpile); + return parameterList.ParameterList.Items.Select(Transpile); } } } \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Transpilers/JassToLua/RealLiteralExpressionTranspiler.cs b/src/War3Net.CodeAnalysis.Transpilers/JassToLua/RealLiteralExpressionTranspiler.cs deleted file mode 100644 index a5552c01..00000000 --- a/src/War3Net.CodeAnalysis.Transpilers/JassToLua/RealLiteralExpressionTranspiler.cs +++ /dev/null @@ -1,23 +0,0 @@ -// ------------------------------------------------------------------------------ -// -// Licensed under the MIT license. -// See the LICENSE file in the project root for more information. -// -// ------------------------------------------------------------------------------ - -using CSharpLua.LuaAst; - -using War3Net.CodeAnalysis.Jass.Syntax; - -namespace War3Net.CodeAnalysis.Transpilers -{ - public partial class JassToLuaTranspiler - { - public LuaExpressionSyntax Transpile(JassRealLiteralExpressionSyntax realLiteralExpression, out JassTypeSyntax type) - { - type = JassTypeSyntax.Real; - - return realLiteralExpression.ToString(); - } - } -} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Transpilers/JassToLua/SetStatementTranspiler.cs b/src/War3Net.CodeAnalysis.Transpilers/JassToLua/SetStatementTranspiler.cs index f4610fdc..581374bd 100644 --- a/src/War3Net.CodeAnalysis.Transpilers/JassToLua/SetStatementTranspiler.cs +++ b/src/War3Net.CodeAnalysis.Transpilers/JassToLua/SetStatementTranspiler.cs @@ -16,10 +16,10 @@ public partial class JassToLuaTranspiler public LuaStatementSyntax Transpile(JassSetStatementSyntax setStatement) { return new LuaAssignmentExpressionSyntax( - setStatement.Indexer is null + setStatement.ElementAccessClause is null ? Transpile(setStatement.IdentifierName) - : new LuaTableIndexAccessExpressionSyntax(Transpile(setStatement.IdentifierName), Transpile(setStatement.Indexer, out _)), - Transpile(setStatement.Value)); + : new LuaTableIndexAccessExpressionSyntax(Transpile(setStatement.IdentifierName), Transpile(setStatement.ElementAccessClause.Expression, out _)), + Transpile(setStatement.Value.Expression, out _)); } } } \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Transpilers/JassToLua/StatementListTranspiler.cs b/src/War3Net.CodeAnalysis.Transpilers/JassToLua/StatementListTranspiler.cs deleted file mode 100644 index f727aad1..00000000 --- a/src/War3Net.CodeAnalysis.Transpilers/JassToLua/StatementListTranspiler.cs +++ /dev/null @@ -1,27 +0,0 @@ -// ------------------------------------------------------------------------------ -// -// Licensed under the MIT license. -// See the LICENSE file in the project root for more information. -// -// ------------------------------------------------------------------------------ - -using System.Collections.Generic; -using System.Linq; - -using CSharpLua.LuaAst; - -using War3Net.CodeAnalysis.Jass.Syntax; - -namespace War3Net.CodeAnalysis.Transpilers -{ - public partial class JassToLuaTranspiler - { - public IEnumerable Transpile(JassStatementListSyntax statementList) - { - return statementList.Statements - .Where(statement => !(statement is JassCommentSyntax && IgnoreComments)) - .Where(statement => !(statement is JassEmptySyntax && IgnoreEmptyStatements)) - .Select(Transpile); - } - } -} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Transpilers/JassToLua/StatementTranspiler.cs b/src/War3Net.CodeAnalysis.Transpilers/JassToLua/StatementTranspiler.cs index 594455ba..8caea181 100644 --- a/src/War3Net.CodeAnalysis.Transpilers/JassToLua/StatementTranspiler.cs +++ b/src/War3Net.CodeAnalysis.Transpilers/JassToLua/StatementTranspiler.cs @@ -13,12 +13,10 @@ namespace War3Net.CodeAnalysis.Transpilers { public partial class JassToLuaTranspiler { - public LuaStatementSyntax Transpile(IStatementSyntax statement) + public LuaStatementSyntax Transpile(JassStatementSyntax statement) { return statement switch { - JassEmptySyntax empty => Transpile(empty), - JassCommentSyntax comment => Transpile(comment), JassLocalVariableDeclarationStatementSyntax localVariableDeclarationStatement => Transpile(localVariableDeclarationStatement), JassSetStatementSyntax setStatement => Transpile(setStatement), JassCallStatementSyntax callStatement => Transpile(callStatement), diff --git a/src/War3Net.CodeAnalysis.Transpilers/JassToLua/StringLiteralExpressionTranspiler.cs b/src/War3Net.CodeAnalysis.Transpilers/JassToLua/StringLiteralExpressionTranspiler.cs deleted file mode 100644 index 030ab3a0..00000000 --- a/src/War3Net.CodeAnalysis.Transpilers/JassToLua/StringLiteralExpressionTranspiler.cs +++ /dev/null @@ -1,24 +0,0 @@ -// ------------------------------------------------------------------------------ -// -// Licensed under the MIT license. -// See the LICENSE file in the project root for more information. -// -// ------------------------------------------------------------------------------ - -using CSharpLua.LuaAst; - -using War3Net.CodeAnalysis.Jass; -using War3Net.CodeAnalysis.Jass.Syntax; - -namespace War3Net.CodeAnalysis.Transpilers -{ - public partial class JassToLuaTranspiler - { - public LuaExpressionSyntax Transpile(JassStringLiteralExpressionSyntax stringLiteralExpression, out JassTypeSyntax type) - { - type = JassTypeSyntax.String; - - return $"\"{stringLiteralExpression.Value.Replace($"{JassSymbol.CarriageReturn}", @"\r").Replace($"{JassSymbol.LineFeed}", @"\n")}\""; - } - } -} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Transpilers/JassToLua/SyntaxTokenTranspiler.cs b/src/War3Net.CodeAnalysis.Transpilers/JassToLua/SyntaxTokenTranspiler.cs new file mode 100644 index 00000000..7ebfcaa1 --- /dev/null +++ b/src/War3Net.CodeAnalysis.Transpilers/JassToLua/SyntaxTokenTranspiler.cs @@ -0,0 +1,57 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using System; +using System.Collections.Generic; +using System.Linq; + +using CSharpLua.LuaAst; + +using War3Net.CodeAnalysis.Jass.Syntax; + +namespace War3Net.CodeAnalysis.Transpilers +{ + public partial class JassToLuaTranspiler + { + private const string AntiReservedKeywordConflictPrefix = "_"; + + private static readonly Lazy> _reservedKeywords = new Lazy>(() => GetLuaKeywords().ToHashSet(StringComparer.Ordinal)); + + public string Transpile(JassSyntaxToken token) + { + return _reservedKeywords.Value.Contains(token.Text) + ? $"{AntiReservedKeywordConflictPrefix}{token.Text}" + : token.Text; + } + + private static IEnumerable GetLuaKeywords() + { + yield return LuaSyntaxNode.Keyword.And; + yield return LuaSyntaxNode.Keyword.Break; + yield return LuaSyntaxNode.Keyword.Do; + yield return LuaSyntaxNode.Keyword.Else; + yield return LuaSyntaxNode.Keyword.ElseIf; + yield return LuaSyntaxNode.Keyword.End; + yield return LuaSyntaxNode.Keyword.False; + yield return LuaSyntaxNode.Keyword.For; + yield return LuaSyntaxNode.Keyword.Function; + yield return LuaSyntaxNode.Keyword.Goto; + yield return LuaSyntaxNode.Keyword.If; + yield return LuaSyntaxNode.Keyword.In; + yield return LuaSyntaxNode.Keyword.Local; + yield return LuaSyntaxNode.Keyword.Nil; + yield return LuaSyntaxNode.Keyword.Not; + yield return LuaSyntaxNode.Keyword.Or; + yield return LuaSyntaxNode.Keyword.Repeat; + yield return LuaSyntaxNode.Keyword.Return; + yield return LuaSyntaxNode.Keyword.Then; + yield return LuaSyntaxNode.Keyword.True; + yield return LuaSyntaxNode.Keyword.Until; + yield return LuaSyntaxNode.Keyword.While; + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Transpilers/JassToLua/UnaryExpressionTranspiler.cs b/src/War3Net.CodeAnalysis.Transpilers/JassToLua/UnaryExpressionTranspiler.cs index fb1de684..74ebcaa5 100644 --- a/src/War3Net.CodeAnalysis.Transpilers/JassToLua/UnaryExpressionTranspiler.cs +++ b/src/War3Net.CodeAnalysis.Transpilers/JassToLua/UnaryExpressionTranspiler.cs @@ -7,6 +7,7 @@ using CSharpLua.LuaAst; +using War3Net.CodeAnalysis.Jass; using War3Net.CodeAnalysis.Jass.Syntax; namespace War3Net.CodeAnalysis.Transpilers @@ -17,7 +18,17 @@ public LuaExpressionSyntax Transpile(JassUnaryExpressionSyntax unaryExpression, { return new LuaPrefixUnaryExpressionSyntax( Transpile(unaryExpression.Expression, out type), - Transpile(unaryExpression.Operator)); + TranspileUnaryExpressionKind(unaryExpression.SyntaxKind)); + } + + public string TranspileUnaryExpressionKind(JassSyntaxKind expressionKind) + { + return expressionKind switch + { + JassSyntaxKind.UnaryPlusExpression => LuaSyntaxNode.Tokens.Plus, + JassSyntaxKind.UnaryMinusExpression => LuaSyntaxNode.Tokens.Sub, + JassSyntaxKind.LogicalNotExpression => LuaSyntaxNode.Keyword.Not, + }; } } } \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Transpilers/JassToLua/UnaryOperatorTranspiler.cs b/src/War3Net.CodeAnalysis.Transpilers/JassToLua/UnaryOperatorTranspiler.cs deleted file mode 100644 index 003cf001..00000000 --- a/src/War3Net.CodeAnalysis.Transpilers/JassToLua/UnaryOperatorTranspiler.cs +++ /dev/null @@ -1,26 +0,0 @@ -// ------------------------------------------------------------------------------ -// -// Licensed under the MIT license. -// See the LICENSE file in the project root for more information. -// -// ------------------------------------------------------------------------------ - -using CSharpLua.LuaAst; - -using War3Net.CodeAnalysis.Jass.Syntax; - -namespace War3Net.CodeAnalysis.Transpilers -{ - public partial class JassToLuaTranspiler - { - public string Transpile(UnaryOperatorType unaryOperator) - { - return unaryOperator switch - { - UnaryOperatorType.Plus => LuaSyntaxNode.Tokens.Plus, - UnaryOperatorType.Minus => LuaSyntaxNode.Tokens.Sub, - UnaryOperatorType.Not => LuaSyntaxNode.Keyword.Not, - }; - } - } -} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Transpilers/JassToLua/VariableDeclaratorTranspiler.cs b/src/War3Net.CodeAnalysis.Transpilers/JassToLua/VariableDeclaratorTranspiler.cs index adc090f5..9cbf7017 100644 --- a/src/War3Net.CodeAnalysis.Transpilers/JassToLua/VariableDeclaratorTranspiler.cs +++ b/src/War3Net.CodeAnalysis.Transpilers/JassToLua/VariableDeclaratorTranspiler.cs @@ -13,7 +13,7 @@ namespace War3Net.CodeAnalysis.Transpilers { public partial class JassToLuaTranspiler { - public LuaLocalDeclarationStatementSyntax Transpile(IVariableDeclaratorSyntax declarator, bool isLocalDeclaration) + public LuaLocalDeclarationStatementSyntax Transpile(JassVariableOrArrayDeclaratorSyntax declarator, bool isLocalDeclaration) { RegisterVariableType(declarator, isLocalDeclaration); diff --git a/src/War3Net.CodeAnalysis.Transpilers/JassToLua/VariableReferenceExpressionTranspiler.cs b/src/War3Net.CodeAnalysis.Transpilers/JassToLua/VariableReferenceExpressionTranspiler.cs deleted file mode 100644 index 8f6791e8..00000000 --- a/src/War3Net.CodeAnalysis.Transpilers/JassToLua/VariableReferenceExpressionTranspiler.cs +++ /dev/null @@ -1,23 +0,0 @@ -// ------------------------------------------------------------------------------ -// -// Licensed under the MIT license. -// See the LICENSE file in the project root for more information. -// -// ------------------------------------------------------------------------------ - -using CSharpLua.LuaAst; - -using War3Net.CodeAnalysis.Jass.Syntax; - -namespace War3Net.CodeAnalysis.Transpilers -{ - public partial class JassToLuaTranspiler - { - public LuaExpressionSyntax Transpile(JassVariableReferenceExpressionSyntax variableReferenceExpression, out JassTypeSyntax type) - { - type = GetVariableType(variableReferenceExpression.IdentifierName); - - return Transpile(variableReferenceExpression.IdentifierName); - } - } -} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis.Transpilers/JassToLuaTranspiler.cs b/src/War3Net.CodeAnalysis.Transpilers/JassToLuaTranspiler.cs index b15b4914..8421ce1f 100644 --- a/src/War3Net.CodeAnalysis.Transpilers/JassToLuaTranspiler.cs +++ b/src/War3Net.CodeAnalysis.Transpilers/JassToLuaTranspiler.cs @@ -39,19 +39,25 @@ public void RegisterJassFile(JassCompilationUnitSyntax compilationUnit) { foreach (var declaration in compilationUnit.Declarations) { - if (declaration is JassGlobalDeclarationListSyntax globalDeclarationList) + if (declaration is JassGlobalsDeclarationSyntax globalsDeclaration) { - foreach (var global in globalDeclarationList.Globals) + foreach (var globalDeclaration in globalsDeclaration.GlobalDeclarations) { - if (global is JassGlobalDeclarationSyntax globalDeclaration) + switch (globalDeclaration) { - RegisterVariableType(globalDeclaration.Declarator, false); + case JassGlobalConstantDeclarationSyntax globalConstantDeclaration: + RegisterVariableType(globalConstantDeclaration); + break; + + case JassGlobalVariableDeclarationSyntax globalVariableDeclaration: + RegisterVariableType(globalVariableDeclaration.Declarator, false); + break; } } } else if (declaration is JassNativeFunctionDeclarationSyntax nativeFunctionDeclaration) { - RegisterFunctionReturnType(nativeFunctionDeclaration.FunctionDeclarator); + RegisterFunctionReturnType(nativeFunctionDeclaration); } else if (declaration is JassFunctionDeclarationSyntax functionDeclaration) { @@ -87,33 +93,43 @@ internal void ClearLocalTypes() internal void RegisterFunctionReturnType(JassFunctionDeclaratorSyntax functionDeclarator) { - _functionReturnTypes.Add(functionDeclarator.IdentifierName.Name, functionDeclarator.ReturnType); + _functionReturnTypes.Add(functionDeclarator.IdentifierName.Token.Text, functionDeclarator.ReturnClause.ReturnType); + } + + internal void RegisterFunctionReturnType(JassNativeFunctionDeclarationSyntax nativeFunctionDeclaration) + { + _functionReturnTypes.Add(nativeFunctionDeclaration.IdentifierName.Token.Text, nativeFunctionDeclaration.ReturnClause.ReturnType); } - private void RegisterVariableType(IVariableDeclaratorSyntax declarator, bool isLocalDeclaration) + private void RegisterVariableType(JassVariableOrArrayDeclaratorSyntax declarator, bool isLocalDeclaration) { switch (declarator) { - case JassArrayDeclaratorSyntax arrayDeclarator: (isLocalDeclaration ? _localTypes : _globalTypes).Add(arrayDeclarator.IdentifierName.Name, arrayDeclarator.Type); break; - case JassVariableDeclaratorSyntax variableDeclarator: (isLocalDeclaration ? _localTypes : _globalTypes).Add(variableDeclarator.IdentifierName.Name, variableDeclarator.Type); break; + case JassArrayDeclaratorSyntax arrayDeclarator: (isLocalDeclaration ? _localTypes : _globalTypes).Add(arrayDeclarator.IdentifierName.Token.Text, arrayDeclarator.Type); break; + case JassVariableDeclaratorSyntax variableDeclarator: (isLocalDeclaration ? _localTypes : _globalTypes).Add(variableDeclarator.IdentifierName.Token.Text, variableDeclarator.Type); break; } } + private void RegisterVariableType(JassGlobalConstantDeclarationSyntax globalConstantDeclaration) + { + _globalTypes.Add(globalConstantDeclaration.IdentifierName.Token.Text, globalConstantDeclaration.Type); + } + private void RegisterLocalVariableType(JassParameterSyntax parameter) { - _localTypes.Add(parameter.IdentifierName.Name, parameter.Type); + _localTypes.Add(parameter.IdentifierName.Token.Text, parameter.Type); } private JassTypeSyntax GetFunctionReturnType(JassIdentifierNameSyntax functionName) { - return _functionReturnTypes.TryGetValue(functionName.Name, out var type) + return _functionReturnTypes.TryGetValue(functionName.Token.Text, out var type) ? type : throw new KeyNotFoundException($"Function '{functionName}' could not be found."); } private JassTypeSyntax GetVariableType(JassIdentifierNameSyntax variableName) { - return (_localTypes.TryGetValue(variableName.Name, out var type) || _globalTypes.TryGetValue(variableName.Name, out type)) + return (_localTypes.TryGetValue(variableName.Token.Text, out var type) || _globalTypes.TryGetValue(variableName.Token.Text, out type)) ? type : throw new KeyNotFoundException($"Variable '{variableName}' could not be found."); } diff --git a/src/War3Net.CodeAnalysis.Transpilers/PolyglotJassToLuaTranspiler.cs b/src/War3Net.CodeAnalysis.Transpilers/PolyglotJassToLuaTranspiler.cs index 917808ba..6f499f3d 100644 --- a/src/War3Net.CodeAnalysis.Transpilers/PolyglotJassToLuaTranspiler.cs +++ b/src/War3Net.CodeAnalysis.Transpilers/PolyglotJassToLuaTranspiler.cs @@ -17,7 +17,7 @@ namespace War3Net.CodeAnalysis.Transpilers { /// - /// Special that can handle lua code embedded in the jass code using //! beginusercode and //! endusercode. + /// Special that can handle lua code embedded in the jass code using //! beginusercode and //! endusercode. /// public class PolyglotJassToLuaTranspiler { @@ -38,195 +38,172 @@ public PolyglotJassToLuaTranspiler( _writer = writer; _isUserCode = false; - _scriptContext = JassScriptContext.Declarations; + _scriptContext = JassScriptContext.TopLevelDeclarations; } private enum JassScriptContext { - Declarations, - Globals, - Statements, + TopLevelDeclarations, + GlobalsBlock, + FunctionBody, } public void Transpile(string input) { using var reader = new StringReader(input); + var lineNumber = 0; while (true) { + lineNumber++; var line = reader.ReadLine(); if (line is null) { break; } - if (JassSyntaxFactory.TryParseComment(line, out var comment)) + var trimmed = line.AsSpan().TrimStart(); + if (trimmed.StartsWith("//")) { - if (string.Equals(comment.Comment, "! beginusercode", StringComparison.Ordinal)) + var comment = trimmed.TrimEnd(); + + if (comment.Equals("//! beginusercode", StringComparison.Ordinal)) { if (_isUserCode) { - throw new ArgumentException("Invalid: beginusercode", nameof(input)); + throw new ArgumentException("Unexpected //! beginusercode", nameof(input)); } _isUserCode = true; continue; } - else if (string.Equals(comment.Comment, "! endusercode", StringComparison.Ordinal)) + else if (comment.Equals("//! endusercode", StringComparison.Ordinal)) { if (!_isUserCode) { - throw new ArgumentException("Invalid: endusercode", nameof(input)); + throw new ArgumentException("Unexpected //! endusercode", nameof(input)); } _isUserCode = false; continue; } + else if (_isUserCode) + { + _writer.WriteLine(line); + } + else if (!_transpiler.IgnoreComments) + { + _renderer.Render(new LuaShortCommentStatement(trimmed[2..].ToString())); + } } - - if (_isUserCode) + else if (_isUserCode) { _writer.WriteLine(line); } - else + else if (JassSyntaxFactory.TryParseScriptLine(line, out var scriptLine)) { - switch (_scriptContext) + try { - case JassScriptContext.Declarations: - Transpile(JassSyntaxFactory.ParseDeclarationLine(line)); - break; - - case JassScriptContext.Globals: - Transpile(JassSyntaxFactory.ParseGlobalLine(line)); - break; - - case JassScriptContext.Statements: - Transpile(JassSyntaxFactory.ParseStatementLine(line)); - break; + Transpile(scriptLine.Value); } + catch (Exception ex) + { + throw new ArgumentException($"Failed to transpile JASS on line {lineNumber}: {line}", nameof(input), ex); + } + } + else + { + throw new ArgumentException($"Invalid JASS on line {lineNumber}: {line}", nameof(input)); } } } - private void Transpile(IDeclarationLineSyntax declarationLine) + private void Transpile(JassSyntaxNodeOrToken scriptLine) + { + if (scriptLine.TryPickToken(out var token, out var node)) + { + Transpile(token); + } + else + { + Transpile(node); + } + } + + private void Transpile(JassSyntaxToken token) { - switch (declarationLine) + switch (token.SyntaxKind) { - case JassCommentSyntax comment: - _renderer.Render((LuaShortCommentStatement)_transpiler.Transpile(comment)); + case JassSyntaxKind.ElseKeyword: + _renderer.RenderElse(); break; - case JassEmptySyntax: - if (!_transpiler.IgnoreEmptyDeclarations) - { - _writer.WriteLine(); - } + case JassSyntaxKind.EndFunctionKeyword: + _renderer.RenderEnd(); + _transpiler.ClearLocalTypes(); + _scriptContext = JassScriptContext.TopLevelDeclarations; + break; + case JassSyntaxKind.EndGlobalsKeyword: + _scriptContext = JassScriptContext.TopLevelDeclarations; break; - case JassTypeDeclarationSyntax: + case JassSyntaxKind.EndIfKeyword: + _renderer.RenderEnd(); break; - case JassNativeFunctionDeclarationSyntax nativeFunctionDeclaration: - _transpiler.RegisterFunctionReturnType(nativeFunctionDeclaration.FunctionDeclarator); + case JassSyntaxKind.EndLoopKeyword: + _renderer.RenderEnd(); break; - case JassGlobalsCustomScriptAction: - _scriptContext = JassScriptContext.Globals; + case JassSyntaxKind.GlobalsKeyword: + _scriptContext = JassScriptContext.GlobalsBlock; break; - case JassFunctionCustomScriptAction functionCustomScriptAction: - _renderer.RenderFunctionDeclarator(_transpiler.Transpile(functionCustomScriptAction.FunctionDeclarator)); - _scriptContext = JassScriptContext.Statements; + case JassSyntaxKind.LoopKeyword: + _renderer.RenderLoop(); break; } } - private void Transpile(IGlobalLineSyntax globalLine) + private void Transpile(JassSyntaxNode node) { - switch (globalLine) + switch (node) { - case JassCommentSyntax comment: - _renderer.Render((LuaShortCommentStatement)_transpiler.Transpile(comment)); - break; - - case JassEmptySyntax: - if (!_transpiler.IgnoreEmptyDeclarations) - { - _writer.WriteLine(); - } - - break; - case JassGlobalDeclarationSyntax globalDeclaration: _renderer.Render((LuaLocalDeclarationStatementSyntax)_transpiler.Transpile(globalDeclaration)); break; - case JassEndGlobalsCustomScriptAction: - _scriptContext = JassScriptContext.Declarations; - break; - } - } - - private void Transpile(IStatementLineSyntax statementLine) - { - switch (statementLine) - { case JassCallStatementSyntax callStatement: _renderer.Render((LuaExpressionStatementSyntax)_transpiler.Transpile(callStatement)); break; - case JassCommentSyntax comment: - _renderer.Render((LuaShortCommentStatement)_transpiler.Transpile(comment)); - break; - - case JassDebugCustomScriptAction: + case JassDebugStatementSyntax: throw new NotSupportedException(); - case JassElseCustomScriptAction: - _renderer.RenderElse(); - break; - - case JassElseIfCustomScriptAction elseIfCustomScriptAction: - _renderer.RenderElseIf(_transpiler.Transpile(elseIfCustomScriptAction.Condition, out _)); - break; - - case JassEmptySyntax: - if (!_transpiler.IgnoreEmptyStatements) - { - _writer.WriteLine(); - } - - break; - - case JassEndFunctionCustomScriptAction: - _renderer.RenderEnd(); - _transpiler.ClearLocalTypes(); - _scriptContext = JassScriptContext.Declarations; - break; - - case JassEndIfCustomScriptAction: - _renderer.RenderEnd(); - break; - - case JassEndLoopCustomScriptAction: - _renderer.RenderEnd(); + case JassElseIfClauseDeclaratorSyntax elseIfClauseDeclarator: + _renderer.RenderElseIf(_transpiler.Transpile(elseIfClauseDeclarator.Condition, out _)); break; case JassExitStatementSyntax exitStatement: _renderer.Render((LuaIfStatementSyntax)_transpiler.Transpile(exitStatement)); break; - case JassIfCustomScriptAction ifCustomScriptAction: - _renderer.RenderIf(_transpiler.Transpile(ifCustomScriptAction.Condition, out _)); + case JassFunctionDeclaratorSyntax functionDeclarator: + _renderer.RenderFunctionDeclarator(_transpiler.Transpile(functionDeclarator)); + _scriptContext = JassScriptContext.FunctionBody; + break; + + case JassIfClauseDeclaratorSyntax ifClauseDeclarator: + _renderer.RenderIf(_transpiler.Transpile(ifClauseDeclarator.Condition, out _)); break; case JassLocalVariableDeclarationStatementSyntax localVariableDeclarationStatement: _renderer.Render((LuaLocalDeclarationStatementSyntax)_transpiler.Transpile(localVariableDeclarationStatement)); break; - case JassLoopCustomScriptAction: - _renderer.RenderLoop(); + case JassNativeFunctionDeclarationSyntax nativeFunctionDeclaration: + _transpiler.RegisterFunctionReturnType(nativeFunctionDeclaration); break; case JassReturnStatementSyntax returnStatement: @@ -236,6 +213,9 @@ private void Transpile(IStatementLineSyntax statementLine) case JassSetStatementSyntax setStatement: _renderer.Render((LuaExpressionStatementSyntax)_transpiler.Transpile(setStatement)); break; + + case JassTypeDeclarationSyntax: + break; } } } diff --git a/src/War3Net.CodeAnalysis/IfThenElseParser.cs b/src/War3Net.CodeAnalysis/IfThenElseParser.cs index 88c7ca61..30f7152c 100644 --- a/src/War3Net.CodeAnalysis/IfThenElseParser.cs +++ b/src/War3Net.CodeAnalysis/IfThenElseParser.cs @@ -185,6 +185,12 @@ public override bool TryParse( if (state.Location <= terminatorStartLoc) { + leadingExpecteds.Dispose(); + elseIfExpecteds.Dispose(); + elseExpecteds.Dispose(); + endIfExpecteds.Dispose(); + itemExpecteds.Dispose(); + throw new InvalidOperationException("IfThenElse() used with an elseif parser which consumed no input"); } @@ -252,6 +258,12 @@ public override bool TryParse( if (state.Location <= itemStartLoc) { + leadingExpecteds.Dispose(); + elseIfExpecteds.Dispose(); + elseExpecteds.Dispose(); + endIfExpecteds.Dispose(); + itemExpecteds.Dispose(); + throw new InvalidOperationException("IfThenElse() used with a parser which consumed no input"); } diff --git a/src/War3Net.CodeAnalysis/IndentedTextWriter.cs b/src/War3Net.CodeAnalysis/IndentedTextWriter.cs new file mode 100644 index 00000000..2ebf5132 --- /dev/null +++ b/src/War3Net.CodeAnalysis/IndentedTextWriter.cs @@ -0,0 +1,143 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using System; +using System.IO; +using System.Text; + +namespace War3Net.CodeAnalysis +{ + public sealed class IndentedTextWriter : TextWriter + { + private const string DefaultIndentString = " "; + + private readonly TextWriter _writer; + private readonly string _indentString; + private int _indentLevel; + private bool _needsIndent; + + public IndentedTextWriter(TextWriter writer) + : this(writer, DefaultIndentString) + { + } + + public IndentedTextWriter(TextWriter writer, string indentString) + { + _writer = writer ?? throw new ArgumentNullException(nameof(writer)); + _indentString = indentString ?? throw new ArgumentNullException(nameof(indentString)); + _needsIndent = true; + } + + /// The existing from which to copy indent and newline strings. + public static IndentedTextWriter New(IndentedTextWriter writer) + { + if (writer is null) + { + throw new ArgumentNullException(nameof(writer)); + } + + var stringWriter = new StringWriter + { + NewLine = writer.NewLine, + }; + + return new IndentedTextWriter(stringWriter, writer.IndentString); + } + + public override Encoding Encoding => _writer.Encoding; + + public override string NewLine => _writer.NewLine; + + public string IndentString => _indentString; + + public int IndentLevel + { + get => _indentLevel; + set => _indentLevel = value < 0 ? 0 : value; + } + + public void Indent() => _indentLevel++; + + public void Unindent() + { + if (_indentLevel == 0) + { + throw new InvalidOperationException("Cannot unindent when indent level is 0."); + } + + _indentLevel--; + } + + public override void Write(char value) + { + if (_needsIndent) + { + WriteIndent(); + } + + _writer.Write(value); + + _needsIndent = value == '\n' || value == '\r'; + } + + public override void Write(string? value) + { + if (string.IsNullOrEmpty(value)) + { + return; + } + + if (_needsIndent) + { + WriteIndent(); + } + + _writer.Write(value); + + _needsIndent = value.EndsWith('\n') || value.EndsWith('\r'); + } + + public override void WriteLine() + { + _writer.WriteLine(); + _needsIndent = true; + } + + public override void WriteLine(string? value) + { + if (_needsIndent) + { + WriteIndent(); + } + + _writer.WriteLine(value); + _needsIndent = true; + } + + public override string? ToString() => _writer.ToString(); + + protected override void Dispose(bool disposing) + { + if (disposing) + { + _writer.Dispose(); + } + + base.Dispose(disposing); + } + + private void WriteIndent() + { + for (var i = 0; i < _indentLevel; i++) + { + _writer.Write(_indentString); + } + + _needsIndent = false; + } + } +} \ No newline at end of file diff --git a/src/War3Net.CodeAnalysis/SeparatedParser.cs b/src/War3Net.CodeAnalysis/SeparatedParser.cs index c44183dc..3094bed4 100644 --- a/src/War3Net.CodeAnalysis/SeparatedParser.cs +++ b/src/War3Net.CodeAnalysis/SeparatedParser.cs @@ -30,8 +30,15 @@ public override bool TryParse( ref PooledList> expecteds, [MaybeNullWhen(false)] out SeparatedSyntaxList result) { + var startLoc = state.Location; if (!_itemParser.TryParse(ref state, ref expecteds, out var firstResult)) { + if (state.Location > startLoc) + { + result = null; + return false; + } + result = SeparatedSyntaxList.Empty; return true; } @@ -72,6 +79,8 @@ public override bool TryParse( if (state.Location <= itemStartLoc) { + childExpecteds.Dispose(); + throw new InvalidOperationException("Separated() used with a parser which consumed no input"); } diff --git a/src/War3Net.CodeAnalysis/SeparatedSyntaxList.cs b/src/War3Net.CodeAnalysis/SeparatedSyntaxList.cs index 0285eeb4..b01044df 100644 --- a/src/War3Net.CodeAnalysis/SeparatedSyntaxList.cs +++ b/src/War3Net.CodeAnalysis/SeparatedSyntaxList.cs @@ -34,6 +34,13 @@ private SeparatedSyntaxList(ImmutableArray items, ImmutableArray Separators => _separators; + public bool IsEmpty => _items.IsEmpty; + + public static SeparatedSyntaxList Create(TItem item) + { + return new SeparatedSyntaxList(ImmutableArray.Create(item), ImmutableArray.Empty); + } + public static SeparatedSyntaxList Create(ImmutableArray items, ImmutableArray separators) { if (items.IsEmpty) @@ -42,6 +49,8 @@ public static SeparatedSyntaxList Create(ImmutableArray(); } + internal Builder(TItem firstItem, int initialCapacity) + { + _itemBuilder = ImmutableArray.CreateBuilder(initialCapacity); + _itemBuilder.Add(firstItem); + _separatorBuilder = ImmutableArray.CreateBuilder(initialCapacity - 1); + } + public void Add(TSeparator separator, TItem item) { _itemBuilder.Add(item); diff --git a/src/War3Net.CodeAnalysis/UntilWithLeadingParser.cs b/src/War3Net.CodeAnalysis/UntilWithLeadingParser.cs index 7a15c3dc..35bd29a9 100644 --- a/src/War3Net.CodeAnalysis/UntilWithLeadingParser.cs +++ b/src/War3Net.CodeAnalysis/UntilWithLeadingParser.cs @@ -113,6 +113,10 @@ public override bool TryParse( if (state.Location <= itemStartLoc) { + leadingExpecteds.Dispose(); + closeExpecteds.Dispose(); + itemExpecteds.Dispose(); + throw new InvalidOperationException("UntilWithLeading() used with a parser which consumed no input"); } diff --git a/src/War3Net.Common/EnumConvert.cs b/src/War3Net.Common/EnumConvert.cs index 8980f14c..dbe0bf37 100644 --- a/src/War3Net.Common/EnumConvert.cs +++ b/src/War3Net.Common/EnumConvert.cs @@ -16,6 +16,16 @@ namespace War3Net.Common public static class EnumConvert where TEnum : struct, Enum { + public static TEnum FromByteRaw(byte value) + { + if (Enum.GetUnderlyingType(typeof(TEnum)) != typeof(byte)) + { + throw new InvalidOperationException($"FromByteRaw requires that enum of type {typeof(TEnum).Name} uses byte as its underlying type."); + } + + return Unsafe.As(ref value); + } + public static TEnum FromByte(byte value, bool allowNoFlags = true) { if (Enum.GetUnderlyingType(typeof(TEnum)) != typeof(byte)) @@ -36,6 +46,16 @@ public static TEnum FromByte(byte value, bool allowNoFlags = true) return result; } + public static TEnum FromSByteRaw(sbyte value) + { + if (Enum.GetUnderlyingType(typeof(TEnum)) != typeof(sbyte)) + { + throw new InvalidOperationException($"FromSByteRaw requires that enum of type {typeof(TEnum).Name} uses sbyte as its underlying type."); + } + + return Unsafe.As(ref value); + } + public static TEnum FromSByte(sbyte value, bool allowNoFlags = true) { if (Enum.GetUnderlyingType(typeof(TEnum)) != typeof(sbyte)) @@ -56,6 +76,16 @@ public static TEnum FromSByte(sbyte value, bool allowNoFlags = true) return result; } + public static TEnum FromInt16Raw(short value) + { + if (Enum.GetUnderlyingType(typeof(TEnum)) != typeof(short)) + { + throw new InvalidOperationException($"FromInt16Raw requires that enum of type {typeof(TEnum).Name} uses short as its underlying type."); + } + + return Unsafe.As(ref value); + } + public static TEnum FromInt16(short value, bool allowNoFlags = true) { if (Enum.GetUnderlyingType(typeof(TEnum)) != typeof(short)) @@ -76,6 +106,16 @@ public static TEnum FromInt16(short value, bool allowNoFlags = true) return result; } + public static TEnum FromUInt16Raw(ushort value) + { + if (Enum.GetUnderlyingType(typeof(TEnum)) != typeof(ushort)) + { + throw new InvalidOperationException($"FromUInt16Raw requires that enum of type {typeof(TEnum).Name} uses ushort as its underlying type."); + } + + return Unsafe.As(ref value); + } + public static TEnum FromUInt16(ushort value, bool allowNoFlags = true) { if (Enum.GetUnderlyingType(typeof(TEnum)) != typeof(ushort)) @@ -96,6 +136,16 @@ public static TEnum FromUInt16(ushort value, bool allowNoFlags = true) return result; } + public static TEnum FromInt32Raw(int value) + { + if (Enum.GetUnderlyingType(typeof(TEnum)) != typeof(int)) + { + throw new InvalidOperationException($"FromInt32Raw requires that enum of type {typeof(TEnum).Name} uses int as its underlying type."); + } + + return Unsafe.As(ref value); + } + public static TEnum FromInt32(int value, bool allowNoFlags = true) { if (Enum.GetUnderlyingType(typeof(TEnum)) != typeof(int)) @@ -116,6 +166,16 @@ public static TEnum FromInt32(int value, bool allowNoFlags = true) return result; } + public static TEnum FromUInt32Raw(uint value) + { + if (Enum.GetUnderlyingType(typeof(TEnum)) != typeof(uint)) + { + throw new InvalidOperationException($"FromUInt32Raw requires that enum of type {typeof(TEnum).Name} uses uint as its underlying type."); + } + + return Unsafe.As(ref value); + } + public static TEnum FromUInt32(uint value, bool allowNoFlags = true) { if (Enum.GetUnderlyingType(typeof(TEnum)) != typeof(uint)) @@ -136,6 +196,16 @@ public static TEnum FromUInt32(uint value, bool allowNoFlags = true) return result; } + public static TEnum FromInt64Raw(long value) + { + if (Enum.GetUnderlyingType(typeof(TEnum)) != typeof(long)) + { + throw new InvalidOperationException($"FromInt64Raw requires that enum of type {typeof(TEnum).Name} uses long as its underlying type."); + } + + return Unsafe.As(ref value); + } + public static TEnum FromInt64(long value, bool allowNoFlags = true) { if (Enum.GetUnderlyingType(typeof(TEnum)) != typeof(long)) @@ -156,6 +226,16 @@ public static TEnum FromInt64(long value, bool allowNoFlags = true) return result; } + public static TEnum FromUInt64Raw(ulong value) + { + if (Enum.GetUnderlyingType(typeof(TEnum)) != typeof(ulong)) + { + throw new InvalidOperationException($"FromUInt64Raw requires that enum of type {typeof(TEnum).Name} uses ulong as its underlying type."); + } + + return Unsafe.As(ref value); + } + public static TEnum FromUInt64(ulong value, bool allowNoFlags = true) { if (Enum.GetUnderlyingType(typeof(TEnum)) != typeof(ulong)) diff --git a/src/War3Net.Common/Extensions/JsonElementExtensions.cs b/src/War3Net.Common/Extensions/JsonElementExtensions.cs index 20c47511..8832b413 100644 --- a/src/War3Net.Common/Extensions/JsonElementExtensions.cs +++ b/src/War3Net.Common/Extensions/JsonElementExtensions.cs @@ -20,6 +20,14 @@ public static TEnum GetByte(this JsonElement jsonElement) : Enum.Parse(jsonElement.GetString()); } + public static TEnum GetByteRaw(this JsonElement jsonElement) + where TEnum : struct, Enum + { + return jsonElement.ValueKind == JsonValueKind.Number + ? EnumConvert.FromByteRaw(jsonElement.GetByte()) + : Enum.Parse(jsonElement.GetString()); + } + public static TEnum GetInt32(this JsonElement jsonElement) where TEnum : struct, Enum { @@ -28,18 +36,38 @@ public static TEnum GetInt32(this JsonElement jsonElement) : Enum.Parse(jsonElement.GetString()); } + public static TEnum GetInt32Raw(this JsonElement jsonElement) + where TEnum : struct, Enum + { + return jsonElement.ValueKind == JsonValueKind.Number + ? EnumConvert.FromInt32Raw(jsonElement.GetInt32()) + : Enum.Parse(jsonElement.GetString()); + } + public static TEnum GetByte(this JsonElement jsonElement, ReadOnlySpan propertyName) where TEnum : struct, Enum { return jsonElement.GetProperty(propertyName).GetByte(); } + public static TEnum GetByteRaw(this JsonElement jsonElement, ReadOnlySpan propertyName) + where TEnum : struct, Enum + { + return jsonElement.GetProperty(propertyName).GetByteRaw(); + } + public static TEnum GetInt32(this JsonElement jsonElement, ReadOnlySpan propertyName) where TEnum : struct, Enum { return jsonElement.GetProperty(propertyName).GetInt32(); } + public static TEnum GetInt32Raw(this JsonElement jsonElement, ReadOnlySpan propertyName) + where TEnum : struct, Enum + { + return jsonElement.GetProperty(propertyName).GetInt32Raw(); + } + public static JsonElement.ArrayEnumerator EnumerateArray(this JsonElement jsonElement, ReadOnlySpan propertyName) => jsonElement.GetProperty(propertyName).EnumerateArray(); public static JsonElement.ObjectEnumerator EnumerateObject(this JsonElement jsonElement, ReadOnlySpan propertyName) => jsonElement.GetProperty(propertyName).EnumerateObject(); diff --git a/submodules/CSharp.lua b/submodules/CSharp.lua index 22c13bdf..59801751 160000 --- a/submodules/CSharp.lua +++ b/submodules/CSharp.lua @@ -1 +1 @@ -Subproject commit 22c13bdfc5dcadeb433000cd7af9f2ddff76f05b +Subproject commit 5980175154b7e42212dff6225e11750ed402d70b diff --git a/tests/War3Net.Build.Core.Tests/Info/MapInfoTests.cs b/tests/War3Net.Build.Core.Tests/Info/MapInfoTests.cs index 193622ed..636828a6 100644 --- a/tests/War3Net.Build.Core.Tests/Info/MapInfoTests.cs +++ b/tests/War3Net.Build.Core.Tests/Info/MapInfoTests.cs @@ -5,13 +5,17 @@ // // ------------------------------------------------------------------------------ +using System; using System.Collections.Generic; using System.IO; +using System.Text.Json; +using System.Text.Json.Serialization; using Microsoft.VisualStudio.TestTools.UnitTesting; using War3Net.Build.Extensions; using War3Net.Build.Info; +using War3Net.Build.Serialization.Json; using War3Net.TestTools.UnitTesting; namespace War3Net.Build.Core.Tests.Info @@ -174,6 +178,36 @@ public void TestParseReforgedMapInfo(string mapInfoFilePath, bool expectCustomAb Assert.AreEqual(expectSupportedModes, mapInfo.SupportedModes); } + [TestMethod] + public void TestJsonSerializeRegression() + { + var mapInfo = Build.MapFactory.Info(); + + var options = new JsonSerializerOptions(); + options.Converters.Add(new JsonMapInfoConverter()); + options.Converters.Add(new JsonStringEnumConverter()); + + var json = JsonSerializer.Serialize(mapInfo, options); + _ = JsonSerializer.Deserialize(json, options); + } + + [TestMethod] + public void TestJsonSerializeInvalidEditorVersion() + { + var mapInfo = Build.MapFactory.Info(); + mapInfo.EditorVersion = (EditorVersion)123456; + + var options = new JsonSerializerOptions(); + options.Converters.Add(new JsonMapInfoConverter()); + options.Converters.Add(new JsonStringEnumConverter()); + + var json = JsonSerializer.Serialize(mapInfo, options); + var result = JsonSerializer.Deserialize(json, options); + + Assert.IsNotNull(result); + Assert.AreEqual(123456, (int)result.EditorVersion); + } + private static void TestParseMapInfoInternal(string mapInfoFilePath) { ParseTestHelper.RunBinaryRWTest( diff --git a/tests/War3Net.Build.Core.Tests/Providers/GameBuildsProviderTests.cs b/tests/War3Net.Build.Core.Tests/Providers/GameBuildsProviderTests.cs index e9af8852..69d3b129 100644 --- a/tests/War3Net.Build.Core.Tests/Providers/GameBuildsProviderTests.cs +++ b/tests/War3Net.Build.Core.Tests/Providers/GameBuildsProviderTests.cs @@ -66,7 +66,7 @@ public void TestVersionAndEditorVersionMatch(Version version, EditorVersion? edi return mappings #if !ENABLE_FLAKY_TESTS - .Where(kvp => GameBuildsProvider.GetGameBuilds(kvp.Key).Any()) + .Where(kvp => GameBuildsProvider.GetGameBuilds(kvp.Key).Count > 0) #endif .Select(kvp => new object?[] { kvp.Key, kvp.Value }); } diff --git a/tests/War3Net.Build.Tests/MapScriptBuilder/Audio/InitSoundsTests.cs b/tests/War3Net.Build.Tests/MapScriptBuilder/Audio/InitSoundsTests.cs index e239e501..689f279b 100644 --- a/tests/War3Net.Build.Tests/MapScriptBuilder/Audio/InitSoundsTests.cs +++ b/tests/War3Net.Build.Tests/MapScriptBuilder/Audio/InitSoundsTests.cs @@ -19,18 +19,18 @@ public partial class MapScriptBuilderTests [DynamicData(nameof(GetTestDataInitSounds), DynamicDataSourceType.Method)] public void TestBodyInitSounds(MapScriptBuilderTestData testData) { - var expected = testData.DeclaredFunctions["InitSounds"]; - var actual = testData.MapScriptBuilder.InitSounds(testData.Map); - - SyntaxAssert.AreEqual(expected, actual); + AssertFunctionGeneratedCorrectly( + testData, + MapScriptBuilder.GeneratedFunctionName.InitSounds, + writer => testData.MapScriptBuilder.GenerateInitSounds(testData.Map, writer)); } [FlakyTestMethod] [DynamicData(nameof(GetUnobfuscatedTestData), DynamicDataSourceType.Method)] public void TestConditionInitSounds(MapScriptBuilderTestData testData) { - var expected = testData.DeclaredFunctions.ContainsKey("InitSounds"); - var actual = testData.MapScriptBuilder.InitSoundsCondition(testData.Map); + var expected = testData.DeclaredFunctions.ContainsKey(MapScriptBuilder.GeneratedFunctionName.InitSounds); + var actual = testData.MapScriptBuilder.ShouldGenerateInitSounds(testData.Map); Assert.AreEqual(expected, actual); } @@ -39,7 +39,7 @@ public void TestConditionInitSounds(MapScriptBuilderTestData testData) { foreach (var testData in _testData) { - if (testData.DeclaredFunctions.ContainsKey("InitSounds")) + if (testData.DeclaredFunctions.ContainsKey(MapScriptBuilder.GeneratedFunctionName.InitSounds)) { yield return new object[] { testData }; } diff --git a/tests/War3Net.Build.Tests/MapScriptBuilder/Common/ConfigTests.cs b/tests/War3Net.Build.Tests/MapScriptBuilder/Common/ConfigTests.cs index b9206309..8cdf72ec 100644 --- a/tests/War3Net.Build.Tests/MapScriptBuilder/Common/ConfigTests.cs +++ b/tests/War3Net.Build.Tests/MapScriptBuilder/Common/ConfigTests.cs @@ -19,18 +19,18 @@ public partial class MapScriptBuilderTests [DynamicData(nameof(GetTestDataConfig), DynamicDataSourceType.Method)] public void TestBodyConfig(MapScriptBuilderTestData testData) { - var expected = testData.DeclaredFunctions["config"]; - var actual = testData.MapScriptBuilder.config(testData.Map); - - SyntaxAssert.AreEqual(expected, actual); + AssertFunctionGeneratedCorrectly( + testData, + MapScriptBuilder.GeneratedFunctionName.Config, + writer => testData.MapScriptBuilder.GenerateConfig(testData.Map, writer)); } [TestMethod] [DynamicData(nameof(GetUnobfuscatedTestData), DynamicDataSourceType.Method)] public void TestConditionConfig(MapScriptBuilderTestData testData) { - var expected = testData.DeclaredFunctions.ContainsKey("config"); - var actual = testData.MapScriptBuilder.configCondition(testData.Map); + var expected = testData.DeclaredFunctions.ContainsKey(MapScriptBuilder.GeneratedFunctionName.Config); + var actual = testData.MapScriptBuilder.ShouldGenerateConfig(testData.Map); Assert.AreEqual(expected, actual); } @@ -39,7 +39,7 @@ public void TestConditionConfig(MapScriptBuilderTestData testData) { foreach (var testData in GetUnobfuscatedTestData()) { - if (((MapScriptBuilderTestData)testData[0]).DeclaredFunctions.ContainsKey("config")) + if (((MapScriptBuilderTestData)testData[0]).DeclaredFunctions.ContainsKey(MapScriptBuilder.GeneratedFunctionName.Config)) { yield return testData; } diff --git a/tests/War3Net.Build.Tests/MapScriptBuilder/Common/MainTests.cs b/tests/War3Net.Build.Tests/MapScriptBuilder/Common/MainTests.cs index 5f0eb45f..bc719328 100644 --- a/tests/War3Net.Build.Tests/MapScriptBuilder/Common/MainTests.cs +++ b/tests/War3Net.Build.Tests/MapScriptBuilder/Common/MainTests.cs @@ -19,18 +19,18 @@ public partial class MapScriptBuilderTests [DynamicData(nameof(GetTestDataMain), DynamicDataSourceType.Method)] public void TestBodyMain(MapScriptBuilderTestData testData) { - var expected = testData.DeclaredFunctions["main"]; - var actual = testData.MapScriptBuilder.main(testData.Map); - - SyntaxAssert.AreEqual(expected, actual); + AssertFunctionGeneratedCorrectly( + testData, + MapScriptBuilder.GeneratedFunctionName.Main, + writer => testData.MapScriptBuilder.GenerateMain(testData.Map, writer)); } [TestMethod] [DynamicData(nameof(GetUnobfuscatedTestData), DynamicDataSourceType.Method)] public void TestConditionMain(MapScriptBuilderTestData testData) { - var expected = testData.DeclaredFunctions.ContainsKey("main"); - var actual = testData.MapScriptBuilder.mainCondition(testData.Map); + var expected = testData.DeclaredFunctions.ContainsKey(MapScriptBuilder.GeneratedFunctionName.Main); + var actual = testData.MapScriptBuilder.ShouldGenerateMain(testData.Map); Assert.AreEqual(expected, actual); } @@ -39,7 +39,7 @@ public void TestConditionMain(MapScriptBuilderTestData testData) { foreach (var testData in GetUnobfuscatedTestData()) { - if (((MapScriptBuilderTestData)testData[0]).DeclaredFunctions.ContainsKey("main")) + if (((MapScriptBuilderTestData)testData[0]).DeclaredFunctions.ContainsKey(MapScriptBuilder.GeneratedFunctionName.Main)) { yield return testData; } diff --git a/tests/War3Net.Build.Tests/MapScriptBuilder/Environment/CreateCamerasTests.cs b/tests/War3Net.Build.Tests/MapScriptBuilder/Environment/CreateCamerasTests.cs index 285b072b..c8f1a8eb 100644 --- a/tests/War3Net.Build.Tests/MapScriptBuilder/Environment/CreateCamerasTests.cs +++ b/tests/War3Net.Build.Tests/MapScriptBuilder/Environment/CreateCamerasTests.cs @@ -19,18 +19,18 @@ public partial class MapScriptBuilderTests [DynamicData(nameof(GetTestDataCreateCameras), DynamicDataSourceType.Method)] public void TestBodyCreateCameras(MapScriptBuilderTestData testData) { - var expected = testData.DeclaredFunctions["CreateCameras"]; - var actual = testData.MapScriptBuilder.CreateCameras(testData.Map); - - SyntaxAssert.AreEqual(expected, actual); + AssertFunctionGeneratedCorrectly( + testData, + MapScriptBuilder.GeneratedFunctionName.CreateCameras, + writer => testData.MapScriptBuilder.GenerateCreateCameras(testData.Map, writer)); } [TestMethod] [DynamicData(nameof(GetUnobfuscatedTestData), DynamicDataSourceType.Method)] public void TestConditionCreateCameras(MapScriptBuilderTestData testData) { - var expected = testData.DeclaredFunctions.ContainsKey("CreateCameras"); - var actual = testData.MapScriptBuilder.CreateCamerasCondition(testData.Map); + var expected = testData.DeclaredFunctions.ContainsKey(MapScriptBuilder.GeneratedFunctionName.CreateCameras); + var actual = testData.MapScriptBuilder.ShouldGenerateCreateCameras(testData.Map); Assert.AreEqual(expected, actual); } @@ -39,7 +39,7 @@ public void TestConditionCreateCameras(MapScriptBuilderTestData testData) { foreach (var testData in _testData) { - if (testData.DeclaredFunctions.ContainsKey("CreateCameras")) + if (testData.DeclaredFunctions.ContainsKey(MapScriptBuilder.GeneratedFunctionName.CreateCameras)) { yield return new object[] { testData }; } diff --git a/tests/War3Net.Build.Tests/MapScriptBuilder/Environment/CreateRegionsTests.cs b/tests/War3Net.Build.Tests/MapScriptBuilder/Environment/CreateRegionsTests.cs index 628f14a2..555b326f 100644 --- a/tests/War3Net.Build.Tests/MapScriptBuilder/Environment/CreateRegionsTests.cs +++ b/tests/War3Net.Build.Tests/MapScriptBuilder/Environment/CreateRegionsTests.cs @@ -19,18 +19,18 @@ public partial class MapScriptBuilderTests [DynamicData(nameof(GetTestDataCreateRegions), DynamicDataSourceType.Method)] public void TestBodyCreateRegions(MapScriptBuilderTestData testData) { - var expected = testData.DeclaredFunctions["CreateRegions"]; - var actual = testData.MapScriptBuilder.CreateRegions(testData.Map); - - SyntaxAssert.AreEqual(expected, actual); + AssertFunctionGeneratedCorrectly( + testData, + MapScriptBuilder.GeneratedFunctionName.CreateRegions, + writer => testData.MapScriptBuilder.GenerateCreateRegions(testData.Map, writer)); } [TestMethod] [DynamicData(nameof(GetUnobfuscatedTestData), DynamicDataSourceType.Method)] public void TestConditionCreateRegions(MapScriptBuilderTestData testData) { - var expected = testData.DeclaredFunctions.ContainsKey("CreateRegions"); - var actual = testData.MapScriptBuilder.CreateRegionsCondition(testData.Map); + var expected = testData.DeclaredFunctions.ContainsKey(MapScriptBuilder.GeneratedFunctionName.CreateRegions); + var actual = testData.MapScriptBuilder.ShouldGenerateCreateRegions(testData.Map); Assert.AreEqual(expected, actual); } @@ -39,7 +39,7 @@ public void TestConditionCreateRegions(MapScriptBuilderTestData testData) { foreach (var testData in _testData) { - if (testData.DeclaredFunctions.ContainsKey("CreateRegions")) + if (testData.DeclaredFunctions.ContainsKey(MapScriptBuilder.GeneratedFunctionName.CreateRegions)) { yield return new object[] { testData }; } diff --git a/tests/War3Net.Build.Tests/MapScriptBuilder/Info/InitAllyPrioritiesTests.cs b/tests/War3Net.Build.Tests/MapScriptBuilder/Info/InitAllyPrioritiesTests.cs index 8e7e1c1a..91f7ce07 100644 --- a/tests/War3Net.Build.Tests/MapScriptBuilder/Info/InitAllyPrioritiesTests.cs +++ b/tests/War3Net.Build.Tests/MapScriptBuilder/Info/InitAllyPrioritiesTests.cs @@ -19,18 +19,18 @@ public partial class MapScriptBuilderTests [DynamicData(nameof(GetTestDataInitAllyPriorities), DynamicDataSourceType.Method)] public void TestBodyInitAllyPriorities(MapScriptBuilderTestData testData) { - var expected = testData.DeclaredFunctions["InitAllyPriorities"]; - var actual = testData.MapScriptBuilder.InitAllyPriorities(testData.Map); - - SyntaxAssert.AreEqual(expected, actual); + AssertFunctionGeneratedCorrectly( + testData, + MapScriptBuilder.GeneratedFunctionName.InitAllyPriorities, + writer => testData.MapScriptBuilder.GenerateInitAllyPriorities(testData.Map, writer)); } [TestMethod] [DynamicData(nameof(GetUnobfuscatedTestData), DynamicDataSourceType.Method)] public void TestConditionInitAllyPriorities(MapScriptBuilderTestData testData) { - var expected = testData.DeclaredFunctions.ContainsKey("InitAllyPriorities"); - var actual = testData.MapScriptBuilder.InitAllyPrioritiesCondition(testData.Map); + var expected = testData.DeclaredFunctions.ContainsKey(MapScriptBuilder.GeneratedFunctionName.InitAllyPriorities); + var actual = testData.MapScriptBuilder.ShouldGenerateInitAllyPriorities(testData.Map); Assert.AreEqual(expected, actual); } @@ -39,7 +39,7 @@ public void TestConditionInitAllyPriorities(MapScriptBuilderTestData testData) { foreach (var testData in _testData) { - if (testData.DeclaredFunctions.ContainsKey("InitAllyPriorities")) + if (testData.DeclaredFunctions.ContainsKey(MapScriptBuilder.GeneratedFunctionName.InitAllyPriorities)) { yield return new object[] { testData }; } diff --git a/tests/War3Net.Build.Tests/MapScriptBuilder/Info/InitCustomPlayerSlotsTests.cs b/tests/War3Net.Build.Tests/MapScriptBuilder/Info/InitCustomPlayerSlotsTests.cs index bb80470e..3785643a 100644 --- a/tests/War3Net.Build.Tests/MapScriptBuilder/Info/InitCustomPlayerSlotsTests.cs +++ b/tests/War3Net.Build.Tests/MapScriptBuilder/Info/InitCustomPlayerSlotsTests.cs @@ -19,18 +19,18 @@ public partial class MapScriptBuilderTests [DynamicData(nameof(GetTestDataInitCustomPlayerSlots), DynamicDataSourceType.Method)] public void TestBodyInitCustomPlayerSlots(MapScriptBuilderTestData testData) { - var expected = testData.DeclaredFunctions["InitCustomPlayerSlots"]; - var actual = testData.MapScriptBuilder.InitCustomPlayerSlots(testData.Map); - - SyntaxAssert.AreEqual(expected, actual); + AssertFunctionGeneratedCorrectly( + testData, + MapScriptBuilder.GeneratedFunctionName.InitCustomPlayerSlots, + writer => testData.MapScriptBuilder.GenerateInitCustomPlayerSlots(testData.Map, writer)); } [TestMethod] [DynamicData(nameof(GetUnobfuscatedTestData), DynamicDataSourceType.Method)] public void TestConditionInitCustomPlayerSlots(MapScriptBuilderTestData testData) { - var expected = testData.DeclaredFunctions.ContainsKey("InitCustomPlayerSlots"); - var actual = testData.MapScriptBuilder.InitCustomPlayerSlotsCondition(testData.Map); + var expected = testData.DeclaredFunctions.ContainsKey(MapScriptBuilder.GeneratedFunctionName.InitCustomPlayerSlots); + var actual = testData.MapScriptBuilder.ShouldGenerateInitCustomPlayerSlots(testData.Map); Assert.AreEqual(expected, actual); } @@ -39,7 +39,7 @@ public void TestConditionInitCustomPlayerSlots(MapScriptBuilderTestData testData { foreach (var testData in _testData) { - if (testData.DeclaredFunctions.ContainsKey("InitCustomPlayerSlots")) + if (testData.DeclaredFunctions.ContainsKey(MapScriptBuilder.GeneratedFunctionName.InitCustomPlayerSlots)) { yield return new object[] { testData }; } diff --git a/tests/War3Net.Build.Tests/MapScriptBuilder/Info/InitCustomTeamsTests.cs b/tests/War3Net.Build.Tests/MapScriptBuilder/Info/InitCustomTeamsTests.cs index 584099fd..96e95016 100644 --- a/tests/War3Net.Build.Tests/MapScriptBuilder/Info/InitCustomTeamsTests.cs +++ b/tests/War3Net.Build.Tests/MapScriptBuilder/Info/InitCustomTeamsTests.cs @@ -23,18 +23,18 @@ public partial class MapScriptBuilderTests [DynamicData(nameof(GetTestDataInitCustomTeams), DynamicDataSourceType.Method)] public void TestBodyInitCustomTeams(MapScriptBuilderTestData testData) { - var expected = testData.DeclaredFunctions["InitCustomTeams"]; - var actual = testData.MapScriptBuilder.InitCustomTeams(testData.Map); - - SyntaxAssert.AreEqual(expected, actual); + AssertFunctionGeneratedCorrectly( + testData, + MapScriptBuilder.GeneratedFunctionName.InitCustomTeams, + writer => testData.MapScriptBuilder.GenerateInitCustomTeams(testData.Map, writer)); } [TestMethod] [DynamicData(nameof(GetUnobfuscatedTestData), DynamicDataSourceType.Method)] public void TestConditionInitCustomTeams(MapScriptBuilderTestData testData) { - var expected = testData.DeclaredFunctions.ContainsKey("InitCustomTeams"); - var actual = testData.MapScriptBuilder.InitCustomTeamsCondition(testData.Map); + var expected = testData.DeclaredFunctions.ContainsKey(MapScriptBuilder.GeneratedFunctionName.InitCustomTeams); + var actual = testData.MapScriptBuilder.ShouldGenerateInitCustomTeams(testData.Map); Assert.AreEqual(expected, actual); } @@ -43,12 +43,12 @@ public void TestConditionInitCustomTeams(MapScriptBuilderTestData testData) [DynamicData(nameof(GetUnobfuscatedTestData), DynamicDataSourceType.Method)] public void TestInvokeConditionInitCustomTeams(MapScriptBuilderTestData testData) { - var expected = testData.DeclaredFunctions.TryGetValue("config", out var config) && - config.Body.Statements.Any(statement => + var expected = testData.DeclaredFunctions.TryGetValue(MapScriptBuilder.GeneratedFunctionName.Config, out var config) && + config.Statements.Any(statement => statement is JassCallStatementSyntax callStatement && - string.Equals(callStatement.IdentifierName.Name, "InitCustomTeams", StringComparison.Ordinal)); + string.Equals(callStatement.IdentifierName.Token.Text, MapScriptBuilder.GeneratedFunctionName.InitCustomTeams, StringComparison.Ordinal)); - var actual = testData.MapScriptBuilder.InitCustomTeamsInvokeCondition(testData.Map); + var actual = testData.MapScriptBuilder.ShouldCallInitCustomTeams(testData.Map); Assert.AreEqual(expected, actual); } @@ -57,7 +57,7 @@ statement is JassCallStatementSyntax callStatement && { foreach (var testData in _testData) { - if (testData.DeclaredFunctions.ContainsKey("InitCustomTeams")) + if (testData.DeclaredFunctions.ContainsKey(MapScriptBuilder.GeneratedFunctionName.InitCustomTeams)) { yield return new object[] { testData }; } diff --git a/tests/War3Net.Build.Tests/MapScriptBuilder/Info/InitRandomGroupsTests.cs b/tests/War3Net.Build.Tests/MapScriptBuilder/Info/InitRandomGroupsTests.cs index 8456dfe3..cb1bc385 100644 --- a/tests/War3Net.Build.Tests/MapScriptBuilder/Info/InitRandomGroupsTests.cs +++ b/tests/War3Net.Build.Tests/MapScriptBuilder/Info/InitRandomGroupsTests.cs @@ -19,18 +19,18 @@ public partial class MapScriptBuilderTests [DynamicData(nameof(GetTestDataInitRandomGroups), DynamicDataSourceType.Method)] public void TestBodyInitRandomGroups(MapScriptBuilderTestData testData) { - var expected = testData.DeclaredFunctions["InitRandomGroups"]; - var actual = testData.MapScriptBuilder.InitRandomGroups(testData.Map); - - SyntaxAssert.AreEqual(expected, actual); + AssertFunctionGeneratedCorrectly( + testData, + MapScriptBuilder.GeneratedFunctionName.InitRandomGroups, + writer => testData.MapScriptBuilder.GenerateInitRandomGroups(testData.Map, writer)); } [TestMethod] [DynamicData(nameof(GetUnobfuscatedTestData), DynamicDataSourceType.Method)] public void TestConditionInitRandomGroups(MapScriptBuilderTestData testData) { - var expected = testData.DeclaredFunctions.ContainsKey("InitRandomGroups"); - var actual = testData.MapScriptBuilder.InitRandomGroupsCondition(testData.Map); + var expected = testData.DeclaredFunctions.ContainsKey(MapScriptBuilder.GeneratedFunctionName.InitRandomGroups); + var actual = testData.MapScriptBuilder.ShouldGenerateInitRandomGroups(testData.Map); Assert.AreEqual(expected, actual); } @@ -39,7 +39,7 @@ public void TestConditionInitRandomGroups(MapScriptBuilderTestData testData) { foreach (var testData in _testData) { - if (testData.DeclaredFunctions.ContainsKey("InitRandomGroups")) + if (testData.DeclaredFunctions.ContainsKey(MapScriptBuilder.GeneratedFunctionName.InitRandomGroups)) { yield return new object[] { testData }; } diff --git a/tests/War3Net.Build.Tests/MapScriptBuilder/Info/InitTechTreeTests.cs b/tests/War3Net.Build.Tests/MapScriptBuilder/Info/InitTechTreeTests.cs new file mode 100644 index 00000000..71dda39f --- /dev/null +++ b/tests/War3Net.Build.Tests/MapScriptBuilder/Info/InitTechTreeTests.cs @@ -0,0 +1,49 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using System.Collections.Generic; + +using Microsoft.VisualStudio.TestTools.UnitTesting; + +using War3Net.TestTools.UnitTesting; + +namespace War3Net.Build.Tests +{ + public partial class MapScriptBuilderTests + { + [FlakyTestMethod] + [DynamicData(nameof(GetTestDataInitTechTree), DynamicDataSourceType.Method)] + public void TestBodyInitTechTree(MapScriptBuilderTestData testData) + { + AssertFunctionGeneratedCorrectly( + testData, + MapScriptBuilder.GeneratedFunctionName.InitTechTree, + writer => testData.MapScriptBuilder.GenerateInitTechTree(testData.Map, writer)); + } + + [FlakyTestMethod] + [DynamicData(nameof(GetUnobfuscatedTestData), DynamicDataSourceType.Method)] + public void TestConditionInitTechTree(MapScriptBuilderTestData testData) + { + var expected = testData.DeclaredFunctions.ContainsKey(MapScriptBuilder.GeneratedFunctionName.InitTechTree); + var actual = testData.MapScriptBuilder.ShouldGenerateInitTechTree(testData.Map); + + Assert.AreEqual(expected, actual); + } + + private static IEnumerable GetTestDataInitTechTree() + { + foreach (var testData in GetUnobfuscatedTestData()) + { + if (((MapScriptBuilderTestData)testData[0]).DeclaredFunctions.ContainsKey(MapScriptBuilder.GeneratedFunctionName.InitTechTree)) + { + yield return testData; + } + } + } + } +} \ No newline at end of file diff --git a/tests/War3Net.Build.Tests/MapScriptBuilder/Info/InitUpgradesTests.cs b/tests/War3Net.Build.Tests/MapScriptBuilder/Info/InitUpgradesTests.cs new file mode 100644 index 00000000..b1a9b1b7 --- /dev/null +++ b/tests/War3Net.Build.Tests/MapScriptBuilder/Info/InitUpgradesTests.cs @@ -0,0 +1,49 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using System.Collections.Generic; + +using Microsoft.VisualStudio.TestTools.UnitTesting; + +using War3Net.TestTools.UnitTesting; + +namespace War3Net.Build.Tests +{ + public partial class MapScriptBuilderTests + { + [FlakyTestMethod] + [DynamicData(nameof(GetTestDataInitUpgrades), DynamicDataSourceType.Method)] + public void TestBodyInitUpgrades(MapScriptBuilderTestData testData) + { + AssertFunctionGeneratedCorrectly( + testData, + MapScriptBuilder.GeneratedFunctionName.InitUpgrades, + writer => testData.MapScriptBuilder.GenerateInitUpgrades(testData.Map, writer)); + } + + [FlakyTestMethod] + [DynamicData(nameof(GetUnobfuscatedTestData), DynamicDataSourceType.Method)] + public void TestConditionInitUpgrades(MapScriptBuilderTestData testData) + { + var expected = testData.DeclaredFunctions.ContainsKey(MapScriptBuilder.GeneratedFunctionName.InitUpgrades); + var actual = testData.MapScriptBuilder.ShouldGenerateInitUpgrades(testData.Map); + + Assert.AreEqual(expected, actual); + } + + private static IEnumerable GetTestDataInitUpgrades() + { + foreach (var testData in GetUnobfuscatedTestData()) + { + if (((MapScriptBuilderTestData)testData[0]).DeclaredFunctions.ContainsKey(MapScriptBuilder.GeneratedFunctionName.InitUpgrades)) + { + yield return testData; + } + } + } + } +} \ No newline at end of file diff --git a/tests/War3Net.Build.Tests/MapScriptBuilder/Script/InitCustomTriggersTests.cs b/tests/War3Net.Build.Tests/MapScriptBuilder/Script/InitCustomTriggersTests.cs index bdbe6ef0..80e0b6a2 100644 --- a/tests/War3Net.Build.Tests/MapScriptBuilder/Script/InitCustomTriggersTests.cs +++ b/tests/War3Net.Build.Tests/MapScriptBuilder/Script/InitCustomTriggersTests.cs @@ -19,18 +19,18 @@ public partial class MapScriptBuilderTests [DynamicData(nameof(GetTestDataInitCustomTriggers), DynamicDataSourceType.Method)] public void TestBodyInitCustomTriggers(MapScriptBuilderTestData testData) { - var expected = testData.DeclaredFunctions["InitCustomTriggers"]; - var actual = testData.MapScriptBuilder.InitCustomTriggers(testData.Map); - - SyntaxAssert.AreEqual(expected, actual); + AssertFunctionGeneratedCorrectly( + testData, + MapScriptBuilder.GeneratedFunctionName.InitCustomTriggers, + writer => testData.MapScriptBuilder.GenerateInitCustomTriggers(testData.Map, writer)); } [FlakyTestMethod] [DynamicData(nameof(GetUnobfuscatedTestData), DynamicDataSourceType.Method)] public void TestConditionInitCustomTriggers(MapScriptBuilderTestData testData) { - var expected = testData.DeclaredFunctions.ContainsKey("InitCustomTriggers"); - var actual = testData.MapScriptBuilder.InitCustomTriggersCondition(testData.Map); + var expected = testData.DeclaredFunctions.ContainsKey(MapScriptBuilder.GeneratedFunctionName.InitCustomTriggers); + var actual = testData.MapScriptBuilder.ShouldGenerateInitCustomTriggers(testData.Map); Assert.AreEqual(expected, actual); } @@ -39,7 +39,7 @@ public void TestConditionInitCustomTriggers(MapScriptBuilderTestData testData) { foreach (var testData in _testData) { - if (!testData.IsMeleeWithoutTrigger && testData.DeclaredFunctions.ContainsKey("InitCustomTriggers")) + if (!testData.IsMeleeWithoutTrigger && testData.DeclaredFunctions.ContainsKey(MapScriptBuilder.GeneratedFunctionName.InitCustomTriggers)) { yield return new object[] { testData }; } diff --git a/tests/War3Net.Build.Tests/MapScriptBuilder/Script/InitGlobalsTests.cs b/tests/War3Net.Build.Tests/MapScriptBuilder/Script/InitGlobalsTests.cs index ea2e432a..866b7001 100644 --- a/tests/War3Net.Build.Tests/MapScriptBuilder/Script/InitGlobalsTests.cs +++ b/tests/War3Net.Build.Tests/MapScriptBuilder/Script/InitGlobalsTests.cs @@ -19,18 +19,18 @@ public partial class MapScriptBuilderTests [DynamicData(nameof(GetTestDataInitGlobals), DynamicDataSourceType.Method)] public void TestBodyInitGlobals(MapScriptBuilderTestData testData) { - var expected = testData.DeclaredFunctions["InitGlobals"]; - var actual = testData.MapScriptBuilder.InitGlobals(testData.Map); - - SyntaxAssert.AreEqual(expected, actual); + AssertFunctionGeneratedCorrectly( + testData, + MapScriptBuilder.GeneratedFunctionName.InitGlobals, + writer => testData.MapScriptBuilder.GenerateInitGlobals(testData.Map, writer)); } [FlakyTestMethod] [DynamicData(nameof(GetUnobfuscatedTestData), DynamicDataSourceType.Method)] public void TestConditionInitGlobals(MapScriptBuilderTestData testData) { - var expected = testData.DeclaredFunctions.ContainsKey("InitGlobals"); - var actual = testData.MapScriptBuilder.InitGlobalsCondition(testData.Map); + var expected = testData.DeclaredFunctions.ContainsKey(MapScriptBuilder.GeneratedFunctionName.InitGlobals); + var actual = testData.MapScriptBuilder.ShouldGenerateInitGlobals(testData.Map); Assert.AreEqual(expected, actual); } @@ -39,7 +39,7 @@ public void TestConditionInitGlobals(MapScriptBuilderTestData testData) { foreach (var testData in _testData) { - if (testData.DeclaredFunctions.ContainsKey("InitGlobals")) + if (testData.DeclaredFunctions.ContainsKey(MapScriptBuilder.GeneratedFunctionName.InitGlobals)) { yield return new object[] { testData }; } diff --git a/tests/War3Net.Build.Tests/MapScriptBuilder/Script/RunInitializationTriggersTests.cs b/tests/War3Net.Build.Tests/MapScriptBuilder/Script/RunInitializationTriggersTests.cs index 438222ab..c1f78369 100644 --- a/tests/War3Net.Build.Tests/MapScriptBuilder/Script/RunInitializationTriggersTests.cs +++ b/tests/War3Net.Build.Tests/MapScriptBuilder/Script/RunInitializationTriggersTests.cs @@ -19,18 +19,18 @@ public partial class MapScriptBuilderTests [DynamicData(nameof(GetTestDataRunInitializationTriggers), DynamicDataSourceType.Method)] public void TestBodyRunInitializationTriggers(MapScriptBuilderTestData testData) { - var expected = testData.DeclaredFunctions["RunInitializationTriggers"]; - var actual = testData.MapScriptBuilder.RunInitializationTriggers(testData.Map); - - SyntaxAssert.AreEqual(expected, actual); + AssertFunctionGeneratedCorrectly( + testData, + MapScriptBuilder.GeneratedFunctionName.RunInitializationTriggers, + writer => testData.MapScriptBuilder.GenerateRunInitializationTriggers(testData.Map, writer)); } [FlakyTestMethod] [DynamicData(nameof(GetUnobfuscatedTestData), DynamicDataSourceType.Method)] public void TestConditionRunInitializationTriggers(MapScriptBuilderTestData testData) { - var expected = testData.DeclaredFunctions.ContainsKey("RunInitializationTriggers"); - var actual = testData.MapScriptBuilder.RunInitializationTriggersCondition(testData.Map); + var expected = testData.DeclaredFunctions.ContainsKey(MapScriptBuilder.GeneratedFunctionName.RunInitializationTriggers); + var actual = testData.MapScriptBuilder.ShouldGenerateRunInitializationTriggers(testData.Map); Assert.AreEqual(expected, actual); } @@ -39,7 +39,7 @@ public void TestConditionRunInitializationTriggers(MapScriptBuilderTestData test { foreach (var testData in _testData) { - if (testData.DeclaredFunctions.ContainsKey("RunInitializationTriggers")) + if (testData.DeclaredFunctions.ContainsKey(MapScriptBuilder.GeneratedFunctionName.RunInitializationTriggers)) { yield return new object[] { testData }; } diff --git a/tests/War3Net.Build.Tests/MapScriptBuilder/Widget/CreateAllDestructablesTests.cs b/tests/War3Net.Build.Tests/MapScriptBuilder/Widget/CreateAllDestructablesTests.cs index 9e774e36..e16328bb 100644 --- a/tests/War3Net.Build.Tests/MapScriptBuilder/Widget/CreateAllDestructablesTests.cs +++ b/tests/War3Net.Build.Tests/MapScriptBuilder/Widget/CreateAllDestructablesTests.cs @@ -19,18 +19,18 @@ public partial class MapScriptBuilderTests [DynamicData(nameof(GetTestDataCreateAllDestructables), DynamicDataSourceType.Method)] public void TestBodyCreateAllDestructables(MapScriptBuilderTestData testData) { - var expected = testData.DeclaredFunctions["CreateAllDestructables"]; - var actual = testData.MapScriptBuilder.CreateAllDestructables(testData.Map); - - SyntaxAssert.AreEqual(expected, actual); + AssertFunctionGeneratedCorrectly( + testData, + MapScriptBuilder.GeneratedFunctionName.CreateAllDestructables, + writer => testData.MapScriptBuilder.GenerateCreateAllDestructables(testData.Map, writer)); } [TestMethod] [DynamicData(nameof(GetUnobfuscatedTestData), DynamicDataSourceType.Method)] public void TestConditionCreateAllDestructables(MapScriptBuilderTestData testData) { - var expected = testData.DeclaredFunctions.ContainsKey("CreateAllDestructables"); - var actual = testData.MapScriptBuilder.CreateAllDestructablesCondition(testData.Map); + var expected = testData.DeclaredFunctions.ContainsKey(MapScriptBuilder.GeneratedFunctionName.CreateAllDestructables); + var actual = testData.MapScriptBuilder.ShouldGenerateCreateAllDestructables(testData.Map); Assert.AreEqual(expected, actual); } @@ -39,7 +39,7 @@ public void TestConditionCreateAllDestructables(MapScriptBuilderTestData testDat { foreach (var testData in _testData) { - if (testData.DeclaredFunctions.ContainsKey("CreateAllDestructables")) + if (testData.DeclaredFunctions.ContainsKey(MapScriptBuilder.GeneratedFunctionName.CreateAllDestructables)) { yield return new object[] { testData }; } diff --git a/tests/War3Net.Build.Tests/MapScriptBuilder/Widget/CreateAllItemsTests.cs b/tests/War3Net.Build.Tests/MapScriptBuilder/Widget/CreateAllItemsTests.cs index d70e1b39..56c80352 100644 --- a/tests/War3Net.Build.Tests/MapScriptBuilder/Widget/CreateAllItemsTests.cs +++ b/tests/War3Net.Build.Tests/MapScriptBuilder/Widget/CreateAllItemsTests.cs @@ -19,18 +19,18 @@ public partial class MapScriptBuilderTests [DynamicData(nameof(GetTestDataCreateAllItems), DynamicDataSourceType.Method)] public void TestBodyCreateAllItems(MapScriptBuilderTestData testData) { - var expected = testData.DeclaredFunctions["CreateAllItems"]; - var actual = testData.MapScriptBuilder.CreateAllItems(testData.Map); - - SyntaxAssert.AreEqual(expected, actual); + AssertFunctionGeneratedCorrectly( + testData, + MapScriptBuilder.GeneratedFunctionName.CreateAllItems, + writer => testData.MapScriptBuilder.GenerateCreateAllItems(testData.Map, writer)); } [TestMethod] [DynamicData(nameof(GetUnobfuscatedTestData), DynamicDataSourceType.Method)] public void TestConditionCreateAllItems(MapScriptBuilderTestData testData) { - var expected = testData.DeclaredFunctions.ContainsKey("CreateAllItems"); - var actual = testData.MapScriptBuilder.CreateAllItemsCondition(testData.Map); + var expected = testData.DeclaredFunctions.ContainsKey(MapScriptBuilder.GeneratedFunctionName.CreateAllItems); + var actual = testData.MapScriptBuilder.ShouldGenerateCreateAllItems(testData.Map); Assert.AreEqual(expected, actual); } @@ -39,7 +39,7 @@ public void TestConditionCreateAllItems(MapScriptBuilderTestData testData) { foreach (var testData in _testData) { - if (testData.DeclaredFunctions.ContainsKey("CreateAllItems")) + if (testData.DeclaredFunctions.ContainsKey(MapScriptBuilder.GeneratedFunctionName.CreateAllItems)) { yield return new object[] { testData }; } diff --git a/tests/War3Net.Build.Tests/MapScriptBuilder/Widget/CreateAllUnitsTests.cs b/tests/War3Net.Build.Tests/MapScriptBuilder/Widget/CreateAllUnitsTests.cs index 7670d027..041ce356 100644 --- a/tests/War3Net.Build.Tests/MapScriptBuilder/Widget/CreateAllUnitsTests.cs +++ b/tests/War3Net.Build.Tests/MapScriptBuilder/Widget/CreateAllUnitsTests.cs @@ -19,18 +19,18 @@ public partial class MapScriptBuilderTests [DynamicData(nameof(GetTestDataCreateAllUnits), DynamicDataSourceType.Method)] public void TestBodyCreateAllUnits(MapScriptBuilderTestData testData) { - var expected = testData.DeclaredFunctions["CreateAllUnits"]; - var actual = testData.MapScriptBuilder.CreateAllUnits(testData.Map); - - SyntaxAssert.AreEqual(expected, actual); + AssertFunctionGeneratedCorrectly( + testData, + MapScriptBuilder.GeneratedFunctionName.CreateAllUnits, + writer => testData.MapScriptBuilder.GenerateCreateAllUnits(testData.Map, writer)); } [TestMethod] [DynamicData(nameof(GetUnobfuscatedTestData), DynamicDataSourceType.Method)] public void TestConditionCreateAllUnits(MapScriptBuilderTestData testData) { - var expected = testData.DeclaredFunctions.ContainsKey("CreateAllUnits"); - var actual = testData.MapScriptBuilder.CreateAllUnitsCondition(testData.Map); + var expected = testData.DeclaredFunctions.ContainsKey(MapScriptBuilder.GeneratedFunctionName.CreateAllUnits); + var actual = testData.MapScriptBuilder.ShouldGenerateCreateAllUnits(testData.Map); Assert.AreEqual(expected, actual); } @@ -39,7 +39,7 @@ public void TestConditionCreateAllUnits(MapScriptBuilderTestData testData) { foreach (var testData in _testData) { - if (testData.DeclaredFunctions.ContainsKey("CreateAllUnits")) + if (testData.DeclaredFunctions.ContainsKey(MapScriptBuilder.GeneratedFunctionName.CreateAllUnits)) { yield return new object[] { testData }; } diff --git a/tests/War3Net.Build.Tests/MapScriptBuilder/Widget/CreateNeutralHostileBuildingsTests.cs b/tests/War3Net.Build.Tests/MapScriptBuilder/Widget/CreateNeutralHostileBuildingsTests.cs new file mode 100644 index 00000000..5f67d9f2 --- /dev/null +++ b/tests/War3Net.Build.Tests/MapScriptBuilder/Widget/CreateNeutralHostileBuildingsTests.cs @@ -0,0 +1,49 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using System.Collections.Generic; + +using Microsoft.VisualStudio.TestTools.UnitTesting; + +using War3Net.TestTools.UnitTesting; + +namespace War3Net.Build.Tests +{ + public partial class MapScriptBuilderTests + { + [FlakyTestMethod] + [DynamicData(nameof(GetTestDataCreateNeutralHostileBuildings), DynamicDataSourceType.Method)] + public void TestBodyCreateNeutralHostileBuildings(MapScriptBuilderTestData testData) + { + AssertFunctionGeneratedCorrectly( + testData, + MapScriptBuilder.GeneratedFunctionName.CreateNeutralHostileBuildings, + writer => testData.MapScriptBuilder.GenerateCreateNeutralHostileBuildings(testData.Map, writer)); + } + + [TestMethod] + [DynamicData(nameof(GetUnobfuscatedTestData), DynamicDataSourceType.Method)] + public void TestConditionCreateNeutralHostileBuildings(MapScriptBuilderTestData testData) + { + var expected = testData.DeclaredFunctions.ContainsKey(MapScriptBuilder.GeneratedFunctionName.CreateNeutralHostileBuildings); + var actual = testData.MapScriptBuilder.ShouldGenerateCreateNeutralHostileBuildings(testData.Map); + + Assert.AreEqual(expected, actual); + } + + private static IEnumerable GetTestDataCreateNeutralHostileBuildings() + { + foreach (var testData in GetUnobfuscatedTestData()) + { + if (((MapScriptBuilderTestData)testData[0]).DeclaredFunctions.ContainsKey(MapScriptBuilder.GeneratedFunctionName.CreateNeutralHostileBuildings)) + { + yield return testData; + } + } + } + } +} \ No newline at end of file diff --git a/tests/War3Net.Build.Tests/MapScriptBuilder/Widget/CreateNeutralHostileTests.cs b/tests/War3Net.Build.Tests/MapScriptBuilder/Widget/CreateNeutralHostileTests.cs index 01dba9f3..3ad4a151 100644 --- a/tests/War3Net.Build.Tests/MapScriptBuilder/Widget/CreateNeutralHostileTests.cs +++ b/tests/War3Net.Build.Tests/MapScriptBuilder/Widget/CreateNeutralHostileTests.cs @@ -19,18 +19,18 @@ public partial class MapScriptBuilderTests [DynamicData(nameof(GetTestDataCreateNeutralHostile), DynamicDataSourceType.Method)] public void TestBodyCreateNeutralHostile(MapScriptBuilderTestData testData) { - var expected = testData.DeclaredFunctions["CreateNeutralHostile"]; - var actual = testData.MapScriptBuilder.CreateNeutralHostile(testData.Map); - - SyntaxAssert.AreEqual(expected, actual); + AssertFunctionGeneratedCorrectly( + testData, + MapScriptBuilder.GeneratedFunctionName.CreateNeutralHostile, + writer => testData.MapScriptBuilder.GenerateCreateNeutralHostile(testData.Map, writer)); } [TestMethod] [DynamicData(nameof(GetUnobfuscatedTestData), DynamicDataSourceType.Method)] public void TestConditionCreateNeutralHostile(MapScriptBuilderTestData testData) { - var expected = testData.DeclaredFunctions.ContainsKey("CreateNeutralHostile"); - var actual = testData.MapScriptBuilder.CreateNeutralHostileCondition(testData.Map); + var expected = testData.DeclaredFunctions.ContainsKey(MapScriptBuilder.GeneratedFunctionName.CreateNeutralHostile); + var actual = testData.MapScriptBuilder.ShouldGenerateCreateNeutralHostile(testData.Map); Assert.AreEqual(expected, actual); } @@ -39,7 +39,7 @@ public void TestConditionCreateNeutralHostile(MapScriptBuilderTestData testData) { foreach (var testData in GetUnobfuscatedTestData()) { - if (((MapScriptBuilderTestData)testData[0]).DeclaredFunctions.ContainsKey("CreateNeutralHostile")) + if (((MapScriptBuilderTestData)testData[0]).DeclaredFunctions.ContainsKey(MapScriptBuilder.GeneratedFunctionName.CreateNeutralHostile)) { yield return testData; } diff --git a/tests/War3Net.Build.Tests/MapScriptBuilder/Widget/CreateNeutralPassiveBuildingsTests.cs b/tests/War3Net.Build.Tests/MapScriptBuilder/Widget/CreateNeutralPassiveBuildingsTests.cs index c4abc6ac..b1cbaa8c 100644 --- a/tests/War3Net.Build.Tests/MapScriptBuilder/Widget/CreateNeutralPassiveBuildingsTests.cs +++ b/tests/War3Net.Build.Tests/MapScriptBuilder/Widget/CreateNeutralPassiveBuildingsTests.cs @@ -19,18 +19,18 @@ public partial class MapScriptBuilderTests [DynamicData(nameof(GetTestDataCreateNeutralPassiveBuildings), DynamicDataSourceType.Method)] public void TestBodyCreateNeutralPassiveBuildings(MapScriptBuilderTestData testData) { - var expected = testData.DeclaredFunctions["CreateNeutralPassiveBuildings"]; - var actual = testData.MapScriptBuilder.CreateNeutralPassiveBuildings(testData.Map); - - SyntaxAssert.AreEqual(expected, actual); + AssertFunctionGeneratedCorrectly( + testData, + MapScriptBuilder.GeneratedFunctionName.CreateNeutralPassiveBuildings, + writer => testData.MapScriptBuilder.GenerateCreateNeutralPassiveBuildings(testData.Map, writer)); } [TestMethod] [DynamicData(nameof(GetUnobfuscatedTestData), DynamicDataSourceType.Method)] public void TestConditionCreateNeutralPassiveBuildings(MapScriptBuilderTestData testData) { - var expected = testData.DeclaredFunctions.ContainsKey("CreateNeutralPassiveBuildings"); - var actual = testData.MapScriptBuilder.CreateNeutralPassiveBuildingsCondition(testData.Map); + var expected = testData.DeclaredFunctions.ContainsKey(MapScriptBuilder.GeneratedFunctionName.CreateNeutralPassiveBuildings); + var actual = testData.MapScriptBuilder.ShouldGenerateCreateNeutralPassiveBuildings(testData.Map); Assert.AreEqual(expected, actual); } @@ -39,7 +39,7 @@ public void TestConditionCreateNeutralPassiveBuildings(MapScriptBuilderTestData { foreach (var testData in GetUnobfuscatedTestData()) { - if (((MapScriptBuilderTestData)testData[0]).DeclaredFunctions.ContainsKey("CreateNeutralPassiveBuildings")) + if (((MapScriptBuilderTestData)testData[0]).DeclaredFunctions.ContainsKey(MapScriptBuilder.GeneratedFunctionName.CreateNeutralPassiveBuildings)) { yield return testData; } diff --git a/tests/War3Net.Build.Tests/MapScriptBuilder/Widget/CreateNeutralPassiveTests.cs b/tests/War3Net.Build.Tests/MapScriptBuilder/Widget/CreateNeutralPassiveTests.cs index 0209dbb8..f769e93a 100644 --- a/tests/War3Net.Build.Tests/MapScriptBuilder/Widget/CreateNeutralPassiveTests.cs +++ b/tests/War3Net.Build.Tests/MapScriptBuilder/Widget/CreateNeutralPassiveTests.cs @@ -19,18 +19,18 @@ public partial class MapScriptBuilderTests [DynamicData(nameof(GetTestDataCreateNeutralPassive), DynamicDataSourceType.Method)] public void TestBodyCreateNeutralPassive(MapScriptBuilderTestData testData) { - var expected = testData.DeclaredFunctions["CreateNeutralPassive"]; - var actual = testData.MapScriptBuilder.CreateNeutralPassive(testData.Map); - - SyntaxAssert.AreEqual(expected, actual); + AssertFunctionGeneratedCorrectly( + testData, + MapScriptBuilder.GeneratedFunctionName.CreateNeutralPassive, + writer => testData.MapScriptBuilder.GenerateCreateNeutralPassive(testData.Map, writer)); } [TestMethod] [DynamicData(nameof(GetUnobfuscatedTestData), DynamicDataSourceType.Method)] public void TestConditionCreateNeutralPassive(MapScriptBuilderTestData testData) { - var expected = testData.DeclaredFunctions.ContainsKey("CreateNeutralPassive"); - var actual = testData.MapScriptBuilder.CreateNeutralPassiveCondition(testData.Map); + var expected = testData.DeclaredFunctions.ContainsKey(MapScriptBuilder.GeneratedFunctionName.CreateNeutralPassive); + var actual = testData.MapScriptBuilder.ShouldGenerateCreateNeutralPassive(testData.Map); Assert.AreEqual(expected, actual); } @@ -39,7 +39,7 @@ public void TestConditionCreateNeutralPassive(MapScriptBuilderTestData testData) { foreach (var testData in GetUnobfuscatedTestData()) { - if (((MapScriptBuilderTestData)testData[0]).DeclaredFunctions.ContainsKey("CreateNeutralPassive")) + if (((MapScriptBuilderTestData)testData[0]).DeclaredFunctions.ContainsKey(MapScriptBuilder.GeneratedFunctionName.CreateNeutralPassive)) { yield return testData; } diff --git a/tests/War3Net.Build.Tests/MapScriptBuilder/Widget/CreateNeutralUnitsTests.cs b/tests/War3Net.Build.Tests/MapScriptBuilder/Widget/CreateNeutralUnitsTests.cs index 7ea5dc7d..024c07e6 100644 --- a/tests/War3Net.Build.Tests/MapScriptBuilder/Widget/CreateNeutralUnitsTests.cs +++ b/tests/War3Net.Build.Tests/MapScriptBuilder/Widget/CreateNeutralUnitsTests.cs @@ -19,18 +19,18 @@ public partial class MapScriptBuilderTests [DynamicData(nameof(GetTestDataCreateNeutralUnits), DynamicDataSourceType.Method)] public void TestBodyCreateNeutralUnits(MapScriptBuilderTestData testData) { - var expected = testData.DeclaredFunctions["CreateNeutralUnits"]; - var actual = testData.MapScriptBuilder.CreateNeutralUnits(testData.Map); - - SyntaxAssert.AreEqual(expected, actual); + AssertFunctionGeneratedCorrectly( + testData, + MapScriptBuilder.GeneratedFunctionName.CreateNeutralUnits, + writer => testData.MapScriptBuilder.GenerateCreateNeutralUnits(testData.Map, writer)); } [TestMethod] [DynamicData(nameof(GetUnobfuscatedTestData), DynamicDataSourceType.Method)] public void TestConditionCreateNeutralUnits(MapScriptBuilderTestData testData) { - var expected = testData.DeclaredFunctions.ContainsKey("CreateNeutralUnits"); - var actual = testData.MapScriptBuilder.CreateNeutralUnitsCondition(testData.Map); + var expected = testData.DeclaredFunctions.ContainsKey(MapScriptBuilder.GeneratedFunctionName.CreateNeutralUnits); + var actual = testData.MapScriptBuilder.ShouldGenerateCreateNeutralUnits(testData.Map); Assert.AreEqual(expected, actual); } @@ -39,7 +39,7 @@ public void TestConditionCreateNeutralUnits(MapScriptBuilderTestData testData) { foreach (var testData in _testData) { - if (testData.DeclaredFunctions.ContainsKey("CreateNeutralUnits")) + if (testData.DeclaredFunctions.ContainsKey(MapScriptBuilder.GeneratedFunctionName.CreateNeutralUnits)) { yield return new object[] { testData }; } diff --git a/tests/War3Net.Build.Tests/MapScriptBuilder/Widget/CreatePlayerBuildingsTests.cs b/tests/War3Net.Build.Tests/MapScriptBuilder/Widget/CreatePlayerBuildingsTests.cs index da044b9d..8da5aaea 100644 --- a/tests/War3Net.Build.Tests/MapScriptBuilder/Widget/CreatePlayerBuildingsTests.cs +++ b/tests/War3Net.Build.Tests/MapScriptBuilder/Widget/CreatePlayerBuildingsTests.cs @@ -19,18 +19,18 @@ public partial class MapScriptBuilderTests [DynamicData(nameof(GetTestDataCreatePlayerBuildings), DynamicDataSourceType.Method)] public void TestBodyCreatePlayerBuildings(MapScriptBuilderTestData testData) { - var expected = testData.DeclaredFunctions["CreatePlayerBuildings"]; - var actual = testData.MapScriptBuilder.CreatePlayerBuildings(testData.Map); - - SyntaxAssert.AreEqual(expected, actual); + AssertFunctionGeneratedCorrectly( + testData, + MapScriptBuilder.GeneratedFunctionName.CreatePlayerBuildings, + writer => testData.MapScriptBuilder.GenerateCreatePlayerBuildings(testData.Map, writer)); } [FlakyTestMethod] [DynamicData(nameof(GetUnobfuscatedTestData), DynamicDataSourceType.Method)] public void TestConditionCreatePlayerBuildings(MapScriptBuilderTestData testData) { - var expected = testData.DeclaredFunctions.ContainsKey("CreatePlayerBuildings"); - var actual = testData.MapScriptBuilder.CreatePlayerBuildingsCondition(testData.Map); + var expected = testData.DeclaredFunctions.ContainsKey(MapScriptBuilder.GeneratedFunctionName.CreatePlayerBuildings); + var actual = testData.MapScriptBuilder.ShouldGenerateCreatePlayerBuildings(testData.Map); Assert.AreEqual(expected, actual); } @@ -39,7 +39,7 @@ public void TestConditionCreatePlayerBuildings(MapScriptBuilderTestData testData { foreach (var testData in GetUnobfuscatedTestData()) { - if (((MapScriptBuilderTestData)testData[0]).DeclaredFunctions.ContainsKey("CreatePlayerBuildings")) + if (((MapScriptBuilderTestData)testData[0]).DeclaredFunctions.ContainsKey(MapScriptBuilder.GeneratedFunctionName.CreatePlayerBuildings)) { yield return testData; } diff --git a/tests/War3Net.Build.Tests/MapScriptBuilder/Widget/CreatePlayerUnitsTests.cs b/tests/War3Net.Build.Tests/MapScriptBuilder/Widget/CreatePlayerUnitsTests.cs index 891ea0af..50f80129 100644 --- a/tests/War3Net.Build.Tests/MapScriptBuilder/Widget/CreatePlayerUnitsTests.cs +++ b/tests/War3Net.Build.Tests/MapScriptBuilder/Widget/CreatePlayerUnitsTests.cs @@ -19,18 +19,18 @@ public partial class MapScriptBuilderTests [DynamicData(nameof(GetTestDataCreatePlayerUnits), DynamicDataSourceType.Method)] public void TestBodyCreatePlayerUnits(MapScriptBuilderTestData testData) { - var expected = testData.DeclaredFunctions["CreatePlayerUnits"]; - var actual = testData.MapScriptBuilder.CreatePlayerUnits(testData.Map); - - SyntaxAssert.AreEqual(expected, actual); + AssertFunctionGeneratedCorrectly( + testData, + MapScriptBuilder.GeneratedFunctionName.CreatePlayerUnits, + writer => testData.MapScriptBuilder.GenerateCreatePlayerUnits(testData.Map, writer)); } [FlakyTestMethod] [DynamicData(nameof(GetUnobfuscatedTestData), DynamicDataSourceType.Method)] public void TestConditionCreatePlayerUnits(MapScriptBuilderTestData testData) { - var expected = testData.DeclaredFunctions.ContainsKey("CreatePlayerUnits"); - var actual = testData.MapScriptBuilder.CreatePlayerUnitsCondition(testData.Map); + var expected = testData.DeclaredFunctions.ContainsKey(MapScriptBuilder.GeneratedFunctionName.CreatePlayerUnits); + var actual = testData.MapScriptBuilder.ShouldGenerateCreatePlayerUnits(testData.Map); Assert.AreEqual(expected, actual); } @@ -39,7 +39,7 @@ public void TestConditionCreatePlayerUnits(MapScriptBuilderTestData testData) { foreach (var testData in _testData) { - if (testData.DeclaredFunctions.ContainsKey("CreatePlayerUnits")) + if (testData.DeclaredFunctions.ContainsKey(MapScriptBuilder.GeneratedFunctionName.CreatePlayerUnits)) { yield return new object[] { testData }; } diff --git a/tests/War3Net.Build.Tests/MapScriptBuilder/Widget/DestructableItemTablesTests.cs b/tests/War3Net.Build.Tests/MapScriptBuilder/Widget/DestructableItemTablesTests.cs index 9cb708b6..402961f5 100644 --- a/tests/War3Net.Build.Tests/MapScriptBuilder/Widget/DestructableItemTablesTests.cs +++ b/tests/War3Net.Build.Tests/MapScriptBuilder/Widget/DestructableItemTablesTests.cs @@ -18,7 +18,7 @@ public partial class MapScriptBuilderTests public void TestConditionDestructableItemTables(MapScriptBuilderTestData testData) { var expected = testData.DeclaredFunctions.ContainsKey("DestructableItemTables"); - var actual = testData.MapScriptBuilder.DestructableItemTablesCondition(testData.Map); + var actual = testData.MapScriptBuilder.ShouldGenerateDestructableItemTables(testData.Map); Assert.AreEqual(expected, actual); } diff --git a/tests/War3Net.Build.Tests/MapScriptBuilderTestData.cs b/tests/War3Net.Build.Tests/MapScriptBuilderTestData.cs index 9e320e04..60cd4f0b 100644 --- a/tests/War3Net.Build.Tests/MapScriptBuilderTestData.cs +++ b/tests/War3Net.Build.Tests/MapScriptBuilderTestData.cs @@ -29,7 +29,7 @@ public MapScriptBuilderTestData(Map map, JassCompilationUnitSyntax compilationUn { if (declaration is JassFunctionDeclarationSyntax functionDeclaration) { - builder.Add(functionDeclaration.FunctionDeclarator.IdentifierName.Name, functionDeclaration); + builder.Add(functionDeclaration.FunctionDeclarator.IdentifierName.Token.Text, functionDeclaration); } } diff --git a/tests/War3Net.Build.Tests/MapScriptBuilderTests.cs b/tests/War3Net.Build.Tests/MapScriptBuilderTests.cs index 0c8da5ef..d124ab1a 100644 --- a/tests/War3Net.Build.Tests/MapScriptBuilderTests.cs +++ b/tests/War3Net.Build.Tests/MapScriptBuilderTests.cs @@ -5,6 +5,7 @@ // // ------------------------------------------------------------------------------ +using System; using System.Collections.Generic; using System.IO; using System.Linq; @@ -12,6 +13,7 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; using War3Net.Build.Info; +using War3Net.CodeAnalysis; using War3Net.CodeAnalysis.Jass; using War3Net.TestTools.UnitTesting; @@ -49,5 +51,22 @@ private static IEnumerable GetMapPaths() { return TestDataProvider.GetDynamicData("*", SearchOption.AllDirectories, "Maps"); } + + private static void AssertFunctionGeneratedCorrectly( + MapScriptBuilderTestData testData, + string functionName, + Action generateFunc) + { + using var stringWriter = new StringWriter(); + stringWriter.NewLine = JassSymbol.CarriageReturnLineFeed; + using var writer = new IndentedTextWriter(stringWriter); + + generateFunc.Invoke(writer); + + var expected = testData.DeclaredFunctions[functionName]; + var actual = JassSyntaxFactory.ParseTopLevelDeclaration(stringWriter.ToString()); + + SyntaxAssert.AreEqual(expected, actual); + } } } \ No newline at end of file diff --git a/tests/War3Net.CodeAnalysis.Jass.Tests/JassLiteralTests.cs b/tests/War3Net.CodeAnalysis.Jass.Tests/JassLiteralTests.cs new file mode 100644 index 00000000..cddc1d0b --- /dev/null +++ b/tests/War3Net.CodeAnalysis.Jass.Tests/JassLiteralTests.cs @@ -0,0 +1,30 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace War3Net.CodeAnalysis.Jass.Tests +{ + [TestClass] + public class JassLiteralTests + { + [TestMethod] + [DataRow("'t'", 't')] + [DataRow("'\\r'", '\r')] + [DataRow("'\\n'", '\n')] + [DataRow("'\\t'", '\t')] + [DataRow("'\\b'", '\b')] + [DataRow("'\\f'", '\f')] + [DataRow("'\\\\'", '\\')] + [DataRow("'\\\"'", '"')] + [DataRow("'\\''", '\'')] + public void TestParseChar(string characterLiteral, char expectedValue) + { + Assert.AreEqual(expectedValue, JassLiteral.ParseChar(characterLiteral)); + } + } +} \ No newline at end of file diff --git a/tests/War3Net.CodeAnalysis.Jass.Tests/Parser/BinaryOperatorParserTests.cs b/tests/War3Net.CodeAnalysis.Jass.Tests/Parser/BinaryOperatorParserTests.cs index 4d636eba..31678f98 100644 --- a/tests/War3Net.CodeAnalysis.Jass.Tests/Parser/BinaryOperatorParserTests.cs +++ b/tests/War3Net.CodeAnalysis.Jass.Tests/Parser/BinaryOperatorParserTests.cs @@ -9,8 +9,6 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; -using War3Net.CodeAnalysis.Jass.Syntax; - namespace War3Net.CodeAnalysis.Jass.Tests.Parser { [TestClass] @@ -18,10 +16,10 @@ public class BinaryOperatorParserTests { [TestMethod] [DynamicData(nameof(GetValidOperators), DynamicDataSourceType.Method)] - public void TestValidOperators(string binaryOperator, BinaryOperatorType expected) + public void TestValidOperators(string binaryOperator, JassSyntaxKind expected) { Assert.IsTrue(JassSyntaxFactory.TryParseBinaryOperator(binaryOperator, out var actual)); - Assert.AreEqual(expected, actual); + Assert.AreEqual(expected, actual.SyntaxKind); } [TestMethod] @@ -33,18 +31,18 @@ public void TestInvalidOperators(string binaryOperator) private static IEnumerable GetValidOperators() { - yield return new object?[] { "+", BinaryOperatorType.Add }; - yield return new object?[] { "-", BinaryOperatorType.Subtract }; - yield return new object?[] { "*", BinaryOperatorType.Multiplication }; - yield return new object?[] { "/", BinaryOperatorType.Division }; - yield return new object?[] { ">", BinaryOperatorType.GreaterThan }; - yield return new object?[] { "<", BinaryOperatorType.LessThan }; - yield return new object?[] { "==", BinaryOperatorType.Equals }; - yield return new object?[] { "!=", BinaryOperatorType.NotEquals }; - yield return new object?[] { ">=", BinaryOperatorType.GreaterOrEqual }; - yield return new object?[] { "<=", BinaryOperatorType.LessOrEqual }; - yield return new object?[] { "and", BinaryOperatorType.And }; - yield return new object?[] { "or", BinaryOperatorType.Or }; + yield return new object?[] { "+", JassSyntaxKind.PlusToken }; + yield return new object?[] { "-", JassSyntaxKind.MinusToken }; + yield return new object?[] { "*", JassSyntaxKind.AsteriskToken }; + yield return new object?[] { "/", JassSyntaxKind.SlashToken }; + yield return new object?[] { ">", JassSyntaxKind.GreaterThanToken }; + yield return new object?[] { "<", JassSyntaxKind.LessThanToken }; + yield return new object?[] { "==", JassSyntaxKind.EqualsEqualsToken }; + yield return new object?[] { "!=", JassSyntaxKind.ExclamationEqualsToken }; + yield return new object?[] { ">=", JassSyntaxKind.GreaterThanEqualsToken }; + yield return new object?[] { "<=", JassSyntaxKind.LessThanEqualsToken }; + yield return new object?[] { "and", JassSyntaxKind.AndKeyword }; + yield return new object?[] { "or", JassSyntaxKind.OrKeyword }; } private static IEnumerable GetInvalidOperators() diff --git a/tests/War3Net.CodeAnalysis.Jass.Tests/Parser/ExpressionParserTests.cs b/tests/War3Net.CodeAnalysis.Jass.Tests/Parser/ExpressionParserTests.cs index a7672794..15080453 100644 --- a/tests/War3Net.CodeAnalysis.Jass.Tests/Parser/ExpressionParserTests.cs +++ b/tests/War3Net.CodeAnalysis.Jass.Tests/Parser/ExpressionParserTests.cs @@ -22,7 +22,7 @@ public class ExpressionParserTests { [TestMethod] [DynamicData(nameof(GetTestExpressions), DynamicDataSourceType.Method)] - public void TestExpressionParser(string expression, IExpressionSyntax? expected = null) + public void TestExpressionParser(string expression, JassExpressionSyntax? expected = null) { if (expected is null) { @@ -39,9 +39,9 @@ public void TestExpressionParser(string expression, IExpressionSyntax? expected { #region InvocationExpression yield return new object?[] { @"foo()", InvocationExpression(@"foo") }; - yield return new object?[] { @"foo( bar )", InvocationExpression(@"foo", VariableReferenceExpression(@"bar")) }; - yield return new object?[] { @"foo ( a , b )", InvocationExpression(@"foo", VariableReferenceExpression(@"a"), VariableReferenceExpression(@"b")) }; - yield return new object?[] { @"foo(a,b)", InvocationExpression(@"foo", VariableReferenceExpression(@"a"), VariableReferenceExpression(@"b")) }; + yield return new object?[] { @"foo( bar )", InvocationExpression(@"foo", IdentifierName(@"bar")) }; + yield return new object?[] { @"foo ( a , b )", InvocationExpression(@"foo", IdentifierName(@"a"), IdentifierName(@"b")) }; + yield return new object?[] { @"foo(a,b)", InvocationExpression(@"foo", IdentifierName(@"a"), IdentifierName(@"b")) }; yield return new object?[] { @"foo(,)" }; yield return new object?[] { @"foo(a,)" }; yield return new object?[] { @"foo(,b)" }; @@ -53,7 +53,7 @@ public void TestExpressionParser(string expression, IExpressionSyntax? expected #endregion #region ArrayReferenceExpression - yield return new object?[] { @"foo[bar]", ArrayReferenceExpression(@"foo", VariableReferenceExpression(@"bar")) }; + yield return new object?[] { @"foo[bar]", ElementAccessExpression(@"foo", IdentifierName(@"bar")) }; yield return new object?[] { @"foo[bar" }; #endregion @@ -64,8 +64,8 @@ public void TestExpressionParser(string expression, IExpressionSyntax? expected #endregion #region VariableReferenceExpression - yield return new object?[] { @"player_id", VariableReferenceExpression(@"player_id") }; - yield return new object?[] { @"player_6", VariableReferenceExpression(@"player_6") }; + yield return new object?[] { @"player_id", IdentifierName(@"player_id") }; + yield return new object?[] { @"player_6", IdentifierName(@"player_6") }; yield return new object?[] { @"player_" }; yield return new object?[] { @"_player" }; yield return new object?[] { @"6player" }; @@ -76,29 +76,29 @@ public void TestExpressionParser(string expression, IExpressionSyntax? expected #endregion #region DecimalLiteralExpression - yield return new object?[] { @"1", new JassDecimalLiteralExpressionSyntax(1) }; - yield return new object?[] { @"255", new JassDecimalLiteralExpressionSyntax(255) }; + yield return new object?[] { @"0", LiteralExpression(Literal(0)) }; + yield return new object?[] { @"1", LiteralExpression(Literal(1)) }; + yield return new object?[] { @"255", LiteralExpression(Literal(255)) }; yield return new object?[] { @"255abc" }; yield return new object?[] { @"255_" }; #endregion #region OctalLiteralExpression - yield return new object?[] { @"0", new JassOctalLiteralExpressionSyntax(0) }; - yield return new object?[] { @"010", new JassOctalLiteralExpressionSyntax(8) }; + yield return new object?[] { @"010", LiteralExpression(Token(JassSyntaxKind.OctalLiteralToken, "010")) }; yield return new object?[] { @"0abc" }; yield return new object?[] { @"0_" }; #endregion #region HexadecimalLiteralExpression - yield return new object?[] { @"$6", new JassHexadecimalLiteralExpressionSyntax(6) }; - yield return new object?[] { @"$A", new JassHexadecimalLiteralExpressionSyntax(10) }; - yield return new object?[] { @"$FF", new JassHexadecimalLiteralExpressionSyntax(255) }; - yield return new object?[] { @"0x6", new JassHexadecimalLiteralExpressionSyntax(6) }; - yield return new object?[] { @"0xA", new JassHexadecimalLiteralExpressionSyntax(10) }; - yield return new object?[] { @"0xFF", new JassHexadecimalLiteralExpressionSyntax(255) }; - yield return new object?[] { @"0X6", new JassHexadecimalLiteralExpressionSyntax(6) }; - yield return new object?[] { @"0XA", new JassHexadecimalLiteralExpressionSyntax(10) }; - yield return new object?[] { @"0XFF", new JassHexadecimalLiteralExpressionSyntax(255) }; + yield return new object?[] { @"$6", LiteralExpression(Token(JassSyntaxKind.HexadecimalLiteralToken, "$6")) }; + yield return new object?[] { @"$A", LiteralExpression(Token(JassSyntaxKind.HexadecimalLiteralToken, "$A")) }; + yield return new object?[] { @"$FF", LiteralExpression(Token(JassSyntaxKind.HexadecimalLiteralToken, "$FF")) }; + yield return new object?[] { @"0x6", LiteralExpression(Token(JassSyntaxKind.HexadecimalLiteralToken, "0x6")) }; + yield return new object?[] { @"0xA", LiteralExpression(Token(JassSyntaxKind.HexadecimalLiteralToken, "0xA")) }; + yield return new object?[] { @"0xFF", LiteralExpression(Token(JassSyntaxKind.HexadecimalLiteralToken, "0xFF")) }; + yield return new object?[] { @"0X6", LiteralExpression(Token(JassSyntaxKind.HexadecimalLiteralToken, "0X6")) }; + yield return new object?[] { @"0XA", LiteralExpression(Token(JassSyntaxKind.HexadecimalLiteralToken, "0XA")) }; + yield return new object?[] { @"0XFF", LiteralExpression(Token(JassSyntaxKind.HexadecimalLiteralToken, "0XFF")) }; yield return new object?[] { @"$ALOL" }; yield return new object?[] { @"$A_" }; yield return new object?[] { @"0xLOL" }; @@ -108,7 +108,7 @@ public void TestExpressionParser(string expression, IExpressionSyntax? expected #endregion #region FourCCLiteralExpression - yield return new object?[] { @"'hpea'", new JassFourCCLiteralExpressionSyntax(@"hpea".FromJassRawcode()) }; + yield return new object?[] { @"'hpea'", LiteralExpression(FourCCLiteral(@"hpea".FromJassRawcode())) }; yield return new object?[] { @"'hpeasant'" }; yield return new object?[] { @"'pea'" }; yield return new object?[] { @"''" }; @@ -116,9 +116,9 @@ public void TestExpressionParser(string expression, IExpressionSyntax? expected #endregion #region RealLiteralExpression - yield return new object?[] { @"0.", LiteralExpression(0f, precision: 0) }; - yield return new object?[] { @".0", LiteralExpression(0f) }; - yield return new object?[] { @"3.141", LiteralExpression(3.141f, precision: 3) }; + yield return new object?[] { @"0.", LiteralExpression(Token(JassSyntaxKind.RealLiteralToken, "0.")) }; + yield return new object?[] { @".0", LiteralExpression(Token(JassSyntaxKind.RealLiteralToken, ".0")) }; + yield return new object?[] { @"3.141", LiteralExpression(Token(JassSyntaxKind.RealLiteralToken, "3.141")) }; yield return new object?[] { @"." }; yield return new object?[] { @"0.abc" }; yield return new object?[] { @"0.0abc" }; @@ -126,27 +126,27 @@ public void TestExpressionParser(string expression, IExpressionSyntax? expected #endregion #region BooleanLiteralExpression - yield return new object?[] { @"true", JassBooleanLiteralExpressionSyntax.True }; - yield return new object?[] { @"false", JassBooleanLiteralExpressionSyntax.False }; + yield return new object?[] { @"true", LiteralExpression(Literal(true)) }; + yield return new object?[] { @"false", LiteralExpression(Literal(false)) }; #endregion #region StringLiteralExpression - yield return new object?[] { "\" true \"", new JassStringLiteralExpressionSyntax(" true ") }; - yield return new object?[] { "\" \\\"true\\\" \"", new JassStringLiteralExpressionSyntax(" \\\"true\\\" ") }; - yield return new object?[] { "\" \r\t\\\\ \"", new JassStringLiteralExpressionSyntax(" \r\t\\\\ ") }; + yield return new object?[] { "\" true \"", LiteralExpression(Literal(" true ")) }; + yield return new object?[] { "\" \\\"true\\\" \"", LiteralExpression(Literal(" \\\"true\\\" ")) }; + yield return new object?[] { "\" \r\t\\\\ \"", LiteralExpression(Literal(" \r\t\\\\ ")) }; yield return new object?[] { "\" true" }; - yield return new object?[] { "\" \n \"", new JassStringLiteralExpressionSyntax(" \n ") }; + yield return new object?[] { "\" \n \"", LiteralExpression(Literal(" \n ")) }; #endregion #region NullLiteralExpression - yield return new object?[] { @"null", JassNullLiteralExpressionSyntax.Value }; + yield return new object?[] { @"null", LiteralExpression(Literal(null)) }; #endregion #region ParenthesizedExpression - yield return new object?[] { @"(0)", new JassParenthesizedExpressionSyntax(new JassOctalLiteralExpressionSyntax(0)) }; - yield return new object?[] { @"(1)", new JassParenthesizedExpressionSyntax(new JassDecimalLiteralExpressionSyntax(1)) }; - yield return new object?[] { @"(player_id)", new JassParenthesizedExpressionSyntax(VariableReferenceExpression(@"player_id")) }; - yield return new object?[] { @"( player_id )", new JassParenthesizedExpressionSyntax(VariableReferenceExpression(@"player_id")) }; + yield return new object?[] { @"(0)", ParenthesizedExpression(LiteralExpression(Literal(0))) }; + yield return new object?[] { @"(1)", ParenthesizedExpression(LiteralExpression(Literal(1))) }; + yield return new object?[] { @"(player_id)", ParenthesizedExpression(IdentifierName(@"player_id")) }; + yield return new object?[] { @"( player_id )", ParenthesizedExpression(IdentifierName(@"player_id")) }; yield return new object?[] { @"(player_id" }; yield return new object?[] { @"player_id)" }; yield return new object?[] { @"()" }; @@ -158,59 +158,54 @@ public void TestExpressionParser(string expression, IExpressionSyntax? expected yield return new object?[] { @"(5 > 0)", - ParenthesizedExpression(new JassBinaryExpressionSyntax( - BinaryOperatorType.GreaterThan, - new JassDecimalLiteralExpressionSyntax(5), - new JassOctalLiteralExpressionSyntax(0))), + ParenthesizedExpression(BinaryGreaterThanExpression( + LiteralExpression(Literal(5)), + LiteralExpression(Literal(0)))), }; yield return new object?[] { @"(0 > foo())", - new JassParenthesizedExpressionSyntax(new JassBinaryExpressionSyntax( - BinaryOperatorType.GreaterThan, - new JassOctalLiteralExpressionSyntax(0), + ParenthesizedExpression(BinaryGreaterThanExpression( + LiteralExpression(Literal(0)), InvocationExpression("foo"))), }; yield return new object?[] { @"(foo() > 0)", - new JassParenthesizedExpressionSyntax(new JassBinaryExpressionSyntax( - BinaryOperatorType.GreaterThan, + ParenthesizedExpression(BinaryGreaterThanExpression( InvocationExpression("foo"), - new JassOctalLiteralExpressionSyntax(0))), + LiteralExpression(Literal(0)))), }; yield return new object?[] { "(GetUnitState(oldUnit, UNIT_STATE_MAX_LIFE) > 0)", - new JassParenthesizedExpressionSyntax(new JassBinaryExpressionSyntax( - BinaryOperatorType.GreaterThan, + ParenthesizedExpression(BinaryGreaterThanExpression( InvocationExpression( "GetUnitState", - VariableReferenceExpression("oldUnit"), - VariableReferenceExpression("UNIT_STATE_MAX_LIFE")), - new JassOctalLiteralExpressionSyntax(0))), + IdentifierName("oldUnit"), + IdentifierName("UNIT_STATE_MAX_LIFE")), + LiteralExpression(Literal(0)))), }; #endregion #region UnaryExpression - yield return new object?[] { @"+6", new JassUnaryExpressionSyntax(UnaryOperatorType.Plus, new JassDecimalLiteralExpressionSyntax(6)) }; - yield return new object?[] { @"-7", new JassUnaryExpressionSyntax(UnaryOperatorType.Minus, new JassDecimalLiteralExpressionSyntax(7)) }; - yield return new object?[] { @"+ 6", new JassUnaryExpressionSyntax(UnaryOperatorType.Plus, new JassDecimalLiteralExpressionSyntax(6)) }; - yield return new object?[] { @"- 7", new JassUnaryExpressionSyntax(UnaryOperatorType.Minus, new JassDecimalLiteralExpressionSyntax(7)) }; - yield return new object?[] { @"not true", new JassUnaryExpressionSyntax(UnaryOperatorType.Not, JassBooleanLiteralExpressionSyntax.True) }; - yield return new object?[] { @"not(true)", new JassUnaryExpressionSyntax(UnaryOperatorType.Not, new JassParenthesizedExpressionSyntax(JassBooleanLiteralExpressionSyntax.True)) }; - yield return new object?[] { @"nottrue", VariableReferenceExpression(@"nottrue") }; + yield return new object?[] { @"+6", UnaryPlusExpression(LiteralExpression(Literal(6))) }; + yield return new object?[] { @"-7", UnaryMinusExpression(LiteralExpression(Literal(7))) }; + yield return new object?[] { @"+ 6", UnaryPlusExpression(LiteralExpression(Literal(6))) }; + yield return new object?[] { @"- 7", UnaryMinusExpression(LiteralExpression(Literal(7))) }; + yield return new object?[] { @"not true", UnaryNotExpression(LiteralExpression(Literal(true))) }; + yield return new object?[] { @"not(true)", UnaryNotExpression(ParenthesizedExpression(LiteralExpression(Literal(true)))) }; + yield return new object?[] { @"nottrue", IdentifierName(@"nottrue") }; #endregion - yield return new object?[] { @"trueandfalseornull", VariableReferenceExpression(@"trueandfalseornull") }; + yield return new object?[] { @"trueandfalseornull", IdentifierName(@"trueandfalseornull") }; - var expr1 = new JassBinaryExpressionSyntax( - BinaryOperatorType.Add, - new JassDecimalLiteralExpressionSyntax(50), - new JassDecimalLiteralExpressionSyntax(60)); + var expr1 = BinaryAddExpression( + LiteralExpression(Literal(50)), + LiteralExpression(Literal(60))); yield return new object?[] { @"50+60", expr1 }; yield return new object?[] { @"50 + 60", expr1 }; @@ -219,33 +214,29 @@ public void TestExpressionParser(string expression, IExpressionSyntax? expected yield return new object?[] { @"2 + 6 * 10", - new JassBinaryExpressionSyntax( - BinaryOperatorType.Add, - new JassDecimalLiteralExpressionSyntax(2), - new JassBinaryExpressionSyntax( - BinaryOperatorType.Multiplication, - new JassDecimalLiteralExpressionSyntax(6), - new JassDecimalLiteralExpressionSyntax(10))), + BinaryAddExpression( + LiteralExpression(Literal(2)), + BinaryMultiplyExpression( + LiteralExpression(Literal(6)), + LiteralExpression(Literal(10)))), }; yield return new object?[] { @"(2 + 6) * 10", - new JassBinaryExpressionSyntax( - BinaryOperatorType.Multiplication, - new JassParenthesizedExpressionSyntax(new JassBinaryExpressionSyntax( - BinaryOperatorType.Add, - new JassDecimalLiteralExpressionSyntax(2), - new JassDecimalLiteralExpressionSyntax(6))), - new JassDecimalLiteralExpressionSyntax(10)), + BinaryMultiplyExpression( + ParenthesizedExpression(BinaryAddExpression( + LiteralExpression(Literal(2)), + LiteralExpression(Literal(6)))), + LiteralExpression(Literal(10))), }; + yield return new object?[] { @"(player_id) * 10", - new JassBinaryExpressionSyntax( - BinaryOperatorType.Multiplication, - new JassParenthesizedExpressionSyntax(VariableReferenceExpression(@"player_id")), - new JassDecimalLiteralExpressionSyntax(10)), + BinaryMultiplyExpression( + ParenthesizedExpression(IdentifierName(@"player_id")), + LiteralExpression(Literal(10))), }; yield return new object?[] @@ -259,17 +250,15 @@ public void TestExpressionParser(string expression, IExpressionSyntax? expected yield return new object?[] { @"FORCE_ALL_PLAYERS[(player_id - 1)] == ConvertedPlayer(player_id)", - new JassBinaryExpressionSyntax( - BinaryOperatorType.Equals, - ArrayReferenceExpression( + BinaryEqualsExpression( + ElementAccessExpression( @"FORCE_ALL_PLAYERS", - new JassParenthesizedExpressionSyntax(new JassBinaryExpressionSyntax( - BinaryOperatorType.Subtract, - VariableReferenceExpression(@"player_id"), - new JassDecimalLiteralExpressionSyntax(1)))), + ParenthesizedExpression(BinarySubtractExpression( + IdentifierName(@"player_id"), + LiteralExpression(Literal(1))))), InvocationExpression( @"ConvertedPlayer", - VariableReferenceExpression(@"player_id"))), + IdentifierName(@"player_id"))), }; } } diff --git a/tests/War3Net.CodeAnalysis.Transpilers.Tests/JassToCSharpTranspilerTests.cs b/tests/War3Net.CodeAnalysis.Transpilers.Tests/JassToCSharpTranspilerTests.cs new file mode 100644 index 00000000..d7566136 --- /dev/null +++ b/tests/War3Net.CodeAnalysis.Transpilers.Tests/JassToCSharpTranspilerTests.cs @@ -0,0 +1,66 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using System.Collections.Generic; +using System.IO; + +using Microsoft.VisualStudio.TestTools.UnitTesting; + +using War3Net.CodeAnalysis.Jass; +using War3Net.TestTools.UnitTesting; + +namespace War3Net.CodeAnalysis.Transpilers.Tests +{ + [TestClass] + public class JassToCSharpTranspilerTests + { + [TestMethod] + [DynamicData(nameof(GetTranspilationFilePaths), DynamicDataSourceType.Method)] + public void TestTranspileToCSharp(string inputJassFilePath, string expectedCSharpFilePath) + { + using var fileStream = File.OpenRead(inputJassFilePath); + using var outputWriter = new StringWriter(); + + GenerateTranspiledScript(fileStream, outputWriter); + + var expectedScript = File.ReadAllText(expectedCSharpFilePath); + var actualScript = outputWriter.ToString(); + + DiffAssert.AreEqual(expectedScript, actualScript); + } + + private static IEnumerable GetTranspilationFilePaths() + { + foreach (var jassFilePath in Directory.EnumerateFiles(TestDataProvider.GetPath("Transpilation"), "*.j", SearchOption.TopDirectoryOnly)) + { + var csharpFilePath = Path.ChangeExtension(jassFilePath, ".cs"); + if (File.Exists(csharpFilePath)) + { + yield return new object?[] { jassFilePath, csharpFilePath }; + } + } + } + + private void GenerateTranspiledScript(Stream inputFileStream, TextWriter outputWriter) + { + var transpiler = new JassToCSharpTranspiler(); + transpiler.ApplyCSharpLuaTemplateAttribute = false; + + using var mapScriptReader = new StreamReader(inputFileStream); + var mapScript = mapScriptReader.ReadToEnd(); + var mapScriptSyntax = JassSyntaxFactory.ParseCompilationUnit(mapScript); + var memberDeclarations = transpiler.Transpile(mapScriptSyntax); + + foreach (var memberDeclaration in memberDeclarations) + { + outputWriter.Write(memberDeclaration.ToFullString()); + } + + outputWriter.Write(transpiler.Transpile(mapScriptSyntax.EndOfFileToken).ToFullString()); + } + } +} \ No newline at end of file diff --git a/tests/War3Net.TestTools.UnitTesting/DiffAssert.cs b/tests/War3Net.TestTools.UnitTesting/DiffAssert.cs new file mode 100644 index 00000000..58de5ca5 --- /dev/null +++ b/tests/War3Net.TestTools.UnitTesting/DiffAssert.cs @@ -0,0 +1,220 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Text; + +using DiffPlex; +using DiffPlex.DiffBuilder; +using DiffPlex.DiffBuilder.Model; + +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace War3Net.TestTools.UnitTesting +{ + public static class DiffAssert + { + /// + /// Gets or sets the number of context lines to show around differences in diff output. + /// Default is 3. + /// + public static int ContextLines { get; set; } = 3; + + /// + /// Asserts that two strings are equal, providing a detailed diff output if they are not. + /// + /// The expected string. + /// The actual string. + public static void AreEqual(string expected, string actual) + { + if (string.Equals(expected, actual, StringComparison.Ordinal)) + { + return; + } + + var diffMessage = BuildDiffMessage(expected, actual); + Assert.Fail(diffMessage); + } + + /// + /// Asserts that two strings are equal, providing a detailed diff output if they are not. + /// + /// The expected string. + /// The actual string. + /// Additional message to include in the assertion failure. + public static void AreEqual(string expected, string actual, string message) + { + if (string.Equals(expected, actual, StringComparison.Ordinal)) + { + return; + } + + var diffMessage = BuildDiffMessage(expected, actual); + Assert.Fail($"{message}{Environment.NewLine}{diffMessage}"); + } + + /// + /// Asserts that two strings are equal, providing a detailed diff output if they are not. + /// Ignores differences in line ending styles (CRLF vs LF). + /// + /// The expected string. + /// The actual string. + public static void AreEqualIgnoreLineEndings(string expected, string actual) + { + var normalizedExpected = NormalizeLineEndings(expected); + var normalizedActual = NormalizeLineEndings(actual); + + if (string.Equals(normalizedExpected, normalizedActual, StringComparison.Ordinal)) + { + return; + } + + var diffMessage = BuildDiffMessage(normalizedExpected, normalizedActual); + Assert.Fail(diffMessage); + } + + /// + /// Asserts that two strings are equal, providing a detailed diff output if they are not. + /// Ignores differences in line ending styles (CRLF vs LF). + /// + /// The expected string. + /// The actual string. + /// Additional message to include in the assertion failure. + public static void AreEqualIgnoreLineEndings(string expected, string actual, string message) + { + var normalizedExpected = NormalizeLineEndings(expected); + var normalizedActual = NormalizeLineEndings(actual); + + if (string.Equals(normalizedExpected, normalizedActual, StringComparison.Ordinal)) + { + return; + } + + var diffMessage = BuildDiffMessage(normalizedExpected, normalizedActual); + Assert.Fail($"{message}{Environment.NewLine}{diffMessage}"); + } + + private static string BuildDiffMessage(string expected, string actual) + { + var diffBuilder = new InlineDiffBuilder(new Differ()); + var diff = diffBuilder.BuildDiffModel(expected, actual, ignoreWhitespace: false); + + var messageBuilder = new StringBuilder(); + messageBuilder.AppendLine(); + messageBuilder.AppendLine("Strings are not equal. Diff:"); + messageBuilder.AppendLine(); + + var addedLines = 0; + var deletedLines = 0; + var modifiedLines = 0; + var contextBuffer = new Queue(); + var skippedLines = 0; + var unchangedLines = ContextLines; + + foreach (var line in diff.Lines) + { + var isChange = line.Type != ChangeType.Unchanged && line.Type != ChangeType.Imaginary; + + if (line.Type == ChangeType.Unchanged && unchangedLines >= ContextLines) + { + contextBuffer.Enqueue($" {line.Text}"); + if (contextBuffer.Count > ContextLines) + { + contextBuffer.Dequeue(); + skippedLines++; + } + + continue; + } + + if (isChange) + { + if (skippedLines > 0) + { + messageBuilder.AppendLine($"... ({skippedLines} unchanged lines omitted) ..."); + skippedLines = 0; + } + + while (contextBuffer.Count > 0) + { + messageBuilder.AppendLine(contextBuffer.Dequeue()); + } + + unchangedLines = 0; + } + + switch (line.Type) + { + case ChangeType.Inserted: + messageBuilder.AppendLine($"+ {line.Text}"); + addedLines++; + break; + + case ChangeType.Deleted: + messageBuilder.AppendLine($"- {line.Text}"); + deletedLines++; + break; + + case ChangeType.Modified: + messageBuilder.AppendLine($"~ {line.Text}"); + modifiedLines++; + break; + + case ChangeType.Imaginary: + // Imaginary lines are used for alignment in side-by-side diffs + // We can skip them in inline diff output + break; + + case ChangeType.Unchanged: + messageBuilder.AppendLine($" {line.Text}"); + unchangedLines++; + break; + } + } + + if (contextBuffer.Count > 0) + { + skippedLines += contextBuffer.Count; + } + + if (skippedLines > 0) + { + messageBuilder.AppendLine($"... ({skippedLines} unchanged lines omitted) ..."); + } + + messageBuilder.AppendLine(); + messageBuilder.AppendLine("Summary:"); + messageBuilder.AppendLine($" Lines added: {addedLines}"); + messageBuilder.AppendLine($" Lines deleted: {deletedLines}"); + messageBuilder.AppendLine($" Lines modified: {modifiedLines}"); + + if (addedLines == 0 && deletedLines == 0 && modifiedLines == 0) + { + return BuildDiffMessage( + StringHelper.ShowNewLineCharacters(expected), + StringHelper.ShowNewLineCharacters(actual)); + } + + return messageBuilder.ToString(); + } + + [return: NotNullIfNotNull(nameof(text))] + private static string? NormalizeLineEndings(string? text) + { + if (text is null) + { + return null; + } + + return text + .Replace("\r\n", "\n", StringComparison.Ordinal) + .Replace("\r", "\n", StringComparison.Ordinal); + } + } +} \ No newline at end of file diff --git a/tests/War3Net.TestTools.UnitTesting/StringHelper.cs b/tests/War3Net.TestTools.UnitTesting/StringHelper.cs new file mode 100644 index 00000000..66cea568 --- /dev/null +++ b/tests/War3Net.TestTools.UnitTesting/StringHelper.cs @@ -0,0 +1,29 @@ +// ------------------------------------------------------------------------------ +// +// Licensed under the MIT license. +// See the LICENSE file in the project root for more information. +// +// ------------------------------------------------------------------------------ + +using System; +using System.Diagnostics.CodeAnalysis; + +namespace War3Net.TestTools.UnitTesting +{ + internal static class StringHelper + { + [return: NotNullIfNotNull(nameof(text))] + internal static string? ShowNewLineCharacters(string? text) + { + if (text is null) + { + return null; + } + + return text + .Replace("\r\n", "\\r\n", StringComparison.Ordinal) + .Replace("\n", "\\n\n", StringComparison.Ordinal) + .Replace("\r", "\\r\n", StringComparison.Ordinal); + } + } +} \ No newline at end of file diff --git a/tests/War3Net.TestTools.UnitTesting/SyntaxAssert.cs b/tests/War3Net.TestTools.UnitTesting/SyntaxAssert.cs index 841d519a..bcbd5c55 100644 --- a/tests/War3Net.TestTools.UnitTesting/SyntaxAssert.cs +++ b/tests/War3Net.TestTools.UnitTesting/SyntaxAssert.cs @@ -22,55 +22,39 @@ public static class SyntaxAssert { public static void AreEqual(JassCompilationUnitSyntax? expected, JassCompilationUnitSyntax? actual) { - if (!expected.NullableEquals(actual)) + if (!expected.NullableEquivalentTo(actual)) { Assert.Fail("Compilation units are not equal:\r\n" + GetAssertFailedMessage(expected, actual)); } } - public static void AreEqual(IDeclarationLineSyntax? expected, IDeclarationLineSyntax? actual) + public static void AreEqual(JassSyntaxNode? expected, JassSyntaxNode? actual) { - if (!expected.NullableEquals(actual)) - { - Assert.Fail("Declaration lines are not equal:\r\n" + GetAssertFailedMessage(expected, actual)); - } - } - - public static void AreEqual(IGlobalLineSyntax? expected, IGlobalLineSyntax? actual) - { - if (!expected.NullableEquals(actual)) - { - Assert.Fail("Global lines are not equal:\r\n" + GetAssertFailedMessage(expected, actual)); - } + if (!expected.NullableEquivalentTo(actual)) + { + Assert.Fail("Syntax nodes are not equal:\r\n" + GetAssertFailedMessage(expected, actual)); + } } - public static void AreEqual(IStatementLineSyntax? expected, IStatementLineSyntax? actual) + public static void AreEqual(JassExpressionSyntax? expected, JassExpressionSyntax? actual) { - if (!expected.NullableEquals(actual)) - { - Assert.Fail("Statement lines are not equal:\r\n" + GetAssertFailedMessage(expected, actual)); - } - } - - public static void AreEqual(IExpressionSyntax? expected, IExpressionSyntax? actual) - { - if (!expected.NullableEquals(actual)) + if (!expected.NullableEquivalentTo(actual)) { Assert.Fail("Expressions are not equal:\r\n" + GetAssertFailedMessage(expected, actual)); } } - public static void AreEqual(ITopLevelDeclarationSyntax? expected, ITopLevelDeclarationSyntax? actual) + public static void AreEqual(JassTopLevelDeclarationSyntax? expected, JassTopLevelDeclarationSyntax? actual) { - if (!expected.NullableEquals(actual)) + if (!expected.NullableEquivalentTo(actual)) { Assert.Fail("Declarations are not equal:\r\n" + GetAssertFailedMessage(expected, actual)); } } - public static void AreEqual(IStatementSyntax? expected, IStatementSyntax? actual) + public static void AreEqual(JassStatementSyntax? expected, JassStatementSyntax? actual) { - if (!expected.NullableEquals(actual)) + if (!expected.NullableEquivalentTo(actual)) { Assert.Fail("Statements are not equal:\r\n" + GetAssertFailedMessage(expected, actual)); } @@ -78,55 +62,39 @@ public static void AreEqual(IStatementSyntax? expected, IStatementSyntax? actual public static void AreNotEqual(JassCompilationUnitSyntax? expected, JassCompilationUnitSyntax? actual) { - if (expected.NullableEquals(actual)) + if (expected.NullableEquivalentTo(actual)) { Assert.Fail($"Compilation units are equal:\r\n'{expected?.ToString()}'<{expected?.GetType().Name ?? "null"}>."); } } - public static void AreNotEqual(IDeclarationLineSyntax? expected, IDeclarationLineSyntax? actual) + public static void AreNotEqual(JassSyntaxNode? expected, JassSyntaxNode? actual) { - if (expected.NullableEquals(actual)) - { - Assert.Fail($"Declaration lines are equal:\r\n'{expected?.ToString()}'<{expected?.GetType().Name ?? "null"}>."); - } - } - - public static void AreNotEqual(IGlobalLineSyntax? expected, IGlobalLineSyntax? actual) - { - if (expected.NullableEquals(actual)) - { - Assert.Fail($"Global lines are equal:\r\n'{expected?.ToString()}'<{expected?.GetType().Name ?? "null"}>."); - } + if (expected.NullableEquivalentTo(actual)) + { + Assert.Fail($"Syntax nodes are equal:\r\n'{expected?.ToString()}'<{expected?.GetType().Name ?? "null"}>."); + } } - public static void AreNotEqual(IStatementLineSyntax? expected, IStatementLineSyntax? actual) + public static void AreNotEqual(JassTopLevelDeclarationSyntax? expected, JassTopLevelDeclarationSyntax? actual) { - if (expected.NullableEquals(actual)) - { - Assert.Fail($"Statement lines are equal:\r\n'{expected?.ToString()}'<{expected?.GetType().Name ?? "null"}>."); - } - } - - public static void AreNotEqual(ITopLevelDeclarationSyntax? expected, ITopLevelDeclarationSyntax? actual) - { - if (expected.NullableEquals(actual)) + if (expected.NullableEquivalentTo(actual)) { Assert.Fail($"Declarations are equal:\r\n'{expected?.ToString()}'<{expected?.GetType().Name ?? "null"}>."); } } - public static void AreNotEqual(IExpressionSyntax? expected, IExpressionSyntax? actual) + public static void AreNotEqual(JassExpressionSyntax? expected, JassExpressionSyntax? actual) { - if (expected.NullableEquals(actual)) + if (expected.NullableEquivalentTo(actual)) { Assert.Fail($"Expressions are equal:\r\n'{expected?.ToString()}'<{expected?.GetType().Name ?? "null"}>."); } } - public static void AreNotEqual(IStatementSyntax? expected, IStatementSyntax? actual) + public static void AreNotEqual(JassStatementSyntax? expected, JassStatementSyntax? actual) { - if (expected.NullableEquals(actual)) + if (expected.NullableEquivalentTo(actual)) { Assert.Fail($"Statements are equal:\r\n'{expected?.ToString()}'<{expected?.GetType().Name ?? "null"}>."); } @@ -168,7 +136,7 @@ private static string GetAssertFailedMessage(JassCompilationUnitSyntax? expected for (var i = 0; i < length; i++) { - if (!expected.Declarations[i].Equals(actual.Declarations[i])) + if (!expected.Declarations[i].IsEquivalentTo(actual.Declarations[i])) { if (messageParts.Count > 20) { @@ -187,32 +155,32 @@ private static string GetAssertFailedMessage(JassCompilationUnitSyntax? expected return GetAssertFailedMessage((object?)expected, actual); } - private static string GetAssertFailedMessage(ITopLevelDeclarationSyntax? expected, ITopLevelDeclarationSyntax? actual) + private static string GetAssertFailedMessage(JassTopLevelDeclarationSyntax? expected, JassTopLevelDeclarationSyntax? actual) { if (expected is JassFunctionDeclarationSyntax expectedFunctionDeclaration && actual is JassFunctionDeclarationSyntax actualFunctionDeclaration) { var messageParts = new List(); - if (!expectedFunctionDeclaration.FunctionDeclarator.Equals(actualFunctionDeclaration.FunctionDeclarator)) + if (!expectedFunctionDeclaration.FunctionDeclarator.IsEquivalentTo(actualFunctionDeclaration.FunctionDeclarator)) { messageParts.Add(GetAssertFailedMessage(expectedFunctionDeclaration.FunctionDeclarator, actualFunctionDeclaration.FunctionDeclarator)); } - var length = expectedFunctionDeclaration.Body.Statements.Length; - if (expectedFunctionDeclaration.Body.Statements.Length != actualFunctionDeclaration.Body.Statements.Length) + var length = expectedFunctionDeclaration.Statements.Length; + if (expectedFunctionDeclaration.Statements.Length != actualFunctionDeclaration.Statements.Length) { - messageParts.Add($"Expected: {expectedFunctionDeclaration.Body.Statements.Length} statements."); - messageParts.Add($" Actual: {actualFunctionDeclaration.Body.Statements.Length} statements."); + messageParts.Add($"Expected: {expectedFunctionDeclaration.Statements.Length} statements."); + messageParts.Add($" Actual: {actualFunctionDeclaration.Statements.Length} statements."); - if (expectedFunctionDeclaration.Body.Statements.Length > actualFunctionDeclaration.Body.Statements.Length) + if (expectedFunctionDeclaration.Statements.Length > actualFunctionDeclaration.Statements.Length) { - length = actualFunctionDeclaration.Body.Statements.Length; + length = actualFunctionDeclaration.Statements.Length; } } for (var i = 0; i < length; i++) { - if (!expectedFunctionDeclaration.Body.Statements[i].Equals(actualFunctionDeclaration.Body.Statements[i])) + if (!expectedFunctionDeclaration.Statements[i].IsEquivalentTo(actualFunctionDeclaration.Statements[i])) { if (messageParts.Count > 20) { @@ -221,7 +189,7 @@ private static string GetAssertFailedMessage(ITopLevelDeclarationSyntax? expecte } messageParts.Add($"Statement #{i + 1}:"); - messageParts.Add(GetAssertFailedMessage(expectedFunctionDeclaration.Body.Statements[i], actualFunctionDeclaration.Body.Statements[i])); + messageParts.Add(GetAssertFailedMessage(expectedFunctionDeclaration.Statements[i], actualFunctionDeclaration.Statements[i])); } } @@ -231,13 +199,35 @@ private static string GetAssertFailedMessage(ITopLevelDeclarationSyntax? expecte return GetAssertFailedMessage((object?)expected, actual); } + public static void AreEqual(JassSyntaxNodeOrToken expected, JassSyntaxNodeOrToken actual) + { + if (expected.IsNode && actual.IsNode) + { + if (!expected.AsNode.IsEquivalentTo(actual.AsNode)) + { + Assert.Fail("Syntax nodes are not equal:\r\n" + GetAssertFailedMessage(expected.AsNode, actual.AsNode)); + } + } + else if (expected.IsToken && actual.IsToken) + { + if (!expected.AsToken.IsEquivalentTo(actual.AsToken)) + { + Assert.Fail($"Syntax tokens are not equal:\r\nExpected: '{expected.AsToken.ToFullString()}'<{expected.AsToken.SyntaxKind}>\r\nActual: '{actual.AsToken.ToFullString()}'<{actual.AsToken.SyntaxKind}>"); + } + } + else + { + Assert.Fail($"Syntax types are not equal:\r\nExpected: {(expected.IsNode ? "Node" : "Token")}\r\nActual: {(actual.IsNode ? "Node" : "Token")}"); + } + } + public static void ExpressionThrowsException(string expression) { var message = new BoxedString(); Assert.ThrowsException(() => message.String = GetExpressionDisplayString(JassSyntaxFactory.ParseExpression(expression)), "\r\n{0}", message); } - private static string GetExpressionDisplayString(IExpressionSyntax? expression) + private static string GetExpressionDisplayString(JassExpressionSyntax? expression) { if (expression is null) { diff --git a/tests/War3Net.TestTools.UnitTesting/TestData/Transpilation/aligned-globals.cs b/tests/War3Net.TestTools.UnitTesting/TestData/Transpilation/aligned-globals.cs new file mode 100644 index 00000000..01c86ff5 --- /dev/null +++ b/tests/War3Net.TestTools.UnitTesting/TestData/Transpilation/aligned-globals.cs @@ -0,0 +1,13 @@ + + public static @event udg_event = default; + public static int udg_integer = default; + public static bool udg_boolean = default; + public static string udg_string = default; + public static System.Action udg_code = default; + + // arrays + public static @event[] udg_events = new @event[JASS_MAX_ARRAY_SIZE]; + public static int[] udg_integers = new int[JASS_MAX_ARRAY_SIZE]; + public static bool[] udg_booleans = new bool[JASS_MAX_ARRAY_SIZE]; + public static string[] udg_strings = new string[JASS_MAX_ARRAY_SIZE]; + public static System.Action[] udg_codes = new System.Action[JASS_MAX_ARRAY_SIZE]; diff --git a/tests/War3Net.TestTools.UnitTesting/TestData/Transpilation/aligned-globals.j b/tests/War3Net.TestTools.UnitTesting/TestData/Transpilation/aligned-globals.j new file mode 100644 index 00000000..b052fa19 --- /dev/null +++ b/tests/War3Net.TestTools.UnitTesting/TestData/Transpilation/aligned-globals.j @@ -0,0 +1,14 @@ +globals + event udg_event + integer udg_integer + boolean udg_boolean + string udg_string + code udg_code + + // arrays + event array udg_events + integer array udg_integers + boolean array udg_booleans + string array udg_strings + code array udg_codes +endglobals \ No newline at end of file diff --git a/tests/War3Net.TestTools.UnitTesting/TestData/Transpilation/if-statements.cs b/tests/War3Net.TestTools.UnitTesting/TestData/Transpilation/if-statements.cs new file mode 100644 index 00000000..b6368a80 --- /dev/null +++ b/tests/War3Net.TestTools.UnitTesting/TestData/Transpilation/if-statements.cs @@ -0,0 +1,37 @@ +public static void Test() { + if (udg_bool==true) { + } // endif + + if ( udg_bool == true ) { + } else { + } // endif + + if (udg_int==0) { + } else if (udg_int==1) { + } else if (udg_int==2) { + } // endif + + if ( udg_int == 0 ) { + } else if ( udg_int == 1 ) { + } else if ( udg_int == 2 ) { + } else { + } // endif + + if(udg_bool==true){ + } // endif + + if ( udg_bool == true ) { + } else { + } // endif + + if(udg_int==0){ + } else if(udg_int==1){ + } else if(udg_int==2){ + } // endif + + if ( udg_int == 0 ) { + } else if ( udg_int == 1 ) { + } else if ( udg_int == 2 ) { + } else { + } // endif +} \ No newline at end of file diff --git a/tests/War3Net.TestTools.UnitTesting/TestData/Transpilation/if-statements.j b/tests/War3Net.TestTools.UnitTesting/TestData/Transpilation/if-statements.j new file mode 100644 index 00000000..9aa30ce8 --- /dev/null +++ b/tests/War3Net.TestTools.UnitTesting/TestData/Transpilation/if-statements.j @@ -0,0 +1,37 @@ +function Test takes nothing returns nothing + if udg_bool==true then + endif // endif + + if udg_bool == true then + else + endif // endif + + if udg_int==0then + elseif udg_int==1then + elseif udg_int==2then + endif // endif + + if udg_int == 0 then + elseif udg_int == 1 then + elseif udg_int == 2 then + else + endif // endif + + if(udg_bool==true)then + endif // endif + + if ( udg_bool == true ) then + else + endif // endif + + if(udg_int==0)then + elseif(udg_int==1)then + elseif(udg_int==2)then + endif // endif + + if ( udg_int == 0 ) then + elseif ( udg_int == 1 ) then + elseif ( udg_int == 2 ) then + else + endif // endif +endfunction \ No newline at end of file diff --git a/tests/War3Net.TestTools.UnitTesting/TestDataProvider.cs b/tests/War3Net.TestTools.UnitTesting/TestDataProvider.cs index c199638f..2a5814a4 100644 --- a/tests/War3Net.TestTools.UnitTesting/TestDataProvider.cs +++ b/tests/War3Net.TestTools.UnitTesting/TestDataProvider.cs @@ -5,6 +5,8 @@ // // ------------------------------------------------------------------------------ +// #define ENABLE_WEB_CAPABILITIES + using System; using System.Collections.Generic; using System.IO; @@ -96,7 +98,9 @@ private static IEnumerable GetTestDataDirectories(params string[] direct } else { +#if ENABLE_WEB_CAPABILITIES DownloadTestData(directory); +#endif } } diff --git a/tests/War3Net.TestTools.UnitTesting/TriggerAssert.cs b/tests/War3Net.TestTools.UnitTesting/TriggerAssert.cs index 1b02441a..c193c2e8 100644 --- a/tests/War3Net.TestTools.UnitTesting/TriggerAssert.cs +++ b/tests/War3Net.TestTools.UnitTesting/TriggerAssert.cs @@ -101,8 +101,8 @@ public static void AreEqual(TriggerFunction expectedFunction, TriggerFunction ac var expectedFunctionParameter = expectedFunction.Parameters.Single(); var actualFunctionParameter = actualFunction.Parameters.Single(); - var expectedCustomScriptAction = JassSyntaxFactory.ParseStatementLine(expectedFunctionParameter.Value); - var actualCustomScriptAction = JassSyntaxFactory.ParseStatementLine(actualFunctionParameter.Value); + var expectedCustomScriptAction = JassSyntaxFactory.ParseCustomScriptAction(expectedFunctionParameter.Value); + var actualCustomScriptAction = JassSyntaxFactory.ParseCustomScriptAction(actualFunctionParameter.Value); Assert.AreEqual(expectedFunctionParameter.Type, actualFunctionParameter.Type); SyntaxAssert.AreEqual(expectedCustomScriptAction, actualCustomScriptAction); diff --git a/tests/War3Net.TestTools.UnitTesting/War3Net.TestTools.UnitTesting.csproj b/tests/War3Net.TestTools.UnitTesting/War3Net.TestTools.UnitTesting.csproj index 7c515bf0..dccdbf74 100644 --- a/tests/War3Net.TestTools.UnitTesting/War3Net.TestTools.UnitTesting.csproj +++ b/tests/War3Net.TestTools.UnitTesting/War3Net.TestTools.UnitTesting.csproj @@ -4,6 +4,10 @@ $(TfmGroupDotNet) + + + + @@ -13,8 +17,10 @@ - - + + + +