基于堆栈的堆栈标记解释器
标记(6), erlang(20)我周末的一个项目是用Erlang编写一个基于堆栈的Markdown解释器。让我先承认这个项目在完整性和性能方面都失败了。
标记的在线方面非常直截了当地用堆栈解析:
*测试* **测试**`test` [测试] [test])[测试]:http://test.com/“test”[test] [test]
甚至相当复杂的组合,比如*如果你不介意的话,请把这个测试出来`*
使用基于堆栈的解释器很容易,尽管在依赖正则表达式的解决方案中可能相当复杂。
然而,Markdown的多行结构在处理一个幼稚的实现时变得非常笨拙,或者至少在处理一个幼稚的实现和缺乏计划的情况下。
考虑无序列表的语法:
-这是一个列表-是的,仍然是一个列表-仍然是一个列表
在创建之前
元素,您需要检查是否已经存在
堆栈上的元素。如果没有,则添加,否则继续。关闭有点复杂,需要在关闭时检查下一行
作出判断的要素。
然后有块状物,这表现了与之相同的方式
引用,除了没有
放置在堆叠上用于跟踪当前位置。
然后是代码块,其行为类似于块引号。
然后是段落,默认情况下,这些段落显示为没有封闭标记的文本。
将这些东西与多个缩进级别和不一致的换行处理结合起来(blockquote中的换行变成了空格,而pre块中的换行仍然是换行,等等),在第一次实现中缺乏计划是相当糟糕的。
下面有一些关于这一进展的更多细节,但我回到了多线方面的绘图板。如果我仍然需要掌握基于堆栈的概念的多线解析,但我仍然认为我认为我做出了一个预编译的阶段,它构建了所有多行构造的DOM,然后使用堆栈-Parser专门针对各个线路。
完整性
测试电池erlang_markdown.
当前为:
测试实例()- >[{“*测试*”,<<“测试”
>>},{“**测试**”,<<“测试”
>>},{“”测试“”,<<“ 测试 code> p>”
>>},{“`测试`”,<<“ 测试 code> p>”
>>},{“[测试](http://test.com/)”,<<“>>},{“[测试](http://test.com/\test2.\)“,<<“>>},{“[测试]:http://test.com/\ n[测试][测试]“,<<“>>},{“[测试]:http://test.com/\test2.“\ n[测试][测试]“,<<“>>},{“出去”,<<“ out p>”
>>},{“出去\ n",<<“ out p>”
>>},{“出去\ n呜呜\ n",<<“ out woot p>”
>>},{“出去\ nWoot“,<<“ out woot p>”
>>},{“>>这是\ n>>测试\ n",<<“这是一个测试 blockquote>”
>>},{“这是\ n一个测试\ n",<<“这是
\ n测试>>},{“这是一个\ n测试\ n是的\ n>>还有这个\ n>>“太”,<<“这是一个
\ ntestyep pre> ,这太 blockquote>“
>>},{“这是一个测试\ n另一个\ n还有另一个*\ n",<<“这是一个测试,另一个是另一个”
>>},{“测试\ n这\ n出去\否\n**是的**”,<<“测试
\ nTheOut pre> 是的 strong> p>“
>>},{“这是一个测试*\否\n导入测试\ n导入Test2.\ n仍然是**测试**\ n",<<“这是一个测试 em> p> 导入测试
\ n导入测试2仍然是测试>>},{“测试\ n这\否\n>>“,<<“测试这个
输出
”>>},{“测试\ n这\否\n>> out.\ n>>再次\ n",<<“再次测试”
>>},{“测试这个\ n**出去**\否\n>>还有这个\ n>>以及\否\n求爱\ n呼\ n",<<“测试这个输出
以及这个
woo
\ n呼
”>>},{“ 这是一个测试\ n前面\否\n*测试这个\ n*出去\否\nWoohoo“,<<“这是一个测试
\ npre pre> woohoo p>“
测试这个 out code> em> p>”
测试此输出
”>>},{“* 这是一个测试\ n*这是什么\ n*还有这个\ n",<<“- 这是一个测试,所以这是
- 和这个
- ”
- 测试此输出
- 测试”
- 测试此输出
这实际上只是一个测试就可以了吗? p>
和这个 p>”
>>}]。以及运行测试电池的输出:
62>计时器:tc公司(降价,测试,[]).跑步测验:EE公司............................失败:<<“ - 测试这个 out strong> em> ul> li>”
>>!=<<“- 测试此输出
”>>(输入:“**测试此**输出***”)失败:<<“这实际上只是一个测试就可以了吗?这个 p>呢?
>>!=<<“这实际上只是一个测试就可以了吗? p>
和这个 p>”
>>(输入:“这真的只是一个考验\ n可以吗?\否\n怎么样\ n这\ n")28测验通过/2测验失败/30全部的测验{1885年,好的}
第一个问题可以通过在输入的末尾添加一个新行来解决,但它确实应该按原样工作。第二个问题揭示了一个根本性的缺乏规划的问题,这导致了这个部分实施变得非常难以修改,也使得它永远无法完全符合。
您会注意到,这些测试不包括多层结构。这个设计考虑到了它们,我很确定让它们工作是很直接的,但老实说,多行解析的总体设计有足够的缺陷,不值得为此费心。
表现
性能测试(不)- >Str1型=“* **这是一个测试** *,所以*\ n*另一条线\否\n1一条线\ n2a线2\ n3.另一条线\否\n",Str2型=“>> Blockquote.\ n>>块引号2\否\nPre Block1.\ n相同的预块\否\n",Str3型=“[测试](http://test.com\这\)\这出了\ n``代码块``\否\n",Str公司=清单:追加([Str1型,Str2型,Str3型]),Longstr.=清单:追加(清单:地图(乐趣(_n)- >Str公司结束,清单:序号(0,100.))),{时间,_}=计时器:tc公司(降价,降价,[Longstr.]),秒=时间/1000000.0,IO.:格式("〜P.查尔斯被带走了〜P.秒〜n",[长度(Longstr.),秒]).
对于大文件而言,性能肯定并不漂亮。有一些明显的O(n ^ 2)操作(将累积的二进制文件合并到一个字符串中,然后重新调温成二进制文件不能是廉价的操作),并且可能是由ad-hoc实现产生的一些不太明显的性能影响。
8>降价:性能测试(1)。428.字符拿5.04e-4秒好的9>降价:性能测试(10)。2354字符拿0.002643.秒好的10>降价:性能测试(100.)。21614字符拿0.040283秒好的11>降价:性能测试(1000)。214214字符拿58.638143秒好的
是的,非常糟糕。出于好奇,我移走了最后一步列出\u到\u二进制文件
在整个结构上重新运行测试:
18>降价:性能测试(1000)。214214字符拿106..470855秒好的
这有人设法近两倍。我会继续前进并考虑一个侥幸,但它肯定没有神奇地改善事情。
经过一些额外的工作......
我将多行逻辑修改成更系统的东西(与以前的逻辑概念类似,但实现更系统),现在它通过了所有测试。唯一缺少的功能是缩进列表。这是非常重要的。
性能也有了很大的提高:
71>降价:性能测试(1)。428.字符拿三.04e-4秒好的72>降价:性能测试(1)。428.字符拿三.04e-4秒好的73>降价:性能测试(10)。2354字符拿0.001699秒好的74>降价:性能测试(100.)。21614字符拿0.033964秒好的75>降价:性能测试(200.)。43014字符拿0.121854秒好的76>降价:性能测试(500)。107214字符拿4.124487.秒好的77>降价:性能测试(1000)。214214字符拿25.825798秒好的
这次我没有决赛的有点系统测试列出\u到\u二进制文件
呼叫,性能似乎完全不一致。虽然它总是比它更快,但有时它比其他尝试非常快(10倍)......
由于一切都在记忆中,它看起来并不应该缓存应该产生重大影响。也许我缺少一些东西。
78>我(降价)。{模块,降价}79>降价:性能测试(1)。428.字符拿三.84e-4秒好的80>降价:性能测试(10)。2354字符拿0.001953秒好的81>降价:性能测试(100.)。21614字符拿0.019525.秒好的82>降价:性能测试(200.)。43014字符拿0.449793秒好的83>降价:性能测试(200.)。43014字符拿0.054803.秒好的84>降价:性能测试(200.)。43014字符拿0.052132秒好的85>降价:性能测试(200.)。43014字符拿0.160802秒好的86>降价:性能测试(500)。107214字符拿0.566199秒好的87>降价:性能测试(500)。107214字符拿0.417525秒好的88>降价:性能测试(500)。107214字符拿0.245372秒好的89>降价:性能测试(1000)。214214字符拿7.128518.秒好的90>降价:性能测试(1000)。214214字符拿11.816736秒好的91>降价:性能测试(1000)。214214字符拿2.119201秒好的92.>降价:性能测试(1000)。214214字符拿17.814619秒好的
嗯。还有更多的工作要做。当GitHub恢复生命时,会将更改推送到GitHub。