Skip to content

Commit b4dac08

Browse files
author
Yeong Sheng, Tan
committed
Add markdown problem
1 parent 2316e2b commit b4dac08

File tree

4 files changed

+179
-0
lines changed

4 files changed

+179
-0
lines changed

config.json

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
"parallel-letter-frequency",
3939
"binary",
4040
"luhn",
41+
"markdown",
4142
"gigasecond",
4243
"queen-attack",
4344
"pascals-triangle",
@@ -272,6 +273,12 @@
272273
"topics": [
273274
]
274275
},
276+
{
277+
"slug": "markdown",
278+
"difficulty": 1,
279+
"topics": [
280+
]
281+
},
275282
{
276283
"slug": "gigasecond",
277284
"difficulty": 1,

exercises/markdown/example.exs

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
defmodule Markdown do
2+
@bold_md "__"
3+
@header_md "#"
4+
@italic_md "_"
5+
@list_md "*"
6+
7+
@bold_open_tag "<em>"
8+
@bold_close_tag "</em>"
9+
@italic_open_tag "<i>"
10+
@italic_close_tag "</i>"
11+
@unordered_list_open_tag "<ul>"
12+
@unordered_list_close_tag "</ul>"
13+
@list_item_open_tag "<li>"
14+
@list_item_close_tag "</li>"
15+
@paragraph_open_tag "<p>"
16+
@paragraph_close_tag "</p>"
17+
@header_partial_open_tag "<h"
18+
@header_partial_close_tag "</h"
19+
20+
def parse(markdown) do
21+
String.split(markdown, "\n") |> parse_line
22+
end
23+
24+
defp parse_line(markdown_split_by_newline) do
25+
Enum.map_join(markdown_split_by_newline, fn(text_with_md) -> process_md(text_with_md) end) |> patch_line_with_unordered_list_tag
26+
end
27+
28+
defp process_md(text_with_md) do
29+
cond do
30+
String.starts_with?(text_with_md, "#") -> parse_header_md_level(text_with_md) |> enclose_with_header_tag
31+
String.starts_with?(text_with_md, "*") -> parse_list_md_level(text_with_md)
32+
true -> String.split(text_with_md) |> enclose_with_paragraph_tag
33+
end
34+
end
35+
36+
defp parse_header_md_level(header_md_with_text) do
37+
[header_md | header_text] = String.split(header_md_with_text)
38+
{to_string(String.length(header_md)), Enum.join(header_text, " ")}
39+
end
40+
41+
defp parse_list_md_level(list_md_with_text) do
42+
list_text = String.trim_leading(list_md_with_text, "* ") |> String.split
43+
@list_item_open_tag <> join_words_with_tags(list_text) <> @list_item_close_tag
44+
end
45+
46+
defp enclose_with_header_tag({header_level, header_text_line}) do
47+
@header_partial_open_tag <> header_level <> ">" <> header_text_line <> @header_partial_close_tag <> header_level <> ">"
48+
end
49+
50+
defp enclose_with_paragraph_tag(text_with_md) do
51+
@paragraph_open_tag <> join_words_with_tags(text_with_md) <> @paragraph_close_tag
52+
end
53+
54+
defp join_words_with_tags(text_with_md) do
55+
Enum.map_join(text_with_md, " ", fn(word_with_md) -> replace_md_with_tag(word_with_md) end)
56+
end
57+
58+
defp replace_md_with_tag(word_with_md) do
59+
replace_prefix_md(word_with_md) |> replace_suffix_md
60+
end
61+
62+
defp replace_prefix_md(word_with_md) do
63+
cond do
64+
word_with_md =~ ~r/^[#{@italic_md}{1}][^#{@italic_md}+]/ -> String.replace_prefix(word_with_md, @italic_md, @italic_open_tag)
65+
word_with_md =~ ~r/^#{@bold_md}{1}/ -> String.replace_prefix(word_with_md, @bold_md, @bold_open_tag)
66+
true -> word_with_md
67+
end
68+
end
69+
70+
defp replace_suffix_md(word_with_md) do
71+
cond do
72+
word_with_md =~ ~r/[^#{@italic_md}{1}][#{@italic_md}{1}]$/ -> String.replace_suffix(word_with_md, @italic_md, @italic_close_tag)
73+
word_with_md =~ ~r/#{@bold_md}{1}$/ -> String.replace_suffix(word_with_md, @bold_md, @bold_close_tag)
74+
true -> word_with_md
75+
end
76+
end
77+
78+
defp patch_line_with_unordered_list_tag(html_line) do
79+
String.replace(html_line, @list_item_open_tag, @unordered_list_open_tag <> @list_item_open_tag, global: false) |> String.replace_suffix(@list_item_close_tag, @list_item_close_tag <> @unordered_list_close_tag)
80+
end
81+
end

exercises/markdown/markdown.exs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
defmodule Markdown do
2+
@doc """
3+
Parses a given string with Markdown syntax and returns the associated HTML for that string.
4+
5+
## Examples
6+
7+
iex> Markdown.parse("This is a paragraph")
8+
"<p>This is a paragraph</p>"
9+
10+
iex> Markdown.parse("#Header!\n* __Bold Item__\n* _Italic Item_")
11+
"<h1>Header!</h1><ul><li><em>Bold Item</em></li><li><i>Italic Item</i></li></ul>"
12+
"""
13+
14+
@spec parse(String.t) :: String.t
15+
def parse(markdown) do
16+
17+
end
18+
end
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
if !System.get_env("EXERCISM_TEST_EXAMPLES") do
2+
Code.load_file("markdown.exs", __DIR__)
3+
end
4+
5+
ExUnit.start
6+
ExUnit.configure exclude: :pending, trace: true
7+
8+
defmodule MarkdownTest do
9+
use ExUnit.Case
10+
11+
# @tag :pending
12+
test "parses normal text as a paragraph" do
13+
input = "This will be a paragraph"
14+
expected = "<p>This will be a paragraph</p>"
15+
assert Markdown.parse(input) == expected
16+
end
17+
18+
@tag :pending
19+
test "parsing italics" do
20+
input = "_This will be italic_"
21+
expected = "<p><i>This will be italic</i></p>"
22+
assert Markdown.parse(input) == expected
23+
end
24+
25+
@tag :pending
26+
test "parsing bold text" do
27+
input = "__This will be bold__"
28+
expected = "<p><em>This will be bold</em></p>"
29+
assert Markdown.parse(input) == expected
30+
end
31+
32+
@tag :pending
33+
test "mixed normal, italics and bold text" do
34+
input = "This will _be_ __mixed__"
35+
expected = "<p>This will <i>be</i> <em>mixed</em></p>"
36+
assert Markdown.parse(input) == expected
37+
end
38+
39+
@tag :pending
40+
test "with h1 header level" do
41+
input = "# This will be an h1"
42+
expected = "<h1>This will be an h1</h1>"
43+
assert Markdown.parse(input) == expected
44+
end
45+
46+
@tag :pending
47+
test "with h2 header level" do
48+
input = "## This will be an h2"
49+
expected = "<h2>This will be an h2</h2>"
50+
assert Markdown.parse(input) == expected
51+
end
52+
53+
@tag :pending
54+
test "with h6 header level" do
55+
input = "###### This will be an h6"
56+
expected = "<h6>This will be an h6</h6>"
57+
assert Markdown.parse(input) == expected
58+
end
59+
60+
@tag :pending
61+
test "unordered lists" do
62+
input = "* Item 1\n* Item 2"
63+
expected = "<ul><li>Item 1</li><li>Item 2</li></ul>"
64+
assert Markdown.parse(input) == expected
65+
end
66+
67+
@tag :pending
68+
test "with a little bit of everything" do
69+
input = "# Header!\n* __Bold Item__\n* _Italic Item_"
70+
expected = "<h1>Header!</h1><ul><li><em>Bold Item</em></li><li><i>Italic Item</i></li></ul>"
71+
assert Markdown.parse(input) == expected
72+
end
73+
end

0 commit comments

Comments
 (0)