2020-04-30 14:39:51 +04:00
|
|
|
|
2020-05-01 19:04:29 +04:00
|
|
|
module Parser (module Parser, gets, pfGrove) where
|
2020-04-30 14:39:51 +04:00
|
|
|
|
|
|
|
import Control.Monad.State
|
|
|
|
import Control.Monad.Writer
|
|
|
|
import Control.Monad.Reader
|
|
|
|
import Control.Monad.Except
|
|
|
|
import Control.Monad.Identity
|
|
|
|
|
2020-05-01 19:04:29 +04:00
|
|
|
import Data.Foldable
|
2020-04-30 14:39:51 +04:00
|
|
|
import Data.Text.Encoding
|
|
|
|
import Data.Text (Text, pack, unpack)
|
2020-05-06 21:26:00 +04:00
|
|
|
import qualified Data.Text as Text
|
2020-04-30 14:39:51 +04:00
|
|
|
|
|
|
|
import qualified Data.ByteString as ByteString
|
|
|
|
import Data.ByteString (ByteString)
|
|
|
|
|
|
|
|
import ParseTree
|
2020-04-30 17:58:35 +04:00
|
|
|
import Range
|
2020-05-01 19:04:29 +04:00
|
|
|
import Pretty
|
2020-04-30 14:39:51 +04:00
|
|
|
|
2020-04-30 17:46:39 +04:00
|
|
|
import Debug.Trace
|
2020-04-30 14:39:51 +04:00
|
|
|
|
|
|
|
data Error
|
2020-04-30 21:06:01 +04:00
|
|
|
= Expected
|
|
|
|
{ eMsg :: Text
|
|
|
|
, eWhole :: Text
|
|
|
|
, eRange :: Range
|
|
|
|
}
|
2020-04-30 14:39:51 +04:00
|
|
|
deriving stock (Show)
|
|
|
|
|
2020-04-30 21:46:37 +04:00
|
|
|
instance Pretty Error where
|
2020-05-01 19:04:29 +04:00
|
|
|
pp (Expected msg found r) = "<<<" <> pp msg <> pp r <> ": " <> pp found <> ">>>"
|
2020-04-30 21:46:37 +04:00
|
|
|
|
2020-04-30 14:39:51 +04:00
|
|
|
newtype Parser a = Parser
|
|
|
|
{ unParser
|
|
|
|
:: WriterT [Error]
|
|
|
|
( ReaderT ParserEnv
|
|
|
|
( StateT ParseForest
|
|
|
|
( ExceptT Error
|
2020-05-01 19:04:29 +04:00
|
|
|
( IO ))))
|
2020-04-30 14:39:51 +04:00
|
|
|
a
|
|
|
|
}
|
|
|
|
deriving newtype
|
|
|
|
( Functor
|
|
|
|
, Applicative
|
|
|
|
, Monad
|
|
|
|
, MonadState ParseForest
|
|
|
|
, MonadWriter [Error]
|
|
|
|
, MonadReader ParserEnv
|
|
|
|
, MonadError Error
|
2020-05-01 19:04:29 +04:00
|
|
|
, MonadIO
|
2020-04-30 14:39:51 +04:00
|
|
|
)
|
|
|
|
|
2020-04-30 21:06:01 +04:00
|
|
|
makeError :: Text -> Parser Error
|
|
|
|
makeError msg = do
|
|
|
|
rng <- getRange
|
|
|
|
makeError' msg rng
|
|
|
|
|
|
|
|
makeError' :: Text -> Range -> Parser Error
|
|
|
|
makeError' msg rng = do
|
|
|
|
rng <- getRange
|
|
|
|
src <- cutOut rng
|
|
|
|
return Expected
|
|
|
|
{ eMsg = msg
|
|
|
|
, eWhole = src
|
|
|
|
, eRange = rng
|
|
|
|
}
|
|
|
|
|
2020-04-30 14:39:51 +04:00
|
|
|
takeNext :: Text -> Parser ParseTree
|
|
|
|
takeNext msg = do
|
|
|
|
st@Forest {pfGrove, pfRange} <- get
|
|
|
|
case pfGrove of
|
|
|
|
[] -> die msg
|
|
|
|
(_, t) : f -> do
|
|
|
|
put st
|
|
|
|
{ pfRange = diffRange pfRange (ptRange t)
|
|
|
|
, pfGrove = f
|
|
|
|
}
|
|
|
|
return t
|
|
|
|
|
2020-04-30 17:46:39 +04:00
|
|
|
field :: Text -> Parser a -> Parser a
|
2020-04-30 14:39:51 +04:00
|
|
|
field name parser = do
|
|
|
|
grove <- gets pfGrove
|
|
|
|
case grove of
|
|
|
|
(name', t) : _
|
|
|
|
| name == name' -> do
|
|
|
|
sandbox True t
|
|
|
|
|
|
|
|
_ -> do
|
|
|
|
case lookup name grove of
|
|
|
|
Just tree -> sandbox False tree
|
|
|
|
Nothing -> die name
|
|
|
|
|
|
|
|
where
|
|
|
|
sandbox firstOne tree@ParseTree {ptID, ptRange} = do
|
|
|
|
st@Forest {pfGrove = grove, pfRange = rng} <- get
|
|
|
|
let grove' = delete name grove
|
|
|
|
put Forest
|
|
|
|
{ pfID = ptID
|
|
|
|
, pfGrove = [(name, tree)]
|
|
|
|
, pfRange = ptRange
|
|
|
|
}
|
|
|
|
|
2020-04-30 17:46:39 +04:00
|
|
|
parser <* put st
|
2020-04-30 14:39:51 +04:00
|
|
|
{ pfGrove = grove'
|
|
|
|
, pfRange = if firstOne then diffRange rng ptRange else rng
|
|
|
|
}
|
|
|
|
|
2020-04-30 21:06:01 +04:00
|
|
|
fallback :: Stubbed a => Text -> Parser a
|
|
|
|
fallback' :: Stubbed a => Text -> Range -> Parser a
|
|
|
|
die :: Text -> Parser a
|
|
|
|
die' :: Text -> Range -> Parser a
|
|
|
|
complain :: Text -> Range -> Parser ()
|
|
|
|
fallback msg = pure . stub =<< makeError msg
|
|
|
|
fallback' msg rng = pure . stub =<< makeError' msg rng
|
|
|
|
die msg = throwError =<< makeError msg
|
|
|
|
die' msg rng = throwError =<< makeError' msg rng
|
|
|
|
complain msg rng = tell . pure =<< makeError' msg rng
|
2020-04-30 14:39:51 +04:00
|
|
|
|
|
|
|
stubbed :: Stubbed a => Text -> Parser a -> Parser a
|
|
|
|
stubbed msg parser = do
|
|
|
|
parser <|> fallback msg
|
|
|
|
|
|
|
|
subtree :: Text -> Parser a -> Parser a
|
|
|
|
subtree msg parser = do
|
|
|
|
ParseTree {ptChildren, ptName} <- takeNext msg
|
|
|
|
if ptName == msg
|
|
|
|
then do
|
|
|
|
save <- get
|
|
|
|
put ptChildren
|
|
|
|
parser <* put save
|
|
|
|
else do
|
|
|
|
die msg
|
|
|
|
|
|
|
|
(<|>) :: Parser a -> Parser a -> Parser a
|
|
|
|
Parser l <|> Parser r = Parser (l `catchError` const r)
|
|
|
|
|
|
|
|
select :: [Parser a] -> Parser a
|
|
|
|
select = foldl1 (<|>)
|
|
|
|
|
|
|
|
optional :: Parser a -> Parser (Maybe a)
|
|
|
|
optional p = fmap Just p <|> return Nothing
|
|
|
|
|
|
|
|
many :: Text -> Parser a -> Parser [a]
|
|
|
|
many msg p = many'
|
|
|
|
where
|
|
|
|
many' = some' <|> pure []
|
2020-04-30 17:46:39 +04:00
|
|
|
some' = do
|
2020-05-01 19:04:29 +04:00
|
|
|
hasPossibleInput
|
2020-04-30 17:46:39 +04:00
|
|
|
(x, consumed) <- productive p
|
|
|
|
if consumed then do
|
|
|
|
xs <- many'
|
|
|
|
return (x : xs)
|
|
|
|
else do
|
|
|
|
return [x]
|
2020-04-30 14:39:51 +04:00
|
|
|
|
|
|
|
some :: Text -> Parser a -> Parser [a]
|
|
|
|
some msg p = some'
|
|
|
|
where
|
|
|
|
many' = some' <|> pure []
|
2020-04-30 17:46:39 +04:00
|
|
|
some' = do
|
2020-05-01 19:04:29 +04:00
|
|
|
hasPossibleInput
|
2020-04-30 17:46:39 +04:00
|
|
|
(x, consumed) <- productive p
|
|
|
|
if consumed then do
|
|
|
|
xs <- many'
|
|
|
|
return (x : xs)
|
|
|
|
else do
|
|
|
|
return [x]
|
2020-04-30 14:39:51 +04:00
|
|
|
|
|
|
|
getTreeID :: Parser (Maybe Int)
|
|
|
|
getTreeID = Parser do
|
|
|
|
pfGrove <$> get >>= return . \case
|
|
|
|
[] -> Nothing
|
|
|
|
(_, tree) : _ -> Just (ptID tree)
|
|
|
|
|
2020-04-30 17:46:39 +04:00
|
|
|
productive :: Parser a -> Parser (a, Bool)
|
|
|
|
productive p = do
|
2020-04-30 14:39:51 +04:00
|
|
|
was <- getTreeID
|
|
|
|
res <- p
|
|
|
|
now <- getTreeID
|
2020-04-30 17:46:39 +04:00
|
|
|
return (res, was /= now)
|
2020-04-30 14:39:51 +04:00
|
|
|
|
2020-05-01 19:04:29 +04:00
|
|
|
hasPossibleInput :: Parser ()
|
|
|
|
hasPossibleInput = do
|
|
|
|
yes <- gets (not . null . pfGrove)
|
|
|
|
unless yes do
|
|
|
|
die "something"
|
|
|
|
|
2020-04-30 14:39:51 +04:00
|
|
|
data ParserEnv = ParserEnv
|
2020-05-01 19:04:29 +04:00
|
|
|
{ peSource :: ByteString
|
2020-04-30 14:39:51 +04:00
|
|
|
}
|
|
|
|
|
2020-05-01 19:04:29 +04:00
|
|
|
puts :: MonadIO m => Show a => a -> m ()
|
|
|
|
puts = liftIO . print
|
|
|
|
|
2020-04-30 14:39:51 +04:00
|
|
|
runParser :: Parser a -> FilePath -> IO (a, [Error])
|
|
|
|
runParser (Parser parser) fin = do
|
|
|
|
pforest <- toParseTree fin
|
|
|
|
text <- ByteString.readFile fin
|
2020-05-01 19:04:29 +04:00
|
|
|
res <-
|
|
|
|
runExceptT
|
2020-04-30 14:39:51 +04:00
|
|
|
$ flip runStateT pforest
|
2020-05-01 19:04:29 +04:00
|
|
|
$ flip runReaderT (ParserEnv text)
|
2020-04-30 14:39:51 +04:00
|
|
|
$ runWriterT
|
|
|
|
$ parser
|
|
|
|
|
|
|
|
either (error . show) (return . fst) res
|
|
|
|
|
2020-05-01 19:04:29 +04:00
|
|
|
debugParser :: Parser a -> FilePath -> IO a
|
|
|
|
debugParser parser fin = do
|
|
|
|
(res, errs) <- runParser parser fin
|
|
|
|
putStrLn "Errors:"
|
|
|
|
for_ errs (print . nest 2 . pp)
|
|
|
|
putStrLn ""
|
|
|
|
putStrLn "Result:"
|
|
|
|
return res
|
|
|
|
|
2020-04-30 14:39:51 +04:00
|
|
|
token :: Text -> Parser Text
|
|
|
|
token node = do
|
|
|
|
tree@ParseTree {ptName, ptRange} <- takeNext node
|
|
|
|
if ptName == node
|
|
|
|
then do
|
2020-04-30 21:06:01 +04:00
|
|
|
cutOut ptRange
|
2020-04-30 14:39:51 +04:00
|
|
|
|
|
|
|
else do
|
2020-04-30 21:06:01 +04:00
|
|
|
die' node ptRange
|
2020-04-30 14:39:51 +04:00
|
|
|
|
|
|
|
anything :: Parser Text
|
|
|
|
anything = do
|
|
|
|
tree <- takeNext "anything"
|
2020-04-30 21:06:01 +04:00
|
|
|
cutOut $ ptRange tree
|
2020-04-30 14:39:51 +04:00
|
|
|
|
|
|
|
consume :: Text -> Parser ()
|
|
|
|
consume node = do
|
|
|
|
ParseTree {ptName, ptRange} <- takeNext node
|
|
|
|
when (ptName /= node) do
|
2020-04-30 21:06:01 +04:00
|
|
|
complain node ptRange
|
2020-04-30 14:39:51 +04:00
|
|
|
|
2020-04-30 17:46:39 +04:00
|
|
|
consumeOrDie :: Text -> Parser ()
|
|
|
|
consumeOrDie node = do
|
|
|
|
ParseTree {ptName, ptRange} <- takeNext node
|
|
|
|
when (ptName /= node) do
|
2020-04-30 21:06:01 +04:00
|
|
|
die' node ptRange
|
2020-04-30 17:46:39 +04:00
|
|
|
|
2020-04-30 21:06:01 +04:00
|
|
|
cutOut :: Range -> Parser Text
|
|
|
|
cutOut (Range (_, _, s) (_, _, f)) = do
|
|
|
|
bs <- asks peSource
|
|
|
|
return $ decodeUtf8 $ ByteString.take (f - s) (ByteString.drop s bs)
|
2020-04-30 14:39:51 +04:00
|
|
|
|
|
|
|
range :: Parser a -> Parser (a, Range)
|
|
|
|
range parser =
|
|
|
|
get >>= \case
|
|
|
|
Forest {pfGrove = (,) _ ParseTree {ptRange} : _} -> do
|
|
|
|
a <- parser
|
|
|
|
return (a, ptRange)
|
|
|
|
|
|
|
|
Forest {pfRange} -> do
|
|
|
|
a <- parser
|
|
|
|
return (a, pfRange)
|
|
|
|
|
|
|
|
getRange :: Parser Range
|
|
|
|
getRange = snd <$> range (return ())
|
|
|
|
|
|
|
|
delete :: Eq k => k -> [(k, v)] -> [(k, v)]
|
|
|
|
delete _ [] = []
|
|
|
|
delete k ((k', v) : rest) =
|
|
|
|
if k == k'
|
|
|
|
then rest
|
2020-05-06 21:26:00 +04:00
|
|
|
else delete k rest
|
2020-04-30 14:39:51 +04:00
|
|
|
|
2020-04-30 17:46:39 +04:00
|
|
|
notFollowedBy :: Parser a -> Parser ()
|
|
|
|
notFollowedBy parser = do
|
|
|
|
good <- do
|
|
|
|
parser
|
|
|
|
return False
|
|
|
|
<|> do
|
|
|
|
return True
|
|
|
|
|
|
|
|
unless good do
|
|
|
|
die "notFollowedBy"
|
|
|
|
|
2020-04-30 14:39:51 +04:00
|
|
|
class Stubbed a where
|
|
|
|
stub :: Error -> a
|
|
|
|
|
|
|
|
instance Stubbed Text where
|
2020-05-06 21:26:00 +04:00
|
|
|
stub = pack . show
|
|
|
|
|
|
|
|
instance Stubbed [a] where
|
|
|
|
stub _ = []
|
|
|
|
|
2020-05-07 00:31:05 +04:00
|
|
|
instance Stubbed a => Stubbed (Maybe a) where
|
|
|
|
stub = Just . stub
|
|
|
|
|
2020-05-06 21:26:00 +04:00
|
|
|
inside :: Stubbed a => Text -> Parser a -> Parser a
|
|
|
|
inside sig parser = do
|
|
|
|
let (f, st') = Text.breakOn ":" sig
|
|
|
|
let st = Text.drop 1 st'
|
|
|
|
if Text.null f
|
|
|
|
then do
|
|
|
|
subtree st do
|
|
|
|
stubbed f do
|
|
|
|
parser
|
|
|
|
else do
|
|
|
|
field f do
|
|
|
|
stubbed f do
|
|
|
|
if Text.null st
|
|
|
|
then do
|
|
|
|
parser
|
|
|
|
else do
|
|
|
|
subtree st do
|
|
|
|
parser
|
2020-05-06 21:59:34 +04:00
|
|
|
|
2020-05-06 22:15:19 +04:00
|
|
|
data ASTInfo = ASTInfo
|
|
|
|
{ aiRange :: Range
|
|
|
|
, aiComments :: [Text]
|
|
|
|
}
|
|
|
|
|
|
|
|
ctor :: (ASTInfo -> a) -> Parser a
|
|
|
|
ctor = (<$> (ASTInfo <$> getRange <*> pure []))
|