From 54ba62bfb7e77db43df7f50dbdfc9640bc10ebb2 Mon Sep 17 00:00:00 2001
From: "Sven M. Hallberg" <pesco@khjk.org>
Date: Wed, 19 Jun 2013 17:01:13 +0200
Subject: [PATCH] record conflicts in a special HLRAction type

---
 src/backends/lalr.c | 13 ++++++++++---
 src/backends/lr.c   | 21 +++++++++++++++++++++
 src/backends/lr.h   | 13 ++++++++++---
 src/backends/lr0.c  | 24 +++++++++++++++---------
 4 files changed, 56 insertions(+), 15 deletions(-)

diff --git a/src/backends/lalr.c b/src/backends/lalr.c
index fa67e5a7..698b106d 100644
--- a/src/backends/lalr.c
+++ b/src/backends/lalr.c
@@ -138,7 +138,8 @@ int h_lrtable_put(HLRTable *tbl, size_t state, HCFChoice *x, HLRAction *action)
   HLRAction *prev = h_hashtable_get(tbl->rows[state], x);
   if(prev && prev != action) {
     // conflict
-    h_slist_push(tbl->inadeq, (void *)(uintptr_t)state);
+    action = h_lr_conflict(tbl->arena, prev, action);
+    h_hashtable_put(tbl->rows[state], x, action);
     return -1;
   } else {
     h_hashtable_put(tbl->rows[state], x, action);
@@ -221,6 +222,7 @@ int h_lalr_compile(HAllocator* mm__, HParser* parser, const void* params)
     
     for(HSlistNode *x=inadeq->head; x; x=x->next) {
       size_t state = (uintptr_t)x->elem;
+      bool inadeq = false;
       
       // clear old forall entry, it's being replaced by more fine-grained ones
       table->forall[state] = NULL;
@@ -255,7 +257,8 @@ int h_lalr_compile(HAllocator* mm__, HParser* parser, const void* params)
             if(fs->end_branch) {
               HCFChoice *terminal = h_arena_malloc(arena, sizeof(HCFChoice));
               terminal->type = HCF_END;
-              h_lrtable_put(table, state, terminal, action);
+              if(h_lrtable_put(table, state, terminal, action) < 0)
+                inadeq = true;
             }
             H_FOREACH(fs->char_branches, void *key, HStringMap *m)
               if(!m->epsilon_branch)
@@ -265,10 +268,14 @@ int h_lalr_compile(HAllocator* mm__, HParser* parser, const void* params)
               terminal->type = HCF_CHAR; 
               terminal->chr = key_char((HCharKey)key);
 
-              h_lrtable_put(table, state, terminal, action);
+              if(h_lrtable_put(table, state, terminal, action) < 0)
+                inadeq = true;
             H_END_FOREACH  // lookahead character
         } H_END_FOREACH // enhanced production
       H_END_FOREACH  // reducible item
+
+      if(inadeq)
+        h_slist_push(table->inadeq, (void *)(uintptr_t)state);
     }
   }
 
diff --git a/src/backends/lr.c b/src/backends/lr.c
index 2d329b13..bf066455 100644
--- a/src/backends/lr.c
+++ b/src/backends/lr.c
@@ -159,6 +159,25 @@ HLRAction *h_reduce_action(HArena *arena, const HLRItem *item)
   return action;
 }
 
+// adds 'new' to the branches of 'action'
+// returns a 'action' if it is already of type HLR_CONFLICT
+// allocates a new HLRAction otherwise
+HLRAction *h_lr_conflict(HArena *arena, HLRAction *action, HLRAction *new)
+{
+  if(action->type != HLR_CONFLICT) {
+    HLRAction *old = action;
+    action = h_arena_malloc(arena, sizeof(HLRAction));
+    action->type = HLR_CONFLICT;
+    action->branches = h_slist_new(arena);
+    h_slist_push(action->branches, old);
+  }
+
+  assert(action->type == HLR_CONFLICT);
+  h_slist_push(action->branches, new);
+
+  return action;
+}
+
 
 
 /* LR driver */
@@ -239,6 +258,8 @@ bool h_lrengine_step(HLREngine *engine, const HLRAction *action)
   if(action == NULL)
     return false;   // no handle recognizable in input, terminate
 
+  assert(action->type == HLR_SHIFT || action->type == HLR_REDUCE);
+
   if(action->type == HLR_SHIFT) {
     h_slist_push(left, (void *)(uintptr_t)engine->state);
     h_slist_pop(right);                       // symbol (discard)
diff --git a/src/backends/lr.h b/src/backends/lr.h
index b95c1332..13e10d41 100644
--- a/src/backends/lr.h
+++ b/src/backends/lr.h
@@ -28,16 +28,22 @@ typedef struct HLRItem_ {
 } HLRItem;
 
 typedef struct HLRAction_ {
-  enum {HLR_SHIFT, HLR_REDUCE} type;
+  enum {HLR_SHIFT, HLR_REDUCE, HLR_CONFLICT} type;
   union {
-    size_t nextstate;   // used with SHIFT
+    // used with HLR_SHIFT
+    size_t nextstate;
+
+    // used with HLR_REDUCE
     struct {
       HCFChoice *lhs;   // symbol carrying semantic actions etc.
       size_t length;    // # of symbols in rhs
 #ifndef NDEBUG
       HCFChoice **rhs;  // NB: the rhs symbols are not needed for the parse
 #endif
-    } production;       // used with REDUCE
+    } production;
+
+    // used with HLR_CONFLICT
+    HSlist *branches;   // list of possible HLRActions
   };
 } HLRAction;
 
@@ -104,6 +110,7 @@ void h_lrtable_free(HLRTable *table);
 HLREngine *h_lrengine_new(HArena *arena, HArena *tarena, const HLRTable *table);
 HLRAction *h_reduce_action(HArena *arena, const HLRItem *item);
 HLRAction *h_shift_action(HArena *arena, size_t nextstate);
+HLRAction *h_lr_conflict(HArena *arena, HLRAction *action, HLRAction *new);
 
 bool h_eq_symbol(const void *p, const void *q);
 bool h_eq_lr_itemset(const void *p, const void *q);
diff --git a/src/backends/lr0.c b/src/backends/lr0.c
index aab2ad19..67cf2aa0 100644
--- a/src/backends/lr0.c
+++ b/src/backends/lr0.c
@@ -182,23 +182,29 @@ HLRTable *h_lr0_table(HCFGrammar *g, const HLRDFA *dfa)
 
   // add reduce entries, record inadequate states
   for(size_t i=0; i<dfa->nstates; i++) {
+    bool inadeq = false;
+
     // find reducible items in state
     H_FOREACH_KEY(dfa->states[i], HLRItem *item)
       if(item->mark == item->len) { // mark at the end
-        // check for conflicts
-        // XXX store more informative stuff in the inadeq records?
+        HLRAction *reduce = h_reduce_action(arena, item);
+        
+        // check for reduce/reduce conflict on forall
         if(table->forall[i]) {
-          // reduce/reduce conflict with a previous item
-          h_slist_push(table->inadeq, (void *)(uintptr_t)i);
-        } else if(!h_hashtable_empty(table->rows[i])) {
-          // shift/reduce conflict with one of the row's entries
-          h_slist_push(table->inadeq, (void *)(uintptr_t)i);
+          reduce = h_lr_conflict(arena, table->forall[i], reduce);
+          inadeq = true;
         }
+        table->forall[i] = reduce;
 
-        // set reduce action for the entire row
-        table->forall[i] = h_reduce_action(arena, item);
+        // check for shift/reduce conflict with other entries
+        // NOTE: these are not recorded as HLR_CONFLICTs at this point
+        if(!h_hashtable_empty(table->rows[i]))
+          inadeq = true;
       }
     H_END_FOREACH
+
+    if(inadeq)
+      h_slist_push(table->inadeq, (void *)(uintptr_t)i);
   }
 
   return table;
-- 
GitLab