Skip to content

Commit

Permalink
Merge pull request #545 from y-iihoshi/revise-parsers
Browse files Browse the repository at this point in the history
Revise parsers
  • Loading branch information
y-iihoshi authored Jan 1, 2025
2 parents 69c3272 + e007a61 commit 23917d4
Show file tree
Hide file tree
Showing 84 changed files with 506 additions and 198 deletions.
64 changes: 64 additions & 0 deletions ThScoreFileConverter.Core.Tests/Models/IntegerParserTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
using System.Text.RegularExpressions;
using ThScoreFileConverter.Core.Models;

namespace ThScoreFileConverter.Core.Tests.Models;

[TestClass]
public class IntegerParserTests
{
[TestMethod]
public void ParseTestDefault()
{
var parser = new IntegerParser();

var pattern = $@"var (\w+) = ({parser.Pattern});";
var evaluator = new MatchEvaluator(match =>
{
var name = match.Groups[1].Value;
var value = parser.Parse(match.Groups[2]);
return $"{name}^2 == {value * value}";
});

var pairs = new[]
{
("var a = 1;", "a^2 == 1"),
("var b = 23;", "b^2 == 529"),
("var c = 456;", "c^2 == 207936"),
};

foreach (var pair in pairs)
{
var replaced = Regex.Replace(pair.Item1, pattern, evaluator);
Assert.AreEqual(pair.Item2, replaced);
}
}

[TestMethod]
public void ParseTest()
{
var parser = new IntegerParser(@"[2-4]");

var pattern = $@"var (\w+) = ({parser.Pattern});";
var evaluator = new MatchEvaluator(match =>
{
var name = match.Groups[1].Value;
var value = parser.Parse(match.Groups[2]);
return $"{name}^2 == {value * value}";
});

var pairs = new[]
{
("var a = 1;", "var a = 1;"),
("var b = 2;", "b^2 == 4"),
("var c = 3;", "c^2 == 9"),
("var d = 4;", "d^2 == 16"),
("var e = 5;", "var e = 5;"),
};

foreach (var pair in pairs)
{
var replaced = Regex.Replace(pair.Item1, pattern, evaluator);
Assert.AreEqual(pair.Item2, replaced);
}
}
}
42 changes: 42 additions & 0 deletions ThScoreFileConverter.Core.Tests/Models/Th143/SceneParserTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
using System.Text.RegularExpressions;
using ThScoreFileConverter.Core.Models.Th143;

namespace ThScoreFileConverter.Core.Tests.Models.Th143;

[TestClass]
public class SceneParserTests
{
[TestMethod]
public void ParseTest()
{
var parser = new SceneParser();

var pattern = $@"var (\w+) = ({parser.Pattern});";
var evaluator = new MatchEvaluator(match =>
{
var name = match.Groups[1].Value;
var value = parser.Parse(match.Groups[2]);
return $"{name}^2 == {value * value}";
});

var pairs = new[]
{
("var a0 = 0;", "a0^2 == 100"),
("var a1 = 1;", "a1^2 == 1"),
("var a2 = 2;", "a2^2 == 4"),
("var a3 = 3;", "a3^2 == 9"),
("var a4 = 4;", "a4^2 == 16"),
("var a5 = 5;", "a5^2 == 25"),
("var a6 = 6;", "a6^2 == 36"),
("var a7 = 7;", "a7^2 == 49"),
("var a8 = 8;", "a8^2 == 64"),
("var a9 = 9;", "a9^2 == 81"),
};

foreach (var pair in pairs)
{
var replaced = Regex.Replace(pair.Item1, pattern, evaluator);
Assert.AreEqual(pair.Item2, replaced);
}
}
}
38 changes: 38 additions & 0 deletions ThScoreFileConverter.Core/Models/IntegerParser.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
//-----------------------------------------------------------------------
// <copyright file="IntegerParser.cs" company="None">
// Copyright (c) IIHOSHI Yoshinori.
// Licensed under the BSD-2-Clause license. See LICENSE.txt file in the project root for full license information.
// </copyright>
//-----------------------------------------------------------------------

using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Text.RegularExpressions;
using CommunityToolkit.Diagnostics;

namespace ThScoreFileConverter.Core.Models;

/// <summary>
/// Provides a parser for an integer value.
/// </summary>
/// <param name="pattern">The regular expression used for parsing.</param>
public class IntegerParser([StringSyntax(StringSyntaxAttribute.Regex)] string pattern) : IRegexParser<int>
{
/// <summary>
/// Initializes a new instance of the <see cref="IntegerParser"/> class.
/// </summary>
public IntegerParser()
: this(@"\d+")
{
}

/// <inheritdoc/>
public string Pattern { get; } = pattern;

/// <inheritdoc/>
public virtual int Parse(Group group)
{
Guard.IsNotNull(group);
return int.Parse(group.Value, CultureInfo.InvariantCulture);
}
}
35 changes: 35 additions & 0 deletions ThScoreFileConverter.Core/Models/Th143/SceneParser.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
//-----------------------------------------------------------------------
// <copyright file="SceneParser.cs" company="None">
// Copyright (c) IIHOSHI Yoshinori.
// Licensed under the BSD-2-Clause license. See LICENSE.txt file in the project root for full license information.
// </copyright>
//-----------------------------------------------------------------------

using System.Text.RegularExpressions;

namespace ThScoreFileConverter.Core.Models.Th143;

/// <summary>
/// Provides the parser of ISC scenes.
/// </summary>
public sealed class SceneParser : IntegerParser
{
/// <summary>
/// Initializes a new instance of the <see cref="SceneParser"/> class.
/// </summary>
public SceneParser()
: base(@"\d")
{
}

/// <summary>
/// Converts from the group matched with the pattern to a value indicating a scene.
/// </summary>
/// <param name="group">The group matched by <see cref="IntegerParser.Pattern"/>.</param>
/// <returns>The parsed value indicating a scene.</returns>
public override int Parse(Group group)
{
var scene = base.Parse(group);
return scene == 0 ? 10 : scene;
}
}
9 changes: 0 additions & 9 deletions ThScoreFileConverter.Tests/Helpers/IntegerHelperTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -48,15 +48,6 @@ public void ToOneBasedTestExceeded()
_ = Assert.ThrowsException<ArgumentOutOfRangeException>(() => IntegerHelper.ToOneBased(10));
}

[TestMethod]
public void ParseTest()
{
Assert.AreEqual(123, IntegerHelper.Parse("123"));
_ = Assert.ThrowsException<ArgumentNullException>(() => IntegerHelper.Parse(null!));
_ = Assert.ThrowsException<FormatException>(() => IntegerHelper.Parse(string.Empty));
_ = Assert.ThrowsException<FormatException>(() => IntegerHelper.Parse("abc"));
}

[DataTestMethod]
[DataRow(0, 1)]
[DataRow(1, 1)]
Expand Down
11 changes: 0 additions & 11 deletions ThScoreFileConverter/Helpers/IntegerHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
//-----------------------------------------------------------------------

using System;
using System.Globalization;
using CommunityToolkit.Diagnostics;

namespace ThScoreFileConverter.Helpers;
Expand Down Expand Up @@ -40,16 +39,6 @@ public static int ToOneBased(int input)
return (input + 1) % 10;
}

/// <summary>
/// Converts the string representation of a number to its 32-bit signed integer equivalent.
/// </summary>
/// <param name="s">A string containing a number to convert.</param>
/// <returns>A 32-bit signed integer equivalent to the number contained in <paramref name="s"/>.</returns>
public static int Parse(string s)
{
return int.Parse(s, CultureInfo.InvariantCulture);
}

/// <summary>
/// Gets the number of decimal digits.
/// </summary>
Expand Down
5 changes: 3 additions & 2 deletions ThScoreFileConverter/Models/Th06/CardReplacerBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,12 @@ protected CardReplacerBase(
Func<SpellCardInfo<TStage, TLevel>, string> cardLevelToString)
{
var numDigits = IntegerHelper.GetNumDigits(cardTable.Count);
var cardNumberParser = new IntegerParser($@"\d{{{numDigits}}}");

this.pattern = StringHelper.Create($@"{formatPrefix}CARD(\d{{{numDigits}}})([NR])");
this.pattern = StringHelper.Create($"{formatPrefix}CARD({cardNumberParser.Pattern})([NR])");
this.evaluator = new MatchEvaluator(match =>
{
var number = IntegerHelper.Parse(match.Groups[1].Value);
var number = cardNumberParser.Parse(match.Groups[1]);
var type = match.Groups[2].Value.ToUpperInvariant();

if (cardTable.TryGetValue(number, out var cardInfo))
Expand Down
9 changes: 6 additions & 3 deletions ThScoreFileConverter/Models/Th06/CareerReplacer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using ThScoreFileConverter.Core.Models;
using ThScoreFileConverter.Helpers;

namespace ThScoreFileConverter.Models.Th06;
Expand All @@ -19,12 +20,14 @@ namespace ThScoreFileConverter.Models.Th06;
internal sealed class CareerReplacer(IReadOnlyDictionary<int, ICardAttack> cardAttacks, INumberFormatter formatter)
: IStringReplaceable
{
private static readonly string Pattern = StringHelper.Create($@"{Definitions.FormatPrefix}C(\d{{2}})([12])");
private static readonly IntegerParser CardNumberParser = new(@"\d{2}");
private static readonly IntegerParser TypeParser = new(@"[12]");
private static readonly string Pattern = StringHelper.Create($"{Definitions.FormatPrefix}C({CardNumberParser.Pattern})({TypeParser.Pattern})");

private readonly MatchEvaluator evaluator = new(match =>
{
var number = IntegerHelper.Parse(match.Groups[1].Value);
var type = IntegerHelper.Parse(match.Groups[2].Value);
var number = CardNumberParser.Parse(match.Groups[1]);
var type = TypeParser.Parse(match.Groups[2]);

Func<ICardAttack, int> getCount = type switch
{
Expand Down
5 changes: 3 additions & 2 deletions ThScoreFileConverter/Models/Th06/CollectRateReplacer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,9 @@ namespace ThScoreFileConverter.Models.Th06;
// %T06CRG[x][y]
internal sealed class CollectRateReplacer : IStringReplaceable
{
private static readonly IntegerParser TypeParser = new(@"[12]");
private static readonly string Pattern = StringHelper.Create(
$"{Definitions.FormatPrefix}CRG({Parsers.StageWithTotalParser.Pattern})([12])");
$"{Definitions.FormatPrefix}CRG({Parsers.StageWithTotalParser.Pattern})({TypeParser.Pattern})");

private readonly MatchEvaluator evaluator;

Expand All @@ -29,7 +30,7 @@ public CollectRateReplacer(IReadOnlyDictionary<int, ICardAttack> cardAttacks, IN
this.evaluator = new MatchEvaluator(match =>
{
var stage = Parsers.StageWithTotalParser.Parse(match.Groups[1]);
var type = IntegerHelper.Parse(match.Groups[2].Value);
var type = TypeParser.Parse(match.Groups[2]);

#pragma warning disable IDE0072 // Add missing cases to switch expression
Func<ICardAttack, bool> findByStage = stage switch
Expand Down
8 changes: 5 additions & 3 deletions ThScoreFileConverter/Models/Th06/ScoreReplacer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,15 +27,17 @@ internal sealed class ScoreReplacer(
INumberFormatter formatter)
: IStringReplaceable
{
private static readonly IntegerParser RankParser = new(@"\d");
private static readonly IntegerParser TypeParser = new(@"[1-3]");
private static readonly string Pattern = StringHelper.Create(
$@"{Definitions.FormatPrefix}SCR({Parsers.LevelParser.Pattern})({Parsers.CharaParser.Pattern})(\d)([1-3])");
$"{Definitions.FormatPrefix}SCR({Parsers.LevelParser.Pattern})({Parsers.CharaParser.Pattern})({RankParser.Pattern})({TypeParser.Pattern})");

private readonly MatchEvaluator evaluator = new(match =>
{
var level = Parsers.LevelParser.Parse(match.Groups[1]);
var chara = Parsers.CharaParser.Parse(match.Groups[2].Value);
var rank = IntegerHelper.ToZeroBased(IntegerHelper.Parse(match.Groups[3].Value));
var type = IntegerHelper.Parse(match.Groups[4].Value);
var rank = IntegerHelper.ToZeroBased(RankParser.Parse(match.Groups[3]));
var type = TypeParser.Parse(match.Groups[4]);

var key = (chara, level);
var score = (rankings.TryGetValue(key, out var ranking) && (rank < ranking.Count))
Expand Down
9 changes: 6 additions & 3 deletions ThScoreFileConverter/Models/Th07/CareerReplacer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using ThScoreFileConverter.Core.Models;
using ThScoreFileConverter.Helpers;

namespace ThScoreFileConverter.Models.Th07;
Expand All @@ -19,14 +20,16 @@ namespace ThScoreFileConverter.Models.Th07;
internal sealed class CareerReplacer(IReadOnlyDictionary<int, ICardAttack> cardAttacks, INumberFormatter formatter)
: IStringReplaceable
{
private static readonly IntegerParser CardNumberParser = new(@"\d{3}");
private static readonly IntegerParser TypeParser = new(@"[1-3]");
private static readonly string Pattern = StringHelper.Create(
$@"{Definitions.FormatPrefix}C(\d{{3}})({Parsers.CharaWithTotalParser.Pattern})([1-3])");
$"{Definitions.FormatPrefix}C({CardNumberParser.Pattern})({Parsers.CharaWithTotalParser.Pattern})({TypeParser.Pattern})");

private readonly MatchEvaluator evaluator = new(match =>
{
var number = IntegerHelper.Parse(match.Groups[1].Value);
var number = CardNumberParser.Parse(match.Groups[1]);
var chara = Parsers.CharaWithTotalParser.Parse(match.Groups[2].Value);
var type = IntegerHelper.Parse(match.Groups[3].Value);
var type = TypeParser.Parse(match.Groups[3]);

Func<ICardAttack, long> getValue = type switch
{
Expand Down
5 changes: 3 additions & 2 deletions ThScoreFileConverter/Models/Th07/CollectRateReplacer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,9 @@ namespace ThScoreFileConverter.Models.Th07;
// %T07CRG[w][xx][yy][z]
internal sealed class CollectRateReplacer : IStringReplaceable
{
private static readonly Core.Models.IntegerParser TypeParser = new(@"[12]");
private static readonly string Pattern = StringHelper.Create(
$"{Definitions.FormatPrefix}CRG({Parsers.LevelWithTotalParser.Pattern})({Parsers.CharaWithTotalParser.Pattern})({Parsers.StageWithTotalParser.Pattern})([12])");
$"{Definitions.FormatPrefix}CRG({Parsers.LevelWithTotalParser.Pattern})({Parsers.CharaWithTotalParser.Pattern})({Parsers.StageWithTotalParser.Pattern})({TypeParser.Pattern})");

private readonly MatchEvaluator evaluator;

Expand All @@ -31,7 +32,7 @@ public CollectRateReplacer(IReadOnlyDictionary<int, ICardAttack> cardAttacks, IN
var level = Parsers.LevelWithTotalParser.Parse(match.Groups[1]);
var chara = Parsers.CharaWithTotalParser.Parse(match.Groups[2].Value);
var stage = Parsers.StageWithTotalParser.Parse(match.Groups[3]);
var type = IntegerHelper.Parse(match.Groups[4].Value);
var type = TypeParser.Parse(match.Groups[4]);

if (stage is StageWithTotal.Extra or StageWithTotal.Phantasm)
return match.ToString();
Expand Down
5 changes: 3 additions & 2 deletions ThScoreFileConverter/Models/Th07/PracticeReplacer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,16 @@ internal sealed class PracticeReplacer(
INumberFormatter formatter)
: IStringReplaceable
{
private static readonly Core.Models.IntegerParser TypeParser = new(@"[12]");
private static readonly string Pattern = StringHelper.Create(
$"{Definitions.FormatPrefix}PRAC({Parsers.LevelParser.Pattern})({Parsers.CharaParser.Pattern})({Parsers.StageParser.Pattern})([12])");
$"{Definitions.FormatPrefix}PRAC({Parsers.LevelParser.Pattern})({Parsers.CharaParser.Pattern})({Parsers.StageParser.Pattern})({TypeParser.Pattern})");

private readonly MatchEvaluator evaluator = new(match =>
{
var level = Parsers.LevelParser.Parse(match.Groups[1]);
var chara = Parsers.CharaParser.Parse(match.Groups[2].Value);
var stage = Parsers.StageParser.Parse(match.Groups[3]);
var type = IntegerHelper.Parse(match.Groups[4].Value);
var type = TypeParser.Parse(match.Groups[4]);

int GetValue(IPracticeScore score)
{
Expand Down
8 changes: 5 additions & 3 deletions ThScoreFileConverter/Models/Th07/ScoreReplacer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,17 @@ internal sealed class ScoreReplacer(
INumberFormatter formatter)
: IStringReplaceable
{
private static readonly Core.Models.IntegerParser RankParser = new(@"\d");
private static readonly Core.Models.IntegerParser TypeParser = new(@"[1-5]");
private static readonly string Pattern = StringHelper.Create(
$@"{Definitions.FormatPrefix}SCR({Parsers.LevelParser.Pattern})({Parsers.CharaParser.Pattern})(\d)([1-5])");
$"{Definitions.FormatPrefix}SCR({Parsers.LevelParser.Pattern})({Parsers.CharaParser.Pattern})({RankParser.Pattern})({TypeParser.Pattern})");

private readonly MatchEvaluator evaluator = new(match =>
{
var level = Parsers.LevelParser.Parse(match.Groups[1]);
var chara = Parsers.CharaParser.Parse(match.Groups[2].Value);
var rank = IntegerHelper.ToZeroBased(IntegerHelper.Parse(match.Groups[3].Value));
var type = IntegerHelper.Parse(match.Groups[4].Value);
var rank = IntegerHelper.ToZeroBased(RankParser.Parse(match.Groups[3]));
var type = TypeParser.Parse(match.Groups[4]);

var key = (chara, level);
var score = (rankings.TryGetValue(key, out var ranking) && (rank < ranking.Count))
Expand Down
Loading

0 comments on commit 23917d4

Please sign in to comment.