tscc-compiler
Version:
An LALR(1) compiler compiler written in Typescript
265 lines (257 loc) • 9.22 kB
text/typescript
import { BitSet } from '../util/bitset';
import { Grammar } from './grammar';
import { console } from '../util/common';
import { OutputStream } from '../util/io';
import { Item,Action,ItemSet } from './item-set';
import { List } from '../util/list';
import { ParseTable } from './ptable';
import { TokenDef, Assoc, convertTokenToString } from './token-entry';
import { TokenSet } from './token-set';
enum ConflictType {
RR = 0, SR = 1
};
export class Conflict{
type: ConflictType;
set: ItemSet;
token: TokenDef;
used: Item;
discarded: Item;
static cNames: string[] = ['reduce/reduce','shift/reduce'];
toString(): string{
return `state ${this.set.stateIndex}, ${Conflict.cNames[this.type]} conflict:\n` +
` token: ${convertTokenToString(this.token)}\n` +
` used rule: ${this.used.toString()}\n` +
` discarded rule: ${this.discarded.toString()}`;
}
}
export function genInitialSet(g: Grammar): ItemSet{
var start = g.nts[0].rules[0];
var iset = new ItemSet(g);
iset.index = 0;
var set1 = new TokenSet(g.tokenCount);
set1.add(1);// eof
iset.add(start,0,true,set1,false);
return iset;
}
/**
* implementation of the Hanolee algorithm
* @param g
*/
export function genItemSets(g: Grammar): { result: List<ItemSet>, iterations: number }{
var htable: {[s: string]: ItemSet[]} = {};
var iterations = 0;
function addToTable(iset: ItemSet){
var h = iset.kernelHash();
if(htable[h] === undefined){
htable[h] = [];
}
htable[h].push(iset);
}
function forEachInBucket(set: ItemSet, cb: (s: ItemSet) => any){
var b = htable[set.kernelHash()];
if(b !== undefined){
for(var i = 0;i < b.length;i++){
if(cb(b[i])) break;
}
}
}
var index = 1;
var todoList = new List<ItemSet>();
var incList = new List<ItemSet>();
var doneList = new List<ItemSet>();
todoList.append(genInitialSet(g));
while(!todoList.isEmpty() || !incList.isEmpty()){
var comeFrom = null;
//phase 1,generate transition item set from incomplete list.
if(!incList.isEmpty()){
var set = comeFrom = incList.pull();
set.forEach(function(item){
if(item.actionType === Action.NONE){
console.assert(item.canShift());
var shift = item.getShift();
var newSet = new ItemSet(g);
newSet.index = index++;
todoList.append(newSet);
//mark all the symbols in 'set'
set.forEach(item1 => {
if(item1.canShift()){
var rItem = item1.getShift();
if(rItem === shift){
item1.actionType = Action.SHIFT;
item1.shift = newSet;
newSet.add(item1.rule,item1.marker + 1,true,item1.lah,false);
}
}
});
}
});
set.complete = true;
doneList.append(set);
}
//phase 2,find possible merges,and do them.
while(!todoList.isEmpty()){
var set = todoList.pull();
set.closure();
set.forEach(item => {
if(!item.canShift()){
item.actionType = Action.REDUCE;
}
});
var merged = null;
forEachInBucket(set, gSet => {
if(gSet.canMergeTo(set)){
if(gSet.mergeTo(set)){
if(gSet.complete){
merged = gSet;
}
}
//fix previous transition actions to merged set
if(comeFrom !== null){
comeFrom.forEach(function(sItem){
if(sItem.actionType === Action.SHIFT && sItem.shift === set){
sItem.shift = gSet;
}
});
}
set = null;
return true;
}
return false;
});
//if set is merged with another complete set,
if(merged !== null){
doneList.remove(merged);
incList.append(merged);
merged.complete = false;
}
//not merged
else if(set !== null){
incList.append(set);
addToTable(set);
}
}
iterations++;
}
var i = 0;
doneList.forEach(set => {
set.stateIndex = i++;
});
return {
result: doneList,
iterations: iterations,
};
}
export function genParseTable(g: Grammar, doneList: List<ItemSet>): { result: ParseTable, conflicts: Conflict[] }{
var conflicts = [];
function resolveSRConflict(set: ItemSet, shift: Item, reduce: Item){
var token = g.tokens[shift.getShift()];
if(token.assoc !== Assoc.UNDEFINED){
var ruleP = reduce.rule.pr;
if(ruleP !== -1){
if(ruleP > token.pr){
return reduce;
}
else if(ruleP < token.pr){
return shift;
}
else {
if(token.assoc === Assoc.LEFT){
return reduce;
}
else if(token.assoc === Assoc.RIGHT){
return shift;
}
else if(token.assoc === Assoc.NON){
return Item.NULL;
}
else {
console.assert(false);
}
}
}
}
var cf = new Conflict();
cf.type = ConflictType.SR;
cf.set = set;
cf.token = token;
cf.used = shift;
cf.discarded = reduce;
conflicts.push(cf);
return shift;
}
function resolveRRConflict(set: ItemSet, r1: Item, r2: Item, token: number){
let tdef = g.tokens[token];
if(r1.rule.pr !== -1 && r2.rule.pr !== -1 && r1.rule.pr !== r2.rule.pr){
return r1.rule.pr > r2.rule.pr ? r1 : r2;
}
else {
var used = r1.rule.index > r2.rule.index ? r2 : r1;
var discarded = r1.rule.index > r2.rule.index ? r1 : r2;
var cf = new Conflict();
cf.type = ConflictType.RR;
cf.set = set;
cf.token = tdef;
cf.used = used;
cf.discarded = discarded;
conflicts.push(cf);
return used;
}
}
var ptable = new ParseTable(g,doneList.size);
doneList.forEach(set => {
set.forEach(item => {
if(item.actionType === Action.SHIFT){
var sItem = item.getShift();
if(g.isToken(sItem)){
// do shift
var tindex = set.stateIndex * g.tokenCount + sItem;
var cItem = ptable.shift[tindex];
if(cItem !== null){
// shift-reduce conflict
if(cItem.actionType === Action.REDUCE){
ptable.shift[tindex] = resolveSRConflict(set,item,cItem);
}
else {
// shift-shift conflicts shouldnt occur
console.assert(cItem.shift === item.shift);
}
}
else {
ptable.shift[tindex] = item;
}
}
else {
// do goto
var tindex = set.stateIndex * g.nts.length + (-sItem - 1);
ptable.gotot[tindex] = item;
}
}
else if(item.actionType === Action.REDUCE){
for(var i = 0;i < g.tokenCount;i++){
if(item.lah.contains(i + 1)){
var index = set.stateIndex * g.tokenCount + i;
var cItem = ptable.shift[index];
if(cItem !== null){
if(cItem.actionType === Action.REDUCE){
ptable.shift[index] = resolveRRConflict(set,cItem,item,i);
}
else if(cItem.actionType === Action.SHIFT){
ptable.shift[index] = resolveSRConflict(set,cItem,item);
}
}
else {
ptable.shift[index] = item;
}
}
}
}
else {
console.assert(false);
}
});
});
return {
result: ptable,
conflicts: conflicts
};
}