我一直在研究使用LPEG实现语法突出显示支持的a text editor。启动和运行非常简单,但是我仅完成了最低要求。
我定义了一堆这样的模式:
-- 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 hard\n");
最佳答案
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" )
关于parsing - 使用lpeg仅捕获单词边界,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/38690698/