packages feed

spade-0.1.0.0: src/Compiler/AST/Common.hs

module Compiler.AST.Common where

import Common
import Control.Applicative
import Control.Monad
import Data.List.NonEmpty as NE
import Data.Text

import Compiler.AST.Parser.Common
import Compiler.Lexer
import Compiler.Lexer.Comments
import Parser
import Test.Common

parseComment :: AstParser Comment
parseComment = parseToken "comment" (\case
  TkComment c -> Just c
  _           -> Nothing)

parseToken :: Text -> (TokenRaw -> Maybe a) -> AstParser a
parseToken name fn = ParserM name $ liftModifier $ \s -> case s of
  ((Token tr _ _) : rst) -> pure $ case fn tr of
    Just a  -> (Right a, rst)
    Nothing -> (Left CantHandle, s)
  _ -> pure (Left CantHandle, s)

parseIdentifier :: AstParser Identifier
parseIdentifier =  parseToken "Identifier" (\case
  TkIdentifier i -> Just i
  _              -> Nothing)

parseDelimeter :: Delimeter -> AstParser Delimeter
parseDelimeter dl =  parseToken (toSource dl) (\case
  TkDelimeter l -> if l == dl then Just l else Nothing
  _             -> Nothing)

parseKeyword :: Keyword -> AstParser Keyword
parseKeyword kw =  parseToken (toSource kw) (\case
  TkKeyword k -> if kw == k then Just k else Nothing
  _           -> Nothing)

parseOperator :: AstParser Operator
parseOperator = parseToken "Operator" (\case
  TkOperator k -> Just k
  _            -> Nothing)

parseOperator' :: Operator -> AstParser ()
parseOperator' op = parseToken "Operator" (\case
  TkOperator k -> if k == op then Just () else Nothing
  _            -> Nothing)

mandatoryNewline :: AstParser ()
mandatoryNewline = parseToken "NL" (\case
  TkWhitespace (NewLine _) -> Just ()
  _                        -> Nothing)

whitespace :: AstParser ()
whitespace = void $ optional $ parseToken "WS" (\case
  TkWhitespace (Space _) -> Just ()
  TkWhitespace (Tab _)   -> Just ()
  _                      -> Nothing)

whitespaceNL :: AstParser ()
whitespaceNL = void $ optional $ parseToken "NL" (\case
  TkWhitespace (NewLine _) -> Just ()
  _                        -> Nothing)

surroundWs :: AstParser a -> AstParser a
surroundWs p@(ParserM name _) = nameParser (name <> " surrounded by WS or NL ") $ do
  whitespaceOrNl
  a <- p
  whitespaceOrNl
  pure a

whitespaceOrNl :: AstParser ()
whitespaceOrNl = void $ many $ parseToken "WS" (\case
  TkWhitespace (Space _)   -> Just ()
  TkWhitespace (Tab _)     -> Just ()
  TkWhitespace (NewLine _) -> Just ()
  _                        -> Nothing)

surroundWs_ :: AstParser a -> AstParser ()
surroundWs_ p@(ParserM name _) = nameParser (name <> " surrounded by WS") $ void $ surroundWs p

mandatory :: AstParser a -> AstParser a
mandatory (ParserM name fn) = ParserM ("mandatory " <> name) $ \s -> do
  case (astInput s) of
    [] -> pure (Left $ FatalError $ CustomError $ "Expecting " <> name <> " but ran out of input ", s)
    _ -> fn s  >>= \case
        r@(Right _, _) -> pure r
        (Left fe@(FatalError _), _)    ->  pure (Left fe, s)
        (Left fe@(FatalErrorWithLocation _ _), _)    ->  pure (Left fe, s)
        (Left e, s'') -> case astInput s'' of
          [] -> pure (Left e, s)
          (h: _) -> pure (Left $ FatalErrorWithLocation (tkLocation h) $ CustomError $ "Expecting " <> name <> " but got " <> (pack $ show h), s)

nonEmptyGen :: Gen a -> Gen (NonEmpty a)
nonEmptyGen p = do h <- p; t <- list (linear 0 3) p; pure (h :| t)

wst :: Text
wst = toSource $ TkWhitespace (Space 1)

nlt :: Text
nlt = toSource $ TkWhitespace (NewLine 1)

parseItemListInParen :: AstParser a -> AstParser (Maybe (NonEmpty a))
parseItemListInParen itemParser = do
  surroundWs_ (parseDelimeter DlParenOpen)
  args <- optional itemParser >>= \case
    Just argHead -> do
      argsTail <- many $ do
        surroundWs_ $ parseDelimeter DlComma
        itemParser
      pure $ Just (argHead :| argsTail)
    Nothing -> pure Nothing
  surroundWs_ (parseDelimeter DlParenClose)
  pure args