-----------------------------------------------------------------------------
Abstract syntax for grammar files.

(c) 1993-2001 Andy Gill, Simon Marlow
-----------------------------------------------------------------------------

Here is the abstract syntax of the language we parse.

> module Happy.Frontend.AbsSyn (
>       BookendedAbsSyn(..),
>       AbsSyn(..), Directive(..),
>       getTokenType, getTokenSpec, getParserNames, getLexer,
>       getImportedIdentity, getMonad, ErrorHandlerInfo(..), getError,
>       getPrios, getPrioNames, getExpect, getErrorExpectedMode,
>       getAttributes, getAttributetype, getAttributeGrammarExtras,
>       parseTokenSpec,
>       Rule(..), Prod(..), Term(..), Prec(..),
>       TokenSpec(..) -- reexport
>  ) where

> import Data.Char (isAlphaNum)
> import Happy.Grammar
> import Happy.Grammar.ExpressionWithHole

> data BookendedAbsSyn
>     = BookendedAbsSyn
>         (Maybe String)       -- header
>         (AbsSyn String)
>         (Maybe String)       -- footer

> data AbsSyn e
>     = AbsSyn
>         [Directive String]   -- directives
>         [Rule e]             -- productions

> data Rule e
>     = Rule
>         String               -- name of the rule
>         [String]             -- parameters (see parametrized productions)
>         [Prod e]             -- productions
>         (Maybe String)       -- type of the rule

> data Prod e
>     = Prod
>         [Term]               -- terms that make up the rule
>         e                    -- code body that runs when the rule reduces
>         Int                  -- line number
>         Prec                 -- inline precedence annotation for the rule

> data Term
>     = App
>         String               -- name of the term
>         [Term]               -- parameter arguments (usually this is empty)

> data Prec
>     = PrecNone               -- no user-specified precedence
>     | PrecShift              -- %shift
>     | PrecId String          -- %prec ID
>   deriving Show

%-----------------------------------------------------------------------------
Parser Generator Directives.

ToDo: find a consistent way to analyse all the directives together and
generate some error messages.

>
> data Directive a
>       = TokenType     String                  -- %tokentype
>       | TokenSpec     [(a, TokenSpec)]        -- %token
>       | TokenName     String (Maybe String) Bool -- %name/%partial (True <=> %partial)
>       | TokenLexer    String String           -- %lexer
>       | TokenImportedIdentity                                 -- %importedidentity
>       | TokenMonad    String String String String -- %monad
>       | TokenNonassoc [String]                -- %nonassoc
>       | TokenRight    [String]                -- %right
>       | TokenLeft     [String]                -- %left
>       | TokenExpect   Int                     -- %expect
>       | TokenError    String (Maybe String)   -- %error
>       | TokenErrorExpected                    -- %error.expected
>       | TokenErrorHandlerType String          -- %errorhandlertype
>       | TokenAttributetype String             -- %attributetype
>       | TokenAttribute String String          -- %attribute
>   deriving (Eq, Show)

> getTokenType :: [Directive t] -> String
> getTokenType ds
>       = case [ t | (TokenType t) <- ds ] of
>               [t] -> t
>               []  -> error "no token type given"
>               _   -> error "multiple token types"

> getParserNames :: [Directive t] -> [Directive t]
> getParserNames ds = [ t | t@(TokenName _ _ _) <- ds ]

> getLexer :: [Directive t] -> Maybe (String, String)
> getLexer ds
>       = case [ (a,b) | (TokenLexer a b) <- ds ] of
>               [t] -> Just t
>               []  -> Nothing
>               _   -> error "multiple lexer directives"

> getImportedIdentity :: [Directive t] -> Bool
> getImportedIdentity ds
>       = case [ (()) | TokenImportedIdentity <- ds ] of
>               [_] -> True
>               []  -> False
>               _   -> error "multiple importedidentity directives"

> getMonad :: [Directive t] -> (Bool, String, String, String, String)
> getMonad ds
>       = case [ (True,a,b,c,d) | (TokenMonad a b c d) <- ds ] of
>               [t] -> t
>               []  -> (False,"()","HappyIdentity","Happy_Prelude.>>=","Happy_Prelude.return")
>               _   -> error "multiple monad directives"

> getTokenSpec :: [Directive t] -> [(t, TokenSpec)]
> getTokenSpec ds = concat [ t | (TokenSpec t) <- ds ]

> getPrios :: [Directive t] -> [Directive t]
> getPrios ds = [ d | d <- ds,
>                 case d of
>                   TokenNonassoc _ -> True
>                   TokenLeft _ -> True
>                   TokenRight _ -> True
>                   _ -> False
>               ]

> getPrioNames :: Directive t -> [String]
> getPrioNames (TokenNonassoc s) = s
> getPrioNames (TokenLeft s)     = s
> getPrioNames (TokenRight s)    = s
> getPrioNames _                 = error "Not an associativity token"

> getExpect :: [Directive t] -> Maybe Int
> getExpect ds
>         = case [ n | (TokenExpect n) <- ds ] of
>                 [t] -> Just t
>                 []  -> Nothing
>                 _   -> error "multiple expect directives"

> getError :: [Directive t] -> ErrorHandlerInfo
> getError ds
>       = case [ (a, mb_b) | (TokenError a mb_b) <- ds ] of
>               []                        -> DefaultErrorHandler
>               [(a,Nothing)]             -> CustomErrorHandler a
>               [(abort,Just addMessage)] -> ResumptiveErrorHandler abort addMessage
>               _   -> error "multiple error directives"


> getErrorExpectedMode :: Eq t => [Directive t] -> ErrorExpectedMode
> getErrorExpectedMode ds
>   | ["explist"] <- old_directive
>   = OldExpected
>   | TokenErrorExpected `elem` ds
>   = NewExpected
>   | length old_directive > 1
>   = error "multiple errorhandlertype directives"
>   | otherwise
>   = NoExpected
>   where
>     old_directive = [ a | (TokenErrorHandlerType a) <- ds ]

> getAttributes :: [Directive t] -> [(String, String)]
> getAttributes ds
>         = [ (ident,typ) | (TokenAttribute ident typ) <- ds ]

> getAttributetype :: [Directive t] -> Maybe String
> getAttributetype ds
>         = case [ t | (TokenAttributetype t) <- ds ] of
>                  [t] -> Just t
>                  []  -> Nothing
>                  _   -> error "multiple attributetype directives"

> getAttributeGrammarExtras :: [Directive t] -> Maybe AttributeGrammarExtras
> getAttributeGrammarExtras ds = case (getAttributes ds, getAttributetype ds) of
>   ([], Nothing) -> Nothing
>   (as, Just at) -> Just $ AttributeGrammarExtras {
>           attributes = as,
>           attributetype = at
>       }
>   (_ : _, Nothing) -> error "attributes found without attribute type directive"

> -- | Parse a token spec.
> --
> -- The first occurence of '$$' indicates an expression in which the '$$'
> -- will be substituted for the actual lexed token. '$$' in string or char
> -- literals ('".."' and '\'.\'') however does not count.
> parseTokenSpec :: String -> TokenSpec
> parseTokenSpec code0 = go code0 ""
>   where go code acc =
>           case code of
>               [] -> TokenFixed code0
>
>               '"'  :r    -> case reads code :: [(String,String)] of
>                                []       -> go r ('"':acc)
>                                (s,r'):_ -> go r' (reverse (show s) ++ acc)
>               a:'\'' :r | isAlphaNum a -> go r ('\'':a:acc)
>               '\'' :r    -> case reads code :: [(Char,String)] of
>                                []       -> go r ('\'':acc)
>                                (c,r'):_ -> go r' (reverse (show c) ++ acc)
>               '\\':'$':r -> go r ('$':acc)
>               '$':'$':r  -> TokenWithValue $ ExpressionWithHole (reverse acc) r
>               c:r  -> go r (c:acc)
