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

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.Pipe;

class Lexer {
    enum Status {
        END,
        READY,
        WAITING,
        BIG_PACKET,
        BAD_PACKET
    };

    // Use a small buffer to cover the case when a lexeme doesn't fit.
    // In real world use a larger buffer.
    public static final int BUFSIZE = 10;

    public static class State {
        Pipe.SourceChannel source;
        byte[] yyinput;
        int yycursor;
        int yymarker;
        int yylimit;
        int token;
        int yystate;
        int received;

        public State(Pipe pipe) {
            source = pipe.source();
            // Sentinel at `yylimit` offset is set to zero, which triggers YYFILL.
            yyinput = new byte[BUFSIZE + 1];
            yycursor = yymarker = yylimit = token = BUFSIZE;
            yystate = -1;
            received = 0;
        }
    }

    private static void log(String format, Object... args) {
        if (false) { System.out.printf(format + "\n", args); }
    }

    private static Status fill(State st) throws IOException {
        // Error: lexeme too long. In real life can reallocate a larger buffer.
        if (st.token < 1) { return Status.BIG_PACKET; }

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

        // Fill free space at the end of buffer with new data from file.
        ByteBuffer buffer = ByteBuffer.wrap(st.yyinput, st.yylimit, BUFSIZE - st.yylimit);
        int have = st.source.read(buffer);
        if (have != -1) st.yylimit += have; // -1 means that pipe is closed
        st.yyinput[st.yylimit] = 0; // append sentinel symbol

        return Status.READY;
    }

    private static Status lex(State yyrecord) {
        int yych;
        loop: while (true) {
            yyrecord.token = yyrecord.yycursor;
            
{
    int yystate = yyrecord.yystate;
    yyl: while (true) {
        switch (yystate) {
            case -1:
            case 0:
                yych = Byte.toUnsignedInt(yyrecord.yyinput[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 Status.WAITING;
                        }
                        yyrecord.yycursor += 1;
                        yystate = 1;
                        continue yyl;
                }
            case 1:
                yystate = 2;
                continue yyl;
            case 2:
                yyrecord.yystate = -1;
                { return Status.BAD_PACKET; }
            case 3:
                yyrecord.yymarker = yyrecord.yycursor;
                yych = Byte.toUnsignedInt(yyrecord.yyinput[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 Status.WAITING;
                        }
                        yystate = 2;
                        continue yyl;
                }
            case 4:
                yyrecord.yystate = -1;
                { yyrecord.received += 1; continue loop; }
            case 5:
                yych = Byte.toUnsignedInt(yyrecord.yyinput[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 Status.WAITING;
                        }
                        yystate = 6;
                        continue yyl;
                }
            case 6:
                yyrecord.yycursor = yyrecord.yymarker;
                yystate = 2;
                continue yyl;
            case 7:
                yyrecord.yystate = -1;
                { return Status.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 new IllegalStateException("internal lexer error");
        }
    }
}

        }
    }

    public static void test(String[] packets, Status expect) throws IOException {
        // Create a pipe.
        Pipe pipe = Pipe.open();
        Pipe.SinkChannel sink = pipe.sink();

        // Initialize lexer state
        Lexer.State st = new Lexer.State(pipe);

        // 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.
        int send = 0;
        Status status;
        while (true) {
            status = lex(st);

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

                if (send < packets.length) {
                    log("sent packet %d: %s", send, packets[send]);
                    ByteBuffer buffer = ByteBuffer.wrap(packets[send].getBytes());
                    sink.write(buffer);
                    send += 1;
                } else {
                    sink.close();
                }

                status = fill(st);
                if (status == Status.BIG_PACKET) {
                    log("error: packet too big");
                    break;
                }
                assert status == Status.READY;
            } else {
                assert status == Status.BAD_PACKET;
                log("error: ill-formed packet");
                break;
            }
        }

        // Check results.
        assert status == expect;
        if (status == Status.END) {
            assert send == st.received;
        }
    }

    public static void main(String []args) throws IOException {
        test(new String[]{}, Status.END);
        test(new String[]{"zero;", "one;", "two;", "three;", "four;"}, Status.END);
        test(new String[]{"zer0;"}, Status.BAD_PACKET);
        test(new String[]{"goooooooooogle;"}, Status.BIG_PACKET);
    }
};
