StrictMark:Markdown,重构

2021-01-30 01:59:16

Markdown是一种出色的轻量级标记:简约,易于阅读和书写。 GitHub,Bitbucket,Reddit,Diaspora,Stack Exchange和许多其他语言都支持Markdown,但这并不是没有问题的。降价可以说是基于先例的。它的目的是整理各种……以前存在的做法。因此,实现之间是混乱且不一致的。

StrictMark是Markdown的合理子集,它以可能的最短形式语法实现所有功能。因此,语法统一且无歧义。这个想法是,StrictMark可以重用所有现有的Markdown支持,而无需共享传统语法的权重及其偶然的复杂性。

Markdown的实现是不一致的。 Vim会以一种方式突出显示它,而VS Code则以不同的方式进行突出显示,结果HTML则是另一回事。针对具有正式语法的不一致实现的教科书修复。在更简单的情况下,这可能是适当的eBNF语法,也可能只是一些正则表达式。必须有正式而明确的东西。 HTML拥有,CSS拥有,每种标记或编程语言都拥有。但Markdown令人遗憾的是,语法本身含糊不清,以至于形式化语法成为一条痛苦之路。例如,HTML(几乎是轻量级的)对于< / elements>具有统一的语法。使用Markdown,每个元素都有其自己的语法,并且那些语法相互影响。过去曾尝试过Markdown正式语法,但是如果你问我,结果是难以理解的。基于PEG的语法长700行。对于极简主义的标记,这很多。好讽刺。

CommonMark是Markdown的一项编纂工作,产生了迄今为止最完整的规范。尽管如此,该规范基于规则和例外,没有语法。规范的文本充满" legalese&#34 ;:

«缩进的代码块不能中断段落,因此段落与后面的缩进的代码块之间必须有空白行»

在这里,它描述了两个特定标记元素之间的相互关系的细节。但是N个元素会产生N * Nrelations!考虑到ATX标题始终是单行,而Setext标题可以是多行。为什么?因为这种说法。这是一大堆规则,另一个例外。这就是为什么它建议一种解析策略的原因。而不是解析器生成器。

尚不清楚CommonMak规范是否已解决所有极端情况和歧义。也许很明确,因为该规范正在积极修订中。代码也是如此。 CommonMark C解析器是10KLoC的手写代码。它有很多例外和调整。在编写RON文档时,我立即遇到了问题;必须使用HEAD版本,该版本已解决了这些问题。

总的来说,看似理论上的语法问题会导致大量的偶然复杂性。 Markdown混乱且难以推理;目前尚不清楚如何解释给定的结构。当然,对于当前的用途来说,组合混乱可能不是问题。 (尽管,我对此非常怀疑。)毕竟,现有的libcmark解析器是模糊的,因此相当可靠。不过,如果您想在Markdown的基础上进行构建,Markdown仍然是一个非常不稳定的基础。构建比自述文件更高级的内容。就像完整的所见即所得编辑或差异突出显示或其他复杂行为一样。

StrictMark的目标是制作Markdown子集,这是一种适当的标记语言。通过合理化语法并将其形式化来消除偶然的复杂性。本文档为StrictMark。

可能有人会问,为什么要让Markdown成为一种合适的语言?毕竟,有足够的HTML了。是的,HTML是一种广泛支持的标准,但是它毫无希望。我们认为,谁负担得起开发/支持适当的HTML引擎的能力?这大约是全世界的一家半公司,因此人们对简约的超文本标记语言很感兴趣。

下一个问题很明显。如果StrictMark曾经被大规模使用,它自然不会变成大象吗?请考虑Wikipedia / Mediawiki标记。一旦像所有这样的标记一样整洁,精益,它就演变成一团糟。例如,表支持呢?有人认为这是必要的,这是当之无愧的。

我计划通过启用包含来防止功能泛滥。文档可能会通过超链接引用其他文档和对象。它还可以通过超链接包含其他对象和文档。好老的< img>标签是包含的一个例子。这甚至都不会扩展语法:StrictMark在通常的包含情况下重新使用图像语法。一旦需要表,就将包含表!渲染器要处理所有其他数据类型。 CSV是一种比HTML或Markdown更好的表格格式。

在大多数情况下,StrictMark是CommonMark的向后兼容子集。任何现有的CommonMark工具都将很好地支持StrictMark。 StrictMark解析器可能无法理解任意Markdown。

正式语法。 StrictMark在其结构部分(即块)是一种常规语言。内联标记语法基于正则表达式定义的标记。这样,仅可以在正则表达式中实现体面的解析器。

有一种方法可以做到尽可能统一,因此:空格,而不是制表符!因为空格可以代替制表符,所以不能反过来。仅适用于ATX标题。格式括号仅*个字符*宽。所有块格式均统一缩进。

没有诡异的距离动作。每行都可以单独解析。所有结构标记均为4个字符宽,因此凹痕是均匀的。这种限制使嵌套结构清晰明确。

内联标记的嵌套非常有限;无论如何,它是次要的。有明确的标记优先级;代码跨度胜过强力,胜于强力。

HTML不是唯一的输出格式。可以是PDF,DOC,TeX等。因此,没有HTML插入。对其他格式使用包含。

Markdown内联标记似乎很简单。可悲的是,事实并非如此。由于语法不规则且模棱两可,因此很难正确实施。因此,StrictMarkration通过以下方式对内联标记进行标准化:

所有内联标记被视为括弧。括号使用正则表达式分别匹配;例如(?< = \ s)[*](?= \ S)是STRONG的开头括号。括号对只有在满足优先级规则时才有效。

开括号可以与以下任何该类型的闭括号配对。一个新的开括号将取消任何之前同类的不匹配的开括号。

较高优先级的括号可能嵌套在较低优先级的括号中,但反之则不行(较低的括号被取消)。如果优先顺序相同,则以较早的范围为准。

这似乎是限制性的,但又是:内联格式具有辅助作用。它必须自己拉重量,否则不能拉重量。准确的括号模式在语法附​​录中列出。

CommonMark支持的唯一链接形式是完整的参考链接。链接标签的长度必须恰好是一个符号。这种方法:

参考定义可以放在任何地方。最好将它们放在节的末尾或文档末尾。例如:

如果您有10个以上的链接,请使用字母。如果您有成千上万的链接,请使用Unicode符号。

包含和图像使用与链接相同的语法,带有感叹号!例如:

容器和入口块可以包含其他块,叶块则不能。根据叶子块的类型,它可以包含文本,元数据或根本不包含任何内容,

与块相关的标记在该行的开头,以四个符号组成。线的那一部分称为块堆栈。允许的嵌套模式是(INDENT | QUOTE)* LIST?叶?。在没有显式叶块的情况下,将隐含格式化的文本段落。

可以在以下各行中继续执行块:空行被认为是代替块标记的缩进(四个空格)。空行被视为堆栈中容器块的续行,而不是叶块。

#多行标题1.此条目开始,然后继续并继续... 2. ...直到下一个条目。

块堆栈深度的变化会导致容器嵌套的更改。少于4个空格的额外缩进没有意义,这使得逐行解析和解释更加容易。如果缩进级别以裸露的开头开始,则会创建通用容器块(在换句话说,一个div)。 WithCommonMark,应该是一个代码块。 StrictMark对此进行了概括。

如果块标记以非空格符号结尾,则下一个符号必须为空白(空格)。例如,唯一的< h4>标记是####,后面必须跟空格。该空格可能是下一个标记或行本身的一部分。

>块引用,-项目符号列表,1.编号列表,通用块(div),``围栏代码块,##标头(4级),[x]:参考定义,____标尺,[] TODO条目(在清单)。

StrictMark限于四个级别的标题。只允许ATXheader。标头可以是多行。标记仅在行的开头。例子:

请注意,标头标记用空格填充为4个字符,与块标记的其余部分一样。如果最后一个字符不是一个空格,则内联文本必须以空格开头。

无序列表标记符号是破折号-。其他两个Markdown选项是*和+。但是*是模棱两可的,而+是不受欢迎的,并且只能是一种方法!

有序列表的标记为12.数字点。同样,listmarkup每个级别需要四个字符。如果最后一个字符不是空格,则第一个内联文本字符必须是空格。项目符号的特殊缩进由用户决定。印刷术纯粹主义者可能更喜欢_1._,而喜欢使用Tab的人会写1 .__。支持GitHub ToDo扩展语法作为另一个叶子块_ []。

-项目符号列表-仍为项目符号1.嵌套编号列表2.更多编号的普通段落,也嵌套,缩进4个字符1.另一个嵌套列表2.包含两个条目-恢复项目符号列表1.我是排版2.纯粹主义者,我在Mac上将终端设置为3.自定义字体。 <!--> 1.我经常使用Tab,2.我不喜欢打扰。

请注意,编号列表仅限于999个正确编号的条目。从技术上讲,对所有条目编号1.将产生完全正确的输出,但是原始标记将不会正确编号。

为了分隔相同类型的两个相邻列表,CommonMark建议使用空的HTML注释。我们不能做得比建议完全相同的技巧更好。这算作两个虚拟块标记,总共8个字符。在实践中,您会在两者之间插入一些文本。

唯一的一种格式,其中延续不一定是空的缩进,也可以是引号。强烈建议使用缩进。

代码块使用带有四个反引号的栅栏语法。开头的栅栏可能会提到所使用的语言。代码必须缩进4个字符;遵循与其他所有块容器相同的延续规则。结束码栏是可选的;代码块的结尾可以通过缺少缩进来表示。该代码可以安全地使用四个反引号。

## C O N T A I N E R B L O C K S CHAR =任何; WS = [\ t \ r \ n]; NONWS = CHAR-WS; #StrictMark不允许\ n \ r,因为只能有一种方法! #此外,notepad.exe现在支持Unix换行符。 NL =" \ n&#34 ;; NONNL = CHAR-NL; INLINE = NONNL *; MARKUP = [`* _ \ [\]]; PUNCT ="!" .." /" | ":" .."" | " [" .."`" | " {" .."〜&#34 ;; NONWSP = NONWS-PUNCT; WSP = WS | PUNCT; WSA = WS; INDENT =" &#34 ;; HEAD1 =" #" | " #" | " #" | "#&#34 ;; HEAD2 =" ##" | " ##" | " ##&#34 ;; HEAD3 =" ###" | " ###&#34 ;; HEAD4 =" ####&#34 ;; HEADER = HEAD1 | HEAD2 | HEAD3 | HEAD4; QUOTE ="> " | " > " | " > " | " >&#34 ;; ULIST =" -" | "-" | " -" | " -&#34 ;; OLIST =" "数字"。 " |数字"。 " | " "数字"。" |数位"。 " | " "数位"。" |位数位数"。&#34 ;;围栏="````&#34 ;; HLINE =" ----&#34 ;; LINK_LABEL = CODEPOINT-WSP-"]&#34 ;; REFDEF =" [" LINK_LABEL"]:&#34 ;; NESTBLOCK = INDENT |报价; LISTBLOCK = ULIST |奥利斯特; LEAFBLOCK =标题|围栏| HLINE | REFDEF; BLOCK_STACK = NESTBLOCK * LISTBLOCK?叶块? FUCKED_LINE =" TODO&#34 ;; LINE =(BLOCK_STACK INLINE NL); WIKITEXT = LINE *; ##不是每个单词都是URL候选者;单独使用URL解析器##,因为完整的URL语法会使状态机崩溃AWORD =(NONWS +);字= AWORD(WS + AWORD)*; ##强调文字EMPH_BOTH_FLANKING = PUNCT" _" PUNCT; EMPH_LEFT_FLANKING =(WSP" _" NONWS)-EMPH_BOTH_FLANKING; EMPH_RIGHT_FLANKING =(NONWS" _" WSP)-EMPH_BOTH_FLANKING; ##强调;可以嵌套URI,代码范围;可能包含转义符STRONG_BOTH_FLANKING = PUNCT" *" PUNCT; STRONG_LEFT_FLANKING =(WSP" *" NONWS)-STRONG_BOTH_FLANKING; STRONG_RIGHT_FLANKING =(NONWS" *" WSP)-STRONG_BOTH_FLANKING; STRONG_INTRAWORD =" *" NONWSP +" *" ; ##参考链接LINK_OPEN = [^ \]]" [" ; LINK_CLOSE ="] [" LINK_LABEL"]" ; ##代码范围包含任意Unicode,所有字符均按字面解析CODE ="`" ; ##反斜杠和&换码符的优先级比其他内联标记的优先级更高## BACKSLASH_ESC =" \\" PUNCT; AMPERSAND_ESC ="" (alnum + |"#" digit + |"#" [xX] xdigit +)&#34 ;;" ; ESC = BACKSLASH_ESC | AMPERSAND_ESC; ##为防止组合状态爆炸,我们在##嵌入式汤中(分别为左/右)找到标记元素,然后通过嵌套规则过滤它们。LEFT_MARKUP = EMPH_LEFT_FLANKING | STRONG_LEFT_FLANKING | LINK_OPEN; RIGHT_MARKUP = EMPH_RIGHT_FLANKING | STRONG_RIGHT_FLANKING | LINK_CLOSE; OTHER = STRONG_INTRAWORD |代码| ESC键; INLINE_SOUP =(LEFT_MARKUP | RIGHT_MARKUP | OTHER | CHAR)*; LINK_REF_DEF = REFDEF WS *(NONWS +)(WS + ["](CHAR-["])* ["])? WS *; } %%