Skip to content

Commit 6627266

Browse files
committed
Merge remote-tracking branch 'origin/main' into webbju/refactor-parsing
2 parents 26db68e + 6785aed commit 6627266

File tree

10 files changed

+234
-79
lines changed

10 files changed

+234
-79
lines changed

ServerCodeExciser/AssemblyInfo.cs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
using System.Runtime.CompilerServices;
2+
using System.Runtime.InteropServices;
3+
4+
// In SDK-style projects such as this one, several assembly attributes that were historically
5+
// defined in this file are now automatically added during build and populated with
6+
// values defined in project properties. For details of which attributes are included
7+
// and how to customise this process see: https://aka.ms/assembly-info-properties
8+
9+
10+
// Setting ComVisible to false makes the types in this assembly not visible to COM
11+
// components. If you need to access a type in this assembly from COM, set the ComVisible
12+
// attribute to true on that type.
13+
14+
[assembly: ComVisible(false)]
15+
16+
// The following GUID is for the ID of the typelib if this project is exposed to COM.
17+
18+
[assembly: Guid("23858a33-edb8-4a1a-81c6-6a7347386704")]
19+
20+
[assembly: InternalsVisibleTo("ServerCodeExciserTest")]

ServerCodeExciser/ServerCodeExcisionProcessor.cs

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -242,8 +242,12 @@ internal EExciserReturnValues ProcessCodeFile(string script, string outputFile,
242242
}
243243

244244
// Determine if there are any existing preprocessor server-code exclusions in the source file.
245-
var preprocessorNodes = PreprocessorParser.Parse(commonTokenStream);
246-
var detectedPreprocessorServerOnlyScopes = FindScopesForSymbol(preprocessorNodes, scope => scope.Directive.Contains(excisionLanguage.ServerScopeStartString, StringComparison.Ordinal));
245+
var preprocessorScopes = PreprocessorParser.Parse(commonTokenStream);
246+
var detectedPreprocessorServerOnlyScopes = new List<PreprocessorScope>();
247+
FindPreprocessorScopesForSymbolRecursive(
248+
preprocessorScopes,
249+
scope => scope.Directive.Contains(excisionLanguage.ServerScopeStartString, StringComparison.Ordinal),
250+
detectedPreprocessorServerOnlyScopes);
247251

248252
// Process scopes we've evaluated must be server only.
249253
foreach (ServerOnlyScopeData currentScope in visitor.DetectedServerOnlyScopes)
@@ -316,7 +320,7 @@ internal EExciserReturnValues ProcessCodeFile(string script, string outputFile,
316320
// Next we must add dummy reference variables if they exist.
317321
// If there is already a block of dummy reference variables we skip adding new ones, there is no guarantee we are adding the right code.
318322
var dummyVarScope = "#ifndef " + excisionLanguage.ServerPrecompilerSymbol;
319-
if (!FindScopesForSymbol(preprocessorNodes, scope => scope.Directive.Contains(dummyVarScope, StringComparison.Ordinal)).Any())
323+
if (!FindScopesForSymbol(preprocessorScopes, scope => scope.Directive.Contains(dummyVarScope, StringComparison.Ordinal)).Any())
320324
{
321325
foreach (KeyValuePair<int, HashSet<string>> dummyRefDataPair in visitor.ClassStartIdxDummyReferenceData)
322326
{
@@ -433,11 +437,16 @@ private static (int StartIndex, int StopIndex) TrimWhitespace(ReadOnlySpan<char>
433437
return (startIndex, stopIndex);
434438
}
435439

436-
public static List<PreprocessorScope> FindScopesForSymbol(List<PreprocessorScope> scopes, Predicate<PreprocessorScope> predicate)
440+
private static void FindPreprocessorScopesForSymbolRecursive(List<PreprocessorScope> scopes, Predicate<PreprocessorScope> predicate, List<PreprocessorScope> result)
437441
{
438-
var result = new List<PreprocessorScope>();
439-
FindScopesForSymbolRecursive(scopes, predicate, result);
440-
return result;
442+
foreach (var scope in scopes)
443+
{
444+
if (predicate(scope))
445+
{
446+
result.Add(scope);
447+
}
448+
FindPreprocessorScopesForSymbolRecursive(scope.Children, predicate, result);
449+
}
441450
}
442451

443452
private static void FindScopesForSymbolRecursive(List<PreprocessorScope> scopes, Predicate<PreprocessorScope> predicate, List<PreprocessorScope> result)
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
using System;
2+
using System.IO;
3+
using Antlr4.Runtime;
4+
using Microsoft.VisualStudio.TestTools.UnitTesting;
5+
using UnrealAngelscriptServerCodeExcision;
6+
7+
namespace ServerCodeExciser.Tests
8+
{
9+
[TestClass]
10+
public class AngelscriptSyntaxTests
11+
{
12+
private sealed class ThrowingErrorListener : BaseErrorListener
13+
{
14+
public override void SyntaxError(TextWriter output, IRecognizer recognizer, IToken offendingSymbol, int line, int charPositionInLine, string msg, RecognitionException e)
15+
{
16+
throw new InvalidOperationException($"line {line}:{charPositionInLine} {msg}");
17+
}
18+
}
19+
20+
[TestMethod]
21+
[DataRow("mynamespace")]
22+
[DataRow("Nested::mynamespace")]
23+
public void Namespace(string @namespace)
24+
{
25+
ParseScript($"namespace {@namespace}\r\n{{\r\n}}\r\n");
26+
}
27+
28+
[TestMethod]
29+
public void NamedArgumentsFunctionCall()
30+
{
31+
ParseScript("void Func(bool Arg1 = false, int Arg2 = 0) {}\r\nvoid main() { Func(Arg2: 1, Arg1: true); }\r\n");
32+
}
33+
34+
[TestMethod]
35+
[DataRow("")]
36+
[DataRow("final")]
37+
[DataRow("override")]
38+
[DataRow("no_discard")] // UnrealAngelscript
39+
[DataRow("allow_discard")] // UnrealAngelscript
40+
[DataRow("accept_temporary_this")] // UnrealAngelscript
41+
[DataRow("const allow_discard")] // UnrealAngelscript
42+
public void FunctionModifier(string modifier)
43+
{
44+
ParseScript($"bool Func() {modifier}\r\n{{\r\nreturn true;\r\n}}");
45+
}
46+
47+
[TestMethod]
48+
[DataRow("int", "0")]
49+
[DataRow("int8", "0")]
50+
[DataRow("int16", "0")]
51+
[DataRow("int32", "0")]
52+
[DataRow("int64", "0")]
53+
[DataRow("uint", "0")]
54+
[DataRow("uint8", "0")]
55+
[DataRow("uint16", "0")]
56+
[DataRow("uint32", "0")]
57+
[DataRow("uint64", "0")]
58+
[DataRow("float", "0.0f")]
59+
[DataRow("float32", "0.0f")]
60+
[DataRow("float64", "0.0")]
61+
[DataRow("doublt", "0.0")]
62+
[DataRow("bool", "false")]
63+
[DataRow("string", "\"\\0\"")]
64+
[DataRow("string", "\"\\xFFFF\"")]
65+
[DataRow("string", "\"\\uFFFF\"")]
66+
[DataRow("string", "\"\\uFFFFFFFF\"")]
67+
[DataRow("string", "\"\"\"\r\nheredoc string\r\n\"\"\"")] // heredoc: https://www.angelcode.com/angelscript/sdk/docs/manual/doc_datatypes_strings.html
68+
[DataRow("FName", "n\"MyName\"")] // FName Literals: https://angelscript.hazelight.se/scripting/fname-literals/
69+
[DataRow("string", "f\"Formatted String: {L:0.1f}\"")] // Formatted Strings: https://angelscript.hazelight.se/scripting/format-strings/
70+
public void DataType(string type, string value)
71+
{
72+
ParseScript($"{type} VAR = {value};");
73+
}
74+
75+
[TestMethod]
76+
[DataRow("")]
77+
[DataRow("UCLASS()")]
78+
[DataRow("UCLASS(Abstract)")]
79+
public void UClass(string annotation)
80+
{
81+
ParseScript($"{annotation} class ClassName : BaseClass {{}};");
82+
}
83+
84+
[TestMethod]
85+
[DataRow("")]
86+
[DataRow("UPROPERTY()")]
87+
[DataRow("UPROPERTY(DefaultComponent)")]
88+
[DataRow("UPROPERTY(DefaultComponent,)")]
89+
[DataRow("UPROPERTY(DefaultComponent, RootComponent)")]
90+
public void UProperty(string annotation)
91+
{
92+
ParseScript($"class ClassName : BaseClass\r\n{{\r\n\t{annotation}\r\nDummyType DummyProperty;\r\n}};");
93+
}
94+
95+
private static void ParseScript(string script)
96+
{
97+
var lexer = new UnrealAngelscriptLexer(new AntlrInputStream(script));
98+
var tokenStream = new CommonTokenStream(lexer);
99+
var parser = new UnrealAngelscriptParser(tokenStream);
100+
parser.RemoveErrorListeners();
101+
parser.AddErrorListener(new ThrowingErrorListener());
102+
new UnrealAngelscriptSimpleVisitor(script).VisitChildren(parser.script());
103+
}
104+
}
105+
}

ServerCodeExciserTest/ExcisionIntegrationTests.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
using System;
22
using System.IO;
33
using Microsoft.VisualStudio.TestTools.UnitTesting;
4-
using ServerCodeExciser;
54
using ServerCodeExcisionCommon;
65
using UnrealAngelscriptServerCodeExcision;
76

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
using Antlr4.Runtime;
2+
using Microsoft.VisualStudio.TestTools.UnitTesting;
3+
4+
namespace ServerCodeExciser.Tests
5+
{
6+
[TestClass]
7+
public class PreprocessorParserTests
8+
{
9+
[TestMethod]
10+
public void ConditionalBranchTest()
11+
{
12+
var script = "#ifdef WITH_SERVER\r\n" +
13+
"#elif RELEASE\r\n" +
14+
"#elif DEBUG\r\n" +
15+
"#else\r\n" +
16+
"#endif // WITH_SERVER\r\n";
17+
18+
var lexer = new UnrealAngelscriptLexer(new AntlrInputStream(script));
19+
var tokenStream = new CommonTokenStream(lexer);
20+
tokenStream.Fill();
21+
22+
var nodes = PreprocessorParser.Parse(tokenStream);
23+
Assert.HasCount(1, nodes);
24+
Assert.AreEqual("#ifdef WITH_SERVER", nodes[0].Directive);
25+
Assert.AreEqual("#elif RELEASE", nodes[0].Children[0].Directive);
26+
Assert.AreEqual("#elif DEBUG", nodes[0].Children[1].Directive);
27+
Assert.AreEqual("#else", nodes[0].Children[2].Directive);
28+
Assert.AreEqual(1, nodes[0].Span.Start.Line);
29+
Assert.AreEqual(0, nodes[0].Span.Start.Column);
30+
Assert.AreEqual(0, nodes[0].Span.StartIndex);
31+
Assert.AreEqual(5, nodes[0].Span.End.Line);
32+
Assert.AreEqual(0, nodes[0].Span.End.Column);
33+
Assert.AreEqual(script.Length - "\r\n".Length - 1, nodes[0].Span.EndIndex);
34+
}
35+
36+
[TestMethod]
37+
public void NestedTest()
38+
{
39+
var script = "#ifdef WITH_SERVER\r\n" +
40+
" #if RELEASE\r\n" +
41+
" #elif DEBUG\r\n" +
42+
" #endif // !RELEASE\r\n" +
43+
"#endif // WITH_SERVER\r\n";
44+
45+
var lexer = new UnrealAngelscriptLexer(new AntlrInputStream(script));
46+
var tokenStream = new CommonTokenStream(lexer);
47+
tokenStream.Fill();
48+
49+
var nodes = PreprocessorParser.Parse(tokenStream);
50+
Assert.HasCount(1, nodes);
51+
Assert.AreEqual("#ifdef WITH_SERVER", nodes[0].Directive);
52+
Assert.AreEqual("#if RELEASE", nodes[0].Children[0].Directive);
53+
Assert.AreEqual("#elif DEBUG", nodes[0].Children[0].Children[0].Directive);
54+
Assert.AreEqual(0, nodes[0].Span.End.Column);
55+
Assert.AreEqual(script.Length - "\r\n".Length - 1, nodes[0].Span.EndIndex);
56+
}
57+
}
58+
}

ServerCodeExciserTest/Problems/Angelscript/DatatypesTest.as

Lines changed: 0 additions & 27 deletions
This file was deleted.

ServerCodeExciserTest/Problems/Angelscript/DatatypesTest.as.solution

Lines changed: 0 additions & 27 deletions
This file was deleted.

ServerCodeExcisionCommon/SourcePosition.cs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
using Antlr4.Runtime;
2-
31
namespace ServerCodeExcisionCommon
42
{
53
/// <summary>

UnrealAngelscriptParser/Grammar/UnrealAngelscriptLexer.g4

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,11 @@ FloatingLiteral:
2424
// https://angelscript.hazelight.se/scripting/format-strings/
2525
fragment Angelscriptstringprefix: 'n' | 'f';
2626

27-
StringLiteral: (Encodingprefix | Angelscriptstringprefix)? (Rawstring | '"' Schar* '"');
27+
// Angelscript Heredoc string literals
28+
// https://www.angelcode.com/angelscript/sdk/docs/manual/doc_datatypes_strings.html
29+
fragment HeredocString: '"""' .*? '"""';
30+
31+
StringLiteral: (Encodingprefix | Angelscriptstringprefix)? (HeredocString | Rawstring | '"' Schar* '"');
2832

2933
BooleanLiteral: False | True;
3034

@@ -87,6 +91,12 @@ Bool: 'bool';
8791

8892
/* UnrealAngelscript */
8993

94+
NoDiscard: 'no_discard';
95+
96+
AllowDiscard: 'allow_discard';
97+
98+
AcceptTemporaryThis: 'accept_temporary_this';
99+
90100
UClass: 'UCLASS';
91101

92102
UStruct: 'USTRUCT';
@@ -109,12 +119,6 @@ Check: 'check';
109119

110120
Auto: 'auto';
111121

112-
NoDiscard: 'no_discard';
113-
114-
AllowDiscard: 'allow_discard';
115-
116-
AcceptTemporaryThis: 'accept_temporary_this';
117-
118122
Access: 'access';
119123

120124
Break: 'break';

0 commit comments

Comments
 (0)