内嵌解析
很容易遇到这样的情况:在需要高亮的代码中还混淆着其它语言种类的代码(最常见的例子为Html内嵌css和js,以下也将以此为例)。这是一件让人头疼的事情,因为无论采用何种方法,内嵌的语言和原本语言的规则一定是不同的。这意味着必须将它们区分开来对待。从这一点出发,自然而然就能引出问题的关键所在——如何区分?
剥离内容
先来考虑最简单的情况:
<head> <script>var i = 0;</script> </head>
假如没有第2行的script标签以及其内部的代码,那么整个就是纯html代码高亮,这个没有什么难度(如果已经完全理解前三篇的此法分析的话)。然而不凑巧的是,关键点就在于script标签中会出现js代码,html的此法分析中并没有js的词法规则,两者不能等同。那么怎么办呢?
答案是将它们剥离出来。上例是最简单的例子,我们在对html进行此法分析的时候,一旦读到了<script>开始标签,接着便去寻找</script>结束标签(一般会使用String.indexOf()来查找),然后将标签里面的内容单独提取出来。这是第一步,如果做完的话,此时html的高亮结果应该是:除了js代码没有高亮(即默认颜色)以外,其它的html代码均被正确高亮了。
更复杂的情况
剥离到此还没有结束,因为剥离要考虑其它一些复杂的元素。看以下代码:
<head> <script>var i = 0; //</script> </script> </head>
代码的第3行中,出现了单行注释,其中有被注释掉的script结束标签。这点需要格外注意。假若使用String.indexOf()来查询script结束标签的话,那么就会在第3行结束。这样就错了,因为第3行实际上是个注释,真正的结束符在第4行。
以此延伸,除了上面的情况以外,引号中的字符串、多行注释、正则里面均会出现类似情况。因此,单纯的String.indexOf()是肯定不行的。我们必须对js代码部分进行预处理。
预处理
在as的解析部分,实际上主要分为两大块:词法分析和存储结果。词法分析即是前面几篇一直在讲解的内容;存储结果即是将分析出来的代码链接起来,说白了就是简单的字符串拼接。
在html中的js代码可能会出现混淆script结束标签的情况之下,唯一解决的办法就是对js也进行简单的词法分析预处理,但不存储结果。因为分析是“读”,而存储是“写”。写的耗时要比读多多了,而且预处理只是为了防止注释、字符串和正则的混淆,不需要真正地进行解析,实现复杂读也比较低。等完全将js代码从html中剥离后,再交给js解析器来做真正的工作,如此也保证了代码的不重复。
<head> <script> var i = 0; //</script> "</script>" /*</script>*/ /[</script>]/ </script> </head>
html中的预处理,至少要保证能将3到7行的js代码完全剥离出来交给js解析器处理,其它html代码则由本身来完成高亮。
状态区分
接下来的难点可能还是在如何在html解析的时候完成区分上面。在这里,我设置了一个state变量用以标识状态。仔细考虑下html,无非发现它主要有以下几种状态:
- html节点:即<>内的tag,还有随之的一些属性内容。如:<img width=”100px”/>。
- text节点:文本内容,段落p中最常见到。
- css节点:style中的css代码。
- script节点:script中的js代码。
在默认最开始的时候,是文本节点。一旦遇到了左尖括号,并且随后跟的是个正确的节点名(<x>绝对不是个正确的节点,所以不能当成节点来处理),那么就进入节点状态来解析;当节点解析完了之后,返回文本状态。css和script节点是个两个特殊的节点,因为在它们的开始标签结束之后,要进行预处理查找结束标签。实际上会做其中的一个,另外一个也就懂了。
值得注意的是,html标签中有单个类型的存在,比如<br/>,它不需要成对出现,甚至可以写成<br>。省略/的又是另外一种自闭合类型。它们在处理起来有点麻烦,特别是涉及到深度折叠的时候。解决的办法也是设置状态变量,标识当前节点属于那种类型,以此来区分判断。
这篇写得可能有点简单,因为的确是比较抽象的东西。我也偷偷懒,相信能做到前章所提的词法分析的情况下,纯理论来读本篇也不是什么难事了。可能直接读我的源代码反而会更容易些。在下一篇当中,我会介绍as和js的交互以及jssc的大概处理流程,它将作为结束篇章。

文章不错,呵呵
[...] 在下一篇中,我将讲述jssc 5是如何做到嵌套高亮(html中内嵌css和js)的。 [...]