
import Test.HUnit

import Eval
import Parser
import Value

testEval :: String -> String -> Test
testEval expr expected = TestCase $
    case readExpr expr of
        Right expr' -> case eval expr' [] of
            Left s -> assertEqual "yeah" expected s
            Right (v, e) -> case readExpr expected of
                Right expected' -> assertEqual "yeah" expected' v

testAdd1 = testEval "(+ 2 3)" "5"
testAdd2 = testEval "(+ 2 3 4)" "9"

testGt1 = testEval "(> 3 2)" "#t"
testGt2 = testEval "(> 2 3)" "#f"
testGt3 = testEval "(> 4 3 2)" "#t"
testGt4 = testEval "(> 2 4 3)" "#f"

testIf1 = testEval "(if (> 2 1) 3 4)" "3"
testIf2 = testEval "(if (> 1 2) 3 4)" "4"

testLet1 = testEval "(let ((a 1)) 2)" "2"
testLet2 = testEval "(let ((a 1)) a)" "1"
testLet3 = testEval "(let)" "invalid let form"
testLet4 = testEval "(let (a 1) a)" "invalid let form"
testLet5 = testEval "(let ((a 1)))" "invalid let form"

testLambda1 = testEval "((lambda (x) (* x 2)) 3)" "6"
testLambda2 = testEval "(let ((f (lambda (x) (* x 2)))) (f 3))" "6"

-- test13 = testEval "(let ((x 3)) (- x 1))" "2"
-- test13 = testEval "(let ((sumto (lambda (x) (if (> x 1) (+ x (sumto (- x 1))) 2)))) (sumto 5))" "15"

testRecurse = testEval "(let ((fact (lambda (x) (if (> x 0) (* x (fact (- x 1))) 1)))) (fact 5))" "120"

testCar = testEval "(car '(1 2 3))" "1"
testCdr = testEval "(cdr '(1 2 3))" "(2 3)"

testCons1 = testEval "(cons 1 '(2 3))" "(1 2 3)"
testCons2 = testEval "(cons 1 '(2 . 3))" "(1 2 . 3)"
testCons3 = testEval "(cons 1 2)" "(1 . 2)"

testUndefined = testEval "x" "'x' is undefined"
testUndefined2 = testEval "(x)" "'x' is undefined"

testDefine1 = testEval "(define a 2)" "a"
testDefine2 = testEval "(define (f x) (* 2 x))" "f"
testDefine3 = testEval "(define (x) 3)" "x"
testDefine4 = testEval "(define)" "invalid define form"
testDefine5 = testEval "(define x)" "invalid define form"
testDefine6 = testEval "(define (x))" "invalid define form"

tests = TestList [
    testAdd1,
    testAdd2,
    testGt1,
    testGt2,
    testGt3,
    testGt4,
    testIf1,
    testIf2,
    testLet1,
    testLet2,
    testLet3,
    testLet4,
    testLet5,
    testLambda1,
    testLambda2,
    testRecurse,
    testCar,
    testCdr,
    testCons1,
    testCons2,
    testCons3,
    testUndefined,
    testUndefined2,
    testDefine1,
    testDefine2,
    testDefine3,
    testDefine4,
    testDefine5,
    testDefine6]

main = runTestTT tests

