packages feed

uu-cco-examples-0.1.0.1: src/CCO/ArithBool/AG/Evaluation.ag

imports
{
import CCO.Feedback         (Feedback, errorMessage)
import CCO.Printing
import CCO.SourcePos        (Source (..), Pos (..), SourcePos (..))
import CCO.Tree             (ATerm (App), Tree (fromTree, toTree))
import CCO.Tree.Parser      (parseTree, app, arg)
import Control.Applicative  (Applicative (pure), (<$>))
import Control.Monad        (when, unless)
}

-------------------------------------------------------------------------------
-- Syntax
-------------------------------------------------------------------------------

{
-- | Type of values.
data Val
  = VNum Num_  -- ^ Numeral.
  | VFalse     -- ^ False.
  | VTrue      -- ^ True.

instance Tree Val where
  fromTree (VNum n) = App "Num"   [fromTree n]
  fromTree VFalse   = App "False" []
  fromTree VTrue    = App "True"  []

  toTree = parseTree [ app "Num"   (VNum <$> arg)
                     , app "False" (pure VFalse)
                     , app "True"  (pure VTrue)
                     ]

instance Printable Val where
  pp (VNum n) = showable n
  pp VFalse   = text "false"
  pp VTrue    = text "true"

-- | Retrieves whether a 'Val' is a 'VNum'.
isVNum :: Val -> Bool
isVNum (VNum _) = True
isVNum _        = False

-- | Retrieves whether a 'Val' is 'VFalse'.
isVFalse :: Val -> Bool
isVFalse VFalse = True
isVFalse _      = False

-- | Retrieves whether a 'Val' is 'VTrue'.
isVTrue :: Val -> Bool
isVTrue VTrue = True
isVTrue _     = False

-- | Retrieves whether a 'Val' is either 'VFalse' or 'VTrue'.
isBool :: Val -> Bool
isBool v = isVFalse v || isVTrue v

-- | Retrieves a textual description of the type of a 'Val'.
describeTy :: Val -> String
describeTy (VNum _) = "natural number"
describeTy VFalse   = "boolean"
describeTy VTrue    = "boolean"
}

-------------------------------------------------------------------------------
-- Evaluation
-------------------------------------------------------------------------------

attr Tm Tm_
  syn val :: {Feedback Val}

sem Tm_
  | Num    lhs.val = return (VNum @n)
  | False_ lhs.val = return VFalse
  | True_  lhs.val = return VTrue
  | If     lhs.val = matchBool @t1.pos @t1.val @t2.val @t3.val
  | Add    lhs.val = do n1 <- matchNat @t1.pos @t1.val 
                        n2 <- matchNat @t2.pos @t2.val
                        return (VNum (n1 + n2))
  | Sub    lhs.val = do n1 <- matchNat @t1.pos @t1.val 
                        n2 <- matchNat @t2.pos @t2.val
                        return (VNum (if n2 > n1 then 0 else n1 - n2))
  | Mul    lhs.val = do n1 <- matchNat @t1.pos @t1.val 
                        n2 <- matchNat @t2.pos @t2.val
                        return (VNum (n1 * n2))
  | Div    lhs.val = do n1 <- matchNat @t1.pos @t1.val
                        n2 <- matchNat @t2.pos @t2.val
                        when (n2 == 0) (errDivByZero @lhs.pos)
                        return (VNum (n1 `div` n2))
  | Lt     lhs.val = do n1 <- matchNat @t1.pos @t1.val
                        n2 <- matchNat @t2.pos @t2.val
                        return (if n1 < n2 then VTrue else VFalse)
  | Eq     lhs.val = do n1 <- matchNat @t1.pos @t1.val
                        n2 <- matchNat @t2.pos @t2.val
                        return (if n1 == n2 then VTrue else VFalse)
  | Gt     lhs.val = do n1 <- matchNat @t1.pos @t1.val
                        n2 <- matchNat @t2.pos @t2.val
                        return (if n1 > n2 then VTrue else VFalse)

-------------------------------------------------------------------------------
-- Run-time type checking
-------------------------------------------------------------------------------

{
-- | Checks whether a 'Val' is a numeral.
-- If the check succeeds, the numeral is returned; if not, a run-time type
-- error is issued.
matchNat :: SourcePos -> Feedback Val -> Feedback Num_
matchNat pos fv = do
  v@(~(VNum n)) <- fv
  unless (isVNum v) (errTyMismatch pos "natural number" (describeTy v))
  return n

-- | Checks whether a 'Val' is a boolean constant.
-- If the check succeeds, one of two continuations (for 'VTrue' and 'VFalse',
-- respectively) is selected; if not, a run-time type error is issued.
matchBool :: SourcePos -> Feedback Val -> Feedback a -> Feedback a ->
             Feedback a
matchBool pos fv ft ff = do
  v <- fv
  unless (isBool v) (errTyMismatch pos "boolean" (describeTy v))
  case v of
    VFalse -> ff
    VTrue  -> ft
}

-------------------------------------------------------------------------------
-- Run-time errors
-------------------------------------------------------------------------------

{
-- | Produces a division-by-zero error.
errDivByZero :: SourcePos -> Feedback ()
errDivByZero pos =
  errorMessage . wrapped $
  describeSourcePos pos ++ ": Run-time error: division by zero."

-- | Given a source position, a textual description of the expected type, and
-- a textual description of the actual type, produces a run-time type error.
errTyMismatch :: SourcePos -> String -> String -> Feedback ()
errTyMismatch pos expected encountered
   = errorMessage (ppHeader >-< text " " >-< ppExpected >-< ppEncountered)
  where
    ppHeader      = wrapped $
                    describeSourcePos pos ++ ": Run-time error: type mismatch."
    ppExpected    = text "? expected    : " >|< wrapped expected
    ppEncountered = text "? encountered : " >|< wrapped encountered
}