diff --git a/config.json b/config.json index d8c6bb9b71..491a5e9d5c 100644 --- a/config.json +++ b/config.json @@ -863,6 +863,14 @@ "conditionals" ] }, + { + "uuid": "1818f134-0ed9-e680-9b29-45ffd2b3344b0f841c7", + "slug": "markdown", + "difficulty": 3, + "topics": [ + "refactoring" + ] + }, { "uuid": "e348a307-078c-5280-65af-a159283d4e79438b755", "slug": "forth", diff --git a/exercises/markdown/README.md b/exercises/markdown/README.md new file mode 100644 index 0000000000..8a772cb3fc --- /dev/null +++ b/exercises/markdown/README.md @@ -0,0 +1,30 @@ +# Markdown + +The markdown exercise is a refactoring exercise. There is code that parses a +given string with [Markdown +syntax](https://guides.github.com/features/mastering-markdown/) and returns the +associated HTML for that string. Even though this code is confusingly written +and hard to follow, somehow it works and all the tests are passing! Your +challenge is to re-write this code to make it easier to read and maintain +while still making sure that all the tests keep passing. + +It would be helpful if you made notes of what you did in your refactoring in +comments so reviewers can see that, but it isn't strictly necessary. The most +important thing is to make the code better! + +### Submitting Exercises + +Note that, when trying to submit an exercise, make sure the solution is in the `exercism/python/` directory. + +For example, if you're submitting `bob.py` for the Bob exercise, the submit command would be something like `exercism submit /python/bob/bob.py`. + + +For more detailed information about running tests, code style and linting, +please see the [help page](http://exercism.io/languages/python). + +## Source + +Syntax [https://guides.github.com/features/mastering-markdown/](https://guides.github.com/features/mastering-markdown/) + +## Submitting Incomplete Solutions +It's possible to submit an incomplete solution so you can see how others have completed the exercise. diff --git a/exercises/markdown/example.py b/exercises/markdown/example.py new file mode 100644 index 0000000000..7321ad6f34 --- /dev/null +++ b/exercises/markdown/example.py @@ -0,0 +1,80 @@ +import re + + +def parse_markdown(markdown): + lines = markdown.split('\n') + html = '' + in_list = False + for line in lines: + res = parse_line(line, in_list) + html += res['line'] + in_list = res['in_list'] + if in_list: + html += '' + return html + + +def wrap(line, tag): + return '<{tag}>{line}'.format(line=line, tag=tag) + + +def check_headers(line): + pattern = '# (.*)' + for i in range(6): + if re.match(pattern, line): + return wrap(line[(i + 2):], 'h' + str(i + 1)) + pattern = '#' + pattern + return line + + +def check_bold(line): + bold_pattern = '(.*)__(.*)__(.*)' + bold_match = re.match(bold_pattern, line) + if bold_match: + return bold_match.group(1) + wrap(bold_match.group(2), 'strong')\ + + bold_match.group(3) + else: + return None + + +def check_italic(line): + italic_pattern = '(.*)_(.*)_(.*)' + italic_match = re.match(italic_pattern, line) + if italic_match: + return italic_match.group(1) + wrap(italic_match.group(2), 'em')\ + + italic_match.group(3) + else: + return None + + +def parse_line(line, in_list): + res = check_headers(line) + + list_match = re.match(r'\* (.*)', res) + + if (list_match): + if not in_list: + res = '' + in_list = False + + if not re.match(')?
  • _(.*)', res): + res = re.sub('(.*)(
  • )(.*)(
  • )(.*)', r'\1\2

    \3

    \4\5', res) + + while check_bold(res): + res = check_bold(res) + while check_italic(res): + res = check_italic(res) + + return { + 'line': res, + 'in_list': in_list + } diff --git a/exercises/markdown/markdown.py b/exercises/markdown/markdown.py new file mode 100644 index 0000000000..456a11e935 --- /dev/null +++ b/exercises/markdown/markdown.py @@ -0,0 +1,71 @@ +import re + + +def parse_markdown(markdown): + lines = markdown.split('\n') + res = '' + in_list = False + for i in lines: + if re.match('###### (.*)', i) is not None: + i = '
    ' + i[7:] + '
    ' + elif re.match('## (.*)', i) is not None: + i = '

    ' + i[3:] + '

    ' + elif re.match('# (.*)', i) is not None: + i = '

    ' + i[2:] + '

    ' + m = re.match(r'\* (.*)', i) + if m: + if not in_list: + in_list = True + is_bold = False + is_italic = False + curr = m.group(1) + m1 = re.match('(.*)__(.*)__(.*)', curr) + if m1: + curr = m1.group(1) + '' + \ + m1.group(2) + '' + m1.group(3) + is_bold = True + m1 = re.match('(.*)_(.*)_(.*)', curr) + if m1: + curr = m1.group(1) + '' + m1.group(2) + \ + '' + m1.group(3) + is_italic = True + if is_italic or is_bold: + i = '
    • ' + curr + '
    • ' + else: + i = '
      • ' + curr + '

      • ' + else: + is_bold = False + is_italic = False + curr = m.group(1) + m1 = re.match('(.*)__(.*)__(.*)', curr) + if m1: + curr = m1.group(1) + '' + \ + m1.group(2) + '' + m1.group(3) + is_bold = True + m1 = re.match('(.*)_(.*)_(.*)', curr) + if m1: + curr = m1.group(1) + '' + m1.group(2) + \ + '' + m1.group(3) + is_italic = True + if is_italic or is_bold: + i = '
      • ' + curr + '
      • ' + else: + i = '
      • ' + curr + '

      • ' + else: + if in_list: + i = '
      +i' + in_list = False + + m = re.match('' + m = re.match('(.*)__(.*)__(.*)', i) + if m: + i = m.group(1) + '' + m.group(2) + '' + m.group(3) + m = re.match('(.*)_(.*)_(.*)', i) + if m: + i = m.group(1) + '' + m.group(2) + '' + m.group(3) + res += i + if in_list: + res += '
    ' + return res diff --git a/exercises/markdown/markdown_test.py b/exercises/markdown/markdown_test.py new file mode 100644 index 0000000000..6042594ff3 --- /dev/null +++ b/exercises/markdown/markdown_test.py @@ -0,0 +1,51 @@ +import unittest +from markdown import parse_markdown + + +# Tests adapted from `problem-specifications//canonical-data.json` @ v1.0.0 + + +class TestMarkdown(unittest.TestCase): + + def test_paragraph(self): + self.assertEqual(parse_markdown('This will be a paragraph'), + '

    This will be a paragraph

    ') + + def test_italics(self): + self.assertEqual(parse_markdown('_This will be italic_'), + '

    This will be italic

    ') + + def test_bold(self): + self.assertEqual(parse_markdown('__This will be bold__'), + '

    This will be bold

    ') + + def test_mixed(self): + self.assertEqual(parse_markdown('This will _be_ __mixed__'), + '

    This will be mixed

    ') + + def test_h1(self): + self.assertEqual(parse_markdown('# This will be an h1'), + '

    This will be an h1

    ') + + def test_h2(self): + self.assertEqual(parse_markdown('## This will be an h2'), + '

    This will be an h2

    ') + + def test_h6(self): + self.assertEqual(parse_markdown( + '###### This will be an h6'), '
    This will be an h6
    ') + + def test_unordered_lists(self): + self.assertEqual(parse_markdown('* Item 1\n* Item 2'), + '
    • Item 1

    • ' + '
    • Item 2

    ') + + def test_little_bit_of_everything(self): + self.assertEqual(parse_markdown( + '# Header!\n* __Bold Item__\n* _Italic Item_'), + '

    Header!

    • Bold Item
    • ' + '
    • Italic Item
    ') + + +if __name__ == '__main__': + unittest.main()