parent
a0daa7a76d
commit
02f4464058
9 changed files with 277 additions and 57 deletions
|
|
@ -8,9 +8,13 @@
|
|||
#
|
||||
|
||||
## ==================================
|
||||
## rst: Nim-flavored reStructuredText
|
||||
## rst
|
||||
## ==================================
|
||||
##
|
||||
## ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
## Nim-flavored reStructuredText
|
||||
## ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
##
|
||||
## This module implements a `reStructuredText`:idx: (RST) parser.
|
||||
## A large subset is implemented with some limitations_ and
|
||||
## `Nim-specific features`_.
|
||||
|
|
@ -410,7 +414,14 @@ proc getTokens(buffer: string, skipPounds: bool, tokens: var TokenSeq): int =
|
|||
tokens[0].kind = tkIndent
|
||||
|
||||
type
|
||||
LevelMap = array[char, int]
|
||||
LevelInfo = object
|
||||
symbol: char # adornment character
|
||||
hasOverline: bool # has also overline (besides underline)?
|
||||
line: int # the last line of this style occurrence
|
||||
# (for error message)
|
||||
hasPeers: bool # has headings on the same level of hierarchy?
|
||||
LevelMap = seq[LevelInfo] # Saves for each possible title adornment
|
||||
# style its level in the current document.
|
||||
Substitution = object
|
||||
key*: string
|
||||
value*: PRstNode
|
||||
|
|
@ -433,7 +444,10 @@ type
|
|||
|
||||
SharedState = object
|
||||
options: RstParseOptions # parsing options
|
||||
uLevel, oLevel: int # counters for the section levels
|
||||
hLevels: LevelMap # hierarchy of heading styles
|
||||
hTitleCnt: int # =0 if no title, =1 if only main title,
|
||||
# =2 if both title and subtitle are present
|
||||
hCurLevel: int # current section level
|
||||
subs: seq[Substitution] # substitutions
|
||||
refs: seq[Substitution] # references
|
||||
anchors: seq[AnchorSubst] # internal target substitutions
|
||||
|
|
@ -443,14 +457,6 @@ type
|
|||
lineFootnoteSymRef: seq[int] # footnote line, their reference [*]_
|
||||
footnotes: seq[FootnoteSubst] # correspondence b/w footnote label,
|
||||
# number, order of occurrence
|
||||
underlineToLevel: LevelMap # Saves for each possible title adornment
|
||||
# character its level in the
|
||||
# current document.
|
||||
# This is for single underline adornments.
|
||||
overlineToLevel: LevelMap # Saves for each possible title adornment
|
||||
# character its level in the current
|
||||
# document.
|
||||
# This is for over-underline adornments.
|
||||
msgHandler: MsgHandler # How to handle errors.
|
||||
findFile: FindFileHandler # How to find files.
|
||||
|
||||
|
|
@ -1408,11 +1414,30 @@ proc parseLiteralBlock(p: var RstParser): PRstNode =
|
|||
inc p.idx
|
||||
result.add(n)
|
||||
|
||||
proc getLevel(map: var LevelMap, lvl: var int, c: char): int =
|
||||
if map[c] == 0:
|
||||
inc lvl
|
||||
map[c] = lvl
|
||||
result = map[c]
|
||||
proc getLevel(p: var RstParser, c: char, hasOverline: bool): int =
|
||||
## Returns (preliminary) heading level corresponding to `c` and
|
||||
## `hasOverline`. If level does not exist, add it first.
|
||||
for i, hType in p.s.hLevels:
|
||||
if hType.symbol == c and hType.hasOverline == hasOverline:
|
||||
p.s.hLevels[i].line = curLine(p)
|
||||
p.s.hLevels[i].hasPeers = true
|
||||
return i
|
||||
p.s.hLevels.add LevelInfo(symbol: c, hasOverline: hasOverline,
|
||||
line: curLine(p), hasPeers: false)
|
||||
result = p.s.hLevels.len - 1
|
||||
|
||||
proc countTitles(p: var RstParser, n: PRstNode) =
|
||||
## Fill `p.s.hTitleCnt`
|
||||
for node in n.sons:
|
||||
if node != nil:
|
||||
if node.kind notin {rnOverline, rnSubstitutionDef, rnDefaultRole}:
|
||||
break
|
||||
if node.kind == rnOverline:
|
||||
if p.s.hLevels[p.s.hTitleCnt].hasPeers:
|
||||
break
|
||||
inc p.s.hTitleCnt
|
||||
if p.s.hTitleCnt >= 2:
|
||||
break
|
||||
|
||||
proc tokenAfterNewline(p: RstParser): int =
|
||||
result = p.idx
|
||||
|
|
@ -1529,7 +1554,7 @@ proc whichSection(p: RstParser): RstNodeKind =
|
|||
result = rnLeaf
|
||||
of tkPunct:
|
||||
if isMarkdownHeadline(p):
|
||||
result = rnHeadline
|
||||
result = rnMarkdownHeadline
|
||||
elif roSupportMarkdown in p.s.options and predNL(p) and
|
||||
match(p, p.idx, "| w") and findPipe(p, p.idx+3):
|
||||
result = rnMarkdownTable
|
||||
|
|
@ -1599,7 +1624,8 @@ proc parseParagraph(p: var RstParser, result: PRstNode) =
|
|||
elif currentTok(p).ival == currInd(p):
|
||||
inc p.idx
|
||||
case whichSection(p)
|
||||
of rnParagraph, rnLeaf, rnHeadline, rnOverline, rnDirective:
|
||||
of rnParagraph, rnLeaf, rnHeadline, rnMarkdownHeadline,
|
||||
rnOverline, rnDirective:
|
||||
result.add newLeaf(" ")
|
||||
of rnLineBlock:
|
||||
result.addIfNotNil(parseLineBlock(p))
|
||||
|
|
@ -1620,20 +1646,60 @@ proc parseParagraph(p: var RstParser, result: PRstNode) =
|
|||
parseInline(p, result)
|
||||
else: break
|
||||
|
||||
proc checkHeadingHierarchy(p: RstParser, lvl: int) =
|
||||
if lvl - p.s.hCurLevel > 1: # broken hierarchy!
|
||||
proc descr(l: int): string =
|
||||
(if p.s.hLevels[l].hasOverline: "overline " else: "underline ") &
|
||||
repeat(p.s.hLevels[l].symbol, 5)
|
||||
var msg = "(section level inconsistent: "
|
||||
msg.add descr(lvl) & " unexpectedly found, " &
|
||||
"while the following intermediate section level(s) are missing on lines "
|
||||
msg.add $p.s.hLevels[p.s.hCurLevel].line & ".." & $curLine(p) & ":"
|
||||
for l in p.s.hCurLevel+1 .. lvl-1:
|
||||
msg.add " " & descr(l)
|
||||
if l != lvl-1: msg.add ","
|
||||
rstMessage(p, meNewSectionExpected, msg & ")")
|
||||
|
||||
proc parseHeadline(p: var RstParser): PRstNode =
|
||||
result = newRstNode(rnHeadline)
|
||||
if isMarkdownHeadline(p):
|
||||
result = newRstNode(rnMarkdownHeadline)
|
||||
# Note that level hierarchy is not checked for markdown headings
|
||||
result.level = currentTok(p).symbol.len
|
||||
assert(nextTok(p).kind == tkWhite)
|
||||
inc p.idx, 2
|
||||
parseUntilNewline(p, result)
|
||||
else:
|
||||
result = newRstNode(rnHeadline)
|
||||
parseUntilNewline(p, result)
|
||||
assert(currentTok(p).kind == tkIndent)
|
||||
assert(nextTok(p).kind == tkAdornment)
|
||||
var c = nextTok(p).symbol[0]
|
||||
inc p.idx, 2
|
||||
result.level = getLevel(p.s.underlineToLevel, p.s.uLevel, c)
|
||||
result.level = getLevel(p, c, hasOverline=false)
|
||||
checkHeadingHierarchy(p, result.level)
|
||||
p.s.hCurLevel = result.level
|
||||
addAnchor(p, rstnodeToRefname(result), reset=true)
|
||||
|
||||
proc parseOverline(p: var RstParser): PRstNode =
|
||||
var c = currentTok(p).symbol[0]
|
||||
inc p.idx, 2
|
||||
result = newRstNode(rnOverline)
|
||||
while true:
|
||||
parseUntilNewline(p, result)
|
||||
if currentTok(p).kind == tkIndent:
|
||||
inc p.idx
|
||||
if prevTok(p).ival > currInd(p):
|
||||
result.add newLeaf(" ")
|
||||
else:
|
||||
break
|
||||
else:
|
||||
break
|
||||
result.level = getLevel(p, c, hasOverline=true)
|
||||
checkHeadingHierarchy(p, result.level)
|
||||
p.s.hCurLevel = result.level
|
||||
if currentTok(p).kind == tkAdornment:
|
||||
inc p.idx
|
||||
if currentTok(p).kind == tkIndent: inc p.idx
|
||||
addAnchor(p, rstnodeToRefname(result), reset=true)
|
||||
|
||||
type
|
||||
|
|
@ -1779,26 +1845,6 @@ proc parseTransition(p: var RstParser): PRstNode =
|
|||
if currentTok(p).kind == tkIndent: inc p.idx
|
||||
if currentTok(p).kind == tkIndent: inc p.idx
|
||||
|
||||
proc parseOverline(p: var RstParser): PRstNode =
|
||||
var c = currentTok(p).symbol[0]
|
||||
inc p.idx, 2
|
||||
result = newRstNode(rnOverline)
|
||||
while true:
|
||||
parseUntilNewline(p, result)
|
||||
if currentTok(p).kind == tkIndent:
|
||||
inc p.idx
|
||||
if prevTok(p).ival > currInd(p):
|
||||
result.add newLeaf(" ")
|
||||
else:
|
||||
break
|
||||
else:
|
||||
break
|
||||
result.level = getLevel(p.s.overlineToLevel, p.s.oLevel, c)
|
||||
if currentTok(p).kind == tkAdornment:
|
||||
inc p.idx # XXX: check?
|
||||
if currentTok(p).kind == tkIndent: inc p.idx
|
||||
addAnchor(p, rstnodeToRefname(result), reset=true)
|
||||
|
||||
proc parseBulletList(p: var RstParser): PRstNode =
|
||||
result = nil
|
||||
if nextTok(p).kind == tkWhite:
|
||||
|
|
@ -1982,7 +2028,7 @@ proc parseSection(p: var RstParser, result: PRstNode) =
|
|||
if p.idx > 0: dec p.idx
|
||||
a = parseFields(p)
|
||||
of rnTransition: a = parseTransition(p)
|
||||
of rnHeadline: a = parseHeadline(p)
|
||||
of rnHeadline, rnMarkdownHeadline: a = parseHeadline(p)
|
||||
of rnOverline: a = parseOverline(p)
|
||||
of rnTable: a = parseSimpleTable(p)
|
||||
of rnMarkdownTable: a = parseMarkdownTable(p)
|
||||
|
|
@ -2399,6 +2445,15 @@ proc resolveSubs(p: var RstParser, n: PRstNode): PRstNode =
|
|||
var e = getEnv(key)
|
||||
if e != "": result = newLeaf(e)
|
||||
else: rstMessage(p, mwUnknownSubstitution, key)
|
||||
of rnHeadline, rnOverline:
|
||||
# fix up section levels depending on presence of a title and subtitle
|
||||
if p.s.hTitleCnt == 2:
|
||||
if n.level == 1: # it's the subtitle
|
||||
n.level = 0
|
||||
elif n.level >= 2: # normal sections
|
||||
n.level -= 1
|
||||
elif p.s.hTitleCnt == 0:
|
||||
n.level += 1
|
||||
of rnRef:
|
||||
let refn = rstnodeToRefname(n)
|
||||
var y = findRef(p, refn)
|
||||
|
|
@ -2498,6 +2553,7 @@ proc rstParse*(text, filename: string,
|
|||
p.line = line
|
||||
p.col = column + getTokens(text, roSkipPounds in options, p.tok)
|
||||
let unresolved = parseDoc(p)
|
||||
countTitles(p, unresolved)
|
||||
orderFootnotes(p)
|
||||
result = resolveSubs(p, unresolved)
|
||||
hasToc = p.hasToc
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue