commit experiments
This commit is contained in:
parent
2d1fbf4be2
commit
e89368d861
2 changed files with 101 additions and 20 deletions
|
@ -8,6 +8,7 @@ const ren = new HtmlStrRenderer();
|
|||
|
||||
Deno.test({
|
||||
name: "should skip empty line",
|
||||
only: true,
|
||||
fn: () => {
|
||||
const par = new MarkdownParser();
|
||||
assertEquals(ren.render(par.parse("\n")), "");
|
||||
|
@ -21,15 +22,30 @@ Deno.test({
|
|||
|
||||
Deno.test({
|
||||
name: "should parse empty ATX header",
|
||||
only: true,
|
||||
fn: () => {
|
||||
const par = new MarkdownParser();
|
||||
const res = par.parse("#");
|
||||
assertEquals(ren.render(res), "<h1></h1>");
|
||||
assertEquals(ren.render(par.parse("#")), "<h1></h1>");
|
||||
assertEquals(ren.render(par.parse("##")), "<h2></h2>");
|
||||
},
|
||||
});
|
||||
|
||||
Deno.test({
|
||||
name: "should parse ATX header if line contains additional spaces",
|
||||
only: true,
|
||||
fn: () => {
|
||||
const par = new MarkdownParser();
|
||||
assertEquals(ren.render(par.parse(" #")), "<h1></h1>");
|
||||
assertEquals(ren.render(par.parse(" #")), "<h1></h1>");
|
||||
assertEquals(ren.render(par.parse(" ##")), "<h2></h2>");
|
||||
assertEquals(ren.render(par.parse(" #")), "<h1></h1>");
|
||||
assertEquals(ren.render(par.parse(" ##")), "<h2></h2>");
|
||||
},
|
||||
});
|
||||
|
||||
Deno.test({
|
||||
name: "should parse ATX header with text",
|
||||
only: true,
|
||||
fn: () => {
|
||||
const par = new MarkdownParser();
|
||||
assertEquals(ren.render(par.parse("# hello")), "<h1>hello</h1>");
|
||||
|
@ -50,16 +66,6 @@ Deno.test({
|
|||
},
|
||||
});
|
||||
|
||||
Deno.test({
|
||||
name: "should parse ATX header if line contains additional spaces",
|
||||
fn: () => {
|
||||
const par = new MarkdownParser();
|
||||
assertEquals(ren.render(par.parse(" # hello")), "<h1>hello</h1>");
|
||||
assertEquals(ren.render(par.parse(" # hello")), "<h1>hello</h1>");
|
||||
assertEquals(ren.render(par.parse(" # hello")), "<h1>hello</h1>");
|
||||
},
|
||||
});
|
||||
|
||||
Deno.test({
|
||||
name: "should parse ATX header with closing sequence",
|
||||
fn: () => {
|
||||
|
|
91
par/md.ts
91
par/md.ts
|
@ -23,6 +23,8 @@ import { Parser } from "./types.ts";
|
|||
|
||||
const RE_EMPTY_LINE = /^[ ]*\r?\n/;
|
||||
|
||||
const RE_ATX_HEADING = /^[ ]{0,3}(#{1,6})(?:$|([ ].+)(?:[ ]?#*[ ]*)?$)/;
|
||||
|
||||
const RE_OPEN_ATX_HEADING = /^[ ]{0,3}(#{1,6})([ ]|$)/;
|
||||
const RE_CLOSE_ATX_HEADING = /(^|[ ]+)#*[ ]*$/;
|
||||
|
||||
|
@ -35,20 +37,93 @@ export class MarkdownParser implements Parser {
|
|||
parse(input: string): AnyNode {
|
||||
const astDoc: AstDocument = { kind: AstKind.Document, content: [] };
|
||||
|
||||
let readStr = input;
|
||||
while (readStr.length) {
|
||||
const newReadStr = skipEmptyLine(readStr) ??
|
||||
parseAtxHeading(astDoc, readStr) ??
|
||||
parseList(astDoc, readStr) ??
|
||||
parseParagraph(astDoc, readStr);
|
||||
if (isNil(newReadStr)) break;
|
||||
readStr = newReadStr;
|
||||
const segments = splitLines(input);
|
||||
|
||||
console.log({ segments });
|
||||
|
||||
const res = parseHeaderFromSegments(segments);
|
||||
if (res != null) {
|
||||
astDoc.content.push(res.ast);
|
||||
}
|
||||
|
||||
// let readStr = input;
|
||||
// while (readStr.length) {
|
||||
// const newReadStr = skipEmptyLine(readStr) ??
|
||||
// parseAtxHeading(astDoc, readStr) ??
|
||||
// parseList(astDoc, readStr) ??
|
||||
// parseParagraph(astDoc, readStr);
|
||||
// if (isNil(newReadStr)) break;
|
||||
// readStr = newReadStr;
|
||||
// }
|
||||
|
||||
return new Fragment(astDoc.content.map(DocChild));
|
||||
}
|
||||
}
|
||||
|
||||
function parseHeaderFromSegments(
|
||||
lines: Lines,
|
||||
): ParsedRes<AstAtxHeading> | null {
|
||||
const headingMatch = RE_ATX_HEADING.exec(lines[0]);
|
||||
if (headingMatch == null) return null;
|
||||
|
||||
const inlineContent = parseInlineContentFromSegments([headingMatch[2]]);
|
||||
|
||||
const ast: AstAtxHeading = {
|
||||
kind: AstKind.AtxHeading,
|
||||
content: [],
|
||||
level: headingMatch[1].length as HeadingLevel,
|
||||
};
|
||||
|
||||
return {
|
||||
ast,
|
||||
restLines: lines.slice(1),
|
||||
};
|
||||
}
|
||||
|
||||
function parseInlineContentFromSegments(
|
||||
lines: Lines,
|
||||
): ParsedRes<AstInlineContent[]> | null {
|
||||
if (!lines.length) return null;
|
||||
|
||||
const linkMatch = RE_LINK.exec(readStr);
|
||||
if (!isNil(linkMatch)) {
|
||||
const astLink: AstLink = {
|
||||
kind: AstKind.Link,
|
||||
destination: encodeURI(linkMatch[3] ?? linkMatch[2]),
|
||||
title: linkMatch[5],
|
||||
content: [],
|
||||
};
|
||||
|
||||
// 1. parse before link
|
||||
parseText(ast, readStr.slice(0, linkMatch.index));
|
||||
|
||||
// 2. create link and parse inner content for link
|
||||
ast.content.push(astLink);
|
||||
parseText(astLink, linkMatch[1]);
|
||||
|
||||
// 3. parse rest text
|
||||
return parseInlineContent(
|
||||
ast,
|
||||
readStr.slice(linkMatch.index + linkMatch[0].length),
|
||||
);
|
||||
} else {
|
||||
return parseText(ast, readStr);
|
||||
}
|
||||
}
|
||||
|
||||
interface ParsedRes<T> {
|
||||
ast: T;
|
||||
restLines: Lines;
|
||||
}
|
||||
|
||||
function splitLines(input: string): Lines {
|
||||
return input.split("\n");
|
||||
}
|
||||
|
||||
type Lines = string[];
|
||||
|
||||
// ---- OLD
|
||||
|
||||
function List(ast: AstList): Elem {
|
||||
// switch (ast.kind)
|
||||
return BulletList(ast);
|
||||
|
|
Reference in a new issue