tscc-compiler
Version:
An LALR(1) compiler compiler written in Typescript
249 lines (232 loc) • 7.88 kB
text/typescript
import { genItemSets,genParseTable, Conflict } from '../grammar/ptable-gen';
import { lexer } from '../lexer/pattern';
import { testParse } from './parse-test';
import { File } from '../parser/file';
import { ItemSet } from '../grammar/item-set';
import { ParseTable, IParseTable, printParseTable } from '../grammar/ptable';
import { TokenDef } from '../grammar/token-entry';
import { NtDef } from '../grammar/grammar';
import { List } from '../util/list';
import { OutputStream, StringIS } from '../util/io';
import { Context } from '../util/context';
import { JsccError, JsccWarning, ErrPrintOption } from '../util/E';
import { CompressedPTable } from '../grammar/ptable-compress';
import { TemplateInput } from '../codegen/def';
import { yyparse } from '../parser/parser';
import { templateExists, listTemplates, FileCreator, generateCode } from '../codegen/template';
import { markPosition } from '../parser/node';
import { DFATable } from '../lexer/dfa-table';
import { LexAction } from '../lexer/action';
import { io } from '../main';
import { EscapeDef, escapeString } from '../util/span';
export interface TSCCContext {
compile(source: string, fname: string);
reset();
setEscape(e: {[s: string]: string});
beginTime(s: string);
endTime();
printItemSets(stream: OutputStream);
printTable (os: OutputStream, showlah: boolean, showFullItemsets: boolean);
printDFA(os: OutputStream);
printError(os: OutputStream, opt?: ErrPrintOption);
printWarning(os: OutputStream, opt?: ErrPrintOption);
printDetailedTime(os: OutputStream);
hasWarning(): boolean;
hasError(): boolean;
warningSummary(): string;
isTerminated(): boolean;
isDone(): boolean;
testParse(tokens: string[], onErr: (msg: string) => any): string[];
genCode(fc: FileCreator);
}
export function createContext(): TSCCContext{
let file: File = null;
let itemSets: List<ItemSet> = null;
let iterationCount: number = 0;
let parseTable: CompressedPTable = null;
let errors: JsccError[] = [];
let warnings: JsccError[] = [];
let needLinecbs: ((ctx: Context, lines: string[]) => any)[] = [];
let terminated = false;
let done = false;
let timers: {name: string, start: Date, end: Date}[] = [];
let escapes: EscapeDef = null;
let ctx: Context = {
warn,
err,
requireLines: cb => needLinecbs.push(cb),
beginTime,
endTime,
};
return {
compile,
setEscape,
reset,
beginTime,
endTime,
printItemSets,
printTable,
printDFA,
testParse: (tokens, onErr) => testParse(file.grammar, parseTable, tokens, onErr),
printError,
printWarning,
printDetailedTime,
hasWarning,
hasError,
warningSummary: () => `${warnings.length} warning(s), ${errors.length} error(s)`,
genCode,
isTerminated: () => terminated,
isDone: () => done
};
function reset(){
file = null;
itemSets = null;
iterationCount = 0;
parseTable = null;
errors.length = 0;
warnings.length = 0;
needLinecbs.length = 0;
terminated = false;
done = false;
timers.length = 0;
}
function setEscape(e: {[s: string]: string}){
escapes = escapes || {};
for(var from in e){
escapes[from] = e[from];
}
}
function escape(s: string): string{
return escapes ? escapeString(s, escapes) : s;
}
function compile(source: string, fname: string){
reset();
var f = yyparse(ctx, source);
let lines = source.split(f.eol);
for(let cb of needLinecbs){
cb(ctx, lines);
}
if(hasError()){
terminated = true;
return;
}
f.name = fname;
var g = f.grammar;
file = f;
// we still could have errors here
for(var s of g.tokens){
if(!s.used){
let msg = `token <${s.sym}> is never used, definations are(is):\n`;
for(let pos of s.appears){
msg += markPosition(pos, lines) + '\n';
}
warn(new JsccWarning(msg));
}
}
for(var s2 of g.nts){
if(!s2.used){
warn(new JsccWarning(`non terminal "${s2.sym}" is unreachable`));
}
}
if(f.output !== null && !templateExists(f.output.val)){
let msg = `template for '${f.output.val}' is not implemented yet ` + markPosition(f.output, lines) + '\n';
msg += 'available templates are: ' + listTemplates().join(', ');
err(new JsccError(msg));
}
if(hasError()){
terminated = true;
return;
}
// don't proceed if any error has been detected
beginTime('generate first sets');
g.genFirstSets();
endTime();
beginTime('generate item sets');
var temp = genItemSets(g);
endTime();
itemSets = temp.result;
iterationCount = temp.iterations;
beginTime('generate parse table');
var temp2 = genParseTable(g, itemSets);
endTime();
temp2.result.findDefAct();
beginTime('compress parse table');
parseTable = new CompressedPTable(temp2.result);
endTime();
for(let cf of temp2.conflicts){
warn(new JsccWarning(cf.toString()));
}
beginTime('generate lexical DFA tables');
for(let dfa of file.lexDFA){
file.dfaTables.push(new DFATable<LexAction>(dfa));
}
endTime();
done = true;
}
function beginTime(name: string){
timers.push({ name, start: new Date(), end: null});
}
function endTime(){
timers[timers.length - 1].end = new Date();
}
function warn(w: JsccWarning){
warnings.push(w);
}
function err(e: JsccError){
errors.push(e);
}
function printItemSets(os: OutputStream){
os.writeln(`${itemSets.size} state(s) in total,finished in ${iterationCount} iteration(s).`);
itemSets.forEach(function(s){
os.writeln(escape(s.toString({ showTrailer: true })));
});
}
function printTable (os: OutputStream, showlah: boolean, showFullItemsets: boolean){
printParseTable(os, parseTable, itemSets, showlah, showFullItemsets, escapes);
}
function printDetailedTime(os: OutputStream){
for(var t of timers){
os.writeln(`${t.name}: ${(t.end.valueOf() - t.start.valueOf()) / 1000}s`);
}
}
function printDFA(os: OutputStream){
var i = 0;
for(let s of file.dfaTables){
os.writeln(`DFA state ${i}`);
s.print(os, escapes);
os.writeln();
os.writeln();
i++;
}
}
function printError(os: OutputStream, opt?: ErrPrintOption){
for(let e of errors){
os.writeln(e.toString(opt).toString(escapes));
}
os.writeln();
}
function printWarning(os: OutputStream, opt?: ErrPrintOption){
for(let w of warnings){
os.writeln(w.toString(opt).toString(escapes));
}
os.writeln();
}
function hasWarning(){
return warnings.length > 0;
}
function hasError(){
return errors.length > 0;
}
function genCode(fc: FileCreator){
var tempin = getTemplateInput();
generateCode(tempin.output, tempin, fc);
}
function getTemplateInput(): TemplateInput{
return {
endl: file.eol,
output: file.output === null ? 'typescript' : file.output.val,
pt: parseTable,
file: file
};
}
}