packages feed

syntactic-0.7: Language/Syntactic/Constructs/Symbol.hs

{-# LANGUAGE OverlappingInstances #-}

-- | Generic symbols
--
-- 'Sym' provides a simple way to make syntactic symbols for prototyping.
-- However, note that 'Sym' is quite unsafe as it only uses 'String' to
-- distinguish between different symbols. Also, 'Sym' has a very free type that
-- allows any number of arguments.

module Language.Syntactic.Constructs.Symbol where



import Data.Typeable

import Data.Hash
import Data.Proxy

import Language.Syntactic



data Sym ctx a
  where
    Sym :: (ConsType a, Sat ctx (EvalResult a)) =>
        String -> ConsEval a -> Sym ctx a

instance WitnessCons (Sym ctx)
  where
    witnessCons (Sym _ _) = ConsWit

instance WitnessSat (Sym ctx)
  where
    type SatContext (Sym ctx) = ctx
    witnessSat (Sym _ _) = SatWit

instance MaybeWitnessSat ctx (Sym ctx)
  where
    maybeWitnessSat = maybeWitnessSatDefault

instance MaybeWitnessSat ctx1 (Sym ctx2)
  where
    maybeWitnessSat _ _ = Nothing

instance ExprEq (Sym ctx)
  where
    exprEq (Sym a _) (Sym b _) = a==b
    exprHash (Sym name _)      = hash name

instance Render (Sym ctx)
  where
    renderPart [] (Sym name _) = name
    renderPart args (Sym name _)
        | isInfix   = "(" ++ unwords [a,op,b] ++ ")"
        | otherwise = "(" ++ unwords (name : args) ++ ")"
      where
        [a,b] = args
        op    = init $ tail name
        isInfix
          =  not (null name)
          && head name == '('
          && last name == ')'
          && length args == 2

instance ToTree (Sym ctx)

instance Eval (Sym ctx)
  where
    evaluate (Sym _ a) = fromEval a



-- | Class of expressions that can be treated as symbols
class IsSymbol expr
  where
    toSym :: expr a -> Sym Poly a

-- | Default implementation of 'exprEq'
exprEqSym :: IsSymbol expr => expr a -> expr b -> Bool
exprEqSym a b = exprEq (toSym a) (toSym b)

-- | Default implementation of 'exprHash'
exprHashSym :: IsSymbol expr => expr a -> Hash
exprHashSym = exprHash . toSym

-- | Default implementation of 'renderPart'
renderPartSym :: IsSymbol expr => [String] -> expr a -> String
renderPartSym args = renderPart args . toSym

-- | Default implementation of 'evaluate'
evaluateSym :: IsSymbol expr => expr a -> a
evaluateSym = evaluate . toSym