github.com/hirochachacha/plua@v0.0.0-20170217012138-c82f520cc725/stdlib/table/table.go (about)

     1  package table
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  
     7  	"github.com/hirochachacha/plua/internal/arith"
     8  	"github.com/hirochachacha/plua/internal/limits"
     9  	"github.com/hirochachacha/plua/internal/version"
    10  	"github.com/hirochachacha/plua/object"
    11  	"github.com/hirochachacha/plua/object/fnutil"
    12  )
    13  
    14  func concat(th object.Thread, args ...object.Value) ([]object.Value, *object.RuntimeError) {
    15  	ap := fnutil.NewArgParser(th, args)
    16  
    17  	t, err := ap.ToTable(0)
    18  	if err != nil {
    19  		return nil, err
    20  	}
    21  
    22  	sep, err := ap.OptGoString(1, "")
    23  	if err != nil {
    24  		return nil, err
    25  	}
    26  
    27  	i, err := ap.OptGoInt(2, 1)
    28  	if err != nil {
    29  		return nil, err
    30  	}
    31  
    32  	tlen, err := callLen(th, t)
    33  	if err != nil {
    34  		return nil, err
    35  	}
    36  
    37  	j, err := ap.OptGoInt(3, tlen)
    38  	if err != nil {
    39  		return nil, err
    40  	}
    41  
    42  	if i > j {
    43  		return []object.Value{object.String("")}, nil
    44  	}
    45  
    46  	var buf bytes.Buffer
    47  
    48  	var val object.Value
    49  	var ok bool
    50  	var tmp string
    51  
    52  	for {
    53  		val, err = arith.CallGettable(th, t, object.Integer(i))
    54  		if err != nil {
    55  			return nil, err
    56  		}
    57  
    58  		tmp, ok = object.ToGoString(val)
    59  		if !ok {
    60  			return nil, object.NewRuntimeError(fmt.Sprintf("invalid value (%s) at index %d in table for 'concat'", object.ToType(val), i))
    61  		}
    62  
    63  		buf.WriteString(tmp)
    64  
    65  		if i == j {
    66  			break
    67  		}
    68  
    69  		buf.WriteString(sep)
    70  
    71  		i++
    72  	}
    73  
    74  	return []object.Value{object.String(buf.String())}, nil
    75  }
    76  
    77  // table.insert (list, [pos,] value)
    78  func insert(th object.Thread, args ...object.Value) ([]object.Value, *object.RuntimeError) {
    79  	ap := fnutil.NewArgParser(th, args)
    80  
    81  	t, err := ap.ToTable(0)
    82  	if err != nil {
    83  		return nil, err
    84  	}
    85  
    86  	tlen, err := callLen(th, t)
    87  	if err != nil {
    88  		return nil, err
    89  	}
    90  
    91  	switch len(args) {
    92  	case 2:
    93  		val, err := ap.ToValue(1)
    94  		if err != nil {
    95  			return nil, err
    96  		}
    97  
    98  		err = arith.CallSettable(th, t, object.Integer(tlen+1), val)
    99  		if err != nil {
   100  			return nil, err
   101  		}
   102  
   103  		return nil, nil
   104  	case 3:
   105  		pos, err := ap.OptGoInt(1, tlen+1)
   106  		if err != nil {
   107  			return nil, err
   108  		}
   109  
   110  		if pos < 1 || pos > tlen+1 {
   111  			return nil, ap.ArgError(1, "position out of bounds")
   112  		}
   113  
   114  		for i := tlen + 1; i > pos; i-- {
   115  			v, err := arith.CallGettable(th, t, object.Integer(i-1))
   116  			if err != nil {
   117  				return nil, err
   118  			}
   119  			err = arith.CallSettable(th, t, object.Integer(i), v)
   120  			if err != nil {
   121  				return nil, err
   122  			}
   123  		}
   124  
   125  		val, err := ap.ToValue(2)
   126  		if err != nil {
   127  			return nil, err
   128  		}
   129  
   130  		err = arith.CallSettable(th, t, object.Integer(pos), val)
   131  		if err != nil {
   132  			return nil, err
   133  		}
   134  
   135  		return nil, nil
   136  	default:
   137  		return nil, object.NewRuntimeError("wrong number of arguments to 'insert'")
   138  	}
   139  }
   140  
   141  // TODO support for not table type
   142  func move(th object.Thread, args ...object.Value) ([]object.Value, *object.RuntimeError) {
   143  	ap := fnutil.NewArgParser(th, args)
   144  
   145  	a1, err := ap.ToTable(0)
   146  	if err != nil {
   147  		return nil, err
   148  	}
   149  
   150  	f, err := ap.ToGoInt(1)
   151  	if err != nil {
   152  		return nil, err
   153  	}
   154  
   155  	e, err := ap.ToGoInt(2)
   156  	if err != nil {
   157  		return nil, err
   158  	}
   159  
   160  	t, err := ap.ToGoInt(3)
   161  	if err != nil {
   162  		return nil, err
   163  	}
   164  
   165  	a2 := a1
   166  	if _, ok := ap.Get(4); ok {
   167  		a2, err = ap.ToTable(4)
   168  		if err != nil {
   169  			return nil, err
   170  		}
   171  	}
   172  
   173  	// copy(a2[t:], a1[f:e])
   174  	if e >= f {
   175  		if f <= 0 && e > version.MAXMOVE+f || f > 0 && e-f > version.MAXMOVE {
   176  			return nil, ap.ArgError(2, "too many elements to move")
   177  		}
   178  
   179  		if t > int(limits.MaxInt)-(e-f) {
   180  			return nil, ap.ArgError(3, "destination wrap around")
   181  		}
   182  
   183  		if t > e || t <= f || a1 != a2 { // no overwrap
   184  			for i := 0; i <= e-f; i++ {
   185  				v, err := arith.CallGettable(th, a1, object.Integer(f+i))
   186  				if err != nil {
   187  					return nil, err
   188  				}
   189  				err = arith.CallSettable(th, a2, object.Integer(t+i), v)
   190  				if err != nil {
   191  					return nil, err
   192  				}
   193  			}
   194  		} else {
   195  			for i := e - f; i >= 0; i-- {
   196  				v, err := arith.CallGettable(th, a1, object.Integer(f+i))
   197  				if err != nil {
   198  					return nil, err
   199  				}
   200  				err = arith.CallSettable(th, a2, object.Integer(t+i), v)
   201  				if err != nil {
   202  					return nil, err
   203  				}
   204  			}
   205  		}
   206  	}
   207  
   208  	return []object.Value{a2}, nil
   209  }
   210  
   211  func pack(th object.Thread, args ...object.Value) ([]object.Value, *object.RuntimeError) {
   212  	t := th.NewTableArray(append([]object.Value{}, args...))
   213  
   214  	t.Set(object.String("n"), object.Integer(len(args)))
   215  
   216  	return []object.Value{t}, nil
   217  }
   218  
   219  func remove(th object.Thread, args ...object.Value) ([]object.Value, *object.RuntimeError) {
   220  	ap := fnutil.NewArgParser(th, args)
   221  
   222  	t, err := ap.ToTable(0)
   223  	if err != nil {
   224  		return nil, err
   225  	}
   226  
   227  	tlen, err := callLen(th, t)
   228  	if err != nil {
   229  		return nil, err
   230  	}
   231  
   232  	pos, err := ap.OptGoInt(1, tlen)
   233  	if err != nil {
   234  		return nil, err
   235  	}
   236  
   237  	if tlen == pos {
   238  		val, err := arith.CallGettable(th, t, object.Integer(pos))
   239  		if err != nil {
   240  			return nil, err
   241  		}
   242  		if val != nil {
   243  			err = arith.CallSettable(th, t, object.Integer(pos), nil)
   244  			if err != nil {
   245  				return nil, err
   246  			}
   247  		}
   248  		return []object.Value{val}, nil
   249  	}
   250  
   251  	if pos < 1 || pos > tlen+1 {
   252  		return nil, ap.ArgError(1, "position out of bounds")
   253  	}
   254  
   255  	val, err := arith.CallGettable(th, t, object.Integer(pos))
   256  	if err != nil {
   257  		return nil, err
   258  	}
   259  
   260  	for i := pos; i < tlen+1; i++ {
   261  		v, err := arith.CallGettable(th, t, object.Integer(i+1))
   262  		if err != nil {
   263  			return nil, err
   264  		}
   265  		err = arith.CallSettable(th, t, object.Integer(i), v)
   266  		if err != nil {
   267  			return nil, err
   268  		}
   269  	}
   270  
   271  	return []object.Value{val}, nil
   272  }
   273  
   274  func unpack(th object.Thread, args ...object.Value) ([]object.Value, *object.RuntimeError) {
   275  	ap := fnutil.NewArgParser(th, args)
   276  
   277  	t, err := ap.ToTable(0)
   278  	if err != nil {
   279  		return nil, err
   280  	}
   281  
   282  	i, err := ap.OptGoInt(1, 1)
   283  	if err != nil {
   284  		return nil, err
   285  	}
   286  
   287  	tlen, err := callLen(th, t)
   288  	if err != nil {
   289  		return nil, err
   290  	}
   291  
   292  	j, err := ap.OptGoInt(2, tlen)
   293  	if err != nil {
   294  		return nil, err
   295  	}
   296  
   297  	if i > j {
   298  		return nil, nil
   299  	}
   300  
   301  	if i <= 0 && j > version.MAXUNPACK+i || i > 0 && j-i > version.MAXUNPACK {
   302  		return nil, object.NewRuntimeError("too many results to unpack")
   303  	}
   304  
   305  	rets := make([]object.Value, 0, j-i+1)
   306  
   307  	for {
   308  		val, err := arith.CallGettable(th, t, object.Integer(i))
   309  		if err != nil {
   310  			return nil, err
   311  		}
   312  
   313  		rets = append(rets, val)
   314  
   315  		if i == j {
   316  			break
   317  		}
   318  
   319  		i++
   320  	}
   321  
   322  	return rets, nil
   323  }
   324  
   325  func Open(th object.Thread, args ...object.Value) ([]object.Value, *object.RuntimeError) {
   326  	m := th.NewTableSize(0, 7)
   327  
   328  	m.Set(object.String("concat"), object.GoFunction(concat))
   329  	m.Set(object.String("insert"), object.GoFunction(insert))
   330  	m.Set(object.String("move"), object.GoFunction(move))
   331  	m.Set(object.String("pack"), object.GoFunction(pack))
   332  	m.Set(object.String("remove"), object.GoFunction(remove))
   333  	m.Set(object.String("sort"), object.GoFunction(sort))
   334  	m.Set(object.String("unpack"), object.GoFunction(unpack))
   335  
   336  	return []object.Value{m}, nil
   337  }