debug-0.1.1: test/Variables.hs
{-# LANGUAGE ViewPatterns #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE PartialTypeSignatures #-}
{-# OPTIONS_GHC -Wno-partial-type-signatures #-}
{-# LANGUAGE OverloadedStrings #-}
-- {-# OPTIONS_GHC -dth-dec-file #-} -- turn on to debug TH
module Variables(main) where
import Control.Exception.Extra
import Control.Monad
import Data.List
import Data.Text (Text, pack, unpack)
import qualified Data.Text as T
import Data.Tuple.Extra
import Debug
import Debug.Util
import System.FilePath
import System.Directory
import Debug.DebugTrace
debug [d|
quicksort :: Ord a => [a] -> [a]
quicksort [] = []
quicksort (x:xs) = quicksort lt ++ [x] ++ quicksort gt
where (lt, gt) = partition (<= x) xs
|]
quicksort_vars :: [(Text, Text)]
quicksort_vars =
[ ("$arg1", "\"haskell\"")
, ("$result", "\"aehklls\"")
, ("++", "\"aehklls\"")
, ("++'", "\"hklls\"")
, ("gt", "\"skll\"")
, ("lt", "\"ae\"")
, ("partition", "(\"ae\",\"skll\")")
, ("quicksort", "\"ae\"")
, ("quicksort'", "\"klls\"")
, ("x", "'h'")
, ("xs", "\"askell\"") ]
debug [d|
quicksortBy :: (a -> a -> Bool) -> [a] -> [a]
quicksortBy op [] = []
quicksortBy op (x:xs) = quicksortBy op lt ++ [x] ++ quicksortBy op gt
where (lt, gt) = partitionBy (op x) xs
partitionBy :: (a -> Bool) -> [a] -> ([a],[a])
{-# INLINE partitionBy #-}
partitionBy p xs = foldr (select p) ([],[]) xs
select :: (a -> Bool) -> a -> ([a], [a]) -> ([a], [a])
select p x ~(ts,fs) | p x = (x:ts,fs)
| otherwise = (ts, x:fs)
|]
debug [d|
type1 :: m a -> m a
type1 = id
-- type2 :: Int -> m Int
-- type2 _ = undefined
|]
debug [d|
lcm_gcd :: (Integral a) => a -> a -> Double
lcm_gcd x y =
let least = lcm x y
in fromIntegral least ^^ gcd x y
|]
lcm_gcd_vars :: [(Text, Text)]
lcm_gcd_vars =
[ ("$arg1", "6")
, ("$arg2", "15")
, ("$result", "27000.0")
, ("^^", "27000.0")
, ("fromIntegral", "30.0")
, ("gcd", "3")
, ("lcm", "30")
, ("least", "30")
, ("x", "6")
, ("y", "15") ]
debug [d|
lcm_gcd_log :: Int -> Int -> Float
lcm_gcd_log x y =
let base = fromIntegral $ gcd x y
val = fromIntegral (x `lcm` y) - base
in logBase base val ** base
|]
lcm_gcd_log_vars :: [(Text, Text)]
lcm_gcd_log_vars =
[ ("$arg1", "6")
, ("$arg2", "15")
, ("$result", "27.0")
, ("**", "27.0")
, ("-", "27.0")
, ("base", "3.0")
, ("fromIntegral", "30.0")
, ("gcd", "3")
, ("lcm", "30")
, ("logBase", "3.0")
, ("val", "27.0")
, ("x", "6")
, ("y", "15") ]
debug [d|
f :: Int -> Int
f n = (2 * n) + 1
case_test :: [Int] -> [Int] -> [Int]
case_test ys zs =
case ys of
x : xs -> f x : xs ++ zs
[] -> zs
|]
case_test_vars :: [(Text, Text)]
case_test_vars =
[ ("$arg1", "[2,3,4]")
, ("$arg2", "[7,8,9]")
, ("$result", "[5,3,4,7,8,9]")
, ("++", "[3,4,7,8,9]")
, (":", "[5,3,4,7,8,9]")
, ("f", "5")
, ("x", "2")
, ("xs", "[3,4]")
, ("ys", "[2,3,4]")
, ("zs", "[7,8,9]") ]
debug [d|
twoXs :: [Int] -> [Int] -> [Int]
twoXs x y =
case x of
x : xs -> f x : xs ++ y
[] -> y
|]
twoXs_vars :: [(Text, Text)]
twoXs_vars =
[ ("$arg1", "[2,3,4]")
, ("$arg2", "[7,8,9]")
, ("$result", "[5,3,4,7,8,9]")
, ("++", "[3,4,7,8,9]")
, (":", "[5,3,4,7,8,9]")
, ("f", "5")
, ("x", "[2,3,4]")
, ("x'", "2")
, ("xs", "[3,4]")
, ("y", "[7,8,9]") ]
debug [d|
--barely comprehensible test with multiple values for x and xs
manyXs :: [Int] -> [Int] -> [Int]
manyXs x y =
case x of
x : xs ->
f x : case xs of
x : xs -> f x : xs ++ y
[] -> y
[] -> y
|]
manyXs_vars :: [(Text, Text)]
manyXs_vars =
[ ("$arg1", "[2,3,4]")
, ("$arg2", "[7,8,9]")
, ("$result", "[5,7,4,7,8,9]")
, ("++", "[4,7,8,9]")
, (":", "[5,7,4,7,8,9]")
, (":'", "[7,4,7,8,9]")
, ("f", "5")
, ("f'", "7")
, ("x", "[2,3,4]")
, ("x'", "2")
, ("x''", "3")
, ("xs", "[3,4]")
, ("xs'", "[4]")
, ("y", "[7,8,9]") ]
debug [d|
where_1 :: Int -> Int -> Int
where_1 x y = bar x + bar y
where bar z = z * z
|]
where_1_vars :: [(Text, Text)]
where_1_vars =
[ ("$arg1", "5")
, ("$arg2", "7")
, ("$result", "74")
, ("*", "25")
, ("*'", "49")
, ("+", "74")
, ("bar", "25")
, ("bar'", "49")
, ("x", "5")
, ("y", "7")
, ("z", "5")
, ("z'", "7") ]
debug [d|
where_2 :: [Int] -> [Int]
where_2 xs = fmap f xs where
f x = x * x
|]
where_2_vars :: [(Text, Text)]
where_2_vars =
[ ("$arg1", "[1,2,3,4,5,6,7,8,9]")
, ("$result", "[1,4,9,16,25,36,49,64,81]")
, ("*", "1")
, ("*'", "4")
, ("*''", "9")
, ("*'''", "16")
, ("fmap", "[1,4,9,16,25,36,49,64,81]")
, ("x", "1")
, ("x'", "2")
, ("x''", "3")
, ("x'''", "4")
, ("xs", "[1,2,3,4,5,6,7,8,9]") ]
explicit :: (Ord a, Show a) => [a] -> [a]
explicit = quicksort'
where
quicksort' arg1 = fun "quicksort" $ \t -> quicksort'' t (var t "arg1" arg1)
quicksort'' t [] = []
quicksort'' t ((var t "x" -> x):(var t "xs" -> xs)) = quicksort' lt ++ [x] ++ quicksort' gt
where (var t "lt" -> lt, var t "gt" -> gt) = partition (<= x) xs
testExample name expr testDisabled = do
_ <- return ()
putStrLn $ "Testing " ++ name
debugClear
print expr
unless testDisabled $
checkVars name expr
writeFile ("output" </> name <.> "js") . ("var trace =\n" ++) . (++ ";") =<< debugJSON
debugSave $ "output" </> name <.> "html"
-- see https://github.com/feuerbach/ansi-terminal/issues/47 as this test fails on Appveyor
-- can remove once ansi-terminal-0.8 is available in Stackage LTS (which will be v11)
try_ debugPrint
putStrLn "\n\n"
checkVars :: Show a => String -> a -> IO ()
checkVars name expr = do
trace <- getDebugTrace
let varList = map (first funName) $ getTraceVars trace
case lookup (pack name) varList of
Nothing -> fail $ "Cant find the function " ++ name ++ " in the trace"
Just vars ->
case lookup name expectedVars of
Nothing -> fail $ "Can't find the list of expected variables for the function " ++ name
Just expected -> case checkEachVar vars expected of
[] -> do
when (length vars /= length expected) $
fail $ "Expected " ++ show (length expected) ++ " variables, but found " ++ show (length vars)
return ()
xs -> fail $ "\n" ++ unlines xs
checkEachVar :: [(Text, Text)] -> [(Text, Text)] -> [String]
checkEachVar vars expected = foldr f [] vars where
f :: (Text, Text) -> [String] -> [String]
f (key, val) acc =
case lookup key expected of
Nothing -> ("Couldn't find the variable " ++ T.unpack key ++ " in the expected results") : acc
Just v -> if v /= val
then ("Expected " ++ unpack val ++ " but got " ++ unpack v ++ " for the variable " ++ unpack key) : acc
else acc
expectedVars :: [(String, [(Text, Text)])]
expectedVars = [ ("quicksort", quicksort_vars)
, ("lcm_gcd", lcm_gcd_vars)
, ("lcm_gcd_log", lcm_gcd_log_vars)
, ("case_test", case_test_vars)
, ("twoXs", twoXs_vars)
, ("manyXs", manyXs_vars)
, ("where_1", where_1_vars)
, ("where_2", where_2_vars) ]
main = do
createDirectoryIfMissing True "output"
testExample "quicksort" (quicksort "haskell") False
testExample "lcm_gcd" (lcm_gcd 6 15) False
testExample "lcm_gcd_log" (lcm_gcd_log 6 15) False
testExample "case_test" (case_test [2,3,4] [7,8,9]) False
testExample "twoXs" (twoXs [2,3,4] [7,8,9]) False
testExample "manyXs" (manyXs [2,3,4] [7,8,9]) False
testExample "where_1" (where_1 5 7) False
testExample "where_2" (where_2 [1,2,3,4,5,6,7,8,9]) False
--skipping test of quicksortBy for now, as it looks like $arg1 is missing
testExample "quicksortBy" (quicksortBy (<) "haskell") True
testExample "explicit" (explicit "haskell") True
copyFile "output/quicksort.js" "trace.js" -- useful for debugging the HTML
evaluate type1
-- evaluate type2
let a === b = if a == b then putStr "." else fail $ show (a, "/=", b)
removeExtraDigits "_quicksort_0" === "_quicksort"
removeLet let0 === "f"
removeLet let1 === "select_2"
removeLet let2 === "Data.Foldable.foldr"
removeLet let3 === "Data.Foldable.foldr"
mkLegalInfixVar "+" === "plus"
mkLegalInfixVar "<!>" === "lt_bang_gt"
mkLegalInfixVar "`lcd`" === "lcd"
mkLegalInfixVar "abc" === "abc"
putStrLn " done"
let0, let1, let2 :: String
let0 = "f"
let1 = "let (Debug.DebugTrace.var tag_0 \"_select_0\" -> _select_0_1) = select_2 p_3"
let2 = "let (Debug.DebugTrace.var tag_0 \"_foldr\" -> _foldr_1) = Data.Foldable.foldr \
\(let (Debug.DebugTrace.var tag_0 \"_select_0\" -> _select_0_2) = select_3 p_4 \
\in _select_0_2)"
let3 = "let (Debug.DebugTrace.var tag_0 \"_foldr'\" -> _foldr'_1) = (let (Debug.DebugTrace.var \tag_0 \
\\"_foldr\" -> _foldr_2) = Data.Foldable.foldr (let (Debug.DebugTrace.var tag_0 \"_select_0\" \
\-> _select_0_3) = select_4 p_5 in _select_0_3) in _foldr_2) ([], []) \
\in _foldr'_1"