Skip to content

Commit 38fe7d8

Browse files
committed
Find longest non-command
1 parent 568df7e commit 38fe7d8

File tree

1 file changed

+19
-10
lines changed

1 file changed

+19
-10
lines changed

CSharpMath/Structures/Dictionary.cs

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -40,11 +40,20 @@ public void Add<TCollection>(TCollection keys, Func<TKey, TValue> valueFunc) whe
4040
foreach (var key in keys) Add(key, valueFunc(key));
4141
}
4242
}
43+
class DescendingStringLengthIComparer<TValue> : IComparer<Tuple<string, TValue>> {
44+
int IComparer<Tuple<string, TValue>>.Compare(Tuple<string, TValue> x, Tuple<string, TValue> y) {
45+
if (x.Item1.Length > y.Item1.Length) { return -1; }
46+
else if (x.Item1.Length < y.Item1.Length) { return 1; }
47+
else { return string.CompareOrdinal(x.Item1, y.Item1); }
48+
}
49+
}
50+
4351
/// <summary>
4452
/// A dictionary-based helper where the keys are classes of LaTeX <see cref="string"/>s, with special treatment
4553
/// for commands (starting "\"). The start of an inputted <see cref="Span{Char}"/> is parsed, and an arbitrary object
46-
/// <typeparamref name="TValue"/> is returned, along with the number of matching characters. Processing is based on dictionary lookup
47-
/// with fallack to specified default functions for command and non-commands when lookup fails.
54+
/// <typeparamref name="TValue"/> is returned, along with the number of matching characters. Processing is based on
55+
/// dictionary lookup with fallack to specified default functions for command and non-commands when lookup fails.
56+
/// For non-commands, dictionary lookup finds the longest matching non-command.
4857
/// </summary>
4958
[SuppressMessage("Naming", "CA1710:Identifiers should have correct suffix",
5059
Justification = "This is conceptually a dictionary but has different lookup behavior")]
@@ -60,17 +69,18 @@ public LaTeXCommandDictionary(DefaultDelegate defaultParser,
6069
if (SplitCommand(key.AsSpan()) != key.Length - 1)
6170
commands.Add(key, value);
6271
else throw new ArgumentException("Key is unreachable: " + key, nameof(key));
63-
else nonCommands.Add(key, value);
72+
else nonCommands.Add(new Tuple<string, TValue>(key, value));
6473
};
6574
}
6675
readonly DefaultDelegate defaultParser;
6776
readonly DefaultDelegate defaultParserForCommands;
6877

69-
readonly Dictionary<string, TValue> nonCommands = new Dictionary<string, TValue>();
78+
readonly SortedSet<Tuple<string, TValue>> nonCommands =
79+
new SortedSet<Tuple<string, TValue>>(new DescendingStringLengthIComparer<TValue>());
7080
readonly Dictionary<string, TValue> commands = new Dictionary<string, TValue>();
7181

7282
public IEnumerator<KeyValuePair<string, TValue>> GetEnumerator() =>
73-
nonCommands.Select(kvp => new KeyValuePair<string, TValue>(kvp.Key, kvp.Value))
83+
nonCommands.Select(t => new KeyValuePair<string, TValue>(t.Item1, t.Item2))
7484
.Concat(commands.Select(kvp => new KeyValuePair<string, TValue>(kvp.Key, kvp.Value)))
7585
.GetEnumerator();
7686
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
@@ -111,12 +121,11 @@ static int SplitCommand(ReadOnlySpan<char> chars) {
111121
return TryLookupNonCommand(chars);
112122
}
113123
Result<(TValue Result, int SplitIndex)> TryLookupNonCommand(ReadOnlySpan<char> chars) {
114-
string? commandFound = null; // TODO:short-circuit when found
115-
foreach (string command in nonCommands.Keys) {
116-
if (chars.StartsWith(command.AsSpan(), StringComparison.Ordinal)) {
117-
commandFound = command; }
124+
foreach (Tuple<string,TValue> t in nonCommands) {
125+
if (chars.StartsWith(t.Item1.AsSpan(), StringComparison.Ordinal)) {
126+
return Result.Ok((t.Item2, t.Item1.Length)); }
118127
}
119-
return commandFound == null ? defaultParser(chars) : Result.Ok((nonCommands[commandFound],commandFound.Length));
128+
return defaultParser(chars);
120129
}
121130
}
122131

0 commit comments

Comments
 (0)