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

     1  package stringlib
     2  
     3  import rt "github.com/arnodel/golua/runtime"
     4  
     5  var (
     6  	string__add  = stringBinOp(rt.Add, "__add")
     7  	string__sub  = stringBinOp(rt.Sub, "__sub")
     8  	string__mul  = stringBinOp(rt.Mul, "__mul")
     9  	string__div  = stringBinOp(rt.Div, "__div")
    10  	string__idiv = stringBinOpErr(rt.Idiv, "__idiv")
    11  	string__mod  = stringBinOpErr(rt.Mod, "__mod")
    12  	string__pow  = stringBinOp(rt.Pow, "__pow")
    13  	string__unm  = stringUnOp(rt.Unm, "__unm")
    14  )
    15  
    16  func stringBinOp(f func(x, y rt.Value) (rt.Value, bool), op string) func(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
    17  	return func(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
    18  		if err := c.CheckNArgs(2); err != nil {
    19  			return nil, err
    20  		}
    21  		x, y := c.Arg(0), c.Arg(1)
    22  		nx, kx := rt.ToNumberValue(x)
    23  		ny, ky := rt.ToNumberValue(y)
    24  		if kx != rt.NaN && ky != rt.NaN {
    25  			z, _ := f(nx, ny)
    26  			return c.PushingNext1(t.Runtime, z), nil
    27  		}
    28  		if y.Type() != rt.StringType {
    29  			next := c.Next()
    30  			err, ok := rt.Metacall(t, y, op, []rt.Value{x, y}, next)
    31  			if ok {
    32  				if err != nil {
    33  					return nil, err
    34  				}
    35  				return next, nil
    36  			}
    37  		}
    38  		return nil, rt.BinaryArithmeticError(op[2:], nx, ny)
    39  	}
    40  }
    41  
    42  func stringBinOpErr(f func(x, y rt.Value) (rt.Value, bool, error), op string) func(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
    43  	return func(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
    44  		if err := c.CheckNArgs(2); err != nil {
    45  			return nil, err
    46  		}
    47  		x, y := c.Arg(0), c.Arg(1)
    48  		nx, kx := rt.ToNumberValue(x)
    49  		ny, ky := rt.ToNumberValue(y)
    50  		if kx != rt.NaN && ky != rt.NaN {
    51  			z, _, err := f(nx, ny)
    52  			if err != nil {
    53  				return nil, err
    54  			}
    55  			return c.PushingNext1(t.Runtime, z), nil
    56  		}
    57  		if y.Type() != rt.StringType {
    58  			next := c.Next()
    59  			err, ok := rt.Metacall(t, y, op, []rt.Value{x, y}, next)
    60  			if ok {
    61  				if err != nil {
    62  					return nil, err
    63  				}
    64  				return next, nil
    65  			}
    66  		}
    67  		return nil, rt.BinaryArithmeticError(op[2:], nx, ny)
    68  	}
    69  }
    70  
    71  func stringUnOp(f func(x rt.Value) (rt.Value, bool), op string) func(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
    72  	return func(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
    73  		if err := c.Check1Arg(); err != nil {
    74  			return nil, err
    75  		}
    76  		x := c.Arg(0)
    77  		nx, _ := rt.ToNumberValue(x)
    78  		z, ok := f(nx)
    79  		if ok {
    80  			return c.PushingNext1(t.Runtime, z), nil
    81  		}
    82  		return nil, rt.UnaryArithmeticError(op[2:], nx)
    83  	}
    84  }