// re2v $INPUT -o $OUTPUT

// Maximum number of capturing groups among all rules.
/*!maxnmatch:re2c*/

struct SemVer {
    major int
    minor int
    patch int
}

fn s2n(s string) int { // convert pre-parsed string to number
    mut n := 0
    for c in s { n = n * 10 + int(c - 48) }
    return n
}

fn parse(yyinput string) ?SemVer {
    mut yycursor, mut yymarker := 0, 0

    // Allocate memory for capturing parentheses (twice the number of groups).
    mut yypmatch := []int{len: yymaxnmatch * 2}
    mut yynmatch := 0

    // Intermediate tag variables used by the lexer (must be autogenerated).
    /*!stags:re2c format = 'mut @@ := 0\n'; */

    /*!re2c
        re2c:yyfill:enable = 0;
        re2c:posix-captures = 1;

        num = [0-9]+;

        (num) "." (num) ("." num)? [\x00] {
            // `yynmatch` is the number of capturing groups
            if yynmatch != 4 { panic("expected 4 submatch groups") }

            // Even `yypmatch` values are for opening parentheses, odd values
            // are for closing parentheses, the first group is the whole match.
            return SemVer {
                major: s2n(yyinput[yypmatch[2]..yypmatch[3]]),
                minor: s2n(yyinput[yypmatch[4]..yypmatch[5]]),
                patch: if yypmatch[6] == -1 {0} else {s2n(yyinput[yypmatch[6] + 1..yypmatch[7]])}
            }
        }
        * { return none }
    */
}

fn main() {
    test := fn (result ?SemVer, expect ?SemVer) {
        if r := result {
            if e := expect { if r != e { panic("expected $e, got $r") } }
        } else {
            if _ := result { panic("expected none") }
        }
    }
    test(parse("23.34\0"), SemVer{23, 34, 0})
    test(parse("1.2.9999\0"), SemVer{1, 2, 9999})
    test(parse("1.a\0"), none)
}
