Compare commits
2 commits
6c175effb4
...
ef1baea81b
Author | SHA1 | Date | |
---|---|---|---|
ef1baea81b | |||
ffb08ed9ee |
4 changed files with 130 additions and 0 deletions
6
.gitignore
vendored
6
.gitignore
vendored
|
@ -9,5 +9,11 @@
|
|||
!/import_map.json
|
||||
|
||||
!/core
|
||||
/core/*
|
||||
!/ren
|
||||
/ren/*
|
||||
!/par
|
||||
/par/*
|
||||
|
||||
!/**/*.ts
|
||||
|
||||
|
|
46
par/md.test.ts
Normal file
46
par/md.test.ts
Normal file
|
@ -0,0 +1,46 @@
|
|||
import { assertEquals } from "testing/asserts.ts";
|
||||
import { HtmlStrRenderer } from "../ren/html_str.ts";
|
||||
import { MarkdownParser } from "./md.ts";
|
||||
|
||||
const ren = new HtmlStrRenderer();
|
||||
|
||||
Deno.test({
|
||||
name: "should parse header",
|
||||
fn: () => {
|
||||
const par = new MarkdownParser();
|
||||
const res = par.parse("#");
|
||||
assertEquals(ren.render(res), "<h1></h1>");
|
||||
},
|
||||
});
|
||||
|
||||
Deno.test({
|
||||
name: "should parse header with text",
|
||||
fn: () => {
|
||||
const par = new MarkdownParser();
|
||||
const res = par.parse("# hello");
|
||||
assertEquals(ren.render(res), "<h1>hello</h1>");
|
||||
},
|
||||
});
|
||||
|
||||
Deno.test({
|
||||
name: "should parse header with specific level",
|
||||
fn: () => {
|
||||
const par = new MarkdownParser();
|
||||
assertEquals(ren.render(par.parse("# hello")), "<h1>hello</h1>");
|
||||
assertEquals(ren.render(par.parse("## hello")), "<h2>hello</h2>");
|
||||
assertEquals(ren.render(par.parse("### hello")), "<h3>hello</h3>");
|
||||
assertEquals(ren.render(par.parse("#### hello")), "<h4>hello</h4>");
|
||||
assertEquals(ren.render(par.parse("##### hello")), "<h5>hello</h5>");
|
||||
assertEquals(ren.render(par.parse("###### hello")), "<h6>hello</h6>");
|
||||
},
|
||||
});
|
||||
|
||||
Deno.test({
|
||||
name: "should parse 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>");
|
||||
},
|
||||
});
|
73
par/md.ts
Normal file
73
par/md.ts
Normal file
|
@ -0,0 +1,73 @@
|
|||
import { AnyNode, Elem, Fragment, TextNode } from "../core/node.ts";
|
||||
import { isNil } from "../core/utils.ts";
|
||||
import { Parser } from "./types.ts";
|
||||
|
||||
const RE_OPEN_HEADING = /^\s{0,3}(#{1,6})(\s|$)/;
|
||||
|
||||
export class MarkdownParser implements Parser {
|
||||
parse(input: string): AnyNode {
|
||||
const ast: AstDocument = { kind: AstKind.Document, content: [] };
|
||||
|
||||
let readStr = input;
|
||||
|
||||
const match = RE_OPEN_HEADING.exec(readStr);
|
||||
if (!isNil(match)) {
|
||||
readStr = readStr.slice(match[0].length);
|
||||
|
||||
console.log({ match });
|
||||
|
||||
const heading: AstHeading = {
|
||||
kind: AstKind.Heading,
|
||||
level: match[1].length as HeadingLevel,
|
||||
content: [],
|
||||
};
|
||||
ast.content.push(heading);
|
||||
|
||||
if (match[2].length > 0) {
|
||||
const textContent = readStr.split("\n", 1)[0];
|
||||
readStr = readStr.slice(textContent.length);
|
||||
|
||||
const text: AstText = {
|
||||
kind: AstKind.Text,
|
||||
content: textContent,
|
||||
};
|
||||
|
||||
heading.content.push(text);
|
||||
}
|
||||
}
|
||||
|
||||
return new Fragment(ast.content.map(Heading));
|
||||
}
|
||||
}
|
||||
|
||||
function Heading(ast: AstHeading): Elem {
|
||||
return new Elem(`h${ast.level}`, {}, ast.content.map(Text));
|
||||
}
|
||||
|
||||
function Text(ast: AstText): TextNode {
|
||||
return new TextNode(ast.content);
|
||||
}
|
||||
|
||||
// AST
|
||||
|
||||
type AstDocument = BaseAstItem<AstKind.Document, AstDocumentChild[]>;
|
||||
type AstDocumentChild = AstHeading;
|
||||
|
||||
interface AstHeading extends BaseAstItem<AstKind.Heading, AstText[]> {
|
||||
level: HeadingLevel;
|
||||
}
|
||||
|
||||
type AstText = BaseAstItem<AstKind.Text, string>;
|
||||
|
||||
type HeadingLevel = 1 | 2 | 3 | 4 | 5 | 6;
|
||||
|
||||
interface BaseAstItem<K extends AstKind, Cont> {
|
||||
kind: K;
|
||||
content: Cont;
|
||||
}
|
||||
|
||||
enum AstKind {
|
||||
Document,
|
||||
Heading,
|
||||
Text,
|
||||
}
|
5
par/types.ts
Normal file
5
par/types.ts
Normal file
|
@ -0,0 +1,5 @@
|
|||
import { AnyNode } from "../core/node.ts";
|
||||
|
||||
export interface Parser {
|
||||
parse(input: string): AnyNode;
|
||||
}
|
Reference in a new issue