【问题标题】:Is syntax-highlighting programming languages using regular expressions possible?使用正则表达式的语法高亮编程语言是否可行?
【发布时间】:2012-04-14 21:44:20
【问题描述】:

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

现在,最近,Rainbow.js 语法荧光笔已经发布。它的前提被描述为非常简单:

Rainbow 本身非常简单。它遍历代码块,处理正则表达式模式,并将匹配模式包装在标签中。

我认为语法突出显示本质上是一项与语言解析具有相同复杂性的任务,如果我们假设它必须既好又适用于多种语言。尽管如此,虽然该库有相当多的criticism,但无论是HackerNews discussion(作为技术倾斜讨论的示例)都没有提到使用正则表达式突出显示语法在一般情况下基本上是不可能的案例,我认为这是一个重大的、引人注目的缺陷。

现在的问题是:我缺少什么吗?特别是:

  1. 一般情况下是否可以使用正则表达式突出显示语法?
  2. 这是应用的 80/20 规则的一个实例,其中正则表达式可能足够有用?

【问题讨论】:

    标签: regex parsing syntax-highlighting


    【解决方案1】:

    使用正则表达式突出显示语法是一门古老的艺术。我认为即使是 Emacs 和 vi 也是这样开始的。

    我认为语法突出显示本质上是一项与语言解析具有相同复杂性的任务,[...]

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

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

    当然,有些极端情况是 regexp 单独无法捕捉到的。一个典型的例子是:

    foo(bla, bar);
    

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

    所以:如果您的要求是低级别的(即没有上面的示例),这是一个 100/0% 的规则,而对于现实世界的东西通常是一个 90/10 的规则。

    【讨论】:

      【解决方案2】:

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

      但是,使用正则表达式单独来做到这一点是自找麻烦。考虑在 Python 中匹配字符串的情况。 Python 允许使用单引号 ' 或双引号 " 分隔字符串。此外,它允许使用三引号 '''""" 的多行字符串(“heredoc 语法”)。

      那么下面哪些部分是字符串,哪些不是?你能构造一个正确识别字符串文字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 中)比计算机科学意义上的严格 regular 表达式具有更强的表达能力。但仅仅因为你 can use regular expressions 并不意味着你应该


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


      编辑 2:有关语法高亮中极端情况的示例,只需查看 StackOverflow 对上述代码的语法高亮。

      【讨论】:

      • 对,因为词法分析器定义了上下文无关文法(CFG),而正则表达式只能定义正则文法(RG)。
      • 使用“词法分析器”并不一定意味着上下文无关的语法 - 词法分析是您可以对所有类型的语言进行的操作。也就是说,大多数编程语言都有上下文无关的语法。
      • 我怀疑你甚至需要任何没有包含在 19 世纪正则表达式数学定义中的奇特功能。您提供的反向引用可以很容易地替换,因为它只能是四件事中的一次 - ('...')|("...")|('''...''')|("""...""")Python language reference 中描述了每种字符串的确切内容,并且看起来很简单,可以转换为正则表达式。
      • str2 是否意味着以 ' 结尾?
      • @delnan :你是对的,当然。我将证明我给出的具体示例可能不是问题。但是还有很多其他极端情况需要处理(例如 A.H. 的回答。)
      【解决方案3】:

      基本上没有。

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

      Regex 不适合这样的任务。

      【讨论】:

      • -1 错误:对于语法高亮,你不需要解析语言,你只需要tokenize它。你知道,找出哪个部分是字符串文字或标识符或整数文字或关键字。它周围是否有括号,它们是否平衡,不相关。
      • 对不起,这是错误的。语法高亮确实需要识别嵌套结构。这取决于语言。对于某些语言,仅仅识别标记是不够的。并且一些语法突出显示确实可以识别不平衡的括号:例如Vim。
      • @delnan 我不同意,因为这取决于你想要什么样的突出显示。如果它只是关键字 OK 标记化,但如果您想突出显示匹配的大括号或折叠方法(如 Eclipse 所做的) - 您需要了解该语言。此外,“解析”和“标记化”具有相当接近的含义,尤其是与使用正则表达式相比时。我已经编辑了我的答案以包括“标记化”,这样做我不相信会改变我的答案的基本含义。另外,请阅读投票率最高的答案 - 它基本上说明了我在说什么。
      • (1) 高亮匹配括号和折叠方法超出了语法高亮的大多数定义(尽管有相当多的程序一起完成)。 (2) 标记化不是“接近”解析,除非您将“一个更简单且受限制的任务在同一大方向上,经常一起使用”算作“接近”。 (3) AH 的答案要详细得多,并且(正确地)区分了 90% 最好用正则表达式完成,而 10% 是一个可能想要做但实际上不可能做到的仅使用正则表达式完成。
      • @Bohemian 是正确的。尝试编写一个匹配 js 对象的正则表达式。你不能用正则表达式来做,因为正则表达式是有限的。 CFG可以是无限的。一个js对象可以有无限个嵌套对象。基本的高亮可以通过正则表达式实现,但真正的语法高亮需要语言的语法,因此语言语法高亮。
      【解决方案4】:

      一个很好的例子是 Vim 中的语法高亮实现。它使用基于正则表达式的模式。但是,这些模式用于识别文档中的分层包含结构,而不是简单地标记输入。

      您可以声明以正则表达式模式匹配开始和结束的区域(加上另一个有助于跳过中间材料的模式)。这些区域可以声明它们包含其他区域或简单模式。包含可以是递归的。 Vim 解决了所有这些问题。所以它本质上是一种上下文无关的解析形式。

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

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

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

      或者考虑类似 shell,你可以有一个字符串文字类型的语法,比如"foo bar",但是在里面,你可以有一个命令替换,在里面你必须递归地识别和着色 shell 语法:@987654324 @。

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2014-08-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-11-07
        • 2012-05-20
        相关资源
        最近更新 更多