/*
    generated by jscc, an LALR(1) parser generator made by hadroncfy.
    template for typescript, written by hadroncfy, aussi.
*/
<% for(let h of input.file.header){ %>
<%= 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; %>
var <%= prefix + tname + ts(': ' + type + '[]')%> = [ 

    <% echo(tab); %>
    <% for(let i of t){
        echo(leftAlign(mapper(i), align));
        echo(',');
        count++ >= lc && (count = 1, echo(input.endl + tab));
    } %>
]; 
<% } 
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}; }`;
    }
}
%>
/*
    constants
*/
var <%= prefix %>lf = '\n'.charCodeAt(0);
var <%= prefix %>cr = '\r'.charCodeAt(0);
<% if(ists){ %>
interface DFATable{
    pnext: number[];
    disnext: number[];
    checknext: number[];
    maxAsicii: number;
    classTable: number[];
    unicodeClassTable: number[];
    isEnd: number[];
    hasArc: number[];
};
<% } %>
<% 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');
%>
var <%= prefix %>lextable<%= String(n) + ts(': DFATable') %> = {
    pnext: <%= tn('lexpnext') %>,
    disnext: <%= tn('lexdisnext') %>,
    checknext: <%= tn('lexchecknext') %>,
    maxAsicii: <%= t.maxAsicii %>,
    classTable: <%= tn('lexclassTable') %>,
    unicodeClassTable: <%= tn('lexunicodeClassTable') %>,
    isEnd: <%= tn('lexisEnd') %>,
    hasArc: <%= tn('lexhasArc') %>
};
<% } %>
/*
    dfa table definations
*/
<% for(let i = 0, _a = dfaTables; i < _a.length; i++){
    printDFATable(_a[i], i);
} %>
/*
    dfa tables
*/
var <%= prefix %>dfaTables<%= ts(': DFATable[]') %> = [
<% for(let i = 0;i < dfaTables.length; i++){ %>
    <%= prefix %>lextable<%= i %>,
<% } %>
];
/*
    find unicode class
*/
function <%= prefix %>findUnicodeClass(uc<%= ts(": number[]") %>, c<%= ts(": number") %>){
    for(var i = 0; i < uc.length; i += 3){
        if(c >= uc[i + 1] && c <= uc[i + 2]){
            return uc[i];
        }
        else if(c < uc[i + 1]){
            return -1;
        }
    }
    return -1;
}
/*
    tokens that a lexical dfa state can return
*/
<% 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);
    });
} %>

<% let pt = input.pt; %>
var <%= prefix %>stateCount = <%= pt.stateCount %>;
var <%= prefix %>tokenCount = <%= input.file.grammar.tokens.length %>;
var <%= prefix %>actERR = <%= pt.stateCount + 1 %>;
/*
    compressed action table: action = <%= prefix %>pact[<%= prefix %>disact[STATE-NUM] + TOKEN]
    when action > 0, shift the token and goto state (action - 1);
    when action < 0, reduce with rule (1-action);
    when action = 0, do default action.
*/
<% 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();
    }
}); %>
/*
    displacement of action table.
*/
<% printTable<number>('disact', 'number', pt.disact, 6, 10, t => t.toString()); %>
/*
    used to check if a position in <%= prefix  %>pact is out of bouds.
    if <%= prefix  %>checkact[<%= prefix %>disact[STATE-NUM] + TOKEN] = STATE-NUM, this position is not out of bounds.
*/
<% printTable<number>('checkact', 'number', pt.checkact, 6, 10, t => t === undefined ? '0' : t.toString()); %>
/*
    default action table. action = <%= prefix %>defred[STATE-NUM],
    where action is the number of the rule to reduce with.
*/
<% printTable<number>('defred', 'number', pt.defred, 6, 10, t => t.toString()); %>
/*
    compressed goto table: goto = <%= prefix  %>pgoto[<%= prefix %>disgoto[STATE-NUM] + NON_TERMINAL]
*/
<% printTable<Item>('pgoto', 'number', pt.pgoto, 6, 10, t => {
    if(t === null){
        return '-1';
    }
    else {
        return t.shift.stateIndex.toString();
    }
}); %>
/*
    displacement of the goto table
*/
<% printTable<number>('disgoto', 'number', pt.disgoto, 6, 10, t => t.toString()); %>
/*
    length of each rule: rule length = <%= prefix %>ruleLen[RULE-NUM]
*/
<% printTable<Rule>('ruleLen', 'number', pt.g.rules, 6, 10, r => r.rhs.length.toString()); %>
/*
    index of the LHS of each rule
*/
<% printTable<Rule>('lhs', 'number', pt.g.rules, 6, 10, r => r.lhs.index.toString()); %>
/*
    token names
*/
<% printTable<TokenDef>('tokenNames', 'string', pt.g.tokens, 20, 3, t => `"${t.sym.replace(/"/g, '\\"')}"`); %>
/*
    token alias
*/
<% printTable<TokenDef>('tokenAlias', 'string', pt.g.tokens, 20, 3, t => t.alias ? `"${t.alias.replace(/"/g, '\\"')}"` : "null"); %>
<% let className = getOpt('className', 'Parser'); %>

<% 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'; %>
    function <%= prefix %>doLexAction<%= n %>(<%= statevn + ts(": number")%>){
        var <%= prefix %>tk = <%= prefix %>lexTokens<%= n %>[<%= statevn %>];
        <%= prefix %>tk !== -1 && <%= prefix %>prepareToken(<%= prefix %>tk);
        switch(<%= statevn %>){
    <% for(let i = 0, _a = dfa.states; i < _a.length; i++){ 
        if(_a[i].endAction !== null && _a[i].endAction.data.actions.length > 0){ %>
            case <%= i %>:
                <% echoLine(''); echo('                '); %>
                <% _a[i].endAction.data.toCode(codegen); %>
                break;
        <% }
    } %>
            default:;
        }
    }
<% } %>

<% if(ists){ %>
function tokenToString(tk: number){
    return <%= prefix %>tokenAlias[tk] === null ? `<${<%= prefix %>tokenNames[tk]}>` : `"${<%= prefix %>tokenAlias[tk]}"`;
}
<% } else { %>
function tokenToString(tk){
    return <%= prefix %>tokenAlias[tk] === null ? "<" + <%= prefix %>tokenNames[tk] + ">" : '"' + <%= prefix %>tokenAlias[tk] + '"';
}
<% } %>
function getExpectedTokens(state<%= ts(': number') %>)<%= ts(': number[]') %>{
        var dis = <%= prefix %>disact[state];
        var ret<%= ts(': number[]') %> = [];
        function expect(tk<%= ts(": number") %>){
            var ind = dis + tk;
            if(ind < 0 || ind >= <%= prefix %>pact.length || state !== <%= prefix %>checkact[ind]){
                return <%= prefix %>defred[state] !== -1;
            }
            else {
                return true;
            }
        }
        for(var tk = 0; tk < <%= prefix %>tokenCount; tk++){
            expect(tk) && ret.push(tk);
        }
        return ret;
}
// Token kinds
<%= 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++;
} %>
};
<% if(ists){ %>
class Token {
    constructor(
        public id: number,
        public val: string,
        public startLine: number,
        public startColumn: number,
        public endLine: number,
        public endColumn: number
    ){}
    clone(){
        return new Token(
            this.id,
            this.val,
            this.startLine,
            this.startColumn,
            this.endLine,
            this.endColumn
        );
    }
    toString(){
        return (<%= prefix %>tokenAlias[this.id] === null ? 
            `<${<%= prefix %>tokenNames[this.id]}>` :
            `"${<%= prefix %>tokenAlias[this.id]}"`) + `("${this.val}")`;
    }
}
interface <%= className %>{
    init(<%= n(input.file.initArg) %>);
    accept(s: string);
    end();
    parse(input: ParserInput | string): boolean;
    load(input: ParserInput | string);
    nextToken(): Token;

    setLineTerminator(lt: LineTerm);
    getLineTerminator(): LineTerm;
    halt();
    on(ent: 'lexicalerror', cb: (c: string, line: number, col: number) => any);
    on(ent: 'syntaxerror', cb: (t: Token, state: number) => any);
    on(ent: 'accept', cb: () => any);

    enableBlocks();
    disableBlocks();
    loadParserState(state: ParserState);
    getParserState(): ParserState;
}
<% } else { %>
function Token(id, val, startLine, startColumn, endLine, endColumn){
    this.id = id;
    this.val = val;
    this.startLine = startLine;
    this.startColumn = startColumn;
    this.endLine = endLine;
    this.endColumn = endColumn;
}
Token.prototype.clone = function(){
    return new Token(
        this.id,
        this.val,
        this.startLine,
        this.startColumn,
        this.endLine,
        this.endColumn
    );
}
Token.prototype.toString = function(){
    return (<%= prefix %>tokenAlias[this.id] === null ? 
        '<' + <%= prefix %>tokenNames[this.id] + '>' :
        '"' + <%= prefix %>tokenAlias[this.id] + '"') + "(" + this.val + ")";
}
<% } %>
<% let stype = n(input.file.sematicType, 'any'); %>
<% if(ists){ %>
interface ParserState {
    lexState: number[];
    lrState: number[];
    sematicS: <%= stype %>[];
};
interface ParserInput {
    current(): number;
    next();
    isEof(): boolean;
    backup(s: string);
};
enum LineTerm{
    NONE = 1,
    AUTO,
    CR,
    LF,
    CRLF
};
<% } else { %>
var LineTerm = {
    NONE: 1,
    AUTO: 2,
    CR: 3,
    LF: 4,
    CRLF: 5
};
<% } %>

function create<%= className %>()<%= ts(': ' + className) %> {
    //#region parser state variables
    var <%= prefix %>lexState<%= ts(": number[]") %>;
    var <%= prefix %>state<%= ts(": number") %>;
    var <%= prefix %>lastCR<%= ts(': boolean') %>;
    var <%= prefix %>matched<%= ts(": string") %>;
    var <%= prefix %>marker<%= ts(": { state: number, line: number, column: number }") %> = { state: -1, line: 0, column: 0 };
    var <%= prefix %>backupCount<%= ts(": number") %>;
    var <%= prefix %>line<%= ts(": number") %>;
    var <%= prefix %>column<%= ts(": number") %>;
    var <%= prefix %>tline<%= ts(": number") %>;
    var <%= prefix %>tcolumn<%= ts(": number") %>;

    var <%= prefix %>lrState<%= ts(": number[]") %>;
    var <%= prefix %>sematicS<%= ts(': ' + stype + '[]') %>;
    //#endregion

    var <%= prefix %>input<%= ts(': ParserInput') %>;
    var <%= prefix %>sematicVal<%= ts(': ' + stype) %>;
    var <%= prefix %>tokenQueue<%= ts(": Token[]") %>;
    var <%= prefix %>token<%= ts(": Token") %>;
    var <%= prefix %>stop<%= ts(': boolean') %>;
    var <%= prefix %>tokenEmitted<%= ts(': boolean') %>;
    var <%= prefix %>enableBlock<%= ts(': boolean') %> = true;
    var <%= prefix %>lineTerm<%= ts(': LineTerm') %>;

    var <%= prefix %>handlers<%= ts(": {[s: string]: ((a1?, a2?, a3?) => any)[]}") %> = {};

    // extra members, defined by %extra_arg
    <%= n(input.file.extraArgs) %>

<% if(ists) { %>
    return {
        init,
        on,
        setLineTerminator,
        getLineTerminator: () => <%= prefix %>lineTerm,
        accept,
        end,
        load,
        parse,
        nextToken,
        halt,
        enableBlocks,
        disableBlocks,
        loadParserState,
        getParserState
    };
<% } else { %>
    return {
        init: init,
        on: on,
        setLineTerminator: setLineTerminator,
        getLineTerminator: function() { return <%= prefix %>lineTerm; },
        accept: accept,
        end: end,
        load: load,
        nextToken: nextToken,
        halt: halt,
        enableBlocks: enableBlocks,
        disableBlocks: disableBlocks,
        loadParserState: loadParserState,
        getParserState: getParserState,
        parse: parse
    };
<% } %>
    function init(<%= n(input.file.initArg) %>){
        <%= prefix %>lexState = [ 0 ];// DEFAULT
        <%= prefix %>state = 0;
        <%= prefix %>matched = '';
        <%= prefix %>token = new Token(-1, null, 0, 0, 0, 0);
        <%= prefix %>marker.state = -1;
        <%= prefix %>backupCount = 0;
        <%= prefix %>line = <%= prefix %>tline = 0;
        <%= prefix %>column = <%= prefix %>tcolumn = 0;
        
        <%= prefix %>lrState = [ 0 ];
        <%= prefix %>sematicS = [];
        <%= prefix %>sematicVal = null;
        <%= prefix %>tokenQueue = [];

        <%= prefix %>lineTerm = LineTerm.AUTO;
        <%= prefix %>lastCR = false;

        <%= n(input.file.initBody) %>

        <%= prefix %>tryReduce();
    }
    function load(input<%= ts(': ParserInput | string') %>){
        if(typeof input === 'string'){
            var i = 0;
            var s<%= ts(': string') %> = input;
            <%= prefix %>input = {
                current: <%= lambda('()', 'i < s.length ? s.charCodeAt(i) : null') %>,
                next: <%= lambda('()', 'i++') %>,
                isEof: <%= lambda('()', 'i >= s.length') %>,
                backup: <%= lambda('t', 'i -= t.length') %>
            }
        }
        else {
            <%= prefix %>input = input;
        }
    }
    function nextToken()<%= ts(': Token') %>{
        <%= prefix %>tokenEmitted = false;
        while(!<%= prefix %>stop && !<%= prefix %>tokenEmitted){
            var c = <%= prefix %>input.current();
            if(c !== null){
                <%= prefix %>acceptChar(c);
            }
            // null means end of file or no input available at present
            else if(<%= prefix %>input.isEof()){
                if(<%= prefix %>acceptEOF()){
                    break;
                }
            }
            else {
                return null;
            }
        }
        return <%= prefix %>token;
    }
    function setLineTerminator(lt<%= ts(': LineTerm') %>){
        <%= prefix %>lineTerm = lt;
    }
    function enableBlocks(){
        <%= prefix %>enableBlock = true;
    }
    function disableBlocks(){
        <%= prefix %>enableBlock = false;
    }
    /**
     *  input a string
     *  @api public
     *  @deprecated
     */
    function accept(s<%= ts(": string") %>){
        var i = 0;
        load({
            current: <%= lambda('()', 'i < s.length ? s.charCodeAt(i) : null') %>,
            next: <%= lambda('()', 'i++') %>,
            isEof: <%= lambda('()', 'i >= s.length') %>,
            backup: <%= lambda('t', 'i -= t.length') %>
        });
        while(!<%= prefix %>stop && nextToken().id !== 0);
    }
    /**
     *  tell the compiler that end of file is reached
     *  @api public
     */
    function end(){
        
    }
    function parse(input<%= ts(': ParserInput | string') %>)<%= ts(": boolean") %> {
        load(input);
        var t;
        while(!<%= prefix %>stop){
            t = nextToken();
            if(t === null){
                return false;
            }
            else if(t.id === 0){
                return true;
            }
        }
        return true;
    }
    function halt(){
        <%= prefix %>stop = true;
    }
    function loadParserState(state<%= ts(': ParserState') %>){
        <%= prefix %>lexState = state.lexState;
        <%= prefix %>lrState = state.lrState;
        <%= prefix %>sematicS = state.sematicS;
    }
    function getParserState()<%= ts(': ParserState') %> {
        return {
            lexState: <%= prefix %>lexState,
            lrState: <%= prefix %>lrState,
            sematicS: <%= prefix %>sematicS
        };
    }
<% if(input.file.tokenHookArg){ %>
    function <%= prefix %>tokenHook(<%= input.file.tokenHookArg.val + ts(': Token')%>)<%= ts(': boolean') %>{
        <%= input.file.tokenHookBody.val %>
    }
<% } %>
    /**
     *  set 
     */
    function <%= prefix %>setImg(s<%= ts(": string") %>){
        <%= prefix %>matched = s;
        <%= prefix %>tline = <%= prefix %>line;
        <%= prefix %>tcolumn = <%= prefix %>column;
    }
    function <%= prefix %>prepareToken(tid<%= ts(": number") %>){
        <%= prefix %>token.id = tid;
        <%= prefix %>token.val = <%= prefix %>matched;
        <%= prefix %>token.startLine = <%= prefix %>tline;
        <%= prefix %>token.startColumn = <%= prefix %>tcolumn;
        <%= prefix %>token.endLine = <%= prefix %>line;
        <%= prefix %>token.endColumn = <%= prefix %>column - 1;

<% if(input.file.tokenHookArg){ %>
        if(!<%= prefix %>tokenHook(<%= prefix %>token)){
            <%= prefix %>tokenQueue.push(<%= prefix %>token);
        }
<% } else { %>
        <%= prefix %>tokenQueue.push(<%= prefix %>token);
<% } %>

        <%= prefix %>tokenEmitted = true;
        <%= prefix %>matched = '';
        <%= prefix %>tline = <%= prefix %>line;
        <%= prefix %>tcolumn = <%= prefix %>column;
    }
    function <%= prefix %>emit(name<%= ts(": string") + ts(", a1?, a2?, a3?", ", a1, a2, a3") %>){
        var cbs = <%= prefix %>handlers[name];
        if(cbs){
            for(var i = 0; i < cbs.length; i++){
                cbs[i](a1, a2, a3);
            }
        }
    }
    function on(name<%= ts(": string") %>, cb<%= ts(": (a1?, a2?, a3?) => any") %>){
        <%= prefix %>handlers[name] || (<%= prefix %>handlers[name] = []);
        <%= prefix %>handlers[name].push(cb);
    }
<% for(let i = 0, _a = dfaTables; i < _a.length; i++){
    printLexActionsFunc(_a[i], i);
} %>
    /**
     *  do a lexical action
     *  @api private
     *  @internal
     */
    function <%= prefix %>doLexAction(lexstate<%= ts(": number") %>, state<%= ts(": number") %>){
        switch(lexstate){
<% for(let i = 0;i < dfaTables.length;i++){ %>
            case <%= i %>:
                <%= prefix %>doLexAction<%= i %>(state);
                break;
<% } %>
            default:;
        }
        <%= prefix %>tokenQueue.length > 0 && <%= prefix %>acceptToken(null);
    }
    function <%= prefix %>rollback(){
        var ret = <%= prefix %>matched.substr(<%= prefix %>matched.length - <%= prefix %>backupCount, <%= prefix %>backupCount);
        <%= prefix %>input.backup(ret);
        <%= prefix %>matched = <%= prefix %>matched.substr(0, <%= prefix %>matched.length - <%= prefix %>backupCount);
        <%= prefix %>backupCount = 0;
        <%= prefix %>line = <%= prefix %>marker.line;
        <%= prefix %>column = <%= prefix %>marker.column;
        <%= prefix %>state = <%= prefix %>marker.state;
        <%= prefix %>marker.state = -1;
    }
    function <%= prefix %>mark(){
        <%= prefix %>marker.state = <%= prefix %>state;
        <%= prefix %>marker.line = <%= prefix %>line;
        <%= prefix %>marker.column = <%= prefix %>column;
        <%= prefix %>backupCount = 0;
    }
    function <%= prefix %>consume(c<%= ts(": number") %>){
        // c === <%= prefix %>eol ? (<%= prefix %>line++, <%= prefix %>column = 0) : (<%= prefix %>column += c > 0xff ? 2 : 1);
        switch(<%= prefix%>lineTerm){
            case LineTerm.NONE:
                <%= prefix %>column += c > 0xff ? 2 : 1;
                break;
            case LineTerm.CR:
                c === <%= prefix %>cr ? (<%= prefix %>line++, <%= prefix %>column = 0) : (<%= prefix %>column += c > 0xff ? 2 : 1);
                break;
            case LineTerm.LF:
                c === <%= prefix %>lf ? (<%= prefix %>line++, <%= prefix %>column = 0) : (<%= prefix %>column += c > 0xff ? 2 : 1);
                break;
            case LineTerm.CRLF:
                if(<%= prefix %>lastCR){
                    if(c === <%= prefix %>lf){
                        <%= prefix %>line++, <%= prefix %>column = 0;
                        <%= prefix %>lastCR = false;
                    }
                    else {
                        <%= prefix %>column += c > 0xff ? 2 : 1;
                        <%= prefix %>lastCR = c === <%= prefix %>cr;
                    }
                }
                else {
                    <%= prefix %>column += c > 0xff ? 2 : 1;
                    <%= prefix %>lastCR = c === <%= prefix %>cr;
                }
                break;
            case LineTerm.AUTO:
                if(<%= prefix %>lastCR){
                    if(c === <%= prefix %>lf){
                        <%= prefix %>line++, <%= prefix %>column = 0;
                        <%= prefix %>lastCR = false;
                        <%= prefix%>lineTerm = LineTerm.CRLF;
                    }
                    else {
                        <%= prefix %>line++, <%= prefix %>column = 0;
                        <%= prefix%>lineTerm = LineTerm.CR;
                        c === <%= prefix %>cr ? (<%= prefix %>line++, <%= prefix %>column = 0) : (<%= prefix %>column += c > 0xff ? 2 : 1);
                    }
                }
                else if(c === <%= prefix %>lf){
                    <%= prefix %>line++, <%= prefix %>column = 0;
                    <%= prefix%>lineTerm = LineTerm.LF;
                }
                else {
                    <%= prefix %>column += c > 0xff ? 2 : 1;
                    <%= prefix %>lastCR = c === <%= prefix %>cr;
                }
                break;
        }
        <%= prefix %>matched += String.fromCharCode(c);
        <%= prefix %>marker.state !== -1 && (<%= prefix %>backupCount++);
        <%= prefix %>input.next();
    }
    /**
     *  accept a character
     *  @return - true if the character is consumed, false if not consumed
     *  @api private
     *  @internal
     */
    function <%= prefix %>acceptChar(ccode<%= ts(": number") %>){
        var lexstate = <%= prefix %>lexState[<%= prefix %>lexState.length - 1];
        var ltable = <%= prefix %>dfaTables[lexstate];
        var isEnd = ltable.isEnd[<%= prefix %>state] === 1;
        var hasArc = ltable.hasArc[<%= prefix %>state] === 1;
        // get the class of the given character
        var cl = ccode < ltable.maxAsicii ? ltable.classTable[ccode] : <%= prefix %>findUnicodeClass(ltable.unicodeClassTable, ccode);
        // find the next state to go
        var nstate = -1;
        if(cl !== -1){
            var ind = ltable.disnext[<%= prefix %>state] + cl;
            if(ind >= 0 && ind < ltable.pnext.length && ltable.checknext[ind] === <%= prefix %>state){
                nstate = ltable.pnext[ind];
            }
        }
        if(isEnd){
            // if current state is a terminate state, be careful
            if(hasArc){
                if(nstate === -1){
                    // nowhere to go, stay where we are
                    <%= prefix %>doLexAction(lexstate, <%= prefix %>state);
                    // recover
                    <%= prefix %>marker.state = -1;
                    <%= prefix %>backupCount = 0;
                    <%= prefix %>state = 0;                    
                    // character not consumed
                }
                else {
                    // now we can either go to that new state, or stay where we are
                    // it is prefered to move forward, but that could lead to errors,
                    // so we need to memorize this state before move on, in case if 
                    // an error occurs later, we could just return to this state.
                    <%= prefix %>mark();
                    <%= prefix %>state = nstate;
                    <%= prefix %>consume(ccode);
                    // check for terminate state
                    <%= prefix %>tryLexEnd();
                }
            }
            else {
                // current state doesn't lead to any state, just stay here.
                <%= prefix %>doLexAction(lexstate, <%= prefix %>state);
                // recover
                <%= prefix %>marker.state = -1;
                <%= prefix %>backupCount = 0;
                <%= prefix %>state = 0;
                // character not consumed
            }
        }
        else {
            if(nstate === -1){
                // nowhere to go at current state, error may have occured.
                // check marker to verify that
                if(<%= prefix %>marker.state !== -1){
                    // we have a previously marked state, which is a terminate state.
                    <%= prefix %>rollback();
                    <%= prefix %>doLexAction(lexstate, <%= prefix %>state);
                    <%= prefix %>state = 0;
                    // accept(s);
                    // character not consumed
                }
                else {
                    // error occurs
                    <%= prefix %>emit('lexicalerror', String.fromCharCode(ccode), <%= prefix %>line, <%= prefix %>column);
                    // force consume
                    <%= prefix %>consume(ccode);
                    // emit an error token
                    <%= prefix %>prepareToken(1);
                    <%= prefix %>tokenQueue.length > 0 && <%= prefix %>acceptToken(null);
                    <%= prefix %>state = 0;
                }
            }
            else {
                <%= prefix %>state = nstate;
                // character consumed
                <%= prefix %>consume(ccode);
                // check for terminate state
                <%= prefix %>tryLexEnd();
            }
        }
    }
    function <%= prefix %>acceptEOF(){
        if(<%= prefix %>state === 0){
            // recover
            <%= prefix %>prepareToken(0);
            <%= prefix %>acceptToken(null);
            return true;
        }
        else {
            var lexstate = <%= prefix %>lexState[<%= prefix %>lexState.length - 1];
            var ltable = <%= prefix %>dfaTables[lexstate];
            var isEnd = ltable.isEnd[<%= prefix %>state];
            if(isEnd){
                <%= prefix %>doLexAction(lexstate, <%= prefix %>state);
                <%= prefix %>state = 0;
                <%= prefix %>marker.state = -1;
                return false;
            }
            else if(<%= prefix %>marker.state !== -1){
                <%= prefix %>rollback();
                <%= prefix %>doLexAction(lexstate, <%= prefix %>state);
                <%= prefix %>state = 0;
                return false;
            }
            else {
                <%= prefix %>emit('lexicalerror', '', <%= prefix %>line, <%= prefix %>column);
                <%= prefix %>prepareToken(1);
                <%= prefix %>tokenQueue.length > 0 && <%= prefix %>acceptToken(null);
                <%= prefix %>state = 0;
                return true;
            }
        }
    }
    function <%= prefix %>tryLexEnd(){
        var lexstate = <%= prefix %>lexState[<%= prefix %>lexState.length - 1];
        var ltable = <%= prefix %>dfaTables[lexstate];
        var isEnd = ltable.isEnd[<%= prefix %>state] === 1;
        var hasArc = ltable.hasArc[<%= prefix %>state] === 1;
        if(isEnd && !hasArc){
            <%= prefix %>doLexAction(lexstate, <%= prefix %>state);
            <%= prefix %>marker.state = -1;
            <%= prefix %>backupCount = 0;
            <%= prefix %>state = 0;
        }
    }
<% 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){ %>
            case <%= rule.index %>:
                /* <%= rule.toString() %> */
            <% for(let uvar in rule.vars){ %>
                var <%= uvar %> = <%= prefix %>sematicS[<%= prefix %>sp - <%= rule.rhs.length - rule.vars[uvar].val %>];
            <% }
            for(let uvar2 in rule.usedVars){ %>
                var <%= uvar2 %> = <%= prefix %>sematicS[<%= prefix %>sp - <%= rule.usedVars[uvar2].val %>];
            <% }
            echoLine('');
            echo('                ');
            rule.action.toCode(codegen); %>
                break;
        <% }
    }
} %>
    function <%= prefix %>doReduction(<%= prefix %>rulenum<%= ts(": number") %>){
        var <%= prefix %>nt = <%= prefix %>lhs[<%= prefix %>rulenum];
        var <%= prefix %>sp = <%= prefix %>sematicS.length;
        var <%= prefix %>top = <%= prefix %>sematicS[<%= prefix %>sp - <%= prefix %>ruleLen[<%= prefix %>rulenum]] || null;
        switch(<%= prefix %>rulenum){
<% printReduceActions(); %>
        }
        <%= prefix %>lrState.length -= <%= prefix %>ruleLen[<%= prefix %>rulenum];
        var <%= prefix %>cstate = <%= prefix %>lrState[<%= prefix %>lrState.length - 1];
        <%= prefix %>lrState.push(<%= prefix %>pgoto[<%= prefix %>disgoto[<%= prefix %>cstate] + <%= prefix %>nt]);

        <%= prefix %>sematicS.length -= <%= prefix %>ruleLen[<%= prefix %>rulenum];
        <%= prefix %>sematicS.push(<%= prefix %>top);
    }
    function <%= prefix %>acceptToken(tk<%= ts(": Token") %>){
        // look up action table
        var shifted = false;
        tk !== null && <%= prefix %>tokenQueue.push(tk);
        while(!<%= prefix %>stop && <%= prefix %>tokenQueue.length > 0){
            var t = <%= prefix %>tokenQueue[0];
            var cstate = <%= prefix %>lrState[<%= prefix %>lrState.length - 1];
            var ind = <%= prefix %>disact[cstate] + t.id;
            var act = 0;
            if(ind < 0 || ind >= <%= prefix %>pact.length || <%= prefix %>checkact[ind] !== cstate){
                act = -<%= prefix %>defred[cstate] - 1;
            }
            else {
                act = <%= prefix %>pact[ind];
            }
            if(act === <%= prefix %>actERR){
                // explicit error
                <%= prefix %>syntaxError(t);
                <%= prefix %>tokenQueue.shift();
            }
            else if(act > 0){
                // shift
                if(t.id === 0){
                    // end of file
                    <%= prefix %>stop = true;
                    <%= prefix %>emit('accept');
                    <%= prefix %>tokenQueue.shift();
                }
                else {
                    <%= prefix %>lrState.push(act - 1);
                    <%= prefix %>sematicS.push(<%= prefix %>sematicVal);
                    <%= prefix %>sematicVal = null;
                    <%= prefix %>tryReduce();
                    // token consumed
                    <%= prefix %>tokenQueue.shift();
                }
            }
            else if(act < 0){
                <%= prefix %>doReduction(-act - 1);
                <%= prefix %>tryReduce();
            }
            else {
                // error
                <%= prefix %>syntaxError(t);
                // force consume
                <%= prefix %>tokenQueue.shift();
            }
        }
    }
    function <%= prefix %>tryReduce(){
        var cstate = <%= prefix %>lrState[<%= prefix %>lrState.length - 1];
        var act;
        while(<%= prefix %>disact[cstate] === -<%= prefix %>tokenCount && (act = <%= prefix %>defred[cstate]) !== -1){
            <%= prefix %>doReduction(act);
            cstate = <%= prefix %>lrState[<%= prefix %>lrState.length - 1];
        }
    }
    function <%= prefix %>syntaxError(t<%= ts(": Token") %>){
        <%= prefix %>emit("syntaxerror", t, <%= prefix %>lrState[<%= prefix %>lrState.length - 1]);
    }
}
<%= n(input.file.epilogue) %>