Cache regex instances in most cases (#27699)

Using static Regex functions that take in a pattern is bad because the pattern constantly needs to be re-parsed. With https://github.com/space-wizards/RobustToolbox/pull/5107, the engine has an analyzer to warn for this practice now.

This commit brings most of content up to snuff already, though some of the tricker code I left for somebody else.
This commit is contained in:
Pieter-Jan Briers
2024-05-06 00:57:32 +02:00
committed by GitHub
parent 70d3cf7ba4
commit 4a2a63a86b
10 changed files with 74 additions and 60 deletions

View File

@@ -10,6 +10,10 @@ public sealed class FrenchAccentSystem : EntitySystem
{
[Dependency] private readonly ReplacementAccentSystem _replacement = default!;
private static readonly Regex RegexTh = new(@"th", RegexOptions.IgnoreCase);
private static readonly Regex RegexStartH = new(@"(?<!\w)h", RegexOptions.IgnoreCase);
private static readonly Regex RegexSpacePunctuation = new(@"(?<=\w\w)[!?;:](?!\w)", RegexOptions.IgnoreCase);
public override void Initialize()
{
base.Initialize();
@@ -23,17 +27,14 @@ public sealed class FrenchAccentSystem : EntitySystem
msg = _replacement.ApplyReplacements(msg, "french");
// replaces th with dz
msg = Regex.Replace(msg, @"th", "'z", RegexOptions.IgnoreCase);
// replaces th with dz
msg = RegexTh.Replace(msg, "'z");
// removes the letter h from the start of words.
msg = Regex.Replace(msg, @"(?<!\w)[h]", "'", RegexOptions.IgnoreCase);
msg = RegexStartH.Replace(msg, "'");
// spaces out ! ? : and ;.
msg = Regex.Replace(msg, @"(?<=\w\w)!(?!\w)", " !", RegexOptions.IgnoreCase);
msg = Regex.Replace(msg, @"(?<=\w\w)[?](?!\w)", " ?", RegexOptions.IgnoreCase);
msg = Regex.Replace(msg, @"(?<=\w\w)[;](?!\w)", " ;", RegexOptions.IgnoreCase);
msg = Regex.Replace(msg, @"(?<=\w\w)[:](?!\w)", " :", RegexOptions.IgnoreCase);
msg = RegexSpacePunctuation.Replace(msg, " $&");
return msg;
}

View File

@@ -5,6 +5,13 @@ namespace Content.Server.Speech.EntitySystems;
public sealed class FrontalLispSystem : EntitySystem
{
// @formatter:off
private static readonly Regex RegexUpperTh = new(@"[T]+[Ss]+|[S]+[Cc]+(?=[IiEeYy]+)|[C]+(?=[IiEeYy]+)|[P][Ss]+|([S]+[Tt]+|[T]+)(?=[Ii]+[Oo]+[Uu]*[Nn]*)|[C]+[Hh]+(?=[Ii]*[Ee]*)|[Z]+|[S]+|[X]+(?=[Ee]+)");
private static readonly Regex RegexLowerTh = new(@"[t]+[s]+|[s]+[c]+(?=[iey]+)|[c]+(?=[iey]+)|[p][s]+|([s]+[t]+|[t]+)(?=[i]+[o]+[u]*[n]*)|[c]+[h]+(?=[i]*[e]*)|[z]+|[s]+|[x]+(?=[e]+)");
private static readonly Regex RegexUpperEcks = new(@"[E]+[Xx]+[Cc]*|[X]+");
private static readonly Regex RegexLowerEcks = new(@"[e]+[x]+[c]*|[x]+");
// @formatter:on
public override void Initialize()
{
base.Initialize();
@@ -16,11 +23,11 @@ public sealed class FrontalLispSystem : EntitySystem
var message = args.Message;
// handles ts, sc(i|e|y), c(i|e|y), ps, st(io(u|n)), ch(i|e), z, s
message = Regex.Replace(message, @"[T]+[Ss]+|[S]+[Cc]+(?=[IiEeYy]+)|[C]+(?=[IiEeYy]+)|[P][Ss]+|([S]+[Tt]+|[T]+)(?=[Ii]+[Oo]+[Uu]*[Nn]*)|[C]+[Hh]+(?=[Ii]*[Ee]*)|[Z]+|[S]+|[X]+(?=[Ee]+)", "TH");
message = Regex.Replace(message, @"[t]+[s]+|[s]+[c]+(?=[iey]+)|[c]+(?=[iey]+)|[p][s]+|([s]+[t]+|[t]+)(?=[i]+[o]+[u]*[n]*)|[c]+[h]+(?=[i]*[e]*)|[z]+|[s]+|[x]+(?=[e]+)", "th");
message = RegexUpperTh.Replace(message, "TH");
message = RegexLowerTh.Replace(message, "th");
// handles ex(c), x
message = Regex.Replace(message, @"[E]+[Xx]+[Cc]*|[X]+", "EKTH");
message = Regex.Replace(message, @"[e]+[x]+[c]*|[x]+", "ekth");
message = RegexUpperEcks.Replace(message, "EKTH");
message = RegexLowerEcks.Replace(message, "ekth");
args.Message = message;
}

View File

@@ -5,6 +5,12 @@ namespace Content.Server.Speech.EntitySystems;
public sealed class LizardAccentSystem : EntitySystem
{
private static readonly Regex RegexLowerS = new("s+");
private static readonly Regex RegexUpperS = new("S+");
private static readonly Regex RegexInternalX = new(@"(\w)x");
private static readonly Regex RegexLowerEndX = new(@"\bx([\-|r|R]|\b)");
private static readonly Regex RegexUpperEndX = new(@"\bX([\-|r|R]|\b)");
public override void Initialize()
{
base.Initialize();
@@ -16,15 +22,15 @@ public sealed class LizardAccentSystem : EntitySystem
var message = args.Message;
// hissss
message = Regex.Replace(message, "s+", "sss");
message = RegexLowerS.Replace(message, "sss");
// hiSSS
message = Regex.Replace(message, "S+", "SSS");
message = RegexUpperS.Replace(message, "SSS");
// ekssit
message = Regex.Replace(message, @"(\w)x", "$1kss");
message = RegexInternalX.Replace(message, "$1kss");
// ecks
message = Regex.Replace(message, @"\bx([\-|r|R]|\b)", "ecks$1");
message = RegexLowerEndX.Replace(message, "ecks$1");
// eckS
message = Regex.Replace(message, @"\bX([\-|r|R]|\b)", "ECKS$1");
message = RegexUpperEndX.Replace(message, "ECKS$1");
args.Message = message;
}

View File

@@ -1,4 +1,3 @@
using System.Globalization;
using System.Linq;
using System.Text.RegularExpressions;
using Content.Server.Speech.Components;
@@ -8,30 +7,17 @@ namespace Content.Server.Speech.EntitySystems;
public sealed class MobsterAccentSystem : EntitySystem
{
private static readonly Regex RegexIng = new(@"(?<=\w\w)(in)g(?!\w)", RegexOptions.IgnoreCase);
private static readonly Regex RegexLowerOr = new(@"(?<=\w)o[Rr](?=\w)");
private static readonly Regex RegexUpperOr = new(@"(?<=\w)O[Rr](?=\w)");
private static readonly Regex RegexLowerAr = new(@"(?<=\w)a[Rr](?=\w)");
private static readonly Regex RegexUpperAr = new(@"(?<=\w)A[Rr](?=\w)");
private static readonly Regex RegexFirstWord = new(@"^(\S+)");
private static readonly Regex RegexLastWord = new(@"(\S+)$");
[Dependency] private readonly IRobustRandom _random = default!;
[Dependency] private readonly ReplacementAccentSystem _replacement = default!;
private static readonly Dictionary<string, string> DirectReplacements = new()
{
{ "let me", "lemme" },
{ "should", "oughta" },
{ "the", "da" },
{ "them", "dem" },
{ "attack", "whack" },
{ "kill", "whack" },
{ "murder", "whack" },
{ "dead", "sleepin' with da fishies"},
{ "hey", "ey'o" },
{ "hi", "ey'o"},
{ "hello", "ey'o"},
{ "rules", "roolz" },
{ "you", "yous" },
{ "have to", "gotta" },
{ "going to", "boutta" },
{ "about to", "boutta" },
{ "here", "'ere" }
};
public override void Initialize()
{
base.Initialize();
@@ -51,20 +37,20 @@ public sealed class MobsterAccentSystem : EntitySystem
// thinking -> thinkin'
// king -> king
//Uses captures groups to make sure the captialization of IN is kept
msg = Regex.Replace(msg, @"(?<=\w\w)(in)g(?!\w)", "$1'", RegexOptions.IgnoreCase);
msg = RegexIng.Replace(msg, "$1'");
// or -> uh and ar -> ah in the middle of words (fuhget, tahget)
msg = Regex.Replace(msg, @"(?<=\w)o[Rr](?=\w)", "uh");
msg = Regex.Replace(msg, @"(?<=\w)O[Rr](?=\w)", "UH");
msg = Regex.Replace(msg, @"(?<=\w)a[Rr](?=\w)", "ah");
msg = Regex.Replace(msg, @"(?<=\w)A[Rr](?=\w)", "AH");
msg = RegexLowerOr.Replace(msg, "uh");
msg = RegexUpperOr.Replace(msg, "UH");
msg = RegexLowerAr.Replace(msg, "ah");
msg = RegexUpperAr.Replace(msg, "AH");
// Prefix
if (_random.Prob(0.15f))
{
//Checks if the first word of the sentence is all caps
//So the prefix can be allcapped and to not resanitize the captial
var firstWordAllCaps = !Regex.Match(msg, @"^(\S+)").Value.Any(char.IsLower);
var firstWordAllCaps = !RegexFirstWord.Match(msg).Value.Any(char.IsLower);
var pick = _random.Next(1, 2);
// Reverse sanitize capital
@@ -84,7 +70,7 @@ public sealed class MobsterAccentSystem : EntitySystem
{
//Checks if the last word of the sentence is all caps
//So the suffix can be allcapped
var lastWordAllCaps = !Regex.Match(msg, @"(\S+)$").Value.Any(char.IsLower);
var lastWordAllCaps = !RegexLastWord.Match(msg).Value.Any(char.IsLower);
var suffix = "";
if (component.IsBoss)
{
@@ -94,7 +80,7 @@ public sealed class MobsterAccentSystem : EntitySystem
else
{
var pick = _random.Next(1, 3);
suffix = Loc.GetString($"accent-mobster-suffix-minion-{pick}");
suffix = Loc.GetString($"accent-mobster-suffix-minion-{pick}");
}
if (lastWordAllCaps)
suffix = suffix.ToUpper();

View File

@@ -5,6 +5,9 @@ namespace Content.Server.Speech.EntitySystems;
public sealed class MothAccentSystem : EntitySystem
{
private static readonly Regex RegexLowerBuzz = new Regex("z{1,3}");
private static readonly Regex RegexUpperBuzz = new Regex("Z{1,3}");
public override void Initialize()
{
base.Initialize();
@@ -16,10 +19,10 @@ public sealed class MothAccentSystem : EntitySystem
var message = args.Message;
// buzzz
message = Regex.Replace(message, "z{1,3}", "zzz");
message = RegexLowerBuzz.Replace(message, "zzz");
// buZZZ
message = Regex.Replace(message, "Z{1,3}", "ZZZ");
message = RegexUpperBuzz.Replace(message, "ZZZ");
args.Message = message;
}
}

View File

@@ -7,6 +7,8 @@ namespace Content.Server.Speech.EntitySystems;
public sealed partial class ParrotAccentSystem : EntitySystem
{
private static readonly Regex WordCleanupRegex = new Regex("[^A-Za-z0-9 -]");
[Dependency] private readonly IRobustRandom _random = default!;
public override void Initialize()
@@ -27,7 +29,7 @@ public sealed partial class ParrotAccentSystem : EntitySystem
if (_random.Prob(entity.Comp.LongestWordRepeatChance))
{
// Don't count non-alphanumeric characters as parts of words
var cleaned = Regex.Replace(message, "[^A-Za-z0-9 -]", string.Empty);
var cleaned = WordCleanupRegex.Replace(message, string.Empty);
// Split on whitespace and favor words towards the end of the message
var words = cleaned.Split(null).Reverse();
// Find longest word

View File

@@ -7,6 +7,8 @@ namespace Content.Server.Speech.EntitySystems;
public sealed class PirateAccentSystem : EntitySystem
{
private static readonly Regex FirstWordAllCapsRegex = new(@"^(\S+)");
[Dependency] private readonly IRobustRandom _random = default!;
[Dependency] private readonly ReplacementAccentSystem _replacement = default!;
@@ -26,7 +28,7 @@ public sealed class PirateAccentSystem : EntitySystem
return msg;
//Checks if the first word of the sentence is all caps
//So the prefix can be allcapped and to not resanitize the captial
var firstWordAllCaps = !Regex.Match(msg, @"^(\S+)").Value.Any(char.IsLower);
var firstWordAllCaps = !FirstWordAllCapsRegex.Match(msg).Value.Any(char.IsLower);
var pick = _random.Pick(component.PirateWords);
var pirateWord = Loc.GetString(pick);

View File

@@ -7,6 +7,8 @@ namespace Content.Server.Speech.EntitySystems
{
public sealed class ScrambledAccentSystem : EntitySystem
{
private static readonly Regex RegexLoneI = new(@"(?<=\ )i(?=[\ \.\?]|$)");
[Dependency] private readonly IRobustRandom _random = default!;
public override void Initialize()
@@ -34,7 +36,7 @@ namespace Content.Server.Speech.EntitySystems
msg = msg[0].ToString().ToUpper() + msg.Remove(0, 1);
// Capitalize lone i's
msg = Regex.Replace(msg, @"(?<=\ )i(?=[\ \.\?]|$)", "I");
msg = RegexLoneI.Replace(msg, "I");
return msg;
}

View File

@@ -5,8 +5,12 @@ namespace Content.Server.Speech.EntitySystems;
public sealed class SouthernAccentSystem : EntitySystem
{
private static readonly Regex RegexIng = new(@"ing\b");
private static readonly Regex RegexAnd = new(@"\band\b");
private static readonly Regex RegexDve = new("d've");
[Dependency] private readonly ReplacementAccentSystem _replacement = default!;
public override void Initialize()
{
base.Initialize();
@@ -20,9 +24,9 @@ public sealed class SouthernAccentSystem : EntitySystem
message = _replacement.ApplyReplacements(message, "southern");
//They shoulda started runnin' an' hidin' from me!
message = Regex.Replace(message, @"ing\b", "in'");
message = Regex.Replace(message, @"\band\b", "an'");
message = Regex.Replace(message, "d've", "da");
message = RegexIng.Replace(message, "in'");
message = RegexAnd.Replace(message, "an'");
message = RegexDve.Replace(message, "da");
args.Message = message;
}
};

View File

@@ -26,6 +26,9 @@ namespace Content.Shared.Preferences
[Serializable, NetSerializable]
public sealed partial class HumanoidCharacterProfile : ICharacterProfile
{
private static readonly Regex RestrictedNameRegex = new("[^A-Z,a-z,0-9, -]");
private static readonly Regex ICNameCaseRegex = new(@"^(?<word>\w)|\b(?<word>\w)(?=\w*$)");
public const int MaxNameLength = 32;
public const int MaxDescLength = 512;
@@ -418,15 +421,13 @@ namespace Content.Shared.Preferences
if (configManager.GetCVar(CCVars.RestrictedNames))
{
name = Regex.Replace(name, @"[^A-Z,a-z,0-9, -]", string.Empty);
name = RestrictedNameRegex.Replace(name, string.Empty);
}
if (configManager.GetCVar(CCVars.ICNameCase))
{
// This regex replaces the first character of the first and last words of the name with their uppercase version
name = Regex.Replace(name,
@"^(?<word>\w)|\b(?<word>\w)(?=\w*$)",
m => m.Groups["word"].Value.ToUpper());
name = ICNameCaseRegex.Replace(name, m => m.Groups["word"].Value.ToUpper());
}
if (string.IsNullOrEmpty(name))