{-# LANGUAGE Safe #-} -- | This module parses files using the syntax demonstrated below. -- The full grammar is available in the Happy source file. -- -- @ -- -- Line comments until newline -- layout: -- based: -- configuration: -- {} -- empty section -- -- sections: -- "glguy" -- -- {- Block comments -- {- nested comments -} -- "O'caml style {- strings in comments" -- so you can comment out otherwise valid -- portions of your config -- -} -- atoms : yes -- -- decimal : -1234 -- hexadecimal: 0x1234 -- octal : 0o1234 -- binary : 0b1010 -- -- lists: -- * sections: in-lists -- next-section: still-in-list -- * [ "inline", "lists" ] -- * * "nestable" -- * "layout" -- * "lists" -- * 3 -- -- unicode : "standard Haskell format strings (1 ≤ 2)\x2228(2 ≤ 3)" -- @ module Config ( Section(..) , Value(..) , Atom(..) , parse , pretty ) where import Config.Value (Atom(..), Value(..), Section(..)) import Config.Parser (parseValue) import Config.Pretty (pretty) import Config.Lexer (scanTokens) import Config.Tokens (Error(..), Position(..), Located(..), layoutPass, Token) import qualified Config.Tokens as T import Numeric (showIntAtBase) import Data.Char (intToDigit) import Data.Text (Text) import qualified Data.Text as Text -- | Parse a configuration file and return the result on the -- right, or the position of an error on the left. -- Note: Text file lines are terminated by new-lines. parse :: Text {- ^ Source -} -> Either String Value {- ^ Either ErrorMessage Result -} parse txt = case parseValue (layoutPass (scanTokens txt)) of Right x -> Right x Left (Located posn token) -> Left (explain posn token) explain :: Position -> Token -> String explain posn token = show (posLine posn) ++ ":" ++ show (posColumn posn) ++ ": " ++ case token of T.Error e -> explainError e T.Atom atom -> "parse error: unexpected atom: " ++ Text.unpack atom T.String str -> "parse error: unexpected string: " ++ show (Text.unpack str) T.Bullet -> "parse error: unexpected bullet '*'" T.Comma -> "parse error: unexpected comma ','" T.Section s -> "parse error: unexpected section: " ++ Text.unpack s T.Number 2 n -> "parse error: unexpected number: 0b" ++ showIntAtBase 2 intToDigit n "" T.Number 8 n -> "parse error: unexpected number: 0o" ++ showIntAtBase 8 intToDigit n "" T.Number 16 n -> "parse error: unexpected number: 0x" ++ showIntAtBase 16 intToDigit n "" T.Number _ n -> "parse error: unexpected number: " ++ showIntAtBase 10 intToDigit n "" T.OpenList -> "parse error: unexpected start of list '['" T.CloseList -> "parse error: unexpected end of list ']'" T.OpenMap -> "parse error: unexpected start of section '{'" T.CloseMap -> "parse error: unexpected end of section '}'" T.LayoutSep -> "parse error: unexpected end of block" T.LayoutEnd -> "parse error: unexpected end of block" T.EOF -> "parse error: unexpected end of file" explainError :: Error -> String explainError e = case e of T.UntermComment -> "lexical error: unterminated comment" T.UntermCommentString -> "lexical error: unterminated string literal in comment" T.UntermString -> "lexical error: unterminated string literal" T.UntermFile -> "lexical error: unterminated line" T.BadEscape c -> "lexical error: bad escape sequence: " ++ Text.unpack c T.NoMatch c -> "lexical error at character " ++ show c