229 lines
4.5 KiB
JavaScript
229 lines
4.5 KiB
JavaScript
const md2tokens = (markdown, newline) => {
|
|
// Simple lexer and parser
|
|
let i = 0;
|
|
let current_string = "";
|
|
newline = newline == null ? true : newline;
|
|
let newline_count = newline ? 1 : 0;
|
|
|
|
let tokens = [];
|
|
|
|
let match = (c) => {
|
|
if (c == markdown[i]) {
|
|
i++;
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
let advance = () => {
|
|
i++;
|
|
if (i < markdown.length)
|
|
return true
|
|
|
|
return false
|
|
}
|
|
|
|
let capture_until = (c) => {
|
|
let capture = "";
|
|
while (markdown[i] != c && markdown.length > i) {
|
|
capture += markdown[i];
|
|
i++;
|
|
}
|
|
return capture;
|
|
}
|
|
|
|
let finish = (type) => {
|
|
if (current_string.trim().length == 0) {
|
|
current_string = "";
|
|
return;
|
|
}
|
|
|
|
tokens.push({ type: type == null ? "span" : type, content: current_string })
|
|
current_string = "";
|
|
}
|
|
|
|
let def = () => {
|
|
current_string += markdown[i];
|
|
newline = false;
|
|
newline_count = 0;
|
|
}
|
|
|
|
while (i < markdown.length) {
|
|
switch (markdown[i]) {
|
|
case '\\':
|
|
i++;
|
|
current_string += markdown[i];
|
|
break;
|
|
|
|
case '*':
|
|
finish();
|
|
advance();
|
|
let depth_em = 1;
|
|
let type = "em";
|
|
if (match("*")) {
|
|
type = "bold";
|
|
depth_em = 2;
|
|
}
|
|
|
|
let found_match = () => {
|
|
for (let j = 0; j < depth_em; j++) {
|
|
if (markdown[i + j] != "*")
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
|
|
while (!found_match()) {
|
|
current_string += markdown[i];
|
|
if (!advance())
|
|
break;
|
|
}
|
|
|
|
advance();
|
|
|
|
finish(type);
|
|
break;
|
|
|
|
case "#":
|
|
if (!newline) {
|
|
def();
|
|
break;
|
|
}
|
|
|
|
finish();
|
|
let depth_he = 0;
|
|
do {
|
|
depth_he++;
|
|
|
|
} while (match("#"));
|
|
|
|
while (markdown[i] != "\n" && markdown.length > i) {
|
|
current_string += markdown[i];
|
|
i++
|
|
}
|
|
|
|
tokens.push({ type: "header", level: depth_he, content: current_string });
|
|
current_string = "";
|
|
break;
|
|
|
|
case '!':
|
|
if (markdown[i + 1] != "[") {
|
|
def();
|
|
break;
|
|
}
|
|
|
|
finish();
|
|
advance();
|
|
advance();
|
|
let alt_text = capture_until("]");
|
|
advance();
|
|
if (!match("(")) {
|
|
current_string = `![${alt_text}]`;
|
|
def();
|
|
break;
|
|
}
|
|
|
|
let url = capture_until("\"");
|
|
advance();
|
|
let title = capture_until("\"");
|
|
advance();
|
|
if (!match(")")) {
|
|
current_string = `;
|
|
break;
|
|
}
|
|
|
|
tokens.push({type:"image", alt: alt_text, url: url, title: title})
|
|
break;
|
|
|
|
case '-':
|
|
if (!newline) {
|
|
def();
|
|
break;
|
|
}
|
|
|
|
let items = [];
|
|
|
|
advance();
|
|
|
|
do {
|
|
let text = capture_until("\n");
|
|
advance();
|
|
items.push(md2tokens(text, false));
|
|
} while (match("-"));
|
|
|
|
tokens.push({type:"ul", items: items});
|
|
|
|
break;
|
|
|
|
case '\n':
|
|
if (newline_count > 2)
|
|
break;
|
|
|
|
newline = true;
|
|
newline_count++;
|
|
finish();
|
|
tokens.push({ type: "newline" })
|
|
break;
|
|
|
|
case '`':
|
|
finish();
|
|
advance();
|
|
current_string = capture_until("`");
|
|
finish("inlinecode");
|
|
break;
|
|
|
|
default:
|
|
def()
|
|
}
|
|
i++;
|
|
}
|
|
|
|
finish();
|
|
|
|
console.log(tokens);
|
|
|
|
return tokens;
|
|
};
|
|
|
|
const tokens2html = (tokens) => {
|
|
let output = "";
|
|
|
|
for (let token of tokens) {
|
|
switch (token.type) {
|
|
case "span":
|
|
output += `<span>${token.content}</span>`;
|
|
break
|
|
case "bold":
|
|
output += `<b>${token.content}</b>`
|
|
break;
|
|
case "em":
|
|
output += `<i>${token.content}</i>`
|
|
break;
|
|
case "header":
|
|
output += `<h${token.level}>${token.content}</h${token.level}>`
|
|
break;
|
|
case "newline":
|
|
output += "<br>"
|
|
break;
|
|
case "image":
|
|
output += `<p style="text-align:center"><img src="${token.url}" alt="${token.alt}"></p>`;
|
|
break
|
|
case "ul":
|
|
console.log(token);
|
|
output += "<ul>";
|
|
for (let item of token.items) {
|
|
output += `<li>${tokens2html(item)}</li>`
|
|
}
|
|
output += "</ul>";
|
|
break;
|
|
case "inlinecode":
|
|
output += `<code>${token.content}</code>`;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return output;
|
|
}
|