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({
|
Deno.test({
|
||||||
name: "should skip empty line",
|
name: "should skip empty line",
|
||||||
|
only: true,
|
||||||
fn: () => {
|
fn: () => {
|
||||||
const par = new MarkdownParser();
|
const par = new MarkdownParser();
|
||||||
assertEquals(ren.render(par.parse("\n")), "");
|
assertEquals(ren.render(par.parse("\n")), "");
|
||||||
|
@ -21,15 +22,30 @@ Deno.test({
|
||||||
|
|
||||||
Deno.test({
|
Deno.test({
|
||||||
name: "should parse empty ATX header",
|
name: "should parse empty ATX header",
|
||||||
|
only: true,
|
||||||
fn: () => {
|
fn: () => {
|
||||||
const par = new MarkdownParser();
|
const par = new MarkdownParser();
|
||||||
const res = par.parse("#");
|
assertEquals(ren.render(par.parse("#")), "<h1></h1>");
|
||||||
assertEquals(ren.render(res), "<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({
|
Deno.test({
|
||||||
name: "should parse ATX header with text",
|
name: "should parse ATX header with text",
|
||||||
|
only: true,
|
||||||
fn: () => {
|
fn: () => {
|
||||||
const par = new MarkdownParser();
|
const par = new MarkdownParser();
|
||||||
assertEquals(ren.render(par.parse("# hello")), "<h1>hello</h1>");
|
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({
|
Deno.test({
|
||||||
name: "should parse ATX header with closing sequence",
|
name: "should parse ATX header with closing sequence",
|
||||||
fn: () => {
|
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_EMPTY_LINE = /^[ ]*\r?\n/;
|
||||||
|
|
||||||
|
const RE_ATX_HEADING = /^[ ]{0,3}(#{1,6})(?:$|([ ].+)(?:[ ]?#*[ ]*)?$)/;
|
||||||
|
|
||||||
const RE_OPEN_ATX_HEADING = /^[ ]{0,3}(#{1,6})([ ]|$)/;
|
const RE_OPEN_ATX_HEADING = /^[ ]{0,3}(#{1,6})([ ]|$)/;
|
||||||
const RE_CLOSE_ATX_HEADING = /(^|[ ]+)#*[ ]*$/;
|
const RE_CLOSE_ATX_HEADING = /(^|[ ]+)#*[ ]*$/;
|
||||||
|
|
||||||
|
@ -35,20 +37,93 @@ export class MarkdownParser implements Parser {
|
||||||
parse(input: string): AnyNode {
|
parse(input: string): AnyNode {
|
||||||
const astDoc: AstDocument = { kind: AstKind.Document, content: [] };
|
const astDoc: AstDocument = { kind: AstKind.Document, content: [] };
|
||||||
|
|
||||||
let readStr = input;
|
const segments = splitLines(input);
|
||||||
while (readStr.length) {
|
|
||||||
const newReadStr = skipEmptyLine(readStr) ??
|
console.log({ segments });
|
||||||
parseAtxHeading(astDoc, readStr) ??
|
|
||||||
parseList(astDoc, readStr) ??
|
const res = parseHeaderFromSegments(segments);
|
||||||
parseParagraph(astDoc, readStr);
|
if (res != null) {
|
||||||
if (isNil(newReadStr)) break;
|
astDoc.content.push(res.ast);
|
||||||
readStr = newReadStr;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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));
|
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 {
|
function List(ast: AstList): Elem {
|
||||||
// switch (ast.kind)
|
// switch (ast.kind)
|
||||||
return BulletList(ast);
|
return BulletList(ast);
|
||||||
|
|
Reference in a new issue