UNPKG

tscc-compiler

Version:

An LALR(1) compiler compiler written in Typescript

249 lines (232 loc) 7.88 kB
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 }; } }