packages feed

config-value-0.2: src/Config/Parser.y

{
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE Trustworthy #-}

module Config.Parser (parse) where

import Control.Applicative
import Control.Monad
import Data.ByteString (ByteString)
import Data.Text (Text)
import qualified Data.ByteString.Char8 as B8

import Config.Value   (Section(..), Value(..))
import Config.Lexer   (scanTokens)
import Config.Tokens  (Located(..), Position(..), Token, layoutPass)
import qualified Config.Tokens as T

}

%token

SECTION                         { Located _ (T.Section $$)      }
STRING                          { Located _ (T.String $$)       }
NUMBER                          { Located _ $$@T.Number{}       }
'yes'                           { Located _ T.Yes               }
'no'                            { Located _ T.No                }
'*'                             { Located _ T.Bullet            }
'['                             { Located _ T.OpenList          }
','                             { Located _ T.Comma             }
']'                             { Located _ T.CloseList         }
'{'                             { Located _ T.OpenMap           }
'}'                             { Located _ T.CloseMap          }
SEP                             { Located _ T.LayoutSep         }
END                             { Located _ T.LayoutEnd         }

%tokentype                      { Located Token                 }
%lexer { lexerP }               { Located _ T.EOF               }
%monad { ParseM }
%name value value

%%

value ::                        { Value                         }
  : sections END                { Sections (reverse $1)         }
  | list     END                { List     (reverse $1)         }
  | simple                      { $1                            }

simple ::                       { Value                         }
  : NUMBER                      { number $1                     }
  | STRING                      { Text   $1                     }
  | 'yes'                       { Bool True                     }
  | 'no'                        { Bool False                    }
  | '{' '}'                     { Sections []                   }
  | '[' inlinelist ']'          { List     $2                   }

sections ::                     { [Section]                     }
  :              section        { [$1]                          }
  | sections SEP section        { $3 : $1                       }

section ::                      { Section                       }
  : SECTION value               { Section $1 $2                 }

list ::                         { [Value]                       }
  :          '*' value          { [$2]                          }
  | list SEP '*' value          { $4 : $1                       }

inlinelist ::                   { [Value]                       }
  :                             { []                            }
  | inlinelist1                 { reverse $1                    }

inlinelist1 ::                  { [Value]                       }
  :                 simple      { [$1]                          }
  | inlinelist1 ',' simple      { $3 : $1                       }



{

number :: T.Token -> Value
number (T.Number base val) = Number base val
number _                   = error "Config.Parser.number: fatal error"

newtype ParseM a = ParseM
  { runParseM :: Position -> [Located Token] -> Either (Int,Int) (Position,[Located Token], a) }

-- | Parse a configuration value and return the result on the
-- right, or the position of an error on the left.
parse ::
  Text                   {- ^ Source                      -} ->
  Either (Int,Int) Value {- ^ Either (Line,Column) Result -}
parse txt =
  do (_,_,x) <- runParseM value (error "previous token")
              $ layoutPass
              $ scanTokens txt
     return x

instance Functor ParseM where
  fmap          = liftM

instance Applicative ParseM where
  (<*>)         = ap
  pure          = return

instance Monad ParseM where
  return x      = ParseM $ \t ts ->
                     do return (t,ts,x)
  m >>= f       = ParseM $ \t ts ->
                     do (t',ts',x) <- runParseM m t ts
                        runParseM (f x) t' ts'

lexerP :: (Located Token -> ParseM a) -> ParseM a
lexerP k = ParseM $ \_ toks ->
  case toks of
    []      -> error "Unexpected end of token stream"
    t:toks' -> runParseM (k t) (locPosition t) toks'

-- required by 'happy'
happyError :: ParseM a
happyError = ParseM $ \posn _ -> Left (posLine posn, posColumn posn)

}