/* Generated by re2c */
// re2c $INPUT -o $OUTPUT -fi
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/select.h>
#include <assert.h>

#define YYMAXFILL 19

static const size_t SIZE = 4096;

struct input_t {
  char buf[SIZE + YYMAXFILL];
  char *lim;
  char *cur;
  char *tok;
  char *mark;
  int state;
  unsigned yyaccept;
  char yych;
  FILE *file;

  input_t(FILE * file)
    : buf()
    , lim(buf + SIZE)
    , cur(lim)
    , tok(lim)
    , mark(lim)
    , state(0)
    , yyaccept(0)
    , yych(0)
    , file(file)
  {}

  bool fill()
  {
    const size_t free = (tok - buf) + SIZE - (lim - buf);
    if (free < 1) return false;
    const size_t prefix = (tok - buf);

    memmove(buf, tok, buf - tok + SIZE);
    lim -= prefix;
    cur -= prefix;
    tok -= prefix;
    mark -= prefix;
    size_t to_read = SIZE - (lim - buf);
    printf("> Reading input, can take up to %lu bytes\n", to_read);
    size_t bytes_read = fread(lim, 1, to_read, file);
    lim += bytes_read;
    lim[0] = 0;

    // quick make a copy of buffer with newlines replaced w/ _
    char b[40];
    snprintf(b, 40, "%s", buf);
    for(int i = 0; i < 40; i++) {
      if ('\n' == b[i]) { b[i] = '_'; }
    }
    printf("> Read %lu bytes from input, current buffer: >%.40s<\n", bytes_read, b);

    return true;
  }
};

enum status_t {
  OK,
  FAIL,
  NEED_MORE_INPUT,
  WHITESPACE,
  WORD,
  THING
};
const char * STATUSES[] = {
  "OK",
  "FAIL",
  "NEED_MORE_INPUT",
  "WHITESPACE",
  "WORD",
  "THING"
};

#define YYGETSTATE()  in.state
#define YYSETSTATE(s) in.state = s
#define YYFILL() do { \
  printf("< Returning for more input\n"); \
  return NEED_MORE_INPUT; \
} while (0)

static status_t lex(input_t &in)
{
switch (YYGETSTATE()) {
	case 0:
		if (in.lim <= in.cur) goto yy28;
		goto yyFillLabel0;
	case 1:
		if (in.lim <= in.cur) goto yy4;
		goto yyFillLabel1;
	case 2:
		if (in.lim <= in.cur) goto yy7;
		goto yyFillLabel2;
	case 3:
		if (in.lim <= in.cur) goto yy7;
		goto yyFillLabel3;
	case 4:
		if (in.lim <= in.cur) goto yy7;
		goto yyFillLabel4;
	case 5:
		if (in.lim <= in.cur) goto yy7;
		goto yyFillLabel5;
	case 6:
		if (in.lim <= in.cur) goto yy7;
		goto yyFillLabel6;
	case 7:
		if (in.lim <= in.cur) goto yy7;
		goto yyFillLabel7;
	case 8:
		if (in.lim <= in.cur) goto yy14;
		goto yyFillLabel8;
	case 9:
		if (in.lim <= in.cur) goto yy14;
		goto yyFillLabel9;
	case 10:
		if (in.lim <= in.cur) goto yy14;
		goto yyFillLabel10;
	case 11:
		if (in.lim <= in.cur) goto yy14;
		goto yyFillLabel11;
	case 12:
		if (in.lim <= in.cur) goto yy14;
		goto yyFillLabel12;
	case 13:
		if (in.lim <= in.cur) goto yy14;
		goto yyFillLabel13;
	case 14:
		if (in.lim <= in.cur) goto yy14;
		goto yyFillLabel14;
	case 15:
		if (in.lim <= in.cur) goto yy14;
		goto yyFillLabel15;
	case 16:
		if (in.lim <= in.cur) goto yy14;
		goto yyFillLabel16;
	case 17:
		if (in.lim <= in.cur) goto yy14;
		goto yyFillLabel17;
	case 18:
		if (in.lim <= in.cur) goto yy14;
		goto yyFillLabel18;
	case 19:
		if (in.lim <= in.cur) goto yy14;
		goto yyFillLabel19;
	case 20:
		if (in.lim <= in.cur) goto yy14;
		goto yyFillLabel20;
	default: goto yy0;
}


yy0:
yyFillLabel0:
	in.yych = *in.cur;
	switch (in.yych) {
		case '\n':
		case ' ': goto yy3;
		case 'A':
		case 'B':
		case 'C':
		case 'D':
		case 'E':
		case 'F':
		case 'G':
		case 'H':
		case 'I':
		case 'J':
		case 'K':
		case 'L':
		case 'M':
		case 'N':
		case 'O':
		case 'P':
		case 'Q':
		case 'R':
		case 'S':
		case 'U':
		case 'V':
		case 'W':
		case 'X':
		case 'Y':
		case 'Z':
		case 'a':
		case 'b':
		case 'c':
		case 'd':
		case 'e':
		case 'f':
		case 'g':
		case 'h':
		case 'i':
		case 'j':
		case 'k':
		case 'l':
		case 'm':
		case 'n':
		case 'o':
		case 'p':
		case 'q':
		case 'r':
		case 's':
		case 't':
		case 'u':
		case 'v':
		case 'w':
		case 'x':
		case 'y':
		case 'z': goto yy5;
		case 'T': goto yy8;
		default:
			if (in.lim <= in.cur) {
				YYSETSTATE(0);
				YYFILL();
			}
			goto yy2;
	}
yy2:
	++in.cur;
	YYSETSTATE(-1);
	{ printf("< Unexpected character >%c<\n", in.yych); return FAIL; }
yy3:
	++in.cur;
yyFillLabel1:
	in.yych = *in.cur;
	switch (in.yych) {
		case '\n':
		case ' ': goto yy3;
		default:
			if (in.lim <= in.cur) {
				YYSETSTATE(1);
				YYFILL();
			}
			goto yy4;
	}
yy4:
	YYSETSTATE(-1);
	{ printf("< whitespace\n");                         return WHITESPACE; }
yy5:
	++in.cur;
yyFillLabel2:
	in.yych = *in.cur;
yy6:
	switch (in.yych) {
		case 'A':
		case 'B':
		case 'C':
		case 'D':
		case 'E':
		case 'F':
		case 'G':
		case 'H':
		case 'I':
		case 'J':
		case 'K':
		case 'L':
		case 'M':
		case 'N':
		case 'O':
		case 'P':
		case 'Q':
		case 'R':
		case 'S':
		case 'T':
		case 'U':
		case 'V':
		case 'W':
		case 'X':
		case 'Y':
		case 'Z':
		case 'a':
		case 'b':
		case 'c':
		case 'd':
		case 'e':
		case 'f':
		case 'g':
		case 'h':
		case 'i':
		case 'j':
		case 'k':
		case 'l':
		case 'm':
		case 'n':
		case 'o':
		case 'p':
		case 'q':
		case 'r':
		case 's':
		case 't':
		case 'u':
		case 'v':
		case 'w':
		case 'x':
		case 'y':
		case 'z': goto yy5;
		default:
			if (in.lim <= in.cur) {
				YYSETSTATE(2);
				YYFILL();
			}
			goto yy7;
	}
yy7:
	YYSETSTATE(-1);
	{ printf("< word\n");                               return WORD; }
yy8:
	++in.cur;
yyFillLabel3:
	in.yych = *in.cur;
	switch (in.yych) {
		case 0x00:
			if (in.lim <= in.cur) {
				YYSETSTATE(3);
				YYFILL();
			}
			goto yy7;
		case 'H': goto yy9;
		default: goto yy6;
	}
yy9:
	++in.cur;
yyFillLabel4:
	in.yych = *in.cur;
	switch (in.yych) {
		case 0x00:
			if (in.lim <= in.cur) {
				YYSETSTATE(4);
				YYFILL();
			}
			goto yy7;
		case 'I': goto yy10;
		default: goto yy6;
	}
yy10:
	++in.cur;
yyFillLabel5:
	in.yych = *in.cur;
	switch (in.yych) {
		case 0x00:
			if (in.lim <= in.cur) {
				YYSETSTATE(5);
				YYFILL();
			}
			goto yy7;
		case 'N': goto yy11;
		default: goto yy6;
	}
yy11:
	++in.cur;
yyFillLabel6:
	in.yych = *in.cur;
	switch (in.yych) {
		case 0x00:
			if (in.lim <= in.cur) {
				YYSETSTATE(6);
				YYFILL();
			}
			goto yy7;
		case 'G': goto yy12;
		default: goto yy6;
	}
yy12:
	in.mark = ++in.cur;
yyFillLabel7:
	in.yych = *in.cur;
	switch (in.yych) {
		case 0x00:
			if (in.lim <= in.cur) {
				YYSETSTATE(7);
				YYFILL();
			}
			goto yy7;
		case '\n': goto yy13;
		default: goto yy6;
	}
yy13:
	++in.cur;
yyFillLabel8:
	in.yych = *in.cur;
	switch (in.yych) {
		case 'W': goto yy15;
		default:
			if (in.lim <= in.cur) {
				YYSETSTATE(8);
				YYFILL();
			}
			goto yy14;
	}
yy14:
	in.cur = in.mark;
	goto yy7;
yy15:
	++in.cur;
yyFillLabel9:
	in.yych = *in.cur;
	switch (in.yych) {
		case 'I': goto yy16;
		default:
			if (in.lim <= in.cur) {
				YYSETSTATE(9);
				YYFILL();
			}
			goto yy14;
	}
yy16:
	++in.cur;
yyFillLabel10:
	in.yych = *in.cur;
	switch (in.yych) {
		case 'T': goto yy17;
		default:
			if (in.lim <= in.cur) {
				YYSETSTATE(10);
				YYFILL();
			}
			goto yy14;
	}
yy17:
	++in.cur;
yyFillLabel11:
	in.yych = *in.cur;
	switch (in.yych) {
		case 'H': goto yy18;
		default:
			if (in.lim <= in.cur) {
				YYSETSTATE(11);
				YYFILL();
			}
			goto yy14;
	}
yy18:
	++in.cur;
yyFillLabel12:
	in.yych = *in.cur;
	switch (in.yych) {
		case '\n': goto yy19;
		default:
			if (in.lim <= in.cur) {
				YYSETSTATE(12);
				YYFILL();
			}
			goto yy14;
	}
yy19:
	++in.cur;
yyFillLabel13:
	in.yych = *in.cur;
	switch (in.yych) {
		case 'N': goto yy20;
		default:
			if (in.lim <= in.cur) {
				YYSETSTATE(13);
				YYFILL();
			}
			goto yy14;
	}
yy20:
	++in.cur;
yyFillLabel14:
	in.yych = *in.cur;
	switch (in.yych) {
		case 'E': goto yy21;
		default:
			if (in.lim <= in.cur) {
				YYSETSTATE(14);
				YYFILL();
			}
			goto yy14;
	}
yy21:
	++in.cur;
yyFillLabel15:
	in.yych = *in.cur;
	switch (in.yych) {
		case 'W': goto yy22;
		default:
			if (in.lim <= in.cur) {
				YYSETSTATE(15);
				YYFILL();
			}
			goto yy14;
	}
yy22:
	++in.cur;
yyFillLabel16:
	in.yych = *in.cur;
	switch (in.yych) {
		case 'L': goto yy23;
		default:
			if (in.lim <= in.cur) {
				YYSETSTATE(16);
				YYFILL();
			}
			goto yy14;
	}
yy23:
	++in.cur;
yyFillLabel17:
	in.yych = *in.cur;
	switch (in.yych) {
		case 'I': goto yy24;
		default:
			if (in.lim <= in.cur) {
				YYSETSTATE(17);
				YYFILL();
			}
			goto yy14;
	}
yy24:
	++in.cur;
yyFillLabel18:
	in.yych = *in.cur;
	switch (in.yych) {
		case 'N': goto yy25;
		default:
			if (in.lim <= in.cur) {
				YYSETSTATE(18);
				YYFILL();
			}
			goto yy14;
	}
yy25:
	++in.cur;
yyFillLabel19:
	in.yych = *in.cur;
	switch (in.yych) {
		case 'E': goto yy26;
		default:
			if (in.lim <= in.cur) {
				YYSETSTATE(19);
				YYFILL();
			}
			goto yy14;
	}
yy26:
	++in.cur;
yyFillLabel20:
	in.yych = *in.cur;
	switch (in.yych) {
		case 'S': goto yy27;
		default:
			if (in.lim <= in.cur) {
				YYSETSTATE(20);
				YYFILL();
			}
			goto yy14;
	}
yy27:
	++in.cur;
	YYSETSTATE(-1);
	{ printf("< Thing w/ newlines\n");                  return THING; }
yy28:
	YYSETSTATE(-1);
	{ printf("< EOF\n");                                return OK; }

}

int main()
{
  int fds[2];
  pipe(fds);
  fcntl(fds[0], F_SETFL, fcntl(fds[0], F_GETFL) | O_NONBLOCK);
  FILE * write = fdopen(fds[1], "w");
  FILE * read = fdopen(fds[0], "r");
  input_t in(read);

  const char * packets[] = {
    "THING\n",
    "WITH\n",
    "NEWLINES\n",
    "H", "E", "L", "O", "\n",
    "HELO\n",
    "THING\nWITH\n",
    "NEWLINES"
  };
  size_t num_packets = sizeof(packets) / sizeof(char *);
  size_t current_packet = 0;

  enum status_t result = NEED_MORE_INPUT;

  while (OK != result) {
    switch (result) {
      case NEED_MORE_INPUT:
        if (current_packet == num_packets) {
          printf("Not enough input\n");
          goto end;
        }

        fwrite(packets[current_packet], strlen(packets[current_packet]), 1, write);
        fflush(write);
        current_packet++;
        printf("Packet %lu / %lu written\n", current_packet, num_packets);

        if (current_packet == num_packets) {
          printf("%lu / %lu packets sent, Closing down communication channel\n",
            current_packet, num_packets);
          fclose(write);
          write = NULL;
        }

        in.fill();
        break;

      case FAIL:
        goto end;

      default:
        // careful, need to reset state (re2c forgets to do it)
        YYSETSTATE(0);
        break;
    }

    result = lex(in);
    printf("Received response from lexer: %s\n", STATUSES[result]);
  }

end:

  // cleanup
  fclose(read);
  if (write) {
    fclose(write);
  }

  return result == OK ? 0 : 1;
}

#undef YYGETSTATE
#undef YYSETSTATE
#undef YYFILL
