mirror of
https://github.com/jgm/pandoc.git
synced 2026-08-29 01:41:20 +08:00
RST reader: Use a new one-pass parsing strategy.
Instead of having an initial pass where we collect reference definitions, we create links with target `##SUBST##something` or `##REF##something` or `##NOTE##something`, and resolve these in a pass over the parsed AST. This allows us to handle link references that are not at the top level. Closes #10281.
This commit is contained in:
+101
-110
@@ -40,6 +40,7 @@ import Text.Pandoc.Options
|
|||||||
import Text.Pandoc.Parsing
|
import Text.Pandoc.Parsing
|
||||||
import Text.Pandoc.Shared
|
import Text.Pandoc.Shared
|
||||||
import Text.Pandoc.URI
|
import Text.Pandoc.URI
|
||||||
|
import Text.Pandoc.Walk (walkM)
|
||||||
import qualified Text.Pandoc.UTF8 as UTF8
|
import qualified Text.Pandoc.UTF8 as UTF8
|
||||||
import Data.Time.Format
|
import Data.Time.Format
|
||||||
import System.FilePath (takeDirectory)
|
import System.FilePath (takeDirectory)
|
||||||
@@ -150,43 +151,79 @@ metaFromDefList ds meta = adjustAuthors $ foldr f meta ds
|
|||||||
|
|
||||||
parseRST :: PandocMonad m => RSTParser m Pandoc
|
parseRST :: PandocMonad m => RSTParser m Pandoc
|
||||||
parseRST = do
|
parseRST = do
|
||||||
|
standalone <- getOption readerStandalone
|
||||||
optional blanklines -- skip blank lines at beginning of file
|
optional blanklines -- skip blank lines at beginning of file
|
||||||
startPos <- getPosition
|
|
||||||
-- go through once just to get list of reference keys and notes
|
|
||||||
-- docMinusKeys is the raw document with blanks where the keys were...
|
|
||||||
let chunk = referenceKey
|
|
||||||
<|> anchorDef
|
|
||||||
<|> noteBlock
|
|
||||||
<|> citationBlock
|
|
||||||
<|> (snd <$> withRaw comment)
|
|
||||||
<|> headerBlock
|
|
||||||
<|> lineClump
|
|
||||||
docMinusKeys <- Sources <$>
|
|
||||||
manyTill (do pos <- getPosition
|
|
||||||
t <- chunk
|
|
||||||
return (pos, t)) eof
|
|
||||||
-- UGLY: we collapse source position information.
|
|
||||||
-- TODO: fix the parser to use the F monad instead of two passes
|
|
||||||
setInput docMinusKeys
|
|
||||||
setPosition startPos
|
|
||||||
st' <- getState
|
|
||||||
let reversedNotes = stateNotes st'
|
|
||||||
updateState $ \s -> s { stateNotes = reverse reversedNotes
|
|
||||||
, stateIdentifiers = mempty }
|
|
||||||
-- now parse it for real...
|
|
||||||
blocks <- B.toList <$> parseBlocks
|
blocks <- B.toList <$> parseBlocks
|
||||||
citations <- sort . M.toList . stateCitations <$> getState
|
citations <- sort . M.toList . stateCitations <$> getState
|
||||||
citationItems <- mapM parseCitation citations
|
citationItems <- mapM parseCitation citations
|
||||||
let refBlock = [Div ("citations",[],[]) $
|
let refBlock = [Div ("citations",[],[]) $
|
||||||
B.toList $ B.definitionList citationItems | not (null citationItems)]
|
B.toList $ B.definitionList citationItems | not (null citationItems)]
|
||||||
standalone <- getOption readerStandalone
|
|
||||||
state <- getState
|
state <- getState
|
||||||
let meta = stateMeta state
|
let meta = stateMeta state
|
||||||
let (blocks', meta') = if standalone
|
let (blocks', meta') = if standalone
|
||||||
then titleTransform (blocks, meta)
|
then titleTransform (blocks, meta)
|
||||||
else (blocks, meta)
|
else (blocks, meta)
|
||||||
|
let reversedNotes = stateNotes state
|
||||||
|
updateState $ \s -> s { stateNotes = reverse reversedNotes }
|
||||||
|
doc <- walkM resolveReferences (Pandoc meta' (blocks' ++ refBlock))
|
||||||
reportLogMessages
|
reportLogMessages
|
||||||
return $ Pandoc meta' (blocks' ++ refBlock)
|
return doc
|
||||||
|
|
||||||
|
resolveReferences :: PandocMonad m => Inline -> RSTParser m Inline
|
||||||
|
resolveReferences x@(Link _ ils (s,_))
|
||||||
|
| Just ref <- T.stripPrefix "##REF##" s = do
|
||||||
|
let isAnonKey (Key (T.uncons -> Just ('_',_))) = True
|
||||||
|
isAnonKey _ = False
|
||||||
|
state <- getState
|
||||||
|
let keyTable = stateKeys state
|
||||||
|
let anonKeys = sort $ filter isAnonKey $ M.keys keyTable
|
||||||
|
key <- if ref == "_" -- anonymous key
|
||||||
|
then
|
||||||
|
case anonKeys of
|
||||||
|
[] -> mzero -- TODO log?
|
||||||
|
(k:_) -> return k
|
||||||
|
else return $ toKey ref
|
||||||
|
((src,tit), attr) <- lookupKey [] key
|
||||||
|
-- if anonymous link, remove key so it won't be used again
|
||||||
|
when (isAnonKey key) $ updateState $ \st ->
|
||||||
|
st{ stateKeys = M.delete key keyTable }
|
||||||
|
return $ Link attr ils (src, tit)
|
||||||
|
| Just ref <- T.stripPrefix "##NOTE##" s = do
|
||||||
|
state <- getState
|
||||||
|
let notes = stateNotes state
|
||||||
|
case lookup ref notes of
|
||||||
|
Nothing -> do
|
||||||
|
pos <- getPosition
|
||||||
|
logMessage $ ReferenceNotFound ref pos
|
||||||
|
return x
|
||||||
|
Just raw -> do
|
||||||
|
-- We temporarily empty the note list while parsing the note,
|
||||||
|
-- so that we don't get infinite loops with notes inside notes...
|
||||||
|
-- Note references inside other notes are allowed in reST, but
|
||||||
|
-- not yet in this implementation.
|
||||||
|
updateState $ \st -> st{ stateNotes = [] }
|
||||||
|
contents <- parseFromString' parseBlocks raw
|
||||||
|
let newnotes = if ref == "*" || ref == "#" -- auto-numbered
|
||||||
|
-- delete the note so the next auto-numbered note
|
||||||
|
-- doesn't get the same contents:
|
||||||
|
then deleteFirstsBy (==) notes [(ref,raw)]
|
||||||
|
else notes
|
||||||
|
updateState $ \st -> st{ stateNotes = newnotes }
|
||||||
|
return $ Note (B.toList contents)
|
||||||
|
| Just ref <- T.stripPrefix "##SUBST##" s = do
|
||||||
|
substTable <- stateSubstitutions <$> getState
|
||||||
|
let key = toKey $ stripFirstAndLast ref
|
||||||
|
case M.lookup key substTable of
|
||||||
|
Nothing -> do
|
||||||
|
pos <- getPosition
|
||||||
|
logMessage $ ReferenceNotFound (tshow key) pos
|
||||||
|
return x
|
||||||
|
Just target -> case
|
||||||
|
B.toList target of
|
||||||
|
[t] -> return t
|
||||||
|
ts -> return $ Span nullAttr ts
|
||||||
|
| otherwise = return x
|
||||||
|
resolveReferences x = return x
|
||||||
|
|
||||||
parseCitation :: PandocMonad m
|
parseCitation :: PandocMonad m
|
||||||
=> (Text, Text) -> RSTParser m (Inlines, [Blocks])
|
=> (Text, Text) -> RSTParser m (Inlines, [Blocks])
|
||||||
@@ -207,6 +244,9 @@ block :: PandocMonad m => RSTParser m Blocks
|
|||||||
block = choice [ codeBlock
|
block = choice [ codeBlock
|
||||||
, blockQuote
|
, blockQuote
|
||||||
, fieldList
|
, fieldList
|
||||||
|
, referenceKey
|
||||||
|
, noteBlock
|
||||||
|
, citationBlock
|
||||||
, directive
|
, directive
|
||||||
, anchor
|
, anchor
|
||||||
, comment
|
, comment
|
||||||
@@ -309,7 +349,10 @@ doubleHeader = do
|
|||||||
Just ind -> (headerTable, ind + 1)
|
Just ind -> (headerTable, ind + 1)
|
||||||
Nothing -> (headerTable ++ [DoubleHeader c], length headerTable + 1)
|
Nothing -> (headerTable ++ [DoubleHeader c], length headerTable + 1)
|
||||||
setState (state { stateHeaderTable = headerTable' })
|
setState (state { stateHeaderTable = headerTable' })
|
||||||
attr <- registerHeader nullAttr txt
|
attr@(ident,_,_) <- registerHeader nullAttr txt
|
||||||
|
let key = toKey (stringify txt)
|
||||||
|
updateState $ \s ->
|
||||||
|
s { stateKeys = M.insert key (("#" <> ident,""), nullAttr) $ stateKeys s }
|
||||||
return $ B.headerWith attr level txt
|
return $ B.headerWith attr level txt
|
||||||
|
|
||||||
doubleHeader' :: PandocMonad m => RSTParser m (Inlines, Char)
|
doubleHeader' :: PandocMonad m => RSTParser m (Inlines, Char)
|
||||||
@@ -338,7 +381,10 @@ singleHeader = do
|
|||||||
Just ind -> (headerTable, ind + 1)
|
Just ind -> (headerTable, ind + 1)
|
||||||
Nothing -> (headerTable ++ [SingleHeader c], length headerTable + 1)
|
Nothing -> (headerTable ++ [SingleHeader c], length headerTable + 1)
|
||||||
setState (state { stateHeaderTable = headerTable' })
|
setState (state { stateHeaderTable = headerTable' })
|
||||||
attr <- registerHeader nullAttr txt
|
attr@(ident,_,_) <- registerHeader nullAttr txt
|
||||||
|
let key = toKey (stringify txt)
|
||||||
|
updateState $ \s ->
|
||||||
|
s { stateKeys = M.insert key (("#" <> ident,""), nullAttr) $ stateKeys s }
|
||||||
return $ B.headerWith attr level txt
|
return $ B.headerWith attr level txt
|
||||||
|
|
||||||
singleHeader' :: PandocMonad m => RSTParser m (Inlines, Char)
|
singleHeader' :: PandocMonad m => RSTParser m (Inlines, Char)
|
||||||
@@ -1059,27 +1105,24 @@ mkAttr ident classes fields = (ident, classes, fields')
|
|||||||
--- note block
|
--- note block
|
||||||
---
|
---
|
||||||
|
|
||||||
noteBlock :: Monad m => RSTParser m Text
|
noteBlock :: Monad m => RSTParser m Blocks
|
||||||
noteBlock = try $ do
|
noteBlock = try $ do
|
||||||
(ref, raw, replacement) <- noteBlock' noteMarker
|
(ref, raw) <- noteBlock' noteMarker
|
||||||
updateState $ \s -> s { stateNotes = (ref, raw) : stateNotes s }
|
updateState $ \s -> s { stateNotes = (ref, raw) : stateNotes s }
|
||||||
-- return blanks so line count isn't affected
|
return mempty
|
||||||
return replacement
|
|
||||||
|
|
||||||
citationBlock :: Monad m => RSTParser m Text
|
citationBlock :: Monad m => RSTParser m Blocks
|
||||||
citationBlock = try $ do
|
citationBlock = try $ do
|
||||||
(ref, raw, replacement) <- noteBlock' citationMarker
|
(ref, raw) <- noteBlock' citationMarker
|
||||||
updateState $ \s ->
|
updateState $ \s ->
|
||||||
s { stateCitations = M.insert ref raw (stateCitations s),
|
s { stateCitations = M.insert ref raw (stateCitations s),
|
||||||
stateKeys = M.insert (toKey ref) (("#" <> ref,""), ("",["citation"],[]))
|
stateKeys = M.insert (toKey ref) (("#" <> ref,""), ("",["citation"],[]))
|
||||||
(stateKeys s) }
|
(stateKeys s) }
|
||||||
-- return blanks so line count isn't affected
|
return mempty
|
||||||
return replacement
|
|
||||||
|
|
||||||
noteBlock' :: Monad m
|
noteBlock' :: Monad m
|
||||||
=> RSTParser m Text -> RSTParser m (Text, Text, Text)
|
=> RSTParser m Text -> RSTParser m (Text, Text)
|
||||||
noteBlock' marker = try $ do
|
noteBlock' marker = try $ do
|
||||||
startPos <- getPosition
|
|
||||||
string ".."
|
string ".."
|
||||||
spaceChar >> skipMany spaceChar
|
spaceChar >> skipMany spaceChar
|
||||||
ref <- marker
|
ref <- marker
|
||||||
@@ -1087,10 +1130,8 @@ noteBlock' marker = try $ do
|
|||||||
<|> (newline >> return "")
|
<|> (newline >> return "")
|
||||||
blanks <- option "" blanklines
|
blanks <- option "" blanklines
|
||||||
rest <- option "" indentedBlock
|
rest <- option "" indentedBlock
|
||||||
endPos <- getPosition
|
|
||||||
let raw = first <> "\n" <> blanks <> rest <> "\n"
|
let raw = first <> "\n" <> blanks <> rest <> "\n"
|
||||||
let replacement = T.replicate (sourceLine endPos - sourceLine startPos) "\n"
|
return (ref, raw)
|
||||||
return (ref, raw, replacement)
|
|
||||||
|
|
||||||
citationMarker :: Monad m => RSTParser m Text
|
citationMarker :: Monad m => RSTParser m Text
|
||||||
citationMarker = do
|
citationMarker = do
|
||||||
@@ -1132,14 +1173,11 @@ simpleReferenceName = do
|
|||||||
referenceName :: PandocMonad m => RSTParser m Text
|
referenceName :: PandocMonad m => RSTParser m Text
|
||||||
referenceName = quotedReferenceName <|> simpleReferenceName
|
referenceName = quotedReferenceName <|> simpleReferenceName
|
||||||
|
|
||||||
referenceKey :: PandocMonad m => RSTParser m Text
|
referenceKey :: PandocMonad m => RSTParser m Blocks
|
||||||
referenceKey = do
|
referenceKey = do
|
||||||
startPos <- getPosition
|
|
||||||
choice [substKey, anonymousKey, regularKey]
|
choice [substKey, anonymousKey, regularKey]
|
||||||
optional blanklines
|
optional blanklines
|
||||||
endPos <- getPosition
|
return mempty
|
||||||
-- return enough blanks to replace key
|
|
||||||
return $ T.replicate (sourceLine endPos - sourceLine startPos) "\n"
|
|
||||||
|
|
||||||
targetURI :: Monad m => ParsecT Sources st m Text
|
targetURI :: Monad m => ParsecT Sources st m Text
|
||||||
targetURI = do
|
targetURI = do
|
||||||
@@ -1217,19 +1255,14 @@ regularKey = try $ do
|
|||||||
updateState $ \s -> s { stateKeys = M.insert key ((src,""), nullAttr) $
|
updateState $ \s -> s { stateKeys = M.insert key ((src,""), nullAttr) $
|
||||||
stateKeys s }
|
stateKeys s }
|
||||||
|
|
||||||
anchorDef :: PandocMonad m => RSTParser m Text
|
|
||||||
anchorDef = try $ do
|
|
||||||
(refs, raw) <- withRaw $ try (referenceNames <* blanklines)
|
|
||||||
forM_ refs $ \rawkey ->
|
|
||||||
updateState $ \s -> s { stateKeys =
|
|
||||||
M.insert (toKey rawkey) (("#" <> rawkey,""), nullAttr) $ stateKeys s }
|
|
||||||
-- keep this for 2nd round of parsing, where we'll add the divs (anchor)
|
|
||||||
return raw
|
|
||||||
|
|
||||||
anchor :: PandocMonad m => RSTParser m Blocks
|
anchor :: PandocMonad m => RSTParser m Blocks
|
||||||
anchor = try $ do
|
anchor = try $ do
|
||||||
refs <- referenceNames
|
refs <- referenceNames
|
||||||
blanklines
|
blanklines
|
||||||
|
forM_ refs $ \rawkey ->
|
||||||
|
updateState $ \s -> s { stateKeys =
|
||||||
|
M.insert (toKey rawkey) (("#" <> rawkey,""), nullAttr)
|
||||||
|
(stateKeys s) }
|
||||||
b <- block
|
b <- block
|
||||||
let addDiv ref = B.divWith (ref, [], [])
|
let addDiv ref = B.divWith (ref, [], [])
|
||||||
let emptySpanWithId id' = Span (id',[],[]) []
|
let emptySpanWithId id' = Span (id',[],[]) []
|
||||||
@@ -1245,16 +1278,6 @@ anchor = try $ do
|
|||||||
-- because it hides them from promoteHeader, see #4240
|
-- because it hides them from promoteHeader, see #4240
|
||||||
_ -> return $ foldr addDiv b refs
|
_ -> return $ foldr addDiv b refs
|
||||||
|
|
||||||
headerBlock :: PandocMonad m => RSTParser m Text
|
|
||||||
headerBlock = do
|
|
||||||
((txt, _), raw) <- withRaw (doubleHeader' <|> singleHeader')
|
|
||||||
(ident,_,_) <- registerHeader nullAttr txt
|
|
||||||
let key = toKey (stringify txt)
|
|
||||||
updateState $ \s -> s { stateKeys = M.insert key (("#" <> ident,""), nullAttr)
|
|
||||||
$ stateKeys s }
|
|
||||||
return raw
|
|
||||||
|
|
||||||
|
|
||||||
--
|
--
|
||||||
-- tables
|
-- tables
|
||||||
--
|
--
|
||||||
@@ -1628,25 +1651,17 @@ citationName = do
|
|||||||
raw <- citationMarker
|
raw <- citationMarker
|
||||||
return $ "[" <> raw <> "]"
|
return $ "[" <> raw <> "]"
|
||||||
|
|
||||||
|
-- We store the reference link label as the link target,
|
||||||
|
-- preceded by '##REF##'. This is replaced after the AST
|
||||||
|
-- has been built by the resolved reference.
|
||||||
referenceLink :: PandocMonad m => RSTParser m Inlines
|
referenceLink :: PandocMonad m => RSTParser m Inlines
|
||||||
referenceLink = try $ do
|
referenceLink = try $ do
|
||||||
ref <- (referenceName <|> citationName) <* char '_'
|
ref <- (referenceName <|> citationName) <* char '_'
|
||||||
let label' = B.text ref
|
isAnonymous <- (True <$ char '_') <|> pure False
|
||||||
let isAnonKey (Key (T.uncons -> Just ('_',_))) = True
|
let ref' = if isAnonymous
|
||||||
isAnonKey _ = False
|
then "_"
|
||||||
state <- getState
|
else ref
|
||||||
let keyTable = stateKeys state
|
pure $ B.linkWith nullAttr ("##REF##" <> ref') "" (B.text ref)
|
||||||
key <- option (toKey ref) $
|
|
||||||
do char '_'
|
|
||||||
let anonKeys = sort $ filter isAnonKey $ M.keys keyTable
|
|
||||||
case anonKeys of
|
|
||||||
[] -> mzero
|
|
||||||
(k:_) -> return k
|
|
||||||
((src,tit), attr) <- lookupKey [] key
|
|
||||||
-- if anonymous link, remove key so it won't be used again
|
|
||||||
when (isAnonKey key) $ updateState $ \s ->
|
|
||||||
s{ stateKeys = M.delete key keyTable }
|
|
||||||
return $ B.linkWith attr src tit label'
|
|
||||||
|
|
||||||
-- We keep a list of oldkeys so we can detect lookup loops.
|
-- We keep a list of oldkeys so we can detect lookup loops.
|
||||||
lookupKey :: PandocMonad m
|
lookupKey :: PandocMonad m
|
||||||
@@ -1666,6 +1681,10 @@ lookupKey oldkeys key = do
|
|||||||
let newkey = toKey rawkey
|
let newkey = toKey rawkey
|
||||||
if newkey `elem` oldkeys
|
if newkey `elem` oldkeys
|
||||||
then do
|
then do
|
||||||
|
-- TODO the pos is not going to be accurate
|
||||||
|
-- because we're calling this after the AST is
|
||||||
|
-- constructed. Probably good to remove that
|
||||||
|
-- parameter form CircularReference at some point.
|
||||||
logMessage $ CircularReference rawkey pos
|
logMessage $ CircularReference rawkey pos
|
||||||
return (("",""),nullAttr)
|
return (("",""),nullAttr)
|
||||||
else lookupKey (key:oldkeys) newkey
|
else lookupKey (key:oldkeys) newkey
|
||||||
@@ -1687,42 +1706,14 @@ autoLink = autoURI <|> autoEmail
|
|||||||
subst :: PandocMonad m => RSTParser m Inlines
|
subst :: PandocMonad m => RSTParser m Inlines
|
||||||
subst = try $ do
|
subst = try $ do
|
||||||
(_,ref) <- withRaw $ enclosed (char '|') (char '|') inline
|
(_,ref) <- withRaw $ enclosed (char '|') (char '|') inline
|
||||||
state <- getState
|
pure $ B.linkWith nullAttr ("##SUBST##" <> ref) "" (B.text ref)
|
||||||
let substTable = stateSubstitutions state
|
|
||||||
let key = toKey $ stripFirstAndLast ref
|
|
||||||
case M.lookup key substTable of
|
|
||||||
Nothing -> do
|
|
||||||
pos <- getPosition
|
|
||||||
logMessage $ ReferenceNotFound (tshow key) pos
|
|
||||||
return mempty
|
|
||||||
Just target -> return target
|
|
||||||
|
|
||||||
note :: PandocMonad m => RSTParser m Inlines
|
note :: PandocMonad m => RSTParser m Inlines
|
||||||
note = try $ do
|
note = try $ do
|
||||||
optional whitespace
|
optional whitespace
|
||||||
ref <- noteMarker
|
ref <- noteMarker
|
||||||
char '_'
|
char '_'
|
||||||
state <- getState
|
pure $ B.linkWith nullAttr ("##NOTE##" <> ref) "" (B.text ref)
|
||||||
let notes = stateNotes state
|
|
||||||
case lookup ref notes of
|
|
||||||
Nothing -> do
|
|
||||||
pos <- getPosition
|
|
||||||
logMessage $ ReferenceNotFound ref pos
|
|
||||||
return mempty
|
|
||||||
Just raw -> do
|
|
||||||
-- We temporarily empty the note list while parsing the note,
|
|
||||||
-- so that we don't get infinite loops with notes inside notes...
|
|
||||||
-- Note references inside other notes are allowed in reST, but
|
|
||||||
-- not yet in this implementation.
|
|
||||||
updateState $ \st -> st{ stateNotes = [] }
|
|
||||||
contents <- parseFromString' parseBlocks raw
|
|
||||||
let newnotes = if ref == "*" || ref == "#" -- auto-numbered
|
|
||||||
-- delete the note so the next auto-numbered note
|
|
||||||
-- doesn't get the same contents:
|
|
||||||
then deleteFirstsBy (==) notes [(ref,raw)]
|
|
||||||
else notes
|
|
||||||
updateState $ \st -> st{ stateNotes = newnotes }
|
|
||||||
return $ B.note contents
|
|
||||||
|
|
||||||
smart :: PandocMonad m => RSTParser m Inlines
|
smart :: PandocMonad m => RSTParser m Inlines
|
||||||
smart = smartPunctuation inline
|
smart = smartPunctuation inline
|
||||||
|
|||||||
@@ -0,0 +1,68 @@
|
|||||||
|
```
|
||||||
|
% pandoc -f rst -t native
|
||||||
|
`Want Speed? Pass by Value`_
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
For more information about the pass-by-value idiom, read: `Want Speed? Pass by Value`_.
|
||||||
|
|
||||||
|
.. _Want Speed? Pass by Value: https://web.archive.org/web/20140205194657/http://cpp-next.com/archive/2009/08/want-speed-pass-by-value/
|
||||||
|
^D
|
||||||
|
[ Para
|
||||||
|
[ Link
|
||||||
|
( "" , [] , [] )
|
||||||
|
[ Str "Want"
|
||||||
|
, Space
|
||||||
|
, Str "Speed?"
|
||||||
|
, Space
|
||||||
|
, Str "Pass"
|
||||||
|
, Space
|
||||||
|
, Str "by"
|
||||||
|
, Space
|
||||||
|
, Str "Value"
|
||||||
|
]
|
||||||
|
( "https://web.archive.org/web/20140205194657/http://cpp-next.com/archive/2009/08/want-speed-pass-by-value/"
|
||||||
|
, ""
|
||||||
|
)
|
||||||
|
]
|
||||||
|
, Div
|
||||||
|
( "" , [ "note" ] , [] )
|
||||||
|
[ Div ( "" , [ "title" ] , [] ) [ Para [ Str "Note" ] ]
|
||||||
|
, Para
|
||||||
|
[ Str "For"
|
||||||
|
, Space
|
||||||
|
, Str "more"
|
||||||
|
, Space
|
||||||
|
, Str "information"
|
||||||
|
, Space
|
||||||
|
, Str "about"
|
||||||
|
, Space
|
||||||
|
, Str "the"
|
||||||
|
, Space
|
||||||
|
, Str "pass-by-value"
|
||||||
|
, Space
|
||||||
|
, Str "idiom,"
|
||||||
|
, Space
|
||||||
|
, Str "read:"
|
||||||
|
, Space
|
||||||
|
, Link
|
||||||
|
( "" , [] , [] )
|
||||||
|
[ Str "Want"
|
||||||
|
, Space
|
||||||
|
, Str "Speed?"
|
||||||
|
, Space
|
||||||
|
, Str "Pass"
|
||||||
|
, Space
|
||||||
|
, Str "by"
|
||||||
|
, Space
|
||||||
|
, Str "Value"
|
||||||
|
]
|
||||||
|
( "https://web.archive.org/web/20140205194657/http://cpp-next.com/archive/2009/08/want-speed-pass-by-value/"
|
||||||
|
, ""
|
||||||
|
)
|
||||||
|
, Str "."
|
||||||
|
]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
+1
-1
@@ -37,7 +37,7 @@ Loop detection:
|
|||||||
|
|
||||||
__ link1_
|
__ link1_
|
||||||
^D
|
^D
|
||||||
2> [WARNING] Circular reference 'link1' at line 1 column 15
|
2> [WARNING] Circular reference 'link1' at line 8 column 1
|
||||||
<p><a href="">click here</a></p>
|
<p><a href="">click here</a></p>
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user