From ffb08ed9ee28c18a5058b2f21e5e8f392f80b79e Mon Sep 17 00:00:00 2001 From: Dmitriy Pleshevskiy Date: Sat, 11 Jun 2022 22:07:22 +0300 Subject: [PATCH] par(md): add parsing heading --- .gitignore | 6 +++++ par/md.test.ts | 23 ++++++++++++++++ par/md.ts | 73 ++++++++++++++++++++++++++++++++++++++++++++++++++ par/types.ts | 5 ++++ 4 files changed, 107 insertions(+) create mode 100644 par/md.test.ts create mode 100644 par/md.ts create mode 100644 par/types.ts diff --git a/.gitignore b/.gitignore index 8c55080..4634098 100644 --- a/.gitignore +++ b/.gitignore @@ -9,5 +9,11 @@ !/import_map.json !/core +/core/* !/ren +/ren/* +!/par +/par/* + +!/**/*.ts diff --git a/par/md.test.ts b/par/md.test.ts new file mode 100644 index 0000000..436708b --- /dev/null +++ b/par/md.test.ts @@ -0,0 +1,23 @@ +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), "

"); + }, +}); + +Deno.test({ + name: "should parse header with text", + fn: () => { + const par = new MarkdownParser(); + const res = par.parse("# hello"); + assertEquals(ren.render(res), "

hello

"); + }, +}); diff --git a/par/md.ts b/par/md.ts new file mode 100644 index 0000000..e6a275a --- /dev/null +++ b/par/md.ts @@ -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; +type AstDocumentChild = AstHeading; + +interface AstHeading extends BaseAstItem { + level: HeadingLevel; +} + +type AstText = BaseAstItem; + +type HeadingLevel = 1 | 2 | 3 | 4 | 5 | 6; + +interface BaseAstItem { + kind: K; + content: Cont; +} + +enum AstKind { + Document, + Heading, + Text, +} diff --git a/par/types.ts b/par/types.ts new file mode 100644 index 0000000..ecbf894 --- /dev/null +++ b/par/types.ts @@ -0,0 +1,5 @@ +import { AnyNode } from "../core/node.ts"; + +export interface Parser { + parse(input: string): AnyNode; +}