packages feed

glualint-1.24.6: src/GLua/Parser.hs

{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE Rank2Types #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE NoMonomorphismRestriction #-}
{-# LANGUAGE CPP #-}
{-# LANGUAGE LambdaCase #-}

-- | Parser based on <http://www.lua.org/manual/5.2/manual.html#9>
module GLua.Parser where

import GLua.TokenTypes
import GLua.AG.Token
import GLua.AG.AST
import qualified GLua.Lexer as Lex

import Text.ParserCombinators.UU
import Text.ParserCombinators.UU.BasicInstances

-- | MTokens with the positions of the next MToken (used in the advance of parser)
data MTokenPos = MTokenPos MToken Region

data RegionProgression =
  RegionProgression {lastRegion :: Region, nextRegion :: Region}
  deriving (Show)

emptyRgPrgs :: RegionProgression
emptyRgPrgs = RegionProgression emptyRg emptyRg

instance Show MTokenPos where
  show (MTokenPos tok _) = show tok

-- | Custom parser that parses MTokens
type AParser a = P (Str MTokenPos [MTokenPos] RegionProgression) a

-- | RegionProgression is a location that can be updated by MTokens
instance IsLocationUpdatedBy RegionProgression MTokenPos where
    -- advance :: RegionProgression -> MToken -> RegionProgression
    -- Assume the position of the next MToken
    advance _ (MTokenPos mt p) = RegionProgression (mpos mt) p

resultsToRegion :: (a, [Error RegionProgression]) -> (a, [Error Region])
resultsToRegion (a, errs) = (a, map errorToRegion errs)

-- | Parse Garry's mod Lua tokens to an abstract syntax tree.
-- Also returns parse errors
parseGLua :: [MToken] -> (AST, [Error Region])
parseGLua mts =
  let
    (cms, ts) = splitComments . filter (not . isWhitespace) $ mts
  in
    resultsToRegion $ execAParser (parseChunk cms) ts

-- | Parse a string directly into an AST
parseGLuaFromString :: String -> (AST, [Error Region])
parseGLuaFromString = parseGLua . filter (not . isWhitespace) . fst . Lex.execParseTokens

-- | Parse a string directly
parseFromString :: AParser a -> String -> (a, [Error Region])
parseFromString p = resultsToRegion . execAParser p . filter (not . isWhitespace) . fst . Lex.execParseTokens

-- | Create a parsable string from MTokens
createString :: [MToken] -> Str MTokenPos [MTokenPos] RegionProgression
createString [] = createStr emptyRgPrgs []
createString mts@(MToken p _ : xs) = createStr (RegionProgression p (nextRg mts')) mtpos where
    mts' = xs ++ [last mts] -- Repeat last element of mts
    mkMtPos mt (MToken p' _) = MTokenPos mt p'
    mtpos = zipWith mkMtPos mts mts'

    nextRg (MToken p' _ : _) = p'
    nextRg [] = undefined


errorToRegion :: Error RegionProgression -> Error Region
errorToRegion (Inserted a p b) = Inserted a (nextRegion p) b
errorToRegion (Deleted a p b) = Deleted a (nextRegion p) b
errorToRegion (Replaced a b p c) = Replaced a b (nextRegion p) c
errorToRegion (DeletedAtEnd s) = DeletedAtEnd s

-- | Position in Region (as opposed to RegionProgression)
pPos' :: AParser Region
pPos' = nextRegion <$> pPos

-- | Text.ParserCombinators.UU.Utils.execParser modified to parse MTokens
-- The first MToken might not be on the first line, so use the first MToken's position to start
execAParser :: AParser a -> [MToken] -> (a, [Error RegionProgression])
execAParser p mts@[] = parse_h ((,) <$> p <*> pEnd) . createString $ mts
execAParser p mts@(_ : _) = parse_h ((,) <$> p <*> pEnd) . createString $ mts -- createStr (mpos m) $ mts


pMSatisfy :: (MToken -> Bool) -> Token -> String -> AParser MToken
pMSatisfy f t ins = getToken <$> pSatisfy f' (Insertion ins (MTokenPos (MToken ep t) ep) 5) where
    f' :: MTokenPos -> Bool
    f' (MTokenPos tok _) = f tok

    getToken :: MTokenPos -> MToken
    getToken (MTokenPos t' _) = t'

    ep = Region (LineColPos 0 0 0) (LineColPos 0 0 0)

-- | Parse a single Metatoken, based on a positionless token (much like pSym)
pMTok :: Token -> AParser MToken
pMTok t = pMSatisfy isToken t $ "'" ++ show t ++ "'"
    where
        isToken :: MToken -> Bool
        isToken (MToken _ tok) = t == tok

-- | Parse a list of identifiers
parseNameList :: AParser [MToken]
parseNameList = (:) <$> pName <*> pMany (pMTok Comma *> pName)

-- | Parse list of function parameters
parseParList :: AParser [MToken]
parseParList = (pMTok VarArg <<|> pName) <**> (
                    pMTok Comma <**> (
                        (\a _ c -> [c, a]) <$> pMTok VarArg <<|>
                        (\a _ c -> c : a)  <$> parseParList
                    ) `opt` (: [])
               ) `opt` []

-- | Parses the full AST
-- Its first parameter contains all comments
-- Assumes the mtokens fed to the AParser have no comments
parseChunk :: [MToken] -> AParser AST
parseChunk cms = AST cms <$> parseBlock

-- | Parse a block with an optional return value
parseBlock :: AParser Block
parseBlock = Block <$> pInterleaved (pMTok Semicolon) parseMStat <*> (parseReturn <<|> pReturn NoReturn)


-- | A thing of which the region is to be parsed
annotated :: (Region -> a -> b) -> AParser a -> AParser b
annotated f p = (\s t e -> f (Region (rgStart s) (rgEnd $ lastRegion $ pos $ e)) t) <$> pPos' <*> p <*> pState

parseMStat :: AParser MStat
parseMStat = annotated MStat parseStat

-- | Parser that is interleaved with 0 or more of the other parser
pInterleaved :: AParser a -> AParser b -> AParser [b]
pInterleaved sep q = pMany sep *> pMany (q <* pMany sep)

-- | Behemoth parser that parses either function call statements or global declaration statements
-- Big in size and complexity because prefix expressions are BITCHES
-- The problem lies in the complexity of prefix expressions:
-- hotten.totten["tenten"](tentoonstelling) -- This is a function call
-- hotten.totten["tenten"] = tentoonstelling -- This is a declaration.
-- hotten.totten["tenten"], tentoonstelling = 1, 2 -- This is also a declaration.
-- One may find an arbitrary amount of expression suffixes (indexations/calls) before
-- finding a comma or equals sign that proves that it is a declaration.
-- Also, goto can be an identifier
parseCallDef :: AParser Stat
parseCallDef = parseGoto <<|>
               (PFVar <$> pName <<|> -- Statemens begin with either a simple name or parenthesised expression
                ExprVar <$ pMTok LRound <*> parseExpression <* pMTok RRound) <**> (
                  -- Either there are more suffixes yet to be found (contSearch)
                  -- or there aren't and we will find either a comma or =-sign (varDecl namedVarDecl)
                  contSearch <<|>
                  varDecl namedVarDecl
                )
  where
    -- Try to parse a goto statement
    parseGoto :: AParser Stat
    parseGoto = (PFVar <$> pMTok (Identifier "goto")) <**>
                  ((\n _ -> AGoto n) <$> pName <<|>
                    contSearch <<|>
                    varDecl namedVarDecl)

    -- Simple direct declaration: varName, ... = 1, ...
    namedVarDecl :: [PrefixExp] -> [MExpr] -> (ExprSuffixList -> PrefixExp) -> Stat
    namedVarDecl vars exprs pfe = let pfes = (pfe []) : vars in Def (zip pfes $ map Just exprs ++ repeat Nothing)

    -- This is where we know it's a variable declaration
    -- Takes a function that turns it into a proper Def Stat
    varDecl :: ([PrefixExp] -> [MExpr] -> b) -> AParser b
    varDecl f = f <$> opt (pMTok Comma *> parseVarList) [] <*
              pMTok Equals <*>
              parseExpressionList

    -- We know that there is at least one suffix (indexation or call).
    -- Search for more suffixes and make either a call or declaration from it
    contSearch :: AParser ((ExprSuffixList -> PrefixExp) -> Stat)
    contSearch = (\(ss, mkStat) pfe -> mkStat $ pfe ss) <$> searchDeeper

    -- We either find a call suffix or an indexation suffix
    -- When it's a function call, try searching for more suffixes, if that doesn't work, it's a function call.
    -- When it's an indexation suffix, search for more suffixes or know that it's a declaration.
    searchDeeper :: AParser ([PFExprSuffix], PrefixExp -> Stat)
    searchDeeper = (pPFExprCallSuffix <**> (mergeDeeperSearch <$> searchDeeper <<|> pReturn (\s -> ([s], AFuncCall)))) <<|>
                   (pPFExprIndexSuffix <**> (mergeDeeperSearch <$> searchDeeper <<|> varDecl complexDecl))

    -- Merge the finding of more suffixes with the currently found suffix
    mergeDeeperSearch :: ([PFExprSuffix], PrefixExp -> Stat) -> PFExprSuffix -> ([PFExprSuffix], PrefixExp -> Stat)
    mergeDeeperSearch (ss, f) s = (s : ss, f)

    -- Multiple suffixes have been found, and proof has been found that this must be a declaration.
    -- Now to give all the collected suffixes and a function that creates the declaration
    complexDecl :: [PrefixExp] -> [MExpr] -> PFExprSuffix -> ([PFExprSuffix], PrefixExp -> Stat)
    complexDecl vars exprs s = ([s], \pf -> Def (zip (pf : vars) $ map Just exprs ++ repeat Nothing))


-- | Parse a single statement
parseStat :: AParser Stat
parseStat = parseCallDef <<|>
            ALabel <$> parseLabel <<|>
            ABreak <$ pMTok Break <<|>
            AContinue <$ pMTok Continue <<|>
            --AGoto <$ pMTok (Identifier "goto") <*> pName <|>
            ADo <$ pMTok Do <*> parseBlock <* pMTok End <<|>
            AWhile <$ pMTok While <*> parseExpression <* pMTok Do <*> parseBlock <* pMTok End <<|>
            ARepeat <$ pMTok Repeat <*> parseBlock <* pMTok Until <*> parseExpression <<|>
            parseIf <<|>
            parseFor <<|>
            AFunc <$ pMTok Function <*> parseFuncName <*>
             pPacked (pMTok LRound) (pMTok RRound) parseParList <*>
             parseBlock <*
             pMTok End <<|>
            -- local function and local vars both begin with "local"
            pMTok Local <**> (
              -- local function
              (\n p b _l -> ALocFunc n p b) <$
                pMTok Function <*> parseLocFuncName <*> pPacked (pMTok LRound) (pMTok RRound) parseParList <*>
                parseBlock <* pMTok End <<|>
              -- local variables
              (\v (_p, e) _l -> LocDef (zip v $ map Just e ++ repeat Nothing)) <$> parseLocalVarList <*> ((,) <$ pMTok Equals <*> pPos' <*> parseExpressionList <<|> (,) <$> pPos' <*> pReturn [])
            )


-- | Parse if then elseif then else end expressions
parseIf :: AParser Stat
parseIf = AIf <$ pMTok If <*> parseExpression <* pMTok Then <*>
            parseBlock <*>
            -- elseif
            pMany (annotated MElseIf $ (,) <$ pMTok Elseif <*> parseExpression <* pMTok Then <*> parseBlock) <*>
            -- else
            optional (annotated MElse $ pMTok Else *> parseBlock) <*
            pMTok End

-- | Parse numeric and generic for loops
parseFor :: AParser Stat
parseFor = do
  pMTok For
  firstName <- pName
  -- If you see an =-sign, it's a numeric for loop. It'll be a generic for loop otherwise
  isNumericLoop <- (const True <$> pMTok Equals <<|> const False <$> pReturn ())
  if isNumericLoop then do
      startExp <- parseExpression
      pMTok Comma
      toExp <- parseExpression
      step <- pMTok Comma *> parseExpression <<|> MExpr <$> pPos' <*> pReturn (ANumber "1")
      pMTok Do
      block <- parseBlock
      pMTok End
      pReturn $ ANFor firstName startExp toExp step block
  else do
     vars <- (:) firstName <$ pMTok Comma <*> parseNameList <<|> pReturn [firstName]
     pMTok In
     exprs <- parseExpressionList
     pMTok Do
     block <- parseBlock
     pMTok End
     pReturn $ AGFor vars exprs block


-- | Parse a return value
parseReturn :: AParser AReturn
parseReturn = AReturn <$> pPos' <* pMTok Return <*> opt parseExpressionList [] <* pMany (pMTok Semicolon)

-- | Label
parseLabel :: AParser MToken
parseLabel = pMSatisfy isLabel (Label "someLabel") "Some label"
    where
        isLabel :: MToken -> Bool
        isLabel (MToken _ (Label _)) = True
        isLabel _ = False

-- | Function name (includes dot indices and meta indices)
parseFuncName :: AParser FuncName
parseFuncName = (\a b c -> FuncName (a:b) c) <$> pName <*> pMany (pMTok Dot *> pName) <*>
                opt (Just <$ pMTok Colon <*> pName) Nothing

-- | Local function name. Does not include dot and meta indices, since they're not allowed in meta functions
parseLocFuncName :: AParser FuncName
parseLocFuncName = (\a -> FuncName [a] Nothing) <$> pName

-- | Parse a number into an expression
parseNumber :: AParser Expr
parseNumber = toAnumber <$> pMSatisfy isNumber (TNumber "0") "Number"
    where
        isNumber :: MToken -> Bool
        isNumber (MToken _ (TNumber _)) = True
        isNumber _ = False

        -- A better solution would be to have a single `MToken -> Maybe Expr` function, but I am too
        -- lazy to write that.
        toAnumber :: MToken -> Expr
        toAnumber = \case
          (MToken _ (TNumber str)) -> ANumber str
          _ -> error "unreachable"

-- | Parse any kind of string
parseString :: AParser MToken
parseString = pMSatisfy isString (DQString "someString") "String"
    where
        isString :: MToken -> Bool
        isString (MToken _ (DQString _)) = True
        isString (MToken _ (SQString _)) = True
        isString (MToken _ (MLString _)) = True
        isString _ = False

-- | Parse an identifier
pName :: AParser MToken
pName = pMSatisfy isName (Identifier "someVariable") "Variable"
    where
        isName :: MToken -> Bool
        isName (MToken _ (Identifier _)) = True
        isName _ = False

-- | Parse variable list (var1, var2, var3)
parseVarList :: AParser [PrefixExp]
parseVarList = pList1Sep (pMTok Comma) parseVar

-- | Parse local variable list (var1, var2, var3), without suffixes
parseLocalVarList :: AParser [PrefixExp]
parseLocalVarList = pList1Sep (pMTok Comma) (PFVar <$> pName <*> pure [])

-- | list of expressions
parseExpressionList :: AParser [MExpr]
parseExpressionList = pList1Sep (pMTok Comma) parseExpression

-- | Subexpressions, i.e. without operators
parseSubExpression :: AParser Expr
parseSubExpression = ANil <$ pMTok Nil <<|>
                  AFalse <$ pMTok TFalse <<|>
                  ATrue <$ pMTok TTrue <<|>
                  parseNumber <<|>
                  AString <$> parseString <<|>
                  AVarArg <$ pMTok VarArg <<|>
                  parseAnonymFunc <<|>
                  APrefixExpr <$> parsePrefixExp <<|>
                  ATableConstructor <$> parseTableConstructor

-- | Separate parser for anonymous function subexpression
parseAnonymFunc :: AParser Expr
parseAnonymFunc = AnonymousFunc <$
                   pMTok Function <*>
                   pPacked (pMTok LRound) (pMTok RRound) parseParList <*>
                   parseBlock <*
                   pMTok End

-- | Parse operators of the same precedence in a chain
samePrioL :: [(Token, BinOp)] -> AParser MExpr -> AParser MExpr
samePrioL ops pr = pChainl (choice (map f ops)) pr
  where
    choice = foldr (<<|>) pFail
    f :: (Token, BinOp) -> AParser (MExpr -> MExpr -> MExpr)
    f (t, at) = (\p e1 e2 -> MExpr p (BinOpExpr at e1 e2)) <$> pPos' <* pMTok t

samePrioR :: [(Token, BinOp)] -> AParser MExpr -> AParser MExpr
samePrioR ops pr = pChainr (choice (map f ops)) pr
  where
    choice = foldr (<<|>) pFail
    f :: (Token, BinOp) -> AParser (MExpr -> MExpr -> MExpr)
    f (t, at) = (\p e1 e2 -> MExpr p (BinOpExpr at e1 e2)) <$> pPos' <* pMTok t

-- | Parse unary operator (-, not, #)
parseUnOp :: AParser UnOp
parseUnOp = UnMinus <$ pMTok Minus <<|>
            ANot    <$ pMTok Not   <<|>
            ANot    <$ pMTok CNot  <<|>
            AHash   <$ pMTok Hash

-- | Operators, sorted by priority
-- Priority from: http://www.lua.org/manual/5.2/manual.html#3.4.7
lvl1, lvl2, lvl3, lvl4, lvl5, lvl6, lvl8 :: [(Token, BinOp)]
lvl1 = [(Or, AOr), (COr, AOr)]
lvl2 = [(And, AAnd), (CAnd, AAnd)]
lvl3 = [(TLT, ALT), (TGT, AGT), (TLEQ, ALEQ), (TGEQ, AGEQ), (TNEq, ANEq), (TCNEq, ANEq), (TEq, AEq)]
lvl4 = [(Concatenate, AConcatenate)]
lvl5 = [(Plus, APlus), (Minus, BinMinus)]
lvl6 = [(Multiply, AMultiply), (Divide, ADivide), (Modulus, AModulus)]
-- lvl7 is unary operators
lvl8 = [(Power, APower)]


-- | Parse chains of binary and unary operators
parseExpression :: AParser MExpr
parseExpression = samePrioL lvl1 $
                  samePrioL lvl2 $
                  samePrioL lvl3 $
                  samePrioR lvl4 $
                  samePrioL lvl5 $
                  samePrioL lvl6 $
                  MExpr <$> pPos' <*> (UnOpExpr <$> parseUnOp <*> parseExpression) <<|> -- lvl7
                  samePrioR lvl8 (MExpr <$> pPos' <*> (parseSubExpression <|> UnOpExpr <$> parseUnOp <*> parseExpression))

-- | Parses a binary operator
parseBinOp :: AParser BinOp
parseBinOp = const AOr          <$> pMTok Or           <<|>
             const AOr          <$> pMTok COr          <<|>
             const AAnd         <$> pMTok And          <<|>
             const AAnd         <$> pMTok CAnd         <<|>
             const ALT          <$> pMTok TLT          <<|>
             const AGT          <$> pMTok TGT          <<|>
             const ALEQ         <$> pMTok TLEQ         <<|>
             const AGEQ         <$> pMTok TGEQ         <<|>
             const ANEq         <$> pMTok TNEq         <<|>
             const ANEq         <$> pMTok TCNEq        <<|>
             const AEq          <$> pMTok TEq          <<|>
             const AConcatenate <$> pMTok Concatenate  <<|>
             const APlus        <$> pMTok Plus         <<|>
             const BinMinus     <$> pMTok Minus        <<|>
             const AMultiply    <$> pMTok Multiply     <<|>
             const ADivide      <$> pMTok Divide       <<|>
             const AModulus     <$> pMTok Modulus      <<|>
             const APower       <$> pMTok Power


-- | Prefix expressions
-- can have any arbitrary list of expression suffixes
parsePrefixExp :: AParser PrefixExp
parsePrefixExp = pPrefixExp (pMany pPFExprSuffix)

-- | Prefix expressions
-- The suffixes define rules on the allowed suffixes
pPrefixExp :: AParser [PFExprSuffix] -> AParser PrefixExp
pPrefixExp suffixes = PFVar <$> pName <*> suffixes <<|>
                      ExprVar <$ pMTok LRound <*> parseExpression <* pMTok RRound <*> suffixes

-- | Parse any expression suffix
pPFExprSuffix :: AParser PFExprSuffix
pPFExprSuffix = pPFExprCallSuffix <<|> pPFExprIndexSuffix

-- | Parse an indexing expression suffix
pPFExprCallSuffix :: AParser PFExprSuffix
pPFExprCallSuffix = Call <$> parseArgs <<|>
                    MetaCall <$ pMTok Colon <*> pName <*> parseArgs

-- | Parse an indexing expression suffix
pPFExprIndexSuffix :: AParser PFExprSuffix
pPFExprIndexSuffix = ExprIndex <$ pMTok LSquare <*> parseExpression <* pMTok RSquare <<|>
                     DotIndex <$ pMTok Dot <*> pName

-- | Function calls are prefix expressions, but the last suffix MUST be either a function call or a metafunction call
pFunctionCall :: AParser PrefixExp
pFunctionCall = pPrefixExp suffixes
    where
        suffixes = concat <$> pSome ((\ix c -> ix ++ [c]) <$> pSome pPFExprIndexSuffix <*> pPFExprCallSuffix <<|>
                                     (:[])                <$> pPFExprCallSuffix)

-- | single variable. Note: definition differs from reference to circumvent the left recursion
-- var ::= Name [{PFExprSuffix}* indexation] | '(' exp ')' {PFExprSuffix}* indexation
-- where "{PFExprSuffix}* indexation" is any arbitrary sequence of prefix expression suffixes that end with an indexation
parseVar :: AParser PrefixExp
parseVar = pPrefixExp suffixes
    where
        suffixes = concat <$> pMany ((\c ix -> c ++ [ix]) <$> pSome pPFExprCallSuffix <*> pPFExprIndexSuffix <<|>
                                     (:[])                <$> pPFExprIndexSuffix)

-- | Arguments of a function call (including brackets)
parseArgs :: AParser Args
parseArgs = ListArgs <$ pMTok LRound <*> opt parseExpressionList [] <* pMTok RRound <<|>
            TableArg <$> parseTableConstructor <<|>
            StringArg <$> parseString

-- | Table constructor
parseTableConstructor :: AParser [Field]
parseTableConstructor = pMTok LCurly *> parseFieldList <* pMTok RCurly

-- | A list of table entries
-- Grammar: field {separator field} [separator]
parseFieldList :: AParser [Field]
parseFieldList =
    parseField <**>
      ( parseFieldSep <**>
          ((\rest sep field -> field sep : rest) <$> (parseFieldList <<|> pure [])) <<|>
        pure (\field -> [field NoSep])
      ) <<|>
    pure []

-- | Makes an unnamed field out of a list of suffixes, a position and a name.
-- This function gets called when we know a field is unnamed and contains an expression that
-- starts with a PrefixExp
-- See the parseField parser where it is used
makeUnNamedField :: Maybe (BinOp, MExpr) -> ExprSuffixList -> (Region, MToken) -> (FieldSep -> Field)
makeUnNamedField Nothing sfs (p, nm) = UnnamedField $ MExpr p $ APrefixExpr $ PFVar nm sfs
makeUnNamedField (Just (op, mexpr)) sfs (p, nm) = UnnamedField $ MExpr p $ (merge (APrefixExpr $ PFVar nm sfs) mexpr)
  where
    -- Merge the first prefixExpr into the expression tree
    merge :: Expr -> MExpr -> Expr
    merge pf e@(MExpr _ (BinOpExpr op' l r)) =  if op > op' then
                                                  BinOpExpr op' (MExpr p $ (merge pf l)) r
                                                else
                                                  BinOpExpr op (MExpr p pf) e
    merge pf e = BinOpExpr op (MExpr p pf) e

-- | A field in a table
parseField :: AParser (FieldSep -> Field)
parseField = ExprField <$ pMTok LSquare <*> parseExpression <* pMTok RSquare <* pMTok Equals <*> parseExpression <<|>
              ((,) <$> pPos' <*> pName <**>
                -- Named field has equals sign immediately after the name
                (((\e (_, n) -> NamedField n e) <$ pMTok Equals <*> parseExpression) <<|>

                -- The lack of equals sign means it's an unnamed field.
                -- The expression of the unnamed field must be starting with a PFVar Prefix expression
                pMany pPFExprSuffix <**>
                  ( makeUnNamedField <$>
                    (
                      -- There are operators, so the expression goes on beyond the prefixExpression
                      curry Just <$> parseBinOp <*> parseExpression <<|>
                      -- There are no operators after the prefix expression
                      pReturn Nothing
                    )
                  )
                )
              ) <<|>
              UnnamedField <$> parseExpression

-- | Field separator
parseFieldSep :: AParser FieldSep
parseFieldSep =
    CommaSep <$ pMTok Comma <<|>
    SemicolonSep <$ pMTok Semicolon