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 }