]> granicus.if.org Git - re2c/commitdiff
Make re2c:eof usable with push-model lexers (-f, --storable-state option).
authorUlya Trofimovich <skvadrik@gmail.com>
Tue, 15 Jan 2019 23:45:53 +0000 (23:45 +0000)
committerUlya Trofimovich <skvadrik@gmail.com>
Wed, 16 Jan 2019 00:04:42 +0000 (00:04 +0000)
re2c/src/codegen/emit_action.cc
re2c/src/codegen/emit_dfa.cc
re2c/src/parse/validate.cc
re2c/test/eof/nonblocking_push.fi.c [new file with mode: 0644]
re2c/test/eof/nonblocking_push.fi.re [new file with mode: 0644]

index 199fc492c3229cf25f8dc6da38424ca2f3785eb0..5714b2dd815f7d2d76974f51b692e9f95f0e8e5f 100644 (file)
@@ -31,8 +31,8 @@ static void emit_accept_binary (Output &o, uint32_t ind, const DFA &dfa, const a
 static void emit_accept        (Output &o, uint32_t ind, const DFA &dfa, const accept_t &acc);
 static void emit_rule          (Output &o, uint32_t ind, const DFA &dfa, size_t rule_idx);
 static void gen_fintags        (Output &o, uint32_t ind, const DFA &dfa, const Rule &rule);
-static void gen_goto           (code_lines_t &code, const State *from, const State *to, const DFA &dfa, tcid_t tcid, const opt_t *opts, bool skip, bool fill);
-static void gen_on_eof         (code_lines_t &code, const opt_t *opts, const DFA &dfa, const State *from, const State *to);
+static void gen_goto           (code_lines_t &, const State *, const State *, const DFA &, tcid_t, const opt_t *, bool, bool, uint32_t);
+static void gen_on_eof         (code_lines_t &, const opt_t *, const DFA &, const State *, const State *, uint32_t);
 static bool endstate           (const State *s);
 static void flushln            (code_lines_t &code, std::ostringstream &o);
 
@@ -245,13 +245,18 @@ void gen_rescan_label(Output &o, const State *s)
     if (opts->eof == NOEOF || endstate(s)) return;
 
     o.wstring(opts->labelPrefix).wlabel(s->label).ws("_:\n");
+
+    if (opts->fFlag) {
+        o.wstring(opts->yyfilllabel).wu32(o.fill_index).ws(":\n");
+        ++o.fill_index;
+    }
 }
 
 void gen_goto_case(Output &o, uint32_t ind, const State *from, const State *to,
     const DFA &dfa, tcid_t tcid, bool skip, bool fill)
 {
     code_lines_t code;
-    gen_goto(code, from, to, dfa, tcid, o.block().opts, skip, fill);
+    gen_goto(code, from, to, dfa, tcid, o.block().opts, skip, fill, o.fill_index);
     const size_t lines = code.size();
 
     if (lines == 1) {
@@ -268,7 +273,7 @@ void gen_goto_if(Output &o, uint32_t ind, const State *from, const State *to,
     const DFA &dfa, tcid_t tcid, bool skip, bool eof)
 {
     code_lines_t code;
-    gen_goto(code, from, to, dfa, tcid, o.block().opts, skip, eof);
+    gen_goto(code, from, to, dfa, tcid, o.block().opts, skip, eof, o.fill_index);
     const size_t lines = code.size();
 
     if (lines == 1) {
@@ -286,7 +291,7 @@ void gen_goto_plain(Output &o, uint32_t ind, const State *from, const State *to,
     const DFA &dfa, tcid_t tcid, bool skip, bool eof)
 {
     code_lines_t code;
-    gen_goto(code, from, to, dfa, tcid, o.block().opts, skip, eof);
+    gen_goto(code, from, to, dfa, tcid, o.block().opts, skip, eof, o.fill_index);
     const size_t lines = code.size();
 
     for (size_t i = 0; i < lines; ++i) {
@@ -295,10 +300,11 @@ void gen_goto_plain(Output &o, uint32_t ind, const State *from, const State *to,
 }
 
 void gen_goto(code_lines_t &code, const State *from, const State *to
-    , const DFA &dfa, tcid_t tcid, const opt_t *opts, bool skip, bool fill)
+    , const DFA &dfa, tcid_t tcid, const opt_t *opts, bool skip, bool fill
+    , uint32_t fillidx)
 {
     if (fill) {
-        gen_on_eof(code, opts, dfa, from, to);
+        gen_on_eof(code, opts, dfa, from, to, fillidx);
     }
 
     if (skip && !opts->lookahead) {
@@ -322,7 +328,7 @@ void gen_goto(code_lines_t &code, const State *from, const State *to
 }
 
 void gen_on_eof(code_lines_t &code, const opt_t *opts, const DFA &dfa
-  , const State *from, const State *to)
+  , const State *from, const State *to, uint32_t fillidx)
 {
     const State *retry = from->action.type == Action::MOVE ? from->prev : from;
     const State *fallback = from->rule == Rule::NONE
@@ -341,36 +347,51 @@ void gen_on_eof(code_lines_t &code, const opt_t *opts, const DFA &dfa
     o << ") {";
     flushln(code, o);
 
-    o << opts->indString << "if (" << opts->fill << " () == 0) "
-        << "goto " << opts->labelPrefix << retry->label << "_;";
-    flushln(code, o);
+    if (opts->fFlag) {
+        --fillidx;
+        std::string s = opts->state_set;
+        strrreplace(s, opts->state_set_arg, fillidx);
+        o << opts->indString << s;
+        if (!opts->state_set_naked) {
+            o << "(" << fillidx << ");";
+        }
+        flushln(code, o);
 
-    if (from->action.type == Action::INITIAL) {
-        o << opts->indString << "else goto " << opts->labelPrefix << "eof;";
+        o << opts->indString << opts->fill << " ();";
         flushln(code, o);
     }
-    else if (fallback != to) {
-        code_lines_t tagcode;
-        gen_settags(tagcode, dfa, falltags, opts);
+    else {
+        o << opts->indString << "if (" << opts->fill << " () == 0) "
+            << "goto " << opts->labelPrefix << retry->label << "_;";
+        flushln(code, o);
 
-        if (tagcode.empty()) {
-            o << opts->indString << "else goto " << opts->labelPrefix << fallback->label << ";";
+        if (from->action.type == Action::INITIAL) {
+            o << opts->indString << "else goto " << opts->labelPrefix << "eof;";
             flushln(code, o);
         }
-        else {
-            o << opts->indString << "else {";
-            flushln(code, o);
+        else if (fallback != to) {
+            code_lines_t tagcode;
+            gen_settags(tagcode, dfa, falltags, opts);
 
-            for (uint32_t i = 0; i < tagcode.size(); ++i) {
-                code.push_back(opts->indString + opts->indString + tagcode[i]);
+            if (tagcode.empty()) {
+                o << opts->indString << "else goto " << opts->labelPrefix << fallback->label << ";";
+                flushln(code, o);
             }
+            else {
+                o << opts->indString << "else {";
+                flushln(code, o);
 
-            o << opts->indString << opts->indString
-                << "goto " << opts->labelPrefix << fallback->label << ";";
-            flushln(code, o);
+                for (uint32_t i = 0; i < tagcode.size(); ++i) {
+                    code.push_back(opts->indString + opts->indString + tagcode[i]);
+                }
 
-            o << opts->indString << "}";
-            flushln(code, o);
+                o << opts->indString << opts->indString
+                    << "goto " << opts->labelPrefix << fallback->label << ";";
+                flushln(code, o);
+
+                o << opts->indString << "}";
+                flushln(code, o);
+            }
         }
     }
 
index 3d0bb60c89fee8dd951f62cb14d831919f50d246..83584b570c5e266260dfa5cc80a45f489ebbc792 100644 (file)
@@ -48,7 +48,7 @@ void emit_eof(Output & o, uint32_t ind, const Code *code)
 {
     const opt_t *opts = o.block().opts;
 
-    if (opts->eof == NOEOF) return;
+    if (opts->eof == NOEOF || opts->fFlag) return;
 
     o.wstring(opts->labelPrefix).ws("eof:\n");
     o.wdelay_line_info_input(code->fline, code->fname);
index b02d972fcec1f8c3a5bfd83417c932e6e521ae3a..c0709d1114f086ad167d6c4618e3d7f16a23bcba 100644 (file)
@@ -46,10 +46,14 @@ void validate_ast(const specs_t &specs, const opt_t *opts)
             fatal("%sEOF rule found, but 're2c:eof' configuration is not set",
                 incond(i->name).c_str());
         }
-        else if (i->eofs.empty() && opts->eof != NOEOF) {
+        else if (i->eofs.empty() && opts->eof != NOEOF && !opts->fFlag) {
             fatal("%s're2c:eof' configuration is set, but no EOF rule found",
                 incond(i->name).c_str());
         }
+        else if (!i->eofs.empty() && opts->fFlag) {
+            fatal("%sEOF rule is unreachable in push-model lexers",
+                incond(i->name).c_str());
+        }
         else if (i->eofs.size() > 1) {
             fatal_l(i->eofs[1]->fline,
                 "EOF rule %sis already defined at line %u",
diff --git a/re2c/test/eof/nonblocking_push.fi.c b/re2c/test/eof/nonblocking_push.fi.c
new file mode 100644 (file)
index 0000000..197cb81
--- /dev/null
@@ -0,0 +1,619 @@
+/* Generated by re2c */
+#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;
+
+    // simulate the END packet (can as well send a normal packet)
+    if (feof(file)) ++lim;
+
+    // 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()) {
+default: goto yy0;
+case 0: goto yyFillLabel0;
+case 1: goto yyFillLabel1;
+case 2: goto yyFillLabel2;
+case 3: goto yyFillLabel3;
+case 4: goto yyFillLabel4;
+case 5: goto yyFillLabel5;
+case 6: goto yyFillLabel6;
+case 7: goto yyFillLabel7;
+case 8: goto yyFillLabel8;
+case 9: goto yyFillLabel9;
+case 10: goto yyFillLabel10;
+case 11: goto yyFillLabel11;
+case 12: goto yyFillLabel12;
+case 13: goto yyFillLabel13;
+case 14: goto yyFillLabel14;
+case 15: goto yyFillLabel15;
+case 16: goto yyFillLabel16;
+case 17: goto yyFillLabel17;
+case 18: goto yyFillLabel18;
+case 19: goto yyFillLabel19;
+case 20: goto yyFillLabel20;
+}
+
+
+
+yy1:
+yy0:
+yy1_:
+yyFillLabel0:
+       in.yych = *in.cur;
+       switch (in.yych) {
+       case 0x00:
+               if (in.lim <= in.cur) {
+                       YYSETSTATE(0);
+                       YYFILL ();
+               }
+               goto yy2;
+       case '\n':
+       case ' ':       goto yy6;
+       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 yy9;
+       case 'T':       goto yy12;
+       default:        goto yy4;
+       }
+yy2:
+       ++in.cur;
+       { printf("< EOF\n");                                return OK; }
+yy4:
+       ++in.cur;
+       { printf("< Unexpected character >%c<\n", in.yych); return FAIL; }
+yy6:
+       ++in.cur;
+yy6_:
+yyFillLabel1:
+       in.yych = *in.cur;
+yy7:
+       switch (in.yych) {
+       case '\n':
+       case ' ':       goto yy6;
+       default:
+               if (in.lim <= in.cur) {
+                       YYSETSTATE(1);
+                       YYFILL ();
+               }
+               goto yy8;
+       }
+yy8:
+       { printf("< whitespace\n");                         return WHITESPACE; }
+yy9:
+       ++in.cur;
+yy9_:
+yyFillLabel2:
+       in.yych = *in.cur;
+yy10:
+       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 yy9;
+       default:
+               if (in.lim <= in.cur) {
+                       YYSETSTATE(2);
+                       YYFILL ();
+               }
+               goto yy11;
+       }
+yy11:
+       { printf("< word\n");                               return WORD; }
+yy12:
+       ++in.cur;
+yy12_:
+yyFillLabel3:
+       in.yych = *in.cur;
+       switch (in.yych) {
+       case 0x00:
+               if (in.lim <= in.cur) {
+                       YYSETSTATE(3);
+                       YYFILL ();
+               }
+               goto yy11;
+       case 'H':       goto yy13;
+       default:        goto yy10;
+       }
+yy13:
+       ++in.cur;
+yy13_:
+yyFillLabel4:
+       in.yych = *in.cur;
+       switch (in.yych) {
+       case 0x00:
+               if (in.lim <= in.cur) {
+                       YYSETSTATE(4);
+                       YYFILL ();
+               }
+               goto yy11;
+       case 'I':       goto yy14;
+       default:        goto yy10;
+       }
+yy14:
+       ++in.cur;
+yy14_:
+yyFillLabel5:
+       in.yych = *in.cur;
+       switch (in.yych) {
+       case 0x00:
+               if (in.lim <= in.cur) {
+                       YYSETSTATE(5);
+                       YYFILL ();
+               }
+               goto yy11;
+       case 'N':       goto yy15;
+       default:        goto yy10;
+       }
+yy15:
+       ++in.cur;
+yy15_:
+yyFillLabel6:
+       in.yych = *in.cur;
+       switch (in.yych) {
+       case 0x00:
+               if (in.lim <= in.cur) {
+                       YYSETSTATE(6);
+                       YYFILL ();
+               }
+               goto yy11;
+       case 'G':       goto yy16;
+       default:        goto yy10;
+       }
+yy16:
+       in.mark = ++in.cur;
+yy16_:
+yyFillLabel7:
+       in.yych = *in.cur;
+       switch (in.yych) {
+       case 0x00:
+               if (in.lim <= in.cur) {
+                       YYSETSTATE(7);
+                       YYFILL ();
+               }
+               goto yy11;
+       case '\n':      goto yy17;
+       default:        goto yy10;
+       }
+yy17:
+       ++in.cur;
+yy17_:
+yyFillLabel8:
+       in.yych = *in.cur;
+       switch (in.yych) {
+       case 'W':       goto yy19;
+       default:
+               if (in.lim <= in.cur) {
+                       YYSETSTATE(8);
+                       YYFILL ();
+               }
+               goto yy18;
+       }
+yy18:
+       in.cur = in.mark;
+       goto yy11;
+yy19:
+       ++in.cur;
+yy19_:
+yyFillLabel9:
+       in.yych = *in.cur;
+       switch (in.yych) {
+       case 'I':       goto yy20;
+       default:
+               if (in.lim <= in.cur) {
+                       YYSETSTATE(9);
+                       YYFILL ();
+               }
+               goto yy18;
+       }
+yy20:
+       ++in.cur;
+yy20_:
+yyFillLabel10:
+       in.yych = *in.cur;
+       switch (in.yych) {
+       case 'T':       goto yy21;
+       default:
+               if (in.lim <= in.cur) {
+                       YYSETSTATE(10);
+                       YYFILL ();
+               }
+               goto yy18;
+       }
+yy21:
+       ++in.cur;
+yy21_:
+yyFillLabel11:
+       in.yych = *in.cur;
+       switch (in.yych) {
+       case 'H':       goto yy22;
+       default:
+               if (in.lim <= in.cur) {
+                       YYSETSTATE(11);
+                       YYFILL ();
+               }
+               goto yy18;
+       }
+yy22:
+       ++in.cur;
+yy22_:
+yyFillLabel12:
+       in.yych = *in.cur;
+       switch (in.yych) {
+       case '\n':      goto yy23;
+       default:
+               if (in.lim <= in.cur) {
+                       YYSETSTATE(12);
+                       YYFILL ();
+               }
+               goto yy18;
+       }
+yy23:
+       ++in.cur;
+yy23_:
+yyFillLabel13:
+       in.yych = *in.cur;
+       switch (in.yych) {
+       case 'N':       goto yy24;
+       default:
+               if (in.lim <= in.cur) {
+                       YYSETSTATE(13);
+                       YYFILL ();
+               }
+               goto yy18;
+       }
+yy24:
+       ++in.cur;
+yy24_:
+yyFillLabel14:
+       in.yych = *in.cur;
+       switch (in.yych) {
+       case 'E':       goto yy25;
+       default:
+               if (in.lim <= in.cur) {
+                       YYSETSTATE(14);
+                       YYFILL ();
+               }
+               goto yy18;
+       }
+yy25:
+       ++in.cur;
+yy25_:
+yyFillLabel15:
+       in.yych = *in.cur;
+       switch (in.yych) {
+       case 'W':       goto yy26;
+       default:
+               if (in.lim <= in.cur) {
+                       YYSETSTATE(15);
+                       YYFILL ();
+               }
+               goto yy18;
+       }
+yy26:
+       ++in.cur;
+yy26_:
+yyFillLabel16:
+       in.yych = *in.cur;
+       switch (in.yych) {
+       case 'L':       goto yy27;
+       default:
+               if (in.lim <= in.cur) {
+                       YYSETSTATE(16);
+                       YYFILL ();
+               }
+               goto yy18;
+       }
+yy27:
+       ++in.cur;
+yy27_:
+yyFillLabel17:
+       in.yych = *in.cur;
+       switch (in.yych) {
+       case 'I':       goto yy28;
+       default:
+               if (in.lim <= in.cur) {
+                       YYSETSTATE(17);
+                       YYFILL ();
+               }
+               goto yy18;
+       }
+yy28:
+       ++in.cur;
+yy28_:
+yyFillLabel18:
+       in.yych = *in.cur;
+       switch (in.yych) {
+       case 'N':       goto yy29;
+       default:
+               if (in.lim <= in.cur) {
+                       YYSETSTATE(18);
+                       YYFILL ();
+               }
+               goto yy18;
+       }
+yy29:
+       ++in.cur;
+yy29_:
+yyFillLabel19:
+       in.yych = *in.cur;
+       switch (in.yych) {
+       case 'E':       goto yy30;
+       default:
+               if (in.lim <= in.cur) {
+                       YYSETSTATE(19);
+                       YYFILL ();
+               }
+               goto yy18;
+       }
+yy30:
+       ++in.cur;
+yy30_:
+yyFillLabel20:
+       in.yych = *in.cur;
+       switch (in.yych) {
+       case 'S':       goto yy31;
+       default:
+               if (in.lim <= in.cur) {
+                       YYSETSTATE(20);
+                       YYFILL ();
+               }
+               goto yy18;
+       }
+yy31:
+       ++in.cur;
+       { printf("< Thing w/ newlines\n");                  return THING; }
+
+}
+
+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
diff --git a/re2c/test/eof/nonblocking_push.fi.re b/re2c/test/eof/nonblocking_push.fi.re
new file mode 100644 (file)
index 0000000..1537aa6
--- /dev/null
@@ -0,0 +1,181 @@
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/select.h>
+#include <assert.h>
+
+/*!max:re2c*/
+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;
+
+    // simulate the END packet (can as well send a normal packet)
+    if (feof(file)) ++lim;
+
+    // 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)
+{
+/*!getstate:re2c*/
+/*!re2c
+    re2c:define:YYCTYPE  = char;
+    re2c:define:YYCURSOR = in.cur;
+    re2c:define:YYLIMIT  = in.lim;
+    re2c:define:YYMARKER = in.mark;
+    re2c:variable:yych   = in.yych;
+    re2c:eof             = 0;
+
+    *                       { printf("< Unexpected character >%c<\n", in.yych); return FAIL; }
+    [\x00]                  { printf("< EOF\n");                                return OK; }
+    [\n ]+                  { printf("< whitespace\n");                         return WHITESPACE; }
+    [a-zA-Z]+               { printf("< word\n");                               return WORD; }
+    "THING\nWITH\nNEWLINES" { printf("< Thing w/ newlines\n");                  return THING; }
+*/
+}
+
+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