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

     1  package utf8
     2  
     3  import (
     4  	"unicode/utf8"
     5  
     6  	"github.com/hirochachacha/plua/object"
     7  	"github.com/hirochachacha/plua/object/fnutil"
     8  )
     9  
    10  func char(th object.Thread, args ...object.Value) ([]object.Value, *object.RuntimeError) {
    11  	if len(args) == 0 {
    12  		return []object.Value{object.String("")}, nil
    13  	}
    14  
    15  	ap := fnutil.NewArgParser(th, args)
    16  
    17  	rs := make([]rune, len(args))
    18  
    19  	for i := range args {
    20  		i64, err := ap.ToGoInt64(i)
    21  		if err != nil {
    22  			return nil, err
    23  		}
    24  
    25  		if i64 > utf8.MaxRune {
    26  			return nil, ap.ArgError(i, "value out of range")
    27  		}
    28  
    29  		rs[i] = rune(i64)
    30  	}
    31  
    32  	return []object.Value{object.String(string(rs))}, nil
    33  }
    34  
    35  func nextcode(th object.Thread, args ...object.Value) ([]object.Value, *object.RuntimeError) {
    36  	ap := fnutil.NewArgParser(th, args)
    37  
    38  	s, err := ap.ToGoString(0)
    39  	if err != nil {
    40  		return nil, err
    41  	}
    42  
    43  	off, err := ap.ToGoInt(1)
    44  	if err != nil {
    45  		return nil, err
    46  	}
    47  
    48  	if off >= len(s) {
    49  		return nil, nil
    50  	}
    51  
    52  	if off <= 0 {
    53  		r, _ := utf8.DecodeRuneInString(s)
    54  		if r == utf8.RuneError {
    55  			return nil, object.NewRuntimeError("invalid UTF-8 code")
    56  		}
    57  
    58  		return []object.Value{object.Integer(1), object.Integer(r)}, nil
    59  	}
    60  
    61  	r, rsize := utf8.DecodeRuneInString(s[off-1:])
    62  	if r == utf8.RuneError {
    63  		return nil, object.NewRuntimeError("invalid UTF-8 code")
    64  	}
    65  
    66  	off += rsize
    67  
    68  	if off > len(s) {
    69  		return nil, nil
    70  	}
    71  
    72  	r, _ = utf8.DecodeRuneInString(s[off-1:])
    73  	if r == utf8.RuneError {
    74  		return nil, object.NewRuntimeError("invalid UTF-8 code")
    75  	}
    76  
    77  	return []object.Value{object.Integer(off), object.Integer(r)}, nil
    78  }
    79  
    80  func codes(th object.Thread, args ...object.Value) ([]object.Value, *object.RuntimeError) {
    81  	ap := fnutil.NewArgParser(th, args)
    82  
    83  	s, err := ap.ToString(0)
    84  	if err != nil {
    85  		return nil, err
    86  	}
    87  
    88  	return []object.Value{object.GoFunction(nextcode), s, object.Integer(0)}, nil
    89  }
    90  
    91  func codepoint(th object.Thread, args ...object.Value) ([]object.Value, *object.RuntimeError) {
    92  	ap := fnutil.NewArgParser(th, args)
    93  
    94  	s, err := ap.ToGoString(0)
    95  	if err != nil {
    96  		return nil, err
    97  	}
    98  
    99  	i, err := ap.OptGoInt(1, 1)
   100  	if err != nil {
   101  		return nil, err
   102  	}
   103  	if i < 0 {
   104  		i = len(s) + 1 + i
   105  	}
   106  	if i <= 0 {
   107  		return nil, ap.ArgError(1, "out of range")
   108  	}
   109  
   110  	j, err := ap.OptGoInt(2, i)
   111  	if err != nil {
   112  		return nil, err
   113  	}
   114  	if j < 0 {
   115  		j = len(s) + 1 + j
   116  	}
   117  	if j > len(s) {
   118  		return nil, ap.ArgError(2, "out of range")
   119  	}
   120  
   121  	if i > j {
   122  		return nil, nil
   123  	}
   124  
   125  	var rets []object.Value
   126  
   127  	for {
   128  		r, k := utf8.DecodeRuneInString(s[i-1:])
   129  
   130  		if r == utf8.RuneError {
   131  			return nil, object.NewRuntimeError("invalid UTF-8 code")
   132  		}
   133  
   134  		rets = append(rets, object.Integer(r))
   135  
   136  		i += k
   137  
   138  		if i > j {
   139  			break
   140  		}
   141  	}
   142  
   143  	return rets, nil
   144  }
   145  
   146  func _len(th object.Thread, args ...object.Value) ([]object.Value, *object.RuntimeError) {
   147  	ap := fnutil.NewArgParser(th, args)
   148  
   149  	s, err := ap.ToGoString(0)
   150  	if err != nil {
   151  		return nil, err
   152  	}
   153  
   154  	i, err := ap.OptGoInt(1, 1)
   155  	if err != nil {
   156  		return nil, err
   157  	}
   158  	if i < 0 {
   159  		i = len(s) + 1 + i
   160  	}
   161  	if i <= 0 || i > len(s)+1 {
   162  		return nil, ap.ArgError(1, "out of range")
   163  	}
   164  
   165  	j, err := ap.OptGoInt(2, -1)
   166  	if err != nil {
   167  		return nil, err
   168  	}
   169  	if j < 0 {
   170  		j = len(s) + 1 + j
   171  	}
   172  	if j >= len(s)+1 {
   173  		return nil, ap.ArgError(2, "out of range")
   174  	}
   175  
   176  	var n int
   177  	for i <= j {
   178  		r, rsize := utf8.DecodeRuneInString(s[i-1:])
   179  		if r == utf8.RuneError {
   180  			return []object.Value{object.False, object.Integer(i)}, nil
   181  		}
   182  		i += rsize
   183  		n++
   184  	}
   185  
   186  	return []object.Value{object.Integer(n)}, nil
   187  }
   188  
   189  func offset(th object.Thread, args ...object.Value) ([]object.Value, *object.RuntimeError) {
   190  	ap := fnutil.NewArgParser(th, args)
   191  
   192  	s, err := ap.ToGoString(0)
   193  	if err != nil {
   194  		return nil, err
   195  	}
   196  
   197  	n, err := ap.ToGoInt(1)
   198  	if err != nil {
   199  		return nil, err
   200  	}
   201  	i := 1
   202  	if n < 0 {
   203  		i = len(s) + 1
   204  	}
   205  
   206  	i, err = ap.OptGoInt(2, i)
   207  	if err != nil {
   208  		return nil, err
   209  	}
   210  	if i < 0 {
   211  		i = len(s) + 1 + i
   212  	}
   213  	if i <= 0 || i > len(s)+1 {
   214  		return nil, ap.ArgError(2, "position out of range")
   215  	}
   216  
   217  	var r rune
   218  	var rsize int
   219  
   220  	switch {
   221  	case n < 0:
   222  		if i != len(s)+1 && !utf8.RuneStart(s[i-1]) {
   223  			return nil, object.NewRuntimeError("initial position is a continuation byte")
   224  		}
   225  		for n < 0 && i > 0 {
   226  			r, rsize = utf8.DecodeLastRuneInString(s[:i-1])
   227  			if r == utf8.RuneError {
   228  				break
   229  			}
   230  			i -= rsize
   231  			n++
   232  		}
   233  	case n > 0:
   234  		if i != len(s)+1 && !utf8.RuneStart(s[i-1]) {
   235  			return nil, object.NewRuntimeError("initial position is a continuation byte")
   236  		}
   237  		n--
   238  		for n > 0 && i < len(s)+1 {
   239  			r, rsize = utf8.DecodeRuneInString(s[i-1:])
   240  			if r == utf8.RuneError {
   241  				break
   242  			}
   243  			i += rsize
   244  			n--
   245  		}
   246  	default:
   247  		if len(s) == i-1 {
   248  			return []object.Value{object.Integer(i)}, nil
   249  		}
   250  		for i > 0 && !utf8.RuneStart(s[i-1]) {
   251  			i--
   252  		}
   253  	}
   254  
   255  	if n == 0 {
   256  		return []object.Value{object.Integer(i)}, nil
   257  	}
   258  
   259  	return nil, nil
   260  }
   261  
   262  func Open(th object.Thread, args ...object.Value) ([]object.Value, *object.RuntimeError) {
   263  	m := th.NewTableSize(0, 6)
   264  
   265  	m.Set(object.String("charpattern"), object.String("[\x00-\x7F\xC2-\xF4][\x80-\xBF]*"))
   266  
   267  	m.Set(object.String("char"), object.GoFunction(char))
   268  	m.Set(object.String("codes"), object.GoFunction(codes))
   269  	m.Set(object.String("codepoint"), object.GoFunction(codepoint))
   270  	m.Set(object.String("len"), object.GoFunction(_len))
   271  	m.Set(object.String("offset"), object.GoFunction(offset))
   272  
   273  	return []object.Value{m}, nil
   274  }