packages feed

polyglot-0.1.1.0: src/Parsers/Haskell/DataDef.hs

module Parsers.Haskell.DataDef where

import Parsers.Haskell.Common      (class', ctor, var)
import Parsers.Haskell.Type        (anyKindedType, type', typeCtor, typeParam)
import SyntaxTrees.Haskell.DataDef (DataCtorDef (..), DataDef (..),
                                    DerivingClause (..), DerivingStrategy (..),
                                    FieldDef (..), NamedFieldDef (..),
                                    NewTypeDef (..), TypeDef (..),
                                    UnNamedFieldDef (..))

import Bookhound.Parser              (Parser, withError)
import Bookhound.ParserCombinators   (IsMatch (..), anySepBy, someSepBy, (<#>),
                                      (<|>), (|*), (|?))
import Bookhound.Parsers.Char        (colon, comma, equal)
import Bookhound.Parsers.Collections (tupleOf)
import Bookhound.Parsers.String      (maybeWithinParens, withinCurlyBrackets,
                                      withinParens)

import Data.Foldable (Foldable (fold))


typeDef :: Parser TypeDef
typeDef = withError "Type declaration" $
  TypeDef <$> ((is "type") *> typeCtor)
          <*> (typeParam |*) <* equal
          <*> anyKindedType

newtypeDef :: Parser NewTypeDef
newtypeDef = withError "Newtype declaration" $
  NewTypeDef <$> (is "newtype" *> typeCtor)
             <*> (typeParam |*) <* equal
             <*> ctor
             <*> fieldDef
             <*> (derivingClause |*)

dataDef :: Parser DataDef
dataDef = withError "Data declaration" $
  DataDef <$> (is "data" *> typeCtor)
          <*> (typeParam |*)
          <*> (fold <$> alternatives)
          <*> (derivingClause |*)
  where
    alternatives = ((equal *> someSepBy (is "|") dataCtorDef) |?)


namedFieldDef :: Parser NamedFieldDef
namedFieldDef = NamedFieldDef <$> var <* (colon <#> 2)
                              <*> type'

unNamedFieldDef :: Parser UnNamedFieldDef
unNamedFieldDef = UnNamedFieldDef <$> (withinParens type' <|> type')

fieldDef :: Parser FieldDef
fieldDef = UnNamedField <$> unNamedFieldDef <|>
           NamedField   <$> withinCurlyBrackets namedFieldDef

dataCtorDef :: Parser DataCtorDef
dataCtorDef = NamedFieldsCtor   <$> ctor
                                <*> (withinCurlyBrackets $
                                    anySepBy comma namedFieldDef) <|>
              UnNamedFieldsCtor <$> ctor
                                <*> (unNamedFieldDef |*)

derivingClause :: Parser DerivingClause
derivingClause = (is "deriving" *>
                  ((is "stock" |?) *>
                      (Deriving StandardDeriving <$> derivingList))
                  <|> (is "newtype" *>
                       (Deriving NewTypeDeriving <$> derivingList))
                  <|> (is "anyclass" *>
                       (Deriving AnyClassDeriving <$> derivingList))
                  <|> (DerivingVia <$> derivingList
                                   <*> (is "via" *> anyKindedType)))
  where
    derivingList = (tupleOf class' <|> pure <$> maybeWithinParens class')