Lua filters: iterate over AST element fields when using pairs

This makes it possible to iterate over all field names of an AST element
by using a generic `for` loop with `pairs`:

    for field_name, field_content in pairs(element) do
      …
    end

Raw table fields of AST elements should be considered an implementation
detail and might change in the future. Accessing element properties
should always happen through the fields listed in the Lua filter docs.

Note that the iterator currently excludes the `t`/`tag` field.
This commit is contained in:
Albert Krewinkel 2018-10-20 15:06:16 +02:00
parent 8d4027da4d
commit 916db81ade
No known key found for this signature in database
GPG key ID: 388DC0B21F631124

View file

@ -73,6 +73,39 @@ local function create_accessor_functions (fn_template, accessors)
return res
end
--- Get list of top-level fields from field descriptor table.
-- E.g.: `top_level_fields{'foo', {bar='baz'}, {'qux', 'quux'}}`
-- gives {'foo, 'bar', 'qux', 'quux'}
-- @local
local function top_level_fields (fields)
local result = List:new{}
for _, v in ipairs(fields) do
if type(v) == 'string' then
table.insert(result, v)
elseif type(v) == 'table' and #v == 0 and next(v) then
table.insert(result, (next(v)))
else
result:extend(top_level_fields(v))
end
end
return result
end
--- Creates a function which behaves like next, but respects field names.
-- @local
local function make_next_function (fields)
local field_indices = {}
for i, f in ipairs(fields) do
field_indices[f] = i
end
return function (t, field)
local raw_idx = field == nil and 0 or field_indices[field]
local next_field = fields[raw_idx + 1]
return next_field, t[next_field]
end
end
--- Create a new table which allows to access numerical indices via accessor
-- functions.
-- @local
@ -102,6 +135,15 @@ local function create_accessor_behavior (tag, accessors)
rawset(t, k, v)
end
end
behavior.__pairs = function (t)
if accessors == nil then
return next, t
end
local iterable_fields = type(accessors) == 'string'
and {accessors}
or top_level_fields(accessors)
return make_next_function(iterable_fields), t
end
return behavior
end
@ -842,6 +884,14 @@ M.Attr.behavior.__newindex = function(t, k, v)
rawset(t, k, v)
end
end
M.Attr.behavior.__pairs = function(t)
local field_names = M.Attr.behavior._field_names
local fields = {}
for name, i in pairs(field_names) do
fields[i] = name
end
return make_next_function(fields), t, nil
end
-- Citation
M.Citation = AstElement:make_subtype'Citation'
@ -892,6 +942,14 @@ M.ListAttributes.behavior.__newindex = function (t, k, v)
rawset(t, k, v)
end
end
M.ListAttributes.behavior.__pairs = function(t)
local field_names = M.ListAttributes.behavior._field_names
local fields = {}
for name, i in pairs(field_names) do
fields[i] = name
end
return make_next_function(fields), t, nil
end
------------------------------------------------------------------------