mirror of
https://git.savannah.gnu.org/git/bison.git
synced 2026-03-09 04:13:03 +00:00
cex: add support for state-item pair graph generation
* src/lssi.h, src/lssi.c, src/state-item.h, src/state-item.c: New.
This commit is contained in:
committed by
Akim Demaille
parent
94bfdf3b4b
commit
5807dd9279
371
src/lssi.c
Normal file
371
src/lssi.c
Normal file
@@ -0,0 +1,371 @@
|
||||
/* Lookahead sensative state item searches for counterexample generation
|
||||
|
||||
Copyright (C) 2020 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of Bison, the GNU Compiler Compiler.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <gl_linked_list.h>
|
||||
#include <gl_xlist.h>
|
||||
|
||||
#include "getargs.h"
|
||||
#include "lssi.h"
|
||||
#include "nullable.h"
|
||||
|
||||
// lookahead sensative state item
|
||||
typedef struct lssi
|
||||
{
|
||||
state_item_number si;
|
||||
struct lssi *parent;
|
||||
// this is the precise lookahead set (follow_L from the CupEx paper)
|
||||
bitset lookahead;
|
||||
bool free_lookahead;
|
||||
} lssi;
|
||||
|
||||
lssi *
|
||||
new_lssi (state_item_number si, lssi *p, bitset l, bool free_l)
|
||||
{
|
||||
lssi *ret = xmalloc (sizeof (lssi));
|
||||
ret->si = si;
|
||||
ret->parent = p;
|
||||
ret->lookahead = l;
|
||||
ret->free_lookahead = free_l;
|
||||
return ret;
|
||||
}
|
||||
|
||||
void
|
||||
lssi_free (lssi *sn)
|
||||
{
|
||||
if (sn == NULL)
|
||||
return;
|
||||
if (sn->free_lookahead)
|
||||
bitset_free (sn->lookahead);
|
||||
free (sn);
|
||||
}
|
||||
|
||||
static size_t
|
||||
lssi_hasher (lssi *sn, size_t max)
|
||||
{
|
||||
size_t hash = sn->si;
|
||||
bitset_iterator biter;
|
||||
symbol_number syn;
|
||||
BITSET_FOR_EACH (biter, sn->lookahead, syn, 0)
|
||||
hash += syn;
|
||||
return hash % max;
|
||||
}
|
||||
|
||||
static bool
|
||||
lssi_comparator (lssi *s1, lssi *s2)
|
||||
{
|
||||
if (s1->si == s2->si)
|
||||
{
|
||||
if (s1->lookahead == s2->lookahead)
|
||||
return true;
|
||||
return bitset_equal_p (s1->lookahead, s2->lookahead);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
append_lssi (lssi *sn, Hash_table *visited, gl_list_t queue)
|
||||
{
|
||||
if (hash_lookup (visited, sn))
|
||||
{
|
||||
sn->free_lookahead = false;
|
||||
lssi_free (sn);
|
||||
return false;
|
||||
}
|
||||
if (!hash_insert (visited, sn))
|
||||
xalloc_die ();
|
||||
gl_list_add_last (queue, sn);
|
||||
return true;
|
||||
}
|
||||
|
||||
#if 0
|
||||
static void
|
||||
lssi_print (lssi *l)
|
||||
{
|
||||
print_state_item (state_items + l->si, stdout);
|
||||
if (l->lookahead)
|
||||
{
|
||||
printf ("FOLLOWL = { ");
|
||||
bitset_iterator biter;
|
||||
symbol_number sin;
|
||||
BITSET_FOR_EACH (biter, l->lookahead, sin, 0)
|
||||
printf ("%s, \n", symbols[sin]->tag);
|
||||
puts ("}");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Compute the set of state-items that can reach the given conflict item via
|
||||
* a combination of transitions or production steps.
|
||||
*/
|
||||
bitset
|
||||
eligible_state_items (state_item *target)
|
||||
{
|
||||
bitset result = bitset_create (nstate_items, BITSET_FIXED);
|
||||
gl_list_t queue =
|
||||
gl_list_create (GL_LINKED_LIST, NULL, NULL, NULL, true, 1,
|
||||
(const void **) &target);
|
||||
while (gl_list_size (queue) > 0)
|
||||
{
|
||||
state_item *si = (state_item *) gl_list_get_at (queue, 0);
|
||||
gl_list_remove_at (queue, 0);
|
||||
if (bitset_test (result, si - state_items))
|
||||
continue;
|
||||
bitset_set (result, si - state_items);
|
||||
// search all reverse edges.
|
||||
bitset rsi = si_revs[si - state_items];
|
||||
bitset_iterator biter;
|
||||
state_item_number sin;
|
||||
BITSET_FOR_EACH (biter, rsi, sin, 0)
|
||||
gl_list_add_last (queue, &state_items[sin]);
|
||||
}
|
||||
gl_list_free (queue);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute the shortest lookahead-sensitive path from the start state to
|
||||
* this conflict. If optimized is true, only consider parser states
|
||||
* that can reach the conflict state.
|
||||
*/
|
||||
gl_list_t
|
||||
shortest_path_from_start (state_item_number target, symbol_number next_sym)
|
||||
{
|
||||
bitset eligible = eligible_state_items (&state_items[target]);
|
||||
Hash_table *visited = hash_initialize (32,
|
||||
NULL,
|
||||
(Hash_hasher) lssi_hasher,
|
||||
(Hash_comparator) lssi_comparator,
|
||||
(Hash_data_freer) lssi_free);
|
||||
bitset il = bitset_create (nsyms, BITSET_FIXED);
|
||||
bitset_set (il, 0);
|
||||
lssi *init = new_lssi (0, NULL, il, true);
|
||||
gl_list_t queue = gl_list_create (GL_LINKED_LIST, NULL, NULL,
|
||||
NULL,
|
||||
true, 1, (const void **) &init);
|
||||
// breadth-first search
|
||||
bool finished = false;
|
||||
lssi *n;
|
||||
while (gl_list_size (queue) > 0)
|
||||
{
|
||||
n = (lssi *) gl_list_get_at (queue, 0);
|
||||
gl_list_remove_at (queue, 0);
|
||||
state_item_number last = n->si;
|
||||
if (target == last && bitset_test (n->lookahead, next_sym))
|
||||
{
|
||||
finished = true;
|
||||
break;
|
||||
}
|
||||
// Transitions don't change follow_L
|
||||
if (si_trans[last] >= 0)
|
||||
{
|
||||
state_item_number nextSI = si_trans[last];
|
||||
if (bitset_test (eligible, nextSI))
|
||||
{
|
||||
lssi *next = new_lssi (nextSI, n, n->lookahead, false);
|
||||
append_lssi (next, visited, queue);
|
||||
}
|
||||
}
|
||||
// For production steps, follow_L is based on the symbol after the
|
||||
// non-terminal being produced.
|
||||
// if no such symbol exists, follow_L is unchanged
|
||||
// if the symbol is a terminal, follow_L only contains that terminal
|
||||
// if the symbol is not nullable, follow_L is its FIRSTS set
|
||||
// if the symbol is nullable, follow_L is its FIRSTS set unioned with
|
||||
// this logic applied to the next symbol in the rule
|
||||
bitset p = si_prods_lookup (last);
|
||||
if (p)
|
||||
{
|
||||
state_item si = state_items[last];
|
||||
// Compute follow_L as above
|
||||
bitset lookahead = bitset_create (nsyms, BITSET_FIXED);
|
||||
item_number *pos = si.item + 1;
|
||||
for (; !item_number_is_rule_number (*pos); ++pos)
|
||||
{
|
||||
item_number it = *pos;
|
||||
if (ISTOKEN (it))
|
||||
{
|
||||
bitset_set (lookahead, it);
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
bitset_union (lookahead, lookahead, FIRSTS (it));
|
||||
if (!nullable[it - ntokens])
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (item_number_is_rule_number (*pos))
|
||||
bitset_union (lookahead, n->lookahead, lookahead);
|
||||
|
||||
bool lookahead_used = false;
|
||||
// Try all possible production steps within this parser state.
|
||||
bitset_iterator biter;
|
||||
state_item_number nextSI;
|
||||
BITSET_FOR_EACH (biter, p, nextSI, 0)
|
||||
{
|
||||
if (!bitset_test (eligible, nextSI))
|
||||
continue;
|
||||
lssi *next = new_lssi (nextSI, n, lookahead,
|
||||
!lookahead_used);
|
||||
lookahead_used = append_lssi (next, visited, queue)
|
||||
|| lookahead_used;
|
||||
}
|
||||
if (!lookahead_used)
|
||||
bitset_free (lookahead);
|
||||
}
|
||||
}
|
||||
if (!finished)
|
||||
{
|
||||
gl_list_free (queue);
|
||||
fputs ("Cannot find shortest path to conflict state.", stderr);
|
||||
exit (1);
|
||||
}
|
||||
gl_list_t ret =
|
||||
gl_list_create_empty (GL_LINKED_LIST, NULL, NULL, NULL, true);
|
||||
for (lssi *sn = n; sn != NULL; sn = sn->parent)
|
||||
gl_list_add_first (ret, state_items + sn->si);
|
||||
|
||||
hash_free (visited);
|
||||
gl_list_free (queue);
|
||||
|
||||
if (trace_flag & trace_cex)
|
||||
{
|
||||
puts ("REDUCE ITEM PATH:");
|
||||
gl_list_iterator_t it = gl_list_iterator (ret);
|
||||
const void *sip;
|
||||
while (gl_list_iterator_next (&it, &sip, NULL))
|
||||
print_state_item ((state_item *) sip, stdout);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the given terminal is in the given symbol set or can begin
|
||||
* a nonterminal in the given symbol set.
|
||||
*/
|
||||
bool
|
||||
intersect_symbol (symbol_number sym, bitset syms)
|
||||
{
|
||||
if (!syms)
|
||||
return true;
|
||||
bitset_iterator biter;
|
||||
symbol_number sn;
|
||||
BITSET_FOR_EACH (biter, syms, sn, 0)
|
||||
{
|
||||
if (sym == sn)
|
||||
return true;
|
||||
if (ISVAR (sn) && bitset_test (FIRSTS (sn), sym))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if any symbol in ts is in syms
|
||||
* or can begin a nonterminal syms.
|
||||
*/
|
||||
bool
|
||||
intersect (bitset ts, bitset syms)
|
||||
{
|
||||
if (!syms || !ts)
|
||||
return true;
|
||||
bitset_iterator biter;
|
||||
symbol_number sn;
|
||||
BITSET_FOR_EACH (biter, syms, sn, 0)
|
||||
{
|
||||
if (bitset_test (ts, sn))
|
||||
return true;
|
||||
if (ISVAR (sn) && !bitset_disjoint_p (ts, FIRSTS (sn)))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Compute a list of state_items that have a production to n with respect
|
||||
* to its lookahead
|
||||
*/
|
||||
gl_list_t
|
||||
lssi_reverse_production (const state_item *si, bitset lookahead)
|
||||
{
|
||||
gl_list_t result =
|
||||
gl_list_create_empty (GL_LINKED_LIST, NULL, NULL, NULL, true);
|
||||
if (SI_TRANSITION (si))
|
||||
return result;
|
||||
// A production step was made to the current lalr_item.
|
||||
// Check that the next symbol in the parent lalr_item is
|
||||
// compatible with the lookahead.
|
||||
bitset_iterator biter;
|
||||
state_item_number sin;
|
||||
BITSET_FOR_EACH (biter, si_revs[si - state_items], sin, 0)
|
||||
{
|
||||
state_item *prevsi = state_items + sin;
|
||||
if (!production_allowed (prevsi, si))
|
||||
continue;
|
||||
bitset prev_lookahead = prevsi->lookahead;
|
||||
if (item_number_is_rule_number (*(prevsi->item)))
|
||||
{
|
||||
// reduce item
|
||||
// Check that some lookaheads can be preserved.
|
||||
if (!intersect (prev_lookahead, lookahead))
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
// shift item
|
||||
if (lookahead)
|
||||
{
|
||||
// Check that lookahead is compatible with the first
|
||||
// possible symbols in the rest of the production.
|
||||
// Alternatively, if the rest of the production is
|
||||
// nullable, the lookahead must be compatible with
|
||||
// the lookahead of the corresponding item.
|
||||
bool applicable = false;
|
||||
bool nlable = true;
|
||||
for (item_number *pos = prevsi->item + 1;
|
||||
!applicable && nlable && item_number_is_symbol_number (*pos);
|
||||
++pos)
|
||||
{
|
||||
symbol_number next_sym = item_number_as_symbol_number (*pos);
|
||||
if (ISTOKEN (next_sym))
|
||||
{
|
||||
applicable = intersect_symbol (next_sym, lookahead);
|
||||
nlable = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
applicable = intersect (FIRSTS (next_sym), lookahead);
|
||||
if (!applicable)
|
||||
nlable = nullable[next_sym - ntokens];
|
||||
}
|
||||
}
|
||||
if (!applicable && !nlable)
|
||||
continue;
|
||||
}
|
||||
}
|
||||
gl_list_add_last (result, prevsi);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
57
src/lssi.h
Normal file
57
src/lssi.h
Normal file
@@ -0,0 +1,57 @@
|
||||
/* Lookahead sensative state item searches for counterexample generation
|
||||
|
||||
Copyright (C) 2020 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of Bison, the GNU Compiler Compiler.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
||||
|
||||
#ifndef LSSI_H
|
||||
# define LSSI_H
|
||||
|
||||
# include "state-item.h"
|
||||
|
||||
/*
|
||||
All state-item graph nodes should also include a precise follow set (follow_L).
|
||||
However, ignoring follow_L saves a lot of memory and is a pretty good approximation.
|
||||
These functions exist to enforce restrictions caused by follow_L sets.
|
||||
*/
|
||||
|
||||
/*
|
||||
* find shortest lookahead-sensitive path of state-items to target such that
|
||||
* next_sym is in the follow_L set of target in that position.
|
||||
*/
|
||||
gl_list_t shortest_path_from_start (state_item_number target,
|
||||
symbol_number next_sym);
|
||||
|
||||
/**
|
||||
* Determine if the given terminal is in the given symbol set or can begin
|
||||
* a nonterminal in the given symbol set.
|
||||
*/
|
||||
bool intersect_symbol (symbol_number sym, bitset syms);
|
||||
|
||||
/**
|
||||
* Determine if any symbol in ts is in syms
|
||||
* or can begin with a nonterminal in syms.
|
||||
*/
|
||||
bool intersect (bitset ts, bitset syms);
|
||||
|
||||
/**
|
||||
* Compute a set of sequences of state-items that can make production steps
|
||||
* to this state-item such that the resulting possible lookahead symbols are
|
||||
* as given.
|
||||
*/
|
||||
gl_list_t lssi_reverse_production (const state_item *si, bitset lookahead);
|
||||
|
||||
#endif /* LSSI_H */
|
||||
566
src/state-item.c
Normal file
566
src/state-item.c
Normal file
@@ -0,0 +1,566 @@
|
||||
/* Counterexample Generation Search Nodes
|
||||
|
||||
Copyright (C) 2020 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of Bison, the GNU Compiler Compiler.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include "state-item.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <gl_linked_list.h>
|
||||
#include <gl_xlist.h>
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
|
||||
#include "closure.h"
|
||||
#include "getargs.h"
|
||||
#include "nullable.h"
|
||||
|
||||
size_t nstate_items;
|
||||
state_item_number *state_item_map;
|
||||
state_item *state_items;
|
||||
|
||||
state_item_number *si_trans;
|
||||
bitsetv si_revs;
|
||||
Hash_table *si_prods;
|
||||
|
||||
// hash functions for index -> bitset hash maps
|
||||
typedef struct
|
||||
{
|
||||
int key;
|
||||
bitset l;
|
||||
} hash_pair;
|
||||
|
||||
static size_t
|
||||
hash_pair_hasher (const hash_pair *sl, size_t max)
|
||||
{
|
||||
return sl->key % max;
|
||||
}
|
||||
|
||||
static bool
|
||||
hash_pair_comparator (const hash_pair *l, const hash_pair *r)
|
||||
{
|
||||
return l->key == r->key;
|
||||
}
|
||||
|
||||
static void
|
||||
hash_pair_free (hash_pair *hp)
|
||||
{
|
||||
bitset_free (hp->l);
|
||||
free (hp);
|
||||
}
|
||||
|
||||
static bitset
|
||||
hash_pair_lookup (Hash_table *tab, int key)
|
||||
{
|
||||
hash_pair *l = xmalloc (sizeof (hash_pair));
|
||||
l->key = key;
|
||||
hash_pair *hp = (hash_pair *) hash_lookup (tab, l);
|
||||
if (!hp)
|
||||
return NULL;
|
||||
return hp->l;
|
||||
}
|
||||
|
||||
static void
|
||||
hash_pair_insert (Hash_table *tab, int key, bitset val)
|
||||
{
|
||||
hash_pair *hp = xmalloc (sizeof (hash_pair));
|
||||
hp->key = key;
|
||||
hp->l = val;
|
||||
if (!hash_insert (tab, hp))
|
||||
xalloc_die ();
|
||||
}
|
||||
|
||||
static void
|
||||
hash_pair_remove (Hash_table *tab, int key)
|
||||
{
|
||||
hash_pair *hp = xmalloc (sizeof (hash_pair));
|
||||
hp->key = key;
|
||||
hash_delete (tab, hp);
|
||||
}
|
||||
|
||||
/* return a state_item from a state's id and the offset of the item
|
||||
within the state.
|
||||
*/
|
||||
state_item *
|
||||
state_item_lookup (state_number s, state_item_number off)
|
||||
{
|
||||
return &state_items[state_item_index_lookup (s, off)];
|
||||
}
|
||||
|
||||
static inline void
|
||||
state_item_set (state_item_number sidx, state *s, item_number off)
|
||||
{
|
||||
state_item *si = state_items + sidx;
|
||||
si->state = s;
|
||||
si->item = &ritem[off];
|
||||
si->lookahead = NULL;
|
||||
si_trans[sidx] = -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize state_items set
|
||||
*/
|
||||
static void
|
||||
init_state_items (void)
|
||||
{
|
||||
nstate_items = 0;
|
||||
bitsetv production_items = bitsetv_create (nstates, nritems, BITSET_SPARSE);
|
||||
for (int i = 0; i < nstates; ++i)
|
||||
{
|
||||
state *s = states[i];
|
||||
nstate_items += s->nitems;
|
||||
closure (s->items, s->nitems);
|
||||
for (size_t j = 0; j < nitemset; ++j)
|
||||
if (itemset[j] > 0
|
||||
&& item_number_is_rule_number (ritem[itemset[j] - 1]))
|
||||
{
|
||||
bitset_set (production_items[i], itemset[j]);
|
||||
++nstate_items;
|
||||
}
|
||||
}
|
||||
state_item_map = xnmalloc (nstates + 1, sizeof (state_item_number));
|
||||
state_items = xnmalloc (nstate_items, sizeof (state_item));
|
||||
si_trans = xnmalloc (nstate_items, sizeof (state_item_number));
|
||||
si_revs = bitsetv_create (nstate_items, nstate_items, BITSET_SPARSE);
|
||||
state_item_number sidx = 0;
|
||||
for (int i = 0; i < nstates; ++i)
|
||||
{
|
||||
state_item_map[i] = sidx;
|
||||
int rule_search_idx = 0;
|
||||
state *s = states[i];
|
||||
reductions *red = s->reductions;
|
||||
for (int j = 0; j < s->nitems; ++j)
|
||||
{
|
||||
state_item_set (sidx, s, s->items[j]);
|
||||
state_item *si = state_items + sidx;
|
||||
const rule *r = item_rule (si->item);
|
||||
if (red->rules[rule_search_idx] < r)
|
||||
++rule_search_idx;
|
||||
if (rule_search_idx < red->num && r == red->rules[rule_search_idx])
|
||||
{
|
||||
bitsetv lookahead = red->lookahead_tokens;
|
||||
if (lookahead)
|
||||
si->lookahead = lookahead[rule_search_idx];
|
||||
}
|
||||
++sidx;
|
||||
}
|
||||
bitset_iterator biter;
|
||||
item_number off;
|
||||
BITSET_FOR_EACH (biter, production_items[i], off, 0)
|
||||
{
|
||||
state_item_set (sidx, s, off);
|
||||
if (item_number_is_rule_number (ritem[off]))
|
||||
{
|
||||
bitsetv lookahead = red->lookahead_tokens;
|
||||
if (lookahead)
|
||||
state_items[sidx].lookahead = lookahead[rule_search_idx];
|
||||
++rule_search_idx;
|
||||
}
|
||||
++sidx;
|
||||
}
|
||||
|
||||
}
|
||||
state_item_map[nstates] = nstate_items;
|
||||
}
|
||||
|
||||
static size_t
|
||||
state_sym_hasher (const void *st, size_t max)
|
||||
{
|
||||
return ((state *) st)->accessing_symbol % max;
|
||||
}
|
||||
|
||||
static bool
|
||||
state_sym_comparator (const void *s1, const void *s2)
|
||||
{
|
||||
return ((state *) s1)->accessing_symbol == ((state *) s2)->accessing_symbol;
|
||||
}
|
||||
|
||||
static state *
|
||||
state_sym_lookup (symbol_number sym, Hash_table *h)
|
||||
{
|
||||
state *s = xmalloc (sizeof (state));
|
||||
s->accessing_symbol = sym;
|
||||
return hash_lookup (h, s);
|
||||
}
|
||||
|
||||
static void
|
||||
init_trans (void)
|
||||
{
|
||||
for (state_number i = 0; i < nstates; ++i)
|
||||
{
|
||||
// Generate a hash set that maps from accepting symbols to the states
|
||||
// this state transitions to.
|
||||
state *s = states[i];
|
||||
transitions *t = s->transitions;
|
||||
Hash_table *transition_set
|
||||
= hash_initialize (t->num, NULL, (Hash_hasher) state_sym_hasher,
|
||||
(Hash_comparator) state_sym_comparator, NULL);
|
||||
for (int j = 0; j < t->num; ++j)
|
||||
if (!TRANSITION_IS_DISABLED (t, j))
|
||||
if (!hash_insert (transition_set, t->states[j]))
|
||||
xalloc_die ();
|
||||
for (int j = state_item_map[i]; j < state_item_map[i + 1]; ++j)
|
||||
{
|
||||
item_number *item = state_items[j].item;
|
||||
if (item_number_is_rule_number (*item))
|
||||
continue;
|
||||
state *dst = state_sym_lookup (*item, transition_set);
|
||||
if (!dst)
|
||||
continue;
|
||||
// find the item in the destination state that corresponds
|
||||
// to the transition of item
|
||||
for (int k = 0; k < dst->nitems; ++k)
|
||||
{
|
||||
if (item + 1 == ritem + dst->items[k])
|
||||
{
|
||||
state_item_number dstSI =
|
||||
state_item_index_lookup (dst->number, k);
|
||||
|
||||
si_trans[j] = dstSI;
|
||||
bitset_set (si_revs[dstSI], j);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bitset
|
||||
si_prods_lookup (state_item_number si)
|
||||
{
|
||||
return hash_pair_lookup (si_prods, si);
|
||||
}
|
||||
|
||||
static void
|
||||
init_prods (void)
|
||||
{
|
||||
si_prods = hash_initialize (nstate_items,
|
||||
NULL,
|
||||
(Hash_hasher) hash_pair_hasher,
|
||||
(Hash_comparator) hash_pair_comparator,
|
||||
(Hash_data_freer) hash_pair_free);
|
||||
for (int i = 0; i < nstates; ++i)
|
||||
{
|
||||
state *s = states[i];
|
||||
// closure_map is a hash map from nonterminals to a set
|
||||
// of the items that produce those nonterminals
|
||||
Hash_table *closure_map
|
||||
= hash_initialize (nsyms - ntokens, NULL,
|
||||
(Hash_hasher) hash_pair_hasher,
|
||||
(Hash_comparator) hash_pair_comparator,
|
||||
NULL);
|
||||
|
||||
// Add the nitems of state to skip to the production portion
|
||||
// of that state's state_items
|
||||
for (int j = state_item_map[i] + s->nitems;
|
||||
j < state_item_map[i + 1]; ++j)
|
||||
{
|
||||
state_item *src = state_items + j;
|
||||
item_number *item = src->item;
|
||||
symbol_number lhs = item_rule (item)->lhs->number;
|
||||
bitset itms = hash_pair_lookup (closure_map, lhs);
|
||||
if (!itms)
|
||||
{
|
||||
itms = bitset_create (nstate_items, BITSET_SPARSE);
|
||||
hash_pair_insert (closure_map, lhs, itms);
|
||||
}
|
||||
bitset_set (itms, j);
|
||||
}
|
||||
// For each item with a dot followed by a nonterminal,
|
||||
// try to create a production edge.
|
||||
for (int j = state_item_map[i]; j < state_item_map[i + 1]; ++j)
|
||||
{
|
||||
state_item *src = state_items + j;
|
||||
item_number item = *(src->item);
|
||||
// Skip reduce items and items with terminals after the dot
|
||||
if (item_number_is_rule_number (item) || ISTOKEN (item))
|
||||
continue;
|
||||
symbol_number sym = item_number_as_symbol_number (item);
|
||||
bitset lb = hash_pair_lookup (closure_map, sym);
|
||||
if (lb)
|
||||
{
|
||||
bitset copy = bitset_create (nstate_items, BITSET_SPARSE);
|
||||
bitset_copy (copy, lb);
|
||||
hash_pair *prod_hp = xmalloc (sizeof (hash_pair));
|
||||
prod_hp->key = j;
|
||||
prod_hp->l = copy;
|
||||
//update prods
|
||||
if (!hash_insert (si_prods, prod_hp))
|
||||
xalloc_die ();
|
||||
|
||||
//update revs
|
||||
bitset_iterator biter;
|
||||
state_item_number prod;
|
||||
BITSET_FOR_EACH (biter, copy, prod, 0)
|
||||
bitset_set (si_revs[prod], j);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/* Since lookaheads are only generated for reductions,
|
||||
we need to propogate lookahead sets backwards as
|
||||
the searches require each state_item to have a lookahead.
|
||||
*/
|
||||
static inline void
|
||||
gen_lookaheads (void)
|
||||
{
|
||||
for (state_item_number i = 0; i < nstate_items; ++i)
|
||||
{
|
||||
state_item *si = state_items + i;
|
||||
if (item_number_is_symbol_number (*(si->item)) || !si->lookahead)
|
||||
continue;
|
||||
|
||||
bitset lookahead = si->lookahead;
|
||||
gl_list_t queue =
|
||||
gl_list_create (GL_LINKED_LIST, NULL, NULL, NULL, true, 1,
|
||||
(const void **) &si);
|
||||
|
||||
// For each reduction item, traverse through all state_items
|
||||
// accessible through reverse transition steps, and set their
|
||||
// lookaheads to the reduction items lookahead
|
||||
while (gl_list_size (queue) > 0)
|
||||
{
|
||||
state_item *prev = (state_item *) gl_list_get_at (queue, 0);
|
||||
gl_list_remove_at (queue, 0);
|
||||
prev->lookahead = lookahead;
|
||||
if (SI_TRANSITION (prev))
|
||||
{
|
||||
bitset rsi = si_revs[prev - state_items];
|
||||
bitset_iterator biter;
|
||||
state_item_number sin;
|
||||
BITSET_FOR_EACH (biter, rsi, sin, 0)
|
||||
gl_list_add_first (queue, &state_items[sin]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bitsetv firsts = NULL;
|
||||
|
||||
void
|
||||
init_firsts (void)
|
||||
{
|
||||
firsts = bitsetv_create (nvars, nsyms, BITSET_FIXED);
|
||||
for (rule_number i = 0; i < nrules; ++i)
|
||||
{
|
||||
rule *r = rules + i;
|
||||
item_number *n = r->rhs;
|
||||
// Iterate through nullable nonterminals to try to find a terminal.
|
||||
while (item_number_is_symbol_number (*n) && ISVAR (*n)
|
||||
&& nullable[*n - ntokens])
|
||||
++n;
|
||||
if (item_number_is_rule_number (*n) || ISVAR (*n))
|
||||
continue;
|
||||
|
||||
symbol_number lhs = r->lhs->number;
|
||||
bitset_set (FIRSTS (lhs), *n);
|
||||
}
|
||||
bool change = true;
|
||||
while (change)
|
||||
{
|
||||
change = false;
|
||||
for (rule_number i = 0; i < nrules; ++i)
|
||||
{
|
||||
rule *r = rules + i;
|
||||
symbol_number lhs = r->lhs->number;
|
||||
bitset f_lhs = FIRSTS (lhs);
|
||||
for (item_number *n = r->rhs;
|
||||
item_number_is_symbol_number (*n) &&
|
||||
ISVAR (*n);
|
||||
++n)
|
||||
{
|
||||
bitset f = FIRSTS (*n);
|
||||
if (!bitset_subset_p (f_lhs, f))
|
||||
{
|
||||
change = true;
|
||||
bitset_union (f_lhs, f_lhs, f);
|
||||
}
|
||||
if (!nullable[*n - ntokens])
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static inline void
|
||||
disable_state_item (state_item_number sin)
|
||||
{
|
||||
si_trans[sin] = -2;
|
||||
hash_pair_remove (si_prods, sin);
|
||||
}
|
||||
|
||||
/*
|
||||
To make searches more efficient, we can prune away paths that are
|
||||
caused by disabled transitions.
|
||||
*/
|
||||
static void
|
||||
prune_disabled_paths (void)
|
||||
{
|
||||
for (int i = nstate_items - 1; i >= 0; --i)
|
||||
{
|
||||
state_item *si = state_items + i;
|
||||
if (si_trans[i] == -1 && item_number_is_symbol_number (*si->item))
|
||||
{
|
||||
// disable the transitions out of i
|
||||
for (state_item_number j = si_trans[i]; j != -1; j = si_trans[j])
|
||||
disable_state_item (j);
|
||||
|
||||
gl_list_t queue =
|
||||
gl_list_create (GL_LINKED_LIST, NULL, NULL, NULL, true, 1,
|
||||
(const void **) &si);
|
||||
|
||||
// For each disabled transition, traverse through all state_items
|
||||
// accessible through reverse transition steps, and set their
|
||||
// lookaheads to the reduction items lookahead
|
||||
while (gl_list_size (queue) > 0)
|
||||
{
|
||||
const state_item *prev = gl_list_get_at (queue, 0);
|
||||
gl_list_remove_at (queue, 0);
|
||||
state_item_number prev_num = prev - state_items;
|
||||
disable_state_item (prev_num);
|
||||
|
||||
bitset rsi = si_revs[prev_num];
|
||||
bitset_iterator biter;
|
||||
state_item_number sin;
|
||||
BITSET_FOR_EACH (biter, rsi, sin, 0)
|
||||
{
|
||||
if (SI_TRANSITION (prev))
|
||||
gl_list_add_first (queue, &state_items[sin]);
|
||||
else
|
||||
{
|
||||
bitset p = si_prods_lookup (sin);
|
||||
if (p)
|
||||
bitset_reset (p, prev_num);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
print_state_item (const state_item *si, FILE *out)
|
||||
{
|
||||
fprintf (out, "%d:", si->state->number);
|
||||
item_print (si->item, NULL, out);
|
||||
putc ('\n', out);
|
||||
}
|
||||
|
||||
/**
|
||||
* Report set counts and the state_item graph if trace is enabled
|
||||
*/
|
||||
static void
|
||||
state_items_report (void)
|
||||
{
|
||||
printf ("# state items: %zu\n", nstate_items);
|
||||
for (state_number i = 0; i < nstates; ++i)
|
||||
{
|
||||
printf ("State %d:\n", i);
|
||||
for (int j = state_item_map[i]; j < state_item_map[i + 1]; ++j)
|
||||
{
|
||||
item_print (state_items[j].item, NULL, stdout);
|
||||
puts ("");
|
||||
if (si_trans[j] >= 0)
|
||||
{
|
||||
fputs (" -> ", stdout);
|
||||
print_state_item (state_items + si_trans[j], stdout);
|
||||
}
|
||||
|
||||
bitset sets[2] = { si_prods_lookup (j), si_revs[j] };
|
||||
const char *txt[2] = { " => ", " <- " };
|
||||
for (int seti = 0; seti < 2; ++seti)
|
||||
{
|
||||
bitset b = sets[seti];
|
||||
if (b)
|
||||
{
|
||||
bitset_iterator biter;
|
||||
state_item_number sin;
|
||||
BITSET_FOR_EACH (biter, b, sin, 0)
|
||||
{
|
||||
fputs (txt[seti], stdout);
|
||||
print_state_item (state_items + sin, stdout);
|
||||
}
|
||||
}
|
||||
}
|
||||
puts ("");
|
||||
}
|
||||
}
|
||||
printf ("FIRSTS\n");
|
||||
for (symbol_number i = ntokens; i < nsyms; ++i)
|
||||
{
|
||||
printf (" %s firsts\n", symbols[i]->tag);
|
||||
bitset_iterator iter;
|
||||
symbol_number j;
|
||||
BITSET_FOR_EACH (iter, FIRSTS (i), j, 0)
|
||||
printf (" %s\n", symbols[j]->tag);
|
||||
}
|
||||
puts ("\n");
|
||||
}
|
||||
|
||||
void
|
||||
state_items_init (void)
|
||||
{
|
||||
time_t start = time (NULL);
|
||||
init_state_items ();
|
||||
init_trans ();
|
||||
init_prods ();
|
||||
gen_lookaheads ();
|
||||
init_firsts ();
|
||||
prune_disabled_paths ();
|
||||
if (trace_flag & trace_cex)
|
||||
{
|
||||
printf ("init: %f\n", difftime (time (NULL), start));
|
||||
state_items_report ();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
state_items_free (void)
|
||||
{
|
||||
hash_free (si_prods);
|
||||
bitsetv_free (si_revs);
|
||||
free (si_trans);
|
||||
free (state_items);
|
||||
bitsetv_free (firsts);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine, using precedence and associativity, whether the next
|
||||
* production is allowed from the current production.
|
||||
*/
|
||||
bool
|
||||
production_allowed (const state_item *si, const state_item *next)
|
||||
{
|
||||
sym_content *s1 = item_rule (si->item)->lhs;
|
||||
sym_content *s2 = item_rule (next->item)->lhs;
|
||||
int prec1 = s1->prec;
|
||||
int prec2 = s2->prec;
|
||||
if (prec1 >= 0 && prec2 >= 0)
|
||||
{
|
||||
// Do not expand if lower precedence.
|
||||
if (prec1 > prec2)
|
||||
return false;
|
||||
// Do not expand if same precedence, but left-associative.
|
||||
if (prec1 == prec2 && s1->assoc == left_assoc)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
100
src/state-item.h
Normal file
100
src/state-item.h
Normal file
@@ -0,0 +1,100 @@
|
||||
/* Counterexample Generation Search Nodes
|
||||
|
||||
Copyright (C) 2020 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of Bison, the GNU Compiler Compiler.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
||||
|
||||
#ifndef STATE_ITEM_H
|
||||
# define STATE_ITEM_H
|
||||
|
||||
# include <bitsetv.h>
|
||||
# include <gl_list.h>
|
||||
# include <hash.h>
|
||||
|
||||
# include "gram.h"
|
||||
# include "state.h"
|
||||
|
||||
/* Initializes a graph connecting (state, production item) pairs to
|
||||
pairs they can make a transition or production step to. This graph
|
||||
is used to search for paths that represent counterexamples of some
|
||||
conflict.
|
||||
|
||||
state_items is an array of state state-item pairs ordered by state.
|
||||
state_item_map maps state numbers to the first item which
|
||||
corresponds to it in the array. A state's portion in state_items
|
||||
begins with its items in the same order as it was in the
|
||||
state. This is then followed by productions from the closure of the
|
||||
state in order by rule.
|
||||
|
||||
There are two type of edges in this graph transitions and
|
||||
productions. Transitions are the same as transitions from the
|
||||
parser except edges are only between items from the same
|
||||
rule. These are stored as an array "si_trans" (as most items will
|
||||
have transitions) which are indexed the same way as state_items.
|
||||
|
||||
Productions are edges from items with a nonterminal after the dot to
|
||||
the production of that nonterminal in the same state. These edges are
|
||||
stored as a hash map "si_prods" from a state_item to a set of what productions
|
||||
it goes from/to
|
||||
|
||||
The inverses of these edges are stored in an array of bitsets,
|
||||
"si_revs." A state-item that begins with a dot will have reverse
|
||||
production edges, and all others will have reverse transition
|
||||
edges. */
|
||||
|
||||
# define SI_DISABLED(sin) (si_trans[sin] == -2)
|
||||
# define SI_PRODUCTION(si) ((si) == state_items || *((si)->item - 1) < 0)
|
||||
# define SI_TRANSITION(si) ((si) != state_items && *((si)->item - 1) >= 0)
|
||||
|
||||
typedef int state_item_number;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
state *state;
|
||||
item_number *item;
|
||||
bitset lookahead;
|
||||
} state_item;
|
||||
|
||||
extern bitsetv firsts;
|
||||
# define FIRSTS(sym) firsts[(sym) - ntokens]
|
||||
|
||||
extern size_t nstate_items;
|
||||
extern state_item_number *state_item_map;
|
||||
|
||||
/** Array mapping state_item_numbers to state_items */
|
||||
extern state_item *state_items;
|
||||
|
||||
/** state-item graph edges */
|
||||
extern state_item_number *si_trans;
|
||||
extern bitsetv si_revs;
|
||||
|
||||
state_item *state_item_lookup (state_number s, state_item_number off);
|
||||
|
||||
static inline state_item_number
|
||||
state_item_index_lookup (state_number s, state_item_number off)
|
||||
{
|
||||
return state_item_map[s] + off;
|
||||
}
|
||||
|
||||
bitset si_prods_lookup (state_item_number si);
|
||||
|
||||
void state_items_init (void);
|
||||
void print_state_item (const state_item *si, FILE *out);
|
||||
void state_items_free (void);
|
||||
|
||||
bool production_allowed (const state_item *si, const state_item *next);
|
||||
|
||||
#endif /* STATE_ITEM_H */
|
||||
Reference in New Issue
Block a user