@@ -491,34 +491,24 @@ public ValueTask<int> Select(
491491 CancellationToken cancellationToken
492492 )
493493 {
494- var arg0 = context . GetArgument ( 0 ) ;
495-
496- if ( arg0 . TryRead < int > ( out var index ) )
497- {
498- if ( Math . Abs ( index ) > context . ArgumentCount )
499- {
500- throw new LuaRuntimeException (
501- context . State ,
502- "bad argument #1 to 'select' (index out of range)"
503- ) ;
504- }
505-
506- var span =
507- index >= 0
508- ? context . Arguments [ index .. ]
509- : context . Arguments [ ( context . ArgumentCount + index ) .. ] ;
510-
511- return new ( context . Return ( span ) ) ;
512- }
513- else if ( arg0 . TryRead < string > ( out var str ) && str == "#" )
514- {
515- return new ( context . Return ( context . ArgumentCount - 1 ) ) ;
516- }
517- else
518- {
519- LuaRuntimeException . BadArgument ( context . State , 1 , LuaValueType . Number , arg0 . Type ) ;
520- return default ;
521- }
494+ var selector = context . GetArgument ( 0 ) ;
495+ if ( selector . Type == LuaValueType . String )
496+ {
497+ var text = selector . UnsafeReadString ( ) ;
498+ if ( text . Length > 0 && text [ 0 ] == '#' )
499+ return new ( context . Return ( context . ArgumentCount - 1 ) ) ;
500+ }
501+
502+ var index = Math . Truncate ( context . GetArgument < double > ( 0 ) ) ;
503+ if ( ! MathEx . IsFinite ( index ) )
504+ context . ThrowBadArgument ( 1 , "index out of range" ) ;
505+ if ( index < 0 )
506+ index += context . ArgumentCount ;
507+ else if ( index > context . ArgumentCount )
508+ index = context . ArgumentCount ;
509+ if ( index < 1 )
510+ context . ThrowBadArgument ( 1 , "index out of range" ) ;
511+ return new ( context . Return ( context . Arguments [ ( int ) index .. ] ) ) ;
522512 }
523513
524514 public ValueTask < int > SetMetatable (
@@ -560,152 +550,65 @@ public ValueTask<int> ToNumber(
560550 CancellationToken cancellationToken
561551 )
562552 {
563- var e = context . GetArgument ( 0 ) ;
564- int ? toBase = context . HasArgument ( 1 ) ? ( int ) context . GetArgument < double > ( 1 ) : null ;
565-
566- if ( toBase != null && ( toBase < 2 || toBase > 36 ) )
567- {
568- throw new LuaRuntimeException (
569- context . State ,
570- "bad argument #2 to 'tonumber' (base out of range)"
553+ var value = context . GetArgument ( 0 ) ;
554+ if ( ! context . HasArgument ( 1 ) )
555+ {
556+ if ( value . Type == LuaValueType . Number )
557+ return new ( context . Return ( value ) ) ;
558+ return new (
559+ context . Return (
560+ value . TryReadDouble ( out var number ) && ! double . IsNaN ( number )
561+ ? new LuaValue ( number )
562+ : LuaValue . Nil
563+ )
571564 ) ;
572565 }
573566
574- double ? value = null ;
575- if ( e . Type is LuaValueType . Number )
576- {
577- value = e . UnsafeRead < double > ( ) ;
578- }
579- else if ( e . TryRead < string > ( out var str ) )
580- {
581- if ( toBase == null )
582- {
583- if ( e . TryRead < double > ( out var result ) )
584- {
585- value = result ;
586- }
587- }
588- else if ( toBase == 10 )
589- {
590- if (
591- double . TryParse (
592- str ,
593- NumberStyles . Float ,
594- CultureInfo . InvariantCulture ,
595- out var result
596- )
597- )
598- {
599- value = result ;
600- }
601- }
602- else
603- {
604- try
605- {
606- // if the base is not 10, str cannot contain a minus sign
607- var span = str . AsSpan ( ) . Trim ( ) ;
608- if ( span . Length == 0 )
609- {
610- goto END ;
611- }
612-
613- var first = span [ 0 ] ;
614- var sign = first == '-' ? - 1 : 1 ;
615- if ( first is '+' or '-' )
616- {
617- span = span [ 1 .. ] ;
618- }
619-
620- if ( span . Length == 0 )
621- {
622- goto END ;
623- }
624-
625- if ( toBase == 16 && span . Length > 2 && span [ 0 ] is '0' && span [ 1 ] is 'x' or 'X' )
626- {
627- value = sign * HexConverter . ToDouble ( span ) ;
628- }
629- else
630- {
631- value = sign * StringToDouble ( span , toBase . Value ) ;
632- }
633- }
634- catch ( FormatException )
635- {
636- goto END ;
637- }
638- }
639- }
567+ // Lua coerces numeric inputs to text before interpreting an explicit base.
568+ string text ;
569+ if ( value . Type == LuaValueType . String )
570+ text = value . UnsafeReadString ( ) ;
571+ else if ( value . Type == LuaValueType . Number )
572+ text = value . ToString ( ) ;
640573 else
641574 {
642- goto END ;
643- }
644-
645- END :
646- if ( value is double . NaN )
647- {
648- value = null ;
575+ LuaRuntimeException . BadArgument ( context . State , 1 , LuaValueType . String , value . Type ) ;
576+ return default ;
649577 }
650578
651- return new ( context . Return ( value ?? LuaValue . Nil ) ) ;
579+ var toBase = ( int ) context . GetArgument < double > ( 1 ) ;
580+ if ( toBase is < 2 or > 36 )
581+ context . ThrowBadArgument ( 2 , "base out of range" ) ;
582+ var parsed = StringToDouble ( text . AsSpan ( ) , toBase ) ;
583+ return new ( context . Return ( parsed . HasValue ? new LuaValue ( parsed . Value ) : LuaValue . Nil ) ) ;
652584 }
653585
654- static double StringToDouble ( ReadOnlySpan < char > text , int toBase )
586+ static double ? StringToDouble ( ReadOnlySpan < char > text , int toBase )
655587 {
656- var value = 0.0 ;
657- for ( var i = 0 ; i < text . Length ; i ++ )
658- {
659- var v = text [ i ] switch
588+ text = text . Trim ( " \f \n \r \t \v " ) ;
589+ if ( text . IsEmpty )
590+ return null ;
591+ var negative = text [ 0 ] == '-' ;
592+ if ( text [ 0 ] is '+' or '-' )
593+ text = text [ 1 .. ] ;
594+ if ( text . IsEmpty )
595+ return null ;
596+
597+ double value = 0 ;
598+ foreach ( var c in text )
599+ {
600+ var digit = c switch
660601 {
661- '0' => 0 ,
662- '1' => 1 ,
663- '2' => 2 ,
664- '3' => 3 ,
665- '4' => 4 ,
666- '5' => 5 ,
667- '6' => 6 ,
668- '7' => 7 ,
669- '8' => 8 ,
670- '9' => 9 ,
671- 'a' or 'A' => 10 ,
672- 'b' or 'B' => 11 ,
673- 'c' or 'C' => 12 ,
674- 'd' or 'D' => 13 ,
675- 'e' or 'E' => 14 ,
676- 'f' or 'F' => 15 ,
677- 'g' or 'G' => 16 ,
678- 'h' or 'H' => 17 ,
679- 'i' or 'I' => 18 ,
680- 'j' or 'J' => 19 ,
681- 'k' or 'K' => 20 ,
682- 'l' or 'L' => 21 ,
683- 'm' or 'M' => 22 ,
684- 'n' or 'N' => 23 ,
685- 'o' or 'O' => 24 ,
686- 'p' or 'P' => 25 ,
687- 'q' or 'Q' => 26 ,
688- 'r' or 'R' => 27 ,
689- 's' or 'S' => 28 ,
690- 't' or 'T' => 29 ,
691- 'u' or 'U' => 30 ,
692- 'v' or 'V' => 31 ,
693- 'w' or 'W' => 32 ,
694- 'x' or 'X' => 33 ,
695- 'y' or 'Y' => 34 ,
696- 'z' or 'Z' => 35 ,
697- _ => throw new FormatException ( "Invalid digit." ) ,
602+ >= '0' and <= '9' => c - '0' ,
603+ >= 'A' and <= 'Z' => c - 'A' + 10 ,
604+ >= 'a' and <= 'z' => c - 'a' + 10 ,
605+ _ => 36 ,
698606 } ;
699-
700- if ( v >= toBase )
701- {
702- throw new FormatException ( ) ;
703- }
704-
705- value += v * Math . Pow ( toBase , text . Length - i - 1 ) ;
607+ if ( digit >= toBase )
608+ return null ;
609+ value = value * toBase + digit ;
706610 }
707-
708- return value ;
611+ return negative ? - value : value ;
709612 }
710613
711614 public ValueTask < int > ToString (
0 commit comments