packages feed

jacinda-3.2.0.0: src/L.x

{
    {-# LANGUAGE OverloadedStrings #-}
    module L ( alexMonadScan
             , alexInitUserState
             , runAlexSt
             , withAlexSt
             , freshName
             , newVarAlex
             , AlexPosn (..)
             , Alex (..)
             , Token (..)
             , Keyword (..)
             , Sym (..)
             , Builtin (..)
             , Var (..)
             , AlexUserState
             ) where

import Control.Arrow ((&&&))
import Data.Bifunctor (first)
import qualified Data.ByteString.Lazy as BSL
import Data.Functor (($>))
import qualified Data.IntMap as IM
import qualified Data.Map as M
import qualified Data.Text as T
import Nm
import Prettyprinter (Pretty (pretty), (<+>), colon, squotes)
import U

}

%wrapper "monadUserState-strict-text"

$digit = [0-9]

$latin = [a-zA-Z]

@follow_char = [$latin $digit \_]

$str_special = [\\\']

@escape_str = \\ ([$str_special nt] | "ESC")

@string = \' ([^ $str_special] | @escape_str)* \'

@escape_rr = \\ [\\\/]

@rr = "/" ([^\/] | @escape_rr)* "/"

@name = [a-z] @follow_char*
@tyname = [A-Z] @follow_char*

@float = $digit+\.$digit+

tokens :-

    <dfn> {
        x                        { res VarX }
        y                        { res VarY }
    }

    <0> "["                      { sym LSqBracket `andBegin` dfn } -- FIXME: this doesn't allow nested

    <0,dfn> {

        $white+                  ;

        "{.".*                   ;
        "#!".*                   ; -- shebang

        ":="                     { sym DefEq }
        "≔"                      { sym DefEq }
        "{"                      { sym LBrace }
        "}"                      { sym RBrace }

        "#."                     { sym FilterTok }

        -- symbols/operators
        "%"                      { sym PercentTok }
        "*"                      { sym TimesTok }
        "**"                     { sym ExpTok }
        "+"                      { sym PlusTok }
        "-"                      { sym MinusTok }

        "|"                      { sym FoldTok }
        \"                       { sym Quot }
        "^"                      { sym Caret }
        "^*"                     { sym CaretStar }
        "|>"                     { sym Fold1Tok }
        ⍬                        { sym Zilde }

        "="                      { sym EqTok }
        "!="                     { sym NeqTok }
        "<="                     { sym LeqTok }
        "<"                      { sym LtTok }
        ">="                     { sym GeqTok }
        ">"                      { sym GtTok }
        "&"                      { sym AndTok }
        "||"                     { sym OrTok }
        "("                      { sym LParen }
        ")"                      { sym RParen }
        "&("                     { sym LAnchor }
        "$>"                     { sym IceCreamCone }
        "{%"                     { sym LBracePercent }
        "#{"                     { sym LBraceOctothorpe }
        "{|"                     { sym LBraceBar }
        "]"                      { sym RSqBracket `andBegin` 0 }
        ".="                     { sym DotEq }
        "~"                      { sym Tilde }
        "!~"                     { sym NotMatchTok }
        "~?"                     { sym MMatch }
        ","                      { sym Comma }
        ",,"                     { sym DoubleComma }
        "."                      { sym Dot }
        "#"                      { sym TallyTok }
        "#*"                     { sym LengthTok }
        "[:"                     { sym ConstTok }
        "!"                      { sym Exclamation }
        ":"                      { sym Colon }
        ";"                      { sym Semicolon }
        "\."                     { sym BackslashDot }
        \\                       { sym Backslash }
        λ                        { sym Backslash }
        "|`"                     { sym CeilSym }
        ⌈                        { sym CeilSym }
        "|."                     { sym FloorSym }
        ⌊                        { sym FloorSym }
        "~."                     { sym DedupTok }
        dedup                    { sym DedupTok }
        "~.*"                    { sym DedupOnTok }
        ".?"                     { sym CatMaybesTok }
        catMaybes                { sym CatMaybesTok }
        ":?"                     { sym MapMaybeTok }
        "~*"                     { sym CapTok }
        "-."                     { sym NegTok }
        "`*"                     { sym LastFieldTok }
        "`$"                     { sym FieldListTok }
        \?                       { sym QuestionMark }
        "@@"                     { sym AmpAmp }

        in                       { mkKw KwIn }
        let                      { mkKw KwLet }
        val                      { mkKw KwVal }
        end                      { mkKw KwEnd }
        :set                     { mkKw KwSet }
        :flush                   { mkKw KwFlush }
        fn                       { mkKw KwFn }
        "@include"               { mkKw KwInclude }
        if                       { mkKw KwIf }
        then                     { mkKw KwThen }
        else                     { mkKw KwElse }
        asv                      { mkKw KwAsv }
        usv                      { mkKw KwUsv }
        csv                      { mkKw KwCsv }

        fs                       { res VarFs }
        rs                       { res VarRs }
        ix                       { res VarIx }
        ⍳                        { res VarIx }
        nf                       { res VarNf }
        ¨                        { sym Quot }
        min                      { res VarMin }
        max                      { res VarMax }

        substr                   { mkBuiltin BSubstr }
        split                    { mkBuiltin BSplit }
        splitc                   { mkBuiltin BSplitc }
        sprintf                  { mkBuiltin BSprintf }
        option                   { mkBuiltin BOption }
        floor                    { mkBuiltin BFloor }
        ceil                     { mkBuiltin BCeil }
        match                    { mkBuiltin BMatch }
        ices                     { mkBuiltin BIxes }
        captures                 { mkBuiltin BCaptures }
        Some                     { mkBuiltin BSome }
        None                     { mkBuiltin BNone }
        fp                       { mkBuiltin BFp }
        mapMaybe                 { mkBuiltin BMapMaybe }
        dedupOn                  { mkBuiltin BDedupOn }
        filter                   { mkBuiltin BFilt }
        fold                     { mkBuiltin BFold }
        fold1                    { mkBuiltin BFold1 }
        scan                     { mkBuiltin BScan }
        "sub1"                   { mkBuiltin BSub1 }
        subs                     { mkBuiltin BSubs }
        "head#"                  { mkBuiltin BHead }
        "tail#"                  { mkBuiltin BTail }
        "last#"                  { mkBuiltin BLast }
        "init#"                  { mkBuiltin BInit }
        "take#"                  { mkBuiltin BTake }
        "drop#"                  { mkBuiltin BDrop }
        reintercalate            { mkBuiltin BRein }

        ":i"                     { mkBuiltin BIParse }
        ":f"                     { mkBuiltin BFParse }

        "#t"                     { tok (\p _ -> alex $ TokBool p True) }
        "#f"                     { tok (\p _ -> alex $ TokBool p False) }

        \$$digit+                { tok (\p s -> alex $ TokStreamLit p (read $ T.unpack $ T.tail s)) }
        `$digit+                 { tok (\p s -> alex $ TokFieldLit p (read $ T.unpack $ T.tail s)) }

        "."$digit+               { tok (\p s -> alex $ TokAccess p (read $ T.unpack $ T.tail s)) }
        "->"$digit+              { tok (\p s -> alex $ TokSelect p (read $ T.unpack $ T.drop 2 s)) }
        "->"$latin+              { tok (\p s -> TokR p <$> newIdentAlex p (T.drop 2 s)) }
        $digit+                  { tok (\p s -> alex $ TokInt p (read $ T.unpack s)) }
        _$digit+                 { tok (\p s -> alex $ TokInt p (negate $ read $ T.unpack $ T.tail s)) }

        $digit+\.$digit+         { tok (\p s -> alex $ TokFloat p (read $ T.unpack s)) }
        _$digit+\.$digit+        { tok (\p s -> alex $ TokFloat p (negate $ read $ T.unpack $ T.tail s)) }

        @string                  { tok (\p s -> alex $ TokStr p (escReplace $ T.init $ T.tail s)) }

        -- TODO: allow chars to be escaped
        @rr                      { tok (\p s -> alex $ TokRR p (escRr $ T.init $ T.tail s)) }

        @name                    { tok (\p s -> TokName p <$> newIdentAlex p s) }
        @tyname                  { tok (\p s -> TokTyName p <$> newIdentAlex p s) }

    }

{

dropQuotes :: BSL.ByteString -> BSL.ByteString
dropQuotes = BSL.init . BSL.tail

alex :: a -> Alex a
alex = pure

tok f (p,_,_,s) len = f p (T.take len s)

constructor c t = tok (\p _ -> alex $ c p t)

res = constructor TokResVar

mkKw = constructor TokKeyword

sym = constructor TokSym

mkBuiltin = constructor TokBuiltin

-- this is inefficient but w/e
escReplace :: T.Text -> T.Text
escReplace =
      T.replace "\\\"" "\""
    . T.replace "\\\\" "\\"
    . T.replace "\\ESC" "\ESC"
    . T.replace "\\n" "\n"
    . T.replace "\\t" "\t"

escRr :: T.Text -> T.Text
escRr = T.replace "\\/" "/"

instance Pretty AlexPosn where
    pretty (AlexPn _ line col) = pretty line <> colon <> pretty col

-- functional bimap?
type AlexUserState = (Int, M.Map T.Text Int, IM.IntMap (Nm AlexPosn))

alexInitUserState :: AlexUserState
alexInitUserState = (0, mempty, mempty)

gets_alex :: (AlexState -> a) -> Alex a
gets_alex f = Alex (Right . (id &&& f))

get_pos :: Alex AlexPosn
get_pos = gets_alex alex_pos

alexEOF = EOF <$> get_pos

data Sym = PlusTok | MinusTok | PercentTok
         | ExpTok
         | FoldTok
         | IceCreamCone
         | Fold1Tok
         | Quot
         | TimesTok
         | DefEq
         | Colon
         | LBrace | RBrace
         | LParen
         | LAnchor
         | DotEq
         | RParen
         | LSqBracket | RSqBracket
         | Semicolon
         | Underscore
         | EqTok | NeqTok
         | GtTok | GeqTok
         | LtTok | LeqTok
         | AndTok | OrTok
         | Tilde | NotMatchTok | MMatch
         | Comma | Dot
         | DoubleComma
         | TallyTok | LengthTok
         | ConstTok
         | LBracePercent
         | LBraceOctothorpe
         | LBraceBar
         | Exclamation
         | Caret | CaretStar
         | Zilde
         | Backslash
         | BackslashDot
         | QuestionMark
         | FilterTok
         | FloorSym | CeilSym
         | DedupTok | DedupOnTok
         | CatMaybesTok | MapMaybeTok
         | CapTok
         | NegTok
         | LastFieldTok | FieldListTok
         | AmpAmp

instance Pretty Sym where
    pretty PlusTok          = "+"
    pretty MinusTok         = "-"
    pretty PercentTok       = "%"
    pretty ExpTok           = "**"
    pretty FoldTok          = "|"
    pretty Fold1Tok         = "|>"
    pretty TimesTok         = "*"
    pretty DefEq            = ":="
    pretty DotEq            = ".="
    pretty Colon            = ":"
    pretty LBrace           = "{"
    pretty RBrace           = "}"
    pretty Semicolon        = ";"
    pretty Underscore       = "_"
    pretty EqTok            = "="
    pretty LeqTok           = "<="
    pretty LtTok            = "<"
    pretty NeqTok           = "!="
    pretty GeqTok           = ">="
    pretty GtTok            = ">"
    pretty AndTok           = "&"
    pretty OrTok            = "||"
    pretty LParen           = "("
    pretty RParen           = ")"
    pretty LAnchor          = "&("
    pretty LSqBracket       = "["
    pretty RSqBracket       = "]"
    pretty Tilde            = "~"
    pretty NotMatchTok      = "!~"
    pretty MMatch           = "~?"
    pretty Comma            = ","
    pretty DoubleComma      = ",,"
    pretty Dot              = "."
    pretty TallyTok         = "#"
    pretty Zilde            = "⍬"
    pretty LengthTok        = "#*"
    pretty Quot             = "¨"
    pretty Caret            = "^"
    pretty CaretStar        = "^*"
    pretty ConstTok         = "[:"
    pretty LBracePercent    = "{%"
    pretty LBraceOctothorpe = "#{"
    pretty LBraceBar        = "{|"
    pretty Exclamation      = "!"
    pretty Backslash        = "\\"
    pretty BackslashDot     = "\\."
    pretty FilterTok        = "#."
    pretty FloorSym         = "⌊"
    pretty CeilSym          = "⌈"
    pretty DedupTok         = "~."
    pretty DedupOnTok       = "~.*"
    pretty CatMaybesTok     = ".?"
    pretty MapMaybeTok      = ":?"
    pretty CapTok           = "~*"
    pretty NegTok           = "-."
    pretty LastFieldTok     = "`*"
    pretty FieldListTok     = "`$"
    pretty IceCreamCone     = "$>"
    pretty QuestionMark     = "?"
    pretty AmpAmp           = "@@"

data Keyword = KwLet
             | KwIn
             | KwVal
             | KwEnd
             | KwSet
             | KwFlush
             | KwFn
             | KwInclude
             | KwIf | KwThen | KwElse
             | KwAsv | KwUsv | KwCsv

-- | Reserved/special variables
data Var = VarX | VarY
         | VarFs | VarRs
         | VarOfs | VarOrs
         | VarIx | VarNf
         | VarMin | VarMax

instance Pretty Var where
    pretty VarX = "x"; pretty VarY = "y"
    pretty VarFs = "fs"; pretty VarRs = "rs"
    pretty VarOrs = "ors"; pretty VarOfs = "ofs"
    pretty VarMin = "min"; pretty VarMax = "max"
    pretty VarIx = "⍳"; pretty VarNf = "nf"

instance Pretty Keyword where
    pretty KwLet     = "let"
    pretty KwIn      = "in"
    pretty KwVal     = "val"
    pretty KwEnd     = "end"
    pretty KwFn      = "fn"
    pretty KwSet     = ":set"
    pretty KwFlush   = ":flush"
    pretty KwInclude = "@include"
    pretty KwIf      = "if"
    pretty KwThen    = "then"
    pretty KwElse    = "else"
    pretty KwUsv     = "usv"
    pretty KwAsv     = "asv"
    pretty KwCsv     = "csv"

data Builtin = BIParse | BFParse
             | BSubstr
             | BSplit | BSplitc
             | BOption
             | BSprintf
             | BFloor | BCeil
             | BMatch | BIxes
             | BCaptures
             | BSome | BNone
             | BFp
             | BMapMaybe
             | BDedupOn
             | BFilt
             | BFold | BFold1
             | BScan
             | BSub1 | BSubs
             | BHead | BTail
             | BInit | BLast
             | BDrop | BTake
             | BRein

instance Pretty Builtin where
    pretty BIParse   = ":i"
    pretty BFParse   = ":f"
    pretty BSubstr   = "substr"
    pretty BSplit    = "split"
    pretty BOption   = "option"
    pretty BSplitc   = "splitc"
    pretty BSprintf  = "sprintf"
    pretty BFloor    = "floor"
    pretty BCeil     = "ceil"
    pretty BMatch    = "match"
    pretty BIxes     = "ices"
    pretty BSome     = "Some"
    pretty BNone     = "None"
    pretty BFp       = "fp"
    pretty BCaptures = "captures"
    pretty BMapMaybe = "mapMaybe"
    pretty BDedupOn  = "dedupOn"
    pretty BFilt     = "filter"
    pretty BFold     = "fold"
    pretty BFold1    = "fold1"
    pretty BScan     = "scan"
    pretty BSub1     = "sub1"
    pretty BSubs     = "subs"
    pretty BHead     = "head#"
    pretty BTail     = "tail#"
    pretty BInit     = "init#"
    pretty BLast     = "last#"
    pretty BTake     = "take#"
    pretty BDrop     = "drop#"
    pretty BRein     = "reintercalate"

data Token a = EOF { loc :: a }
             | TokSym { loc :: a, _sym :: Sym }
             | TokName { loc :: a, _name :: Nm a }
             | TokTyName { loc :: a, _tyName :: TyName a }
             | TokBuiltin { loc :: a, _builtin :: Builtin }
             | TokKeyword { loc :: a, _kw :: Keyword }
             | TokResVar { loc :: a, _var :: Var }
             | TokInt { loc :: a, int :: Integer }
             | TokFloat { loc :: a, float :: Double }
             | TokBool { loc :: a, boolTok :: Bool }
             | TokStr { loc :: a, strTok :: T.Text }
             | TokStreamLit { loc :: a, ix :: Int }
             | TokFieldLit { loc :: a, ix :: Int }
             | TokRR { loc :: a, rr :: T.Text }
             | TokAccess { loc :: a, ix :: Int }
             | TokSelect { loc :: a, field :: Int }
             | TokR { loc :: a, nfield :: Nm a }

instance Pretty (Token a) where
    pretty EOF{}              = "(eof)"
    pretty (TokSym _ s)       = "symbol" <+> squotes (pretty s)
    pretty (TokName _ n)      = "identifier" <+> squotes (pretty n)
    pretty (TokTyName _ tn)   = "identifier" <+> squotes (pretty tn)
    pretty (TokBuiltin _ b)   = "builtin" <+> squotes (pretty b)
    pretty (TokKeyword _ kw)  = "keyword" <+> squotes (pretty kw)
    pretty (TokInt _ i)       = pretty i
    pretty (TokStr _ str)     = squotes (pretty str)
    pretty (TokStreamLit _ i) = "$" <> pretty i
    pretty (TokFieldLit _ i)  = "`" <> pretty i
    pretty (TokRR _ rr')      = "/" <> pretty rr' <> "/"
    pretty (TokResVar _ v)    = "reserved variable" <+> squotes (pretty v)
    pretty (TokBool _ True)   = "#t"
    pretty (TokBool _ False)  = "#f"
    pretty (TokAccess _ i)    = "." <> pretty i
    pretty (TokFloat _ f)     = pretty f
    pretty (TokSelect _ i)    = "->" <> pretty i
    pretty (TokR _ r)         = "->" <> pretty r

freshName :: T.Text -> Alex (Nm AlexPosn)
freshName t = do
    pos <- get_pos
    (i, ns, us) <- alexGetUserState
    let (j, n) = freshIdent pos t i
    alexSetUserState (j, ns, us) $> n

newVarAlex :: T.Text -> Alex (Nm AlexPosn)
newVarAlex t = do {pos <- get_pos; newIdentAlex pos t}

newIdentAlex :: AlexPosn -> T.Text -> Alex (Nm AlexPosn)
newIdentAlex pos t = do
    st <- alexGetUserState
    let (st', n) = newIdent pos t st
    alexSetUserState st' $> n

freshIdent :: AlexPosn -> T.Text -> Int -> (Int, Nm AlexPosn)
freshIdent pos t max' =
    let i=max'+1; nm=Nm t (U i) pos
        in (i, nm)

newIdent :: AlexPosn -> T.Text -> AlexUserState -> (AlexUserState, Nm AlexPosn)
newIdent pos t pre@(max', names, uniqs) =
    case M.lookup t names of
        Just i -> (pre, Nm t (U i) pos)
        Nothing -> let i = max' + 1
            in let newName = Nm t (U i) pos
                in ((i, M.insert t i names, IM.insert i newName uniqs), newName)

runAlexSt :: T.Text -> Alex a -> Either String (AlexUserState, a)
runAlexSt inp = withAlexSt inp alexInitUserState

withAlexSt :: T.Text -> AlexUserState -> Alex a -> Either String (AlexUserState, a)
withAlexSt inp ust (Alex f) = first alex_ust <$> f
    (AlexState { alex_bytes = []
               , alex_pos = alexStartPos
               , alex_inp = inp
               , alex_chr = '\n'
               , alex_ust = ust
               , alex_scd = 0
               })

}