demangler-1.3.2.0: src/Demangler/Substitution.hs
{-# LANGUAGE CPP #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TypeApplications #-}
module Demangler.Substitution
(
-- * Parse a substitution reference
substitution
-- * Substitute the found substitution into the result
, substituteUnscopedName
, substitutePrefix
, substitutePrefixR
, substituteTemplateParam
, substituteTemplatePrefix
, substituteType
, stdSubstToType
, substituteUnresolvedType
-- * When a subtitution candidate has been parsed, it is recorded here
, canSubstUnscopedTemplateName
, canSubstPrefix
, canSubstTemplateArg
, canSubstTemplateParam
, canSubstTemplatePrefix
, canSubstType
, canSubstTypes
, canSubstUnresolvedType
, dropLastSubst
)
where
import Control.Applicative
import Control.Lens ( (&), (^.), (%~) )
import Control.Monad
import Data.List.NonEmpty ( NonEmpty((:|)) )
import qualified Data.List.NonEmpty as NEL
import Data.Maybe
import Data.Sequence ( (|>), ViewR((:>)) )
import qualified Data.Sequence as Seq
import Demangler.Engine
import Demangler.Structure
import Demangler.PPrint ()
#ifdef MIN_VERSION_panic
-- The debug flag is enabled in the cabal file
import Demangler.Context
import Text.Sayable
import Debug.Trace
#endif
import Prelude hiding ( last )
--------------------
-- * Handling Substitutions
--
-- Substition could be handled at parsing time or at pretty-printing time. This
-- implementation handles substitution at parsing time for the following reasons:
--
-- 1. Parser "type" information to confirm that the substitution value is
-- appropriate to be substituted in the current parsing element.
--
-- 2. The resulting Demangled structure is fully expressed and does not need to
-- hold additional "sequencing" information that would allow Substitution
-- target identification. Also simplifies use of the Demangled structure.
--
-- 3. The sequencing information can also be more difficult to re-determine at
-- pretty-printing time (viz. the efforts in the itanium-abi package which
-- performs substitution at pretty-printing time).
--
-- Substitution is very tricky: the BNF (at the URL in the Dismantle module, and
-- which isn't fully correct) specifies that wherever "<substitution>" appears, a
-- substitution may be made OR a substitution capture can occur. It also states
-- that substitutions are not duplicated. It implies (but is not clear) that
-- there are actually two substitution namespaces: regular substitutions and
-- template subsitutions, where the former are accessed as "S[n]_" and the latter
-- are accessed as "T[m]_", where the n and m ordering are within the associated
-- namespace. Here are the additional rules and exceptions not discussed:
--
-- * Constructor/Destructor names are not captured or substituted (but operator
-- names are)
--
-- * Known substitutions ("St" for "std::", "Sa" for "std::allocator", etc.)
-- are *not* added as a possible substitution if they appear alone, but if
-- they are part of a prefix (i.e. they are followed by other information)
-- then they are added as part of that longer sequence (e.g. "foo::list<i>"
-- will add "foo", "foo::list", and "foo::list<i>" for a total of 3 possible
-- substitution candidates whereas "std::list<i>" will add "std::list" and
-- "std::list<i>" for only 2 possible substitution candidates.
--
-- * Template argument substutions are in a different namespace and recursive
-- template arguments are not substitition candidates (e.g. foo<bar<int>>)
-- results in only one template substitution candidate (bar<int>).
-- | Parse a substitution specification and get the raw Substitution' result;
-- these should always be translated and never actually returned in the Demangled
-- result.
substitution :: AnyNext Substitution'
substitution =
asum' [ match "S" >=> base36_num >=> rmap (Subs . toEnum) >=> match "_"
, match "S_" >=> rmap (const SubsFirst)
, match "St" >=> rmap (const $ SubsConst SubStd)
, match "Sa" >=> rmap (const $ SubsConst SubAlloc)
, match "Sb" >=> rmap (const $ SubsConst SubBasicString)
, match "Ss" >=> rmap (const $ SubsConst $ SubStdType BasicStringChar)
, match "Si" >=> rmap (const $ SubsConst $ SubStdType BasicIStream)
, match "So" >=> rmap (const $ SubsConst $ SubStdType BasicOStream)
, match "Sd" >=> rmap (const $ SubsConst $ SubStdType BasicIOStream)
]
-- Internal to lookup a parsed Substitution'
getSubst :: NextArg Substitution' -> Either Substitution (Maybe SubsCandidate)
getSubst i =
case i ^. nVal of
SubsFirst -> Right $ Seq.lookup 0 $ i ^. nSubs
Subs n -> Right $ Seq.lookup (fromEnum n + 1) $ i ^. nSubs
SubsConst s -> Left s
#ifdef MIN_VERSION_panic
dumpSubs :: (Monad f, Applicative f)
=> NextArg a -> String -> f (NextArg a)
dumpSubs spec what = do
mapM_ (traceM . show) $ Seq.zip
(Seq.fromList [0.. Seq.length (spec ^. nSubs)])
((\ue -> (ue, sez @"debug" (addContext ue (spec ^. nContext))))
<$> spec ^. nSubs)
mapM_ (traceM . show) $ Seq.zip
(Seq.fromList [0.. Seq.length (spec ^. nTmplSubs)])
((\ue -> ('T', ue, sez @"debug" (addContext ue (spec ^. nContext))))
<$> spec ^. nTmplSubs)
traceM $ "Subs Total: " <> show ((Seq.length $ spec ^. nSubs) + (Seq.length $ spec ^. nTmplSubs)) <> " "
<> show ((Seq.length $ spec ^. nSubs), (Seq.length $ spec ^. nTmplSubs))
<> " --> " <> sez @"debug" what
pure spec
#endif
invalidSubst :: Show a => String -> NextArg a -> Maybe SubsCandidate -> Maybe b
invalidSubst for spec = \case
Just s -> do
#ifdef MIN_VERSION_panic
-- Debug details
_ <- dumpSubs spec "Just"
#endif
cannot Demangler for
[ "Invalid " <> for <> " substitution (" <> show (spec ^. nVal) <> "):"
, show s
]
Nothing -> do
#ifdef MIN_VERSION_panic
-- Debug details
_ <- dumpSubs spec "Nothing"
#endif
cannot Demangler for
[ "Invalid " <> for <> " substitution reference:"
, show (spec ^. nVal)
]
substituteUnscopedName :: Next Substitution UnscopedName
-> Next Substitution' UnscopedName
substituteUnscopedName direct i =
case getSubst i of
Right (Just (SC_UQName s n)) -> ret i $ UnScName s n
Right (Just (SC_Prefix p)) ->
let getUsn = \case
Prefix prefixr -> getUsn_PR prefixr
_ -> Nothing
getUsn_PR = \case
PrefixUQName uqn PrefixEnd -> Just $ UnScName False uqn
PrefixUQName (StdSubst SubStd) sp ->
case getUsn_PR sp of
Nothing -> Nothing
Just (UnScName _ uqn) -> Just $ UnScName True uqn
Just n@(UnScSubst _) -> Just n
PrefixUQName (StdSubst _) _ -> Nothing
PrefixUQName _ _ -> Nothing
PrefixEnd -> Nothing
PrefixTemplateArgs _ _ -> Nothing
in case getUsn p of
Nothing -> Nothing
Just usn -> ret i usn
Right _ -> Nothing
Left s -> direct =<< ret i s
substituteType :: (Next Substitution Type_) -> Next Substitution' Type_
substituteType embed i =
case getSubst i of
Right (Just (SC_Type t)) -> ret i t
Right (Just (SC_Prefix p)) -> ret i =<< prefixToType p
Right (Just (SC_UQName isStd uqn)) ->
ret i $ ClassUnionStructEnum $ UnscopedName $ UnScName isStd uqn
Right o -> invalidSubst "Type" i o
Left s -> embed =<< ret i s
prefixToType :: Prefix -> Maybe Type_
prefixToType pfx =
ClassUnionStructEnum
<$> case prefixInitLast pfx of
Nothing -> cannot Demangler "prefixToType"
[ "Cannot convert prefix to type: " <> show pfx ]
Just (iniPfx, Left uqn) ->
Just $ NameNested $ NestedName iniPfx uqn [] Nothing
Just (iniPfx, Right ta) ->
let tmpltpfx =
case prefixInitLast iniPfx of
Just (EmptyPrefix, Left luqn) ->
Just $ GlobalTemplate (luqn :| [])
Just (p, Left luqn) ->
Just $ NestedTemplate p (luqn :| [])
_ -> cannot Demangler "prefixToType"
[ "Cannot convert ta prefix to type: " <> show pfx ]
mkntn tpfx = NestedTemplateName tpfx ta [] Nothing
in NameNested . mkntn <$> tmpltpfx
substituteUnresolvedType :: Next Substitution UnresolvedType
-> Next Substitution' UnresolvedType
substituteUnresolvedType direct i =
case getSubst i of
Right (Just (SC_Prefix p)) -> ret i $ URTSubstPrefix p
Right (Just (SC_UnresolvedType urt)) -> ret i urt
Right x -> cannot Demangler "substituteUnresolvedType"
[ "Cannot convert to an unresolved type: " <> show x ]
Left s -> direct =<< ret i s
substitutePrefix :: Next Substitution Prefix -> Next Substitution' Prefix
substitutePrefix direct i =
case getSubst i of
Right (Just (SC_Prefix p)) -> ret i p
Right o -> invalidSubst "Prefix" i o
Left s -> direct =<< ret i s
substitutePrefixR :: Next Substitution PrefixR -> Next Substitution' PrefixR
substitutePrefixR direct i =
case getSubst i of
Right o@(Just (SC_Type (ClassUnionStructEnum nm))) ->
case name2prefix nm of
Just pfx -> ret i pfx
Nothing -> invalidSubst "PrefixR.SC_Type.ClassUnionStructEnum" i o
Right (Just (SC_Prefix (Prefix sp))) -> ret i sp
Right o -> invalidSubst "PrefixR" i o
Left s -> ret i s >>= direct
where
name2prefix = \case
NameNested nn -> nn2prefix nn
UnscopedName (UnScName True _uqn) -> Nothing
-- Just $ PrefixUQName (SourceName (!!! "std") mempty) $ PrefixUQName uqn PrefixEnd
UnscopedName (UnScName False uqn) -> Just $ PrefixUQName uqn PrefixEnd
UnscopedName (UnScSubst s) -> Just $ PrefixUQName (StdSubst s) PrefixEnd
UnscopedTemplateName nm _tmplArgs -> name2prefix nm
LocalName _enc nm _disc -> name2prefix nm -- discriminators are invisible
StringLitName _ _ -> Nothing
nn2prefix = \case
NestedName pf@(Prefix _) uqn _cvq _mbref ->
case extendPrefix pf $ PrefixUQName uqn PrefixEnd of
Prefix pfr -> Just pfr
_ -> Nothing
_ -> Nothing
substituteTemplatePrefix :: (Next Substitution TemplatePrefix)
-> Next Substitution' TemplatePrefix
substituteTemplatePrefix direct i =
case getSubst i of
Right o@(Just (SC_Prefix (Prefix p2))) ->
let go = \case
PrefixEnd -> Nothing
PrefixUQName uqn sp ->
case go sp of
Nothing -> Just $ GlobalTemplate $ uqn :| []
Just (GlobalTemplate uqns) ->
Just $ GlobalTemplate $ NEL.cons uqn uqns
Just (NestedTemplate (Prefix p) uqns) ->
Just $ NestedTemplate (Prefix $ PrefixUQName uqn p) uqns
_ -> Nothing -- ??
PrefixTemplateArgs ta sp ->
case go sp of
Nothing -> Nothing
Just (GlobalTemplate uqns) ->
Just $ NestedTemplate (Prefix $ PrefixTemplateArgs ta PrefixEnd) uqns
Just (NestedTemplate (Prefix p) uqns) ->
Just $ NestedTemplate (Prefix $ PrefixTemplateArgs ta p) uqns
_ -> Nothing -- ??
in case go p2 of
Nothing -> invalidSubst "Template Prefix (2)" i o
Just o' -> ret i o'
Right (Just (SC_Prefix (PrefixTemplateParam p PrefixEnd))) ->
ret i $ TemplateTemplateParam p
Right o -> invalidSubst "Template Prefix" i o
Left s -> ret i s >>= direct
-- | Calls to replace a template substitution ("T[n]_") with the replacement
-- value from the original template argument specification. Note that these
-- substitutions are independent of the normal substitutions (i.e. "S[n]_").
substituteTemplateParam :: Next (Maybe Int) TemplateParam
substituteTemplateParam i =
let idx = maybe 0 (+1) $ i ^. nVal
in case Seq.lookup idx (i ^. nTmplSubs) of
Just a -> ret i a
_ ->
#ifdef MIN_VERSION_panic
-- Debug details
dumpSubs i "Nothing Template Param" >>
#endif
cannot Demangler "substituteTemplateParam"
[ "Invalid Template Param substitution reference: "
, show (i ^. nVal)
]
-- | Called during parsing to add a SubsCandidate for future Substitution lookup.
canSubst :: SubsCandidate -> Next a a
canSubst what i = pure $ i & nSubs %~ (|> what)
dropLastSubst :: Next a a
dropLastSubst i = pure $ i & nSubs %~ dropLast
where dropLast s = case Seq.viewr s of
Seq.EmptyR -> s
s' :> _ -> s'
-- | Called during parsing to add an unscoped template name for future
-- Substitution lookup.
canSubstUnscopedTemplateName :: Next Name Name
canSubstUnscopedTemplateName i =
case i ^. nVal of
UnscopedName (UnScName isStd uqn) -> canSubst (SC_UQName isStd uqn) i
_ -> pure i
-- | Called during parsing to add a NamePrefix SubsCandidate for future
-- Substitution lookup. This one is a bit different because the Prefix may
-- contain a NonEmpty list of Unqualified names: each init of that list is a
-- substitutable.
canSubstPrefix :: Next Prefix Prefix
canSubstPrefix i = canSubst (SC_Prefix $ i ^. nVal) i
-- | Called during parsing to add an Type_ SubsCandidate for future Substitution
-- lookup.
canSubstType :: Next Type_ Type_
canSubstType i = canSubst (SC_Type $ i ^. nVal) i
canSubstTypes :: Next (NEL.NonEmpty Type_) (NEL.NonEmpty Type_)
canSubstTypes i =
let subT i' ty = canSubst (SC_Type $ ty) i'
in foldM subT i (i ^. nVal)
canSubstUnresolvedType :: Next UnresolvedType UnresolvedType
canSubstUnresolvedType i = canSubst (SC_UnresolvedType $ i ^. nVal) i
canSubstTemplatePrefix :: Next TemplatePrefix TemplatePrefix
canSubstTemplatePrefix i = canSubst (SC_TemplatePrefix $ i ^. nVal) i
canSubstTemplateParam :: Next TemplateParam TemplateParam
canSubstTemplateParam = canSubstTemplateArg
canSubstTemplateArg :: Next TemplateArg TemplateArg
canSubstTemplateArg i =
if i ^. nTmplSubsLock
then pure i
else pure $ i & nTmplSubs %~ (|> (i ^. nVal))
----------------------------------------------------------------------
stdSubstToType :: Next Substitution Type_
stdSubstToType i =
case (i ^. nVal) of
SubStdType stdTy -> ret i $ StdType stdTy
s -> cannot Demangler "stdSubstToType"
[ "Substitution " <> show s <> " is not a type" ]