[parsing/mfm] Fix incorrect handling of lists

This commit is contained in:
Laura Hausmann 2024-10-24 22:42:04 +02:00
parent 27ac36d747
commit 5349346f52
No known key found for this signature in database
GPG key ID: D044E84C5BE01605
2 changed files with 41 additions and 8 deletions

View file

@ -174,6 +174,17 @@ module private MfmParser =
| None -> None | None -> None
| Some items -> items |> dict |> Some | Some items -> items |> dict |> Some
let pushLine: Parser<unit, int64> =
fun stream ->
stream.UserState <- stream.Line
Reply(())
let assertLine: Parser<unit, int64> =
fun stream ->
match stream.UserState = stream.Line with
| true -> Reply(())
| false -> Reply(Error, messageError "Line changed")
// References // References
let node, nodeRef = createParserForwardedToRef () let node, nodeRef = createParserForwardedToRef ()
let inlineNode, inlineNodeRef = createParserForwardedToRef () let inlineNode, inlineNodeRef = createParserForwardedToRef ()
@ -195,21 +206,25 @@ module private MfmParser =
// Node parsers // Node parsers
let italicNode = let italicNode =
(italicPattern >>. manyTill inlineNode italicPattern) (italicPattern >>. pushLine >>. manyTill inlineNode italicPattern .>> assertLine)
<|> (skipString "<i>" >>. manyTill inlineNode (skipString "</i>")) <|> (skipString "<i>" >>. pushLine >>. manyTill inlineNode (skipString "</i>")
.>> assertLine)
|>> fun c -> MfmItalicNode(aggregateTextInline c) :> MfmNode |>> fun c -> MfmItalicNode(aggregateTextInline c) :> MfmNode
let boldNode = let boldNode =
(skipString "**" >>. manyTill inlineNode (skipString "**")) (skipString "**" >>. pushLine >>. manyTill inlineNode (skipString "**")
<|> (skipString "<b>" >>. manyTill inlineNode (skipString "</b>")) .>> assertLine)
<|> (skipString "<b>" >>. pushLine >>. manyTill inlineNode (skipString "</b>")
.>> assertLine)
|>> fun c -> MfmBoldNode(aggregateTextInline c) :> MfmNode |>> fun c -> MfmBoldNode(aggregateTextInline c) :> MfmNode
let strikeNode = let strikeNode =
skipString "~~" >>. manyTill inlineNode (skipString "~~") skipString "~~" >>. pushLine >>. manyTill inlineNode (skipString "~~")
.>> assertLine
|>> fun c -> MfmStrikeNode(aggregateTextInline c) :> MfmNode |>> fun c -> MfmStrikeNode(aggregateTextInline c) :> MfmNode
let codeNode = let codeNode =
codePattern >>. manyCharsTill anyChar codePattern codePattern >>. pushLine >>. manyCharsTill anyChar codePattern .>> assertLine
|>> fun v -> MfmInlineCodeNode(v) :> MfmNode |>> fun v -> MfmInlineCodeNode(v) :> MfmNode
let codeBlockNode = let codeBlockNode =
@ -225,7 +240,8 @@ module private MfmParser =
|>> fun (lang: string option, code: string) -> MfmCodeBlockNode(code, lang) :> MfmNode |>> fun (lang: string option, code: string) -> MfmCodeBlockNode(code, lang) :> MfmNode
let mathNode = let mathNode =
skipString "\(" >>. manyCharsTill anyChar (skipString "\)") skipString "\(" >>. pushLine >>. manyCharsTill anyChar (skipString "\)")
.>> assertLine
|>> fun f -> MfmMathInlineNode(f) :> MfmNode |>> fun f -> MfmMathInlineNode(f) :> MfmNode
let mathBlockNode = let mathBlockNode =
@ -368,6 +384,7 @@ module private MfmParser =
// Populate references // Populate references
do nodeRef.Value <- choice <| seqAttempt (seqFlatten <| nodeSeq) do nodeRef.Value <- choice <| seqAttempt (seqFlatten <| nodeSeq)
do inlineNodeRef.Value <- choice <| (seqAttempt inlineNodeSeq) |>> fun v -> v :?> MfmInlineNode do inlineNodeRef.Value <- choice <| (seqAttempt inlineNodeSeq) |>> fun v -> v :?> MfmInlineNode
// Final parse command // Final parse command
@ -377,6 +394,6 @@ open MfmParser
module Mfm = module Mfm =
let parse str = let parse str =
match run parse str with match runParserOnString parse 0 "" str with
| Success(result, _, _) -> aggregateText result | Success(result, _, _) -> aggregateText result
| Failure(s, _, _) -> failwith $"Failed to parse MFM: {s}" | Failure(s, _, _) -> failwith $"Failed to parse MFM: {s}"

View file

@ -34,6 +34,22 @@ public class MfmTests
resMixedAlt.Should().Equal(expected, MfmNodeEqual); resMixedAlt.Should().Equal(expected, MfmNodeEqual);
} }
[TestMethod]
public void TestParseList()
{
const string input = """
* test
* test2
* test3
""";
List<MfmNode> expected = [new MfmTextNode("* test\n* test2\n* test3")];
var res = Mfm.parse(input).ToList();
res.Should().Equal(expected, MfmNodeEqual);
MfmSerializer.Serialize(res).Should().BeEquivalentTo(input);
}
[TestMethod] [TestMethod]
public void TestParseCode() public void TestParseCode()
{ {