如何正确地将另一种语言嵌入到我当前的语言定义中?

How to correctly embed another language into my current language defintion?

我有 Sublime Text 3 Smalltalk sublime-syntax file (YAML),我想为嵌入式 C 代码添加突出显示支持。

smalltalk代码中的inline C(总是以^%\{开始,以%\}$结束)代码

一个简单的例子(不多C但想要一个简单的例子):

sigABRT
    "return the signal number for SIGABRT - 0 if not supported by OS
     (the numeric value is not the same across unix-systems)"

%{  /* NOCONTEXT */
#ifdef SIGABRT
    RETURN ( __mkSmallInteger(SIGABRT) );
#else
    RETURN ( __mkSmallInteger(0) );
#endif
%}
!

Sublime text 中有新功能 embed(甚至还有一个 example)。

我试过这样做:

- match: '^%\{'
  embed: scope:source.c
  embed_scope: meta.environment.embedded.c.smalltalk source.c.embedded
  escape: '%\}$'

但是,我无法将它正确地合并到我的 current highlighting file 中。

有谁知道如何正确地将一种语言嵌入到另一种语言中?

这个问题有点棘手,因为您提供了一个示例语法定义和一些 Smalltalk 源代码示例,但是提供的代码没有被提供的语法突出显示,因为它的结构不正确。

为了我们的目的,我们假设您提供的 Smalltalk 示例是以下示例。这可能有效也可能无效(自从我使用 Smalltalk 以来已经有很长时间了)但它突出显示了您的语法,所以让我们称其足够好以用于测试目的。

Object subclass: Test [
    sigABRT
        "return the signal number for SIGABRT - 0 if not supported by OS
         (the numeric value is not the same across unix-systems)"

    %{  /* NOCONTEXT */
    #ifdef SIGABRT
        RETURN ( __mkSmallInteger(SIGABRT) );
    #else
        RETURN ( __mkSmallInteger(0) );
    #endif
    %}
    !
].

你上面提供的语法匹配是正确的,所以我猜你的问题出在你把它放在语法中的什么地方。

所以我们假设在语法定义中我们可能希望在多个地方匹配这些 C 块之一;在这种情况下,我们可能希望在包含匹配项的语法中创建一个新的 context,以便我们可以在需要的地方 include

c-block:
  - match: '%\{'
    embed: scope:source.c
    embed_scope: meta.environment.embedded.c.smalltalk source.c.embedded
    escape: '%\}$'

这与您在上面提供的摘录相同,但置于上下文中。因此,假设像这样的块可以出现的第一个地方是块的主体。您的语法中有一个 block-body 上下文,因此我们将 include 粘贴到它的末尾以包含这个新上下文:

block-body:
  - include: pragma
  - include: selector
  - include: literal
  - include: block
  - include: comment
  - include: c-block

然而,这并没有达到预期的效果;突出显示不正确:

很明显,至少从 C 注释开始,可能更早,突出显示就出错了。如果在光标位于注释上时使用 Tools > Developer > Show Scope Name,您可以看到分配的范围是 source.smalltalk entity.name.function,这意味着语法将 C 注释开头视为方法名称。

%{ 结构似乎也没有正确突出显示,检查显示 % 字符的范围是 source.smalltalk keyword.other

所以实际上目前的问题是,有了上面的定义,而不是将 %{ 视为开始 C 块,它被视为关键字,如果它是关键字,那么规则匹配 C 块根本不会触发。

如果您查看语法,main 上下文如下所示:

main:
  - match: '([a-zA-Z][a-zA-Z0-9]*)\s*(subclass:)\s*([a-zA-Z][a-zA-Z0-9]*)\s*\['
    captures:
      1: entity.other.inherited-class
      2: keyword.other
      3: entity.name.type
    push:
      - match: '\]'
        pop: true
      - include: pragma
      - match: '(([a-zA-Z][a-zA-Z0-9]*:)|[+\-\/\*~<>=@%|&?!.,:;^]+)\s*([a-zA-Z][a-zA-Z0-9]*)'
        captures:
          1: entity.name.function
          3: variable.other
      - match: "([a-zA-Z][a-zA-Z0-9]*)"
        scope: entity.name.function
      - include: block
      - include: comment
      - include: block-body

这些规则表明,当我们看到以 BaseClass subclass: SubClass [ 开头的行时,我们正在进入一个匿名上下文(通过 push)来处理 [=91] 的内容=] 正文(或块或其他)。

匿名上下文包含当它看到结束 ] 字符时弹出的规则,两个不同的匹配项以查找函数名称,然后是 [=30= 的上下文中的 include ]、commentblock-body

当您 include 时,Sublime 从该上下文中获取所有 match 规则,并在您执行插入的位置插入它们的副本,就像您刚刚手动输入它们一样那里。

此外,当 context 中有多个规则可能匹配时,上下文中的第一个 match 规则将被应用(即它 "wins"领带)。

范围keyword.other适用于来自pragma上下文和selector上下文的规则,selector上下文可以匹配单个% 字符作为关键字。

因此这里的问题是,由于 include c-block 出现在 block-body 上下文的包含列表中的 selector 之后,selector 上下文正在查找并匹配% C 块规则前的字符可以找到它。

然后解决方案是将 include c-block 的位置移动到该项目之前,以确保它首先匹配:

block-body:
  - include: c-block
  - include: pragma
  - include: selector
  - include: literal
  - include: block
  - include: comment

有了这个,块突出显示更像我们期望的那样:

我能够使用 Regex 前瞻和后视解决类似的问题

    - match: ' (?=\{")'
      embed: scope:source.json
      escape: '(?<=\})$'