tscc-compiler
Version:
An LALR(1) compiler compiler written in Typescript
233 lines (222 loc) • 7.19 kB
text/typescript
import { TokenSet } from './token-set';
import { Rule, Grammar } from './grammar';
import { ListNode } from '../util/list';
export enum Action {
NONE = 1,
SHIFT,
REDUCE
}
export interface ItemSetPrintOpt {
showlah?: boolean;
showTrailer?: boolean;
}
export class Item{
public marker: number = 0;
public lah: TokenSet;
public rule: Rule;
public isKernel: boolean;
public shift: ItemSet = null;
public actionType = Action.NONE;
public changed: boolean = true;
static NULL: Item = {} as Item;
constructor(rule: Rule, ik: boolean){
this.rule = rule;
this.isKernel = ik;
this.lah = new TokenSet(rule.g.tokenCount);
}
canShift(): boolean{
return this.rule.rhs.length > this.marker;
}
getShift(): number{
return this.rule.rhs[this.marker];
}
toString(opt?: ItemSetPrintOpt): string {
var showlah = (opt && opt.showlah) || false;
var showTrailer = (opt && opt.showTrailer) || false;
var r = this.rule;
var ret = '[ ' + this.rule.toString(this.marker) + (showlah ? ', { ' + this.lah.toString(this.rule.g) + ' }' : '') + ' ]';
this.isKernel && (ret += '*');
if(showTrailer){
switch(this.actionType){
case Action.NONE: ret += '(-)';break;
case Action.SHIFT: ret += '(s' + this.shift.stateIndex + ')';break;
case Action.REDUCE: ret += '(r)';break;
}
}
return ret;
}
hash(): string{
return this.rule.index + '-' + this.marker;
}
hasRRConflictWith(i: Item): boolean{
return this.actionType === Action.REDUCE && i.actionType === Action.REDUCE && this.rule.index !== i.rule.index && this.lah.hasIntersection(i.lah);
}
getFollowSet(set: TokenSet): void{
var g = this.rule.g;
var i;
for(i = this.marker + 1;i < this.rule.rhs.length;i++){
var mItem = this.rule.rhs[i];
if(g.isToken(mItem)){
set.add(mItem + 1);
break;
}
else {
//var set1 = g.sym[mItem].firstSet
var set1 = g.nts[-mItem - 1].firstSet
set.union(set1);
set.remove(0);
if(!set1.contains(0)){
break;
}
}
}
if(i === this.rule.rhs.length){
set.union(this.lah);
}
}
}
export class ItemSet implements ListNode<ItemSet>{
g: Grammar;
// it: { [s: string]: Item } = {};
items: Item[] = [];
// item table, indexed by rule number
itemTable: Item[][] = [];
reduces: Item[] = [];
complete: boolean = false;
index: number = -1;
stateIndex: number = 0;
prev: ItemSet = null;
next: ItemSet = null;
data: ItemSet;
merges: number[] = [];
constructor(g: Grammar){
this.g = g;
this.data = this;
}
add(rule: Rule, marker: number, ik: boolean, lah: TokenSet, reset: boolean): boolean{
var entry = this.itemTable[rule.index] = this.itemTable[rule.index] || [];
var it = entry[marker];
if(it === undefined){
var n = new Item(rule,ik);
n.marker = marker;
if(lah){
n.lah.union(lah);
}
entry[marker] = n;
this.items.push(n);
marker === rule.rhs.length && this.reduces.push(n);
return true;
}
else if(lah){
var ret = it.lah.union(lah);
if(reset && ret && it.canShift()){
it.actionType = Action.NONE;
}
ret && (it.changed = true);
return ret;
}
}
contains(){
}
closure(): void{
var changed = true;
var tSet = new TokenSet(this.g.tokenCount);
var cela = this;
while(changed){
changed = false;
for(var item of this.items){
if(item.changed && item.canShift()){
var ritem = item.getShift();
if(ritem < 0){
tSet.removeAll();
item.getFollowSet(tSet);
this.g.forEachRuleOfNt(-ritem - 1, rule => {
changed = cela.add(rule, 0, false, tSet, false) || changed;
return false;
});
}
}
item.changed = false;
}
}
}
toString(opt?: ItemSetPrintOpt): string{
var showlah = (opt && opt.showlah) || false;
var showTrailer = (opt && opt.showTrailer) || false;
var ret = 's' + this.stateIndex + '';
if(this.index !== null){
ret += '(i' + this.index;
}
else {
ret += '(i?';
}
if(this.merges.length > 0){
ret += ', merged from ';
for(var i = 0;i < this.merges.length;i++){
if(i > 0){
ret += ', ';
}
ret += 'i' + this.merges[i];
}
}
ret += ')\n';
for(var item of this.items){
ret += item.toString(opt) + '\n';
}
return ret;
}
kernelHash(): string{
var ret = 0;
for(var item of this.items){
if(item.isKernel){
ret += item.rule.index << 5 + item.rule.index + item.marker;
}
}
return String(ret);
}
forEach(cb: (it: Item) => void){
for(var item of this.items){
cb(item);
}
}
canMergeTo(s: ItemSet): boolean{
var dup: boolean = true;
for(var i = 0; i < this.g.rules.length; i++){
var t1 = this.itemTable[i], t2 = s.itemTable[i];
dup = dup && !!(t1 && t2 || !t1 && !t2);
if(t1 || t2){
var rhs = this.g.rules[i].rhs;
// check for identical LR0 kernel items
for(var j = 0; j <= rhs.length; j++){
if(
t1 && t1[j] && t1[j].isKernel && (!t2 || !t2[j] || !t2[j].isKernel)
|| t2 && t2[j] && t2[j].isKernel && (!t1 || !t1[j] || !t1[j].isKernel)
){
return false;
}
else {
dup = dup && (!t1 && !t1 && !t1[j] && !t2[j] || t1 && t2 && t1[j] && t2[j] && t1[j].lah.equals(t2[j].lah) );
}
}
}
}
if(!dup){
for(var rit of this.reduces){
for(var rit2 of s.reduces){
if(rit.rule !== rit2.rule && rit.lah.hasIntersection(rit2.lah)){
return false;
}
}
}
}
return true;
}
mergeTo(s: ItemSet): boolean{
var ret = false;
for(var it of s.items){
ret = this.add(it.rule, it.marker, it.isKernel, it.lah, true) || ret;
}
this.merges.push(s.index);
return ret;
}
}