Skip to content

Commit

Permalink
Sublists?
Browse files Browse the repository at this point in the history
  • Loading branch information
audrey-yang committed Dec 15, 2021
1 parent 3d4c43c commit 062c1be
Show file tree
Hide file tree
Showing 10 changed files with 176 additions and 59 deletions.
4 changes: 4 additions & 0 deletions sample.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<html><h1>Heading 1</h1><hr><p>This is <code>inline code</code>. </p><h5>A smaller heading</h5><p><b>bold</b>, <i>italic</i>, <del>struckthrough</del></p><pre><code>fold :: (a -> b -> b) -> b -> [a] -> b
fold f z [] = z
fold f z (x:xs) = f x (fold f z xs)
</code></pre><ol start="3"><li><p>This is a numbered list.</p></li><li><p>This is the second item.</p></li><li><p>This is the third item.</p></li><ol start="1"><li><p>This is the first sub-item</p></li></ol></ol><ul><li><p>This is an unordered list.</p></li><ul><li><p>This is also an unordered list</p></li><li><p>With another element</p></li><ul><li><p>This too</p></li></ul></ul><li><p>With another list item and <b>bold text</b></p></li></ul><p>A regular paragraph with a <a href="www.google.com">this is a link</a>.</p><img alt="This is an image example" src="quokka.jpeg"><blockquote><p>Quotable quotes</p><p>"Quote me!"</p></blockquote><table><thead><tr><th>Syntax </th><th>Description </th></tr></thead><tbody><tr><td>Header </td><td><b>Title</b> </td></tr><tr><td>Paragraph </td><td><code>Code</code> </td></tr></tbody></table><p>The end.</p></html>
4 changes: 4 additions & 0 deletions sample.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,12 @@ fold f z (x:xs) = f x (fold f z xs)
3. This is a numbered list.
4. This is the second item.
5. This is the third item.
1. This is the first sub-item

- This is an unordered list.
- This is also an unordered list
- With another element
- This too
- With another list item and **bold text**

A regular paragraph with a [this is a link](www.google.com).
Expand Down
20 changes: 16 additions & 4 deletions src/HTMLParser.hs
Original file line number Diff line number Diff line change
Expand Up @@ -44,16 +44,28 @@ hHeadingP = choice [checkHeader i | i <- [1 .. 6]]
checkHeader i = try $ Heading i <$> lineContainer ("h" ++ show i)

-- | Parses for a list item
hLiP :: Parser S.Line
hLiP = lineContainer "li"
hLiP :: Parser Block
hLiP = betweenTag "li" hBlockP

hSubListP :: Int -> Parser Block
hSubListP level = try (hSubOlListP (level + 1)) <|> hSubUlListP (level + 1)

-- | Parses for an unordered list (<ul><li></li>...</ul>)
hUlListP :: Parser Block
hUlListP = UnorderedList <$> container "ul" hLiP
hUlListP = hSubUlListP 0

hSubUlListP :: Int -> Parser Block
hSubUlListP level = flip UnorderedList level <$>
container "ul" (try (hSubListP level) <|> hLiP)

-- | Parses for an ordered list (<ol><li></li>...</ol>)
hOlListP :: Parser Block
hOlListP = OrderedList <$> ((,) <$> (read <$> openingWithAttr "ol" "start") <*> manyTill hLiP (try (closingTag "ol")))
hOlListP = hSubOlListP 0

hSubOlListP :: Int -> Parser Block
hSubOlListP level = flip OrderedList level <$>
((,) <$> (read <$> openingWithAttr "ol" "start") <*>
manyTill (try (hSubListP level) <|> hLiP) (try (closingTag "ol")))

-- | Parses for an image (<img src=\"url\">()
hImgP :: Parser Block
Expand Down
13 changes: 9 additions & 4 deletions src/HTMLPrettyPrinter.hs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module HTMLPrettyPrinter where

import Syntax (Block (..), Doc (Doc), Line, TableBody (..), TableCell (..), TableHead (..), TableRow (..), Text (..))
import Syntax (Block (..), Doc (Doc), Line (..), TableBody (..), TableCell (..), TableHead (..), TableRow (..), Text (..))
import qualified Syntax as S
import Text.PrettyPrint hiding (braces, parens, sep, (<>))
import qualified Text.PrettyPrint as PP
Expand All @@ -18,16 +18,21 @@ instance PP S.Doc where
instance PP S.Block where
pp (S.Heading n l) = tag ("h" ++ show n) (pp l)
pp (S.Paragraph l) = tag "p" (pp l)
pp (S.OrderedList (startVal, ls)) =
tagWithAttrs "ol" [("start", show startVal)] (PP.hcat $ map (tag "li" . pp) ls)
pp (S.UnorderedList ls) = tag "ul" (PP.hcat $ map (tag "li" . pp) ls)
pp (S.OrderedList (startVal, ls) _) =
tagWithAttrs "ol" [("start", show startVal)] (PP.hcat $ map tagIfNotSublist ls)
pp (S.UnorderedList ls _) = tag "ul" (PP.hcat $ map tagIfNotSublist ls)
pp (S.Image alt src) = tagWithAttrs "img" [("alt", alt), ("src", src)] mempty
pp (S.BlockQuote ls) = tag "blockquote" (PP.hcat $ map (tag "p" . pp) ls)
pp (S.CodeBlock str) = tag "pre" $ tag "code" (PP.text str)
pp S.Hr = PP.text "<hr>"
pp S.Br = PP.text "<br>"
pp (S.Table thead tbody) = tag "table" $ PP.hcat [pp thead, pp tbody]

tagIfNotSublist :: Block -> PP.Doc
tagIfNotSublist x@(UnorderedList _ _) = pp x
tagIfNotSublist x@(OrderedList _ _) = pp x
tagIfNotSublist x = (tag "li" . pp) x

instance PP S.Line where
pp (S.Line ts) = PP.hcat (map pp ts)

Expand Down
60 changes: 43 additions & 17 deletions src/MarkdownParser.hs
Original file line number Diff line number Diff line change
Expand Up @@ -44,29 +44,55 @@ headingP = do
Monad.guard (length hx < 7)
Heading (length hx) <$> lineP

liP :: Parser Block
liP = tryLiP <* many newLineChar
where
tryLiP = try brP
<|> try hrP
<|> try headingP
<|> try quoteP
<|> try codeBlockP
<|> try tableP
<|> paragraphP

subListP :: Int -> Parser Block
subListP level =
try (wsP (string $ replicate level '\t') *> subUlListP level)
<|> (wsP (string $ replicate level '\t') *> subOlListP level)

-- | Parses for an unordered list (- list item)
ulListP :: Parser Block
ulListP =
UnorderedList <$> do
-- first hyphen must have at least one space after
wsP (string "- " <|> string "* ")
firstItem <- lineP
remainingItems <- many $
do
wsP (string "-" <|> string "*")
lineP
ulListP = subUlListP 0

subUlListP :: Int -> Parser Block
subUlListP level =
flip UnorderedList level <$> do
wsP (string "- " <|> string "* ") -- first hyphen must have at least one space after
firstItem <- liP
remainingItems <- many (
try (subListP (level + 1))
<|> do
string (replicate level '\t') *> wsP (string "-" <|> string "*")
liP
)
return $ firstItem : remainingItems

-- | Parses for an ordered list (1. list item)
olListP :: Parser Block
olListP =
OrderedList <$> do
startVal <- int <* wsP (string ". ")
firstItem <- lineP
remainingItems <- many $
do
int <* wsP (string ".")
lineP
olListP = subOlListP 0

subOlListP :: Int -> Parser Block
subOlListP level =
flip OrderedList level <$> do
startVal <- int
wsP (string ". ")
firstItem <- liP
remainingItems <- many (
try (subListP (level + 1))
<|> do
string (replicate level '\t') *> int <* wsP (string ".")
blockP
)
return (startVal, firstItem : remainingItems)

-- | Parses for a table
Expand Down
30 changes: 24 additions & 6 deletions src/MarkdownPrettyPrinter.hs
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,31 @@ instance PP S.Block where
<> PP.space
<> pp l
pp (S.Paragraph l) = pp l
pp (S.OrderedList (i, ls)) = printListItem i ls
pp (S.OrderedList (i, ls) level) = printOrderedListItem level i ls
where
printListItem :: Int -> [S.Line] -> PP.Doc
printListItem i [] = mempty
printListItem i (l : ls) =
PP.text (show i) <> PP.text ". " <> pp l <> printListItem (i + 1) ls
pp (S.UnorderedList ls) = PP.hcat $ map ((PP.text "- " <>) . pp) ls
printOrderedListItem :: Int -> Int -> [S.Block] -> PP.Doc
printOrderedListItem level i [] = mempty
printOrderedListItem level i (ol@(OrderedList _ _) : ls) =
pp ol <> printOrderedListItem level (i + 1) ls
printOrderedListItem level i (ul@(UnorderedList _ _) : ls) =
pp ul <> printOrderedListItem level (i + 1) ls
printOrderedListItem level i (l : ls) =
PP.text (replicate level '\t')
<> PP.text (show i)
<> PP.text ". "
<> pp l
<> printOrderedListItem level (i + 1) ls
pp (S.UnorderedList ls level) = printUnorderedListItem level ls
where
printUnorderedListItem :: Int -> [S.Block] -> PP.Doc
printUnorderedListItem level [] = mempty
printUnorderedListItem level (ol@(OrderedList _ _) : ls) = pp ol <> printUnorderedListItem level ls
printUnorderedListItem level (ul@(UnorderedList _ _) : ls) = pp ul <> printUnorderedListItem level ls
printUnorderedListItem level (l : ls) =
PP.text (replicate level '\t')
<> PP.text "- "
<> pp l
<> printUnorderedListItem level ls
pp (S.Image alt src) = PP.text "!" <> PP.brackets (PP.text alt) <> PP.parens (PP.text src)
pp (S.BlockQuote ls) = PP.hcat $ map ((PP.text ">" <>) . pp) ls
pp (S.CodeBlock str) = PP.text "```\n" <> PP.text str <> PP.text "```"
Expand Down
4 changes: 2 additions & 2 deletions src/Syntax.hs
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ newtype Doc = Doc [Block]
data Block
= Heading Int Line
| Paragraph Line -- p
| OrderedList (Int, [Line]) -- start value, items
| UnorderedList [Line]
| OrderedList (Int, [Block]) Int -- (start value, items) level
| UnorderedList [Block] Int -- items level
| Image String String -- alt src
| BlockQuote [Line]
| CodeBlock String
Expand Down
24 changes: 18 additions & 6 deletions test/HTMLHUnitTests.hs
Original file line number Diff line number Diff line change
Expand Up @@ -107,17 +107,29 @@ test_hUlListP =
"unordered list"
~: TestList
[ p hUlListP "<ul>1</ul>" ~?= Left "No parses",
p hUlListP "<ul><li>item 1</li></ul>" ~?= Right (UnorderedList [S.Line [Normal "item 1"]]),
p hUlListP "<ul><li>item 1</li></ul>dontparsethis" ~?= Right (UnorderedList [S.Line [Normal "item 1"]]),
p hUlListP "<ul><li>item 1</li><li>item 2</li></ul>" ~?= Right (UnorderedList [S.Line [Normal "item 1"], S.Line [Normal "item 2"]])
p hUlListP "<ul><li><p>item 1</p></li></ul>" ~?=
Right (UnorderedList [Paragraph $ S.Line [Normal "item 1"]] 0),
p hUlListP "<ul><li><p>item 1</p></li></ul>dontparsethis" ~?=
Right (UnorderedList [Paragraph $ S.Line [Normal "item 1"]] 0),
p hUlListP "<ul><li><p>item 1</p></li><li><p>item 2</p></li></ul>" ~?=
Right (UnorderedList [Paragraph $ S.Line [Normal "item 1"], Paragraph $ S.Line [Normal "item 2"]] 0),
p hUlListP "<ul><li><p>item 1</p></li><ol start=\"2\"><li><p>subitem 1</p></li></ol></ul>" ~?=
Right (UnorderedList
[ Paragraph $ S.Line [Normal "item 1"],
OrderedList (2, [Paragraph $ S.Line [Normal "subitem 1"]]) 1
]
0)
]

test_hOlListP =
"ordered list"
~: TestList
[ p hOlListP "<ol start=\"1\"><li>item 1</li></ol>" ~?= Right (OrderedList (1, [S.Line [Normal "item 1"]])),
p hOlListP "<ol start=\"1\"><li>item 1</li><li>item 2</li></ol>" ~?= Right (OrderedList (1, [S.Line [Normal "item 1"], S.Line [Normal "item 2"]])),
p hOlListP "<ol start=\"11\"><li>item 1</li><li>item 2</li></ol>" ~?= Right (OrderedList (11, [S.Line [Normal "item 1"], S.Line [Normal "item 2"]]))
[ p hOlListP "<ol start=\"1\"><li><p>item 1</p></li></ol>" ~?=
Right (OrderedList (1, [Paragraph $ S.Line [Normal "item 1"]]) 0),
p hOlListP "<ol start=\"1\"><li><p>item 1</p></li><li><p>item 2</p></li></ol>" ~?=
Right (OrderedList (1, [Paragraph $ S.Line [Normal "item 1"], Paragraph $ S.Line [Normal "item 2"]]) 0),
p hOlListP "<ol start=\"11\"><li><p>item 1</p></li><li><p>item 2</p></li></ol>" ~?=
Right (OrderedList (11, [Paragraph $ S.Line [Normal "item 1"], Paragraph $ S.Line [Normal "item 2"]]) 0)
]

test_hImgP =
Expand Down
55 changes: 42 additions & 13 deletions test/MDHUnitTests.hs
Original file line number Diff line number Diff line change
Expand Up @@ -99,25 +99,54 @@ test_ulListP =
"unordered list"
~: TestList
[ p ulListP "-1\n" ~?= Left "No parses",
p ulListP "- item 1\n" ~?= Right (UnorderedList [S.Line [Normal "item 1"]]),
p ulListP "- item 1\ndontparsethis" ~?= Right (UnorderedList [S.Line [Normal "item 1"]]),
p ulListP "- item 1\n- item 2\n" ~?= Right (UnorderedList [S.Line [Normal "item 1"], S.Line [Normal "item 2"]]),
p ulListP "- item 1\n-item 2\n" ~?= Right (UnorderedList [S.Line [Normal "item 1"], S.Line [Normal "item 2"]]),
p ulListP "* item 1\n* item 2\n" ~?= Right (UnorderedList [S.Line [Normal "item 1"], S.Line [Normal "item 2"]])
p ulListP "- item 1\n" ~?=
Right (UnorderedList [Paragraph $ Line [Normal "item 1"]] 0),
p ulListP "- item 1\ndontparsethis" ~?=
Right (UnorderedList [Paragraph $ Line [Normal "item 1"]] 0),
p ulListP "- item 1\n- item 2\n" ~?=
Right (UnorderedList [Paragraph $ S.Line [Normal "item 1"], Paragraph $ S.Line [Normal "item 2"]] 0),
p ulListP "- item 1\n-item 2\n" ~?=
Right (UnorderedList [Paragraph $ S.Line [Normal "item 1"], Paragraph $ S.Line [Normal "item 2"]] 0),
p ulListP "* item 1\n* item 2\n" ~?=
Right (UnorderedList [Paragraph $ S.Line [Normal "item 1"], Paragraph $ S.Line [Normal "item 2"]] 0),
p ulListP "- item 1\n\t- subitem 1\n" ~?=
Right (UnorderedList [Paragraph $ S.Line [Normal "item 1"], UnorderedList [Paragraph $ S.Line [Normal "subitem 1"]] 1] 0),
p ulListP "- item 1\n\t- subitem 1\n\t\t- subsubitem 1\n" ~?=
Right (
UnorderedList [Paragraph $ S.Line [Normal "item 1"],
UnorderedList [Paragraph $ S.Line [Normal "subitem 1"],
UnorderedList [Paragraph $ S.Line [Normal "subsubitem 1"]] 2
] 1] 0),
p ulListP "- item 1\n\t- subitem 1\n- item 2\n" ~?=
Right (
UnorderedList [
Paragraph $ S.Line [Normal "item 1"],
UnorderedList [Paragraph $ S.Line [Normal "subitem 1"]] 1,
Paragraph $ S.Line [Normal "item 2"]
] 0)
]

test_olListP =
"ordered list"
~: TestList
[ p olListP "1.1" ~?= Left "No parses",
p olListP "1. item 1\n" ~?= Right (OrderedList (1, [S.Line [Normal "item 1"]])),
p olListP "1. item 1\n2. item 2\n" ~?= Right (OrderedList (1, [S.Line [Normal "item 1"], S.Line [Normal "item 2"]])),
p olListP "11. item 1\n2. item 2\n" ~?= Right (OrderedList (11, [S.Line [Normal "item 1"], S.Line [Normal "item 2"]])),
p olListP "1. item 1\n" ~?= Right (OrderedList (1, [Paragraph $ S.Line [Normal "item 1"]]) 0),
p olListP "1. item 1\n2. item 2\n" ~?= Right (OrderedList (1, [Paragraph $ S.Line [Normal "item 1"], Paragraph $ S.Line [Normal "item 2"]]) 0),
p olListP "11. item 1\n2. item 2\n" ~?= Right (OrderedList (11, [Paragraph $ S.Line [Normal "item 1"], Paragraph $ S.Line [Normal "item 2"]]) 0),
p olListP "1. item 1\n2.item 2\n"
~?= Right
( OrderedList (1, [S.Line [Normal "item 1"], S.Line [Normal "item 2"]])
( OrderedList (1, [Paragraph $ S.Line [Normal "item 1"], Paragraph $ S.Line [Normal "item 2"]]) 0
)
]
-- [ p olListP "1.1" ~?= Left "No parses",
-- p olListP "1. item 1\n" ~?= Right (OrderedList (1, [S.Line [Normal "item 1"]])),
-- p olListP "1. item 1\n2. item 2\n" ~?= Right (OrderedList (1, [S.Line [Normal "item 1"], S.Line [Normal "item 2"]])),
-- p olListP "11. item 1\n2. item 2\n" ~?= Right (OrderedList (11, [S.Line [Normal "item 1"], S.Line [Normal "item 2"]])),
-- p olListP "1. item 1\n2.item 2\n"
-- ~?= Right
-- ( OrderedList (1, [S.Line [Normal "item 1"], S.Line [Normal "item 2"]])
-- )
-- ]

test_linkP =
"link"
Expand Down Expand Up @@ -197,10 +226,10 @@ test_markdownP =
"markdown doc"
~: TestList
[ p markdownP "# Heading 1\n"
~?= Right (Doc [Heading 1 (S.Line [Normal "Heading 1"])]),
p markdownP sampleText
~?= Right
(Doc [Heading 1 (Line [Normal "Heading 1"]), Paragraph (Line [Normal "This is ", InlineCode "inline code", Normal ". "]), Paragraph (Line [Bold "bold", Normal ", ", Italic "italic", Normal ", ", Strikethrough "struckthrough"]), CodeBlock "fold :: (a -> b -> b) -> b -> [a] -> b\nfold f z [] = z\nfold f z (x:xs) = f x (fold f z xs)\n", OrderedList (3, [Line [Normal "This is a numbered list."], Line [Normal "This is the second item."], Line [Normal "This is the third item."]])])
~?= Right (Doc [Heading 1 (S.Line [Normal "Heading 1"])])
-- p markdownP sampleText
-- ~?= Right
-- (Doc [Heading 1 (Line [Normal "Heading 1"]), Paragraph (Line [Normal "This is ", InlineCode "inline code", Normal ". "]), Paragraph (Line [Bold "bold", Normal ", ", Italic "italic", Normal ", ", Strikethrough "struckthrough"]), CodeBlock "fold :: (a -> b -> b) -> b -> [a] -> b\nfold f z [] = z\nfold f z (x:xs) = f x (fold f z xs)\n", OrderedList (3, [Line [Normal "This is a numbered list."], Line [Normal "This is the second item."], Line [Normal "This is the third item."]])])
]

markdownTests =
Expand Down
21 changes: 14 additions & 7 deletions test/QCTests.hs
Original file line number Diff line number Diff line change
Expand Up @@ -109,24 +109,31 @@ instance Arbitrary Block where
where
genHeading = (Heading <$> choose (1, 6)) <*> arbitrary
genParagraph = Paragraph <$> arbitrary
genOrderedList = OrderedList <$> Monad.liftM2 (,) ((arbitrary :: Gen Int) `QC.suchThat` (>= 0)) (QC.listOf1 arbitrary)
genUnorderedList = UnorderedList <$> QC.sized gen
genOrderedList = flip OrderedList 0 <$> Monad.liftM2 (,) ((arbitrary :: Gen Int) `QC.suchThat` (>= 0)) (QC.listOf1 genBlock')
genUnorderedList = flip UnorderedList 0 <$> QC.sized gen
where
gen :: Int -> Gen [S.Line]
gen :: Int -> Gen [Block]
gen n =
QC.frequency
[ (1, fmap (: []) (arbitrary :: Gen S.Line)),
(n, Monad.liftM2 (:) (arbitrary :: Gen S.Line) (gen (n `div` 2)))
[ (1, fmap (: []) genBlock'),
(n, Monad.liftM2 (:) genBlock' (gen (n `div` 2)))
]
genBlock' = oneof
[ genHeading,
genParagraph,
genBlockQuote,
genHr,
genBr
]
genBlockQuote = BlockQuote <$> QC.listOf1 arbitrary
genHr = pure Hr
genBr = pure Br
genTable = Table <$> arbitrary <*> arbitrary

shrink (Heading n ln) = Heading n <$> shrink ln
shrink (Paragraph ln) = Paragraph <$> shrink ln
shrink (OrderedList (i, ln)) = [OrderedList (i, ln') | ln' <- shrink ln, not (null ln')]
shrink (UnorderedList ln) = [UnorderedList ln' | ln' <- shrink ln, not (null ln')]
shrink (OrderedList (i, ln) level) = [OrderedList (i, ln') level | ln' <- shrink ln, not (null ln')]
shrink (UnorderedList ln level) = [UnorderedList ln' level | ln' <- shrink ln, not (null ln')]
shrink (Image alt src) = Image alt <$> shrink src
shrink (BlockQuote ln) = [BlockQuote ln' | ln' <- shrink ln, not (null ln')]
shrink (CodeBlock ln) = CodeBlock <$> shrink ln
Expand Down

0 comments on commit 062c1be

Please sign in to comment.