@@ -274,6 +274,8 @@ struct Decimal(size_t maxSize64)
274274 {
275275 bool recentUnderscore;
276276 }
277+ // Was there a recent character within the set: ['.', 'e', 'E', 'd', 'D']?
278+ bool recentModifier;
277279 static if (! allowLeadingZeros)
278280 {
279281 if (d == 0 )
@@ -299,6 +301,7 @@ struct Decimal(size_t maxSize64)
299301 return false ;
300302 key = DecimalExponentKey.dot;
301303 dot = true ;
304+ recentModifier = true ;
302305 goto F;
303306 }
304307 }
@@ -323,6 +326,7 @@ struct Decimal(size_t maxSize64)
323326 {
324327 recentUnderscore = false ;
325328 }
329+ recentModifier = false ;
326330 {
327331 import mir.checkedint: mulu;
328332 bool overflow;
@@ -342,31 +346,35 @@ struct Decimal(size_t maxSize64)
342346 coefficient.length = v != 0 ;
343347 static if (allowUnderscores)
344348 {
345- return ! recentUnderscore;
349+ return ! recentUnderscore && ! recentModifier ;
346350 }
347351 else
348352 {
349- return true ;
353+ return ! recentModifier ;
350354 }
351355 }
352- static if (allowUnderscores)
353- {
354- if (recentUnderscore)
355- return false ;
356- }
356+
357357 switch (d)
358358 {
359359 case DecimalExponentKey.dot:
360+ // The dot modifier CANNOT be preceded by any modifiers.
361+ if (recentModifier || key != DecimalExponentKey.none)
362+ break ;
363+
364+ static if (allowUnderscores)
365+ {
366+ // If we allow underscores, the dot also CANNOT be preceded by any underscores.
367+ // It must be preceded by a number.
368+ if (recentUnderscore)
369+ break ;
370+ }
360371 key = DecimalExponentKey.dot;
361372 if (_expect(dot, false ))
362373 break ;
363374 dot = true ;
364375 if (str.length)
365376 {
366- static if (allowUnderscores)
367- {
368- recentUnderscore = true ;
369- }
377+ recentModifier = true ;
370378 continue ;
371379 }
372380 static if (allowDotOnBounds)
@@ -388,14 +396,24 @@ struct Decimal(size_t maxSize64)
388396 case DecimalExponentKey.e:
389397 case DecimalExponentKey.E:
390398 import mir.parse: parse;
399+ // We don't really care if the e/E/d/D modifiers are preceded by a modifier,
400+ // so as long as they are a dot or a regular number.
401+ if (key != DecimalExponentKey.dot && key != DecimalExponentKey.none)
402+ break ;
391403 key = cast (DecimalExponentKey)d;
392- if (parse(str, exponent) && str.length == 0 )
404+ if (parse(str, exponent) && str.length == 0 ) {
405+ recentModifier = false ;
393406 goto E;
407+ }
394408 break ;
395409 }
396410 static if (allowUnderscores)
397411 {
398412 case ' _' - ' 0' :
413+ // A underscore cannot be preceded by an underscore or a modifier.
414+ if (recentUnderscore || recentModifier)
415+ break ;
416+
399417 recentUnderscore = true ;
400418 if (str.length)
401419 continue ;
@@ -554,6 +572,52 @@ struct Decimal(size_t maxSize64)
554572 assert (key == DecimalExponentKey.E);
555573 assert (cast (double ) decimal == 0 );
556574
575+ /+ + Test that Issue #365 is handled properly +/
576+ assert (decimal.fromStringImpl(" 123456.e0" , key));
577+ assert (key == DecimalExponentKey.e);
578+ assert (cast (double ) decimal == 123_456.0 );
579+
580+ assert (decimal.fromStringImpl(" 123_456.e0" , key));
581+ assert (key == DecimalExponentKey.e);
582+ assert (cast (double ) decimal == 123_456.0 );
583+
584+ assert (decimal.fromStringImpl(" 123456.E0" , key));
585+ assert (key == DecimalExponentKey.E);
586+ assert (cast (double ) decimal == 123_456.0 );
587+
588+ assert (decimal.fromStringImpl(" 123_456.E0" , key));
589+ assert (key == DecimalExponentKey.E);
590+ assert (cast (double ) decimal == 123_456.0 );
591+
592+ assert (decimal.fromStringImpl(" 123456.d0" , key));
593+ assert (key == DecimalExponentKey.d);
594+ assert (cast (double ) decimal == 123_456.0 );
595+
596+ assert (decimal.fromStringImpl(" 123_456.d0" , key));
597+ assert (key == DecimalExponentKey.d);
598+ assert (cast (double ) decimal == 123_456.0 );
599+
600+ assert (decimal.fromStringImpl(" 123456.D0" , key));
601+ assert (key == DecimalExponentKey.D);
602+ assert (cast (double ) decimal == 123_456.0 );
603+
604+ assert (decimal.fromStringImpl(" 123_456.D0" , key));
605+ assert (key == DecimalExponentKey.D);
606+ assert (cast (double ) decimal == 123_456.0 );
607+
608+ /+ + Test invalid examples with the fix introduced for Issue #365 +/
609+ assert (! decimal.fromStringImpl(" 123_456_.D0" , key));
610+ assert (! decimal.fromStringImpl(" 123_456.DD0" , key));
611+ assert (! decimal.fromStringImpl(" 123_456_.E0" , key));
612+ assert (! decimal.fromStringImpl(" 123_456.EE0" , key));
613+ assert (! decimal.fromStringImpl(" 123456.ED0" , key));
614+ assert (! decimal.fromStringImpl(" 123456E0D0" , key));
615+ assert (! decimal.fromStringImpl(" 123456._D0" , key));
616+ assert (! decimal.fromStringImpl(" 123456_.D0" , key));
617+ assert (! decimal.fromStringImpl(" 123456.E0D0" , key));
618+ assert (! decimal.fromStringImpl(" 123456.D0_" , key));
619+ assert (! decimal.fromStringImpl(" 123456_" , key));
620+
557621 foreach (str; [" -nan" , " -NaN" , " -NAN" ])
558622 {
559623 assert (decimal.fromStringImpl(str, key));
0 commit comments