import junit.framework.TestCase;

/*
 * Created on Feb 4, 2004
 */

/**
 * @author David Matuszek
 * @version Feb 6, 2004
 */
public class ParserTest extends TestCase {
    Parser parser;
    /**
     * Constructor for ParserTest.
     * @param arg0
     */
    public ParserTest(String arg0) {
        super(arg0);
    }

    public void testParser() {
        parser = new Parser("");
        parser = new Parser("2 + 2");
    }

    public void testExpression() {
        Tree expected;
        
        use("250");
        assertTrue(parser.expression());
        assertEquals(tree("250"), stackTop());
        
        use("hello");
        assertTrue(parser.expression());
        assertEquals(tree("hello"), stackTop());

        use("(xyz + 3)");
        assertTrue(parser.expression());
        assertEquals(tree("+", "xyz", "3"), stackTop());

        use("a + b + c");
        assertTrue(parser.expression());
        assertEquals(tree("+", tree("+", "a", "b"), "c"), stackTop());

        use("3 * 12 - 7");
        assertTrue(parser.expression());
        assertEquals(tree("-", tree("*", "3", "12"), tree("7")), stackTop());

        use("12 * 5 - 3 * 4 / 6 + 8");
        assertTrue(parser.expression());
        expected = tree("+",
                      tree("-",
                         tree("*", "12", "5"),
                         tree("/",
                            tree("*", "3", "4"),
                            "6"
                           )
                        ),
                      "8"
                     );
        assertEquals(expected, stackTop());
                     
        use("12 * ((5 - 3) * 4) / 6 + (8)");
        assertTrue(parser.expression());
        expected = tree("+",
                      tree("/",
                         tree("*",
                            "12",
                            tree("*",
                               tree("-","5","3"),
                               "4")),
                         "6"),
                      "8");
        assertEquals(expected, stackTop());
        
        use("");
        assertFalse(parser.expression());
        
        use("#");
        assertFalse(parser.expression());

        try {
            use("17 +");
            assertFalse(parser.expression());
            fail();
        }
        catch (LogoException e) {
        }
        try {
            use("22 *");
            assertFalse(parser.expression());
            fail();
        }
        catch (LogoException e) {
        }
    }

    public void testTerm() {        
        use("12");
        assertTrue(parser.term());
        assertEquals(tree("12"), stackTop());

        use("3*12");
        assertTrue(parser.term());
        assertEquals(tree("*", "3", "12"), stackTop());

        use("x * y * z");
        assertTrue(parser.term());
        assertEquals(tree("*", tree("*", "x", "y"), "z"), stackTop());
        
        use("20 * 3 / 4");
        assertTrue(parser.term());
        assertEquals(tree("/", tree("*", "20", "3"), tree("4")), stackTop());

        use("20 * 3 / 4 + 5");
        assertTrue(parser.term());
        assertEquals(tree("/", tree("*", "20", "3"), "4"), stackTop());
        unused("+ 5");
        
        use("");
        assertFalse(parser.term());
        unused("");
        
        use("#");
        assertFalse(parser.term());unused("#");

    }

    public void testFactor() {
        use("12");
        assertTrue(parser.factor());
        assertEquals(tree("12"), stackTop());

        use("hello");
        assertTrue(parser.factor());
        assertEquals(tree("hello"), stackTop());
        
        use("(xyz + 3)");
        assertTrue(parser.factor());
        assertEquals(tree("+", "xyz", "3"), stackTop());
        
        use("12 * 5");
        assertTrue(parser.factor());
        assertEquals(tree("12"), stackTop());
        unused("* 5");
        
        use("17 +");
        assertTrue(parser.factor());
        assertEquals(tree("17"), stackTop());
        unused("+");

        use("");
        assertFalse(parser.factor());
        unused("");
        
        use("#");
        assertFalse(parser.factor());
        unused("#");
    }

    public void testAdd_operator() {
        use("+ - + $");
        assertTrue(parser.addOperator());
        assertTrue(parser.addOperator());
        assertTrue(parser.addOperator());
        assertFalse(parser.addOperator());
        unused("$");
    }

    public void testMultiply_operator() {
        use("* / $");
        assertTrue(parser.multiplyOperator());
        assertTrue(parser.multiplyOperator());
        assertFalse(parser.multiplyOperator());
        unused("$");
    }
    
//  ----- "Helper" methods

    private void unused(String rest) {
        Tokenizer actual = parser.tokenizer;
        Tokenizer expected = new Tokenizer(rest);
        while (expected.hasNext()) {
            Token actualToken = (Token)actual.next();
            Token expectedToken = (Token)expected.next();
            assertEquals(expectedToken, actualToken);
        }    
    }
    
    /**
     * Sets the <code>parser</code> instance to use the given string.
     * 
     * @param s The string to be parsed.
     */
    private void use(String s) {
        parser = new Parser(s);
    }
    
    /**
     * Returns the current top of the stack.
     *
     * @return The top of the stack.
     */
    private Object stackTop() {
        return parser.stack.peek();
    }
    
    /**
     * Returns a Tree node consisting of a single leaf; the
     * node will contain a Token with a String as its value. <br>
     * Given a Tree, return the same Tree.<br>
     * Given a Token, return a Tree with the Token as its value.<br>
     * Given a String, make it into a Token, return a Tree
     * with the Token as its value.
     * 
     * @param value A Tree, Token, or String from which to
              construct the Tree node.
     * @return A Tree leaf node containing a Token whose value
     *         is the parameter.
     */
    private Tree tree(Object value) {
        if (value instanceof Tree) {
            return (Tree) value;
        }
        if (value instanceof Token) {
            return new Tree(value);
        }
        else if (value instanceof String) {
            return new Tree(makeToken((String) value));
        }
        assert false: "Illegal argument: tree(" + value + ")";
        return null; 
    }
    
    /**
     * Builds a Tree that can be compared with the one the
     * Parser produces. Any String or Token arguments will be
     * converted to Tree nodes containing Tokens.
     * 
     * @param op The String value to use in the Token in the root.
     * @param leftChild The object to be made into a left child.
     * @param rightChild The object to be made into a right child.
     * @return The resultant Tree.
     */
    private Tree tree(String op, Object leftChild, Object rightChild) {
        return new Tree(makeToken(op), tree(leftChild), tree(rightChild));
    }
    
    /**
     * Quick'n'dirty routine to make a Token from a String. The
     * type (name, number, or symbol) is inferred from the first
     * character; no error checking is done.
     * 
     * @param s The string to turn into a Token.
     * @return A Token whose value is the given string and whose
     *         type has been inferred from the first character.
     */
    private Token makeToken(String s) {
        char ch = s.charAt(0);
        if (Character.isDigit(ch))
            return new Token(Type.NUMBER, s);
        if (Character.isLetter(ch))
            return new Token(Type.NAME, s);
        else
            return new Token(Type.SYMBOL, s);
    }
}
