Brace expansion using ranges: Difference between revisions

→‎{{header|jq}}: right-most-first
(→‎{{header|jq}}: right-most-first)
 
(34 intermediate revisions by 12 users not shown)
Line 1:
{{draft task}}{{clarified-review}}
 
;Task
{{task heading}}
 
Write and test a function which expands one or more Unix-style '''numeric and alphabetic range braces''' embedded in a larger string.<br><br>
Line 54:
{{task heading|Tests}}
 
Generate and display here the expansion of (at least) each of the nineten example lines shown abovebelow.
 
The JavaScript implementation below uses parser combinators, aiming to encode a more or less full and legible description of the <pre><PREAMBLE><AMBLE><POSTSCRIPT></pre> range brace grammar, but you should use any resource that suggests itself in your language, including parser libraries.
Line 60:
(The grammar of range expansion, unlike that of nested list expansion, is not recursive, so even regular expressions should prove serviceable here).
 
The output of the JS implementation, which aims to match the brace expansion behaviour of the default '''zsh''' shell on macOScurrent Catalinaversions isof macOS:
 
<pre>simpleNumberRising{1..3}.txt ->
Line 81:
minusSignFlipsSequence 025.txt
minusSignFlipsSequence 030.txt
 
reverseSteppedNumberRising{1..6..-2}.txt ->
reverseSteppedNumberRising5.txt
reverseSteppedNumberRising3.txt
reverseSteppedNumberRising1.txt
 
combined-{Q..P}{2..1}.txt ->
Line 103 ⟶ 108:
rangeless{random}string</pre>
 
{{Template:Strings}}
;Related tasks:
* &nbsp; [[Range_expansion|range expansion]]
 
:* &nbsp; [[Brace_expansion]]
:* &nbsp; [[Range_expansion]]
<br><br>
=={{header|11l}}==
{{trans|Nim}}
 
<syntaxhighlight lang="11l">F intFromString(s) -> Int?
X.try
R Int(s)
X.catch ValueError
R N
 
F parseRange(r)
I r.empty {R [‘{}’]}
V sp = r.split(‘..’)
I sp.len == 1 {R [‘{’r‘}’]}
V first = sp[0]
V last = sp[1]
V incr = I sp.len == 2 {‘1’} E sp[2]
 
Int? val1 = intFromString(first)
Int? val2 = intFromString(last)
Int? val3 = intFromString(incr)
 
I val3 == N {R [‘{’r‘}’]}
V n3 = val3
V numeric = val1 != N & val2 != N
 
Int n1
Int n2
I numeric
n1 = val1
n2 = val2
E
I (val1 != N & val2 == N) | (val1 == N & val2 != N)
R [‘{’r‘}’]
I first.len != 1 | last.len != 1
R [‘{’r‘}’]
n1 = first[0].code
n2 = last[0].code
 
V width = 1
I numeric
width = max(first.len, last.len)
 
I n3 == 0
R I numeric {[String(n1).zfill(width)]} E [first]
 
V asc = n1 < n2
I n3 < 0
asc = !asc
swap(&n1, &n2)
n3 = -n3
 
[String] result
V i = n1
I asc
L i <= n2
result.append(I numeric {String(i).zfill(width)} E Char(code' i))
i += n3
E
L i >= n2
result.append(I numeric {String(i).zfill(width)} E Char(code' i))
i -= n3
R result
 
F rangeExpand(s)
V result = [‘’]
V rng = ‘’
V inRng = 0B
 
L(c) s
I c == ‘{’ & !inRng
inRng = 1B
rng = ‘’
E I c == ‘}’ & inRng
V rngRes = parseRange(rng)
[String] res
L(r) result
L(rr) rngRes
res.append(r‘’rr)
result = move(res)
inRng = 0B
E I inRng
rng ‘’= c
E
L(&s) result
s ‘’= c
 
I inRng
L(&s) result
s ‘’= ‘{’rng
R result
 
-V examples = [‘simpleNumberRising{1..3}.txt’,
‘simpleAlphaDescending-{Z..X}.txt’,
‘steppedDownAndPadded-{10..00..5}.txt’,
‘minusSignFlipsSequence {030..20..-5}.txt’,
‘combined-{Q..P}{2..1}.txt’,
‘li{teral’,
‘rangeless{}empty’,
‘rangeless{random}string’,
‘mixedNumberAlpha{5..k}’,
‘steppedAlphaRising{P..Z..2}.txt’,
‘stops after endpoint-{02..10..3}.txt’]
 
L(s) examples
print(s" ->\n ", end' ‘’)
V res = rangeExpand(s)
print(res.join("\n "))
print()</syntaxhighlight>
 
{{out}}
<pre>
simpleNumberRising{1..3}.txt ->
simpleNumberRising1.txt
simpleNumberRising2.txt
simpleNumberRising3.txt
 
simpleAlphaDescending-{Z..X}.txt ->
simpleAlphaDescending-Z.txt
simpleAlphaDescending-Y.txt
simpleAlphaDescending-X.txt
 
steppedDownAndPadded-{10..00..5}.txt ->
steppedDownAndPadded-10.txt
steppedDownAndPadded-05.txt
steppedDownAndPadded-00.txt
 
minusSignFlipsSequence {030..20..-5}.txt ->
minusSignFlipsSequence 020.txt
minusSignFlipsSequence 025.txt
minusSignFlipsSequence 030.txt
 
combined-{Q..P}{2..1}.txt ->
combined-Q2.txt
combined-Q1.txt
combined-P2.txt
combined-P1.txt
 
li{teral ->
li{teral
 
rangeless{}empty ->
rangeless{}empty
 
rangeless{random}string ->
rangeless{random}string
 
mixedNumberAlpha{5..k} ->
mixedNumberAlpha{5..k}
 
steppedAlphaRising{P..Z..2}.txt ->
steppedAlphaRisingP.txt
steppedAlphaRisingR.txt
steppedAlphaRisingT.txt
steppedAlphaRisingV.txt
steppedAlphaRisingX.txt
steppedAlphaRisingZ.txt
 
stops after endpoint-{02..10..3}.txt ->
stops after endpoint-02.txt
stops after endpoint-05.txt
stops after endpoint-08.txt
 
</pre>
=={{header|AutoHotkey}}==
<syntaxhighlight lang="autohotkey">Brace_expansion_using_ranges(line){
needle := "^.*\K{(?P<Start>[^{}]+?)\..(?P<End>[^{}]+?)(?:\..(?P<Incr>[^{}]+?))?}"
while true
{
while pos := RegExMatch(line, needle, m, A_Index=1?1:pos+StrLen(m))
{
char := false, step := "", output := ""
reverse := InStr(mIncr, "-") ? true : false
if mStart is number
pad1 := pad(mStart), pad2 := pad(mEnd), pad := StrLen(pad1)>=StrLen(pad2) ? pad1 : pad2
else
mStart := Ord(mStart), mEnd := Ord(mEnd), char := true
mIncr := (mIncr?Abs(mIncr):1) * (mStart>mEnd?-1:1)
loop % Abs((mStart-mEnd)/mIncr) + 1
{
step := mStart + (A_Index-1) * mIncr
step := pad <> "" ? SubStr(pad . step, 1-StrLen(pad)) : step
step := char ? Chr(step) : step
Rep := StrReplace(line, m, step)
output := reverse ? rep "`n" output : output .= Rep "`n"
}
output := Trim(Output, "`n")
}
if RegExMatch(output, needle)
line := output
else
break
}
return output ? output : line
}
pad(num){
if RegExMatch(num, "`am)^(0+)(?=[1-9]|0$)", m)
loop % StrLen(num)
pad .= "0"
return pad
}</syntaxhighlight>
Examples:<syntaxhighlight lang="autohotkey">data=
(
simpleNumberRising{1..3}.txt
simpleAlphaDescending-{Z..X}.txt
steppedDownAndPadded-{10..00..5}.txt
minusSignFlipsSequence {030..20..-5}.txt
reverseSteppedNumberRising{1..6..-2}.txt
combined-{Q..P}{2..1}.txt
emoji{🌵..🌶}{🌽..🌾}etc
li{teral
rangeless{}empty
rangeless{random}string
)
for i, line in StrSplit(data, "`n", "`r")
result .= line " ->`n" RegExReplace(Brace_expansion_using_ranges(line), "`am)^", "`t") "`n`n"
 
MsgBox, 262144, , % result
return</syntaxhighlight>
{{out}}
<pre>simpleNumberRising{1..3}.txt ->
simpleNumberRising1.txt
simpleNumberRising2.txt
simpleNumberRising3.txt
 
simpleAlphaDescending-{Z..X}.txt ->
simpleAlphaDescending-Z.txt
simpleAlphaDescending-Y.txt
simpleAlphaDescending-X.txt
 
steppedDownAndPadded-{10..00..5}.txt ->
steppedDownAndPadded-10.txt
steppedDownAndPadded-05.txt
steppedDownAndPadded-00.txt
 
minusSignFlipsSequence {030..20..-5}.txt ->
minusSignFlipsSequence 020.txt
minusSignFlipsSequence 025.txt
minusSignFlipsSequence 030.txt
 
reverseSteppedNumberRising{1..6..-2}.txt ->
reverseSteppedNumberRising5.txt
reverseSteppedNumberRising3.txt
reverseSteppedNumberRising1.txt
 
combined-{Q..P}{2..1}.txt ->
combined-Q2.txt
combined-Q1.txt
combined-P2.txt
combined-P1.txt
 
emoji{🌵..🌶}{🌽..🌾}etc ->
emoji🌵🌽etc
emoji🌵🌾etc
emoji🌶🌽etc
emoji🌶🌾etc
 
li{teral ->
li{teral
 
rangeless{}empty ->
rangeless{}empty
 
rangeless{random}string ->
rangeless{random}string</pre>
=={{header|F_Sharp|F#}}==
<syntaxhighlight lang="fsharp">
// Brace expansion using ranges. Nigel Galloway: October 6th., 2021
let fUC, fUR=System.Text.Rune.GetUnicodeCategory,(fun n->System.Text.Rune.GetRuneAt(n,0))
let fV(n,i,g,e,l,s)=let l=if l="" then 1 else int l in match l with 0->None |_->Some(n,i,g,e,int l,s)
let(|Valid|_|)(n:System.Text.RegularExpressions.Match)=let fN(g:string)=n.Groups.[g].Value in if n.Success then fV(fN "n",fN "i",fN "g",fN "e",fN "l",fN "s") else None
let fN(g:string)=let mutable g=g.EnumerateRunes() in if g.MoveNext() && not(g.MoveNext()) then true else false
let(|I|_|)(n,g)=if fN n && fN g then (let n,g=fUR n,fUR g in if fUC n=fUC g then Some(n,g) else None) else None
let(|G|_|)(n:string,g:string)=try let n,g=(int n,int g) in Some(n,g) with _->None
let(|E|_|)(n:string,g:string)=if n.[0]='0' || g.[0]='0' then match (n,g) with G(e,l)->Some(e,l,max n.Length g.Length) |_->None else None
let fL n=let fN i g e l=let n=[i..(if i>g then -l else l)..g] in if e="-" then List.rev n else n
let fG n g=let n,buf=string n, System.Text.StringBuilder() in (for _ in 1..g-n.Length do buf.Append 0); buf.Append n; buf.ToString()
match System.Text.RegularExpressions.Regex.Match(n,@"^(?<n>.*?){(?<i>.*?)\.\.(?<g>.*?)(\.\.(?<e>[-]+)?(?<l>[0-9]*?))?}(?<s>.*)$") with
Valid(n,i,g,e,l,s)->match (i,g) with I(i,g)->Some(fN i.Value g.Value e l|>Seq.map(fun g->sprintf "%s%A%s" n (System.Text.Rune(g)) s))
|E(i,g,z)->Some(fN i g e l|>Seq.map(fun g->sprintf "%s%s%s" n (fG g z) s))
|G(i,g)->Some(fN i g e l|>Seq.map(fun g->sprintf "%s%A%s" n (string g) s)) |_->None
|_->None
let rec expBraces n=seq{match fL n with Some n->yield!(n|>Seq.collect(expBraces)) |_->yield n}
let tests=["simpleNumberRising{1..3}.txt";"steppedNumberRising{1..6..2}.txt";"reverseSteppedNumberRising{1..6..-2}.txt";"steppedNumberDescending{20..9..2}.txt";"simpleAlphaDescending-{Z..X}.txt";"steppedDownAndPadded-{10..00..5}.txt";"minusSignFlipsSequence {030..20..-5}.txt";"combined-{Q..P}{2..1}.txt";"emoji{🌵..🌶}{🌽..🌾}etc";"li{teral";"rangeless{random}string";"rangeless{}empty";"steppedAlphaDescending-{Z..M..2}.txt";"reversedSteppedAlphaDescending-{Z..M..-2}.txt"]
tests|>List.iter(fun g->printfn $"%s{g}->"; for n in expBraces g do printfn $" %s{n}")
</syntaxhighlight>
{{out}}
<pre>
simpleNumberRising{1..3}.txt->
simpleNumberRising1.txt
simpleNumberRising2.txt
simpleNumberRising3.txt
steppedNumberRising{1..6..2}.txt->
steppedNumberRising1.txt
steppedNumberRising3.txt
steppedNumberRising5.txt
reverseSteppedNumberRising{1..6..-2}.txt->
reverseSteppedNumberRising5.txt
reverseSteppedNumberRising3.txt
reverseSteppedNumberRising1.txt
steppedNumberDescending{20..9..2}.txt->
steppedNumberDescending"20".txt
steppedNumberDescending"18".txt
steppedNumberDescending"16".txt
steppedNumberDescending"14".txt
steppedNumberDescending"12".txt
steppedNumberDescending"10".txt
simpleAlphaDescending-{Z..X}.txt->
simpleAlphaDescending-Z.txt
simpleAlphaDescending-Y.txt
simpleAlphaDescending-X.txt
steppedDownAndPadded-{10..00..5}.txt->
steppedDownAndPadded-10.txt
steppedDownAndPadded-05.txt
steppedDownAndPadded-00.txt
minusSignFlipsSequence {030..20..-5}.txt->
minusSignFlipsSequence 020.txt
minusSignFlipsSequence 025.txt
minusSignFlipsSequence 030.txt
combined-{Q..P}{2..1}.txt->
combined-Q2.txt
combined-Q1.txt
combined-P2.txt
combined-P1.txt
emoji{🌵..🌶}{🌽..🌾}etc->
emoji🌵🌽etc
emoji🌵🌾etc
emoji🌶🌽etc
emoji🌶🌾etc
li{teral->
li{teral
rangeless{random}string->
rangeless{random}string
rangeless{}empty->
rangeless{}empty
steppedAlphaDescending-{Z..M..2}.txt->
steppedAlphaDescending-Z.txt
steppedAlphaDescending-X.txt
steppedAlphaDescending-V.txt
steppedAlphaDescending-T.txt
steppedAlphaDescending-R.txt
steppedAlphaDescending-P.txt
steppedAlphaDescending-N.txt
reversedSteppedAlphaDescending-{Z..M..-2}.txt->
reversedSteppedAlphaDescending-N.txt
reversedSteppedAlphaDescending-P.txt
reversedSteppedAlphaDescending-R.txt
reversedSteppedAlphaDescending-T.txt
reversedSteppedAlphaDescending-V.txt
reversedSteppedAlphaDescending-X.txt
reversedSteppedAlphaDescending-Z.txt
</pre>
=={{header|Go}}==
{{trans|Wren}}
<langsyntaxhighlight lang="go">package main
 
import (
Line 119 ⟶ 474:
"unicode/utf8"
)
 
func sign(n int) int {
switch {
case n < 0:
return -1
case n > 0:
return 1
}
return 0
}
 
func abs(n int) int {
if n < 0 {
return -n
}
return n
}
 
func parseRange(r string) []string {
Line 170 ⟶ 542:
if n3 < 0 {
asc = !asc
n1, n2t := n2, n1
d := abs(n1-n2) % (-n3)
n1 = n2 - d*sign(n2-n1)
n2 = t
n3 = -n3
}
Line 235 ⟶ 610:
"steppedDownAndPadded-{10..00..5}.txt",
"minusSignFlipsSequence {030..20..-5}.txt",
"reverseSteppedNumberRising{1..6..-2}.txt",
"combined-{Q..P}{2..1}.txt",
"emoji{🌵..🌶}{🌽..🌾}etc",
Line 243 ⟶ 619:
"steppedAlphaRising{P..Z..2}.txt",
"stops after endpoint-{02..10..3}.txt",
"steppedNumberRising{1..6..2}.txt",
"steppedNumberDescending{20..9..2}",
"steppedAlphaDescending-{Z..M..2}.txt",
"reversedSteppedAlphaDescending-{Z..M..-2}.txt",
}
for _, s := range examples {
Line 250 ⟶ 630:
fmt.Println()
}
}</langsyntaxhighlight>
 
{{out}}
Line 256 ⟶ 636:
Same as Wren entry.
</pre>
 
=={{header|JavaScript}}==
<langsyntaxhighlight lang="javascript">(() => {
'"use strict'";
 
// --------------- BRACE-RANGE EXPANSION ---------------
 
// braceExpandWithRange :: String -> [String]
Line 271 ⟶ 650:
braceRangeExpansion()
))(s);
 
return 0 < expansions.length ? (() => {
const [parsed, residue] = Array.from(expansions[0];
 
expansions[0]
);
return suffixAdd(
parsed.reduce(
uncurry(suffixMultiply),
[''""]
)
)([residue.join(''"")]);
})() : [s];
};
 
 
// ----------- BRACE-RANGE EXPANSION PARSER ------------
 
// braceRangeExpansion :: [String]
Line 300 ⟶ 679:
affixLeaf(),
fmapP(xs => [xs])(
between(char('"{'"))(char('"}'"))(
altP(
numericSequence()
Line 312 ⟶ 691:
 
 
// ----------------------- TESTS -----------------------
// main :: IO ()
const main = () => {
const tests = [
'"simpleNumberRising{1..3}.txt'",
'"simpleAlphaDescending-{Z..X}.txt'",
'"steppedDownAndPadded-{10..00..5}.txt'",
'"minusSignFlipsSequence {030..20..-5}.txt'",
'combined-"reverseSteppedNumberRising{Q1..P}{26..1-2}.txt'",
'emoji"combined-{🌵Q..🌶P}{🌽2..🌾1}etc'.txt",
'li"emoji{teral'🌵..🌶}{🌽..🌾}etc",
'rangeless"li{}empty'teral",
'"rangeless{random}string'empty",
"rangeless{random}string"
];
 
return tests.map(s => {
s => s + ' -> ' + '\n\t' + (const
expanded = braceExpandWithRange(s).join('\n\t')
.join("\n\t");
 
).join('\n\n');
return `${s} -> \n\t${expanded}`;
})
.join("\n\n");
};
 
 
// ----------- BRACE-RANGE COMPONENT PARSERS -----------
 
// affixLeaf :: () -> Parser String
Line 342 ⟶ 725:
// characters before or after a pair of braces.
fmapP(cs => [
[cs.join(''"")]
])(
many(choice([noneOf('"{\\'"), escape()]))
);
 
Line 354 ⟶ 737:
fmapP(ab => {
const [from, to] = ab;
 
return from !== to ? (
enumFromThenToChar(from)(
Line 361 ⟶ 745:
})(
ordinalRange(satisfy(
c => !'"0123456789'".includes(c)
))
);
Line 377 ⟶ 761:
[from, to, by] = triple.map(
sn => (sn[0] ? negate : identity)(
parseInt(sn[1], 10)
)
);
 
return map(
compose(justifyRight(w)('"0'"), str)
)(
0 > by ? (
enumFromThenTo(to)(to0 -> by) ? (from)
) : enumFromThenTo(from)( reverse
from + (to >= from ? by) : -by)identity
)(to)
enumFromThenTo(from)(
from + (
to < from ? (
-abs(by)
) : abs(by)
)
)(to)
)
);
};
Line 398 ⟶ 791:
// The String component contains the digits.
bindP(
option(''"")(char('"-'"))
)(sign => bindP(
some(digit())
Line 424 ⟶ 817:
option(Tuple(false)(1))(
bindP(
string('"..'")
)(_() => bindP(
numericPart()
)(pureP))
Line 440 ⟶ 833:
p
)(from => bindP(
string('"..'")
)(_() => bindP(
p
)(compose(pureP, append([from])))));
Line 454 ⟶ 847:
(a, x) => (0 < a) || (1 > x.length) ? (
a
) : '"0'" !== x[0] ? a : x.length,
0
);
Line 471 ⟶ 864:
 
 
// ------------ GENERIC PARSER COMBINATORS -------------
 
// Parser :: String -> [(a, String)] -> Parser a
Line 477 ⟶ 870:
// A function lifted into a Parser object.
({
type: '"Parser'",
parser: f
});
Line 487 ⟶ 880:
q => Parser(s => {
const xs = parse(p)(s);
 
return 0 < xs.length ? (
xs
Line 500 ⟶ 894:
p => Parser(
s => parse(pf)(s).flatMap(
vr([v, r]) => parse(
fmapP(vr[0]v)(p)
)(vr[1]r)
)
);
Line 514 ⟶ 908:
pClose => p => bindP(
pOpen
)(_() => bindP(
p
)(x => bindP(
pClose
)(_() => pureP(x))));
 
 
Line 531 ⟶ 925:
f => Parser(
s => parse(p)(s).flatMap(
tpl([x, r]) => parse(f(tpl[0]x))(tpl[1]r)
)
);
Line 539 ⟶ 933:
const char = x =>
// A particular single character.
satisfy(c => x === c);
 
 
Line 558 ⟶ 952:
const emptyP = () =>
// The empty list.
Parser(_() => []);
 
 
// escape :: Parser String
const escape = () =>
fmapP(xs => xs.join(''""))(
sequenceP([char('"\\'"), item()])
);
 
Line 574 ⟶ 968:
p => Parser(
s => parse(p)(s).flatMap(
vr => Tuplefirst(f(vr[0]))(vr[1])
)
);
Line 582 ⟶ 976:
const item = () =>
// A single character.
Parser(s => {
sconst =>[h, 0...t] <= s.length ? [;
 
Tuple(s[0])(
return Boolean(h) ? s.slice(1)[
Tuple(h)(t)
] : [];
});
 
 
Line 604 ⟶ 998:
// Lifts a parser for a simple type of value
// to a parser for a list of such values.
const some_psomeP = pq =>
liftA2P(
x => xs => [x].concat(xs)
)(pq)(many(pq));
 
return Parser(
s => parse(
0 < s.length ? (
altP(some_psomeP(p))(pureP([]))
) : pureP([])
)(s)
Line 634 ⟶ 1,029:
const parse = p =>
// The result of parsing s with p.
s => {p.parser([...s]);
//showLog('s', s)
return p.parser([...s]);
};
 
 
Line 651 ⟶ 1,043:
// Any character for which the
// given predicate returns true.
Parser(s => {
sconst =>[h, 0...t] <= s.length ? (;
 
test(s[0]) ? [
return Boolean(h) ? Tuple(s[0])(s.slice(1))
test(h) ? [
Tuple(h)(t)
] : []
) : [];
});
 
 
// sepBy1 :: Parser a -> Parser b -> Parser [a]
const sepBy1 = p =>
// One or more occurrences of p, as
// separated by (discarded) instances of sep.
sep => bindP(
p
)(x => bindP(
many(bindP(
sep
)(_ => bindP(
p
)(pureP))))(
xs => pureP([x].concat(xs))));
 
 
// sequenceP :: [Parser a] -> Parser [a]
Line 682 ⟶ 1,060:
s => ps.reduce(
(a, q) => a.flatMap(
vr([v, r]) => parse(q)(snd(vr)r).flatMap(
first(xs => fst(vr)v.concat(xs))
)
),
Line 696 ⟶ 1,074:
// Lifts a parser for a simple type of value
// to a parser for a list of such values.
const many_pmanyP = pq =>
altP(some(pq))(pureP([]));
 
return Parser(
s => parse(
liftA2P(
x => xs => [x].concat(xs)
)(p)(many_pmanyP(p))
)(s)
);
Line 711 ⟶ 1,090:
const string = s =>
// A particular string.
fmapP(cs => cs.join(''""))(
sequenceP([...s].map(char))
);
 
 
// ----------------- GENERAL FUNCTIONS -----------------
 
// Tuple (,) :: a -> b -> (a, b)
const Tuple = a =>
b => ({
type: '"Tuple'",
'"0'": a,
'"1'": b,
length: 2,
*[Symbol.iterator]() {
for (const k in this) {
if (!isNaN(k)) {
yield this[k];
}
}
}
});
 
 
// abs :: Num -> Num
const abs =
// Absolute value of a given number
// without the sign.
x => 0 > x ? (
-x
) : x;
 
 
Line 755 ⟶ 1,150:
 
// concat :: [[a]] -> [a]
//const concat ::= [String]xs -=> String
const concat = xs => .flat(1);
ys => 0 < ys.length ? (
ys.every(Array.isArray) ? (
[]
) : ''
).concat(...ys) : ys
)(list(xs));
 
 
// enumFromThenTo :: Int -> Int -> Int -> [Int]
const enumFromThenTo = x1m =>
x2// =>Integer yvalues =>enumerated {from m to n
// with a step constdefined dby = x2(nxt - x1;m).
nxt => n => {
const d = nxt - m;
 
return Array.from({
length: (Math.floor(yn - x2nxt) / d) + 2
}, (_, i) => x1m + (d * i));
};
 
Line 781 ⟶ 1,173:
.map(x => x.codePointAt(0)),
d = i2 - i1;
 
return Array.from({
length: (Math.floor(iY - i2) / d) + 2
Line 791 ⟶ 1,184:
// A simple function lifted to one which applies
// to a tuple, transforming only its first item.
xy([x, y]) => Tuple(f(xy[0]x))(y);
xy[1]
);
 
 
// flip :: (a -> b -> c) -> b -> a -> c
const flip = op =>
// The binary function op with
// its arguments reversed.
1 < op.length ? (
(a, b) => op(b, a)
) : (x => y => op(y)(x));
 
 
// fst :: (a, b) -> a
const fst = tpl =>
// First member of a pair.
tpl[0];
 
 
// fromEnum :: Enum a => a -> Int
const fromEnum = x =>
typeof x !== '"string'" ? (
x.constructor === Object ? (
x.value
) : parseInt(Number(x), 10)
) : x.codePointAt(0);
 
Line 824 ⟶ 1,209:
// The identity function. (`id`, in Haskell)
x;
 
 
// isAlpha :: Char -> Bool
const isAlpha = c =>
/[A-Za-z\u00C0-\u00FF]/.test(c);
 
 
Line 834 ⟶ 1,214:
const isDigit = c => {
const n = c.codePointAt(0);
 
return 48 <= n && 57 >= n;
};
Line 845 ⟶ 1,226:
s.padStart(n, c)
) : s;
 
 
// list :: StringOrArrayLike b => b -> [a]
const list = xs =>
// xs itself, if it is an Array,
// or an Array derived from xs.
Array.isArray(xs) ? (
xs
) : Array.from(xs || []);
 
 
Line 867 ⟶ 1,239:
const maxBound = x => {
const e = x.enum;
 
return Boolean(e) ? (
e[e[x.max]]
) : {
'"number'": Number.MAX_SAFE_INTEGER,
'"string'": String.fromCodePoint(0x10FFFF),
'"boolean'": true
} [typeof x];
};
Line 879 ⟶ 1,252:
const minBound = x => {
const e = x.enum;
 
return Boolean(e) ? (
e[e[0]]
) : {
'"number'": Number.MIN_SAFE_INTEGER,
'"string'": String.fromCodePoint(0),
'"boolean'": false
} [typeof x];
};
Line 897 ⟶ 1,271:
const pred = x => {
const t = typeof x;
 
return 'number' !== t ? (() => {
return "number" !== t ? (() => {
const [i, mn] = [x, minBound(x)].map(fromEnum);
 
return i > mn ? (
toEnum(x)(i - 1)
) : Error('"succ :: enum out of range.'");
})() : x > Number.MIN_SAFE_INTEGER ? (
x - 1
) : Error('"succ :: Num out of range.'");
};
 
 
// showLogreverse :: [a] -> IO ()[a]
const showLogreverse = (...args)xs =>
consolexs.logslice(0).reverse();
args
.map(JSON.stringify)
.join(' -> ')
);
 
 
// snd :: (a, b) -> b
const snd = tpl =>
// Second member of a pair.
tpl[1];
 
 
Line 926 ⟶ 1,292:
const str = x =>
Array.isArray(x) && x.every(
v => ('"string'" === typeof v) && (1 === v.length)
) ? (
x.join(''"")
) : x.toString();
 
Line 935 ⟶ 1,301:
const succ = x => {
const t = typeof x;
 
return 'number' !== t ? (() => {
return "number" !== t ? (
const [i, mx] = [x, maxBound(x)].map(fromEnum);
return(() i=> < mx ? ({
toEnumconst [i, mx] = [x, maxBound(x)].map(1 + i)
) : Error('succ :: enum out of range.') fromEnum
})() : x < Number.MAX_SAFE_INTEGER ? ( );
 
return i < mx ? (
toEnum(x)(1 + i)
) : Error("succ :: enum out of range.");
})()
) : x < Number.MAX_SAFE_INTEGER ? (
1 + x
) : Error('"succ :: Num out of range.'");
};
 
Line 951 ⟶ 1,323:
// allowing the function to make the right mapping
x => ({
'"number'": Number,
'"string'": String.fromCodePoint,
'"boolean'": Boolean,
'"object'": v => e.min + v
} [typeof e])(x);
 
Line 962 ⟶ 1,334:
// A function over a pair, derived
// from a curried function.
function (...args) => {
const
args = arguments,
xy = Boolean(args.length % 2) ? (
args[0]
) : args;
 
return f(xy[0])(xy[1]);
};
Line 973 ⟶ 1,345:
// MAIN ---
return main();
})();</langsyntaxhighlight>
{{Out}}
<pre>simpleNumberRising{1..3}.txt ->
Line 994 ⟶ 1,366:
minusSignFlipsSequence 025.txt
minusSignFlipsSequence 030.txt
 
reverseSteppedNumberRising{1..6..-2}.txt ->
reverseSteppedNumberRising5.txt
reverseSteppedNumberRising3.txt
reverseSteppedNumberRising1.txt
 
combined-{Q..P}{2..1}.txt ->
Line 1,015 ⟶ 1,392:
rangeless{random}string ->
rangeless{random}string</pre>
=={{header|jq}}==
'''Works with jq, the C implementation of jq'''
 
'''Works with gojq, the Go implementation of jq'''
 
This implementation relies on "reluctant" regex parsing.
 
Range expressions of the form {x..y}, where x and y are single
characters, are allowed, even if exactly one of them is a digit.
 
When expanding an expression with more than one range, the program
as given below produces an ordering
based on expansion of the left-most range first.
A trivial change in two places is sufficient to produce the alternative ordering.
<syntaxhighlight lang="jq">
# Left-pad with 0s
def lpad($len): tostring | ($len - length) as $l | ("0" * $l) + .;
 
def expand:
# The key to success here is reluctance (".*?")
def cap:
capture("(?<head>^.*?)[{](?<from>[0-9]+|.)[.][.](?<to>[0-9]+|.)"
+ "([.][.](?<sign>-)?(?<increment>[0-9]))?[}](?<tail>.*)$");
 
def ton: if . == null then . else tonumber end;
 
# Produce a stream of integers, handling implicit descent.
# $i and $j should be integers.
# If $i and $j are distinct, then expand($i;$j;null;null) will include both,
# otherwise just $i.
def expand($i; $j; $sign; $increment):
(if $increment == null then 1 else $increment end) as $inc
| if $sign == null
then if $i <= $j
then range($i; $j + 1; $inc)
else range($i; $j - 1; - $inc)
end
else [expand($i; $j; null; $increment)] | reverse[]
end ;
 
# Produce a stream of single characters, handling implicit descent
def explode($x; $y; $sign; $increment):
($x|explode[0]) as $x
| ($y|explode[0]) as $y
| expand($x; $y; $sign; $increment)
| [.] | implode;
 
# The number of leading 0s of the input string
def leadingZeros: match("^0*") | .string | length;
def padding($x; $y):
($x | leadingZeros) as $a
| ($y | leadingZeros) as $b
| [if $a > 0 then ($x|length) else 0 end,
if $b > 0 then ($y|length) else 0 end]
| max;
( cap as $c
| if ($c.from|test("[0-9]+")) and ($c.to|test("[0-9]+"))
then padding($c.from; $c.to) as $padding
| $c.head
+ ( expand($c.from|tonumber;
$c.to|tonumber;
$c.sign;
$c.increment | ton) | lpad($padding))
+ ($c.tail | expand)
elif ($c.from|length == 1) and ($c.to|length == 1)
then $c.head + explode($c.from; $c.to; $c.sign; $c.increment|ton)
+ ($c.tail | expand)
else ""
end )
// . ;
 
def examples:
"simpleNumberRising{1..3}.txt",
"simpleAlphaDescending-{Z..X}.txt",
"steppedDownAndPadded-{10..00..5}.txt",
"minusSignFlipsSequence {030..20..-5}.txt",
"reverseSteppedNumberRising{1..6..-2}.txt",
"combined-{Q..P}{2..1}.txt",
"emoji{🌵..🌶}{🌽..🌾}etc",
"li{teral",
"rangeless{}empty",
"rangeless{random}string",
"mixedNumberAlpha{5..k}",
"steppedAlphaRising{P..Z..2}.txt",
"stops after endpoint-{02..10..3}.txt",
"steppedNumberRising{1..6..2}.txt",
"steppedNumberDescending{20..9..2}",
"steppedAlphaDescending-{Z..M..2}.txt",
"reversedSteppedAlphaDescending-{Z..M..-2}.txt"
;
 
examples
| "\(.) ->",
" \(expand)", ""
</syntaxhighlight>
{{output}}
<pre style="height:20lh;overflow:auto>
simpleNumberRising{1..3}.txt ->
simpleNumberRising1.txt
simpleNumberRising2.txt
simpleNumberRising3.txt
 
simpleAlphaDescending-{Z..X}.txt ->
simpleAlphaDescending-Z.txt
simpleAlphaDescending-Y.txt
simpleAlphaDescending-X.txt
 
steppedDownAndPadded-{10..00..5}.txt ->
steppedDownAndPadded-10.txt
steppedDownAndPadded-05.txt
steppedDownAndPadded-00.txt
 
minusSignFlipsSequence {030..20..-5}.txt ->
minusSignFlipsSequence 020.txt
minusSignFlipsSequence 025.txt
minusSignFlipsSequence 030.txt
 
reverseSteppedNumberRising{1..6..-2}.txt ->
reverseSteppedNumberRising5.txt
reverseSteppedNumberRising3.txt
reverseSteppedNumberRising1.txt
 
combined-{Q..P}{2..1}.txt ->
combined-Q2.txt
combined-P2.txt
combined-Q1.txt
combined-P1.txt
 
emoji{🌵..🌶}{🌽..🌾}etc ->
emoji🌵🌽etc
emoji🌶🌽etc
emoji🌵🌾etc
emoji🌶🌾etc
 
li{teral ->
li{teral
 
rangeless{}empty ->
rangeless{}empty
 
rangeless{random}string ->
rangeless{random}string
 
mixedNumberAlpha{5..k} ->
mixedNumberAlpha5
mixedNumberAlpha6
mixedNumberAlpha7
mixedNumberAlpha8
mixedNumberAlpha9
mixedNumberAlpha:
mixedNumberAlpha;
mixedNumberAlpha<
mixedNumberAlpha=
mixedNumberAlpha>
mixedNumberAlpha?
mixedNumberAlpha@
mixedNumberAlphaA
mixedNumberAlphaB
mixedNumberAlphaC
mixedNumberAlphaD
mixedNumberAlphaE
mixedNumberAlphaF
mixedNumberAlphaG
mixedNumberAlphaH
mixedNumberAlphaI
mixedNumberAlphaJ
mixedNumberAlphaK
mixedNumberAlphaL
mixedNumberAlphaM
mixedNumberAlphaN
mixedNumberAlphaO
mixedNumberAlphaP
mixedNumberAlphaQ
mixedNumberAlphaR
mixedNumberAlphaS
mixedNumberAlphaT
mixedNumberAlphaU
mixedNumberAlphaV
mixedNumberAlphaW
mixedNumberAlphaX
mixedNumberAlphaY
mixedNumberAlphaZ
mixedNumberAlpha[
mixedNumberAlpha\
mixedNumberAlpha]
mixedNumberAlpha^
mixedNumberAlpha_
mixedNumberAlpha`
mixedNumberAlphaa
mixedNumberAlphab
mixedNumberAlphac
mixedNumberAlphad
mixedNumberAlphae
mixedNumberAlphaf
mixedNumberAlphag
mixedNumberAlphah
mixedNumberAlphai
mixedNumberAlphaj
mixedNumberAlphak
 
steppedAlphaRising{P..Z..2}.txt ->
steppedAlphaRisingP.txt
steppedAlphaRisingR.txt
steppedAlphaRisingT.txt
steppedAlphaRisingV.txt
steppedAlphaRisingX.txt
steppedAlphaRisingZ.txt
 
stops after endpoint-{02..10..3}.txt ->
stops after endpoint-02.txt
stops after endpoint-05.txt
stops after endpoint-08.txt
 
steppedNumberRising{1..6..2}.txt ->
steppedNumberRising1.txt
steppedNumberRising3.txt
steppedNumberRising5.txt
 
steppedNumberDescending{20..9..2} ->
steppedNumberDescending20
steppedNumberDescending18
steppedNumberDescending16
steppedNumberDescending14
steppedNumberDescending12
steppedNumberDescending10
 
steppedAlphaDescending-{Z..M..2}.txt ->
steppedAlphaDescending-Z.txt
steppedAlphaDescending-X.txt
steppedAlphaDescending-V.txt
steppedAlphaDescending-T.txt
steppedAlphaDescending-R.txt
steppedAlphaDescending-P.txt
steppedAlphaDescending-N.txt
 
reversedSteppedAlphaDescending-{Z..M..-2}.txt ->
reversedSteppedAlphaDescending-N.txt
reversedSteppedAlphaDescending-P.txt
reversedSteppedAlphaDescending-R.txt
reversedSteppedAlphaDescending-T.txt
reversedSteppedAlphaDescending-V.txt
reversedSteppedAlphaDescending-X.txt
reversedSteppedAlphaDescending-Z.txt
</pre>
 
=={{header|Julia}}==
<syntaxhighlight lang="julia">padzeros(str) = (len = length(str)) > 1 && str[1] == '0' ? len : 0
Julia currently does not handle certain trigraphs in strings correctly, such as 🌶, so that test is omitted.
<lang julia>padzeros(str) = (len = length(str)) > 1 && str[1] == '0' ? len : 0
 
function ranged(str)
rang = filter(!isempty, split(str[2:end-1], r"\{|\}|\.\."))
delta = length(rang) > 2 ? parse(Int, rang[3]) : 1
if delta < 0
Line 1,049 ⟶ 1,671:
"minusSignFlipsSequence {030..20..-5}.txt",
"combined-{Q..P}{2..1}.txt",
"emoji{🌵..🌶}{🌽..🌾}etc",
"li{teral",
"rangeless{}empty",
Line 1,058 ⟶ 1,681:
println(test, "->\n", [" " * x * "\n" for x in splatrange(test)]...)
end
</langsyntaxhighlight>{{out}}
<pre>
simpleNumberRising{1..3}.txt->
Line 1,085 ⟶ 1,708:
combined-P2.txt
combined-P1.txt
 
emoji{🌵..🌶}{🌽..🌾}etc->
emoji🌵🌽etc
emoji🌵🌾etc
emoji🌶🌽etc
emoji🌶🌾etc
 
li{teral->
Line 1,112 ⟶ 1,741:
</pre>
 
=={{header|Nim}}==
{{trans|Wren}}
<syntaxhighlight lang="nim">import options, strutils, unicode
 
 
func intFromString(s: string): Option[int] =
## Try to parse an int. Return some(int) if parsing
## was successful, return none(int) if it failed.
try:
let n = s.parseInt()
result = some(n)
except ValueError:
result = none(int)
 
 
func parseRange(r: string): seq[string] =
 
if r.len == 0: return @["{}"] # rangeless, empty.
let sp = r.split("..")
if sp.len == 1: return @['{' & r & '}']
let first = sp[0]
let last = sp[1]
let incr = if sp.len == 2: "1" else: sp[2]
 
let val1 = intFromString(first)
let val2 = intFromString(last)
let val3 = intFromString(incr)
 
if val3.isNone(): return @['{' & r & '}'] # increment isn't a number.
var n3 = val3.get()
let numeric = val1.isSome and val2.isSome
 
var n1, n2: int
if numeric:
n1 = val1.get()
n2 = val2.get()
else:
if val1.isSome and val2.isNone or val1.isNone and val2.isSome:
return @['{' & r & '}'] # mixed numeric/alpha not expanded.
if first.runeLen != 1 or last.runeLen != 1:
return @['{' & r & '}'] # start/end are not both single alpha.
n1 = first.toRunes[0].int
n2 = last.toRunes[0].int
 
var width = 1
if numeric:
width = if first.len < last.len: last.len else: first.len
 
if n3 == 0:
# Zero increment.
return if numeric: @[n1.intToStr(width)] else: @[first]
 
var asc = n1 < n2
if n3 < 0:
asc = not asc
swap n1, n2
n3 = -n3
 
var i = n1
if asc:
while i <= n2:
result.add if numeric: i.intToStr(width) else: $Rune(i)
inc i, n3
else:
while i >= n2:
result.add if numeric: i.intToStr(width) else: $Rune(i)
dec i, n3
 
 
func rangeExpand(s: string): seq[string] =
result = @[""]
var rng = ""
var inRng = false
 
for c in s:
if c == '{' and not inRng:
inRng = true
rng = ""
elif c == '}' and inRng:
let rngRes = rng.parseRange()
var res: seq[string]
for i in 0..result.high:
for j in 0..rngRes.high:
res.add result[i] & rngRes[j]
result = move(res)
inRng = false
elif inRng:
rng.add c
else:
for s in result.mitems: s.add c
 
if inRng:
for s in result.mitems: s.add '{' & rng # unmatched braces.
 
 
when isMainModule:
 
const Examples = ["simpleNumberRising{1..3}.txt",
"simpleAlphaDescending-{Z..X}.txt",
"steppedDownAndPadded-{10..00..5}.txt",
"minusSignFlipsSequence {030..20..-5}.txt",
"combined-{Q..P}{2..1}.txt",
"emoji{🌵..🌶}{🌽..🌾}etc",
"li{teral",
"rangeless{}empty",
"rangeless{random}string",
"mixedNumberAlpha{5..k}",
"steppedAlphaRising{P..Z..2}.txt",
"stops after endpoint-{02..10..3}.txt"]
 
for s in Examples:
stdout.write s, " →\n "
let res = rangeExpand(s)
stdout.write res.join("\n ")
echo '\n'
</syntaxhighlight>
 
{{out}}
<pre>simpleNumberRising{1..3}.txt →
simpleNumberRising1.txt
simpleNumberRising2.txt
simpleNumberRising3.txt
 
simpleAlphaDescending-{Z..X}.txt →
simpleAlphaDescending-Z.txt
simpleAlphaDescending-Y.txt
simpleAlphaDescending-X.txt
 
steppedDownAndPadded-{10..00..5}.txt →
steppedDownAndPadded-10.txt
steppedDownAndPadded-05.txt
steppedDownAndPadded-00.txt
 
minusSignFlipsSequence {030..20..-5}.txt →
minusSignFlipsSequence 020.txt
minusSignFlipsSequence 025.txt
minusSignFlipsSequence 030.txt
 
combined-{Q..P}{2..1}.txt →
combined-Q2.txt
combined-Q1.txt
combined-P2.txt
combined-P1.txt
 
emoji{🌵..🌶}{🌽..🌾}etc →
emoji🌵🌽etc
emoji🌵🌾etc
emoji🌶🌽etc
emoji🌶🌾etc
 
li{teral →
li{teral
 
rangeless{}empty →
rangeless{}empty
 
rangeless{random}string →
rangeless{random}string
 
mixedNumberAlpha{5..k} →
mixedNumberAlpha{5..k}
 
steppedAlphaRising{P..Z..2}.txt →
steppedAlphaRisingP.txt
steppedAlphaRisingR.txt
steppedAlphaRisingT.txt
steppedAlphaRisingV.txt
steppedAlphaRisingX.txt
steppedAlphaRisingZ.txt
 
stops after endpoint-{02..10..3}.txt →
stops after endpoint-02.txt
stops after endpoint-05.txt
stops after endpoint-08.txt
</pre>
=={{header|Phix}}==
{{trans|Wren}}
<!--<syntaxhighlight lang="phix">(phixonline)-->
<span style="color: #7060A8;">requires</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"0.8.2"</span><span style="color: #0000FF;">)</span> <span style="color: #000080;font-style:italic;">-- (is_integer() is new, plus "==sign(inc)" found me a long-buried compiler bug)</span>
<span style="color: #008080;">function</span> <span style="color: #000000;">parse_range</span><span style="color: #0000FF;">(</span><span style="color: #004080;">string</span> <span style="color: #000000;">r</span><span style="color: #0000FF;">)</span>
<span style="color: #004080;">sequence</span> <span style="color: #000000;">sp</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">split</span><span style="color: #0000FF;">(</span><span style="color: #000000;">r</span><span style="color: #0000FF;">,</span><span style="color: #008000;">".."</span><span style="color: #0000FF;">)&{</span><span style="color: #008000;">"1"</span><span style="color: #0000FF;">},</span>
<span style="color: #000000;">res</span> <span style="color: #0000FF;">=</span> <span style="color: #0000FF;">{}</span>
<span style="color: #008080;">if</span> <span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">sp</span><span style="color: #0000FF;">)>=</span><span style="color: #000000;">3</span> <span style="color: #008080;">then</span>
<span style="color: #004080;">string</span> <span style="color: #0000FF;">{</span><span style="color: #000000;">strange</span><span style="color: #0000FF;">,</span><span style="color: #000000;">ending</span><span style="color: #0000FF;">,</span><span style="color: #000000;">step</span><span style="color: #0000FF;">}</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">sp</span>
<span style="color: #004080;">integer</span> <span style="color: #000000;">inc</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">to_integer</span><span style="color: #0000FF;">(</span><span style="color: #000000;">step</span><span style="color: #0000FF;">)</span>
<span style="color: #008080;">if</span> <span style="color: #000000;">inc</span><span style="color: #0000FF;">!=</span><span style="color: #000000;">0</span> <span style="color: #008080;">then</span>
<span style="color: #004080;">bool</span> <span style="color: #000000;">ns</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">is_integer</span><span style="color: #0000FF;">(</span><span style="color: #000000;">strange</span><span style="color: #0000FF;">),</span>
<span style="color: #000000;">ne</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">is_integer</span><span style="color: #0000FF;">(</span><span style="color: #000000;">ending</span><span style="color: #0000FF;">)</span>
<span style="color: #008080;">if</span> <span style="color: #000000;">ns</span><span style="color: #0000FF;">=</span><span style="color: #000000;">ne</span> <span style="color: #008080;">then</span>
<span style="color: #008080;">if</span> <span style="color: #000000;">ns</span> <span style="color: #008080;">then</span>
<span style="color: #004080;">integer</span> <span style="color: #000000;">s</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">to_integer</span><span style="color: #0000FF;">(</span><span style="color: #000000;">strange</span><span style="color: #0000FF;">),</span>
<span style="color: #000000;">e</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">to_integer</span><span style="color: #0000FF;">(</span><span style="color: #000000;">ending</span><span style="color: #0000FF;">),</span>
<span style="color: #000000;">w</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">max</span><span style="color: #0000FF;">(</span><span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">strange</span><span style="color: #0000FF;">),</span><span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">ending</span><span style="color: #0000FF;">))</span>
<span style="color: #008080;">if</span> <span style="color: #000000;">inc</span><span style="color: #0000FF;"><</span><span style="color: #000000;">0</span> <span style="color: #008080;">then</span> <span style="color: #0000FF;">{</span><span style="color: #000000;">s</span><span style="color: #0000FF;">,</span><span style="color: #000000;">e</span><span style="color: #0000FF;">,</span><span style="color: #000000;">inc</span><span style="color: #0000FF;">}</span> <span style="color: #0000FF;">=</span> <span style="color: #0000FF;">{</span><span style="color: #000000;">e</span><span style="color: #0000FF;">,</span><span style="color: #000000;">s</span><span style="color: #0000FF;">,-</span><span style="color: #000000;">inc</span><span style="color: #0000FF;">}</span> <span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
<span style="color: #008080;">if</span> <span style="color: #000000;">s</span><span style="color: #0000FF;">></span><span style="color: #000000;">e</span> <span style="color: #008080;">then</span> <span style="color: #000000;">inc</span> <span style="color: #0000FF;">*=</span> <span style="color: #0000FF;">-</span><span style="color: #000000;">1</span> <span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
<span style="color: #004080;">integer</span> <span style="color: #000000;">zfill</span> <span style="color: #0000FF;">=</span> <span style="color: #0000FF;">(</span><span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">strange</span><span style="color: #0000FF;">)></span><span style="color: #000000;">1</span> <span style="color: #008080;">and</span> <span style="color: #000000;">strange</span><span style="color: #0000FF;">[</span><span style="color: #000000;">1</span><span style="color: #0000FF;">]=</span><span style="color: #008000;">'0'</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">or</span>
<span style="color: #0000FF;">(</span><span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">ending</span><span style="color: #0000FF;">)></span><span style="color: #000000;">1</span> <span style="color: #008080;">and</span> <span style="color: #000000;">ending</span><span style="color: #0000FF;">[</span><span style="color: #000000;">1</span><span style="color: #0000FF;">]=</span><span style="color: #008000;">'0'</span><span style="color: #0000FF;">)</span>
<span style="color: #004080;">string</span> <span style="color: #000000;">fmt</span> <span style="color: #0000FF;">=</span> <span style="color: #008080;">iff</span><span style="color: #0000FF;">(</span><span style="color: #000000;">zfill</span><span style="color: #0000FF;">?</span><span style="color: #7060A8;">sprintf</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"%%0%dd"</span><span style="color: #0000FF;">,{</span><span style="color: #000000;">w</span><span style="color: #0000FF;">}):</span><span style="color: #008000;">"%d"</span><span style="color: #0000FF;">)</span>
<span style="color: #008080;">for</span> <span style="color: #000000;">k</span><span style="color: #0000FF;">=</span><span style="color: #000000;">s</span> <span style="color: #008080;">to</span> <span style="color: #000000;">e</span> <span style="color: #008080;">by</span> <span style="color: #000000;">inc</span> <span style="color: #008080;">do</span>
<span style="color: #000000;">res</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">append</span><span style="color: #0000FF;">(</span><span style="color: #000000;">res</span><span style="color: #0000FF;">,</span><span style="color: #7060A8;">sprintf</span><span style="color: #0000FF;">(</span><span style="color: #000000;">fmt</span><span style="color: #0000FF;">,</span><span style="color: #000000;">k</span><span style="color: #0000FF;">))</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">for</span>
<span style="color: #008080;">return</span> <span style="color: #000000;">res</span>
<span style="color: #008080;">elsif</span> <span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">strange</span><span style="color: #0000FF;">)=</span><span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">ending</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">then</span>
<span style="color: #004080;">bool</span> <span style="color: #000000;">ok</span> <span style="color: #0000FF;">=</span> <span style="color: #0000FF;">(</span><span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">strange</span><span style="color: #0000FF;">)=</span><span style="color: #000000;">1</span><span style="color: #0000FF;">)</span>
<span style="color: #008080;">if</span> <span style="color: #008080;">not</span> <span style="color: #000000;">ok</span> <span style="color: #008080;">then</span>
<span style="color: #004080;">object</span> <span style="color: #000000;">s32</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">utf8_to_utf32</span><span style="color: #0000FF;">(</span><span style="color: #000000;">strange</span><span style="color: #0000FF;">,-</span><span style="color: #000000;">1</span><span style="color: #0000FF;">),</span>
<span style="color: #000000;">e32</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">utf8_to_utf32</span><span style="color: #0000FF;">(</span><span style="color: #000000;">ending</span><span style="color: #0000FF;">,-</span><span style="color: #000000;">1</span><span style="color: #0000FF;">)</span>
<span style="color: #008080;">if</span> <span style="color: #004080;">sequence</span><span style="color: #0000FF;">(</span><span style="color: #000000;">s32</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">and</span> <span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">s32</span><span style="color: #0000FF;">)=</span><span style="color: #000000;">1</span>
<span style="color: #008080;">and</span> <span style="color: #004080;">sequence</span><span style="color: #0000FF;">(</span><span style="color: #000000;">e32</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">and</span> <span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">e32</span><span style="color: #0000FF;">)=</span><span style="color: #000000;">1</span> <span style="color: #008080;">then</span>
<span style="color: #000000;">ok</span> <span style="color: #0000FF;">=</span> <span style="color: #004600;">true</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
<span style="color: #008080;">if</span> <span style="color: #000000;">ok</span> <span style="color: #008080;">then</span>
<span style="color: #008080;">if</span> <span style="color: #000000;">strange</span><span style="color: #0000FF;">></span><span style="color: #000000;">ending</span> <span style="color: #008080;">then</span> <span style="color: #000000;">inc</span> <span style="color: #0000FF;">*=</span> <span style="color: #0000FF;">-</span><span style="color: #000000;">1</span> <span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
<span style="color: #008080;">while</span> <span style="color: #004600;">true</span> <span style="color: #008080;">do</span>
<span style="color: #000000;">res</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">append</span><span style="color: #0000FF;">(</span><span style="color: #000000;">res</span><span style="color: #0000FF;">,</span><span style="color: #000000;">strange</span><span style="color: #0000FF;">)</span>
<span style="color: #004080;">integer</span> <span style="color: #000000;">sdx</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">strange</span><span style="color: #0000FF;">)</span>
<span style="color: #008080;">while</span> <span style="color: #004600;">true</span> <span style="color: #008080;">do</span>
<span style="color: #004080;">integer</span> <span style="color: #000000;">ch</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">strange</span><span style="color: #0000FF;">[</span><span style="color: #000000;">sdx</span><span style="color: #0000FF;">]+</span><span style="color: #000000;">inc</span>
<span style="color: #008080;">if</span> <span style="color: #000000;">ch</span><span style="color: #0000FF;"><=</span><span style="color: #000000;">#FF</span> <span style="color: #008080;">and</span> <span style="color: #000000;">ch</span><span style="color: #0000FF;">>=</span><span style="color: #000000;">#00</span> <span style="color: #008080;">then</span>
<span style="color: #000000;">strange</span><span style="color: #0000FF;">[</span><span style="color: #000000;">sdx</span><span style="color: #0000FF;">]</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">ch</span>
<span style="color: #008080;">exit</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
<span style="color: #000000;">strange</span><span style="color: #0000FF;">[</span><span style="color: #000000;">sdx</span><span style="color: #0000FF;">]</span> <span style="color: #0000FF;">=</span> <span style="color: #008080;">iff</span><span style="color: #0000FF;">(</span><span style="color: #000000;">inc</span><span style="color: #0000FF;"><</span><span style="color: #000000;">0</span><span style="color: #0000FF;">?</span><span style="color: #000000;">#FF</span><span style="color: #0000FF;">:</span><span style="color: #000000;">#00</span><span style="color: #0000FF;">)</span>
<span style="color: #000000;">sdx</span> <span style="color: #0000FF;">-=</span> <span style="color: #000000;">1</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">while</span>
<span style="color: #008080;">if</span> <span style="color: #7060A8;">compare</span><span style="color: #0000FF;">(</span><span style="color: #000000;">strange</span><span style="color: #0000FF;">,</span><span style="color: #000000;">ending</span><span style="color: #0000FF;">)==</span><span style="color: #7060A8;">sign</span><span style="color: #0000FF;">(</span><span style="color: #000000;">inc</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">then</span> <span style="color: #008080;">exit</span> <span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
<span style="color: #008080;">if</span> <span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">res</span><span style="color: #0000FF;">)></span><span style="color: #000000;">10</span> <span style="color: #008080;">then</span> <span style="color: #0000FF;">?</span><span style="color: #000000;">9</span><span style="color: #0000FF;">/</span><span style="color: #000000;">0</span> <span style="color: #008080;">end</span> <span style="color: #008080;">if</span> <span style="color: #000080;font-style:italic;">-- (sanity check)</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">while</span>
<span style="color: #008080;">return</span> <span style="color: #000000;">res</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span> <span style="color: #000080;font-style:italic;">-- ([utf8] strings not single char)</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span> <span style="color: #000080;font-style:italic;">-- (neither numeric nor same-length alpha)</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span> <span style="color: #000080;font-style:italic;">-- (mixed numeric/alpha)</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span> <span style="color: #000080;font-style:italic;">-- (non-numeric increment)</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span> <span style="color: #000080;font-style:italic;">-- (rangeless)</span>
<span style="color: #008080;">return</span> <span style="color: #0000FF;">{</span><span style="color: #008000;">"{"</span><span style="color: #0000FF;">&</span><span style="color: #000000;">r</span><span style="color: #0000FF;">&</span><span style="color: #008000;">"}"</span><span style="color: #0000FF;">}</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">function</span>
<span style="color: #008080;">function</span> <span style="color: #000000;">range_expand</span><span style="color: #0000FF;">(</span><span style="color: #004080;">string</span> <span style="color: #000000;">s</span><span style="color: #0000FF;">)</span>
<span style="color: #004080;">sequence</span> <span style="color: #000000;">res</span> <span style="color: #0000FF;">=</span> <span style="color: #0000FF;">{</span><span style="color: #008000;">""</span><span style="color: #0000FF;">}</span>
<span style="color: #004080;">string</span> <span style="color: #000000;">range</span> <span style="color: #0000FF;">=</span> <span style="color: #008000;">""</span>
<span style="color: #004080;">bool</span> <span style="color: #000000;">in_range</span> <span style="color: #0000FF;">=</span> <span style="color: #004600;">false</span>
<span style="color: #008080;">for</span> <span style="color: #000000;">k</span><span style="color: #0000FF;">=</span><span style="color: #000000;">1</span> <span style="color: #008080;">to</span> <span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">s</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">do</span>
<span style="color: #004080;">integer</span> <span style="color: #000000;">c</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">s</span><span style="color: #0000FF;">[</span><span style="color: #000000;">k</span><span style="color: #0000FF;">]</span>
<span style="color: #008080;">if</span> <span style="color: #000000;">c</span> <span style="color: #0000FF;">==</span> <span style="color: #008000;">'{'</span> <span style="color: #008080;">and</span> <span style="color: #008080;">not</span> <span style="color: #000000;">in_range</span> <span style="color: #008080;">then</span>
<span style="color: #000000;">in_range</span> <span style="color: #0000FF;">=</span> <span style="color: #004600;">true</span>
<span style="color: #000000;">range</span> <span style="color: #0000FF;">=</span> <span style="color: #008000;">""</span>
<span style="color: #008080;">elsif</span> <span style="color: #000000;">c</span> <span style="color: #0000FF;">==</span> <span style="color: #008000;">'}'</span> <span style="color: #008080;">and</span> <span style="color: #000000;">in_range</span> <span style="color: #008080;">then</span>
<span style="color: #004080;">sequence</span> <span style="color: #000000;">range_res</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">parse_range</span><span style="color: #0000FF;">(</span><span style="color: #000000;">range</span><span style="color: #0000FF;">),</span>
<span style="color: #000000;">prev_res</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">res</span>
<span style="color: #000000;">res</span> <span style="color: #0000FF;">=</span> <span style="color: #0000FF;">{}</span>
<span style="color: #008080;">for</span> <span style="color: #000000;">i</span><span style="color: #0000FF;">=</span><span style="color: #000000;">1</span> <span style="color: #008080;">to</span> <span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">prev_res</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">do</span>
<span style="color: #008080;">for</span> <span style="color: #000000;">j</span><span style="color: #0000FF;">=</span><span style="color: #000000;">1</span> <span style="color: #008080;">to</span> <span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">range_res</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">do</span>
<span style="color: #000000;">res</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">append</span><span style="color: #0000FF;">(</span><span style="color: #000000;">res</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">prev_res</span><span style="color: #0000FF;">[</span><span style="color: #000000;">i</span><span style="color: #0000FF;">]</span> <span style="color: #0000FF;">&</span> <span style="color: #000000;">range_res</span><span style="color: #0000FF;">[</span><span style="color: #000000;">j</span><span style="color: #0000FF;">])</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">for</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">for</span>
<span style="color: #000000;">in_range</span> <span style="color: #0000FF;">=</span> <span style="color: #004600;">false</span>
<span style="color: #008080;">elsif</span> <span style="color: #000000;">in_range</span> <span style="color: #008080;">then</span>
<span style="color: #000000;">range</span> <span style="color: #0000FF;">&=</span> <span style="color: #000000;">c</span>
<span style="color: #008080;">else</span>
<span style="color: #008080;">for</span> <span style="color: #000000;">i</span><span style="color: #0000FF;">=</span><span style="color: #000000;">1</span> <span style="color: #008080;">to</span> <span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">res</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">do</span>
<span style="color: #000000;">res</span><span style="color: #0000FF;">[</span><span style="color: #000000;">i</span><span style="color: #0000FF;">]</span> <span style="color: #0000FF;">&=</span> <span style="color: #000000;">c</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">for</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">for</span>
<span style="color: #008080;">if</span> <span style="color: #000000;">in_range</span> <span style="color: #008080;">then</span>
<span style="color: #008080;">for</span> <span style="color: #000000;">i</span><span style="color: #0000FF;">=</span><span style="color: #000000;">1</span> <span style="color: #008080;">to</span> <span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">res</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">do</span>
<span style="color: #000000;">res</span><span style="color: #0000FF;">[</span><span style="color: #000000;">i</span><span style="color: #0000FF;">]</span> <span style="color: #0000FF;">&=</span> <span style="color: #008000;">"{"</span> <span style="color: #0000FF;">&</span> <span style="color: #000000;">range</span> <span style="color: #000080;font-style:italic;">// unmatched braces</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">for</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
<span style="color: #008080;">return</span> <span style="color: #000000;">res</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">function</span>
<span style="color: #008080;">constant</span> <span style="color: #000000;">examples</span> <span style="color: #0000FF;">=</span> <span style="color: #0000FF;">{</span>
<span style="color: #008000;">"simpleNumberRising{1..3}.txt"</span><span style="color: #0000FF;">,</span>
<span style="color: #008000;">"simpleAlphaDescending-{Z..X}.txt"</span><span style="color: #0000FF;">,</span>
<span style="color: #008000;">"steppedDownAndPadded-{10..00..5}.txt"</span><span style="color: #0000FF;">,</span>
<span style="color: #008000;">"minusSignFlipsSequence {030..20..-5}.txt"</span><span style="color: #0000FF;">,</span>
<span style="color: #008000;">"combined-{Q..P}{2..1}.txt"</span><span style="color: #0000FF;">,</span>
<span style="color: #008000;">"emoji{🌵..🌶}{🌽..🌾}etc"</span><span style="color: #0000FF;">,</span>
<span style="color: #008000;">"multi char emoji ranges fail {🌵🌵..🌵🌶}"</span><span style="color: #0000FF;">,</span>
<span style="color: #008000;">"li{teral"</span><span style="color: #0000FF;">,</span>
<span style="color: #008000;">"rangeless{}empty"</span><span style="color: #0000FF;">,</span>
<span style="color: #008000;">"rangeless{random}string"</span><span style="color: #0000FF;">,</span>
<span style="color: #008000;">"mixedNumberAlpha{5..k}"</span><span style="color: #0000FF;">,</span>
<span style="color: #008000;">"steppedAlphaRising{P..Z..2}.txt"</span><span style="color: #0000FF;">,</span>
<span style="color: #008000;">"stops after endpoint-{02..10..3}.txt"</span>
<span style="color: #0000FF;">}</span>
<span style="color: #008080;">for</span> <span style="color: #000000;">i</span><span style="color: #0000FF;">=</span><span style="color: #000000;">1</span> <span style="color: #008080;">to</span> <span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">examples</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">do</span>
<span style="color: #004080;">string</span> <span style="color: #000000;">s</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">examples</span><span style="color: #0000FF;">[</span><span style="color: #000000;">i</span><span style="color: #0000FF;">]</span>
<span style="color: #7060A8;">printf</span><span style="color: #0000FF;">(</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"%s -&gt;\n %s\n"</span><span style="color: #0000FF;">,{</span><span style="color: #000000;">s</span><span style="color: #0000FF;">,</span><span style="color: #7060A8;">join</span><span style="color: #0000FF;">(</span><span style="color: #000000;">range_expand</span><span style="color: #0000FF;">(</span><span style="color: #000000;">s</span><span style="color: #0000FF;">),</span><span style="color: #008000;">"\n "</span><span style="color: #0000FF;">)})</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">for</span>
<!--</syntaxhighlight>-->
{{out}}
Note that, as usual, unicode output does not look good on a windows console for tests 6 & 7 (linux output shown)
<pre>
simpleNumberRising{1..3}.txt ->
simpleNumberRising1.txt
simpleNumberRising2.txt
simpleNumberRising3.txt
simpleAlphaDescending-{Z..X}.txt ->
simpleAlphaDescending-Z.txt
simpleAlphaDescending-Y.txt
simpleAlphaDescending-X.txt
steppedDownAndPadded-{10..00..5}.txt ->
steppedDownAndPadded-10.txt
steppedDownAndPadded-05.txt
steppedDownAndPadded-00.txt
minusSignFlipsSequence {030..20..-5}.txt ->
minusSignFlipsSequence 020.txt
minusSignFlipsSequence 025.txt
minusSignFlipsSequence 030.txt
combined-{Q..P}{2..1}.txt ->
combined-Q2.txt
combined-Q1.txt
combined-P2.txt
combined-P1.txt
emoji{🌵..🌶}{🌽..🌾}etc ->
emoji🌵🌽etc
emoji🌵🌾etc
emoji🌶🌽etc
emoji🌶🌾etc
multi char emoji ranges fail {🌵🌵..🌵🌶} ->
multi char emoji ranges fail {🌵🌵..🌵🌶}
li{teral ->
li{teral
rangeless{}empty ->
rangeless{}empty
rangeless{random}string ->
rangeless{random}string
mixedNumberAlpha{5..k} ->
mixedNumberAlpha{5..k}
steppedAlphaRising{P..Z..2}.txt ->
steppedAlphaRisingP.txt
steppedAlphaRisingR.txt
steppedAlphaRisingT.txt
steppedAlphaRisingV.txt
steppedAlphaRisingX.txt
steppedAlphaRisingZ.txt
stops after endpoint-{02..10..3}.txt ->
stops after endpoint-02.txt
stops after endpoint-05.txt
stops after endpoint-08.txt
</pre>
=={{header|Python}}==
<syntaxhighlight lang="python">"""Brace expansion using ranges. Requires Python >= 3.6.
 
Here we use regular expressions for parsing and take an object orientated approach
to expansion of range expressions.
 
This implementation supports stepped ordinal range expressions.
"""
 
from __future__ import annotations
 
import itertools
import re
 
from abc import ABC
from abc import abstractmethod
 
from typing import Iterable
from typing import Optional
 
 
RE_SPEC = [
(
"INT_RANGE",
r"\{(?P<int_start>[0-9]+)..(?P<int_stop>[0-9]+)(?:(?:..)?(?P<int_step>-?[0-9]+))?}",
),
(
"ORD_RANGE",
r"\{(?P<ord_start>[^0-9])..(?P<ord_stop>[^0-9])(?:(?:..)?(?P<ord_step>-?[0-9]+))?}",
),
(
"LITERAL",
r".+?(?=\{|$)",
),
]
 
 
RE_EXPRESSION = re.compile(
"|".join(rf"(?P<{name}>{pattern})" for name, pattern in RE_SPEC)
)
 
 
class Expression(ABC):
"""Brace expression abstract base class."""
 
@abstractmethod
def expand(self, prefix: str) -> Iterable[str]:
pass
 
 
class Literal(Expression):
"""An expression literal."""
 
def __init__(self, value: str):
self.value = value
 
def expand(self, prefix: str) -> Iterable[str]:
return [f"{prefix}{self.value}"]
 
 
class IntRange(Expression):
"""An integer range expression."""
 
def __init__(
self, start: int, stop: int, step: Optional[int] = None, zfill: int = 0
):
self.start, self.stop, self.step = fix_range(start, stop, step)
self.zfill = zfill
 
def expand(self, prefix: str) -> Iterable[str]:
return (
f"{prefix}{str(i).zfill(self.zfill)}"
for i in range(self.start, self.stop, self.step)
)
 
 
class OrdRange(Expression):
"""An ordinal range expression."""
 
def __init__(self, start: str, stop: str, step: Optional[int] = None):
self.start, self.stop, self.step = fix_range(ord(start), ord(stop), step)
 
def expand(self, prefix: str) -> Iterable[str]:
return (f"{prefix}{chr(i)}" for i in range(self.start, self.stop, self.step))
 
 
def expand(expressions: Iterable[Expression]) -> Iterable[str]:
"""Expand a sequence of ``Expression``s. Each expression builds on the results
of the expressions that come before it in the sequence."""
expanded = [""]
 
for expression in expressions:
expanded = itertools.chain.from_iterable(
[expression.expand(prefix) for prefix in expanded]
)
 
return expanded
 
 
def zero_fill(start, stop) -> int:
"""Return the target zero padding width."""
 
def _zfill(s):
if len(s) <= 1 or not s.startswith("0"):
return 0
return len(s)
 
return max(_zfill(start), _zfill(stop))
 
 
def fix_range(start, stop, step):
"""Transform start, stop and step so that we can pass them to Python's
built-in ``range`` function."""
if not step:
# Zero or None. Explicit zero gets changed to default.
if start <= stop:
# Default step for ascending ranges.
step = 1
else:
# Default step for descending ranges.
step = -1
 
elif step < 0:
# A negative step means we reverse the range.
start, stop = stop, start
 
if start < stop:
step = abs(step)
else:
start -= 1
stop -= 1
 
elif start > stop:
# A descending range with explicit step.
step = -step
 
# Don't overshoot or fall short.
if (start - stop) % step == 0:
stop += step
 
return start, stop, step
 
 
def parse(expression: str) -> Iterable[Expression]:
"""Generate a sequence of ``Expression``s from the given range expression."""
for match in RE_EXPRESSION.finditer(expression):
kind = match.lastgroup
 
if kind == "INT_RANGE":
start = match.group("int_start")
stop = match.group("int_stop")
step = match.group("int_step")
zfill = zero_fill(start, stop)
 
if step is not None:
step = int(step)
 
yield IntRange(int(start), int(stop), step, zfill=zfill)
 
elif kind == "ORD_RANGE":
start = match.group("ord_start")
stop = match.group("ord_stop")
step = match.group("ord_step")
 
if step is not None:
step = int(step)
 
yield OrdRange(start, stop, step)
 
elif kind == "LITERAL":
yield Literal(match.group())
 
 
def examples():
cases = [
r"simpleNumberRising{1..3}.txt",
r"simpleAlphaDescending-{Z..X}.txt",
r"steppedDownAndPadded-{10..00..5}.txt",
r"minusSignFlipsSequence {030..20..-5}.txt",
r"reverseSteppedNumberRising{1..6..-2}.txt",
r"combined-{Q..P}{2..1}.txt",
r"emoji{🌵..🌶}{🌽..🌾}etc",
r"li{teral",
r"rangeless{}empty",
r"rangeless{random}string",
# Extra examples, not from the task description.
r"steppedNumberRising{1..6..2}.txt",
r"steppedNumberDescending{20..9..2}.txt",
r"steppedAlphaDescending-{Z..M..2}.txt",
r"reverseSteppedAlphaRising{A..F..-2}.txt",
r"reversedSteppedAlphaDescending-{Z..M..-2}.txt",
]
 
for case in cases:
print(f"{case} ->")
expressions = parse(case)
 
for itm in expand(expressions):
print(f"{' '*4}{itm}")
 
print("") # Blank line between cases
 
 
if __name__ == "__main__":
examples()
</syntaxhighlight>
 
{{out}}
<pre>
simpleNumberRising{1..3}.txt ->
simpleNumberRising1.txt
simpleNumberRising2.txt
simpleNumberRising3.txt
 
simpleAlphaDescending-{Z..X}.txt ->
simpleAlphaDescending-Z.txt
simpleAlphaDescending-Y.txt
simpleAlphaDescending-X.txt
 
steppedDownAndPadded-{10..00..5}.txt ->
steppedDownAndPadded-10.txt
steppedDownAndPadded-05.txt
steppedDownAndPadded-00.txt
 
minusSignFlipsSequence {030..20..-5}.txt ->
minusSignFlipsSequence 020.txt
minusSignFlipsSequence 025.txt
minusSignFlipsSequence 030.txt
 
reverseSteppedNumberRising{1..6..-2}.txt ->
reverseSteppedNumberRising5.txt
reverseSteppedNumberRising3.txt
reverseSteppedNumberRising1.txt
 
combined-{Q..P}{2..1}.txt ->
combined-Q2.txt
combined-Q1.txt
combined-P2.txt
combined-P1.txt
 
emoji{🌵..🌶}{🌽..🌾}etc ->
emoji🌵🌽etc
emoji🌵🌾etc
emoji🌶🌽etc
emoji🌶🌾etc
 
li{teral ->
li{teral
 
rangeless{}empty ->
rangeless{}empty
 
rangeless{random}string ->
rangeless{random}string
 
steppedNumberRising{1..6..2}.txt ->
steppedNumberRising1.txt
steppedNumberRising3.txt
steppedNumberRising5.txt
 
steppedNumberDescending{20..9..2}.txt ->
steppedNumberDescending20.txt
steppedNumberDescending18.txt
steppedNumberDescending16.txt
steppedNumberDescending14.txt
steppedNumberDescending12.txt
steppedNumberDescending10.txt
 
steppedAlphaDescending-{Z..M..2}.txt ->
steppedAlphaDescending-Z.txt
steppedAlphaDescending-X.txt
steppedAlphaDescending-V.txt
steppedAlphaDescending-T.txt
steppedAlphaDescending-R.txt
steppedAlphaDescending-P.txt
steppedAlphaDescending-N.txt
 
reverseSteppedAlphaRising{A..F..-2}.txt ->
reverseSteppedAlphaRisingE.txt
reverseSteppedAlphaRisingC.txt
reverseSteppedAlphaRisingA.txt
 
reversedSteppedAlphaDescending-{Z..M..-2}.txt ->
reversedSteppedAlphaDescending-M.txt
reversedSteppedAlphaDescending-O.txt
reversedSteppedAlphaDescending-Q.txt
reversedSteppedAlphaDescending-S.txt
reversedSteppedAlphaDescending-U.txt
reversedSteppedAlphaDescending-W.txt
reversedSteppedAlphaDescending-Y.txt
</pre>
=={{header|Raku}}==
{{works with|Rakudo|2020.08.1}}
Also implements some of the string list functions described on the bash-hackers page.
 
<syntaxhighlight lang="raku" perl6line>my $range = rx/ '{' $<start> = <-[.]>+? '..' $<end> = <-[.]>+? ['..' $<incr> = ['-'?\d+] ]? '}' /;
my $list = rx/ ^ $<prefix> = .*? '{' (<-[,}]>+) +%% ',' '}' $<postfix> = .* $/;
 
Line 1,203 ⟶ 2,470:
say '';
}
</syntaxhighlight>
</lang>
{{out}}
<pre>simpleNumberRising{1..3}.txt ->
Line 1,294 ⟶ 2,561:
multi char emoji ranges fail {🌵🌵..🌵🌶}
</pre>
 
=={{header|Wren}}==
{{libheader|Wren-fmt}}
Added threea furtherfew more examples to test:the minimum number needed for the task.
<syntaxhighlight lang="wren">import "./fmt" for Fmt
* Mixed number/alpha ranges which apparently are not expanded.
* Stepped alpha ranges which appear to be allowed.
* Stepped ranges which stop after the endpoint (Raku example).
<br>
<lang ecmascript>import "/fmt" for Fmt
 
var parseRange = Fn.new { |r|
Line 1,330 ⟶ 2,592:
asc = !asc
var t = n1
n1var d = (n1 - n2).abs % (-n3)
n1 = n2 - d * (n2 - n1).sign
n2 = t
n3 = -n3
Line 1,381 ⟶ 2,644:
"steppedDownAndPadded-{10..00..5}.txt",
"minusSignFlipsSequence {030..20..-5}.txt",
"reverseSteppedNumberRising{1..6..-2}.txt",
"combined-{Q..P}{2..1}.txt",
"emoji{🌵..🌶}{🌽..🌾}etc",
Line 1,388 ⟶ 2,652:
"mixedNumberAlpha{5..k}",
"steppedAlphaRising{P..Z..2}.txt",
"stops after endpoint-{02..10..3}.txt",
"steppedNumberRising{1..6..2}.txt",
"steppedNumberDescending{20..9..2}",
"steppedAlphaDescending-{Z..M..2}.txt",
"reversedSteppedAlphaDescending-{Z..M..-2}.txt"
]
 
Line 1,396 ⟶ 2,664:
System.print(res.join("\n "))
System.print()
}</langsyntaxhighlight>
 
{{out}}
Line 1,419 ⟶ 2,687:
minusSignFlipsSequence 025.txt
minusSignFlipsSequence 030.txt
 
reverseSteppedNumberRising{1..6..-2}.txt ->
reverseSteppedNumberRising5.txt
reverseSteppedNumberRising3.txt
reverseSteppedNumberRising1.txt
 
combined-{Q..P}{2..1}.txt ->
Line 1,456 ⟶ 2,729:
stops after endpoint-05.txt
stops after endpoint-08.txt
 
steppedNumberRising{1..6..2}.txt ->
steppedNumberRising1.txt
steppedNumberRising3.txt
steppedNumberRising5.txt
 
steppedNumberDescending{20..9..2} ->
steppedNumberDescending20
steppedNumberDescending18
steppedNumberDescending16
steppedNumberDescending14
steppedNumberDescending12
steppedNumberDescending10
 
steppedAlphaDescending-{Z..M..2}.txt ->
steppedAlphaDescending-Z.txt
steppedAlphaDescending-X.txt
steppedAlphaDescending-V.txt
steppedAlphaDescending-T.txt
steppedAlphaDescending-R.txt
steppedAlphaDescending-P.txt
steppedAlphaDescending-N.txt
 
reversedSteppedAlphaDescending-{Z..M..-2}.txt ->
reversedSteppedAlphaDescending-N.txt
reversedSteppedAlphaDescending-P.txt
reversedSteppedAlphaDescending-R.txt
reversedSteppedAlphaDescending-T.txt
reversedSteppedAlphaDescending-V.txt
reversedSteppedAlphaDescending-X.txt
reversedSteppedAlphaDescending-Z.txt
</pre>
2,442

edits