使用LPeg维基文本

时间:2010-04-24 22:41:44

标签: lua

长篇故事即将来临,但我会尽量保持简短。我有许多纯文本段落,我从系统中提取并以wiki格式重新输出,以便复制所述数据不是一项艰巨的任务。这一切都很顺利,除了没有为我们有页面的'主题'生成自动引用,最终需要通过阅读所有文本并通过将主题更改为[[主题]手动添加来添加]

第一个要求:每个主题只能被点击一次,这是第一次出现。否则,它将成为一个真正垃圾链接,它会降低可读性。避免与以相同单词开头的主题出现问题

第二个要求:重叠的主题名称应该以最“精确”的主题获取链接的方式处理,并且在以后的情况下,不那么精确的主题不会被链接,因为它们可能不正确。 / p>

示例:

topics = { "Project", "Mary", "Mr. Moore", "Project Omega"}
input = "Mary and Mr. Moore work together on Project Omega. Mr. Moore hates both Mary and Project Omega, but Mary simply loves the Project."
output = function_to_be_written(input)
-- "[[Mary]] and [[Mr. Moore]] work together on [[Project Omega]]. Mr. Moore hates both Mary and Project Omega, but Mary simply loves the [[Project]]."

现在,我很快发现了一个简单或复杂的 string.gsub()无法满足我满足第二个要求所需要的东西,因为它没有办法说'考虑这个匹配为如果没有发生 - 我希望你进一步回溯'。我需要引擎做类似的事情:

input = "abc def ghi"
-- Looping over the input would, in this order, match the following strings:
-- 1) abc def ghi
-- 2) abc def
-- 3) abc
-- 4) def ghi
-- 5) def
-- 6) ghi

一旦字符串与实际主题匹配并且之前未被其wikified版本替换,它就会被替换。如果此主题之前已被wikified版本替换,请不要替换,而只是在主题末尾继续匹配。 (因此对于主题“abc def”,它会在两种情况下测试“ghi”。)

因此我到达了LPeg。我已经阅读了它,玩了它,但它相当复杂,虽然我认为我需要以某种方式使用 lpeg.Cmt lpeg.Cs ,我是无法将两者恰当地混合在一起,使我想做的工作。我没有发布我的练习尝试,因为他们质量很差,而且可能更容易混淆任何人,而不是帮助澄清我的问题。

(为什么我要使用PEG而不是自己编写三重嵌套循环?因为我不想这样做,这是学习PEG的一个很好的借口......除了我在脑海里除非LPeg不可能,否则第一个不是选项。)

2 个答案:

答案 0 :(得分:1)

那么为什么不使用string.find?它仅搜索第一个主题事件,并为您提供其起始索引和长度。您所要做的就是在结果中加上'[['。 对于每个块,复制主题表,并在找到第一个出现时删除它。 按长度排序主题,最先排长,以便首先找到最相关的主题

LPeg是一个很好的工具,但没有必要在这里使用它。

答案 1 :(得分:1)

所以...我感到无聊,需要做点什么:

topics = { "Project", "Mary", "Mr. Moore", "Project Omega"}

pcall ( require , 'luarocks.require' )
require 'lpeg'
local locale = lpeg.locale ( )
local endofstring = -lpeg.P(1)
local endoftoken = (locale.space+locale.punct)^1

table.sort ( topics , function ( a , b ) return #a > #b end ) -- Sort by word length (longest first)
local topicpattern = lpeg.P ( false )
for i = 1, #topics do
    topicpattern = topicpattern + topics [ i ]
end

function wikify ( input )
    local topicsleft = { }
    for i = 1 , #topics do
        topicsleft [ topics [ i ] ] = true
    end

    local makelink = function ( topic )
        if topicsleft [ topic ] then
            topicsleft [ topic ] = nil
            return "[[" .. topic .. "]]"
        else
            return topic
        end
    end

    local patt = lpeg.Ct ( 
        (
            lpeg.Cs ( ( topicpattern / makelink ) )* #(-locale.alnum+endofstring) -- Match topics followed by something thats not alphanumeric
            + lpeg.C ( ( lpeg.P ( 1 ) - endoftoken )^0 * endoftoken ) -- Skip tokens that aren't topics
        )^0 * endofstring -- Match adfinum until end of string
    )
    return table.concat ( patt:match ( input ) )
end

print(wikify("Mary and Mr. Moore work together on Project Omega. Mr. Moore hates both Mary and Project Omega, but Mary simply loves the Project.")..'"')
print(wikify("Mary and Mr. Moore work on Project Omegality. Mr. Moore hates Mary and Project Omega, but Mary loves the Projectaaa.")..'"')

我开始制作一个与所有不同主题相匹配的模式;我们希望首先匹配最长的主题,因此按照从最长到最短的字长对表进行排序。 现在我们需要列出我们在当前输入中没有看到的主题。 makelink引用/链接主题,如果我们还没有看到它,否则留下它。

现在为实际的lpeg东西:

  • lpeg.Ct将我们所有的捕获信息打包到一个表中(一起用于输出)
  • topicpattern / makelink捕获一个主题,并通过我们的makelink函数传入。
  • lpeg.Cs将makelink的结果替换回主题匹配的位置。
  • + lpeg.C ( ( lpeg.P ( 1 ) - locale.space )^0 * locale.space^1 )如果我们与主题不匹配,请跳过一个单词(即不是空格后跟空格)
  • ^0重复。

希望这就是你想要的东西:)。

Daurn

注意:编辑的代码,描述不再正确