sn62

Finished
1function wrap(text, tag) {
1// Helper functions for HTML tag wrapping
2function wrapInTag(text, tag) {
23 return `<${tag}>${text}</${tag}>`;
34}
45
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}>`;
6// Markdown inline element parsers
7function parseBoldText(markdown) {
8 const pattern = new RegExp(`__([^_]+)__`); // Match text between __ markers
9 const replacement = `<strong>$1</strong>`;
1210 return markdown.replace(pattern, replacement);
1311}
1412
15function parse__(markdown) {
16 return parser(markdown, '__', 'strong');
17}
18
19function parse_(markdown) {
20 return parser(markdown, '_', 'em');
13function parseItalicText(markdown) {
14 const pattern = new RegExp(`_([^_]+)_`); // Match text between _ markers
15 const replacement = `<em>$1</em>`;
16 return markdown.replace(pattern, replacement);
2117}
2218
23function parseText(markdown, list) {
24 const parsedText = parse_(parse__(markdown));
25 if (list) {
26 return parsedText;
27 } else {
28 return wrap(parsedText, 'p');
29 }
19function parseInlineElements(markdown) {
20 // Parse bold and italic text (order matters - bold first)
21 return parseItalicText(parseBoldText(markdown));
3022}
3123
32function parseHeader(markdown, list) {
33 let count = 0;
24// Main parsing functions for different Markdown elements
25function parseHeaderText(markdown, currentlyInList) {
26 // Count consecutive # characters at the beginning of the line
27 let headerLevel = 0;
3428 for (let i = 0; i < markdown.length; i++) {
3529 if (markdown[i] === '#') {
36 count += 1;
30 headerLevel += 1;
3731 } else {
3832 break;
3933 }
4034 }
41 if (count === 0 || count > 6) {
42 return [null, list];
35
36 // Valid headers are h1-h6 (1-6 # characters)
37 if (headerLevel === 0 || headerLevel > 6) {
38 return [null, currentlyInList];
4339 }
44 const headerTag = `h${count}`;
45 const headerHtml = wrap(markdown.substring(count + 1), headerTag);
46 if (list) {
40
41 // Extract header content (skip # characters and the following space)
42 const headerContent = markdown.substring(headerLevel + 1);
43 const headerTag = `h${headerLevel}`;
44 const headerHtml = wrapInTag(headerContent, headerTag);
45
46 // If we were in a list, close it before starting the header
47 if (currentlyInList) {
4748 return [`</ul>${headerHtml}`, false];
4849 } else {
4950 return [headerHtml, false];
5051 }
5152}
5253
53function parseLineItem(markdown, list) {
54 if (markdown.startsWith('*')) {
55 const innerHtml = wrap(parseText(markdown.substring(2), true), 'li');
56 if (list) {
57 return [innerHtml, true];
54function parseListItem(markdown, currentlyInList) {
55 // Check if line starts with '* ' (list item marker)
56 if (markdown.startsWith('* ')) {
57 // Parse inline elements in the list item content
58 const listItemContent = markdown.substring(2); // Skip '* ' prefix
59 const parsedContent = parseInlineElements(listItemContent);
60 const listItemHtml = wrapInTag(parsedContent, 'li');
61
62 // If we're already in a list, just add the list item
63 // Otherwise, start a new list
64 if (currentlyInList) {
65 return [listItemHtml, true];
5866 } else {
59 return [`<ul>${innerHtml}`, true];
67 return [`<ul>${listItemHtml}`, true];
6068 }
6169 }
62 return [null, list];
70 return [null, currentlyInList];
6371}
6472
65function parseParagraph(markdown, list) {
66 if (!list) {
67 return [parseText(markdown, false), false];
73function parseParagraphText(markdown, currentlyInList) {
74 // Parse inline elements in the paragraph
75 const parsedText = parseInlineElements(markdown);
76
77 // If we're in a list, close it and start a paragraph
78 // Otherwise, just wrap in paragraph tags
79 if (currentlyInList) {
80 return [`</ul>${wrapInTag(parsedText, 'p')}`, false];
6881 } else {
69 return [`</ul>${parseText(markdown, false)}`, false];
82 return [wrapInTag(parsedText, 'p'), false];
7083 }
7184}
7285
73function parseLine(markdown, list) {
74 let [result, inListAfter] = parseHeader(markdown, list);
86function parseLine(markdown, currentlyInList) {
87 // Try to parse as header first
88 let [result, newListState] = parseHeaderText(markdown, currentlyInList);
89
90 // If not a header, try to parse as list item
7591 if (result === null) {
76 [result, inListAfter] = parseLineItem(markdown, list);
92 [result, newListState] = parseListItem(markdown, currentlyInList);
7793 }
94
95 // If not a list item, parse as paragraph
7896 if (result === null) {
79 [result, inListAfter] = parseParagraph(markdown, list);
97 [result, newListState] = parseParagraphText(markdown, currentlyInList);
8098 }
99
100 // This should never happen with valid input
81101 if (result === null) {
82 throw new Error('Remove this line and implement the function');
102 throw new Error('Unable to parse line: ' + markdown);
83103 }
84 return [result, inListAfter];
104
105 return [result, newListState];
85106}
86107
87108/**
88 * @param {string} markdown
89 * @returns {string}
109 * Parse Markdown text and convert it to HTML
110 * @param {string} markdown - The Markdown text to parse
111 * @returns {string} - The resulting HTML
90112 */
91113export function parse(markdown) {
114 // Split input into lines for processing
92115 const lines = markdown.split('\n');
93 let result = '';
94 let list = false;
116 let htmlResult = '';
117 let currentlyInList = false;
118
119 // Process each line of the Markdown text
95120 for (let i = 0; i < lines.length; i++) {
96 let [lineResult, newList] = parseLine(lines[i], list);
97 result += lineResult;
98 list = newList;
121 const [lineHtml, newListState] = parseLine(lines[i], currentlyInList);
122 htmlResult += lineHtml;
123 currentlyInList = newListState;
99124 }
100 if (list) {
101 return result + '</ul>';
125
126 // Close any open list at the end of the document
127 if (currentlyInList) {
128 return htmlResult + '</ul>';
102129 } else {
103 return result;
130 return htmlResult;
104131 }
105132}
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.