packages feed

bibdb-0.3.0: src/Parser/Parser.y

-----------------------------------------------------------------------------
-- |
-- Module      : Parser.Parser
-- Description : Parser for bibdb files
-- Maintainer  : coskuacay@gmail.com
-- Stability   : experimental
--
-- This module provides a parser for bibdb files that is intended to be
-- used with an Alex generated parser.
-----------------------------------------------------------------------------
{
module Parser.Parser
  ( -- * Parsers
    Parser
  , Entry (..)
  , runParser
  , fileParser
  , tokenParser
    -- * Lexemes
  , Token
  , Lexeme
  , token
  ) where

import Control.Monad (liftM)

import Data.ByteString.Lazy.Char8 (ByteString)
import Text.PrettyPrint
import Text.PrettyPrint.HughesPJClass (Pretty (..))

import Parser.Location ( SrcSpan, Located (..), mergeLocated
                       , Loc, makeLoc, unLoc
                       )
import Parser.Token
import Parser.Lexer
import Reference
}

%name fileParser1 File
%name tokenParser1 Tokens

%monad { Alex }
%lexer { lexer } { Lexeme _ TEof }
%tokentype { Lexeme }
%error { parseError }


%token
  -- Keywords
  as           { Lexeme _ TAs }
  ':'          { Lexeme _ TColon }
  stype        { Lexeme _ (TType _) }
  ident        { Lexeme _ (TIdent _) }


%%

{--------------------------------------------------------------------------
    Helper production rules
--------------------------------------------------------------------------}

-- An optional production
Opt(p) : p               { Just $1 }
       | {- empty -}     { Nothing }

-- Empty production
Empty : {- empty -}      { () }

-- A possibly empty list of 'p's separated by 'sep's
ListSep(p, sep) : ListSep1(p, sep)    { $1 }
                | {- empty -}         { [] }

-- A list of 'p's separated by 'sep's
ListSep1(p, sep) : ListSep1R(p, sep)  { reverse $1 }

-- A list of 'p's separated by 'sep's in reverse order
ListSep1R(p, sep) : ListSep1R(p, sep) sep p  { $3 : $1 }
                  | p                        { [$1] }

-- A list of 'p's with no separators
List(p) : ListSep(p, Empty)                 { $1 }


{--------------------------------------------------------------------------
  Token Parser
--------------------------------------------------------------------------}


Tokens :: { [Lexeme] }
Tokens : List(Token) { $1 }

Token :: { Lexeme }
Token : stype        { $1 }
      | ident        { $1 }
      | ':'          { $1 }
      | as           { $1 }


{--------------------------------------------------------------------------
  Entry Parser
--------------------------------------------------------------------------}

Entry :: { Entry }
Entry : Source              { Entry (location $1) (fst $ unLoc $1) (snd $ unLoc $1) }
      | Source as Ident     { Entry (mergeLocated $1 $3) (fst $ unLoc $1) (unLoc $3) }


Source :: { Loc (Source, RefIdent) }
Source : stype ':' Key      { % parseSource (mergeLocated $1 $3) $1 (unLoc $3) }

Key :: { Loc SourceKey }
Key : ident                 { makeLoc (location $1) $ SourceKey $ (\(TIdent id) -> id) (token $1) }

Ident :: { Loc RefIdent }
Ident : ident               { makeLoc (location $1) $ RefIdent $ (\(TIdent id) -> id) (token $1) }


{--------------------------------------------------------------------------
    Top level
--------------------------------------------------------------------------}

File :: { [Entry] }
File : List(Entry)    { $1 }


{

{--------------------------------------------------------------------------
  Interface
--------------------------------------------------------------------------}

-- | A monadic parser
type Parser t = Alex t

-- | Run a 'Parser' on the given 'ByteString'
runParser :: ByteString -> Parser t -> Either String t
runParser = runAlex

-- | Parser for a bibdb file
fileParser :: FilePath -> Parser [Entry]
fileParser path = setSrcFile path >> fileParser1

-- | Parse tokens only and return a list of lexemes. Mainly used for debugging.
tokenParser :: FilePath -> Parser [Lexeme]
tokenParser path = setSrcFile path >> tokenParser1


{--------------------------------------------------------------------------
  Entry
--------------------------------------------------------------------------}

data Entry = Entry
  { entryLoc    :: SrcSpan
  , entrySource :: Source
  , entryIdent  :: RefIdent
  }


instance Located Entry where
  location (Entry loc _ _) = loc

instance Pretty Entry where
  pPrint (Entry _ s id) = pPrint s <+> text "as" <+> pPrint id


{--------------------------------------------------------------------------
  Parser Helpers
--------------------------------------------------------------------------}

parseSource :: SrcSpan -> Lexeme -> SourceKey -> Alex (Loc (Source, RefIdent))
parseSource loc (Lexeme _ (TType t)) key = do
  t' <- parseType t
  let ident = t ++ ":" ++ (\(SourceKey k) -> k) key
  return $ makeLoc loc (Source t' key, RefIdent ident)
parseSource loc _ key = undefined

parseType :: String -> Alex SourceType
parseType "DBLP" = return Dblp
parseType "dblp" = return Dblp
parseType "CiteSeerX" = return CiteSeerX
parseType "DOI" = return Doi
parseType "doi" = return Doi
parseType t = lexError $ "invalid source type" ++ t


{--------------------------------------------------------------------------
  Happy related
--------------------------------------------------------------------------}

parseError :: Lexeme -> Alex a
parseError l = lexError $ "parse error on " ++ show (token l)

}