github.com/arnodel/golua@v0.0.0-20230215163904-e0b5347eaaa1/lib/base/tonumber.go (about)

     1  package base
     2  
     3  import (
     4  	"bytes"
     5  	"errors"
     6  
     7  	rt "github.com/arnodel/golua/runtime"
     8  )
     9  
    10  func tonumber(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
    11  	if err := c.Check1Arg(); err != nil {
    12  		return nil, err
    13  	}
    14  	nargs := c.NArgs()
    15  	next := c.Next()
    16  	n := c.Arg(0)
    17  	if nargs == 1 {
    18  		n, tp := rt.ToNumberValue(n)
    19  		if tp != rt.NaN {
    20  			t.Push1(next, n)
    21  		} else {
    22  			t.Push1(next, rt.NilValue)
    23  		}
    24  		return next, nil
    25  	}
    26  	base, err := c.IntArg(1)
    27  	if err != nil {
    28  		return nil, err
    29  	}
    30  	if base < 2 || base > 36 {
    31  		return nil, errors.New("#2 out of range")
    32  	}
    33  	s, ok := n.TryString()
    34  	if !ok {
    35  		return nil, errors.New("#1 must be a string")
    36  	}
    37  	digits := bytes.TrimSpace([]byte(s))
    38  	if len(digits) == 0 {
    39  		t.Push1(next, rt.NilValue)
    40  		return next, nil
    41  	}
    42  	var number int64
    43  	var sign int64 = 1
    44  	if digits[0] == '-' || digits[0] == '+' {
    45  		if digits[0] == '-' {
    46  			sign = -1
    47  		}
    48  		digits = digits[1:]
    49  		if len(digits) == 0 {
    50  			t.Push1(next, rt.NilValue)
    51  			return next, nil
    52  		}
    53  	}
    54  	for _, digit := range digits {
    55  		var d int64
    56  		switch {
    57  		case '0' <= digit && digit <= '9':
    58  			d = int64(digit - '0')
    59  		case 'a' <= digit && digit <= 'z':
    60  			d = int64(digit - 'a' + 10)
    61  		case 'A' <= digit && digit <= 'Z':
    62  			d = int64(digit - 'A' + 10)
    63  		default:
    64  			t.Push1(next, rt.NilValue)
    65  			return next, nil
    66  		}
    67  		if d >= base {
    68  			t.Push1(next, rt.NilValue)
    69  			return next, nil
    70  		}
    71  		number = number*base + d
    72  	}
    73  	t.Push1(next, rt.IntValue(sign*number))
    74  	return next, nil
    75  }