/* This file was generated by SableCC (http://www.sablecc.org/). */

package parser;

import lexer.*;
import node.*;
import analysis.*;
import java.util.*;

import java.io.DataInputStream;
import java.io.BufferedInputStream;
import java.io.IOException;

@SuppressWarnings("nls")
public class Parser
{
    public final Analysis ignoredTokens = new AnalysisAdapter();

    protected ArrayList nodeList;

    private final Lexer lexer;
    private final ListIterator stack = new LinkedList().listIterator();
    private int last_pos;
    private int last_line;
    private Token last_token;
    private final TokenIndex converter = new TokenIndex();
    private final int[] action = new int[2];

    private final static int SHIFT = 0;
    private final static int REDUCE = 1;
    private final static int ACCEPT = 2;
    private final static int ERROR = 3;

    public Parser(@SuppressWarnings("hiding") Lexer lexer)
    {
        this.lexer = lexer;
    }

    protected void filter() throws ParserException, LexerException, IOException
    {
        // Empty body
    }

    private void push(int numstate, ArrayList listNode, boolean hidden) throws ParserException, LexerException, IOException
    {
        this.nodeList = listNode;

        if(!hidden)
        {
            filter();
        }

        if(!this.stack.hasNext())
        {
            this.stack.add(new State(numstate, this.nodeList));
            return;
        }

        State s = (State) this.stack.next();
        s.state = numstate;
        s.nodes = this.nodeList;
    }

    private int goTo(int index)
    {
        int state = state();
        int low = 1;
        int high = gotoTable[index].length - 1;
        int value = gotoTable[index][0][1];

        while(low <= high)
        {
            int middle = (low + high) / 2;

            if(state < gotoTable[index][middle][0])
            {
                high = middle - 1;
            }
            else if(state > gotoTable[index][middle][0])
            {
                low = middle + 1;
            }
            else
            {
                value = gotoTable[index][middle][1];
                break;
            }
        }

        return value;
    }

    private int state()
    {
        State s = (State) this.stack.previous();
        this.stack.next();
        return s.state;
    }

    private ArrayList pop()
    {
        return ((State) this.stack.previous()).nodes;
    }

    private int index(Switchable token)
    {
        this.converter.index = -1;
        token.apply(this.converter);
        return this.converter.index;
    }

    @SuppressWarnings("unchecked")
    public Start parse() throws ParserException, LexerException, IOException
    {
        push(0, null, true);
        List<Node> ign = null;
        while(true)
        {
            while(index(this.lexer.peek()) == -1)
            {
                if(ign == null)
                {
                    ign = new LinkedList<Node>();
                }

                ign.add(this.lexer.next());
            }

            if(ign != null)
            {
                this.ignoredTokens.setIn(this.lexer.peek(), ign);
                ign = null;
            }

            this.last_pos = this.lexer.peek().getPos();
            this.last_line = this.lexer.peek().getLine();
            this.last_token = this.lexer.peek();

            int index = index(this.lexer.peek());
            this.action[0] = Parser.actionTable[state()][0][1];
            this.action[1] = Parser.actionTable[state()][0][2];

            int low = 1;
            int high = Parser.actionTable[state()].length - 1;

            while(low <= high)
            {
                int middle = (low + high) / 2;

                if(index < Parser.actionTable[state()][middle][0])
                {
                    high = middle - 1;
                }
                else if(index > Parser.actionTable[state()][middle][0])
                {
                    low = middle + 1;
                }
                else
                {
                    this.action[0] = Parser.actionTable[state()][middle][1];
                    this.action[1] = Parser.actionTable[state()][middle][2];
                    break;
                }
            }

            switch(this.action[0])
            {
                case SHIFT:
		    {
		        ArrayList list = new ArrayList();
		        list.add(this.lexer.next());
                        push(this.action[1], list, false);
                    }
		    break;
                case REDUCE:
                    switch(this.action[1])
                    {
                    case 0: /* reduce AAddAdditive */
		    {
			ArrayList list = new0();
			push(goTo(0), list, false);
		    }
		    break;
                    case 1: /* reduce ASubAdditive */
		    {
			ArrayList list = new1();
			push(goTo(0), list, false);
		    }
		    break;
                    case 2: /* reduce AOneAdditive */
		    {
			ArrayList list = new2();
			push(goTo(0), list, false);
		    }
		    break;
                    case 3: /* reduce AMulMultiplicative */
		    {
			ArrayList list = new3();
			push(goTo(1), list, false);
		    }
		    break;
                    case 4: /* reduce ADivMultiplicative */
		    {
			ArrayList list = new4();
			push(goTo(1), list, false);
		    }
		    break;
                    case 5: /* reduce AOneMultiplicative */
		    {
			ArrayList list = new5();
			push(goTo(1), list, false);
		    }
		    break;
                    case 6: /* reduce AParPrimary */
		    {
			ArrayList list = new6();
			push(goTo(2), list, false);
		    }
		    break;
                    case 7: /* reduce ANumPrimary */
		    {
			ArrayList list = new7();
			push(goTo(2), list, false);
		    }
		    break;
                    }
                    break;
                case ACCEPT:
                    {
                        EOF node2 = (EOF) this.lexer.next();
                        PAdditive node1 = (PAdditive) pop().get(0);
                        Start node = new Start(node1, node2);
                        return node;
                    }
                case ERROR:
                    throw new ParserException(this.last_token,
                        "[" + this.last_line + "," + this.last_pos + "] " +
                        Parser.errorMessages[Parser.errors[this.action[1]]]);
            }
        }
    }



    @SuppressWarnings("unchecked")
    ArrayList new0() /* reduce AAddAdditive */
    {
        @SuppressWarnings("hiding") ArrayList nodeList = new ArrayList();

        @SuppressWarnings("unused") ArrayList nodeArrayList3 = pop();
        @SuppressWarnings("unused") ArrayList nodeArrayList2 = pop();
        @SuppressWarnings("unused") ArrayList nodeArrayList1 = pop();
        PAdditive padditiveNode1;
        {
            // Block
        PAdditive padditiveNode2;
        TAdd taddNode3;
        PMultiplicative pmultiplicativeNode4;
        padditiveNode2 = (PAdditive)nodeArrayList1.get(0);
        taddNode3 = (TAdd)nodeArrayList2.get(0);
        pmultiplicativeNode4 = (PMultiplicative)nodeArrayList3.get(0);

        padditiveNode1 = new AAddAdditive(padditiveNode2, taddNode3, pmultiplicativeNode4);
        }
	nodeList.add(padditiveNode1);
        return nodeList;
    }



    @SuppressWarnings("unchecked")
    ArrayList new1() /* reduce ASubAdditive */
    {
        @SuppressWarnings("hiding") ArrayList nodeList = new ArrayList();

        @SuppressWarnings("unused") ArrayList nodeArrayList3 = pop();
        @SuppressWarnings("unused") ArrayList nodeArrayList2 = pop();
        @SuppressWarnings("unused") ArrayList nodeArrayList1 = pop();
        PAdditive padditiveNode1;
        {
            // Block
        PAdditive padditiveNode2;
        TSub tsubNode3;
        PMultiplicative pmultiplicativeNode4;
        padditiveNode2 = (PAdditive)nodeArrayList1.get(0);
        tsubNode3 = (TSub)nodeArrayList2.get(0);
        pmultiplicativeNode4 = (PMultiplicative)nodeArrayList3.get(0);

        padditiveNode1 = new ASubAdditive(padditiveNode2, tsubNode3, pmultiplicativeNode4);
        }
	nodeList.add(padditiveNode1);
        return nodeList;
    }



    @SuppressWarnings("unchecked")
    ArrayList new2() /* reduce AOneAdditive */
    {
        @SuppressWarnings("hiding") ArrayList nodeList = new ArrayList();

        @SuppressWarnings("unused") ArrayList nodeArrayList1 = pop();
        PAdditive padditiveNode1;
        {
            // Block
        PMultiplicative pmultiplicativeNode2;
        pmultiplicativeNode2 = (PMultiplicative)nodeArrayList1.get(0);

        padditiveNode1 = new AOneAdditive(pmultiplicativeNode2);
        }
	nodeList.add(padditiveNode1);
        return nodeList;
    }



    @SuppressWarnings("unchecked")
    ArrayList new3() /* reduce AMulMultiplicative */
    {
        @SuppressWarnings("hiding") ArrayList nodeList = new ArrayList();

        @SuppressWarnings("unused") ArrayList nodeArrayList3 = pop();
        @SuppressWarnings("unused") ArrayList nodeArrayList2 = pop();
        @SuppressWarnings("unused") ArrayList nodeArrayList1 = pop();
        PMultiplicative pmultiplicativeNode1;
        {
            // Block
        PMultiplicative pmultiplicativeNode2;
        TMul tmulNode3;
        PPrimary pprimaryNode4;
        pmultiplicativeNode2 = (PMultiplicative)nodeArrayList1.get(0);
        tmulNode3 = (TMul)nodeArrayList2.get(0);
        pprimaryNode4 = (PPrimary)nodeArrayList3.get(0);

        pmultiplicativeNode1 = new AMulMultiplicative(pmultiplicativeNode2, tmulNode3, pprimaryNode4);
        }
	nodeList.add(pmultiplicativeNode1);
        return nodeList;
    }



    @SuppressWarnings("unchecked")
    ArrayList new4() /* reduce ADivMultiplicative */
    {
        @SuppressWarnings("hiding") ArrayList nodeList = new ArrayList();

        @SuppressWarnings("unused") ArrayList nodeArrayList3 = pop();
        @SuppressWarnings("unused") ArrayList nodeArrayList2 = pop();
        @SuppressWarnings("unused") ArrayList nodeArrayList1 = pop();
        PMultiplicative pmultiplicativeNode1;
        {
            // Block
        PMultiplicative pmultiplicativeNode2;
        TDiv tdivNode3;
        PPrimary pprimaryNode4;
        pmultiplicativeNode2 = (PMultiplicative)nodeArrayList1.get(0);
        tdivNode3 = (TDiv)nodeArrayList2.get(0);
        pprimaryNode4 = (PPrimary)nodeArrayList3.get(0);

        pmultiplicativeNode1 = new ADivMultiplicative(pmultiplicativeNode2, tdivNode3, pprimaryNode4);
        }
	nodeList.add(pmultiplicativeNode1);
        return nodeList;
    }



    @SuppressWarnings("unchecked")
    ArrayList new5() /* reduce AOneMultiplicative */
    {
        @SuppressWarnings("hiding") ArrayList nodeList = new ArrayList();

        @SuppressWarnings("unused") ArrayList nodeArrayList1 = pop();
        PMultiplicative pmultiplicativeNode1;
        {
            // Block
        PPrimary pprimaryNode2;
        pprimaryNode2 = (PPrimary)nodeArrayList1.get(0);

        pmultiplicativeNode1 = new AOneMultiplicative(pprimaryNode2);
        }
	nodeList.add(pmultiplicativeNode1);
        return nodeList;
    }



    @SuppressWarnings("unchecked")
    ArrayList new6() /* reduce AParPrimary */
    {
        @SuppressWarnings("hiding") ArrayList nodeList = new ArrayList();

        @SuppressWarnings("unused") ArrayList nodeArrayList3 = pop();
        @SuppressWarnings("unused") ArrayList nodeArrayList2 = pop();
        @SuppressWarnings("unused") ArrayList nodeArrayList1 = pop();
        PPrimary pprimaryNode1;
        {
            // Block
        TLpar tlparNode2;
        PAdditive padditiveNode3;
        TRpar trparNode4;
        tlparNode2 = (TLpar)nodeArrayList1.get(0);
        padditiveNode3 = (PAdditive)nodeArrayList2.get(0);
        trparNode4 = (TRpar)nodeArrayList3.get(0);

        pprimaryNode1 = new AParPrimary(tlparNode2, padditiveNode3, trparNode4);
        }
	nodeList.add(pprimaryNode1);
        return nodeList;
    }



    @SuppressWarnings("unchecked")
    ArrayList new7() /* reduce ANumPrimary */
    {
        @SuppressWarnings("hiding") ArrayList nodeList = new ArrayList();

        @SuppressWarnings("unused") ArrayList nodeArrayList1 = pop();
        PPrimary pprimaryNode1;
        {
            // Block
        TNumber tnumberNode2;
        tnumberNode2 = (TNumber)nodeArrayList1.get(0);

        pprimaryNode1 = new ANumPrimary(tnumberNode2);
        }
	nodeList.add(pprimaryNode1);
        return nodeList;
    }



    private static int[][][] actionTable;
/*      {
			{{-1, ERROR, 0}, {0, SHIFT, 1}, {5, SHIFT, 2}, },
			{{-1, REDUCE, 7}, },
			{{-1, ERROR, 2}, {0, SHIFT, 1}, {5, SHIFT, 2}, },
			{{-1, ERROR, 3}, {1, SHIFT, 7}, {2, SHIFT, 8}, {7, ACCEPT, -1}, },
			{{-1, REDUCE, 2}, {3, SHIFT, 9}, {4, SHIFT, 10}, },
			{{-1, REDUCE, 5}, },
			{{-1, ERROR, 6}, {1, SHIFT, 7}, {2, SHIFT, 8}, {6, SHIFT, 11}, },
			{{-1, ERROR, 7}, {0, SHIFT, 1}, {5, SHIFT, 2}, },
			{{-1, ERROR, 8}, {0, SHIFT, 1}, {5, SHIFT, 2}, },
			{{-1, ERROR, 9}, {0, SHIFT, 1}, {5, SHIFT, 2}, },
			{{-1, ERROR, 10}, {0, SHIFT, 1}, {5, SHIFT, 2}, },
			{{-1, REDUCE, 6}, },
			{{-1, REDUCE, 0}, {3, SHIFT, 9}, {4, SHIFT, 10}, },
			{{-1, REDUCE, 1}, {3, SHIFT, 9}, {4, SHIFT, 10}, },
			{{-1, REDUCE, 3}, },
			{{-1, REDUCE, 4}, },
        };*/
    private static int[][][] gotoTable;
/*      {
			{{-1, 3}, {2, 6}, },
			{{-1, 4}, {7, 12}, {8, 13}, },
			{{-1, 5}, {9, 14}, {10, 15}, },
        };*/
    private static String[] errorMessages;
/*      {
			"expecting: number, '('",
			"expecting: '+', '-', '*', '/', ')', EOF",
			"expecting: '+', '-', EOF",
			"expecting: '+', '-', ')'",
        };*/
    private static int[] errors;
/*      {
			0, 1, 0, 2, 1, 1, 3, 0, 0, 0, 0, 1, 1, 1, 1, 1, 
        };*/

    static 
    {
        try
        {
            DataInputStream s = new DataInputStream(
                new BufferedInputStream(
                Parser.class.getResourceAsStream("parser.dat")));

            // read actionTable
            int length = s.readInt();
            Parser.actionTable = new int[length][][];
            for(int i = 0; i < Parser.actionTable.length; i++)
            {
                length = s.readInt();
                Parser.actionTable[i] = new int[length][3];
                for(int j = 0; j < Parser.actionTable[i].length; j++)
                {
                for(int k = 0; k < 3; k++)
                {
                    Parser.actionTable[i][j][k] = s.readInt();
                }
                }
            }

            // read gotoTable
            length = s.readInt();
            gotoTable = new int[length][][];
            for(int i = 0; i < gotoTable.length; i++)
            {
                length = s.readInt();
                gotoTable[i] = new int[length][2];
                for(int j = 0; j < gotoTable[i].length; j++)
                {
                for(int k = 0; k < 2; k++)
                {
                    gotoTable[i][j][k] = s.readInt();
                }
                }
            }

            // read errorMessages
            length = s.readInt();
            errorMessages = new String[length];
            for(int i = 0; i < errorMessages.length; i++)
            {
                length = s.readInt();
                StringBuffer buffer = new StringBuffer();

                for(int j = 0; j < length; j++)
                {
                buffer.append(s.readChar());
                }
                errorMessages[i] = buffer.toString();
            }

            // read errors
            length = s.readInt();
            errors = new int[length];
            for(int i = 0; i < errors.length; i++)
            {
                errors[i] = s.readInt();
            }

            s.close();
        }
        catch(Exception e)
        {
            throw new RuntimeException("The file \"parser.dat\" is either missing or corrupted.");
        }
    }
}
