使用 lpeg 仅捕获单词边界

Using lpeg to only capture on word boundaries

我一直在研究 a text editor,它使用 LPEG 来实现语法高亮支持。准备工作 运行 非常简单,但我只完成了最低要求。

我已经定义了一堆这样的模式:

 -- Keywords
 local keyword = C(
    P"auto" +
    P"break" +
    P"case" +
    P"char" + 
    P"int" 
    -- more ..
  ) / function() add_syntax( RED, ... )

这可以正确处理输入,但遗憾的是匹配太多。例如 int 匹配 printf 中间,这是预期的,因为我使用“P”进行文字匹配。

显然要执行 "proper" 突出显示我需要在单词边界上进行匹配,这样 "int" 匹配 "int",但不匹配 "printf"、"vsprintf"等

我试图用它来限制匹配只发生在“<[{ \n”之后,但这并没有达到我想要的效果:

  -- space, newline, comma, brackets followed by the keyword
  S(" \n(<{,")^1 * P"auto"  + 

是否有一个我在这里缺少的简单、明显的解决方案来仅匹配被空格或您期望在 C 代码中的其他字符包围的 keywords/tokens?我确实需要捕获的令牌以便突出显示它,但除此之外我没有采用任何特定方法。

例如这些应该匹配:

 int foo;
 void(int argc,std::list<int,int> ) { .. };

但这不应该:

 fprintf(stderr, "blah.  patterns are hard\n");

我认为您应该否定匹配模式,类似于 documentation:

示例中的匹配模式

If we want to look for a pattern only at word boundaries, we can use the following transformer:

local t = lpeg.locale()
function atwordboundary (p)
  return lpeg.P{
    [1] = p + t.alpha^0 * (1 - t.alpha)^1 * lpeg.V(1)
  }
end

SO answer 也讨论了一些类似的解决方案,因此您可能会感兴趣。

还有 another editor component 使用 LPeg 进行语法高亮分析,因此您可能想看看他们是如何处理这个问题的(或者使用他们的词法分析器,如果它适用于您的设计)。

LPeg 构造 -pattern(或更具体地说,在下面的示例中 -idchar)可以很好地确保当前匹配未被遵循 pattern(即 idchar)。幸运的是,这也适用于输入末尾的空字符串,因此我们不需要对此进行特殊处理。为确保匹配 前面没有 模式,LPeg 提供了 lpeg.B(pattern)。不幸的是,这需要一个匹配固定长度字符串的模式,因此在输入的开头不起作用。要修复以下代码,在回退到检查字符串其余部分的后缀 前缀的模式之前,分别尝试在输入开头不带 lpeg.B() 的情况下进行匹配:

local L = require( "lpeg" )

local function decorate( word )
  -- highlighting in UNIX terminals
  return "[32;1m"..word.."[0m"
end

-- matches characters that may be part of an identifier
local idchar = L.R( "az", "AZ", "09" ) + L.P"_"
-- list of keywords to be highlighted
local keywords = L.C( L.P"in" +
                      L.P"for" )

local function highlight( s )
  local p = L.P{
    (L.V"nosuffix" + "") * (L.V"exactmatch" + 1)^0,
    nosuffix = (keywords / decorate) * -idchar,
    exactmatch = L.B( 1 - idchar ) * L.V"nosuffix",
  }
  return L.match( L.Cs( p ), s )
end

-- tests:
print( highlight"" )
print( highlight"hello world" )
print( highlight"in 0in int for  xfor for_ |for| in" )