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}`);
        },
        popLexState(){
            echo(`${prefix}lexState.pop()`);
        },
        setImg(n: string){
            echo(`${prefix}setImg("${n}")`);
        },
        tokenObj(){
            echo(prefix + 'token');
        }, // $token
        matched(){
            echo(prefix + 'matched');
        },
        lhs(){
            echo(prefix + "top");
        }, // $$
        emitToken(tid: number){
            echo(`${prefix}tokenQueue.push(new Token(${tid}, null, -1, 0, 0, 0))`);
        }
    };
    for(let rule of input.file.grammar.rules){
        if(rule.action !== null){ 
    echoLine("");
    echo("            case ");
    echo(rule.index );
    echoLine(":");
    echo("                /* ");
    echo(rule.toString() );
    echo(" */");
    for(let uvar in rule.vars){ 
    echoLine("");
    echo("                var ");
    echo(uvar );
    echo(" = ");
    echo(prefix );
    echo("sematicS[");
    echo(prefix );
    echo("sp - ");
    echo(rule.rhs.length - rule.vars[uvar].val );
    echo("];");
    }
            for(let uvar2 in rule.usedVars){ 
    echoLine("");
    echo("                var ");
    echo(uvar2 );
    echo(" = ");
    echo(prefix );
    echo("sematicS[");
    echo(prefix );
    echo("sp - ");
    echo(rule.usedVars[uvar2].val );
    echo("];");
    }
            echoLine('');
            echo('                ');
            rule.action.toCode(codegen); 
    echoLine("");
    echo("                break;");
    }
    }
} 
    echoLine("");
    echo("    function ");
    echo(prefix );
    echo("doReduction(");
    echo(prefix );
    echo("rulenum");
    echo(ts(": number") );
    echoLine("){");
    echo("        var ");
    echo(prefix );
    echo("nt = ");
    echo(prefix );
    echo("lhs[");
    echo(prefix );
    echoLine("rulenum];");
    echo("        var ");
    echo(prefix );
    echo("sp = ");
    echo(prefix );
    echoLine("sematicS.length;");
    echo("        var ");
    echo(prefix );
    echo("top = ");
    echo(prefix );
    echo("sematicS[");
    echo(prefix );
    echo("sp - ");
    echo(prefix );
    echo("ruleLen[");
    echo(prefix );
    echoLine("rulenum]] || null;");
    echo("        switch(");
    echo(prefix );
    echo("rulenum){");
    printReduceActions(); 
    echoLine("");
    echoLine("        }");
    echo("        ");
    echo(prefix );
    echo("lrState.length -= ");
    echo(prefix );
    echo("ruleLen[");
    echo(prefix );
    echoLine("rulenum];");
    echo("        var ");
    echo(prefix );
    echo("cstate = ");
    echo(prefix );
    echo("lrState[");
    echo(prefix );
    echoLine("lrState.length - 1];");
    echo("        ");
    echo(prefix );
    echo("lrState.push(");
    echo(prefix );
    echo("pgoto[");
    echo(prefix );
    echo("disgoto[");
    echo(prefix );
    echo("cstate] + ");
    echo(prefix );
    echoLine("nt]);");
    echoLine("");
    echo("        ");
    echo(prefix );
    echo("sematicS.length -= ");
    echo(prefix );
    echo("ruleLen[");
    echo(prefix );
    echoLine("rulenum];");
    echo("        ");
    echo(prefix );
    echo("sematicS.push(");
    echo(prefix );
    echoLine("top);");
    echoLine("    }");
    echo("    function ");
    echo(prefix );
    echo("acceptToken(tk");
    echo(ts(": Token") );
    echoLine("){");
    echoLine("        // look up action table");
    echoLine("        var shifted = false;");
    echo("        tk !== null && ");
    echo(prefix );
    echoLine("tokenQueue.push(tk);");
    echo("        while(!");
    echo(prefix );
    echo("stop && ");
    echo(prefix );
    echoLine("tokenQueue.length > 0){");
    echo("            var t = ");
    echo(prefix );
    echoLine("tokenQueue[0];");
    echo("            var cstate = ");
    echo(prefix );
    echo("lrState[");
    echo(prefix );
    echoLine("lrState.length - 1];");
    echo("            var ind = ");
    echo(prefix );
    echoLine("disact[cstate] + t.id;");
    echoLine("            var act = 0;");
    echo("            if(ind < 0 || ind >= ");
    echo(prefix );
    echo("pact.length || ");
    echo(prefix );
    echoLine("checkact[ind] !== cstate){");
    echo("                act = -");
    echo(prefix );
    echoLine("defred[cstate] - 1;");
    echoLine("            }");
    echoLine("            else {");
    echo("                act = ");
    echo(prefix );
    echoLine("pact[ind];");
    echoLine("            }");
    echo("            if(act === ");
    echo(prefix );
    echoLine("actERR){");
    echoLine("                // explicit error");
    echo("                ");
    echo(prefix );
    echoLine("syntaxError(t);");
    echo("                ");
    echo(prefix );
    echoLine("tokenQueue.shift();");
    echoLine("            }");
    echoLine("            else if(act > 0){");
    echoLine("                // shift");
    echoLine("                if(t.id === 0){");
    echoLine("                    // end of file");
    echo("                    ");
    echo(prefix );
    echoLine("stop = true;");
    echo("                    ");
    echo(prefix );
    echoLine("emit('accept');");
    echo("                    ");
    echo(prefix );
    echoLine("tokenQueue.shift();");
    echoLine("                }");
    echoLine("                else {");
    echo("                    ");
    echo(prefix );
    echoLine("lrState.push(act - 1);");
    echo("                    ");
    echo(prefix );
    echo("sematicS.push(");
    echo(prefix );
    echoLine("sematicVal);");
    echo("                    ");
    echo(prefix );
    echoLine("sematicVal = null;");
    echo("                    ");
    echo(prefix );
    echoLine("tryReduce();");
    echoLine("                    // token consumed");
    echo("                    ");
    echo(prefix );
    echoLine("tokenQueue.shift();");
    echoLine("                }");
    echoLine("            }");
    echoLine("            else if(act < 0){");
    echo("                ");
    echo(prefix );
    echoLine("doReduction(-act - 1);");
    echo("                ");
    echo(prefix );
    echoLine("tryReduce();");
    echoLine("            }");
    echoLine("            else {");
    echoLine("                // error");
    echo("                ");
    echo(prefix );
    echoLine("syntaxError(t);");
    echoLine("                // force consume");
    echo("                ");
    echo(prefix );
    echoLine("tokenQueue.shift();");
    echoLine("            }");
    echoLine("        }");
    echoLine("    }");
    echo("    function ");
    echo(prefix );
    echoLine("tryReduce(){");
    echo("        var cstate = ");
    echo(prefix );
    echo("lrState[");
    echo(prefix );
    echoLine("lrState.length - 1];");
    echoLine("        var act;");
    echo("        while(");
    echo(prefix );
    echo("disact[cstate] === -");
    echo(prefix );
    echo("tokenCount && (act = ");
    echo(prefix );
    echoLine("defred[cstate]) !== -1){");
    echo("            ");
    echo(prefix );
    echoLine("doReduction(act);");
    echo("            cstate = ");
    echo(prefix );
    echo("lrState[");
    echo(prefix );
    echoLine("lrState.length - 1];");
    echoLine("        }");
    echoLine("    }");
    echo("    function ");
    echo(prefix );
    echo("syntaxError(t");
    echo(ts(": Token") );
    echoLine("){");
    echo("        ");
    echo(prefix );
    echo("emit(\"syntaxerror\", t, ");
    echo(prefix );
    echo("lrState[");
    echo(prefix );
    echoLine("lrState.length - 1]);");
    echoLine("    }");
    echoLine("}");
    echo(n(input.file.epilogue) );

}