tscc-compiler
Version:
An LALR(1) compiler compiler written in Typescript
1,678 lines (1,675 loc) • 57.5 kB
text/typescript
import { TemplateInput, TemplateOutput } from '../def';
import { Item, Action } from '../../grammar/item-set';
import { Rule } from '../../grammar/grammar';
import { TokenDef } from '../../grammar/token-entry';
import { CodeGenerator } from '../code-generator';
import { DFA } from '../../lexer/dfa';
import { LexAction } from '../../lexer/action';
import { State, Arc } from '../../lexer/state';
import { oo, _oo } from '../../util/interval-set';
import { JNode, Position } from '../../parser/node'
import { DFATable } from '../../lexer/dfa-table'
export default function(input: TemplateInput, output: TemplateOutput){
echoLine("/*");
echoLine(" generated by jscc, an LALR(1) parser generator made by hadroncfy.");
echoLine(" template for typescript, written by hadroncfy, aussi.");
echo("*/");
for(let h of input.file.header){
echoLine("");
echo(h.val );
}
let prefix = input.file.prefix;
let tab = getOpt('tab', ' ');
let ists = input.output === 'typescript';
function ts(s: string, s2?: string){
return ists ? s : (s2 || '');
}
function n(t: JNode, def: string = ''){
return t === null ? def : t.val;
}
function getOpt(n: string, def: string){
let t = input.file.opt[n];
return t === undefined ? def : t.val.val;
}
function echo(s: string | number){
output.write(s);
}
function echoLine(s: string | number){
output.writeln(s);
}
function leftAlign(s: string, al: number): string{
function repeat(s: string, t: number){
let ret = '';
while(t --> 0) ret += s;
return ret;
}
return (s.length < al ? repeat(' ', al - s.length) : '') + s;
}
function printTable<T>(tname: string, type: string, t: T[], align: number, lc: number, mapper: (d: T) => string){
let count = 1;
echoLine("");
echo("var ");
echo(prefix + tname + ts(': ' + type + '[]'));
echoLine(" = [ ");
echo(tab);
for(let i of t){
echo(leftAlign(mapper(i), align));
echo(',');
count++ >= lc && (count = 1, echo(input.endl + tab));
}
echoLine("");
echo("]; ");
}
function lambda(arg: string, body: string): string{
if(ists){
return `${arg} => ${body}`;
}
else {
var a = arg.charAt(0) === '(' ? arg : `(${arg})`;
return `function${a}{ return ${body}; }`;
}
}
echoLine("");
echoLine("/*");
echoLine(" constants");
echoLine("*/");
echo("var ");
echo(prefix );
echoLine("lf = '\\n'.charCodeAt(0);");
echo("var ");
echo(prefix );
echo("cr = '\\r'.charCodeAt(0);");
if(ists){
echoLine("");
echoLine("interface DFATable{");
echoLine(" pnext: number[];");
echoLine(" disnext: number[];");
echoLine(" checknext: number[];");
echoLine(" maxAsicii: number;");
echoLine(" classTable: number[];");
echoLine(" unicodeClassTable: number[];");
echoLine(" isEnd: number[];");
echoLine(" hasArc: number[];");
echo("};");
}
let dfaTables = input.file.dfaTables;
function printDFATable(t: DFATable<LexAction>, n: number){
function tn(s: string){
return prefix + s + String(n);
}
printTable<Arc<LexAction>>('lexpnext' + n, 'number', t.pnext, 6, 10, a => a === null ? '-1' : String(a.to.index));
printTable<number>('lexdisnext' + n, 'number', t.disnext, 6, 10, a => String(a));
printTable<number>('lexchecknext' + n, 'number', t.checknext, 6, 10, a => String(a));
printTable<number>('lexclassTable' + n, 'number', t.classTable, 6, 10, a => String(a));
printTable<number>('lexunicodeClassTable' + n, 'number', t.unicodeClassTable, 6, 10, a => String(a));
printTable<State<LexAction>>('lexisEnd' + n, 'number', t.states, 1, 15, a => a.endAction === null ? '0' : '1');
printTable<State<LexAction>>('lexhasArc' + n, 'number', t.states, 1, 15, a => a.arcs.length === 0 ? '0' : '1');
echoLine("");
echo("var ");
echo(prefix );
echo("lextable");
echo(String(n) + ts(': DFATable') );
echoLine(" = {");
echo(" pnext: ");
echo(tn('lexpnext') );
echoLine(",");
echo(" disnext: ");
echo(tn('lexdisnext') );
echoLine(",");
echo(" checknext: ");
echo(tn('lexchecknext') );
echoLine(",");
echo(" maxAsicii: ");
echo(t.maxAsicii );
echoLine(",");
echo(" classTable: ");
echo(tn('lexclassTable') );
echoLine(",");
echo(" unicodeClassTable: ");
echo(tn('lexunicodeClassTable') );
echoLine(",");
echo(" isEnd: ");
echo(tn('lexisEnd') );
echoLine(",");
echo(" hasArc: ");
echo(tn('lexhasArc') );
echoLine("");
echo("};");
}
echoLine("");
echoLine("/*");
echoLine(" dfa table definations");
echo("*/");
for(let i = 0, _a = dfaTables; i < _a.length; i++){
printDFATable(_a[i], i);
}
echoLine("");
echoLine("/*");
echoLine(" dfa tables");
echoLine("*/");
echo("var ");
echo(prefix );
echo("dfaTables");
echo(ts(': DFATable[]') );
echo(" = [");
for(let i = 0;i < dfaTables.length; i++){
echoLine("");
echo(" ");
echo(prefix );
echo("lextable");
echo(i );
echo(",");
}
echoLine("");
echoLine("];");
echoLine("/*");
echoLine(" find unicode class");
echoLine("*/");
echo("function ");
echo(prefix );
echo("findUnicodeClass(uc");
echo(ts(": number[]") );
echo(", c");
echo(ts(": number") );
echoLine("){");
echoLine(" for(var i = 0; i < uc.length; i += 3){");
echoLine(" if(c >= uc[i + 1] && c <= uc[i + 2]){");
echoLine(" return uc[i];");
echoLine(" }");
echoLine(" else if(c < uc[i + 1]){");
echoLine(" return -1;");
echoLine(" }");
echoLine(" }");
echoLine(" return -1;");
echoLine("}");
echoLine("/*");
echoLine(" tokens that a lexical dfa state can return");
echo("*/");
for(let i = 0, _a = dfaTables; i < _a.length; i++){
printTable<State<LexAction>>('lexTokens' + i, 'number', _a[i].states, 6, 10, s => {
return s.endAction === null || s.endAction.data.token === null ? '-1' : String(s.endAction.data.token.index);
});
}
echoLine("");
let pt = input.pt;
echoLine("");
echo("var ");
echo(prefix );
echo("stateCount = ");
echo(pt.stateCount );
echoLine(";");
echo("var ");
echo(prefix );
echo("tokenCount = ");
echo(input.file.grammar.tokens.length );
echoLine(";");
echo("var ");
echo(prefix );
echo("actERR = ");
echo(pt.stateCount + 1 );
echoLine(";");
echoLine("/*");
echo(" compressed action table: action = ");
echo(prefix );
echo("pact[");
echo(prefix );
echoLine("disact[STATE-NUM] + TOKEN]");
echoLine(" when action > 0, shift the token and goto state (action - 1);");
echoLine(" when action < 0, reduce with rule (1-action);");
echoLine(" when action = 0, do default action.");
echo("*/");
printTable<Item>('pact', 'number', pt.pact, 6, 10, t => {
if(t === null){
return '0';
}
else if(t === Item.NULL){
return String(pt.stateCount + 1);
}
else if(t.actionType === Action.SHIFT){
return (t.shift.stateIndex + 1).toString();
}
else if(t.actionType === Action.REDUCE){
return (-t.rule.index - 1).toString();
}
});
echoLine("");
echoLine("/*");
echoLine(" displacement of action table.");
echo("*/");
printTable<number>('disact', 'number', pt.disact, 6, 10, t => t.toString());
echoLine("");
echoLine("/*");
echo(" used to check if a position in ");
echo(prefix );
echoLine("pact is out of bouds.");
echo(" if ");
echo(prefix );
echo("checkact[");
echo(prefix );
echoLine("disact[STATE-NUM] + TOKEN] = STATE-NUM, this position is not out of bounds.");
echo("*/");
printTable<number>('checkact', 'number', pt.checkact, 6, 10, t => t === undefined ? '0' : t.toString());
echoLine("");
echoLine("/*");
echo(" default action table. action = ");
echo(prefix );
echoLine("defred[STATE-NUM],");
echoLine(" where action is the number of the rule to reduce with.");
echo("*/");
printTable<number>('defred', 'number', pt.defred, 6, 10, t => t.toString());
echoLine("");
echoLine("/*");
echo(" compressed goto table: goto = ");
echo(prefix );
echo("pgoto[");
echo(prefix );
echoLine("disgoto[STATE-NUM] + NON_TERMINAL]");
echo("*/");
printTable<Item>('pgoto', 'number', pt.pgoto, 6, 10, t => {
if(t === null){
return '-1';
}
else {
return t.shift.stateIndex.toString();
}
});
echoLine("");
echoLine("/*");
echoLine(" displacement of the goto table");
echo("*/");
printTable<number>('disgoto', 'number', pt.disgoto, 6, 10, t => t.toString());
echoLine("");
echoLine("/*");
echo(" length of each rule: rule length = ");
echo(prefix );
echoLine("ruleLen[RULE-NUM]");
echo("*/");
printTable<Rule>('ruleLen', 'number', pt.g.rules, 6, 10, r => r.rhs.length.toString());
echoLine("");
echoLine("/*");
echoLine(" index of the LHS of each rule");
echo("*/");
printTable<Rule>('lhs', 'number', pt.g.rules, 6, 10, r => r.lhs.index.toString());
echoLine("");
echoLine("/*");
echoLine(" token names");
echo("*/");
printTable<TokenDef>('tokenNames', 'string', pt.g.tokens, 20, 3, t => `"${t.sym.replace(/"/g, '\\"')}"`);
echoLine("");
echoLine("/*");
echoLine(" token alias");
echo("*/");
printTable<TokenDef>('tokenAlias', 'string', pt.g.tokens, 20, 3, t => t.alias ? `"${t.alias.replace(/"/g, '\\"')}"` : "null");
let className = getOpt('className', 'Parser');
echoLine("");
function printLexActionsFunc(dfa: DFATable<LexAction>, n: number){
let codegen = {
raw(s: string){
echo(s);
},
beginBlock(pos: Position, always: boolean){
!always && echo(`if(${prefix}enableBlock)`);
echo('{');
},
endBlock(pos: Position){
echo('}');
},
pushLexState(n: number){
echo(`${prefix}lexState.push(${n})`);
},
switchToLexState(n: number){
echo(`${prefix}lexState[${prefix}lexState.length - 1] = ${n}`);
},
popLexState(){
echo(`${prefix}lexState.pop()`);
},
setImg(n: string){
echo(`${prefix}setImg("${n}")`);
},
tokenObj(){
echo(prefix + 'token');
}, // $token
matched(){
echo(prefix + 'matched');
},
lhs(){
echo(prefix + "sematicVal");
}, // $$
emitToken(tid: number){
echo(`${prefix}tokenQueue.push(new Token(${tid}, null, -1, 0, 0, 0))`);
}
};
let statevn = prefix + 'staten';
echoLine("");
echo(" function ");
echo(prefix );
echo("doLexAction");
echo(n );
echo("(");
echo(statevn + ts(": number"));
echoLine("){");
echo(" var ");
echo(prefix );
echo("tk = ");
echo(prefix );
echo("lexTokens");
echo(n );
echo("[");
echo(statevn );
echoLine("];");
echo(" ");
echo(prefix );
echo("tk !== -1 && ");
echo(prefix );
echo("prepareToken(");
echo(prefix );
echoLine("tk);");
echo(" switch(");
echo(statevn );
echo("){");
for(let i = 0, _a = dfa.states; i < _a.length; i++){
if(_a[i].endAction !== null && _a[i].endAction.data.actions.length > 0){
echoLine("");
echo(" case ");
echo(i );
echo(":");
echoLine(''); echo(' ');
_a[i].endAction.data.toCode(codegen);
echoLine("");
echo(" break;");
}
}
echoLine("");
echoLine(" default:;");
echoLine(" }");
echo(" }");
}
echoLine("");
if(ists){
echoLine("");
echoLine("function tokenToString(tk: number){");
echo(" return ");
echo(prefix );
echo("tokenAlias[tk] === null ? `<${");
echo(prefix );
echo("tokenNames[tk]}>` : `\"${");
echo(prefix );
echoLine("tokenAlias[tk]}\"`;");
echo("}");
} else {
echoLine("");
echoLine("function tokenToString(tk){");
echo(" return ");
echo(prefix );
echo("tokenAlias[tk] === null ? \"<\" + ");
echo(prefix );
echo("tokenNames[tk] + \">\" : '\"' + ");
echo(prefix );
echoLine("tokenAlias[tk] + '\"';");
echo("}");
}
echoLine("");
echo("function getExpectedTokens(state");
echo(ts(': number') );
echo(")");
echo(ts(': number[]') );
echoLine("{");
echo(" var dis = ");
echo(prefix );
echoLine("disact[state];");
echo(" var ret");
echo(ts(': number[]') );
echoLine(" = [];");
echo(" function expect(tk");
echo(ts(": number") );
echoLine("){");
echoLine(" var ind = dis + tk;");
echo(" if(ind < 0 || ind >= ");
echo(prefix );
echo("pact.length || state !== ");
echo(prefix );
echoLine("checkact[ind]){");
echo(" return ");
echo(prefix );
echoLine("defred[state] !== -1;");
echoLine(" }");
echoLine(" else {");
echoLine(" return true;");
echoLine(" }");
echoLine(" }");
echo(" for(var tk = 0; tk < ");
echo(prefix );
echoLine("tokenCount; tk++){");
echoLine(" expect(tk) && ret.push(tk);");
echoLine(" }");
echoLine(" return ret;");
echoLine("}");
echoLine("// Token kinds");
echo(ts('enum TokenKind {', 'var TokenKind = {') );
var i = 0;
echoLine('');
for(var tdef of input.file.grammar.tokens){
echoLine(' ' + ts(`${tdef.sym} = ${i}`, `${tdef.sym} : ${i}`) + ',');
i++;
}
echoLine("");
echo("};");
if(ists){
echoLine("");
echoLine("class Token {");
echoLine(" constructor(");
echoLine(" public id: number,");
echoLine(" public val: string,");
echoLine(" public startLine: number,");
echoLine(" public startColumn: number,");
echoLine(" public endLine: number,");
echoLine(" public endColumn: number");
echoLine(" ){}");
echoLine(" clone(){");
echoLine(" return new Token(");
echoLine(" this.id,");
echoLine(" this.val,");
echoLine(" this.startLine,");
echoLine(" this.startColumn,");
echoLine(" this.endLine,");
echoLine(" this.endColumn");
echoLine(" );");
echoLine(" }");
echoLine(" toString(){");
echo(" return (");
echo(prefix );
echoLine("tokenAlias[this.id] === null ? ");
echo(" `<${");
echo(prefix );
echoLine("tokenNames[this.id]}>` :");
echo(" `\"${");
echo(prefix );
echoLine("tokenAlias[this.id]}\"`) + `(\"${this.val}\")`;");
echoLine(" }");
echoLine("}");
echo("interface ");
echo(className );
echoLine("{");
echo(" init(");
echo(n(input.file.initArg) );
echoLine(");");
echoLine(" accept(s: string);");
echoLine(" end();");
echoLine(" parse(input: ParserInput | string): boolean;");
echoLine(" load(input: ParserInput | string);");
echoLine(" nextToken(): Token;");
echoLine("");
echoLine(" setLineTerminator(lt: LineTerm);");
echoLine(" getLineTerminator(): LineTerm;");
echoLine(" halt();");
echoLine(" on(ent: 'lexicalerror', cb: (c: string, line: number, col: number) => any);");
echoLine(" on(ent: 'syntaxerror', cb: (t: Token, state: number) => any);");
echoLine(" on(ent: 'accept', cb: () => any);");
echoLine("");
echoLine(" enableBlocks();");
echoLine(" disableBlocks();");
echoLine(" loadParserState(state: ParserState);");
echoLine(" getParserState(): ParserState;");
echo("}");
} else {
echoLine("");
echoLine("function Token(id, val, startLine, startColumn, endLine, endColumn){");
echoLine(" this.id = id;");
echoLine(" this.val = val;");
echoLine(" this.startLine = startLine;");
echoLine(" this.startColumn = startColumn;");
echoLine(" this.endLine = endLine;");
echoLine(" this.endColumn = endColumn;");
echoLine("}");
echoLine("Token.prototype.clone = function(){");
echoLine(" return new Token(");
echoLine(" this.id,");
echoLine(" this.val,");
echoLine(" this.startLine,");
echoLine(" this.startColumn,");
echoLine(" this.endLine,");
echoLine(" this.endColumn");
echoLine(" );");
echoLine("}");
echoLine("Token.prototype.toString = function(){");
echo(" return (");
echo(prefix );
echoLine("tokenAlias[this.id] === null ? ");
echo(" '<' + ");
echo(prefix );
echoLine("tokenNames[this.id] + '>' :");
echo(" '\"' + ");
echo(prefix );
echoLine("tokenAlias[this.id] + '\"') + \"(\" + this.val + \")\";");
echo("}");
}
let stype = n(input.file.sematicType, 'any');
if(ists){
echoLine("");
echoLine("interface ParserState {");
echoLine(" lexState: number[];");
echoLine(" lrState: number[];");
echo(" sematicS: ");
echo(stype );
echoLine("[];");
echoLine("};");
echoLine("interface ParserInput {");
echoLine(" current(): number;");
echoLine(" next();");
echoLine(" isEof(): boolean;");
echoLine(" backup(s: string);");
echoLine("};");
echoLine("enum LineTerm{");
echoLine(" NONE = 1,");
echoLine(" AUTO,");
echoLine(" CR,");
echoLine(" LF,");
echoLine(" CRLF");
echo("};");
} else {
echoLine("");
echoLine("var LineTerm = {");
echoLine(" NONE: 1,");
echoLine(" AUTO: 2,");
echoLine(" CR: 3,");
echoLine(" LF: 4,");
echoLine(" CRLF: 5");
echo("};");
}
echoLine("");
echoLine("");
echo("function create");
echo(className );
echo("()");
echo(ts(': ' + className) );
echoLine(" {");
echoLine(" //#region parser state variables");
echo(" var ");
echo(prefix );
echo("lexState");
echo(ts(": number[]") );
echoLine(";");
echo(" var ");
echo(prefix );
echo("state");
echo(ts(": number") );
echoLine(";");
echo(" var ");
echo(prefix );
echo("lastCR");
echo(ts(': boolean') );
echoLine(";");
echo(" var ");
echo(prefix );
echo("matched");
echo(ts(": string") );
echoLine(";");
echo(" var ");
echo(prefix );
echo("marker");
echo(ts(": { state: number, line: number, column: number }") );
echoLine(" = { state: -1, line: 0, column: 0 };");
echo(" var ");
echo(prefix );
echo("backupCount");
echo(ts(": number") );
echoLine(";");
echo(" var ");
echo(prefix );
echo("line");
echo(ts(": number") );
echoLine(";");
echo(" var ");
echo(prefix );
echo("column");
echo(ts(": number") );
echoLine(";");
echo(" var ");
echo(prefix );
echo("tline");
echo(ts(": number") );
echoLine(";");
echo(" var ");
echo(prefix );
echo("tcolumn");
echo(ts(": number") );
echoLine(";");
echoLine("");
echo(" var ");
echo(prefix );
echo("lrState");
echo(ts(": number[]") );
echoLine(";");
echo(" var ");
echo(prefix );
echo("sematicS");
echo(ts(': ' + stype + '[]') );
echoLine(";");
echoLine(" //#endregion");
echoLine("");
echo(" var ");
echo(prefix );
echo("input");
echo(ts(': ParserInput') );
echoLine(";");
echo(" var ");
echo(prefix );
echo("sematicVal");
echo(ts(': ' + stype) );
echoLine(";");
echo(" var ");
echo(prefix );
echo("tokenQueue");
echo(ts(": Token[]") );
echoLine(";");
echo(" var ");
echo(prefix );
echo("token");
echo(ts(": Token") );
echoLine(";");
echo(" var ");
echo(prefix );
echo("stop");
echo(ts(': boolean') );
echoLine(";");
echo(" var ");
echo(prefix );
echo("tokenEmitted");
echo(ts(': boolean') );
echoLine(";");
echo(" var ");
echo(prefix );
echo("enableBlock");
echo(ts(': boolean') );
echoLine(" = true;");
echo(" var ");
echo(prefix );
echo("lineTerm");
echo(ts(': LineTerm') );
echoLine(";");
echoLine("");
echo(" var ");
echo(prefix );
echo("handlers");
echo(ts(": {[s: string]: ((a1?, a2?, a3?) => any)[]}") );
echoLine(" = {};");
echoLine("");
echoLine(" // extra members, defined by %extra_arg");
echo(" ");
echo(n(input.file.extraArgs) );
echoLine("");
if(ists) {
echoLine("");
echoLine(" return {");
echoLine(" init,");
echoLine(" on,");
echoLine(" setLineTerminator,");
echo(" getLineTerminator: () => ");
echo(prefix );
echoLine("lineTerm,");
echoLine(" accept,");
echoLine(" end,");
echoLine(" load,");
echoLine(" parse,");
echoLine(" nextToken,");
echoLine(" halt,");
echoLine(" enableBlocks,");
echoLine(" disableBlocks,");
echoLine(" loadParserState,");
echoLine(" getParserState");
echo(" };");
} else {
echoLine("");
echoLine(" return {");
echoLine(" init: init,");
echoLine(" on: on,");
echoLine(" setLineTerminator: setLineTerminator,");
echo(" getLineTerminator: function() { return ");
echo(prefix );
echoLine("lineTerm; },");
echoLine(" accept: accept,");
echoLine(" end: end,");
echoLine(" load: load,");
echoLine(" nextToken: nextToken,");
echoLine(" halt: halt,");
echoLine(" enableBlocks: enableBlocks,");
echoLine(" disableBlocks: disableBlocks,");
echoLine(" loadParserState: loadParserState,");
echoLine(" getParserState: getParserState,");
echoLine(" parse: parse");
echo(" };");
}
echoLine("");
echo(" function init(");
echo(n(input.file.initArg) );
echoLine("){");
echo(" ");
echo(prefix );
echoLine("lexState = [ 0 ];// DEFAULT");
echo(" ");
echo(prefix );
echoLine("state = 0;");
echo(" ");
echo(prefix );
echoLine("matched = '';");
echo(" ");
echo(prefix );
echoLine("token = new Token(-1, null, 0, 0, 0, 0);");
echo(" ");
echo(prefix );
echoLine("marker.state = -1;");
echo(" ");
echo(prefix );
echoLine("backupCount = 0;");
echo(" ");
echo(prefix );
echo("line = ");
echo(prefix );
echoLine("tline = 0;");
echo(" ");
echo(prefix );
echo("column = ");
echo(prefix );
echoLine("tcolumn = 0;");
echoLine(" ");
echo(" ");
echo(prefix );
echoLine("lrState = [ 0 ];");
echo(" ");
echo(prefix );
echoLine("sematicS = [];");
echo(" ");
echo(prefix );
echoLine("sematicVal = null;");
echo(" ");
echo(prefix );
echoLine("tokenQueue = [];");
echoLine("");
echo(" ");
echo(prefix );
echoLine("lineTerm = LineTerm.AUTO;");
echo(" ");
echo(prefix );
echoLine("lastCR = false;");
echoLine("");
echo(" ");
echo(n(input.file.initBody) );
echoLine("");
echoLine("");
echo(" ");
echo(prefix );
echoLine("tryReduce();");
echoLine(" }");
echo(" function load(input");
echo(ts(': ParserInput | string') );
echoLine("){");
echoLine(" if(typeof input === 'string'){");
echoLine(" var i = 0;");
echo(" var s");
echo(ts(': string') );
echoLine(" = input;");
echo(" ");
echo(prefix );
echoLine("input = {");
echo(" current: ");
echo(lambda('()', 'i < s.length ? s.charCodeAt(i) : null') );
echoLine(",");
echo(" next: ");
echo(lambda('()', 'i++') );
echoLine(",");
echo(" isEof: ");
echo(lambda('()', 'i >= s.length') );
echoLine(",");
echo(" backup: ");
echo(lambda('t', 'i -= t.length') );
echoLine("");
echoLine(" }");
echoLine(" }");
echoLine(" else {");
echo(" ");
echo(prefix );
echoLine("input = input;");
echoLine(" }");
echoLine(" }");
echo(" function nextToken()");
echo(ts(': Token') );
echoLine("{");
echo(" ");
echo(prefix );
echoLine("tokenEmitted = false;");
echo(" while(!");
echo(prefix );
echo("stop && !");
echo(prefix );
echoLine("tokenEmitted){");
echo(" var c = ");
echo(prefix );
echoLine("input.current();");
echoLine(" if(c !== null){");
echo(" ");
echo(prefix );
echoLine("acceptChar(c);");
echoLine(" }");
echoLine(" // null means end of file or no input available at present");
echo(" else if(");
echo(prefix );
echoLine("input.isEof()){");
echo(" if(");
echo(prefix );
echoLine("acceptEOF()){");
echoLine(" break;");
echoLine(" }");
echoLine(" }");
echoLine(" else {");
echoLine(" return null;");
echoLine(" }");
echoLine(" }");
echo(" return ");
echo(prefix );
echoLine("token;");
echoLine(" }");
echo(" function setLineTerminator(lt");
echo(ts(': LineTerm') );
echoLine("){");
echo(" ");
echo(prefix );
echoLine("lineTerm = lt;");
echoLine(" }");
echoLine(" function enableBlocks(){");
echo(" ");
echo(prefix );
echoLine("enableBlock = true;");
echoLine(" }");
echoLine(" function disableBlocks(){");
echo(" ");
echo(prefix );
echoLine("enableBlock = false;");
echoLine(" }");
echoLine(" /**");
echoLine(" * input a string");
echoLine(" * @api public");
echoLine(" * @deprecated");
echoLine(" */");
echo(" function accept(s");
echo(ts(": string") );
echoLine("){");
echoLine(" var i = 0;");
echoLine(" load({");
echo(" current: ");
echo(lambda('()', 'i < s.length ? s.charCodeAt(i) : null') );
echoLine(",");
echo(" next: ");
echo(lambda('()', 'i++') );
echoLine(",");
echo(" isEof: ");
echo(lambda('()', 'i >= s.length') );
echoLine(",");
echo(" backup: ");
echo(lambda('t', 'i -= t.length') );
echoLine("");
echoLine(" });");
echo(" while(!");
echo(prefix );
echoLine("stop && nextToken().id !== 0);");
echoLine(" }");
echoLine(" /**");
echoLine(" * tell the compiler that end of file is reached");
echoLine(" * @api public");
echoLine(" */");
echoLine(" function end(){");
echoLine(" ");
echoLine(" }");
echo(" function parse(input");
echo(ts(': ParserInput | string') );
echo(")");
echo(ts(": boolean") );
echoLine(" {");
echoLine(" load(input);");
echoLine(" var t;");
echo(" while(!");
echo(prefix );
echoLine("stop){");
echoLine(" t = nextToken();");
echoLine(" if(t === null){");
echoLine(" return false;");
echoLine(" }");
echoLine(" else if(t.id === 0){");
echoLine(" return true;");
echoLine(" }");
echoLine(" }");
echoLine(" return true;");
echoLine(" }");
echoLine(" function halt(){");
echo(" ");
echo(prefix );
echoLine("stop = true;");
echoLine(" }");
echo(" function loadParserState(state");
echo(ts(': ParserState') );
echoLine("){");
echo(" ");
echo(prefix );
echoLine("lexState = state.lexState;");
echo(" ");
echo(prefix );
echoLine("lrState = state.lrState;");
echo(" ");
echo(prefix );
echoLine("sematicS = state.sematicS;");
echoLine(" }");
echo(" function getParserState()");
echo(ts(': ParserState') );
echoLine(" {");
echoLine(" return {");
echo(" lexState: ");
echo(prefix );
echoLine("lexState,");
echo(" lrState: ");
echo(prefix );
echoLine("lrState,");
echo(" sematicS: ");
echo(prefix );
echoLine("sematicS");
echoLine(" };");
echo(" }");
if(input.file.tokenHookArg){
echoLine("");
echo(" function ");
echo(prefix );
echo("tokenHook(");
echo(input.file.tokenHookArg.val + ts(': Token'));
echo(")");
echo(ts(': boolean') );
echoLine("{");
echo(" ");
echo(input.file.tokenHookBody.val );
echoLine("");
echo(" }");
}
echoLine("");
echoLine(" /**");
echoLine(" * set ");
echoLine(" */");
echo(" function ");
echo(prefix );
echo("setImg(s");
echo(ts(": string") );
echoLine("){");
echo(" ");
echo(prefix );
echoLine("matched = s;");
echo(" ");
echo(prefix );
echo("tline = ");
echo(prefix );
echoLine("line;");
echo(" ");
echo(prefix );
echo("tcolumn = ");
echo(prefix );
echoLine("column;");
echoLine(" }");
echo(" function ");
echo(prefix );
echo("prepareToken(tid");
echo(ts(": number") );
echoLine("){");
echo(" ");
echo(prefix );
echoLine("token.id = tid;");
echo(" ");
echo(prefix );
echo("token.val = ");
echo(prefix );
echoLine("matched;");
echo(" ");
echo(prefix );
echo("token.startLine = ");
echo(prefix );
echoLine("tline;");
echo(" ");
echo(prefix );
echo("token.startColumn = ");
echo(prefix );
echoLine("tcolumn;");
echo(" ");
echo(prefix );
echo("token.endLine = ");
echo(prefix );
echoLine("line;");
echo(" ");
echo(prefix );
echo("token.endColumn = ");
echo(prefix );
echoLine("column - 1;");
if(input.file.tokenHookArg){
echoLine("");
echo(" if(!");
echo(prefix );
echo("tokenHook(");
echo(prefix );
echoLine("token)){");
echo(" ");
echo(prefix );
echo("tokenQueue.push(");
echo(prefix );
echoLine("token);");
echo(" }");
} else {
echoLine("");
echo(" ");
echo(prefix );
echo("tokenQueue.push(");
echo(prefix );
echo("token);");
}
echoLine("");
echoLine("");
echo(" ");
echo(prefix );
echoLine("tokenEmitted = true;");
echo(" ");
echo(prefix );
echoLine("matched = '';");
echo(" ");
echo(prefix );
echo("tline = ");
echo(prefix );
echoLine("line;");
echo(" ");
echo(prefix );
echo("tcolumn = ");
echo(prefix );
echoLine("column;");
echoLine(" }");
echo(" function ");
echo(prefix );
echo("emit(name");
echo(ts(": string") + ts(", a1?, a2?, a3?", ", a1, a2, a3") );
echoLine("){");
echo(" var cbs = ");
echo(prefix );
echoLine("handlers[name];");
echoLine(" if(cbs){");
echoLine(" for(var i = 0; i < cbs.length; i++){");
echoLine(" cbs[i](a1, a2, a3);");
echoLine(" }");
echoLine(" }");
echoLine(" }");
echo(" function on(name");
echo(ts(": string") );
echo(", cb");
echo(ts(": (a1?, a2?, a3?) => any") );
echoLine("){");
echo(" ");
echo(prefix );
echo("handlers[name] || (");
echo(prefix );
echoLine("handlers[name] = []);");
echo(" ");
echo(prefix );
echoLine("handlers[name].push(cb);");
echo(" }");
for(let i = 0, _a = dfaTables; i < _a.length; i++){
printLexActionsFunc(_a[i], i);
}
echoLine("");
echoLine(" /**");
echoLine(" * do a lexical action");
echoLine(" * @api private");
echoLine(" * @internal");
echoLine(" */");
echo(" function ");
echo(prefix );
echo("doLexAction(lexstate");
echo(ts(": number") );
echo(", state");
echo(ts(": number") );
echoLine("){");
echo(" switch(lexstate){");
for(let i = 0;i < dfaTables.length;i++){
echoLine("");
echo(" case ");
echo(i );
echoLine(":");
echo(" ");
echo(prefix );
echo("doLexAction");
echo(i );
echoLine("(state);");
echo(" break;");
}
echoLine("");
echoLine(" default:;");
echoLine(" }");
echo(" ");
echo(prefix );
echo("tokenQueue.length > 0 && ");
echo(prefix );
echoLine("acceptToken(null);");
echoLine(" }");
echo(" function ");
echo(prefix );
echoLine("rollback(){");
echo(" var ret = ");
echo(prefix );
echo("matched.substr(");
echo(prefix );
echo("matched.length - ");
echo(prefix );
echo("backupCount, ");
echo(prefix );
echoLine("backupCount);");
echo(" ");
echo(prefix );
echoLine("input.backup(ret);");
echo(" ");
echo(prefix );
echo("matched = ");
echo(prefix );
echo("matched.substr(0, ");
echo(prefix );
echo("matched.length - ");
echo(prefix );
echoLine("backupCount);");
echo(" ");
echo(prefix );
echoLine("backupCount = 0;");
echo(" ");
echo(prefix );
echo("line = ");
echo(prefix );
echoLine("marker.line;");
echo(" ");
echo(prefix );
echo("column = ");
echo(prefix );
echoLine("marker.column;");
echo(" ");
echo(prefix );
echo("state = ");
echo(prefix );
echoLine("marker.state;");
echo(" ");
echo(prefix );
echoLine("marker.state = -1;");
echoLine(" }");
echo(" function ");
echo(prefix );
echoLine("mark(){");
echo(" ");
echo(prefix );
echo("marker.state = ");
echo(prefix );
echoLine("state;");
echo(" ");
echo(prefix );
echo("marker.line = ");
echo(prefix );
echoLine("line;");
echo(" ");
echo(prefix );
echo("marker.column = ");
echo(prefix );
echoLine("column;");
echo(" ");
echo(prefix );
echoLine("backupCount = 0;");
echoLine(" }");
echo(" function ");
echo(prefix );
echo("consume(c");
echo(ts(": number") );
echoLine("){");
echo(" // c === ");
echo(prefix );
echo("eol ? (");
echo(prefix );
echo("line++, ");
echo(prefix );
echo("column = 0) : (");
echo(prefix );
echoLine("column += c > 0xff ? 2 : 1);");
echo(" switch(");
echo(prefix);
echoLine("lineTerm){");
echoLine(" case LineTerm.NONE:");
echo(" ");
echo(prefix );
echoLine("column += c > 0xff ? 2 : 1;");
echoLine(" break;");
echoLine(" case LineTerm.CR:");
echo(" c === ");
echo(prefix );
echo("cr ? (");
echo(prefix );
echo("line++, ");
echo(prefix );
echo("column = 0) : (");
echo(prefix );
echoLine("column += c > 0xff ? 2 : 1);");
echoLine(" break;");
echoLine(" case LineTerm.LF:");
echo(" c === ");
echo(prefix );
echo("lf ? (");
echo(prefix );
echo("line++, ");
echo(prefix );
echo("column = 0) : (");
echo(prefix );
echoLine("column += c > 0xff ? 2 : 1);");
echoLine(" break;");
echoLine(" case LineTerm.CRLF:");
echo(" if(");
echo(prefix );
echoLine("lastCR){");
echo(" if(c === ");
echo(prefix );
echoLine("lf){");
echo(" ");
echo(prefix );
echo("line++, ");
echo(prefix );
echoLine("column = 0;");
echo(" ");
echo(prefix );
echoLine("lastCR = false;");
echoLine(" }");
echoLine(" else {");
echo(" ");
echo(prefix );
echoLine("column += c > 0xff ? 2 : 1;");
echo(" ");
echo(prefix );
echo("lastCR = c === ");
echo(prefix );
echoLine("cr;");
echoLine(" }");
echoLine(" }");
echoLine(" else {");
echo(" ");
echo(prefix );
echoLine("column += c > 0xff ? 2 : 1;");
echo(" ");
echo(prefix );
echo("lastCR = c === ");
echo(prefix );
echoLine("cr;");
echoLine(" }");
echoLine(" break;");
echoLine(" case LineTerm.AUTO:");
echo(" if(");
echo(prefix );
echoLine("lastCR){");
echo(" if(c === ");
echo(prefix );
echoLine("lf){");
echo(" ");
echo(prefix );
echo("line++, ");
echo(prefix );
echoLine("column = 0;");
echo(" ");
echo(prefix );
echoLine("lastCR = false;");
echo(" ");
echo(prefix);
echoLine("lineTerm = LineTerm.CRLF;");
echoLine(" }");
echoLine(" else {");
echo(" ");
echo(prefix );
echo("line++, ");
echo(prefix );
echoLine("column = 0;");
echo(" ");
echo(prefix);
echoLine("lineTerm = LineTerm.CR;");
echo(" c === ");
echo(prefix );
echo("cr ? (");
echo(prefix );
echo("line++, ");
echo(prefix );
echo("column = 0) : (");
echo(prefix );
echoLine("column += c > 0xff ? 2 : 1);");
echoLine(" }");
echoLine(" }");
echo(" else if(c === ");
echo(prefix );
echoLine("lf){");
echo(" ");
echo(prefix );
echo("line++, ");
echo(prefix );
echoLine("column = 0;");
echo(" ");
echo(prefix);
echoLine("lineTerm = LineTerm.LF;");
echoLine(" }");
echoLine(" else {");
echo(" ");
echo(prefix );
echoLine("column += c > 0xff ? 2 : 1;");
echo(" ");
echo(prefix );
echo("lastCR = c === ");
echo(prefix );
echoLine("cr;");
echoLine(" }");
echoLine(" break;");
echoLine(" }");
echo(" ");
echo(prefix );
echoLine("matched += String.fromCharCode(c);");
echo(" ");
echo(prefix );
echo("marker.state !== -1 && (");
echo(prefix );
echoLine("backupCount++);");
echo(" ");
echo(prefix );
echoLine("input.next();");
echoLine(" }");
echoLine(" /**");
echoLine(" * accept a character");
echoLine(" * @return - true if the character is consumed, false if not consumed");
echoLine(" * @api private");
echoLine(" * @internal");
echoLine(" */");
echo(" function ");
echo(prefix );
echo("acceptChar(ccode");
echo(ts(": number") );
echoLine("){");
echo(" var lexstate = ");
echo(prefix );
echo("lexState[");
echo(prefix );
echoLine("lexState.length - 1];");
echo(" var ltable = ");
echo(prefix );
echoLine("dfaTables[lexstate];");
echo(" var isEnd = ltable.isEnd[");
echo(prefix );
echoLine("state] === 1;");
echo(" var hasArc = ltable.hasArc[");
echo(prefix );
echoLine("state] === 1;");
echoLine(" // get the class of the given character");
echo(" var cl = ccode < ltable.maxAsicii ? ltable.classTable[ccode] : ");
echo(prefix );
echoLine("findUnicodeClass(ltable.unicodeClassTable, ccode);");
echoLine(" // find the next state to go");
echoLine(" var nstate = -1;");
echoLine(" if(cl !== -1){");
echo(" var ind = ltable.disnext[");
echo(prefix );
echoLine("state] + cl;");
echo(" if(ind >= 0 && ind < ltable.pnext.length && ltable.checknext[ind] === ");
echo(prefix );
echoLine("state){");
echoLine(" nstate = ltable.pnext[ind];");
echoLine(" }");
echoLine(" }");
echoLine(" if(isEnd){");
echoLine(" // if current state is a terminate state, be careful");
echoLine(" if(hasArc){");
echoLine(" if(nstate === -1){");
echoLine(" // nowhere to go, stay where we are");
echo(" ");
echo(prefix );
echo("doLexAction(lexstate, ");
echo(prefix );
echoLine("state);");
echoLine(" // recover");
echo(" ");
echo(prefix );
echoLine("marker.state = -1;");
echo(" ");
echo(prefix );
echoLine("backupCount = 0;");
echo(" ");
echo(prefix );
echoLine("state = 0; ");
echoLine(" // character not consumed");
echoLine(" }");
echoLine(" else {");
echoLine(" // now we can either go to that new state, or stay where we are");
echoLine(" // it is prefered to move forward, but that could lead to errors,");
echoLine(" // so we need to memorize this state before move on, in case if ");
echoLine(" // an error occurs later, we could just return to this state.");
echo(" ");
echo(prefix );
echoLine("mark();");
echo(" ");
echo(prefix );
echoLine("state = nstate;");
echo(" ");
echo(prefix );
echoLine("consume(ccode);");
echoLine(" // check for terminate state");
echo(" ");
echo(prefix );
echoLine("tryLexEnd();");
echoLine(" }");
echoLine(" }");
echoLine(" else {");
echoLine(" // current state doesn't lead to any state, just stay here.");
echo(" ");
echo(prefix );
echo("doLexAction(lexstate, ");
echo(prefix );
echoLine("state);");
echoLine(" // recover");
echo(" ");
echo(prefix );
echoLine("marker.state = -1;");
echo(" ");
echo(prefix );
echoLine("backupCount = 0;");
echo(" ");
echo(prefix );
echoLine("state = 0;");
echoLine(" // character not consumed");
echoLine(" }");
echoLine(" }");
echoLine(" else {");
echoLine(" if(nstate === -1){");
echoLine(" // nowhere to go at current state, error may have occured.");
echoLine(" // check marker to verify that");
echo(" if(");
echo(prefix );
echoLine("marker.state !== -1){");
echoLine(" // we have a previously marked state, which is a terminate state.");
echo(" ");
echo(prefix );
echoLine("rollback();");
echo(" ");
echo(prefix );
echo("doLexAction(lexstate, ");
echo(prefix );
echoLine("state);");
echo(" ");
echo(prefix );
echoLine("state = 0;");
echoLine(" // accept(s);");
echoLine(" // character not consumed");
echoLine(" }");
echoLine(" else {");
echoLine(" // error occurs");
echo(" ");
echo(prefix );
echo("emit('lexicalerror', String.fromCharCode(ccode), ");
echo(prefix );
echo("line, ");
echo(prefix );
echoLine("column);");
echoLine(" // force consume");
echo(" ");
echo(prefix );
echoLine("consume(ccode);");
echoLine(" // emit an error token");
echo(" ");
echo(prefix );
echoLine("prepareToken(1);");
echo(" ");
echo(prefix );
echo("tokenQueue.length > 0 && ");
echo(prefix );
echoLine("acceptToken(null);");
echo(" ");
echo(prefix );
echoLine("state = 0;");
echoLine(" }");
echoLine(" }");
echoLine(" else {");
echo(" ");
echo(prefix );
echoLine("state = nstate;");
echoLine(" // character consumed");
echo(" ");
echo(prefix );
echoLine("consume(ccode);");
echoLine(" // check for terminate state");
echo(" ");
echo(prefix );
echoLine("tryLexEnd();");
echoLine(" }");
echoLine(" }");
echoLine(" }");
echo(" function ");
echo(prefix );
echoLine("acceptEOF(){");
echo(" if(");
echo(prefix );
echoLine("state === 0){");
echoLine(" // recover");
echo(" ");
echo(prefix );
echoLine("prepareToken(0);");
echo(" ");
echo(prefix );
echoLine("acceptToken(null);");
echoLine(" return true;");
echoLine(" }");
echoLine(" else {");
echo(" var lexstate = ");
echo(prefix );
echo("lexState[");
echo(prefix );
echoLine("lexState.length - 1];");
echo(" var ltable = ");
echo(prefix );
echoLine("dfaTables[lexstate];");
echo(" var isEnd = ltable.isEnd[");
echo(prefix );
echoLine("state];");
echoLine(" if(isEnd){");
echo(" ");
echo(prefix );
echo("doLexAction(lexstate, ");
echo(prefix );
echoLine("state);");
echo(" ");
echo(prefix );
echoLine("state = 0;");
echo(" ");
echo(prefix );
echoLine("marker.state = -1;");
echoLine(" return false;");
echoLine(" }");
echo(" else if(");
echo(prefix );
echoLine("marker.state !== -1){");
echo(" ");
echo(prefix );
echoLine("rollback();");
echo(" ");
echo(prefix );
echo("doLexAction(lexstate, ");
echo(prefix );
echoLine("state);");
echo(" ");
echo(prefix );
echoLine("state = 0;");
echoLine(" return false;");
echoLine(" }");
echoLine(" else {");
echo(" ");
echo(prefix );
echo("emit('lexicalerror', '', ");
echo(prefix );
echo("line, ");
echo(prefix );
echoLine("column);");
echo(" ");
echo(prefix );
echoLine("prepareToken(1);");
echo(" ");
echo(prefix );
echo("tokenQueue.length > 0 && ");
echo(prefix );
echoLine("acceptToken(null);");
echo(" ");
echo(prefix );
echoLine("state = 0;");
echoLine(" return true;");
echoLine(" }");
echoLine(" }");
echoLine(" }");
echo(" function ");
echo(prefix );
echoLine("tryLexEnd(){");
echo(" var lexstate = ");
echo(prefix );
echo("lexState[");
echo(prefix );
echoLine("lexState.length - 1];");
echo(" var ltable = ");
echo(prefix );
echoLine("dfaTables[lexstate];");
echo(" var isEnd = ltable.isEnd[");
echo(prefix );
echoLine("state] === 1;");
echo(" var hasArc = ltable.hasArc[");
echo(prefix );
echoLine("state] === 1;");
echoLine(" if(isEnd && !hasArc){");
echo(" ");
echo(prefix );
echo("doLexAction(lexstate, ");
echo(prefix );
echoLine("state);");
echo(" ");
echo(prefix );
echoLine("marker.state = -1;");
echo(" ");
echo(prefix );
echoLine("backupCount = 0;");
echo(" ");
echo(prefix );
echoLine("state = 0;");
echoLine(" }");
echo(" }");
function printReduceActions(){
let codegen = {
raw(s: string){
echo(s);
},
beginBlock(pos: Position, always: boolean){
!always && echo(`if(${prefix}enableBlock)`);
echo('{');
},
endBlock(pos: Position){
echo('} ');
},
pushLexState(n: number){
echo(`${prefix}lexState.push(${n})`);
},
switchToLexState(n: number){
echo(`${prefix}lexState[${prefix}lexState.length - 1] = ${n}`);
},
popLexStat