本文延续[url=http://hax.iteye.com/blog/181358]上一篇blog[/url],对语法解析问题做一点点思考。注意,本文的角度仅仅是从轻量级解析方面考虑,也就是说不考虑像语言的编译器或解释器那样要完整精确地将源码解析为AST。轻量级解析的目标应用是诸如语法高亮、预处理、lint、语法糖衣或简单的语言扩展之类。
因此讨论顺序也比较特殊,从注释开始。在上述处理中一般会要排除注释。
通常语言可能支持两种comments,单行comments和多行comments。
单行comments是从一个特定符号组合开始,到换行符结束。常见的单行comments包括:
// comments
# comments
-- comments
' comments
rem comments
多行comments是从一个特定符号组合开始到一个特定符号组合结束。多行comments要考虑是否允许嵌套(通常是不允许嵌套的)。常见的多行comments包括:
/* comments */
<!-- comments -->
" comments "
再次是字符串源文本(string literal)。最常见的就是引号包围的部分。需要注意的是几点:
1. 特殊字符的escape,如\"、\'还有\\。
2. 可能会支持某种字符表达方式,例如\unnnn。有时这种表达法可能不限于字符串源文本而是整个源代码。
3. 某些特殊形式的字符串源文本,例如允许内插变量的(如"x=${x}"),无escape的(如@"c:\test"),跨行的(如"""...""")等。
语言的语法格式通常会包含一些特殊符号,例如EOS(如“;”)、block(如“{}”)等,然而在根据这些符号进行解析时,必须排除comments和string literal中的这些符号。但是comments所用符号和string literal的定界符本身也可能包含于string literal和comments中。比如一个包含注释定界符的字符串"/*"。
许多语言还支持正则表达式源文本(regex literal)。这使得情况更复杂。注意由于正则源文本常常用/regex/形式包围,需要与除法符号区分开来。这存在一个很严重的问题,因为判断是否适用于除法,还是适用于正则,可能要用到语义的信息。因此,为了保证轻量级,我们可能要做一些折衷。
comments、string literal和regex literal等是语言中的特殊构造,它们可能包含大量字符,包括语言的保留字和用于定界的标点符号等。而且它们往往可以包含对方的定界符,如在comments中的string literal开始符号就不再产生string literal了,而string literal中的comments起始符号也不再产生comments了。
排除了它们之后,剩下的就是语言本身用到的特殊符号。
首先是EOS(语句结束符)。大体可以分为三类:
一类是以特殊符号为结束符,例如C-style的“;”。
一类是以EOL(行结束符)为结束符,有时也允许同时采用EOS。比如Python。
一类是没有明确的EOS,最有名的就是JavaScript,其次如Groovy。
其次是代码块。大体是三类:
一类是以符号表示,如C-style的花括号结构。
另一类是以关键字表示,如begin end。
还有一类是以缩进表示,如Python。
再次是语言的保留字。包括关键字、特定的literal(如true、false)等。
注意,有些关键字并不总是关键字,而是可以在上下文中充当一般的标识符。
其他特殊构造包括各种literal,如数字源文本、数组、映射表、元组等。
最后,有些场景存在多种语言嵌套的情况。例如HTML会嵌套CSS、脚本等。有些语言则支持内嵌XML literal(如E4X)或内嵌其他语言(如LINQ、SQLJ?)。这些情况可能会很复杂,轻量级的解析方法很可能无法应对。
因此讨论顺序也比较特殊,从注释开始。在上述处理中一般会要排除注释。
通常语言可能支持两种comments,单行comments和多行comments。
单行comments是从一个特定符号组合开始,到换行符结束。常见的单行comments包括:
// comments
# comments
-- comments
' comments
rem comments
多行comments是从一个特定符号组合开始到一个特定符号组合结束。多行comments要考虑是否允许嵌套(通常是不允许嵌套的)。常见的多行comments包括:
/* comments */
<!-- comments -->
" comments "
再次是字符串源文本(string literal)。最常见的就是引号包围的部分。需要注意的是几点:
1. 特殊字符的escape,如\"、\'还有\\。
2. 可能会支持某种字符表达方式,例如\unnnn。有时这种表达法可能不限于字符串源文本而是整个源代码。
3. 某些特殊形式的字符串源文本,例如允许内插变量的(如"x=${x}"),无escape的(如@"c:\test"),跨行的(如"""...""")等。
语言的语法格式通常会包含一些特殊符号,例如EOS(如“;”)、block(如“{}”)等,然而在根据这些符号进行解析时,必须排除comments和string literal中的这些符号。但是comments所用符号和string literal的定界符本身也可能包含于string literal和comments中。比如一个包含注释定界符的字符串"/*"。
许多语言还支持正则表达式源文本(regex literal)。这使得情况更复杂。注意由于正则源文本常常用/regex/形式包围,需要与除法符号区分开来。这存在一个很严重的问题,因为判断是否适用于除法,还是适用于正则,可能要用到语义的信息。因此,为了保证轻量级,我们可能要做一些折衷。
comments、string literal和regex literal等是语言中的特殊构造,它们可能包含大量字符,包括语言的保留字和用于定界的标点符号等。而且它们往往可以包含对方的定界符,如在comments中的string literal开始符号就不再产生string literal了,而string literal中的comments起始符号也不再产生comments了。
排除了它们之后,剩下的就是语言本身用到的特殊符号。
首先是EOS(语句结束符)。大体可以分为三类:
一类是以特殊符号为结束符,例如C-style的“;”。
一类是以EOL(行结束符)为结束符,有时也允许同时采用EOS。比如Python。
一类是没有明确的EOS,最有名的就是JavaScript,其次如Groovy。
其次是代码块。大体是三类:
一类是以符号表示,如C-style的花括号结构。
另一类是以关键字表示,如begin end。
还有一类是以缩进表示,如Python。
再次是语言的保留字。包括关键字、特定的literal(如true、false)等。
注意,有些关键字并不总是关键字,而是可以在上下文中充当一般的标识符。
其他特殊构造包括各种literal,如数字源文本、数组、映射表、元组等。
最后,有些场景存在多种语言嵌套的情况。例如HTML会嵌套CSS、脚本等。有些语言则支持内嵌XML literal(如E4X)或内嵌其他语言(如LINQ、SQLJ?)。这些情况可能会很复杂,轻量级的解析方法很可能无法应对。