|
|
@@ -3,17 +3,20 @@ module ranges
|
|
|
|
import strconv
|
|
|
|
import strconv
|
|
|
|
import math.big
|
|
|
|
import math.big
|
|
|
|
|
|
|
|
|
|
|
|
struct Range[T] {
|
|
|
|
@[noinit]
|
|
|
|
limit T
|
|
|
|
pub struct Range[T] {
|
|
|
|
|
|
|
|
pub:
|
|
|
|
|
|
|
|
start T
|
|
|
|
|
|
|
|
end T
|
|
|
|
step T
|
|
|
|
step T
|
|
|
|
is_neg bool
|
|
|
|
is_neg bool // Is set to true if range end value is lesser than start value.
|
|
|
|
mut:
|
|
|
|
mut:
|
|
|
|
cur T
|
|
|
|
cur T
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// next returns the new element from range or none if range end is reached.
|
|
|
|
// next returns the new element from range or none if range end is reached.
|
|
|
|
pub fn (mut r Range[T]) next() ?T {
|
|
|
|
pub fn (mut r Range[T]) next() ?T {
|
|
|
|
if (r.is_neg && r.cur < r.limit) || (!r.is_neg && r.cur > r.limit) {
|
|
|
|
if (r.is_neg && r.cur < r.end) || (!r.is_neg && r.cur > r.end) {
|
|
|
|
return none
|
|
|
|
return none
|
|
|
|
}
|
|
|
|
}
|
|
|
|
defer {
|
|
|
|
defer {
|
|
|
@@ -22,6 +25,21 @@ pub fn (mut r Range[T]) next() ?T {
|
|
|
|
return r.cur
|
|
|
|
return r.cur
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// reset resets the internal iterator state to its initial value, after which the iterator can be reused.
|
|
|
|
|
|
|
|
// Note: `for i in iter {` does not modify the internal iterator state, but direct `next()` call does.
|
|
|
|
|
|
|
|
pub fn (mut r Range[T]) reset() {
|
|
|
|
|
|
|
|
r.cur = r.start
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// with_step returns copy of the range with new step value.
|
|
|
|
|
|
|
|
pub fn (r Range[T]) with_step[T](step T) Range[T] {
|
|
|
|
|
|
|
|
return Range[T]{
|
|
|
|
|
|
|
|
...r
|
|
|
|
|
|
|
|
step: step
|
|
|
|
|
|
|
|
cur: r.start
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// range creates new Range iterator with given start, end and step values.
|
|
|
|
// range creates new Range iterator with given start, end and step values.
|
|
|
|
//
|
|
|
|
//
|
|
|
|
// Generally numbers are expected. If type is a struct the following operators
|
|
|
|
// Generally numbers are expected. If type is a struct the following operators
|
|
|
@@ -33,7 +51,8 @@ pub fn (mut r Range[T]) next() ?T {
|
|
|
|
// Note: Zero step value will cause an infitite loop!
|
|
|
|
// Note: Zero step value will cause an infitite loop!
|
|
|
|
pub fn range[T](start T, end T, step T) Range[T] {
|
|
|
|
pub fn range[T](start T, end T, step T) Range[T] {
|
|
|
|
return Range[T]{
|
|
|
|
return Range[T]{
|
|
|
|
limit: end
|
|
|
|
start: start
|
|
|
|
|
|
|
|
end: end
|
|
|
|
step: step
|
|
|
|
step: step
|
|
|
|
cur: start
|
|
|
|
cur: start
|
|
|
|
is_neg: start > end
|
|
|
|
is_neg: start > end
|
|
|
@@ -52,28 +71,28 @@ pub:
|
|
|
|
// Use from_string_custom if you want to use custom type with special string
|
|
|
|
// Use from_string_custom if you want to use custom type with special string
|
|
|
|
// convertion rules.
|
|
|
|
// convertion rules.
|
|
|
|
//
|
|
|
|
//
|
|
|
|
// Supported string formats are `start-end`, `start[:step]end`. start and end
|
|
|
|
// Supported string formats are `start-end[/step]` and `start[:step]:end`. start
|
|
|
|
// values are sepatared by 'sep' which is hypen (`-`) by default. Single number
|
|
|
|
// and end values are sepatared by 'sep' which is hypen (`-`) by default. Single
|
|
|
|
// will be interpreted as range of one element. Several ranges can be specified
|
|
|
|
// number will be interpreted as range of one element. Several ranges can be
|
|
|
|
// in a line, separated by 'group_sep' (comma by default). 'sep' and 'group_sep'
|
|
|
|
// specified in a line, separated by 'group_sep' (comma by default). 'sep' and
|
|
|
|
// can be overrided by user.
|
|
|
|
// 'group_sep' can be overrided by user.
|
|
|
|
//
|
|
|
|
//
|
|
|
|
// Some example input strings:
|
|
|
|
// Some example input strings:
|
|
|
|
//
|
|
|
|
//
|
|
|
|
// * `5` - range from 5 to 5 (single element).
|
|
|
|
// * `5` - range from 5 to 5 (single element).
|
|
|
|
// * `0-10` - range from 0 to 10.
|
|
|
|
// * `0-10` - range from 0 to 10.
|
|
|
|
|
|
|
|
// * `0-100/5` - range in Cron-style syntax from 0 to 100 with step 5.
|
|
|
|
// * `15:-1:0` - range in MathLab-style syntax from 15 to 0 with negative step -1.
|
|
|
|
// * `15:-1:0` - range in MathLab-style syntax from 15 to 0 with negative step -1.
|
|
|
|
// * `1..8` - range from 1 to 8 with '..' sep.
|
|
|
|
// * `1..8` - range from 1 to 8 with '..' sep.
|
|
|
|
// * `0-7,64-71` - multiple ranges: from 0 to 7 and from 64 to 71.
|
|
|
|
// * `0-7,64-71` - multiple ranges: from 0 to 7 and from 64 to 71.
|
|
|
|
//
|
|
|
|
//
|
|
|
|
// Only MathLab-style syntax allows you to specify a step directly in the string.
|
|
|
|
// If the step value is not specified, it will be set to one.
|
|
|
|
// For all other cases, the step is equal to one.
|
|
|
|
|
|
|
|
//
|
|
|
|
//
|
|
|
|
// Example: assert ranges.from_string[int]('1-7')! == [ranges.range(1, 7, 1)]
|
|
|
|
// Example: assert ranges.from_string[int]('1-7')! == [ranges.range(1, 7, 1)]
|
|
|
|
pub fn from_string[T](s string, config RangeFromStringConfig) ![]Range[T] {
|
|
|
|
pub fn from_string[T](s string, config RangeFromStringConfig) ![]Range[T] {
|
|
|
|
mut result := []Range[T]{}
|
|
|
|
mut result := []Range[T]{}
|
|
|
|
for i in s.split(config.group_sep) {
|
|
|
|
for i in s.split(config.group_sep) {
|
|
|
|
range_str := parse_string(i, config.sep)!
|
|
|
|
range_str := split_string(i, config.sep)!
|
|
|
|
// vfmt off
|
|
|
|
// vfmt off
|
|
|
|
result << range[T](
|
|
|
|
result << range[T](
|
|
|
|
convert_string[T](range_str[0])!,
|
|
|
|
convert_string[T](range_str[0])!,
|
|
|
@@ -116,7 +135,7 @@ pub type StringConvertFn[T] = fn (s string) !T
|
|
|
|
pub fn from_string_custom[T](s string, conv StringConvertFn[T], config RangeFromStringConfig) ![]Range[T] {
|
|
|
|
pub fn from_string_custom[T](s string, conv StringConvertFn[T], config RangeFromStringConfig) ![]Range[T] {
|
|
|
|
mut result := []Range[T]{}
|
|
|
|
mut result := []Range[T]{}
|
|
|
|
for i in s.split(config.group_sep) {
|
|
|
|
for i in s.split(config.group_sep) {
|
|
|
|
range_str := parse_string(i, config.sep)!
|
|
|
|
range_str := split_string(i, config.sep)!
|
|
|
|
start := conv[T](range_str[0])!
|
|
|
|
start := conv[T](range_str[0])!
|
|
|
|
end := conv[T](range_str[1])!
|
|
|
|
end := conv[T](range_str[1])!
|
|
|
|
step := conv[T](range_str[2])!
|
|
|
|
step := conv[T](range_str[2])!
|
|
|
@@ -125,7 +144,7 @@ pub fn from_string_custom[T](s string, conv StringConvertFn[T], config RangeFrom
|
|
|
|
return result
|
|
|
|
return result
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
fn parse_string(s string, sep string) ![]string {
|
|
|
|
fn split_string(s string, sep string) ![]string {
|
|
|
|
parts := s.split(sep)
|
|
|
|
parts := s.split(sep)
|
|
|
|
if parts.any(|x| x.is_blank()) || parts.len !in [1, 2, 3] {
|
|
|
|
if parts.any(|x| x.is_blank()) || parts.len !in [1, 2, 3] {
|
|
|
|
return error('`start${sep}end` or `start[:step]:end`' +
|
|
|
|
return error('`start${sep}end` or `start[:step]:end`' +
|
|
|
@@ -133,12 +152,19 @@ fn parse_string(s string, sep string) ![]string {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if parts.len == 1 {
|
|
|
|
if parts.len == 1 {
|
|
|
|
return [parts[0], parts[0], '1']
|
|
|
|
return [parts[0], parts[0], '1']
|
|
|
|
} else if parts.len == 2 {
|
|
|
|
}
|
|
|
|
|
|
|
|
if parts.len == 2 {
|
|
|
|
|
|
|
|
if parts[1].contains('/') {
|
|
|
|
|
|
|
|
end, step := parts[1].split_once('/') or { '', '' }
|
|
|
|
|
|
|
|
return [parts[0], end, step]
|
|
|
|
|
|
|
|
}
|
|
|
|
return [parts[0], parts[1], '1']
|
|
|
|
return [parts[0], parts[1], '1']
|
|
|
|
} else if sep == ':' && parts.len == 3 {
|
|
|
|
}
|
|
|
|
|
|
|
|
if sep == ':' && parts.len == 3 {
|
|
|
|
return [parts[0], parts[2], parts[1]]
|
|
|
|
return [parts[0], parts[2], parts[1]]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return error('invalid range string: ${s}')
|
|
|
|
return error('invalid range string: expected `start[${sep}step]${sep}end` ' +
|
|
|
|
|
|
|
|
'or `start${sep}end[/step]` format, got `${s}`')
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
fn convert_string[T](s string) !T {
|
|
|
|
fn convert_string[T](s string) !T {
|
|
|
|