zuramaru-0.1.0.0: src/Maru/Parser.hs
{-# LANGUAGE OverloadedStrings #-}
-- | The parsers
module Maru.Parser
( parseTest
, prettyPrint
, parseErrorPretty
, parse
, debugParse
, ParseResult
) where
import Control.Applicative ((<|>))
import Control.Monad (mapM_)
import Control.Monad.Fail (fail)
import Maru.Type.Parser (ParseLog(..), ParseErrorResult, MaruParser, runMaruParser)
import Maru.Type.SExpr
import Prelude hiding (fail)
import Safe (readMay)
import qualified Data.Text.IO as TIO
import qualified Maru.Type.SExpr as MTS
import qualified Text.Megaparsec as P
type ParseResult = Either ParseErrorResult CallowSExpr
-- | Parse code to AST, and show AST and logs
parseTest :: SourceCode -> IO ()
parseTest = prettyPrint . debugParse
-- | Pretty print result of debugParse
prettyPrint :: (ParseResult, [ParseLog]) -> IO ()
prettyPrint (parseResult, logs) = do
mapM_ (TIO.putStrLn . unParseLog) logs
putStrLn ""
case parseResult of
Left e -> putStrLn $ P.parseErrorPretty e
Right a -> putStrLn $ "Success:\n> " ++ show a
-- | Parse code to AST without logs
parse :: SourceCode -> ParseResult
parse = fst . debugParse
-- | Parse code to AST with logs
debugParse :: SourceCode -> (ParseResult, [ParseLog])
debugParse = runMaruParser sexprParser
parseErrorPretty :: ParseErrorResult -> String
parseErrorPretty = P.parseErrorPretty
sexprParser :: MaruParser CallowSExpr
sexprParser = do
P.space
quoteParser <<> atomParser <<> listParser
where
-- but "(quote x)" is not parsed to `Quote (AtomSymbol "x")` in here,
-- it is parsed to `Cons (AtomSymbol "quote") (Cons (AtomSymbol "x") Nil)`
-- in the outer of here
quoteParser :: MaruParser CallowSExpr
quoteParser = do
P.char '\''
Quote' <$> sexprParser
atomParser :: MaruParser CallowSExpr
atomParser = (numberParser <<> boolParser <<> symbolParser) <* P.space
listParser :: MaruParser CallowSExpr
listParser = do
P.char '('
P.space
xs <- P.many sexprParser
P.space
P.char ')'
P.space
return $ scottEncode' xs
numberParser :: MaruParser CallowSExpr
numberParser = naturalNumberParser <<> positiveNumberParser <<> negativeNumberParser
boolParser :: MaruParser CallowSExpr
boolParser = return . AtomBool' =<< judgeBool =<< P.string "true" <|> P.string "false"
symbolParser :: MaruParser CallowSExpr
symbolParser = return . AtomSymbol' . MTS.pack =<< P.some (P.noneOf ['\'', '(', ')', ' '])
naturalNumberParser :: MaruParser CallowSExpr
naturalNumberParser = return . AtomInt' =<< read' =<< P.some P.digitChar
positiveNumberParser :: MaruParser CallowSExpr
positiveNumberParser = do
P.char '+'
numberParser
negativeNumberParser :: MaruParser CallowSExpr
negativeNumberParser = do
P.char '-'
txt <- P.some P.digitChar
AtomInt' . negate <$> read' txt
-- |
-- Return the constant successive parser for `Bool` if the string is "true" or "false".
-- Return the constant failure parser if it is the another string.
judgeBool :: String -> MaruParser Bool
judgeBool "true" = return True
judgeBool "false" = return False
judgeBool _ = fail "boolParser is failed"
-- |
-- Read it,
-- but if the reading is failed, the whole of `MaruParser` context to be failure.
-- Otherwise, the result into the context and Return it.
read' :: Read a => String -> MaruParser a
read' x =
case readMay x of
Nothing -> fail "read': fatal error! a reading the `String` to the `Read` value is failed"
Just x' -> return x'
infixr 5 <<>
-- | Don't consume the tokens if the left parser is failed
(<<>) :: MaruParser a -> MaruParser a -> MaruParser a
x <<> y = P.try x <|> y