##HEADER##

function ##PREFIX##dbg_print(text) {
    ##PREFIX##dbg_string += text + '\n';
}

function ##PREFIX##lex(info) {
    var state = 0,
        match = -1,
        match_pos = 0,
        start = 0,
        pos = info.offset + 1;

    do {
        pos -= 1;
        state = 0;
        match = -2;
        start = pos;

        if (info.src.length <= start) {
            return ##EOF##;
        }

        do {
            ##DFA##
            pos += 1;

        } while (state > -1);

    } while (##WHITESPACE## > -1 && match === ##WHITESPACE##);

    if (match > -1) {
        info.att = info.src.substr(start, match_pos - start);
        info.offset = match_pos;

        ##TERMINAL_ACTIONS##
    } else {
        info.att = '';
        match = -1;
    }

    return match;
}


function ##PREFIX##parse(src, err_off, err_la) {
    var i, act, go, la, rval, rvstack, rsstack, undef,
        act_tab, pop_tab, goto_tab, labels,
        sstack = [],
        vstack = [],
        err_cnt = 0,
        info = {};

    ##TABLES##

    ##LABELS##

    info.offset = 0;
    info.src = src;
    info.att = '';

    if (!err_off) {
        err_off	= [];
    }

    if (!err_la) {
        err_la = [];
    }

    sstack.push(0);
    vstack.push(0);

    la = ##PREFIX##lex(info);

    while (true) {
        act = ##ERROR##;

        for (i = 0; i < act_tab[sstack[sstack.length - 1]].length; i += 2) {
            if (act_tab[sstack[sstack.length - 1]][i] === la) {
                act = act_tab[sstack[sstack.length - 1]][i + 1];
                break;
            }
        }

        if (##PREFIX##dbg_withtrace && sstack.length > 0) {
            ##PREFIX##dbg_print('\nState ' + sstack[sstack.length - 1] + '\n' +
                '\tLookahead: ' + labels[la] + ' (\'' + info.att + '\')\n' +
                '\tAction: ' + act + '\n' +
                '\tSource: \'' + info.src.substr(info.offset, 30) +
                ((info.offset + 30 < info.src.length) ? '...' : '') +
                '\'\n' + '\tStack: ' + sstack.join() + '\n' +
                '\tValue stack: ' + vstack.join() + '\n');
        }

        //Panic-mode: Try recovery when parse-error occurs!
        if (act === ##ERROR##) {
            if (##PREFIX##dbg_withtrace) {
                ##PREFIX##dbg_print('Error detected: There is no reduce or shift on the symbol ' + labels[la]);
            }

            err_cnt += 1;
            err_off.push(info.offset - info.att.length);
            err_la.push([]);
            for (i = 0; i < act_tab[sstack[sstack.length - 1]].length; i += 2) {
                err_la[err_la.length - 1].push(labels[act_tab[sstack[sstack.length - 1]][i]]);
            }

            //Remember the original stack!
            rsstack = [];
            rvstack = [];
            for (i = 0; i < sstack.length; i++) {
                rsstack[i] = sstack[i];
                rvstack[i] = vstack[i];
            }

            while (act === ##ERROR## && la !== ##EOF##) {
                if (##PREFIX##dbg_withtrace) {
                    ##PREFIX##dbg_print('\tError recovery\n' +
                        'Current lookahead: ' + labels[la] + ' (' + info.att + ')\n' +
                        'Action: ' + act + '\n\n');
                }

                if (la === -1) {
                    info.offset += 1;
                }

                while (act === ##ERROR## && sstack.length > 0) {
                    sstack.pop();
                    vstack.pop();

                    if (sstack.length === 0) {
                        break;
                    }

                    act = ##ERROR##;

                    for (i = 0; i < act_tab[sstack[sstack.length - 1]].length; i += 2) {
                        if (act_tab[sstack[sstack.length - 1]][i] === la) {
                            act = act_tab[sstack[sstack.length - 1]][i + 1];
                            break;
                        }
                    }
                }

                if (act !== ##ERROR##) {
                    break;
                }

                for (i = 0; i < rsstack.length; i++) {
                    sstack.push(rsstack[i]);
                    vstack.push(rvstack[i]);
                }

                la = ##PREFIX##lex(info);
            }

            if (act === ##ERROR##) {
                if (##PREFIX##dbg_withtrace) {
                    ##PREFIX##dbg_print('\tError recovery failed, terminating parse process...');
                }
                break;
            }


            if (##PREFIX##dbg_withtrace) {
                ##PREFIX##dbg_print('\tError recovery succeeded, continuing');
            }
        }

        //Shift
        if (act > 0) {
            if (##PREFIX##dbg_withtrace) {
                ##PREFIX##dbg_print('Shifting symbol: ' + labels[la] + ' (' + info.att + ')');
            }

            sstack.push(act);
            vstack.push(info.att);

            la = ##PREFIX##lex(info);

            if (##PREFIX##dbg_withtrace) {
                ##PREFIX##dbg_print('\tNew lookahead symbol: ' + labels[la] + ' (' + info.att + ')');
            }
        //Reduce
        } else {
            act *= -1;

            if (##PREFIX##dbg_withtrace) {
                ##PREFIX##dbg_print('Reducing by producution: ' + act);
            }

            rval = undef;

            if (##PREFIX##dbg_withtrace) {
                ##PREFIX##dbg_print('\tPerforming semantic action...');
            }

            ##ACTIONS##

            if (##PREFIX##dbg_withtrace) {
                ##PREFIX##dbg_print('\tPopping ' + pop_tab[act][1] + ' off the stack...');
            }

            for (i = 0; i < pop_tab[act][1]; i++) {
                sstack.pop();
                str = vstack.pop();
            }

            go = -1;
            for (i = 0; i < goto_tab[sstack[sstack.length - 1]].length; i += 2) {
                if (goto_tab[sstack[sstack.length - 1]][i] === pop_tab[act][0]) {
                    go = goto_tab[sstack[sstack.length - 1]][i + 1];
                    break;
                }
            }

            if (act === 0) {
                break;
            }

            if (##PREFIX##dbg_withtrace) {
                ##PREFIX##dbg_print('\tPushing non-terminal ' + labels[pop_tab[act][0]]);
            }

            sstack.push(go);
            vstack.push(rval);
        }

        if (##PREFIX##dbg_withtrace) {
            JXG.debug(##PREFIX##dbg_string);
            ##PREFIX##dbg_string = '';
        }
    }

    if (##PREFIX##dbg_withtrace) {
        ##PREFIX##dbg_print('\nParse complete.');
        JXG.debug(##PREFIX##dbg_string);
    }

    return err_cnt;
}

##FOOTER##