packages feed

free-theorems (empty) → 0.2

raw patch · 33 files changed

+6609/−0 lines, 33 filesdep +basedep +containersdep +haskell-srcsetup-changed

Dependencies added: base, containers, haskell-src, haskell-src-exts, mtl, pretty

Files

+ LICENSE view
@@ -0,0 +1,4 @@+This software is in the public domain.  Permission to use,  copy, modify,  and+distribute this software and its documentation for any purpose and without fee+is hereby granted,  without any conditions or restrictions.   This software is+provided "as is" without expressed or implied warranty.
+ README view
@@ -0,0 +1,57 @@++  DESCRIPTION++This library may be used to automatically generate free theorems+[1,2] from Haskell type signatures. It supports Haskell 98 and +additionally higher-rank functions. Beside primitive Haskell types+(Int, Integer, Float, Double, Char), it already includes lists and+tuples. The library provides means to add other data types.++++  DEPENDENCIES++See the file `free-theorems.cabal' for dependencies. Note that there,+two parser libraries are listed. If only one is needed, the library+is easily adjustable by commenting out the corresponding dependencies+and exported modules.+++  +  INSTALL++Since this library is cabalised, it uses the standard installation+process.+  +  runhaskell Setup.lhs configure+  runhaskell Setup.lhs build+  runhaskell Setup.lhs install++++  USAGE++See the Haddock-generated documentation for detailed information on+how to use this library.++++  DOCUMENTATION++If Haddock is available, documentation may be +generated automatically from the sources.+  +  runhaskell Setup.lhs haddock++++-------++[1] Philip Wadler, Theorems for free!, In Functional Programming+    Languages and Computer Architecture, Proceedings, 1989.++[2] Patricia Johann and Janis Voigtländer, The Impact of seq on Free+    Theorems-Based Program Transformations, In Fundamenta +    Informaticae, 2006.++
+ Setup.lhs view
@@ -0,0 +1,4 @@+#!/usr/bin/env runhaskell+ +> import Distribution.Simple+> main = defaultMain
+ free-theorems.cabal view
@@ -0,0 +1,51 @@+name:           free-theorems+version:        0.2+license:        PublicDomain+license-file:   LICENSE+author:         Sascha Boehme+maintainer:     voigt@tcs.inf.tu-dresden.de+synopsis:       Automatic generation of free theorems.+description:+    The free-theorems library allows to automatically generate free+    theorems from Haskell type expressions. It supports nearly all+    Haskell 98 types except of type constructor classes, and in+    addition it can also handle higher-rank functions. Free theorems+    are generated for three different sublanguages of Haskell, a+    basic one corresponding to the polymorphic lambda-calculus of +    Girard-Reynolds, an extension of that allowing for recursion and+    errors, and finally a sublanguage additionally allowing seq.+    In the last two sublanguages, also inequational free theorems+    may be derived in addition to classical equational results.+category:       Language+tested-with: 	GHC==6.8.2+build-type:	Simple+build-depends:+    base >= 1.0+  , mtl >= 1.0+  , haskell-src >= 1.0+  , haskell-src-exts >= 0.2.1+  , pretty >= 1.0.0.0+  , containers >= 0.1.0.1+exposed-modules:+    Language.Haskell.FreeTheorems+    Language.Haskell.FreeTheorems.Syntax+    Language.Haskell.FreeTheorems.Parser.Haskell98+    Language.Haskell.FreeTheorems.Parser.Hsx+    Language.Haskell.FreeTheorems.Theorems+other-modules:+    Language.Haskell.FreeTheorems.BasicSyntax+    Language.Haskell.FreeTheorems.ValidSyntax+    Language.Haskell.FreeTheorems.NameStores+    Language.Haskell.FreeTheorems.Frontend+    Language.Haskell.FreeTheorems.Frontend.Error+    Language.Haskell.FreeTheorems.Frontend.TypeExpressions+    Language.Haskell.FreeTheorems.Frontend.CheckLocal+    Language.Haskell.FreeTheorems.Frontend.CheckGlobal+    Language.Haskell.FreeTheorems.LanguageSubsets+    Language.Haskell.FreeTheorems.Intermediate+    Language.Haskell.FreeTheorems.Unfold+    Language.Haskell.FreeTheorems.PrettyBase+    Language.Haskell.FreeTheorems.PrettyTypes+    Language.Haskell.FreeTheorems.PrettyTheorems+hs-source-dirs: src+extensions:     Generics, DeriveDataTypeable, Rank2Types, PatternSignatures
+ runtests view
@@ -0,0 +1,3 @@+#!/bin/bash++runhaskell -isrc -fglasgow-exts src/Runtests.hs
+ src/Arbitraries.hs view
@@ -0,0 +1,195 @@++++-- | Gives instance of the class Arbitrary for several data types of the+--   library. These instances are needed by QuickCheck.+--   See also "Tests".++module Arbitraries where++++import Control.Monad+import Data.Generics (Typeable, Data)+import Test.QuickCheck++import Language.Haskell.FreeTheorems.Syntax+import Language.Haskell.FreeTheorems.LanguageSubsets++++newtype ListOfDeclarations = ListOfDeclarations +  { getDeclarations :: [Declaration] }+  deriving (Eq, Show)++instance Arbitrary ListOfDeclarations where+  arbitrary = do n <- choose (1, 100)+                 liftM ListOfDeclarations (replicateM n arbitrary)+  coarbitrary _ = id++++instance Arbitrary Declaration where+  arbitrary = oneof [ liftM DataDecl arbitrary+                    , liftM NewtypeDecl arbitrary+                    , liftM TypeDecl arbitrary+                    , liftM ClassDecl arbitrary+                    , liftM TypeSig arbitrary ]+  coarbitrary _ = id++++instance Arbitrary DataDeclaration where+  arbitrary = do n <- arbIdent id 'T'+                        [ "Bool", "Maybe", "Either", "Ordering" ]+                 v <- choose (0, 5)+                 c <- choose (1, 7)+                 liftM2 (Data n) (replicateM v arbitrary) +                                 (replicateM c arbitrary)+  coarbitrary _ = id++++instance Arbitrary NewtypeDeclaration where+  arbitrary = do n <- arbIdent id 'T' []+                 v <- choose (0, 5)+                 c <- arbIdent id 'D' []+                 liftM2 (\vs -> Newtype n vs c)+                        (replicateM v arbitrary) arbitrary+  coarbitrary _ = id++++instance Arbitrary TypeDeclaration where+  arbitrary = do n <- arbIdent id 'T'+                        [ "String", "Ordering", "Rational", "ShowS", "ReadS"+                        , "FilePath" ]+                 v <- choose (0, 5)+                 liftM2 (Type n) (replicateM v arbitrary) arbitrary+  coarbitrary _ = id++++instance Arbitrary ClassDeclaration where+  arbitrary = do c <- choose (0, 3)+                 p <- replicateM c (arbitrary :: Gen TypeClass)+                 n <- arbIdent id 'C'+                        [ "Eq", "Ord", "Num", "Integral", "Show", "Read"+                        , "Bounded", "Enum", "Real", "Fractional", "Floating"+                        , "RealFrac", "RealFloat" ]+                 s <- choose (0, 10)+                 liftM2 (Class p n) arbitrary (replicateM s arbitrary)+  coarbitrary _ = id++++instance Arbitrary Signature where+  arbitrary = do s <- arbIdent id 'f'+                        [ "not", "(&&)", "(||)", "(==)", "(/=)", "maybe"+                        , "either", "fst", "snd", "curry", "uncurry", "(<)"+                        , "(>)", "max", "min", "succ", "pred", "(+)", "(-)"+                        , "div", "mod", "pi", "id", "flip", "const", "map"+                        , "filter", "head", "tail", "length", "foldr", "foldl" ]+                 liftM (Signature s) arbitrary+  coarbitrary _ = id++++instance Arbitrary DataConstructorDeclaration where+  arbitrary = do con <- arbIdent id 'D'+                            [ "False", "True", "Left", "Right", "Nothing"+                            , "Just", "LT", "GT", "EQ" ]+                 i <- choose (0, 5)+                 liftM (DataCon con) (replicateM i arbitrary)+  coarbitrary _ = id++++instance Arbitrary BangTypeExpression where+  arbitrary = oneof [ liftM Banged arbitrary, liftM Unbanged arbitrary ]+  coarbitrary _ = id++++instance Arbitrary TypeExpression where+  arbitrary = sized arbTypeExpr+  coarbitrary _ = id++arbTypeExpr n =+  if n == 0+    then oneof [ liftM TypeVar arbitrary+               , liftM (\c -> TypeCon c []) arbitrary+               , liftM TypeExp arbitrary ]+    else frequency [ (1, arbTypeExpr 0)+                   , (2, arbComplexTypeExpr (n `div` 2))+                   ]++arbComplexTypeExpr n = oneof+  [ do r <- choose (1, 7)+       liftM2 TypeCon arbitrary (replicateM r (arbTypeExpr n))+  , liftM2 TypeFun (arbTypeExpr n) (arbTypeExpr n)+  , do c <- choose (0, 2)+       liftM3 TypeAbs arbitrary (replicateM c arbitrary) (arbTypeExpr n)+  ]++++instance Arbitrary TypeConstructor where+  arbitrary = oneof+                [ oneof +                    [ return ConUnit+                    , return ConList+                    , do n <- choose (2, 15)+                         return (ConTuple n)+                    , return ConInt+                    , return ConInteger+                    , return ConFloat+                    , return ConDouble+                    , return ConChar+                    ]+                , arbIdent Con 'T' +                    [ "Bool", "Maybe", "Either", "String", "Ordering"+                    , "Rational", "ShowS", "ReadS", "FilePath" ]+                ]+  coarbitrary _ = id++++instance Arbitrary TypeVariable where+  arbitrary = arbIdent TV 'a' ["a", "b", "c", "d", "e"]+  coarbitrary _ = id++++instance Arbitrary TypeClass where+  arbitrary = arbIdent TC 'C'+                [ "Eq", "Ord", "Num", "Integral", "Show", "Read", "Bounded"+                , "Enum", "Real", "Fractional", "Floating", "RealFrac"+                , "RealFloat" ]+  coarbitrary _ = id++++instance Arbitrary FixedTypeExpression where+  arbitrary = oneof (map (return . TF . Ident) [ "t1", "t2", "t3", "t4", "t5" ])+  coarbitrary _ = id++++instance Arbitrary LanguageSubset where+  arbitrary = oneof+    [ return $ BasicSubset+    , return $ SubsetWithFix EquationalTheorem+    , return $ SubsetWithFix InequationalTheorem+    , return $ SubsetWithSeq EquationalTheorem+    , return $ SubsetWithSeq InequationalTheorem+    ]+  coarbitrary _ = id++++arbIdent :: (Identifier -> a) -> Char -> [String] -> Gen a+arbIdent f c xs =+  oneof . map (return . f . Ident) $ xs ++ map (\i -> c : show i) [1..20]++
+ src/FrontendCheckGlobalTests.hs view
@@ -0,0 +1,188 @@++++module FrontendCheckGlobalTests (tests) where++++import Control.Monad.Writer (runWriter)+import Data.Generics (everything, mkQ)+import Data.List (nub, find)+import Data.Maybe (mapMaybe, catMaybes)+import Data.Set as Set (isSubsetOf, union, empty, singleton, fromList)+import Test.QuickCheck++import Tests+import Language.Haskell.FreeTheorems.Syntax+import Language.Haskell.FreeTheorems.Frontend as FT +import Language.Haskell.FreeTheorems.Frontend.CheckGlobal+++++-- | Runs all tests.++tests :: IO ()+tests = do+  doTest "check global at most one declaration per name"+    prop_checkGlobalAtMostOneDeclPerName+  doTest "check global arities match" prop_checkGlobalAritiesMatch+  doTest "check global type class hierarchy acyclic"+    prop_checkGlobalAcyclicTypeClasses+  doTest "check global type synonyms not mutually recursive"+    prop_checkGlobalTypeSynonymsNotMutuallyRecursive+  doTest "check global only declared classes occur"+    prop_checkGlobalTypeClasses    +  doTest "check global only declared constructors occur"+    prop_checkGlobalConstructors++++++-- | Property: Check that there are no duplicate declarations by comparing the +--   names of the declarations.++prop_checkGlobalAtMostOneDeclPerName ds0 =+  checkDecls ds0 $ \ds ->+    let names = map getDeclarationName ds+     in length (nub names) == length names++++-- | Property: Checks that every type constructor is used with the arity it was+--   declared with. If any occurring type constructor is not declared, no arity+--   check is performed for it.++prop_checkGlobalAritiesMatch ds0 =+  checkDecls ds0 $ \ds ->+    everything (&&) (True `mkQ` checkArity ds) ds+    +checkArity ds t = case t of+  TypeCon con ts -> correctArity ds con (length ts)+  otherwise      -> True++correctArity ds con arity = case con of+  ConUnit    -> arity == 0+  ConList    -> arity == 1+  ConTuple n -> arity == n+  ConInt     -> arity == 0+  ConInteger -> arity == 0+  ConFloat   -> arity == 0+  ConDouble  -> arity == 0+  ConChar    -> arity == 0+  Con c      -> maybe True (== arity) (getArityFromDecl ds c)++getArityFromDecl ds c =+  case find (\d -> getDeclarationName d == c) ds of+    Just (DataDecl d)    -> Just . length . dataVars $ d+    Just (NewtypeDecl d) -> Just . length . newtypeVars $ d+    Just (TypeDecl d)    -> Just . length . typeVars $ d+    otherwise            -> Nothing++++-- | Property: Checks that the type class hierarchy is acyclic.++prop_checkGlobalAcyclicTypeClasses ds0 =+  checkDecls ds0 $ \ds ->+    hasCycle classDeps ds++classDeps d = case d of+  ClassDecl d -> map (\(TC c) -> c) (superClasses d)+  otherwise   -> []++++-- | Property: Checks that type synonyms are not mutually recursively declared.++prop_checkGlobalTypeSynonymsNotMutuallyRecursive ds0 =+  checkDecls ds0 $ \ds ->+    hasCycle (typeDeps (mapMaybe getTypeSynName ds)) ds++getTypeSynName d = case d of+  TypeDecl d -> Just (typeName d)+  otherwise  -> Nothing++typeDeps ds = everything (++) ([] `mkQ` getTypeCon)+  where+    getTypeCon t = case t of+      TypeCon (Con c) _ -> if c `elem` ds then [c] else []+      otherwise         -> []++++-- | Property: Check that every occurring type class is declared.++prop_checkGlobalTypeClasses ds0 =+  checkDecls ds0 $ \ds ->+    occurringClasses ds `Set.isSubsetOf` declaredClasses ds+  +occurringClasses = everything Set.union (Set.empty `mkQ` occurring)+  where+    occurring (TC c) = Set.singleton c++declaredClasses = Set.fromList . mapMaybe getClassName+  where+    getClassName d = case d of+      ClassDecl d -> Just (className d)+      otherwise   -> Nothing++++-- | Property: Check that every occurring type constructor is declared.++prop_checkGlobalConstructors ds0 =+  checkDecls ds0 $ \ds ->+    occurringCons ds `Set.isSubsetOf` declaredCons ds++occurringCons = everything Set.union (Set.empty `mkQ` occurring)+  where+    occurring con = case con of+      Con c     -> Set.singleton c+      otherwise -> Set.empty++declaredCons = Set.fromList . mapMaybe getConName+  where+    getConName d = case d of+      DataDecl d    -> Just (dataName d)+      NewtypeDecl d -> Just (newtypeName d)+      TypeDecl d    -> Just (typeName d)+      otherwise     -> Nothing++++++++-- | Runs a property on to list of declarations. The first list is checked+--   and then fed to 'checkGlobal' along with the second list. The result+--   and the first (checked) list are then given to the property.++checkDecls :: ListOfDeclarations -> ([Declaration] -> Bool) -> Property+checkDecls ds prop =+  let ds' = fst . runWriter . checkGlobal [] . getDeclarations $ ds+   in not (null ds') ==> prop ds'+        +++-- | Checks if the given list of declarations has any cycles. The test is based+--   on the provided function which computes the dependencies of a declaration.++hasCycle :: (Declaration -> [Identifier]) -> [Declaration] -> Bool+hasCycle deps ds = +  any (\d -> cycle (length ds) d d) ds+  where+    cycle i d1 d2 =+      if i == 0+        then False+        else null (deps d2)+             || getDeclarationName d1 `elem` deps d2+             || any (cycle (i-1) d1) (declsDependingOn d2)++    declsDependingOn d =+      filter (\d' -> getDeclarationName d' `elem` deps d) ds+++
+ src/FrontendCheckLocalTests.hs view
@@ -0,0 +1,425 @@++++module FrontendCheckLocalTests (tests) where++++import Control.Monad.Writer (runWriter)+import Data.Generics (Typeable, Data, everything, mkQ)+import Data.List (nub)+import Data.Maybe (fromJust, isJust, mapMaybe)+import Data.Set as Set+    (Set, empty, union, fromList, isSubsetOf, member, singleton)+import Test.QuickCheck++import Tests++import Language.Haskell.FreeTheorems.Syntax+import Language.Haskell.FreeTheorems.Frontend+import Language.Haskell.FreeTheorems.Frontend.Error+import Language.Haskell.FreeTheorems.Frontend.TypeExpressions+import Language.Haskell.FreeTheorems.Frontend.CheckLocal+import Language.Haskell.FreeTheorems.Frontend.CheckGlobal++++-- | Runs all tests.++tests :: IO ()+tests = do++  doTest "check local data free variables" prop_checkLocalDataFreeVars+  doTest "check local data distinct variables" prop_checkLocalDataVars+  doTest "check local data not primitive" prop_checkLocalDataNotPrim+  doTest "check local data no fixed types" prop_checkLocalDataNoFixedTEs+  doTest "check local data has data constructor" prop_checkLocalDataNotEmpty+  doTest "check local data not nested" prop_checkLocalDataNotNested+  doTest "check local data no function nor abstraction"+    prop_checkLocalDataAbsFun++  doTest "check local newtype free variables" prop_checkLocalNewtypeFreeVars+  doTest "check local newtype distinct variables" prop_checkLocalNewtypeVars+  doTest "check local newtype not primitive" prop_checkLocalNewtypeNotPrim+  doTest "check local newtype no fixed types" prop_checkLocalNewtypeNoFixedTEs+  doTest "check local newtype not nested" prop_checkLocalNewtypeNotNested+  doTest "check local newtype no function nor abstraction"+    prop_checkLocalNewtypeAbsFun++  doTest "check local type free variables" prop_checkLocalTypeFreeVars+  doTest "check local type distinct variables" prop_checkLocalTypeVars+  doTest "check local type not primitive" prop_checkLocalTypeNotPrim+  doTest "check local type no fixed types" prop_checkLocalTypeNoFixedTEs+  doTest "check local type not nested" prop_checkLocalTypeNotNested++  doTest "check local class methods distinct"+    prop_checkLocalClassMethodsDistinct+  doTest "check local class variable is free in methods"+    prop_checkLocalClassFreeVar+  doTest "check local class not recursive" prop_checkLocalClassNotRecursive+  doTest "check local class not primitive" prop_checkLocalClassNotPrim+  doTest "check local class no fixed types" prop_checkLocalClassNoFixedTEs++  doTest "check local signature no fixed types"+    prop_checkLocalSignatureNoFixedTEs++   +++++------- Test local checks -----------------------------------------------------+++-- | Property: Checks in data declarations that free variables of the right-hand+--   side are declared on the left-hand side.++prop_checkLocalDataFreeVars d = +  checkInData d $ \d' ->+    allFreeVars (dataCons d') `Set.isSubsetOf` Set.fromList (dataVars d')+  where+    allFreeVars :: (Typeable a, Data a) => a -> Set.Set TypeVariable+    allFreeVars = everything (Set.union) (Set.empty `mkQ` freeVars)++    freeVars :: BangTypeExpression -> Set.Set TypeVariable+    freeVars = freeTypeVariables . withoutBang++++-- | Property: Checks in data declarations that the left-hand side variables are+--   pairwise distinct.++prop_checkLocalDataVars d =+  checkInData d $ \d' ->+    length (nub (dataVars d')) == length (dataVars d')++++-- | Property: Checks in data declarations that the declared type constructor is+--   not a primitive type.++prop_checkLocalDataNotPrim d =+  checkInData d $ \d' ->+    isNotPrimitive (dataName d')++++-- | Property: Checks in data declarations that no FixedTypeExpression occurs+--   anywhere.++prop_checkLocalDataNoFixedTEs d =+  checkInData d $ \d' ->+    not (hasFixedTypeExpressions d')++++-- | Property: Checks that data declarations have at least one data constructor.++prop_checkLocalDataNotEmpty d =+  checkInData d $ \d' ->+    not (null (dataCons d'))++++-- | Property: Checks that data declarations are not nested.++prop_checkLocalDataNotNested d =+  checkInData d $ \d' ->+    not (isNested (dataName d') (dataCons d'))++++-- | Property: Checks in newtype declarations that variables of the right-hand+--   side are declared on the left-hand side.++prop_checkLocalNewtypeFreeVars d =+  checkInNewtype d $ \d' ->+    freeTypeVariables (newtypeRhs d') +        `Set.isSubsetOf` Set.fromList (newtypeVars d')++++-- | Property: Checks in newtype declarations that left-hand side variables+--   are pairwise distinct.++prop_checkLocalNewtypeVars d =+  checkInNewtype d $ \d' ->+    length (nub (newtypeVars d')) == length (newtypeVars d')++++-- | Property: Checks in newtype declarations that the declared type constructor+--   is not equal to the name of a primitive type.++prop_checkLocalNewtypeNotPrim d =+  checkInNewtype d $ \d' ->+    isNotPrimitive (newtypeName d')++++-- | Property: Checks in newtype declarations that no FixedTypeExpression +--   occurs.++prop_checkLocalNewtypeNoFixedTEs d =+  checkInNewtype d $ \d' ->+    not (hasFixedTypeExpressions d')++++-- | Property: Checks that newtype declarations are not nested.++prop_checkLocalNewtypeNotNested d =+  checkInNewtype d $ \d' ->+    not (isNested (newtypeName d') (newtypeRhs d'))++++-- | Property: Checks in type declarations that variables of the right-hand+--   side are declared on the left-hand side.++prop_checkLocalTypeFreeVars d =+  checkInType d $ \d' ->+    freeTypeVariables (typeRhs d') `Set.isSubsetOf` Set.fromList (typeVars d')++++-- | Property: Checks in type declarations that left-hand side variables+--   are pairwise distinct.++prop_checkLocalTypeVars d =+  checkInType d $ \d' ->+    length (nub (typeVars d')) == length (typeVars d')++++-- | Property: Checks in type declarations that the declared type constructor+--   is not equal to the name of a primitive type.++prop_checkLocalTypeNotPrim d =+  checkInType d $ \d' ->+    isNotPrimitive (typeName d')++++-- | Property: Checks in type declarations that no FixedTypeExpression +--   occurs.++prop_checkLocalTypeNoFixedTEs d =+  checkInType d $ \d' ->+    not (hasFixedTypeExpressions d')++++-- | Property: Checks that type declarations are not recursive.++prop_checkLocalTypeNotNested d =+  checkInType d $ \d' ->+    not (isRecursive (typeName d') (typeRhs d'))++++-- | Property: Checks in class declarations that the class methods have pairwise+--   distinct names.++prop_checkLocalClassMethodsDistinct d =+  checkInClass d $ \d' ->+    let methodNames = map signatureName (classFuns d')+     in length (nub methodNames) == length methodNames++++-- | Property: Checks in class declarations that the class variable occurs free+--   in all class method types.++prop_checkLocalClassFreeVar d =+  checkInClass d $ \d' ->+    let set = Set.singleton (classVar d')+     in all (\t -> (classVar d') `Set.member` freeTypeVariables t)+            (map signatureType (classFuns d'))++++-- | Property: Checks in class declarations that the class name does not occur+--   in a type expression of any class method.++prop_checkLocalClassNotRecursive d =+  checkInClass d $ \d' ->+    not (isRecursive (className d') (classFuns d'))    ++++-- | Property: Checks in class declarations that the declared class name+--   is not equal to the name of a primitive type.++prop_checkLocalClassNotPrim d =+  checkInClass d $ \d' ->+    isNotPrimitive (className d')++++-- | Property: Checks in class declarations that no FixedTypeExpression +--   occurs.++prop_checkLocalClassNoFixedTEs d =+  checkInClass d $ \d' ->+    not (hasFixedTypeExpressions d')++++-- | Property: Checks in type signatures that no FixedTypeExpression occurs.++prop_checkLocalSignatureNoFixedTEs d =+  checkInSignature d $ \d' ->+    not (hasFixedTypeExpressions d')++++-- | Property: Checks in data declarations that there is no type abstraction+--   and no function type constructor.++prop_checkLocalDataAbsFun d = prop_checkAbsFun [DataDecl d] forcedCheck+  where types = d :: DataDeclaration++++-- | Property: Checks in newtype declarations that there is no type abstraction+--   and no function type constructor.++prop_checkLocalNewtypeAbsFun d = prop_checkAbsFun [NewtypeDecl d] forcedCheck+  where types = d :: NewtypeDeclaration++++-- | Helper function to check that a value does not contain type abstractions+--   nor function type constructors.++prop_checkAbsFun d test = test hasNoAbsNorFun . runCheck $ d+  where+    runCheck = fst . runWriter . checkDataAndNewtypeDeclarations+    +    hasNoAbsNorFun = everything (&&) (True `mkQ` noAbsFun)+    +    noAbsFun t = case t of+      TypeFun _ _   -> False+      TypeAbs _ _ _ -> False+      otherwise     -> True++++++-- Test helper functions ------------------------------------------------------+++-- | Runs a local check on a data declaration.++checkInData :: DataDeclaration -> (DataDeclaration -> Bool) -> Property+checkInData d prop = +  trivialCheck prop . mapMaybe toData . runLocalCheck $ [DataDecl d]+  where+    toData d = case d of { DataDecl d' -> Just d' ; otherwise -> Nothing }++++-- | Runs a local check on a newtype declaration.++checkInNewtype :: NewtypeDeclaration -> (NewtypeDeclaration -> Bool) -> Property+checkInNewtype d prop =+  forcedCheck prop . mapMaybe toNewtype . runLocalCheck $ [NewtypeDecl d]+  where+    toNewtype d = case d of { NewtypeDecl d' -> Just d' ; otherwise -> Nothing }++++-- | Runs a local check on a type declaration.++checkInType :: TypeDeclaration -> (TypeDeclaration -> Bool) -> Property+checkInType d prop = +  forcedCheck prop . mapMaybe toType . runLocalCheck $ [TypeDecl d]+  where+    toType d = case d of { TypeDecl d' -> Just d' ; otherwise -> Nothing }++++-- | Runs a local check on a class declaration.++checkInClass :: ClassDeclaration -> (ClassDeclaration -> Bool) -> Property+checkInClass d prop = +  forcedCheck prop . mapMaybe toClass . runLocalCheck $ [ClassDecl d]+  where+    toClass d = case d of { ClassDecl d' -> Just d' ; otherwise -> Nothing }++++-- | Runs a local check on a type signature.++checkInSignature :: Signature -> (Signature -> Bool) -> Property+checkInSignature d prop = +  forcedCheck prop . mapMaybe toSig . runLocalCheck $ [TypeSig d]+  where+    toSig d = case d of { TypeSig d' -> Just d' ; otherwise -> Nothing }++++-- | Runs a check on the head of a list. This check forces the list ot have at+--   least one element.++forcedCheck :: (a -> Bool) -> [a] -> Property+forcedCheck prop xs = not (null xs) ==> prop (head xs)++++-- | Runs a trivial check on a list. If the list is empty, the property is not+--   checked.++trivialCheck :: (a -> Bool) -> [a] -> Property+trivialCheck prop xs = +  trivial (null xs) (if null xs then True else prop (head xs))++++-- | Runs a local check.++runLocalCheck :: [Declaration] -> [Declaration]+runLocalCheck = fst . runWriter . checkLocal++++-- | Tests if an identifier is not equal to one of the primitive type names.++isNotPrimitive :: Identifier -> Bool+isNotPrimitive i =+  unpackIdent i `notElem` [ "Int", "Integer", "Float", "Double", "Char" ]++++-- | Tests if a given element contains FixedTypeExpressions.++hasFixedTypeExpressions :: (Typeable a, Data a) => a -> Bool+hasFixedTypeExpressions =+  everything (||) (False `mkQ` (const True :: FixedTypeExpression -> Bool))++++-- | Tests if an element is nested.++isNested :: (Typeable a, Data a) => Identifier -> a -> Bool+isNested con = everything (||) (False `mkQ` nested)+  where+    nested t = case t of+      TypeCon (Con c) ts -> (c == con) && (any (not . isTypeVar) ts)+      otherwise          -> False++    isTypeVar t = case t of+      TypeVar _ -> True+      otherwise -> False++++-- | Tests if an element is recursive.++isRecursive :: (Typeable a, Data a) => Identifier -> a -> Bool+isRecursive con = everything (||) (False `mkQ` (\c -> c == con))+++
+ src/FrontendOtherTests.hs view
@@ -0,0 +1,77 @@+++module FrontendOtherTests (tests) where++++import Control.Monad (liftM)+import Control.Monad.Writer (runWriter)+import Data.Generics (gcount, mkQ)+import Data.Maybe (mapMaybe)+import Data.Set as Set (isSubsetOf)+import Test.QuickCheck ++import Tests++import Language.Haskell.FreeTheorems.Syntax+import Language.Haskell.FreeTheorems.ValidSyntax+import Language.Haskell.FreeTheorems.Frontend as FT+import Language.Haskell.FreeTheorems.Frontend.TypeExpressions+import Language.Haskell.FreeTheorems.Frontend.CheckLocal+import Language.Haskell.FreeTheorems.Frontend.CheckGlobal++++-- | Runs all tests.++tests :: IO ()+tests = do+  doTest "replaceTypeSynonyms is complete" prop_replaceTypeSynonymsIsComplete+  doTest "check is stable" prop_checkIsStable+++++++-- | Property: Replacing every type synonym in a type expression does not leave+--   any type synonym.++prop_replaceTypeSynonymsIsComplete ds = +  withTypeDecls ds $ \ts ->+    let ds' = getDeclarations ds+    in countTypeConSyn ts (replaceAllTypeSynonyms ts ds') == 0++countTypeConSyn ts = gcount (False `mkQ` select)+  where +    select con = case con of+      Con c     -> c `elem` map typeName ts+      otherwise -> False++++-- | Checks a list of declarations and filters all type declarations which then+--   are fed to a property.++withTypeDecls :: ListOfDeclarations -> ([TypeDeclaration] -> Bool) -> Property+withTypeDecls ds prop =+  let getTypeSyn d = case d of { TypeDecl d -> Just d ; otherwise -> Nothing }+      process ds = fst . runWriter $ checkLocal ds >>= checkGlobal []+      typeSyns = mapMaybe getTypeSyn . process . getDeclarations $ ds+   in trivial (null typeSyns) $ prop typeSyns+++++-- | Property: Checks that applying 'check' twice does not more that applying+--   'check once.++prop_checkIsStable ds = +  let once  ds = FT.check ds+      twice ds = FT.check . map rawDeclaration =<< FT.check ds+      count f = length . fst . runWriter . f . getDeclarations $ ds+   in count once == count twice ++++
+ src/FrontendTypeExpressionsTests.hs view
@@ -0,0 +1,314 @@++++module FrontendTypeExpressionsTests (tests) where++++import Data.Generics (everything, something, somewhere, mkQ, gcount, mkM)+import Data.Map as Map (elems, keysSet, singleton, insert, fromList)+import Data.Set as Set+    ( isSubsetOf, union, member, null, intersection, fromList, difference+    , empty, size, singleton )++import Tests+import Language.Haskell.FreeTheorems.Syntax+import Language.Haskell.FreeTheorems.Frontend.TypeExpressions++++-- | Runs all tests.++tests :: IO ()+tests = do+  doTest "freeVariables are in allVariables" prop_freeVariablesAllVariables+  doTest "every variable is free or bound" prop_freeVariablesBoundVariables+  doTest "allVariables finds at least one variable of a type "+    prop_variablesAreFound+  doTest "closure binds free variables" prop_closureVariables+  doTest "freeVariables . closure == []" prop_closureFreeVariables+  doTest "closureFor emptySet == id" prop_closureForEmptySet+  doTest "count after closure is correct" prop_closureCount+  doTest "createNewTypeVariable creates unique variables"+    prop_createNewTypeVariableNotIn+  doTest"count after alphaConversion is correct" prop_alphaConversionCount+  doTest "alphaConversion keeps freeVariables" prop_alphaConversionFreeVariables+  doTest "alphaConversion substitutes a variable" prop_alphaConversionVariable+  doTest "substituteVariable replaces free variable"+    prop_substituteTypeVariableReplacesFreeVariables+  doTest "substituteVariable keeps free variables"+    prop_substituteTypeVariableKeepsFreeVariables+  doTest "type is contained after substituteTypeVariables"+    prop_substituteTypeVariableTypeInserted+  doTest "count after substituteVariable is correct"+    prop_substituteTypeVariableWithCount++++++------- Test properties -------------------------------------------------------+++-- | Property: Free variables of a type expression are a subset of all+--   type variables occurring in a type expression.++prop_freeVariablesAllVariables t = +  freeTypeVariables t `Set.isSubsetOf` allTypeVariables t+  where types = t :: TypeExpression++++-- | Property: Every type variable occurring in a type expression is free or+--   bound.++prop_freeVariablesBoundVariables t = (free `Set.union` bound) == all+  where +    types = t :: TypeExpression+    free  = freeTypeVariables t+    all   = allTypeVariables  t+    bound = boundVariables t++++-- | Property: If a type expression contains at least one variable, it is also+--   found by allTypeVariables.++prop_variablesAreFound t =+  maybe True (\v -> v `Set.member` allTypeVariables t) (getVariableFrom t)+  where +    types = t :: TypeExpression++++-- | Property: Given a set of type variables, the closure of a type expression+--   for this set does not contain free type variables in that set.++prop_closureVariables t vs =+  Set.null (set `Set.intersection` freeTypeVariables (closureFor set t))+  where+    types = (t :: TypeExpression, vs :: [TypeVariable])+    set = Set.fromList vs++++-- | Property: There are no free type variables after closing a type expression+--   for all free type variables.++prop_closureFreeVariables t =+  Set.null . freeTypeVariables . closureFor (freeTypeVariables t) $ t+  where+    types = t :: TypeExpression++++-- | Property: The closure for an empty set of type variables does not change a+--   type expression.++prop_closureForEmptySet t = closureFor Set.empty t == t+  where+    types = t :: TypeExpression+  +++-- | Property: The count of type constructors does not increase by the closure.++prop_closureCount t vs = +  countTypeAbs t + Set.size set == countTypeAbs (closureFor set t)+  && countTypeFun t == countTypeFun (closureFor set t)+  && countTypeCon t == countTypeCon (closureFor set t)+  where+    types = (t :: TypeExpression, vs :: [TypeVariable])+    set = Set.fromList vs++++-- | Property: A newly created type variable does not occur in the list of+--   forbidden type variables.++prop_createNewTypeVariableNotIn vs =+  not (createNewTypeVariableNotIn s `Set.member` s)+  where+    types = vs :: [TypeVariable]+    s = Set.fromList vs++++-- | Property: Alpha conversion does not increase or decrease the number of+--   type constructors.++prop_alphaConversionCount t v =+  countTypeCon t == countTypeCon (alphaConversion v t)+  && countTypeAbs t == countTypeAbs (alphaConversion v t)+  && countTypeFun t == countTypeFun (alphaConversion v t)+  where types = (t :: TypeExpression, v :: TypeVariable)++++-- | Property: Alpha conversion keeps free variables free.++prop_alphaConversionFreeVariables t v =+  freeTypeVariables t == freeTypeVariables (alphaConversion v t)+  where types = (t :: TypeExpression, v :: TypeVariable)++++-- | Property: After alpha conversion, the replaced variable does not occur+--   in the type expression (except as free variable) and is not bound anymore.++prop_alphaConversionVariable t v =+  (v `Set.member` freeAfterAC || not (v `Set.member` allAfterAC))+  && not (v `Set.member` boundVariables acT)+  where+    types = (t :: TypeExpression, v :: TypeVariable)+    acT = alphaConversion v t+    allAfterAC  = allTypeVariables acT+    freeAfterAC = freeTypeVariables acT++++-- | Property: Replacing type variables with (closed) type expressions in a+--   type expression removes the free variables from the latter type+--   expression (as long as they are not free in any type expression which is+--   inserted).++prop_substituteTypeVariableReplacesFreeVariables (v1,v2) (t1,t2) t =+  (not (v1 `Set.member` vs)+   || v1 `Set.member` freeTypeVariables (fooCon (Map.elems m))+   || not (v1 `Set.member` freeTypeVariables rt))+  &&+  (not (v2 `Set.member` vs)+   || v2 `Set.member` freeTypeVariables  (fooCon (Map.elems m))+   || not (v2 `Set.member` freeTypeVariables rt))++  where+    types = (v1 :: TypeVariable, v2 :: TypeVariable,+             t1 :: TypeExpression, t2 :: TypeExpression,+             t :: TypeExpression)+    m  = Map.fromList [(v1,t1), (v2,t2)]+    vs = Map.keysSet m+    rt = substituteTypeVariables m t+    fooCon = TypeCon (Con $ Ident "Foo")+    +++-- | Property: Replacing a type variable in a type expression keeps the other+--   free variables free.++prop_substituteTypeVariableKeepsFreeVariables (v1,v2) (t1,t2) t i =+  (freeT `Set.difference` vs) `Set.isSubsetOf` freeR+  where+    types = (v1 :: TypeVariable, v2 :: TypeVariable,+             t1 :: TypeExpression, t2 :: TypeExpression, +             t :: TypeExpression, i :: Int)+    m1 = Map.singleton v1 t1+    m  = if i `mod` 2 == 0 then m1 else Map.insert v2 t2 m1+    vs = Map.keysSet m+    rt = substituteTypeVariables m t+    freeT = freeTypeVariables t+    freeR = freeTypeVariables rt++++-- | Property: If a type variable which should be substituted by a type+--   expression is free, then the type expression must occur in the result.++prop_substituteTypeVariableTypeInserted (v1,v2) (t1,t2) t =+  (not (v1 `Set.member` freeTypeVariables t) || t1 `occursIn` rt)+  &&+  (not (v2 `Set.member` freeTypeVariables t) || t2 `occursIn` rt)+  where+    types = (v1 :: TypeVariable, v2 :: TypeVariable,+             t1 :: TypeExpression, t2 :: TypeExpression,+             t :: TypeExpression)+    m  = Map.fromList [(v1,t1), (v2,t2)]+    vs = Map.keysSet m+    rt = substituteTypeVariables m t++++-- | Property: Replacing a type variable increases only the number of type+--   constructors.++prop_substituteTypeVariableWithCount (v1,v2) (t1,t2) t i =+  countTypeFun rt >= countTypeFun t+  && countTypeAbs rt >= countTypeAbs t+  && countTypeCon rt >= countTypeCon t+  where+    types = (v1 :: TypeVariable, v2 :: TypeVariable,+             t1 :: TypeExpression, t2 :: TypeExpression, +             t :: TypeExpression, i :: Int)+    m1 = Map.singleton v1 t1+    m  = if i `mod` 2 == 0 then m1 else Map.insert v2 t2 m1+    vs = Map.keysSet m+    rt = substituteTypeVariables m t++++++-- Test helper functions ------------------------------------------------------++++-- | Returns a set of all bound variables of a type expression.++boundVariables t = everything Set.union (Set.empty `mkQ` select) t+  where+    select t = case t of+      TypeAbs v _ _ -> Set.singleton v +      otherwise     -> Set.empty++++-- | Returns the first variable found in a type expression.+getVariableFrom t = something (Nothing `mkQ` select) t+  where+    select t = case t of+      TypeVar v     -> Just v+      TypeAbs v _ _ -> Just v+      otherwise     -> Nothing++++-- | Counts the number of user-defined type constructors.++countTypeCon t = gcount (False `mkQ` select) t+  where+    select t = case t of+      TypeCon _ _ -> True+      otherwise   -> False++++-- | Counts the number of function type constructors.++countTypeFun t = gcount (False `mkQ` select) t+  where+    select t = case t of+      TypeFun _ _ -> True+      otherwise   -> False++++-- | Counts the number of type abstraction constructors. ++countTypeAbs t = gcount (False `mkQ` select) t+  where+    select t = case t of+      TypeAbs _ _ _ -> True+      otherwise     -> False++++-- | Returns True if t1 occurs in t2.++t1 `occursIn` t2 = case somewhere (mkM $ findT t2) t1 of+  Nothing -> False+  Just _  -> True+  where+    findT t1 t2 = if (t1 == t2) then Just t1 else Nothing++++
+ src/InterpretationTests.hs view
@@ -0,0 +1,100 @@+++++module InterpretationTests (tests) where++++tests :: IO ()+tests = do+  return ()+++++{-+-- Helper functions -----------------------------------------------------------++++-- | Counts the number of relational actions of the function type constructor.++countRelFun t = gcount (False `mkQ` select) t+  where+    select t = case t of+      RelFun _ _ _ -> True+      otherwise    -> False++++-- | Counts the number of relational actions of the type abstraction+--   constructor.++countRelAbs t = gcount (False `mkQ` select) t+  where+    select t = case t of+      RelAbs _ _ _ -> True+      otherwise    -> False++++-- | Counts the number of relational actions of nullary type constructors.++countRelBasic t = gcount (False `mkQ` select) t+  where+    select t = case t of+      RelBasic _   -> True+      otherwise -> False++++-- | Counts the number of relational actions for n-ary type constructors.++countRelLift t = gcount (False `mkQ` select) t+  where+    select t = case t of+      RelLift _ _ -> True+      otherwise   -> False++++-- Define the test properties -------------------------------------------------++++-- | Property: The number of type constructors must equal the number of+--   corresponding relations.++prop_interpretCount t l =+  countTypeFun t' == countRelFun rel+  && countTypeAbs t' == countRelAbs rel+  && countTypeCon t' == countRelLift rel + countRelBasic rel+  where+    types = (t :: TypeExpression, l :: LanguageSubset)+    t' = closure t+    sig = ValidSignature (Signature (Ident "x") t')+    Intermediate _ rel = interpret l sig++++-- Run the tests --------------------------------------------------------------++++-- | The main function which runs the list of tests.++main = do+  t "count after interpretation is ok                   "+    prop_interpretCount++  where+    t desc prop = do+      putStr $ desc ++ " ... "+      -- quickCheck prop+      check (defaultConfig {configMaxTest = 100}) prop++-}+++
+ src/Language/Haskell/FreeTheorems.hs view
@@ -0,0 +1,197 @@++++-- | Data structures and functions to automatically generate free theorems.+--+--   This library is based on the following papers:+--+--   * /Theorems For Free!/, Philip Wadler, in Functional Programming Languages+--     and Computer Architecture Proceedings, 1989.+--     <http://homepages.inf.ed.ac.uk/wadler/papers/free/free.ps>+--+--   * /The Impact of seq on Free Theorems-Based Program Transformations/,+--     Patricia Johann and Janis Voigtl&#xE4;nder, Fundamenta Informaticae,+--     2006. <http://www.orchid.inf.tu-dresden.de/~voigt/seqFinal.pdf>+--+--+--   The intended usage of this library is as follows.+--   +--   (1) Parse a list of declarations using one of two parsers +--       ('Language.Haskell.FreeTheorems.Parser.Haskell98.parse' or +--       'Language.Haskell.FreeTheorems.Parser.Hsx.parse') or any other+--       suitable parser.+--       Use 'check' to obtain a list of valid declarations.+--+--   (2) Optional:+--       Parse more declarations and validate them against the previously +--       loaded list of valid declarations with 'checkAgainst'.+--+--   (3) Extract all valid signatures from a list of valid declarations by+--       'filterSignatures'.+--+--   (4) Interpret a signature ('interpret'), transform it to a theorem+--       ('asTheorem') and pretty-print it ('prettyTheorem').+--+--   (5) Optional: Specialise relation variables to functions +--       ('relationVariables' and 'specialise').+--+--   (6) Optional: Extract lifted relations to show their definition+--       ('unfoldLifts') and pretty-print them ('prettyUnfoldedLift').+--+--   (7) Optional: Extract class constraints to show their definition+--       ('unfoldClasses') and pretty-print them ('prettyUnfoldedClass').+--++module Language.Haskell.FreeTheorems (++    -- * Valid declarations++    -- $restrictions++    ValidDeclaration+  , ValidSignature+  , rawDeclaration+  , rawSignature+  , filterSignatures++    +    -- * Manufacturing valid declarations +    +  , Parsed+  , Checked+  , runChecks+  , check+  , checkAgainst++    +    -- * Generating free theorems++  , LanguageSubset (..)+  , TheoremType (..)+  , Intermediate+  , interpret+  , asTheorem+  , relationVariables+  , specialise+  , specialiseInverse+  , unfoldLifts+  , unfoldClasses+++    -- * Pretty printing+    +    -- | The pretty printer is based on the module \"Text.PrettyPrint\" which+    --   is usually implemented by \"Text.PrettyPrint.HughesPJ\". See there for+    --   information on how to modify documents.++  , PrettyTheoremOption (..)+  , prettyDeclaration+  , prettySignature+  , prettyTheorem+  , prettyRelationVariable+  , prettyUnfoldedLift+  , prettyUnfoldedClass++) where++++import Text.PrettyPrint (Doc, empty)++import Language.Haskell.FreeTheorems.BasicSyntax+import Language.Haskell.FreeTheorems.ValidSyntax+import Language.Haskell.FreeTheorems.Frontend+import Language.Haskell.FreeTheorems.LanguageSubsets+import Language.Haskell.FreeTheorems.Intermediate+import Language.Haskell.FreeTheorems.Theorems+import Language.Haskell.FreeTheorems.Unfold+import Language.Haskell.FreeTheorems.PrettyTypes+import Language.Haskell.FreeTheorems.PrettyTheorems+++++++-- $restrictions+--+--   The restrictions on valid declarations and valid type signatures, above+--   what is already ensured by the stucture of declarations (see +--   "Language.Haskell.FreeTheorems.Syntax"), are as follows:+--+--   For @data@ and @newtype@ declarations:+--+--   * The declared type constructor is not a primitive type, i.e. it is not+--     equal to @Int@, @Integer@, @Float@, @Double@ nor @Char@.+--+--   * The variables occurring on the right-hand side have to be mentioned on+--     the left-hand side, and the left-hand side variables are pairwise+--     distinct.+--   +--   * There is at least one data constructor in the declaration of an+--     algebraic data type.+--+--   * The declaration is not nested, i.e. if the declared type constructor+--     occurs on the right-hand side, it has only type variables as arguments.+--+--   * No 'Language.Haskell.FreeTheorems.Syntax.FixedTypeExpression' occurs+--     in any type expression on the right-hand side.+--+--   * After replacing all type synonyms: No function type constructor and no+--     type abstraction occurs on the right-hand side.+--+--   For @type@ declarations:+--+--   * The declared type constructor is not a primitive type, i.e. it is not+--     equal to @Int@, @Integer@, @Float@, @Double@ nor @Char@.+--+--   * The variables occurring on the right-hand side have to be mentioned on+--     the left-hand side, and the left-hand side variables are pairwise+--     distinct.+--+--   * The declaration is not recursive, i.e. if the declared type constructor+--     occurs nowhere on the right-hand side.+--+--   * There is no group of @type@ declarations which are mutually recursive.+--+--   * No 'Language.Haskell.FreeTheorems.Syntax.FixedTypeExpression' occurs+--     in the type expression on the right-hand side.+--   +--   For @class@ declarations:+--+--   * The declared type class does not equal a primitive type.+--   +--   * The names of the class methods are pairwise distinct. +--   +--   * The class variable occurs in the type expression of every class method.+--   +--   * The name of the class does not occur in a type expression of any class+--     method.+--   +--   * No 'Language.Haskell.FreeTheorems.Syntax.FixedTypeExpression' occurs+--     in a type expression of any class method.+--+--   * The type class hierarchy is acyclic.+--+--   For type signatures:+--   +--   * No 'Language.Haskell.FreeTheorems.Syntax.FixedTypeExpression' occurs+--     in the type expression of a signature.+--+--   Additionally, the following global restrictions need to hold:+--   +--   * There may be at most one declaration only for every name.+--+--   * Every type class occurring in any type expression is declared.+--+--   * Every type constructor occurring in any type expression is declared.+--     Furthermore, the number of arguments to every type constructor has to+--     match the number of type variables the given on the left-hand side of the+--     declaration of that type constructor.+--+--   Type synonyms do not occur in type expressions of valid declarations.+--   Every type expression of a valid declaration is closed. A special case are+--   class methods. Their types have the class variable as the only free type+--   variable.++
+ src/Language/Haskell/FreeTheorems/BasicSyntax.hs view
@@ -0,0 +1,256 @@++++-- | Declares the basic syntax of a Haskell98 subset enriched with +--   higher-ranked functions. Additionally, it defines small convenience+--   functions. ++module Language.Haskell.FreeTheorems.BasicSyntax where++++import Data.Generics (Typeable, Data)++++-- | A Haskell declaration which corresponds to a @type@, @data@, @newtype@,+--   @class@ or type signature declaration.+--+--   In type expressions, type variables must not be applied to type+--   expressions. Thus, for example, the functions of the @Monad@ class are not+--   expressible.+--   However, in extension to Haskell98, higher-rank types can be expressed.+--   +--   This data type does not reflect all information of a declaration. Only the+--   aspects needed by the FreeTheorems library are covered.++data Declaration+  = TypeDecl TypeDeclaration            -- ^ A @type@ declaration.+  | DataDecl DataDeclaration            -- ^ A @data@ declaration.+  | NewtypeDecl NewtypeDeclaration      -- ^ A @newtype@ declaration.+  | ClassDecl ClassDeclaration          -- ^ A @class@ declaration.+  | TypeSig Signature                   -- ^ A type signature.+  deriving (Eq, Typeable, Data)++++-- | Gets the name of the item declared by a declaration.+--   This is the type constructor for @data@, @newtype@ and @type@ declarations,+--   the name of a class for a @class@ declaration or the name of a type+--   signature.++getDeclarationName :: Declaration -> Identifier+getDeclarationName (DataDecl d)    = dataName d+getDeclarationName (NewtypeDecl d) = newtypeName d+getDeclarationName (TypeDecl d)    = typeName d+getDeclarationName (ClassDecl d)   = className d+getDeclarationName (TypeSig s)     = signatureName s++++-- | Gets the arity of a type constructor or @Nothing@ if this is not a+--   @data@, @newtype@ or @type@ declaration.++getDeclarationArity :: Declaration -> Maybe Int+getDeclarationArity (DataDecl d)    = Just . length . dataVars $ d+getDeclarationArity (NewtypeDecl d) = Just . length . newtypeVars $ d+getDeclarationArity (TypeDecl d)    = Just . length . typeVars $ d+getDeclarationArity (ClassDecl d)   = Nothing+getDeclarationArity (TypeSig s)     = Nothing++++-- | A @type@ declaration for a type synonym.++data TypeDeclaration = Type +  { typeName :: Identifier     -- ^ The type constructor name.+  , typeVars :: [TypeVariable] -- ^ The type variables on the left-hand side.+  , typeRhs  :: TypeExpression -- ^ The type expression on the right-hand side.+  }+  deriving (Eq, Typeable, Data)++++-- | A @data@ declaration for an algebraic data type.+--+--   Note that the context and the deriving parts of a @data@ declaration are+--   ignored.++data DataDeclaration = Data +  { dataName     :: Identifier+        -- ^ The name of the type constructor.++  , dataVars     :: [TypeVariable]+        -- ^ The type variables on the left-hand side.++  , dataCons     :: [DataConstructorDeclaration]+        -- ^ The declarations of the data constructors on the right-hand side.++  }+  deriving (Eq, Typeable, Data)++++-- | A @newtype@ declaration for a type renaming.+--+--   Note that the context and the deriving parts of a @newtype@ declaration are+--   ignored.++data NewtypeDeclaration = Newtype +  { newtypeName     :: Identifier       +        -- ^ The name of the type constructor.+  +  , newtypeVars     :: [TypeVariable]   +        -- ^ The type variables of the left-hand side.+  +  , newtypeCon      :: Identifier+        -- ^ The name of the data constructor on the right-hand side.+  +  , newtypeRhs      :: TypeExpression+        -- ^ The type expression on the right-hand side.+  +  }+  deriving (Eq, Typeable, Data)++++-- | A @class@ declaration for a type class.+--+--   Note that, except of type signatures of class methods, all other+--   declarations inside the class are ignored.++data ClassDeclaration = Class +  { superClasses :: [TypeClass]     +        -- ^ The superclasses of this class.+  +  , className    :: Identifier      +        -- ^ The name of this type class.+  +  , classVar     :: TypeVariable    +        -- ^ The type variable constrained by this type class.+  +  , classFuns    :: [Signature]+        -- ^ The type signatures of the class methods.+  +  }+  deriving (Eq, Typeable, Data)++++-- | A type signature.++data Signature = Signature+  { signatureName :: Identifier     +        -- ^ The name of the signature, i.e. the name of a variable or function.+  +  , signatureType :: TypeExpression+        -- ^ The type expression of the type signature.+  +  }+  deriving (Eq, Typeable, Data)++++-- | An identifier.+--   This data type tags every @String@ occurring in a declaration or a type+--   expression.++newtype Identifier = Ident { unpackIdent :: String }+  deriving (Eq, Ord, Typeable, Data)++++-- | A data constructor declaration.++data DataConstructorDeclaration = DataCon +  { dataConName  :: Identifier+        -- ^ The name of the data constructor.+  +  , dataConTypes :: [BangTypeExpression]+        -- ^ The type arguments of the data constructor.+  +  }+  deriving (Eq, Typeable, Data)++++-- | Indicates whether in an algebraic data type declaration a strictness+--   annotation is used.++data BangTypeExpression+  = Banged { withoutBang :: TypeExpression }+      -- ^ A type expression with a strictness flag \"@!@\".++  | Unbanged { withoutBang :: TypeExpression }+      -- ^ A type expression without a strictness flag.++  deriving (Eq, Typeable, Data)++++-- | A Haskell type expression. This data type supports also higher-rank+--   functions. Unlike in Haskell98, a type variable must not be applied to+--   type expressions.++data TypeExpression+  = TypeVar TypeVariable+      -- ^ A type variable.++  | TypeCon TypeConstructor [TypeExpression]+      -- ^ A type constructor. This covers algebraic data types, type synonyms,+      --   and type renamings as well as predefined standard data types like+      --   lists and tuples.++  | TypeFun TypeExpression TypeExpression+      -- ^ The function type constructor @->@.++  | TypeAbs TypeVariable [TypeClass] TypeExpression+      -- ^ The type abstraction constructor @forall@.++  | TypeExp FixedTypeExpression+      -- ^ A variable representing a fixed type expression.++  deriving (Eq, Typeable, Data)++++-- | The data type for type constructors.++data TypeConstructor+  = ConUnit        -- ^ The unit type constructor @()@.+  | ConList        -- ^ The list type constructor @[]@.+  | ConTuple Int   -- ^ The tuple type constructors with given arity.+  | ConInt         -- ^ The Haskell type @Int@.+  | ConInteger     -- ^ The Haskell type @Integer@.+  | ConFloat       -- ^ The Haskell type @Float@.+  | ConDouble      -- ^ The Haskell type @Double@.+  | ConChar        -- ^ The Haskell type @Char@.+  | Con Identifier -- ^ Any other type constructor with a given name.+  deriving (Eq, Typeable, Data)++++-- | Identifies a Haskell type class.++newtype TypeClass = TC Identifier+  deriving (Eq, Typeable, Data)++++-- | Identifies a Haskell type variable++newtype TypeVariable = TV Identifier+  deriving (Eq, Ord, Typeable, Data)++++-- | Represents an abbreviation for some fixed type expression.+--   It does not occur in Haskell98 source code, but it can occur in generated+--   theorems.++newtype FixedTypeExpression = TF Identifier+  deriving (Eq, Typeable, Data)++++
+ src/Language/Haskell/FreeTheorems/Frontend.hs view
@@ -0,0 +1,136 @@++++-- | Defines functions to ensure that only valid declarations and type +--   signatures are fed to the FreeTheorems library. The given functions are+--   intended as second stage after parsing declarations.++module Language.Haskell.FreeTheorems.Frontend (+    Checked+  , Parsed+  , runChecks+  , check+  , checkAgainst+) where++++import Data.Generics (everything, extQ, mkQ)+import Data.List (partition, intersect)+import Data.Maybe (mapMaybe)++import Language.Haskell.FreeTheorems.Syntax+import Language.Haskell.FreeTheorems.ValidSyntax (ValidDeclaration (..))+import Language.Haskell.FreeTheorems.Frontend.Error (Checked, Parsed, runChecks)+import Language.Haskell.FreeTheorems.Frontend.TypeExpressions+    (replaceAllTypeSynonyms, closeTypeExpressions)+import Language.Haskell.FreeTheorems.Frontend.CheckLocal+    (checkLocal, checkDataAndNewtypeDeclarations)+import Language.Haskell.FreeTheorems.Frontend.CheckGlobal (checkGlobal)++++-- | Checks a list of declarations.+--   It returns a list of all declarations which are valid and an error message+--   for all those declarations which are not valid.++check :: [Declaration] -> Checked [ValidDeclaration]+check = checkAgainst []++++-- | Checks a list of declarations against a given list of valid+--   declarations.+--   It returns a list of all declarations from the second argument which are+--   valid. Moreover, the result contains an error message for all those+--   declarations which are not valid.+--+--   The declarations given in the second argument may be based on those of the+--   first argument. For example, if the first argument contains a valid+--   declaration of a type \"Foo\" and if the second argument contains the+--   following declaration+--+--   > type Bar = Foo+--+--   then also the declaration of \"Bar\" is valid.++checkAgainst :: +    [ValidDeclaration] +    -> [Declaration] +    -> Checked [ValidDeclaration]++checkAgainst vds ds = +    +    -- start from 'ds'+  return ds+   +    -- perform local checks:+    --   * free variables of the right-hand side are declared on the left-hand+    --     of declarations+    --   * type variables of the left-hand side are pairwise distinct+    --   * primitive types are not declared+    --   * FixedTypeExpression does not occur anywhere+    --   * type synonyms are not recursive+    --   * data and newtype are not nested+    --   * classes methods are pairwise distinct, don't use the owning class+    --     and have the class variable as free variable+  >>= checkLocal+  +    -- perform global checks:+    --   * at most one declaration per name+    --   * arity checks of type constructors in all type expressions+    --   * type class hierarchy is acyclic+    --   * type synonym declarations are not mutually recursive+    --   * all used constructors and classes are declared+  >>= checkGlobal vds++    -- replace all type synonyms, use also the valid type synonyms+  >>= \ds' -> +    let getTypeSyn d = case d of { TypeDecl t -> Just t ; otherwise -> Nothing }+        typeSyns = mapMaybe getTypeSyn (map rawDeclaration vds ++ ds')+     in return (replaceAllTypeSynonyms typeSyns ds')++    -- checks in data and newtype declarations: no abstractions, no functions+  >>= checkDataAndNewtypeDeclarations++    -- finally, close all type signatures and class methods and transform all+    -- declarations to valid ones+  >>= return . makeValid vds . closeTypeExpressions++++-- | Turns a list of declarations into valid declarations.+--   Additionally, every declaration is checked whether it depends on any +--   algebraic data type with strictness flags.++makeValid :: [ValidDeclaration] -> [Declaration] -> [ValidDeclaration]+makeValid vds ds = +  let strict = map rawDeclaration (filter isStrictDeclaration vds)+      knownStrict = map getDeclarationName +                        (strict ++ filter hasStrictnessFlags ds)+      +      rec ss ds = +        let (ns, os) = partition (dependsOnStrictTypes ss) ds+         in if null ns+              then ss+              else rec (ss ++ map getDeclarationName ns) os++      allStrict = rec knownStrict ds+   +   in map (\d -> ValidDeclaration d (getDeclarationName d `elem` allStrict)) ds+  where+    hasStrictnessFlags d = +      let hasBang (Banged _)   = True+          hasBang (Unbanged _) = False+       in everything (||) (False `mkQ` hasBang) d+    +    dependsOnStrictTypes ss d = +      let getCons c = case c of { Con n -> [n] ; otherwise -> [] }+          getClasses (TC n) = [n]+          ns = everything (++) ([] `mkQ` getCons `extQ` getClasses) d+       in not (null (ns `intersect` ss))++ +++
+ src/Language/Haskell/FreeTheorems/Frontend/CheckGlobal.hs view
@@ -0,0 +1,418 @@++++-- | Defines global checks, i.e. checks which need to look at more than one+--   declaration at a time.++module Language.Haskell.FreeTheorems.Frontend.CheckGlobal (checkGlobal) where++++import Control.Monad (when)+import Control.Monad.Error (throwError)+import Control.Monad.Writer (tell)+import Data.Generics (Typeable, Data, everything, everywhereM, extQ, mkQ, mkM)+import Data.List (intersperse, partition, nub, intersect)+import qualified Data.Map as Map (Map, empty, insert, lookup)+import Data.Maybe (mapMaybe, fromJust)+import qualified Data.Set as Set+    ( Set, empty, singleton, union, fromList, isSubsetOf, member, difference+    , partition, null, elems, size )++import Language.Haskell.FreeTheorems.BasicSyntax+import Language.Haskell.FreeTheorems.ValidSyntax+import Language.Haskell.FreeTheorems.Frontend.Error++++++------- Global checks ---------------------------------------------------------+++-- | Perform global checks, i.e. looks at more than one declaration at a time.+--   The following restrictions will be checked:+--+--   * Every symbol is declared at most once.+--   +--   * Every type constructor is used in the arity it was declared with.+--+--   * Type synonyms are not mutually recursive.+--+--   * The type class hierachy is acyclic.+--+--   * In every type expression, only declared type constructors and only+--     declared type classes occur.++checkGlobal :: [ValidDeclaration] -> [Declaration] -> Checked [Declaration] +checkGlobal vds ds =+  -- run through all declarations in 'ds' to test whether any name occurs twice+  checkUnique vds ds++  -- then, run through all remaining declarations and check the arities of all+  -- type constructors+  >>= checkArities vds++  -- extract all type synonyms which are not mutually recursive+  >>= checkAcyclicTypeSynonyms++  -- extract all type classes whose type hierarchy is acyclic+  >>= checkAcyclicTypeClasses++  -- finally, take only those declarations which contain only declared type+  -- constructors and type classes+  >>= checkAllConsAndClassesDeclared vds++++++------- Check that declarations are unique ------------------------------------+++-- | Checks that every name has at most one declaration, or that there are no+--   two declarations with the same name.+--+--   The first argument gives a list of already checked declarations against+--   which the second argument is tested. The resulting list contains all+--   elements of the first argument and only the valid declarations of the+--   second argument.++checkUnique :: [ValidDeclaration] -> [Declaration] -> Checked [Declaration]+checkUnique vds ds =+  let -- extract all known declaration names, both from 'vds' and from 'ds'+      knownNames = map getDeclarationName (map rawDeclaration vds ++ ds)+    +      -- test if the name of a declaration occurs more than once in 'knownNames'+      occursMoreThanOnce d = +        let allOccurrences = filter (== (getDeclarationName d)) knownNames+         in length allOccurrences > 1+ +      -- construct a list 'us' of all unique declarations and a list 'ms' of all+      -- declarations which names occur more than once+      (ms, us) = partition occursMoreThanOnce ds++      -- extract the names which occur more than once+      multiples = map unpackIdent . nub . map getDeclarationName $ ms++      error s = [pp ("Multiple declarations for `" ++ s ++ "'.")]+   +   in do when (not (null multiples)) $ mapM_ (tell . error) multiples+         return us++++++------- Check arities of type constructors ------------------------------------+++-- | Checks the arity of all type constructors. If an undeclared type+--   constructor is found, no arity check will be performed, because+--   any declaration containing undeclared type constructors will be filtered+--   out in the next step of checking (see 'checkGlobal').++checkArities :: [ValidDeclaration] -> [Declaration] -> Checked [Declaration]+checkArities vds ds =+  let -- build a map of arities+      mkMap d m = case getDeclarationArity d of+                    Nothing     -> m+                    Just arity  -> Map.insert (getDeclarationName d) arity m+      arityMap = foldr mkMap Map.empty (map rawDeclaration vds ++ ds)++   in foldChecks (\d -> inDecl d $ checkArity arityMap d) ds++++-- | Checks the arities of all occurring type constructors according to the +--   given arity map.++checkArity :: (Typeable a, Data a) => Map.Map Identifier Int -> a -> ErrorOr a+checkArity arityMap = everywhereM (mkM checkCorrectArity)+  where+    -- extracts the type constructors and relates expected and found arities+    checkCorrectArity t = case t of+      TypeCon ConUnit ts    -> errorArity t "()"      0 (length ts)+      TypeCon ConList ts    -> errorArity t "[]"      1 (length ts)+      TypeCon ConInt ts     -> errorArity t "Int"     0 (length ts)+      TypeCon ConInteger ts -> errorArity t "Integer" 0 (length ts)+      TypeCon ConFloat ts   -> errorArity t "Float"   0 (length ts)+      TypeCon ConDouble ts  -> errorArity t "Double"  0 (length ts)+      TypeCon ConChar ts    -> errorArity t "Char"    0 (length ts)+      TypeCon (Con c) ts    -> case Map.lookup c arityMap of+                                 Nothing -> return t+                                 Just i  -> let n = unpackIdent c+                                             in errorArity t n i (length ts)+      +      TypeCon (ConTuple n) ts -> do+        errorArity t ("(" ++ replicate (n-1) ',' ++ ")") n (length ts)+        errorIf (n < 2) $+          pp "A tuple type constructor must have at least two arguments."+        return t+                                 +      otherwise             -> return t++    -- performs the actual checking and error message creation+    errorArity t conName expected found = +      let args k = case k of+            0         -> "no argument"+            1         -> "1 argument"+            otherwise -> show k ++ " arguments"+       in do errorIf (found /= expected) $+                pp ("Type constructor `" ++ conName ++ "' was declared to have "+                    ++ args expected ++ ", but it is used with " ++ args found +                    ++ ".")+             return t+   +++++------- Acyclic tests ---------------------------------------------------------+++-- | Checks that type synonym declarations are not mutually recursive.+--   Error messages are created for all type synonym declarations which are+--   mutually recursive with other type synonym declarations.++checkAcyclicTypeSynonyms :: [Declaration] -> Checked [Declaration]+checkAcyclicTypeSynonyms ds =+  let -- gets the name of a type synonym declaration or Nothing+      getTypeSynonymName d = +        case d of { TypeDecl d -> Just (typeName d) ; otherwise -> Nothing }+      +      -- the list of all known type synonym names+      allTypeSynonymNames = mapMaybe getTypeSynonymName ds++      -- extracts a type synonym name from a type expression+      occurringTypeSynonyms t = case t of+        TypeCon (Con c) _ -> if c `elem` allTypeSynonymNames +                               then Set.singleton c+                               else Set.empty+        otherwise         -> Set.empty+      +      -- given an element (e.g. a declaration), this function determines all+      -- type synonyms which this element is based on+      getDependencies = +        everything Set.union (Set.empty `mkQ` occurringTypeSynonyms)++      -- the error message for all unaccepted declarations+      error = "Declarations of type synonyms must not be mutually recursive."++      -- filter all mutually recursive declarations+   in checkDependencyGraph ds getDependencies error "type synonym"++++-- | Checks that the type class hierarchy is acyclic. An error message is+--   created for every type class which is part of a cycle.+--+--   Undeclared type classes occurring as superclasses are ignored. They will+--   be filtered out in the next step (see 'checkGlobal').++checkAcyclicTypeClasses :: [Declaration] -> Checked [Declaration]+checkAcyclicTypeClasses ds =+  let -- gets the name of a class declaration or Nothing+      getClassName d = +        case d of { ClassDecl d -> Just (className d) ; otherwise -> Nothing }+      +      -- the list of all known class names+      allClassNames = mapMaybe getClassName ds++      -- given a class declaration, this function returns the set of all+      -- superclasses having a known declaration+      getSuperClasses d = case d of+        ClassDecl d -> let cs = map (\(TC c) -> c) . superClasses $ d+                        in Set.fromList (cs `intersect` allClassNames)+        otherwise   -> Set.empty++      -- the error message for all unaccepted declarations+      error =+        "The type class hierarchy formed by the type classes and their "+        ++ "superclasses must not be acyclic."++      -- filter all acyclic type classes+   in checkDependencyGraph ds getSuperClasses error "type class"++++-- | Applies 'recursivePartition' to the arguments and generates error messages+--   for all erroneous declarations.++checkDependencyGraph :: +    [Declaration] +    -> (Declaration -> Set.Set Identifier) +    -> String+    -> String+    -> Checked [Declaration]++checkDependencyGraph ds getDependencies errMsg tag = do+  let (ok, err) = recursivePartition ds getDependencies+  when (not (null err)) $+    tell [pp (errMsg+              ++ violating tag +                   (map (unpackIdent . getDeclarationName . fst) err))]+  return ok++++-- | Partitions a list of declarations using a dependency function.+--   Every declaration, which depends only on the declarations given by the+--   third argument, is put into the left set.+--   Every declaration, which depends only on the declarations already in the+--   left set, is put also into the left set. This step is recursively repeated+--   until no more declarations are added to the left set.+--   This function terminates if the first argument is a finite list.++recursivePartition :: +    [Declaration] +    -> (Declaration -> Set.Set Identifier) +    -> ([Declaration], [(Declaration, Set.Set Identifier)])++recursivePartition decls getDependencies =+  let -- to increase efficency, calculate the dependencies beforehand+      -- and use the declaration names as keys (declaration names are unique)+      mkMap d m = Map.insert (getDeclarationName d) (getDependencies d) m+      depMap = foldr mkMap Map.empty decls++      -- checks if 'd' depends only on 'ds' and 'extras', +      -- i.e. if 'd' is fully contained in 'ds' and 'extras'+      dependsOn d ds = +        let deps = fromJust (Map.lookup d depMap)+         in deps `Set.isSubsetOf` ds++      -- implements the actual partitioning+      select (ds, rs) = +        let (ds', rs') = Set.partition (\d -> d `dependsOn` ds) rs+         in if Set.null ds'+              then (ds, rs)+              else select (ds `Set.union` ds', rs')++      -- run the partitioning, 'ok' is the accepted set while 'err' contains+      -- all erroneous declarations+      (s1, s2) = select (Set.empty, Set.fromList (map getDeclarationName decls))+      (ok, err) = partition (\d -> getDeclarationName d `Set.member` s1) decls++      -- reduce the mapping to erroneous declarations only such that every+      -- declaration is only mapped to names of erroneous declarations+      getErrDeps d = +        let deps = fromJust (Map.lookup (getDeclarationName d) depMap)+         in deps `Set.difference` s1+      errMap = foldr (\d m -> (d, getErrDeps d) : m) [] err++   in (ok, errMap)++++++------- Check declared type constuctors and classes ---------------------------+++data Name+  = CON Identifier+  | CLA Identifier+  | OTH Identifier+  deriving (Eq, Ord)+++getDeclarationName' :: Declaration -> Name+getDeclarationName' (TypeDecl d)    = CON (typeName d)+getDeclarationName' (DataDecl d)    = CON (dataName d)+getDeclarationName' (NewtypeDecl d) = CON (newtypeName d)+getDeclarationName' (ClassDecl d)   = CLA (className d)+getDeclarationName' (TypeSig s)     = OTH (signatureName s)+++unpackName :: Name -> Identifier+unpackName (CON c) = c+unpackName (CLA c) = c+unpackName (OTH c) = c++++-- | Checks that all declarations depend only on declared type constructors and+--   declared type classes.++checkAllConsAndClassesDeclared :: +    [ValidDeclaration] -> [Declaration] -> Checked [Declaration]+checkAllConsAndClassesDeclared vds ds = +  let -- gets a type constructor name occurring in a type expression+      getCons t = case t of+        TypeCon (Con c) _ -> Set.singleton (CON c)+        otherwise         -> Set.empty++      -- gets a type class name+      getClasses (TC c) = Set.singleton (CLA c)++      -- gets all type class names and all type constructor names occurring+      -- in an element (e.g. a declaration)+      getDependencies = +        everything Set.union (const Set.empty `extQ` getCons `extQ` getClasses)++      -- the error message for all unaccepted declarations+      error d is = +        inDecl d $+          throwError $+            pp ("The following type constructors or type classes are not "+                ++ "declared or their declaration contains errors: "+                ++ (concat . intersperse ", " . map (unpackIdent . unpackName) +                   $ is))++      (ok, err) = partitionDeclared ds getDependencies (map rawDeclaration vds)++      -- filter all declarations which only depend on declared type constructors+      -- and declared type classes+   in do tell (mapMaybe (\(d, is) -> getError . error d . Set.elems $ is) err)+         return ok+  +++-- | Partitions a given list to all those declarations which don't rely +--   directly or indirectly on undeclared type constructors or type classes.+--   Compare with 'recursivePartition'.++partitionDeclared :: +    [Declaration] +    -> (Declaration -> Set.Set Name) +    -> [Declaration]+    -> ([Declaration], [(Declaration, Set.Set Name)])++partitionDeclared decls getDependencies extraDecls =+  let -- to increase efficency, calculate the dependencies beforehand+      -- and use the declaration names as keys (declaration names are unique)+      mkMap d m = Map.insert (getDeclarationName' d) (getDependencies d) m+      depMap = foldr mkMap Map.empty decls++      -- the list of extra names+      extras = Set.fromList (map getDeclarationName' extraDecls)++      -- checks if 'd' depends only on 'ds' and 'extras', +      -- i.e. if 'd' is fully contained in 'ds' and 'extras'+      dependsOn d ds = +        let deps = fromJust (Map.lookup d depMap)+         in deps `Set.isSubsetOf` (extras `Set.union` ds)++      -- implements the actual partitioning+      select (ds, es) = +        let (ds', es') = Set.partition (\d -> d `dependsOn` ds) ds+         in if Set.size ds == Set.size ds'+              then (ds, es)+              else select (ds', es `Set.union` es')++      -- run the partitioning, 'ok' is the accepted set while 'err' contains+      -- all erroneous declarations+      (s1, s2) = select (Set.fromList (map getDeclarationName' decls), Set.empty)+      (ok, err) = partition (\d -> getDeclarationName' d `Set.member` s1) decls++      -- reduce the mapping to erroneous declarations only such that every+      -- declaration is only mapped to names of erroneous declarations+      getErrDeps d = +        let deps = fromJust (Map.lookup (getDeclarationName' d) depMap)+         in deps `Set.difference` (extras `Set.union` s1)+      errMap = foldr (\d m -> (d, getErrDeps d) : m) [] err++   in (ok, errMap)+++
+ src/Language/Haskell/FreeTheorems/Frontend/CheckLocal.hs view
@@ -0,0 +1,397 @@++++-- | Defines local checks, i.e. checks which only look at one declaration at a+--   time.++module Language.Haskell.FreeTheorems.Frontend.CheckLocal (+    checkLocal+  , checkDataAndNewtypeDeclarations+) where++++import Data.Generics (Data, everything, mkQ)+import Data.List (group, sort)+import Data.Maybe (mapMaybe, fromJust, isJust)+import qualified Data.Set as Set+    ( Set, union, empty, difference, fromList, null, elems, isSubsetOf+    , singleton)++import Language.Haskell.FreeTheorems.Syntax+import Language.Haskell.FreeTheorems.Frontend.Error+import Language.Haskell.FreeTheorems.Frontend.TypeExpressions++++++------- Local checks ----------------------------------------------------------+++-- | Check validity of every declaration.+--   This includes ensuring that fixed type expressions occur nowhere, that only+--   declared type variables occur in right-hand sides and that no primitive+--   type is declared, among other restrictions.+--+--   Local checks comprise all those which can be down by just looking at a+--   single declaration.++checkLocal :: [Declaration] -> Checked [Declaration]+checkLocal = foldChecks checkDecl+  where+    checkDecl :: Declaration -> ErrorOr ()+    checkDecl (DataDecl d)    = checkDataDecl d+    checkDecl (NewtypeDecl d) = checkNewtypeDecl d+    checkDecl (TypeDecl d)    = checkTypeDecl d+    checkDecl (ClassDecl d)   = checkClassDecl d+    checkDecl (TypeSig sig)   = checkSignature sig++++-- | Checks a @data@ declaration. The following restrictions must hold:+--   +--   * The declared type constructor is not a primitive type.+--   * The variables occurring on the right-hand side have to be mentioned on+--     the left-hand side, and the left-hand side variables are pairwise+--     distinct.+--   * The declaration is not nested, i.e. if the declared type constructor+--     occurs on the right-hand side, it has only type variables as arguments.+--   * No fixed type expression occurs in any type expression.++checkDataDecl :: DataDeclaration -> ErrorOr ()+checkDataDecl d =+  inDecl (DataDecl d) $ do+    checkNotPrimitive (dataName d)+    checkVariables (dataVars d)+                   (everything Set.union+                      (Set.empty `mkQ` (freeTypeVariables . withoutBang)) +                      (dataCons d))+    checkNotEmpty (dataCons d)+    mapM_ (checkNotNested (dataName d) (map TypeVar (dataVars d)))+          (conNamesAndTypes d)+    mapM_ (checkNoFixedTEsNamed "data constructor") (conNamesAndTypes d)+  where+    conNamesAndTypes = +      map (makePair dataConName (map withoutBang . dataConTypes)) . dataCons++++-- | Checks a @newtype@ declaration. The following restrictions must hold:+--   +--   * The declared type constructor is not a primitive type.+--   * The variables occurring on the right-hand side have to be mentioned on+--     the left-hand side, and the left-hand side variables are pairwise+--     distinct.+--   * The declaration is not nested, i.e. if the declared type constructor+--     occurs on the right-hand side, it has only type variables as arguments.+--   * No fixed type expression occurs in the right-hand side type expression.++checkNewtypeDecl :: NewtypeDeclaration -> ErrorOr ()+checkNewtypeDecl d =+  inDecl (NewtypeDecl d) $ do+    checkNotPrimitive (newtypeName d)+    checkVariables (newtypeVars d) (freeTypeVariables $ newtypeRhs d)+    checkNotNested (newtypeName d) (map TypeVar (newtypeVars d)) (conAndType d)+    checkNoFixedTEsNamed "data constructor" (conAndType d)+  where+    conAndType = makePair newtypeCon (singletonList . newtypeRhs)++++-- | Checks a @type@ declaration. The following restrictions must hold:+--   +--   * The declared type constructor is not a primitive type.+--   * The variables occurring on the right-hand side have to be mentioned on+--     the left-hand side, and the left-hand side variables are pairwise+--     distinct.+--   * The declaration must not be recursive, i.e. the type constructor declared+--     by this declaration must not occur on th right-hand side.+--   * No fixed type expression occurs in the right-hand side type expression.++checkTypeDecl :: TypeDeclaration -> ErrorOr ()+checkTypeDecl d = +  inDecl (TypeDecl d) $ do+    checkNotPrimitive (typeName d)+    checkVariables (typeVars d) (freeTypeVariables $ typeRhs d)+    checkTypeDeclNotRecursive (typeName d) (typeRhs d)+    checkNoFixedTEs (typeRhs d)++++-- | Checks a @class@ declaration. The following restrictions must hold:+--   +--   * The declared type class does not equal a primitive type.+--   * The names of the class methods are pairwise distinct. +--   * The class variable occurs in the type expression of every class method.+--   * The name of the class does not occur in a type expression of any class+--     method.+--   * No fixed type expression occurs in a type expression of any class method.++checkClassDecl :: ClassDeclaration -> ErrorOr ()+checkClassDecl d =+  inDecl (ClassDecl d) $ do+    checkNotPrimitive (className d)+    checkClassMethodsDistinct (map signatureName . classFuns $ d)+    checkClassVarInMethods (classVar d) (classFuns d)+    checkClassDeclNotRecursive (className d) (classFuns d)+    mapM_ (checkNoFixedTEsNamed "class method")+          (map (makePair signatureName (singletonList . signatureType))+               (classFuns d))++++-- | Checks a type signature. The following restrictions must hold:+--   +--   * No fixed type expressions occurs in the type expression of this type+--     signature.++checkSignature :: Signature -> ErrorOr ()+checkSignature s =+  inDecl (TypeSig s) $ do+    checkNoFixedTEs (signatureType s)++++++------- Special checks for data and newtype declarations ----------------------+++-- | Check data and newtype declarations for occurring function type+--   constructors or type abstraction constructors. If any declaration contains+--   one of these, an error message is created. All other declarations are+--   passed.++checkDataAndNewtypeDeclarations :: [Declaration] -> Checked [Declaration]+checkDataAndNewtypeDeclarations = foldChecks checkDN+  where+    checkDN :: Declaration -> ErrorOr ()+    checkDN d = case d of+      DataDecl d'    -> inDecl d (mapM_ checkAbsFun (dataConsAndTypes d'))+      NewtypeDecl d' -> inDecl d (checkAbsFun (newtypeConAndType d'))+      otherwise      -> return ()++    dataConsAndTypes =+      map (makePair dataConName (map withoutBang . dataConTypes)) . dataCons+    +    newtypeConAndType = makePair newtypeCon (singletonList . newtypeRhs)+++++++------- Checking restrictions -------------------------------------------------+++-- | Checks if the given identifier is not a name of a primitive type.+--   Otherwise, an error message is created.++checkNotPrimitive :: Identifier -> ErrorOr ()+checkNotPrimitive (Ident name) =+  errorIf (name `elem` ["Int", "Integer", "Float", "Double", "Char"]) $+    pp ("A primitive type must not have a declaration.")+  +++-- | Checks if the second argument set is contained in the first argument list.+--   If not, an error message is returned.+--+--   Checks also if first argument contains pairwise distinct variables.+--   If not, an error message is returned.++checkVariables :: [TypeVariable] -> Set.Set TypeVariable -> ErrorOr ()+checkVariables vs rvs = do+  let es = extractRepeatingElements vs+  errorIf (not $ null es) $+    pp ("Type variables must not be given more than once on the left-hand "+        ++ "side of a declaration. "+        ++ violating "variable" (map varName $ es))++  let set = rvs `Set.difference` Set.fromList vs+  errorIf (not (Set.null set)) $+    pp ("Type variables occurring on the right-hand side of a declaration must "+        ++ "be declared on the left-hand side. "+        ++ violating "variable" (map varName . Set.elems $ set))++  where+    varName (TV v) = unpackIdent v++++-- | Checks that there is at least one data constructor declaration in the the+--   declaration of an algebraic data type.++checkNotEmpty :: [DataConstructorDeclaration] -> ErrorOr ()+checkNotEmpty cons =+  errorIf (null cons) $+    pp ("The declaration of an algebraic data type must have at least one "+        ++ "data constructor.")++++-- | Checks if the identifiers occurs in any of the given type expressions as+--   a type constructor. If so, and if the identifier is applied not only to+--   type variables, it is called nested and an error message is created.++checkNotNested :: +    Identifier -> [TypeExpression] -> (Identifier, [TypeExpression]) +    -> ErrorOr ()+checkNotNested con vs (dcon, ts) =+  errorIf (any (satisfiesSomewhere isNested) ts) $+    pp ("Declarations must not be nested."+        ++ violating "data constructor" [unpackIdent dcon])+  where+    isNested t = case t of+      TypeCon (Con c) ts -> c == con && ts /= vs+      otherwise          -> False++++-- | Checks if a type declaration is recursive, i.e. the identifier occurs in+--   the given type expression as a type constructor.+--   If so, an error message is created.++checkTypeDeclNotRecursive :: Identifier -> TypeExpression -> ErrorOr ()+checkTypeDeclNotRecursive ident t =+  errorIf (satisfiesSomewhere (isCon ident) t) $+    pp ("A type synonym must not be declared recursively.")+  where+    isCon ident t = case t of+      TypeCon (Con c) _ -> c == ident+      otherwise         -> False++++-- | Checks that the names of class methods are pairwise distinct.+--   If not, an error message is created.++checkClassMethodsDistinct :: [Identifier] -> ErrorOr ()+checkClassMethodsDistinct is =+  let es = extractRepeatingElements is+   in errorIf (not $ null es) $+        pp ("Class methods must not be declared more than once. "+            ++ violating "class method" (map unpackIdent es))++++-- | Checks if the given identifier occurs as free type variable in every+--   signature. If not, an error message is created.++checkClassVarInMethods :: TypeVariable -> [Signature] -> ErrorOr ()+checkClassVarInMethods v@(TV vName) ss =+  let setOfv      = Set.singleton v+      vIsFreeIn t = setOfv `Set.isSubsetOf` freeTypeVariables t+      ms          = filter (not . vIsFreeIn . signatureType) ss+   in errorIf (not $ null ms) $+        pp ("The type variable `" ++ unpackIdent vName ++ "' must occur free "+            ++ "in the type expressions of every class method. "+            ++ violating "class method" (map (unpackIdent . signatureName) ms))+    +++-- | Checks that the name of a type class does not occur in any of the class+--   methods. Otherwise, an error message is created.+checkClassDeclNotRecursive :: Identifier -> [Signature] -> ErrorOr ()+checkClassDeclNotRecursive ident sigs =+  let hasThisClass = satisfiesSomewhere (\c -> c == TC ident)+      ms           = filter (hasThisClass . signatureType) sigs+   in errorIf (not $ null ms) $+        pp ("The type class `" ++ unpackIdent ident ++ "' must not occur in a "+            ++ "type expression of any class method of this class. "+            ++ violating "class method" (map (unpackIdent . signatureName) ms))++++-- | Checks that no FixedTypeExpression occurs in the given list of named+--   type expressions. The first argument is used in generating a helpful error+--   message and describes what kind of items the second argument contains.++checkNoFixedTEsNamed :: String -> (Identifier, [TypeExpression]) -> ErrorOr ()+checkNoFixedTEsNamed tag (con, ts) =+  let es = mapMaybe checkNoFixedTEsPlain ts+   in errorIf (not . null $ es) $+        pp (head es ++ violating tag [unpackIdent con])++++-- | Checks that no FixedTypeExpression occurs in a type expression.+--   If it does, an error message is created.++checkNoFixedTEs :: TypeExpression -> ErrorOr ()+checkNoFixedTEs t = +  let e = checkNoFixedTEsPlain t+   in errorIf (isJust e) (pp . fromJust $ e)+  +++-- | Returns an error if a FixedTypeExpression occurs in the argument, otherwise+--   returns @Nothing@.++checkNoFixedTEsPlain :: TypeExpression -> Maybe String+checkNoFixedTEsPlain t =+  if (satisfiesSomewhere isFixedTE t)+    then Just "A fixed type expression must not occur in a type expression."+    else Nothing+  where+    isFixedTE t = case t of+      TypeExp _ -> True+      otherwise -> False++++-- | Checks that no function type constructor and no type abstraction+--   constructor occur in the given named list of type expressions.++checkAbsFun :: (Identifier, [TypeExpression]) -> ErrorOr ()+checkAbsFun (con, ts) =+  errorIf (satisfiesSomewhere isAbsOrFun ts) $+    pp ("Algebraic data types and type renamings must be declared without type "+        ++ "abstractions and function type constructors occurring on the "+        ++ "right-hand side."+        ++ violating "data constructor" [unpackIdent con])+  where+    isAbsOrFun t = case t of+      TypeFun _ _   -> True+      TypeAbs _ _ _ -> True+      otherwise     -> False++++++------- Helper functions ------------------------------------------------------+++-- | Applies two functions to a value and creates a pair of the results.++makePair :: (a -> b) -> (a -> c) -> a -> (b, c)+makePair f g x = (f x, g x)+++-- | Creates a list containing just one element.++singletonList :: a -> [a]+singletonList x = [x]++++-- | Filters all elements which occur more than once in the given list.+--   Only one representative is returned for every group of equal items.++extractRepeatingElements :: Ord a => [a] -> [a]+extractRepeatingElements =+  map head . filter (\vs -> length vs > 1) . group . sort++++-- | Tests if a predicate holds somewhere in an arbitrary tree.++satisfiesSomewhere :: (Data a, Data b) => (a -> Bool) -> b -> Bool+satisfiesSomewhere predicate x = everything (||) (False `mkQ` predicate) x+++++
+ src/Language/Haskell/FreeTheorems/Frontend/Error.hs view
@@ -0,0 +1,130 @@++++-- | Provides error handling functions for checking parser output.+--   The functions and data types of this module are mostly tiny, little+--   helpers used by all parser modules.++module Language.Haskell.FreeTheorems.Frontend.Error where++++import Control.Monad (foldM)+import Control.Monad.Error (Error(..), throwError)+import Control.Monad.Writer (Writer, runWriter, tell)+import Data.List (intersperse)+import Text.PrettyPrint (Doc, empty, text, fsep, ($$), nest)++import Language.Haskell.FreeTheorems.Syntax+    (Declaration, getDeclarationName, unpackIdent)++++-- | A wrapper type for a @Writer@ which stores pretty-printable documents along+--   with checked values.++type Checked a = Writer [Doc] a++++-- | A wrapper type for @Writer@ which stores pretty-printable documents along+--   with parsed values.+--   This type is provided as standard return type for parsers.++type Parsed a = Writer [Doc] a++++++-- | The error type is just a synonym for @Either@ where errors are represented+--   by a pretty-printable @Doc@.++type ErrorOr a = Either Doc a++-- needed to make 'ErrorOr' a monad+instance Error Doc where+  noMsg    = empty+  strMsg s = text s++++-- | A wrapper function for @runWriter@.++runChecks :: Checked a -> (a, [Doc])+runChecks = runWriter++++-- | Applies a checking function (the first argument) element-wise to a list of+--   values (the second argument). The result list contains only those elements+--   for which the checking function does not yield an error.++foldChecks :: (a -> ErrorOr b) -> [a] -> Checked [a]+foldChecks check = foldM doCheck []+  where+    doCheck xs x = +      case getError (check x) of+        Nothing -> return (xs ++ [x])+        Just e  -> tell [e] >> return xs++++-- | Checks if the argument contains an error.++isError :: ErrorOr a -> Bool+isError = either (const True) (const False)++++-- | Returns the error message stored in the argument or @Nothing@ if there is +--   no error message in the argument.++getError :: ErrorOr a -> Maybe Doc+getError = either Just (const Nothing)++++-- | If the first argument is True, then the second argument is taken as an+--   error message. Otherwise () is returned as a non-error message.++errorIf :: Bool -> Doc -> ErrorOr ()+errorIf False = return . const ()+errorIf True  = throwError++++-- | Transforms a string into a pretty-printed document by splitting the string+--   into words and forming a pretty paragraph of all words.++pp :: String -> Doc+pp = fsep . map text . words++++-- | Checks a declaration for errors.+--   If the second argument is an error, this function extends the error +--   message to make clear it belongs to a declaration.++inDecl :: Declaration -> ErrorOr a -> ErrorOr a+inDecl d e = case getError e of+  Nothing  -> e+  Just doc -> throwError $+                pp ("In the declaration of " +                    ++ unpackIdent (getDeclarationName d) ++ ":")+                $$ nest 2 doc+    +++-- | Used to extend error messages by a list of items violating a certain rule. ++violating :: String -> [String] -> String+violating name xs =+  let text = if length xs == 1+               then " The following " ++ name ++ " violates this rule: "+               else " The following " ++ name ++ "s violate this rule: "+   in text ++ (concat . intersperse ", " $ xs)++++
+ src/Language/Haskell/FreeTheorems/Frontend/TypeExpressions.hs view
@@ -0,0 +1,302 @@++++-- | Defines standard functions for modifying type expressions or retrieving+--   information from type expressions.++module Language.Haskell.FreeTheorems.Frontend.TypeExpressions (+    freeTypeVariables+  , allTypeVariables+  , createNewTypeVariableNotIn+  , alphaConversion+  , substituteTypeVariables+  , replaceAllTypeSynonyms+  , closeTypeExpressions+  , closureFor+) where++++import Data.Generics+    ( Typeable, Data, synthesize, mkQ, everywhere, mkT, gmapT, GenericQ+    , GenericT )+import Data.List (find)+import Data.Set as Set+    ( Set, empty, union, insert, delete, fold, unions, difference, singleton+    , member )+import Data.Map as Map (Map, empty, lookup, delete, insert)+import Data.Maybe (maybe, fromJust)++import Language.Haskell.FreeTheorems.Syntax+import Language.Haskell.FreeTheorems.NameStores (typeNameStore)++++++------- Functions for type variables ------------------------------------------+++-- | Returns all type variables occurring in a type expression.++allTypeVariables :: TypeExpression -> Set.Set TypeVariable+allTypeVariables = synthesize Set.empty Set.union (id `mkQ` update)+  where+    update t s = case t of+      TypeVar v     -> Set.insert v s+      TypeAbs v _ _ -> Set.insert v s+      otherwise     -> s++++-- | Returns the free type variables of a type expression.++freeTypeVariables :: TypeExpression -> Set.Set TypeVariable+freeTypeVariables = synthesize Set.empty Set.union (id `mkQ` update)+  where+    update t s = case t of+      TypeVar v     -> Set.insert v s+      TypeAbs v _ _ -> Set.delete v s+      otherwise     -> s++++-- | Substitutes every free occurrence of all type variables given in the map+--   with the type expressions they are mapped to.++substituteTypeVariables ::+    Map.Map TypeVariable TypeExpression +    -> TypeExpression +    -> TypeExpression++substituteTypeVariables env t = +  everywhereWith (id `mkQ` update) (mkTw substitute) env t+  where+    -- Updates the environment used in the top-down traversal.+    -- Removes bound type variables from the mapping. Thus, these variables+    -- won't be replaced in the second stage.+    update t env = case t of+      TypeAbs v _ _ -> Map.delete v env+      otherwise     -> env+    +    -- Replaces a type variable by a type expression, if the type variable is+    -- contained in the environment.+    substitute env t = case t of+      TypeVar v -> maybe t id (Map.lookup v env)+      otherwise -> t++++-- | Creates a new type variable not occurring in the given set of type+--   variables.+--+--   A type variable can either be named by the letters \'a\' to \'e\' or, if+--   that causes conflicts, by the letter \'a\' concatenated with a number+--   starting from 1.++createNewTypeVariableNotIn :: Set.Set TypeVariable -> TypeVariable+createNewTypeVariableNotIn forbiddenVars =+  let vars = map (TV . Ident) typeNameStore+   in fromJust $ find (\v -> not (v `Set.member` forbiddenVars)) vars++++-- | Replaces every bound occurrence of the given type variable in the given+--   type expression with a new type variable which is created by+--   'createNewTypeVariableNotIn'.+--+--   Several bound occurrences of the given type variable are replaced with the+--   same new type variable. Only one new type variable is created altogether.++alphaConversion :: TypeVariable -> TypeExpression -> TypeExpression+alphaConversion old t =+  everywhereWith (id `mkQ` change) (mkTw replace) id t+  where+    -- The new type variable which will replace 'old' everywhere in the given+    -- type expression.+    new = createNewTypeVariableNotIn (allTypeVariables t)+    +    -- This function replaces any type variable equal to 'old' with 'new' and+    -- keeps all other type variables unchanged.+    rep v = if (v == old) then new else v++    -- Modifies the function which replaces type variables.+    -- If we are at the type abstraction where 'old' is bound, then 'old' has+    -- to be replaced in every subexpression by the new type variable.+    change t f = case t of+      TypeAbs v _ _ -> if (v == old) then rep else f+      otherwise     -> f++    -- Applies the current replacement function to type variables.+    -- In type abstractions, the static function 'rep' is used to replace+    -- 'old' with 'new' or otherwise keep the type variable.+    -- Note that - independent of the usage of 'rep' - the replacement function+    -- 'r' will be modified by 'change' when advancing to subexpressions.+    replace r t = case t of+      TypeVar v       -> TypeVar (r v)+      TypeAbs v cs t' -> TypeAbs (rep v) cs t'+      otherwise       -> t++++++------- Generic helper definitions --------------------------------------------+++-- | Generic transformations using a value of fixed type.++type GenericTw u = forall a . Data a => u -> a -> a++++-- | Make a generic transformation which uses a value of fixed type.+--   This function takes a specific case into a general case, such that no+--   transformation is applied for types not covered by the specific case.++mkTw :: (Typeable a, Typeable b) => (u -> a -> a) -> u -> b -> b+mkTw f u = mkT $ f u++++-- | Pushes a value in a top-down fashion trough a tree and applies that value+--   from bottom to top to every node.+--+--   More detailed, the expression+--+-- >   everywhereWith update apply v+--+--   is evaluated as follows:+--   The value @v@ is transfered through the tree from top to bottom while,+--   at every node, the function @update@ is applied to it. This allows the+--   initial value to be changed like, for example, an environment gathering+--   information from the root to the leaf while moving through the tree.+--   Thereafter, the transformation function @apply@ is applied to every node+--   from bottom to top. It might use the value distributed to that node.++everywhereWith :: GenericQ (u -> u) -> GenericTw u -> u -> GenericT+everywhereWith k f u x = (f u) $ gmapT (everywhereWith k f (k x u)) x++++++------- Replacing type synonyms -----------------------------------------------+++-- | Replaces all type synonyms in an arbitrary tree.+--   The first argument gives the list of known type synonyms and their +--   declarations. Every occurrence of one of those type synonyms in the second+--   argument is replaced by the according right-hand side of the declaration.+--   +--   Note that the type synonym declarations given in the first argument may+--   themselves contain type synonyms. However, type synonym declarations must+--   not be recursive nor mutually recursive.++replaceAllTypeSynonyms :: (Typeable a, Data a) => [TypeDeclaration] -> a -> a+replaceAllTypeSynonyms knownTypes = everywhere (mkT replace)+    -- This functions replaces all type synonyms in a bottom-up manner.+    -- Thus, when applying 'replace', all type synonyms are already removed+    -- from all children of the node.++  where+    -- Replacing type synonyms only affects type constructors.+    -- Check if there is a type synonym declaration for the given type +    -- constructor. If not, just return the unchanged type expression.+    -- Otherwise replace the type synonym by its definition.+    replace t = case t of+      TypeCon c ts -> maybe t (applyTypeSynonym ts) (findTypeDecl knownTypes c)+      otherwise    -> t++    -- Applies the declaration of a type synonym to a list of type expressions.+    -- The type expression composed in this way is returned.+    -- Note that the structure of 'replaceTypeSynonyms' guarantees that there is+    -- no type synonym in any of the type expressions of 'ts'.+    applyTypeSynonym ts (Type _ vs t) =+      let +          -- First, remove all type synonyms from the declaration's right-hand+          -- side. Note that this terminates because type expressions cannot be+          -- declared recursively nor mutually recursively.+          t1 = replaceAllTypeSynonyms knownTypes t++          -- Construct an environment to be used to substitute every free+          -- variable in 't' by the appropiate type expression of 'ts'.+          env = foldr (uncurry Map.insert) Map.empty (zip vs ts)++          -- Rename all bound variables in 't' such that no free variables of+          -- any type expression in 'ts' will get bound.+          allFreeVariables = Set.unions $ map freeTypeVariables ts+          t2 = Set.fold alphaConversion t1 allFreeVariables++          -- Finally, apply the declaration's right-hand side to 'ts' and return+          -- the constructed type expression.+       in substituteTypeVariables env t2+  +++-- | Looks up the declaration for a type synonym constructor.+--   If the given type constructor is not a type synonym or there is no+--   declaration for this type constructor in the declarations list, then+--   @Nothing@ is returned.++findTypeDecl :: [TypeDeclaration] -> TypeConstructor -> Maybe TypeDeclaration+findTypeDecl decls con = case con of+  Con name  -> find (\d -> typeName d == name) decls +  otherwise -> Nothing++++++------- Closing type expressions ----------------------------------------------+++-- | Closes all type expressions in type signature declarations.+--   Class methods are also modified, in that every free variable expect the+--   class variable is explicitly bound.++closeTypeExpressions :: [Declaration] -> [Declaration]+closeTypeExpressions = map closeDecl+  where+    closeDecl d = case d of+      ClassDecl d -> ClassDecl (closeClassDecl d)+      TypeSig sig -> TypeSig (closeSignature sig)+      otherwise   -> d++++-- | Closes type signatures of class methods. Afterwards, the class variable is+--   still free in all class methods.++closeClassDecl :: ClassDeclaration -> ClassDeclaration+closeClassDecl d =+  d { classFuns = map (closureWithout (classVar d)) (classFuns d) }+  where+    -- Close the class method signature while keeping the class variable free.+    closureWithout v s = +      let t = signatureType s+          freeVars = freeTypeVariables t `Set.difference` Set.singleton v+       in s { signatureType = closureFor freeVars t }++++-- | Close a type signature declaration.++closeSignature :: Signature -> Signature+closeSignature s =+  let t = signatureType s+   in s { signatureType = closureFor (freeTypeVariables t) t }++++-- | Explicitly binds all type variables of the first argument by a type +--   abstraction in the given type expression.++closureFor :: Set.Set TypeVariable -> TypeExpression -> TypeExpression+closureFor vs t = Set.fold (\v -> TypeAbs v []) t vs++++++
+ src/Language/Haskell/FreeTheorems/Intermediate.hs view
@@ -0,0 +1,408 @@++++-- | Declares an intermediate data structure along with a function to transform+--   type signatures into the intermediate structure. There are also other+--   functions working on intermediate structures, namely to retrieve relation+--   variables and to instantiate them to functions.++module Language.Haskell.FreeTheorems.Intermediate (+    Intermediate (..)+  , interpret+  , interpretM+  , relationVariables+  , specialise+  , specialiseInverse+) where++++import Control.Monad (liftM, liftM2, mapM)+import Control.Monad.Reader (ReaderT, ask, runReaderT, local)+import Control.Monad.State (State, get, put, runState)+import Control.Monad.Trans (lift)+import Data.Generics ( Typeable, Data, everywhere, everything, listify, mkT+                     , mkQ, extQ)+import qualified Data.Map as Map (Map, empty, lookup, insert, map)++import Language.Haskell.FreeTheorems.LanguageSubsets+import Language.Haskell.FreeTheorems.Syntax +import Language.Haskell.FreeTheorems.ValidSyntax+import Language.Haskell.FreeTheorems.Theorems+import Language.Haskell.FreeTheorems.Frontend.TypeExpressions+    ( substituteTypeVariables )+import Language.Haskell.FreeTheorems.NameStores +    ( relationNameStore, typeExpressionNameStore, functionNameStore1, functionNameStore2 )+++++------- Intermediate data structure -------------------------------------------+++-- | A structure describing the intermediate result of interpreting a type+--   expression as a relation.++data Intermediate = Intermediate +  { intermediateName      :: String +        -- ^ The name of the symbol for which the theorem is to be generated.+  +  , intermediateSubset    :: LanguageSubset+        -- ^ The language subset in which the theorem is to be generated.+  +  , intermediateRelation :: Relation+        -- ^ The relation obtained from the type.+  +  , functionVariableNames1 :: [String]+        -- ^ A name store for new, fresh function names.+        --   This is needed because functions can be specialised step-by-step+        --   after having generated the first relation from a type.++  , functionVariableNames2 :: [String]+        -- ^ Another name store for new, fresh function names, disjoint from+        --   the one above.++  , signatureNames :: [String]+        -- ^ The names of all known signatures. These names must not be used to+        --   generate names of functions and variables.+  +  , interpretNameStore :: NameStore +        -- ^ A name store to generate new relation variables and type+        --   expressions.+  +  }++++++------- Interpret types as relations ------------------------------------------++++-- | Interprets a valid signature as a relation. This is the key point for+--   generating free theorems.++interpret :: +    [ValidDeclaration] -> LanguageSubset -> ValidSignature -> Maybe Intermediate+interpret vds l s =+  let n  = unpackIdent . signatureName . rawSignature $ s+      ss = getSignatureNames (map rawDeclaration vds)+      fs = n : ss+      t  = signatureType . rawSignature $ s+      (i, rs) = runState (runReaderT (interpretM l t) Map.empty) (initialState fs)+      r = Intermediate n l i (filter (`notElem` fs) functionNameStore1) (filter (`notElem` fs) functionNameStore2) ss rs+   in case l of+        SubsetWithSeq _ -> Just r+        otherwise       -> if containsStrictTypes vds s +                             then Nothing+                             else Just r+  where+    getSignatureNames = everything (++) ([] `mkQ` getSigName)+    getSigName (Signature i _) = [unpackIdent i]++    containsStrictTypes vds s = +      let rs = rawSignature s+          ns = everything (++) ([] `mkQ` getCons `extQ` getClasses) rs+          ds = map (getDeclarationName . rawDeclaration) +                   (filter isStrictDeclaration vds)+          isStrict n = n `elem` ds+       in any isStrict ns++    getCons c = case c of { Con n -> [n] ; otherwise -> [] }+    getClasses (TC n) = [n]++++-- | Transforms a type expression into a relation. The environment is used to+--   map seen type variables to newly created relation variables. The state+--   serves for creating relation variables.++interpretM :: +    LanguageSubset +    -> TypeExpression +    -> ReaderT Environment (State NameStore) Relation++interpretM l t = case t of++    -- get the environment from the reader, lookup the relation variable for+    -- the given type variable (this operation will never fail because+    -- in the initial type expression, all occurring type variables are bound+    -- by type abstraction which are resolved by updating the environment, see+    -- below) and create a relation consisting solely of the relation variable+  TypeVar v -> Map.lookup v =<< ask+  +    -- either create a basic relation or a lift relation, depending on the +    -- subtypes+  TypeCon c ts -> do+    rs <- mapM (interpretM l) ts   -- interpret the subtypes+    ri <- mkRelationInfo l t       -- create the relation info+        +        -- checks if an intermediate relation is a basic case+    let basic rel = case rel of { RelBasic _ -> True ; otherwise -> False }++        -- create a basic intermediate relation if all subrelations are basic+        -- (or if there is no subrelation), otherwise create a lifted relation+    if all basic rs+      then return (RelBasic (RelationInfo l t t))+      else return (RelLift ri c rs)++    -- create a relation for function types+  TypeFun t1 t2 -> do+    ri <- mkRelationInfo l t       -- create the relation info+    liftM2 (RelFun ri) (interpretM l t1) (interpretM l t2)++    -- create a relation for type abstractions+  TypeAbs v cs t' -> do+    ri <- mkRelationInfo l t                    -- create the relation info+    (rv, t1, t2) <- lift newRelationVariable    -- create a new variable+    let rvar = RelVar (RelationInfo l t1 t2) rv+    r  <- local (Map.insert v rvar) $ interpretM l t'  -- subrelations+    let res = relRes l ++ (if null cs then [] else [RespectsClasses cs])+    return (RelAbs ri rv (t1,t2) res r)++  where+    mkRelationInfo l t = do+      env <- ask+        -- create the 'left' and 'right' type expression of 't',+        -- i.e. replace all free variables by the left or right type+        -- expressions of the corresponding relation variable+      let getLt = relationLeftType . relationInfo+      let getRt = relationRightType . relationInfo+      let lt = substituteTypeVariables (Map.map getLt env) t+      let rt = substituteTypeVariables (Map.map getRt env) t+      return (RelationInfo l lt rt)+++    -- Returns the restrictions for relations, depending on the current+    -- language subset and theorem type.+    relRes l = case l of+      BasicSubset                       -> [ ]+      SubsetWithFix EquationalTheorem   -> [ Strict, Continuous ]+      SubsetWithFix InequationalTheorem -> [ Strict, Continuous+                                           , LeftClosed ]+      SubsetWithSeq EquationalTheorem   -> [ Strict, Continuous+                                           , BottomReflecting ]+      SubsetWithSeq InequationalTheorem -> [ Strict, Continuous, Total+                                           , LeftClosed ]+   +++++------- Helper definitions for the interpretation -----------------------------+++-- | An environment mapping type variables to intermediate relation variables+--   (stored as relations).++type Environment = Map.Map TypeVariable Relation ++++-- | Represents the names of future variable names. The first component provides+--   names for relations, while the second component provides names for type+--   expressions.++type NameStore = ([String], [TypeExpression])++++-- | Initialises the name store. Both components of the name store are infinite+--   list.+--   For more information, see 'Language.Haskell.FreeTheorems.NameStore'.++initialState :: [String] -> NameStore+initialState ns = +   ( relationNameStore+   , map (TypeExp . TF . Ident) . filter (`notElem` ns)+         $ typeExpressionNameStore )++++-- | Creates a new relation variable using the name store.++newRelationVariable :: +    State NameStore (RelationVariable, TypeExpression, TypeExpression)+newRelationVariable = do+  (rvs, ts) <- get+  let ([rv], rvs') = splitAt 1 rvs+  let ([t1, t2], ts') = splitAt 2 ts+  put (rvs', ts') +  return (RVar rv, t1, t2)++++++------- Instantiation of relation variables -----------------------------------+++-- | Creates a list of all bound relation variables in an intermediate+--   structure, which can be specialised to a function. ++relationVariables :: Intermediate -> [RelationVariable]+relationVariables (Intermediate _ _ rel _ _ _ _) = getRVar True rel+  where+    getRVar ok rel = case rel of+      RelLift _ _ rs    -> concatMap (getRVar ok) rs+      RelFun _ r1 r2    -> getRVar (not ok) r1 ++ getRVar ok r2+      RelAbs _ rv _ _ r -> (if ok then [rv] else []) ++ getRVar ok r+      FunAbs _ _ _ _ r  -> getRVar ok r +      otherwise         -> []++++-- | Specialises a relation variable to a function variable in an intermediate+--   structure.++specialise :: Intermediate -> RelationVariable -> Intermediate+specialise ir rv = reduceLifts (replaceRelVar ir rv Left)++++-- | Specialises a relation variable to an inverse function variable.+--   This function does not modify intermediate structures in subsets with+--   equational theorems.++specialiseInverse :: Intermediate -> RelationVariable -> Intermediate+specialiseInverse ir rv = +  case theoremType (intermediateSubset ir) of+    EquationalTheorem   -> ir+    InequationalTheorem -> reduceLifts  (replaceRelVar ir rv Right)++++-- | Replaces a relation variable with a function variable.++replaceRelVar :: +    Intermediate -> RelationVariable +    -> (TermVariable -> Either TermVariable TermVariable) -> Intermediate+replaceRelVar ir (RVar rv) leftOrRight =+  let ([funName], fns) = splitAt 1 (functionVariableNames1 ir)+      fv = leftOrRight . TVar $ funName+      relation = intermediateRelation ir+   in ir { intermediateRelation  = everywhere (mkT $ replace rv fv) relation+         , functionVariableNames1 = drop 1 (functionVariableNames1 ir)+         }+  where+    -- perform the actual replacement+    -- when replacing a relation by a 'right' function in a relation+    -- abstraction, the types have to be flipped+    replace rv fv rel = case rel of+      RelVar ri (RVar r) -> +        let tv = either (Left . TermVar) (Right . TermVar) fv+         in if rv == r then FunVar ri tv else rel+      RelAbs ri (RVar r) ts res rel' ->+        let res' = either (const funResL) (const funResR) fv+         in if rv == r+              then FunAbs ri fv ts (res' ++ (classConstraints res)) rel'+              else rel+      otherwise -> rel++    -- the restrictions for functions in the equational setting and for+    -- 'left' functions in inequational settings+    funResL = case intermediateSubset ir of+      BasicSubset     -> [ ]+      SubsetWithFix _ -> [ Strict ]+      SubsetWithSeq _ -> [ Strict, Total ]+    +    -- the restrictions for 'right' functions in the inequational settings+    funResR = case intermediateSubset ir of+      BasicSubset     -> [ ]+      SubsetWithFix _ -> [ ]+      SubsetWithSeq _ -> [ Strict ]++    -- returns the class constraints+    classConstraints res = filter isCC res+      where +        isCC r = case r of { RespectsClasses _ -> True ; otherwise -> False }++++-- | Applies simplifications on lifted constructors. +--   If the argument is a function then lifted lists are replaced by map and+--   lifted Maybes are replaced by fmap.++reduceLifts :: Intermediate -> Intermediate+reduceLifts ir = +--  ir { intermediateRelation = reduceEverywhere (intermediateRelation ir) }+  ir { intermediateRelation = re True (intermediateRelation ir) }+  where+--    reduceEverywhere = everywhere (mkT reduce)++    re ok rel = case rel of+      RelLift ri con rs     -> if ok +                                 then reduce (RelLift ri con (map (re ok) rs))+                                 else rel+      RelFun ri r1 r2       -> RelFun ri (re (mk' (not ok) ri r1) r1) +                                         (re (mk ok ri r2) r2)+      RelAbs ri rv ts res r -> RelAbs ri rv ts res (re ok r)+      FunAbs ri fv ts res r -> FunAbs ri fv ts res (re ok r)+      otherwise             -> rel++    mk' ok ri r = case theoremType (relationLanguageSubset ri) of+                    EquationalTheorem   -> True+                    InequationalTheorem -> +                      case r of+                        RelLift _ ConList _ -> True+                        otherwise           -> ok+++    mk ok ri r = case theoremType (relationLanguageSubset ri) of+                   EquationalTheorem   -> True+                   InequationalTheorem -> ok+++    -- Transforms a lifted constructor to a function, if possible.+    -- This function is applied in a bottom-up manner, therefore the+    -- arguments of the lifted constructor are already reduced.+    reduce rel = case rel of+      RelLift ri con rs -> maybe rel id (toTerm ri con rs)+      otherwise         -> rel++    -- Tries to transform a lifted relation. If not succesful, Nothing is+    -- returned.+    toTerm ri con rs = do+      f <- funSymbol con+      -- first check if all arguments are 'left' functions+      case mapM leftFun rs of+        Just fts -> Just . FunVar ri . Left $ term f fts+        Nothing  -> -- then check if all arguments are 'right' functions+                    case mapM rightFun rs of+                      Just fts -> Just . FunVar ri . Right $ term f fts+                      Nothing  -> Nothing++    -- Returns the function symbol associated with a lifted constructor.+    funSymbol con = case con of+      ConList             -> Just . TVar $ "map"+      Con (Ident "Maybe") -> Just . TVar $ "fmap"+      otherwise           -> Nothing++    -- Checks if 'rel' is a 'left' function. If so, its term and type is+    -- returned, otherwise Nothing.+    leftFun rel = case rel of+      FunVar ri (Left f) -> Just (f, ( relationLeftType ri+                                     , relationRightType ri))+      otherwise          -> Nothing++    -- Checks if 'rel' is a 'right' function. If so, its term and type is+    -- returned, otherwise Nothing.+    -- The returned type is flipped mirroring the fact that right functions are+    -- actually inverse functions.+    rightFun rel = case rel of+      FunVar ri (Right f) -> Just (f, ( relationRightType ri+                                      , relationLeftType ri))+      otherwise           -> Nothing++    -- Creates a term by instantiating 'f' and applying the arguments of 'fts'.+    term f fts = +        let (fs, ts) = unzip fts+            termins t (t1, t2) = TermIns (TermIns t t1) t2+         in foldl TermApp (foldl termins (TermVar f) ts) fs+      +++++
+ src/Language/Haskell/FreeTheorems/LanguageSubsets.hs view
@@ -0,0 +1,59 @@+++-- | Declares the available Haskell language subsets and the result types for+--   generating free theorems. ++module Language.Haskell.FreeTheorems.LanguageSubsets where++++import Data.Generics (Typeable, Data)++++-- | Descriptions of the Haskell language subsets for which free theorems can+--   be generated.++data LanguageSubset+  = BasicSubset+        -- ^ This subset describes only terms in which no undefinedness may.+        --   This excludes terms defined using general recursion or selective+        --   strictness (as offered by @seq@).++  | SubsetWithFix TheoremType+        -- ^ This subset describes terms in which undefined values may occur+        --   such as introduced by a fixpoint combinator or possibly failing+        --   pattern matches (if not all cases are covered).+        --   This excludes any term based on @seq@.++  | SubsetWithSeq TheoremType+        -- ^ Additionally to the fix subset, this subset allows terms to+        --   be defined using @seq@.++  deriving (Typeable, Data, Eq)++++-- | Returns the theorem type for a given language subset.++theoremType :: LanguageSubset -> TheoremType+theoremType l = case l of+  BasicSubset     -> EquationalTheorem+  SubsetWithFix t -> t+  SubsetWithSeq t -> t++++-- | The result type for generating free theorems.++data TheoremType+  = EquationalTheorem+        -- ^ An equational free theorem should be generated.++  | InequationalTheorem+        -- ^ Two inequational free theorems should be generated.++  deriving (Typeable, Data, Eq)+++
+ src/Language/Haskell/FreeTheorems/NameStores.hs view
@@ -0,0 +1,65 @@++++-- | Provides functions to generate new variable names of different kinds.++module Language.Haskell.FreeTheorems.NameStores where++++import Data.List (unfoldr)++++-- | An infinite list of names for type variables.++typeNameStore :: [String]+typeNameStore = createNames "abcde" 'a'++++-- | An infinite list of names for relation variables.++relationNameStore :: [String]+relationNameStore = createNames "RS" 'R'++++-- | An infinite list of names for type expressions.++typeExpressionNameStore :: [String]+typeExpressionNameStore = createNames "" 't'++++-- | An infinite list of names for function variables.++functionNameStore1 :: [String]+functionNameStore1 = createNames "fgh" 'f'+++-- | Another infinite list of names for function variables.++functionNameStore2 :: [String]+functionNameStore2 = createNames "pqrs" 'p'++++-- | An infinite list of names for term variables.++variableNameStore :: [String]+variableNameStore = createNames "xyzvwabcdeijklmn" 'x'++++-- | Creates an infinite list of names based on a list of simple names and a+--   default prefix. After simple names have been used, the numbers starting+--   from 1 are appended to the default prefix to generate more names.++createNames :: [Char] -> Char -> [String]+createNames prefixes prefix =+  let unpack is = case is of { (c:cs) -> Just ([c], cs) ; otherwise -> Nothing }+   in unfoldr unpack prefixes ++ (map (\i -> prefix : show i) [1..])+++
+ src/Language/Haskell/FreeTheorems/Parser/Haskell98.hs view
@@ -0,0 +1,472 @@++++-- | Defines a function to parse a string into a list of declarations.+--   This module is based on the \'haskell-src\' package most probably included+--   with every Haskell compiler.++module Language.Haskell.FreeTheorems.Parser.Haskell98 (parse) where++++import Control.Monad (foldM, liftM, liftM2)+import Control.Monad.Error (throwError)+import Control.Monad.Writer (Writer, tell)+import Data.Generics (everywhere, mkT)+import Data.List (nub)+import Language.Haskell.Parser (parseModule, ParseResult(..))+import Language.Haskell.Syntax+import Text.PrettyPrint++import qualified Language.Haskell.FreeTheorems.Syntax as S+import Language.Haskell.FreeTheorems.Frontend.Error+++++------- Main parser function --------------------------------------------------+++-- | Parses a string to a list of declarations.+--   The string should contain a Haskell module.+--+--   This function is based on the Haskell98 parser of the \'haskell-src\'+--   package, i.e. the module \'Language.Haskell.Parser\'.+--   That parser supports only Haskell98 and a few extensions. Especially, it+--   does not support explicit quantification of type variables and thus no +--   higher-rank functions.+--+--   The declarations returned by 'parse' include only @type@, @data@, +--   @newtype@, @class@ and type signature declarations.+--   All other declarations and syntactical elements in the input are ignored.+--   +--   Furthermore, the following restrictions apply:+--+--   * Multi-parameter type classes are not allowed and therefore ignored. When+--     declaring a type class, the argument to the type class name must be a+--     single type variable.+--+--   * A type variable must not be applied to any type. That means, for+--     example, that the type @m a@ is not accepted.+--+--   * Contexts and @deriving@ parts in @data@ and @newtype@ declarations+--     are ignored.+--+--   * The module names are ignored. If any identifier was given qualified, the+--     module part of a qualified name is ignored.+--   +--   * Special Haskell constructors (unit, list function) are not allowed as+--     identifiers.+--+--   If a parser error occurs, as suitable error message is returned in the+--   second component of the returned tuple and the first component will be the+--   empty list.+--   However, if parsing was successful, but the parsed structures could not+--   be completely transformed into @Declaration@s, suitable transformation+--   error messages are returned in the second component while the first+--   components contains all declarations which could be transformed+--   successfully.++parse :: String -> Parsed [S.Declaration]+parse text = case parseModule text of+  ParseOk hsModule -> let decls = transform . filterDeclarations $ hsModule+                       in foldM collectDeclarations [] decls+  ParseFailed l _  -> do tell [pp ("Parse error at (" ++ show (srcLine l)+                                   ++ ":" ++ show (srcColumn l) ++ ").")]+                         return []+  where+    collectDeclarations :: [S.Declaration] -> HsDecl -> Parsed [S.Declaration]+    collectDeclarations ds d = +      case mkDeclaration d of+        Left e   -> tell [e] >> return ds+        Right d' -> return (ds ++ [d'])+      +++++------- Filter declarations ---------------------------------------------------++++-- | Filters all declarations of a Haskell module.++filterDeclarations :: HsModule -> [HsDecl]+filterDeclarations (HsModule _ _ _ _ ds) = filter isAcceptedDeclaration ds+  where+    isAcceptedDeclaration decl = case decl of+      HsTypeDecl _ _ _ _        -> True+      HsDataDecl _ _ _ _ _ _    -> True+      HsNewTypeDecl _ _ _ _ _ _ -> True+      HsClassDecl _ _ _ _ _     -> True+      HsTypeSig _ _ _           -> True+      otherwise                 -> False++++-- | Transforms a list of declarations by simplifying type signatures.++transform :: [HsDecl] -> [HsDecl]+transform = everywhere (mkT extendTypeSignature)+  where+    -- Type signatures can be given for several names at once.+    -- This function transforms declarations such that every type signature is+    -- given for exactly one name only.+    extendTypeSignature :: [HsDecl] -> [HsDecl]+    extendTypeSignature ds = case ds of+      ((HsTypeSig l ns t):ds') -> (map (\n -> HsTypeSig l [n] t) ns) ++ ds'+      otherwise                -> ds++++++------- Transform declarations ------------------------------------------------++++-- | Transforms a declaration.++mkDeclaration :: HsDecl -> ErrorOr S.Declaration+mkDeclaration decl = case decl of+  HsTypeDecl l n vs t               -> addErr l n (mkType n vs t)+  HsDataDecl l _ n vs cs _          -> addErr l n (mkData n vs cs)+  HsNewTypeDecl l _ n vs c _        -> addErr l n (mkNewtype n vs c)+  HsClassDecl l scs n [v] ds        -> addErr l n (mkClass scs n v ds)+  HsTypeSig l [n] (HsQualType cx t) -> addErr l n (mkSignature cx n t)++  HsClassDecl l _ n [] _      -> addErr l n (throwError missingVar)+  HsClassDecl l _ n (_:_:_) _ -> addErr l n (throwError noMultiParam)++  -- no other case con occur, see above function 'filterDeclarations'. +++missingVar   = pp "Missing type variable to be constrained by type class."+noMultiParam = pp "Multi-parameter type classes are not allowed."++++-- | Adds an error message based on the name of a declaration if the given+--   transformation caused an error.++addErr :: SrcLoc -> HsName -> ErrorOr S.Declaration-> ErrorOr S.Declaration+addErr loc name e = case getError e of+  Nothing  -> e+  Just doc -> throwError $+                pp ("In the declaration of `" ++ hsNameToString name +                    ++ "' at (" ++ show (srcLine loc) ++ ":"+                    ++ show (srcColumn loc) ++ "):")+                $$ nest 2 doc++++-- | Transforms the components of a type declaration.++mkType :: HsName -> [HsName] -> HsType -> ErrorOr S.Declaration+mkType name vars ty = do+  ident <- mkIdentifier name+  tvs   <- mapM mkTypeVariable vars+  t     <- mkTypeExpression ty+  return (S.TypeDecl (S.Type ident tvs t))++++-- | Transforms the components of a data declaration.++mkData :: HsName -> [HsName] -> [HsConDecl] -> ErrorOr S.Declaration+mkData name vars cons = do+  ident <- mkIdentifier name+  tvs   <- mapM mkTypeVariable vars+  ds    <- mapM mkDataConstructorDeclaration cons+  return (S.DataDecl (S.Data ident tvs ds))+       +++-- | Transforms a data constructor declaration.++mkDataConstructorDeclaration :: +    HsConDecl -> ErrorOr S.DataConstructorDeclaration++mkDataConstructorDeclaration (HsConDecl _ name btys) = mkDataConDecl name btys++mkDataConstructorDeclaration (HsRecDecl _ name rbtys) = +  let btys = concatMap (\(l,ty) -> replicate (length l) ty) rbtys+   in mkDataConDecl name btys+  +++-- | Transforms the components of a data constructor declaration.++mkDataConDecl ::+    HsName -> [HsBangType] -> ErrorOr S.DataConstructorDeclaration++mkDataConDecl name btys = do+  ident <- mkIdentifier name+  bts   <- mapM mkBangTyEx btys+  return (S.DataCon ident bts)+  where+    mkBangTyEx (HsBangedTy ty)   = liftM S.Banged   (mkTypeExpression ty)+    mkBangTyEx (HsUnBangedTy ty) = liftM S.Unbanged (mkTypeExpression ty)++++-- | Transforms the components of a newtype declaration.++mkNewtype :: HsName -> [HsName] -> HsConDecl -> ErrorOr S.Declaration+mkNewtype name vars con = do+  ident   <- mkIdentifier name+  tvs     <- mapM mkTypeVariable vars+  (con,t) <- mkNewtypeConDecl con+  return (S.NewtypeDecl (S.Newtype ident tvs con t))+  where+    mkNewtypeConDecl (HsConDecl _ c bts) = mkNCD c bts+    mkNewtypeConDecl (HsRecDecl _ c bts) = mkNCD c (snd $ unzip bts)++    mkNCD c [bty] = liftM2 (,) (mkIdentifier c) (bang bty)+    mkNCD c []      = throwError errNewtype+    mkNCD c (_:_:_) = throwError errNewtype++    errNewtype = +      pp "A `newtype' declaration must have exactly one type expression."++    bang (HsUnBangedTy ty) = mkTypeExpression ty+    bang (HsBangedTy ty)   = +      throwError (pp "A `newtype' declaration must not use a strictness flag.")++++-- | Transforms the components of a Haskell class declaration.+--   Every declaration in the class body is ignored except of type signatures.++mkClass :: HsContext -> HsName -> HsName -> [HsDecl] -> ErrorOr S.Declaration+mkClass ctx name var decls = do+  ident   <- mkIdentifier name+  tv      <- mkTypeVariable var+  superCs <- mkContext ctx >>= check tv+  sigs    <- liftM (map toSig) (mapM mkDeclaration (filter isSig decls))+    -- mapping 'isSig' is safe because after applying 'filter' no other+    -- declarations are left except of type signatures++  return (S.ClassDecl (S.Class superCs ident tv sigs))+  where+    -- Returns 'True' if a declaration is a type signature, otherwise 'False'.+    isSig :: HsDecl -> Bool+    isSig decl = case decl of+      HsTypeSig _ _ _ -> True+      otherwise       -> False++    -- Extracts a signature from a declaration.+    -- Note that no other has to be given here because all declarations passed+    -- as argument to this function are definitely type signatures.+    -- See application of 'isSig' above.+    toSig :: S.Declaration -> S.Signature+    toSig (S.TypeSig s) = s++    -- Checks if only the given type variable occurs in the second parameter.+    -- If not, an error is returned, otherwise, the list of type classes is+    -- extracted.+    check :: +        S.TypeVariable +        -> [(S.TypeClass, S.TypeVariable)] +        -> ErrorOr [S.TypeClass]+    check tv@(S.TV (S.Ident v)) ctx =+      let (tcs, tvs) = unzip ctx+       in if null (filter (/= tv) tvs)+        then return tcs+        else throwError (errClass v)++    errClass v = +      pp $ "Only `" ++ v ++ "' can be constrained by the superclasses."++++-- | Transforms the components of a Haskell type signature.+--   The context is added to the type expression.++mkSignature :: HsContext -> HsName -> HsType -> ErrorOr S.Declaration+mkSignature ctx var ty = do+  context <- mkContext ctx+  ident   <- mkIdentifier var+  t       <- mkTypeExpression ty+  return $ S.TypeSig (S.Signature ident (merge context t))+  where+    -- Merges the context and the type expression. The context is represented+    -- as type abstractions.+    merge :: +        [(S.TypeClass, S.TypeVariable)] +        -> S.TypeExpression +        -> S.TypeExpression+    merge ctx t =+      let -- All variables occurring in a context.+          vars      = (nub . snd . unzip) ctx+          -- Returns all classes associated to a type variable 'v' in 'ctx'.+          classes v = map fst (filter ((==) v . snd) ctx)+       in foldr (\v -> S.TypeAbs v (classes v)) t vars++++-- | Transforms a Haskell context.+--   If the context contains not only variables, but also more complex types,+--   this function fails with an appropriate error message.++mkContext :: HsContext -> ErrorOr [(S.TypeClass, S.TypeVariable)]+mkContext = mapM trans+  where+    trans (qname, tys) = case tys of+      [HsTyVar var] -> do ident <- liftM S.TC (mkIdentifierQ qname)+                          tv    <- mkTypeVariable var+                          return $ (ident, tv)+      otherwise     -> throwError errContext++errContext =+  pp "Only a type variable may be constrained by a class in a context."++++++------- Transform type expressions --------------------------------------------++++-- | Transforms a Haskell type.+--   Note that a type variable is not allowed to be applied to some type.++mkTypeExpression :: HsType -> ErrorOr S.TypeExpression+mkTypeExpression (HsTyVar var)     = liftM S.TypeVar (mkTypeVariable var)+mkTypeExpression (HsTyApp ty1 ty2) = mkAppTyEx ty1 [ty2]+mkTypeExpression (HsTyCon qname)   = mkTypeConstructorApp qname []++mkTypeExpression (HsTyFun ty1 ty2) = do+  t1 <- mkTypeExpression ty1+  t2 <- mkTypeExpression ty2+  return (S.TypeFun t1 t2)++mkTypeExpression (HsTyTuple tys)   = do+  ts <- mapM mkTypeExpression tys+  return (S.TypeCon (S.ConTuple (length ts)) ts)+++++-- | Collects applied types and transforms them into a type expression.++mkAppTyEx :: HsType -> [HsType] -> ErrorOr S.TypeExpression+mkAppTyEx ty tys = case ty of+  HsTyFun _ _   -> throwError $ pp ("A function type must not be applied to a "+                                    ++ "type.")+  HsTyTuple _   -> throwError (pp "A tuple type must not be applied to a type.")+  HsTyVar _     -> throwError (pp "A variable must not be applied to a type.")+  HsTyApp t1 t2 -> mkAppTyEx t1 (t2 : tys)+  HsTyCon qname -> mapM mkTypeExpression tys >>= mkTypeConstructorApp qname ++++-- | Interprets a qualified name as a type constructor and applies it to a list+--   of type expressions.+--   The function type constructor is handled specially because it has to have+--   exactly two arguments.++mkTypeConstructorApp :: +    HsQName +    -> [S.TypeExpression] +    -> ErrorOr S.TypeExpression++mkTypeConstructorApp (Special HsFunCon) [t1,t2] = return $ S.TypeFun t1 t2+mkTypeConstructorApp (Special HsFunCon) _       = throwError errorTypeConstructorApp++mkTypeConstructorApp qname              ts      = +  liftM (\con -> S.TypeCon con ts) (mkTypeConstructor qname)++errorTypeConstructorApp =+  pp "The function type constructor `->' must be applied to exactly two types."++++-- | Transforms a qualified name into a type constructor.+--   Special care is taken for primitive types which could be qualified by+--   \'Prelude\'.++mkTypeConstructor :: HsQName -> ErrorOr S.TypeConstructor+mkTypeConstructor (Qual (Module mod) hsName) = +  if mod == "Prelude"+    then return (asCon hsName)+    else return (S.Con $ hsNameToIdentifier hsName)+mkTypeConstructor (UnQual hsName)          = return $ asCon hsName+mkTypeConstructor (Special HsUnitCon)      = return $ S.ConUnit+mkTypeConstructor (Special HsListCon)      = return $ S.ConList+mkTypeConstructor (Special (HsTupleCon n)) = return $ S.ConTuple n++-- missing case '(Special HsFunCon)' cannot occur,+-- see function 'mkTypeCOnstructorApp'++-- missing case '(Special HsCons)' cannot occur,+-- this is not valid Haskell syntax++++-- | Transforms a name into a type constructor. This functions differentiates+--   between primitive types and other types.++asCon :: HsName -> S.TypeConstructor+asCon name = case name of+  HsIdent "Int"     -> S.ConInt+  HsIdent "Integer" -> S.ConInteger+  HsIdent "Float"   -> S.ConFloat+  HsIdent "Double"  -> S.ConDouble+  HsIdent "Char"    -> S.ConChar+  otherwise         -> S.Con $ hsNameToIdentifier name++++-- | Transforms a Haskell name into a type variable.++mkTypeVariable :: HsName -> ErrorOr S.TypeVariable+mkTypeVariable = return . S.TV . hsNameToIdentifier++++-- | Transforms a qualified Haskell name into an identifier.+--   The module part of a qualified name is ignored.+--   This function fails with an appropriate error message when applied to a+--   special Haskell constructor, i.e. a unit, list, function or tuple+--   constructor.++mkIdentifierQ :: HsQName -> ErrorOr S.Identifier+mkIdentifierQ (UnQual hsName)          = return (hsNameToIdentifier hsName)+mkIdentifierQ (Qual (Module _) hsName) = return (hsNameToIdentifier hsName)++mkIdentifierQ (Special HsUnitCon)      = throwErrorIdentifierQ "`()'"+mkIdentifierQ (Special HsListCon)      = throwErrorIdentifierQ "`[]'"+mkIdentifierQ (Special HsFunCon)       = throwErrorIdentifierQ "`->'"+mkIdentifierQ (Special HsCons)         = throwErrorIdentifierQ "`:'"+mkIdentifierQ (Special (HsTupleCon _)) = throwErrorIdentifierQ "for tuples"++throwErrorIdentifierQ s = throwError $ pp $+  "The constructor " ++ s ++ " must not be used as an identifier."++++-- | Transforms a Haskell name into an identifier.+--   This function encapsulates 'hsNameToIdentifier' into the 'ErrorOr' monad.++mkIdentifier :: HsName -> ErrorOr S.Identifier+mkIdentifier = return . hsNameToIdentifier++++-- | Transforms a Haskell name into an identifier.++hsNameToIdentifier :: HsName -> S.Identifier+hsNameToIdentifier = S.Ident . hsNameToString++++-- | Transforms a Haskell name into a string.+--   Haskell symbols are surrounded by parentheses.++hsNameToString :: HsName -> String+hsNameToString (HsIdent s)  = s+hsNameToString (HsSymbol s) = "(" ++ s ++ ")"++
+ src/Language/Haskell/FreeTheorems/Parser/Hsx.hs view
@@ -0,0 +1,528 @@+++module Language.Haskell.FreeTheorems.Parser.Hsx (parse) where++++import Control.Monad (foldM, liftM, liftM2, when)+import Control.Monad.Error (Error (..), throwError)+import Control.Monad.Reader (ReaderT, runReaderT, local, ask)+import Control.Monad.Trans (lift)+import Control.Monad.Writer (Writer, tell)+import Data.Generics (everywhere, mkT)+import Data.Maybe (fromMaybe)+import Data.List (nub, (\\), intersect)+import Language.Haskell.Hsx.Parser (parseModule, ParseResult(..))+import Language.Haskell.Hsx.Syntax+import Text.PrettyPrint++import qualified Language.Haskell.FreeTheorems.Syntax as S+import Language.Haskell.FreeTheorems.Frontend.Error+++++------- Main parser function --------------------------------------------------+++-- | Parses a string to a list of declarations.+--   The string should contain a Haskell module.+--+--   This function is based on the extended Haskell parser of the +--   \'haskell-src-exts\' package.+--+--   The declarations returned by 'parse' include only @type@, @data@, +--   @newtype@, @class@ and type signature declarations.+--   All other declarations and syntactical elements in the input are ignored.+--   +--   Furthermore, the following restrictions apply:+--+--   * Multi-parameter type classes are not allowed and therefore ignored. When+--     declaring a type class, the argument to the type class name must be a+--     single type variable.+--+--   * Only type variables can be constrained by type classes. That means, for+--     example, the type @Eq [a] => [a]@ is not accepted.+--+--   * A type variable must not be applied to any type. That means, for+--     example, that the type @m a@ is not accepted.+--+--   * Contexts and @deriving@ parts in @data@ and @newtype@ declarations+--     are ignored.+--+--   * The module names are ignored. If any identifier was given qualified, the+--     module part of a qualified name is ignored.+--   +--   * Special Haskell constructors (unit, list function) are not allowed as+--     identifiers.+--+--   * Further extensions over Haskell98 allowed by the underlying parser are+--     also forbidden, namely generalised algebraic data types and unboxed +--     tuples.+--+--   If a parser error occurs, as suitable error message is returned in the+--   second component of the returned tuple and the first component will be the+--   empty list.+--   However, if parsing was successful, but the parsed structures could not+--   be completely transformed into @Declaration@s, suitable transformation+--   error messages are returned in the second component while the first+--   components contains all declarations which could be transformed+--   successfully.++parse :: String -> Parsed [S.Declaration]+parse text = case parseModule text of+  ParseOk hsModule -> let decls = transform . filterDeclarations $ hsModule+                       in foldM collectDeclarations [] decls+  ParseFailed l _  -> do tell [pp ("Parse error at (" ++ show (srcLine l)+                                   ++ ":" ++ show (srcColumn l) ++ ").")]+                         return []+  where+    collectDeclarations :: [S.Declaration] -> HsDecl -> Parsed [S.Declaration]+    collectDeclarations ds d = +      case mkDeclaration d of+        Left e   -> tell [e] >> return ds+        Right d' -> return (ds ++ [d'])+      +++++------- Filter declarations ---------------------------------------------------++++-- | Filters all declarations of a Haskell module.++filterDeclarations :: HsModule -> [HsDecl]+filterDeclarations (HsModule _ _ _ _ ds) = filter isAcceptedDeclaration ds+  where+    isAcceptedDeclaration decl = case decl of+      HsTypeDecl _ _ _ _        -> True+      HsDataDecl _ _ _ _ _ _    -> True+      HsNewTypeDecl _ _ _ _ _ _ -> True+      HsClassDecl _ _ _ _ _ _   -> True+      HsTypeSig _ _ _           -> True+      otherwise                 -> False++++-- | Transforms a list of declarations by simplifying type signatures.++transform :: [HsDecl] -> [HsDecl]+transform = everywhere (mkT extendTypeSignature)+  where+    -- Type signatures can be given for several names at once.+    -- This function transforms declarations such that every type signature is+    -- given for exactly one name only.+    extendTypeSignature :: [HsDecl] -> [HsDecl]+    extendTypeSignature ds = case ds of+      ((HsTypeSig l ns t):ds') -> (map (\n -> HsTypeSig l [n] t) ns) ++ ds'+      otherwise                -> ds++++++------- Transform declarations ------------------------------------------------+++-- | Transforms a declaration.++mkDeclaration :: HsDecl -> ErrorOr S.Declaration+mkDeclaration decl = case decl of+  HsTypeDecl l n vs t           -> addErr l n (mkType n vs t)+  HsDataDecl l _ n vs cs _      -> addErr l n (mkData n vs cs)+  HsNewTypeDecl l _ n vs c _    -> addErr l n (mkNewtype n vs c)+  HsClassDecl l scs n [v] _ ds  -> addErr l n (mkClass scs n v ds)+  HsTypeSig l [n] t             -> addErr l n (mkSignature n t)++  HsClassDecl l _ n [] _ _      -> addErr l n (throwError missingVar)+  HsClassDecl l _ n (_:_:_) _ _ -> addErr l n (throwError noMultiParam)++  -- no other case con occur, see above function 'filterDeclarations'. +++missingVar   = pp "Missing type variable to be constrained by the type class."+noMultiParam = pp "Multi-parameter type classes are not allowed."++++-- | Adds an error message based on the name of a declaration if the given+--   transformation caused an error.++addErr :: SrcLoc -> HsName -> ErrorOr S.Declaration-> ErrorOr S.Declaration+addErr loc name e = case getError e of+  Nothing  -> e+  Just doc -> throwError $+                pp ("In the declaration of `" ++ hsNameToString name +                    ++ "' at (" ++ show (srcLine loc) ++ ":" +                    ++ show (srcColumn loc) ++ "):")+                $$ nest 2 doc++++-- | Transforms the components of a type declaration.++mkType :: HsName -> [HsName] -> HsType -> ErrorOr S.Declaration+mkType name vars ty = do+  ident <- mkIdentifier name+  tvs   <- mapM mkTypeVariable vars+  t     <- mkTypeExpression ty+  return (S.TypeDecl (S.Type ident tvs t))++++-- | Transforms the components of a data declaration.++mkData :: HsName -> [HsName] -> [HsQualConDecl] -> ErrorOr S.Declaration+mkData name vars cons = do+  ident <- mkIdentifier name+  tvs   <- mapM mkTypeVariable vars+  ds    <- mapM mkDataConstructorDeclaration cons+  return (S.DataDecl (S.Data ident tvs ds))+       +++-- | Transforms a data constructor declaration.++mkDataConstructorDeclaration :: +    HsQualConDecl -> ErrorOr S.DataConstructorDeclaration++mkDataConstructorDeclaration (HsQualConDecl _ _ _ (HsConDecl name btys)) =+  mkDataConDecl name btys++mkDataConstructorDeclaration (HsQualConDecl _ _ _ (HsRecDecl name rbtys)) =+  let btys = concatMap (\(l,ty) -> replicate (length l) ty) rbtys+   in mkDataConDecl name btys+  +++-- | Transforms the components of a data constructor declaration.++mkDataConDecl ::+    HsName +    -> [HsBangType] +    -> ErrorOr S.DataConstructorDeclaration++mkDataConDecl name btys = do+  ident <- mkIdentifier name+  bts   <- mapM mkBangTyEx btys+  return (S.DataCon ident bts)+  where+    mkBangTyEx (HsBangedTy ty)   = liftM S.Banged   (mkTypeExpression ty)+    mkBangTyEx (HsUnBangedTy ty) = liftM S.Unbanged (mkTypeExpression ty)++++-- | Transforms the components of a newtype declaration.++mkNewtype :: HsName -> [HsName] -> HsQualConDecl -> ErrorOr S.Declaration+mkNewtype name vars (HsQualConDecl _ _ _ con) = do+  ident   <- mkIdentifier name+  tvs     <- mapM mkTypeVariable vars+  (con,t) <- mkNewtypeConDecl con+  return (S.NewtypeDecl (S.Newtype ident tvs con t))+  where+    mkNewtypeConDecl (HsConDecl c bts) = mkNCD c bts+    mkNewtypeConDecl (HsRecDecl c bts) = mkNCD c (snd $ unzip bts)++    mkNCD c [bty] = liftM2 (,) (mkIdentifier c) (bang bty)+    mkNCD c []      = throwError errNewtype+    mkNCD c (_:_:_) = throwError errNewtype++    errNewtype = +      pp "A `newtype' declaration must have exactly one type expression."++    bang (HsUnBangedTy ty) = mkTypeExpression ty+    bang (HsBangedTy ty)   = +      throwError (pp "A `newtype' declaration must not use a strictness flag.")++++-- | Transforms the components of a Haskell class declaration.+--   Every declaration in the class body is ignored except of type signatures.++mkClass :: HsContext -> HsName -> HsName -> [HsDecl] -> ErrorOr S.Declaration+mkClass ctx name var decls = do+  ident   <- mkIdentifier name+  tv      <- mkTypeVariable var+  superCs <- mkContext ctx >>= check tv+  sigs    <- liftM (map toSig) (mapM mkDeclaration (filter isSig decls))+    -- mapping 'isSig' is safe because after applying 'filter' no other+    -- declarations are left except of type signatures++  return (S.ClassDecl (S.Class superCs ident tv sigs))+  where+    -- Returns 'True' if a declaration is a type signature, otherwise 'False'.+    isSig :: HsDecl -> Bool+    isSig decl = case decl of+      HsTypeSig _ _ _ -> True+      otherwise       -> False++    -- Extracts a signature from a declaration.+    -- Note that no other has to be given here because all declarations passed+    -- as argument to this function are definitely type signatures.+    -- See application of 'isSig' above.+    toSig :: S.Declaration -> S.Signature+    toSig (S.TypeSig s) = s++    -- Checks if only the given type variable occurs in the second parameter.+    -- If not, an error is returned, otherwise, the list of type classes is+    -- extracted.+    check :: +        S.TypeVariable +        -> [(S.TypeClass, S.TypeVariable)] +        -> ErrorOr [S.TypeClass]+    check tv@(S.TV (S.Ident v)) ctx =+      let (tcs, tvs) = unzip ctx+       in if null (filter (/= tv) tvs)+        then return tcs+        else throwError (errClass v)++    errClass v = +      pp $ "Only `" ++ v ++ "' can be constrained by the superclasses."++++-- | Transforms the components of a Haskell type signature.+--   The context is added to the type expression.++mkSignature :: HsName -> HsType -> ErrorOr S.Declaration+mkSignature var ty = do+  ident   <- mkIdentifier var+  t       <- mkTypeExpression ty+  return $ S.TypeSig (S.Signature ident t)++++-- | Transforms a Haskell context.+--   If the context contains not only variables, but also more complex types,+--   this function fails with an appropriate error message.++mkContext :: HsContext -> ErrorOr [(S.TypeClass, S.TypeVariable)]+mkContext = mapM trans+  where+    trans (HsClassA qname [HsTyVar var]) = do+      ident <- liftM S.TC (mkIdentifierQ qname)+      tv    <- mkTypeVariable var+      return $ (ident, tv) +    +    trans (HsClassA _ _) = throwError errContext+    trans (HsIParam _ _) = throwError errImplicit++errContext =+  pp "Only a type variable may be constrained by a class in a context."++errImplicit = +  pp "Implicit parameters are not allowed." +++++------- Transform type expressions --------------------------------------------++++type EnvErrorOr a = ReaderT [S.TypeVariable] (Either Doc) a++++mkTypeExpression :: HsType -> ErrorOr S.TypeExpression+mkTypeExpression ty = runReaderT (mkTypeExpressionT ty) []++++-- | Transforms a Haskell type.+--   Note that a type variable is not allowed to be applied to some type.++mkTypeExpressionT :: HsType -> EnvErrorOr S.TypeExpression+mkTypeExpressionT (HsTyVar var)     = liftM S.TypeVar +                                            (lift (mkTypeVariable var))+mkTypeExpressionT (HsTyApp ty1 ty2) = lift (mkAppTyEx ty1 [ty2])+mkTypeExpressionT (HsTyCon qname)   = lift (mkTypeConstructorApp qname [])++mkTypeExpressionT (HsTyInfix ty1 qname ty2) = -- infix type constructor+  mkTypeExpressionT (HsTyApp (HsTyApp (HsTyCon qname) ty1) ty2)++mkTypeExpressionT (HsTyFun ty1 ty2) = do+  t1 <- mkTypeExpressionT ty1+  t2 <- mkTypeExpressionT ty2+  return (S.TypeFun t1 t2)++mkTypeExpressionT (HsTyTuple Boxed tys)   = do+  ts <- mapM mkTypeExpressionT tys+  return (S.TypeCon (S.ConTuple (length ts)) ts)++mkTypeExpressionT (HsTyForall maybeVars ctx ty) =+  mkForallTyEx (fromMaybe [] maybeVars) ctx ty++mkTypeExpressionT (HsTyPred _) = +  throwError (pp "Implicit parameters are not allowed.")++mkTypeExpressionT (HsTyTuple Unboxed _ ) = +  throwError (pp "Unboxed tuples are not allowed.")++++-- | Checks type abstractions for unique variables, merges the contexts and+--   creates a type expression.++mkForallTyEx :: [HsName] -> HsContext -> HsType -> EnvErrorOr S.TypeExpression+mkForallTyEx vars ctx ty = do+  vs <- unique vars+  cx <- lift (mkContext ctx)+  let unboundVars = (nub . snd . unzip $ cx) \\ vs+  let allVars = vs ++ unboundVars+  knownVars <- ask+  let errVars = knownVars `intersect` unboundVars+  when (not (null errVars)) $ throwError $ pp $ +    "The constrained type variable `" ++ (S.unpackIdent . (\(S.TV i) -> i) . head $ errVars)+    ++ "' must be explicitly quantified."+  liftM (merge allVars cx) (local (++ allVars) (mkTypeExpressionT ty))+  where+    -- Checks if the elements of the argument are unique, and throws an error+    -- otherwise.+    unique :: [HsName] -> EnvErrorOr [S.TypeVariable]+    unique []     = return []+    unique (v:vs) = if v `elem` vs+                      then throwError (pp $+                             "Conflicting type variables in a type "+                             ++ "abstraction, the type variable `"+                             ++ hsNameToString v ++ "' is quantified more "+                             ++ "than once.")+                      else liftM2 (:) (lift (mkTypeVariable v)) (unique vs)++    -- Merges the context and the type expression. The context is represented+    -- as type abstractions.+    merge :: +        [S.TypeVariable] -> [(S.TypeClass, S.TypeVariable)] +        -> S.TypeExpression -> S.TypeExpression+    merge vs cx t = foldr (\v -> S.TypeAbs v (classes cx v)) t vs++    -- Returns classes constraining v.+    classes cx v = nub (map fst (filter ((==) v . snd) cx))++++-- | Collects applied types and transforms them into a type expression.++mkAppTyEx :: HsType -> [HsType] -> ErrorOr S.TypeExpression+mkAppTyEx ty tys = case ty of+  HsTyFun _ _   -> throwError $ pp ("A function type must not be applied to a "+                                    ++ "type.")+  HsTyTuple _ _ -> throwError (pp "A tuple type must not be applied to a type.")+  HsTyVar _     -> throwError (pp "A variable must not be applied to a type.")+  HsTyApp t1 t2 -> mkAppTyEx t1 (t2 : tys)+  HsTyCon qname -> mapM mkTypeExpression tys >>= mkTypeConstructorApp qname ++++-- | Interprets a qualified name as a type constructor and applies it to a list+--   of type expressions.+--   The function type constructor is handled specially because it has to have+--   exactly two arguments.++mkTypeConstructorApp :: +    HsQName +    -> [S.TypeExpression] +    -> ErrorOr S.TypeExpression++mkTypeConstructorApp (Special HsFunCon) [t1,t2] = return $ S.TypeFun t1 t2+mkTypeConstructorApp (Special HsFunCon) _       = throwError errorTypeConstructorApp++mkTypeConstructorApp qname              ts      = +  liftM (\con -> S.TypeCon con ts) (mkTypeConstructor qname)++errorTypeConstructorApp =+  pp "The function type constructor `->' must be applied to exactly two types."++++-- | Transforms a qualified name into a type constructor.+--   Special care is taken for primitive types which could be qualified by+--   \'Prelude\'.++mkTypeConstructor :: HsQName -> ErrorOr S.TypeConstructor+mkTypeConstructor (Qual (Module mod) hsName) = +  if mod == "Prelude"+    then return (asCon hsName)+    else return (S.Con $ hsNameToIdentifier hsName)+mkTypeConstructor (UnQual hsName)          = return $ asCon hsName+mkTypeConstructor (Special HsUnitCon)      = return $ S.ConUnit+mkTypeConstructor (Special HsListCon)      = return $ S.ConList+mkTypeConstructor (Special (HsTupleCon n)) = return $ S.ConTuple n++-- missing case '(Special HsFunCon)' cannot occur,+-- see function 'mkTypeCOnstructorApp'++-- missing case '(Special HsCons)' cannot occur,+-- this is not valid Haskell syntax++++-- | Transforms a name into a type constructor. This functions differentiates+--   between primitive types and other types.++asCon :: HsName -> S.TypeConstructor+asCon name = case name of+  HsIdent "Int"     -> S.ConInt+  HsIdent "Integer" -> S.ConInteger+  HsIdent "Float"   -> S.ConFloat+  HsIdent "Double"  -> S.ConDouble+  HsIdent "Char"    -> S.ConChar+  otherwise         -> S.Con $ hsNameToIdentifier name++++-- | Transforms a Haskell name into a type variable.++mkTypeVariable :: HsName -> ErrorOr S.TypeVariable+mkTypeVariable = return . S.TV . hsNameToIdentifier++++-- | Transforms a qualified Haskell name into an identifier.+--   The module part of a qualified name is ignored.+--   This function fails with an appropriate error message when applied to a+--   special Haskell constructor, i.e. a unit, list, function or tuple+--   constructor.++mkIdentifierQ :: HsQName -> ErrorOr S.Identifier+mkIdentifierQ (UnQual hsName)          = return (hsNameToIdentifier hsName)+mkIdentifierQ (Qual (Module _) hsName) = return (hsNameToIdentifier hsName)++mkIdentifierQ (Special HsUnitCon)      = throwErrorIdentifierQ "`()'"+mkIdentifierQ (Special HsListCon)      = throwErrorIdentifierQ "`[]'"+mkIdentifierQ (Special HsFunCon)       = throwErrorIdentifierQ "`->'"+mkIdentifierQ (Special HsCons)         = throwErrorIdentifierQ "`:'"+mkIdentifierQ (Special (HsTupleCon _)) = throwErrorIdentifierQ "for tuples"++throwErrorIdentifierQ s = throwError $ pp $+  "The constructor " ++ s ++ " must not be used as an identifier."++++-- | Transforms a Haskell name into an identifier.+--   This function encapsulates 'hsNameToIdentifier' into the 'ErrorOr' monad.++mkIdentifier :: HsName -> ErrorOr S.Identifier+mkIdentifier = return . hsNameToIdentifier++++-- | Transforms a Haskell name into an identifier.++hsNameToIdentifier :: HsName -> S.Identifier+hsNameToIdentifier = S.Ident . hsNameToString++++-- | Transforms a Haskell name into a string.+--   Haskell symbols are surrounded by parentheses.++hsNameToString :: HsName -> String+hsNameToString (HsIdent s)  = s+hsNameToString (HsSymbol s) = "(" ++ s ++ ")"+++
+ src/Language/Haskell/FreeTheorems/PrettyBase.hs view
@@ -0,0 +1,58 @@++++module Language.Haskell.FreeTheorems.PrettyBase where++++import Text.PrettyPrint++++-- | Prints a list of documents where all documents not fitting on a line are+--   printed in following lines indented by the amount given as the first+--   argument.++isep :: Int -> [Doc] -> Doc+isep _ [] = empty+isep n (d:ds) = nest n $ fsep $ (nest (-n) d) : ds++++-- | Puts parentheses around a document, if the first argument is 'True'.++parensIf :: Bool -> Doc -> Doc+parensIf putParens = if putParens then parens else id++++-- | A data type to describe around which type expressions parentheses are to be+--   put.++data Parens+  = NoParens        -- ^ Don't put any parentheses.+  | ParensFun       -- ^ The type expression occurs as an argument to a+                    --   function.+  | ParensFunOrCon  -- ^ The type expression occurs as an argument to a+                    --   function, type constructor or type class.+  deriving (Eq, Ord)++++-- | Returns a document when a condition holds. Otherwise, the empty document+--   is returned.++when :: Bool -> Doc -> Doc+when False = const empty+when True  = id++++-- | Returns a list of documents when a condition holds. Otherwise, the empty+--   list is returned.++whenL :: Bool -> [Doc] -> [Doc]+whenL False = const []+whenL True  = id++
+ src/Language/Haskell/FreeTheorems/PrettyTheorems.hs view
@@ -0,0 +1,464 @@++++-- | Pretty printer for theorems.+--   It provides functions to transform theorems into documents.+--+--   See the module \"Text.PrettyPrint\" for more details about the used+--   document type.++module Language.Haskell.FreeTheorems.PrettyTheorems (+    PrettyTheoremOption (..)+  , prettyTheorem+  , prettyRelationVariable+  , prettyUnfoldedLift+  , prettyUnfoldedClass+) where++++import Data.List (partition, find)+import Data.Maybe (mapMaybe)+import Text.PrettyPrint++import Language.Haskell.FreeTheorems.Syntax+import Language.Haskell.FreeTheorems.LanguageSubsets+import Language.Haskell.FreeTheorems.Theorems+import Language.Haskell.FreeTheorems.PrettyBase+import Language.Haskell.FreeTheorems.PrettyTypes++++------- Options ---------------------------------------------------------------+++-- | Possible options for pretty-printing theorems.++data PrettyTheoremOption+  = OmitTypeInstantiations      +        -- ^ Omits all instantiations of types. This option makes theorems +        --   usually better readable.+  +  | OmitLanguageSubsets+        -- ^ Omit mentioning language subsets explicitly for certain relations.+  +  deriving Eq++++------- Pretty control data ---------------------------------------------------+++data PrettyControl = PrettyControl+  { options     :: [PrettyTheoremOption]+  , parentheses :: Parens+  , inPremise   :: Bool+  }+++defaultPrettyControl :: [PrettyTheoremOption] -> PrettyControl+defaultPrettyControl opts = +  PrettyControl+    { options = opts+    , parentheses = NoParens+    , inPremise = False+    }+++withTypeInstantiations :: PrettyControl -> Bool+withTypeInstantiations = notElem OmitTypeInstantiations . options+++withLanguageSubsets :: PrettyControl -> Bool+withLanguageSubsets = notElem OmitLanguageSubsets . options+++noParens :: PrettyControl -> PrettyControl+noParens pc = pc { parentheses = NoParens }+++useParens :: PrettyControl -> PrettyControl+useParens pc = pc { parentheses = ParensFun }+++withParens :: PrettyControl -> Bool+withParens pc = parentheses pc > NoParens+++setPremise :: PrettyControl -> PrettyControl+setPremise pc = pc { inPremise = not (inPremise pc) }++++++------- Language subsets ------------------------------------------------------+++-- | Pretty-prints a language subset.++prettyLanguageSubset :: LanguageSubset -> Doc+prettyLanguageSubset l = case l of+  BasicSubset                       -> text "basic"+  SubsetWithFix EquationalTheorem   -> text "fix" <> comma <> text "="+  SubsetWithFix InequationalTheorem -> text "fix" <> comma <> text "[="+  SubsetWithSeq EquationalTheorem   -> text "seq" <> comma <> text "="+  SubsetWithSeq InequationalTheorem -> text "seq" <> comma <> text "[="++++++------- Formulas --------------------------------------------------------------+++-- | Pretty-prints a theorem.++prettyTheorem :: [PrettyTheoremOption] -> Theorem -> Doc+prettyTheorem opt = prettyFormula (defaultPrettyControl opt)++++-- | Pretty-prints a formula.++prettyFormula :: PrettyControl -> Formula -> Doc+prettyFormula pc (ForallRelations v (t1,t2) res f) =+  let rv = prettyRelationVariable v+      ts = prettyTypeExpression NoParens t1 <> comma +           <> prettyTypeExpression NoParens t2+      cs = getTypeClasses res+      ds = if null cs+             then empty+             else parens . fsep . punctuate comma . map prettyTypeClass $ cs+   in parensIf (withParens pc) $ +        sep +          [ fsep $+              [ text "forall"+              , ts, text "in", text "TYPES" <> ds <> comma+              , rv, text "in"+              , text "REL" <> parens ts+                 <> if null res then char '.' else comma ]+              ++ prettyRestrictionList rv res+          , nest 1 (prettyFormula (noParens pc) f) ]++prettyFormula pc (ForallFunctions v (t1,t2) res f) =+  let ts = prettyTypeExpression NoParens t1 <> comma +           <> prettyTypeExpression NoParens t2+      pv = either prettyTermVariable prettyTermVariable v+      cs = getTypeClasses res+      ds = if null cs+             then empty+             else parens . fsep . punctuate comma . map prettyTypeClass $ cs+   in parensIf (withParens pc) $ +        sep +          [ fsep $+              [ text "forall"+              , ts, text "in", text "TYPES" <> ds <> comma +              , pv, text "::"+              , prettyTypeExpression NoParens +                (either (\_ -> TypeFun t1 t2) (\_ -> TypeFun t2 t1) v)+                 <> if null res then char '.' else comma ]+              ++ prettyRestrictionList pv res+          , nest 1 (prettyFormula (noParens pc) f) ]++prettyFormula pc (ForallPairs (v1,v2) r f) =+  parensIf (withParens pc) $+    sep [ fsep [ text "forall"+               , parens (prettyTermVariable v1 <> comma +                         <+> prettyTermVariable v2)+               , text "in", prettyRelation (useParens pc) False r <> char '.' ]+        , nest 1 (prettyFormula (noParens pc) f) ]++prettyFormula pc (ForallVariables v t f) =+  parensIf (withParens pc) $+    sep [ fsep [ text "forall", prettyTermVariable v, text "::"+               , prettyTypeExpression NoParens t <> char '.' ]+        , nest 1 (prettyFormula (noParens pc) f) ]++prettyFormula pc (Equivalence f1 f2) =+  parensIf (withParens pc) $+    sep [ prettyFormula (useParens pc) f1+        , text "<=>" <+> prettyFormula (useParens pc) f2]++prettyFormula pc (Implication f1 f2) =+  parensIf (withParens pc) $+    sep [ prettyFormula (useParens (setPremise pc)) f1+        , text "==>" <+> prettyFormula (useParens pc) f2]++prettyFormula pc (Conjunction f1 f2) =+  parensIf (withParens pc) $+    sep [ prettyFormula (useParens pc) f1+        , text "&&" <+> prettyFormula (useParens pc) f2]++prettyFormula pc (Predicate predicate) = +  parensIf (withParens pc) (prettyPredicate (noParens pc) predicate)++++-- | Returns the type classes of a restriction list.++getTypeClasses :: [Restriction] -> [TypeClass]+getTypeClasses = concatMap exTC+  where exTC r = case r of+          RespectsClasses tcs -> tcs+          otherwise           -> []++++-- | Pretty-prints a list of restrictions.++prettyRestrictionList :: Doc -> [Restriction] -> [Doc]+prettyRestrictionList v res = case res of+  []         -> []+  (r:[])     -> v : [ prettyRestriction r <> char '.' ]+  (r1:r2:[]) -> v : (punctuate comma [prettyRestriction r1] ++ [text "and", prettyRestriction r2 <> char '.' ])+  otherwise  -> let dres = map prettyRestriction res+                 in v : (punctuate comma (init dres ++ [text "and"]) +                        ++ [last dres <> char '.' ])++++-- | Pretty-prints a restriction.++prettyRestriction :: Restriction -> Doc+prettyRestriction Strict           = text "strict"+prettyRestriction Continuous       = text "continuous"+prettyRestriction Total            = text "total"+prettyRestriction BottomReflecting = text "bottom-reflecting"+prettyRestriction LeftClosed       = text "left-closed"+prettyRestriction (RespectsClasses tcs) =+  when (not (null tcs)) $+    fsep [ text "respects"+         , parensIf (length tcs > 1) $+             fsep . punctuate comma . map prettyTypeClass $ tcs ]++++-- | Pretty-prints a predicate.++prettyPredicate :: PrettyControl -> Predicate -> Doc+prettyPredicate pc (IsMember x y r) =+  fsep [ parens (prettyTerm (noParens pc) x <> comma +                 <+> prettyTerm (noParens pc) y)+       , text "in", prettyRelation (useParens pc) False r ]++prettyPredicate pc (IsEqual x y) = +  fsep [ prettyTerm (noParens pc) x, char '=', prettyTerm (noParens pc) y ]++prettyPredicate pc (IsLessEq x y) = +  fsep [ prettyTerm (noParens pc) x, text "[=", prettyTerm (noParens pc) y ]++prettyPredicate pc (IsNotBot x) = +  parensIf (withParens pc) $+    fsep [ prettyTerm (noParens pc) x, text "/=", text "_|_" ]++++-- | Pretty-prints a term.++prettyTerm :: PrettyControl -> Term -> Doc+prettyTerm pc (TermVar v) = prettyTermVariable v++prettyTerm pc (TermApp t1 t2) = +  parensIf (withParens pc) $ prettyTerm (noParens pc) t1 <+> prettyTerm (useParens pc) t2++prettyTerm pc (TermIns t ty) =+  let d = prettyTypeExpression NoParens ty+      withTypes = withTypeInstantiations pc+      p = if withTypes then useParens else noParens+      pt = prettyTerm (useParens pc) t +   in pt <> showInstantiation withTypes d++++-- | Shows an instantiation (by a type).++showInstantiation :: Bool -> Doc -> Doc+showInstantiation False _ = empty+showInstantiation True  d = char '_' <> braces d+++-- | Pretty-prints a term variable.++prettyTermVariable :: TermVariable -> Doc+prettyTermVariable (TVar v) = text v++++------- Relations -------------------------------------------------------------+++-- | Pretty-prints a relation.++prettyRelation :: PrettyControl -> Bool -> Relation -> Doc+prettyRelation _ _ (RelVar _ rv) = prettyRelationVariable rv++prettyRelation pc _ (FunVar ri (Left t)) = +  case theoremType (relationLanguageSubset ri) of +    EquationalTheorem   -> prettyTerm (noParens pc) t+    InequationalTheorem -> prettyTerm (useParens pc) t <> text " ; [="++prettyRelation pc _ (FunVar _ (Right t)) = +  text "[= ; " <> prettyTerm (useParens pc) t <> text "^{-1}"++prettyRelation _ _ (RelBasic ri) = +  case theoremType (relationLanguageSubset ri) of+    EquationalTheorem   -> text "id"+    InequationalTheorem -> text "[="++prettyRelation pc omitOrder (RelLift ri con rs) =+  let pl = case relationLanguageSubset ri of+             BasicSubset -> text "lift"+             otherwise   -> case theoremType (relationLanguageSubset ri) of+                              EquationalTheorem   -> text "lift"+                              InequationalTheorem -> if omitOrder+                                                       then text "lift"+                                                       else text "[= ; lift"+   in pl <> braces (prettyTypeConstructor con)+         <> (parens . foldl1 (\a b -> a <> comma <> b)  +                    . map (prettyRelation (noParens pc) False) $ rs)+--         <> (parens . fsep . punctuate comma +--                           . map (prettyRelation (noParens pc) False) $ rs)+++prettyRelation pc _ (RelFun ri r1 r2) = +  let l = if withLanguageSubsets pc+            then case relationLanguageSubset ri of+                   SubsetWithSeq _ -> text "^" <> braces (prettyLanguageSubset +                                                  (relationLanguageSubset ri))+                   otherwise       -> empty+            else empty+   in parensIf (withParens pc) $+        fsep [ prettyRelation (useParens pc) False r1+             , text "->" <> l+             , prettyRelation (useParens pc) False r2 ]++prettyRelation pc _ (RelAbs ri v _ res r) = +  let tcs = getTypeClasses res+      dcs = if null tcs+              then empty+              else parens (fcat . punctuate comma . map prettyTypeClass $ tcs)+   in parensIf (withParens pc) $+        fsep [ text "forall"+             , prettyRelationVariable v+             , text "in"+             , text "REL" +                <> (when (withLanguageSubsets pc) $+                     text "^" <> braces (prettyLanguageSubset +                                          (relationLanguageSubset ri)))+                <> dcs+                <> char '.'+             , prettyRelation (useParens pc) False r ]++prettyRelation pc _ (FunAbs ri v _ res r) =+  let tcs = getTypeClasses res+      dcs = if null tcs+              then empty+              else parens (fcat . punctuate comma . map prettyTypeClass $ tcs)+   in parensIf (withParens pc) $+        fsep [ text "forall"+             , either prettyTermVariable prettyTermVariable v+             , text "in"+             , either (const (text "FUN")) (const (text "FUN_i")) v+                <> (when (withLanguageSubsets pc) $+                     text "^" <> braces (prettyLanguageSubset +                                          (relationLanguageSubset ri)))+                <> dcs+                <> char '.'+             , prettyRelation (useParens pc) False r ]++++++-- | Pretty-prints a relation variable.++prettyRelationVariable :: RelationVariable -> Doc+prettyRelationVariable (RVar r) = text r++++++------- Unfolded formulas -----------------------------------------------------+++-- | Pretty-prints an unfolded lift relation.++prettyUnfoldedLift :: [PrettyTheoremOption] -> UnfoldedLift -> Doc+prettyUnfoldedLift opt (UnfoldedLift r dcs) =+  let pc = defaultPrettyControl opt+      sc = braces . fsep . punctuate comma . map (prettyUnfoldedDataCon pc) +           $ simpleCons+      ccs = map (braces . prettyUnfoldedDataCon pc) complexCons+      dcs = if null simpleCons then ccs else sc : ccs+   in vcat $+        [ prettyRelation (noParens pc) True r ]+        ++ zipWith (\t d -> nest 2 (text t <+> d)) ("=" : repeat "u") dcs+  where+    (simpleCons, complexCons) = partition isSimpleCon dcs++    isSimpleCon d = case d of+      BotPair   -> True+      ConPair _ -> True+      otherwise -> False++++-- | Pretty-prints an unfolded data constructor declaration.++prettyUnfoldedDataCon :: PrettyControl -> UnfoldedDataCon -> Doc+prettyUnfoldedDataCon _ BotPair = +  parens (fsep (punctuate comma [ text "_|_", text "_|_" ]))++prettyUnfoldedDataCon _ (ConPair d) =+  let d' = prettyDataConstructor d []+   in parens (fsep (punctuate comma [ d', d' ]))++prettyUnfoldedDataCon pc (ConMore d xs ys f) = +  let d1 = prettyDataConstructor d xs+      d2 = prettyDataConstructor d ys+   in sep [ fsep [ parens . fsep . punctuate comma $ [d1, d2] +                 , text "|" ]+          , nest 2 (prettyFormula pc f) ]++++-- | Pretty-prints a data constructor.++prettyDataConstructor :: DataConstructor -> [TermVariable] -> Doc+prettyDataConstructor DConEmptyList _    = brackets empty+prettyDataConstructor DConConsList [x,y] = fsep [prettyTermVariable x, text ":"+                                                , prettyTermVariable y]+prettyDataConstructor DConConsList _     = brackets empty+prettyDataConstructor (DConTuple _) xs = +  parens (fsep . punctuate comma . map prettyTermVariable $ xs)+prettyDataConstructor (DCon s) xs = fsep (text s : map prettyTermVariable xs)++++-- | Pretty-prints an unfolded type class.++prettyUnfoldedClass :: [PrettyTheoremOption] -> UnfoldedClass -> Doc+prettyUnfoldedClass opt (UnfoldedClass tcs tc v fs) =+  let pc = defaultPrettyControl opt+      pv = either prettyRelationVariable prettyTermVariable v +      intro = [ pv, text "respects", prettyTypeClass tc ] +   in if null tcs && null fs+        then fsep $ intro ++ (map text . words $+                      "without further restrictions")+        else vcat $+               (fsep $+                  intro ++ [text "if"]+                  ++ if null tcs+                       then []+                       else [ pv, text "respects" ]+                            ++ (punctuate comma . map prettyTypeClass $ tcs)+                  ++ if not (null tcs) && not (null fs)+                       then [ text "and" ]+                       else [])+               : (map (nest 2 . prettyFormula pc) fs)++++
+ src/Language/Haskell/FreeTheorems/PrettyTypes.hs view
@@ -0,0 +1,285 @@++++-- | Pretty printer for Haskell declarations.+--   It provides functions to transform declarations and especially type+--   signatures into documents.+--+--   See the module \"Text.PrettyPrint\" for more details about the used+--   document type.++module Language.Haskell.FreeTheorems.PrettyTypes where++++import Text.PrettyPrint++import Language.Haskell.FreeTheorems.BasicSyntax+import Language.Haskell.FreeTheorems.PrettyBase++++------- Declarations ----------------------------------------------------------+++-- | Pretty-prints a declaration.++prettyDeclaration :: Declaration -> Doc+prettyDeclaration (TypeDecl decl)    = prettyTypeDeclaration decl+prettyDeclaration (DataDecl decl)    = prettyDataDeclaration decl+prettyDeclaration (NewtypeDecl decl) = prettyNewtypeDeclaration decl+prettyDeclaration (ClassDecl decl)   = prettyClassDeclaration decl+prettyDeclaration (TypeSig decl)     = prettySignature decl+++instance Show Declaration where+  show = show . prettyDeclaration+  showList ds = (++) (show . vcat . map prettyDeclaration $ ds)++++-- | Pretty-prints a type declaration.++prettyTypeDeclaration :: TypeDeclaration -> Doc+prettyTypeDeclaration (Type ident vs t) =+  isep 2 (+    [text "type", prettyIdentifier ident]+    ++ map prettyTypeVariable vs+    ++ [text "=", prettyTypeExpression NoParens t])++instance Show TypeDeclaration where+  show = show . prettyTypeDeclaration++++-- | Pretty-prints a data declaration.++prettyDataDeclaration :: DataDeclaration -> Doc+prettyDataDeclaration (Data ident vs ds) = +  isep 4 [ isep 2 (+             [text "data", prettyIdentifier ident]+             ++ (map prettyTypeVariable vs))+         , vcat (zipWith (<+>) (char '=' : repeat (char '|'))+                               (map prettyDataConstructorDeclaration ds))]++instance Show DataDeclaration where+  show = show . prettyDataDeclaration++++-- | Pretty-prints a data constructor declaration.++prettyDataConstructorDeclaration :: DataConstructorDeclaration -> Doc+prettyDataConstructorDeclaration (DataCon ident bs) = +  isep 2 $ [prettyIdentifier ident] ++ map prettyBangTypeExpression bs++instance Show DataConstructorDeclaration where+  show = show . prettyDataConstructorDeclaration++++-- | Pretty-prints a type expression having an optional strictness flag.++prettyBangTypeExpression :: BangTypeExpression -> Doc+prettyBangTypeExpression (Banged t)   = char '!'+                                        <> prettyTypeExpression ParensFunOrCon t+prettyBangTypeExpression (Unbanged t) = prettyTypeExpression ParensFunOrCon t++instance Show BangTypeExpression where+  show = show . prettyBangTypeExpression++++-- | Pretty-prints a newtype declaration.++prettyNewtypeDeclaration :: NewtypeDeclaration -> Doc+prettyNewtypeDeclaration (Newtype ident vs con t) =+  isep 2 (+    [text "newtype", prettyIdentifier ident]+    ++ map prettyTypeVariable vs+    ++ [char '=', prettyIdentifier con, prettyTypeExpression ParensFunOrCon t])++instance Show NewtypeDeclaration where+  show = show . prettyNewtypeDeclaration++++-- | Pretty-prints a class declaration.++prettyClassDeclaration :: ClassDeclaration -> Doc+prettyClassDeclaration (Class scs ident tv sigs) =+  let ctx = zip scs (repeat tv)+   in isep 2 [text "class", prettyContext ctx, prettyIdentifier ident,+              prettyTypeVariable tv,+              if null sigs then empty else text "where"]+      $$ nest 4 (vcat (map prettySignature sigs))++instance Show ClassDeclaration where+  show = show . prettyClassDeclaration++++-- | Pretty-prints a type signature.++prettySignature :: Signature -> Doc+prettySignature (Signature ident t) =+  isep 2 [prettyIdentifier ident, text "::", prettyTypeExpression NoParens t]++instance Show Signature where+  show = show . prettySignature++++-- | Pretty-prints a class context constraining certain type variables.++prettyContext :: [(TypeClass, TypeVariable)] -> Doc+prettyContext []  = empty+prettyContext ctx =+  let prettyCV (c,v) = prettyTypeClass c <+> prettyTypeVariable v+   in fsep (+        (parensIf ((length ctx) > 1) +                  (fsep $ punctuate comma $ map prettyCV ctx))+        : [text "=>"])++++------- Type expressions ------------------------------------------------------+++instance Show TypeExpression where+  showsPrec d t = +    let p = case d of+              0         -> NoParens+              1         -> ParensFun+              otherwise -> ParensFunOrCon+     in (++) (show (prettyTypeExpression p t))++++-- | Pretty-prints a type expression.++prettyTypeExpression :: Parens -> TypeExpression -> Doc++prettyTypeExpression _ (TypeVar v) = prettyTypeVariable v++prettyTypeExpression _ (TypeCon ConUnit _) = prettyTypeConstructor ConUnit++prettyTypeExpression _ (TypeCon ConList [t]) =+  brackets (prettyTypeExpression NoParens t)++    -- the following two cases are given to pretty-print also invalid+    -- type expressions, they should usually not occur+prettyTypeExpression _ (TypeCon ConList []) = prettyTypeConstructor ConList+prettyTypeExpression _ (TypeCon ConList (_:_:_)) = brackets (text "...")++prettyTypeExpression _ (TypeCon (ConTuple _) ts) = +  parens $ fsep $ punctuate comma $ map (prettyTypeExpression NoParens) ts++prettyTypeExpression _ (TypeCon ConInt _)     = prettyTypeConstructor ConInt+prettyTypeExpression _ (TypeCon ConInteger _) = prettyTypeConstructor ConInteger+prettyTypeExpression _ (TypeCon ConFloat _)   = prettyTypeConstructor ConFloat+prettyTypeExpression _ (TypeCon ConDouble _)  = prettyTypeConstructor ConDouble+prettyTypeExpression _ (TypeCon ConChar _)    = prettyTypeConstructor ConChar++prettyTypeExpression p (TypeCon (Con ident) ts) =+  parensIf (p >= ParensFunOrCon && not (null ts)) $+    isep 2 $ (prettyIdentifier ident) +             : (map (prettyTypeExpression ParensFunOrCon) ts)++prettyTypeExpression p (TypeFun t1 t2) =+  parensIf (p > NoParens) $ +    fsep (zipWith (<+>) (empty : repeat (text "->")) +                        (map (prettyTypeExpression ParensFun) (t1 : funs t2)))+  where+    funs (TypeFun t1 t2) = t1 : funs t2+    funs t               = [t]++prettyTypeExpression p (TypeAbs v tcs t) =+  let (vs, cx, t') = collectAbstractions v tcs t+   in parensIf (p > NoParens) $+        fsep $ +          [text "forall"] ++ (map prettyTypeVariable vs)+          ++ [char '.', prettyContext cx, prettyTypeExpression NoParens t']++prettyTypeExpression p (TypeExp te) = prettyFixedTypeExpression te++++-- | Collects all type abstractions which follow each other. This is used to get+--   a more compact output.++collectAbstractions :: +    TypeVariable +    -> [TypeClass] +    -> TypeExpression +    -> ([TypeVariable], [(TypeClass, TypeVariable)], TypeExpression)++collectAbstractions v tcs t =+  let cx = zip tcs (repeat v)+   in case t of+        TypeAbs v' tcs' t' -> +          let (vs, cx', t'') = collectAbstractions v' tcs' t'+           in (v : vs, cx ++ cx', t'')+         +        otherwise          -> ([v], cx, t)++++-- | Pretty-prints a type constructor.++prettyTypeConstructor :: TypeConstructor -> Doc+prettyTypeConstructor ConUnit      = parens (empty)  +prettyTypeConstructor ConList      = brackets (empty)+prettyTypeConstructor (ConTuple n) =+  parens . hcat . punctuate comma . take n . repeat $ empty+prettyTypeConstructor ConInt       = text "Int"+prettyTypeConstructor ConInteger   = text "Integer"+prettyTypeConstructor ConFloat     = text "Float"+prettyTypeConstructor ConDouble    = text "Double"+prettyTypeConstructor ConChar      = text "Char"+prettyTypeConstructor (Con c)      = prettyIdentifier c ++++-- | Pretty-prints a type variable.++prettyTypeVariable :: TypeVariable -> Doc+prettyTypeVariable (TV ident) = prettyIdentifier ident++instance Show TypeVariable where+  show = show . prettyTypeVariable++++-- | Pretty-prints a type class.++prettyTypeClass :: TypeClass -> Doc+prettyTypeClass (TC ident) = prettyIdentifier ident++instance Show TypeClass where+  show = show . prettyTypeClass++++-- | Pretty-prints a fixed type expression.++prettyFixedTypeExpression :: FixedTypeExpression -> Doc+prettyFixedTypeExpression (TF ident) = prettyIdentifier ident++instance Show FixedTypeExpression where+  show = show . prettyFixedTypeExpression++++-- | Pretty-prints an identifier.++prettyIdentifier :: Identifier -> Doc+prettyIdentifier (Ident i) = text i++instance Show Identifier where+  show = show . prettyIdentifier+++++
+ src/Language/Haskell/FreeTheorems/Syntax.hs view
@@ -0,0 +1,54 @@++++-- | Declars data types describing the abstract syntax of a subset of Haskell+--   in the FreeTheorems library. Only declarations and type expressions are+--   covered by these data types.+--+--   Note that the data types of this module do not reflect Haskell98.+--   This is because they are able to express higher-rank functions which are+--   not part of Haskell98.+--   Also, in type expressions, a type variable must not be applied to any type+--   expression. Thus, for example, the type @m a@, as occuring in the functions+--   of the @Monad@ type class, is not expressable.+--   The reason for this restriction is that the FreeTheorems library cannot+--   handle such types.++module Language.Haskell.FreeTheorems.Syntax (++    -- * Declarations++    Declaration (..)+  , getDeclarationName+  , getDeclarationArity+  , DataDeclaration (..)+  , NewtypeDeclaration (..)+  , TypeDeclaration (..)+  , ClassDeclaration (..)+  , Signature (..)+  , DataConstructorDeclaration (..)+  , BangTypeExpression (..)+  ++    -- * Type expressions++  , TypeExpression (..)+  , TypeConstructor (..)+  , TypeClass (..)+  , TypeVariable (..)+  , FixedTypeExpression (..)+++    -- * Identifiers++  , Identifier (..)++) where++++import Language.Haskell.FreeTheorems.BasicSyntax+import Language.Haskell.FreeTheorems.ValidSyntax+import Language.Haskell.FreeTheorems.PrettyTypes++
+ src/Language/Haskell/FreeTheorems/Theorems.hs view
@@ -0,0 +1,230 @@++++-- | Data structures to describe theorems generated from types.++module Language.Haskell.FreeTheorems.Theorems where++++import Data.Generics (Typeable, Data)++import Language.Haskell.FreeTheorems.BasicSyntax+import Language.Haskell.FreeTheorems.LanguageSubsets++++-- | A theorem which is generated from a type signature.++type Theorem = Formula++++-- | Logical formula constituting automatically generated theorems.++data Formula +  = ForallRelations RelationVariable (TypeExpression, TypeExpression)+                    [Restriction] Formula+        -- ^ Quantifies a relation variable and two type expressions.+  +  | ForallFunctions (Either TermVariable TermVariable) +                    (TypeExpression, TypeExpression) [Restriction] Formula+        -- ^ Quantifies a function variable and two type expressions.+  +  | ForallPairs (TermVariable, TermVariable) Relation Formula+        -- ^ Quantifies two term variables taken from a relation.+  +  | ForallVariables TermVariable TypeExpression Formula+        -- ^ Quantifies a term variable of a certain type.+  +  | Equivalence Formula Formula+        -- ^ Two formulas are equivalent.+  +  | Implication Formula Formula+        -- ^ The first formula implies the second formula.+  +  | Conjunction Formula Formula+        -- ^ The first formula and the second formula.+  +  | Predicate Predicate+        -- ^ A basic formula.+  +  deriving (Typeable, Data)++++-- | Restrictions on functions and relations.++data Restriction+  = Strict+  | Continuous+  | Total+  | BottomReflecting+  | LeftClosed+  | RespectsClasses [TypeClass]+  deriving (Typeable, Data, Eq)++++-- | Predicates occurring in formulas.++data Predicate+  = IsMember Term Term Relation+        -- ^ The pair of two terms is contained in a relation.+  +  | IsEqual Term Term+        -- ^ Two terms are equal.+  +  | IsLessEq Term Term+        -- ^ The first term is less defined than the second one, based on the+        --   semantical approximation order.+  +  | IsNotBot Term+        -- ^ The term is not equal to @_|_@.+  +  deriving (Typeable, Data)++++-- | Terms consisting of variables, applications and instantiations.++data Term+  = TermVar TermVariable            -- ^ A term variable.+  | TermIns Term TypeExpression     -- ^ Instantiation of a term.+  | TermApp Term Term               -- ^ Application of a term to a term.+  deriving (Typeable, Data, Eq)++++-- | Variables occurring in terms.++newtype TermVariable = TVar String+  deriving (Typeable, Data, Eq)++++-- | Relations are the foundations of free theorems.++data Relation+  = RelVar RelationInfo RelationVariable +        -- ^ A relation variable.+ +  | FunVar RelationInfo (Either Term Term)+        -- ^ A function variable.+        --   It might be either a function to be applied on the left side (in+        --   equational and inequational cases) or on the right side (in +        --   inequational cases only).+        --   In inequational cases, the term is additionally composed with the+        --   semantic approximation partial order.+ +  | RelBasic RelationInfo+        -- ^ A basic relation corresponding to a nullary type constructor.+        --   Depending on the theorem type, this can be either an equivalence+        --   relation or the semantic approximation partial order.+  +  | RelLift RelationInfo TypeConstructor [Relation]+        -- ^ A lifted relation for any nonnullary type constructor.+        --   The semantics of lifted relations is differs with the language+        --   subset:+        --   In inequational subsets lifted relations explicitly require+        --   left-closedness by composition with the semantic approximation +        --   partial order.+        --   In equational subsets with fix or seq, this relation requires+        --   strictness explicitly by relating the undefined value with itself.+  +  | RelFun RelationInfo Relation Relation+        -- ^ A relation corresponding to a function type constructor.+        --   The semantics of this relation differs with the language subset:+        --   In the equational subset with seq, this relation is explicitly+        --   requiring bottom-reflectiveness of its members.+        --   In the inequational subset with seq, this relation is explicitly+        --   requiring totality of its members.+  +  | RelAbs RelationInfo RelationVariable (TypeExpression, TypeExpression)+           [Restriction] Relation+        -- ^ A relation corresponding to a type abstraction.++  | FunAbs RelationInfo (Either TermVariable TermVariable)+           (TypeExpression, TypeExpression) [Restriction] Relation+        -- ^ A quantified function.++  deriving (Typeable, Data, Eq)++++-- | Extracts the relation information from a relation.++relationInfo :: Relation -> RelationInfo+relationInfo rel = case rel of+  RelVar ri _       -> ri+  FunVar ri _       -> ri+  RelBasic ri       -> ri+  RelLift ri _ _    -> ri+  RelFun ri _ _     -> ri+  RelAbs ri _ _ _ _ -> ri+  FunAbs ri _ _ _ _ -> ri++++-- | The relation information stored with every relation.++data RelationInfo = RelationInfo+  { relationLanguageSubset :: LanguageSubset+        -- ^ The language subset in which a relation was generated.+  +  , relationLeftType       :: TypeExpression+        -- ^ The type of the first components of pairs contained in a relation.+  +  , relationRightType      :: TypeExpression+        -- ^ The type of the second components of pairs contained in a +        --   relation.+  +  }+  deriving (Typeable, Data, Eq)++++-- | A relation variable.++newtype RelationVariable = RVar String+  deriving (Typeable, Data, Eq)++++-- | Describes unfolded lift relations.++data UnfoldedLift = UnfoldedLift Relation [UnfoldedDataCon]+  deriving (Typeable, Data)++++-- | A relational descriptions of a data constructor.++data UnfoldedDataCon+  = BotPair+  | ConPair DataConstructor+  | ConMore DataConstructor [TermVariable] [TermVariable] Formula+  deriving (Typeable, Data)++++-- | Data constructors.++data DataConstructor+  = DConEmptyList   -- ^ The nullary data constructor @[]@.+  | DConConsList    -- ^ The binary data constructor @:@.+  | DConTuple Int   -- ^ The n-ary tuple data constructor.+  | DCon String     -- ^ Any other data constructor.+  deriving (Typeable, Data)++++-- | A relational description of a class declaration.++data UnfoldedClass +  = UnfoldedClass [TypeClass] TypeClass (Either RelationVariable TermVariable)+                  [Formula]+  deriving (Typeable, Data)+++
+ src/Language/Haskell/FreeTheorems/Unfold.hs view
@@ -0,0 +1,521 @@++++module Language.Haskell.FreeTheorems.Unfold (+    asTheorem+  , unfoldFormula+  , unfoldLifts+  , unfoldClasses+) where++++import Control.Monad (liftM)+import Control.Monad.State (StateT, get, put, evalStateT, evalState)+import Control.Monad.Reader (Reader, ask, local, runReader, runReaderT)+import Data.Generics (everything, extQ, listify, Data, mkQ)+import Data.List (unfoldr, nub, find, (\\), nubBy)+import Data.Map as Map (fromList)+import Data.Maybe (fromJust)++import Language.Haskell.FreeTheorems.BasicSyntax+import Language.Haskell.FreeTheorems.ValidSyntax+import Language.Haskell.FreeTheorems.LanguageSubsets+import Language.Haskell.FreeTheorems.Intermediate+import Language.Haskell.FreeTheorems.Theorems+import Language.Haskell.FreeTheorems.NameStores++++++------- Basic structures and functions ----------------------------------------+++-- | Abbreviation for the state used to unfold relations to theorems.++type Unfolded a = StateT UnfoldedState (Reader (Bool,Bool)) a++++-- | The state used to unfold relations to theorems.++data UnfoldedState = UnfoldedState +  { newVariableNames :: [String]+        -- ^ An infinite list storing names for variables.+        --   Every element of this list is distinct to the elements of+        --   'newFunctionNames1' and 'newFunctionNames2'.+  +  , newFunctionNames1 :: [String]+        -- ^ An infinite list storing names for functions.+        --   Every element of this list is distinct to the elements of+        --   'newVariableNames' and 'newFunctionNames2'.++  , newFunctionNames2 :: [String]+        -- ^ Another infinite list storing names for functions.+        --   Every element of this list is distinct to the elements of+        --   'newVariableNames' and 'newFunctionNames1'.+  }+  +++-- | Create the initial name store which serves for creating new variable names.++initialState :: Intermediate -> UnfoldedState+initialState ir = +  let fs = intermediateName ir : signatureNames ir+   in UnfoldedState+        { newVariableNames = filter (`notElem` fs) variableNameStore+          -- variable names must not equal the name of the intermediate+          -- variable names don't ever collide with function names or names of+          -- fixed type expressions (see 'NameStores' module)+  +        , newFunctionNames1 = functionVariableNames1 ir+          -- take the name store of functions which was already used during+          -- generation and modification of the intermediate relations++        , newFunctionNames2 = functionVariableNames2 ir+          -- take the name store of functions which was already used during+          -- generation and modification of the intermediate relations+        }+        +++-- | Creates a new term variable. The name is chosen depending on the given+--   type expression, i.e. either a function variable name or a variable name+--   is returned.++newVariableFor :: TypeExpression -> Unfolded TermVariable+newVariableFor t = do+  case t of+    TypeFun _ _    -> do state <- get+                         let ([f], fs) = splitAt 1 (newFunctionNames2 state)+                         put (state { newFunctionNames2 = fs })+                         return (TVar f)+    +    TypeAbs _ _ t' -> newVariableFor t'+    +    otherwise      -> do state <- get+                         let ([x], xs) = splitAt 1 (newVariableNames state)+                         put (state { newVariableNames = xs })+                         return (TVar x)++++-- | Checks if simplifications are possible.++simplificationsAllowed :: Unfolded Bool+simplificationsAllowed = do+  (simplificationPossible, allowAnySimplification) <- ask+  if allowAnySimplification+    then return simplificationPossible+    else return False+++++-- | Toggles the simplification state in the unfolding of the argument.++toggleSimplifications :: Unfolded a -> Unfolded a+toggleSimplifications = local (\(p,a) -> (not p, a))++++++------- Unfolding formulas ----------------------------------------------------+++-- | Unfolds an intermediate structure to a theorem.++asTheorem :: Intermediate -> Theorem+asTheorem i = +  let v = TermVar . TVar . intermediateName $ i+      r = intermediateRelation i+      s = initialState i+   in runReader (evalStateT (unfoldFormula v v r) s) (True, True)++++-- | Unfolds the logical relation "R" in the expression "(x,y) in R" to a+--   theorem. It works by recursively applying unfolding operations of+--   relational actions.++unfoldFormula :: Term -> Term -> Relation -> Unfolded Formula+unfoldFormula x y rel = case rel of+  RelVar _ _           -> return . Predicate . IsMember x y $ rel+  FunVar ri term       -> unfoldTerm x y ri term+  RelBasic ri          -> unfoldBasic x y ri+  RelLift _ _ _        -> return . Predicate . IsMember x y $ rel+  RelFun ri r1 r2      -> unfoldFun x y ri r1 r2+  RelAbs ri v ts res r -> unfoldAbsRel x y ri v ts res r+  FunAbs ri v ts res r -> unfoldAbsFun x y ri v ts res r++++-- | Unfolding operation for terms, i.e. relations specialised to functions.++unfoldTerm :: +    Term -> Term -> RelationInfo -> Either Term Term -> Unfolded Formula+unfoldTerm x y ri term = return . Predicate $+  case term of+    Left t  -> case theoremType (relationLanguageSubset ri) of+                 EquationalTheorem   -> IsEqual (TermApp t x) y+                 InequationalTheorem -> IsLessEq (TermApp t x) y+    Right t -> IsLessEq x (TermApp t y)++++-- | Unfolding operation for nullary relational actions.++unfoldBasic :: Term -> Term -> RelationInfo -> Unfolded Formula+unfoldBasic x y ri = return . Predicate $+  case theoremType (relationLanguageSubset ri) of+    EquationalTheorem   -> IsEqual  x y+    InequationalTheorem -> IsLessEq x y+  +++-- | Unfolding operation for relational actions of type abstractions.++unfoldAbsRel :: +    Term -> Term -> RelationInfo +    -> RelationVariable -> (TypeExpression, TypeExpression)+    -> [Restriction] -> Relation -> Unfolded Formula++unfoldAbsRel x y ri v (t1,t2) res rel = do+  rightSide <- unfoldFormula (TermIns x t1) (TermIns y t2) rel+  return (ForallRelations v (t1, t2) res rightSide)++++-- | Unfolding operation for relational actions of type abstractions+--   (for an abstraction of a function).++unfoldAbsFun :: +    Term -> Term -> RelationInfo +    -> Either TermVariable TermVariable -> (TypeExpression, TypeExpression)+    -> [Restriction] -> Relation -> Unfolded Formula++unfoldAbsFun x y ri v (t1,t2) res rel = do+  rightSide <- unfoldFormula (TermIns x t1) (TermIns y t2) rel+  return (ForallFunctions v (t1, t2) res rightSide)++++-- | Unfolding operation for relational actions of function type constructors.++unfoldFun :: +    Term -> Term -> RelationInfo -> Relation -> Relation -> Unfolded Formula+unfoldFun x y ri rel1 rel2 =+  case rel1 of+    RelVar _ _          -> unfoldFunPairs x y ri rel1 rel2+    FunVar _ t          -> +      let ta = either (\t -> Left (TermApp t)) (\t -> Right (TermApp t)) t+          one = unfoldFunOneVar x y ri ta rel1 rel2+          two = unfoldFunVars x y ri rel1 rel2+       in case theoremType (relationLanguageSubset ri) of+            EquationalTheorem   -> one+            InequationalTheorem -> do+              simple <- simplificationsAllowed+              if simple then one else two+    RelBasic _          -> +      case theoremType (relationLanguageSubset ri) of+        EquationalTheorem   -> unfoldFunOneVar x y ri (Left id) rel1 rel2+        InequationalTheorem -> unfoldFunVars x y ri rel1 rel2+    RelLift _ _ _       -> unfoldFunPairs x y ri rel1 rel2+    RelFun _ _ _        -> unfoldFunVars x y ri rel1 rel2 +    RelAbs _ _ _ _ _    -> unfoldFunVars x y ri rel1 rel2+    FunAbs _ _ _ _ _    -> unfoldFunVars x y ri rel1 rel2++++unfoldFunOneVar :: +    Term -> Term -> RelationInfo -> Either (Term -> Term) (Term -> Term) +    -> Relation -> Relation -> Unfolded Formula+unfoldFunOneVar x y ri termapp rel1 rel2 = do+  let t = either (const (relationLeftType (relationInfo rel1))) +                 (const (relationRightType (relationInfo rel1)))+                 termapp+  +  x' <- newVariableFor t+  let tx' = TermVar x'++  f <- case termapp of+         Left t  -> unfoldFormula (TermApp x tx') (TermApp y (t tx')) rel2+         Right t -> unfoldFormula (TermApp x (t tx')) (TermApp y tx') rel2++  addRestriction x y (relationLanguageSubset ri) (ForallVariables x' t f)++++unfoldFunPairs :: +    Term -> Term -> RelationInfo -> Relation -> Relation -> Unfolded Formula+unfoldFunPairs x y ri rel1 rel2 = do+  x' <- newVariableFor . relationLeftType  . relationInfo $ rel1+  y' <- newVariableFor . relationRightType . relationInfo $ rel1++  f  <- unfoldFormula (TermApp x (TermVar x')) (TermApp y (TermVar y')) rel2+  +  addRestriction x y (relationLanguageSubset ri) (ForallPairs (x', y') rel1 f)+  +++unfoldFunVars :: +    Term -> Term -> RelationInfo -> Relation -> Relation -> Unfolded Formula+unfoldFunVars x y ri rel1 rel2 = do+  let t1 = relationLeftType (relationInfo rel1)+  let t2 = relationRightType (relationInfo rel1)++  x' <- newVariableFor t1+  y' <- newVariableFor t2++  l  <- toggleSimplifications (unfoldFormula (TermVar x') (TermVar y') rel1)+  r  <- unfoldFormula (TermApp x (TermVar x')) (TermApp y (TermVar y')) rel2++  let f = ForallVariables x' t1 (ForallVariables y' t2 (Implication l r))+  addRestriction x y (relationLanguageSubset ri) f++++addRestriction :: Term -> Term -> LanguageSubset -> Formula -> Unfolded Formula+addRestriction x y l f = do+  simple <- simplificationsAllowed+  case l of+    SubsetWithSeq EquationalTheorem -> +      if simple+        then return f+        else let botrefl = Equivalence (Predicate (IsNotBot x))+                                       (Predicate (IsNotBot y))+              in return $ Conjunction botrefl f+    SubsetWithSeq InequationalTheorem -> +      if simple+        then return f+        else return $ Conjunction (Implication (Predicate (IsNotBot x)) +                                               (Predicate (IsNotBot y))) f+    otherwise -> return f++++++------- Unfold lifts ----------------------------------------------------------+++-- | Extracts all lift relations and returns their definition.++unfoldLifts :: [ValidDeclaration] -> Intermediate -> [UnfoldedLift]  +unfoldLifts vds i =+  let decls = map rawDeclaration vds+      rs = collectLifts (intermediateRelation i)++      recUnfold done rs = let (us, ms) = unzip (map unfold rs)+                              ns = concat ms \\ (done ++ rs)+                           in if null ns+                                then us+                                else us ++ recUnfold (done ++ rs) ns+      +      unfold r = case r of +                   RelLift ri con rs -> let (u,ms) = unfoldDecl decls ri con rs+                                         in (UnfoldedLift r u, ms)+      +      eqLift (UnfoldedLift r1 _) (UnfoldedLift r2 _) = r1 == r2+   in nubBy eqLift $ recUnfold [] rs++++collectLifts :: Data a => a -> [Relation]+collectLifts = nub . listify isLift+  where+    isLift rel = case rel of+      RelLift _ _ _ -> True+      otherwise     -> False++++unfoldDecl :: +    [Declaration] -> RelationInfo -> TypeConstructor -> [Relation] +    -> ([UnfoldedDataCon], [Relation])+unfoldDecl decls ri con rs = +  let botPair = case relationLanguageSubset ri of+                  BasicSubset -> []+                  otherwise   -> [BotPair]+      vars t n = map (\i -> TVar (t : show i)) [1..n]+   in case con of+        ConList    -> (botPair ++ unfoldList ri (head rs), [])+        ConTuple n -> (botPair ++ [unfoldTuple n rs], [])+        otherwise  -> +          let d = fromJust (find (isDeclOf con) decls)+           in case d of+                DataDecl d'    -> +                  let ucs = map (unfoldCon (dataVars d') rs) (dataCons d')+                   in (botPair ++ ucs, collectLifts ucs)+                NewtypeDecl d' -> +                  let uc = unfoldCon (newtypeVars d') rs +                                     (DataCon (newtypeCon d') +                                              [Unbanged (newtypeRhs d')])+                   in ([uc], collectLifts uc)+  where+    isDeclOf (Con c) d = case d of+      DataDecl _    -> getDeclarationName d == c+      NewtypeDecl _ -> getDeclarationName d == c+      otherwise     -> False++++unfoldList :: RelationInfo -> Relation -> [UnfoldedDataCon]+unfoldList ri rel = +  let x  = TVar "x"+      y  = TVar "y"+      xs = TVar "xs"+      ys = TVar "ys"+      vs = listify (\(_::TermVariable) -> True) rel+      fs = map (\(TVar v) -> v) (x:y:xs:ys:vs) +   in [ ConPair DConEmptyList+      , ConMore DConConsList [x,xs] [y,ys]+            (Conjunction (unfoldFormulaEx fs (TermVar x, TermVar y, rel))+                         (Predicate (IsMember (TermVar xs) (TermVar ys) +                                              (RelLift ri ConList [rel]))))+      ]+++unfoldTuple :: Int -> [Relation] -> UnfoldedDataCon+unfoldTuple n rs = +  let xs = map (\i -> TVar ('x' : show i)) [1..n]+      ys = map (\i -> TVar ('y' : show i)) [1..n]+      vs = listify (\(_::TermVariable) -> True) rs+      fs = map (\(TVar v) -> v) (xs ++ ys ++ vs)+      txs = map TermVar xs+      tys = map TermVar ys+      th = foldl1 Conjunction (map (unfoldFormulaEx fs) (zip3 txs tys rs))+   in ConMore (DConTuple n) xs ys th++++unfoldCon :: +    [TypeVariable] -> [Relation] -> DataConstructorDeclaration +    -> UnfoldedDataCon+unfoldCon vs rs (DataCon name ts) =+  if null ts+    then ConPair (DCon (unpackIdent name))+    else let n  = length ts+             xs = map (\i -> TVar ('x' : show i)) [1..n]+             ys = map (\i -> TVar ('y' : show i)) [1..n]+             is = map (interpretEx ([], []) vs rs . withoutBang) ts+             os = listify (\(_::TermVariable) -> True) rs+             fs = map (\(TVar v) -> v) (xs ++ ys ++ os)+             txs = map TermVar xs+             tys = map TermVar ys+             th = foldl1 Conjunction (map (unfoldFormulaEx fs) +                                          (zip3 txs tys is))+          in ConMore (DCon (unpackIdent name)) xs ys th+                              +++++unfoldFormulaEx :: +    [String] -> (Term, Term, Relation) -> Formula+unfoldFormulaEx forbidden (x, y, rel) = +  let s = UnfoldedState+          { newVariableNames = filter (`notElem` forbidden) variableNameStore+          , newFunctionNames1 = filter (`notElem` forbidden) functionNameStore1+          , newFunctionNames2 = filter (`notElem` forbidden) functionNameStore2+          }+   in runReader (evalStateT (unfoldFormula x y rel) s) (True, False)+                                                    ++interpretEx :: +    ([String], [TypeExpression]) -> [TypeVariable] -> [Relation] +    -> TypeExpression -> Relation+interpretEx ns vs rs t = +  let e = Map.fromList (zip vs rs)+      l = relationLanguageSubset . relationInfo . head $ rs+   in evalState (runReaderT (interpretM l t) e) ns+++++++------- Exported functions ----------------------------------------------------+++-- | Extracts all class constraints and returns their definition.++unfoldClasses :: [ValidDeclaration] -> Intermediate -> [UnfoldedClass]+unfoldClasses vds i = +  let ds = map rawDeclaration vds+      cs = collectClasses (intermediateRelation i)+      ns = map (\(TVar n) -> n) (listify (\(_::TermVariable) -> True) cs)+      fs = signatureNames i ++ [intermediateName i] ++ ns+      rs = interpretNameStore i+      +      recUnfold done cs =+        let (us, os) = unzip (map (unfoldClass rs ds fs) cs)+            done' = done ++ cs +            ns = concat os \\ done'+         in if null ns+              then us+              else us ++ recUnfold done' ns++   in recUnfold [] cs++++collectClasses :: Data a => a -> [(Relation, TypeClass)]+collectClasses = nub . everything (++) ([] `mkQ` getCC)+  where+    getCC rel = case rel of+      RelAbs ri rv (t1,t2) res _ -> +        let cs  = concatMap getClasses res+            ri' = ri { relationLeftType = t1+                     , relationRightType = t2 }+            r   = RelVar ri' rv+         in map (\c -> (r, c)) cs+      FunAbs ri fv (t1, t2) res _ ->+        let cs  = concatMap getClasses res+            ri' = ri { relationLeftType = t1+                     , relationRightType = t2 }+            r   = FunVar ri' (either (Left . TermVar) (Right . TermVar) fv)+         in map (\c -> (r, c)) cs+      otherwise           -> []++    getClasses r = case r of+      RespectsClasses tcs -> tcs+      otherwise           -> []++++unfoldClass :: +    ([String], [TypeExpression]) -> [Declaration] -> [String] +    -> (Relation, TypeClass) -> (UnfoldedClass, [(Relation, TypeClass)])+unfoldClass istore decls forbiddenNames (r, c@(TC name)) =+  let ClassDecl d = fromJust (find (\d -> getDeclarationName d == name) decls)+      ri = relationInfo r++      interpretSig s = interpretEx istore [classVar d] [r] (signatureType s)+      +      methodName = TermVar . TVar . unpackIdent . signatureName+      leftMethod s = TermIns (methodName s) (relationLeftType ri)+      rightMethod s = TermIns (methodName s) (relationRightType ri)+      +      asFormula s = unfoldFormulaEx+                      forbiddenNames +                      (leftMethod s, rightMethod s, interpretSig s)++      fs = map asFormula (classFuns d)++      ps = map (\c -> (r,c)) (superClasses d)+      ds = concatMap collectClasses fs++      v = case r of+            RelVar _ rv -> Left rv+            FunVar _ fv -> either (Right . unterm) (Right . unterm) fv+      unterm (TermVar v) = v+   +   in (UnfoldedClass (superClasses d) c v fs, ps ++ ds)++++
+ src/Language/Haskell/FreeTheorems/ValidSyntax.hs view
@@ -0,0 +1,52 @@++++-- | Declares data types which describe valid declarations and valid type +--   signatures. A declaration or type signature is valid when all checks (see+--   "Language.Haskell.FreeTheorems.Frontend") were passed successfully.++module Language.Haskell.FreeTheorems.ValidSyntax where++++import Data.Generics (Typeable, Data)+import Data.Maybe (mapMaybe)++import Language.Haskell.FreeTheorems.BasicSyntax++++-- | Marks a valid declaration.++data ValidDeclaration = ValidDeclaration +  { rawDeclaration :: Declaration +        -- ^ Returns the declaration structure hidden in a valid declaration.++  , isStrictDeclaration :: Bool+        -- ^ Indicates whether the declarations declares or depends on an +        --   algebraic data type with strictness flag.+  }++++-- | Marks a valid type signature.++newtype ValidSignature = ValidSignature +  { rawSignature :: Signature+        -- ^ Returns the signature structure hidden in a valid type signature.+  }++++-- | Extracts all type signatures from a list of declarations.++filterSignatures :: [ValidDeclaration] -> [ValidSignature]+filterSignatures = mapMaybe asSignature+  where +    asSignature (ValidDeclaration decl _) = +      case decl of+        TypeSig sig -> Just (ValidSignature sig)+        otherwise   -> Nothing+++
+ src/ParserPrettyPrinterTests.hs view
@@ -0,0 +1,100 @@++++module ParserPrettyPrinterTests (tests) where++++import Control.Monad (liftM, replicateM)+import Control.Monad.Writer (runWriter)+import Data.Generics (everywhere, mkT)+import Test.QuickCheck+import Text.PrettyPrint (vcat)++import Language.Haskell.FreeTheorems.Syntax+import qualified Language.Haskell.FreeTheorems.Parser.Haskell98 as Haskell98+import qualified Language.Haskell.FreeTheorems.Parser.Hsx as Hsx+import Language.Haskell.FreeTheorems.PrettyTypes++import Tests++++-- | All test cases.++tests :: IO ()+tests = do+  doTest "Haskell98.parse . prettyPrint == id" prop_parsePrettyPrint_Haskell98+  doTest "Hsx.parse . prettyPrint == id" prop_parsePrettyPrint_Hsx++++-- | Property: Parsing a pretty-printed declaration results in the same+--   declaration. This property is based on the Haskell98 parser.++prop_parsePrettyPrint_Haskell98 decls = +  let (pds, es) = runWriter . Haskell98.parse +                            . show . vcat . map prettyDeclaration $ ds+   in not (null ds) ==> (null es && not (null pds) && pds == ds)+  where+    types = decls :: ListOfDeclarations+    ds = map modifyTypeExpressions (getDeclarations decls)++    -- type expressions have to be modified because arbitrary type expressions+    -- may contain FixedTypeExpressions, explicit type abstractions and +    -- type constructors applied to a wrong number of arguments+    modifyTypeExpressions = everywhere (mkT adjustType)++    adjustType t = case t of+      TypeCon ConUnit _       -> TypeCon ConUnit []+      TypeCon ConList []      -> TypeCon ConList [TypeCon ConUnit []]+      TypeCon ConList (x:_)   -> TypeCon ConList [x]+      TypeCon (ConTuple _) [] -> TypeCon ConUnit []+      TypeCon (ConTuple n) xs -> if length xs == 1+                                   then TypeCon ConList xs+                                   else TypeCon (ConTuple (length xs)) xs+      TypeCon ConInt _        -> TypeCon ConInt []+      TypeCon ConInteger _    -> TypeCon ConInteger []+      TypeCon ConFloat _      -> TypeCon ConFloat []+      TypeCon ConDouble _     -> TypeCon ConDouble []+      TypeCon ConChar _       -> TypeCon ConChar []+      TypeAbs _ _ t'          -> t'+      TypeExp (TF i)          -> TypeVar (TV i)+      otherwise               -> t++++-- | Property: Parsing a pretty-printed declaration results in the same+--   declaration. This property is based on the Hsx parser.++prop_parsePrettyPrint_Hsx decls = +  let (pds, es) = runWriter . Hsx.parse +                            . show . vcat . map prettyDeclaration $ ds+   in not (null ds) ==> (null es && not (null pds) && pds == ds)+  where+    types = decls :: ListOfDeclarations+    ds = map modifyTypeExpressions (getDeclarations decls)++    -- type expressions have to be modified because arbitrary type expressions+    -- may contain FixedTypeExpressions, explicit type abstractions and +    -- type constructors applied to a wrong number of arguments+    modifyTypeExpressions = everywhere (mkT adjustType)++    adjustType t = case t of+      TypeCon ConUnit _       -> TypeCon ConUnit []+      TypeCon ConList []      -> TypeCon ConList [TypeCon ConUnit []]+      TypeCon ConList (x:_)   -> TypeCon ConList [x]+      TypeCon (ConTuple _) [] -> TypeCon ConUnit []+      TypeCon (ConTuple n) xs -> if length xs == 1+                                   then TypeCon ConList xs+                                   else TypeCon (ConTuple (length xs)) xs+      TypeCon ConInt _        -> TypeCon ConInt []+      TypeCon ConInteger _    -> TypeCon ConInteger []+      TypeCon ConFloat _      -> TypeCon ConFloat []+      TypeCon ConDouble _     -> TypeCon ConDouble []+      TypeCon ConChar _       -> TypeCon ConChar []+      TypeAbs _ _ t'          -> t'+      TypeExp (TF i)          -> TypeVar (TV i)+      otherwise               -> t++
+ src/Runtests.hs view
@@ -0,0 +1,21 @@++++import FrontendTypeExpressionsTests as FrontendTypeExpressions (tests)+import FrontendCheckLocalTests as FrontendCheckLocal (tests)+import FrontendCheckGlobalTests as FrontendCheckGlobal (tests)+import FrontendOtherTests as FrontendOther (tests)+import ParserPrettyPrinterTests as ParserPrettyPrinter (tests)++++-- | Run all tests defined for the FreeTheorems library.++main = do+  FrontendTypeExpressions.tests+  FrontendCheckLocal.tests+  FrontendCheckGlobal.tests+  FrontendOther.tests+  ParserPrettyPrinter.tests+  +
+ src/Tests.hs view
@@ -0,0 +1,38 @@++++-- | Defines functions which help to define tests.++module Tests (+    module Arbitraries+  , doTest+) where++++import Test.QuickCheck++import Arbitraries++++-- | Runs a test in a standardised way.+--   +--   A test must have a label which should not be longer than 75 characters.+--   Otherwise, it is truncated.++doTest :: (Arbitrary a, Show a, Testable b) => String -> (a -> b) -> IO ()+doTest name prop = do+  putStrLn (fixString 75 name ++ ":")+  putStr "   "+  check (defaultConfig {configMaxTest = 100}) prop   -- quickCheck prop+  where+    fixString :: Int -> String -> String+    fixString len s =+      if length s <= len+        then s+        else take (len - 3) s ++ "..."++++