// Generated by re2js
// re2js $INPUT -o $OUTPUT -f

const fs = require('fs')

// Use a small buffer to cover the case when a lexeme doesn't fit.
// In real world use a larger buffer.
const BUFSIZE = 10
const DEBUG = false
const END = 0
const READY = 1
const WAITING = 2
const BIG_PACKET = 3
const BAD_PACKET = 4

function log() {
    if (DEBUG) console.log.apply(console, arguments)
}

function fill(st) {
    // Error: lexeme too long. In real life could reallocate a larger buffer.
    if (st.token < 1) return BIG_PACKET

    // Shift buffer contents (discard everything up to the current token).
    st.yyinput.copy(st.yyinput, 0, st.token, st.yylimit)
    st.yycursor -= st.token;
    st.yymarker -= st.token;
    st.yylimit -= st.token;
    st.token = 0;

    // Read a new chunk of data from file and append it to `yyinput`.
    let want = BUFSIZE - st.yylimit - 1 // -1 for sentinel
    let nread = fs.readSync(st.file, st.yyinput, st.yylimit, want)
    st.yylimit += nread
    st.yyinput.writeUInt8(0, st.yylimit) // sentinel

    return READY
}

function lex(yyrecord) {
    loop: while (true) {
        yyrecord.token = yyrecord.yycursor
        
{
    let yystate = yyrecord.yystate
    yyl: while (true) {
        switch (yystate) {
            case -1:
            case 0:
                yych = yyrecord.yyinput.readUInt8(yyrecord.yycursor)
                switch (yych) {
                    case 0x61:
                    case 0x62:
                    case 0x63:
                    case 0x64:
                    case 0x65:
                    case 0x66:
                    case 0x67:
                    case 0x68:
                    case 0x69:
                    case 0x6A:
                    case 0x6B:
                    case 0x6C:
                    case 0x6D:
                    case 0x6E:
                    case 0x6F:
                    case 0x70:
                    case 0x71:
                    case 0x72:
                    case 0x73:
                    case 0x74:
                    case 0x75:
                    case 0x76:
                    case 0x77:
                    case 0x78:
                    case 0x79:
                    case 0x7A:
                        yyrecord.yycursor += 1;
                        yystate = 3
                        continue yyl
                    default:
                        if (yyrecord.yylimit <= yyrecord.yycursor) {
                            yyrecord.yystate = 8;
                            return WAITING
                        }
                        yyrecord.yycursor += 1;
                        yystate = 1
                        continue yyl
                }
            case 1:
                yystate = 2
                continue yyl
            case 2:
                yyrecord.yystate = -1;
                { return BAD_PACKET }
            case 3:
                yyrecord.yymarker = yyrecord.yycursor;
                yych = yyrecord.yyinput.readUInt8(yyrecord.yycursor)
                switch (yych) {
                    case 0x3B:
                        yyrecord.yycursor += 1;
                        yystate = 4
                        continue yyl
                    case 0x61:
                    case 0x62:
                    case 0x63:
                    case 0x64:
                    case 0x65:
                    case 0x66:
                    case 0x67:
                    case 0x68:
                    case 0x69:
                    case 0x6A:
                    case 0x6B:
                    case 0x6C:
                    case 0x6D:
                    case 0x6E:
                    case 0x6F:
                    case 0x70:
                    case 0x71:
                    case 0x72:
                    case 0x73:
                    case 0x74:
                    case 0x75:
                    case 0x76:
                    case 0x77:
                    case 0x78:
                    case 0x79:
                    case 0x7A:
                        yyrecord.yycursor += 1;
                        yystate = 5
                        continue yyl
                    default:
                        if (yyrecord.yylimit <= yyrecord.yycursor) {
                            yyrecord.yystate = 9;
                            return WAITING
                        }
                        yystate = 2
                        continue yyl
                }
            case 4:
                yyrecord.yystate = -1;
                { yyrecord.received += 1; continue loop }
            case 5:
                yych = yyrecord.yyinput.readUInt8(yyrecord.yycursor)
                switch (yych) {
                    case 0x3B:
                        yyrecord.yycursor += 1;
                        yystate = 4
                        continue yyl
                    case 0x61:
                    case 0x62:
                    case 0x63:
                    case 0x64:
                    case 0x65:
                    case 0x66:
                    case 0x67:
                    case 0x68:
                    case 0x69:
                    case 0x6A:
                    case 0x6B:
                    case 0x6C:
                    case 0x6D:
                    case 0x6E:
                    case 0x6F:
                    case 0x70:
                    case 0x71:
                    case 0x72:
                    case 0x73:
                    case 0x74:
                    case 0x75:
                    case 0x76:
                    case 0x77:
                    case 0x78:
                    case 0x79:
                    case 0x7A:
                        yyrecord.yycursor += 1;
                        yystate = 5
                        continue yyl
                    default:
                        if (yyrecord.yylimit <= yyrecord.yycursor) {
                            yyrecord.yystate = 10;
                            return WAITING
                        }
                        yystate = 6
                        continue yyl
                }
            case 6:
                yyrecord.yycursor = yyrecord.yymarker;
                yystate = 2
                continue yyl
            case 7:
                yyrecord.yystate = -1;
                { return END }
            case 8:
                if (yyrecord.yylimit <= yyrecord.yycursor) {
                    yystate = 7
                    continue yyl
                }
                yystate = 0
                continue yyl
            case 9:
                if (yyrecord.yylimit <= yyrecord.yycursor) {
                    yystate = 2
                    continue yyl
                }
                yystate = 3
                continue yyl
            case 10:
                if (yyrecord.yylimit <= yyrecord.yycursor) {
                    yystate = 6
                    continue yyl
                }
                yystate = 5
                continue yyl
            default:
                throw "internal lexer error"
        }
    }
}

    }
}

function test(packets, expect) {
    // Emulate a "pipe" by opening the same file for reading and writing.
    let fname = "input"
    let fw = fs.openSync(fname, 'w');
    let fr = fs.openSync(fname, 'r');

    // Init lexer state.
    let limit = BUFSIZE - 1 // exclude terminating null
    let st = {
        file: fr,
        yyinput: Buffer.alloc(BUFSIZE),
        yylimit: limit,
        yycursor: limit,
        yymarker: limit,
        token: limit,
        yystate: -1,
        received: 0
    }

    // Main loop. The buffer contains incomplete data which appears packet by
    // packet. When the lexer needs more input it saves its internal state and
    // returns to the caller which should provide more input and resume lexing.
    let send = 0
    let status
    loop: while (true) {
        status = lex(st)

        if (status == END) {
            log("done: got", st.received, "packets")
            break loop
        } else if (status == WAITING) {
            log("waiting...");

            if (send < packets.length) {
                log("sent packet", send, packets[send])
                fs.writeFileSync(fw, packets[send])
                send += 1
            }

            status = fill(st)
            log("queue:", st.yyinput.toString())
            if (status == BIG_PACKET) {
                log("error: packet too big")
                break loop
            }

            if (status != READY) throw "expected READY"
        } else {
            if (status != BAD_PACKET) throw "expected BAD_PACKET"
            log("error: ill-formed packet")
            break loop
        }
    }

    // Check results.
    if (status != expect) throw "unexpected status"
    if (status == END && st.received != send) "unexpected packet count"

    // Cleanup.
    fs.unlinkSync(fname, function(err){ if (err) throw err; })
}

function main() {
    test([], END)
    test(["zero;", "one;", "two;", "three;", "four;"], END)
    test(["zer0;"], BAD_PACKET)
    test(["goooooooooogle;"], BIG_PACKET)
}

main()
