demangler-1.3.2.0: src/Demangler/Engine.hs
{-# LANGUAGE CPP #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE TupleSections #-}
-- | This is an internal module that provides the core parsing engine
-- functionality for the Demangler.
module Demangler.Engine
where
import Control.Applicative
import Control.Lens ( Lens, Lens', (&), (^.), (.~), _1, _2, _3 )
import Data.Char
import Data.List.NonEmpty ( NonEmpty((:|)) )
import Data.Maybe ( fromMaybe )
import qualified Data.Sequence as Seq
import Data.Text ( Text )
import qualified Data.Text as T
import Demangler.Context
import Demangler.Structure
#ifdef MIN_VERSION_panic
-- The debug flag is enabled in the cabal file
import Debug.Trace
import Panic
instance PanicComponent Demangler where
panicComponentName _ = "Demangler"
panicComponentIssues = const "https://github.com/GaloisInc/demangler/issues"
panicComponentRevision _ = ("main", "-")
cannot :: PanicComponent a => a -> String -> [String] -> b
cannot = panic
#else
cannot :: a -> String -> [String] -> Maybe b
cannot _ _ _ = Nothing -- signals a parsing failure, ultimately resulting in Original
#endif
data Demangler = Demangler
type Result = (Demangled, Context)
--------------------
-- Mangled name parsing basis types
-- | Next encodes a parsing step: provided with the current string and the
-- context, it may succeed with the remainder of the string after the portion
-- consumed by this parser and the updated context.
type Next a b = NextArg a -> Maybe (NextArg b)
type NextArg a = (Text, (a, (Context, ( Seq.Seq SubsCandidate
, (Seq.Seq TemplateArg, Bool, Bool)))))
type AnyNext b = forall a . Next a b
nInp :: Lens' (NextArg a) Text
nInp = _1
nVal :: Lens (NextArg a) (NextArg b) a b
nVal = _2 . _1
nContext :: Lens' (NextArg a) Context
nContext = _2 . _2 . _1
nSubs :: Lens' (NextArg a) (Seq.Seq SubsCandidate)
nSubs = _2 . _2 . _2 . _1
nTmplSubs :: Lens' (NextArg a) (Seq.Seq TemplateArg)
nTmplSubs = _2 . _2 . _2 . _2 . _1
nTmplSubsLatch :: Lens' (NextArg a) Bool
nTmplSubsLatch = _2 . _2 . _2 . _2 . _2
nTmplSubsLock :: Lens' (NextArg a) Bool
nTmplSubsLock = _2 . _2 . _2 . _2 . _3
ret :: Applicative f => NextArg a -> b -> f (NextArg b)
ret i v = pure (_2 . _1 .~ v $ i)
ret' :: Applicative f => b -> NextArg a -> f (NextArg b)
ret' a b = ret b a
rmap :: Applicative f => (a -> b) -> NextArg a -> f (NextArg b)
rmap f i = ret i $ f (i ^. nVal)
rapply :: Applicative f => NextArg (a -> b, a) -> f (NextArg b)
rapply = rmap $ uncurry ($)
--------------------
-- Helpers
match :: Text -> Next a a
match n i =
let (mp, rp) = T.splitAt (T.length n) (i ^. nInp)
in do require (mp == n)
pure $ i & nInp .~ rp
single_digit_num :: AnyNext Int
single_digit_num i =
let txt = i ^. nInp
d = T.head txt
in if not (T.null txt) && isDigit d
then Just $ i & nInp .~ T.drop 1 txt & nVal .~ ord d - ord '0'
else Nothing
digits_num :: AnyNext Int
digits_num i =
let (d,r) = T.span isDigit $ i ^. nInp
in if T.null d
then Nothing
else Just $ i & nInp .~ r & nVal .~ read (T.unpack d)
base36_num :: AnyNext Int
base36_num i =
let isB36char c = or [ isDigit c, isAsciiUpper c ]
nextB36Digit a x = (a * 36)
+ (ord x - if isDigit x then ord '0' else ord 'A' - 10)
(d,r) = T.span isB36char $ i ^. nInp
in if T.null d
then Nothing
else Just $ i & nInp .~ r & nVal .~ T.foldl nextB36Digit 0 d
require :: Bool -> Maybe ()
require t = if t then Just () else Nothing
rdiscard :: NextArg a -> NextArg ()
rdiscard = nVal .~ ()
#if MIN_VERSION_base(4,16,0)
#else
asum :: (Foldable t, Alternative f) => t (f a) -> f a
asum = foldr (<|>) empty
#endif
asum' :: (Foldable t, Functor t, Alternative f) => t (a -> f b) -> a -> f b
asum' opts i = asum $ ($ i) <$> opts
tbd :: String -> Next a b
#ifdef MIN_VERSION_panic
tbd why i = trace ("TBD: " <> why <> " @ " <> (T.unpack $ i ^. nInp)) Nothing
#else
tbd _why = const Nothing
#endif
some' :: AnyNext a -> Next b (NonEmpty a)
some' p i = do e1 <- p i
go p e1 >>= rmap (e1 ^. nVal :|)
where go p' i' = case p' i' of
Nothing -> ret i' []
Just e -> go p' e >>= rmap (e ^. nVal :)
many' :: Next () a -> Next () [a]
many' p i = case p i of
Nothing -> ret i []
Just e -> many' p (rdiscard e) >>= rmap (e ^. nVal :)
optional' :: Next a b -> Next a (a, Maybe b)
optional' o i = (o i >>= rmap ((i ^. nVal,) . Just)) <|> rmap (,Nothing) i
(>&=>) :: Next a b -> Next b c -> Next a (b,c)
a >&=> b = \i -> do x <- a i
y <- b x
rmap ((x ^. nVal,)) y
infixl 2 >&=>
insert :: b -> Next a b
insert v = pure . (nVal .~ v)
noop :: Next a a
noop = Just
end_of_input :: Next a a
end_of_input i = if T.null (i ^. nInp) then Just i else Nothing
#ifdef MIN_VERSION_panic
traceP :: String -> Next a a
traceP at i = trace (at <> " @ " <> (T.unpack $ i ^. nInp) <> " #subs=" <> show (Seq.length (i ^. nSubs)) <> " #tmplSubs=" <> show (Seq.length (i ^. nTmplSubs))) $ Just i
#endif
----------------------------------------------------------------------
-- | Add an additional PrefixR entry to the end of a Prefix.
extendPrefix :: Prefix -> PrefixR -> Prefix
extendPrefix = \case
PrefixTemplateParam tp PrefixEnd -> PrefixTemplateParam tp
PrefixDeclType dt PrefixEnd -> PrefixDeclType dt
Prefix PrefixEnd -> Prefix
PrefixTemplateParam tp sp -> PrefixTemplateParam tp . extPfx2 sp
PrefixDeclType dt sp -> PrefixDeclType dt . extPfx2 sp
Prefix sp -> Prefix . extPfx2 sp
o -> fromMaybe (const o) $ cannot Demangler "extendPrefix"
[ "What prefix to extend? " <> show o ]
extPfx2 :: PrefixR -> (PrefixR -> PrefixR)
extPfx2 = \case
PrefixEnd -> id
PrefixUQName uqn sp -> PrefixUQName uqn . extPfx2 sp
PrefixTemplateArgs ta sp -> PrefixTemplateArgs ta . extPfx2 sp
-- | Split a prefix into the last element and the initial portion without that
-- last element (if possible).
prefixInitLast :: Prefix -> Maybe (Prefix, Either UnqualifiedName TemplateArgs)
prefixInitLast = \case
PrefixTemplateParam tp p2 -> descend (PrefixTemplateParam tp) p2
PrefixDeclType dt sp -> descend (PrefixDeclType dt) sp
Prefix p2 -> descend Prefix p2
o -> cannot Demangler "prefixInitLast"
[ "Not a valid prefix for this need"
, show o
]
where
descend :: (PrefixR -> Prefix) -> PrefixR
-> Maybe (Prefix, Either UnqualifiedName TemplateArgs)
descend mkPfx pfxR = let (rf, mb'le) = go (id, Nothing) pfxR
in case mb'le of
Nothing -> Nothing
Just le -> Just (mkPfx $ rf PrefixEnd, le)
go seen = \case
PrefixUQName uqn PrefixEnd -> (fst seen, Just $ Left uqn)
PrefixUQName uqn subpfx ->
go (fst seen . PrefixUQName uqn, Just $ Left uqn) subpfx
PrefixTemplateArgs ta PrefixEnd -> (fst seen, Just $ Right ta)
PrefixTemplateArgs ta subpfx ->
go (fst seen . PrefixTemplateArgs ta, Just $ Right ta) subpfx
PrefixEnd -> seen