语法高亮的编程语言是否可能使用正则表达式?

时间:2012-03-31 12:44:53

标签: regex parsing syntax-highlighting

我们现在都知道,使用正则表达式解析HTML通常是不可能的,因为它正在解析上下文敏感的语法,而正则表达式只能解析常规语法。其他编程语言也是如此。

现在,最近宣布了Rainbow.js语法高亮显示器。它的前提被描述为非常简单:

彩虹本身很简单。它遍历代码块,处理正则表达式模式,并在标记中包装匹配模式。

我认为语法突出显然本质上是与语言解析相同的复杂任务,如果我们假设它必须既好又适合多种语言。尽管如此,虽然该库中有相当多的criticism,但HackerNews discussion(以技术倾向的讨论为例)都提到使用正则表达式突出显示语法基本上是在一般情况下是不可能的,我认为这是一个主要的,显示停止的缺陷。

现在的问题是:我有什么东西不见了吗?特别是:

  1. 通常可以使用正则表达式突出显示语法吗?
  2. 这是一个应用80/20规则的实例,正则表达式是否足够有用?

4 个答案:

答案 0 :(得分:13)

使用regexp突出显示语法是一门古老的艺术。我想甚至Emacs和vi也是这样开始的。

  

我认为语法高亮基本上是与语言分析相同复杂的任务,[...]

没有。不同之处在于:编译器需要真正的解析,因为他需要理解完整的程序,并且还需要从该理解中生成东西。另一方面,语法突出显示不需要理解代码。它只需要理解语言的一般结构 - 什么是字符串文字 - 什么是关键字......等等。这种差异的副作用是:您可以突出显示语法不正确的代码,但无法解析它。

稍微不同的方法:解析语言通常是一个两步过程:lexing(将字节流拆分为“令牌”流)和真正的解析(将令牌流带入一些复杂的结构 - 通常是抽象语法树)。 Lexing通常使用----正则表达式完成。请参阅flex文档。这基本上都是荧光笔需要理解的基本语法。

当然,有一些角落案例单靠regexp无法捕获。一个典型的例子是:

foo(bla, bar);

这里foo可能是对静态方法或实例方法或宏或其他内容的调用。但你的正则表达式荧光笔无法推断出这一点。它只能为“一般呼叫”添加颜色。

所以:如果你的要求是低级的(即没有上面的例子),那么这是一个100/0%的规则,通常是现实世界的90/10规则。

答案 1 :(得分:3)

您可以使用正则表达式作为解决方案的部分进行语法突出显示。更具体地说,作为“词法分析器”的一部分,将输入文本分块为符号。这实际上是大多数编译器/解释器工作的方式。

要使用正则表达式单独这样做,却要求麻烦。考虑在Python中匹配字符串的情况。 Python允许字符串由单引号'或双引号"分隔。此外,它允许使用三引号'''"""的多行字符串(“heredoc syntax”)。

以下哪些部分是字符串,哪些不是?你能构造一个正则表达式来正确识别字符串文字str1 - str6吗?

str1 = "hello, world!"

str2 = 'hello, world!'

str3 = "The canonical test program is 'Hello World'."

str4 = '"Why," Peter said, "That\'s ludicrous. Who would do that?"'

str5 = """The heredoc syntax is handy for cases where you don't want to escape strings. "Very convenient."
"""

str6 = """Code sample:
s1 = "hi!"
s2 = 'Hi!'
S3 = '''
- apples
- oranges
- bananas
'''
"""

“你不能(使用正则表达式解析HTML |进程程序)因为(HTML |编程语言)具有嵌套结构 - 它们不是常规”这一论点并不完全正确 - 现代正则表达式(特别是在Perl中)比计算机科学意义上的严格常规表达具有更强的表达能力。但仅仅因为你can use regular expressions并不意味着你应该


编辑:如果您的正则表达式支持搜索模式中的反向引用,则上面的字符串匹配问题也不算太糟糕。像('|"|'''|""").+?\1这样的多行正则表达式可能会这样做。


编辑2:有关语法高亮显示中的边角情况的示例,请查看StackOverflow上面代码的语法高亮显示。

答案 2 :(得分:2)

基本上没有。

您需要一个能够理解语言的解析器/标记器来选择要突出显示的位。

正则表达式并没有为这项任务削减芥末。

答案 3 :(得分:1)

要看的一个很好的例子是Vim中的语法高亮实现。它使用基于常规表达的模式。但是,模式用于识别文档中的分层包含结构,而不仅仅是标记输入。

您可以声明在正则表达式模式匹配时开始和结束的区域(以及另一个有助于跳过中间材质的模式)。这些区域可以声明它们包含其他区域或简单模式。遏制可以是递归的。 Vim完成所有这些工作。所以它本质上是一种无上下文解析的形式。

这种方法可以处理具有不同嵌入级别,具有不同词汇属性的语言。

例如,我有一种语言,其中基本上有两组关键字(由于正在进行的域语言嵌入)。我编写的Vim语法突出显示规则正确识别上下文并以不同方式着色关键字。请注意,这些关键字组之间存在一些重叠:相同的单词,在不同的上下文中具有不同的含义。

有关此示例,请参阅:http://www.kylheku.com/cgit/txr/tree/genman.txr。 如果您搜索语法(do,您会发现一个实例的颜色为紫色,另一个实例为绿色。它们是不同的:一个是文本提取语言,另一个是嵌入式Lisp方言。 Vim的语法高亮功能足以处理混合使用不同关键字的语言。 (是的,虽然这是通过网络提供的,但它实际上是一个进行语法突出显示的Vim进程。)

或者考虑shell之类的东西,你可以在其中使用字符串文字类型语法,如"foo bar",但在那里,你可以有一个命令替换,你必须在其中递归识别和着色shell语法: "foo $(for x in *; do ...; done) bar"

所以不,你不能用正则表达式标记来做有用的,准确的语法高亮显示,但是使用分层解析的正则表达式可以做得很好。