UNPKG

tscc-compiler

Version:

An LALR(1) compiler compiler written in Typescript

1,678 lines (1,675 loc) 57.5 kB
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