{-# OPTIONS_GHC -fno-warn-missing-signatures #-}
{-# OPTIONS_GHC -fno-warn-unused-do-bind #-}
module Data.Kicad.SExpr.Parse
( parse
, parseWithFilename
)
where
import Text.ParserCombinators.Parsec hiding (spaces, parse)
import qualified Text.ParserCombinators.Parsec as Parsec (parse)
import Text.Parsec.Char (endOfLine)
import Text.Parsec (getPosition)

import Data.Kicad.SExpr.SExpr

{-| Parse a 'String' as a 'SExpr' or return an error. -}
parse :: String -> Either String SExpr
parse = parseWithFilename ""

{-| Parse a 'String' as a 'SExpr' giving a filename to use in the source code location -}
parseWithFilename :: String -> String -> Either String SExpr
parseWithFilename filename input =
    case Parsec.parse parseListOrComment filename input of
        Left err -> Left $ "Parse Error: " ++ show err
        Right val -> Right val


parseListOrComment :: Parser SExpr
parseListOrComment = do
    spaces
    skipMany parseComment
    s <- parseList
    return s


parseComment :: Parser String
parseComment = do
    char '#'
    s <- many (noneOf "\r\n")
    endOfLine
    spaces
    return s


parseList :: Parser SExpr
parseList = do
    pos <- getPosition
    char '('
    spaces
    list <- try parseExpr `sepEndBy` spaces
    char ')'
    spaces
    return $ List pos list


parseExpr :: Parser SExpr
parseExpr =  try parseString
         <|> try parseListOrComment
         <?> "a double, string or s-expression"


parseString :: Parser SExpr
parseString =
    do pos <- getPosition
       str <- parseQuotedString <|> parseUnquotedString <?> "string"
       return $ Atom pos str
            where
                parseQuotedString  = do
                    char '"'
                    x <- many (noneOf "\\\"" <|> (char '\\' >> anyChar))
                    char '"'
                    return x
                parseUnquotedString = many1 (noneOf " ()\r\n")


spaces = skipMany spaceChar
spaceChar = oneOf "\r\n\t "