Don't throw errors on RST tables in Markdown and RstMarkdown modes (#22165)

* Don't throw errors on RST tables in Markdown and RstMarkdown modes

Additions to RST simple tables (#19859) made their parsing more
restrictive, which can introduce problems with of some old
nimforum posts, which have tables with sloppily aligned columns
(like this one:
https://github.com/nim-lang/nimforum/issues/330#issuecomment-1376039966).
Also this strictness contradicts to Markdown style of not getting
in the way (ignoring errors).

So this PR proposes a new strategy of dealing with errors:
* In Markdown and legacy (old default) RstMarkdown we try to
  continue parsing, emitting only warnings
* And only in pure RST mode we throw a error

I expect that this strategy will be applied to more parts of markup code
in the future.

* Don't return anything in `checkColumns`
This commit is contained in:
Andrey Makarov
2023-06-28 14:38:54 -06:00
committed by GitHub
parent b35942ef83
commit 57de460437
2 changed files with 62 additions and 15 deletions

View File

@@ -585,6 +585,29 @@ proc rstMessage(p: RstParser, msgKind: MsgKind) =
p.col + currentTok(p).col, msgKind,
currentTok(p).symbol)
# Functions `isPureRst` & `stopOrWarn` address differences between
# Markdown and RST:
# * Markdown always tries to continue working. If it is really impossible
# to parse a markup element, its proc just returns `nil` and parsing
# continues for it as for normal text paragraph.
# The downside is that real mistakes/typos are often silently ignored.
# The same applies to legacy `RstMarkdown` mode for nimforum.
# * RST really signals errors. The downside is that it's more intrusive -
# the user must escape special syntax with \ explicitly.
#
# TODO: we need to apply this strategy to all markup elements eventually.
func isPureRst(p: RstParser): bool =
roSupportMarkdown notin p.s.options
proc stopOrWarn(p: RstParser, errorType: MsgKind, arg: string) =
let realMsgKind = if isPureRst(p): errorType else: mwRstStyle
rstMessage(p, realMsgKind, arg)
proc stopOrWarn(p: RstParser, errorType: MsgKind, arg: string, line, col: int) =
let realMsgKind = if isPureRst(p): errorType else: mwRstStyle
rstMessage(p, realMsgKind, arg, line, col)
proc currInd(p: RstParser): int =
result = p.indentStack[high(p.indentStack)]
@@ -2596,11 +2619,11 @@ proc getColumns(p: RstParser, cols: var RstCols, startIdx: int): int =
proc checkColumns(p: RstParser, cols: RstCols) =
var i = p.idx
if p.tok[i].symbol[0] != '=':
rstMessage(p, mwRstStyle,
stopOrWarn(p, meIllformedTable,
"only tables with `=` columns specification are allowed")
for col in 0 ..< cols.len:
if tokEnd(p, i) != cols[col].stop:
rstMessage(p, meIllformedTable,
stopOrWarn(p, meIllformedTable,
"end of table column #$1 should end at position $2" % [
$(col+1), $(cols[col].stop+ColRstOffset)],
p.tok[i].line, tokEnd(p, i))
@@ -2609,12 +2632,12 @@ proc checkColumns(p: RstParser, cols: RstCols) =
if p.tok[i].kind == tkWhite:
inc i
if p.tok[i].kind notin {tkIndent, tkEof}:
rstMessage(p, meIllformedTable, "extraneous column specification")
stopOrWarn(p, meIllformedTable, "extraneous column specification")
elif p.tok[i].kind == tkWhite:
inc i
else:
rstMessage(p, meIllformedTable, "no enough table columns",
p.tok[i].line, p.tok[i].col)
stopOrWarn(p, meIllformedTable,
"no enough table columns", p.tok[i].line, p.tok[i].col)
proc getSpans(p: RstParser, nextLine: int,
cols: RstCols, unitedCols: RstCols): seq[int] =
@@ -2669,17 +2692,18 @@ proc parseSimpleTableRow(p: var RstParser, cols: RstCols, colChar: char): PRstNo
if tokEnd(p) <= colEnd(nCell):
if tokStart(p) < colStart(nCell):
if currentTok(p).kind != tkWhite:
rstMessage(p, meIllformedTable,
stopOrWarn(p, meIllformedTable,
"this word crosses table column from the left")
else:
inc p.idx
row[nCell].add(currentTok(p).symbol)
else:
row[nCell].add(currentTok(p).symbol)
inc p.idx
inc p.idx
else:
if tokStart(p) < colEnd(nCell) and currentTok(p).kind != tkWhite:
rstMessage(p, meIllformedTable,
stopOrWarn(p, meIllformedTable,
"this word crosses table column from the right")
row[nCell].add(currentTok(p).symbol)
inc p.idx
inc nCell
if currentTok(p).kind == tkIndent: inc p.idx
if tokEnd(p) <= colEnd(0): break

View File

@@ -29,7 +29,9 @@ import os
import std/[assertions, syncio]
const preferMarkdown = {roPreferMarkdown, roSupportMarkdown, roNimFile, roSandboxDisabled}
# legacy nimforum / old default mode:
const preferRst = {roSupportMarkdown, roNimFile, roSandboxDisabled}
const pureRst = {roNimFile, roSandboxDisabled}
proc toAst(input: string,
rstOptions: RstParseOptions = preferMarkdown,
@@ -917,10 +919,31 @@ suite "RST tables":
====== ======
Inputs Output
====== ======
""".toAst(error=error) == "")
""".toAst(rstOptions = pureRst, error = error) == "")
check(error[] == "input(2, 2) Error: Illformed table: " &
"this word crosses table column from the right")
# In nimforum compatibility mode & Markdown we raise a warning instead:
let expected = dedent"""
rnTable colCount=2
rnTableRow
rnTableDataCell
rnLeaf 'Inputs'
rnTableDataCell
rnLeaf 'Output'
"""
for opt in [preferRst, preferMarkdown]:
var warnings = new seq[string]
check(
dedent"""
====== ======
Inputs Output
====== ======
""".toAst(rstOptions = opt, warnings = warnings) == expected)
check(warnings[] == @[
"input(2, 2) Warning: RST style: this word crosses table column from the right"])
test "tables with slightly overflowed cells cause an error (2)":
var error = new string
check("" == dedent"""
@@ -929,7 +952,7 @@ suite "RST tables":
===== ===== ======
False False False
===== ===== ======
""".toAst(error=error))
""".toAst(rstOptions = pureRst, error = error))
check(error[] == "input(2, 8) Error: Illformed table: " &
"this word crosses table column from the right")
@@ -941,7 +964,7 @@ suite "RST tables":
===== ===== ======
False False False
===== ===== ======
""".toAst(error=error))
""".toAst(rstOptions = pureRst, error = error))
check(error[] == "input(2, 7) Error: Illformed table: " &
"this word crosses table column from the left")
@@ -954,7 +977,7 @@ suite "RST tables":
===== ======
False False
===== =======
""".toAst(error=error))
""".toAst(rstOptions = pureRst, error = error))
check(error[] == "input(5, 14) Error: Illformed table: " &
"end of table column #2 should end at position 13")
@@ -966,7 +989,7 @@ suite "RST tables":
===== =======
False False
===== ======
""".toAst(error=error))
""".toAst(rstOptions = pureRst, error = error))
check(error[] == "input(3, 14) Error: Illformed table: " &
"end of table column #2 should end at position 13")