// Generated by re2zig
// re2zig $INPUT -o $OUTPUT

const std = @import("std");

const none = std.math.maxInt(usize);
const mtag_root = none - 1;

const err = error.SyntaxError;

// An m-tag tree is a way to store histories with an O(1) copy operation.
// Histories naturally form a tree, as they have common start and fork at some
// point. The tree is stored as an array of pairs (tag value, link to parent).
// An m-tag is represented with a single link in the tree (array index).
const MtagElem = struct {
    elem: usize, // tag value
    pred: usize, // index of the predecessor node or root
};

// Append a single value to an m-tag history.
fn add_mtag(trie: *std.ArrayList(MtagElem), mtag: usize, value: usize) !usize {
    try trie.append(std.testing.allocator, MtagElem{.elem = value, .pred = mtag});
    return trie.items.len - 1;
}

// Recursively unwind tag histories and collect version components.
fn unwind(trie: *std.ArrayList(MtagElem),
          x: usize,
          y: usize,
          str: []const u8,
          ver: *std.ArrayList(u32)) !void {
    // Reached the root of the m-tag tree, stop recursion.
    if (x == mtag_root and y == mtag_root) return;

    // Unwind history further.
    try unwind(trie, trie.items[x].pred, trie.items[y].pred, str, ver);

    // Get tag values. Tag histories must have equal length.
    std.debug.assert(x != mtag_root and y != mtag_root);
    const ex = trie.items[x].elem;
    const ey = trie.items[y].elem;

    if (ex != none and ey != none) {
        // Both tags are valid string indices, extract component.
        try ver.append(std.testing.allocator, s2n(str[ex..ey]));
    } else {
        // Both tags are none (this corresponds to zero repetitions).
        std.debug.assert(ex == none and ey == none);
    }
}

fn s2n(str: []const u8) u32 { // convert a pre-parsed string to a number
    var n: u32 = 0;
    for (str) |c| { n = n * 10 + (c - 48); }
    return n;
}

fn parse(yyinput: [:0]const u8) !std.ArrayList(u32) {
    var yycursor: usize = 0;
    var yymarker: usize = 0;
    var mt = try std.ArrayList(MtagElem).initCapacity(std.testing.allocator, 0);
    defer mt.deinit(std.testing.allocator);

    // Final tag variables available in semantic action.
    var t1: usize = none;var t2: usize = none;
    var t3: usize = mtag_root;var t4: usize = mtag_root;

    // Intermediate tag variables used by the lexer (must be autogenerated).
    var yyt1: usize = none;var yyt2: usize = none;
    var yytm3: usize = mtag_root;var yytm4: usize = mtag_root;

    
    var yych: u8 = 0;
    var yystate: u32 = 0;
    yyl: while (true) {
        switch (yystate) {
            0 => {
                yych = yyinput[yycursor];
                switch (yych) {
                    0x30...0x39 => {
                        yyt1 = yycursor;
                        yycursor += 1;
                        yystate = 3;
                        continue :yyl;
                    },
                    else => {
                        yycursor += 1;
                        yystate = 1;
                        continue :yyl;
                    },
                }
            },
            1 => {
                yystate = 2;
                continue :yyl;
            },
            2 => { return error.SyntaxError; },
            3 => {
                yymarker = yycursor;
                yych = yyinput[yycursor];
                switch (yych) {
                    0x00 => {
                        yytm4 = add_mtag(&mt, yytm4, none) catch none;
                        yytm3 = add_mtag(&mt, yytm3, none) catch none;
                        yyt2 = yycursor;
                        yycursor += 1;
                        yystate = 4;
                        continue :yyl;
                    },
                    0x2E => {
                        yyt2 = yycursor;
                        yycursor += 1;
                        yystate = 5;
                        continue :yyl;
                    },
                    0x30...0x39 => {
                        yycursor += 1;
                        yystate = 7;
                        continue :yyl;
                    },
                    else => {
                        yystate = 2;
                        continue :yyl;
                    },
                }
            },
            4 => {
                t1 = yyt1;
                t2 = yyt2;
                t3 = yytm3;
                t4 = yytm4;
                
            var ver = try std.ArrayList(u32).initCapacity(std.testing.allocator, 0);
            try ver.append(std.testing.allocator, s2n(yyinput[t1..t2]));
            try unwind(&mt, t3, t4, yyinput, &ver);
            return ver;

            },
            5 => {
                yych = yyinput[yycursor];
                switch (yych) {
                    0x30...0x39 => {
                        yytm3 = add_mtag(&mt, yytm3, yycursor) catch none;
                        yycursor += 1;
                        yystate = 8;
                        continue :yyl;
                    },
                    else => {
                        yystate = 6;
                        continue :yyl;
                    },
                }
            },
            6 => {
                yycursor = yymarker;
                yystate = 2;
                continue :yyl;
            },
            7 => {
                yych = yyinput[yycursor];
                switch (yych) {
                    0x00 => {
                        yytm4 = add_mtag(&mt, yytm4, none) catch none;
                        yytm3 = add_mtag(&mt, yytm3, none) catch none;
                        yyt2 = yycursor;
                        yycursor += 1;
                        yystate = 4;
                        continue :yyl;
                    },
                    0x2E => {
                        yyt2 = yycursor;
                        yycursor += 1;
                        yystate = 5;
                        continue :yyl;
                    },
                    0x30...0x39 => {
                        yycursor += 1;
                        yystate = 7;
                        continue :yyl;
                    },
                    else => {
                        yystate = 6;
                        continue :yyl;
                    },
                }
            },
            8 => {
                yych = yyinput[yycursor];
                switch (yych) {
                    0x00 => {
                        yytm4 = add_mtag(&mt, yytm4, yycursor) catch none;
                        yycursor += 1;
                        yystate = 4;
                        continue :yyl;
                    },
                    0x2E => {
                        yytm4 = add_mtag(&mt, yytm4, yycursor) catch none;
                        yycursor += 1;
                        yystate = 5;
                        continue :yyl;
                    },
                    0x30...0x39 => {
                        yycursor += 1;
                        yystate = 8;
                        continue :yyl;
                    },
                    else => {
                        yystate = 6;
                        continue :yyl;
                    },
                }
            },
            else => { @panic("internal lexer error"); },
        }
    }

}

test {
    var result = try parse("1");
    var expect = [_]u32{1};
    try std.testing.expectEqualDeep(&expect, result.items);
    result.deinit(std.testing.allocator);
}

test {
    var result = try parse("1.2.3.4.5.6.7");
    var expect = [_]u32{1, 2, 3, 4, 5, 6, 7};
    try std.testing.expectEqualDeep(&expect, result.items);
    result.deinit(std.testing.allocator);
}

test {
    const result = parse("1.2.") catch null;
    try std.testing.expectEqualDeep(result, null);
}
