// Code generated by re2v, DO NOT EDIT.
//line "v/state/push.re":1
// re2v -f $INPUT -o $OUTPUT

import log
import os

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

struct State {
mut:
    file     os.File
    yyinput  []u8
    yycursor int
    yymarker int
    yylimit  int
    token    int
    yystate  int
}

enum Status {
    lex_end
    lex_ready
    lex_waiting
    lex_bad_packet
    lex_big_packet
}

fn fill(mut st &State) Status {
    shift := st.token
    used := st.yylimit - st.token
    free := bufsize - used

    // Error: no space. In real life can reallocate a larger buffer.
    if free < 1 { return .lex_big_packet }

    // Shift buffer contents (discard already processed data).
    copy(mut &st.yyinput, st.yyinput[shift..shift+used])
    st.yycursor -= shift
    st.yymarker -= shift
    st.yylimit -= shift
    st.token -= shift

    // Fill free space at the end of buffer with new data.
    pos := st.file.tell() or { 0 }
    if n := st.file.read_bytes_into(u64(pos), mut st.yyinput[st.yylimit..bufsize]) {
        st.yylimit += n
    }
    st.yyinput[st.yylimit] = 0 // append sentinel symbol

    return .lex_ready
}

fn lex(mut yyrecord &State, mut recv &int) Status {
    mut yych := u8(0)
    
//line "v/state/push.v":60
match yyrecord.yystate {
    0 {
        if yyrecord.yylimit <= yyrecord.yycursor {
            unsafe { goto yy8 }
        }
        unsafe { goto yyFillLabel0 }
    }
    1 {
        if yyrecord.yylimit <= yyrecord.yycursor {
            unsafe { goto yy3 }
        }
        unsafe { goto yyFillLabel1 }
    }
    2 {
        if yyrecord.yylimit <= yyrecord.yycursor {
            unsafe { goto yy7 }
        }
        unsafe { goto yyFillLabel2 }
    }
    else { unsafe { goto yy0 } }
}
//line "v/state/push.re":56

loop:
    yyrecord.token = yyrecord.yycursor
    
//line "v/state/push.v":87
yy0:
yyFillLabel0:
    yych = yyrecord.yyinput[yyrecord.yycursor]
    match yych {
        0x61...0x7A { unsafe { goto yy4 } }
        else {
            if yyrecord.yylimit <= yyrecord.yycursor {
                yyrecord.yystate = 0
                return .lex_waiting
            }
            unsafe { goto yy2 }
        }
    }
yy2:
    yyrecord.yycursor += 1
yy3:
    yyrecord.yystate = -1
//line "v/state/push.re":66
    return .lex_bad_packet
//line "v/state/push.v":107
yy4:
    yyrecord.yycursor += 1
    yyrecord.yymarker = yyrecord.yycursor
yyFillLabel1:
    yych = yyrecord.yyinput[yyrecord.yycursor]
    match yych {
        0x3B { unsafe { goto yy5 } }
        0x61...0x7A { unsafe { goto yy6 } }
        else {
            if yyrecord.yylimit <= yyrecord.yycursor {
                yyrecord.yystate = 1
                return .lex_waiting
            }
            unsafe { goto yy3 }
        }
    }
yy5:
    yyrecord.yycursor += 1
    yyrecord.yystate = -1
//line "v/state/push.re":68
    recv += 1; unsafe{ goto loop }
//line "v/state/push.v":129
yy6:
    yyrecord.yycursor += 1
yyFillLabel2:
    yych = yyrecord.yyinput[yyrecord.yycursor]
    match yych {
        0x3B { unsafe { goto yy5 } }
        0x61...0x7A { unsafe { goto yy6 } }
        else {
            if yyrecord.yylimit <= yyrecord.yycursor {
                yyrecord.yystate = 2
                return .lex_waiting
            }
            unsafe { goto yy7 }
        }
    }
yy7:
    yyrecord.yycursor = yyrecord.yymarker
    unsafe { goto yy3 }
yy8:
    yyrecord.yystate = -1
//line "v/state/push.re":67
    return .lex_end
//line "v/state/push.v":152
//line "v/state/push.re":69

}

fn test(expect Status, packets []string) {
    // Create a pipe (open the same file for reading and writing).
    fname := "pipe"
    mut fw := os.create(fname) or { panic("cannot create file") }
    mut fr := os.open(fname) or { panic("cannot open file") }

    // Initialize lexer state: `state` value is -1, all offsets are at the end
    // of buffer.
    mut st := &State{
        file:     fr,
        // Sentinel at `yylimit` offset is set to zero, which triggers YYFILL.
        yyinput:  []u8{len: bufsize + 1},
        yycursor: bufsize,
        yymarker: bufsize,
        yylimit:  bufsize,
        token:    bufsize,
        yystate:  -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.
    mut status := Status.lex_ready
    mut send := 0
    mut recv := 0
    for {
        status = lex(mut &st, mut &recv)
        if status == .lex_end {
            break
        } else if status == .lex_waiting {
            if send < packets.len {
                log.debug("sending packet $send")
                fw.write_string(packets[send]) or { panic("cannot write to file") }
                fw.flush()
                send += 1
            }
            status = fill(mut &st)
            log.debug("filled buffer $st.yyinput, status $status")
            if status != .lex_ready {
                break
            }
        } else if status == .lex_bad_packet {
            break
        }
    }

    // Check results.
    if status != expect || (status == .lex_end && recv != send) {
        panic("expected $expect with $send packet(s), got $status with $recv packet(s)")
    }

    // Cleanup: remove input file.
    fr.close()
    fw.close()
    os.rm(fname) or { panic("cannot remove file") }
}

fn main() {
    //log.set_level(.debug)

    test(.lex_end, [])
    test(.lex_end, ["zero;", "one;", "two;", "three;", "four;"])
    test(.lex_bad_packet, ["??;"])
    test(.lex_big_packet, ["looooooooooooong;"])
}
