|
1 | 1 | using System; |
2 | 2 | using System.Collections.Generic; |
3 | 3 | using System.IO; |
| 4 | +using System.Linq; |
4 | 5 | using System.Text; |
5 | 6 | using System.Text.RegularExpressions; |
6 | 7 | using Antlr4.Runtime; |
@@ -185,6 +186,7 @@ private ExcisionStats ProcessCodeFile(string fileName, string inputPath, EExcisi |
185 | 186 | var inputStream = new AntlrInputStream(script); |
186 | 187 | var lexer = excisionLanguage.CreateLexer<UnrealAngelscriptLexer>(inputStream); |
187 | 188 | lexer.AddErrorListener(new ExcisionLexerErrorListener()); |
| 189 | + |
188 | 190 | var commonTokenStream = new CommonTokenStream(lexer); |
189 | 191 | var parser = excisionLanguage.CreateParser<UnrealAngelscriptParser>(commonTokenStream); |
190 | 192 | parser.AddErrorListener(new ExcisionParserErrorListener()); |
@@ -221,23 +223,69 @@ private ExcisionStats ProcessCodeFile(string fileName, string inputPath, EExcisi |
221 | 223 | { |
222 | 224 | visitor.VisitContext(parser.script()); |
223 | 225 |
|
| 226 | + var preprocessorTokens = commonTokenStream |
| 227 | + .GetTokens() |
| 228 | + .Where(t => t.Channel == UnrealAngelscriptLexer.PREPROCESSOR_CHANNEL) |
| 229 | + .ToList(); |
| 230 | + |
224 | 231 | if (_parameters.UseFunctionStats) |
225 | 232 | { |
226 | 233 | stats.TotalNrCharacters = visitor.TotalNumberOfFunctionCharactersVisited; |
227 | 234 | } |
228 | 235 |
|
| 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 | + |
229 | 271 | // First process all server only scopes. |
230 | 272 | foreach (ServerOnlyScopeData currentScope in visitor.DetectedServerOnlyScopes) |
231 | 273 | { |
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) |
237 | 275 | { |
238 | 276 | continue; |
239 | 277 | } |
240 | 278 |
|
| 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 | + |
241 | 289 | // If there are already injected macros where we want to go, we should skip injecting. |
242 | 290 | System.Diagnostics.Debug.Assert(currentScope.StopIndex > currentScope.StartIndex, "There must be some invalid pattern here! Stop is before start!"); |
243 | 291 | serverCodeInjections.Add(new KeyValuePair<int, string>(currentScope.StartIndex, "\r\n" + excisionLanguage.ServerScopeStartString)); |
@@ -318,7 +366,89 @@ private static bool IsWhitespace(char c) |
318 | 366 | return c == ' ' || c == '\t' || c == '\r' || c == '\n'; |
319 | 367 | } |
320 | 368 |
|
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) |
322 | 452 | { |
323 | 453 | if (lookAhead) |
324 | 454 | { |
|
0 commit comments