packages feed

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

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

module Config.Parser (parse) where

import Control.Applicative
import Control.Monad
import Data.ByteString.Lazy (ByteString)
import qualified Data.ByteString.Lazy.Char8 as L8

import Config.Value   (Section(..), Value(..))
import Config.Lexer   (scanTokens)
import Config.Tokens  (PosToken(..), layoutPass)
import qualified Config.Tokens as T

}

%token

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

%tokentype                      { PosToken                      }
%lexer { lexerP }               { PosToken _ _ 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 :: PosToken -> [PosToken] -> Either (Int,Int) (PosToken,[PosToken], a) }

-- | Parse a configuration value and return the result on the
-- right, or the position of an error on the left.
parse ::
  ByteString             {- ^ UTF-8 encoded source        -} ->
  Either (Int,Int) Value {- ^ Either (Line,Column) Result -}
parse bytes =
  do let toks = layoutPass (scanTokens bytes)
     (_,_,x) <- runParseM value (error "previous token") toks
     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 :: (PosToken -> ParseM a) -> ParseM a
lexerP k = ParseM $ \_ toks ->
  case toks of
    []      -> error "Unexpected end of token stream"
    t:toks' -> runParseM (k t) t toks'

-- required by 'happy'
happyError :: ParseM a
happyError = ParseM $ \(PosToken line column _) _ -> Left (line,column)

}