mirror of
https://github.com/neovim/neovim.git
synced 2025-09-05 19:08:15 +00:00

If pack() is called with a single value, it does not create a table; it simply returns the value it is passed. When unpack is called with a table argument, it interprets that table as a list of values that were packed together into a table. This causes a problem when the single value being packed is _itself_ a table. pack() will not place it into another table, but unpack() sees the table argument and tries to unpack it. To fix this, we add a simple "tag" to packed table values so that unpack() only attempts to unpack tables that have this tag. Other tables are left alone. The tag is simply the length of the table.
852 lines
18 KiB
Lua
852 lines
18 KiB
Lua
--- Iterator implementation.
|
|
|
|
---@class Iter
|
|
local Iter = {}
|
|
Iter.__index = Iter
|
|
Iter.__call = function(self)
|
|
return self:next()
|
|
end
|
|
|
|
--- Special case implementations for iterators on list tables.
|
|
---@class ListIter : Iter
|
|
---@field _table table Underlying table data (table iterators only)
|
|
---@field _head number Index to the front of a table iterator (table iterators only)
|
|
---@field _tail number Index to the end of a table iterator (table iterators only)
|
|
local ListIter = {}
|
|
ListIter.__index = setmetatable(ListIter, Iter)
|
|
ListIter.__call = function(self)
|
|
return self:next()
|
|
end
|
|
|
|
--- Special case implementations for iterators on non-list tables.
|
|
---@class TableIter : Iter
|
|
local TableIter = {}
|
|
TableIter.__index = setmetatable(TableIter, Iter)
|
|
TableIter.__call = function(self)
|
|
return self:next()
|
|
end
|
|
|
|
---@private
|
|
local function unpack(t)
|
|
if type(t) == 'table' and t.__n ~= nil then
|
|
return _G.unpack(t, 1, t.__n)
|
|
end
|
|
return t
|
|
end
|
|
|
|
---@private
|
|
local function pack(...)
|
|
local n = select('#', ...)
|
|
if n > 1 then
|
|
return { __n = n, ... }
|
|
end
|
|
return ...
|
|
end
|
|
|
|
--- Add a filter step to the iterator pipeline.
|
|
---
|
|
--- Example:
|
|
--- <pre>lua
|
|
--- local bufs = vim.iter(vim.api.nvim_list_bufs()):filter(vim.api.nvim_buf_is_loaded)
|
|
--- </pre>
|
|
---
|
|
---@param f function(...):bool Takes all values returned from the previous stage in the pipeline and
|
|
--- returns false or nil if the current iterator element should be
|
|
--- removed.
|
|
---@return Iter
|
|
function Iter.filter(self, f)
|
|
---@private
|
|
local function fn(...)
|
|
local result = nil
|
|
if select(1, ...) ~= nil then
|
|
if not f(...) then
|
|
return true, nil
|
|
else
|
|
result = pack(...)
|
|
end
|
|
end
|
|
return false, result
|
|
end
|
|
|
|
local next = self.next
|
|
self.next = function(this)
|
|
local cont, result
|
|
repeat
|
|
cont, result = fn(next(this))
|
|
until not cont
|
|
return unpack(result)
|
|
end
|
|
return self
|
|
end
|
|
|
|
---@private
|
|
function ListIter.filter(self, f)
|
|
local inc = self._head < self._tail and 1 or -1
|
|
local n = self._head
|
|
for i = self._head, self._tail - inc, inc do
|
|
local v = self._table[i]
|
|
if f(unpack(v)) then
|
|
self._table[n] = v
|
|
n = n + inc
|
|
end
|
|
end
|
|
self._tail = n
|
|
return self
|
|
end
|
|
|
|
--- Add a map step to the iterator pipeline.
|
|
---
|
|
--- If the map function returns nil, the value is filtered from the iterator.
|
|
---
|
|
--- Example:
|
|
--- <pre>lua
|
|
--- local it = vim.iter({ 1, 2, 3, 4 }):map(function(v)
|
|
--- if v % 2 == 0 then
|
|
--- return v * 3
|
|
--- end
|
|
--- end)
|
|
--- it:totable()
|
|
--- -- { 6, 12 }
|
|
--- </pre>
|
|
---
|
|
---@param f function(...):any Mapping function. Takes all values returned from the previous stage
|
|
--- in the pipeline as arguments and returns one or more new values,
|
|
--- which are used in the next pipeline stage. Nil return values returned
|
|
--- are filtered from the output.
|
|
---@return Iter
|
|
function Iter.map(self, f)
|
|
---@private
|
|
local function fn(...)
|
|
local result = nil
|
|
if select(1, ...) ~= nil then
|
|
result = pack(f(...))
|
|
if result == nil then
|
|
return true, nil
|
|
end
|
|
end
|
|
return false, result
|
|
end
|
|
|
|
local next = self.next
|
|
self.next = function(this)
|
|
local cont, result
|
|
repeat
|
|
cont, result = fn(next(this))
|
|
until not cont
|
|
return unpack(result)
|
|
end
|
|
return self
|
|
end
|
|
|
|
---@private
|
|
function ListIter.map(self, f)
|
|
local inc = self._head < self._tail and 1 or -1
|
|
local n = self._head
|
|
for i = self._head, self._tail - inc, inc do
|
|
local v = pack(f(unpack(self._table[i])))
|
|
if v ~= nil then
|
|
self._table[n] = v
|
|
n = n + inc
|
|
end
|
|
end
|
|
self._tail = n
|
|
return self
|
|
end
|
|
|
|
--- Call a function once for each item in the pipeline.
|
|
---
|
|
--- This is used for functions which have side effects. To modify the values in the iterator, use
|
|
--- |Iter:map()|.
|
|
---
|
|
--- This function drains the iterator.
|
|
---
|
|
---@param f function(...) Function to execute for each item in the pipeline. Takes all of the
|
|
--- values returned by the previous stage in the pipeline as arguments.
|
|
function Iter.each(self, f)
|
|
---@private
|
|
local function fn(...)
|
|
if select(1, ...) ~= nil then
|
|
f(...)
|
|
return true
|
|
end
|
|
end
|
|
while fn(self:next()) do
|
|
end
|
|
end
|
|
|
|
---@private
|
|
function ListIter.each(self, f)
|
|
local inc = self._head < self._tail and 1 or -1
|
|
for i = self._head, self._tail - inc, inc do
|
|
f(unpack(self._table[i]))
|
|
end
|
|
self._head = self._tail
|
|
end
|
|
|
|
--- Collect the iterator into a table.
|
|
---
|
|
--- The resulting table depends on the initial source in the iterator pipeline. List-like tables
|
|
--- and function iterators will be collected into a list-like table. If multiple values are returned
|
|
--- from the final stage in the iterator pipeline, each value will be included in a table. If a
|
|
--- map-like table was used as the initial source, then a map-like table is returned.
|
|
---
|
|
--- Examples:
|
|
--- <pre>lua
|
|
--- vim.iter(string.gmatch('100 20 50', '%d+')):map(tonumber):totable()
|
|
--- -- { 100, 20, 50 }
|
|
---
|
|
--- vim.iter({ 1, 2, 3 }):map(function(v) return v, 2 * v end):totable()
|
|
--- -- { { 1, 2 }, { 2, 4 }, { 3, 6 } }
|
|
---
|
|
--- vim.iter({ a = 1, b = 2, c = 3 }):filter(function(k, v) return v % 2 ~= 0 end):totable()
|
|
--- -- { a = 1, c = 3 }
|
|
--- </pre>
|
|
---
|
|
---@return table
|
|
function Iter.totable(self)
|
|
local t = {}
|
|
|
|
while true do
|
|
local args = pack(self:next())
|
|
if args == nil then
|
|
break
|
|
end
|
|
|
|
if type(args) == 'table' then
|
|
-- Removed packed table tag if it exists
|
|
args.__n = nil
|
|
end
|
|
|
|
t[#t + 1] = args
|
|
end
|
|
return t
|
|
end
|
|
|
|
---@private
|
|
function ListIter.totable(self)
|
|
if self._head == 1 and self._tail == #self._table + 1 and self.next == ListIter.next then
|
|
-- Remove any packed table tags
|
|
for i = 1, #self._table do
|
|
local v = self._table[i]
|
|
if type(v) == 'table' then
|
|
v.__n = nil
|
|
self._table[i] = v
|
|
end
|
|
end
|
|
return self._table
|
|
end
|
|
|
|
return Iter.totable(self)
|
|
end
|
|
|
|
---@private
|
|
function TableIter.totable(self)
|
|
local t = {}
|
|
for k, v in self do
|
|
t[k] = v
|
|
end
|
|
return t
|
|
end
|
|
|
|
--- Fold an iterator or table into a single value.
|
|
---
|
|
---@generic A
|
|
---
|
|
---@param init A Initial value of the accumulator.
|
|
---@param f function(acc:A, ...):A Accumulation function.
|
|
---@return A
|
|
function Iter.fold(self, init, f)
|
|
local acc = init
|
|
|
|
--- Use a closure to handle var args returned from iterator
|
|
---@private
|
|
local function fn(...)
|
|
if select(1, ...) ~= nil then
|
|
acc = f(acc, ...)
|
|
return true
|
|
end
|
|
end
|
|
|
|
while fn(self:next()) do
|
|
end
|
|
return acc
|
|
end
|
|
|
|
---@private
|
|
function ListIter.fold(self, init, f)
|
|
local acc = init
|
|
local inc = self._head < self._tail and 1 or -1
|
|
for i = self._head, self._tail - inc, inc do
|
|
acc = f(acc, unpack(self._table[i]))
|
|
end
|
|
return acc
|
|
end
|
|
|
|
--- Return the next value from the iterator.
|
|
---
|
|
--- Example:
|
|
--- <pre>lua
|
|
---
|
|
--- local it = vim.iter(string.gmatch('1 2 3', '%d+')):map(tonumber)
|
|
--- it:next()
|
|
--- -- 1
|
|
--- it:next()
|
|
--- -- 2
|
|
--- it:next()
|
|
--- -- 3
|
|
---
|
|
--- </pre>
|
|
---
|
|
---@return any
|
|
function Iter.next(self) -- luacheck: no unused args
|
|
-- This function is provided by the source iterator in Iter.new. This definition exists only for
|
|
-- the docstring
|
|
end
|
|
|
|
---@private
|
|
function ListIter.next(self)
|
|
if self._head ~= self._tail then
|
|
local v = self._table[self._head]
|
|
local inc = self._head < self._tail and 1 or -1
|
|
self._head = self._head + inc
|
|
return unpack(v)
|
|
end
|
|
end
|
|
|
|
--- Reverse an iterator.
|
|
---
|
|
--- Only supported for iterators on list-like tables.
|
|
---
|
|
--- Example:
|
|
--- <pre>lua
|
|
---
|
|
--- local it = vim.iter({ 3, 6, 9, 12 }):rev()
|
|
--- it:totable()
|
|
--- -- { 12, 9, 6, 3 }
|
|
---
|
|
--- </pre>
|
|
---
|
|
---@return Iter
|
|
function Iter.rev(self)
|
|
error('rev() requires a list-like table')
|
|
return self
|
|
end
|
|
|
|
---@private
|
|
function ListIter.rev(self)
|
|
local inc = self._head < self._tail and 1 or -1
|
|
self._head, self._tail = self._tail - inc, self._head - inc
|
|
return self
|
|
end
|
|
|
|
--- Peek at the next value in the iterator without consuming it.
|
|
---
|
|
--- Only supported for iterators on list-like tables.
|
|
---
|
|
--- Example:
|
|
--- <pre>lua
|
|
---
|
|
--- local it = vim.iter({ 3, 6, 9, 12 })
|
|
--- it:peek()
|
|
--- -- 3
|
|
--- it:peek()
|
|
--- -- 3
|
|
--- it:next()
|
|
--- -- 3
|
|
---
|
|
--- </pre>
|
|
---
|
|
---@return any
|
|
function Iter.peek(self) -- luacheck: no unused args
|
|
error('peek() requires a list-like table')
|
|
end
|
|
|
|
---@private
|
|
function ListIter.peek(self)
|
|
if self._head ~= self._tail then
|
|
return self._table[self._head]
|
|
end
|
|
end
|
|
|
|
--- Find the first value in the iterator that satisfies the given predicate.
|
|
---
|
|
--- Advances the iterator. Returns nil and drains the iterator if no value is found.
|
|
---
|
|
--- Examples:
|
|
--- <pre>lua
|
|
---
|
|
--- local it = vim.iter({ 3, 6, 9, 12 })
|
|
--- it:find(12)
|
|
--- -- 12
|
|
---
|
|
--- local it = vim.iter({ 3, 6, 9, 12 })
|
|
--- it:find(20)
|
|
--- -- nil
|
|
---
|
|
--- local it = vim.iter({ 3, 6, 9, 12 })
|
|
--- it:find(function(v) return v % 4 == 0 end)
|
|
--- -- 12
|
|
---
|
|
--- </pre>
|
|
---
|
|
---@return any
|
|
function Iter.find(self, f)
|
|
if type(f) ~= 'function' then
|
|
local val = f
|
|
f = function(v)
|
|
return v == val
|
|
end
|
|
end
|
|
|
|
local result = nil
|
|
|
|
--- Use a closure to handle var args returned from iterator
|
|
---@private
|
|
local function fn(...)
|
|
if select(1, ...) ~= nil then
|
|
if f(...) then
|
|
result = pack(...)
|
|
else
|
|
return true
|
|
end
|
|
end
|
|
end
|
|
|
|
while fn(self:next()) do
|
|
end
|
|
return unpack(result)
|
|
end
|
|
|
|
--- Find the first value in the iterator that satisfies the given predicate, starting from the end.
|
|
---
|
|
--- Advances the iterator. Returns nil and drains the iterator if no value is found.
|
|
---
|
|
--- Only supported for iterators on list-like tables.
|
|
---
|
|
--- Examples:
|
|
--- <pre>lua
|
|
---
|
|
--- local it = vim.iter({ 1, 2, 3, 2, 1 }):enumerate()
|
|
--- it:rfind(1)
|
|
--- -- 5 1
|
|
--- it:rfind(1)
|
|
--- -- 1 1
|
|
---
|
|
--- </pre>
|
|
---
|
|
---@see Iter.find
|
|
---
|
|
---@return any
|
|
function Iter.rfind(self, f) -- luacheck: no unused args
|
|
error('rfind() requires a list-like table')
|
|
end
|
|
|
|
---@private
|
|
function ListIter.rfind(self, f) -- luacheck: no unused args
|
|
if type(f) ~= 'function' then
|
|
local val = f
|
|
f = function(v)
|
|
return v == val
|
|
end
|
|
end
|
|
|
|
local inc = self._head < self._tail and 1 or -1
|
|
for i = self._tail - inc, self._head, -inc do
|
|
local v = self._table[i]
|
|
if f(unpack(v)) then
|
|
self._tail = i
|
|
return unpack(v)
|
|
end
|
|
end
|
|
self._head = self._tail
|
|
end
|
|
|
|
--- Return the next value from the end of the iterator.
|
|
---
|
|
--- Only supported for iterators on list-like tables.
|
|
---
|
|
--- Example:
|
|
--- <pre>lua
|
|
--- local it = vim.iter({1, 2, 3, 4})
|
|
--- it:nextback()
|
|
--- -- 4
|
|
--- it:nextback()
|
|
--- -- 3
|
|
--- </pre>
|
|
---
|
|
---@return any
|
|
function Iter.nextback(self) -- luacheck: no unused args
|
|
error('nextback() requires a list-like table')
|
|
end
|
|
|
|
function ListIter.nextback(self)
|
|
if self._head ~= self._tail then
|
|
local inc = self._head < self._tail and 1 or -1
|
|
self._tail = self._tail - inc
|
|
return self._table[self._tail]
|
|
end
|
|
end
|
|
|
|
--- Return the next value from the end of the iterator without consuming it.
|
|
---
|
|
--- Only supported for iterators on list-like tables.
|
|
---
|
|
--- Example:
|
|
--- <pre>lua
|
|
--- local it = vim.iter({1, 2, 3, 4})
|
|
--- it:peekback()
|
|
--- -- 4
|
|
--- it:peekback()
|
|
--- -- 4
|
|
--- it:nextback()
|
|
--- -- 4
|
|
--- </pre>
|
|
---
|
|
---@return any
|
|
function Iter.peekback(self) -- luacheck: no unused args
|
|
error('peekback() requires a list-like table')
|
|
end
|
|
|
|
function ListIter.peekback(self)
|
|
if self._head ~= self._tail then
|
|
local inc = self._head < self._tail and 1 or -1
|
|
return self._table[self._tail - inc]
|
|
end
|
|
end
|
|
|
|
--- Skip values in the iterator.
|
|
---
|
|
--- Example:
|
|
--- <pre>lua
|
|
---
|
|
--- local it = vim.iter({ 3, 6, 9, 12 }):skip(2)
|
|
--- it:next()
|
|
--- -- 9
|
|
---
|
|
--- </pre>
|
|
---
|
|
---@param n number Number of values to skip.
|
|
---@return Iter
|
|
function Iter.skip(self, n)
|
|
for _ = 1, n do
|
|
local _ = self:next()
|
|
end
|
|
return self
|
|
end
|
|
|
|
---@private
|
|
function ListIter.skip(self, n)
|
|
local inc = self._head < self._tail and n or -n
|
|
self._head = self._head + inc
|
|
if (inc > 0 and self._head > self._tail) or (inc < 0 and self._head < self._tail) then
|
|
self._head = self._tail
|
|
end
|
|
return self
|
|
end
|
|
|
|
--- Skip values in the iterator starting from the end.
|
|
---
|
|
--- Only supported for iterators on list-like tables.
|
|
---
|
|
--- Example:
|
|
--- <pre>lua
|
|
--- local it = vim.iter({ 1, 2, 3, 4, 5 }):skipback(2)
|
|
--- it:next()
|
|
--- -- 1
|
|
--- it:nextback()
|
|
--- -- 3
|
|
--- </pre>
|
|
---
|
|
---@param n number Number of values to skip.
|
|
---@return Iter
|
|
function Iter.skipback(self, n) -- luacheck: no unused args
|
|
error('skipback() requires a list-like table')
|
|
return self
|
|
end
|
|
|
|
---@private
|
|
function ListIter.skipback(self, n)
|
|
local inc = self._head < self._tail and n or -n
|
|
self._tail = self._tail - inc
|
|
if (inc > 0 and self._head > self._tail) or (inc < 0 and self._head < self._tail) then
|
|
self._head = self._tail
|
|
end
|
|
return self
|
|
end
|
|
|
|
--- Return the nth value in the iterator.
|
|
---
|
|
--- This function advances the iterator.
|
|
---
|
|
--- Example:
|
|
--- <pre>lua
|
|
---
|
|
--- local it = vim.iter({ 3, 6, 9, 12 })
|
|
--- it:nth(2)
|
|
--- -- 6
|
|
--- it:nth(2)
|
|
--- -- 12
|
|
---
|
|
--- </pre>
|
|
---
|
|
---@param n number The index of the value to return.
|
|
---@return any
|
|
function Iter.nth(self, n)
|
|
if n > 0 then
|
|
return self:skip(n - 1):next()
|
|
end
|
|
end
|
|
|
|
--- Return the nth value from the end of the iterator.
|
|
---
|
|
--- This function advances the iterator.
|
|
---
|
|
--- Only supported for iterators on list-like tables.
|
|
---
|
|
--- Example:
|
|
--- <pre>lua
|
|
---
|
|
--- local it = vim.iter({ 3, 6, 9, 12 })
|
|
--- it:nthback(2)
|
|
--- -- 9
|
|
--- it:nthback(2)
|
|
--- -- 3
|
|
---
|
|
--- </pre>
|
|
---
|
|
---@param n number The index of the value to return.
|
|
---@return any
|
|
function Iter.nthback(self, n)
|
|
if n > 0 then
|
|
return self:skipback(n - 1):nextback()
|
|
end
|
|
end
|
|
|
|
--- Slice an iterator, changing its start and end positions.
|
|
---
|
|
--- This is equivalent to :skip(first - 1):skipback(len - last + 1)
|
|
---
|
|
--- Only supported for iterators on list-like tables.
|
|
---
|
|
---@param first number
|
|
---@param last number
|
|
---@return Iter
|
|
function Iter.slice(self, first, last) -- luacheck: no unused args
|
|
return self:skip(math.max(0, first - 1)):skipback(math.max(0, self._tail - last - 1))
|
|
end
|
|
|
|
--- Return true if any of the items in the iterator match the given predicate.
|
|
---
|
|
---@param pred function(...):bool Predicate function. Takes all values returned from the previous
|
|
--- stage in the pipeline as arguments and returns true if the
|
|
--- predicate matches.
|
|
function Iter.any(self, pred)
|
|
local any = false
|
|
|
|
--- Use a closure to handle var args returned from iterator
|
|
---@private
|
|
local function fn(...)
|
|
if select(1, ...) ~= nil then
|
|
if pred(...) then
|
|
any = true
|
|
else
|
|
return true
|
|
end
|
|
end
|
|
end
|
|
|
|
while fn(self:next()) do
|
|
end
|
|
return any
|
|
end
|
|
|
|
--- Return true if all of the items in the iterator match the given predicate.
|
|
---
|
|
---@param pred function(...):bool Predicate function. Takes all values returned from the previous
|
|
--- stage in the pipeline as arguments and returns true if the
|
|
--- predicate matches.
|
|
function Iter.all(self, pred)
|
|
local all = true
|
|
|
|
---@private
|
|
local function fn(...)
|
|
if select(1, ...) ~= nil then
|
|
if not pred(...) then
|
|
all = false
|
|
else
|
|
return true
|
|
end
|
|
end
|
|
end
|
|
|
|
while fn(self:next()) do
|
|
end
|
|
return all
|
|
end
|
|
|
|
--- Return the last item in the iterator.
|
|
---
|
|
--- Drains the iterator.
|
|
---
|
|
--- Example:
|
|
--- <pre>lua
|
|
---
|
|
--- local it = vim.iter(vim.gsplit('abcdefg', ''))
|
|
--- it:last()
|
|
--- -- 'g'
|
|
---
|
|
--- local it = vim.iter({ 3, 6, 9, 12, 15 })
|
|
--- it:last()
|
|
--- -- 15
|
|
---
|
|
--- </pre>
|
|
---
|
|
---@return any
|
|
function Iter.last(self)
|
|
local last = self:next()
|
|
local cur = self:next()
|
|
while cur do
|
|
last = cur
|
|
cur = self:next()
|
|
end
|
|
return last
|
|
end
|
|
|
|
---@private
|
|
function ListIter.last(self)
|
|
local inc = self._head < self._tail and 1 or -1
|
|
local v = self._table[self._tail - inc]
|
|
self._head = self._tail
|
|
return v
|
|
end
|
|
|
|
--- Add an iterator stage that returns the current iterator count as well as the iterator value.
|
|
---
|
|
--- For list tables, prefer
|
|
--- <pre>lua
|
|
--- vim.iter(ipairs(t))
|
|
--- </pre>
|
|
---
|
|
--- over
|
|
---
|
|
--- <pre>lua
|
|
--- vim.iter(t):enumerate()
|
|
--- </pre>
|
|
---
|
|
--- as the former is faster.
|
|
---
|
|
--- Example:
|
|
--- <pre>lua
|
|
---
|
|
--- local it = vim.iter(vim.gsplit('abc', '')):enumerate()
|
|
--- it:next()
|
|
--- -- 1 'a'
|
|
--- it:next()
|
|
--- -- 2 'b'
|
|
--- it:next()
|
|
--- -- 3 'c'
|
|
---
|
|
--- </pre>
|
|
---
|
|
---@return Iter
|
|
function Iter.enumerate(self)
|
|
local i = 0
|
|
return self:map(function(...)
|
|
i = i + 1
|
|
return i, ...
|
|
end)
|
|
end
|
|
|
|
---@private
|
|
function ListIter.enumerate(self)
|
|
local inc = self._head < self._tail and 1 or -1
|
|
for i = self._head, self._tail - inc, inc do
|
|
local v = self._table[i]
|
|
self._table[i] = pack(i, v)
|
|
end
|
|
return self
|
|
end
|
|
|
|
--- Create a new Iter object from a table or iterator.
|
|
---
|
|
---@param src table|function Table or iterator to drain values from
|
|
---@return Iter
|
|
function Iter.new(src, ...)
|
|
local it = {}
|
|
if type(src) == 'table' then
|
|
local t = {}
|
|
|
|
-- Check if source table can be treated like a list (indices are consecutive integers
|
|
-- starting from 1)
|
|
local count = 0
|
|
for _ in pairs(src) do
|
|
count = count + 1
|
|
local v = src[count]
|
|
if v == nil then
|
|
return TableIter.new(src)
|
|
end
|
|
t[count] = v
|
|
end
|
|
return ListIter.new(t)
|
|
end
|
|
|
|
if type(src) == 'function' then
|
|
local s, var = ...
|
|
|
|
--- Use a closure to handle var args returned from iterator
|
|
---@private
|
|
local function fn(...)
|
|
if select(1, ...) ~= nil then
|
|
var = select(1, ...)
|
|
return ...
|
|
end
|
|
end
|
|
|
|
function it.next()
|
|
return fn(src(s, var))
|
|
end
|
|
|
|
setmetatable(it, Iter)
|
|
else
|
|
error('src must be a table or function')
|
|
end
|
|
return it
|
|
end
|
|
|
|
--- Create a new ListIter
|
|
---
|
|
---@param t table List-like table. Caller guarantees that this table is a valid list.
|
|
---@return Iter
|
|
---@private
|
|
function ListIter.new(t)
|
|
local it = {}
|
|
it._table = t
|
|
it._head = 1
|
|
it._tail = #t + 1
|
|
setmetatable(it, ListIter)
|
|
return it
|
|
end
|
|
|
|
--- Create a new TableIter
|
|
---
|
|
---@param t table Table to iterate over. For list-like tables, use ListIter.new instead.
|
|
---@return Iter
|
|
---@private
|
|
function TableIter.new(t)
|
|
local it = {}
|
|
|
|
local index = nil
|
|
function it.next()
|
|
local k, v = next(t, index)
|
|
if k ~= nil then
|
|
index = k
|
|
return k, v
|
|
end
|
|
end
|
|
|
|
setmetatable(it, TableIter)
|
|
return it
|
|
end
|
|
|
|
return Iter
|