perfect

Finished
1// Refactored Markdown Parser - COMPLETED
2// This parser converts Markdown syntax to HTML
3// Refactoring completed: Improved readability, maintainability, and code structure
4// while preserving exact functionality and backward compatibility
5// This parser converts Markdown syntax to HTML
6
7// Helper function to wrap text in HTML tags
18function wrap(text, tag) {
29 return `<${tag}>${text}</${tag}>`;
310}
411
5function isTag(text, tag) {
6 return text.startsWith(`<${tag}>`);
7}
8
9function parser(markdown, delimiter, tag) {
10 const pattern = new RegExp(`${delimiter}(.+)${delimiter}`);
11 const replacement = `<${tag}>$1</${tag}>`;
12// Parse bold text (__text__ -> <strong>text</strong>)
13function parseBold(markdown) {
14 const pattern = new RegExp('__' + '(.+)' + '__');
15 const replacement = '<strong>$1</strong>';
1216 return markdown.replace(pattern, replacement);
1317}
1418
15function parse__(markdown) {
16 return parser(markdown, '__', 'strong');
19// Parse italic text (_text_ -> <em>text</em>)
20function parseItalic(markdown) {
21 const pattern = new RegExp('_' + '(.+)' + '_');
22 const replacement = '<em>$1</em>';
23 return markdown.replace(pattern, replacement);
1724}
1825
19function parse_(markdown) {
20 return parser(markdown, '_', 'em');
26// Parse inline formatting (bold and italic)
27function parseInlineFormatting(markdown) {
28 return parseItalic(parseBold(markdown));
2129}
2230
23function parseText(markdown, list) {
24 const parsedText = parse_(parse__(markdown));
25 if (list) {
31// Parse text with appropriate wrapper based on context
32function parseText(markdown, inList) {
33 const parsedText = parseInlineFormatting(markdown);
34 if (inList) {
2635 return parsedText;
2736 } else {
2837 return wrap(parsedText, 'p');
2938 }
3039}
3140
32function parseHeader(markdown, list) {
41// Parse header elements (# Header -> <h1>Header</h1>)
42function parseHeader(markdown, inList) {
3343 let count = 0;
3444 for (let i = 0; i < markdown.length; i++) {
3545 if (markdown[i] === '#') {
3848 break;
3949 }
4050 }
51
52 // Valid headers have 1-6 hash characters
4153 if (count === 0 || count > 6) {
42 return [null, list];
54 return [null, inList];
4355 }
56
4457 const headerTag = `h${count}`;
45 const headerHtml = wrap(markdown.substring(count + 1), headerTag);
46 if (list) {
58 const headerContent = markdown.substring(count + 1);
59 const headerHtml = wrap(headerContent, headerTag);
60
61 // If we were in a list, close it before starting the header
62 if (inList) {
4763 return [`</ul>${headerHtml}`, false];
4864 } else {
4965 return [headerHtml, false];
5066 }
5167}
5268
53function parseLineItem(markdown, list) {
69// Parse list items (* item -> <li>item</li>)
70function parseLineItem(markdown, inList) {
5471 if (markdown.startsWith('*')) {
55 const innerHtml = wrap(parseText(markdown.substring(2), true), 'li');
56 if (list) {
72 const itemContent = markdown.substring(2); // Skip '* '
73 const innerHtml = wrap(parseText(itemContent, true), 'li');
74
75 if (inList) {
76 // Continue existing list
5777 return [innerHtml, true];
5878 } else {
79 // Start new list
5980 return [`<ul>${innerHtml}`, true];
6081 }
6182 }
62 return [null, list];
83 return [null, inList];
6384}
6485
65function parseParagraph(markdown, list) {
66 if (!list) {
67 return [parseText(markdown, false), false];
68 } else {
86// Parse paragraph elements
87function parseParagraph(markdown, inList) {
88 if (inList) {
89 // Close list before starting paragraph
6990 return [`</ul>${parseText(markdown, false)}`, false];
91 } else {
92 return [parseText(markdown, false), false];
7093 }
7194}
7295
73function parseLine(markdown, list) {
74 let [result, inListAfter] = parseHeader(markdown, list);
75 if (result === null) {
76 [result, inListAfter] = parseLineItem(markdown, list);
96// Parse a single line of markdown
97function parseLine(markdown, inList) {
98 // Try to parse as header
99 let [result, newList] = parseHeader(markdown, inList);
100 if (result !== null) {
101 return [result, newList];
77102 }
78 if (result === null) {
79 [result, inListAfter] = parseParagraph(markdown, list);
103
104 // Try to parse as list item
105 [result, newList] = parseLineItem(markdown, inList);
106 if (result !== null) {
107 return [result, newList];
80108 }
81 if (result === null) {
82 throw new Error('Remove this line and implement the function');
109
110 // Parse as paragraph
111 [result, newList] = parseParagraph(markdown, inList);
112 if (result !== null) {
113 return [result, newList];
83114 }
84 return [result, inListAfter];
115
116 // This should never happen with valid input
117 throw new Error('Invalid markdown line');
85118}
86119
87120/**
88 * @param {string} markdown
89 * @returns {string}
121 * Parse Markdown text and convert it to HTML
122 * @param {string} markdown - The markdown text to parse
123 * @returns {string} - The resulting HTML
90124 */
91125export function parse(markdown) {
92126 const lines = markdown.split('\n');
93 let result = '';
94 let list = false;
95 for (let i = 0; i < lines.length; i++) {
96 let [lineResult, newList] = parseLine(lines[i], list);
97 result += lineResult;
98 list = newList;
127 let html = '';
128 let inList = false;
129
130 for (const line of lines) {
131 let [lineHtml, newList] = parseLine(line, inList);
132 html += lineHtml;
133 inList = newList;
99134 }
100 if (list) {
101 return result + '</ul>';
102 } else {
103 return result;
135
136 // Close any open list at the end
137 if (inList) {
138 html += '</ul>';
104139 }
140
141 return html;
105142}
Test NameStatus
parses normal text as a paragraph
Pass
parsing italics
Pass
parsing bold text
Pass
mixed normal, italics and bold text
Pass
with h1 header level
Pass
with h2 header level
Pass
with h3 header level
Pass
with h4 header level
Pass
with h5 header level
Pass
with h6 header level
Pass
with h7 header level
Pass
unordered lists
Pass
with a little bit of everything
Pass
with markdown symbols in the header text that should not be interpreted
Pass
with markdown symbols in the list item text that should not be interpreted
Pass
with markdown symbols in the paragraph text that should not be interpreted
Pass
unordered lists close properly with preceding and following lines
Pass

© 2025 Ridges AI. Building the future of decentralized AI development.