Brace expansion: Difference between revisions

m
→‎{{header|Wren}}: Changed to Wren S/H
m (→‎{{header|Perl 6}}: and removed un-needed enclosures)
m (→‎{{header|Wren}}: Changed to Wren S/H)
 
(40 intermediate revisions by 22 users not shown)
Line 3:
Brace expansion is a type of parameter expansion [[wp:Bash_%28Unix_shell%29#Brace_expansion|made popular by Unix shells]], where it allows users to specify multiple similar string parameters without having to type them all out. E.g. the parameter <code>enable_{audio,video}</code> would be interpreted as if both <code>enable_audio</code> and <code>enable_video</code> had been specified.
 
 
{{task heading}}
;Task
 
Write a function that can perform brace expansion on any input string, according to the following specification.<br>
Line 226 ⟶ 227:
{{task heading|Test Cases}}
 
:::{| class="wikitable" style="white-space: nowrap;"
|-
! Input<br><small style="font-weight:normal">''(single string)''</small>
Line 267 ⟶ 268:
<hr style="clear:both; margin-bottom:1em;"/>
 
{{Template:Strings}}
 
 
:* &nbsp; [[Brace_expansion_using_ranges]]
<br><br>
=={{header|11l}}==
{{trans|Python}}
<langsyntaxhighlight lang="11l">F getitem(=s, depth = 0)
V out = [‘’]
L s != ‘’
Line 306 ⟶ 312:
R ([‘’] * 0, ‘’)
 
L(s) |‘~/{Downloads,Pictures}/*.{jpg,gif,png}
It{{em,alic}iz,erat}e{d,}, please.
{,{,gotta have{ ,\, again\, }}more }cowbell!
{}} some }{,{\\{ edge, edge} \,}{ cases, {here} \\\\\}’.split("\n")
print(([s] [+] getitem(s)[0]).join("\n\t")"\n")</langsyntaxhighlight>
{{out}}
<pre>
Line 325 ⟶ 331:
{}} some }{,{\\ edge \,}{ cases, {here} \\\\\}
</pre>
=={{header|AppleScript}}==
 
This may seem like a cheat, but it ''is'' a legitimate and obvious way to tackle the problem with AppleScript in the unlikely event that the need should arise. It adjusts the escape levels in the input text, feeds it as a shell script to macOS's sh shell, and lets ''it'' handle the expansion.
=={{header|AutoHotkey}}==
{{incorrect|AutoHotkey|Output for edge cases is wrong.}}
 
<syntaxhighlight lang="applescript">use AppleScript version "2.4" -- OS X 10.10 (Yosemite) or later
<lang autohotkey>; AutoHotkey runs Perl-compatible regular expressions no problem
use framework "Foundation"
t=~/{Downloads,Pictures}/ *.{jpg,gif,png} ; Note I added a space so the RosettaCode syntax highlighting will work. The AutoHotkey interpreter handles it no problem.
use scripting additions
msgbox % expand(t)
t=It{{em,alic}iz,erat}e{d,}, please.
msgbox % expand(t)
t={,{,gotta have{ ,\, again\, }}more }cowbell!
msgbox % expand(t)
t={}} some }{,{\\{ edge, edge} \,}{ cases, {here} \\\\\}
msgbox % expand(t)
 
on braceExpansion(textForExpansion)
expand(t) {
-- Massage the text to pass to a shell script: -
t:=RegExReplace(t, "\\\\", "\!") ; if you want to use these character combinations literally in your string, just switch these susbstitutions to something unicode
-- Single-quote it to render everything in it initially immune from brace and file-system expansion.
,t:=RegExReplace(t, "\\,", "\#")
-- Include a return at the end to get a new line at the end of each eventual expansion.
,t:=RegExReplace(t, "\\\{", "\@")
set textForExpansion to quoted form of (textForExpansion & return)
,t:=RegExReplace(t, "\\\}", "\&")
-- Switch to ASObjC text for a couple of regex substitutions.
a=1
set textForExpansion to current application's class "NSMutableString"'s stringWithString:(textForExpansion)
While a or b
-- Increase the escape level of every instance of /two/ backslashes (represented by eight in the
; expand (braces with multiple commas) which are (apparently inside something else)
-- AppleScript string for the search regex) and isolate each result inside its own quote marks.
t:=RegExReplace(t, "Sxm)^(.*) ([{,]) ([^{},]*) \{ ([^{},]*) , ([^{},]* , [^{}]*) \} ([^{},]*?) ([},]) (.*)$", "$1$2$3$4$6,$3{$5}$6$7$8", a)
tell textForExpansion to replaceOccurrencesOfString:("\\\\\\\\") withString:("''$0$0''") ¬
; expand (braces with single commas) which are (apparently inside something else)
options:(current application's NSRegularExpressionSearch) range:({0, its |length|()})
,t:=RegExReplace(t, "Sxm)^(.*) ([{,]) ([^{},]*) \{ ([^{},]*) , ([^{},]*) \} ([^{},]*?) ([},]) (.*)$", "$1$2$3$4$6,$3$5$6$7$8", b)
-- UNquote every run of braces and/or commas not now immediately preceded by a backslash.
a=1
tell textForExpansion to replaceOccurrencesOfString:("(?<!\\\\)[{,}]++") withString:("'$0'") ¬
While a or b
options:(current application's NSRegularExpressionSearch) range:({0, its |length|()})
; expand braces with single commas
t:=RegExReplace(t, "Sxm)^(.*) \{ ([^{},]*) , ([^{},]*) \} (.*)$", "$1$2$4`r`n$1$3$4", a)
-- Pass the massaged text to a shell script to be 'echo'-ed. Since a space will still be inserted
; expand braces with multiple commas
-- between each expansion, also delete the space after each return in the result.
,t:=RegExReplace(t, "Sxm)^(.*) \{ ([^{},]*) , ([^{},]* , [^{}]*) \} (.*)$", "$1$2$4`r`n$1{$3}$4", b)
-- Return a list of the individual expansions.
t:=RegExReplace(t, "\\!", "\\")
return (do shell script ("echo " & textForExpansion & " | sed -E 's/([[:cntrl:]]) /\\1/g'"))'s paragraphs
,t:=RegExReplace(t, "\\#", "\,")
end braceExpansion
,t:=RegExReplace(t, "\\@", "\{")
 
,t:=RegExReplace(t, "\\&", "\}")
-- Test:
Return t
set output to braceExpansion("~/{Downloads,Pictures}/*.{jpg,gif,png}") & ¬
}</lang>
braceExpansion("It{{em,alic}iz,erat}e{d,}, please.") & ¬
{{out}}
braceExpansion("{,{,gotta have{ ,\\, again\\, }}more }cowbell!") & ¬
<pre>~/Downloads/*.jpg
braceExpansion("{}} some }{,{\\\\{ edge, edge} \\,}{ cases, {here} \\\\\\\\\\}")
set astid to AppleScript's text item delimiters
set AppleScript's text item delimiters to linefeed
set output to output as text
set AppleScript's text item delimiters to astid
 
log output -- To see the result without the backslash-escaping.</syntaxhighlight>
 
{{output}}
<pre>(*~/Downloads/*.jpg
~/Downloads/*.gif
~/Downloads/*.png
Line 369 ⟶ 379:
~/Pictures/*.gif
~/Pictures/*.png
Itemized, please.
Itemize, please.
Italicized, please.
Italicize, please.
Iterated, please.
Iterate, please.
cowbell!
more cowbell!
gotta have more cowbell!
gotta have\, again\, more cowbell!
{}} some }{,{\\ edge \,}{ cases, {here} \\\\\}
{}} some }{,{\\ edge \,}{ cases, {here} \\\\\}*)</pre>
=={{header|AutoHotkey}}==
 
<syntaxhighlight lang="autohotkey">;This one is a lot more simpler than the rest
BraceExp(string, del:="`n") {
Loop, Parse, string
if (A_LoopField = "{")
break
else
substring .= A_LoopField
substr := SubStr(string, InStr(string, "{")+1, InStr(string, "}")-InStr(string, "{")-1)
Loop, Parse, substr, `,
toreturn .= substring . A_LoopField . del
return toreturn
}
 
Msgbox, % BraceExp("enable_{video,audio}")
Msgbox, % BraceExp("apple {bush,tree}")
Msgbox, % BraceExp("h{i,ello}")
Msgbox, % BraceExp("rosetta{code,stone}")</syntaxhighlight>
{{out}}
<pre>enable_video
enable_audio
 
apple bush
apple tree
 
hi
hello
 
rosettacode
rosettastone</pre>
=={{header|C}}==
Handles only properly formed input.
<syntaxhighlight lang="c">#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
 
#define BUFFER_SIZE 128
 
typedef unsigned char character;
typedef character *string;
 
typedef struct node_t node;
struct node_t {
enum tag_t {
NODE_LEAF,
NODE_TREE,
NODE_SEQ,
} tag;
 
union {
string str;
node *root;
} data;
 
node *next;
};
 
node *allocate_node(enum tag_t tag) {
node *n = malloc(sizeof(node));
if (n == NULL) {
fprintf(stderr, "Failed to allocate node for tag: %d\n", tag);
exit(1);
}
n->tag = tag;
n->next = NULL;
return n;
}
 
node *make_leaf(string str) {
node *n = allocate_node(NODE_LEAF);
n->data.str = str;
return n;
}
 
node *make_tree() {
node *n = allocate_node(NODE_TREE);
n->data.root = NULL;
return n;
}
 
node *make_seq() {
node *n = allocate_node(NODE_SEQ);
n->data.root = NULL;
return n;
}
 
void deallocate_node(node *n) {
if (n == NULL) {
return;
}
 
deallocate_node(n->next);
n->next = NULL;
 
if (n->tag == NODE_LEAF) {
free(n->data.str);
n->data.str = NULL;
} else if (n->tag == NODE_TREE || n->tag == NODE_SEQ) {
deallocate_node(n->data.root);
n->data.root = NULL;
} else {
fprintf(stderr, "Cannot deallocate node with tag: %d\n", n->tag);
exit(1);
}
 
free(n);
}
 
void append(node *root, node *elem) {
if (root == NULL) {
fprintf(stderr, "Cannot append to uninitialized node.");
exit(1);
}
if (elem == NULL) {
return;
}
 
if (root->tag == NODE_SEQ || root->tag == NODE_TREE) {
if (root->data.root == NULL) {
root->data.root = elem;
} else {
node *it = root->data.root;
while (it->next != NULL) {
it = it->next;
}
it->next = elem;
}
} else {
fprintf(stderr, "Cannot append to node with tag: %d\n", root->tag);
exit(1);
}
}
 
size_t count(node *n) {
if (n == NULL) {
return 0;
}
 
if (n->tag == NODE_LEAF) {
return 1;
}
if (n->tag == NODE_TREE) {
size_t sum = 0;
node *it = n->data.root;
while (it != NULL) {
sum += count(it);
it = it->next;
}
return sum;
}
if (n->tag == NODE_SEQ) {
size_t prod = 1;
node *it = n->data.root;
while (it != NULL) {
prod *= count(it);
it = it->next;
}
return prod;
}
 
fprintf(stderr, "Cannot count node with tag: %d\n", n->tag);
exit(1);
}
 
void expand(node *n, size_t pos) {
if (n == NULL) {
return;
}
 
if (n->tag == NODE_LEAF) {
printf(n->data.str);
} else if (n->tag == NODE_TREE) {
node *it = n->data.root;
while (true) {
size_t cnt = count(it);
if (pos < cnt) {
expand(it, pos);
break;
}
pos -= cnt;
it = it->next;
}
} else if (n->tag == NODE_SEQ) {
size_t prod = pos;
node *it = n->data.root;
while (it != NULL) {
size_t cnt = count(it);
 
size_t rem = prod % cnt;
expand(it, rem);
 
it = it->next;
}
} else {
fprintf(stderr, "Cannot expand node with tag: %d\n", n->tag);
exit(1);
}
}
 
string allocate_string(string src) {
size_t len = strlen(src);
string out = calloc(len + 1, sizeof(character));
if (out == NULL) {
fprintf(stderr, "Failed to allocate a copy of the string.");
exit(1);
}
strcpy(out, src);
return out;
}
 
node *parse_seq(string input, size_t *pos);
 
node *parse_tree(string input, size_t *pos) {
node *root = make_tree();
 
character buffer[BUFFER_SIZE] = { 0 };
size_t bufpos = 0;
size_t depth = 0;
bool asSeq = false;
bool allow = false;
 
while (input[*pos] != 0) {
character c = input[(*pos)++];
if (c == '\\') {
c = input[(*pos)++];
if (c == 0) {
break;
}
buffer[bufpos++] = '\\';
buffer[bufpos++] = c;
buffer[bufpos] = 0;
} else if (c == '{') {
buffer[bufpos++] = c;
buffer[bufpos] = 0;
asSeq = true;
depth++;
} else if (c == '}') {
if (depth-- > 0) {
buffer[bufpos++] = c;
buffer[bufpos] = 0;
} else {
if (asSeq) {
size_t new_pos = 0;
node *seq = parse_seq(buffer, &new_pos);
append(root, seq);
} else {
append(root, make_leaf(allocate_string(buffer)));
}
break;
}
} else if (c == ',') {
if (depth == 0) {
if (asSeq) {
size_t new_pos = 0;
node *seq = parse_seq(buffer, &new_pos);
append(root, seq);
bufpos = 0;
buffer[bufpos] = 0;
asSeq = false;
} else {
append(root, make_leaf(allocate_string(buffer)));
bufpos = 0;
buffer[bufpos] = 0;
}
} else {
buffer[bufpos++] = c;
buffer[bufpos] = 0;
}
} else {
buffer[bufpos++] = c;
buffer[bufpos] = 0;
}
}
 
return root;
}
 
node *parse_seq(string input, size_t *pos) {
node *root = make_seq();
 
character buffer[BUFFER_SIZE] = { 0 };
size_t bufpos = 0;
 
while (input[*pos] != 0) {
character c = input[(*pos)++];
if (c == '\\') {
c = input[(*pos)++];
if (c == 0) {
break;
}
buffer[bufpos++] = c;
buffer[bufpos] = 0;
} else if (c == '{') {
node *tree = parse_tree(input, pos);
if (bufpos > 0) {
append(root, make_leaf(allocate_string(buffer)));
bufpos = 0;
buffer[bufpos] = 0;
}
append(root, tree);
} else {
buffer[bufpos++] = c;
buffer[bufpos] = 0;
}
}
 
if (bufpos > 0) {
append(root, make_leaf(allocate_string(buffer)));
bufpos = 0;
buffer[bufpos] = 0;
}
 
return root;
}
 
void test(string input) {
size_t pos = 0;
node *n = parse_seq(input, &pos);
size_t cnt = count(n);
size_t i;
 
printf("Pattern: %s\n", input);
 
for (i = 0; i < cnt; i++) {
expand(n, i);
printf("\n");
}
printf("\n");
 
deallocate_node(n);
}
 
int main() {
test("~/{Downloads,Pictures}/*.{jpg,gif,png}");
test("It{{em,alic}iz,erat}e{d,}, please.");
test("{,{,gotta have{ ,\\, again\\, }}more }cowbell!");
 
//not sure how to parse this one
//test("{}} some }{,{\\{ edge, edge} \,}{ cases, {here} \\\\\}");
 
return 0;
}</syntaxhighlight>
{{out}}
<pre>Pattern: ~/{Downloads,Pictures}/*.{jpg,gif,png}
~/Downloads/*.jpg
~/Pictures/*.gif
~/Downloads/*.png
~/Pictures/*.jpg
~/Downloads/*.gif
~/Pictures/*.png
 
Pattern: It{{em,alic}iz,erat}e{d,}, please.
Itemized, please.
Italicize, please.
Iterated, please.
Itemize, please.
Italicized, please.
Iterate, please.
 
Pattern: {,{,gotta have{ ,\, again\, }}more }cowbell!
cowbell!
more cowbell!
gotta have more cowbell!
gotta have\, again\, more cowbell!</pre>
=={{header|C sharp|C#}}==
This approach turns the string into a tree structure, with Tokens that are either text, a concatenation or an alteration.
{{works with|C sharp|8}}
<syntaxhighlight lang="csharp">using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using static System.Linq.Enumerable;
 
public static class BraceExpansion
{
enum TokenType { OpenBrace, CloseBrace, Separator, Text, Alternate, Concat }
const char L = '{', R = '}', S = ',';
public static void Main() {
string[] input = {
"It{{em,alic}iz,erat}e{d,}, please.",
"~/{Downloads,Pictures}/*.{jpg,gif,png}",
@"{,{,gotta have{ ,\, again\, }}more }cowbell!",
@"{}} some }{,{\\{ edge, edge} \,}{ cases, {here} \\\\\}"
};
foreach (string text in input) Expand(text);
}
static void Expand(string input) {
Token token = Tokenize(input);
foreach (string value in token) Console.WriteLine(value);
Console.WriteLine();
}
static Token Tokenize(string input) {
var tokens = new List<Token>();
var buffer = new StringBuilder();
bool escaping = false;
int level = 0;
foreach (char c in input) {
(escaping, level, tokens, buffer) = c switch {
_ when escaping => (false, level, tokens, buffer.Append(c)),
'\\' => (true, level, tokens, buffer.Append(c)),
L => (escaping, level + 1,
tokens.With(buffer.Flush()).With(new Token(c.ToString(), TokenType.OpenBrace)), buffer),
S when level > 0 => (escaping, level,
tokens.With(buffer.Flush()).With(new Token(c.ToString(), TokenType.Separator)), buffer),
R when level > 0 => (escaping, level - 1,
tokens.With(buffer.Flush()).With(new Token(c.ToString(), TokenType.CloseBrace)).Merge(), buffer),
_ => (escaping, level, tokens, buffer.Append(c))
};
}
if (buffer.Length > 0) tokens.Add(buffer.Flush());
for (int i = 0; i < tokens.Count; i++) {
if (tokens[i].Type == TokenType.OpenBrace || tokens[i].Type == TokenType.Separator) {
tokens[i] = tokens[i].Value; //Change to text
}
}
return new Token(tokens, TokenType.Concat);
}
static List<Token> Merge(this List<Token> list) {
int separators = 0;
int last = list.Count - 1;
for (int i = list.Count - 3; i >= 0; i--) {
if (list[i].Type == TokenType.Separator) {
separators++;
Concat(list, i + 1, last);
list.RemoveAt(i);
last = i;
} else if (list[i].Type == TokenType.OpenBrace) {
Concat(list, i + 1, last);
if (separators > 0) {
list[i] = new Token(list.Range((i+1)..^1), TokenType.Alternate);
list.RemoveRange(i+1, list.Count - i - 1);
} else {
list[i] = L.ToString();
list[^1] = R.ToString();
Concat(list, i, list.Count);
}
break;
}
}
return list;
}
static void Concat(List<Token> list, int s, int e) {
for (int i = e - 2; i >= s; i--) {
(Token a, Token b) = (list[i], list[i+1]);
switch (a.Type, b.Type) {
case (TokenType.Text, TokenType.Text):
list[i] = a.Value + b.Value;
list.RemoveAt(i+1);
break;
case (TokenType.Concat, TokenType.Concat):
a.SubTokens.AddRange(b.SubTokens);
list.RemoveAt(i+1);
break;
case (TokenType.Concat, TokenType.Text) when b.Value == "":
list.RemoveAt(i+1);
break;
case (TokenType.Text, TokenType.Concat) when a.Value == "":
list.RemoveAt(i);
break;
default:
list[i] = new Token(new [] { a, b }, TokenType.Concat);
list.RemoveAt(i+1);
break;
}
}
}
private struct Token : IEnumerable<string>
{
private List<Token>? _subTokens;
public string Value { get; }
public TokenType Type { get; }
public List<Token> SubTokens => _subTokens ??= new List<Token>();
public Token(string value, TokenType type) => (Value, Type, _subTokens) = (value, type, null);
public Token(IEnumerable<Token> subTokens, TokenType type) => (Value, Type, _subTokens) = ("", type, subTokens.ToList());
public static implicit operator Token(string value) => new Token(value, TokenType.Text);
public IEnumerator<string> GetEnumerator() => (Type switch
{
TokenType.Concat => SubTokens.Select(t => t.AsEnumerable()).CartesianProduct().Select(p => string.Join("", p)),
TokenType.Alternate => from t in SubTokens from s in t select s,
_ => Repeat(Value, 1)
}).GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}
//===Extension methods===
static IEnumerable<IEnumerable<T>> CartesianProduct<T>(this IEnumerable<IEnumerable<T>> sequences) {
IEnumerable<IEnumerable<T>> emptyProduct = new[] { Empty<T>() };
return sequences.Aggregate(
emptyProduct,
(accumulator, sequence) =>
from acc in accumulator
from item in sequence
select acc.Concat(new [] { item }));
}
static List<Token> With(this List<Token> list, Token token) {
list.Add(token);
return list;
}
static IEnumerable<Token> Range(this List<Token> list, Range range) {
int start = range.Start.GetOffset(list.Count);
int end = range.End.GetOffset(list.Count);
for (int i = start; i < end; i++) yield return list[i];
}
static string Flush(this StringBuilder builder) {
string result = builder.ToString();
builder.Clear();
return result;
}
}</syntaxhighlight>
 
{{trans|Python}}
<syntaxhighlight lang="csharp">public static class BraceExpansion
{
const char L = '{', R = '}', S = ',';
//Main method same as above
 
static void Expand(string input) {
foreach (string s in GetItem(input).Item1) Console.WriteLine(s);
Console.WriteLine();
}
 
static (List<string>, string) GetItem(string s, int depth = 0) {
var output = new List<string> {""};
while (s.Length > 0) {
string c = s[..1];
if (depth > 0 && (c[0] == S || c[0] == R)) return (output, s);
if (c[0] == L) {
var x = GetGroup(s[1..], depth+1);
if (!(x.Item1 is null)) {
(output, s) = ((from a in output from b in x.Item1 select a + b).ToList(), x.Item2);
continue;
}
}
if (c[0] == '\\' && s.Length > 1) {
c += s[1];
s = s[1..];
}
(output, s) = ((from a in output select a + c).ToList(), s[1..]);
}
return (output, s);
}
 
static (List<string>?, string) GetGroup(string s, int depth) {
var output = new List<string>();
bool comma = false;
while (s.Length > 0) {
List<string>? g;
(g, s) = GetItem(s, depth);
if (s.Length == 0) break;
output.AddRange(g);
 
if (s[0] == R) {
if (comma) return (output, s[1..]);
return ((from a in output select L + a + R).ToList(), s[1..]);
}
if (s[0] == S) (comma, s) = (true, s[1..]);
}
return (null, "");
}
}</syntaxhighlight>
{{out}}
<pre>
Itemized, please.
Itemize, please.
Line 376 ⟶ 976:
Iterated, please.
Iterate, please.
 
~/Downloads/*.jpg
~/Downloads/*.gif
~/Downloads/*.png
~/Pictures/*.jpg
~/Pictures/*.gif
~/Pictures/*.png
 
cowbell!
Line 382 ⟶ 989:
gotta have\, again\, more cowbell!
 
{}} some }{,{\\ edge \,}{ cases, {here} \\\\\}
{}} some }{,{\\edgy edge \,}{ cases, {here} \\\\\}</pre>
 
=={{header|C++}}==
C++11 solution:
 
<langsyntaxhighlight lang="cpp">#include <iostream>
#include <iterator>
#include <string>
Line 560 ⟶ 1,166:
return 0;
}</langsyntaxhighlight>
 
=={{header|Common Lisp}}==
<langsyntaxhighlight lang="lisp">(defstruct alternation
(alternatives nil :type list))
 
Line 648 ⟶ 1,253:
(dolist (output (expand input))
(format t " ~A~%" output))
(terpri)))</langsyntaxhighlight>
{{out}}
<pre>~/{Downloads,Pictures}/*.{jpg,gif,png}
Line 676 ⟶ 1,281:
{}} some }{,{\\ edge \,}{ cases, {here} \\\\\}
</pre>
 
=={{header|D}}==
{{trans|Python}}
This code is not UTF-corrected, because it uses slicing instead of front, popFront, etc.
<langsyntaxhighlight lang="d">import std.stdio, std.typecons, std.array, std.range, std.algorithm, std.string;
 
Nullable!(Tuple!(string[], string)) getGroup(string s, in uint depth)
Line 760 ⟶ 1,364:
foreach (const s; testCases.splitLines)
writefln("%s\n%-( %s\n%)\n", s, s.getItems[0]);
}</langsyntaxhighlight>
{{out}}
<pre>~/{Downloads,Pictures}/*.{jpg,gif,png}
Line 832 ⟶ 1,436:
{a,b{2e}f
</pre>
 
=={{header|Elixir}}==
{{trans|Ruby}}
<langsyntaxhighlight lang="elixir">defmodule Brace_expansion do
def getitem(s), do: getitem(String.codepoints(s), 0, [""])
Line 886 ⟶ 1,489:
|> Enum.each(fn str -> IO.puts "\t#{str}" end)
IO.puts ""
end)</langsyntaxhighlight>
 
{{out}}
Line 916 ⟶ 1,519:
{}} some }{,{\\ edge \,}{ cases, {here} \\\\\}
</pre>
 
=={{header|Haskell}}==
[http://www.reddit.com/r/readablecode/comments/1w6exe/p6_crosswalk_braceexpansionparses/cf229at "Here is a direct translation to Haskell using parsec"] (of [http://rosettacode.org/mw/index.php?title=Brace_expansion&oldid=175567#Perl_6 an earlier version of the Perl 6 solution]):
 
<lang haskell>{-# LANGUAGE FlexibleContexts #-}
 
import Text.Parsec
(Parsec, (<|>), anyChar, between, char, many, many1, noneOf, parse,
try)
 
parser :: Parsec String u [String]
parser =
expand <$> many (try alts <|> try alt1 <|> escape <|> pure . pure <$> anyChar)
where
alts = concat <$> between (char '{') (char '}') (alt `sepBy2` char ',')
alt1 =
(: []) . ('{' :) . (++ "}") <$>
between (char '{') (char '}') (many $ noneOf ",{}")
alt =
expand <$>
many (try alts <|> try alt1 <|> escape <|> pure . pure <$> noneOf ",}")
escape = pure <$> sequence [char '\\', anyChar]
expand = foldr ((<*>) . fmap (++)) [""]
p `sepBy2` sep = (:) <$> p <*> many1 (sep >> p)
 
showExpansion :: String -> String
showExpansion = (++) <*> ("\n-->\n" ++) . either show unlines . parse parser []
 
main :: IO ()
main =
mapM_
(putStrLn . showExpansion)
[ "~/{Downloads,Pictures}/*.{jpg,gif,png}"
, "It{{em,alic}iz,erat}e{d,}, please."
, "{,{,gotta have{ ,\\, again\\, }}more }cowbell!"
, "{}} some {\\\\{edge,edgy} }{ cases, here\\\\\\\\\\}"
]</lang>
{{out}}
<pre>~/{Downloads,Pictures}/*.{jpg,gif,png}
-->
~/Downloads/*.jpg
~/Downloads/*.gif
~/Downloads/*.png
~/Pictures/*.jpg
~/Pictures/*.gif
~/Pictures/*.png
 
It{{em,alic}iz,erat}e{d,}, please.
-->
Itemized, please.
Itemize, please.
Italicized, please.
Italicize, please.
Iterated, please.
Iterate, please.
 
{,{,gotta have{ ,\, again\, }}more }cowbell!
-->
cowbell!
more cowbell!
gotta have more cowbell!
gotta have\, again\, more cowbell!
 
{}} some {\\{edge,edgy} }{ cases, here\\\\\}
-->
{}} some {\\edge }{ cases, here\\\\\}
{}} some {\\edgy }{ cases, here\\\\\}</pre>
 
=={{header|Go}}==
<code>expand.go</code>:
<langsyntaxhighlight lang="go">package expand
 
// Expander is anything that can be expanded into a slice of strings.
Line 1,187 ⟶ 1,722:
alt = append(alt, sub)
return alt
}</langsyntaxhighlight>
<code>expand_test.go</code>
<langsyntaxhighlight lang="go">package expand
 
import (
Line 1,272 ⟶ 1,807:
Expand(input)
}
}</langsyntaxhighlight>
{{out}}
<pre>
Line 1,327 ⟶ 1,862:
ok rosetta_code/Brace_expansion 3.347s
</pre>
=={{header|Groovy}}==
{{trans|Java}}
<syntaxhighlight lang="groovy">class BraceExpansion {
static void main(String[] args) {
for (String s : [
"It{{em,alic}iz,erat}e{d,}, please.",
"~/{Downloads,Pictures}/*.{jpg,gif,png}",
"{,{,gotta have{ ,\\, again\\, }}more }cowbell!",
"{}} some }{,{\\\\{ edge, edge} \\,}{ cases, {here} \\\\\\\\\\}"
]) {
println()
expand(s)
}
}
 
static void expand(String s) {
expandR("", s, "")
}
 
private static void expandR(String pre, String s, String suf) {
int i1 = -1, i2 = 0
String noEscape = s.replaceAll("([\\\\]{2}|[\\\\][,}{])", " ")
StringBuilder sb = null
 
outer:
while ((i1 = noEscape.indexOf('{', i1 + 1)) != -1) {
i2 = i1 + 1
sb = new StringBuilder(s)
for (int depth = 1; i2 < s.length() && depth > 0; i2++) {
char c = noEscape.charAt(i2)
depth = (c == ('{' as char)) ? ++depth : depth
depth = (c == ('}' as char)) ? --depth : depth
if (c == (',' as char) && depth == 1) {
sb.setCharAt(i2, '\u0000' as char)
} else if (c == ('}' as char) && depth == 0 && sb.indexOf("\u0000") != -1) {
break outer
}
}
}
if (i1 == -1) {
if (suf.length() > 0) {
expandR(pre + s, suf, "")
} else {
printf("%s%s%s%n", pre, s, suf)
}
} else {
for (String m : sb.substring(i1 + 1, i2).split("\u0000", -1)) {
expandR(pre + s.substring(0, i1), m, s.substring(i2 + 1) + suf)
}
}
}
}</syntaxhighlight>
{{out}}
<pre>Itemized, please.
Itemize, please.
Italicized, please.
Italicize, please.
Iterated, please.
Iterate, please.
 
~/Downloads/*.jpg
~/Downloads/*.gif
~/Downloads/*.png
~/Pictures/*.jpg
~/Pictures/*.gif
~/Pictures/*.png
 
cowbell!
more cowbell!
gotta have more cowbell!
gotta have\, again\, more cowbell!
 
{}} some }{,{\\ edge \,}{ cases, {here} \\\\\}
{}} some }{,{\\ edge \,}{ cases, {here} \\\\\}</pre>
=={{header|Haskell}}==
[http://www.reddit.com/r/readablecode/comments/1w6exe/p6_crosswalk_braceexpansionparses/cf229at "Here is a direct translation to Haskell using parsec"] (of [http://rosettacode.org/mw/index.php?title=Brace_expansion&oldid=175567#Raku an earlier version of the Raku solution]):
 
<syntaxhighlight lang="haskell">import qualified Text.Parsec as P
 
showExpansion :: String -> String
showExpansion =
(<>) . (<> "\n-->\n") <*> (either show unlines . P.parse parser [])
 
parser :: P.Parsec String u [String]
parser = expansion P.anyChar
 
expansion :: P.Parsec String u Char -> P.Parsec String u [String]
expansion =
fmap expand .
P.many .
((P.try alts P.<|> P.try alt1 P.<|> escape) P.<|>) . fmap (pure . pure)
 
expand :: [[String]] -> [String]
expand = foldr ((<*>) . fmap (<>)) [[]]
 
alts :: P.Parsec String u [String]
alts = concat <$> P.between (P.char '{') (P.char '}') (alt `sepBy2` P.char ',')
 
alt :: P.Parsec String u [String]
alt = expansion (P.noneOf ",}")
 
alt1 :: P.Parsec String u [String]
alt1 =
(\x -> ['{' : (x <> "}")]) <$>
P.between (P.char '{') (P.char '}') (P.many $ P.noneOf ",{}")
 
sepBy2 :: P.Parsec String u a -> P.Parsec String u b -> P.Parsec String u [a]
p `sepBy2` sep = (:) <$> p <*> P.many1 (sep >> p)
 
escape :: P.Parsec String u [String]
escape = pure <$> sequence [P.char '\\', P.anyChar]
 
main :: IO ()
main =
mapM_
(putStrLn . showExpansion)
[ "~/{Downloads,Pictures}/*.{jpg,gif,png}"
, "It{{em,alic}iz,erat}e{d,}, please."
, "{,{,gotta have{ ,\\, again\\, }}more }cowbell!"
, "{}} some }{,{\\\\{ edge, edge} \\,}{ cases, {here} \\\\\\\\\\}"
]</syntaxhighlight>
{{out}}
<pre>It{{em,alic}iz,erat}e{d,}, please.
-->
Itemized, please.
Itemize, please.
Italicized, please.
Italicize, please.
Iterated, please.
Iterate, please.
 
{,{,gotta have{ ,\, again\, }}more }cowbell!
-->
cowbell!
more cowbell!
gotta have more cowbell!
gotta have\, again\, more cowbell!
 
{}} some }{,{\\{ edge, edge} \,}{ cases, {here} \\\\\}
-->
{}} some }{ cases, {here} \\\\\}
{}} some }{\\ edge \,{ cases, {here} \\\\\}
{}} some }{\\ edge \,{ cases, {here} \\\\\}</pre>
=={{header|J}}==
 
Implementation:
 
<syntaxhighlight lang="j">
<lang J>
NB. legit { , and } do not follow a legit backslash:
legit=: 1,_1}.4>(3;(_2[\"1".;._2]0 :0);('\';a.);0 _1 0 1)&;:&.(' '&,)
Line 1,374 ⟶ 2,051:
options=. }:mask <;._1 y
prefix,each options,each suffix
)</langsyntaxhighlight>
 
Examples:
 
<langsyntaxhighlight Jlang="j"> >expand t1
~/Downloads/*.jpg
~/Downloads/*.gif
Line 1,399 ⟶ 2,076:
>expand t4
{}} some {\\edge }{ cases, here\\\}
{}} some {\\edgy }{ cases, here\\\}</langsyntaxhighlight>
 
Explanation:
Line 1,408 ⟶ 2,085:
 
Finally, for each integer that we've used to mark delimiter locations, split out each of the marked options (each with a copy of that group's prefix and suffix). (Then when all that is done, take the absolute values convert back to unicode for the final result.)
 
=={{header|Java}}==
Should be able to handle all printable Unicode.
<langsyntaxhighlight lang="java">public class BraceExpansion {
 
public static void main(String[] args) {
Line 1,456 ⟶ 2,132:
}
}
}</langsyntaxhighlight>
 
<pre>Itemized, please.
Line 1,479 ⟶ 2,155:
{}} some }{,{\\ edge \,}{ cases, {here} \\\\\}
{}} some }{,{\\ edge \,}{ cases, {here} \\\\\}</pre>
 
 
=={{header|JavaScript}}==
 
Line 1,491 ⟶ 2,165:
Each node of the parse tree consists of one of two simple functions (AND: syntagmatic concatenation, OR: flattening of paradigms) with a set of arguments, each of which may be a plain string or an AND or OR subtree. The expansions are derived by evaluating the parse tree as an expression.
 
<langsyntaxhighlight JavaScriptlang="javascript">(function () {
'use strict'
 
// Index of any closing brace matching the opening brace at iPosn
// brace at iPosn,
// with the indices of any immediately-enclosed commas
// with the indices of any immediately-enclosed commas.
function bracePair(tkns, iPosn, iNest, lstCommas) {
iffunction bracePair(iPosn >= tkns.length ||, iPosn, <iNest, 0lstCommas) return null;{
if (iPosn >= tkns.length || iPosn < 0) return null;
 
var t = tkns[iPosn],
n = (t === '{') ? iNestn + 1 := (t === '}{') ? iNest - 1 : iNest),(
lst = (t === ',' && iNest === 1) ? lstCommas.concat(iPosn)iNest :+ lstCommas;1
) : (t === '}' ? (
iNest - 1
) : iNest),
lst = (t === ',' && iNest === 1) ? (
lstCommas.concat(iPosn)
) : lstCommas;
 
return n ? bracePair(tkns, iPosn + 1, n, lst) : {
close: iPosn,
commas: lst
};
}
 
// Parse of a SYNTAGM subtree
function andTree(dctSofar, tkns) {
if (!tkns.length) return [dctSofar, []];
 
var dctParse = dctSofar ? dctSofar : {
fn: and,
args: []
},
 
head = tkns[0],
tail = head ? tkns.slice(1) : [],
 
dctBrace = head === '{' ? bracePair(
tkns, 0, 0, []
) : null,
 
lstOR = dctBrace && dctBrace.close && dctBrace.commas.length ? (
dctBrace.close
splitAt(dctBrace.close + 1, tkns)
) && dctBrace.commas.length ? (
) : null;
splitAt(dctBrace.close + 1, tkns)
) : null;
 
return andTree({
fn: and,
args: dctParse.args.concat(
lstOR ? (
lstOR ? orTree(dctParse, lstOR[0], dctBrace.commas) : head
orTree(dctParse, lstOR[0], dctBrace.commas)
)
}, lstOR ? lstOR[1] : tail ); : head
)
}
}, lstOR ? (
lstOR[1]
) : tail);
}
 
// Parse of a PARADIGM subtree
function orTree(dctSofar, tkns, lstCommas) {
if (!tkns.length) return [dctSofar, []];
var iLast = lstCommas.length;
 
return {
fn: or,
args: splitsAt(
lstCommas, tkns
).map(function (x, i) {
var ts = x.slice(1, i === iLast ? -1var :ts void= 0);x.slice(
1, i === iLast ? (
-1
) : void 0
);
 
return ts.length ? ts : [''];
}).map(function (ts) {
return ts.length > 1 ? andTree(null, ts)[0] : ts[0];
andTree(null, ts)[0]
})
) : ts[0];
};
})
};
}
 
// List of unescaped braces and commas, and remaining strings
function tokens(str) {
// Filter function excludes empty splitting artefacts
var toS = function (x) {
return x.toString();
};
 
return str.split(/(\\\\)/).filter(toS).reduce(function (a, s) {
return a.concat(s.charAt(0) === '\\' ? s : s.split(
/(\\*[{,}])/
).filter(toS));
}, []);
}
 
// PARSE TREE OPERATOR (1 of 2)
// Each possible head * each possible tail
function and(args) {
var lng = args.length,
head = lng ? args[0] : null,
lstHead = "string" === typeof head ? [head] : head;(
[head]
) : head;
 
return lng ? (
1 < lng ? lstHead.reduce(function (a, h) {
return a.concat(and(args.slice(1)).map(function (t) {
return h + and(args.slice(1)).map(function (t;) {
})) return h + t;
}, []) : lstHead })
) : [] );
}, []) : lstHead
}
) : [];
}
 
// PARSE TREE OPERATOR (2 of 2)
// Each option flattened
function or(args) {
return args.reduce(function (a, b) {
return a.concat(b);
}, []);
}
 
// One list split into two (first sublist length n)
function splitAt(n, lst) {
return n < lst.length + 1 ? [lst.slice(0, n), lst.slice(n)] : [lst, []];
lst.slice(0, n), lst.slice(n)
}
] : [lst, []];
}
 
// One list split into several (sublist lengths [n])
function splitsAt(lstN, lst) {
return lstN.reduceRight(function (a, x) {
return splitAt(x, a[0]).concat(a.slice(1));
}, [lst]);
}
 
// Value of the parse tree
function evaluated(e) {
return typeof e === 'string' ? e :
e.fn(e.args.map(evaluated));
}
 
// JSON prettyprint (for parse tree, token list etc)
function pp(e) {
return JSON.stringify(e, function (k, v) {
return typeof v === 'function' ? (
'[function ' + v.name + ']'
) : v;
}, 2)
}
 
 
// ----------------------- MAIN ------------------------
// MAIN
 
// s -> [s]
function expansions(s) {
// BRACE EXPRESSION PARSED
var dctParse = andTree(null, tokens(s))[0];
 
// ABSTRACT SYNTAX TREE LOGGED
console.log(pp(dctParse));
 
// AST EVALUATED TO LIST OF STRINGS
return evaluated(dctParse);
}
 
 
// Sample expressions, double-escaped for quotation in source code.
// double-escaped for quotation in source code.
var lstTests = [
var lstTests = [
'~/{Downloads,Pictures}/*.{jpg,gif,png}',
'It~/{{emDownloads,alicPictures}iz,erat}e/*.{djpg,}gif, please.png}',
'It{,{em,gotta havealic}iz,erat}e{ d,\\}, again\\, }}more }cowbell!please.',
'{}} some } '{,{\\\\,gotta have{ edge, edge} \\,}{ casesagain\\, {here}}more \\\\\\\\\\}cowbell!',
'{}} some }{,{\\\\{ edge, edge} \\,}{ cases, {here} \\\\\\\\\\}'
];
];
 
 
// 1. Return each expression with an indented list of its expansions, while
// 2. logging each parse tree to the console.log() stream
 
return lstTests.map(function (s) {
return s + '\n\n' + expansions(s).map(function (x) {
return ' ' + x;
}).join('\n');
}).join('\n\n');
 
})();</langsyntaxhighlight>
 
Value returned by function:
Line 1,691 ⟶ 2,391:
Sample of parse trees logged to the console:
 
<syntaxhighlight lang="javascript">{
<lang JavaScript>{
"fn": "[function and]",
"args": [
Line 1,725 ⟶ 2,425:
" please."
]
}</langsyntaxhighlight>
=={{header|jq}}==
''Adapted from [[#Wren|Wren]]''
 
{{works with|jq}}
'''Also works with gojq, the Go implementation of jq'''
 
<syntaxhighlight lang=jq>
# Input: a string
# Emit an array of the expansions
def expand:
 
# Emit [ array, string ]
def getItem($depth):
 
def getGroup($depth):
{ out: [], comma: false, s: . }
| until (.s == "" or .return;
(.s | getItem($depth)) as $t
| $t[0] as $g
| .s = $t[1]
| if .s == "" then .return = [[], ""]
else .out += $g
| if .s[0:1] == "}"
then if .comma then .return = [.out, .s[1:]]
else .return = [ (.out | map( "{" + . + "}" )), .s[1:]]
end
else if .s[0:1] == ","
then .comma = true
| .s |= .[1:]
else .
end
end
end)
| if .return then .return else [[], ""] end ;
 
{ out: [""], s: .}
| until( (.s == "") or .return;
.c = .s[0:1]
| if ($depth > 0) and (.c == "," or .c == "}")
then .return = [.out, .s]
else .cont = false
| if .c == "{"
then (.s[1:] | getGroup($depth+1)) as $x
| if $x[0] | length > 0
# conform to the "lexicographic" ordering requirement
then .out |= [ .[] as $o | $o + $x[0][] ]
| .s = $x[1]
| .cont = true
else .
end
else .
end
end
| if (.cont | not)
then if (.c == "\\") and ((.s|length) > 1)
then .c += .s[1:2]
| .s |= .[1:]
else .
end
| .out = [.out[] + .c]
| .s |= .[1:]
else .
end )
| if .return then .return else [.out, .s] end ;
 
getItem(0)[0];
def inputs: [
"~/{Downloads,Pictures}/*.{jpg,gif,png}",
"It{{em,alic}iz,erat}e{d,}, please.",
"{,{,gotta have{ ,\\, again\\, }}more }cowbell!",
"{}} some }{,{\\\\{ edge, edge} \\,}{ cases, {here} \\\\\\\\\\}"
];
 
inputs[]
| "\n",
.,
" " + expand[]
</syntaxhighlight>
{{output}}
<pre>
~/{Downloads,Pictures}/*.{jpg,gif,png}
~/Downloads/*.jpg
~/Downloads/*.gif
~/Downloads/*.png
~/Pictures/*.jpg
~/Pictures/*.gif
~/Pictures/*.png
 
 
It{{em,alic}iz,erat}e{d,}, please.
Itemized, please.
Itemize, please.
Italicized, please.
Italicize, please.
Iterated, please.
Iterate, please.
 
 
{,{,gotta have{ ,\, again\, }}more }cowbell!
cowbell!
more cowbell!
gotta have more cowbell!
gotta have\, again\, more cowbell!
 
 
{}} some }{,{\\{ edge, edge} \,}{ cases, {here} \\\\\}
{}} some }{,{\\ edge \,}{ cases, {here} \\\\\}
{}} some }{,{\\ edge \,}{ cases, {here} \\\\\}
</pre>
 
=={{header|Julia}}==
{{trans|Python}} <langsyntaxhighlight lang="julia"> function getitem(s, depth=0)
out = [""]
while s != ""
Line 1,780 ⟶ 2,589:
println(ans)
end
end </langsyntaxhighlight> {{output}} <pre>
~/{Downloads,Pictures}/*.{jpg,gif,png}
--------------------------------------------
Line 1,814 ⟶ 2,623:
=={{header|Kotlin}}==
{{trans|Java}}
<langsyntaxhighlight lang="scala">// version 1.1.2
 
object BraceExpansion {
Line 1,865 ⟶ 2,674:
BraceExpansion.expand(s)
}
}</langsyntaxhighlight>
 
{{out}}
Line 1,891 ⟶ 2,700:
{}} some }{,{\\ edge \,}{ cases, {here} \\\\\}
</pre>
=={{header|Lua}}==
 
{{trans|Python}}
 
Note that this code isn't very memory efficient.
 
<syntaxhighlight lang="lua">local function wrapEachItem(items, prefix, suffix)
local itemsWrapped = {}
 
for i, item in ipairs(items) do
itemsWrapped[i] = prefix .. item .. suffix
end
 
return itemsWrapped
end
 
local function getAllItemCombinationsConcatenated(aItems, bItems)
local combinations = {}
 
for _, a in ipairs(aItems) do
for _, b in ipairs(bItems) do
table.insert(combinations, a..b)
end
end
 
return combinations
end
 
local getItems -- Forward declaration.
 
local function getGroup(s, pos, depth)
local groupItems = {}
local foundComma = false
 
while pos <= #s do
local items
items, pos = getItems(s, pos, depth)
if pos > #s then break end
 
for _, item in ipairs(items) do
table.insert(groupItems, item)
end
 
local c = s:sub(pos, pos)
 
if c == "}" then -- Possibly end of group.
if foundComma then return groupItems, pos+1 end
return wrapEachItem(groupItems, "{", "}"), pos+1 -- No group.
 
elseif c == "," then
foundComma, pos = true, pos+1
end
end
 
return nil -- No group.
end
 
function getItems(s, pos, depth)
local items = {""}
 
while pos <= #s do
local c = s:sub(pos, pos)
 
if depth > 0 and (c == "," or c == "}") then -- End of item in surrounding group.
return items, pos
end
 
local groupItems, nextPos = nil
if c == "{" then -- Possibly start of a group.
groupItems, nextPos = getGroup(s, pos+1, depth+1)
end
 
if groupItems then
items, pos = getAllItemCombinationsConcatenated(items, groupItems), nextPos
else
if c == "\\" and pos < #s then -- Escaped character.
pos = pos + 1
c = c .. s:sub(pos, pos)
end
items, pos = wrapEachItem(items, "", c), pos+1
end
end
 
return items, pos
end
 
local tests = [[
~/{Downloads,Pictures}/*.{jpg,gif,png}
It{{em,alic}iz,erat}e{d,}, please.
{,{,gotta have{ ,\, again\, }}more }cowbell!
{}} some }{,{\\{ edge, edge} \,}{ cases, {here} \\\\\}
]]
 
for test in tests:gmatch"[^\n]+" do
print(test)
for _, item in ipairs(getItems(test, 1, 0)) do
print("\t"..item)
end
print()
end</syntaxhighlight>
 
{{out}}
<pre>
~/{Downloads,Pictures}/*.{jpg,gif,png}
~/Downloads/*.jpg
~/Downloads/*.gif
~/Downloads/*.png
~/Pictures/*.jpg
~/Pictures/*.gif
~/Pictures/*.png
...
{}} some }{,{\\{ edge, edge} \,}{ cases, {here} \\\\\}
{}} some }{,{\\ edge \,}{ cases, {here} \\\\\}
{}} some }{,{\\ edge \,}{ cases, {here} \\\\\}
</pre>
=={{header|Mathematica}}/{{header|Wolfram Language}}==
<syntaxhighlight lang="mathematica">(*The strategy is to first capture all special sub-expressions and reformat them so they are semantically clear. The built in function Distribute could then do the work of creating the alternatives, but the order wouldn't match that given in the instructions (although as a set the alternatives would be correct). I'll take a more complicated route so as to follow the instructions exactly.*)
 
(*A few named constants for readability.*)
EscapeToken="\\";(*In Mathematica, backslash is an escape character when inputing a string, so we need to escape it.*)
LeftBraceToken="{";
RightBraceToken="}";
 
(*This basically sequesters escaped substrings so that they don't get matched during later processing.*)
CaptureEscapes[exp:{___String}]:=SequenceReplace[exp,{EscapeToken,x_}:>EscapeToken<>x];
 
(*Any remaining braces are un-escaped. I'm "unstringifying" them to more easily pick them out during later processing.*)
CaptureBraces[exp:{___String}]:=ReplaceAll[exp,{LeftBraceToken->LeftBrace,RightBraceToken->RightBrace}];
 
(*Building up trees for the braced expressions. Extra braces are just raw data, so transform them back to strings.*)
CaptureBraceTrees[exp:{(_String|LeftBrace|RightBrace)...}]:=ReplaceAll[FixedPoint[SequenceReplace[{LeftBrace,seq:(_String|_BraceTree)...,RightBrace}:>BraceTree[seq]],exp],{LeftBrace->LeftBraceToken,RightBrace->RightBraceToken}];
 
(*At thie point, we should have an expression with well-braced substructures representing potential alternatives. We must expand brace trees to alternatives in the correct order.*)
ExpandBraceTrees[exp:Expr[head___String,bt_BraceTree,tail___]]:=ReplaceAll[Thread[Expr[head,ToAlternatives[bt],tail]],alt_Alt:>Sequence@@alt];
ExpandBraceTrees[exp:Expr[___String]]:={exp};
ExpandBraceTrees[exps:{__Expr}]:=Catenate[ExpandBraceTrees/@exps];
 
(*If there are no commas, then it's a literal sub-expression. Otherwise, it's a set of alternatives.*)
ToAlternatives[bt_BraceTree]:={LeftBraceToken<>StringJoin@@bt<>RightBraceToken}/;FreeQ[bt,","];
ToAlternatives[BraceTree[","]]=ToAlternatives[BraceTree["",",",""]];
ToAlternatives[bt:BraceTree[",",__]]:=ToAlternatives[Prepend[bt,""]];
ToAlternatives[bt:BraceTree[__,","]]:=ToAlternatives[Append[bt,""]];
ToAlternatives[bt_BraceTree]:=Alt@@@SequenceSplit[List@@bt,{","}];
 
NormalizeExpression=Apply[Expr]@*CaptureBraceTrees@*CaptureBraces@*CaptureEscapes@*Characters;
 
BraceExpand[str_String]:=ReplaceAll[FixedPoint[ExpandBraceTrees,NormalizeExpression[str]],Expr->StringJoin];
 
(*Data was stored in a local file.*)
BraceTestData=ReadList[FileNameJoin[{NotebookDirectory[],"BraceTestData.txt"}],String];BraceTestData//TableForm</syntaxhighlight>
<pre>~/{Downloads,Pictures}/*.{jpg,gif,png}
It{{em,alic}iz,erat}e{d,}, please.
{,{,gotta have{ ,\, again\, }}more }cowbell!
{}} some }{,{\\{ edge, edge} \,}{ cases, {here} \\\\\}</pre>
 
{{out}}
<pre>Column[Column /@ BraceExpand /@ BraceTestData, Left, 2]</pre>
<pre>~/Downloads/*.jpg
~/Downloads/*.gif
~/Downloads/*.png
~/Pictures/*.jpg
~/Pictures/*.gif
~/Pictures/*.png
 
 
Itemized, please.
Itemize, please.
Italicized, please.
Italicize, please.
Iterated, please.
Iterate, please.
 
 
cowbell!
more cowbell!
gotta have more cowbell!
gotta have\, again\, more cowbell!
 
 
{}} some }{,\\ edge \,{ cases, {here} \\\\\}
{}} some }{,\\ edge \,{ cases, {here} \\\\\}</pre>
=={{header|Nim}}==
{{trans|Seed7}}
<syntaxhighlight lang="nim">proc expandBraces(str: string) =
 
var
escaped = false
depth = 0
bracePoints: seq[int]
bracesToParse: seq[int]
 
for idx, ch in str:
case ch
of '\\':
escaped = not escaped
of '{':
inc depth
if not escaped and depth == 1:
bracePoints = @[idx]
of ',':
if not escaped and depth == 1:
bracePoints &= idx
of '}':
if not escaped and depth == 1 and bracePoints.len >= 2:
bracesToParse = bracePoints & idx
dec depth
else:
discard
if ch != '\\':
escaped = false
 
if bracesToParse.len > 0:
let prefix = str[0..<bracesToParse[0]]
let suffix = str[(bracesToParse[^1] + 1)..^1]
for idx in 1..bracesToParse.high:
let option = str[(bracesToParse[idx - 1] + 1)..(bracesToParse[idx] - 1)]
expandBraces(prefix & option & suffix)
 
else:
echo " ", str
 
#———————————————————————————————————————————————————————————————————————————————————————————————————
 
when isMainModule:
 
for str in ["It{{em,alic}iz,erat}e{d,}, please.",
"~/{Downloads,Pictures}/*.{jpg,gif,png}",
"{,{,gotta have{ ,\\, again\\, }}more }cowbell!",
"{}} some }{,{\\\\{ edge, edge} \\,}{ cases, {here} \\\\\\\\\\}"]:
echo "\nExpansions of \"", str, "\":"
expandBraces(str)</syntaxhighlight>
 
{{out}}
<pre>
Expansions of "It{{em,alic}iz,erat}e{d,}, please.":
Itemized, please.
Italicized, please.
Iterated, please.
Itemize, please.
Italicize, please.
Iterate, please.
 
Expansions of "~/{Downloads,Pictures}/*.{jpg,gif,png}":
~/Downloads/*.jpg
~/Pictures/*.jpg
~/Downloads/*.gif
~/Pictures/*.gif
~/Downloads/*.png
~/Pictures/*.png
 
Expansions of "{,{,gotta have{ ,\, again\, }}more }cowbell!":
cowbell!
more cowbell!
gotta have more cowbell!
gotta have\, again\, more cowbell!
 
Expansions of "{}} some }{,{\\{ edge, edge} \,}{ cases, {here} \\\\\}":
{}} some }{,{\\ edge \,}{ cases, {here} \\\\\}
{}} some }{,{\\ edge \,}{ cases, {here} \\\\\}</pre>
=={{header|Perl}}==
 
Line 1,899 ⟶ 2,966:
So here is a manual solution that implements the specification precisely:
 
<langsyntaxhighlight lang="perl">sub brace_expand {
my $input = shift;
my @stack = ([my $current = ['']]);
Line 1,939 ⟶ 3,006:
return @$current;
}</langsyntaxhighlight>
 
Usage demonstration:
<langsyntaxhighlight lang="perl">while (my $input = <DATA>) {
chomp($input);
print "$input\n";
Line 1,953 ⟶ 3,020:
It{{em,alic}iz,erat}e{d,}, please.
{,{,gotta have{ ,\, again\, }}more }cowbell!
{}} some }{,{\\{ edge, edge} \,}{ cases, {here} \\\\\}</langsyntaxhighlight>
{{out}}
<pre>
Line 1,983 ⟶ 3,050:
 
</pre>
=={{header|Phix}}==
Fairly straightforward recursive solution
<!--<syntaxhighlight lang="phix">(phixonline)-->
<span style="color: #000080;font-style:italic;">-- demo\rosetta\Brace_expansion.exw</span>
<span style="color: #008080;">with</span> <span style="color: #008080;">javascript_semantics</span>
<span style="color: #008080;">function</span> <span style="color: #000000;">pair</span><span style="color: #0000FF;">(</span><span style="color: #004080;">sequence</span> <span style="color: #000000;">stems</span><span style="color: #0000FF;">,</span> <span style="color: #004080;">sequence</span> <span style="color: #000000;">brest</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: #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;">stems</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;">brest</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;">stems</span><span style="color: #0000FF;">[</span><span style="color: #000000;">i</span><span style="color: #0000FF;">]&</span><span style="color: #000000;">brest</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: #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;">function</span> <span style="color: #000000;">brarse</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;">integer</span> <span style="color: #000000;">idx</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">1</span>
<span style="color: #008080;">while</span> <span style="color: #000000;">idx</span><span style="color: #0000FF;"><=</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;">ch</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">s</span><span style="color: #0000FF;">[</span><span style="color: #000000;">idx</span><span style="color: #0000FF;">]</span>
<span style="color: #008080;">if</span> <span style="color: #000000;">ch</span><span style="color: #0000FF;">=</span><span style="color: #008000;">'{'</span> <span style="color: #008080;">then</span>
<span style="color: #004080;">sequence</span> <span style="color: #000000;">alts</span> <span style="color: #0000FF;">=</span> <span style="color: #0000FF;">{</span><span style="color: #000000;">idx</span><span style="color: #0000FF;">}</span>
<span style="color: #000000;">idx</span> <span style="color: #0000FF;">+=</span> <span style="color: #000000;">1</span>
<span style="color: #004080;">integer</span> <span style="color: #000000;">l0</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">idx</span>
<span style="color: #004080;">bool</span> <span style="color: #000000;">nest</span> <span style="color: #0000FF;">=</span> <span style="color: #004600;">false</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">bl0</span> <span style="color: #0000FF;">=</span> <span style="color: #004600;">false</span>
<span style="color: #004080;">integer</span> <span style="color: #000000;">level</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">1</span>
<span style="color: #008080;">while</span> <span style="color: #000000;">idx</span><span style="color: #0000FF;"><=</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: #008080;">switch</span> <span style="color: #000000;">s</span><span style="color: #0000FF;">[</span><span style="color: #000000;">idx</span><span style="color: #0000FF;">]</span> <span style="color: #008080;">do</span>
<span style="color: #008080;">case</span> <span style="color: #008000;">'{'</span><span style="color: #0000FF;">:</span> <span style="color: #000000;">level</span> <span style="color: #0000FF;">+=</span> <span style="color: #000000;">1</span>
<span style="color: #000000;">nest</span> <span style="color: #0000FF;">=</span> <span style="color: #004600;">true</span>
<span style="color: #008080;">case</span> <span style="color: #008000;">'}'</span><span style="color: #0000FF;">:</span> <span style="color: #000000;">level</span> <span style="color: #0000FF;">-=</span> <span style="color: #000000;">1</span>
<span style="color: #000000;">bl0</span> <span style="color: #0000FF;">=</span> <span style="color: #0000FF;">(</span><span style="color: #000000;">level</span><span style="color: #0000FF;">=</span><span style="color: #000000;">0</span><span style="color: #0000FF;">)</span>
<span style="color: #008080;">case</span> <span style="color: #008000;">','</span><span style="color: #0000FF;">:</span> <span style="color: #008080;">if</span> <span style="color: #000000;">level</span><span style="color: #0000FF;">=</span><span style="color: #000000;">1</span> <span style="color: #008080;">then</span>
<span style="color: #000000;">alts</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">append</span><span style="color: #0000FF;">(</span><span style="color: #000000;">alts</span><span style="color: #0000FF;">,</span><span style="color: #000000;">idx</span><span style="color: #0000FF;">)</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
<span style="color: #008080;">case</span> <span style="color: #008000;">'\\'</span><span style="color: #0000FF;">:</span> <span style="color: #000000;">idx</span> <span style="color: #0000FF;">+=</span> <span style="color: #000000;">1</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">switch</span>
<span style="color: #008080;">if</span> <span style="color: #000000;">bl0</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: #000000;">idx</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;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">alts</span><span style="color: #0000FF;">)></span><span style="color: #000000;">1</span> <span style="color: #008080;">and</span> <span style="color: #000000;">level</span><span style="color: #0000FF;">=</span><span style="color: #000000;">0</span> <span style="color: #008080;">then</span>
<span style="color: #000000;">alts</span> <span style="color: #0000FF;">&=</span> <span style="color: #000000;">idx</span>
<span style="color: #004080;">sequence</span> <span style="color: #000000;">stems</span> <span style="color: #0000FF;">=</span> <span style="color: #0000FF;">{}</span>
<span style="color: #004080;">string</span> <span style="color: #000000;">stem</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">s</span><span style="color: #0000FF;">[</span><span style="color: #000000;">1</span><span style="color: #0000FF;">..</span><span style="color: #000000;">alts</span><span style="color: #0000FF;">[</span><span style="color: #000000;">1</span><span style="color: #0000FF;">]-</span><span style="color: #000000;">1</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;">2</span> <span style="color: #008080;">to</span> <span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">alts</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">do</span>
<span style="color: #004080;">string</span> <span style="color: #000000;">rest</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">s</span><span style="color: #0000FF;">[</span><span style="color: #000000;">alts</span><span style="color: #0000FF;">[</span><span style="color: #000000;">i</span><span style="color: #0000FF;">-</span><span style="color: #000000;">1</span><span style="color: #0000FF;">]+</span><span style="color: #000000;">1</span><span style="color: #0000FF;">..</span><span style="color: #000000;">alts</span><span style="color: #0000FF;">[</span><span style="color: #000000;">i</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: #000000;">nest</span> <span style="color: #008080;">then</span>
<span style="color: #004080;">sequence</span> <span style="color: #000000;">inners</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">brarse</span><span style="color: #0000FF;">(</span><span style="color: #000000;">rest</span><span style="color: #0000FF;">)</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;">inners</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">do</span>
<span style="color: #000000;">stems</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">append</span><span style="color: #0000FF;">(</span><span style="color: #000000;">stems</span><span style="color: #0000FF;">,</span><span style="color: #000000;">stem</span><span style="color: #0000FF;">&</span><span style="color: #000000;">inners</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;">else</span>
<span style="color: #000000;">stems</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">append</span><span style="color: #0000FF;">(</span><span style="color: #000000;">stems</span><span style="color: #0000FF;">,</span><span style="color: #000000;">stem</span><span style="color: #0000FF;">&</span><span style="color: #000000;">rest</span><span style="color: #0000FF;">)</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;">return</span> <span style="color: #000000;">pair</span><span style="color: #0000FF;">(</span><span style="color: #000000;">stems</span><span style="color: #0000FF;">,</span><span style="color: #000000;">brarse</span><span style="color: #0000FF;">(</span><span style="color: #000000;">s</span><span style="color: #0000FF;">[</span><span style="color: #000000;">idx</span><span style="color: #0000FF;">+</span><span style="color: #000000;">1</span><span style="color: #0000FF;">..$]))</span>
<span style="color: #008080;">elsif</span> <span style="color: #000000;">nest</span> <span style="color: #008080;">then</span>
<span style="color: #008080;">return</span> <span style="color: #000000;">pair</span><span style="color: #0000FF;">({</span><span style="color: #000000;">s</span><span style="color: #0000FF;">[</span><span style="color: #000000;">1</span><span style="color: #0000FF;">..</span><span style="color: #000000;">l0</span><span style="color: #0000FF;">-</span><span style="color: #000000;">1</span><span style="color: #0000FF;">]},</span><span style="color: #000000;">brarse</span><span style="color: #0000FF;">(</span><span style="color: #000000;">s</span><span style="color: #0000FF;">[</span><span style="color: #000000;">l0</span><span style="color: #0000FF;">..$]))</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: #000000;">idx</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;">return</span> <span style="color: #0000FF;">{</span><span style="color: #000000;">s</span><span style="color: #0000FF;">}</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">function</span>
<span style="color: #000080;font-style:italic;">-- (since ? and pp() add their own backslash escapes:)</span>
<span style="color: #008080;">procedure</span> <span style="color: #000000;">edump</span><span style="color: #0000FF;">(</span><span style="color: #004080;">sequence</span> <span style="color: #000000;">x</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;">x</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">do</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\n"</span><span style="color: #0000FF;">,{</span><span style="color: #000000;">x</span><span style="color: #0000FF;">[</span><span style="color: #000000;">i</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;">procedure</span>
<span style="color: #000000;">edump</span><span style="color: #0000FF;">(</span><span style="color: #000000;">brarse</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"~/{Downloads,Pictures}/*.{jpg,gif,png}"</span><span style="color: #0000FF;">))</span>
<span style="color: #000000;">edump</span><span style="color: #0000FF;">(</span><span style="color: #000000;">brarse</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"It{{em,alic}iz,erat}e{d,}, please."</span><span style="color: #0000FF;">))</span>
<span style="color: #000000;">edump</span><span style="color: #0000FF;">(</span><span style="color: #000000;">brarse</span><span style="color: #0000FF;">(</span><span style="color: #008000;">`{,{,gotta have{ ,\, again\, }}more }cowbell!`</span><span style="color: #0000FF;">))</span>
<span style="color: #000000;">edump</span><span style="color: #0000FF;">(</span><span style="color: #000000;">brarse</span><span style="color: #0000FF;">(</span><span style="color: #008000;">`{}} some }{,{\\{ edge, edge} \,}{ cases, {here} \\\\\}`</span><span style="color: #0000FF;">))</span>
<!--</syntaxhighlight>-->
{{Out}}
<pre>
~/Downloads/*.jpg
~/Downloads/*.gif
~/Downloads/*.png
~/Pictures/*.jpg
~/Pictures/*.gif
~/Pictures/*.png
Itemized, please.
Itemize, please.
Italicized, please.
Italicize, please.
Iterated, please.
Iterate, please.
cowbell!
more cowbell!
gotta have more cowbell!
gotta have\, again\, more cowbell!
{}} some }{,{\\ edge \,}{ cases, {here} \\\\\}
{}} some }{,{\\ edge \,}{ cases, {here} \\\\\}
</pre>
=={{header|PHP}}==
{{trans|Python}}
<syntaxhighlight lang="php">function getitem($s,$depth=0) {
$out = [''];
while ($s) {
$c = $s[0];
if ($depth && ($c == ',' || $c == '}')) {
return [$out, $s];
}
if ($c == '{') {
$x = getgroup(substr($s, 1), $depth + 1);
if($x) {
$tmp = [];
foreach($out as $a) {
foreach($x[0] as $b) {
$tmp[] = $a . $b;
}
}
$out = $tmp;
$s = $x[1];
continue;
}
}
if ($c == '\\' && strlen($s) > 1) {
list($s, $c) = [substr($s, 1), ($c . $s[1])];
}
 
$tmp = [];
=={{header|Perl 6}}==
foreach($out as $a) {
The task description allows the taking of shortcuts, but please note that we are not taking any shortcuts here. The solution is short because this particular problem happens to map quite naturally to the strengths of Perl 6.
$tmp[] = $a . $c;
 
}
First, the parsing is handled with a grammar that can backtrack in the few places this problem needs it. The <tt>+</tt> quantifier matches one or more alternatives (we handle the case of a single alternative in the walk now), and the <tt>%</tt> modifier requires a comma between each quantified item to its left. Note that the <tt>*</tt> quantifiers do <i>not</i> backtrack here, because the <tt>token</tt> keyword suppresses that; all the backtracking here fails over to a different alternative in an outer alternation (that is, things separated by the <tt>|</tt> character in the grammar. Most of these failovers just nibble an uninteresting character and continue.)
$out = $tmp;
 
$s = substr($s, 1);
On the other end, we recursively walk the parse tree returning expanded sublists, and we do the cartesian concatenation of sublists at each level by use of the <tt>X~</tt> operator, which is a "cross" metaoperator used on a simple <tt>~</tt> concatenation. As a list infix operator, <tt>X~</tt> does not care how many items are on either side, which is just what you want in this case, since some of the arguments are strings and some are lists. Here we use a fold or reduction form in square brackets to interpose the cross-concat between each value generated by the map, which returns a mixture of lists and literal strings. One other thing that might not be obvious: if we bind to the match variable, <tt>$/</tt>, we automatically get all the syntactic sugar for its submatches. In this case, <tt>$0</tt> is short for <tt>$/[0]</tt>, and represents all the submatches captured by 0th set of parens in either <tt>TOP</tt> or <tt>alt</tt>. <tt>$&lt;meta&gt;</tt> is likewise short for <tt>$/&lt;meta&gt;</tt>, and retrieves what was captured by that named submatch.
<lang perl6>grammar BraceExpansion {
}
token TOP { ( <meta> | . )* }
return [$out, $s];
token meta { '{' <alts> '}' | \\ . }
token alts { <alt>+ % ',' }
token alt { ( <meta> | <-[ , } ]> )* }
}
function getgroup($s,$depth) {
list($out, $comma) = [[], false];
while ($s) {
list($g, $s) = getitem($s, $depth);
if (!$s) {
break;
}
$out = array_merge($out, $g);
if ($s[0] == '}') {
if ($comma) {
return [$out, substr($s, 1)];
}
 
$tmp = [];
sub crosswalk($/) {
foreach($out as $a) {
|[X~] flat '', $0.map: -> $/ { $<meta><alts><alt>.&alternatives or ~$/ }
$tmp[] = '{' . $a . '}';
}
return [$tmp, substr($s, 1)];
}
if ($s[0] == ',') {
list($comma, $s) = [true, substr($s, 1)];
}
}
return null;
}
 
$lines = <<< 'END'
sub alternatives($_) {
~/{Downloads,Pictures}/*.{jpg,gif,png}
when :not { () }
It{{em,alic}iz,erat}e{d,}, please.
when 1 { '{' X~ $_».&crosswalk X~ '}' }
{,{,gotta have{ ,\, again\, }}more }cowbell!
default { $_».&crosswalk }
{}} some }{,{\\{ edge, edge} \,}{ cases, {here} \\\\\}
}
END;
 
foreach( explode("\n", $lines) as $line ) {
sub brace-expand($s) { crosswalk BraceExpansion.parse($s) }
printf("\n%s\n", $line);
 
foreach( getitem($line)[0] as $expansion ) {
# Testing:
printf(" %s\n", $expansion);
 
sub bxtest(*@s) {
for @s -> $s {
say "\n$s";
for brace-expand($s) {
say " ", $_;
}
}
}</syntaxhighlight>
}
 
bxtest Q:to/END/.lines;
~/{Downloads,Pictures}/*.{jpg,gif,png}
It{{em,alic}iz,erat}e{d,}, please.
{,{,gotta have{ ,\, again\, }}more }cowbell!
a{b{1,2}c
a{1,2}b}c
a{1,{2},3}b
more{ darn{ cowbell,},}
ab{c,d\,e{f,g\h},i\,j{k,l\,m}n,o\,p}qr
{a,{\,b}c
a{b,{{c}}
{a{\}b,c}d
END</lang>
{{out}}
<pre>
<pre>~/{Downloads,Pictures}/*.{jpg,gif,png}
~/{Downloads,Pictures}/*.{jpg,gif,png}
~/Downloads/*.jpg
~/Downloads/*.gif
Line 2,056 ⟶ 3,247:
gotta have\, again\, more cowbell!
 
{}} some }{,{\\{ edge,edgy edge} \,}{ cases, {here} \\\\\}
{}} some }{,{\\ edge \,}{ cases, {here} \\\\\}
{}} some }{,{\\edgy edge \,}{ cases, {here} \\\\\}
 
a{b{1,2}c
a{b1c
a{b2c
 
a{1,2}b}c
a1b}c
a2b}c
 
a{1,{2},3}b
a1b
a{2}b
a3b
 
more{ darn{ cowbell,},}
more darn cowbell
more darn
more
 
ab{c,d\,e{f,g\h},i\,j{k,l\,m}n,o\,p}qr
abcqr
abd\,efqr
abd\,eg\hqr
abi\,jknqr
abi\,jl\,mnqr
abo\,pqr
 
{a,{\,b}c
{a,{\,b}c
 
a{b,{{c}}
a{b,{{c}}
 
{a{\}b,c}d
{a\}bd
{acd</pre>
 
=={{header|Phix}}==
Fairly straightforward recursive solution
<lang Phix>--
-- demo\rosetta\Brace_expansion.exw
-- ================================
--
function pair(sequence stems, sequence brest)
sequence res = {}
for i=1 to length(stems) do
for j=1 to length(brest) do
res = append(res,stems[i]&brest[j])
end for
end for
return res
end function
 
function brarse(string s)
integer idx = 1
while idx<=length(s) do
integer ch = s[idx]
if ch='{' then
sequence alts = {idx}
idx += 1
integer l0 = idx
bool nest = false
integer level = 1
while idx<=length(s) do
switch s[idx] do
case '{': level += 1
nest = true
case '}': level -= 1
if level=0 then exit end if
case ',': if level=1 then
alts = append(alts,idx)
end if
case '\\': idx += 1
end switch
idx += 1
end while
if length(alts)>1 and level=0 then
alts &= idx
sequence stems = {}
string stem = s[1..alts[1]-1]
for i=2 to length(alts) do
string rest = s[alts[i-1]+1..alts[i]-1]
if nest then
sequence inners = brarse(rest)
for j=1 to length(inners) do
stems = append(stems,stem&inners[j])
end for
else
stems = append(stems,stem&rest)
end if
end for
return pair(stems,brarse(s[idx+1..$]))
elsif nest then
return pair({s[1..l0-1]},brarse(s[l0..$]))
end if
end if
idx += 1
end while
return {s}
end function
 
-- (since ? and pp() add their own backslash escapes:)
procedure edump(sequence x)
for i=1 to length(x) do
printf(1,"%s\n",{x[i]})
end for
end procedure
 
edump(brarse("~/{Downloads,Pictures}/*.{jpg,gif,png}"))
edump(brarse("It{{em,alic}iz,erat}e{d,}, please."))
edump(brarse(`{,{,gotta have{ ,\, again\, }}more }cowbell!`))
edump(brarse(`{}} some }{,{\\{ edge, edge} \,}{ cases, {here} \\\\\}`))</lang>
{{Out}}
<pre>
~/Downloads/*.jpg
~/Downloads/*.gif
~/Downloads/*.png
~/Pictures/*.jpg
~/Pictures/*.gif
~/Pictures/*.png
Itemized, please.
Itemize, please.
Italicized, please.
Italicize, please.
Iterated, please.
Iterate, please.
cowbell!
more cowbell!
gotta have more cowbell!
gotta have\, again\, more cowbell!
{}} some }{,{\\ edge \,}{ cases, {here} \\\\\}
{}} some }{,{\\ edge \,}{ cases, {here} \\\\\}
</pre>
 
=={{header|PicoLisp}}==
<langsyntaxhighlight PicoLisplang="picolisp">(de braceExpand (Str)
(let Lst
(make
Line 2,225 ⟶ 3,283:
(if (pair (car Lst))
(mapcan recurse (car Lst))
(list (car Lst)) ) ) ) ) ) ) )</langsyntaxhighlight>
Test:
<langsyntaxhighlight PicoLisplang="picolisp">(test
(quote
"~/Downloads/*.jpg"
Line 2,259 ⟶ 3,317:
"{}} some }{,{\\ edge \,}{ cases, {here} \\\\\}"
"{}} some }{,{\\ edge \,}{ cases, {here} \\\\\}" )
(braceExpand "{}} some }{,{\\\\{ edge, edge} \\,}{ cases, {here} \\\\\\\\\\}") )</langsyntaxhighlight>
 
=={{header|PowerShell}}==
{{works with|PowerShell|2}}
<syntaxhighlight lang="powershell">
<lang PowerShell>
function Expand-Braces ( [string]$String )
{
Line 2,319 ⟶ 3,376:
}
}
</syntaxhighlight>
</lang>
<syntaxhighlight lang="powershell">
<lang PowerShell>
$TestStrings = @(
'It{{em,alic}iz,erat}e{d,}, please.'
Line 2,335 ⟶ 3,392:
Expand-Braces $String
}
</syntaxhighlight>
</lang>
{{out}}
<pre>
Line 2,368 ⟶ 3,425:
{}} some }{,{\\ edge \,}{ cases, {here} \\\\\}
</pre>
=={{header|Prolog}}==
<syntaxhighlight lang="prolog">
sym(',', commalist) --> ['\\',','], !.
sym(H, Context) --> [H], { not(H = '{'; H = '}'), (Context = commalist -> not(H = ','); true) }.
syms([H|T], Context) --> sym(H, Context), !, syms(T, Context).
syms([], _) --> [].
symbol(Symbol, Context) --> syms(Syms,Context), {atom_chars(Symbol, Syms)}.
 
braces(Member) --> ['{'], commalist(List), ['}'], {length(List, Len), Len > 1, member(Member, List)}.
 
commalist([H|T]) --> sym_braces(H, commalist), [','], commalist(T).
commalist([H]) --> sym_braces(H, commalist).
 
sym_braces(String, Context) --> symbol(S1, Context), braces(S2), sym_braces(S3, Context), {atomics_to_string([S1,S2,S3],String)}.
sym_braces(String, Context) --> braces(S1), symbol(S2, Context), sym_braces(S3, Context), {atomics_to_string([S1,S2,S3],String)}.
sym_braces(String, Context) --> symbol(String, Context).
sym_braces(String, _) --> braces(String).
sym_braces(String, Context) --> ['{'], sym_braces(S2, Context), {atomics_to_string(['{',S2],String)}.
sym_braces(String, Context) --> ['}'], sym_braces(S2, Context), {atomics_to_string(['}',S2],String)}.
 
grammar(String) --> sym_braces(String, braces).
 
brace_expansion(In, Out) :- atom_chars(In, Chars), findall(Out,grammar(Out, Chars, []), List), list_to_set(List, Out).
</syntaxhighlight>
 
Testing:
<syntaxhighlight lang="prolog">
?- brace_expansion("~/{Downloads,Pictures}/*.{jpg,gif,png}", Out).
Out = ["~/Downloads/*.jpg","~/Downloads/*.gif","~/Downloads/*.png","~/Pictures/*.jpg","~/Pictures/*.gif","~/Pictures/*.png"].
 
?- brace_expansion("It{{em,alic}iz,erat}e{d,}, please.", Out).
Out = ["Itemized, please.", "Itemize, please.", "Iterated, please.", "Iterate, please.", "Italicized, please.", "Italicize, please."].
 
?- brace_expansion("{,{,gotta have{ ,\\, again\\, }}more }cowbell!", Out).
Out = ["cowbell!", "more cowbell!", "gotta have more cowbell!", "gotta have, again, more cowbell!"].
 
</syntaxhighlight>
=={{header|Python}}==
<langsyntaxhighlight lang="python">def getitem(s, depth=0):
out = [""]
while s:
Line 2,404 ⟶ 3,497:
return None
 
# stolen cowbells from perl6Raku example
for s in '''~/{Downloads,Pictures}/*.{jpg,gif,png}
It{{em,alic}iz,erat}e{d,}, please.
{,{,gotta have{ ,\, again\, }}more }cowbell!
{}} some }{,{\\\\{ edge, edge} \,}{ cases, {here} \\\\\\\\\}'''.split('\n'):
print "\n\t".join([s] + getitem(s)[0]) + "\n"</langsyntaxhighlight>
 
{{out}}
Line 2,440 ⟶ 3,533:
 
</pre>
 
=={{header|Racket}}==
{{trans|Python}}
<langsyntaxhighlight lang="racket">#lang racket/base
(require racket/match)
(define (merge-lists as . bss)
Line 2,492 ⟶ 3,584:
))
(for ((s (in-list patterns)) #:when (printf "expand: ~a~%" s) (x (in-list (brace-expand s))))
(printf "\t~a~%" x)))</langsyntaxhighlight>
{{out}}
<pre>expand: ~/{Downloads,Pictures}/*.{jpg,gif,png}
Line 2,516 ⟶ 3,608:
{}} some }{,{\\ edge ,}{ cases, {here} \\\\}
{}} some }{,{\\ edge ,}{ cases, {here} \\\\}</pre>
=={{header|Raku}}==
(formerly Perl 6)
The task description allows the taking of shortcuts, but please note that we are not taking any shortcuts here. The solution is short because this particular problem happens to map quite naturally to the strengths of Raku.
 
First, the parsing is handled with a grammar that can backtrack in the few places this problem needs it. The <tt>+</tt> quantifier matches one or more alternatives (we handle the case of a single alternative in the walk now), and the <tt>%</tt> modifier requires a comma between each quantified item to its left. Note that the <tt>*</tt> quantifiers do <i>not</i> backtrack here, because the <tt>token</tt> keyword suppresses that; all the backtracking here fails over to a different alternative in an outer alternation (that is, things separated by the <tt>|</tt> character in the grammar. Most of these failovers just nibble an uninteresting character and continue.)
 
On the other end, we recursively walk the parse tree returning expanded sublists, and we do the cartesian concatenation of sublists at each level by use of the <tt>X~</tt> operator, which is a "cross" metaoperator used on a simple <tt>~</tt> concatenation. As a list infix operator, <tt>X~</tt> does not care how many items are on either side, which is just what you want in this case, since some of the arguments are strings and some are lists. Here we use a fold or reduction form in square brackets to interpose the cross-concat between each value generated by the map, which returns a mixture of lists and literal strings. One other thing that might not be obvious: if we bind to the match variable, <tt>$/</tt>, we automatically get all the syntactic sugar for its submatches. In this case, <tt>$0</tt> is short for <tt>$/[0]</tt>, and represents all the submatches captured by 0th set of parens in either <tt>TOP</tt> or <tt>alt</tt>. <tt>$&lt;meta&gt;</tt> is likewise short for <tt>$/&lt;meta&gt;</tt>, and retrieves what was captured by that named submatch.
<syntaxhighlight lang="raku" line>grammar BraceExpansion {
token TOP { ( <meta> | . )* }
token meta { '{' <alts> '}' | \\ . }
token alts { <alt>+ % ',' }
token alt { ( <meta> | <-[ , } ]> )* }
}
 
sub crosswalk($/) {
|[X~] flat '', $0.map: -> $/ { $<meta><alts><alt>.&alternatives or ~$/ }
}
 
sub alternatives($_) {
when :not { () }
when 1 { '{' X~ $_».&crosswalk X~ '}' }
default { $_».&crosswalk }
}
 
sub brace-expand($s) { crosswalk BraceExpansion.parse($s) }
 
# Testing:
 
sub bxtest(*@s) {
for @s -> $s {
say "\n$s";
for brace-expand($s) {
say " ", $_;
}
}
}
 
bxtest Q:to/END/.lines;
~/{Downloads,Pictures}/*.{jpg,gif,png}
It{{em,alic}iz,erat}e{d,}, please.
{,{,gotta have{ ,\, again\, }}more }cowbell!
a{b{1,2}c
a{1,2}b}c
a{1,{2},3}b
more{ darn{ cowbell,},}
ab{c,d\,e{f,g\h},i\,j{k,l\,m}n,o\,p}qr
{a,{\,b}c
a{b,{{c}}
{a{\}b,c}d
END</syntaxhighlight>
{{out}}
<pre>~/{Downloads,Pictures}/*.{jpg,gif,png}
~/Downloads/*.jpg
~/Downloads/*.gif
~/Downloads/*.png
~/Pictures/*.jpg
~/Pictures/*.gif
~/Pictures/*.png
 
It{{em,alic}iz,erat}e{d,}, please.
Itemized, please.
Itemize, please.
Italicized, please.
Italicize, please.
Iterated, please.
Iterate, please.
 
{,{,gotta have{ ,\, again\, }}more }cowbell!
cowbell!
more cowbell!
gotta have more cowbell!
gotta have\, again\, more cowbell!
 
{}} some {\\{edge,edgy} }{ cases, here\\\}
{}} some {\\edge }{ cases, here\\\}
{}} some {\\edgy }{ cases, here\\\}
 
a{b{1,2}c
a{b1c
a{b2c
 
a{1,2}b}c
a1b}c
a2b}c
 
a{1,{2},3}b
a1b
a{2}b
a3b
 
more{ darn{ cowbell,},}
more darn cowbell
more darn
more
 
ab{c,d\,e{f,g\h},i\,j{k,l\,m}n,o\,p}qr
abcqr
abd\,efqr
abd\,eg\hqr
abi\,jknqr
abi\,jl\,mnqr
abo\,pqr
 
{a,{\,b}c
{a,{\,b}c
 
a{b,{{c}}
a{b,{{c}}
 
{a{\}b,c}d
{a\}bd
{acd</pre>
=={{header|REXX}}==
<syntaxhighlight lang ="rexx">/*------- REXX --------------------------------------------------------------
* Brace expansion
* 26.07.2016
Line 2,725 ⟶ 3,928:
Say arg(1) /* show on screen */
Call lineout oid,arg(1) /* write to file */
Return</langsyntaxhighlight>
{{out}}
<pre>J:\>rexx braces
Line 2,772 ⟶ 3,975:
{}
1 {}</pre>
 
=={{header|Ruby}}==
{{trans|Python}}
<langsyntaxhighlight lang="ruby">def getitem(s, depth=0)
out = [""]
until s.empty?
Line 2,815 ⟶ 4,017:
puts getitem(s)[0].map{|str| "\t"+str}
puts
end</langsyntaxhighlight>
 
{{out}}
Line 2,845 ⟶ 4,047:
{}} some }{,{\\ edge \,}{ cases, {here} \\\\\}
</pre>
 
=={{header|Rust}}==
<langsyntaxhighlight lang="rust">const OPEN_CHAR: char = '{';
const CLOSE_CHAR: char = '}';
const SEPARATOR: char = ',';
Line 3,030 ⟶ 4,231:
println!("{}", line);
}
}</langsyntaxhighlight>
 
 
Line 3,065 ⟶ 4,266:
 
{{trans|Python}}
<langsyntaxhighlight lang="rust">
fn main() {
let input = "~/{Downloads,Pictures}/*.{jpg,gif,png}
Line 3,146 ⟶ 4,347:
None
}
</syntaxhighlight>
</lang>
 
{{out}}
Line 3,173 ⟶ 4,374:
{}} some }{,{\\ edge \,}{ cases, {here} \\\\\}
</pre>
 
=={{header|Scala}}==
{{works with|Scala|2.11}}
<langsyntaxhighlight lang="scala">
import collection.mutable.ListBuffer
case class State(isChild: Boolean, alts: ListBuffer[String], rem: List[Char])
Line 3,207 ⟶ 4,407:
parseElem(State(false, ListBuffer(""), s.toList)).alts
}
</syntaxhighlight>
</lang>
Demonstrating:
<langsyntaxhighlight lang="scala">
println(expand("""~/{Downloads,Pictures}/*.{jpg,gif,png}""") mkString "\n")
println(expand("It{{em,alic}iz,erat}e{d,}, please.") mkString "\n")
println(expand("""{,{,gotta have{ ,\, again\, }}more }cowbell!""") mkString "\n")
println(expand("""{}} some }{,{\\{ edge, edge} \,}{ cases, {here} \\\\\}""") mkString "\n")
</syntaxhighlight>
</lang>
{{out}}
<pre>
Line 3,236 ⟶ 4,436:
{}} some }{,\\ edge \,{ cases, {here} \\\\\}
</pre>
 
=={{header|Scheme}}==
<syntaxhighlight lang="scheme">
<lang Scheme>
(define (parse-brackets str)
;; We parse the bracketed strings using an accumulator and a stack
Line 3,329 ⟶ 4,528:
(bracket-expand "It{{em,alic}iz,erat}e{d,}")
;; '("Ited" "Ite" "Itemed" "Iteme" "Italiced" "Italice" "Itized" "Itize" "Iterated" "Iterate")
</syntaxhighlight>
</lang>
 
=={{header|Seed7}}==
<langsyntaxhighlight lang="seed7">$ include "seed7_05.s7i";
 
const proc: expandBraces (in string: stri) is func
Line 3,391 ⟶ 4,589:
expandBraces(stri);
end for;
end func;</langsyntaxhighlight>
 
{{out}}
Line 3,418 ⟶ 4,616:
{}} some }{,{\\ edge \,}{ cases, {here} \\\\\}
</pre>
 
=={{header|Sidef}}==
{{trans|Perl}}
<langsyntaxhighlight lang="ruby">func brace_expand (input) {
var current = ['']
var stack = [[current]]
Line 3,482 ⟶ 4,679:
It{{em,alic}iz,erat}e{d,}, please.
{,{,gotta have{ ,\, again\, }}more }cowbell!
{}} some }{,{\\{ edge, edge} \,}{ cases, {here} \\\\\}</langsyntaxhighlight>
{{out}}
<pre>
Line 3,513 ⟶ 4,710:
=={{header|Simula}}==
{{trans|Python}}
<langsyntaxhighlight lang="simula">CLASS ARRAYLISTS;
BEGIN
 
Line 3,599 ⟶ 4,796:
END;
 
END;</langsyntaxhighlight><syntaxhighlight lang ="simula">EXTERNAL CLASS ARRAYLISTS;
ARRAYLISTS
BEGIN
Line 3,749 ⟶ 4,946:
 
END
</syntaxhighlight>
</lang>
{{out}}
<pre>~/{Downloads,Pictures}/*.{jpg,gif,png}
Line 3,777 ⟶ 4,974:
{}} some }{,{\\ edge \,}{ cases, {here} \\\\\}
 
</pre>
=={{header|Tailspin}}==
<syntaxhighlight lang="tailspin">
templates braceExpansion
composer braceParse
[ <part|'[{}\\,]'>* ] // This is not simply <production> because there may be unbalanced special chars
rule production: [ <part>* ]
rule part: <alternation|balancedBraces|escapedCharacter|'[^{}\\,]+'>+
rule alternation: (<='{'>) [ <production> <alternate>+ ] (<='}'>)
rule alternate: (<=','>) <production>
rule balancedBraces: <='{'> <part>* <='}'>
rule escapedCharacter: <'\\.'>
end braceParse
 
templates collateSequence
data part <[]|''> local
@: [''];
$... -> #
$@!
when <´part´ '.*'> do
def part: $;
@: [$@... -> '$;$part;'];
otherwise
def alternates: [ $... -> collateSequence ... ];
@: [$@... -> \(def prefix: $; $alternates... -> '$prefix;$;' ! \)];
end collateSequence
 
$ -> braceParse -> collateSequence !
end braceExpansion
 
'~/{Downloads,Pictures}/*.{jpg,gif,png}' -> '"$;" expands to:$ -> braceExpansion ... -> '$#10;$;';$#10;$#10;' -> !OUT::write
 
'It{{em,alic}iz,erat}e{d,}, please.' -> '"$;" expands to $ -> braceExpansion ... -> '$#10;$;';$#10;$#10;' -> !OUT::write
 
'{,{,gotta have{ ,\, again\, }}more }cowbell!' -> '"$;" expands to $ -> braceExpansion ... -> '$#10;$;';$#10;$#10;' -> !OUT::write
 
'{}} some }{,{\\{ edge, edge} \,}{ cases, {here} \\\\\}' -> '"$;" expands to $ -> braceExpansion ... -> '$#10;$;';$#10;$#10;' -> !OUT::write
</syntaxhighlight>
{{out}}
<pre>
"~/{Downloads,Pictures}/*.{jpg,gif,png}" expands to:
~/Downloads/*.jpg
~/Downloads/*.gif
~/Downloads/*.png
~/Pictures/*.jpg
~/Pictures/*.gif
~/Pictures/*.png
 
"It{{em,alic}iz,erat}e{d,}, please." expands to
Itemized, please.
Itemize, please.
Italicized, please.
Italicize, please.
Iterated, please.
Iterate, please.
 
"{,{,gotta have{ ,\, again\, }}more }cowbell!" expands to
cowbell!
more cowbell!
gotta have more cowbell!
gotta have\, again\, more cowbell!
 
"{}} some }{,{\\{ edge, edge} \,}{ cases, {here} \\\\\}" expands to
{}} some }{,{\\ edge \,}{ cases, {here} \\\\\}
{}} some }{,{\\ edge \,}{ cases, {here} \\\\\}
</pre>
 
=={{header|Tcl}}==
{{works with|Tcl|8.6}}
<langsyntaxhighlight lang="tcl">package require Tcl 8.6
 
proc combine {cases1 cases2 {insert ""}} {
Line 3,850 ⟶ 5,112:
}
return $current
}</langsyntaxhighlight>
Demonstrating:
<langsyntaxhighlight lang="tcl">foreach testcase {
"~/{Downloads,Pictures}/*.{jpg,gif,png}"
"It{{em,alic}iz,erat}e{d,}, please."
Line 3,859 ⟶ 5,121:
} {
puts $testcase\n\t[join [commatize $testcase] \n\t]
}</langsyntaxhighlight>
{{out}}
<pre>
Line 3,884 ⟶ 5,146:
{}} some }{,{\\ edge \,}{ cases, {here} \\\\\}
{}} some }{,{\\ edge \,}{ cases, {here} \\\\\}
</pre>
=={{header|TXR}}==
 
The approach here is to parse the notation into a nested tree of strings. In the following diagram the <code>-></code> arrow indicates that the tree on the left denotes the list of output strings on the right.
 
A list with no operator symbol denotes catenation:
 
<pre>("foo" "bar") -> ("foobar")</pre>
 
The <code>/</code> symbol (slash, usually denoting arithmetic division) denotes alternation:
 
<pre>(/ "foo" "bar") -> ("foo" "bar")
("inter" (/ "pol" "pret") "ation") -> ("interpolation" "interpretation")</pre>
 
This notation is processed by the <code>bexp-expand</code> function to produce the list of strings which it denotes. The <code>bexp-parse</code> function parses a string containing brace expansion into the above notation.
 
The backslashes and edge cases are handled between the tokenizing and parsing. Backslashed characters are represented as tokens which include the backslash. Thus the <code>\{</code> token compares unequal to <code>{</code> and isn't mistaken for it. These backslashed tokens just look like any other text that has no special meaning.
 
The empty <code>{}</code> is handled as a token, but other cases of braces containing no commas are handled in the parser.
 
When the parser has scanned a complete, valid brace that contains no comma, instead of generating a <code>(/ ...)</code> tree node from the content, it generates <code>("{" ... "}")</code>, rendering the braces as literal strings. The <code>...</code> content may contain <code>/</code> operators, as required.
 
When the parser has scanned an incomplete brace, it puts out <code>("{" ...)</code>: the dangling brace is represented literally, followed by the items that have been parsed out. The comma elements are preserved in this case; the lack of a closing brace turns off their meaning.
 
In the main case of a balanced brace with commas, the parsed out elements are split on the commas, which are removed, and that forms the arguments of <code>/</code> node.
 
<syntaxhighlight lang="txrlisp">;; API
(defun brace-expand (str)
(bexp-expand (bexp-parse str)))
 
;; parser
(defstruct bexp-parse-ctx ()
str
toks)
 
(defun bexp-parse (str)
(let ((ctx (new bexp-parse-ctx
str str
;; tokenizer
toks (remqual "" (tok #/([{},]|{}|\\\\|\\.)/ t str)))))
(build
(whilet ((next (pop ctx.toks)))
(add
(if (equal next "{")
(bexp-parse-brace ctx)
next))))))
 
(defun bexp-parse-brace (ctx)
(buildn
(let ((orig-toks ctx.toks))
(caseq (whilet ((next (pop ctx.toks)))
(casequal next
("{" (add (bexp-parse-brace ctx)))
("}" (return :ok))
(t (add next))))
(:ok
(cond
((memqual "," (get))
(flow (get)
(split* @1 (op where (op equal ",")))
(cons '/)))
(t
(add* "{")
(add "}")
(get))))
(nil
(add* "{")
(get))))))
 
;; expander
(defun bexp-expand (tree : (path (new list-builder)))
(build
(match-case tree
(() (add (cat-str path.(get))))
(((/ . @alt) . @rest)
(let ((saved-path path.(get)))
(each ((elem alt))
path.(oust saved-path)
(pend (bexp-expand (cons elem rest) path)))))
((@(consp @succ) . @rest)
(pend (bexp-expand (append succ rest) path)))
((@head . @rest)
path.(add head)
(pend (bexp-expand rest path))))))
 
;; Tests
(tprint (brace-expand "~/{Downloads,Pictures}/*.{jpg,gif,png}"))
(tprint (brace-expand "It{{em,alic}iz,erat}e{d,}, please."))
(tprint (brace-expand "{,{,gotta have{ ,\\, again\\, }}more }cowbell!"))
(tprint (brace-expand "{}} some }{,{\\\\{ edge, edge} \\,}{ cases, {here} \\\\\\\\\\}"))</syntaxhighlight>
 
{{out}}
 
<pre>~/Downloads/*.jpg
~/Downloads/*.gif
~/Downloads/*.png
~/Pictures/*.jpg
~/Pictures/*.gif
~/Pictures/*.png
Itemized, please.
Itemize, please.
Italicized, please.
Italicize, please.
Iterated, please.
Iterate, please.
cowbell!
more cowbell!
gotta have more cowbell!
gotta have\, again\, more cowbell!
{}} some }{,{\\ edge \,}{ cases, {here} \\\\\}
{}} some }{,{\\ edge \,}{ cases, {here} \\\\\}
</pre>
 
=={{header|Visual Basic .NET}}==
{{trans|Python}}
<syntaxhighlight lang="vbnet">Module Module1
 
Function GetGroup(s As String, depth As Integer) As Tuple(Of List(Of String), String)
Dim out As New List(Of String)
Dim comma = False
While Not String.IsNullOrEmpty(s)
Dim gs = GetItem(s, depth)
Dim g = gs.Item1
s = gs.Item2
If String.IsNullOrEmpty(s) Then
Exit While
End If
out.AddRange(g)
 
If s(0) = "}" Then
If comma Then
Return Tuple.Create(out, s.Substring(1))
End If
Return Tuple.Create(out.Select(Function(a) "{" + a + "}").ToList(), s.Substring(1))
End If
 
If s(0) = "," Then
comma = True
s = s.Substring(1)
End If
End While
Return Nothing
End Function
 
Function GetItem(s As String, Optional depth As Integer = 0) As Tuple(Of List(Of String), String)
Dim out As New List(Of String) From {""}
While Not String.IsNullOrEmpty(s)
Dim c = s(0)
If depth > 0 AndAlso (c = "," OrElse c = "}") Then
Return Tuple.Create(out, s)
End If
If c = "{" Then
Dim x = GetGroup(s.Substring(1), depth + 1)
If Not IsNothing(x) Then
Dim tout As New List(Of String)
For Each a In out
For Each b In x.Item1
tout.Add(a + b)
Next
Next
out = tout
s = x.Item2
Continue While
End If
End If
If c = "\" AndAlso s.Length > 1 Then
c += s(1)
s = s.Substring(1)
End If
out = out.Select(Function(a) a + c).ToList()
s = s.Substring(1)
End While
Return Tuple.Create(out, s)
End Function
 
Sub Main()
For Each s In {
"It{{em,alic}iz,erat}e{d,}, please.",
"~/{Downloads,Pictures}/*.{jpg,gif,png}",
"{,{,gotta have{ ,\, again\, }}more }cowbell!",
"{}} some }{,{\\{ edge, edge} \,}{ cases, {here} \\\\\}"
}
Dim fmt = "{0}" + vbNewLine + vbTab + "{1}"
Dim parts = GetItem(s)
Dim res = String.Join(vbNewLine + vbTab, parts.Item1)
Console.WriteLine(fmt, s, res)
Next
End Sub
 
End Module</syntaxhighlight>
{{out}}
<pre>It{{em,alic}iz,erat}e{d,}, please.
Itemized, please.
Itemize, please.
Italicized, please.
Italicize, please.
Iterated, please.
Iterate, please.
~/{Downloads,Pictures}/*.{jpg,gif,png}
~/Downloads/*.jpg
~/Downloads/*.gif
~/Downloads/*.png
~/Pictures/*.jpg
~/Pictures/*.gif
~/Pictures/*.png
{,{,gotta have{ ,\, again\, }}more }cowbell!
cowbell!
more cowbell!
gotta have more cowbell!
gotta have\ again\ more cowbell!
{}} some }{,{\\{ edge, edge} \,}{ cases, {here} \\\\\}
{}} some }{,{\ edge \}{ cases, {here} \\\
{}} some }{,{\ edge \}{ cases, {here} \\\</pre>
 
=={{header|Wren}}==
{{trans|Python}}
<syntaxhighlight lang="wren">var getGroup // forward declaration
 
var getItem = Fn.new { |s, depth|
var out = [""]
while (s != "") {
var c = s[0]
if (depth > 0 && (c == "," || c == "}")) return [out, s]
var cont = false
if (c == "{") {
var x = getGroup.call(s[1..-1], depth+1)
if (!x[0].isEmpty) {
var t = []
for (a in out) {
for (b in x[0]) {
t.add(a + b)
}
}
out = t
s = x[1]
cont = true
}
}
if (!cont) {
if (c == "\\" && s.count > 1) {
c = c + s[1]
s = s[1..-1]
}
out = out.map { |a| a + c }.toList
s = s[1..-1]
}
}
return [out, s]
}
 
getGroup = Fn.new { |s, depth|
var out = []
var comma = false
while (s != "") {
var t = getItem.call(s, depth)
var g = t[0]
s = t[1]
if (s == "") break
out.addAll(g)
if (s[0] == "}") {
if (comma) return [out, s[1..-1]]
return [out.map { |a| "{" + a + "}" }.toList, s[1..-1]]
}
if (s[0] == ",") {
comma = true
s = s[1..-1]
}
}
return [[], ""]
}
 
var inputs = [
"~/{Downloads,Pictures}/*.{jpg,gif,png}",
"It{{em,alic}iz,erat}e{d,}, please.",
"{,{,gotta have{ ,\\, again\\, }}more }cowbell!",
"{}} some }{,{\\\\{ edge, edge} \\,}{ cases, {here} \\\\\\\\\\}"
]
for (input in inputs) {
System.print(input)
for (s in getItem.call(input, 0)[0]) System.print(" " + s)
System.print()
}</syntaxhighlight>
 
{{out}}
<pre>
~/{Downloads,Pictures}/*.{jpg,gif,png}
~/Downloads/*.jpg
~/Downloads/*.gif
~/Downloads/*.png
~/Pictures/*.jpg
~/Pictures/*.gif
~/Pictures/*.png
 
It{{em,alic}iz,erat}e{d,}, please.
Itemized, please.
Itemize, please.
Italicized, please.
Italicize, please.
Iterated, please.
Iterate, please.
 
{,{,gotta have{ ,\, again\, }}more }cowbell!
cowbell!
more cowbell!
gotta have more cowbell!
gotta have\, again\, more cowbell!
 
{}} some }{,{\\{ edge, edge} \,}{ cases, {here} \\\\\}
{}} some }{,{\\ edge \,}{ cases, {here} \\\\\}
{}} some }{,{\\ edge \,}{ cases, {here} \\\\\}
</pre>
 
=={{header|zkl}}==
This is a two pass algorithm (2*length(string)), one pass to find valid {} pairs, the next pass to expand them.
<langsyntaxhighlight lang="zkl">fcn eyeball(code,ps=L(),brace=False){ //-->indexes of valid braces & commas
cs:=L();
foreach c in (code){ // start fresh or continue (if recursing)
Line 3,919 ⟶ 5,491:
}
strings
}</langsyntaxhighlight>
<langsyntaxhighlight lang="zkl">foreach bs in (T("It{{em,alic}iz,erat}e{d,}", "~/{Downloads,Pictures}/*.{jpg,gif,png}",
"It{{em,alic}iz,erat}e{d,}, please.", "a{2,1}b{X,Y,X}c", 0'|a\\{\\\{b,c\,d}|,
"{a,b{c{,{d}}e}f", 0'|{,{,gotta have{ ,\, again\, }}more }cowbell!|,
Line 3,926 ⟶ 5,498:
{
"%s expands to\n %s".fmt(bs,expando(bs)).println();
}</langsyntaxhighlight>
{{out}}
<pre>
9,476

edits