Skip to content

Commit 5398aa7

Browse files
committed
Preprocessor support.
1 parent 0ec428d commit 5398aa7

File tree

4 files changed

+180
-18
lines changed

4 files changed

+180
-18
lines changed

ServerCodeExciser/ServerCodeExcisionProcessor.cs

Lines changed: 136 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using System;
22
using System.Collections.Generic;
33
using System.IO;
4+
using System.Linq;
45
using System.Text;
56
using System.Text.RegularExpressions;
67
using Antlr4.Runtime;
@@ -185,6 +186,7 @@ private ExcisionStats ProcessCodeFile(string fileName, string inputPath, EExcisi
185186
var inputStream = new AntlrInputStream(script);
186187
var lexer = excisionLanguage.CreateLexer<UnrealAngelscriptLexer>(inputStream);
187188
lexer.AddErrorListener(new ExcisionLexerErrorListener());
189+
188190
var commonTokenStream = new CommonTokenStream(lexer);
189191
var parser = excisionLanguage.CreateParser<UnrealAngelscriptParser>(commonTokenStream);
190192
parser.AddErrorListener(new ExcisionParserErrorListener());
@@ -221,23 +223,69 @@ private ExcisionStats ProcessCodeFile(string fileName, string inputPath, EExcisi
221223
{
222224
visitor.VisitContext(parser.script());
223225

226+
var preprocessorTokens = commonTokenStream
227+
.GetTokens()
228+
.Where(t => t.Channel == UnrealAngelscriptLexer.PREPROCESSOR_CHANNEL)
229+
.ToList();
230+
224231
if (_parameters.UseFunctionStats)
225232
{
226233
stats.TotalNrCharacters = visitor.TotalNumberOfFunctionCharactersVisited;
227234
}
228235

236+
// Find matching #ifdef WITH_SERVER / #endif pairs
237+
var serverGuardRanges = new List<(string Directive, int StartIndex, int StopIndex)>();
238+
var ifStack = new Stack<IToken>();
239+
var ifBranchStack = new Stack<IToken>();
240+
241+
foreach (var token in preprocessorTokens)
242+
{
243+
switch (token.Text)
244+
{
245+
case var t when t.StartsWith("#if", StringComparison.Ordinal):
246+
ifStack.Push(token);
247+
break;
248+
case var t when t.StartsWith("#elif", StringComparison.Ordinal):
249+
ifBranchStack.Push(token);
250+
break;
251+
case var t when t.StartsWith("#else", StringComparison.Ordinal):
252+
ifBranchStack.Push(token);
253+
break;
254+
case var t when t.StartsWith("#endif", StringComparison.Ordinal):
255+
if (ifStack.TryPop(out var removed))
256+
{
257+
while (ifBranchStack.TryPop(out var removeBranch))
258+
{
259+
serverGuardRanges.Add((removeBranch.Text, removed.StartIndex, token.StopIndex));
260+
}
261+
262+
serverGuardRanges.Add((removed.Text, removed.StartIndex, token.StopIndex));
263+
}
264+
break;
265+
}
266+
}
267+
System.Diagnostics.Debug.Assert(ifStack.Count == 0);
268+
269+
//var existingPreprocessorGuards = FindPreprocessorGuards(script, excisionLanguage.ServerPrecompilerSymbol);
270+
229271
// First process all server only scopes.
230272
foreach (ServerOnlyScopeData currentScope in visitor.DetectedServerOnlyScopes)
231273
{
232-
if (currentScope.StartIndex == -1
233-
|| currentScope.StopIndex == -1
234-
|| InjectedMacroAlreadyExistsAtLocation(answerText, currentScope.StartIndex, true, true, excisionLanguage.ServerScopeStartString)
235-
|| InjectedMacroAlreadyExistsAtLocation(answerText, currentScope.StartIndex, false, false, excisionLanguage.ServerScopeStartString)
236-
|| InjectedMacroAlreadyExistsAtLocation(answerText, currentScope.StopIndex, false, false, excisionLanguage.ServerScopeEndString))
274+
if (currentScope.StartIndex == -1 || currentScope.StopIndex == -1)
237275
{
238276
continue;
239277
}
240278

279+
// If there are already injected macros where we want to go, we should skip injecting.
280+
var (StartIndex, StopIndex) = TrimWhitespace(script, currentScope);
281+
//if (existingPreprocessorGuards.Any(x => StartIndex >= x.StartIndex && StopIndex <= x.StopIndex))
282+
if (serverGuardRanges
283+
.Where(x => x.Directive.Contains(excisionLanguage.ServerPrecompilerSymbol, StringComparison.Ordinal))
284+
.Any(x => StartIndex >= x.StartIndex && StopIndex <= x.StopIndex))
285+
{
286+
continue; // inside an existing server guard
287+
}
288+
241289
// If there are already injected macros where we want to go, we should skip injecting.
242290
System.Diagnostics.Debug.Assert(currentScope.StopIndex > currentScope.StartIndex, "There must be some invalid pattern here! Stop is before start!");
243291
serverCodeInjections.Add(new KeyValuePair<int, string>(currentScope.StartIndex, "\r\n" + excisionLanguage.ServerScopeStartString));
@@ -318,7 +366,89 @@ private static bool IsWhitespace(char c)
318366
return c == ' ' || c == '\t' || c == '\r' || c == '\n';
319367
}
320368

321-
private bool InjectedMacroAlreadyExistsAtLocation(StringBuilder script, int index, bool lookAhead, bool ignoreWhitespace, string macro)
369+
/// <summary>
370+
/// Resizes a scope range by excluding whitespace characters.
371+
/// </summary>
372+
public static (int StartIndex, int StopIndex) TrimWhitespace(string script, ServerOnlyScopeData scope)
373+
{
374+
var (startIndex, stopIndex) = (scope.StartIndex, scope.StopIndex);
375+
376+
while (IsWhitespace(script[startIndex]))
377+
{
378+
startIndex++;
379+
}
380+
381+
while (IsWhitespace(script[stopIndex]))
382+
{
383+
stopIndex--;
384+
}
385+
386+
return (startIndex, stopIndex);
387+
}
388+
389+
public static List<(int StartIndex, int StopIndex)> FindPreprocessorGuards(string script, string macro)
390+
{
391+
List<(int StartIndex, int StopIndex)> results = new();
392+
393+
int scriptIndex = 0;
394+
395+
int blockLevel = 0;
396+
int blockStartIndex = -1;
397+
398+
while (scriptIndex < script.Length)
399+
{
400+
if (script[scriptIndex] == '#')
401+
{
402+
if (script[scriptIndex..(scriptIndex + 3)] == "#if" || script[scriptIndex..(scriptIndex + 4)] == "#elif") // #if #ifdef #ifndef
403+
{
404+
if (++blockLevel == 1)
405+
{
406+
int lineEnd = scriptIndex;
407+
while (script[lineEnd] != '\r' && script[lineEnd] != '\n')
408+
{
409+
lineEnd++;
410+
}
411+
412+
if (script[scriptIndex..lineEnd].Contains(macro, StringComparison.Ordinal))
413+
{
414+
blockStartIndex = scriptIndex;
415+
}
416+
}
417+
}
418+
else if (script[scriptIndex..(scriptIndex + 6)] == "#endif")
419+
{
420+
if (--blockLevel == 0 && blockStartIndex != -1)
421+
{
422+
int lineEnd = scriptIndex;
423+
while (script[lineEnd] != '\r' && script[lineEnd] != '\n')
424+
{
425+
lineEnd++;
426+
}
427+
428+
results.Add((blockStartIndex, scriptIndex));
429+
430+
blockStartIndex = -1;
431+
}
432+
}
433+
}
434+
435+
scriptIndex++;
436+
}
437+
438+
return results;
439+
}
440+
441+
private static void FindPreprocessorGuards(TextReader reader)
442+
{
443+
string? line = reader.ReadLine();
444+
445+
while (line != null)
446+
{
447+
448+
}
449+
}
450+
451+
private static bool InjectedMacroAlreadyExistsAtLocation(StringBuilder script, int index, bool lookAhead, bool ignoreWhitespace, string macro)
322452
{
323453
if (lookAhead)
324454
{

ServerCodeExciserTest/Problems/Common/AlreadyHasGuardsTest.common

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@ class UAlreadyHasGuardsTest
22
{
33
void Test()
44
{
5+
// Commented out block.
6+
//#ifdef WITH_SERVER
7+
//#endif
8+
59
int SomethingBefore = 0;
610
SomethingBefore++;
711

@@ -26,14 +30,25 @@ class UAlreadyHasGuardsTest
2630
#endif // WITH_SERVER
2731
}
2832

29-
UFUNCTION(BlueprintOverride)
30-
void Test3(float DeltaSeconds)
33+
void Test3()
3134
{
32-
FTraceScope TraceScope(n"Test3");
3335
check(System::IsServer());
3436

3537
#ifdef WITH_SERVER
36-
GroundContactHitResults.Empty();
38+
int ThisMustBeGuarded = 0;
39+
ThisMustBeGuarded++;
40+
#endif
41+
}
42+
43+
void Test4()
44+
{
45+
check(System::IsServer());
46+
#ifdef WITH_SERVER
47+
48+
#if !RELEASE
49+
int ThisMustBeGuarded = 0;
50+
ThisMustBeGuarded++;
51+
#endif
3752
#endif
3853
}
3954
};

ServerCodeExciserTest/Problems/Common/AlreadyHasGuardsTest.common.solution

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@ class UAlreadyHasGuardsTest
22
{
33
void Test()
44
{
5+
// Commented out block.
6+
//#ifdef WITH_SERVER
7+
//#endif
8+
59
int SomethingBefore = 0;
610
SomethingBefore++;
711

@@ -26,14 +30,25 @@ class UAlreadyHasGuardsTest
2630
#endif // WITH_SERVER
2731
}
2832

29-
UFUNCTION(BlueprintOverride)
30-
void Tick(float DeltaSeconds)
33+
void Test3()
3134
{
32-
FTraceScope TraceScope(n"Tick\u00B0");
3335
check(System::IsServer());
3436

3537
#ifdef WITH_SERVER
36-
GroundContactHitResults.Empty();
38+
int ThisMustBeGuarded = 0;
39+
ThisMustBeGuarded++;
40+
#endif
41+
}
42+
43+
void Test4()
44+
{
45+
check(System::IsServer());
46+
#ifdef WITH_SERVER
47+
48+
#if !RELEASE
49+
int ThisMustBeGuarded = 0;
50+
ThisMustBeGuarded++;
51+
#endif
3752
#endif
3853
}
3954
};

UnrealAngelscriptParser/Grammar/UnrealAngelscriptLexer.g4

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55

66
lexer grammar UnrealAngelscriptLexer;
77

8+
channels { PREPROCESSOR_CHANNEL }
9+
810
IntegerLiteral:
911
DecimalLiteral Integersuffix?
1012
| OctalLiteral Integersuffix?
@@ -34,6 +36,10 @@ UserDefinedLiteral:
3436
| UserDefinedCharacterLiteral
3537
;
3638

39+
MultiLineMacro: '#' (~[\n]*? '\\' '\r'? '\n')+ ~ [\n]+ -> channel (PREPROCESSOR_CHANNEL);
40+
41+
Directive: '#' ~ [\r\n]* -> channel (PREPROCESSOR_CHANNEL);
42+
3743
/*Angelscript*/
3844

3945
Cast: 'cast';
@@ -368,7 +374,3 @@ Newline: ('\r' '\n'? | '\n') -> skip;
368374
BlockComment: '/*' .*? '*/' -> skip;
369375
370376
LineComment: '//' ~ [\r\n]* -> skip;
371-
372-
PreprocessorBranchRemoval: '#else' .*? '#endif' -> skip;
373-
374-
Preprocessor: ('#if' | '#ifdef' | '#else' | '#endif') ~ [\r\n]* -> skip;

0 commit comments

Comments
 (0)