packages feed

language-lustre-1.0.0: Language/Lustre/Parser/Monad.hs

{-# Language OverloadedStrings #-}
module Language.Lustre.Parser.Monad
  ( Parser
  , parseStartingAt
  , parse
  , happyGetToken
  , happyError
  , happyErrorAt
  , ParseError(..)
  ) where

import Control.Monad(liftM,ap)
import Control.Exception (Exception)
import Data.Text(Text)
import AlexTools(prevPos, startPos, range)
import Text.PrettyPrint

import Language.Lustre.Parser.Lexer
import Language.Lustre.Pretty
import Language.Lustre.Panic

newtype Parser a = Parser (PState -> Either ParseError (a, PState))

data PState = PState
  { curToken   :: Maybe (Lexeme Token)
  , nextToknes :: [Lexeme Token]
  }

{-| Run the given parser on the input text. We always try to parse the
whole text, starting at the input, and report an error if there was
left-overs at the end.

The given source position should correspond to the first character in
the text. -}
parseStartingAt ::
  Parser a  {- ^ Describes how to parse the input -} ->
  SourcePos {- ^ Location for the first character in the text -} ->
  Text      {- ^ Parse this text -} ->
  Either ParseError a
parseStartingAt (Parser m) p txt =
  case m s0 of
    Left err -> Left err
    Right (a,sFin) ->
      case nextToknes sFin of
        []    -> Right a
        l : _ -> Left $ ParseError $ lexemeRange l
  where
  s0 = PState { curToken = Nothing, nextToknes = lexer input }

  input = Input { inputPos       = p
                , inputText      = txt
                , inputPrev      = pPos
                , inputPrevChar  =
                    if sourceLine pPos == sourceLine p then ' ' else '\n'
                }
  pPos  = prevPos p

parse :: Parser a {- ^ Describes how to parse -} ->
         Text     {- ^ Name for the input text (e.g., file name) -} ->
         Text     {- ^ The text to parse -} ->
         Either ParseError a
parse p inp = p `parseStartingAt` startPos inp




instance Functor Parser where
  fmap = liftM

instance Applicative Parser where
  pure a = Parser (\ls -> Right (a,ls))
  (<*>)  = ap

instance Monad Parser where
  Parser m >>= k = Parser (\ls -> case m ls of
                                    Left err -> Left err
                                    Right (a,ls1) ->
                                      let Parser m1 = k a
                                      in m1 ls1)

happyGetToken :: (Lexeme Token -> Parser a) -> Parser a
happyGetToken k = Parser $ \s ->
  case nextToknes s of
    []     -> panic "happyGetToken" ["We run out of tokens.", "Missing TokEOF?"]
    t : ts -> let Parser m = k t
              in m PState { curToken = Just t, nextToknes = ts }

newtype ParseError = ParseError SourceRange
                      deriving Show

instance Exception ParseError

happyErrorAt :: SourcePos -> Parser a
happyErrorAt p = Parser (\_ -> Left (ParseError (range p)))

happyError :: Parser a
happyError = Parser $ \s ->
  Left $ ParseError
       $ case curToken s of
           Nothing ->
             case nextToknes s of
               [] -> panic "happyGetToken" ["We run out of tokens.", "Missing TokEOF?"]
               t : _ -> lexemeRange t
           Just t -> lexemeRange t

instance Pretty ParseError where
  ppPrec _ (ParseError x) =
    text (prettySourceRange x ++ ": Parse error.")