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

     1  package stringlib
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"strings"
     7  
     8  	"github.com/arnodel/golua/lib/packagelib"
     9  	"github.com/arnodel/golua/luastrings"
    10  	rt "github.com/arnodel/golua/runtime"
    11  )
    12  
    13  // LibLoader specifies how to load the string lib
    14  var LibLoader = packagelib.Loader{
    15  	Load: load,
    16  	Name: "string",
    17  }
    18  
    19  func load(r *rt.Runtime) (rt.Value, func()) {
    20  	pkg := rt.NewTable()
    21  	pkgVal := rt.TableValue(pkg)
    22  
    23  	rt.SolemnlyDeclareCompliance(
    24  		rt.ComplyCpuSafe|rt.ComplyMemSafe|rt.ComplyTimeSafe|rt.ComplyIoSafe,
    25  
    26  		r.SetEnvGoFunc(pkg, "byte", bytef, 3, false),
    27  		r.SetEnvGoFunc(pkg, "char", char, 0, true),
    28  		r.SetEnvGoFunc(pkg, "dump", dump, 2, false),
    29  		r.SetEnvGoFunc(pkg, "find", find, 4, false),
    30  		r.SetEnvGoFunc(pkg, "gmatch", gmatch, 3, false),
    31  		r.SetEnvGoFunc(pkg, "gsub", gsub, 4, false),
    32  		r.SetEnvGoFunc(pkg, "len", lenf, 1, false),
    33  		r.SetEnvGoFunc(pkg, "lower", lower, 1, false),
    34  		r.SetEnvGoFunc(pkg, "match", match, 3, false),
    35  		r.SetEnvGoFunc(pkg, "upper", upper, 1, false),
    36  		r.SetEnvGoFunc(pkg, "rep", rep, 3, false),
    37  		r.SetEnvGoFunc(pkg, "reverse", reverse, 1, false),
    38  		r.SetEnvGoFunc(pkg, "sub", sub, 3, false),
    39  		r.SetEnvGoFunc(pkg, "format", format, 1, true),
    40  		r.SetEnvGoFunc(pkg, "pack", pack, 1, true),
    41  		r.SetEnvGoFunc(pkg, "packsize", packsize, 1, false),
    42  		r.SetEnvGoFunc(pkg, "unpack", unpack, 3, false),
    43  	)
    44  
    45  	stringMeta := rt.NewTable()
    46  	r.SetEnv(stringMeta, "__index", pkgVal)
    47  
    48  	rt.SolemnlyDeclareCompliance(
    49  		rt.ComplyCpuSafe|rt.ComplyMemSafe|rt.ComplyTimeSafe|rt.ComplyIoSafe,
    50  
    51  		r.SetEnvGoFunc(stringMeta, "__add", string__add, 2, false),
    52  		r.SetEnvGoFunc(stringMeta, "__sub", string__sub, 2, false),
    53  		r.SetEnvGoFunc(stringMeta, "__mul", string__mul, 2, false),
    54  		r.SetEnvGoFunc(stringMeta, "__div", string__div, 2, false),
    55  		r.SetEnvGoFunc(stringMeta, "__idiv", string__idiv, 2, false),
    56  		r.SetEnvGoFunc(stringMeta, "__mod", string__mod, 2, false),
    57  		r.SetEnvGoFunc(stringMeta, "__pow", string__pow, 2, false),
    58  		r.SetEnvGoFunc(stringMeta, "__unm", string__unm, 1, false),
    59  	)
    60  	r.SetStringMeta(stringMeta)
    61  
    62  	return pkgVal, nil
    63  }
    64  
    65  func maxpos(i, j int) int {
    66  	if i > j {
    67  		return i
    68  	}
    69  	return j
    70  }
    71  
    72  func minpos(i, j int) int {
    73  	if i < j {
    74  		return i
    75  	}
    76  	return j
    77  }
    78  
    79  func bytef(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
    80  	if err := c.Check1Arg(); err != nil {
    81  		return nil, err
    82  	}
    83  	s, err := c.StringArg(0)
    84  	if err != nil {
    85  		return nil, err
    86  	}
    87  	i, j := 1, 1
    88  	if c.NArgs() >= 2 {
    89  		ii, err := c.IntArg(1)
    90  		if err != nil {
    91  			return nil, err
    92  		}
    93  		i = luastrings.StringNormPos(s, int(ii))
    94  		j = i
    95  	}
    96  	if c.NArgs() >= 3 {
    97  		jj, err := c.IntArg(2)
    98  		if err != nil {
    99  			return nil, err
   100  		}
   101  		j = luastrings.StringNormPos(s, int(jj))
   102  	}
   103  	next := c.Next()
   104  	i = maxpos(1, i)
   105  	j = minpos(len(s), j)
   106  	for i <= j {
   107  		t.Push1(next, rt.IntValue(int64(s[i-1])))
   108  		i++
   109  	}
   110  	return next, nil
   111  }
   112  
   113  func char(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
   114  	vals := c.Etc()
   115  	buf := make([]byte, len(vals))
   116  	for i, v := range vals {
   117  		x, ok := rt.ToInt(v)
   118  		if !ok {
   119  			return nil, errors.New("arguments must be integers")
   120  		}
   121  		if x < 0 || x > 255 {
   122  			return nil, fmt.Errorf("#%d out of range", i+1)
   123  		}
   124  		buf[i] = byte(x)
   125  	}
   126  	t.RequireBytes(len(buf))
   127  	return c.PushingNext1(t.Runtime, rt.StringValue(string(buf))), nil
   128  }
   129  
   130  func lenf(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
   131  	if err := c.Check1Arg(); err != nil {
   132  		return nil, err
   133  	}
   134  	s, err := c.StringArg(0)
   135  	if err != nil {
   136  		return nil, err
   137  	}
   138  	return c.PushingNext1(t.Runtime, rt.IntValue(int64(len(s)))), nil
   139  }
   140  
   141  func lower(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
   142  	if err := c.Check1Arg(); err != nil {
   143  		return nil, err
   144  	}
   145  	s, err := c.StringArg(0)
   146  	if err != nil {
   147  		return nil, err
   148  	}
   149  	t.RequireBytes(len(s))
   150  	s = strings.ToLower(string(s))
   151  	return c.PushingNext1(t.Runtime, rt.StringValue(s)), nil
   152  }
   153  
   154  func upper(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
   155  	if err := c.Check1Arg(); err != nil {
   156  		return nil, err
   157  	}
   158  	s, err := c.StringArg(0)
   159  	if err != nil {
   160  		return nil, err
   161  	}
   162  	t.RequireBytes(len(s))
   163  	s = strings.ToUpper(string(s))
   164  	return c.PushingNext1(t.Runtime, rt.StringValue(s)), nil
   165  }
   166  
   167  func rep(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
   168  	if err := c.CheckNArgs(2); err != nil {
   169  		return nil, err
   170  	}
   171  	ls, err := c.StringArg(0)
   172  	if err != nil {
   173  		return nil, err
   174  	}
   175  	ln, err := c.IntArg(1)
   176  	if err != nil {
   177  		return nil, err
   178  	}
   179  	n := int(ln)
   180  	if n < 0 {
   181  		return nil, errors.New("#2 out of range")
   182  	}
   183  	var sep []byte
   184  	if c.NArgs() >= 3 {
   185  		lsep, err := c.StringArg(2)
   186  		if err != nil {
   187  			return nil, err
   188  		}
   189  		sep = []byte(lsep)
   190  	}
   191  	if n == 0 {
   192  		return c.PushingNext1(t.Runtime, rt.StringValue("")), nil
   193  	}
   194  	if n == 1 {
   195  		return c.PushingNext1(t.Runtime, rt.StringValue(ls)), nil
   196  	}
   197  	if sep == nil {
   198  		if len(ls)*n/n != len(ls) {
   199  			// Overflow
   200  			return nil, errors.New("rep causes overflow")
   201  		}
   202  		t.RequireBytes(n * len(ls))
   203  		return c.PushingNext1(t.Runtime, rt.StringValue(strings.Repeat(string(ls), n))), nil
   204  	}
   205  	s := []byte(ls)
   206  	builder := strings.Builder{}
   207  	sz1 := n * len(s)
   208  	sz2 := (n - 1) * len(sep)
   209  	sz := sz1 + sz2
   210  	if sz1/n != len(s) || sz2/(n-1) != len(sep) || sz < 0 {
   211  		return nil, errors.New("rep causes overflow")
   212  	}
   213  	t.RequireBytes(n*len(s) + (n-1)*len(sep))
   214  	builder.Grow(sz)
   215  	builder.Write(s)
   216  	for {
   217  		n--
   218  		if n == 0 {
   219  			break
   220  		}
   221  		builder.Write(sep)
   222  		builder.Write(s)
   223  	}
   224  	return c.PushingNext1(t.Runtime, rt.StringValue(builder.String())), nil
   225  }
   226  
   227  func reverse(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
   228  	if err := c.Check1Arg(); err != nil {
   229  		return nil, err
   230  	}
   231  	s, err := c.StringArg(0)
   232  	if err != nil {
   233  		return nil, err
   234  	}
   235  	t.RequireBytes(len(s))
   236  	sb := []byte(s)
   237  	l := len(s) - 1
   238  	for i := 0; 2*i <= l; i++ {
   239  		sb[i], sb[l-i] = sb[l-i], sb[i]
   240  	}
   241  	return c.PushingNext1(t.Runtime, rt.StringValue(string(sb))), nil
   242  }
   243  
   244  func sub(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
   245  	if err := c.CheckNArgs(2); err != nil {
   246  		return nil, err
   247  	}
   248  	s, err := c.StringArg(0)
   249  	if err != nil {
   250  		return nil, err
   251  	}
   252  	ii, err := c.IntArg(1)
   253  	if err != nil {
   254  		return nil, err
   255  	}
   256  	i := luastrings.StringNormPos(s, int(ii))
   257  	j := len(s)
   258  	if c.NArgs() >= 3 {
   259  		jj, err := c.IntArg(2)
   260  		if err != nil {
   261  			return nil, err
   262  		}
   263  		j = luastrings.StringNormPos(s, int(jj))
   264  	}
   265  	var slice string
   266  	i = maxpos(1, i)
   267  	j = minpos(len(s), j)
   268  	if i <= len(s) && i <= j {
   269  		t.RequireBytes(j - i + 1)
   270  		slice = s[i-1 : j]
   271  	}
   272  	return c.PushingNext1(t.Runtime, rt.StringValue(slice)), nil
   273  }