ligo/tools/lsp/squirrel/src/Parser.hs

240 lines
5.2 KiB
Haskell
Raw Normal View History

2020-04-30 14:39:51 +04:00
module Parser where
import Control.Monad.State
import Control.Monad.Writer
import Control.Monad.Reader
import Control.Monad.Except
import Control.Monad.Identity
import Data.Text.Encoding
import Data.Text (Text, pack, unpack)
import qualified Data.ByteString as ByteString
import Data.ByteString (ByteString)
import ParseTree
2020-04-30 17:46:39 +04:00
import Debug.Trace
2020-04-30 14:39:51 +04:00
data Error
= Expected Text Range
| Unexpected Range
deriving stock (Show)
newtype Parser a = Parser
{ unParser
:: WriterT [Error]
( ReaderT ParserEnv
( StateT ParseForest
( ExceptT Error
( Identity ))))
a
}
deriving newtype
( Functor
, Applicative
, Monad
, MonadState ParseForest
, MonadWriter [Error]
, MonadReader ParserEnv
, MonadError Error
)
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
}
fallback :: Stubbed a => Text -> Parser a
fallback msg = (stub . Expected msg) <$> getRange
die :: Text -> Parser a
die msg = throwError . Expected msg =<< getRange
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
(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
(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
data ParserEnv = ParserEnv
{ peRange :: Range
, peSource :: ByteString
}
runParser :: Parser a -> FilePath -> IO (a, [Error])
runParser (Parser parser) fin = do
pforest <- toParseTree fin
text <- ByteString.readFile fin
let
res
= runIdentity
$ runExceptT
$ flip runStateT pforest
$ flip runReaderT (ParserEnv (pfRange pforest) text)
$ runWriterT
$ parser
either (error . show) (return . fst) res
token :: Text -> Parser Text
token node = do
tree@ParseTree {ptName, ptRange} <- takeNext node
if ptName == node
then do
source <- asks peSource
return (cutOut source tree)
else do
throwError $ Expected node ptRange
anything :: Parser Text
anything = do
tree <- takeNext "anything"
source <- asks peSource
return (cutOut source tree)
consume :: Text -> Parser ()
consume node = do
ParseTree {ptName, ptRange} <- takeNext node
when (ptName /= node) do
tell [Expected node ptRange]
2020-04-30 17:46:39 +04:00
consumeOrDie :: Text -> Parser ()
consumeOrDie node = do
ParseTree {ptName, ptRange} <- takeNext node
when (ptName /= node) do
throwError $ Expected node ptRange
2020-04-30 14:39:51 +04:00
cutOut :: ByteString -> ParseTree -> Text
cutOut bs (ParseTree _ _ s f _ _) =
decodeUtf8 $ ByteString.take (f - s) (ByteString.drop s bs)
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
else (k', v) : delete k rest
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-04-30 17:46:39 +04:00
stub = pack . show