kale

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 (__text__ -> <strong>text</strong>)
7function parseBold(markdown) {
8 const pattern = new RegExp('__(.+)__');
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 (_text_ -> <em>text</em>)
14function parseItalic(markdown) {
15 const pattern = new RegExp('_(.+)_');
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) {
26// Parse header (# Header -> <h1>Header</h1>)
27function parseHeader(markdown, inList) {
3328 let count = 0;
3429 for (let i = 0; i < markdown.length; i++) {
3530 if (markdown[i] === '#') {
3833 break;
3934 }
4035 }
36
4137 if (count === 0 || count > 6) {
42 return [null, list];
38 return [null, inList];
4339 }
40
4441 const headerTag = `h${count}`;
45 const headerHtml = wrap(markdown.substring(count + 1), headerTag);
46 if (list) {
42 const headerContent = markdown.substring(count + 1);
43 const headerHtml = wrap(headerContent, headerTag);
44
45 if (inList) {
4746 return [`</ul>${headerHtml}`, false];
4847 } else {
4948 return [headerHtml, false];
5049 }
5150}
5251
53function parseLineItem(markdown, list) {
52// Parse list item (* item -> <li>item</li>)
53function parseListItem(markdown, inList) {
5454 if (markdown.startsWith('*')) {
55 const innerHtml = wrap(parseText(markdown.substring(2), true), 'li');
56 if (list) {
57 return [innerHtml, true];
55 const itemContent = markdown.substring(2);
56 const formattedContent = parseInlineFormatting(itemContent);
57 const listItemHtml = wrap(formattedContent, 'li');
58
59 if (inList) {
60 return [listItemHtml, true];
5861 } else {
59 return [`<ul>${innerHtml}`, true];
62 return [`<ul>${listItemHtml}`, true];
6063 }
6164 }
62 return [null, list];
65 return [null, inList];
6366}
6467
65function parseParagraph(markdown, list) {
66 if (!list) {
67 return [parseText(markdown, false), false];
68// Parse paragraph
69function parseParagraph(markdown, inList) {
70 const formattedText = parseInlineFormatting(markdown);
71
72 if (inList) {
73 return [`</ul>${wrap(formattedText, 'p')}`, false];
6874 } else {
69 return [`</ul>${parseText(markdown, false)}`, false];
75 return [wrap(formattedText, 'p'), false];
7076 }
7177}
7278
73function parseLine(markdown, list) {
74 let [result, inListAfter] = parseHeader(markdown, list);
79// Parse a single line of markdown
80function parseLine(markdown, inList) {
81 // Try to parse as header
82 let [result, newList] = parseHeader(markdown, inList);
83
84 // If not a header, try to parse as list item
7585 if (result === null) {
76 [result, inListAfter] = parseLineItem(markdown, list);
86 [result, newList] = parseListItem(markdown, inList);
7787 }
88
89 // If not a list item, parse as paragraph
7890 if (result === null) {
79 [result, inListAfter] = parseParagraph(markdown, list);
91 [result, newList] = parseParagraph(markdown, inList);
8092 }
93
94 // This should never happen if implementation is complete
8195 if (result === null) {
82 throw new Error('Remove this line and implement the function');
96 throw new Error('Invalid markdown line');
8397 }
84 return [result, inListAfter];
98
99 return [result, newList];
85100}
86101
87102/**
103 * Parse markdown text and convert to HTML
88104 * @param {string} markdown
89105 * @returns {string}
90106 */
91107export function parse(markdown) {
92108 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;
109 let html = '';
110 let inList = false;
111
112 for (const line of lines) {
113 let [lineHtml, newListState] = parseLine(line, inList);
114 html += lineHtml;
115 inList = newListState;
99116 }
100 if (list) {
101 return result + '</ul>';
102 } else {
103 return result;
117
118 // Close any open list tag
119 if (inList) {
120 html += '</ul>';
104121 }
122
123 return html;
105124}
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.