11using System . Text . RegularExpressions ;
22using Antlr4 . Runtime ;
3+ using Antlr4 . Runtime . Misc ;
34using Antlr4 . Runtime . Tree ;
45using MuConvert . Antlr ;
56using MuConvert . chart ;
89using Rationals ;
910using static MuConvert . utils . Alert . LEVEL ;
1011using P = MuConvert . Antlr . SimaiParser ;
12+ using L = MuConvert . Antlr . SimaiLexer ;
13+ using Utils = MuConvert . utils . Utils ;
1114
1215namespace MuConvert . parser ;
1316
@@ -68,6 +71,48 @@ private string Preprocess(string text)
6871 return text ;
6972 }
7073
74+ /**
75+ * 对词法分析得到的token流,在送入parser之前进行一些处理,以尝试修复一些特定类型的错误:
76+ * - 对星星头的修饰符,应该出现在键位号后、星星类型标记之前
77+ */
78+ private CommonTokenStream TokenProcess ( CommonTokenStream src )
79+ {
80+ List < Alert > alertsBuf = [ ] ;
81+
82+ src . Fill ( ) ;
83+ var tokens = src . GetTokens ( ) . Index ( ) . Where ( x=> x . Item . Channel == TokenConstants . DefaultChannel ) . ToList ( ) ;
84+ var r = new TokenStreamRewriter ( src ) ;
85+ bool modified = false ;
86+ for ( int i = 0 ; i < tokens . Count - 1 ; i ++ )
87+ {
88+ var ( idx , token ) = tokens [ i ] ;
89+ if ( token . Type == L . SLIDE_TYPE &&
90+ ( i < tokens . Count - 1 && Utils . IsModifier ( tokens [ i + 1 ] . Item . Type ) ) && // SlideType后面接了modifier
91+ ( i >= 2 && tokens [ i - 1 ] . Item . Type == L . KEY && tokens [ i - 2 ] . Item . Type != L . SLIDE_TYPE ) ) // 判断是否是星星的首个slidetype
92+ { // 类似1-b2[2:1]这种,星星头的修饰符错误地出现在了首个slidetype后面的情况。
93+ // 找到modifier的结束位置
94+ int endPos = i + 1 ;
95+ while ( Utils . IsModifier ( tokens [ endPos + 1 ] . Item . Type ) ) endPos ++ ;
96+ // 将tokens[i]挪到endPos后面去
97+ r . Delete ( idx ) ;
98+ r . InsertAfter ( tokens [ endPos ] . Index , token . Text ) ;
99+ alertsBuf . Add ( new Alert ( Warning , Locale . FixModifiersOnHead + Locale . Fixed , line : token . Line ,
100+ relevantNote : src . GetText ( tokens [ i - 1 ] . Item , tokens [ endPos + 1 ] . Item ) ) ) ;
101+ modified = true ;
102+ }
103+ }
104+
105+ if ( ! modified ) return src ;
106+ // 做过更改,则要重跑lexer
107+ alerts . Clear ( ) ; // 清空上次跑lexer时的报错,避免重复报错
108+ alerts . AddRange ( alertsBuf ) ;
109+ var inputStream = new AntlrInputStream ( r . GetText ( ) ) ;
110+ var lexer = new SimaiLexer ( inputStream ) ;
111+ lexer . RemoveErrorListeners ( ) ;
112+ lexer . AddErrorListener ( new ErrorListener ( this ) ) ;
113+ return new CommonTokenStream ( lexer ) ;
114+ }
115+
71116 public ( Chart , List < Alert > ) Parse ( string text )
72117 {
73118 if ( now != 0 ) throw new Exception ( Locale . InstanceMultipleUsage ) ;
@@ -82,13 +127,14 @@ private string Preprocess(string text)
82127 lexer . RemoveErrorListeners ( ) ;
83128 lexer . AddErrorListener ( new ErrorListener ( this ) ) ;
84129 var tokens = new CommonTokenStream ( lexer ) ;
130+ if ( StrictLevel != StrictLevelEnum . Strict ) tokens = TokenProcess ( tokens ) ;
85131
86132 var parser = new P ( tokens ) { ErrorHandler = ErrorStrategy ( ) } ; // MuConvert.Antlr.SimaiParser
87133 parser . RemoveErrorListeners ( ) ;
88134 parser . AddErrorListener ( new ErrorListener ( this ) ) ;
89135 root = parser . chart ( ) ;
90136 }
91- catch ( Antlr4 . Runtime . Misc . ParseCanceledException e )
137+ catch ( ParseCanceledException e )
92138 { // ErrorListener里会把alerts加好的,因此这里直接抛异常就可以了。
93139 throw new ConversionException ( alerts , e ) ;
94140 }
@@ -191,7 +237,7 @@ public sealed override object VisitNotations(P.NotationsContext context)
191237 return true ;
192238 }
193239
194- private void WarnMoreParentheses ( IList < IToken > ps )
240+ private void WarnMoreThanOneTokens ( IList < IToken > ps )
195241 {
196242 if ( ps . Count <= 1 ) return ;
197243 var extraStr = "'" + string . Join ( "" , ps . Skip ( 1 ) . Select ( x => x . Text ) ) + "'" ;
@@ -205,8 +251,8 @@ private void WarnMoreParentheses(IList<IToken> ps)
205251
206252 private void WarnMoreParentheses ( IList < IToken > lp , IList < IToken > rp )
207253 {
208- WarnMoreParentheses ( lp ) ;
209- WarnMoreParentheses ( rp ) ;
254+ WarnMoreThanOneTokens ( lp ) ;
255+ WarnMoreThanOneTokens ( rp ) ;
210256 }
211257
212258 public sealed override object VisitAbsulouteStepTag ( P . AbsulouteStepTagContext context )
@@ -263,25 +309,26 @@ public sealed override object VisitNoteGroup(P.NoteGroupContext context)
263309 {
264310 noteC = c2 . note ( ) ;
265311
266- var separators = c2 . eachSeparators ( ) . GetText ( ) ! ;
267- if ( separators [ 0 ] == '`' )
312+ var separators = c2 . _sep ;
313+ if ( separators . Count >= 2 && separators . All ( x => x . Type == L . FALSE_EACH ) )
268314 {
269- if ( separators . Length >= 2 )
270- {
271- // 出现连续多个反引号的情况,如"2``3"。
272- // 这并不是标准的simai语法。但是,MajdataView中对此提供了支持,将每个`实现为128分音。
273- // 因此,我们也支持这一特性,在遇到大于一个`时,不实现成FalseEachIndex,而是直接给予相同的实现、每个`错后128分音。
274- var length = separators . Length * new Rational ( 1 , 128 ) ;
275- now = ( now + length ) . CanonicalForm ;
276- extendedFalseEach += length ;
277- falseEachIdx = 0 ;
278- if ( ! extendedFalseEachWarned )
279- {
280- AddAlert ( Warning , Locale . ExtenedFalseEach , context ) ;
281- extendedFalseEachWarned = true ;
282- }
315+ // 出现连续多个反引号的情况,如"2``3"。
316+ // 这并不是标准的simai语法。但是,MajdataView中对此提供了支持,将每个`实现为128分音。
317+ // 因此,我们也支持这一特性,在遇到大于一个`时,不实现成FalseEachIndex,而是直接给予相同的实现、每个`错后128分音。
318+ var length = separators . Count * new Rational ( 1 , 128 ) ;
319+ now = ( now + length ) . CanonicalForm ;
320+ extendedFalseEach += length ;
321+ falseEachIdx = 0 ;
322+ if ( ! extendedFalseEachWarned )
323+ {
324+ AddAlert ( Warning , Locale . ExtenedFalseEach , context ) ;
325+ extendedFalseEachWarned = true ;
283326 }
284- else falseEachIdx ++ ;
327+ }
328+ else
329+ {
330+ WarnMoreThanOneTokens ( separators ) ;
331+ if ( separators [ 0 ] . Type == L . FALSE_EACH ) falseEachIdx ++ ;
285332 }
286333 }
287334 else throw Utils . Fail ( ) ;
@@ -328,7 +375,7 @@ public sealed override object VisitNote(P.NoteContext context)
328375 case P . SharedHeadSlideContext shSlideC :
329376 note = ( Slide ) VisitSharedHeadSlide ( shSlideC ) ;
330377 break ;
331- case ITerminalNode n when n . Symbol . Type == SimaiLexer . KEY :
378+ case ITerminalNode n when n . Symbol . Type == L . KEY :
332379 note = new Tap ( chart , now ) { Key = int . Parse ( n . GetText ( ) ) } ;
333380 break ;
334381 default :
@@ -384,7 +431,7 @@ public sealed override object VisitTap(P.TapContext context)
384431 Key = int . Parse ( context . KEY ( ) . GetText ( ) )
385432 } ;
386433 ApplyModifiers ( [ context . modifiers ( ) ] , result ) ;
387- if ( context . Parent is not P . SlideContext && GetModifier ( SimaiLexer . TAP_TO_STAR ) ) result = new Star ( result ) ; // 发现了”TAP_TO_STAR“的标记,把Tap转换为星星
434+ if ( context . Parent is not P . SlideContext && GetModifier ( L . TAP_TO_STAR ) ) result = new Star ( result ) ; // 发现了”TAP_TO_STAR“的标记,把Tap转换为星星
388435 return result ;
389436 }
390437
@@ -541,12 +588,12 @@ public sealed override object VisitSlide(P.SlideContext context)
541588
542589 // 处理星星头
543590 Tap ? head = ( Tap ) VisitTap ( context . tap ( ) ) ;
544- if ( GetModifier ( SimaiLexer . NO_STAR ) )
591+ if ( GetModifier ( L . NO_STAR ) )
545592 { // 标记了NO_STAR的星星,则不要放head、但是需要手动设置Key
546593 result . Key = head . Key ;
547594 head = null ;
548595 }
549- else if ( ! GetModifier ( SimaiLexer . STAR_TO_TAP ) ) head = new Star ( head ) ; // 除非标记了STAR_TO_TAP,否则把tap转为star
596+ else if ( ! GetModifier ( L . STAR_TO_TAP ) ) head = new Star ( head ) ; // 除非标记了STAR_TO_TAP,否则把tap转为star
550597 result . OwnHead = head ;
551598
552599 currNote = result ;
0 commit comments