Brace expansion using ranges: Difference between revisions

→‎{{header|jq}}: right-most-first
(Added 11l)
(→‎{{header|jq}}: right-most-first)
 
(20 intermediate revisions by 7 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 106 ⟶ 111:
* &nbsp; [[Range_expansion|range expansion]]
<br><br>
 
=={{header|11l}}==
{{trans|Nim}}
 
<langsyntaxhighlight lang="11l">F intFromString(s) -> Int?
X.try
R Int(s)
Line 214 ⟶ 218:
V res = rangeExpand(s)
print(res.join("\n "))
print()</langsyntaxhighlight>
 
{{out}}
Line 270 ⟶ 274:
 
</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 281 ⟶ 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 332 ⟶ 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 397 ⟶ 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 405 ⟶ 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 412 ⟶ 630:
fmt.Println()
}
}</langsyntaxhighlight>
 
{{out}}
Line 418 ⟶ 636:
Same as Wren entry.
</pre>
 
=={{header|JavaScript}}==
<langsyntaxhighlight lang="javascript">(() => {
'"use strict'";
 
// --------------- BRACE-RANGE EXPANSION ---------------
 
// braceExpandWithRange :: String -> [String]
Line 433 ⟶ 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 462 ⟶ 679:
affixLeaf(),
fmapP(xs => [xs])(
between(char('"{'"))(char('"}'"))(
altP(
numericSequence()
Line 474 ⟶ 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 504 ⟶ 725:
// characters before or after a pair of braces.
fmapP(cs => [
[cs.join(''"")]
])(
many(choice([noneOf('"{\\'"), escape()]))
);
 
Line 516 ⟶ 737:
fmapP(ab => {
const [from, to] = ab;
 
return from !== to ? (
enumFromThenToChar(from)(
Line 523 ⟶ 745:
})(
ordinalRange(satisfy(
c => !'"0123456789'".includes(c)
))
);
Line 539 ⟶ 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 560 ⟶ 791:
// The String component contains the digits.
bindP(
option(''"")(char('"-'"))
)(sign => bindP(
some(digit())
Line 586 ⟶ 817:
option(Tuple(false)(1))(
bindP(
string('"..'")
)(_() => bindP(
numericPart()
)(pureP))
Line 602 ⟶ 833:
p
)(from => bindP(
string('"..'")
)(_() => bindP(
p
)(compose(pureP, append([from])))));
Line 616 ⟶ 847:
(a, x) => (0 < a) || (1 > x.length) ? (
a
) : '"0'" !== x[0] ? a : x.length,
0
);
Line 633 ⟶ 864:
 
 
// ------------ GENERIC PARSER COMBINATORS -------------
 
// Parser :: String -> [(a, String)] -> Parser a
Line 639 ⟶ 870:
// A function lifted into a Parser object.
({
type: '"Parser'",
parser: f
});
Line 649 ⟶ 880:
q => Parser(s => {
const xs = parse(p)(s);
 
return 0 < xs.length ? (
xs
Line 662 ⟶ 894:
p => Parser(
s => parse(pf)(s).flatMap(
vr([v, r]) => parse(
fmapP(vr[0]v)(p)
)(vr[1]r)
)
);
Line 676 ⟶ 908:
pClose => p => bindP(
pOpen
)(_() => bindP(
p
)(x => bindP(
pClose
)(_() => pureP(x))));
 
 
Line 693 ⟶ 925:
f => Parser(
s => parse(p)(s).flatMap(
tpl([x, r]) => parse(f(tpl[0]x))(tpl[1]r)
)
);
Line 701 ⟶ 933:
const char = x =>
// A particular single character.
satisfy(c => x === c);
 
 
Line 720 ⟶ 952:
const emptyP = () =>
// The empty list.
Parser(_() => []);
 
 
// escape :: Parser String
const escape = () =>
fmapP(xs => xs.join(''""))(
sequenceP([char('"\\'"), item()])
);
 
Line 736 ⟶ 968:
p => Parser(
s => parse(p)(s).flatMap(
vr => Tuplefirst(f(vr[0]))(vr[1])
)
);
Line 744 ⟶ 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 766 ⟶ 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 796 ⟶ 1,029:
const parse = p =>
// The result of parsing s with p.
s => {p.parser([...s]);
//showLog('s', s)
return p.parser([...s]);
};
 
 
Line 813 ⟶ 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 844 ⟶ 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 858 ⟶ 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 873 ⟶ 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 917 ⟶ 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 943 ⟶ 1,173:
.map(x => x.codePointAt(0)),
d = i2 - i1;
 
return Array.from({
length: (Math.floor(iY - i2) / d) + 2
Line 953 ⟶ 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 986 ⟶ 1,209:
// The identity function. (`id`, in Haskell)
x;
 
 
// isAlpha :: Char -> Bool
const isAlpha = c =>
/[A-Za-z\u00C0-\u00FF]/.test(c);
 
 
Line 996 ⟶ 1,214:
const isDigit = c => {
const n = c.codePointAt(0);
 
return 48 <= n && 57 >= n;
};
Line 1,007 ⟶ 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 1,029 ⟶ 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 1,041 ⟶ 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 1,059 ⟶ 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 1,088 ⟶ 1,292:
const str = x =>
Array.isArray(x) && x.every(
v => ('"string'" === typeof v) && (1 === v.length)
) ? (
x.join(''"")
) : x.toString();
 
Line 1,097 ⟶ 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 1,113 ⟶ 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 1,124 ⟶ 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 1,135 ⟶ 1,345:
// MAIN ---
return main();
})();</langsyntaxhighlight>
{{Out}}
<pre>simpleNumberRising{1..3}.txt ->
Line 1,156 ⟶ 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,177 ⟶ 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}}==
<langsyntaxhighlight lang="julia">padzeros(str) = (len = length(str)) > 1 && str[1] == '0' ? len : 0
 
function ranged(str)
Line 1,220 ⟶ 1,681:
println(test, "->\n", [" " * x * "\n" for x in splatrange(test)]...)
end
</langsyntaxhighlight>{{out}}
<pre>
simpleNumberRising{1..3}.txt->
Line 1,282 ⟶ 1,743:
=={{header|Nim}}==
{{trans|Wren}}
<langsyntaxhighlight Nimlang="nim">import options, strutils, unicode
 
 
Line 1,396 ⟶ 1,857:
stdout.write res.join("\n ")
echo '\n'
</syntaxhighlight>
</lang>
 
{{out}}
Line 1,456 ⟶ 1,917:
stops after endpoint-08.txt
</pre>
 
=={{header|Phix}}==
{{trans|Wren}}
<!--<langsyntaxhighlight Phixlang="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>
Line 1,576 ⟶ 2,036:
<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>
<!--</langsyntaxhighlight>-->
{{out}}
Note that, as usual, unicode output does not look good on a windows console for tests 6 & 7 (linux output shown)
Line 1,628 ⟶ 2,088:
stops after endpoint-08.txt
</pre>
 
=={{header|Python}}==
<syntaxhighlight lang="python">"""Brace expansion using ranges. Requires Python >= 3.6.
<lang python>
"""Brace range expansion. 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.
NOTE: With my current version of bash (GNU bash, version 5.0.3(1)-release), a ``-``
or ``+`` character in front of a `step` has no effect. This implementation reverses
the range if a ``-`` immediately precedes a step, and does not recognize range
expressions that use a ``+``.
 
NOTE: This implementation supports stepped ordinal range expressions.
"""
 
Line 1,760 ⟶ 2,213:
# A negative step means we reverse the range.
start, stop = stop, start
 
step = abs(step)
if start < stop:
step = abs(step)
else:
start -= 1
stop -= 1
 
elif start > stop:
Line 1,806 ⟶ 2,264:
cases = [
r"simpleNumberRising{1..3}.txt",
r"steppedNumberRising{1..6..2}.txt",
r"steppedNumberDescending{20..9..2}.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{random}string",
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",
]
Line 1,832 ⟶ 2,293:
if __name__ == "__main__":
examples()
</syntaxhighlight>
</lang>
 
{{out}}
Line 1,840 ⟶ 2,301:
simpleNumberRising2.txt
simpleNumberRising3.txt
 
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
 
simpleAlphaDescending-{Z..X}.txt ->
Line 1,868 ⟶ 2,316:
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,883 ⟶ 2,336:
li{teral ->
li{teral
 
rangeless{}empty ->
rangeless{}empty
 
rangeless{random}string ->
rangeless{random}string
 
steppedNumberRising{1..6..2}.txt ->
rangeless{}empty ->
steppedNumberRising1.txt
rangeless{}empty
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 ->
Line 1,898 ⟶ 2,364:
steppedAlphaDescending-P.txt
steppedAlphaDescending-N.txt
 
reverseSteppedAlphaRising{A..F..-2}.txt ->
reverseSteppedAlphaRisingE.txt
reverseSteppedAlphaRisingC.txt
reverseSteppedAlphaRisingA.txt
 
reversedSteppedAlphaDescending-{Z..M..-2}.txt ->
Line 1,908 ⟶ 2,379:
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 2,000 ⟶ 2,470:
say '';
}
</syntaxhighlight>
</lang>
{{out}}
<pre>simpleNumberRising{1..3}.txt ->
Line 2,091 ⟶ 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 2,127 ⟶ 2,592:
asc = !asc
var t = n1
n1var d = (n1 - n2).abs % (-n3)
n1 = n2 - d * (n2 - n1).sign
n2 = t
n3 = -n3
Line 2,178 ⟶ 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 2,185 ⟶ 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 2,193 ⟶ 2,664:
System.print(res.join("\n "))
System.print()
}</langsyntaxhighlight>
 
{{out}}
Line 2,216 ⟶ 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 2,253 ⟶ 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