package terse.talk;

import terse.talk.Talk.Terp;
import terse.talk.Talk.Terp.Frame;
import junit.framework.TestCase;

public class ExprTest extends TestCase {
    Terp t;
    Cls usrCls;
    Usr usrInst;

    protected void setUp() throws Exception {
        super.setUp();
        t = new Terp();
        usrCls = t.tUsr;
        usrInst = new Usr(t.tUsr);
    }
    protected Pro eval(Expr.Top top) {
        Frame f = t.newFrame(null, usrInst, top);
        return top.eval(f);
    }

    public void testLit888() {
        Expr.Top top = Parser.parseMethod(usrCls, "temp", " 888 ");
        Talk.say("8888888888 -> TOP %s", top);
        Pro obj = eval(top);
        assertEquals(888.0, obj.asNum().num);
    }
    public void testLitString() {
        Expr.Top top = Parser.parseMethod(usrCls, "temp", " 'foo''bar' ");
        Pro obj = eval(top);
        assertEquals("foo'bar", obj.asStr().str);
    }
    public void testTwoStmts() {
        Expr.Top top = Parser.parseMethod(usrCls, "temp", " . 23 . 42 . . . ");
        assertEquals(42.0, eval(top).asNum().num);
    }
    public void testUnaryNeg() {
        Expr.Top top = Parser.parseMethod(usrCls, "temp", " . 23 . 42 neg . . . ");
        assertEquals(-42.0, eval(top).asNum().num);
    }
    public void testUnaryNegSgn() {
        Expr.Top top = Parser.parseMethod(usrCls, "temp", " . 23 . 42 neg sgn ");
        assertEquals(-1.0, eval(top).asNum().num);
    }
    public void testPlusTwoNums() {
        Expr.Top top = Parser.parseMethod(usrCls, "temp", " 23 pl: 42  ");
        assertEquals(65.0, eval(top).asNum().num);
    }
    public void testPlusTwoNumsWithNeg() {
        Expr.Top top = Parser.parseMethod(usrCls, "temp", " 23 pl: 42 neg  ");
        assertEquals(-19.0, eval(top).asNum().num);
    }
    public void testPlusTwoNumsWithOtherNeg() {
        Expr.Top top = Parser.parseMethod(usrCls, "temp", " 23 neg pl: 42  ");
        assertEquals(19.0, eval(top).asNum().num);
    }
    public void testStoreAndFetch() {
        Expr.Top top = Parser.parseMethod(usrCls, "temp", " n@ 23 pl: 42 . 13 . n  ");
        assertEquals(65.0, eval(top).asNum().num);
    }
    public void testBlock() {
        Expr.Top top = Parser.parseMethod(usrCls, "temp", " [ 23 pl: 42 . ] run  ");
        assertEquals(65.0, eval(top).asNum().num);
    }
    public void testYesNoToTrue() {
        Expr.Top top = Parser.parseMethod(usrCls, "temp", " 1 y: [ e@ 42 ] . 1 n: [ e@ 13 ] . e");
        assertEquals(42.0, eval(top).asNum().num);
    }
    public void testYesNoToFalse() {
        Expr.Top top = Parser.parseMethod(usrCls, "temp", " 0 y: [ a@ 42 ] . 0 n: [ a@ 13 ] . a");
        assertEquals(13.0, eval(top).asNum().num);
    }
    public void testDefiningUsrSubclass() {
        Expr.Top top = Parser.parseMethod(usrCls, "temp", " Usr defineSubclass: 'Foo' . Foo new cls name");
        assertEquals("Foo", eval(top).asStr().str);
    }
    public void testDefiningUsrMethod() {
        Expr.Top top = Parser.parseMethod(usrCls, "temp", " Usr defsub: 'Foo' . "
                + "Foo defmeth: 'xyz' a: '' d: '' c: '888' . " + "Foo new xyz ");
        assertEquals(888.0, eval(top).asNum().num);
    }
    public void testInstVar() {
        Expr.Top top = Parser.parseMethod(usrCls, "temp", "Usr defSub: 'Foo' . " + "Foo defVars: ' x ' . "
                + "Foo defineMethod: 'stor:' abbrev: '' doc: 'Store in x.' code: 'x@ a' . "
                + "Foo defmeth: 'rcl' a: nil d: '' c: 'x' . " + "h = Foo new . " + "h stor: 56 . " + "h rcl neg ");
        assertEquals(-56.0, eval(top).asNum().num);
    }
    public void testVec() {
        Expr.Top top = Parser.parseMethod(usrCls, "temp", "a@ Vec new . a setLen: 10 . a at: 3 p: 33 . a at: 3 ");
        assertEquals(33.0, eval(top).asNum().num);
    }
    public void testDict() {
        Expr.Top top = Parser.parseMethod(usrCls, "temp", "a@ Dict new . a at: 'foo' p: 333 . a at: 'foo' ");
        assertEquals(333.0, eval(top).asNum().num);
    }
    public void testEq() {
        Expr.Top top = Parser.parseMethod(usrCls, "temp", " 5 eq: 2 ");
        assertEquals(t.instFalse, eval(top));
    }
    public void testNe() {
        Expr.Top top = Parser.parseMethod(usrCls, "temp", " 5 ne: 2 ");
        assertEquals(t.instTrue, eval(top));
    }
    public void testLt() {
        Expr.Top top = Parser.parseMethod(usrCls, "temp", " 5 lt: 2 ");
        assertEquals(t.instFalse, eval(top));
    }
    public void testLe() {
        Expr.Top top = Parser.parseMethod(usrCls, "temp", " 5 le: 5 ");
        assertEquals(t.instTrue, eval(top));
    }
    public void testGt() {
        Expr.Top top = Parser.parseMethod(usrCls, "temp", " 5 gt: 2 ");
        assertEquals(t.instTrue, eval(top));
    }
    public void testGe() {
        Expr.Top top = Parser.parseMethod(usrCls, "temp", " 5 ge: 6 ");
        assertEquals(t.instFalse, eval(top));
    }
    public void testStrEq() {
        Expr.Top top = Parser.parseMethod(usrCls, "temp", " 'a' eq: 'x' ");
        assertEquals(t.instFalse, eval(top));
    }
    public void testStrNe() {
        Expr.Top top = Parser.parseMethod(usrCls, "temp", " 'x' ne: 'xx' ");
        assertEquals(t.instTrue, eval(top));
    }
    public void testStrLt() {
        Expr.Top top = Parser.parseMethod(usrCls, "temp", " 'xxx' lt: 'xxx' ");
        assertEquals(t.instFalse, eval(top));
    }
    public void testStrLe() {
        Expr.Top top = Parser.parseMethod(usrCls, "temp", " 'xx' le: 'xxx' ");
        assertEquals(t.instTrue, eval(top));
    }
    public void testStrGt() {
        Expr.Top top = Parser.parseMethod(usrCls, "temp", " '5' gt: '20' ");
        assertEquals(t.instTrue, eval(top));
    }
    public void testStrGe() {
        Expr.Top top = Parser.parseMethod(usrCls, "temp", " '' ge: '!' ");
        assertEquals(t.instFalse, eval(top));
    }
    public void testDoForRange() {
        Expr.Top top = Parser.parseMethod(usrCls, "temp", " s@ 0 . 5 do: [ :i . s@ s pl/ i ] . s ");
        assertEquals(10.0, eval(top).asNum().num);
    }
    public void testDoForVec() {
        Expr.Top top = Parser.parseMethod(usrCls, "temp", " s = 0; v = Vec ap: 3; v do: [ :i ; s= s pl/ i ] . s ");
        assertEquals(3.0, eval(top).asNum().num);
    }
    public void testStrSplit() {
        Expr.Top top = Parser.parseMethod(usrCls, "temp", " 'one/more/time' split: '/' -- at: 2 ");
        assertEquals("time", eval(top).asStr().str);
    }
    public void testBuf() {
        Expr.Top top = Parser.parseMethod(usrCls, "temp", " Buf ap: 'one/' -- ap: 'more/' -- ap: 'time' -- str");
        assertEquals("one/more/time", eval(top).asStr().str);
    }
    public void testBlkToVec() {
        Expr.Top top = Parser.parseMethod(usrCls, "temp", " [ 1 pl/ 1 . 2 pl/ 2 . 3 pl/ 3 . 4 pl/ 4 . ] vec at: 2 ");
        assertEquals(6.0, eval(top).asNum().num);
    }
    public void testBlkToDict() {
        Expr.Top top = Parser.parseMethod(usrCls, "temp", " [ 1 pl/ 1, 2 pl/ 2. 3 pl/ 3, 4 pl/ 4. ] dict at: 2 ");
        assertEquals(4.0, eval(top).asNum().num);
    }
    public void testTriangleNumber() {
        Expr.Top top = Parser.parseMethod(usrCls, "temp", "USR defSub: 'Foo' . Foo trace: 1 . "
                + " Foo defmeth: 'trIaNgle:' a: '' d: '' c:  ' " + "   g@ 0 . "
                + "   RUN[ a gt: 0 ] y/ [ g=  se triangle: R[ a mi: 1 ] -- pl: a  ] . " + "   g' . "
                + "   Foo new TRIANGLE: 6  ");
        assertEquals(21.0, eval(top).asNum().num);
    }
    public void testSuper() {
        Expr.Top top = Parser.parseMethod(usrCls, "temp", "USR defSub:'Foo'. Foo defSub:'Bar'. "
                + "Foo defmeth:'mumble' a:'' d:'' c:' ''Hello'' '.  "
                + "Bar defmeth:'mumble' a:'' d:'' c:' super mumble ap: '' World'' '.  " + "Bar new mumble");
        assertEquals("Hello World", eval(top).asStr().str);
    }
}
