packages feed

atomo-0.2.1: src/Atomo/Parser/Pattern.hs

module Atomo.Parser.Pattern where

import Control.Monad (liftM)
import Text.Parsec

import Atomo.Debug
import Atomo.Parser
import Atomo.Parser.Base
import Atomo.Parser.Primitive
import Atomo.Types hiding (keyword)

pPattern :: Parser Pattern
pPattern = choice
    [ try ppNamed
    , try ppHeadTail
    , try ppMatch
    , ppList
    , ppString
    , ppParticle
    , ppExpr
    , ppAny
    , parens pPattern
    ]

pObjectPattern :: Parser Pattern
pObjectPattern = choice
    [ try ppNamedSensitive
    , try ppHeadTail
    , try ppObject
    , try ppMatch
    , ppList
    , ppString
    , ppParticle
    , ppExpr
    , ppAnySensitive
    , parens pObjectPattern
    ]

ppSet :: Parser Pattern
ppSet = try ppDefine <|> pPattern

ppMacro :: Parser Pattern
ppMacro = try ppMacroKeywords <|> ppMacroSingle

ppExpr :: Parser Pattern
ppExpr = do
    q <- pQuasiQuoted
    return (PExpr (eExpr q))

ppMacroSingle :: Parser Pattern
ppMacroSingle = do
    (t, v) <- choice
        [ try $ do
            t <- ppMacroRole
            v <- identifier
            return (t, v)

        , do
            v <- identifier
            return (PAny, v)
        ]

    return $ psingle v t

ppMacroKeywords :: Parser Pattern
ppMacroKeywords = keywords pkeyword PAny ppMacroRole

ppMacroRole :: Parser Pattern
ppMacroRole = choice
    [ ppExpr
    , try $ symbol "Define" >> return PEDefine
    , try $ symbol "Set" >> return PESet
    , try $ symbol "Dispatch" >> return PEDispatch
    , try $ symbol "Operator" >> return PEOperator
    , try $ symbol "Primitive" >> return PEPrimitive
    , try $ symbol "Block" >> return PEBlock
    , try $ symbol "List" >> return PEList
    , try $ symbol "Macro" >> return PEMacro
    , try $ symbol "Particle" >> return PEParticle
    , try $ symbol "Top" >> return PETop
    , try $ symbol "Quote" >> return PEQuote
    , try $ symbol "Unquote" >> return PEUnquote
    , ppAny
    , ppNamedMacro
    ]
  where
    ppNamedMacro = parens $ do
        n <- identifier
        delimit ":"
        p <- ppMacroRole
        return $ PNamed n p

ppDefine :: Parser Pattern
ppDefine = try ppKeywords <|> ppSingle

ppSingle :: Parser Pattern
ppSingle = do
    (t, v) <- choice
        [ try $ do
            t <- pNonExpr
            dump ("got pNonExpr", t)
            v <- identifier
            return (t, v)
        , try $ do
            ds <- pdCascade
            dump ("got pdCascade", ds)
            p <- cInit ds
            v <- cLast ds
            return (PObject p, v)
        , do
            v <- identifier
            return (PObject (ETop Nothing), v)
        ]

    dump ("single", t, v)

    return $ psingle v t
  where
    cLast (Dispatch _ (ESingle _ n _)) = return n
    cLast _ = fail "last target of dispatch chain is not a single messsage"

    cInit (Dispatch _ (ESingle _ _ x)) = return x
    cInit _ = fail "last target of dispatch chain is not a single messsage"

    -- patterns that would otherwise be mistaken for expressions
    -- if the pdCascade pattern were to grab them
    pNonExpr = choice
        [ try ppNamedSensitive
        , try ppHeadTail
        , try ppMatch
        , ppList
        , ppString
        , ppParticle
        , ppExpr
        , ppWildcard
        , parens pNonExpr
        ]


ppKeywords :: Parser Pattern
ppKeywords = keywords pkeyword (PObject (ETop Nothing)) pObjectPattern

ppNamed :: Parser Pattern
ppNamed = parens $ do
    name <- identifier
    delimit ":"
    p <- pPattern
    return $ PNamed name p

ppNamedSensitive :: Parser Pattern
ppNamedSensitive = parens $ do
    name <- lowIdentifier
    dump ("got ppNamedSensitive", name)
    delimit ":"
    p <- pObjectPattern
    dump ("finished ppNamedSensitive", name, p)
    return $ PNamed name p

ppObject :: Parser Pattern
ppObject = choice
    [ try $ parens ppHeadTail
    , do
        p <- name <|> parens pExpr
        return $ PObject p
    ]
  where
    name = do
        lookAhead capIdentifier
        pdCascade

ppAny :: Parser Pattern
ppAny = ppWildcard
    <|> ppNamedAny
  where
    ppNamedAny = do
        name <- identifier
        return (PNamed name PAny)

ppAnySensitive :: Parser Pattern
ppAnySensitive = ppWildcard
    <|> ppNamedAny
  where
    ppNamedAny = do
        name <- lowIdentifier
        return (PNamed name PAny)

ppWildcard :: Parser Pattern
ppWildcard = char '_' >> notFollowedBy identifier >> spacing >> return PAny

ppMatch :: Parser Pattern
ppMatch = do
    v <- pPrim
    return $ PMatch v

ppHeadTail :: Parser Pattern
ppHeadTail = parens subHT
  where
    subHT = do
        h <- pHead
        dot
        t <- try subHT <|> pPattern
        return $ PHeadTail h t
    pHead = choice
        [ try ppNamed
        , try ppMatch
        , ppList
        , ppString
        , ppParticle
        , ppAny
        , parens pHead
        ]

ppList :: Parser Pattern
ppList = brackets $ do
    ps <- commaSep pPattern
    return $ PList ps

ppString :: Parser Pattern
ppString = do
    cs <- stringLiteral
    return $ PList (map (PMatch . Char) cs)

ppParticle :: Parser Pattern
ppParticle = do
    char '@'
    try keywordParticle <|> singleParticle
  where
    singleParticle = liftM (PMatch . Particle . PMSingle) anyIdentifier

    keywordParticle = choice
        [ parens $ do
            ks <- many1 (keyword pPattern)
            let (ns, ps) = unzip ks
            return $ PPMKeyword ns (PAny:ps)
        , do
            o <- operator
            spacing
            return $ PPMKeyword [o] [PAny, PAny]
        , do
            names <- many1 (anyIdent >>= \n -> char ':' >> return n)
            spacing
            return $ PPMKeyword names (replicate (length names + 1) PAny) 
        ]