A.2.1

Finished
1// Helper function to wrap text in HTML tags
12function wrap(text, tag) {
23 return `<${tag}>${text}</${tag}>`;
34}
45
6// Helper function to check if text starts with a specific HTML tag
57function isTag(text, tag) {
68 return text.startsWith(`<${tag}>`);
79}
810
11// Generic parser for inline formatting elements
912function parser(markdown, delimiter, tag) {
1013 const pattern = new RegExp(`${delimiter}(.+)${delimiter}`);
1114 const replacement = `<${tag}>$1</${tag}>`;
1215 return markdown.replace(pattern, replacement);
1316}
1417
15function parse__(markdown) {
18// Parse strong emphasis (__text__ -> <strong>text</strong>)
19function parseStrong(markdown) {
1620 return parser(markdown, '__', 'strong');
1721}
1822
19function parse_(markdown) {
23// Parse emphasis (_text_ -> <em>text</em>)
24function parseEmphasis(markdown) {
2025 return parser(markdown, '_', 'em');
2126}
2227
23function parseText(markdown, list) {
24 const parsedText = parse_(parse__(markdown));
25 if (list) {
26 return parsedText;
28// Parse inline text formatting and wrap in appropriate block-level tags
29function parseText(markdown, inList) {
30 // Apply inline formatting: strong first, then emphasis
31 const formattedText = parseEmphasis(parseStrong(markdown));
32
33 // If we're in a list, don't wrap in <p> tags
34 if (inList) {
35 return formattedText;
2736 } else {
28 return wrap(parsedText, 'p');
37 return wrap(formattedText, 'p');
2938 }
3039}
3140
32function parseHeader(markdown, list) {
41// Parse header elements (# Header -> <h1>Header</h1>)
42function parseHeader(markdown, inList) {
43 // Count the number of # characters at the beginning
3344 let count = 0;
3445 for (let i = 0; i < markdown.length; i++) {
3546 if (markdown[i] === '#') {
3849 break;
3950 }
4051 }
52
53 // Headers must have 1-6 # characters
4154 if (count === 0 || count > 6) {
42 return [null, list];
55 return [null, inList];
4356 }
57
58 // Create the appropriate header tag (h1-h6)
4459 const headerTag = `h${count}`;
45 const headerHtml = wrap(markdown.substring(count + 1), headerTag);
46 if (list) {
60 const headerContent = markdown.substring(count + 1);
61 const headerHtml = wrap(headerContent, headerTag);
62
63 // If we were in a list, close it before the header
64 if (inList) {
4765 return [`</ul>${headerHtml}`, false];
4866 } else {
4967 return [headerHtml, false];
5068 }
5169}
5270
53function parseLineItem(markdown, list) {
71// Parse list items (* item -> <li>item</li>)
72function parseLineItem(markdown, inList) {
5473 if (markdown.startsWith('*')) {
55 const innerHtml = wrap(parseText(markdown.substring(2), true), 'li');
56 if (list) {
57 return [innerHtml, true];
74 // Parse the content of the list item with inline formatting
75 const itemContent = markdown.substring(2);
76 const formattedContent = parseText(itemContent, true);
77 const listItemHtml = wrap(formattedContent, 'li');
78
79 // If we're already in a list, just add the list item
80 // Otherwise, start a new list
81 if (inList) {
82 return [listItemHtml, true];
5883 } else {
59 return [`<ul>${innerHtml}`, true];
84 return [`<ul>${listItemHtml}`, true];
6085 }
6186 }
62 return [null, list];
87 return [null, inList];
6388}
6489
65function parseParagraph(markdown, list) {
66 if (!list) {
67 return [parseText(markdown, false), false];
90// Parse paragraphs (regular text -> <p>text</p>)
91function parseParagraph(markdown, inList) {
92 // Parse the paragraph content with inline formatting
93 const paragraphHtml = parseText(markdown, false);
94
95 // If we were in a list, close it before the paragraph
96 if (inList) {
97 return [`</ul>${paragraphHtml}`, false];
6898 } else {
69 return [`</ul>${parseText(markdown, false)}`, false];
99 return [paragraphHtml, false];
70100 }
71101}
72102
73function parseLine(markdown, list) {
74 let [result, inListAfter] = parseHeader(markdown, list);
103// Parse a single line of markdown
104function parseLine(markdown, inList) {
105 // Try to parse as header first
106 let [result, newListState] = parseHeader(markdown, inList);
107
108 // If not a header, try to parse as list item
75109 if (result === null) {
76 [result, inListAfter] = parseLineItem(markdown, list);
110 [result, newListState] = parseLineItem(markdown, inList);
77111 }
112
113 // If not a list item, parse as paragraph
78114 if (result === null) {
79 [result, inListAfter] = parseParagraph(markdown, list);
115 [result, newListState] = parseParagraph(markdown, inList);
80116 }
117
118 // This should never happen with valid markdown
81119 if (result === null) {
82 throw new Error('Remove this line and implement the function');
120 throw new Error('Invalid markdown line');
83121 }
84 return [result, inListAfter];
122
123 return [result, newListState];
85124}
86125
87126/**
88 * @param {string} markdown
89 * @returns {string}
127 * Parse markdown text and convert it to HTML
128 * @param {string} markdown - The markdown text to parse
129 * @returns {string} - The resulting HTML
90130 */
91131export function parse(markdown) {
132 // Split the markdown into individual lines
92133 const lines = markdown.split('\n');
93 let result = '';
94 let list = false;
134 let htmlResult = '';
135 let inList = false;
136
137 // Process each line
95138 for (let i = 0; i < lines.length; i++) {
96 let [lineResult, newList] = parseLine(lines[i], list);
97 result += lineResult;
98 list = newList;
139 let [lineHtml, newListState] = parseLine(lines[i], inList);
140 htmlResult += lineHtml;
141 inList = newListState;
99142 }
100 if (list) {
101 return result + '</ul>';
143
144 // If we're still in a list at the end, close it
145 if (inList) {
146 return htmlResult + '</ul>';
102147 } else {
103 return result;
148 return htmlResult;
104149 }
105150}
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.