Skip to content

Commit

Permalink
mark & delete unused productions (jison/zaach#296) and report the bug…
Browse files Browse the repository at this point in the history
…gers which have been killed this way (code: `Production.reachable` attribute and `generator.signalUnusedProductions()` method)
  • Loading branch information
GerHobbelt committed Apr 6, 2016
1 parent 00ec53d commit 0bcee76
Showing 1 changed file with 81 additions and 0 deletions.
81 changes: 81 additions & 0 deletions lib/jison.js
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ var Production = typal.construct({
this.id = id;
this.first = [];
this.precedence = 0;
this.reachable = false;
},
toString: function Production_toString() {
var str = this.symbol;
Expand All @@ -100,6 +101,9 @@ var Production = typal.construct({
if (this.precedence) {
attr_str.push('@' + this.precedence);
}
if (!this.reachable) {
attr_str.push('*RIP*');
}

if (attr_str.length) {
str += '[' + attr_str.join(' ') + ']';
Expand Down Expand Up @@ -266,6 +270,9 @@ generator.processGrammar = function processGrammarDef(grammar) {

// augment the grammar
this.augmentGrammar(grammar);

// detect unused productions and flag/report them
this.signalUnusedProductions();
};

generator.augmentGrammar = function augmentGrammar(grammar) {
Expand Down Expand Up @@ -320,6 +327,80 @@ generator.augmentGrammar = function augmentGrammar(grammar) {
this.grammar = grammar;
};

// Mark & report unused productions
generator.signalUnusedProductions = function () {
var mark = {};

var productions = this.productions;
var nonterminals = this.nonterminals;
var i, p, len, nt, sym;

for (i = 0, len = nonterminals.length; i < len; i++) {
nt = nonterminals[i];
assert(nt.symbol);
mark[nt.symbol] = false;
}

// scan & mark all visited productions
function traverseGrammar(nt) {
assert(nt);
assert(nt.symbol);
mark[nt.symbol] = true;

var prods = nt.productions;
assert(prods);
prods.forEach(function (p) {
assert(p.symbol === nt.symbol);
assert(p.handle);
var rhs = p.handle;
console.log('traverse / mark: ', nt.symbol, ' --> ', rhs);

for (var j = 0, len = rhs.length; j < len; j++) {
var sym = rhs[j];
assert(!sym ? !nonterminals[sym] : true);
if (nonterminals[sym] && !mark[sym]) {
traverseGrammar(nonterminals[sym]);
}
}
});
}

traverseGrammar(nonterminals['$accept' /* this.startSymbol */ ]);

// now any production which is not yet marked is *unused*:
var unused_prods = [];
for (var sym in mark) {
nt = nonterminals[sym];
assert(nt);
var prods = nt.productions;
assert(prods);
var in_use = mark[sym];
prods.forEach(function (p) {
assert(p);
if (in_use) {
p.reachable = true;
} else {
p.reachable = false;
unused_prods.push(p.toString());
}
});

if (!in_use) {
// and kill the unused nonterminals:
delete this.nonterminals[sym];
}
}
if (unused_prods.length) {
console.warn('\nUnused productions in your grammar:\n ' + unused_prods.join('\n ') + '\n\n');
}

// and kill the unused productions:
this.productions = productions.filter(function (p) {
if (!p.reachable) console.warn('KILL PRODUCTION: ' + p);
return p.reachable;
});
};

// set precedence and associativity of operators
function processOperators(ops) {
if (!ops) return {};
Expand Down

0 comments on commit 0bcee76

Please sign in to comment.