我一直在开发一个文本编辑器,该编辑器使用LPEG来实现语法高亮显示支持。启动和运行很简单,但我只完成了最低要求。
我定义了一堆这样的模式:
-- Keywords
local keyword = C(
P"auto" +
P"break" +
P"case" +
P"char" +
P"int"
-- more ..
) / function() add_syntax( RED, ... )
这正确地处理了输入,但遗憾的是匹配太多。例如,int
在printf
的中间匹配,这是预期的,因为我使用"P
"进行文字匹配。
显然,为了执行"适当"的高亮显示,我需要在单词边界上进行匹配,这样"int"匹配"int",但不匹配"printf"、"vsprintf"等。
我试图用它来限制匹配只发生在"<[{ n
"之后,但这并没有达到我想要的效果:
-- space, newline, comma, brackets followed by the keyword
S(" n(<{,")^1 * P"auto" +
有没有一个简单、明显的解决方案,我在这里错过了,只匹配被空白或C代码中期望的其他字符包围的关键字/令牌?我确实需要捕捉到的令牌,这样我就可以突出显示它,但除此之外,我不接受任何特定的方法。
例如,这些应该匹配:
int foo;
void(int argc,std::list<int,int> ) { .. };
但这不应该:
fprintf(stderr, "blah. patterns are hardn");
LPeg构造-pattern
(或者更具体地说,在下面的示例中是-idchar
)很好地确保了当前匹配的后面没有pattern
(即idchar
)。幸运的是,这也适用于输入末尾的空字符串,所以我们不需要对此进行特殊处理。为了确保匹配的前面没有模式,LPeg提供了lpeg.B(pattern)
。不幸的是,这需要一个匹配固定长度字符串的模式,因此在输入开始时不起作用。为了修复以下代码在返回到检查字符串其余部分的后缀和前缀的模式之前,分别尝试在输入开始时不使用lpeg.B()
进行匹配:
local L = require( "lpeg" )
local function decorate( word )
-- highlighting in UNIX terminals
return "27[32;1m"..word.."27[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" )
我认为您应该否定匹配模式,类似于文档中的示例:
如果我们只想在单词边界上寻找模式,我们可以使用以下转换器:
local t = lpeg.locale()
function atwordboundary (p)
return lpeg.P{
[1] = p + t.alpha^0 * (1 - t.alpha)^1 * lpeg.V(1)
}
end
SO的回答也讨论了一些类似的解决方案,因此可能会引起兴趣。
还有另一个编辑器组件使用LPeg进行解析,目的是突出语法,所以您可能想看看它们是如何处理的(或者如果它适用于您的设计,则使用它们的lexer)。