github.com/lmorg/murex@v0.0.0-20240217211045-e081c89cd4ef/builtins/core/mkarray/range.go (about)

     1  package mkarray
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"regexp"
     7  	"strconv"
     8  	"strings"
     9  	"time"
    10  )
    11  
    12  var (
    13  	rxEngAlphaLower = regexp.MustCompile(`^[a-z]$`)
    14  	rxEngAlphaUpper = regexp.MustCompile(`^[A-Z]$`)
    15  	rxAltNumberBase = regexp.MustCompile(`^([a-zA-Z0-9]+)\.\.([a-zA-Z0-9]+)[\.x]([0-9]+)$`)
    16  )
    17  
    18  func rangeToArrayString(b []byte) ([]string, error) {
    19  	split := strings.Split(string(b), "..")
    20  	if len(split) > 2 {
    21  		return nil, fmt.Errorf("invalid syntax. Too many double periods, `..`, in range`%s`. Please escape periods, `\\.`, if you wish to include period in your range", string(b))
    22  	}
    23  
    24  	if len(split) < 2 {
    25  		return nil, fmt.Errorf("invalid syntax. Range periods, `..`, found but cannot determine start and end range in `%s`", string(b))
    26  	}
    27  
    28  	i1, e1 := strconv.Atoi(split[0])
    29  	i2, e2 := strconv.Atoi(split[1])
    30  
    31  	if e1 == nil && e2 == nil {
    32  		switch {
    33  		case i1 < i2:
    34  			a := make([]string, i2-i1+1)
    35  			if split[0][0] != '0' {
    36  				for i := range a {
    37  					a[i] = strconv.Itoa(i + i1)
    38  				}
    39  			} else {
    40  				l := len(split[0])
    41  				s := "%0" + strconv.Itoa(l) + "d"
    42  				for i := range a {
    43  					a[i] = fmt.Sprintf(s, i+i1)
    44  				}
    45  			}
    46  			return a, nil
    47  
    48  		case i1 > i2:
    49  			a := make([]string, i1-i2+1)
    50  			if split[1][0] != '0' {
    51  				for i := range a {
    52  					a[i] = strconv.Itoa(i1 - i)
    53  				}
    54  			} else {
    55  				l := len(split[1])
    56  				s := "%0" + strconv.Itoa(l) + "d"
    57  				for i := range a {
    58  					a[i] = fmt.Sprintf(s, i1-i)
    59  				}
    60  			}
    61  			return a, nil
    62  
    63  		default:
    64  			a := make([]string, 1)
    65  			if split[1][0] != '0' {
    66  				a[0] = strconv.Itoa(i1)
    67  			} else {
    68  				l := len(split[1])
    69  				s := "%0" + strconv.Itoa(l) + "d"
    70  				a[0] = fmt.Sprintf(s, i1)
    71  			}
    72  			return a, nil
    73  		}
    74  	}
    75  
    76  	if rxEngAlphaLower.MatchString(split[0]) && rxEngAlphaLower.MatchString(split[1]) {
    77  		switch {
    78  		case split[0] < split[1]:
    79  			a := make([]string, 0)
    80  			for i := []byte(split[0])[0]; i <= []byte(split[1])[0]; i++ {
    81  				a = append(a, string([]byte{i}))
    82  			}
    83  			return a, nil
    84  		case split[0] > split[1]:
    85  			a := make([]string, 0)
    86  			for i := []byte(split[0])[0]; i >= []byte(split[1])[0]; i-- {
    87  				a = append(a, string([]byte{i}))
    88  			}
    89  			return a, nil
    90  		default:
    91  			return []string{split[0]}, nil
    92  		}
    93  	}
    94  
    95  	if rxEngAlphaUpper.MatchString(split[0]) && rxEngAlphaUpper.MatchString(split[1]) {
    96  		switch {
    97  		case split[0] < split[1]:
    98  			a := make([]string, 0)
    99  			for i := []byte(split[0])[0]; i <= []byte(split[1])[0]; i++ {
   100  				a = append(a, string([]byte{i}))
   101  			}
   102  			return a, nil
   103  		case split[0] > split[1]:
   104  			a := make([]string, 0)
   105  			for i := []byte(split[0])[0]; i >= []byte(split[1])[0]; i-- {
   106  				a = append(a, string([]byte{i}))
   107  			}
   108  			return a, nil
   109  		default:
   110  			return []string{split[0]}, nil
   111  		}
   112  
   113  	}
   114  
   115  	if rxAltNumberBase.Match(b) {
   116  		split = rxAltNumberBase.FindStringSubmatch(string(b))
   117  		base, err := strconv.Atoi(split[3])
   118  		if err != nil {
   119  			return nil, errors.New("unable to determine number base: " + err.Error())
   120  		}
   121  		if base < 2 || base > 36 {
   122  			return nil, errors.New("number base must be between 2 and 36 (inclusive)")
   123  		}
   124  
   125  		i1, err := strconv.ParseInt(split[1], base, 64)
   126  		if err != nil {
   127  			return nil, errors.New("unable to determine start of range: " + err.Error())
   128  		}
   129  
   130  		i2, err := strconv.ParseInt(split[2], base, 64)
   131  		if err != nil {
   132  			return nil, errors.New("unable to determine end of range: " + err.Error())
   133  		}
   134  
   135  		switch {
   136  		case i1 < i2:
   137  			a := make([]string, i2-i1+1)
   138  			if split[0][0] != '0' {
   139  				for i := range a {
   140  					a[i] = strconv.FormatInt(i1+int64(i), base)
   141  				}
   142  			} else {
   143  				l := len(split[1])
   144  				s := "%0" + strconv.Itoa(l) + "s"
   145  				for i := range a {
   146  					a[i] = fmt.Sprintf(s, strconv.FormatInt(i1+int64(i), base))
   147  				}
   148  			}
   149  			return a, nil
   150  
   151  		case i1 > i2:
   152  			a := make([]string, i1-i2+1)
   153  			if split[1][0] != '0' {
   154  				for i := range a {
   155  					a[i] = strconv.FormatInt(i1-int64(i), base)
   156  				}
   157  			} else {
   158  				l := len(split[1])
   159  				s := "%0" + strconv.Itoa(l) + "s"
   160  				for i := range a {
   161  					a[i] = fmt.Sprintf(s, strconv.FormatInt(i1-int64(i), base))
   162  				}
   163  			}
   164  			return a, nil
   165  		default:
   166  			a := make([]string, 1)
   167  			if split[1][0] != '0' {
   168  				a[0] = strconv.FormatInt(i1, base)
   169  			} else {
   170  				l := len(split[1])
   171  				s := "%0" + strconv.Itoa(l) + "s"
   172  				a[0] = fmt.Sprintf(s, strconv.FormatInt(i1, base))
   173  			}
   174  			return a, nil
   175  		}
   176  	}
   177  
   178  	var t1, t2 time.Time
   179  	var now bool
   180  	for i := range dateFormat {
   181  		t1, e1 = time.Parse(dateFormat[i], split[0])
   182  		if e1 == nil || (len(split[0]) == 0 && len(split[1]) > 0) {
   183  			t2, e2 = time.Parse(dateFormat[i], split[1])
   184  
   185  			if e2 == nil || (len(split[0]) > 0 && len(split[1]) == 0) {
   186  				var c int
   187  				switch {
   188  				case len(split[0]) == 0:
   189  					t1, e1 = time.Parse(dateFormat[i], time.Now().Format(dateFormat[i]))
   190  					if e1 != nil {
   191  						return nil, e1
   192  					}
   193  					c = getCase(split[1])
   194  					now = true
   195  				case len(split[1]) == 0:
   196  					t2, e2 = time.Parse(dateFormat[i], time.Now().Format(dateFormat[i]))
   197  					if e2 != nil {
   198  						return nil, e2
   199  					}
   200  					c = getCase(split[0])
   201  					now = true
   202  				default:
   203  					c = getCase(split[0])
   204  				}
   205  
   206  				switch {
   207  				case now && t1.Format(dateFormat[i]) == t2.Format(dateFormat[i]):
   208  					return []string{setCase(t1.Format(dateFormat[i]), c)}, nil
   209  				case t1.Before(t2):
   210  					a := []string{setCase(t1.Format(dateFormat[i]), c)}
   211  					for t1.Before(t2) {
   212  						t1 = t1.AddDate(0, 0, 1)
   213  						a = append(a, setCase(t1.Format(dateFormat[i]), c))
   214  					}
   215  					return a, nil
   216  
   217  				case t1.After(t2):
   218  					a := []string{setCase(t1.Format(dateFormat[i]), c)}
   219  					for t1.After(t2) {
   220  						t1 = t1.AddDate(0, 0, -1)
   221  						a = append(a, setCase(t1.Format(dateFormat[i]), c))
   222  					}
   223  					return a, nil
   224  
   225  				default:
   226  					return []string{setCase(t1.Format(dateFormat[i]), c)}, nil
   227  					//return nil, fmt.Errorf("invalid range. Start and end of range are the same in `%s`", string(b))
   228  				}
   229  			}
   230  		}
   231  	}
   232  
   233  	// Mapped lists. See consts.go
   234  	c := getCase(split[0])
   235  	start := strings.ToLower(split[0])
   236  	end := strings.ToLower(split[1])
   237  	for i := range mapRanges {
   238  		matched, array := mapArray(mapRanges[i][start], mapRanges[i][end], mapRanges[i], c)
   239  		if matched {
   240  			return array, nil
   241  		}
   242  	}
   243  
   244  	return nil, fmt.Errorf("unable to auto-detect range in `%s`", string(b))
   245  }
   246  
   247  func rangeToArrayNumber(b []byte) ([]int, bool, error) {
   248  	split := strings.Split(string(b), "..")
   249  	if len(split) > 2 {
   250  		return nil, false, fmt.Errorf("invalid syntax. Too many double periods, `..`, in range`%s`. Please escape periods, `\\.`, if you wish to include period in your range", string(b))
   251  	}
   252  
   253  	if len(split) < 2 {
   254  		return nil, false, fmt.Errorf("invalid syntax. Range periods, `..`, found but cannot determine start and end range in `%s`", string(b))
   255  	}
   256  
   257  	if len(split[0]) > 1 && split[0][0] == '0' || len(split[1]) > 1 && split[1][0] == '0' {
   258  		return nil, false, nil
   259  	}
   260  
   261  	i1, e1 := strconv.Atoi(split[0])
   262  	i2, e2 := strconv.Atoi(split[1])
   263  
   264  	if e1 == nil && e2 == nil {
   265  		switch {
   266  		case i1 < i2:
   267  			array := make([]int, i2-i1+1)
   268  			for i := range array {
   269  				array[i] = i + i1
   270  			}
   271  			return array, true, nil
   272  
   273  		case i1 > i2:
   274  			array := make([]int, i1-i2+1)
   275  			for i := range array {
   276  				array[i] = i1 - i
   277  			}
   278  			return array, true, nil
   279  
   280  		default:
   281  			array := make([]int, 1)
   282  			array[0] = i1
   283  			return array, true, nil
   284  		}
   285  	}
   286  
   287  	return nil, false, nil
   288  }
   289  
   290  func mapArray(start, end int, constMap map[string]int, c int) (matched bool, array []string) {
   291  	if start == 0 || end == 0 {
   292  		return
   293  	}
   294  
   295  	matched = true
   296  
   297  	consts := make([]string, len(constMap))
   298  	for s, i := range constMap {
   299  		consts[i-1] = setCase(s, c)
   300  	}
   301  
   302  	switch {
   303  	case start < end:
   304  		array = consts[start-1 : end]
   305  
   306  	case start >= end:
   307  		array = consts[start-1:]
   308  		array = append(array, consts[:end]...)
   309  	}
   310  
   311  	return
   312  }