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

时间:2018-07-19 10:31:39

标签: sublimetext3 syntax-highlighting smalltalk

我有Smalltalk sublime-syntax file (YAML)用于Sublime Text 3,我想为嵌入式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文本(甚至还有example)中都有新功能embed

我试图做这样的事情:

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

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

有人知道如何正确地将一种语言嵌入另一种语言吗?

2 个答案:

答案 0 :(得分:1)

这个问题有点棘手,因为您提供了一个示例语法定义和一些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

但是,这没有理想的结果;突出显示不正确:

Incorrect Highlighting

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

看起来%{构造没有正确突出显示,并且检查显示%字符的范围是source.smalltalk keyword.other

因此,实际上,当前的问题是使用上述定义,而不是将%{视为C块的开始,而是将其视为关键字,如果是关键字,则匹配a的规则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)来处理类主体(或块)的内容。等等)。

匿名上下文包含规则,当它看到结束]字符,两个不同的匹配项以查找函数名称时弹出,然后在include的上下文中出现block,分别为commentblock-body

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

此外,当context中有多个规则可能匹配时,上下文中的第一个match规则就是所应用的规则(即,它“赢得”了平局) )。

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

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

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

include c-block

在适当的位置,该块突出显示了更多我们期望的样子:

Correct Highlighting

答案 1 :(得分:1)

我能够通过 Regex 前瞻和后视来解决类似的问题

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