From e6eb2efa882d70c2511c9d76405e0437a51cd2ed Mon Sep 17 00:00:00 2001
From: "Meredith L. Patterson" <clonearmy@gmail.com>
Date: Sun, 13 May 2012 01:18:18 +0100
Subject: [PATCH] Left-recursion caching strategy about halfway implemented.
 Compiles, DOES NOT RUN.

---
 src/hammer.c   | 73 +++++++++++++++++++++++++++++++++++++-------------
 src/hammer.h   |  1 +
 src/internal.h | 24 +++++++++++------
 3 files changed, 71 insertions(+), 27 deletions(-)

diff --git a/src/hammer.c b/src/hammer.c
index 0e48b85c..b3060737 100644
--- a/src/hammer.c
+++ b/src/hammer.c
@@ -35,36 +35,70 @@ guint djbhash(const uint8_t *buf, size_t len) {
   return hash;
 }
 
+void setupLR(const parser_t *p, GQueue *stack, LR_t *recDetect) {
+  
+}
+
+parse_result_t* lr_answer(const parser_t *p, parse_state_t *state, LR_t *growable) {
+  return NULL;
+}
+
+parse_result_t* grow(const parser_t *p, parse_state_t *state, head_t *head) {
+  return NULL;
+}
+
 parse_result_t* do_parse(const parser_t* parser, parse_state_t *state) {
   // TODO(thequux): add caching here.
-  parser_cache_key_t key = {
-    .input_pos = state->input_stream,
-    .parser = parser
-  };
+  parser_cache_key_t *key = a_new(parser_cache_key_t, 1);
+  key->input_pos = state->input_stream;
+  key->parser = parser;
   
   // check to see if there is already a result for this object...
-  if (g_hash_table_contains(state->cache, &key)) {
-    // it exists!
-    // TODO(thequux): handle left recursion case
-    return g_hash_table_lookup(state->cache, &key);
-  } else {
-    // It doesn't exist... run the 
-    parse_result_t *res;
+  if (!g_hash_table_contains(state->cache, key)) {
+    // It doesn't exist, so create a dummy result to cache
+    LR_t *base = a_new(LR_t, 1);
+    base->seed = NULL; base->rule = parser; base->head = NULL;
+    g_queue_push_head(state->input_stream.lr_stack, base);
+    // cache it
+    parser_cache_value_t *dummy = a_new(parser_cache_value_t, 1);
+    dummy->value_type = PC_LEFT; dummy->left = base;
+    g_hash_table_replace(state->cache, key, dummy);
+    // parse the input
+    parse_result_t *tmp_res;
     if (parser)
-      res = parser->fn(parser->env, state);
+      tmp_res = parser->fn(parser->env, state);
     else
-      res = NULL;
+      tmp_res = NULL;
     if (state->input_stream.overrun)
-      res = NULL; // overrun is always failure.
-    // update the cache
-    g_hash_table_replace(state->cache, &key, res);
+      return NULL; // overrun is always failure.
 #ifdef CONSISTENCY_CHECK
-    if (!res) {
+    if (!tmp_res) {
       state->input_stream = INVALID;
-      state->input_stream.input = key.input_pos.input;
+      state->input_stream.input = key->input_pos.input;
     }
 #endif
-    return res;
+    // the base variable has passed equality tests with the cache
+    g_queue_pop_head(state->input_stream.lr_stack);
+    // setupLR, used below, mutates the LR to have a head if appropriate, so we check to see if we have one
+    if (NULL == base->head) {
+      parser_cache_value_t *right = a_new(parser_cache_value_t, 1);
+      right->value_type = PC_RIGHT; right->right = tmp_res;
+      g_hash_table_replace(state->cache, key, right);
+      return tmp_res;
+    } else {
+      base->seed = tmp_res;
+      parse_result_t *res = lr_answer(parser, state, base);
+      return res;
+    }
+  } else {
+    // it exists!
+    parser_cache_value_t *value = g_hash_table_lookup(state->cache, key);
+    if (PC_LEFT == value->value_type) {
+      setupLR(parser, state->input_stream.lr_stack, value->left);
+      return value->left->seed; // BUG: this might not be correct
+    } else {
+      return value->right;
+    }
   }
 }
 
@@ -489,6 +523,7 @@ parse_result_t* parse(const parser_t* parser, const uint8_t* input, size_t lengt
   parse_state->input_stream.overrun = 0;
   parse_state->input_stream.endianness = BIT_BIG_ENDIAN | BYTE_BIG_ENDIAN;
   parse_state->input_stream.length = length;
+  g_queue_init(parse_state->input_stream.lr_stack);
   parse_state->arena = arena;
   parse_result_t *res = do_parse(parser, parse_state);
   // tear down the parse state. For now, leak like a sieve.
diff --git a/src/hammer.h b/src/hammer.h
index a3536414..421c9f33 100644
--- a/src/hammer.h
+++ b/src/hammer.h
@@ -43,6 +43,7 @@ typedef struct input_stream {
   char bit_offset;
   char endianness;
   char overrun;
+  GQueue *lr_stack;
 } input_stream_t;
   
 typedef struct parse_state {
diff --git a/src/internal.h b/src/internal.h
index 8a756c43..e08d5fcd 100644
--- a/src/internal.h
+++ b/src/internal.h
@@ -29,19 +29,27 @@ typedef struct parser_cache_key {
 } parser_cache_key_t;
 
 typedef enum parser_cache_value_type {
-  PC_BASE,
-  PC_IN_RECURSION,
-  PC_LRESULT,
-  PC_RESULT
+  PC_LEFT,
+  PC_RIGHT
 } parser_cache_value_type_t;
 
+typedef struct head {
+  parser_t *head_parser;
+  GSList *involved_set;
+  GSList *eval_set;
+} head_t;
+
+typedef struct LR {
+  parse_result_t *seed;
+  const parser_t *rule;
+  head_t *head;
+} LR_t;
+
 typedef struct parser_cache_value {
   parser_cache_value_type_t value_type;
   union {
-    int base;
-    parse_result_t *in_recursion;
-    parse_result_t *lresult;
-    parse_result_t *result;
+    LR_t *left;
+    parse_result_t *right;
   };
 } parser_cache_value_t;
 
-- 
GitLab