packages feed

jacinda-2.0.2.0: src/A.hs

{-# LANGUAGE DeriveAnyClass    #-}
{-# LANGUAGE DeriveFunctor     #-}
{-# LANGUAGE DeriveGeneric     #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TypeFamilies      #-}

module A ( E (..), T (..), (~>), TB (..), C (..)
         , L (..), N (..), BBin (..), BTer (..)
         , BUn (..), DfnVar (..)
         , D (..), Program (..)
         , mapExpr
         , getS, flushD
         -- * Base functors
         , EF (..)
         ) where

import           Control.DeepSeq    (NFData)
import           Control.Recursion  (Base, Corecursive, Recursive)
import qualified Data.ByteString    as BS
import qualified Data.IntMap        as IM
import           Data.List          (foldl')
import qualified Data.Text          as T
import           Data.Text.Encoding (decodeUtf8)
import qualified Data.Vector        as V
import           GHC.Generics       (Generic)
import           Nm
import           Prettyprinter      (Doc, Pretty (..), braces, brackets, concatWith, encloseSep, flatAlt, group, hardline, indent, parens, pipe, punctuate, tupled, (<+>))
import           Regex.Rure         (RurePtr)

infixr 6 <#>
infixr 6 <##>

(<#>) :: Doc a -> Doc a -> Doc a
(<#>) x y = x <> hardline <> y

(<##>) :: Doc a -> Doc a -> Doc a
(<##>) x y = x <> hardline <> hardline <> y

data TB = TyInteger | TyFloat | TyStr
        | TyStream | TyVec | TyOption
        | TyR | TyBool | TyUnit
        deriving (Eq, Ord)

tupledByFunky :: Doc ann -> [Doc ann] -> Doc ann
tupledByFunky sep = group . encloseSep (flatAlt "⟨ " "⟨") (flatAlt " ⟩" "⟩") sep

tupledBy :: Doc ann -> [Doc ann] -> Doc ann
tupledBy sep = group . encloseSep (flatAlt "( " "(") (flatAlt " )" ")") sep

jacTup :: Pretty a => [a] -> Doc ann
jacTup = tupledBy " . " . fmap pretty

infixr 0 ~>

(~>) :: T -> T -> T
(~>) = TyArr

infixr 0 :$

data T = TyB { tyBuiltin :: TB }
       | (:$) { tyApp0 :: T, tyApp1 :: T } -- TODO: :$
       | TyArr { tyArr0 :: T, tyArr1 :: T }
       | TyVar { tyVar :: Nm () }
       | TyTup { tyTups :: [T] }
       | Rho { tyRho :: Nm (), tyArms :: IM.IntMap T }
       deriving (Eq, Ord)

instance Pretty TB where
    pretty TyInteger = "Integer"; pretty TyStr = "Str"; pretty TyFloat = "Float"
    pretty TyStream = "Stream"; pretty TyVec = "List"; pretty TyOption = "Optional"
    pretty TyBool = "Bool"; pretty TyUnit = "𝟙"; pretty TyR = "Regex"

instance Show TB where show=show.pretty

instance Pretty T where
    pretty (TyB b)        = pretty b
    pretty (ty:$ty')      = pretty ty <+> pretty ty'
    pretty (TyVar n)      = pretty n
    pretty (TyArr ty ty') = pretty ty <+> "⟶" <+> pretty ty'
    pretty (TyTup tys)    = jacTup tys
    pretty (Rho n fs)     = braces (pretty n <+> pipe <+> prettyFields (IM.toList fs))

prettyFields :: [(Int, T)] -> Doc ann
prettyFields = mconcat . punctuate "," . fmap g where g (i, t) = pretty i <> ":" <+> pretty t

instance Show T where show=show.pretty

data BUn = Tally -- length of string field
         | Const
         | Not -- ^ Boolean
         | At Int | Select Int
         | IParse | FParse | Parse
         | Floor | Ceiling
         | Some
         | Dedup | CatMaybes
         | Negate
         | TallyList -- length of vector
         deriving (Eq)

instance Pretty BUn where
    pretty Tally      = "#"
    pretty Const      = "[:"
    pretty Not        = "!"
    pretty (At i)     = "." <> pretty i
    pretty (Select i) = "->" <> pretty i
    pretty IParse     = ":i"
    pretty FParse     = ":f"
    pretty Floor      = "floor"
    pretty Ceiling    = "ceil"
    pretty Parse      = ":"
    pretty Some       = "Some"
    pretty Dedup      = "~."
    pretty CatMaybes  = ".?"
    pretty Negate     = "-."
    pretty TallyList  = "#*"

data BTer = ZipW
          | Fold | Scan
          | Substr
          | Option
          | Captures | AllCaptures
          deriving (Eq)

instance Pretty BTer where
    pretty ZipW        = ","
    pretty Fold        = "|"
    pretty Scan        = "^"
    pretty Substr      = "substr"
    pretty Option      = "option"
    pretty Captures    = "~*"
    pretty AllCaptures = "captures"

-- builtin
data BBin = Plus | Times | Div
          | Minus | Exp
          | Eq | Neq | Geq | Gt | Lt | Leq
          | Map
          | Matches -- ^ @'string' ~ /pat/@
          | NotMatches
          | And | Or
          | Min | Max
          | Split | Splitc
          | Prior | DedupOn | MapMaybe
          | Filter | Fold1
          | Match | Sprintf
          deriving (Eq)

instance Pretty BBin where
    pretty Plus = "+"; pretty Times = "*"; pretty Div = "%"; pretty Minus = "-"
    pretty Eq = "="; pretty Gt = ">"; pretty Lt = "<"; pretty Geq = ">="
    pretty Leq = "<="; pretty Neq = "!="; pretty Map = "¨"; pretty Matches = "~"
    pretty NotMatches = "!~"; pretty And = "&"; pretty Or = "||"
    pretty Max = "max"; pretty Min = "min"; pretty Prior = "\\."; pretty Filter = "#."
    pretty Split = "split"; pretty Splitc = "splitc"; pretty Sprintf = "sprintf"
    pretty Match = "match"; pretty MapMaybe = ":?"; pretty Fold1 = "|>"
    pretty Exp = "**"; pretty DedupOn = "~.*"

data DfnVar = X | Y deriving (Eq)

instance Pretty DfnVar where pretty X = "x"; pretty Y = "y"

-- 0-ary
data N = Ix | Nf | None | Fp deriving (Eq)

data L = ILit !Integer | FLit !Double | BLit !Bool | StrLit BS.ByteString deriving (Generic, NFData, Eq)

-- expression
data E a = Column { eLoc :: a, col :: Int }
         | IParseCol { eLoc :: a, col :: Int } | FParseCol { eLoc :: a, col :: Int } | ParseCol { eLoc :: a, col :: Int }
         | Field { eLoc :: a, eField :: Int } | LastField { eLoc :: a } | FieldList { eLoc :: a }
         | AllField { eLoc :: a } -- ^ Think @$0@ in awk.
         | AllColumn { eLoc :: a } -- ^ Think @$0@ in awk.
         | IParseAllCol { eLoc :: a } -- ^ @$0@, parsed as an integer
         | FParseAllCol { eLoc :: a } -- ^ @$0@, parsed as a float
         | ParseAllCol { eLoc :: a }
         | EApp { eLoc :: a, eApp0 :: E a, eApp1 :: E a }
         | Guarded { eLoc :: a, eP :: E a, eGuarded :: E a }
         | Implicit { eLoc :: a, eImplicit :: E a }
         | Let { eLoc :: a, eBind :: (Nm a, E a), eE :: E a }
         -- TODO: literals type (make pattern matching easier down the road)
         | Var { eLoc :: a, eVar :: !(Nm a) }
         | Lit { eLoc :: a, lit :: !L }
         | RegexLit { eLoc :: a, eRr :: BS.ByteString }
         | Lam { eLoc :: a, eBound :: Nm a, lamE :: E a }
         | Dfn { eLoc :: a, eDfn :: E a }
         | BB { eLoc :: a, eBin :: BBin } | TB { eLoc :: a, eTer :: BTer } | UB { eLoc :: a, eUn :: BUn }
         | NB { eLoc :: a, eNil :: N }
         | Tup { eLoc :: a, esTup :: [E a] }
         | ResVar { eLoc :: a, dfnVar :: DfnVar }
         | RC RurePtr -- compiled regex after normalization
         | Arr { eLoc :: a, elems :: V.Vector (E a) }
         | Anchor { eLoc :: a, eAnchored :: [E a] }
         | Paren { eLoc :: a, eExpr :: E a }
         | OptionVal { eLoc :: a, eMaybe :: Maybe (E a) }
         | Cond { eLoc :: a, eIf :: E a, eThen :: E a, eElse :: E a }
         | In { oop :: E a, ip :: Maybe (E a), mm :: Maybe (E a), istream :: E a }
         | RwB { eLoc :: a, eBin :: BBin } | RwT { eLoc :: a, eTer :: BTer }
         deriving (Functor, Generic)

instance Recursive (E a) where
instance Corecursive (E a) where

data EF a x = ColumnF a Int
            | IParseColF a Int | FParseColF a Int | ParseColF a Int
            | FieldF a Int | LastFieldF a | FieldListF a | AllFieldF a
            | AllColumnF a | IParseAllColF a | FParseAllColF a | ParseAllColF a
            | EAppF a x x
            | GuardedF a x x | ImplicitF a x
            | LetF a (Nm a, x) x
            | VarF a (Nm a)
            | LitF a !L
            | RegexLitF a BS.ByteString
            | LamF a (Nm a) x
            | DfnF a x
            | BBF a BBin | TBF a BTer | UBF a BUn | NBF a N
            | TupF a [x]
            | ResVarF a DfnVar
            | RCF RurePtr
            | ArrF a (V.Vector x)
            | AnchorF a [x]
            | ParenF a x
            | OptionValF a (Maybe x)
            | CondF a x x x
            | InF x (Maybe x) (Maybe x) x
            | RwBF a BBin | RwTF a BTer
            deriving (Generic, Functor)

type instance Base (E a) = (EF a)

instance Pretty N where
    pretty Ix = "⍳"; pretty Nf = "nf"; pretty None = "None"; pretty Fp = "fp"

instance Pretty L where
    pretty (ILit i)     = pretty i
    pretty (FLit d)     = pretty d
    pretty (BLit True)  = "#t"
    pretty (BLit False) = "#f"
    pretty (StrLit str) = pretty (decodeUtf8 str)

instance Pretty (E a) where
    pretty (Column _ i)                                           = "$" <> pretty i
    pretty AllColumn{}                                            = "$0"
    pretty IParseAllCol{}                                         = "$0:i"
    pretty FParseAllCol{}                                         = "$0:f"
    pretty ParseAllCol{}                                          = "$0:"
    pretty (IParseCol _ i)                                        = "$" <> pretty i <> ":i"
    pretty (FParseCol _ i)                                        = "$" <> pretty i <> ":f"
    pretty (ParseCol _ i)                                         = "$" <> pretty i <> ":"
    pretty AllField{}                                             = "`0"
    pretty (Field _ i)                                            = "`" <> pretty i
    pretty LastField{}                                            = "`*"
    pretty FieldList{}                                            = "`$"
    pretty (EApp _ (EApp _ (BB _ Prior) e) e')                    = pretty e <> "\\." <+> pretty e'
    pretty (EApp _ (EApp _ (BB _ Max) e) e')                      = "max" <+> pretty e <+> pretty e'
    pretty (EApp _ (EApp _ (BB _ Min) e) e')                      = "min" <+> pretty e <+> pretty e'
    pretty (EApp _ (EApp _ (BB _ Split) e) e')                    = "split" <+> pretty e <+> pretty e'
    pretty (EApp _ (EApp _ (BB _ Splitc) e) e')                   = "splitc" <+> pretty e <+> pretty e'
    pretty (EApp _ (EApp _ (BB _ Match) e) e')                    = "match" <+> pretty e <+> pretty e'
    pretty (EApp _ (EApp _ (BB _ Sprintf) e) e')                  = "sprintf" <+> pretty e <+> pretty e'
    pretty (EApp _ (EApp _ (BB _ Map) e) e')                      = pretty e <> "¨" <> pretty e'
    pretty (EApp _ (EApp _ (BB _ b) e) e')                        = pretty e <+> pretty b <+> pretty e'
    pretty (EApp _ (BB _ b) e)                                    = parens (pretty e <> pretty b)
    pretty (EApp _ (EApp _ (EApp _ (TB _ Fold) e) e') e'')        = pretty e <> "|" <> pretty e' <+> pretty e''
    pretty (EApp _ (EApp _ (EApp _ (TB _ Scan) e) e') e'')        = pretty e <> "^" <> pretty e' <+> pretty e''
    pretty (EApp _ (EApp _ (EApp _ (TB _ ZipW) op) e') e'')       = "," <> pretty op <+> pretty e' <+> pretty e''
    pretty (EApp _ (EApp _ (EApp _ (TB _ Substr) e) e') e'')      = "substr" <+> pretty e <+> pretty e' <+> pretty e''
    pretty (EApp _ (EApp _ (EApp _ (TB _ Option) e) e') e'')      = "option" <+> pretty e <+> pretty e' <+> pretty e''
    pretty (EApp _ (EApp _ (EApp _ (TB _ AllCaptures) e) e') e'') = "captures" <+> pretty e <+> pretty e' <+> pretty e''
    pretty (EApp _ (EApp _ (EApp _ (TB _ Captures) e) e') e'')    = pretty e <+> "~*" <+> pretty e' <+> pretty e''
    pretty (EApp _ (UB _ (At i)) e)                               = pretty e <> "." <> pretty i
    pretty (EApp _ (UB _ (Select i)) e)                           = pretty e <> "->" <> pretty i
    pretty (EApp _ (UB _ IParse) e')                              = pretty e' <> ":i"
    pretty (EApp _ (UB _ FParse) e')                              = pretty e' <> ":f"
    pretty (EApp _ (UB _ Parse) e')                               = pretty e' <> ":"
    pretty (EApp _ e@UB{} e')                                     = pretty e <> pretty e'
    pretty (EApp _ e e')                                          = pretty e <+> pretty e'
    pretty (Lit _ l)                                              = pretty l
    pretty (Var _ n)                                              = pretty n
    pretty (RegexLit _ rr)                                        = "/" <> pretty (decodeUtf8 rr) <> "/"
    pretty (BB _ b)                                               = parens (pretty b)
    pretty (UB _ u)                                               = pretty u
    pretty (ResVar _ x)                                           = pretty x
    pretty (Tup _ es)                                             = jacTup es
    pretty (Lam _ n e)                                            = parens ("λ" <> pretty n <> "." <+> pretty e)
    pretty (Dfn _ e)                                              = brackets (pretty e)
    pretty (Guarded _ p e)                                        = braces (pretty p) <> braces (pretty e)
    pretty (Implicit _ e)                                         = braces ("|" <+> pretty e)
    pretty (NB _ n)                                               = pretty n
    pretty RC{}                                                   = "(compiled regex)"
    pretty (Let _ (n, b) e)                                       = "let" <+> "val" <+> pretty n <+> ":=" <+> pretty b <+> "in" <+> pretty e <+> "end"
    pretty (Paren _ e)                                            = parens (pretty e)
    pretty (Arr _ es)                                             = tupledByFunky "," (V.toList $ pretty <$> es)
    pretty (Anchor _ es)                                          = "&" <> tupledBy "." (pretty <$> es)
    pretty (OptionVal _ (Just e))                                 = "Some" <+> pretty e
    pretty (OptionVal _ Nothing)                                  = "None"
    pretty (Cond _ e0 e1 e2)                                      = "if" <+> pretty e0 <+> "then" <+> pretty e1 <+> "else" <+> pretty e2
    pretty (RwB _ MapMaybe)                                       = "mapMaybe"
    pretty (RwB _ DedupOn)                                        = "dedupOn"
    pretty (RwB _ Filter)                                         = "filter"
    pretty (RwT _ Fold)                                           = "fold"
    pretty (RwT _ Scan)                                           = "scan"
    pretty (RwB _ Fold1)                                          = "fold1"

instance Show (E a) where show=show.pretty

-- for tests
instance Eq (E a) where
    (==) (Column _ i) (Column _ j)              = i == j
    (==) (IParseCol _ i) (IParseCol _ j)        = i == j
    (==) (FParseCol _ i) (FParseCol _ j)        = i == j
    (==) (Field _ i) (Field _ j)                = i == j
    (==) LastField{} LastField{}                = True
    (==) FieldList{} FieldList{}                = True
    (==) AllColumn{} AllColumn{}                = True
    (==) AllField{} AllField{}                  = True
    (==) (EApp _ e0 e1) (EApp _ e0' e1')        = e0 == e0' && e1 == e1'
    (==) (Guarded _ p e) (Guarded _ p' e')      = p == p' && e == e'
    (==) (Implicit _ e) (Implicit _ e')         = e == e'
    (==) (Let _ (n, eϵ) e) (Let _ (n', eϵ') e') = eqName n n' && e == e' && eϵ == eϵ'
    (==) (Var _ n) (Var _ n')                   = eqName n n'
    (==) (Lam _ n e) (Lam _ n' e')              = eqName n n' && e == e'
    (==) (Lit _ l) (Lit _ l')                   = l == l'
    (==) (RegexLit _ rr) (RegexLit _ rr')       = rr == rr'
    (==) (BB _ b) (BB _ b')                     = b == b'
    (==) (TB _ b) (TB _ b')                     = b == b'
    (==) (UB _ unOp) (UB _ unOp')               = unOp == unOp'
    (==) (NB _ x) (NB _ y)                      = x == y
    (==) (Tup _ es) (Tup _ es')                 = es == es'
    (==) (ResVar _ x) (ResVar _ y)              = x == y
    (==) (Dfn _ f) (Dfn _ g)                    = f == g -- we're testing for lexical equivalence
    (==) RC{} _                                 = error "Cannot compare compiled regex!"
    (==) _ RC{}                                 = error "Cannot compare compiled regex!"
    (==) (Paren _ e) e'                         = e == e'
    (==) e (Paren _ e')                         = e == e'
    (==) (RwB _ b) (RwB _ b')                   = b == b'
    (==) (RwT _ b) (RwT _ b')                   = b == b'
    (==) _ _                                    = False

data C = IsNum | IsEq | IsOrd
       | IsParse | IsPrintf
       | IsSemigroup
       | Functor -- ^ For map (@"@)
       | Foldable | Witherable
       deriving (Eq, Ord)

instance Pretty C where
    pretty IsNum = "Num"; pretty IsEq = "Eq"; pretty IsOrd = "Ord"
    pretty IsParse = "Parseable"; pretty IsSemigroup = "Semigroup"
    pretty Functor = "Functor"; pretty Foldable = "Foldable"
    pretty IsPrintf = "Printf"; pretty Witherable = "Witherable"

instance Show C where show=show.pretty

-- decl
data D a = SetFS T.Text | SetRS T.Text
         | FunDecl (Nm a) [Nm a] (E a)
         | FlushDecl
         | SetAsv
         deriving (Functor)

instance Pretty (D a) where
    pretty (SetFS bs)       = ":set fs :=" <+> "/" <> pretty bs <> "/;"
    pretty (SetRS rs)       = ":set rs :=" <+> "/" <> pretty rs <> "/;"
    pretty (FunDecl n ns e) = "fn" <+> pretty n <> tupled (pretty <$> ns) <+> ":=" <#> indent 2 (pretty e <> ";")
    pretty FlushDecl        = ":flush;"
    pretty SetAsv           = ":set asv;"

data Program a = Program { decls :: [D a], expr :: E a } deriving (Functor)

instance Pretty (Program a) where
    pretty (Program ds e) = concatWith (<##>) (pretty <$> ds) <##> pretty e

instance Show (Program a) where show=show.pretty

flushD :: Program a -> Bool
flushD (Program ds _) = any p ds where p FlushDecl = True; p _ = False

getS :: Program a -> (Maybe T.Text, Maybe T.Text)
getS (Program ds _) = foldl' go (Nothing, Nothing) ds where
    go (_, rs) (SetFS bs) = (Just bs, rs)
    go (_, rs) SetAsv     = (Just "\\x1f", rs)
    go (fs, _) (SetRS bs) = (fs, Just bs)
    go next _             = next

mapExpr :: (E a -> E a) -> Program a -> Program a
mapExpr f (Program ds e) = Program ds (f e)