Skip to content

Commit

Permalink
Works? Need to improve parsing of lang.hpp.
Browse files Browse the repository at this point in the history
  • Loading branch information
MKadaner committed Oct 10, 2023
1 parent 36a4829 commit 57de62b
Show file tree
Hide file tree
Showing 5 changed files with 149 additions and 84 deletions.
2 changes: 1 addition & 1 deletion misc/build-checks/LngChecker/Extensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ public static IEnumerable<string> UnIndent(this IEnumerable<string> lines)
else if (line[0] == '\t')
yield return line[1..];
else
throw new Exception($"Line formatting error: [{line}]");
throw new ProcessingException($"Line formatting error: [{line}]");
}
}

Expand Down
23 changes: 18 additions & 5 deletions misc/build-checks/LngChecker/GlobalConstants.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using System.Collections.Immutable;
using System.Text.RegularExpressions;

namespace LngChecker
Expand All @@ -6,12 +7,24 @@ internal static class GlobalConstants
{
public enum Operation { Validate, WriteBackCorrected };

public const string LangHpp = "lang.hpp";
public const string FarEngLng = "FarEng.lng.m4";
public const string FarLngMask = "Far???.lng.m4";
public static readonly ImmutableList<(string file, string language, string comment)> Languages = new[]
{
( "FarEng.lng.m4", "English", "English" ), // Must be the first
( "FarBel.lng.m4", "Belarusian", "Belarusian (Беларуская)" ),
( "FarCze.lng.m4", "Czech", "Czech (Čeština)" ),
( "FarGer.lng.m4", "German", "German (Deutsch)" ),
( "FarHun.lng.m4", "Hungarian", "Hungarian (Magyar)" ),
( "FarIta.lng.m4", "Italian", "Italian (Italiano)" ),
( "FarLit.lng.m4", "Lithuanian", "Lithuanian (Lietuvių)" ),
( "FarPol.lng.m4", "Polish", "Polish (Polski)" ),
( "FarRus.lng.m4", "Russian", "Russian (Русский)" ),
( "FarSky.lng.m4", "Slovak", "Slovak (Slovenčina)" ),
( "FarSpa.lng.m4", "Spanish", "Spanish (Español)" ),
( "FarUkr.lng.m4", "Ukrainian", "Ukrainian (Українська)" ),
}.ToImmutableList();

public static readonly string FarDir = Path.GetFullPath(Path.Combine(GeneratedConstants.FarManagerRootDir, "far"));
public static readonly string LangHppPath = Path.Combine(FarDir, LangHpp);
public static readonly string FarEngLngPath = Path.Combine(FarDir, FarEngLng);
public static readonly string LangHppPath = Path.Combine(FarDir, "lang.hpp");

public static readonly Regex LangHppLabelPattern = new(@"^(?<label>M[\w\d]+),$", _RegexOptions);

Expand Down
36 changes: 22 additions & 14 deletions misc/build-checks/LngChecker/LangHpp.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,27 +6,35 @@ internal class LangHpp
{
public LangHpp(string path)
{
_path = path;
_labelLines = File.ReadLines(_path)
.SkipWhile(l => l != "{")
.Skip(1)
.TakeWhile(l => l != "};")
.ToImmutableList();
Labels = _labelLines
.UnIndent()
.ToLangHppLabels()
.ToImmutableList();
try
{
_path = path;
_labelLines = File.ReadLines(_path)
.SkipWhile(l => l != "{")
.Skip(1)
.TakeWhile(l => l != "};")
.ToImmutableList();
Labels = _labelLines
.UnIndent()
.ToLangHppLabels()
.ToImmutableList();

Validate();
Validate();
}
catch (ProcessingException e)
{
throw new ProcessingException($@"
C++ header file {Path.GetFileName(_path)} is malformed.
{e.Message}
Cannot continue.");
}
}

private void Validate()
{
if (!_labelLines.SequenceEqual(Generate()))
{
throw new Exception($@"
C++ header file {Path.GetFileName(_path)} is malformed.
Cannot continue.");
throw new ProcessingException("Validation failed");
}
}

Expand Down
119 changes: 86 additions & 33 deletions misc/build-checks/LngChecker/LngChecker.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,55 +8,102 @@ public static int Main(string[] parameters)
{
try
{
var operation = ParseParameters(parameters);
return ParseParameters(parameters)(new LangHpp(GlobalConstants.LangHppPath));
}
catch (ProcessingException e)
{
Console.WriteLine(e.Message);
return 200;
}
}

var langHpp = new LangHpp(GlobalConstants.LangHppPath);
private static int Validate(LangHpp langHpp)
{
var result = 0;
var masterLng = true;

var farEngLng = new LngFile(GlobalConstants.FarEngLngPath);
if (farEngLng.GetNeedTranslationCount() > 0)
foreach (var lngFile in GlobalConstants.Languages.Select(descriptor => new LngFile(descriptor)))
{
var needTranslation = lngFile.GetNeedTranslationCount();
Console.WriteLine($"{lngFile.FileName}: {needTranslation,4} labels need translation.");
if (masterLng)
{
throw new Exception($@"
Some labels in {Path.GetFileName(GlobalConstants.FarEngLngPath)} are marked as need translation.
Cannot continue.");
masterLng = false;
if (needTranslation > 0)
{
Console.WriteLine($" All labels in the master language file {lngFile.FileName} must have values.");
result++;
}
}

if (operation == GlobalConstants.Operation.Validate)
farEngLng.Validate(langHpp.Labels);
else
farEngLng.WriteBackCorrected(langHpp.Labels);
if (lngFile.Validate(langHpp.Labels))
continue;

result++;

foreach (var farLngPath in Directory
.EnumerateFiles(GlobalConstants.FarDir, GlobalConstants.FarLngMask)
.Where(p => !p.Equals(GlobalConstants.FarEngLngPath)))
Action<string, IEnumerable<string>> printLabels = (string header, IEnumerable<string> labels) =>
{
var farLng = new LngFile(farLngPath);
if (operation == GlobalConstants.Operation.Validate)
{
farLng.Validate(langHpp.Labels);
Console.WriteLine($"{Path.GetFileName(farLngPath)}: {farLng.GetNeedTranslationCount(),4} labels need translation.");
}
else
{
farLng.WriteBackCorrected(langHpp.Labels, farEngLng.Labels);
}
}
Console.WriteLine($" {header} labels:");
foreach (var label in labels)
Console.WriteLine($" {label}");
};

var (added, removed) = lngFile.GetDiff(langHpp.Labels.Select(l => l.label));

Console.WriteLine($@"
{lngFile.FileName} does not match master label list.");
printLabels("Added", added);
printLabels("Removed", removed);

Console.WriteLine($" Run the checker with \"{GlobalConstants.Operation.WriteBackCorrected}\" parameter to see the full diff.");
}
catch (Exception e)

return result;
}

private static int WriteBackCorrected(LangHpp langHpp)
{
var masterLng = new LngFile(GlobalConstants.Languages[0]);
if (masterLng.GetNeedTranslationCount() > 0 || !masterLng.Validate(langHpp.Labels))
{
Console.WriteLine(e.Message);
return 1;
Console.WriteLine($"Writing back corrected master language file {masterLng.FileName}.");
masterLng.WriteBackCorrected(langHpp.Labels);
Console.WriteLine($"Master language file {masterLng.FileName} needed corrections. Cannot correct other language files.");
return -1;
}

var result = 0;

foreach (var lngFile in GlobalConstants.Languages.Skip(1).Select(descriptor => new LngFile(descriptor)))
{
if (lngFile.Validate(langHpp.Labels))
continue;

result--;

Console.WriteLine($"Writing back corrected language file {lngFile.FileName}.");
lngFile.WriteBackCorrected(langHpp.Labels, masterLng.Labels);
}

return 0;
return result;
}

private static GlobalConstants.Operation ParseParameters(string[] parameters)
private delegate int Processor(LangHpp langHpp);

private static Processor ParseParameters(string[] parameters)
{
if (parameters.Length == 1 && Enum.TryParse<GlobalConstants.Operation>(parameters[0], true, out var operation))
return operation;
if (parameters.Length == 1)
{
Enum.TryParse<GlobalConstants.Operation>(parameters[0], true, out var operation);
switch (operation)
{
case GlobalConstants.Operation.Validate: return Validate;
case GlobalConstants.Operation.WriteBackCorrected: return WriteBackCorrected;
}
}

var lngCheckerName = Path.GetFileName(Process.GetCurrentProcess().MainModule?.FileName);
throw new ArgumentException($@"
throw new ProcessingException($@"
Invalid parameters.
{lngCheckerName} validates or tries to correct Far language files.
Expand All @@ -67,4 +114,10 @@ Invalid parameters.
");
}
}

internal class ProcessingException : Exception
{
internal ProcessingException(string message) : base(message)
{}
}
}
53 changes: 22 additions & 31 deletions misc/build-checks/LngChecker/LngFile.cs
Original file line number Diff line number Diff line change
@@ -1,72 +1,62 @@
using System.Collections.Immutable;
using System.Text;
using System.Text.RegularExpressions;

namespace LngChecker
{
internal class LngFile
{
public LngFile(string path)
public LngFile((string file, string language, string comment) descriptor)
{
_path = path;
_allLines = File.ReadAllLines(_path).ToImmutableList();
(_language, _comment) = ParseHeader();
FileName = descriptor.file;
_language = descriptor.language;
_comment = descriptor.comment;
_allLines = File.ReadAllLines(FilePath).ToImmutableList();
Labels = _allLines
.Skip(GlobalConstants.LngFileHeaderFormat.Length)
.ToLngLabels()
.ToImmutableDictionary(l => l.label, l => (l.needTranslation, l.value));
}

public void Validate(IEnumerable<(ImmutableList<string> comments, string label)> masterLabels)
public bool Validate(IEnumerable<(ImmutableList<string> comments, string label)> masterLabels)
{
if (!_allLines.SequenceEqual(Generate(masterLabels)))
{
throw new Exception($@"
Language file {Path.GetFileName(_path)} does not match master label list.
Run the checker with ""{GlobalConstants.Operation.WriteBackCorrected}"" parameter to see the diff.");
}
return _allLines.SequenceEqual(GenerateLines(masterLabels));
}

public int GetNeedTranslationCount() => Labels.Count(l => l.Value.needTranslation);

public void WriteBackCorrected(
IEnumerable<(ImmutableList<string> comments, string label)> masterLabels,
ImmutableDictionary<string, (bool needTranslation, string value)>? engLabels = default)
public (IEnumerable<string> added, IEnumerable<string> removed) GetDiff(IEnumerable<string> masterLabels)
{
File.WriteAllLines(_path, Generate(masterLabels, engLabels), Encoding.UTF8);
return (Labels.Keys.Except(masterLabels).OrderBy(l => l), masterLabels.Except(Labels.Keys).OrderBy(l => l));
}

private (string language, string comment) ParseHeader()
public void WriteBackCorrected(
IEnumerable<(ImmutableList<string> comments, string label)> masterLabels,
ImmutableDictionary<string, (bool needTranslation, string value)>? masterValues = default)
{
var match = GlobalConstants.LngFileHeaderLanguagePattern.Match(_allLines[1]);
if (match.Success)
return (match.Groups["language"].Value, match.Groups["comment"].Value);
throw new Exception($@"
Malformed header in the language file {Path.GetFileName(_path)}.
Cannot continue.");
File.WriteAllLines(FilePath, GenerateLines(masterLabels, masterValues), Encoding.UTF8);
}

private IEnumerable<string> Generate(
private IEnumerable<string> GenerateLines(
IEnumerable<(ImmutableList<string> comments, string label)> masterLabels,
ImmutableDictionary<string, (bool needTranslation, string value)>? engLabels = default)
ImmutableDictionary<string, (bool needTranslation, string value)>? masterValues = default)
{
return GlobalConstants.LngFileHeaderFormat
.Select(l => string.Format(l, _language, _comment))
.Concat(masterLabels.SelectMany(l => l.comments.Concat(GetLabelStrings(l.label, engLabels))));
.Concat(masterLabels.SelectMany(l => l.comments.Concat(GetLabelStrings(l.label, masterValues))));
}

private IEnumerable<string> GetLabelStrings(
string label,
ImmutableDictionary<string, (bool needTranslation, string value)>? engLabels)
ImmutableDictionary<string, (bool needTranslation, string value)>? masterValues)
{
if (Labels.TryGetValue(label, out var lngValue))
{
return GetValueStrings(label, lngValue.needTranslation, lngValue.value);
}

if (engLabels?.TryGetValue(label, out var engValue) ?? false)
if (masterValues?.TryGetValue(label, out var masterValue) ?? false)
{
return GetValueStrings(label, true, engValue.value);
return GetValueStrings(label, true, masterValue.value);
}

return GetValueStrings(label, true, $"\"{label}\"");
Expand All @@ -78,11 +68,12 @@ private static IEnumerable<string> GetValueStrings(string label, bool needTransl
yield return $"{label}={value}";
}

public string FileName { get; }
public string FilePath => Path.Combine(GlobalConstants.FarDir, FileName);
public ImmutableDictionary<string, (bool needTranslation, string value)> Labels { get; }

private readonly string _path;
private readonly ImmutableList<string> _allLines;
private readonly string _language;
private readonly string _comment;
private readonly ImmutableList<string> _allLines;
}
}

0 comments on commit 57de62b

Please sign in to comment.