99
1010namespace MuConvert . parser ;
1111
12- public class SimaiParser : SimaiBaseVisitor < object > , IParser
12+ public partial class SimaiParser : SimaiBaseVisitor < object > , IParser
1313{
1414 private readonly Chart chart ;
1515 private readonly List < Alert > alerts = [ ] ;
16+ public bool DontTryFix = false ; // 不准调用Preprocess中的tryFix,而是如遇问题直接报错。
1617
1718 private Rational now = 0 ;
1819 private Rational step = new ( 1 , 4 ) ;
19- private decimal ? absoluteTimeStep = null ; // 此项必须和step本体一起更改
20+ private decimal ? absoluteTimeStep ; // 此项必须和step本体一起更改
2021
2122 private ParserRuleContext ? currContext ; // 供调试报错AddAlert函数使用
2223 private Note ? currNote ; // 用于在部分visitor之间传递额外的参数,如visitDuration、visitSlideBody等,都需要Note对象作为参数传入的情况
2324 private readonly List < string > extraModifiers = [ ] ;
24- private bool absoluteTimeStepWarned = false ; // 用于确保Warning只打印一次
25+ private bool absoluteTimeStepWarned ; // 用于确保Warning只打印一次
2526
2627 public SimaiParser ( bool bigTouch = false , bool isUtage = false , int clockCount = 4 )
2728 {
@@ -45,25 +46,55 @@ public class AntlrErrorListener(SimaiParser parser) : IAntlrErrorListener<IToken
4546 // 这个是给 Parser 用的 (IToken)
4647 public void SyntaxError ( TextWriter output , IRecognizer recognizer , IToken offendingSymbol , int line , int charPositionInLine , string msg , RecognitionException e )
4748 {
48- parser . alerts . Add ( new Alert ( Error , Locale . SimaiGrammarFailed + msg , line : line , relevantNote : e . Context ? . GetText ( ) ) ) ;
49+ // ReSharper disable once ConditionalAccessQualifierIsNonNullableAccordingToAPIContract 这是ANTLR的bug,e可以是空的但却被标记了非空类型
50+ parser . alerts . Add ( new Alert ( Error , Locale . SimaiGrammarFailed + msg , line : line , relevantNote : e ? . Context ? . GetText ( ) ?? offendingSymbol . Text ) ) ;
4951 throw new ConversionException ( parser . alerts , e ) ;
5052 }
5153
5254 // 这个是给 Lexer 用的 (int)
5355 public void SyntaxError ( TextWriter output , IRecognizer recognizer , int offendingSymbol , int line , int charPositionInLine , string msg , RecognitionException e )
5456 {
55- parser . alerts . Add ( new Alert ( Error , Locale . SimaiGrammarFailed + msg , line : line , relevantNote : e . Context ? . GetText ( ) ) ) ;
57+ // ReSharper disable once ConditionalAccessQualifierIsNonNullableAccordingToAPIContract 这是ANTLR的bug,e可以是空的但却被标记了非空类型
58+ parser . alerts . Add ( new Alert ( Error , Locale . SimaiGrammarFailed + msg , line : line , relevantNote : e ? . Context ? . GetText ( ) ) ) ;
5659 throw new ConversionException ( parser . alerts , e ) ;
5760 }
5861 }
5962
60- public string Preprocess ( string text )
63+ private string _replaceAndRecord ( Match m , string repl , List < string > msgArr )
64+ {
65+ var res = m . Result ( repl ) ;
66+ msgArr . Add ( $ "{ m . Value } → { res } ") ;
67+ return res ;
68+ }
69+
70+ private void _warnPrepfixMsg ( string messageTemplate , List < string > msgArr )
71+ {
72+ if ( msgArr . Count == 0 ) return ;
73+ alerts . Add ( new Alert ( Warning , string . Format ( messageTemplate , msgArr . Count , string . Join ( "; " , msgArr ) ) ) ) ;
74+ }
75+
76+ [ GeneratedRegex ( @"(\d)([{(])" ) ]
77+ private static partial Regex PrepFix1 ( ) ; // 补上可能缺失的逗号
78+
79+ [ GeneratedRegex ( @"\[(\d+)-(\d+)\]" ) ]
80+ private static partial Regex PrepFix2 ( ) ; // 错误的Duration语法
81+
82+ public string Preprocess ( string text , bool tryFix = false )
6183 {
6284 // 移除注释
6385 var commentRegex = new Regex ( @"((?<!\[[^\]]*|\{[^\}]*)#|\|\|).*$" , RegexOptions . Multiline ) ;
6486 text = commentRegex . Replace ( text , "" ) ;
65-
66- // TODO 应用更多的纠错修改(从MCM现有代码里面抄)
87+
88+ if ( tryFix )
89+ {
90+ List < string > prepFix1Msgs = [ ] ;
91+ text = PrepFix1 ( ) . Replace ( text , m => _replaceAndRecord ( m , "$1,$2" , prepFix1Msgs ) ) ;
92+ _warnPrepfixMsg ( Locale . PrepFix1 , prepFix1Msgs ) ;
93+
94+ List < string > prepFix2Msgs = [ ] ;
95+ text = PrepFix2 ( ) . Replace ( text , m => _replaceAndRecord ( m , "[$1:$2]" , prepFix2Msgs ) ) ;
96+ _warnPrepfixMsg ( Locale . PrepFix2 , prepFix2Msgs ) ;
97+ }
6798
6899 return text ;
69100 }
@@ -76,19 +107,17 @@ public string Preprocess(string text)
76107 try
77108 {
78109 text = Preprocess ( text ) ;
79- var inputStream = new AntlrInputStream ( text ) ;
80-
81- var errorListener = new AntlrErrorListener ( this ) ;
82- var lexer = new SimaiLexer ( inputStream ) ;
83- lexer . RemoveErrorListeners ( ) ;
84- lexer . AddErrorListener ( errorListener ) ;
85- var tokens = new CommonTokenStream ( lexer ) ;
86-
87- var parser = new P ( tokens ) ; // MuConvert.Antlr.SimaiParser
88- parser . RemoveErrorListeners ( ) ;
89- parser . AddErrorListener ( errorListener ) ;
90- root = parser . chart ( ) ;
91-
110+ try
111+ {
112+ root = RunAntlr ( text ) ; // 普通预处理下,antlr词语法分析一次(仅去除注释)
113+ }
114+ catch ( ConversionException )
115+ {
116+ if ( DontTryFix ) throw ;
117+ alerts . Clear ( ) ;
118+ root = RunAntlr ( Preprocess ( text , true ) ) ; // 尝试应用tryFix之后再来一次
119+ }
120+ // 如果成功,则root就是语法分析树的根节点。从这里开始遍历语法分析树完成工作。
92121 VisitChart ( root ) ;
93122 }
94123 catch ( ConversionException )
@@ -105,6 +134,24 @@ public string Preprocess(string text)
105134 return ( chart , alerts ) ;
106135 }
107136
137+ private P . ChartContext RunAntlr ( string text )
138+ {
139+ var inputStream = new AntlrInputStream ( text ) ;
140+ var errorListener = new AntlrErrorListener ( this ) ;
141+
142+ var lexer = new SimaiLexer ( inputStream ) ;
143+ lexer . RemoveErrorListeners ( ) ;
144+ lexer . AddErrorListener ( errorListener ) ;
145+ var tokens = new CommonTokenStream ( lexer ) ;
146+
147+ var parser = new P ( tokens ) ; // MuConvert.Antlr.SimaiParser
148+ parser . RemoveErrorListeners ( ) ;
149+ parser . AddErrorListener ( errorListener ) ;
150+ var root = parser . chart ( ) ;
151+
152+ return root ;
153+ }
154+
108155 public sealed override object VisitChart ( P . ChartContext context )
109156 {
110157 foreach ( var notations in context . notations ( ) )
0 commit comments