<?php

Mock::generate('Wact_Template_Compiler_Expression_Observer','MockExpressionObserver');

class Wact_Template_Compiler_Expression_ParserTestCase extends Wact_Test_Case {

    protected $parser;
    protected $observer;

    function setUp() {
        $this->observer = new MockExpressionObserver($this);
        $this->parser = new Wact_Template_Compiler_Expression_Parser(NULL);
    }

    function tearDown() {
        unset ( $this->parser );
        unset ( $this->observer );
    }

    function testNoExpression() {
        $text = 'test';
        $this->observer->expectOnce('addLiteral', array($text));

        $this->parser->parseFragments($this->observer, $text);

        $this->observer->tally();
    }

    function testSimpleExpression() {
        $this->observer->expectOnce('addExpression');

        $this->parser->parseFragments($this->observer, '{$1}');

        $this->observer->tally();
    }

    function testCompoundExpression1() {
        $this->observer->expectOnce('addLiteralFragment', array('prefix'));
        $this->observer->expectOnce('addExpressionFragment');
        $this->observer->expectNever('addLiteral');
        $this->observer->expectNever('addExpression');

        $this->parser->parseFragments($this->observer, 'prefix{$1}');

        $this->observer->tally();
    }

    function testCompoundExpression2() {
        $this->observer->expectOnce('addLiteralFragment', array('postfix'));
        $this->observer->expectOnce('addExpressionFragment');
        $this->observer->expectNever('addLiteral');
        $this->observer->expectNever('addExpression');

        $this->parser->parseFragments($this->observer, '{$1}postfix');

        $this->observer->tally();
    }

    function testCompoundExpression3() {
        $this->observer->expectAt(0, 'addLiteralFragment', array('prefix'));
        $this->observer->expectAt(1, 'addLiteralFragment', array('postfix'));
        $this->observer->expectCallCount('addLiteralFragment', 2);
        $this->observer->expectOnce('addExpressionFragment', array('*'));
        $this->observer->expectNever('addLiteral');
        $this->observer->expectNever('addExpression');

        $this->parser->parseFragments($this->observer, 'prefix{$1}postfix');

        $this->observer->tally();
    }

    function testCompoundExpression4() {
        $this->observer->expectAt(0, 'addExpressionFragment', array('*'));
        $this->observer->expectAt(1, 'addExpressionFragment', array('*'));
        $this->observer->expectCallCount('addExpressionFragment', 2);
        $this->observer->expectNever('addLiteral');
        $this->observer->expectNever('addLiteralFragment');
        $this->observer->expectNever('addExpression');

        $this->parser->parseFragments($this->observer, '{$1}{$2}');

        $this->observer->tally();
    }

    function testCompoundExpression5() {
        $this->observer->expectAt(0, 'addExpressionFragment', array('*'));
        $this->observer->expectAt(1, 'addExpressionFragment', array('*'));
        $this->observer->expectCallCount('addExpressionFragment', 2);
        $this->observer->expectOnce('addLiteralFragment', array('internal'));
        $this->observer->expectNever('addLiteral');
        $this->observer->expectNever('addExpression');

        $this->parser->parseFragments($this->observer, '{$1}internal{$2}');

        $this->observer->tally();
    }

    function testExpressionRootIsDefault() {
        $this->observer->expectOnce('addExpression', array(new IsAExpectation('Wact_Template_Compiler_Expression_Node_Html')));

        $this->parser->parseFragments($this->observer, '{$1}', 'Wact_Template_Compiler_Expression_Node_Html');

        $this->observer->tally();
    }

    function testRawFormatterOverridesDefaultRoot() {
        $this->observer->expectOnce('addExpression', array(new NotAExpectation('Wact_Template_Compiler_Expression_Node_Html')));

        $this->parser->parseFragments($this->observer, '{$1!raw}', 'Wact_Template_Compiler_Expression_Node_Html');

        $this->observer->tally();
    }

}

class WactConstantExpressionEvaluationTestCase extends Wact_Test_Case {

    protected $observer;

    function setUp() {
        $this->context = new Wact_Template_Compiler(NULL, NULL);
        $this->parser = new Wact_Template_Compiler_Expression_Parser($this->context);
    }

    function tearDown() {
        unset ( $this->parser );
        unset ( $this->observer );
    }

    function testNull() {
        $expr = $this->parser->parse('null');
        $this->assertIdentical($expr->getValue(), null);
    }

    function testTrue() {
        $expr = $this->parser->parse('true');
        $this->assertIdentical($expr->getValue(), true);
    }

    function testFalse() {
        $expr = $this->parser->parse('false');
        $this->assertIdentical($expr->getValue(), false);
    }

    function testInteger() {
        $expr = $this->parser->parse('1');
        $this->assertIdentical($expr->getValue(), 1);
    }

    function testZero() {
        $expr = $this->parser->parse('0');
        $this->assertIdentical($expr->getValue(), 0);
    }

    function testFloat() {
        $expr = $this->parser->parse('1.2');
        $this->assertIdentical($expr->getValue(), 1.2);
    }

    function testStringDoubleQuotes() {
        $expr = $this->parser->parse('"hello"');
        $this->assertIdentical($expr->getValue(), "hello");
    }

    function testZeroDoubleQuotes() {
        $expr = $this->parser->parse('"0"');
        $this->assertIdentical($expr->getValue(), "0");
    }

    function testStringSingleQuotes() {
        $expr = $this->parser->parse("'hello'");
        $this->assertIdentical($expr->getValue(), "hello");
    }

    function testArrayEmpty() {
        $expr = $this->parser->parse('[]');
        $this->assertIdentical($expr->getValue(), array());
    }

    function testArrayPlain() {
        $expr = $this->parser->parse('[1]');
        $this->assertIdentical($expr->getValue(), array(1));
    }

    function testArrayOnePairInteger() {
        $expr = $this->parser->parse('[ 1 => 2 ]');
        $this->assertIdentical($expr->getValue(), array( 1 => 2));
    }

    function testArrayOnePairString() {
        $expr = $this->parser->parse('[ "key" => "value" ]');
        $this->assertIdentical($expr->getValue(), array( "key" => "value"));
    }

    function testArrayTwoPair() {
        $expr = $this->parser->parse('[ 1 => 3, 2 => 4 ]');
        $this->assertIdentical($expr->getValue(), array( 1 => 3, 2 => 4));
    }
    
    function testArrayValueExpression() {
        $expr = $this->parser->parse('[ 1 => 1 + 2]');
        $this->assertIdentical($expr->getValue(), array( 1 => 3));
    }

    function testAddition() {
        $expr = $this->parser->parse('1+2');
        $this->assertIdentical($expr->getValue(), 1+2);
    }

    function testSubtraction() {
        $expr = $this->parser->parse('1-2');
        $this->assertIdentical($expr->getValue(), 1-2);
    }

    function testMultiplication() {
        $expr = $this->parser->parse('2*3');
        $this->assertIdentical($expr->getValue(), 2*3);
    }

    function testDivision() {
        $expr = $this->parser->parse('8/2');
        $this->assertIdentical($expr->getValue(), 8/2);
    }

    function testModulo() {
        $expr = $this->parser->parse('5%2');
        $this->assertIdentical($expr->getValue(), 5%2);
    }

    function testMinus() {
        $expr = $this->parser->parse('-2');
        $this->assertIdentical($expr->getValue(), -2);
    }

    function testConcatination() {
        $expr = $this->parser->parse('"head" & "tail"');
        $this->assertIdentical($expr->getValue(), 'headtail');
    }

    function testLogicalAnd() {
        $expr = $this->parser->parse('true and false');
        $this->assertIdentical($expr->getValue(), false);
    }

    function testLogicalOr() {
        $expr = $this->parser->parse('true or false');
        $this->assertIdentical($expr->getValue(), true);
    }

    function testLogicalNot() {
        $expr = $this->parser->parse('not false');
        $this->assertIdentical($expr->getValue(), true);
    }

    function testLogicalEqual() {
        $expr = $this->parser->parse('1 == 1');
        $this->assertIdentical($expr->getValue(), true);
    }

    function testLogicalNotEqual() {
        $expr = $this->parser->parse('1 != 2');
        $this->assertIdentical($expr->getValue(), true);
    }

    function testLogicalLessThan() {
        $expr = $this->parser->parse('1 < 2');
        $this->assertIdentical($expr->getValue(), true);
    }

    function testLogicalLessThanOrEqual() {
        $expr = $this->parser->parse('2 <= 2');
        $this->assertIdentical($expr->getValue(), true);
    }

    function testLogicalGreaterThan() {
        $expr = $this->parser->parse('3 > 2');
        $this->assertIdentical($expr->getValue(), true);
    }

    function testLogicalGreaterThanOrEqual() {
        $expr = $this->parser->parse('3 >= 2');
        $this->assertIdentical($expr->getValue(), true);
    }

    function testDereference() {
        $expr = $this->parser->parse('[ "x" => 1, "y" => 2] . y');
        $this->assertIdentical($expr->getValue(), 2);
    }

    function testMyDearAuntSally() {
        $expr = $this->parser->parse('1+2*3');
        $this->assertIdentical($expr->getValue(), 1+2*3);
    }

    function testMyDearAuntSally2() {
        $expr = $this->parser->parse('2*3+1');
        $this->assertIdentical($expr->getValue(), 2*3+1);
    }

    function testMyDearAuntSally3() {
        $expr = $this->parser->parse('2*3+4*5');
        $this->assertIdentical($expr->getValue(), 2*3+4*5);
    }

    function testMyDearAuntSally4() {
        $expr = $this->parser->parse('8-4-2');
        $this->assertIdentical($expr->getValue(), 8-4-2);
    }

    function testMyDearAuntSally5() {
        $expr = $this->parser->parse('24*6*2');
        $this->assertIdentical($expr->getValue(), 24*6*2);
    }

    function testMyDearAuntSally6() {
        $expr = $this->parser->parse('24/6/2');
        $this->assertIdentical($expr->getValue(), 24/6/2);
    }

    function testParenthesis() {
        $expr = $this->parser->parse('8-(4-2)');
        $this->assertIdentical($expr->getValue(), 8-(4-2));
    }

    function testFilterUppercase() {
        $expr = $this->parser->parse('"foo"|uppercase');
        $this->assertIdentical($expr->getValue(), 'FOO');
    }

    function testFilterLowercase() {
        $expr = $this->parser->parse('"FOO"|lowercase');
        $this->assertIdentical($expr->getValue(), 'foo');
    }

    function testFilterChain() {
        $expr = $this->parser->parse('"Foo"|lowercase|uppercase');
        $this->assertIdentical($expr->getValue(), 'FOO');
    }

    function testFilterWithTooManyParameters() {
        $this->expectException();
        $expr = $this->parser->parse('"Foo"|lowercase:"bar"');
    }

    function testFilterNumber() {
        $expr = $this->parser->parse('1.234|number');
        $this->assertIdentical($expr->getValue(), '1');
    }

    function testFilterNumberWithDecimalsParameter() {
        $expr = $this->parser->parse('1.234|number:1');
        $this->assertIdentical($expr->getValue(), '1.2');
    }

    function testFilterNumberWithAllParamtersSpecified() {
        $expr = $this->parser->parse('1001.234|number:1,"x","y"');
        $this->assertIdentical($expr->getValue(), '1y001x2');
    }

    function testFunctionUppercase() {
        $expr = $this->parser->parse('uppercase("foo")');
        $this->assertIdentical($expr->getValue(), 'FOO');
    }

    function testFunctionChain() {
        $expr = $this->parser->parse('uppercase(lowercase("Foo"))');
        $this->assertIdentical($expr->getValue(), 'FOO');
    }

    function testFunctionWithTooManyParameters() {
        $this->expectException();
        $expr = $this->parser->parse('lowercase("Foo", "bar")');
    }

    function testFunctionNumber() {
        $expr = $this->parser->parse('number(1.234)');
        $this->assertIdentical($expr->getValue(), '1');
    }

    function testFunctionNumberWithDecimalsParameter() {
        $expr = $this->parser->parse('number(1.234, 1)');
        $this->assertIdentical($expr->getValue(), '1.2');
    }

    function testFunctionNumberWithAllParamtersSpecified() {
        $expr = $this->parser->parse('number(1001.234, 1, "x" ,"y")');
        $this->assertIdentical($expr->getValue(), '1y001x2');
    }

}

?>