module Test.QuickSpec.Term where
import Test.QuickSpec.Utils.Typeable
import Test.QuickCheck
import Data.Function
import Data.Ord
import Data.Char
import Test.QuickSpec.Utils
data Symbol = Symbol {
index :: Int,
name :: String,
symbolArity :: Int,
silent :: Bool,
undef :: Bool,
symbolType :: TypeRep }
symbol :: Typeable a => String -> Int -> a -> Symbol
symbol x arity v = Symbol 0 x arity False False (typeOf v)
instance Show Symbol where
show = showOp . name
instance Eq Symbol where
(==) = (==) `on` index
instance Ord Symbol where
compare = comparing index
data Term =
Var Symbol
| Const Symbol
| App Term Term deriving Eq
infixl 5 `App`
instance Ord Term where
compare = comparing stamp
where
stamp t = (depth t, size t, occur t, body t)
occur t = length (usort (vars t))
body (Var x) = Left (Left x)
body (Const x) = Left (Right x)
body (App f x) = Right (f, x)
instance Show Term where
showsPrec p t = showString (showTerm p t)
where
brack s = "(" ++ s ++ ")"
parenFun p s | p < 2 = s
| otherwise = brack s
parenOp p s | p < 1 = s
| otherwise = brack s
showTerm p (Var v) = show v
showTerm p (Const x) = show x
showTerm p (Const op `App` x) | isOp (name op) =
brack (showTerm 1 x ++ name op)
showTerm p (Const op `App` x `App` y) | isOp (name op) =
parenOp p (showTerm 1 x ++ name op ++ showTerm 1 y)
showTerm p (f `App` x) =
parenFun p (showTerm 1 f ++ " " ++ showTerm 2 x)
showOp :: String -> String
showOp op | isOp op = "(" ++ op ++ ")"
| otherwise = op
isOp :: String -> Bool
isOp "[]" = False
isOp xs = not (all isIdent xs)
where isIdent x = isAlphaNum x || x == '\''
isUndefined :: Term -> Bool
isUndefined (Const Symbol { undef = True }) = True
isUndefined _ = False
symbols :: Term -> [Symbol]
symbols t = symbols' t []
where symbols' (Var x) = (x:)
symbols' (Const x) = (x:)
symbols' (App f x) = symbols' f . symbols' x
depth, size :: Term -> Int
depth (App f x) = depth f `max` (1 + depth x)
depth _ = 1
size (App f x) = size f + size x
size (Var _) = 0
size (Const _) = 1
holes :: Term -> [(Symbol, Int)]
holes t = holes' 0 t []
where holes' d (Var x) = ((x, d):)
holes' d Const{} = id
holes' d (App f x) = holes' d f . holes' (d+1) x
functor :: Term -> Symbol
functor (Var x) = x
functor (Const x) = x
functor (App f x) = functor f
args :: Term -> [Term]
args = reverse . args'
where args' Var{} = []
args' Const{} = []
args' (App f x) = x:args' f
funs :: Term -> [Symbol]
funs t = aux t []
where aux (Const x) = (x:)
aux Var{} = id
aux (App f x) = aux f . aux x
vars :: Term -> [Symbol]
vars t = aux t []
where aux (Var x) = (x:)
aux (App f x) = aux f . aux x
aux Const{} = id
mapVars :: (Symbol -> Symbol) -> Term -> Term
mapVars f (Var x) = Var (f x)
mapVars f (Const x) = Const x
mapVars f (App t u) = App (mapVars f t) (mapVars f u)
data Expr a = Expr {
term :: Term,
arity :: !Int,
eval :: (forall b. Variable b -> b) -> a }
instance Eq (Expr a) where
(==) = (==) `on` term
instance Ord (Expr a) where
compare = comparing term
instance Show (Expr a) where
show = show . term
data Atom a = Atom {
sym :: Symbol,
value :: a } deriving Functor
newtype Variable a = Variable { unVariable :: Atom (Gen a) } deriving Functor
newtype Constant a = Constant { unConstant :: Atom a } deriving Functor
mapVariable :: (Symbol -> Symbol) -> Variable a -> Variable a
mapVariable f (Variable v) = Variable v { sym = f (sym v) }
mapConstant :: (Symbol -> Symbol) -> Constant a -> Constant a
mapConstant f (Constant v) = Constant v { sym = f (sym v) }
valuation :: Gen (Variable a -> a)
valuation = promote (\(Variable x) -> index (sym x) `variant'` value x)
where
variant' 0 = variant (0 :: Int)
variant' n = variant (1 :: Int) . variant' (n1)
var :: Variable a -> Expr a
var v@(Variable (Atom x _)) = Expr (Var x) 0 (\env -> env v)
con :: Constant a -> Expr a
con (Constant (Atom x v)) = Expr (Const x) (symbolArity x) (const v)
app :: Expr (a -> b) -> Expr a -> Expr b
app (Expr t a f) (Expr u _ x)
| a == 0 = error "Test.QuickSpec.Term.app: oversaturated function"
| otherwise = Expr (App t u) (a 1) (\env -> f env (x env))