/* Generated by re2d */
#line 1 "codegen/d/02_storable_state_with_conditions.re"
// re2d $INPUT -o $OUTPUT -fc --loop-switch
module main;

import core.stdc.stdio;
import core.stdc.string;

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

#line 14 "codegen/d/02_storable_state_with_conditions.d"
enum YYCond {
    yycINIT = 0,
    yycSPACES = 6,
    yycNUMBER = 11,
    yycWORD = 16
};
#line 11 "codegen/d/02_storable_state_with_conditions.re"


struct State {
    FILE* file;
    char[BUFSIZE + 1] buf;
    char* lim, cur, mar, tok;
    int state;
};

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

private Status fill(ref State st) {
    const size_t shift = st.tok - cast(char*)st.buf;
    const size_t used = st.lim - st.tok;
    const size_t free = BUFSIZE - used;

    // Error: no space. In real life can reallocate a larger buffer.
    if (free < 1) return Status.BIG_PACKET;

    // Shift buffer contents (discard already processed data).
    memmove(cast(void*)st.buf, st.tok, used);
    st.lim -= shift;
    st.cur -= shift;
    st.mar -= shift;
    st.tok -= shift;

    // Fill free space at the end of buffer with new data.
    const size_t read = fread(st.lim, 1, free, st.file);
    st.lim += read;
    st.lim[0] = 0; // append sentinel symbol

    return Status.READY;
}

private Status lex(ref State st, uint* nc, uint* wc) {
    char yych;
    /* !getstate:re2c*/

lex:
    for (;;) {
        st.tok = st.cur;
    
#line 64 "codegen/d/02_storable_state_with_conditions.d"
{
    int yystate = st.state;
    yyl: while (true) {
        switch (yystate) {
            case -1: .. case 0:
                yych = *st.cur;
                switch (yych) {
                    case '\t':
                    case ' ':
                        st.cur += 1;
                        yystate = 2;
                        continue yyl;
                    case '0': .. case '9':
                        st.cur += 1;
                        yystate = 3;
                        continue yyl;
                    case 'a': .. case 'z':
                        st.cur += 1;
                        yystate = 4;
                        continue yyl;
                    default:
                        if (st.cur >= st.lim) {
                            st.state = 21;
                            return Status.WAITING;
                        }
                        st.cur += 1;
                        yystate = 1;
                        continue yyl;
                }
            case 1:
                st.state = YYCond.yycINIT;
#line 70 "codegen/d/02_storable_state_with_conditions.re"
                { return Status.BAD_PACKET; }
#line 98 "codegen/d/02_storable_state_with_conditions.d"
            case 2:
                st.cur += -1;
                st.state = YYCond.yycSPACES;
                yystate = YYCond.yycSPACES;
                continue yyl;
            case 3:
                st.cur += -1;
                st.state = YYCond.yycNUMBER;
                yystate = YYCond.yycNUMBER;
                continue yyl;
            case 4:
                st.cur += -1;
                st.state = YYCond.yycWORD;
                yystate = YYCond.yycWORD;
                continue yyl;
            case 5:
                st.state = YYCond.yycINIT;
#line 71 "codegen/d/02_storable_state_with_conditions.re"
                { return Status.END; }
#line 118 "codegen/d/02_storable_state_with_conditions.d"
            case 6:
                yych = *st.cur;
                switch (yych) {
                    case '\t':
                    case ' ':
                        st.cur += 1;
                        yystate = 8;
                        continue yyl;
                    default:
                        if (st.cur >= st.lim) {
                            st.state = 22;
                            return Status.WAITING;
                        }
                        st.cur += 1;
                        yystate = 7;
                        continue yyl;
                }
            case 7:
                st.state = YYCond.yycSPACES;
#line 70 "codegen/d/02_storable_state_with_conditions.re"
                { return Status.BAD_PACKET; }
#line 140 "codegen/d/02_storable_state_with_conditions.d"
            case 8:
                yych = *st.cur;
                switch (yych) {
                    case '\t':
                    case ' ':
                        st.cur += 1;
                        yystate = 8;
                        continue yyl;
                    default:
                        if (st.cur >= st.lim) {
                            st.state = 23;
                            return Status.WAITING;
                        }
                        yystate = 9;
                        continue yyl;
                }
            case 9:
                st.state = YYCond.yycINIT;
#line 77 "codegen/d/02_storable_state_with_conditions.re"
                { continue lex; }
#line 161 "codegen/d/02_storable_state_with_conditions.d"
            case 10:
                st.state = YYCond.yycSPACES;
#line 71 "codegen/d/02_storable_state_with_conditions.re"
                { return Status.END; }
#line 166 "codegen/d/02_storable_state_with_conditions.d"
            case 11:
                yych = *st.cur;
                switch (yych) {
                    case '0': .. case '9':
                        st.cur += 1;
                        yystate = 13;
                        continue yyl;
                    default:
                        if (st.cur >= st.lim) {
                            st.state = 24;
                            return Status.WAITING;
                        }
                        st.cur += 1;
                        yystate = 12;
                        continue yyl;
                }
            case 12:
                st.state = YYCond.yycNUMBER;
#line 70 "codegen/d/02_storable_state_with_conditions.re"
                { return Status.BAD_PACKET; }
#line 187 "codegen/d/02_storable_state_with_conditions.d"
            case 13:
                yych = *st.cur;
                switch (yych) {
                    case '0': .. case '9':
                        st.cur += 1;
                        yystate = 13;
                        continue yyl;
                    default:
                        if (st.cur >= st.lim) {
                            st.state = 25;
                            return Status.WAITING;
                        }
                        yystate = 14;
                        continue yyl;
                }
            case 14:
                st.state = YYCond.yycSPACES;
#line 79 "codegen/d/02_storable_state_with_conditions.re"
                { *nc += 1; continue lex; }
#line 207 "codegen/d/02_storable_state_with_conditions.d"
            case 15:
                st.state = YYCond.yycNUMBER;
#line 71 "codegen/d/02_storable_state_with_conditions.re"
                { return Status.END; }
#line 212 "codegen/d/02_storable_state_with_conditions.d"
            case 16:
                yych = *st.cur;
                switch (yych) {
                    case 'a': .. case 'z':
                        st.cur += 1;
                        yystate = 18;
                        continue yyl;
                    default:
                        if (st.cur >= st.lim) {
                            st.state = 26;
                            return Status.WAITING;
                        }
                        st.cur += 1;
                        yystate = 17;
                        continue yyl;
                }
            case 17:
                st.state = YYCond.yycWORD;
#line 70 "codegen/d/02_storable_state_with_conditions.re"
                { return Status.BAD_PACKET; }
#line 233 "codegen/d/02_storable_state_with_conditions.d"
            case 18:
                yych = *st.cur;
                switch (yych) {
                    case 'a': .. case 'z':
                        st.cur += 1;
                        yystate = 18;
                        continue yyl;
                    default:
                        if (st.cur >= st.lim) {
                            st.state = 27;
                            return Status.WAITING;
                        }
                        yystate = 19;
                        continue yyl;
                }
            case 19:
                st.state = YYCond.yycSPACES;
#line 80 "codegen/d/02_storable_state_with_conditions.re"
                { *wc += 1; continue lex; }
#line 253 "codegen/d/02_storable_state_with_conditions.d"
            case 20:
                st.state = YYCond.yycWORD;
#line 71 "codegen/d/02_storable_state_with_conditions.re"
                { return Status.END; }
#line 258 "codegen/d/02_storable_state_with_conditions.d"
            case 21:
                if (st.cur >= st.lim) {
                    yystate = 5;
                    continue yyl;
                }
                yystate = 0;
                continue yyl;
            case 22:
                if (st.cur >= st.lim) {
                    yystate = 10;
                    continue yyl;
                }
                yystate = 6;
                continue yyl;
            case 23:
                if (st.cur >= st.lim) {
                    yystate = 9;
                    continue yyl;
                }
                yystate = 8;
                continue yyl;
            case 24:
                if (st.cur >= st.lim) {
                    yystate = 15;
                    continue yyl;
                }
                yystate = 11;
                continue yyl;
            case 25:
                if (st.cur >= st.lim) {
                    yystate = 14;
                    continue yyl;
                }
                yystate = 13;
                continue yyl;
            case 26:
                if (st.cur >= st.lim) {
                    yystate = 20;
                    continue yyl;
                }
                yystate = 16;
                continue yyl;
            case 27:
                if (st.cur >= st.lim) {
                    yystate = 19;
                    continue yyl;
                }
                yystate = 18;
                continue yyl;
            default: assert(false);
        }
    }
}
#line 81 "codegen/d/02_storable_state_with_conditions.re"

    }
    assert(0); // unreachable
}

private void test(string[] packets, Status expect, int numbers, int words) {
    // Create a "socket" (open the same file for reading and writing).
    const(char*) fname = "pipe";
    FILE* fw = fopen(fname, "w");
    FILE* fr = fopen(fname, "r");
    setvbuf(fw, null, _IONBF, 0);
    setvbuf(fr, null, _IONBF, 0);

    // Initialize lexer state: `state` value is -1, all pointers are at the end
    // of buffer.
    State st;
    st.file = fr;
    st.cur = st.mar = st.tok = st.lim = cast(char*)st.buf + BUFSIZE;
    // Sentinel (at YYLIMIT pointer) is set to zero, which triggers YYFILL.
    st.lim[0] = 0;
    st.state = -1;

    // 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.
    Status status;
    uint send = 0, nc = 0, wc = 0;
    for (;;) {
        status = lex(st, &nc, &wc);
        if (status == Status.END) {
            debug{printf("done: got %u numbers and %u words\n", nc, wc);}
            break;
        } else if (status == Status.WAITING) {
            debug{printf("waiting...\n");}
            if (send < packets.length) {
                debug{printf("sent packet %u\n", send);}
                fprintf(fw, "%s", cast(char*)packets[send]);
                ++send;
            }
            status = fill(st);
            debug{printf("queue: '%s'\n", cast(char*)st.buf);}
            if (status == Status.BIG_PACKET) {
                debug{printf("error: packet too big\n");}
                break;
            }
            assert(status == Status.READY);
        } else {
            assert(status == Status.BAD_PACKET);
            debug{printf("error: ill-formed packet\n");}
            break;
        }
    }

    // Check results.
    assert(status == expect);
    if (status == Status.END) assert(nc == numbers && wc == words);

    // Cleanup: remove input file.
    fclose(fw);
    fclose(fr);
    remove(fname);
}

void main() {
    string[] packets1 = [];
    string[] packets2 = [" zero one", " ", "123", "4 tw", "o  456789"];
    string[] packets3 = ["zer0"];
    string[] packets4 = ["looooooooooong;"];

    test(packets1, Status.END, 0, 0);
    test(packets2, Status.END, 2, 3);
    test(packets3, Status.BAD_PACKET, -1, -1);
    test(packets4, Status.BIG_PACKET, -1, -1);
}
