@@ -73,6 +73,39 @@ local function create_accessor_functions (fn_template, accessors)
7373 return res
7474end
7575
76+--- Get list of top-level fields from field descriptor table.
77+-- E.g.: `top_level_fields{'foo', {bar='baz'}, {'qux', 'quux'}}`
78+-- gives {'foo, 'bar', 'qux', 'quux'}
79+-- @local
80+local function top_level_fields (fields)
81+ local result = List:new{}
82+ for _, v in ipairs(fields) do
83+ if type(v) == 'string' then
84+ table.insert(result, v)
85+ elseif type(v) == 'table' and #v == 0 and next(v) then
86+ table.insert(result, (next(v)))
87+ else
88+ result:extend(top_level_fields(v))
89+ end
90+ end
91+ return result
92+end
93+
94+--- Creates a function which behaves like next, but respects field names.
95+-- @local
96+local function make_next_function (fields)
97+ local field_indices = {}
98+ for i, f in ipairs(fields) do
99+ field_indices[f] = i
100+ end
101+
102+ return function (t, field)
103+ local raw_idx = field == nil and 0 or field_indices[field]
104+ local next_field = fields[raw_idx + 1]
105+ return next_field, t[next_field]
106+ end
107+end
108+
76109--- Create a new table which allows to access numerical indices via accessor
77110-- functions.
78111-- @local
@@ -102,6 +135,15 @@ local function create_accessor_behavior (tag, accessors)
102135 rawset(t, k, v)
103136 end
104137 end
138+ behavior.__pairs = function (t)
139+ if accessors == nil then
140+ return next, t
141+ end
142+ local iterable_fields = type(accessors) == 'string'
143+ and {accessors}
144+ or top_level_fields(accessors)
145+ return make_next_function(iterable_fields), t
146+ end
105147 return behavior
106148end
107149
@@ -842,6 +884,14 @@ M.Attr.behavior.__newindex = function(t, k, v)
842884 rawset(t, k, v)
843885 end
844886end
887+M.Attr.behavior.__pairs = function(t)
888+ local field_names = M.Attr.behavior._field_names
889+ local fields = {}
890+ for name, i in pairs(field_names) do
891+ fields[i] = name
892+ end
893+ return make_next_function(fields), t, nil
894+end
845895
846896-- Citation
847897M.Citation = AstElement:make_subtype'Citation'
@@ -892,6 +942,14 @@ M.ListAttributes.behavior.__newindex = function (t, k, v)
892942 rawset(t, k, v)
893943 end
894944end
945+M.ListAttributes.behavior.__pairs = function(t)
946+ local field_names = M.ListAttributes.behavior._field_names
947+ local fields = {}
948+ for name, i in pairs(field_names) do
949+ fields[i] = name
950+ end
951+ return make_next_function(fields), t, nil
952+end
895953
896954
897955------------------------------------------------------------------------