Skip to content

Commit d30bdab

Browse files
committed
[O] Simai Lax模式测试,CLI可以指定StrictLevel
1 parent a06bdff commit d30bdab

9 files changed

Lines changed: 62 additions & 14 deletions

File tree

Program.cs

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,20 @@ private static Command BuildRootCommand()
6262
HelpName = "path"
6363
};
6464

65+
var strictOption = new Option<bool>("--strict")
66+
{
67+
Description = "Simai转MA2时,解析使用严格模式。不可与 --lax 同时使用。",
68+
Arity = ArgumentArity.ZeroOrOne,
69+
DefaultValueFactory = _ => false
70+
};
71+
72+
var laxOption = new Option<bool>("--lax")
73+
{
74+
Description = "Simai转MA2时,解析使用宽松模式。不可与 --strict 同时使用。",
75+
Arity = ArgumentArity.ZeroOrOne,
76+
DefaultValueFactory = _ => false
77+
};
78+
6579
var inputArgument = new Argument<string>("path")
6680
{
6781
Description = "可以输入以下几种情况:\n" +
@@ -74,6 +88,8 @@ private static Command BuildRootCommand()
7488

7589
root.Options.Add(levelsOption);
7690
root.Options.Add(outputOption);
91+
root.Options.Add(strictOption);
92+
root.Options.Add(laxOption);
7793
root.Arguments.Add(inputArgument);
7894

7995
root.SetAction(parseResult =>
@@ -82,6 +98,13 @@ private static Command BuildRootCommand()
8298
?? throw new InvalidOperationException("缺少参数 path。");
8399
var levelsRaw = parseResult.GetValue(levelsOption);
84100
_outputSpec = OutputSpec.Parse(parseResult.GetValue(outputOption));
101+
102+
var cliStrict = parseResult.GetValue(strictOption);
103+
var cliLax = parseResult.GetValue(laxOption);
104+
if (cliStrict && cliLax) throw new ArgumentException("不能同时指定 --strict 与 --lax。");
105+
else if (cliStrict) _simaiStrictLevel = SimaiParser.StrictLevelEnum.Strict;
106+
else if (cliLax) _simaiStrictLevel = SimaiParser.StrictLevelEnum.Lax;
107+
85108
RunConvert(inputPath, levelsRaw);
86109
});
87110

@@ -90,6 +113,7 @@ private static Command BuildRootCommand()
90113

91114
/// <summary>由 CLI 在每次 <c>SetAction</c> 入口赋值;转换逻辑只读此字段。</summary>
92115
private static OutputSpec _outputSpec;
116+
private static SimaiParser.StrictLevelEnum _simaiStrictLevel = SimaiParser.StrictLevelEnum.Normal;
93117

94118
private enum OutputSinkKind { Default, Stdout, Directory, File }
95119

@@ -256,6 +280,8 @@ private static void ConvertMa2PathsToMaidata(string outputDir, string title, IRe
256280
{
257281
if (ma2FullPaths.Count == 0)
258282
throw new ArgumentException("未提供任何 .ma2 文件。");
283+
if (_simaiStrictLevel != SimaiParser.StrictLevelEnum.Normal)
284+
throw new ArgumentException("--strict / --lax 仅适用于 Simai(.txt / maidata)转 MA2,不能用于 MA2 转 Simai。");
259285

260286
var paths = ma2FullPaths.Select(Path.GetFullPath).Distinct(StringComparer.OrdinalIgnoreCase).ToArray();
261287
var levelFilter = string.IsNullOrWhiteSpace(levelsRaw) ? null : ParseLevelList(levelsRaw);
@@ -354,7 +380,7 @@ private static void ConvertMaidata(Maidata maidata, IReadOnlyList<int> selected,
354380
var chartInfo = maidata.Levels[id];
355381
var bigTouch = id is 2 or 3;
356382
var isUtage = IsUtageFromLevelString(chartInfo.Level);
357-
var ma2 = SimaiToMa2(chartInfo.Inote, maidata.ClockCount, bigTouch, isUtage);
383+
var ma2 = SimaiToMa2(chartInfo.Inote, maidata.ClockCount, bigTouch, isUtage, _simaiStrictLevel);
358384
if (_outputSpec.Kind == OutputSinkKind.Stdout) Console.Out.Write(ma2);
359385
else File.WriteAllText(outPath, ma2, new UTF8Encoding(encoderShouldEmitUTF8Identifier: false));
360386
}
@@ -367,7 +393,7 @@ private static void ConvertPlainSimai(string text, string inputDir, string input
367393
var outPath = _outputSpec.Kind == OutputSinkKind.File ? _outputSpec.FsPath! : Path.Combine(baseDir, $"lv_{outputLevel}.ma2");
368394
var destNote = _outputSpec.Kind == OutputSinkKind.Stdout ? "(标准输出)" : outPath;
369395
Console.Error.WriteLine($"Simai → MA2: {inputPath}(lv{outputLevel}) → {destNote}");
370-
var ma2 = SimaiToMa2(text);
396+
var ma2 = SimaiToMa2(text, strictLevel: _simaiStrictLevel);
371397
if (_outputSpec.Kind == OutputSinkKind.Stdout) Console.Out.Write(ma2);
372398
else File.WriteAllText(outPath, ma2, new UTF8Encoding(encoderShouldEmitUTF8Identifier: false));
373399
}
@@ -395,9 +421,10 @@ private static void ValidateOutputFileExtension(string filePath, string required
395421
throw new ArgumentException($"输出文件扩展名须为「{requiredExt}」,当前为「{(string.IsNullOrEmpty(ext) ? "(无)" : ext)}」。");
396422
}
397423

398-
private static string SimaiToMa2(string inote, int clockCount=4, bool bigTouch=false, bool isUtage=false)
424+
private static string SimaiToMa2(string inote, int clockCount = 4, bool bigTouch = false, bool isUtage = false,
425+
SimaiParser.StrictLevelEnum strictLevel = SimaiParser.StrictLevelEnum.Normal)
399426
{
400-
var (chart, parseAlerts) = new SimaiParser(bigTouch, clockCount).Parse(inote);
427+
var (chart, parseAlerts) = new SimaiParser(bigTouch, clockCount, strictLevel).Parse(inote);
401428
PrintAlerts(parseAlerts);
402429
var (ma2, genAlerts) = new MA2Generator(isUtage).Generate(chart);
403430
PrintAlerts(genAlerts);

README.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,14 +25,18 @@ MuConvert 是一个支持**Simai与MA2互转**的转谱器。
2525
#### 基本用法
2626

2727
```shell
28-
MuConvert.exe <path> [-l|--levels N[,N...]] [-o|--output <输出路径或->]
28+
MuConvert.exe <path> [-l|--levels N[,N...]] [-o|--output <输出路径或->] [--strict|--lax]
2929
```
3030
3131
- **`path`**:输入路径(必填),可以是 `.txt` / `.ma2` / 目录(见下文)
3232
- **`-l, --levels`**:仅转换指定难度(以 `maidata.txt``&inote_编号` 为准),多个难度用英文逗号分隔;省略则转换全部难度
3333
- **`-o, --output`**:指定输出位置(可选);不传入此参数时,文件将保存到“输入文件所在的目录”。
3434
- 会智能识别你传入的是目录还是文件,做智能的处理,将转谱结果输入到目录下或保存为文件。
3535
- 此外,还可以传入 `-` ,表示输出到stdout。
36+
- **`--strict` / `--lax`**:控制 Simai 解析器 `SimaiParser` 的严格程度(`StrictLevel`),**仅在 Simai → MA2**时有效。`--strict` 对应严格模式,`--lax` 对应宽松模式;二者**不可同时**指定;均省略时为普通模式(`Normal`)。
37+
- `--strict`(严格模式):几乎不会自动修复任何错误,遇到解析错误/非标准语法直接报错退出。
38+
- 默认模式:会尽力尝试修复一些局部性的错误,并给出警告。但遇到无法修复的错误时则会报错退出。
39+
- `--lax`(宽松模式):同上会尽力修复错误,而对于无法修复的错误,会把错误所在的音符**直接吞掉**(并给出警告),以换取解析可以继续向下进行。一般来说除非遇到很严重的问题,不会报错退出。
3640
3741
#### `path` 支持的输入形式与输出规则
3842
通过命令行传入的参数,既可以是文件,也可以是目录。
@@ -113,6 +117,7 @@ return maidataText; // maidataText即为转谱结果
113117
- SimaiParser带有以下选项:
114118
- bool `bigTouch` (默认为false): 是否将谱面中的Touch和TouchHold生成为大尺寸。
115119
- int `clockCount` (默认为4): 控制在谱面开头的“哒哒哒哒”的那几声,有几下。
120+
- StrictLevelEnum `strictLevel` (默认为 `Normal`): 解析 Simai 时的严格程度(`Strict` / `Normal` / `Lax`),影响语法容错与报错策略。各个严格程度策略的具体含义,请参见上方[CLI文档](#基本用法)中的相关描述。
116121
- MA2Generator带有以下选项:
117122
- bool `isUtage` (默认为false): 仅影响生成的MA2的文件头区域的`FES_MODE`的值是1还是0,一般来说是不重要的。
118123

i18n/Locale.Designer.cs

Lines changed: 2 additions & 2 deletions
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
@@ -133,7 +133,7 @@
133133
<data name="UnsuppoertedMA2MET_DEF" xml:space="preserve">
134134
<value>Charts whose MET_DEF is not "4 4" are not supported yet!</value>
135135
</data>
136-
<data name="NoNotesInMA2" xml:space="preserve">
136+
<data name="NoNotesInChart" xml:space="preserve">
137137
<value>The chart has no notes!</value>
138138
</data>
139139
<data name="MA2NoteSentenceTooManyParam" xml:space="preserve">

i18n/Locale.zh-hans.resx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@
133133
<data name="UnsuppoertedMA2MET_DEF" xml:space="preserve">
134134
<value>暂不支持MET_DEF不为"4 4"的谱面!</value>
135135
</data>
136-
<data name="NoNotesInMA2" xml:space="preserve">
136+
<data name="NoNotesInChart" xml:space="preserve">
137137
<value>谱面中没有音符!</value>
138138
</data>
139139
<data name="MA2NoteSentenceTooManyParam" xml:space="preserve">

i18n/Locale.zh-hant.resx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@
133133
<data name="UnsuppoertedMA2MET_DEF" xml:space="preserve">
134134
<value>暫不支援 MET_DEF 不為 "4 4" 的譜面!</value>
135135
</data>
136-
<data name="NoNotesInMA2" xml:space="preserve">
136+
<data name="NoNotesInChart" xml:space="preserve">
137137
<value>譜面中沒有音符!</value>
138138
</data>
139139
<data name="MA2NoteSentenceTooManyParam" xml:space="preserve">

parser/MA2Parser.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -193,14 +193,14 @@ private void WarnParamsCount(int lineNo, ReadOnlySpan<char> line, Rational? time
193193
}
194194
}
195195

196-
if (!bpmRead)
196+
if (chart.Notes.Count == 0)
197197
{
198-
alerts.Add(new Alert(Error, Locale.NoBPMInMA2));
198+
alerts.Add(new Alert(Error, Locale.NoNotesInChart));
199199
throw new ConversionException(alerts);
200200
}
201-
if (chart.Notes.Count == 0)
201+
if (!bpmRead)
202202
{
203-
alerts.Add(new Alert(Error, Locale.NoNotesInMA2));
203+
alerts.Add(new Alert(Error, Locale.NoBPMInMA2));
204204
throw new ConversionException(alerts);
205205
}
206206

parser/simai/SimaiParser.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,11 @@ private CommonTokenStream TokenProcess(CommonTokenStream src)
133133
parser.RemoveErrorListeners();
134134
parser.AddErrorListener(new ErrorListener(this));
135135
root = parser.chart();
136+
if (root.children.Count == 1)
137+
{ // 只有一个EOF
138+
alerts.Add(new Alert(Error, Locale.NoNotesInChart));
139+
throw new ConversionException(alerts);
140+
}
136141
}
137142
catch (ParseCanceledException e)
138143
{ // ErrorListener里会把alerts加好的,因此这里直接抛异常就可以了。

tests/Simai预处理纠错测试.cs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,17 @@ private string SimaiToMa2(string inote, bool DontTryFix = false)
2727
return ma2;
2828
}
2929

30+
/// <summary>非规范 simai → 完整 MA2(<see cref="SimaiParser.StrictLevelEnum.Lax"/>)。</summary>
31+
private string SimaiToMa2Lax(string inote)
32+
{
33+
var parser = new SimaiParser(strictLevel: SimaiParser.StrictLevelEnum.Lax);
34+
var (chart, alerts) = parser.Parse(inote);
35+
var (ma2, alerts2) = new MA2Generator().Generate(chart);
36+
_output.WriteLine(string.Join('\n', alerts));
37+
_output.WriteLine(string.Join('\n', alerts2));
38+
return ma2;
39+
}
40+
3041
public static IEnumerable<object[]> TryFix_Cases()
3142
{
3243
// SimaiError1: 数字与小节拍号 {n} 之间漏写逗号(时间轴被粘在一起)

0 commit comments

Comments
 (0)