corpus

Finished
1// Helper function to wrap text in HTML tags
12function wrap(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// Parse bold text (surrounded by double underscores)
7function parseBold(markdown) {
8 const pattern = new RegExp(`__(.+)__`); // Match text surrounded by __
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');
13// Parse italic text (surrounded by single underscores)
14function parseItalic(markdown) {
15 const pattern = new RegExp(`_(.*)_`); // Match text surrounded by _
16 const replacement = `<em>$1</em>`;
17 return markdown.replace(pattern, replacement);
2118}
2219
23function parseText(markdown, list) {
24 const parsedText = parse_(parse__(markdown));
25 if (list) {
26 return parsedText;
27 } else {
28 return wrap(parsedText, 'p');
29 }
20// Parse inline formatting (bold and italic)
21function parseInlineFormatting(markdown) {
22 // Parse bold first, then italic
23 return parseItalic(parseBold(markdown));
3024}
3125
32function parseHeader(markdown, list) {
33 let count = 0;
26// Parse header markdown (# ## ### etc.)
27function parseHeader(markdown, currentlyInList) {
28 // Count the number of # at the beginning
29 let headerLevel = 0;
3430 for (let i = 0; i < markdown.length; i++) {
3531 if (markdown[i] === '#') {
36 count += 1;
32 headerLevel++;
3733 } else {
3834 break;
3935 }
4036 }
41 if (count === 0 || count > 6) {
42 return [null, list];
37
38 // Valid headers are h1 to h6 (1 to 6 # characters)
39 if (headerLevel === 0 || headerLevel > 6) {
40 return [null, currentlyInList];
4341 }
44 const headerTag = `h${count}`;
45 const headerHtml = wrap(markdown.substring(count + 1), headerTag);
46 if (list) {
42
43 // Create the header tag (h1, h2, etc.)
44 const headerTag = `h${headerLevel}`;
45 // Extract the header text (skip the # characters and the following space)
46 const headerText = markdown.substring(headerLevel + 1);
47 const headerHtml = wrap(headerText, headerTag);
48
49 // If we were in a list, close it before starting the header
50 if (currentlyInList) {
4751 return [`</ul>${headerHtml}`, false];
4852 } else {
4953 return [headerHtml, false];
5054 }
5155}
5256
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];
57// Parse list items (starting with *)
58function parseListItem(markdown, currentlyInList) {
59 if (markdown.startsWith('* ')) {
60 // Parse the text content with inline formatting, keeping it in list context
61 const listItemContent = parseInlineFormatting(markdown.substring(2));
62 const listItemHtml = wrap(listItemContent, 'li');
63
64 // If we're already in a list, just add the list item
65 // Otherwise, start a new unordered list
66 if (currentlyInList) {
67 return [listItemHtml, true];
5868 } else {
59 return [`<ul>${innerHtml}`, true];
69 return [`<ul>${listItemHtml}`, true];
6070 }
6171 }
62 return [null, list];
72 return [null, currentlyInList];
6373}
6474
65function parseParagraph(markdown, list) {
66 if (!list) {
67 return [parseText(markdown, false), false];
75// Parse paragraph text
76function parseParagraph(markdown, currentlyInList) {
77 // Parse inline formatting in the paragraph text
78 const paragraphContent = parseInlineFormatting(markdown);
79
80 // If we were in a list, close it before starting the paragraph
81 if (currentlyInList) {
82 return [`</ul>${wrap(paragraphContent, 'p')}`, false];
6883 } else {
69 return [`</ul>${parseText(markdown, false)}`, false];
84 return [wrap(paragraphContent, 'p'), false];
7085 }
7186}
7287
73function parseLine(markdown, list) {
74 let [result, inListAfter] = parseHeader(markdown, list);
88// Parse a single line of markdown
89function parseLine(markdown, currentlyInList) {
90 // Try to parse as header first
91 let [result, newListState] = parseHeader(markdown, currentlyInList);
92
93 // If not a header, try to parse as list item
7594 if (result === null) {
76 [result, inListAfter] = parseLineItem(markdown, list);
95 [result, newListState] = parseListItem(markdown, currentlyInList);
7796 }
97
98 // If not a list item, parse as paragraph
7899 if (result === null) {
79 [result, inListAfter] = parseParagraph(markdown, list);
100 [result, newListState] = parseParagraph(markdown, currentlyInList);
80101 }
102
103 // This should never happen with valid input
81104 if (result === null) {
82 throw new Error('Remove this line and implement the function');
105 throw new Error('Invalid markdown line');
83106 }
84 return [result, inListAfter];
107
108 return [result, newListState];
85109}
86110
87111/**
88 * @param {string} markdown
89 * @returns {string}
112 * Parse markdown text and convert it to HTML
113 * @param {string} markdown - The markdown text to parse
114 * @returns {string} - The resulting HTML
90115 */
91116export function parse(markdown) {
117 // Split the markdown into individual lines
92118 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;
119 let htmlResult = '';
120 let currentlyInList = false;
121
122 // Process each line
123 for (const line of lines) {
124 const [lineHtml, newListState] = parseLine(line, currentlyInList);
125 htmlResult += lineHtml;
126 currentlyInList = newListState;
99127 }
100 if (list) {
101 return result + '</ul>';
102 } else {
103 return result;
128
129 // If we're still in a list at the end, close it
130 if (currentlyInList) {
131 htmlResult += '</ul>';
104132 }
133
134 return htmlResult;
105135}
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.