Task: Brace expansion

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!");

    
    

    return 0;
}








VB: 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