packages feed

jacinda-3.3.0.2: src/A.hs

{-# LANGUAGE DeriveAnyClass #-}
{-# LANGUAGE DeriveGeneric  #-}

module A ( E (..), T (..), (~>), TB (..), C (..)
         , L (..), N (..), BBin (..), BTer (..)
         , BUn (..), DfnVar (..)
         , D (..), Program (..)
         , Mode (..), awk
         , mapExpr
         , getS, flushD
         ) where

import           Control.DeepSeq    (NFData)
import qualified Data.ByteString    as BS
import qualified Data.IntMap        as IM
import           Data.List          (foldl')
import           Data.Maybe         (isJust)
import qualified Data.Text          as T
import           Data.Text.Encoding (decodeUtf8)
import qualified Data.Vector        as V
import           GHC.Generics       (Generic)
import           Nm
import           Nm.Map             (NmMap)
import qualified Nm.Map             as 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
(<##>) x y = x <> hardline <> hardline <> y

data TB = TyI | TyFloat | TyStr
        | TyStream | TyVec | TyOption
        | TyR | TyBool | TyUnit
        deriving Eq

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

j'Tup :: Pretty a => [a] -> Doc ann
j'Tup = tupledBy " . " . fmap pretty

jrec = encloseSep (flatAlt "#{ " "#{") (flatAlt (hardline <+> "}") "}") (flatAlt " ; " "; ")

infixr 0 ~>

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

infixr 0 :$

data T = TyB { tyBuiltin :: TB }
       | (:$) { tyApp0, tyApp1 :: T }
       | TyArr { tyArr0, tyArr1 :: T }
       | TyVar { tyVar :: Nm () }
       | TyTup { tyTups :: [T] }
       | TyRec { tyres :: NmMap T }
       | Rho { tyRho :: Nm (), tyArms :: IM.IntMap T }
       | Ρ { tyΡ :: Nm (), tyρs :: NmMap T }
       deriving Eq

instance Pretty TB where
    pretty TyI = "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)    = j'Tup tys
    pretty (TyRec rs)     = jrec ((\(n,t) -> pretty n <> ":" <+> pretty t)<$>Nm.toList rs)
    pretty (Rho n fs)     = braces (pretty n <+> pipe <+> prettyFields (IM.toList fs))
    pretty (Ρ n fs)       = braces (pretty n <+> pipe <+> prettyFields (Nm.toList fs))

parensp True=parens; parensp False=id

prettyFields :: Pretty a => [(a, 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 | SelR !(Nm ())
         | IParse | FParse | Parse
         | Floor | Ceiling
         | Some
         | Dedup | CatMaybes
         | Negate
         | TallyList -- length of vector
         | Head | Tail | Init | Last

instance Pretty BUn where
    pretty Tally      = "#"
    pretty Const      = "[:"
    pretty Not        = "!"
    pretty (At i)     = "." <> pretty i
    pretty (Select i) = "->" <> pretty i
    pretty (SelR r)   = "->" <> pretty r
    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  = "#*"
    pretty Head       = "head#"
    pretty Tail       = "tail#"
    pretty Init       = "init#"
    pretty Last       = "last#"

data BTer = ZipW
          | Fold | Scan | ScanList
          | Substr | Sub1 | Subs
          | Option
          | Captures | AllCaptures | Ixes
          | Bookend

instance Pretty BTer where
    pretty ZipW        = ","
    pretty Fold        = "|"
    pretty Scan        = "^"
    pretty ScanList    = "^*"
    pretty Substr      = "substr"
    pretty Option      = "option"
    pretty Captures    = "~*"
    pretty AllCaptures = "captures"
    pretty Sub1        = "sub1"
    pretty Subs        = "subs"
    pretty Bookend     = ",,"
    pretty Ixes        = "ices"

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

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 MMatch = "~?"; 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 = "~.*"; pretty Report = "$>"
    pretty Take = "take#"; pretty Drop = "drop#"; pretty Rein = "reintercalate"
    pretty Nier = "@@"

data DfnVar = X | Y

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

-- 0-ary
data N = Ix | Nf | None | Fp | MZ

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

class PS a where ps :: Int -> a -> Doc ann

-- 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, eApp1 :: E a }
         | Guarded { eLoc :: a, eP, eGuarded :: E a }
         | Implicit { eLoc :: a, eImplicit :: E a }
         | Let { eLoc :: a, eBind :: (Nm a, E a), eE :: E a }
         | Var { eLoc :: a, eVar :: !(Nm a) }
         | F { hole :: !(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] }
         | Rec { eLoc :: a, esR :: [(Nm a, 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, eThen, eElse :: E a }
         | RwB { eLoc :: a, eBin :: BBin } | RwT { eLoc :: a, eTer :: BTer }
         deriving (Functor)

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

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)

mPrec :: BBin -> Maybe Int
mPrec Prior      = Just 5
mPrec DedupOn    = Just 5
mPrec MapMaybe   = Just 5
mPrec Map        = Just 5
mPrec Filter     = Just 5
mPrec Fold1      = Just 5
mPrec Exp        = Just 8
mPrec Plus       = Just 6
mPrec Minus      = Just 6
mPrec Times      = Just 7
mPrec Div        = Just 7
mPrec Eq         = Just 4
mPrec Neq        = Just 4
mPrec Geq        = Just 4
mPrec Gt         = Just 4
mPrec Lt         = Just 4
mPrec Leq        = Just 4
mPrec Matches    = Just 5
mPrec NotMatches = Just 5
mPrec MMatch     = Just 5
mPrec Report     = Just 4
mPrec And        = Just 3
mPrec Or         = Just 2
mPrec _          = Nothing

instance PS (E a) where
    ps _ (Column _ i)    = "$" <> pretty i
    ps _ AllColumn{}     = "$0"
    ps _ IParseAllCol{}  = "$0:i"
    ps _ FParseAllCol{}  = "$0:f"
    ps _ ParseAllCol{}   = "$0:"
    ps _ (IParseCol _ i) = "$" <> pretty i <> ":i"
    ps _ (FParseCol _ i) = "$" <> pretty i <> ":f"
    ps _ (ParseCol _ i)  = "$" <> pretty i <> ":"
    ps _ AllField{}      = "`0"
    ps _ (Field _ i)     = "`" <> pretty i
    ps _ LastField{}     = "`*"
    ps _ FieldList{}     = "`$"
    ps d (EApp _ (EApp _ (BB _ op) e0) e1) | Just d' <- mPrec op = parensp (d>d') (ps (d'+1) e0 <+> pretty op <+> ps (d'+1) e1)
    ps _ (EApp _ (BB _ op) e) | isJust (mPrec op) = parens (pretty e <+> pretty op)
    ps d (EApp _ (UB _ (At i)) e)     = parensp (d>0) (ps 1 e) <> "." <> pretty i
    ps d (EApp _ (UB _ (Select i)) e) = parensp (d>0) (ps 1 e) <> "->" <> pretty i
    ps d (EApp _ (UB _ (SelR n)) e)   = parensp (d>0) (ps 1 e) <> "->" <> pretty n
    ps _ (EApp _ (UB _ IParse) e')    = pretty e' <> ":i"
    ps _ (EApp _ (UB _ FParse) e')    = pretty e' <> ":f"
    ps _ (EApp _ (UB _ Parse) e')     = pretty e' <> ":"
    ps d (EApp _ (EApp _ (EApp _ (TB _ Bookend) e) e') e'')  = parensp (d>3) (ps 4 e <> ",," <> ps 4 e' <+> ps 5 e'')
    ps d (EApp _ (EApp _ (EApp _ (TB _ Fold) e) e') e'')     = parensp (d>5) (ps 6 e <> "|" <> ps 6 e' <+> ps 7 e'')
    ps d (EApp _ (EApp _ (EApp _ (TB _ Scan) e) e') e'')     = parensp (d>5) (ps 6 e <> "^" <> ps 6 e' <+> ps 7 e'')
    ps d (EApp _ (EApp _ (EApp _ (TB _ ScanList) e) e') e'') = parensp (d>5) (ps 6 e <> "^" <> ps 6 e' <+> ps 7 e'')
    ps d (EApp _ (EApp _ (EApp _ (TB _ ZipW) op) e') e'')    = parensp (d>5) ("," <> ps 6 op <+> ps 7 e' <+> ps 8 e'')
    ps d (EApp _ (EApp _ (EApp _ (TB _ Captures) e) e') e'') = parensp (d>6) (ps 7 e <+> "~*" <+> ps 7 e' <+> ps 8 e'')
    ps d (EApp _ e0 e1) = parensp (d>10) (ps 10 e0 <+> ps 11 e1)
    ps d (OptionVal _ (Just e)) = parensp (d>10) ("Some" <+> ps 11 e)
    ps _ (OptionVal _ Nothing) = "None"
    ps _ (BB _ op)         = parens (pretty op)
    ps _ (Lit _ l)         = pretty l
    ps _ (Var _ n)         = pretty n
    ps _ (F n)             = pretty n
    ps _ (RegexLit _ rr)   = "/" <> pretty (decodeUtf8 rr) <> "/"
    ps _ (UB _ u)          = pretty u
    ps _ (ResVar _ x)      = pretty x
    ps _ (Dfn _ e)         = brackets (pretty e)
    ps _ (NB _ n)          = pretty n
    ps _ RC{}              = "(compiled regex)"
    ps _ (Guarded _ p e)   = braces (pretty p) <> braces (pretty e)
    ps _ (Implicit _ e)    = braces ("|" <+> pretty e)
    ps _ (Paren _ e)       = parens (pretty e)
    ps _ (RwB _ MapMaybe)  = "mapMaybe"
    ps _ (RwB _ DedupOn)   = "dedupOn"
    ps _ (RwB _ Filter)    = "filter"
    ps _ (RwT _ Fold)      = "fold"
    ps _ (RwT _ Scan)      = "scan"
    ps _ (RwB _ Fold1)     = "fold1"
    ps _ (Cond _ e0 e1 e2) = "?" <> pretty e0 <> ";" <+> pretty e1 <> ";" <+> pretty e2
    ps _ (Tup _ es)        = j'Tup es
    ps _ (Rec _ es)        = jrec ((\(n,e) -> pretty n <+> ".=" <+> pretty e)<$>es)
    ps _ (Arr _ es)        = tupledByFunky "," (V.toList $ pretty <$> es)
    ps _ (Anchor _ es)     = "&" <> tupledBy "." (pretty <$> es)
    ps _ (Let _ (n, b) e)  = "let" <+> "val" <+> pretty n <+> ":=" <+> pretty b <+> "in" <+> pretty e <+> "end"
    ps d (Lam _ n e)       = parensp (d>1) ("λ" <> pretty n <> "." <+> ps 2 e)
    ps _ (TB _ g)          = pretty g

instance Pretty (E a) where pretty=ps 0

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

data C = IsNum | IsEq | IsOrd
       | IsParse | IsPrintf
       | IsSemigroup | IsMonoid
       | 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"
    pretty IsMonoid = "Monoid"

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 | SetH
         | SetAsv | SetUsv | SetCsv
         | SetOFS T.Text | SetORS T.Text
         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 SetH             = ":set header;"
    pretty SetAsv           = ":set asv;"
    pretty SetUsv           = ":set usv;"
    pretty SetCsv           = ":set csv;"
    pretty (SetOFS sep)     = ":set ofs :=" <+> "'" <> pretty sep <> "';"
    pretty (SetORS sep)     = ":set ors :=" <+> "'" <> pretty sep <> "';"

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

awk = AWK Nothing Nothing False

data Mode = CSV | AWK (Maybe T.Text) (Maybe T.Text) Bool -- field, record, include header in record split

getS :: Program a -> Mode
getS (Program ds _) = foldl' go awk ds where
    go (AWK _ rs b) (SetFS bs) = AWK (Just bs) rs b
    go _ SetAsv                = AWK (Just "\\x1f") (Just "\\x1e") False
    go _ SetUsv                = AWK (Just "␞") (Just "␟") False
    go _ SetCsv                = CSV
    go (AWK fs _ b) (SetRS bs) = AWK fs (Just bs) b
    go (AWK fs rs _) SetH      = AWK fs rs True
    go next _                  = next

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