Remove debug print, document Parser.hs

This commit is contained in:
Kirill Andreev 2020-05-08 00:52:33 +04:00
parent e6299a50ff
commit eace901195
No known key found for this signature in database
GPG Key ID: CF7DA79DE4785A47
3 changed files with 96 additions and 25 deletions

View File

@ -48,7 +48,6 @@ typedecl = do
vardecl :: Parser (Binding ASTInfo) vardecl :: Parser (Binding ASTInfo)
vardecl = do vardecl = do
subtree "var_decl" do subtree "var_decl" do
dump
ctor Var ctor Var
<*> inside "name" name <*> inside "name" name
<*> inside "type" type_ <*> inside "type" type_
@ -179,7 +178,6 @@ lambda_expr = do
seq_expr = do seq_expr = do
subtree "block" do subtree "block" do
dump
ctor Seq <*> do ctor Seq <*> do
many "statement" do many "statement" do
inside "statement" do inside "statement" do
@ -229,7 +227,6 @@ skip = do
case_action :: Parser (Expr ASTInfo) case_action :: Parser (Expr ASTInfo)
case_action = do case_action = do
subtree "case_instr" do subtree "case_instr" do
dump
ctor Case ctor Case
<*> inside "subject" expr <*> inside "subject" expr
<*> many "case" do <*> many "case" do

View File

@ -39,24 +39,22 @@ foreign import ccall unsafe tree_sitter_PascaLigo :: Ptr Language
getNodeTypesPath :: IO FilePath getNodeTypesPath :: IO FilePath
getNodeTypesPath = getDataFileName "../pascaligo/src/node-types.json" getNodeTypesPath = getDataFileName "../pascaligo/src/node-types.json"
-- | The tree tree-sitter produces.
data ParseTree = ParseTree data ParseTree = ParseTree
{ ptID :: Int { ptID :: Int -- ^ Unique number, for fast comparison.
, ptName :: Text.Text , ptName :: Text.Text -- ^ Name of the node.
, ptRange :: Range , ptRange :: Range -- ^ Range of the node.
, ptChildren :: ParseForest , ptChildren :: ParseForest -- ^ Subtrees.
} }
deriving (Show) via PP ParseTree
-- ^ The forest we work with.
data ParseForest = Forest data ParseForest = Forest
{ pfID :: Int { pfID :: Int -- ^ Unique number for comparison.
, pfGrove :: [(Text.Text, ParseTree)] , pfGrove :: [(Text.Text, ParseTree)] -- ^ Subtrees.
, pfRange :: Range , pfRange :: Range -- ^ Full range of the forest.
} }
deriving (Show) via PP ParseForest
instance Show ParseTree where
show = show . pp
instance Show ParseForest where
show = show . pp
instance Pretty ParseTree where instance Pretty ParseTree where
pp (ParseTree _ n r forest) = pp (ParseTree _ n r forest) =
@ -75,6 +73,7 @@ instance Pretty ParseForest where
then nest 2 $ pp tree then nest 2 $ pp tree
else hang (text (Text.unpack field) <> ": ") 2 (pp tree) else hang (text (Text.unpack field) <> ": ") 2 (pp tree)
-- | Feed file contents into PascaLIGO grammar recogniser.
toParseTree :: FilePath -> IO ParseForest toParseTree :: FilePath -> IO ParseForest
toParseTree fin = do toParseTree fin = do
parser <- ts_parser_new parser <- ts_parser_new

View File

@ -21,24 +21,26 @@ import Pretty
import Debug.Trace import Debug.Trace
-- | Parse error.
data Error data Error
= Expected = Expected
{ eMsg :: Text { eMsg :: Text -- ^ Description of what was expected.
, eWhole :: Text , eWhole :: Text -- ^ Offending text.
, eRange :: Range , eRange :: Range -- ^ Location of the error.
} }
deriving stock (Show) deriving (Show) via PP Error
instance Pretty Error where instance Pretty Error where
pp (Expected msg found r) = "" <> pp msg <> pp r <> "" <> pp found <> "" pp (Expected msg found r) = "" <> pp msg <> pp r <> "" <> pp found <> ""
-- | Parser of tree-sitter-made tree.
newtype Parser a = Parser newtype Parser a = Parser
{ unParser { unParser
:: WriterT [Error] :: WriterT [Error] -- Early I though to report errors that way. Obs.
( ReaderT ParserEnv ( ReaderT ParserEnv -- Range/Something.
( StateT ParseForest ( StateT ParseForest -- Current forest to recognise.
( ExceptT Error ( ExceptT Error -- Backtracking. Change `Error` to `()`?
( IO )))) ( IO )))) -- I forgot why. `#include`? Debug via `print`?
a a
} }
deriving newtype deriving newtype
@ -52,11 +54,13 @@ newtype Parser a = Parser
, MonadIO , MonadIO
) )
-- | Generate error originating at current location.
makeError :: Text -> Parser Error makeError :: Text -> Parser Error
makeError msg = do makeError msg = do
rng <- getRange rng <- getRange
makeError' msg rng makeError' msg rng
-- | Generate error originating at given location.
makeError' :: Text -> Range -> Parser Error makeError' :: Text -> Range -> Parser Error
makeError' msg rng = do makeError' msg rng = do
rng <- getRange rng <- getRange
@ -67,6 +71,7 @@ makeError' msg rng = do
, eRange = rng , eRange = rng
} }
-- | Pick next tree in a forest or die with msg.
takeNext :: Text -> Parser ParseTree takeNext :: Text -> Parser ParseTree
takeNext msg = do takeNext msg = do
st@Forest {pfGrove, pfRange} <- get st@Forest {pfGrove, pfRange} <- get
@ -79,6 +84,10 @@ takeNext msg = do
} }
return t return t
-- | Pick a tree with that /field name/ or die with name as msg.
--
-- Will erase all subtrees with different names on the path!
--
field :: Text -> Parser a -> Parser a field :: Text -> Parser a -> Parser a
field name parser = do field name parser = do
grove <- gets pfGrove grove <- gets pfGrove
@ -107,6 +116,7 @@ field name parser = do
, pfRange = if firstOne then diffRange rng ptRange else rng , pfRange = if firstOne then diffRange rng ptRange else rng
} }
-- | Variuos error reports.
fallback :: Stubbed a => Text -> Parser a fallback :: Stubbed a => Text -> Parser a
fallback' :: Stubbed a => Text -> Range -> Parser a fallback' :: Stubbed a => Text -> Range -> Parser a
die :: Text -> Parser a die :: Text -> Parser a
@ -118,10 +128,13 @@ die msg = throwError =<< makeError msg
die' msg rng = throwError =<< makeError' msg rng die' msg rng = throwError =<< makeError' msg rng
complain msg rng = tell . pure =<< makeError' msg rng complain msg rng = tell . pure =<< makeError' msg rng
-- | If a parser fails, return stub with error originating here.
stubbed :: Stubbed a => Text -> Parser a -> Parser a stubbed :: Stubbed a => Text -> Parser a -> Parser a
stubbed msg parser = do stubbed msg parser = do
parser <|> fallback msg parser <|> fallback msg
-- | The forest must start with tree of that name. Its subtrees become new
-- forest. Otherwise, it dies with name as msg.
subtree :: Text -> Parser a -> Parser a subtree :: Text -> Parser a -> Parser a
subtree msg parser = do subtree msg parser = do
ParseTree {ptChildren, ptName} <- takeNext msg ParseTree {ptChildren, ptName} <- takeNext msg
@ -133,6 +146,7 @@ subtree msg parser = do
else do else do
die msg die msg
-- | Because `ExceptT` requires error to be `Monoid` for `Alternative`.
(<|>) :: Parser a -> Parser a -> Parser a (<|>) :: Parser a -> Parser a -> Parser a
Parser l <|> Parser r = Parser (l `catchError` const r) Parser l <|> Parser r = Parser (l `catchError` const r)
@ -142,6 +156,10 @@ select = foldl1 (<|>)
optional :: Parser a -> Parser (Maybe a) optional :: Parser a -> Parser (Maybe a)
optional p = fmap Just p <|> return Nothing optional p = fmap Just p <|> return Nothing
-- | Custom `Alternative.many`.
--
-- TODO: remove msg.
--
many :: Text -> Parser a -> Parser [a] many :: Text -> Parser a -> Parser [a]
many msg p = many' many msg p = many'
where where
@ -155,6 +173,10 @@ many msg p = many'
else do else do
return [x] return [x]
-- | Custom `Alternative.some`.
--
-- TODO: remove msg.
--
some :: Text -> Parser a -> Parser [a] some :: Text -> Parser a -> Parser [a]
some msg p = some' some msg p = some'
where where
@ -168,12 +190,20 @@ some msg p = some'
else do else do
return [x] return [x]
-- | Get UID of current tree. Obsolete.
--
-- TODO: remove.
--
getTreeID :: Parser (Maybe Int) getTreeID :: Parser (Maybe Int)
getTreeID = Parser do getTreeID = Parser do
pfGrove <$> get >>= return . \case pfGrove <$> get >>= return . \case
[] -> Nothing [] -> Nothing
(_, tree) : _ -> Just (ptID tree) (_, tree) : _ -> Just (ptID tree)
-- | Assert the parser consumes input. Obsolete.
--
-- TODO: remove.
--
productive :: Parser a -> Parser (a, Bool) productive :: Parser a -> Parser (a, Bool)
productive p = do productive p = do
was <- getTreeID was <- getTreeID
@ -181,19 +211,33 @@ productive p = do
now <- getTreeID now <- getTreeID
return (res, was /= now) return (res, was /= now)
-- | The `not <$> eos`. Obsolete.
--
-- TODO: remove.
--
hasPossibleInput :: Parser () hasPossibleInput :: Parser ()
hasPossibleInput = do hasPossibleInput = do
yes <- gets (not . null . pfGrove) yes <- gets (not . null . pfGrove)
unless yes do unless yes do
die "something" die "something"
-- | The source of file being parsed. BS, because tree-sitter has offsets
-- in /bytes/.
data ParserEnv = ParserEnv data ParserEnv = ParserEnv
{ peSource :: ByteString { peSource :: ByteString
} }
-- | Debug print via IO. Obsolete.
--
-- TODO: remove. Also, remove IO from Parser tf stack.
--
puts :: MonadIO m => Show a => a -> m () puts :: MonadIO m => Show a => a -> m ()
puts = liftIO . print puts = liftIO . print
-- | Run parser on given file.
--
-- TODO: invent /proper/ 'ERROR'-node collector.
--
runParser :: Parser a -> FilePath -> IO (a, [Error]) runParser :: Parser a -> FilePath -> IO (a, [Error])
runParser (Parser parser) fin = do runParser (Parser parser) fin = do
pforest <- toParseTree fin pforest <- toParseTree fin
@ -207,6 +251,7 @@ runParser (Parser parser) fin = do
either (error . show) (return . fst) res either (error . show) (return . fst) res
-- | Run parser on given file and pretty-print stuff.
debugParser :: Parser a -> FilePath -> IO a debugParser :: Parser a -> FilePath -> IO a
debugParser parser fin = do debugParser parser fin = do
(res, errs) <- runParser parser fin (res, errs) <- runParser parser fin
@ -216,6 +261,7 @@ debugParser parser fin = do
putStrLn "Result:" putStrLn "Result:"
return res return res
-- | Consume next tree if it has give name. Or die.
token :: Text -> Parser Text token :: Text -> Parser Text
token node = do token node = do
tree@ParseTree {ptName, ptRange} <- takeNext node tree@ParseTree {ptName, ptRange} <- takeNext node
@ -226,28 +272,33 @@ token node = do
else do else do
die' node ptRange die' node ptRange
-- | Consume next tree, return its textual representation.
anything :: Parser Text anything :: Parser Text
anything = do anything = do
tree <- takeNext "anything" tree <- takeNext "anything"
cutOut $ ptRange tree cutOut $ ptRange tree
-- | TODO: remove, b/c obsolete.
consume :: Text -> Parser () consume :: Text -> Parser ()
consume node = do consume node = do
ParseTree {ptName, ptRange} <- takeNext node ParseTree {ptName, ptRange} <- takeNext node
when (ptName /= node) do when (ptName /= node) do
complain node ptRange complain node ptRange
-- | TODO: remove, its literally is `void . token`.
consumeOrDie :: Text -> Parser () consumeOrDie :: Text -> Parser ()
consumeOrDie node = do consumeOrDie node = do
ParseTree {ptName, ptRange} <- takeNext node ParseTree {ptName, ptRange} <- takeNext node
when (ptName /= node) do when (ptName /= node) do
die' node ptRange die' node ptRange
-- | Extract textual representation of given range.
cutOut :: Range -> Parser Text cutOut :: Range -> Parser Text
cutOut (Range (_, _, s) (_, _, f)) = do cutOut (Range (_, _, s) (_, _, f)) = do
bs <- asks peSource bs <- asks peSource
return $ decodeUtf8 $ ByteString.take (f - s) (ByteString.drop s bs) return $ decodeUtf8 $ ByteString.take (f - s) (ByteString.drop s bs)
-- | Get range of current tree or forest before the parser was run.
range :: Parser a -> Parser (a, Range) range :: Parser a -> Parser (a, Range)
range parser = range parser =
get >>= \case get >>= \case
@ -259,9 +310,14 @@ range parser =
a <- parser a <- parser
return (a, pfRange) return (a, pfRange)
-- | Get current range.
getRange :: Parser Range getRange :: Parser Range
getRange = snd <$> range (return ()) getRange = snd <$> range (return ())
-- | Remove all keys until given key is found; remove the latter as well.
--
-- Notice: this works differently from `Prelude.remove`!
--
delete :: Eq k => k -> [(k, v)] -> [(k, v)] delete :: Eq k => k -> [(k, v)] -> [(k, v)]
delete _ [] = [] delete _ [] = []
delete k ((k', v) : rest) = delete k ((k', v) : rest) =
@ -269,6 +325,7 @@ delete k ((k', v) : rest) =
then rest then rest
else delete k rest else delete k rest
-- | Parser negation.
notFollowedBy :: Parser a -> Parser () notFollowedBy :: Parser a -> Parser ()
notFollowedBy parser = do notFollowedBy parser = do
good <- do good <- do
@ -280,24 +337,39 @@ notFollowedBy parser = do
unless good do unless good do
die "notFollowedBy" die "notFollowedBy"
-- | For types that have a default replacer with an `Error`.
class Stubbed a where class Stubbed a where
stub :: Error -> a stub :: Error -> a
instance Stubbed Text where instance Stubbed Text where
stub = pack . show stub = pack . show
-- | This is bad, but I had to.
--
-- TODO: find a way to remove this instance.
--
instance Stubbed [a] where instance Stubbed [a] where
stub _ = [] stub _ = []
-- | `Nothing` would be bad default replacer.
instance Stubbed a => Stubbed (Maybe a) where instance Stubbed a => Stubbed (Maybe a) where
stub = Just . stub stub = Just . stub
-- | Universal accessor.
--
-- Usage:
--
-- > inside "$field:$treename"
-- > inside "$field"
-- > inside ":$treename" -- don't, use "subtree"
--
inside :: Stubbed a => Text -> Parser a -> Parser a inside :: Stubbed a => Text -> Parser a -> Parser a
inside sig parser = do inside sig parser = do
let (f, st') = Text.breakOn ":" sig let (f, st') = Text.breakOn ":" sig
let st = Text.drop 1 st' let st = Text.drop 1 st'
if Text.null f if Text.null f
then do then do
-- The order is important.
subtree st do subtree st do
stubbed f do stubbed f do
parser parser
@ -311,13 +383,16 @@ inside sig parser = do
subtree st do subtree st do
parser parser
-- Auto-accumulated information to be fed into AST being build.
data ASTInfo = ASTInfo data ASTInfo = ASTInfo
{ aiRange :: Range { aiRange :: Range
, aiComments :: [Text] , aiComments :: [Text]
} }
-- | Equip given constructor with info.
ctor :: (ASTInfo -> a) -> Parser a ctor :: (ASTInfo -> a) -> Parser a
ctor = (<$> (ASTInfo <$> getRange <*> pure [])) ctor = (<$> (ASTInfo <$> getRange <*> pure []))
-- | /Actual/ debug pring.
dump :: Parser () dump :: Parser ()
dump = gets pfGrove >>= traceShowM dump = gets pfGrove >>= traceShowM