Skip to content

Commit 6dd0bd0

Browse files
committed
[R] 重构双押纠错的实现方式,重构部分函数名,增加一些测试
1 parent d30bdab commit 6dd0bd0

7 files changed

Lines changed: 119 additions & 37 deletions

File tree

i18n/Locale.Designer.cs

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

i18n/Locale.resx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@
8383
<value>bar time {0:W}({1:F2}s)</value>
8484
</data>
8585
<data name="MessageParsing" xml:space="preserve">
86-
<value>parsing {0}</value>
86+
<value>parsing "{0}"</value>
8787
</data>
8888
<data name="Error" xml:space="preserve">
8989
<value>ERROR:</value>

i18n/Locale.zh-hans.resx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@
8383
<value>第{0:W}小节({1:F2}秒)</value>
8484
</data>
8585
<data name="MessageParsing" xml:space="preserve">
86-
<value>解析 {0} 时</value>
86+
<value>解析 "{0}" 时</value>
8787
</data>
8888
<data name="Error" xml:space="preserve">
8989
<value>发生错误:</value>

i18n/Locale.zh-hant.resx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@
8383
<value>第{0:W}小節({1:F2}秒)</value>
8484
</data>
8585
<data name="MessageParsing" xml:space="preserve">
86-
<value>解析 {0} 時</value>
86+
<value>解析 "{0}" 時</value>
8787
</data>
8888
<data name="Error" xml:space="preserve">
8989
<value>發生錯誤:</value>

parser/simai/Simai.g4

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,8 @@ chart: (notations COMMA)* CHART_END? EOF;
4646
notations: (bpmTag | absulouteStepTag | metTag)* noteGroup?;
4747

4848
noteGroup: note eachNote*;
49-
FALSE_EACH: '`';
50-
eachNote: (sep+=('/' | FALSE_EACH))+ note;
49+
FALSE_EACH: '`'+;
50+
eachNote: sep=('/' | FALSE_EACH) note?;
5151

5252
bpmTag: (lp+='(')+ number (rp+=')')+;
5353
absulouteStepTag: (lp+='{')+ '#' number (rp+='}')+;

parser/simai/SimaiParser.cs

Lines changed: 38 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -242,22 +242,28 @@ public sealed override object VisitNotations(P.NotationsContext context)
242242
return true;
243243
}
244244

245-
private void WarnMoreThanOneTokens(IList<IToken> ps)
245+
private void AlertExtraToken(string extraStr)
246246
{
247-
if (ps.Count <= 1) return;
248-
var extraStr = "'" + string.Join("", ps.Skip(1).Select(x => x.Text)) + "'";
247+
if (extraStr.First() != '\'') extraStr = "'" + extraStr + "'";
249248
if (StrictLevel == StrictLevelEnum.Strict)
250249
{ // 严格模式,抛异常
251250
AddAlert(Error, string.Format(Locale.RecoverInlineExtraneousTokenStrict, extraStr));
252251
throw new ConversionException(alerts);
253252
}
254253
else AddAlert(Warning, string.Format(Locale.RecoverInlineExtraneousToken, extraStr));
255254
}
255+
256+
private void AlertIfMoreThanOneTokens(IList<IToken> ps)
257+
{
258+
if (ps.Count <= 1) return;
259+
var extraStr = string.Join("", ps.Skip(1).Select(x => x.Text));
260+
AlertExtraToken(extraStr);
261+
}
256262

257-
private void WarnMoreParentheses(IList<IToken> lp, IList<IToken> rp)
263+
private void AlertIfMoreParentheses(IList<IToken> lp, IList<IToken> rp)
258264
{
259-
WarnMoreThanOneTokens(lp);
260-
WarnMoreThanOneTokens(rp);
265+
AlertIfMoreThanOneTokens(lp);
266+
AlertIfMoreThanOneTokens(rp);
261267
}
262268

263269
public sealed override object VisitAbsulouteStepTag(P.AbsulouteStepTagContext context)
@@ -269,7 +275,7 @@ public sealed override object VisitAbsulouteStepTag(P.AbsulouteStepTagContext co
269275
absoluteTimeStepWarned = true;
270276
}
271277
currContext = context;
272-
WarnMoreParentheses(context._lp, context._rp);
278+
AlertIfMoreParentheses(context._lp, context._rp);
273279
absoluteTimeStep = (decimal)VisitNumber(context.number());
274280
var currentBpm = chart.BpmList.Last().Bpm;
275281
step = (Rational)absoluteTimeStep / (240 / (Rational)currentBpm);
@@ -280,7 +286,7 @@ public sealed override object VisitBpmTag(P.BpmTagContext context)
280286
{
281287
if (SubtreeHasException(context)) return false; // 如果本节点下有异常,则直接整个吞掉,(避免具体的规则遇到不完整子树、爆出更不可预测的错误)
282288
currContext = context;
283-
WarnMoreParentheses(context._lp, context._rp);
289+
AlertIfMoreParentheses(context._lp, context._rp);
284290
var bpm = (decimal)VisitNumber(context.number());
285291
chart.BpmList.Add(new BPM(now, bpm));
286292
if (absoluteTimeStep != null)
@@ -294,7 +300,7 @@ public sealed override object VisitMetTag(P.MetTagContext context)
294300
{ // metTag指的是标记分音的tag,如{4}
295301
if (SubtreeHasException(context)) return false; // 如果本节点下有异常,则直接整个吞掉,(避免具体的规则遇到不完整子树、爆出更不可预测的错误)
296302
currContext = context;
297-
WarnMoreParentheses(context._lp, context._rp);
303+
AlertIfMoreParentheses(context._lp, context._rp);
298304
var quaver = int.Parse(context.@int().GetText());
299305
step = new Rational(1, quaver);
300306
absoluteTimeStep = null;
@@ -313,28 +319,31 @@ public sealed override object VisitNoteGroup(P.NoteGroupContext context)
313319
else if (child is P.EachNoteContext c2)
314320
{
315321
noteC = c2.note();
316-
317-
var separators = c2._sep;
318-
if (separators.Count >= 2 && separators.All(x=>x.Type == L.FALSE_EACH))
322+
if (noteC == null)
319323
{
320-
// 出现连续多个反引号的情况,如"2``3"。
321-
// 这并不是标准的simai语法。但是,MajdataView中对此提供了支持,将每个`实现为128分音。
322-
// 因此,我们也支持这一特性,在遇到大于一个`时,不实现成FalseEachIndex,而是直接给予相同的实现、每个`错后128分音。
323-
var length = separators.Count * new Rational(1, 128);
324-
now = (now + length).CanonicalForm;
325-
extendedFalseEach += length;
326-
falseEachIdx = 0;
327-
if (!extendedFalseEachWarned)
328-
{
329-
AddAlert(Warning, Locale.ExtenedFalseEach, context);
330-
extendedFalseEachWarned = true;
331-
}
324+
AlertExtraToken(c2.sep.Text);
325+
continue;
332326
}
333-
else
327+
if (c2.sep.Type == L.FALSE_EACH)
334328
{
335-
WarnMoreThanOneTokens(separators);
336-
if (separators[0].Type == L.FALSE_EACH) falseEachIdx++;
329+
if (c2.sep.Text.Length >= 2)
330+
{
331+
// 出现连续多个反引号的情况,如"2``3"。
332+
// 这并不是标准的simai语法。但是,MajdataView中对此提供了支持,将每个`实现为128分音。
333+
// 因此,我们也支持这一特性,在遇到大于一个`时,不实现成FalseEachIndex,而是直接给予相同的实现、每个`错后128分音。
334+
var length = c2.sep.Text.Length * new Rational(1, 128);
335+
now = (now + length).CanonicalForm;
336+
extendedFalseEach += length;
337+
falseEachIdx = 0;
338+
if (!extendedFalseEachWarned)
339+
{
340+
AddAlert(Warning, Locale.ExtenedFalseEach, context);
341+
extendedFalseEachWarned = true;
342+
}
343+
}
344+
else falseEachIdx++; // 普通的伪双押
337345
}
346+
// else 是普通双押符号'/'。无需做任何特殊处理,正常解析noteContext就好。
338347
}
339348
else throw Utils.Fail();
340349

@@ -459,7 +468,7 @@ public sealed override object VisitDuration(P.DurationContext? context)
459468
result.InvariantBar = 0;
460469
return result;
461470
}
462-
WarnMoreParentheses(context._lp, context._rp);
471+
AlertIfMoreParentheses(context._lp, context._rp);
463472
if (context.beats() != null) result.InvariantBar = (Rational)VisitBeats(context.beats());
464473
else result.Seconds = (Rational)(decimal)VisitNumber(context.number());
465474
return result;
@@ -505,7 +514,7 @@ public sealed override object VisitSlideDuration(P.SlideDurationContext context)
505514
var result = new Duration(currNote!);
506515
Duration? waitTime = null;
507516
isRealExactWaitTime = false;
508-
WarnMoreParentheses(context._lp, context._rp);
517+
AlertIfMoreParentheses(context._lp, context._rp);
509518

510519
if (context.waitTime() != null)
511520
{
Lines changed: 75 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,11 @@ namespace MuConvert.Tests;
1010
/// 另含带 <c>#</c>、<c>||</c> 行注释的谱面(见 <see cref="Comment_Cases"/> / <see cref="含有注释"/>),与 Preprocess 去注释行为对齐。
1111
/// 在 <see cref="SimaiParser.Preprocess"/> 尚未接入对应替换逻辑前,本文件中的测试失败或抛错属于预期行为(TDD)。
1212
/// </summary>
13-
public class Simai预处理纠错测试
13+
public class Simai纠错测试
1414
{
1515
private readonly ITestOutputHelper _output;
1616

17-
public Simai预处理纠错测试(ITestOutputHelper output) => _output = output;
17+
public Simai纠错测试(ITestOutputHelper output) => _output = output;
1818

1919
/// <summary>规范 simai → 完整 MA2 文本(含头与 BPM)。</summary>
2020
private string SimaiToMa2(string inote, bool DontTryFix = false)
@@ -109,6 +109,12 @@ public static IEnumerable<object[]> TryFix_Cases()
109109
"(120){4}1/2,2/3,4,E",
110110
"(120){4}1//2,2//3,4,E"
111111
];
112+
yield return
113+
[
114+
"1/ 后没有音符",
115+
"(120){4}1,(90)2,3,",
116+
"(120){4}1/,(90)2,3,"
117+
];
112118
}
113119

114120
public static IEnumerable<object[]> Comment_Cases()
@@ -200,4 +206,71 @@ public void 含有注释(string description, string canonicalSimai, string malfo
200206
var actual = SimaiToMa2(malformedSimai, true);
201207
Assert.Equal(expected, actual);
202208
}
209+
210+
/// <summary>
211+
/// 仅覆盖 <see cref="Lax_Scratch_Cases"/>(scratch NewFile1..6);以 Lax 解析 <paramref name="malformedSimai"/>,
212+
/// 结果应与规范串在 Strict 下得到的 MA2 一致。
213+
/// </summary>
214+
[Theory]
215+
[MemberData(nameof(Lax_Scratch_Cases))]
216+
public void Lax模式测试(string description, string canonicalSimai, string malformedSimai)
217+
{
218+
_output.WriteLine(description);
219+
220+
string expected;
221+
try
222+
{
223+
expected = SimaiToMa2(canonicalSimai, true);
224+
}
225+
catch (Exception e)
226+
{
227+
throw new InvalidOperationException($"Canonical simai failed (fix test data): {description}", e);
228+
}
229+
230+
var actual = SimaiToMa2Lax(malformedSimai);
231+
Assert.Equal(expected, actual);
232+
}
233+
234+
/// <summary>
235+
/// 来自 <c>tests/testset/scratch/NewFile1..6.txt</c> 的片段;规范串由 Lax 解析后 <see cref="SimaiGenerator"/> 反写得到。
236+
/// </summary>
237+
public static IEnumerable<object[]> Lax_Scratch_Cases()
238+
{
239+
yield return
240+
[
241+
"无效 &",
242+
"(120){4}1,2,3,",
243+
"(120){4}1,2&,3,"
244+
];
245+
yield return
246+
[
247+
"数字与 '(' BPM 缺逗号",
248+
"(120){4}1,(120)2,3,",
249+
"(120){4}1(120)2,3,"
250+
];
251+
yield return
252+
[
253+
"无法解析的 '-2,'(Lax 丢弃)",
254+
"(120){4},3,4,",
255+
"(120){4}1-2,3,4,"
256+
];
257+
yield return
258+
[
259+
"1/ 后没有音符",
260+
"(120){4}1,(90)2,3,",
261+
"(120){4}1/,(90)2,3,"
262+
];
263+
yield return
264+
[
265+
"多余的 '['",
266+
"(120){4}1,2,3,",
267+
"(120){4}1[,2,3,"
268+
];
269+
yield return
270+
[
271+
"Hold 时长损坏(Lax 丢弃该 Hold)",
272+
"(120){4}1,,3,4-5[6:1],7,8,E",
273+
"(120){4}1,2h[3:,3,4-5[6:1],7,8,E"
274+
];
275+
}
203276
}

0 commit comments

Comments
 (0)