Изменение порядка возврата захватов в шаблоне LPeg?

(Я использую Lua 5.2 и LPeg 0.12)

Предположим, у меня есть образец P это производит некоторое неопределенное количество захватов, если таковые имеются, и я хочу написать создать шаблон Q что захватывает P а также положение после P - но эта позиция должна быть возвращена до захвата P, По сути, если lpeg.match(P * lpeg.Cp(), str, i) результаты в v1, v2, ..., j тогда я хочу lpeg.match(Q, str, i) привести к j, v1, v2, ...,

Это достижимо без необходимости каждый раз создавать новую таблицу P совпадает?

В основном я хочу сделать это, чтобы упростить некоторые функции, которые производят итераторы. Функции итератора Lua без сохранения состояния получают только одну переменную управления, и это должно быть первое значение, возвращаемое функцией итератора.

В мире, который позволяет людям называть последние аргументы переменной функции, я мог бы написать:

function pos_then_captures(pattern)
    local function roll(..., pos)
        return pos, (...)
    end
    return (pattern * lpeg.Cp()) / roll
end

Увы. Простое решение - разумное использование lpeg.Ct():

function pos_then_captures(pattern)
    -- exchange the order of two values and unpack the first parameter
    local function exch(a, b)
        return b, unpack(a)
    end
    return (lpeg.Ct(pattern) * lpeg.Cp()) / exch
end

или чтобы вызывающий абонент lpeg.match сделать пачку / удалить / вставить / распаковать танец. И как бы противно это ни звучало, я бы, наверное, сделал это, потому что lpeg.Ct() может иметь некоторые непредвиденные последствия для патологических, но "правильных" аргументов pos_then_captures,

Каждый из них создает новую таблицу каждый раз pattern успешно сопоставлено, что, по общему признанию, не имеет большого значения в моем приложении, но есть ли способ сделать это без какой-либо магии упаковки-распаковки?

Я не слишком знаком с внутренностями Lua, но мне кажется, что я действительно хочу сделать что-то из стека Lua и поместить его обратно куда-то еще, что не похоже на операцию, которая была бы прямой или эффективной. поддерживается, но, возможно, что-то, что LPeg может сделать в этом конкретном случае.

2 ответа

Захваты времени матча и upvalues ​​делают работу. Эта функция использует Cmt для обеспечения pos установлен, прежде чем наклеить его перед patternЗахваты в pattern / prepend,

Cmt = lpeg.Cmt
Cp  = lpeg.Cp

function prepend_final_pos(pattern)
    -- Upvalues are dynamic, so this variable belongs to a
    -- new environment for each call to prepend_final_pos.
    local pos

    -- lpeg.Cmt(patt, func) passes the entire text being
    -- searched to `function` as the first parameter, then
    -- any captures. Ignore the first parameter.
    local function setpos(_, x)
      pos = x

      -- If we return nothing, Cmt will fail every time
      return true
    end

    -- Keep the varargs safe!
    local function prepend(...)
      return pos, ...
    end

    -- The `/ 0` in `Cmt(etc etc) / 0` is to get rid of that
    -- captured `true` that we picked up from setpos.
    return (pattern / prepend) * (Cmt(Cp(), setpos) / 0)
end

Пример сеанса:

> bar = lpeg.C "bar"
> Pbar = prepend_final_pos(bar)
> print(lpeg.match(Pbar, "foobarzok", 4))
7       bar
> foo = lpeg.C "foo" / "zokzokzok"
> Pfoobar = prepend_final_pos(foo * bar)
> print(lpeg.match(Pfoobar, "foobarzok"))
7       zokzokzok       bar

Как и предполагалось, фактические захваты не влияют на позицию, возвращаемую новым шаблоном; только длина текста соответствует исходному шаблону.

Вы можете сделать это с вашим исходным решением без захватов таблиц или захватов времени матча, как это

function pos_then_captures(pattern)
    local function exch(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, ...)
        if a1 == nil then return end
        if a2 == nil then return a1 end
        if a3 == nil then return a2, a1 end
        if a4 == nil then return a3, a1, a2 end
        if a5 == nil then return a4, a1, a2, a3 end
        if a6 == nil then return a5, a1, a2, a3, a4 end
        if a7 == nil then return a6, a1, a2, a3, a4, a5 end
        if a8 == nil then return a7, a1, a2, a3, a4, a5, a6 end
        if a9 == nil then return a8, a1, a2, a3, a4, a5, a6, a7 end
        if a10 == nil then return a9, a1, a2, a3, a4, a5, a6, a7, a8 end
        local t = { a10, ... }
        return t[#t], a1, a2, a3, a4, a5, a6, a7, a8, a9, unpack(t, 1, #t-1)
    end
    return (pattern * lpeg.Cp()) / exch
end

В следующем примере использования возвращается каждое совпадающее "a" с концом совпадения перед ним.

local p = lpeg.P{ (pos_then_captures(lpeg.C'a') + 1) * lpeg.V(1) + -1 }
print(p:match('abababcd'))

-- output: 2       a       4       a       6       a
Другие вопросы по тегам