packages feed

lol-typing-1.20160822: Language/LOL/Typing/Expr/Test.hs

{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE NoMonomorphismRestriction #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TupleSections #-}
module Expr.Test where

import Control.Monad (Monad(..), when)
import Data.Bool
import Data.Functor.Identity (Identity(..))
import Data.Either (Either(..))
import Data.Function (($), (.))
import Data.Functor ((<$>))
import Data.Traversable (Traversable(..))
import Data.Maybe (Maybe(..))
import Data.Eq (Eq(..))
-- import Data.Tuple (fst, snd)
import qualified Data.Foldable as Foldable
import qualified Data.List as List
-- import Data.Map.Strict (Map)
import qualified Data.Map.Strict as Map
import Data.Monoid (Monoid(..), (<>))
import Test.Tasty
import Test.Tasty.HUnit
-- import Test.HUnit hiding (test)
import System.IO (IO)
-- import Data.Sequence (Seq)
import Data.Text (Text)
-- import Control.Monad.Trans.Class (lift)
-- import qualified Data.Text    as Text
import qualified Data.Text.IO as Text
import Text.Show (Show(..))
import Prelude (error)
-- import Debug.Trace

import qualified Language.LOL.Calculus as Calc
import qualified Language.LOL.Calculus.Read as Calc
import Language.LOL.Typing
-- import qualified Language.LOL.Typing.Collect.Grammar as Collect
import Language.LOL.Typing.Expr.Calculus
import qualified Language.LOL.Typing.Lib.Data.Text.Buildable as Build
import Solver.Test (polytys_env, infer)
-- import Solver.Test (write_log, Collect_Constraints(..), Infer_Polytype(..))

tests :: TestTree
tests = testGroup "Expr"
 [ tests_Calculus
 ]

type Test_Solver
 =   Solver_Greedy_Finite Collect_Infos IO

class Calcify a where
	calcify
	 :: Bool -> a
	 -> IO (Either (Collect_Error (Info Test_Solver)
	                              (Error Test_Solver))
	               (Calc.Term TeTy_Var))
instance Calcify Expr where
	calcify logging expr = do
		ty <- infer logging expr
		case ty of
		 Left err -> return $ Left err
		 Right Solver_Result
			 { solver_result_monotypes   = monosub
			 , solver_result_polytypes   = polysub
			 , solver_result_quantifiers = quants
			 } -> do
			let monoty = monosub `substitute` Monotype_Var 0
			let foralls = subvars monoty
			let consts = Map.unions
				 ( (Map.fromList $ (, ()) <$> List.take (List.length foralls) const_pool)
				 : ((\(name, polyty) -> Map.insert name () $ monoconsts polyty) <$> polytys_env) )
			let Syn_Expr
				 { term_Syn_Expr = term
				 } = wrap_Expr (sem_Expr expr) Inh_Expr
				 { env_Inh_Expr         = mempty
				 , freshvar_Inh_Expr    = 0
				 , monoconsts_Inh_Expr  = consts
				 , monotys_Inh_Expr     = monosub
				 , polytys_Inh_Expr     = (monosub `substitute`) <$> polysub
				 , quantifiers_Inh_Expr = (monosub `substitute`) . (Monotype_Var <$>) <$> quants
				 , renames_Inh_Expr     = mempty
				 }
			return $ Right $
				Foldable.foldr
				 (\(quant, const) ->
					Calc.Term_Abst (Calc.Suggest const)
					 (Calc.Type_Sort Calc.sort_star_mono) .
					((\v -> case v of
						 TeTy_Var_Monovar m | m == quant -> Just (Calc.Suggest const)
						 _ -> Nothing) `Calc.abstract`))
				 term
				 (List.zip foralls const_pool)
instance Calcify Decl where
	calcify logging decl = do
		ty <- infer logging decl
		case ty of
		 Left err -> return $ Left err
		 Right Solver_Result
			 { solver_result_monotypes   = monosub
			 , solver_result_polytypes   = polysub
			 , solver_result_quantifiers = quants
			 } -> do
			let monoty  = monosub `substitute` Monotype_Var 1 -- NOTE: decl.freshvar
			let foralls = subvars monoty
			let consts = Map.unions
				 ( (Map.fromList $ (, ()) <$> List.take (List.length foralls) const_pool)
				 : ((\(name, polyty) -> Map.insert name () $ monoconsts polyty) <$> polytys_env) )
			let Syn_Decl
				 { term_Syn_Decl = term
				 } = wrap_Decl (sem_Decl decl) Inh_Decl
				 { env_Inh_Decl         = mempty
				 , freshvar_Inh_Decl    = 0
				 , monoconsts_Inh_Decl  = consts
				 , monotys_Inh_Decl     = monosub
				 , polytys_Inh_Decl     = (monosub `substitute`) <$> polysub
				 , quantifiers_Inh_Decl = (monosub `substitute`) . (Monotype_Var <$>) <$> quants
				 , renames_Inh_Decl     = mempty
				 }
			return $ Right $
				Foldable.foldr
				 (\(quant, const) ->
					Calc.Term_Abst (Calc.Suggest const)
					 (Calc.Type_Sort Calc.sort_star_mono) .
					((\v -> case v of
						 TeTy_Var_Monovar m | m == quant -> Just (Calc.Suggest const)
						 _ -> Nothing) `Calc.abstract`))
				 term
				 (List.zip foralls const_pool)

calc_monovar_close :: Calc.Term TeTy_Var -> Either Monovar (Calc.Term Calc.Var_Name)
calc_monovar_close te =
	traverse go te
	where
		go (TeTy_Var_Monovar v) = Left v
		go (TeTy_Var_Name n) = Right n

tests_Calculus :: TestTree
tests_Calculus = testGroup "Calculus" $
	let test logging input (expected::Text) = do
		when logging $
			Text.putStrLn ("expr: " <> Build.text input)
		let expect_term =
			case runIdentity $ Calc.read Calc.parse_term expected of
			 Left err -> error $ show err
			 Right x -> x
		got_either_term <- calcify logging input
		case got_either_term of
		 got@(Left _) -> got @?= Right (TeTy_Var_Name <$> expect_term)
		 Right got_term ->
			case calc_monovar_close got_term of
			 Left m -> Left ("Monovar remains in term: " <> show m) @?= Right expect_term
			 got -> do
				when logging $ do
					Text.putStrLn ("exp: " <> Build.text expect_term)
					case got of
					 Right ex -> Text.putStrLn ("got: " <> Build.text ex)
					 _ -> return ()
				got @?= Right expect_term in
	let (==>) = test False in
	-- let (==>>) = test True in
 [ testGroup "Expr" $
	 [ testCase "id" $ ("x".-> "x")
	    ==> "λ(a:*) (x:a) -> x"
	 , testCase "id_Int" $ (("x","Int")..-> "x")
	    ==> "λ(x:Int) -> x"
	 , testCase "id_Int" $ ("x".-> ("x".:("Int"::Monotype)))
	    ==> "λ(x:Int) -> x"
	 , testCase "twice" $ ("f".-> "x".-> "f"!("f"!"x"))
	    ==> "λ(a:*) (f:a -> a) (x:a) -> f (f x)"
	 , testCase "const" $ ("x".-> "y".-> "x")
	    ==> "λ(a:*) (b:*) (x:a) (y:b) -> x"
	 , testCase "compose" $ ("f".-> "g".-> "x".-> "f"!("g"!"x"))
	    ==> "λ(a:*) (b:*) (c:*) (f:a -> b) (g:c -> a) (x:c) -> f (g x)"
	 , testCase "equal" $ ("x".-> "f".= "x" $ "f")
	    ==> "λ(a:*) (x:a) -> (λ(f:a) -> f) x"
	 , testCase "equal" $ ("x".-> "f".= ("y".->"x") $ "pair"!("f"!"True")!("f"!"one") )
	    ==> "λ(a:*) (x:a) -> (λ(f:∀(b:*) -> b -> a) -> pair a a (f Bool True) (f Int one)) (λ(b:*) (y:b) -> x)"
	 {-
	 , testCase "equal" $ ("x".-> "f".= ("y".->"x") $ "equal"!("f"!"True")!("f"!"one") )
	    ==> "λ(a:*) (x:a) -> (λ(f:∀(b:*) -> b -> a) -> equal a (f Bool True) (f Int one)) (λ(b:*) (y:b) -> x)"
	 -}
	 ]
 , testGroup "Decl" $
	 [ testCase "equal" $ Decl_Let Nothing "test"
		("x".-> "f".= ("y".-> "x") $ "equal"!("f"!"True")!("f"!"one") )
	    ==> "λ(a:*) (x:a) -> (λ(f:∀(b:*) -> b -> a) -> equal a (f Bool True) (f Int one)) (λ(b:*) (y:b) -> x)"
	 -- , testCase "Abst rename" $ Decl_Let Nothing "test" ("Bool".-> "f".= ("y".-> "Bool") $ "equal"!("f"!"True")!("f"!"one") )
	 --    ==> "λ(a:*) (x:a) -> (λ(f:∀(b:*) -> b -> a) -> equal a (f Bool True) (f Int one)) (λ(b:*) (y:b) -> x)"
	 -- , testCase "Let rename" $ Decl_Let Nothing "test" ("x".-> "Int".= ("y".-> "x") $ "equal"!("Int"!"True")!("Int"!"one") )
	 --    ==> "λ(a:*) (x:a) -> (λ(f:∀(b:*) -> b -> a) -> equal a (f Bool True) (f Int one)) (λ(b:*) (y:b) -> x)"
	 -- , testCase "equal" $ Decl_Let Nothing "test" ("x".-> "f".= ("y".-> "g".= ("z".->"y") $ "g"!"x") $ "equal"!("f"!"True")!("f"!"True") )
	 --    ==>> "λ(a:*) (x:a) -> (λ(f:∀(b:*) -> b -> b) -> equal Bool (f Bool True) (f Bool True)) (λ(b:*) (y:b) -> (λ(g:∀(c:*) -> c -> b) -> g a x) (λ(c:*) (z:c) -> y))"
	 ]
 ]