packages feed

sugar-data-0.0.0.0: src/Sugar/Data.hs

{-# LANGUAGE OverloadedStrings #-}
module Sugar.Data
  ( Type(..)
  , TypeRef(..)
  , Ty(..)
  , Rec(..)
  , Mem(..)
  , Adt(..)
  , Cons(..)
  , ConsValue(..)
  , Err(..)
  , Error(..)
  , ParamFns(..)
  , tokenToTypes
  , tokenToType
  ) where

import qualified Data.Text as T
import Data.Text (Text)
import Sugar

data Type a m c
  = Type'Ty (Ty a)
  | Type'Rec (Rec a m)
  | Type'Adt (Adt a m c)
  deriving (Show, Eq)

data TypeRef = TypeRef
  { trName :: Text
  , trParams :: [TypeRef]
  , trLoc :: SourceLocation
  } deriving (Show, Eq)

data Ty a = Ty
  { tyName :: Text
  , tyParam :: Maybe a
  , tyType :: TypeRef
  , tyLoc :: SourceLocation
  } deriving (Show, Eq)

data Rec a m = Rec
  { recName :: Text
  , recParam :: Maybe a
  , recMems :: [Mem m]
  , recLoc :: SourceLocation
  } deriving (Show, Eq)

data Mem m = Mem
  { memLabel :: Text
  , memParam :: Maybe m
  , memType :: TypeRef
  , memLoc :: SourceLocation
  } deriving (Show, Eq)

data Adt a m c = Adt
  { adtName :: Text
  , adtParam :: Maybe a
  , adtCons :: [Cons m c]
  , adtLoc :: SourceLocation
  } deriving (Show, Eq)

data Cons m c = Cons
  { consTag :: Text
  , consParam :: Maybe c
  , consValue :: ConsValue m
  , consLoc :: SourceLocation
  } deriving (Show, Eq)

data ConsValue m
  = ConsValue'None
  | ConsValue'TypeRef [TypeRef]
  | ConsValue'Mems [Mem m]
  deriving (Show, Eq)

--

data Err e
  = Err'Info e
  | Err'NotTopLevelMap
  | Err'TypeMismatch
  | Err'TypeRefMismatch
  | Err'TypeRefParamsMismatch
  | Err'MemMismatch
  | Err'MemsMismatch
  | Err'ConsMismatch
  deriving (Show, Eq)

data Error e = Error
  { errorLoc :: SourceLocation
  , errorInfo :: Err e
  } deriving (Show, Eq)

data ParamFns a m c e = ParamFns
  { paramFnType :: [TokenStep] -> Either [Error e] a
  , paramFnMem :: [TokenStep] -> Either [Error e]  m
  , paramFnCons :: [TokenStep] -> Either [Error e]  c
  }

tokenToTypes :: ParamFns a m c e -> TokenStep -> Either [Error e] [Type a m c]
tokenToTypes fns (_, Token'Map ts _) = mapWithErrs (uncurry $ tokenToType fns) ts
tokenToTypes _ (loc, _) = Left [Error loc Err'NotTopLevelMap]

tokenToType :: ParamFns a m c e -> TokenStep -> TokenStep -> Either [Error e] (Type a m c)
tokenToType fns n v =
  case Type'Ty <$> tokenToTy fns n v of
    Right ty -> pure ty
    Left tyErrs -> case Type'Rec <$> tokenToRec fns n v of
      Right rec -> pure rec
      Left recErrs -> case Type'Adt <$> tokenToAdt fns n v of
        Right adt -> pure adt
        Left adtErrs -> Left $ betterErrs (betterErrs tyErrs recErrs) adtErrs

betterErrs :: [Error e] -> [Error e] -> [Error e]
betterErrs [Error _ Err'TypeMismatch] ys = ys
betterErrs xs [Error _ Err'TypeMismatch] = xs
betterErrs _ ys = ys -- FIXME: May not be the best fall-through approach

tokenTypeRef :: TokenStep -> Either [Error e] TypeRef
tokenTypeRef (loc, Token'Text name Nothing) = Right $ TypeRef (T.pack name) [] loc
tokenTypeRef (loc, Token'Text name (Just params)) = TypeRef (T.pack name) <$> (mapWithErrs tokenTypeRef params) <*> pure loc
tokenTypeRef (loc, _) = Left [Error loc Err'TypeRefMismatch]

tokenToTy :: ParamFns a m c e -> TokenStep -> TokenStep -> Either [Error e] (Ty a)
tokenToTy fns (loc, Token'Text t params) v = Ty (T.pack t) <$> tokenParams (paramFnType fns) params <*> tokenTypeRef v <*> pure loc
tokenToTy _ _ (loc, _) = Left [Error loc Err'TypeMismatch]

tokenToRec :: ParamFns a m c e -> TokenStep -> TokenStep -> Either [Error e] (Rec a m)
tokenToRec fns (loc, Token'Text name params) v = Rec (T.pack name) <$> tokenParams (paramFnType fns) params <*> tokenToMems fns v <*> pure loc
tokenToRec  _ _ (loc, _) = Left [Error loc Err'TypeMismatch]

tokenToMems :: ParamFns a m c e -> TokenStep -> Either [Error e] [Mem m]
tokenToMems fns (_, Token'Map mems Nothing) = tokenToMems' fns mems
tokenToMems _ (loc, _) = Left [Error loc Err'MemsMismatch]

tokenToMems' :: ParamFns a m c e -> [(TokenStep,TokenStep)] -> Either [Error e] [Mem m]
tokenToMems' fns = mapWithErrs (uncurry $ tokenToMem fns)

tokenToMem :: ParamFns a m c e -> TokenStep -> TokenStep -> Either [Error e] (Mem m)
tokenToMem fns (loc, Token'Text name params) s = Mem (T.pack name) <$> tokenParams (paramFnMem fns) params <*> tokenTypeRef s <*> pure loc
tokenToMem _ _ (loc, _) = Left [Error loc Err'MemMismatch]

tokenToAdt :: ParamFns a m c e -> TokenStep -> TokenStep -> Either [Error e] (Adt a m c)
tokenToAdt fns (loc, Token'Text name params) (_, Token'List conss _ Nothing) = Adt (T.pack name) <$> tokenParams (paramFnType fns) params <*> mapWithErrs (tokenToCons fns) conss <*> pure loc
tokenToAdt _ _ (loc, _) = Left [Error loc Err'TypeMismatch]

tokenToCons :: ParamFns a m c e -> TokenStep -> Either [Error e] (Cons m c)
tokenToCons fns (loc, Token'Text name params) =  Cons (T.pack name) <$> tokenParams (paramFnCons fns) params <*> pure ConsValue'None <*> pure loc
tokenToCons fns (loc, Token'List [(_, Token'Text name Nothing)] _ params) = Cons (T.pack name) <$> tokenParams (paramFnCons fns) params <*> pure ConsValue'None <*> pure loc
tokenToCons fns (loc, Token'List [(_, Token'Text name Nothing), (_, Token'Map mems Nothing)] _ params) = do
  ms <- tokenToMems' fns mems
  param <- tokenParams (paramFnCons fns) params
  return $ Cons
    (T.pack name)
    param
    (if null ms
      then ConsValue'None
      else ConsValue'Mems ms)
    loc
tokenToCons fns (loc, (Token'List ((_, Token'Text name Nothing):xs) _ params)) = Cons (T.pack name) <$> tokenParams (paramFnCons fns) params <*> (ConsValue'TypeRef <$> mapWithErrs tokenTypeRef xs) <*> pure loc
tokenToCons _ (loc,_) = Left [Error loc Err'ConsMismatch]

mapWithErrs :: (a -> Either [Error e] b) -> [a] -> Either [Error e] [b]
mapWithErrs _ [] = Right []
mapWithErrs f (x:xs) = case f x of
  Left es -> case mapWithErrs f xs of
    Left es' -> Left $ es ++ es'
    Right _ -> Left es
  Right y -> case mapWithErrs f xs of
    Left es -> Left es
    Right ys -> Right $ y : ys

tokenParams :: ([TokenStep] -> Either [Error e] a) -> Maybe [TokenStep] -> Either [Error e] (Maybe a)
tokenParams f params = case params of
  Nothing -> pure Nothing
  Just ps -> Just <$> f ps