First review

This commit is contained in:
Peer Hogeterp committed 2026-09-24 18:06:25 +02:00
1 parent 0e63f93696
commit ad6b700978
10 files changed
+108 -99

No files matched your search

@@ -13,13 +13,15 @@ public sealed class ConfigurationSnippetParser
private ConfigurationSnippetParser(string source) => this.source = source;
private static string TB(string fallbackEN) => I18N.I.T(fallbackEN, typeof(ConfigurationSnippetParser).Namespace, nameof(ConfigurationSnippetParser));
public static bool TryParse(string source, string expectedSection, out LuaTable table, out string issue)
{
table = new LuaTable();
issue = string.Empty;
if (string.IsNullOrWhiteSpace(source) || source.Length > 1_000_000)
{
issue = "Paste one exported configuration snippet (up to 1 MB).";
issue = TB("Paste one exported configuration snippet (up to 1 MB).");
return false;
}
@@ -29,13 +31,13 @@ public sealed class ConfigurationSnippetParser
parser.ExpectWord("CONFIG");
var section = parser.ReadBracketedString();
if (section != expectedSection)
throw new FormatException($"This is a {section} snippet. Paste a {expectedSection} snippet here.");
throw new FormatException(string.Format(TB("This is a {0} snippet. Paste a {1} snippet here."), section, expectedSection));
parser.Expect('[');
parser.Expect('#');
parser.ExpectWord("CONFIG");
if (parser.ReadBracketedString() != expectedSection)
throw new FormatException("The configuration section names do not match.");
throw new FormatException(TB("The configuration section names do not match."));
parser.Expect('+');
parser.Expect('1');
parser.Expect(']');
@@ -43,7 +45,7 @@ public sealed class ConfigurationSnippetParser
table = parser.ReadTable(0);
parser.SkipTrivia();
if (parser.position != source.Length)
throw new FormatException("The snippet must contain exactly one table assignment and no executable code.");
throw new FormatException(TB("The snippet must contain exactly one table assignment and no executable code."));
return true;
}
catch (FormatException exception)
@@ -56,7 +58,7 @@ public sealed class ConfigurationSnippetParser
private LuaTable ReadTable(int depth)
{
if (depth > 32)
throw new FormatException("The snippet contains too many nested tables.");
throw new FormatException(TB("The snippet contains too many nested tables."));
this.Expect('{');
var table = new LuaTable();
var arrayIndex = 1;
@@ -67,13 +69,15 @@ public sealed class ConfigurationSnippetParser
if (this.Take('}'))
return table;
if (this.Take('['))
// "[[" or "[=" opens a long string, which is an array value rather than a bracketed key:
if (this.Peek() == '[' && !this.IsLongStringStart())
{
this.position++;
var key = this.ReadString();
this.Expect(']');
this.Expect('=');
if (!names.Add(key))
throw new FormatException($"The field '{key}' occurs more than once.");
throw new FormatException(string.Format(TB("The field '{0}' occurs more than once."), key));
table[key] = this.ReadValue(depth + 1);
}
else
@@ -83,7 +87,7 @@ public sealed class ConfigurationSnippetParser
if (this.Take('}'))
return table;
if (!this.Take(',') && !this.Take(';'))
throw new FormatException($"Expected a comma or closing brace at character {this.position + 1}.");
throw new FormatException(string.Format(TB("Expected a comma or closing brace at character {0}."), this.position + 1));
}
}
@@ -114,9 +118,11 @@ public sealed class ConfigurationSnippetParser
}
if (this.position > start && double.TryParse(this.source[start..this.position], NumberStyles.Float, CultureInfo.InvariantCulture, out var number) && double.IsFinite(number))
return number;
throw new FormatException($"Only literal values are allowed at character {start + 1}; executable Lua is not accepted.");
throw new FormatException(string.Format(TB("Only literal values are allowed at character {0}; executable Lua is not accepted."), start + 1));
}
private bool IsLongStringStart() => this.position + 1 < this.source.Length && this.source[this.position + 1] is '[' or '=';
private string ReadBracketedString()
{
this.Expect('[');
@@ -136,7 +142,9 @@ public sealed class ConfigurationSnippetParser
while (this.Peek() == '=')
this.position++;
var equals = this.source[equalsStart..this.position];
this.Expect('[');
if (this.Peek() != '[')
throw new FormatException(string.Format(TB("Expected '{0}' at character {1}."), '[', this.position + 1));
this.position++;
if (this.Peek() is '\r' or '\n')
{
if (this.Take('\r'))
@@ -146,13 +154,13 @@ public sealed class ConfigurationSnippetParser
}
var end = this.source.IndexOf("]" + equals + "]", this.position, StringComparison.Ordinal);
if (end < 0)
throw new FormatException("Unterminated long string.");
throw new FormatException(TB("Unterminated long string."));
var value = this.source[this.position..end];
this.position = end + equals.Length + 2;
return value;
}
if (quote is not ('"' or '\''))
throw new FormatException($"Expected a quoted string at character {this.position + 1}.");
throw new FormatException(string.Format(TB("Expected a quoted string at character {0}."), this.position + 1));
this.position++;
var builder = new StringBuilder();
while (this.position < this.source.Length)
@@ -161,7 +169,7 @@ public sealed class ConfigurationSnippetParser
if (c == quote)
return builder.ToString();
if (c is '\r' or '\n')
throw new FormatException("A quoted string contains an unescaped newline.");
throw new FormatException(TB("A quoted string contains an unescaped newline."));
if (c != '\\')
{
builder.Append(c);
@@ -174,10 +182,10 @@ public sealed class ConfigurationSnippetParser
{
'n' => '\n', 'r' => '\r', 't' => '\t', 'a' => '\a', 'b' => '\b', 'f' => '\f', 'v' => '\v',
'\\' => '\\', '"' => '"', '\'' => '\'',
_ => throw new FormatException($"Unsupported string escape \\{c}."),
_ => throw new FormatException(string.Format(TB("Unsupported string escape sequence: {0}"), "\\" + c)),
});
}
throw new FormatException("Unterminated quoted string.");
throw new FormatException(TB("Unterminated quoted string."));
}
private void SkipTrivia()
@@ -214,7 +222,7 @@ public sealed class ConfigurationSnippetParser
private void Expect(char c)
{
if (!this.Take(c))
throw new FormatException($"Expected '{c}' at character {this.position + 1}.");
throw new FormatException(string.Format(TB("Expected '{0}' at character {1}."), c, this.position + 1));
}
private bool TakeWord(string word)
@@ -230,6 +238,6 @@ public sealed class ConfigurationSnippetParser
private void ExpectWord(string word)
{
if (!this.TakeWord(word))
throw new FormatException($"Expected '{word}' at character {this.position + 1}.");
throw new FormatException(string.Format(TB("Expected '{0}' at character {1}."), word, this.position + 1));
}
}