33using Antlr4 . Runtime . Tree ;
44using MuConvert . Antlr ;
55using MuConvert . chart ;
6+ using MuConvert . parser . simai ;
67using MuConvert . utils ;
78using Rationals ;
89using static MuConvert . utils . Alert . LEVEL ;
@@ -77,12 +78,14 @@ private string Preprocess(string text)
7778 try
7879 { // 词语法分析
7980 var inputStream = new AntlrInputStream ( text ) ;
80- var lexer = new SimaiLexer ( inputStream ) { ErrorListeners = { new ErrorListener ( this ) } } ;
81+ var lexer = new SimaiLexer ( inputStream ) ;
82+ lexer . RemoveErrorListeners ( ) ;
83+ lexer . AddErrorListener ( new ErrorListener ( this ) ) ;
8184 var tokens = new CommonTokenStream ( lexer ) ;
82- var parser = new P ( tokens ) // MuConvert.Antlr.SimaiParser
83- {
84- ErrorHandler = ErrorStrategy ( ) , ErrorListeners = { new ErrorListener ( this ) }
85- } ;
85+
86+ var parser = new P ( tokens ) { ErrorHandler = ErrorStrategy ( ) } ; // MuConvert.Antlr.SimaiParser
87+ parser . RemoveErrorListeners ( ) ;
88+ parser . AddErrorListener ( new ErrorListener ( this ) ) ;
8689 root = parser . chart ( ) ;
8790 }
8891 catch ( Antlr4 . Runtime . Misc . ParseCanceledException e )
@@ -116,10 +119,10 @@ private IAntlrErrorStrategy ErrorStrategy()
116119 case StrictLevelEnum . Strict :
117120 return new BailErrorStrategy ( ) ;
118121 case StrictLevelEnum . Lax :
119- return new LaxErrorStrategy ( ) ;
122+ return new LaxErrorStrategy ( this ) ;
120123 case StrictLevelEnum . Normal :
121124 default :
122- return new ModerateErrorStrategy ( ) ;
125+ return new ModerateErrorStrategy ( this ) ;
123126 }
124127 }
125128
@@ -149,6 +152,16 @@ private void AddDefaultBpm()
149152 chart . BpmList . Add ( new BPM ( now , defaultStartBpm ) ) ;
150153 }
151154
155+ private static bool SubtreeHasException ( ParserRuleContext root )
156+ {
157+ if ( root . exception != null ) return true ;
158+ foreach ( var child in root . children ?? Array . Empty < IParseTree > ( ) )
159+ {
160+ if ( child is ParserRuleContext pr && SubtreeHasException ( pr ) ) return true ;
161+ }
162+ return false ;
163+ }
164+
152165 public sealed override object VisitNotations ( P . NotationsContext context )
153166 { // 形如 (120){4}1/1 算作一组notations
154167 foreach ( var child in context . children ?? [ ] )
@@ -178,14 +191,34 @@ public sealed override object VisitNotations(P.NotationsContext context)
178191 return true ;
179192 }
180193
194+ private void WarnMoreParentheses ( IList < IToken > ps )
195+ {
196+ if ( ps . Count <= 1 ) return ;
197+ var extraStr = "'" + string . Join ( "" , ps . Skip ( 1 ) . Select ( x => x . Text ) ) + "'" ;
198+ if ( StrictLevel == StrictLevelEnum . Strict )
199+ { // 严格模式,抛异常
200+ AddAlert ( Error , string . Format ( Locale . RecoverInlineExtraneousTokenStrict , extraStr ) ) ;
201+ throw new ConversionException ( alerts ) ;
202+ }
203+ else AddAlert ( Warning , string . Format ( Locale . RecoverInlineExtraneousToken , extraStr ) ) ;
204+ }
205+
206+ private void WarnMoreParentheses ( IList < IToken > lp , IList < IToken > rp )
207+ {
208+ WarnMoreParentheses ( lp ) ;
209+ WarnMoreParentheses ( rp ) ;
210+ }
211+
181212 public sealed override object VisitAbsulouteStepTag ( P . AbsulouteStepTagContext context )
182213 {
183- if ( context . exception != null ) return false ; // 如果本节点下有异常,则直接整个吞掉,(避免具体的规则遇到不完整子树、爆出更不可预测的错误)
214+ if ( SubtreeHasException ( context ) ) return false ; // 如果本节点下有异常,则直接整个吞掉,(避免具体的规则遇到不完整子树、爆出更不可预测的错误)
184215 if ( ! absoluteTimeStepWarned )
185216 {
186217 AddAlert ( Warning , string . Format ( Locale . AbsoluteStepUsed , context . GetText ( ) ) , context ) ;
187218 absoluteTimeStepWarned = true ;
188219 }
220+ currContext = context ;
221+ WarnMoreParentheses ( context . _lp , context . _rp ) ;
189222 absoluteTimeStep = ( decimal ) VisitNumber ( context . number ( ) ) ;
190223 var currentBpm = chart . BpmList . Last ( ) . Bpm ;
191224 step = ( Rational ) absoluteTimeStep / ( 240 / ( Rational ) currentBpm ) ;
@@ -194,8 +227,9 @@ public sealed override object VisitAbsulouteStepTag(P.AbsulouteStepTagContext co
194227
195228 public sealed override object VisitBpmTag ( P . BpmTagContext context )
196229 {
197- if ( context . exception != null ) return false ; // 如果本节点下有异常,则直接整个吞掉,(避免具体的规则遇到不完整子树、爆出更不可预测的错误)
230+ if ( SubtreeHasException ( context ) ) return false ; // 如果本节点下有异常,则直接整个吞掉,(避免具体的规则遇到不完整子树、爆出更不可预测的错误)
198231 currContext = context ;
232+ WarnMoreParentheses ( context . _lp , context . _rp ) ;
199233 var bpm = ( decimal ) VisitNumber ( context . number ( ) ) ;
200234 chart . BpmList . Add ( new BPM ( now , bpm ) ) ;
201235 if ( absoluteTimeStep != null )
@@ -207,8 +241,9 @@ public sealed override object VisitBpmTag(P.BpmTagContext context)
207241
208242 public sealed override object VisitMetTag ( P . MetTagContext context )
209243 { // metTag指的是标记分音的tag,如{4}
210- if ( context . exception != null ) return false ; // 如果本节点下有异常,则直接整个吞掉,(避免具体的规则遇到不完整子树、爆出更不可预测的错误)
244+ if ( SubtreeHasException ( context ) ) return false ; // 如果本节点下有异常,则直接整个吞掉,(避免具体的规则遇到不完整子树、爆出更不可预测的错误)
211245 currContext = context ;
246+ WarnMoreParentheses ( context . _lp , context . _rp ) ;
212247 var quaver = int . Parse ( context . @int ( ) . GetText ( ) ) ;
213248 step = new Rational ( 1 , quaver ) ;
214249 absoluteTimeStep = null ;
@@ -266,7 +301,7 @@ public sealed override object VisitNote(P.NoteContext context)
266301 // 注:这个函数返回的是List<Note>,因为ANTLR中的NoteContext,虽然大多数时候只对应一个Note,但有时也可能是两个以上!
267302 // 具体而言,两种情况:1. "1234"这种simai允许的tap多押简略记法(等价于"1/2/3/4")
268303 // 2. 同头星星如"1-2[2:1]*-3[2:1]",它在我们定义的ANTLR语法中是作为一个note节点的!
269- if ( context . exception != null ) return new List < Note > ( ) ; // 如果本节点下有异常,则直接整个吞掉,(避免具体的规则遇到不完整子树、爆出更不可预测的错误)
304+ if ( SubtreeHasException ( context ) ) return new List < Note > ( ) ; // 如果本节点下有异常,则直接整个吞掉,(避免具体的规则遇到不完整子树、爆出更不可预测的错误)
270305 currContext = context ;
271306 List < Note > result = [ ] ;
272307 foreach ( var child in context . children )
@@ -326,7 +361,7 @@ private void ApplyModifiers(P.ModifiersContext[] modifiersList, Note note, bool
326361 if ( child is not ITerminalNode modifier ) throw Utils . Fail ( "modifiers里面居然不是ITerminalNode" ) ;
327362 var token = modifier . Symbol ;
328363 if ( token . Text == "b" && note is not Touch ) note . IsBreak = true ;
329- else if ( token . Text == "x" && note is Tap ) note . IsEx = false ;
364+ else if ( token . Text == "x" && note is Tap ) note . IsEx = true ;
330365 else if ( token . Text == "f" && note is Touch touch ) touch . IsFirework = true ;
331366 else extraModifiers . Add ( token ) ;
332367 }
@@ -372,6 +407,7 @@ public sealed override object VisitDuration(P.DurationContext? context)
372407 result . InvariantBar = 0 ;
373408 return result ;
374409 }
410+ WarnMoreParentheses ( context . _lp , context . _rp ) ;
375411 if ( context . beats ( ) != null ) result . InvariantBar = ( Rational ) VisitBeats ( context . beats ( ) ) ;
376412 else result . Seconds = ( Rational ) ( decimal ) VisitNumber ( context . number ( ) ) ;
377413 return result ;
@@ -417,6 +453,7 @@ public sealed override object VisitSlideDuration(P.SlideDurationContext context)
417453 var result = new Duration ( currNote ! ) ;
418454 Duration ? waitTime = null ;
419455 isRealExactWaitTime = false ;
456+ WarnMoreParentheses ( context . _lp , context . _rp ) ;
420457
421458 if ( context . waitTime ( ) != null )
422459 {
0 commit comments