80 lines
2.2 KiB
TypeScript
80 lines
2.2 KiB
TypeScript
import {Tree} from "@lezer/common"
|
|
import {MarkdownParser} from ".."
|
|
|
|
const abbrev: {[abbr: string]: string} = {
|
|
__proto__: null as any,
|
|
CB: "CodeBlock",
|
|
FC: "FencedCode",
|
|
Q: "Blockquote",
|
|
HR: "HorizontalRule",
|
|
BL: "BulletList",
|
|
OL: "OrderedList",
|
|
LI: "ListItem",
|
|
H1: "ATXHeading1",
|
|
H2: "ATXHeading2",
|
|
H3: "ATXHeading3",
|
|
H4: "ATXHeading4",
|
|
H5: "ATXHeading5",
|
|
H6: "ATXHeading6",
|
|
SH1: "SetextHeading1",
|
|
SH2: "SetextHeading2",
|
|
HB: "HTMLBlock",
|
|
PI: "ProcessingInstructionBlock",
|
|
CMB: "CommentBlock",
|
|
LR: "LinkReference",
|
|
P: "Paragraph",
|
|
Esc: "Escape",
|
|
Ent: "Entity",
|
|
BR: "HardBreak",
|
|
Em: "Emphasis",
|
|
St: "StrongEmphasis",
|
|
Ln: "Link",
|
|
Al: "Autolink",
|
|
Im: "Image",
|
|
C: "InlineCode",
|
|
HT: "HTMLTag",
|
|
CM: "Comment",
|
|
Pi: "ProcessingInstruction",
|
|
h: "HeaderMark",
|
|
q: "QuoteMark",
|
|
l: "ListMark",
|
|
L: "LinkMark",
|
|
e: "EmphasisMark",
|
|
c: "CodeMark",
|
|
cI: "CodeInfo",
|
|
cT: "CodeText",
|
|
LT: "LinkTitle",
|
|
LL: "LinkLabel"
|
|
}
|
|
|
|
export class SpecParser {
|
|
constructor(readonly parser: MarkdownParser, readonly localAbbrev?: {[name: string]: string}) {}
|
|
|
|
type(name: string) {
|
|
name = (this.localAbbrev && this.localAbbrev[name]) || abbrev[name] || name
|
|
return this.parser.nodeSet.types.find(t => t.name == name)?.id
|
|
}
|
|
|
|
parse(spec: string, specName: string) {
|
|
let doc = "", buffer = [], stack: number[] = []
|
|
for (let pos = 0; pos < spec.length; pos++) {
|
|
let ch = spec[pos]
|
|
if (ch == "{") {
|
|
let name = /^(\w+):/.exec(spec.slice(pos + 1)), tag = name && this.type(name[1])
|
|
if (tag == null) throw new Error(`Invalid node opening mark at ${pos} in ${specName}`)
|
|
pos += name![0].length
|
|
stack.push(tag, doc.length, buffer.length)
|
|
} else if (ch == "}") {
|
|
if (!stack.length) throw new Error(`Mismatched node close mark at ${pos} in ${specName}`)
|
|
let bufStart = stack.pop()!, from = stack.pop()!, type = stack.pop()!
|
|
buffer.push(type, from, doc.length, 4 + buffer.length - bufStart)
|
|
} else {
|
|
doc += ch
|
|
}
|
|
}
|
|
if (stack.length) throw new Error(`Unclosed node in ${specName}`)
|
|
return {tree: Tree.build({buffer, nodeSet: this.parser.nodeSet, topID: this.type("Document")!, length: doc.length}), doc}
|
|
}
|
|
}
|
|
|