summaryrefslogtreecommitdiff
path: root/test/catch.lua
blob: 849be09d0e4c9a8c4a8e8c037f9b6e3120179c37 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
local m = require 'lpeglabel'

local mt = {}

local function catchedTable()
    return setmetatable({}, mt)
end

function mt.__add(a, b)
    if not a or not b then
        return a or b
    end
    local t = catchedTable()
    for _, v in ipairs(a) do
        t[#t+1] = v
    end
    for _, v in ipairs(b) do
        t[#t+1] = v
    end
    return t
end

local function parseTokens(script, seps)
    local parser = m.P {
        m.Ct(m.V 'Token'^0),
        Token = m.Cp() * (m.V 'Mark' + m.V 'Nl' + m.V 'Text'),
        Mark  = m.Cc 'ML' * m.P '<' * m.C(m.S(seps))
              + m.Cc 'MR' * m.C(m.S(seps)) * m.P '>',
        Nl    = m.Cc 'NL' * m.C(m.P '\r\n' + m.S '\r\n'),
        Text  = m.Cc 'TX' * m.C((1 - m.V 'Nl' - m.V 'Mark')^1),
    }
    local results = parser:match(script)
    return results
end

---@param script string
---@param seps string
return function (script, seps)
    local tokens = parseTokens(script, seps)
    local newBuf = {}
    local result = {}
    local marks  = {}

    local lineOffset = 1
    local line       = 0
    local skipOffset = 0
    for i = 1, #tokens, 3 do
        local offset = tokens[i + 0]
        local mode   = tokens[i + 1]
        local text   = tokens[i + 2]
        if mode == 'TX' then
            newBuf[#newBuf+1] = text
        end
        if mode == 'NL' then
            newBuf[#newBuf+1] = text
            line = line + 1
            lineOffset = offset + #text - skipOffset
        end
        if mode == 'ML' then
            marks[#marks+1] = {
                char     = text,
                position = line * 10000 + offset - skipOffset - lineOffset,
            }
            skipOffset = skipOffset + 1 + #text
        end
        if mode == 'MR' then
            for j = #marks, 1, -1 do
                local mark = marks[j]
                if mark.char == text then
                    local position = line * 10000 + offset - skipOffset - lineOffset
                    if not result[text] then
                        result[text] = catchedTable()
                    end
                    result[text][#result[text]+1] = { mark.position, position }
                    table.remove(marks, j)
                    break
                end
            end
            skipOffset = skipOffset + 1 + #text
        end
    end

    return table.concat(newBuf), result
end